From bb9a52d915411c911516d2460583abd5c64bc678 Mon Sep 17 00:00:00 2001 From: guy Date: Sat, 20 Oct 2007 01:15:14 +0000 Subject: SITA ACN support, from Fulko Hew. --- CREDITS | 1 + FILES | 5 + INSTALL.txt | 7 +- README.sita | 61 ++++ atmuni31.h | 4 +- config.h.in | 3 + configure | 19 +- configure.in | 11 +- fad-sita.c | 62 ++++ pcap-linux.c | 34 ++- pcap-sita.c | 905 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ pcap-sita.h | 12 + pcap-sita.html | 943 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 13 files changed, 2058 insertions(+), 9 deletions(-) create mode 100644 README.sita create mode 100644 fad-sita.c create mode 100644 pcap-sita.c create mode 100644 pcap-sita.h create mode 100644 pcap-sita.html diff --git a/CREDITS b/CREDITS index 0a01153..b357383 100644 --- a/CREDITS +++ b/CREDITS @@ -34,6 +34,7 @@ Additional people who have contributed patches: Erik de Castro Lopo Florent Drouin Franz Schaefer + Fulko Hew Gianluca Varenni Gilbert Hoyek Gisle Vanem diff --git a/FILES b/FILES index bb2d6f8..307d96e 100644 --- a/FILES +++ b/FILES @@ -13,6 +13,7 @@ README.hpux README.linux README.macosx README.septel +README.sita README.tru64 README.Win32 SUNOS4/nit_if.o.sparc @@ -38,6 +39,7 @@ fad-getad.c fad-gifc.c fad-glifc.c fad-null.c +fad-sita.c fad-win32.c filtertest.c findalldevstest.c @@ -101,6 +103,9 @@ pcap-pf.c pcap-pf.h pcap-septel.c pcap-septel.h +pcap-sita.h +pcap-sita.c +pcap-sita.html pcap-stdinc.h pcap-snit.c pcap-snoop.c diff --git a/INSTALL.txt b/INSTALL.txt index 8876b93..d322c1c 100644 --- a/INSTALL.txt +++ b/INSTALL.txt @@ -1,4 +1,4 @@ -@(#) $Header: /tcpdump/master/libpcap/INSTALL.txt,v 1.21 2007-09-22 02:10:17 guy Exp $ (LBL) +@(#) $Header: /tcpdump/master/libpcap/INSTALL.txt,v 1.22 2007-10-20 01:15:14 guy Exp $ (LBL) To build libpcap, run "./configure" (a shell script). The configure script will determine your system attributes and generate an @@ -309,6 +309,7 @@ README.hpux - notes on using libpcap on HP-UX README.linux - notes on using libpcap on Linux README.macosx - notes on using libpcap on Mac OS X README.septel - notes on using libpcap to capture on Intel/Septel devices +README.sita - notes on using libpcap to capture on SITA devices README.tru64 - notes on using libpcap on Digital/Tru64 UNIX README.Win32 - notes on using libpcap on Win32 systems (with WinPcap) SUNOS4 - pre-SunOS 4.1 replacement kernel nit modules @@ -332,6 +333,7 @@ fad-getad.c - pcap_findalldevs() for systems with getifaddrs() fad-gifc.c - pcap_findalldevs() for systems with only SIOCGIFLIST fad-glifc.c - pcap_findalldevs() for systems with SIOCGLIFCONF fad-null.c - pcap_findalldevs() for systems without capture support +fad-sita.c - pcap_findalldevs() for systems with SITA support fad-win32.c - pcap_findalldevs() for WinPcap filtertest.c - test program for BPF compiler findalldevstest.c - test program for pcap_findalldevs() @@ -377,6 +379,9 @@ pcap-pf.c - Ultrix and Digital/Tru64 UNIX Packet Filter support pcap-pf.h - Ultrix and Digital/Tru64 UNIX Packet Filter definitions pcap-septel.c - Intel/Septel device capture support pcap-septel.h - Intel/Septel device capture support +pcap-sita.c - SITA device capture support +pcap-sita.h - SITA device capture support +pcap-sita.html - SITA device capture documentation pcap-stdinc.h - includes and #defines for compiling on Win32 systems pcap-snit.c - SunOS 4.x STREAMS-based Network Interface Tap support pcap-snoop.c - IRIX Snoop network monitoring support diff --git a/README.sita b/README.sita new file mode 100644 index 0000000..9b7e360 --- /dev/null +++ b/README.sita @@ -0,0 +1,61 @@ +The following instructions apply if you have a Linux platform and want +libpcap to support the 'ACN' WAN/LAN router product from from SITA +(http://www.sita.aero) + +See also the libpcap INSTALL.txt file for further libpcap configuration +options. + +These additions/extensions have been made to PCAP to allow it to +capture packets from a SITA ACN device (and potentially others). + +To enable its support you need to ensure that the distribution has +a correct configure.in file; that can be created if neccessay by +using the normal autoconf procedure of: + +aclocal +autoconf +autoheader +automake + +Then run configure with the 'sita' option: + +./configure --with-sita + +Applications built with libpcap configured in this way will only detect SITA +ACN interfaces and will not capture from the native OS packet stream. + +The SITA extension provides a remote datascope operation for capturing +both WAN and LAN protocols. It effectively splits the operation of +PCAP into two halves. The top layer performs the majority of the +work, but interfaces via a TCP session to remote agents that +provide the lower layer functionality of actual sniffing and +filtering. More detailed information regarding the functions and +inter-device protocol and naming conventions are described in detail +in 'pcap-sita.html'. + +pcap_findalldevs() reads the local system's /etc/hosts file looking +for host names that match the format of IOP type devices. ie. aaa_I_x_y +and then queries each associated IP address for a list of its WAN and +LAN devices. The local system the aggregates the lists obtained from +each IOP, sorts it, and provides it (to Wireshark et.al) as the +list of monitorable interfaces. + +Once a valid interface has been selected, pcap_open() is called +which opens a TCP session (to a well known port) on the target IOP +and tells it to start monitoring. + +All captured packets are then forwarded across that TCP session +back to the local 'top layer' for forwarding to the actual +sniffing program (wireshark...) + +Note that the DLT_SITA protocol includes a proprietary header +that is documented as part of the SITA dissector of Wireshark +and is also described in 'pcap-sita.html' for posterity sake. + +That header provides: +- Packet direction (in/out) (1 octet) +- Link layer hardware signal status (1 octet) +- Transmit/Receive error status (2 octets) +- Encapsulated WAN protocol ID (1 octet) + + diff --git a/atmuni31.h b/atmuni31.h index b06cd39..5920e55 100644 --- a/atmuni31.h +++ b/atmuni31.h @@ -29,13 +29,13 @@ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * - * @(#) $Header: /tcpdump/master/libpcap/atmuni31.h,v 1.1 2002-07-11 09:06:32 guy Exp $ (LBL) + * @(#) $Header: /tcpdump/master/libpcap/atmuni31.h,v 1.2 2007-10-20 01:15:14 guy Exp $ (LBL) */ /* Based on UNI3.1 standard by ATM Forum */ /* ATM traffic types based on VPI=0 and (the following VCI */ -#define PPC 0x05 /* Point-to-point signal msg */ +#define ATM_PPC 0x05 /* Point-to-point signal msg */ #define BCC 0x02 /* Broadcast signal msg */ #define OAMF4SC 0x03 /* Segment OAM F4 flow cell */ #define OAMF4EC 0x04 /* End-to-end OAM F4 flow cell */ diff --git a/config.h.in b/config.h.in index 7a39758..bda5e91 100644 --- a/config.h.in +++ b/config.h.in @@ -182,6 +182,9 @@ /* target host supports USB sniffing */ #undef PCAP_SUPPORT_USB +/* include ACN support */ +#undef SITA + /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS diff --git a/configure b/configure index f2a0f93..1b4556d 100755 --- a/configure +++ b/configure @@ -1,5 +1,5 @@ #! /bin/sh -# From configure.in Revision: 1.138 . +# From configure.in Revision: 1.139 . # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.61. # @@ -1289,6 +1289,7 @@ Optional Packages: --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --without-gcc don't use gcc --with-pcap=TYPE use packet capture TYPE + --with-sita include SITA support --with-dag[=DIR] include Endace DAG support ("yes", "no" or DIR; default="yes" on BSD and Linux if present) --with-dag-includes=DIR Endace DAG include directory --with-dag-libraries=DIR Endace DAG library directory @@ -6341,6 +6342,22 @@ cat >>confdefs.h <<\_ACEOF _ACEOF fi + +# Check whether --with-sita was given. +if test "${with_sita+set}" = set; then + withval=$with_sita; + +cat >>confdefs.h <<\_ACEOF +#define SITA 1 +_ACEOF + + SSRC="pcap-sita.c" + { echo "$as_me:$LINENO: Enabling SITA ACN support" >&5 +echo "$as_me: Enabling SITA ACN support" >&6;} + V_FINDALLDEVS=sita + +fi + ;; dag) diff --git a/configure.in b/configure.in index 2e1eb8a..a95cbaa 100644 --- a/configure.in +++ b/configure.in @@ -1,4 +1,4 @@ -dnl @(#) $Header: /tcpdump/master/libpcap/configure.in,v 1.138 2007-10-04 23:06:25 guy Exp $ (LBL) +dnl @(#) $Header: /tcpdump/master/libpcap/configure.in,v 1.139 2007-10-20 01:15:14 guy Exp $ (LBL) dnl dnl Copyright (c) 1994, 1995, 1996, 1997 dnl The Regents of the University of California. All rights reserved. @@ -6,7 +6,7 @@ dnl dnl Process this file with autoconf to produce a configure script. dnl -AC_REVISION($Revision: 1.138 $) +AC_REVISION($Revision: 1.139 $) AC_PREREQ(2.50) AC_INIT(pcap.c) @@ -371,6 +371,13 @@ linux) AC_MSG_ERROR(version 2 or higher required; see the INSTALL doc for more info) fi AC_LBL_TPACKET_STATS + AC_ARG_WITH(sita, [ --with-sita include SITA support], + [ + AC_DEFINE(SITA,1,[include ACN support]) + SSRC="pcap-sita.c" + AC_MSG_NOTICE(Enabling SITA ACN support) + V_FINDALLDEVS=sita + ]) ;; dag) diff --git a/fad-sita.c b/fad-sita.c new file mode 100644 index 0000000..f985e31 --- /dev/null +++ b/fad-sita.c @@ -0,0 +1,62 @@ +/* + * fad-sita.c: Packet capture interface additions for SITA ACN devices + * + * Copyright (c) 2007 Fulko Hew, SITA INC Canada, Inc + * + * License: BSD + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. The names of the authors may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + + /* $Id: fad-sita.c */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "pcap-int.h" + +extern pcap_if_t *acn_if_list; /* pcap's list of available interfaces */ + +extern int acn_parse_hosts_file(char *errbuf); +extern int acn_findalldevs(char *errbuf); + +int pcap_findalldevs(pcap_if_t **alldevsp, char *errbuf) { + + //printf("pcap_findalldevs()\n"); // fulko + + *alldevsp = 0; /* initialize the returned variables before we do anything */ + strcpy(errbuf, ""); + if (acn_parse_hosts_file(errbuf)) /* scan the hosts file for potential IOPs */ + { + //printf("pcap_findalldevs() returning BAD after parsehosts\n"); // fulko + return -1; + } + //printf("pcap_findalldevs() got hostlist now finding devs\n"); // fulko + if (acn_findalldevs(errbuf)) /* then ask the IOPs for their monitorable devices */ + { + //printf("pcap_findalldevs() returning BAD after findalldevs\n"); // fulko + return -1; + } + *alldevsp = acn_if_list; + acn_if_list = 0; /* then forget our list head, because someone will call pcap_freealldevs() to empty the malloc'ed stuff */ + //printf("pcap_findalldevs() returning ZERO OK\n"); // fulko + return 0; +} diff --git a/pcap-linux.c b/pcap-linux.c index ce3ee3f..a2b198a 100644 --- a/pcap-linux.c +++ b/pcap-linux.c @@ -27,7 +27,7 @@ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/pcap-linux.c,v 1.129 2007-10-05 01:40:14 guy Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/pcap-linux.c,v 1.130 2007-10-20 01:15:14 guy Exp $ (LBL)"; #endif /* @@ -96,6 +96,10 @@ static const char rcsid[] _U_ = #include "pcap-bt-linux.h" #endif +#ifdef SITA +#include "pcap-sita.h" +#endif + #include #include #include @@ -324,6 +328,12 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, * trying both methods with the newer method preferred. */ +#ifdef SITA + live_open_ok = acn_open_live((unsigned char *)device, ebuf, &handle->linktype); + handle->md.clear_promisc = promisc; + handle->fd = live_open_ok; + handle->bufsize = handle->snapshot; +#else if ((err = live_open_new(handle, device, promisc, to_ms, ebuf)) == 1) live_open_ok = 1; else if (err == 0) { @@ -331,6 +341,7 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, if (live_open_old(handle, device, promisc, to_ms, ebuf)) live_open_ok = 1; } +#endif if (!live_open_ok) { /* * Both methods to open the packet socket failed. Tidy @@ -344,6 +355,7 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, return NULL; } +#ifndef SITA /* * Compute the buffer size. * @@ -426,6 +438,7 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, } handle->bufsize = handle->snapshot; } +#endif /* Allocate the buffer */ @@ -444,16 +457,22 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, */ handle->selectable_fd = handle->fd; - handle->read_op = pcap_read_linux; handle->inject_op = pcap_inject_linux; handle->setfilter_op = pcap_setfilter_linux; handle->setdirection_op = pcap_setdirection_linux; handle->set_datalink_op = NULL; /* can't change data link type */ handle->getnonblock_op = pcap_getnonblock_fd; handle->setnonblock_op = pcap_setnonblock_fd; - handle->stats_op = pcap_stats_linux; handle->close_op = pcap_close_linux; +#ifdef SITA + handle->read_op = pcap_read_acn; + handle->stats_op = pcap_stats_acn; +#else + handle->read_op = pcap_read_linux; + handle->stats_op = pcap_stats_linux; +#endif + return handle; } @@ -968,6 +987,10 @@ pcap_setfilter_linux(pcap_t *handle, struct bpf_program *filter) return -1; } +#ifdef SITA + return acn_setfilter(handle->fd, filter); +#endif + /* Make our private copy of the filter */ if (install_bpf_program(handle, filter) < 0) @@ -1729,6 +1752,11 @@ static void pcap_close_all(void) static void pcap_close_linux( pcap_t *handle ) { +#ifdef SITA + pcap_close_acn(handle); + return; +#endif + struct pcap *p, *prevp; struct ifreq ifr; diff --git a/pcap-sita.c b/pcap-sita.c new file mode 100644 index 0000000..d892d11 --- /dev/null +++ b/pcap-sita.c @@ -0,0 +1,905 @@ +/* + * pcap-sita.c: Packet capture interface additions for SITA ACN devices + * + * Copyright (c) 2007 Fulko Hew, SITA INC Canada, Inc + * + * License: BSD + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. The names of the authors may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + + /* $Id: pcap-sita.c */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pcap-int.h" + + /* non-configureable manifests follow */ + +#define IOP_SNIFFER_PORT 49152 /* TCP port on the IOP used for 'distributed pcap' usage */ +#define MAX_LINE_SIZE 255 /* max size of a buffer/line in /etc/hosts we allow */ +#define MAX_CHASSIS 8 /* number of chassis in an ACN site */ +#define MAX_GEOSLOT 8 /* max number of access units in an ACN site */ + +#define FIND 0 +#define LIVE 1 + +typedef struct iface { + struct iface *next; /* a pointer to the next interface */ + char *name; /* this interface's name on Wireshark */ + char *IOPname; /* this interface's name on an IOP */ + uint32_t iftype; /* the type of interface (DLT values) */ +} iface_t; + +typedef struct unit { + char *ip; /* this unit's IP address (as extracted from /etc/hosts) */ + int fd; /* the connection to this unit (if it exists) */ + int find_fd; /* a big kludge to avoid my programming limitations since I could have this unit open for findalldevs purposes */ + int first_time; /* 0 = just opened via acn_open_live(), ie. the first time, NZ = nth time */ + struct sockaddr_in *serv_addr; /* the address control block for comms to this unit */ + int chassis; + int geoslot; + iface_t *iface; /* a pointer to a linked list of interface structures */ + char *imsg; /* a pointer to an inbound message */ + int len; /* the current size of the inbound message */ +} unit_t; + +static char *errorString; +static unit_t units[MAX_CHASSIS+1][MAX_GEOSLOT+1]; /* we use indexes of 1 through 8, but we reserve/waste index 0 */ +static fd_set readfds; /* a place to store the file descriptors for the connections to the IOPs */ +static fd_set working_set; +static int max_fs; +static char static_buf[32]; + +pcap_if_t *acn_if_list; /* pcap's list of available interfaces */ + +static void dump_interface_list() { + pcap_if_t *iff; + pcap_addr_t *addr; + int longest_name_len = 0; + char *n, *d, *f; + int if_number = 0; + + iff = acn_if_list; + while (iff) { + if (iff->name && (strlen(iff->name) > longest_name_len)) longest_name_len = strlen(iff->name); + iff = iff->next; + } + iff = acn_if_list; + printf("Interface List:\n"); + while (iff) { + n = (iff->name) ? iff->name : ""; + d = (iff->description) ? iff->description : ""; + f = (iff->flags == PCAP_IF_LOOPBACK) ? "L" : ""; + printf("%3d: %*s %s '%s'\n", if_number++, longest_name_len, n, f, d); + addr = iff->addresses; + while (addr) { + printf("%*s ", (5 + longest_name_len), ""); /* add some indentation */ + printf("%15s ", (addr->addr) ? inet_ntoa(((struct sockaddr_in *)addr->addr)->sin_addr) : ""); + printf("%15s ", (addr->netmask) ? inet_ntoa(((struct sockaddr_in *)addr->netmask)->sin_addr) : ""); + printf("%15s ", (addr->broadaddr) ? inet_ntoa(((struct sockaddr_in *)addr->broadaddr)->sin_addr) : ""); + printf("%15s ", (addr->dstaddr) ? inet_ntoa(((struct sockaddr_in *)addr->dstaddr)->sin_addr) : ""); + printf("\n"); + addr = addr->next; + } + iff = iff->next; + } +} + +static dump(unsigned char *ptr, int i, int indent) { + fprintf(stderr, "%*s", indent, " "); + for (; i > 0; i--) { + fprintf(stderr, "%2.2x ", *ptr++); + } + fprintf(stderr, "\n"); +} + +static void dump_interface_list_p() { + pcap_if_t *iff; + pcap_addr_t *addr; + int longest_name_len = 0; + char *n, *d, *f; + int if_number = 0; + + iff = acn_if_list; + printf("Interface Pointer @ %p is %p:\n", &acn_if_list, iff); + while (iff) { + printf("%3d: %p %p next: %p\n", if_number++, iff->name, iff->description, iff->next); + dump((unsigned char *)iff, sizeof(pcap_if_t), 5); + addr = iff->addresses; + while (addr) { + printf(" %p %p %p %p, next: %p\n", addr->addr, addr->netmask, addr->broadaddr, addr->dstaddr, addr->next); + dump((unsigned char *)addr, sizeof(pcap_addr_t), 10); + addr = addr->next; + } + iff = iff->next; + } +} + +static void dump_unit_table() { + int chassis, geoslot; + iface_t *p; + + printf("%c:%c %s %s\n", 'C', 'S', "fd", "IP Address"); + for (chassis = 0; chassis <= MAX_CHASSIS; chassis++) { + for (geoslot = 0; geoslot <= MAX_GEOSLOT; geoslot++) { + if (units[chassis][geoslot].ip != NULL) + printf("%d:%d %2d %s\n", chassis, geoslot, units[chassis][geoslot].fd, units[chassis][geoslot].ip); + p = units[chassis][geoslot].iface; + while (p) { + char *n = (p->name) ? p->name : ""; + char *i = (p->IOPname) ? p->IOPname : ""; + p = p->next; + printf(" %12s -> %12s\n", i, n); + } + } + } +} + +static int find_unit_by_fd(int fd, int *chassis, int *geoslot, unit_t **unit_ptr) { + int c, s; + + for (c = 0; c <= MAX_CHASSIS; c++) { + for (s = 0; s <= MAX_GEOSLOT; s++) { + if (units[c][s].fd == fd || units[c][s].find_fd == fd) { + if (chassis) *chassis = c; + if (geoslot) *geoslot = s; + if (unit_ptr) *unit_ptr = &units[c][s]; + return 1; + } + } + } + return 0; +} + +static int read_client_nbytes(int fd, int count, unsigned char *buf) { + unit_t *u; + int chassis, geoslot; + int len; + + find_unit_by_fd(fd, &chassis, &geoslot, &u); + while (count) { + if ((len = recv(fd, buf, count, 0)) <= 0) return -1; /* read in whatever data was sent to us */ + count -= len; + buf += len; + } /* till we have everything we are looking for */ + return 0; +} + +static void empty_unit_iface(unit_t *u) { + iface_t *p, *cur; + + cur = u->iface; + while (cur) { /* loop over all the interface entries */ + if (cur->name) free(cur->name); /* throwing away the contents if they exist */ + if (cur->IOPname) free(cur->IOPname); + p = cur->next; + free(cur); /* then throw away the structure itself */ + cur = p; + } + u->iface = 0; /* and finally remember that there are no remaining structure */ +} + +static void empty_unit(int chassis, int geoslot) { + iface_t *p, *cur; + unit_t *u = &units[chassis][geoslot]; + + empty_unit_iface(u); + if (u->imsg) { /* then if an inbound message buffer exists */ + u->imsg = (char *)realloc(u->imsg, 1); /* and re-allocate the old large buffer into a new small one */ + } +} + +static void empty_unit_table() { + int chassis, geoslot; + + for (chassis = 0; chassis <= MAX_CHASSIS; chassis++) { + for (geoslot = 0; geoslot <= MAX_GEOSLOT; geoslot++) { + if (units[chassis][geoslot].ip != NULL) { + free(units[chassis][geoslot].ip); /* get rid of the malloc'ed space that holds the IP address */ + units[chassis][geoslot].ip = 0; /* then set the pointer to NULL */ + } + empty_unit(chassis, geoslot); + } + } +} + +static char *find_nth_interface_name(int n) { + int chassis, geoslot; + iface_t *p; + char *last_name = 0; + + if (n < 0) n = 0; /* ensure we are working with a valid number */ + for (chassis = 0; chassis <= MAX_CHASSIS; chassis++) { /* scan the table... */ + for (geoslot = 0; geoslot <= MAX_GEOSLOT; geoslot++) { + if (units[chassis][geoslot].ip != NULL) { + p = units[chassis][geoslot].iface; + while (p) { /* and all interfaces... */ + if (p->IOPname) last_name = p->name; /* remembering the last name found */ + if (n-- == 0) return last_name; /* and if we hit the instance requested */ + p = p->next; + } + } + } + } + /* if we couldn't fine the selected entry */ + if (last_name) return last_name; /* ... but we did have at least one entry... return the last entry found */ + return ""; /* ... but if there wasn't any entry... return an empty string instead */ +} + +int acn_parse_hosts_file(char *errbuf) { /* returns: -1 = error, 0 = OK */ + FILE *fp; + char buf[MAX_LINE_SIZE]; + char *ptr, *ptr2; + int pos; + int chassis, geoslot; + unit_t *u; + + empty_unit_table(); + if ((fp = fopen("/etc/hosts", "r")) == NULL) { /* try to open the hosts file and if it fails */ + snprintf(errbuf, PCAP_ERRBUF_SIZE, "Cannot open '/etc/hosts' for reading."); /* return the nohostsfile error response */ + return -1; + } + while (fgets(buf, MAX_LINE_SIZE-1, fp)) { /* while looping over the file */ + + pos = strcspn(buf, "#\n\r"); /* find the first comment character or EOL */ + *(buf + pos) = '\0'; /* and clobber it and anything that follows it */ + + pos = strspn(buf, " \t"); /* then find the first non-white space */ + if (pos == strlen(buf)) /* if there is nothing but white space on the line */ + continue; /* ignore that empty line */ + ptr = buf + pos; /* and skip over any of that leading whitespace */ + + if ((ptr2 = strstr(ptr, "_I_")) == NULL) /* skip any lines that don't have names that look like they belong to IOPs */ + continue; + if (*(ptr2 + 4) != '_') /* and skip other lines that have names that don't look like ACN components */ + continue; + *(ptr + strcspn(ptr, " \t")) = '\0'; /* null terminate the IP address so its a standalone string */ + + chassis = *(ptr2 + 3) - '0'; /* extract the chassis number */ + geoslot = *(ptr2 + 5) - '0'; /* and geo-slot number */ + if (chassis < 1 || chassis > MAX_CHASSIS || + geoslot < 1 || geoslot > MAX_GEOSLOT) { /* if the chassis and/or slot numbers appear to be bad... */ + snprintf(errbuf, PCAP_ERRBUF_SIZE, "Invalid ACN name in '/etc/hosts'."); /* warn the user */ + continue; /* and ignore the entry */ + } + if ((ptr2 = (char *)malloc(strlen(ptr) + 1)) == NULL) { + snprintf(errbuf, PCAP_ERRBUF_SIZE, "malloc: %s", pcap_strerror(errno)); + continue; + } + strcpy(ptr2, ptr); /* copy the IP address into our malloc'ed memory */ + u = &units[chassis][geoslot]; + u->ip = ptr2; /* and remember the whole shebang */ + u->chassis = chassis; + u->geoslot = geoslot; + } + fclose(fp); + if (*errbuf) return -1; + else return 0; +} + +static int open_with_IOP(unit_t *u, int flag) { + int sockfd; + char *ip; + + if (u->serv_addr == NULL) { + u->serv_addr = malloc(sizeof(struct sockaddr_in)); + } + ip = u->ip; + bzero((char *)u->serv_addr, sizeof(struct sockaddr_in)); + u->serv_addr->sin_family = AF_INET; + u->serv_addr->sin_addr.s_addr = inet_addr(ip); + u->serv_addr->sin_port = htons(IOP_SNIFFER_PORT); + + if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + fprintf(stderr, "pcap can't open a socket for connecting to IOP at %s\n", ip); + return 0; + } + if (connect(sockfd, (struct sockaddr *)u->serv_addr, sizeof(struct sockaddr_in)) < 0) { + fprintf(stderr, "pcap can't connect to IOP at %s\n", ip); + return 0; + } + if (flag == LIVE) u->fd = sockfd; + else u->find_fd = sockfd; + u->first_time = 0; + return sockfd; /* return the non-zero file descriptor as a 'success' indicator */ +} + +static void close_with_IOP(int chassis, int geoslot, int flag) { + int *id; + + if (flag == LIVE) id = &units[chassis][geoslot].fd; + else id = &units[chassis][geoslot].find_fd; + + if (*id) { /* this was the last time, so... if we are connected... */ + close(*id); /* disconnect us */ + *id = 0; /* and forget that the descriptor exists because we are not open */ + } +} + +void pcap_close_acn(pcap_t *handle) { + int chassis, geoslot; + unit_t *u; + + if (find_unit_by_fd(handle->fd, &chassis, &geoslot, &u) == 0) + return; + close_with_IOP(chassis, geoslot, LIVE); + if (u) + u->first_time = 0; +} + +static void send_to_fd(int fd, int len, unsigned char *str) { + int nwritten; + int chassis, geoslot; + + while (len > 0) { + if ((nwritten = write(fd, str, len)) <= 0) { + find_unit_by_fd(fd, &chassis, &geoslot, NULL); + if (units[chassis][geoslot].fd == fd) close_with_IOP(chassis, geoslot, LIVE); + else if (units[chassis][geoslot].find_fd == fd) close_with_IOP(chassis, geoslot, FIND); + empty_unit(chassis, geoslot); + return; + } + len -= nwritten; + str += nwritten; + } +} + +static void acn_freealldevs() { + + pcap_if_t *iff, *next_iff; + pcap_addr_t *addr, *next_addr; + + for (iff = acn_if_list; iff != NULL; iff = next_iff) { + next_iff = iff->next; + for (addr = iff->addresses; addr != NULL; addr = next_addr) { + next_addr = addr->next; + if (addr->addr) free(addr->addr); + if (addr->netmask) free(addr->netmask); + if (addr->broadaddr) free(addr->broadaddr); + if (addr->dstaddr) free(addr->dstaddr); + free(addr); + } + if (iff->name) free(iff->name); + if (iff->description) free(iff->description); + free(iff); + } +} + +static char *nonUnified_port_num(unit_t *u, int IOPportnum) { + + sprintf(static_buf, "%d_%d", u->chassis, u->geoslot); + return static_buf; +} + +static char *unified_port_num(unit_t *u, int IOPportnum) { + int portnum; + + portnum = ((u->chassis - 1) * 64) + ((u->geoslot - 1) * 8) + IOPportnum + 1; + sprintf(static_buf, "%d", portnum); + return static_buf; +} + +static char *translate_IOP_to_pcap_name(unit_t *u, char *IOPname, ulong iftype) { + iface_t *iface_ptr, *iface; + char *name; + char buf[32]; + char *proto; + char *port; + int IOPportnum = 0; + + iface = malloc(sizeof(iface_t)); /* get memory for a structure */ + bzero((char *)iface, sizeof(iface_t)); + + iface->iftype = iftype; /* remember the interface type of this interface */ + + name = malloc(strlen(IOPname) + 1); /* get memory for the IOP's name */ + strcpy(name, IOPname); /* and copy it in */ + iface->IOPname = name; /* and stick it into the structure */ + + if (strncmp(IOPname, "lo", 2) == 0) { + IOPportnum = atoi(&IOPname[2]); + switch (iftype) { + case DLT_EN10MB: proto = "lo"; port = nonUnified_port_num(u, IOPportnum); break; + default: proto = "???"; port = unified_port_num(u, IOPportnum); break; + } + } else if (strncmp(IOPname, "eth", 3) == 0) { + IOPportnum = atoi(&IOPname[3]); + switch (iftype) { + case DLT_EN10MB: proto = "eth"; port = nonUnified_port_num(u, IOPportnum); break; + default: proto = "???"; port = unified_port_num(u, IOPportnum); break; + } + } else if (strncmp(IOPname, "wan", 3) == 0) { + IOPportnum = atoi(&IOPname[3]); + switch (iftype) { + case DLT_SITA: proto = "wan"; port = unified_port_num(u, IOPportnum); break; + default: proto = "???"; port = unified_port_num(u, IOPportnum); break; + } + } + + sprintf(buf, "%s_%s", proto, port); /* compose the user's name for that IOP port name */ + name = malloc(strlen(buf) + 1); /* get memory for that name */ + strcpy(name, buf); /* and copy it in */ + iface->name = name; /* and stick it into the structure */ + + if (u->iface == 0) { /* if this is the first name */ + u->iface = iface; /* stick this entry at the head of the list */ + } else { + iface_ptr = u->iface; + while (iface_ptr->next) { /* othewise scan the list */ + iface_ptr = iface_ptr->next; /* till we're at the last entry */ + } + iface_ptr->next = iface; /* then tack this entry on the end of the list */ + } + return iface->name; +} + +static int if_sort(char *s1, char *s2) { + char *s1_p2, *s2_p2; + char str1[MAX_LINE_SIZE], str2[MAX_LINE_SIZE]; + int s1_p1_len, s2_p1_len; + int retval; + + if ((s1_p2 = strchr(s1, '_'))) { /* if an underscore is found... */ + s1_p1_len = s1_p2 - s1; /* the prefix length is the difference in pointers */ + s1_p2++; /* the suffix actually starts _after_ the underscore */ + } else { /* otherwise... */ + s1_p1_len = strlen(s1); /* the prefix length is the length of the string itself */ + s1_p2 = 0; /* and there is no suffix */ + } + if ((s2_p2 = strchr(s2, '_'))) { /* now do the same for the second string */ + s2_p1_len = s2_p2 - s2; + s2_p2++; + } else { + s2_p1_len = strlen(s2); + s2_p2 = 0; + } + strncpy(str1, s1, (s1_p1_len > sizeof(str1)) ? s1_p1_len : sizeof(str1)); *(str1 + s1_p1_len) = 0; + strncpy(str2, s2, (s2_p1_len > sizeof(str2)) ? s2_p1_len : sizeof(str2)); *(str2 + s2_p1_len) = 0; + retval = strcmp(str1, str2); + if (retval != 0) return retval; /* if they are not identical, then we can quit now and return the indication */ + return strcmp(s1_p2, s2_p2); /* otherwise we return the result of comparing the 2nd half of the string */ +} + +static void sort_if_table() { + pcap_if_t *p1, *p2, *prev, *temp; + int has_swapped; + + if (!acn_if_list) return; /* nothing to do if the list is empty */ + + while (1) { + p1 = acn_if_list; /* start at the head of the list */ + prev = 0; + has_swapped = 0; + while ((p2 = p1->next)) { + if (if_sort(p1->name, p2->name) > 0) { + if (prev) { /* we are swapping things that are _not_ at the head of the list */ + temp = p2->next; + prev->next = p2; + p2->next = p1; + p1->next = temp; + } else { /* special treatment if we are swapping with the head of the list */ + temp = p2->next; + acn_if_list= p2; + p2->next = p1; + p1->next = temp; + } + p1 = p2; + prev = p1; + has_swapped = 1; + } + prev = p1; + p1 = p1->next; + } + if (has_swapped == 0) + return; + } + return; +} + +static int process_client_data (char *errbuf) { /* returns: -1 = error, 0 = OK */ + int chassis, geoslot; + unit_t *u; + pcap_if_t *iff, *prev_iff; + pcap_addr_t *addr, *prev_addr; + char *ptr; + int address_count; + struct sockaddr_in *s; + char *newname; + ulong interfaceType; + unsigned char flags; + + prev_iff = 0; + for (chassis = 0; chassis <= MAX_CHASSIS; chassis++) { + for (geoslot = 0; geoslot <= MAX_GEOSLOT; geoslot++) { /* now loop over all the devices */ + u = &units[chassis][geoslot]; + empty_unit_iface(u); + ptr = u->imsg; /* point to the start of the msg for this IOP */ + while (ptr < (u->imsg + u->len)) { + if ((iff = malloc(sizeof(pcap_if_t))) == NULL) { + snprintf(errbuf, PCAP_ERRBUF_SIZE, "malloc: %s", pcap_strerror(errno)); + return -1; + } + bzero((char *)iff, sizeof(pcap_if_t)); + if (acn_if_list == 0) acn_if_list = iff; /* remember the head of the list */ + if (prev_iff) prev_iff->next = iff; /* insert a forward link */ + + if (*ptr) { /* if there is a count for the name */ + if ((iff->name = malloc(*ptr + 1)) == NULL) { /* get that amount of space */ + snprintf(errbuf, PCAP_ERRBUF_SIZE, "malloc: %s", pcap_strerror(errno)); + return -1; + } + memcpy(iff->name, (ptr + 1), *ptr); /* copy the name into the malloc'ed space */ + *(iff->name + *ptr) = 0; /* and null terminate the string */ + ptr += *ptr; /* now move the pointer forwards by the length of the count plus the length of the string */ + } + ptr++; + + if (*ptr) { /* if there is a count for the description */ + if ((iff->description = malloc(*ptr + 1)) == NULL) { /* get that amount of space */ + snprintf(errbuf, PCAP_ERRBUF_SIZE, "malloc: %s", pcap_strerror(errno)); + return -1; + } + memcpy(iff->description, (ptr + 1), *ptr); /* copy the name into the malloc'ed space */ + *(iff->description + *ptr) = 0; /* and null terminate the string */ + ptr += *ptr; /* now move the pointer forwards by the length of the count plus the length of the string */ + } + ptr++; + + interfaceType = ntohl(*(ulong *)ptr); + ptr += 4; /* skip over the interface type */ + + flags = *ptr++; + if (flags) iff->flags = PCAP_IF_LOOPBACK; /* if this is a loopback style interface, lets mark it as such */ + + address_count = *ptr++; + + prev_addr = 0; + while (address_count--) { + if ((addr = malloc(sizeof(pcap_addr_t))) == NULL) { + snprintf(errbuf, PCAP_ERRBUF_SIZE, "malloc: %s", pcap_strerror(errno)); + return -1; + } + bzero((char *)addr, sizeof(pcap_addr_t)); + if (iff->addresses == 0) iff->addresses = addr; + if (prev_addr) prev_addr->next = addr; /* insert a forward link */ + if (*ptr) { /* if there is a count for the address */ + if ((s = malloc(sizeof(struct sockaddr_in))) == NULL) { /* get that amount of space */ + snprintf(errbuf, PCAP_ERRBUF_SIZE, "malloc: %s", pcap_strerror(errno)); + return -1; + } + bzero((char *)s, sizeof(struct sockaddr_in)); + addr->addr = (struct sockaddr *)s; + s->sin_family = AF_INET; + s->sin_addr.s_addr = *(ulong *)(ptr + 1); /* copy the address in */ + ptr += *ptr; /* now move the pointer forwards according to the specified length of the address */ + } + ptr++; /* then forwards one more for the 'length of the address' field */ + if (*ptr) { /* process any netmask */ + if ((s = malloc(sizeof(struct sockaddr_in))) == NULL) { + snprintf(errbuf, PCAP_ERRBUF_SIZE, "malloc: %s", pcap_strerror(errno)); + return -1; + } + bzero((char *)s, sizeof(struct sockaddr_in)); + addr->netmask = (struct sockaddr *)s; + s->sin_family = AF_INET; + s->sin_addr.s_addr = *(ulong*)(ptr + 1); + ptr += *ptr; + } + ptr++; + if (*ptr) { /* process any broadcast address */ + if ((s = malloc(sizeof(struct sockaddr_in))) == NULL) { + snprintf(errbuf, PCAP_ERRBUF_SIZE, "malloc: %s", pcap_strerror(errno)); + return -1; + } + bzero((char *)s, sizeof(struct sockaddr_in)); + addr->broadaddr = (struct sockaddr *)s; + s->sin_family = AF_INET; + s->sin_addr.s_addr = *(ulong*)(ptr + 1); + ptr += *ptr; + } + ptr++; + if (*ptr) { /* process any destination address */ + if ((s = malloc(sizeof(struct sockaddr_in))) == NULL) { + snprintf(errbuf, PCAP_ERRBUF_SIZE, "malloc: %s", pcap_strerror(errno)); + return -1; + } + bzero((char *)s, sizeof(struct sockaddr_in)); + addr->dstaddr = (struct sockaddr *)s; + s->sin_family = AF_INET; + s->sin_addr.s_addr = *(ulong*)(ptr + 1); + ptr += *ptr; + } + ptr++; + prev_addr = addr; + } + prev_iff = iff; + + newname = translate_IOP_to_pcap_name(u, iff->name, interfaceType); /* add a translation entry and get a point to the mangled name */ + if ((iff->name = realloc(iff->name, strlen(newname) + 1)) == NULL) { /* we now re-write the name stored in the interface list */ + snprintf(errbuf, PCAP_ERRBUF_SIZE, "realloc: %s", pcap_strerror(errno)); + return -1; + } + strcpy(iff->name, newname); /* to this new name */ + } + } + } + return 0; +} + +static int read_client_data (int fd) { + unsigned char buf[256]; + int chassis, geoslot; + unit_t *u; + int len; + + find_unit_by_fd(fd, &chassis, &geoslot, &u); + + if ((len = recv(fd, buf, sizeof(buf), 0)) <= 0) return 0; /* read in whatever data was sent to us */ + + if ((u->imsg = realloc(u->imsg, (u->len + len))) == NULL) /* extend the buffer for the new data */ + return 0; + memcpy((u->imsg + u->len), buf, len); /* append the new data */ + u->len += len; + return 1; +} + +static void wait_for_all_answers() { + int retval; + struct timeval tv; + int fd; + int chassis, geoslot; + + tv.tv_sec = 2; + tv.tv_usec = 0; + + while (1) { + int flag = 0; + for (fd = 0; fd <= max_fs; fd++) { /* scan the list of descriptors we may be listening to */ + if (FD_ISSET(fd, &readfds)) flag = 1; /* and see if there are any still set */ + } + if (flag == 0) return; /* we are done, when they are all gone */ + + memcpy(&working_set, &readfds, sizeof(readfds)); /* otherwise, we still have to listen for more stuff, till we timeout */ + retval = select(max_fs + 1, &working_set, NULL, NULL, &tv); + if (retval == -1) { /* an error occured !!!!! */ + return; + } else if (retval == 0) { /* timeout occured, so process what we've got sofar and return */ + printf("timeout\n"); + return; + } else { + for (fd = 0; fd <= max_fs; fd++) { /* scan the list of things to do, and do them */ + if (FD_ISSET(fd, &working_set)) { + if (read_client_data(fd) == 0) { /* if the socket has closed */ + FD_CLR(fd, &readfds); /* and descriptors we listen to for errors */ + find_unit_by_fd(fd, &chassis, &geoslot, NULL); + close_with_IOP(chassis, geoslot, FIND); /* and close out connection to him */ + } + } + } + } + } +} + +static char *get_error_response(int fd, char *errbuf) { /* return a pointer on error, NULL on no error */ + char byte; + int len = 0; + + while (1) { + recv(fd, &byte, 1, 0); /* read another byte in */ + if (errbuf && (len++ < PCAP_ERRBUF_SIZE)) { /* and if there is still room in the buffer */ + *errbuf++ = byte; /* stick it in */ + *errbuf = '\0'; /* ensure the string is null terminated just in case we might exceed the buffer's size */ + } + if (byte == '\0') + if (len > 1) { return errbuf; } + else { return NULL; } + } +} + +int acn_findalldevs(char *errbuf) { /* returns: -1 = error, 0 = OK */ + int chassis, geoslot; + unit_t *u; + + FD_ZERO(&readfds); + max_fs = 0; + for (chassis = 0; chassis <= MAX_CHASSIS; chassis++) { + for (geoslot = 0; geoslot <= MAX_GEOSLOT; geoslot++) { + u = &units[chassis][geoslot]; + if (u->ip && (open_with_IOP(u, FIND))) { /* connect to the remote IOP */ + send_to_fd(u->find_fd, 1, (unsigned char *)"\0"); + if (get_error_response(u->find_fd, errbuf)) + close_with_IOP(chassis, geoslot, FIND); + else { + if (u->find_fd > max_fs) + max_fs = u->find_fd; /* remember the highest number currently in use */ + FD_SET(u->find_fd, &readfds); /* we are going to want to read this guy's response to */ + u->len = 0; + send_to_fd(u->find_fd, 1, (unsigned char *)"Q"); /* this interface query request */ + } + } + } + } + wait_for_all_answers(); + if (process_client_data(errbuf)) + return -1; + sort_if_table(); + return 0; +} + +int pcap_stats_acn(pcap_t *handle, struct pcap_stat *ps) { + unsigned char buf[12]; + + send_to_fd(handle->fd, 1, (unsigned char *)"S"); /* send the get_stats command to the IOP */ + + if (read_client_nbytes(handle->fd, sizeof(buf), buf) == -1) return -1; /* try reading the required bytes */ + + ps->ps_recv = ntohl(*(uint32_t *)&buf[0]); /* break the buffer into its three 32 bit components */ + ps->ps_drop = ntohl(*(uint32_t *)&buf[4]); + ps->ps_ifdrop = ntohl(*(uint32_t *)&buf[8]); + + return 0; +} + +int acn_open_live(char *name, char *errbuf, int *linktype) { /* returns 0 on error, else returns the file descriptor */ + int chassis, geoslot; + unit_t *u; + iface_t *p; + pcap_if_t *alldevsp; + + pcap_findalldevs(&alldevsp, errbuf); + for (chassis = 0; chassis <= MAX_CHASSIS; chassis++) { /* scan the table... */ + for (geoslot = 0; geoslot <= MAX_GEOSLOT; geoslot++) { + u = &units[chassis][geoslot]; + if (u->ip != NULL) { + p = u->iface; + while (p) { /* and all interfaces... */ + if (p->IOPname && p->name && (strcmp(p->name, name) == 0)) { /* and if we found the interface we want... */ + *linktype = p->iftype; + open_with_IOP(u, LIVE); /* start a connection with that IOP */ + send_to_fd(u->fd, strlen(p->IOPname)+1, (unsigned char *)p->IOPname); /* send the IOP's interface name, and a terminating null */ + if (get_error_response(u->fd, errbuf)) { + return 0; + } + return u->fd; /* and return that open descriptor */ + } + p = p->next; + } + } + } + } + return 0; /* if the interface wasn't found, return an error */ +} + +void acn_start_monitor(int fd, int snaplen, int timeout, int promiscuous, int direction) { + unsigned char buf[8]; + unit_t *u; + + //printf("acn_start_monitor()\n"); // fulko + find_unit_by_fd(fd, NULL, NULL, &u); + if (u->first_time == 0) { + buf[0] = 'M'; + *(uint32_t *)&buf[1] = htonl(snaplen); + buf[5] = timeout; + buf[6] = promiscuous; + buf[7] = direction; + //printf("acn_start_monitor() first time\n"); // fulko + send_to_fd(fd, 8, buf); /* send the start monitor command with its parameters to the IOP */ + u->first_time = 1; + } + //printf("acn_start_monitor() complete\n"); // fulko +} + +int acn_setfilter(int fd, struct bpf_program *bpf) { + int count; + struct bpf_insn *p; + uint16_t shortInt; + uint32_t longInt; + + send_to_fd(fd, 1, (unsigned char *)"F"); /* BPF filter follows command */ + count = bpf->bf_len; + longInt = htonl(count); + send_to_fd(fd, 4, (unsigned char *)&longInt); /* send the instruction sequence count */ + p = bpf->bf_insns; + while (count--) { /* followed by the list of instructions */ + shortInt = htons(p->code); + longInt = htonl(p->k); + send_to_fd(fd, 2, (unsigned char *)&shortInt); + send_to_fd(fd, 1, (unsigned char *)&p->jt); + send_to_fd(fd, 1, (unsigned char *)&p->jf); + send_to_fd(fd, 4, (unsigned char *)&longInt); + p++; + } + if (get_error_response(fd, NULL)) + return -1; + return 0; +} + +int acn_read_n_bytes_with_timeout(pcap_t *handle, int count) { + struct timeval tv; + int retval, fd; + fd_set r_fds; + fd_set w_fds; + u_char *bp; + int len = 0; + int offset = 0; + + tv.tv_sec = 5; + tv.tv_usec = 0; + + fd = handle->fd; + FD_ZERO(&r_fds); + FD_SET(fd, &r_fds); + memcpy(&w_fds, &r_fds, sizeof(r_fds)); + bp = handle->bp; + while (count) { + retval = select(fd + 1, &w_fds, NULL, NULL, &tv); + if (retval == -1) { /* an error occured !!!!! */ +// fprintf(stderr, "error during packet data read\n"); + return -1; /* but we need to return a good indication to prevent unneccessary popups */ + } else if (retval == 0) { /* timeout occured, so process what we've got sofar and return */ +// fprintf(stderr, "timeout during packet data read\n"); + return -1; + } else { + if ((len = recv(fd, (bp + offset), count, 0)) <= 0) { +// fprintf(stderr, "premature exit during packet data rx\n"); + return -1; + } + count -= len; + offset += len; + } + } + return 0; +} + +int pcap_read_acn(pcap_t *handle, int max_packets, pcap_handler callback, u_char *user) { + #define HEADER_SIZE (4 * 4) + unsigned char packet_header[HEADER_SIZE]; + struct pcap_pkthdr pcap_header; + + //printf("pcap_read_acn()\n"); // fulko + acn_start_monitor(handle->fd, handle->snapshot, handle->md.timeout, handle->md.clear_promisc, handle->direction); /* maybe tell him to start monitoring */ + //printf("pcap_read_acn() after start monitor\n"); // fulko + + handle->bp = packet_header; + if (acn_read_n_bytes_with_timeout(handle, HEADER_SIZE) == -1) return 0; /* try to read a packet header in so we can get the sizeof the packet data */ + + pcap_header.ts.tv_sec = ntohl(*(uint32_t *)&packet_header[0]); /* tv_sec */ + pcap_header.ts.tv_usec = ntohl(*(uint32_t *)&packet_header[4]); /* tv_usec */ + pcap_header.caplen = ntohl(*(uint32_t *)&packet_header[8]); /* caplen */ + pcap_header.len = ntohl(*(uint32_t *)&packet_header[12]); /* len */ + + handle->bp = handle->buffer + handle->offset; /* start off the receive pointer at the right spot */ + if (acn_read_n_bytes_with_timeout(handle, pcap_header.caplen) == -1) return 0; /* then try to read in the rest of the data */ + + callback(user, &pcap_header, handle->bp); /* call the user supplied callback function */ + return 1; +} diff --git a/pcap-sita.h b/pcap-sita.h new file mode 100644 index 0000000..8c50066 --- /dev/null +++ b/pcap-sita.h @@ -0,0 +1,12 @@ +/* + * pcap-sita.h: Packet capture interface for SITA WAN devices + * + * Authors: Fulko Hew (fulko.hew@sita.aero) (+1 905 6815570); + * + * @(#) $Header: /tcpdump/master/libpcap/pcap-sita.h + */ + +void pcap_close_acn(pcap_t *handle); +int pcap_stats_acn(pcap_t *handle, struct pcap_stat *ps); +int pcap_read_acn(pcap_t *handle, int max_packets, pcap_handler callback, u_char *user); + diff --git a/pcap-sita.html b/pcap-sita.html new file mode 100644 index 0000000..5f7884f --- /dev/null +++ b/pcap-sita.html @@ -0,0 +1,943 @@ + + + + + + + + +
+ A "Distributed Pcap" for
Remote Monitoring LANs & WANs

+ (Design Notes for the SITA ACN device)
+
+ Fulko Hew
SITA INC Canada, Inc.
Revised: October 2, 2007 +
+ + +

SUMMARY

+
    + Note: This document is part of the libpcap CVS and was derived from 'pcap.3' (circa Aug/07). +

    + The ACN provides a customized/distributed version of this library that alows SMPs to + interact with the various IOPs within the site providing a standard mechanism + to capture LAN and WAN message traffic. +

    +

    + + + + + + + + + +
    SMPThe Supervisory Management Processor where Wireshark (or equivalent) + runs in conjuction with a libpcap front-end.
    IOPI/O Processors where the monitored ports exist in conjunction + with a custom device driver/libpcap back-end.
    +
    +

    + Each IOP will be capable of supporting multiple connections from an SMP + enabling monitoring of more than one interface at a time, each through + its own seperate connection. The IOP is responsible to ensure and report + an error if any attempt is made to monitor the same interface more than once. +

    + There are three applications that will be supported by the ACN version of libpcap. + They each use a slightly different mode for looping/capturing and termination + as summarized in the following table: +

    +

    + + + + + + + + + + + + + + +
    Application Capture Termination
    wiresharkpcap_dispatch(all packets in one buffer of capture only)pcap_breakloop()
    tsharkpcap_dispatch(one buffer of capture only)Since a CTRL-C was used to terminate the application, pcap_breakloop() is never called.
    tcpdumppcap_loop(all packets in the next buffer, and loop forever)pcap_breakloop()
    +
    +

    + Note: In all cases, the termination of capturing is always (apparently) followed by + pcap_close(). Pcap_breakloop() is only used to stop/suspend looping/processing, + and upon close interpretation of the function definitions, it is possible to resume + capturing following a pcap_breakloop() without any re-initialization. +

    +

    ACN Limitations

    +
      +
    1. Monitoring of backup IOPs is not currently supported. +
    2. Ethernet interfaces cannot be monitored in promiscuous mode. +
    + +
+ +

ROUTINES

+
    + The following list of functions is the sub-set of Pcap functions that have been + altered/enhanced to support the ACN remote monitoring facility. The remainder of the Pcap + functions continue to perform their duties un-altered. Libpcap only supports this + mode of operation if it has been configured/compiled for SITA/ACN support. +

    +

      + pcap_findalldevs
      + pcap_freealldevs
      + pcap_open_live
      + pcap_close
      + pcap_setfilter
      + pcap_dispatch
      + pcap_loop
      + pcap_next
      + pcap_next_ex
      + pcap_stats
      +
    + + These subroutines have been modified for the ACN specific distributed and remote monitoring + ability perform the following basic functions. More detail is provided in the + "SMP/IOP Inter-Process Communication Protocol" section. +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    pcap_open_live()Used to obtain a packet capture descriptor to look at packets on the network.
    + + + + + + +
    SMP -> IOP + The SMP will open a connection to the selected IOP on its 'sniffer' port + to ensure it is available. It sends a null terminated string identifying + the interface to be monitored. +
    IOP -> SMP + After any required processing is complete, the IOP will return a + null terminated string containing an error message if one occured. + If no error occured, a empty string is still returned. + Errors are: +
      +
    • "Interface (xxx) does not exist." +
    • "Interface (xxx) not configured." +
    • "Interface (xxx) already being monitored." +
    +
    pcap_findalldevs()It constructs a list of network devices that can be opened with pcap_open_live().
    + + + + + + + + + + + + + + + +
    SMP + It obtains a list of IOPs currently available (via /etc/hosts). +
    SMP -> IOP + The SMP will sequentially open a connection to each IOP on its 'sniffer' port to ensure + the IOP is available. + It sends a null terminated empty interface ID followed by the query request command. +
    IOP -> SMPThe IOP returns an error response and its list of devices. +
    SMP -> IOP + The SMP closes the TCP connection with each IOP. +
    SMP + The SMP adds the received information to its internal structure. +
    pcap_freealldevs()Used to free a list allocated by pcap_findalldevs().
    + + + +
    SMP + The SMP frees the structure it built as a result of the previous + invocation of pcap_findalldevs(). +
    pcap_dispatch()Used to collect and process packets.
    + + + + + + + + + + + + +
    SMP -> IOP + On the first invocation of pcap_dispatch(), pcap_loop(), or pcap_next(), or pcap_next_ex() following a pcap_open_live(), + the SMP will pass down the monitor start command and various parameters the IOP should use. +
    IOP -> SMP + The IOP now sends a stream of captured data. +
    SMP + The SMP will read the reverse channel of the connection between the SMP and the + IOP that provides the captured data (via 'p->read_op' which is 'pcap_read_linux()' + until the select() call returns a 'no more data' indication. + It will the process (at most) the next 'cnt' packets and invoke the specified + callback function for each packet processed. +
    IOP + The IOP continues to listen for additional commands as well as capturing and forwarding data to the SMP. +
    pcap_loop() + Is similar to pcap_dispatch() except it keeps reading packets until + the requested number of packets are processed or an error occurs. +
    + + + + + + + + + + + + +
    SMP -> IOP + On the first invocation of pcap_dispatch(), pcap_loop(), or pcap_next(), or pcap_next_ex() following a pcap_open_live(), + the SMP will pass down the monitor start command and various parameters the IOP should use. +
    IOP -> SMP + The IOP now sends a stream of captured data. +
    SMP + The SMP continuously reads the next packet from the reverse channel of the connection + between the SMP and the IOP that provides the captured data (via 'p->read_op' + which is 'pcap_read_linux()' until 'cnt' packets have been received. + The specified callback function will be invoked for each packet received. +
    IOP + The IOP continues to listen for additional commands as well as capturing and forwarding data to the SMP. +
    pcap_next() + It reads the next packet (by calling pcap_dispatch() with a count of 1) + and returns a pointer to the data in that packet. +
    + + + + + + + + + + + + +
    SMP -> IOP + On the first invocation of pcap_dispatch(), pcap_loop(), or pcap_next(), or pcap_next_ex() following a pcap_open_live(), + the SMP will pass down the monitor start command and various parameters the IOP should use. +
    IOP -> SMP + The IOP now sends a stream of captured data. +
    SMP + The SMP reads only the next packet from the reverse channel of the connection + between the SMP and the IOP that provides the captured data (via calling pcap_dispatch() + with a count of 1) and returns a pointer to that data by invoking an internal callback. +
    IOP + The IOP continues to listen for additional commands as well as capturing and forwarding data to the SMP. +
    pcap_next_ex()Reads the next packet and returns a success/failure indication.
    + + + + + + + + + + + + +
    SMP -> IOP + On the first invocation of pcap_dispatch(), pcap_loop(), or pcap_next(), or pcap_next_ex() following a pcap_open_live(), + the SMP will pass down the monitor start command and various parameters the IOP should use. +
    IOP -> SMP + The IOP now sends a stream of captured data. +
    SMP + The SMP reads only the next packet from the reverse channel of the connection + between the SMP and the IOP that provides the captured data (via calling pcap_dispatch() + with a count of 1) and returns seperate pointers to both the + packet header and packet data by invoking an internal callback. +
    IOP + The IOP continues to listen for additional commands as well as capturing and forwarding data to the SMP. +
    pcap_setfilter()Used to specify a filter program.
    + + + + + + +
    SMP -> IOP + The SMP sends a 'set filter' command followed by the BPF commands. +
    IOP -> SMP + The IOP returns a null terminated error string if it failed to accept the filter. + If no error occured, then a NULL terminated empty string is returned instead. + Errors are: +
      +
    • "Invalid BPF." +
    • "Insufficient resources for BPF." +
    +
    pcap_stats()Fills in a pcap_stat struct with packet statistics.
    + + + + + + + + + +
    SMP -> IOP + The SMP sends a message to the IOP requesting its statistics. +
    IOP -> SMP + The IOP returns the statistics. +
    SMP + The SMP fills in the structure provided with the information retrieved from the IOP. +
    pcap_close()Closes the file and deallocates resources.
    + + + + + + +
    SMP -> IOP + The SMP closes the file descriptor, and if the descriptor is that of + the comminucation session with an IOP, it too is terminated. +
    IOP + If the IOP detects that its communication session with an SMP + has closed, it will terminate any monitoring in progress, + release any resources and close its end of the session. + It will not maintain persistance of any information or prior mode of operation. +
    +

+ +

+

SMP/IOP Inter-Process Communication Protocol

+ +
    +
  • Communications between an SMP and an IOP consists of a TCP session + between an ephemeral port on the SMP and the well known port of 49152 + (which is the first available port in the 'dynamic and/or private port' + range) on an IOP. +

  • Following a TCP open operation the IOP receives a null terminated + 'interface ID' string to determine the type of operation that follows: +

  • Every command received by an IOP implies a 'stop trace/stop forwarding' operation must + occur before executing the received command. +

  • A session is closed when the SMP closes the TCP session with the IOP. + Obviously monitoring and forwarding is also stopped at that time. + + Note: All multi-octet entities are sent in network neutral order. +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    pcap_findalldevs()SMP -> IOPOpen socket (to each IOP), and sends: +

    + + + + + + + + + + + +
    Name/
    Purpose
    Size
    (in bytes)
    Description
    Interface ID1A NULL to indicate an an empty 'interface ID'.
    +

    IOP -> SMPSend its (possibly empty) NULL terminated error response string.
    SMP -> IOPSends the 'interface query request': +

    + + + + + + + + + + + +
    Name/
    Purpose
    Size
    (in bytes)
    Description
    Interface ID1A 'Q' (indicating 'interface query request').
    +

    IOP -> SMPThe IOP returns a list of sequences of information as + defined by the return parameter of this function call (as shown in the following table). + Elements are specified by providing an unsigned byte preceeding the actual data that contains length information. +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Notes:Name/
    Purpose
    Size
    (in bytes)
    Description
     length1The number of octets in the name field that follows.
    Name1-255The name of the interface. The format of the name is an alphabetic string (indicating + the type of interface) followed by an optional numeric string (indicating the interface's + sequence number). + Sequence numbers (if needed) will begin at zero and progress monotonically upwards. + (i.e. 'eth0', 'lo', 'wan0', etc.) +

    + For an IOP, the alphabetic string will be one of: 'eth', 'wan', and 'lo' + for Ethernet, WAN ports and the IP loopback device respectively. + An IOP currently supports: 'eth0', 'eth1', 'lo', 'wan0' ... 'wan7'. +

    + Note: IOPs and ACNs will not currently support the concept of 'any' interface.

    length1The number of octets in the interface description field that follows.
    Interface Description0-255A description of the interface or it may be an empty string. (i.e. 'ALC')
    Interface Type4The type of interface as defined in the description for pcap_datalink() (in network neutral order).
    Loopback Flag11 = if the interface is a loopback interface, zero = otherwise.
    count1# of address entries that follow. + Each entry is a series of bytes in network neutral order. + See the parameter definition above for more details.
    Repeated 'count' number of times.length1The number of octets in the address field that follows.
    Address1-255The address of this interface (in network neutral order).
    length1The number of octets in the netmask field that follows.
    Network Mask0-255The network mask used on this interface (if applicable) (in network neutral order).
    length1The number of octets in the broadcast address field that follows.
    Broadcast Address0-255The broadcast address of this interface (if applicable) (in network neutral order).
    length1The number of octets in the destination address field that follows.
    Destination Address0-255The destination address of this interface (if applicable) (in network neutral order).
    +

    SMP -> IOPClose the socket.
    IOP -> SMPClose the socket.

    pcap_open_live()SMP -> IOPOpen socket, and sends: +

    + + + + + + + + + + + +
    Name/
    Purpose
    Size
    (in bytes)
    Description
    Interface ID'n''n' octets containing a NULL terminated interface name string.
    +

    IOP -> SMPSend its NULL terminated error response string.

    pcap_dispatch()
    pcap_loop()
    pcap_next()
    pcap_next_ex()
    SMP -> IOPOn the first invocation following a pcap_open_live() or pcap_breakloop() additional information is sent: +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Name/
    Purpose
    Size
    (in bytes)
    Description
    command1'M' (indicating 'monitor start')
    snaplen4snaplen
    timeout1timeout value (in milliseconds)
    promiscuous1A flag indicating that the interface being monitored show operate + in promiscuous mode. [off(0) / on(NZ)]
    direction1A flag indicating the direction of traffic that should be captuted [both(0) / in(1) / out(2)]
    +

    IOP -> SMPSends captured packets.

    pcap_setfilter()SMP -> IOPAt any time, the SMP can issue a set filter command which contains + an indicator, a count of the number of statements in the filter, + followed by the sequence of filter commands represented as a sequence + of C-style structures. +

    + + + + + + + + + + + + + + + + + + + + + +
    Name/
    Purpose
    Size
    (in bytes)
    Description
    command1'F' (indicating 'filter')
    count4The number of command in the Berkeley Packet Filter that follow.
    BPF program'n'8 bytes of each command (repeated 'n' times).
    + Each command consists of that C-style structure which contains: +

    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Name/
    Purpose
    Size
    (in bytes)
    Description
    opcode2The command's opcode.
    'jt'1The 'jump if true' program counter offset.
    'jf'1The 'jump if false' program counter offset.
    'k'4The 'other' data field.
    +

    + Refer to the bpf(4) man page for more details. +

    +

    IOP -> SMPIn return the IOP will send its (possibly empty) NULL terminated error response string.

    pcap_stats()SMP -> IOPAt any time, the SMP can issue a 'retrieve statistics' command which contains:
    +

    + + + + + + + + + + + +
    Name/
    Purpose
    Size
    (in bytes)
    Description
    command1'S' (indicating 'request statistics')
    +

    IOP -> SMPIn return the IOP will send: +

    + + + + + + + + + + + + + + + + + + + + + +
    Name/
    Purpose
    Size
    (in bytes)
    Description
    ps_recv4The number of packets that passed the filter.
    ps_drop4The number of packets that were dropped because the input queue was full, + regardless of whether they passed the filter.
    ps_ifdrop4The number of packets dropped by the network inteface + (regardless of whether they would have passed the input filter).
    +


    pcap_close()SMP -> IOPAt any time, the SMP can close the TCP session with the IOP.

    +

+ +

Interface ID Naming Convention

+
    + Each interface within an IOP will be referred to uniquely. Since an currently contains + 8 monitorable WAN ports and a monitorable Ethernet port, the naming convention is: +

    +

    + + + + + + + + + + + + +
    Interface # Type Name
    1 WAN wan0
    2 WAN wan1
    3 WAN wan2
    4 WAN wan3
    5 WAN wan4
    6 WAN wan5
    7 WAN wan6
    8 WAN wan7
    9 Ethernet eth0
    10 Ethernet eth1
    +
    +
+ +

Packet Trace Data Format

+
    + The format of the trace data that is sent to the SMP follows a portion of the libpcap file format + and is summarized here. This format specifies the generic requirements needed to + be able to decode packets, but does not cover ACN specifics such as custom MAC addressing + and WAN protocol support. +

    + + Although a libpcap file begins with a global header followed by zero or + more records for each captured packet, trace data sent to the SMP does NOT begin with a global header. + A trace sequence looks like this: +

    + + + + + + + + + + +
     [Packet Header]  [Packet Data]  [Packet Header]  [Packet Data]  [Packet Header]  [Packet Data] ...
    + +

    Packet Header

    +
      + Each captured packet starts with a header that contains the following values + (in network neutral order): + + +
      + uint32 tv_sec;  /* timestamp seconds */
      + uint32 tv_usec; /* timestamp microseconds */
      + uint32 caplen;  /* number of octets in the following packet */
      + uint32 len;     /* original length of packet on the wire */
      +		
      +
      + + + + + + + + + + + + + + + + + + +
      tv_secThe date and time when this packet was captured. + This value is in seconds since January 1, 1970 00:00:00 GMT; + this is also known as a UN*X time_t. You can use the ANSI C + time() function from time.h to get this value, + but you might use a more optimized way to get this timestamp value. + If this timestamp isn't based on GMT (UTC), use thiszone + from the global header for adjustments.
      tv_usecThe microseconds when this packet was captured, as an offset to ts_sec. + Beware: this value must never reach 1 second (1,000,000), + in this case ts_sec must be increased instead!
      caplenThe number of bytes actually provided in the capture record. + This value should never become larger than len or the + snaplen value specified during the capture.
      lenThe length of the packet "on the wire" when it was captured. + If caplen and len differ, the actually + saved packet size was limited by the value of snaplen specified + during one of the capture directives such as pcap_dispatch().
      +
    + +

    Packet Data

    +
      + The actual packet data will immediately follow the packet header as a sequence of caplen octets. + Depending on the DLT encoding number assigned to the interface, the packet data will contain an additional + custom header used to convey WAN port related information. +
    + +

    ACN Custom Packet Header

    +
      + PCAP, Wireshark and Tcpdump enhancements have been added to the ACN to support + monitoring of its ports, however each of these facilities were focused on capturing + and displaying traffic from LAN interfaces. The SITA extentions to these facilities + are used to also provide the ability to capture, filter, and display information from + an ACN's WAN ports. +

      + Although each packet follows the standard libpcap format, since there are + two types of interfaces that can be monitored, the format of the data + packet varies slightly. +

      +

        +
      • For Ethernet (like) devices, the packet format is unchanged from the standard Pcap format. +
      • For WAN devices, the packet contains a 5 byte header that preceeds the actual captured data + described by the following table: +
      +

      +

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      OctetNameMask/ValueDefinition
      0Control / Statusxxxxxxx0Transmitted by capture device(see 'Errors' octets)
      xxxxxxx1Received by capture device
      1xxxxxxxNo buffer was available during capture of previous packet.
      1Signalsxxxxxxx1 DSR asserted
      xxxxxx1x DTR asserted
      xxxxx1xx CTS asserted
      xxxx1xxx RTS asserted
      xxx1xxxx DCD asserted
      xx1xxxxx Undefined
      x1xxxxxx Undefined
      1xxxxxxx Undefined
      2Errors
      (octet 1)
        Tx Rx
      xxxxxxx1 Underrun Framing
      xxxxxx1x CTS Lost Parity
      xxxxx1xx UART Error Collision
      xxxx1xxx Re-Tx Limit Reached Long Frame
      xxx1xxxx Undefined Short Frame
      xx1xxxxx Undefined Undefined
      x1xxxxxx Undefined Undefined
      1xxxxxxx Undefined Undefined
      3Errors
      (octet 2)
        Tx Rx
      xxxxxxx1 Undefined Non-Octet Aligned
      xxxxxx1x Undefined Abort Received
      xxxxx1xx Undefined CD Lost
      xxxx1xxx Undefined Digital PLL Error
      xxx1xxxx Undefined Overrun
      xx1xxxxx Undefined Frame Length Violation
      x1xxxxxx Undefined CRC Error
      1xxxxxxx Undefined Break Received
      4Protocol +
      + + + + + + + + + + + + + +
      0x01 - LAPB (BOP)  
      0x02 - Ethernet 1
      0x03 - Async (Interrupt IO)  
      0x04 - Async (Block IO)  
      0x05 - IPARS  
      0x06 - UTS  
      0x07 - PPP (HDLC)  
      0x08 - SDLC  
      0x09 - Token Ring 1
      0x10 - I2C  
      0x11 - DPM Link  
      0x12 - Frame Relay (BOP)  
      +
      +

      + Note 1: + Ethernet and Token Ring frames will never be sent as DLT_SITA (with the 5 octet header), + but will be sent as their corresponding DLT types instead. +

      +
      +
    +

    +

+ -- cgit v1.2.3