// // "$Id: flifstat.cxx 399 2006-11-10 03:36:45Z mike $" // // FLTK-based network status monitor. // // Uses the SNMP Interface MIB (IF-MIB) to get interface statistics. // // Usage: flifstat [host-or-ip [interface]] // // To compile: fltk-config --compile flifstat.cxx // // Copyright 2006 by Michael R Sweet. // // SNMP code Copyright 2006 by Easy Software Products. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License v2 as published // by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if !defined(__APPLE__) || !defined(TCP_NODELAY) # include #endif /* !__APPLE__ || !TCP_NODELAY */ /* * Constants... */ #define SNMP_PORT 161 /* SNMP well-known port */ #define SNMP_MAX_OID 64 /* Maximum number of OID numbers */ #define SNMP_MAX_PACKET 1472 /* Maximum size of SNMP packet */ #define SNMP_MAX_STRING 512 /* Maximum size of string */ #define SNMP_VERSION_1 0 /* SNMPv1 */ #define ASN1_END_OF_CONTENTS 0x00 /* End-of-contents */ #define ASN1_BOOLEAN 0x01 /* BOOLEAN */ #define ASN1_INTEGER 0x02 /* INTEGER or ENUMERATION */ #define ASN1_BIT_STRING 0x03 /* BIT STRING */ #define ASN1_OCTET_STRING 0x04 /* OCTET STRING */ #define ASN1_NULL_VALUE 0x05 /* NULL VALUE */ #define ASN1_OID 0x06 /* OBJECT IDENTIFIER */ #define ASN1_SEQUENCE 0x30 /* SEQUENCE */ #define ASN1_COUNTER32 0x41 /* Counter32 */ #define ASN1_GAUGE32 0x42 /* Gauge32 */ #define ASN1_GET_REQUEST 0xa0 /* Get-Request-PDU */ #define ASN1_GET_RESPONSE 0xa2 /* Get-Response-PDU */ #define MAX_SAMPLES 300 /* Maximum number of samples to track */ /* * Types... */ typedef struct snmp_packet_s /**** SNMP packet ****/ { const char *error; /* Encode/decode error */ int version; /* Version number */ char community[SNMP_MAX_STRING]; /* Community name */ int request_type; /* Request type */ int request_id; /* request-id value */ int error_status; /* error-status value */ int error_index; /* error-index value */ int object_name[SNMP_MAX_OID]; /* object-name value */ int object_type; /* object-value type */ union { int boolean; /* Boolean value */ int integer; /* Integer value */ int oid[SNMP_MAX_OID]; /* OID value */ char string[SNMP_MAX_STRING];/* String value */ } object_value; /* object-value value */ } snmp_packet_t; /* * Display widget to show bandwidth usage and history... */ class IFDisplay : public Fl_Widget { int count_, // Number of samples samples_[MAX_SAMPLES]; // Samples int average_; // Average utilization, octets per second int speed_; // Link speed, bits per second void draw(); public: IFDisplay(int X, int Y, int W, int H, const char *L = 0); void add(int ops); void clear() { average_ = count_ = speed_ = 0; redraw(); } void speed(int bps) { speed_ = bps; redraw(); } }; // // 'IFDisplay::IFDisplay()' - Create a network display widget. // IFDisplay::IFDisplay( int X, // I - X position int Y, // I - Y position int W, // I - Width int H, // I - Height const char *L) // I - Label string : Fl_Widget(X, Y, W, H, L) { count_ = 0; average_ = 0; speed_ = 0; box(FL_THIN_DOWN_BOX); align(FL_ALIGN_LEFT); } // // 'IFDisplay::add()' - Add a sample to the network display widget. // void IFDisplay::add(int ops) // I - Octets per second { if (count_ >= MAX_SAMPLES) { count_ = MAX_SAMPLES - 1; memmove(samples_, samples_ + 1, count_ * sizeof(samples_[0])); } samples_[count_] = ops; count_ ++; int total = 0; // Total of all samples for (int i = 0; i < count_; i ++) total += samples_[i]; average_ = total / count_; redraw(); } // // 'IFDisplay::draw()' - Draw the network display widget. // void IFDisplay::draw() { char s[1024]; // Label string float percent; // Percent usage // Draw label and boxes... draw_label(); draw_box(box(), x(), y(), w(), 20, color()); draw_box(box(), x(), y() + 20, w(), h() - 40, color()); fl_color(color()); fl_rectf(x(), y() + h() - 20, w(), 20); if (!speed_) { fl_color(FL_BLACK); fl_font(FL_HELVETICA_BOLD, FL_NORMAL_SIZE); fl_draw("No Data", x(), y(), w(), 20, FL_ALIGN_CENTER); return; } // Draw upper meter... int X = x() + Fl::box_dx(box()); int Y = y() + Fl::box_dy(box()); int W = w() - Fl::box_dw(box()); int H = 20 - Fl::box_dh(box()); percent = samples_[count_ - 1] * 8.0 / speed_; if (percent < 0.75) fl_color(fl_color_average(FL_YELLOW, FL_GREEN, percent / 0.75)); else if (percent < 1.0) fl_color(fl_color_average(FL_RED, FL_YELLOW, 4.0 * (percent - 0.75))); else { percent = 1.0; fl_color(FL_RED); } fl_rectf(X, Y, (int)(W * percent), H); fl_color(FL_BLACK); fl_yxline(X + W * average_ * 8 / speed_, Y, Y + H - 1); snprintf(s, sizeof(s), "%.1f%%", 100.0 * percent); fl_font(FL_HELVETICA, FL_NORMAL_SIZE); fl_draw(s, X, Y, W, H, FL_ALIGN_LEFT); // Draw lower history graph... Y = y() + h() - 20 + Fl::box_dy(box()) - Fl::box_dh(box()); H = h() - 40 - Fl::box_dh(box()) - 1; fl_color(FL_BLUE); fl_begin_line(); for (int i = 0; i < count_; i ++) { percent = samples_[i] * 8.0 / speed_; if (percent < 1.0) fl_vertex(X + i * W / MAX_SAMPLES, Y - H * percent); else fl_vertex(X + i * W / MAX_SAMPLES, Y - H); } fl_end_line(); fl_color(FL_BLACK); fl_xyline(X, Y - H * average_ * 8 / speed_, X + W - 1); // Draw the current and average Mbps values... snprintf(s, sizeof(s), "AVG: %.2f CUR: %.2f MAX: %.2f Mbps", average_ * 0.000008, samples_[count_ - 1] * 0.000008, speed_ * 0.000001); fl_color(FL_BLACK); fl_draw(s, x(), y() + h() - 20, w(), 20, FL_ALIGN_LEFT); } /* * Local functions... */ static int asn1_decode_snmp(unsigned char *buffer, size_t len, snmp_packet_t *packet); static int asn1_encode_snmp(unsigned char *buffer, size_t len, snmp_packet_t *packet); static int asn1_get_integer(unsigned char **buffer, unsigned char *bufend, int length); static int asn1_get_oid(unsigned char **buffer, unsigned char *bufend, int length, int *oid, int oidsize); static int asn1_get_packed(unsigned char **buffer, unsigned char *bufend); static char *asn1_get_string(unsigned char **buffer, unsigned char *bufend, int length, char *string, int strsize); static int asn1_get_length(unsigned char **buffer, unsigned char *bufend); static int asn1_get_type(unsigned char **buffer, unsigned char *bufend); static void asn1_set_integer(unsigned char **buffer, int integer); static void asn1_set_length(unsigned char **buffer, int length); static void asn1_set_oid(unsigned char **buffer, const int *oid); static void asn1_set_packed(unsigned char **buffer, int integer); static int asn1_size_integer(int integer); static int asn1_size_length(int length); static int asn1_size_oid(const int *oid); static int asn1_size_packed(int integer); static void fd_cb(int fd, void *); static void host_cb(Fl_Widget *, void *); static void interface_cb(Fl_Widget *, void *); static int read_snmp_response(int fd, unsigned *request_id); static void send_snmp_query(int fd, struct sockaddr *addr, int version, const char *community, const unsigned request_id, const int *oid); static void timeout_cb(void *); /* * Local globals... */ static int IFSpeedOID[] = { 1,3,6,1,2,1,2,2,1,5,1,0 }, IFInOctetsOID[] = { 1,3,6,1,2,1,2,2,1,10,1,0 }, IFOutOctetsOID[] = { 1,3,6,1,2,1,2,2,1,16,1,0 }; static int Speed = 0, InOctets = 0, OutOctets = 0; static Fl_Preferences Prefs(Fl_Preferences::USER, "easysw.com", "flifstat"); static int SNMPSocket; static struct addrinfo *Address = 0; static Fl_Double_Window *IFWindow; static char IFTitle[1024]; static Fl_Input *HostField; static Fl_Spinner *InterfaceSpinner; static IFDisplay *InDisplay, *OutDisplay; /* * 'main()' - Monitor network interface via SNMP. */ int // O - Exit status main(int argc, // I - Number of command-line args char *argv[]) // I - Command-line arguments { char host[1024]; // Default host int interface; // Default interface // Check command-line options... if (argc > 3) { fputs("Usage: flifstat [hostname-or-ip [interface]]\n", stderr); return (1); } // Open the SNMP socket... if ((SNMPSocket = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { fl_alert("Unable to create SNMP socket:\n\n%s", strerror(errno)); return (1); } // Use the "gtk+" scheme... Fl::scheme("gtk+"); // Create the window... IFWindow = new Fl_Double_Window(400, 245); InDisplay = new IFDisplay(90, 10, 300, 90, "Receive:"); InDisplay->labelfont(FL_HELVETICA_BOLD); OutDisplay = new IFDisplay(90, 110, 300, 90, "Send:"); OutDisplay->labelfont(FL_HELVETICA_BOLD); HostField = new Fl_Input(90, 210, 180, 25, "Hostname:"); HostField->callback(host_cb); HostField->labelfont(FL_HELVETICA_BOLD); HostField->when(FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY); InterfaceSpinner = new Fl_Spinner(350, 210, 40, 25, "Interface:"); InterfaceSpinner->callback(interface_cb); InterfaceSpinner->labelfont(FL_HELVETICA_BOLD); InterfaceSpinner->range(1.0, 100.0); InterfaceSpinner->step(1.0); IFWindow->end(); IFWindow->show(); // Load the host and interface... if (argc > 1) HostField->value(argv[1]); else { Prefs.get("hostname", host, "", sizeof(host)); HostField->value(host); } if (HostField->value()[0]) host_cb(0, 0); if (argc > 2) interface = atoi(argv[2]); else Prefs.get("interface", interface, 1); if (interface < 1) InterfaceSpinner->value(1.0); else InterfaceSpinner->value(interface); interface_cb(0, 0); // Start things rolling... Fl::add_fd(SNMPSocket, fd_cb); timeout_cb(0); Fl::run(); delete IFWindow; close(SNMPSocket); if (Address) freeaddrinfo(Address); return (0); } /* * 'asn1_decode_snmp()' - Decode a SNMP packet. */ static int /* O - 0 on success, -1 on error */ asn1_decode_snmp(unsigned char *buffer, /* I - Buffer */ size_t len, /* I - Size of buffer */ snmp_packet_t *packet) /* I - SNMP packet */ { unsigned char *bufptr, /* Pointer into the data */ *bufend; /* End of data */ int length; /* Length of value */ /* * Initialize the decoding... */ memset(packet, 0, sizeof(snmp_packet_t)); bufptr = buffer; bufend = buffer + len; if (asn1_get_type(&bufptr, bufend) != ASN1_SEQUENCE) packet->error = "Packet does not start with SEQUENCE"; else if (asn1_get_length(&bufptr, bufend) == 0) packet->error = "SEQUENCE uses indefinite length"; else if (asn1_get_type(&bufptr, bufend) != ASN1_INTEGER) packet->error = "No version number"; else if ((length = asn1_get_length(&bufptr, bufend)) == 0) packet->error = "Version uses indefinite length"; else if ((packet->version = asn1_get_integer(&bufptr, bufend, length)) != SNMP_VERSION_1) packet->error = "Bad SNMP version number"; else if (asn1_get_type(&bufptr, bufend) != ASN1_OCTET_STRING) packet->error = "No community name"; else if ((length = asn1_get_length(&bufptr, bufend)) == 0) packet->error = "Community name uses indefinite length"; else { asn1_get_string(&bufptr, bufend, length, packet->community, sizeof(packet->community)); if ((packet->request_type = asn1_get_type(&bufptr, bufend)) != ASN1_GET_RESPONSE) packet->error = "Packet does not contain a Get-Response-PDU"; else if (asn1_get_length(&bufptr, bufend) == 0) packet->error = "Get-Response-PDU uses indefinite length"; else if (asn1_get_type(&bufptr, bufend) != ASN1_INTEGER) packet->error = "No request-id"; else if ((length = asn1_get_length(&bufptr, bufend)) == 0) packet->error = "request-id uses indefinite length"; else { packet->request_id = asn1_get_integer(&bufptr, bufend, length); if (asn1_get_type(&bufptr, bufend) != ASN1_INTEGER) packet->error = "No error-status"; else if ((length = asn1_get_length(&bufptr, bufend)) == 0) packet->error = "error-status uses indefinite length"; else { packet->error_status = asn1_get_integer(&bufptr, bufend, length); if (asn1_get_type(&bufptr, bufend) != ASN1_INTEGER) packet->error = "No error-index"; else if ((length = asn1_get_length(&bufptr, bufend)) == 0) packet->error = "error-index uses indefinite length"; else { packet->error_index = asn1_get_integer(&bufptr, bufend, length); if (asn1_get_type(&bufptr, bufend) != ASN1_SEQUENCE) packet->error = "No variable-bindings SEQUENCE"; else if (asn1_get_length(&bufptr, bufend) == 0) packet->error = "variable-bindings uses indefinite length"; else if (asn1_get_type(&bufptr, bufend) != ASN1_SEQUENCE) packet->error = "No VarBind SEQUENCE"; else if (asn1_get_length(&bufptr, bufend) == 0) packet->error = "VarBind uses indefinite length"; else if (asn1_get_type(&bufptr, bufend) != ASN1_OID) packet->error = "No name OID"; else if ((length = asn1_get_length(&bufptr, bufend)) == 0) packet->error = "Name OID uses indefinite length"; else { asn1_get_oid(&bufptr, bufend, length, packet->object_name, SNMP_MAX_OID); packet->object_type = asn1_get_type(&bufptr, bufend); if ((length = asn1_get_length(&bufptr, bufend)) == 0 && packet->object_type != ASN1_NULL_VALUE && packet->object_type != ASN1_OCTET_STRING) packet->error = "Value uses indefinite length"; else { switch (packet->object_type) { case ASN1_BOOLEAN : packet->object_value.boolean = asn1_get_integer(&bufptr, bufend, length); break; case ASN1_INTEGER : case ASN1_COUNTER32 : case ASN1_GAUGE32 : packet->object_value.integer = asn1_get_integer(&bufptr, bufend, length); break; case ASN1_NULL_VALUE : break; case ASN1_OCTET_STRING : asn1_get_string(&bufptr, bufend, length, packet->object_value.string, SNMP_MAX_STRING); break; case ASN1_OID : asn1_get_oid(&bufptr, bufend, length, packet->object_value.oid, SNMP_MAX_OID); break; default : packet->error = "Unsupported value type"; break; } } } } } } } return (packet->error ? -1 : 0); } /* * 'asn1_encode_snmp()' - Encode a SNMP packet. */ static int /* O - Length on success, -1 on error */ asn1_encode_snmp(unsigned char *buffer, /* I - Buffer */ size_t bufsize, /* I - Size of buffer */ snmp_packet_t *packet) /* I - SNMP packet */ { unsigned char *bufptr; /* Pointer into buffer */ int total, /* Total length */ msglen, /* Length of entire message */ commlen, /* Length of community string */ reqlen, /* Length of request */ listlen, /* Length of variable list */ varlen, /* Length of variable */ namelen, /* Length of object name OID */ valuelen; /* Length of object value */ /* * Get the lengths of the community string, OID, and message... */ namelen = asn1_size_oid(packet->object_name); switch (packet->object_type) { case ASN1_NULL_VALUE : valuelen = 0; break; case ASN1_BOOLEAN : valuelen = asn1_size_integer(packet->object_value.boolean); break; case ASN1_INTEGER : case ASN1_COUNTER32 : case ASN1_GAUGE32 : valuelen = asn1_size_integer(packet->object_value.integer); break; case ASN1_OCTET_STRING : valuelen = strlen(packet->object_value.string); break; case ASN1_OID : valuelen = asn1_size_oid(packet->object_value.oid); break; default : packet->error = "Unknown object type"; return (-1); } varlen = 1 + asn1_size_length(namelen) + namelen + 1 + asn1_size_length(valuelen) + valuelen; listlen = 1 + asn1_size_length(varlen) + varlen; reqlen = 2 + asn1_size_integer(packet->request_id) + 2 + asn1_size_integer(packet->error_status) + 2 + asn1_size_integer(packet->error_index) + 1 + asn1_size_length(listlen) + listlen; commlen = strlen(packet->community); msglen = 2 + asn1_size_integer(packet->version) + 1 + asn1_size_length(commlen) + commlen + 1 + asn1_size_length(reqlen) + reqlen; total = 1 + asn1_size_length(msglen) + msglen; if (total > bufsize) { packet->error = "Message too large for buffer"; return (-1); } /* * Then format the message... */ bufptr = buffer; *bufptr++ = ASN1_SEQUENCE; /* SNMPv1 message header */ asn1_set_length(&bufptr, msglen); asn1_set_integer(&bufptr, packet->version); /* version */ *bufptr++ = ASN1_OCTET_STRING; /* community */ asn1_set_length(&bufptr, commlen); memcpy(bufptr, packet->community, commlen); bufptr += commlen; *bufptr++ = packet->request_type; /* Get-Request-PDU */ asn1_set_length(&bufptr, reqlen); asn1_set_integer(&bufptr, packet->request_id); asn1_set_integer(&bufptr, packet->error_status); asn1_set_integer(&bufptr, packet->error_index); *bufptr++ = ASN1_SEQUENCE; /* variable-bindings */ asn1_set_length(&bufptr, listlen); *bufptr++ = ASN1_SEQUENCE; /* variable */ asn1_set_length(&bufptr, varlen); asn1_set_oid(&bufptr, packet->object_name); /* ObjectName */ switch (packet->object_type) { case ASN1_NULL_VALUE : *bufptr++ = ASN1_NULL_VALUE; /* ObjectValue */ *bufptr++ = 0; /* Length */ break; case ASN1_BOOLEAN : asn1_set_integer(&bufptr, packet->object_value.boolean); break; case ASN1_INTEGER : case ASN1_COUNTER32 : case ASN1_GAUGE32 : asn1_set_integer(&bufptr, packet->object_value.integer); break; case ASN1_OCTET_STRING : *bufptr++ = ASN1_OCTET_STRING; asn1_set_length(&bufptr, valuelen); memcpy(bufptr, packet->object_value.string, valuelen); bufptr += valuelen; break; case ASN1_OID : asn1_set_oid(&bufptr, packet->object_value.oid); break; } return (bufptr - buffer); } /* * 'asn1_get_integer()' - Get an integer value. */ static int /* O - Integer value */ asn1_get_integer( unsigned char **buffer, /* IO - Pointer in buffer */ unsigned char *bufend, /* I - End of buffer */ int length) /* I - Length of value */ { int value; /* Integer value */ for (value = 0; length > 0 && *buffer < bufend; length --, (*buffer) ++) value = (value << 8) | **buffer; return (value); } /* * 'asn1_get_length()' - Get a value length. */ static int /* O - Length */ asn1_get_length(unsigned char **buffer, /* IO - Pointer in buffer */ unsigned char *bufend) /* I - End of buffer */ { int length; /* Length */ length = **buffer; (*buffer) ++; if (length & 128) length = asn1_get_integer(buffer, bufend, length & 127); return (length); } /* * 'asn1_get_oid()' - Get an OID value. */ static int /* O - Last OID number */ asn1_get_oid( unsigned char **buffer, /* IO - Pointer in buffer */ unsigned char *bufend, /* I - End of buffer */ int length, /* I - Length of value */ int *oid, /* I - OID buffer */ int oidsize) /* I - Size of OID buffer */ { unsigned char *valend; /* End of value */ int *oidend; /* End of OID buffer */ int number; /* OID number */ valend = *buffer + length; oidend = oid + oidsize - 1; if (valend > bufend) valend = bufend; number = asn1_get_packed(buffer, bufend); if (number < 80) { *oid++ = number / 40; number = number % 40; *oid++ = number; } else { *oid++ = 2; number -= 80; *oid++ = number; } while (*buffer < valend) { number = asn1_get_packed(buffer, bufend); if (oid < oidend) *oid++ = number; } *oid = 0; return (number); } /* * 'asn1_get_packed()' - Get a packed integer value. */ static int /* O - Value */ asn1_get_packed( unsigned char **buffer, /* IO - Pointer in buffer */ unsigned char *bufend) /* I - End of buffer */ { int value; /* Value */ value = 0; while ((**buffer & 128) && *buffer < bufend) { value = (value << 7) | (**buffer & 127); (*buffer) ++; } if (*buffer < bufend) { value = (value << 7) | **buffer; (*buffer) ++; } return (value); } /* * 'asn1_get_string()' - Get a string value. */ static char * /* O - String */ asn1_get_string( unsigned char **buffer, /* IO - Pointer in buffer */ unsigned char *bufend, /* I - End of buffer */ int length, /* I - Value length */ char *string, /* I - String buffer */ int strsize) /* I - String buffer size */ { if (length < strsize) { memcpy(string, *buffer, length); string[length] = '\0'; } else { memcpy(string, buffer, strsize - 1); string[strsize - 1] = '\0'; } (*buffer) += length; return (string); } /* * 'asn1_get_type()' - Get a value type. */ static int /* O - Type */ asn1_get_type(unsigned char **buffer, /* IO - Pointer in buffer */ unsigned char *bufend) /* I - End of buffer */ { int type; /* Type */ type = **buffer; (*buffer) ++; if ((type & 31) == 31) type = asn1_get_packed(buffer, bufend); return (type); } /* * 'asn1_set_integer()' - Set an integer value. */ static void asn1_set_integer(unsigned char **buffer,/* IO - Pointer in buffer */ int integer) /* I - Integer value */ { **buffer = ASN1_INTEGER; (*buffer) ++; if (integer > 0x7fffff || integer < -0x800000) { **buffer = 4; (*buffer) ++; **buffer = integer >> 24; (*buffer) ++; **buffer = integer >> 16; (*buffer) ++; **buffer = integer >> 8; (*buffer) ++; **buffer = integer; (*buffer) ++; } else if (integer > 0x7fff || integer < -0x8000) { **buffer = 3; (*buffer) ++; **buffer = integer >> 16; (*buffer) ++; **buffer = integer >> 8; (*buffer) ++; **buffer = integer; (*buffer) ++; } else if (integer > 0x7f || integer < -0x80) { **buffer = 2; (*buffer) ++; **buffer = integer >> 8; (*buffer) ++; **buffer = integer; (*buffer) ++; } else { **buffer = 1; (*buffer) ++; **buffer = integer; (*buffer) ++; } } /* * 'asn1_set_length()' - Set a value length. */ static void asn1_set_length(unsigned char **buffer, /* IO - Pointer in buffer */ int length) /* I - Length value */ { if (length > 255) { **buffer = 0x82; /* 2-byte length */ (*buffer) ++; **buffer = length >> 8; (*buffer) ++; **buffer = length; (*buffer) ++; } else if (length > 127) { **buffer = 0x81; /* 1-byte length */ (*buffer) ++; **buffer = length; (*buffer) ++; } else { **buffer = length; /* Length */ (*buffer) ++; } } /* * 'asn1_set_oid()' - Set an OID value. */ static void asn1_set_oid(unsigned char **buffer, /* IO - Pointer in buffer */ const int *oid) /* I - OID value */ { **buffer = ASN1_OID; (*buffer) ++; asn1_set_length(buffer, asn1_size_oid(oid)); asn1_set_packed(buffer, oid[0] * 40 + oid[1]); for (oid += 2; *oid; oid ++) asn1_set_packed(buffer, *oid); } /* * 'asn1_set_packed()' - Set a packed integer value. */ static void asn1_set_packed(unsigned char **buffer, /* IO - Pointer in buffer */ int integer) /* I - Integer value */ { if (integer > 0xfffffff) { **buffer = (integer >> 28) & 0x7f; (*buffer) ++; } if (integer > 0x1fffff) { **buffer = (integer >> 21) & 0x7f; (*buffer) ++; } if (integer > 0x3fff) { **buffer = (integer >> 14) & 0x7f; (*buffer) ++; } if (integer > 0x7f) { **buffer = (integer >> 7) & 0x7f; (*buffer) ++; } **buffer = integer & 0x7f; (*buffer) ++; } /* * 'asn1_size_integer()' - Figure out the number of bytes needed for an * integer value. */ static int /* O - Size in bytes */ asn1_size_integer(int integer) /* I - Integer value */ { if (integer > 0x7fffff || integer < -0x800000) return (4); else if (integer > 0x7fff || integer < -0x8000) return (3); else if (integer > 0x7f || integer < -0x80) return (2); else return (1); } /* * 'asn1_size_length()' - Figure out the number of bytes needed for a * length value. */ static int /* O - Size in bytes */ asn1_size_length(int length) /* I - Length value */ { if (length > 0xff) return (3); else if (length > 0x7f) return (2); else return (1); } /* * 'asn1_size_oid()' - Figure out the numebr of bytes needed for an * OID value. */ static int /* O - Size in bytes */ asn1_size_oid(const int *oid) /* I - OID value */ { int length; /* Length of value */ for (length = asn1_size_packed(oid[0] * 40 + oid[1]), oid += 2; *oid; oid ++) length += asn1_size_packed(*oid); return (length); } /* * 'asn1_size_packed()' - Figure out the number of bytes needed for a * packed integer value. */ static int /* O - Size in bytes */ asn1_size_packed(int integer) /* I - Integer value */ { if (integer > 0xfffffff) return (5); else if (integer > 0x1fffff) return (4); else if (integer > 0x3fff) return (3); else if (integer > 0x7f) return (2); else return (1); } // // 'fd_cb()' - Read SNMP responses as they come in... // static void fd_cb(int, void *) { unsigned request; // Request ID int val; // Value from request if ((val = read_snmp_response(SNMPSocket, &request)) != -1) { switch (request) { case 0 : /* Speed */ Speed = val; InDisplay->speed(val); OutDisplay->speed(val); break; case 1 : /* InOctets */ if (InOctets) InDisplay->add(val - InOctets); InOctets = val; break; case 2 : /* OutOctets */ if (OutOctets) OutDisplay->add(val - OutOctets); OutOctets = val; break; } } } // // 'host_cb()' - Change the host we are displaying... // static void host_cb(Fl_Widget *wi, void *) { int error; // getaddrinfo() error code struct addrinfo hints; // Address lookup hints // Free any old address... if (Address) { freeaddrinfo(Address); Address = 0; } if (HostField->value()[0]) { // Get the address of the system... memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_flags = 0; hints.ai_socktype = SOCK_STREAM; if ((error = getaddrinfo(HostField->value(), "snmp", &hints, &Address)) != 0) { fl_alert("Unable to lookup \"%s\":\n\n%s", HostField->value(), gai_strerror(error)); return; } if (wi) Prefs.set("hostname", HostField->value()); } Speed = 0; InOctets = 0; OutOctets = 0; InDisplay->clear(); OutDisplay->clear(); snprintf(IFTitle, sizeof(IFTitle), "%s:%d", HostField->value(), (int)InterfaceSpinner->value()); IFWindow->label(IFTitle); } // // 'interface_cb()' - Change the interface we are displaying... // static void interface_cb(Fl_Widget *wi, void *) { Speed = 0; InOctets = 0; OutOctets = 0; InDisplay->clear(); OutDisplay->clear(); IFSpeedOID[sizeof(IFSpeedOID) / sizeof(IFSpeedOID[0]) - 2] = IFInOctetsOID[sizeof(IFInOctetsOID) / sizeof(IFInOctetsOID[0]) - 2] = IFOutOctetsOID[sizeof(IFOutOctetsOID) / sizeof(IFOutOctetsOID[0]) - 2] = (int)InterfaceSpinner->value(); if (wi) Prefs.set("interface", (int)InterfaceSpinner->value()); snprintf(IFTitle, sizeof(IFTitle), "%s:%d", HostField->value(), (int)InterfaceSpinner->value()); IFWindow->label(IFTitle); } /* * 'read_snmp_response()' - Read and parse a SNMP response... */ static int // O - Value read_snmp_response(int fd, // I - SNMP socket file descriptor unsigned *request) // O - Request ID { unsigned char buffer[SNMP_MAX_PACKET]; // Data packet int bytes; // Number of bytes received struct sockaddr_in addr; // Source address socklen_t addrlen; // Source address length snmp_packet_t packet; // Decoded packet /* * Read the response data... */ addrlen = sizeof(struct sockaddr_in); if ((bytes = recvfrom(fd, buffer, sizeof(buffer), 0, (struct sockaddr *)&addr, &addrlen)) < 0) { fprintf(stderr, "ERROR: Unable to read data from socket: %s\n", strerror(errno)); return (-1); } /* * Look for the response status code in the SNMP message header... */ if (asn1_decode_snmp(buffer, bytes, &packet)) { fprintf(stderr, "ERROR: Bad SNMP packet: %s\n", packet.error); return (-1); } if (packet.error_status) return (-1); /* * Process the message... */ *request = packet.request_id; switch (packet.object_type) { case ASN1_INTEGER : case ASN1_COUNTER32 : case ASN1_GAUGE32 : return (packet.object_value.integer); default : return (-1); } } /* * 'send_snmp_query()' - Send an SNMP query packet. */ static void send_snmp_query( int fd, /* I - SNMP socket */ struct sockaddr *addr, /* I - Address to send to */ int version, /* I - SNMP version */ const char *community, /* I - Community name */ const unsigned request_id, /* I - Request ID */ const int *oid) /* I - OID */ { int i; /* Looping var */ snmp_packet_t packet; /* SNMP message packet */ unsigned char buffer[SNMP_MAX_PACKET];/* SNMP message buffer */ int bytes; /* Size of message */ char addrname[32]; /* Address name */ /* * Create the SNMP message... */ memset(&packet, 0, sizeof(packet)); packet.version = version; packet.request_type = ASN1_GET_REQUEST; packet.request_id = request_id; packet.object_type = ASN1_NULL_VALUE; strncpy(packet.community, community, sizeof(packet.community) - 1); for (i = 0; oid[i]; i ++) packet.object_name[i] = oid[i]; bytes = asn1_encode_snmp(buffer, sizeof(buffer), &packet); if (bytes < 0) { fprintf(stderr, "ERROR: Unable to send SNMP query: %s\n", packet.error); return; } /* * Send the message... */ if (sendto(fd, buffer, bytes, 0, addr, sizeof(struct sockaddr_in)) < bytes) fprintf(stderr, "ERROR: Unable to send %d bytes to %s: %s\n", bytes, addrname, strerror(errno)); } // // 'timeout_cb()' - Send a new set of SNMP queries to the host... // static void timeout_cb(void *) { if (Address) { if (!Speed) send_snmp_query(SNMPSocket, Address->ai_addr, SNMP_VERSION_1, "public", 0, IFSpeedOID); send_snmp_query(SNMPSocket, Address->ai_addr, SNMP_VERSION_1, "public", 1, IFInOctetsOID); send_snmp_query(SNMPSocket, Address->ai_addr, SNMP_VERSION_1, "public", 2, IFOutOctetsOID); } Fl::add_timeout(1.0, timeout_cb, 0); } // // End of "flifstat.cxx". //