/* ****************************************************************************** * SOURCE FILE * * Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipnet_pcap.c,v $ $Revision: 1.3.80.5 $ * $Source: /home/interpeak/CVSRoot/ipnet2/src/ipnet_pcap.c,v $ * $Author: jsheng $ * $State: Exp $ $Locker: $ * * INTERPEAK_COPYRIGHT_STRING * Design and implementation by Kenneth Jonsson ****************************************************************************** */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** * Writes all received and sent packets on a network interface to a pcap * compatible file. The created file can be loaded into Wireshark, which * in turn allows for analysis of the stream(s) send and received on the * monitored network interface. */ /* **************************************************************************** * 2 CONFIGURATION **************************************************************************** */ #ifndef IPCOM_KERNEL #define IPCOM_KERNEL #endif #include "ipnet_config.h" /* **************************************************************************** * 3 INCLUDE FILES **************************************************************************** */ #ifdef IPNET_USE_PCAP #define IPCOM_USE_CLIB_PROTO #include #include #include #include #include #include #include "ipnet_pcap.h" #include "ipnet_h.h" /* **************************************************************************** * 4 DEFINES **************************************************************************** */ #define IPNET_PCAP_MAGIC 0xa1b2c3d4 #define IPNET_PCAP_MAGIC_SWAPPED 0xd4c3b2a1 #define IPNET_PCAP_VERSION_MAJOR 2 #define IPNET_PCAP_VERSION_MINOR 4 #define IPNET_PCAP_THISZONE 0 #define IPNET_PCAP_SIGFIGS 0 #define IPNET_PCAP_SNAPLEN 0xffff #define IPNET_PCAP_NETWORK_80211 105 #define IPNET_PCAP_NETWORK_DEFAULT 101 #define IPNET_PCAP_NETWORK_ETHERNET 1 #define IPNET_PCAP_NETWORK_LOOP 101 #define IPNET_PCAP_NETWORK_PPP 9 /* **************************************************************************** * 5 TYPES **************************************************************************** */ /* Global header for capture file */ typedef struct Ipnet_pcap_hdr_struct { Ip_u32 magic_number; /* magic number, 0xa1b2c3d4 -> all fields in correct byteorder 0xd4c3b2a1 -> all fields must be swapped */ Ip_u16 version_major; /* major version number */ Ip_u16 version_minor; /* minor version number */ Ip_s32 thiszone; /* GMT to local correction */ Ip_u32 sigfigs; /* accuracy of timestamps */ Ip_u32 snaplen; /* max length of captured packets, in octets */ Ip_u32 network; /* data link type, IPNET_PCAP_NETWORK_xxx */ } Ipnet_pcap_hdr_t; /* Header for every packet record */ typedef struct Ipnet_pcaprec_hdr_struct { Ip_u32 ts_sec; /* timestamp seconds */ Ip_u32 ts_usec; /* timestamp microseconds */ Ip_u32 incl_len; /* number of octets of packet saved in file */ Ip_u32 orig_len; /* actual length of packet */ } Ipnet_pcaprec_hdr_t; /* Session stored in a file on the local filesystem */ typedef struct Ipnet_pcap_session_file_struct { char name[IPNET_PCAP_FNAMSIZ]; /* filename of the capture file */ } Ipnet_pcap_session_file_t; /* Session sent to a specific address & port */ typedef struct Ipnet_pcap_session_net_struct { int socket; union Ip_sockaddr_union dst; } Ipnet_pcap_session_net_t; struct Ipnet_pcap_session_struct; typedef void (*Ipnet_pcap_add_record)(struct Ipnet_pcap_session_struct *session, Ipcom_pkt *pkt); typedef void (*Ipnet_pcap_dtor)(struct Ipnet_pcap_session_struct *session); typedef struct Ipnet_pcap_session_struct { Ip_u32 ifindex; /* monitored network interface */ Ipnet_link_input link_input; /* original link_input handler for 'netif' */ Ipcom_drv_output drv_output; /* original drv_output handler for 'netif' */ Ipnet_pcap_dtor dtor; /* destructor for the session */ Ipnet_pcap_add_record add_record; /* session implementation for adding a record */ union { Ipnet_pcap_session_file_t file; Ipnet_pcap_session_net_t net; } d; } Ipnet_pcap_session_t; /* **************************************************************************** * 6 EXTERN PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 7 LOCAL PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 8 DATA **************************************************************************** */ IP_STATIC Ipcom_hash *ipnet_pcap_sessions = IP_NULL; /* **************************************************************************** * 10 STATIC FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipnet_pcap_session_key_func *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC unsigned ipnet_pcap_session_key_func(Ip_u32 *ifindex) { return ipcom_hash_update(ifindex, sizeof(*ifindex), 0); } /* *=========================================================================== * ipnet_pcap_session_obj_func *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC unsigned ipnet_pcap_session_obj_func(Ipnet_pcap_session_t *session) { return ipnet_pcap_session_key_func(&session->ifindex); } /* *=========================================================================== * ipnet_pcap_session_cmp_func *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC Ip_bool ipnet_pcap_session_cmp_func(Ipnet_pcap_session_t *session, Ip_u32 *ifindex) { return session->ifindex == *ifindex; } /* *=========================================================================== * ipnet_pcap_fill_header *=========================================================================== * Description: Fills the fields of the global pcap header. * Parameters: h - A pcap header buffer. * Returns: * */ IP_STATIC void ipnet_pcap_fill_header(Ipnet_pcap_hdr_t *h, Ipnet_pcap_session_t *session) { Ipnet_netif *netif = ipnet_if_indextonetif(IPCOM_VR_ANY, session->ifindex); if (netif == IP_NULL) return; h->magic_number = IPNET_PCAP_MAGIC; h->version_major = IPNET_PCAP_VERSION_MAJOR; h->version_minor = IPNET_PCAP_VERSION_MINOR; h->thiszone = IPNET_PCAP_THISZONE; h->sigfigs = IPNET_PCAP_SIGFIGS; h->snaplen = IPNET_PCAP_SNAPLEN; switch (netif->ipcom.type) { case IP_IFT_LOOP: h->network = IPNET_PCAP_NETWORK_LOOP; break; case IP_IFT_ETHER: case IP_IFT_L2VLAN: h->network = IPNET_PCAP_NETWORK_ETHERNET; break; case IP_IFT_IEEE80211: h->network = IPNET_PCAP_NETWORK_80211; break; case IP_IFT_PPP: h->network = IPNET_PCAP_NETWORK_PPP; break; default: h->network = IPNET_PCAP_NETWORK_DEFAULT; break; } } /* *=========================================================================== * ipnet_pcap_new_record *=========================================================================== * Description: Creates a new pcap record for a packet, the record must be * freed with ipcom_free() by the caller. * Parameters: pkt - The packet to add a record for. * Returns: The new record or IP_NULL if out of memory. * */ IP_STATIC Ipnet_pcaprec_hdr_t * ipnet_pcap_new_record(Ipcom_pkt *pkt) { Ipnet_pcaprec_hdr_t *rec; int pkt_len = ipcom_pkt_get_length(pkt); rec = ipcom_malloc(sizeof(*rec) + pkt_len); if (rec != IP_NULL) { struct Ip_timeval tv; Ipcom_pkt *next_part = pkt; int offset = 0; Ip_u8 *data = (Ip_u8 *)(rec + 1); (void)ipcom_gettimeofday(&tv, IP_NULL); rec->ts_sec = (Ip_u32)tv.tv_sec; rec->ts_usec = (Ip_u32)tv.tv_usec; rec->incl_len = (Ip_u32) pkt_len; rec->orig_len = (Ip_u32) pkt_len; while (next_part != IP_NULL) { ipcom_memcpy(data + offset, &next_part->data[next_part->start], next_part->end - next_part->start); offset += (next_part->end - next_part->start); next_part = next_part->next_part; } } return rec; } /* *=========================================================================== * ipnet_pcap_file_add_record *=========================================================================== * Description: Adds a pcap record by appending it to the file associated * with the session. * Parameters: session - A pcap file session. * pkt - The packet to add a record for. * Returns: * */ IP_STATIC void ipnet_pcap_file_add_record(Ipnet_pcap_session_t *session, Ipcom_pkt *pkt) { IP_FILE *f; Ipnet_pcaprec_hdr_t *rec; if (IP_NULL != (f = ipcom_fopen(session->d.file.name, "a"))) { if (IP_NULL != (rec = ipnet_pcap_new_record(pkt))) { (void)ipcom_fwrite(rec, sizeof(*rec) + rec->incl_len, 1, f); ipcom_free(rec); } ipcom_fclose(f); } } /* *=========================================================================== * ipnet_pcap_add_record *=========================================================================== * Description: Adds a packet to the pcap backend storage. * Parameters: netif - The network interface the packet was capture on. * pkt - The packet to capture. * Returns: * */ IP_STATIC Ipnet_pcap_session_t * ipnet_pcap_add_record(Ipcom_netif *netif, Ipcom_pkt *pkt, int pcap_dir, Ip_u16 frame_type, int mac, int payload) { Ipnet_pcap_session_t *session; (void) pcap_dir; (void) frame_type; (void) mac; (void) payload; session = ipcom_hash_get(ipnet_pcap_sessions, &netif->ifindex); ip_assert(session != IP_NULL); session->add_record(session, pkt); return session; } /* *=========================================================================== * ipnet_pcap_link_input *=========================================================================== * Description: * Parameters: * Returns: 0 = success * <0 = error code * */ IP_STATIC int ipnet_pcap_link_input(Ipnet_netif *netif, Ipcom_pkt *pkt) { return ipnet_pcap_add_record(&netif->ipcom, pkt, IP_PACKET_HOST, 0, -1, -1)->link_input(netif, pkt); } /* *=========================================================================== * ipnet_pcap_drv_output *=========================================================================== * Description: * Parameters: * Returns: 0 = success * <0 = error code * */ IP_STATIC int ipnet_pcap_drv_output(Ipcom_netif *netif, Ipcom_pkt *pkt) { return ipnet_pcap_add_record(netif, pkt, IP_PACKET_OUTGOING, 0, -1, -1)->drv_output(netif, pkt); } /* *=========================================================================== * ipnet_pcap_file_init *=========================================================================== * Description: Adds a pcap record by appending it to the file associated * with the session. * Parameters: session - A pcap file session. * pkt - The packet to add a record for. * Returns: 0 = success * <0 = error code * */ IP_STATIC int ipnet_pcap_file_init(Ipnet_pcap_session_t *session, const char *filename) { Ipnet_pcap_hdr_t hdr; IP_FILE *f; session->add_record = ipnet_pcap_file_add_record; session->dtor = IP_NULL; ipcom_strncpy(session->d.file.name, filename, IPNET_PCAP_FNAMSIZ); session->d.file.name[IPNET_PCAP_FNAMSIZ-1] = '\0'; f = ipcom_fopen(filename, "w"); if (f == IP_NULL) { IPCOM_LOG2(ERR, "ipnet_pcap_file_init: failed to create file %s: %s", filename, ipcom_strerror(ipcom_errno)); return -ipcom_errno; } ipnet_pcap_fill_header(&hdr, session); (void)ipcom_fwrite(&hdr, sizeof(hdr), 1, f); ipcom_fclose(f); return 0; } /* *=========================================================================== * ipnet_pcap_net_init *=========================================================================== * Description: Adds a pcap record by appending it to the file associated * with the session. * Parameters: session - A pcap file session. * pkt - The packet to add a record for. * Returns: 0 = success * <0 = error code * */ IP_STATIC int ipnet_pcap_net_init(Ipnet_pcap_session_t *session, union Ip_sockaddr_union *dst) { IPCOM_UNUSED_ARG(session); IPCOM_UNUSED_ARG(dst); /* NOT IMPLEMENTED YET */ return -IP_ERRNO_ENOSYS; } /* **************************************************************************** * 11 GLOBAL FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipnet_pcap_ioctl *=========================================================================== * Description: * Parameters: session - A pcap file session. * pkt - The packet to add a record for. * Returns: 0 = success <0 = error code * */ IP_GLOBAL int ipnet_pcap_ioctl(Ipnet_netif *netif, Ip_u32 command, void *data) { struct Ip_ifreq *ifreq = data; Ipnet_pcap_ioctl_t *pcap = ifreq->ip_ifr_data; Ipnet_pcap_session_t *session; int ret = -IP_ERRNO_EINVAL; (void) command; ip_assert(command == IP_SIOCXPCAP); if (pcap == IP_NULL) return -IP_ERRNO_EINVAL; if (ipnet_pcap_sessions == IP_NULL) { ipnet_pcap_sessions = ipcom_hash_new((Ipcom_hash_obj_func) ipnet_pcap_session_key_func, (Ipcom_hash_key_func) ipnet_pcap_session_obj_func, (Ipcom_hash_cmp_func) ipnet_pcap_session_cmp_func); } session = ipcom_hash_get(ipnet_pcap_sessions, &netif->ipcom.ifindex); switch (pcap->op) { case IPNET_PCAP_OP_START: /* Enable capture */ if (session != IP_NULL) return -IP_ERRNO_EEXIST; session = ipcom_calloc(1, sizeof(*session)); if (session == IP_NULL) return -IP_ERRNO_ENOMEM; session->ifindex = netif->ipcom.ifindex; if (ipcom_hash_add(ipnet_pcap_sessions, session) != IPCOM_SUCCESS) { ipcom_free(session); return -IP_ERRNO_ENOMEM; } switch (pcap->type) { case IPNET_PCAP_TYPE_FILE: ret = ipnet_pcap_file_init(session, pcap->d.filename); break; case IPNET_PCAP_TYPE_NET: ret = ipnet_pcap_net_init(session, &pcap->d.dst); break; default: break; } if (ret != 0) { (void)ipcom_hash_remove(ipnet_pcap_sessions, session); ipcom_free(session); } else { session->link_input = netif->link_input; session->drv_output = netif->ipcom.drv_output; switch (netif->ipcom.type) { case IP_IFT_LOOP: netif->ipcom.drv_output = ipnet_pcap_drv_output; break; case IP_IFT_L2VLAN: case IP_IFT_TUNNEL: netif->ipcom.link_tap = (Ipcom_link_tap) ipnet_pcap_add_record; break; default: netif->link_input = ipnet_pcap_link_input; netif->ipcom.drv_output = ipnet_pcap_drv_output; break; } } break; case IPNET_PCAP_OP_STOP: /* Disable capture */ if (session == IP_NULL) return -IP_ERRNO_ENOENT; if (ipcom_hash_remove(ipnet_pcap_sessions, session) != IPCOM_SUCCESS) IP_PANIC(); if (session->dtor != IP_NULL) session->dtor(session); netif->link_input = session->link_input; netif->ipcom.drv_output = session->drv_output; netif->ipcom.link_tap = IP_NULL; ipcom_free(session); ret = 0; break; default: break; } return ret; } #endif /* IPNET_USE_PCAP */