root/sbin/ipf/libipf/ipft_pc.c

/*
 * Copyright (C) 2012 by Darren Reed.
 *
 * See the IPFILTER.LICENCE file for details on licencing.
 *
 * $Id$
 */
#include "ipf.h"
#include "ipt.h"


struct  llc     {
        int     lc_type;
        int     lc_sz;  /* LLC header length */
        int     lc_to;  /* LLC Type offset */
        int     lc_tl;  /* LLC Type length */
};

/*
 * While many of these maybe the same, some do have different header formats
 * which make this useful.
 */

static  struct  llc     llcs[] = {
        { 0, 0, 0, 0 },                         /* DLT_NULL */
        { 1, 14, 12, 2 },                       /* DLT_Ethernet */
        { 10, 0, 0, 0 },                        /* DLT_FDDI */
        { 12, 0, 0, 0 },                        /* DLT_RAW */
        { -1, -1, -1, -1 }
};

typedef struct {
        u_int   id;
        u_short major;
        u_short minor;
        u_int   timezone;
        u_int   sigfigs;
        u_int   snaplen;
        u_int   type;
} fileheader_t;

typedef struct {
        u_32_t  seconds;
        u_32_t  microseconds;
        u_32_t  caplen;
        u_32_t  wirelen;
} packetheader_t;

static  int     ipcap_open(char *);
static  int     ipcap_close(void);
static  int     ipcap_readip(mb_t *, char **, int *);
static  int     ipcap_read_rec(packetheader_t *);
static  void    iswap_hdr(fileheader_t *);

static  int     pfd = -1, swapped = 0;
static  struct llc      *llcp = NULL;

struct  ipread  pcap = { ipcap_open, ipcap_close, ipcap_readip, 0 };

#define SWAPLONG(y)     \
        ((((y)&0xff)<<24) | (((y)&0xff00)<<8) | (((y)&0xff0000)>>8) | (((y)>>24)&0xff))
#define SWAPSHORT(y)    \
        ( (((y)&0xff)<<8) | (((y)&0xff00)>>8) )

static void
iswap_hdr(fileheader_t *p)
{
        p->major = SWAPSHORT(p->major);
        p->minor = SWAPSHORT(p->minor);
        p->timezone = SWAPLONG(p->timezone);
        p->sigfigs = SWAPLONG(p->sigfigs);
        p->snaplen = SWAPLONG(p->snaplen);
        p->type = SWAPLONG(p->type);
}

static int
ipcap_open(char *fname)
{
        fileheader_t ph;
        int fd, i;

        if (pfd != -1)
                return (pfd);

        if (!strcmp(fname, "-"))
                fd = 0;
        else if ((fd = open(fname, O_RDONLY)) == -1)
                return (-1);

        if (read(fd, (char *)&ph, sizeof(ph)) != sizeof(ph))
                return (-2);

        if (ph.id != 0xa1b2c3d4) {
                if (SWAPLONG(ph.id) != 0xa1b2c3d4) {
                        (void) close(fd);
                        return (-2);
                }
                swapped = 1;
                iswap_hdr(&ph);
        }

        for (i = 0; llcs[i].lc_type != -1; i++)
                if (llcs[i].lc_type == ph.type) {
                        llcp = llcs + i;
                        break;
                }

        if (llcp == NULL) {
                (void) close(fd);
                return (-2);
        }

        pfd = fd;
        printf("opened pcap file %s:\n", fname);
        printf("\tid: %08x version: %d.%d type: %d snap %d\n",
                ph.id, ph.major, ph.minor, ph.type, ph.snaplen);

        return (fd);
}


static int
ipcap_close(void)
{
        return (close(pfd));
}


/*
 * read in the header (and validate) which should be the first record
 * in a pcap file.
 */
static int
ipcap_read_rec(packetheader_t *rec)
{
        int     n, p, i;

        n = sizeof(*rec);

        while (n > 0) {
                i = read(pfd, (char *)rec, sizeof(*rec));
                if (i <= 0)
                        return (-2);
                n -= i;
        }

        if (swapped) {
                rec->caplen = SWAPLONG(rec->caplen);
                rec->wirelen = SWAPLONG(rec->wirelen);
                rec->seconds = SWAPLONG(rec->seconds);
                rec->microseconds = SWAPLONG(rec->microseconds);
        }
        p = rec->caplen;
        n = MIN(p, rec->wirelen);
        if (!n || n < 0)
                return (-3);

        if (p < 0 || p > 65536)
                return (-4);
        return (p);
}


#ifdef  notyet
/*
 * read an entire pcap packet record.  only the data part is copied into
 * the available buffer, with the number of bytes copied returned.
 */
static int
ipcap_read(char *buf, int cnt)
{
        packetheader_t rec;
        static  char    *bufp = NULL;
        int     i, n;

        if ((i = ipcap_read_rec(&rec)) <= 0)
                return (i);

        if (!bufp)
                bufp = malloc(i);
        else
                bufp = realloc(bufp, i);

        if (read(pfd, bufp, i) != i)
                return (-2);

        n = MIN(i, cnt);
        bcopy(bufp, buf, n);
        return (n);
}
#endif


/*
* return only an IP packet read into buf
 */
static int
ipcap_readip(mb_t *mb, char **ifn, int *dir)
{
        static  char    *bufp = NULL;
        packetheader_t  rec;
        struct  llc     *l;
        char    *s, ty[4];
        int     i, j, n;
        char    *buf;
        int     cnt;

#if 0
        ifn = ifn;      /* gcc -Wextra */
        dir = dir;      /* gcc -Wextra */
#endif
        buf = (char *)mb->mb_buf;
        cnt = sizeof(mb->mb_buf);
        l = llcp;

        /* do { */
                if ((i = ipcap_read_rec(&rec)) <= 0)
                        return (i);

                if (!bufp)
                        bufp = malloc(i);
                else
                        bufp = realloc(bufp, i);
                s = bufp;

                for (j = i, n = 0; j > 0; ) {
                        n = read(pfd, s, j);
                        if (n <= 0)
                                return (-2);
                        j -= n;
                        s += n;
                }
                s = bufp;

                i -= l->lc_sz;
                s += l->lc_to;
                bcopy(s, ty, l->lc_tl);
                s += l->lc_tl;
        /* } while (ty[0] != 0x8 && ty[1] != 0); */
        n = MIN(i, cnt);
        bcopy(s, buf, n);
        mb->mb_len = n;
        return (n);
}