588 lines
18 KiB
C
588 lines
18 KiB
C
/*
|
|
******************************************************************************
|
|
* 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 <kenneth@interpeak.se>
|
|
******************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 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 <ipcom_clib.h>
|
|
#include <ipcom_file.h>
|
|
#include <ipcom_hash.h>
|
|
#include <ipcom_pkt.h>
|
|
#include <ipcom_syslog.h>
|
|
#include <ipcom_time.h>
|
|
|
|
#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 */
|