/* $Id: net_read.c,v 0.19 2003/10/16 11:55:00 kjc Exp $ */
/*
* Copyright (c) 1996-2003
* Sony Computer Science Laboratories, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms of parts of or the
* whole original or derived work are permitted provided that the above
* copyright notice is retained and the original work is properly
* attributed to the author. The name of the author 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.
*/
/* net_read.c -- a module to read ethernet packets.
most parts are derived from tcpdump. */
/*
* Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that: (1) source code distributions
* retain the above copyright notice and this paragraph in its entirety, (2)
* distributions including binary code include the above copyright notice and
* this paragraph in its entirety in the documentation or other materials
* provided with the distribution, and (3) all advertising materials mentioning
* features or use of this software display the following acknowledgement:
* ``This product includes software developed by the University of California,
* Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
* the University nor the names of its contributors may 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.
*/
#ifndef lint
char copyright[] =
"@(#) Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994\nThe Regents of the University of California. All rights reserved.\n";
#endif
/*
* tcpdump - monitor tcp/ip traffic on an ethernet.
*
* First written in 1987 by Van Jacobson, Lawrence Berkeley Laboratory.
* Mercilessly hacked and occasionally improved since then via the
* combined efforts of Van, Steve McCanne and Craig Leres of LBL.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/param.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <net/bpf.h>
#ifdef __OpenBSD__
#include <net/if_pflog.h>
#endif
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/if_ether.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <pcap.h>
#ifdef PCAP_HEADERS
#include "llc.h"
#include "fddi.h"
#else
#include "ttt_pcap.h"
#endif
#include "ttt.h"
#include "ttt_account.h"
#ifdef IPV6
#include "ttt_ipv6.h"
#endif
/* for tailqueue macros */
#if defined(HAVE_SYS_QUEUE_H) && !defined(__linux__)
#include <sys/queue.h>
#else
#include "bsd_sys_queue.h"
#endif
/*
* The default snapshot length. This value allows most printers to print
* useful information while keeping the amount of unwanted data down.
* In particular, it allows for an ethernet header, tcp/ip header, and
* 14 bytes of data (assuming no ip options).
*/
#ifdef IPV6
#define DEFAULT_SNAPLEN (68+20) /* is this big enough? */
#else
#define DEFAULT_SNAPLEN 68
#endif
#ifndef ETHERTYPE_PPPOE
#define ETHERTYPE_PPPOE 0x8864 /* PPP Over Ethernet Session Stage */
#endif
char errbuf[PCAP_ERRBUF_SIZE];
char *device;
pcap_t *pd;
int pcapfd;
static int packet_length; /* length of current packet */
/* a function switch to read different types of frames */
void (*ttt_netreader)(u_char *user, const struct pcap_pkthdr *h, const u_char *p);
/* needed by libpacp */
int fddipad = FDDIPAD; /* for proper alignment of header */
struct ip4_frag {
TAILQ_ENTRY(ip4_frag) ip4f_chain;
char ip4f_valid;
u_char ip4f_proto;
u_short ip4f_id;
struct in_addr ip4f_src, ip4f_dst;
struct udphdr ip4f_udphdr;
};
static TAILQ_HEAD(ip4f_list, ip4_frag) ip4f_list; /* IPv4 fragment cache */
#define IP4F_TABSIZE 8 /* IPv4 fragment cache size */
/*
* the following macros are FreeBSD extension. there are two incompatible
* TAILQ_LAST defines in FreeBSD (changed after 2.2.6), so use the new one.
*/
#ifndef TAILQ_EMPTY
#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
#endif
#undef TAILQ_LAST
#define TAILQ_LAST(head, headname) \
(*(((struct headname *)((head)->tqh_last))->tqh_last))
void net_read(int clientdata, int mask);
static void ttt_dumpreader(u_char *user, const struct pcap_pkthdr *h,
const u_char *p);
static void ether_if_read(u_char *user, const struct pcap_pkthdr *h,
const u_char *p);
static void fddi_if_read(u_char *user, const struct pcap_pkthdr *h,
const u_char *p);
static void atm_if_read(u_char *user, const struct pcap_pkthdr *h,
const u_char *p);
static void sl_if_print(u_char *user, const struct pcap_pkthdr *h,
const u_char *p);
static void ppp_if_read(u_char *user, const struct pcap_pkthdr *h,
const u_char *p);
#ifdef DLT_RAW
static void raw_if_read(u_char *pcap, const struct pcap_pkthdr *h,
const u_char *p);
#endif
#ifdef DLT_PPP_BSDOS
static void ppp_bsdos_if_read(u_char *user, const struct pcap_pkthdr *h,
const u_char *p);
#endif
#ifdef DLT_PPP_SERIAL /* netbsd specific */
static void ppp_netbsd_serial_if_read(u_char *user,
const struct pcap_pkthdr *h, const u_char *p);
#endif
static void null_if_read(u_char *user, const struct pcap_pkthdr *h,
const u_char *p);
#ifdef PFLOG_HDRLEN
static void pflog_if_read(u_char *user, const struct pcap_pkthdr *h,
const u_char *p);
#endif
static int ether_encap_read(u_short ethtype, const u_char *p,
int length, int caplen);
static int llc_read(const u_char *p, const int length, const int caplen);
static int pppoe_read(const u_char *bp, const int length, const int caplen);
static int ip_read(const u_char *bp, const int length, const int caplen);
static void ip4f_cache(struct ip *, struct udphdr *);
static struct udphdr *ip4f_lookup(struct ip *);
static int ip4f_init(void);
static struct ip4_frag *ip4f_alloc(void);
static void ip4f_free(struct ip4_frag *);
#ifdef IPV6
static int ipv6_read(const u_char *bp, const int length, const int caplen);
static int read_ipv6hdr(struct ipv6 *ipv6, int *proto, int caplen);
#endif
/* wrapper for tcl callback */
void net_read(int clientdata, int mask)
{
if (pcap_dispatch(pd, 1, ttt_netreader, 0) < 0)
(void)fprintf(stderr, "pcap_dispatch:%s\n", pcap_geterr(pd));
}
int dumpfile_read(void)
{
struct timeval end;
int rval;
end = ttt_dumptime;
end.tv_sec += ttt_interval / 1000;
end.tv_usec += (ttt_interval % 1000) * 1000;
if (end.tv_usec > 1000000) {
end.tv_sec++;
end.tv_usec -= 1000000;
}
while ((rval = pcap_dispatch(pd, 1, ttt_dumpreader, 0)) > 0) {
if (ttt_dumptime.tv_sec > end.tv_sec ||
(ttt_dumptime.tv_sec == end.tv_sec &&
ttt_dumptime.tv_usec >= end.tv_usec))
/* end of the interval */
return (rval);
}
if (rval < 0)
(void)fprintf(stderr, "pcap_dispatch:%s\n", pcap_geterr(pd));
return (rval);
}
static void ttt_dumpreader(u_char *user, const struct pcap_pkthdr *h,
const u_char *p)
{
#ifndef __OpenBSD__
ttt_dumptime.tv_sec = h->ts.tv_sec;
ttt_dumptime.tv_usec = h->ts.tv_usec;
#else
ttt_dumptime = *((struct timeval *)&h->ts);
#endif
(*ttt_netreader)(user, h, p);
}
static void ether_if_read(u_char *user, const struct pcap_pkthdr *h,
const u_char *p)
{
int caplen = h->caplen;
int length = h->len;
struct ether_header *ep;
u_short ether_type;
packet_length = length; /* save data link level packet length */
if (caplen < sizeof(struct ether_header)) {
return;
}
ep = (struct ether_header *)p;
p += sizeof(struct ether_header);
length -= sizeof(struct ether_header);
caplen -= sizeof(struct ether_header);
ether_type = ntohs(ep->ether_type);
if (ether_type < ETHERMTU) {
if (llc_read(p, length, caplen) == 0) {
/* ether_type not known */
}
}
else if (ether_encap_read(ether_type, p, length, caplen) == 0) {
/* ether_type not known */
}
}
static int ether_encap_read(u_short ethtype, const u_char *p,
int length, int caplen)
{
#if 0
/* people love to see the total traffic! */
if (ethtype != ETHERTYPE_IP)
#endif
eth_addsize(ethtype, packet_length);
#ifdef ETHERTYPE_VLAN
recurse:
#endif
switch (ethtype) {
case ETHERTYPE_IP:
ip_read(p, length, caplen);
break;
#ifdef IPV6
case ETHERTYPE_IPV6:
ipv6_read(p, length, caplen);
break;
#endif
#ifdef ETHERTYPE_VLAN
case ETHERTYPE_VLAN:
ethtype = ntohs(*(unsigned short*)(p+2));
p += 4;
length -= 4;
caplen -= 4;
if (ethtype <= ETHERMTU)
/* ether_type not known */
break;
goto recurse;
break;
#endif
case ETHERTYPE_PPPOE:
pppoe_read(p, length, caplen);
break;
default:
break;
}
return (1);
}
static void fddi_if_read(u_char *pcap, const struct pcap_pkthdr *h,
const u_char *p)
{
int caplen = h->caplen;
int length = h->len;
const struct fddi_header *fddip = (struct fddi_header *)p;
packet_length = length; /* save data link level packet length */
if (caplen < FDDI_HDRLEN)
return;
/* Skip over FDDI MAC header */
length -= FDDI_HDRLEN;
p += FDDI_HDRLEN;
caplen -= FDDI_HDRLEN;
/* Frame Control field determines interpretation of packet */
if ((fddip->fddi_fc & FDDIFC_CLFF) == FDDIFC_LLC_ASYNC) {
/* Try to print the LLC-layer header & higher layers */
if (llc_read(p, length, caplen) == 0) {
/* some kinds of LLC packet we cannot handle intelligently */
}
}
else {
/* Some kinds of FDDI packet we cannot handle intelligently */
}
}
#ifndef min
#define min(a, b) (((a)<(b))?(a):(b))
#endif
static int llc_read(const u_char *p, const int length, const int caplen)
{
struct llc llc;
register u_short et;
register int ret;
if (caplen < 3) {
return(0);
}
/* Watch out for possible alignment problems */
memcpy((char *)&llc, (char *)p, min(caplen, sizeof(llc)));
#if 0 /* we are not interested in these */
if (llc.ssap == LLCSAP_GLOBAL && llc.dsap == LLCSAP_GLOBAL) {
/* ipx */
return (1);
}
else if (p[0] == 0xf0 && p[1] == 0xf0) {
/* netbios */
}
if (llc.ssap == LLCSAP_ISONS && llc.dsap == LLCSAP_ISONS
&& llc.llcui == LLC_UI) {
/* iso */
}
#endif /* 0 */
if (llc.ssap == LLCSAP_SNAP && llc.dsap == LLCSAP_SNAP
&& llc.llcui == LLC_UI) {
/* snap */
if (caplen < sizeof(llc)) {
return (0);
}
/* This is an encapsulated Ethernet packet */
#ifdef ALIGN_WORD
{
u_short tmp;
memcpy(&tmp, &llc.ethertype[0], sizeof(u_short));
et = ntohs(tmp);
}
#else
et = ntohs(*(u_short *)&llc.ethertype[0]);
#endif
ret = ether_encap_read(et, p + sizeof(llc),
length - sizeof(llc), caplen - sizeof(llc));
if (ret)
return (ret);
}
/* llcsap */
return(0);
}
static void atm_if_read(u_char *pcap, const struct pcap_pkthdr *h,
const u_char *p)
{
int caplen = h->caplen;
int length = h->len;
u_short ether_type;
packet_length = length; /* save data link level packet length */
if (caplen < 8)
return;
if (p[0] != 0xaa || p[1] != 0xaa || p[2] != 0x03) {
/* unknown format! */
return;
}
ether_type = p[6] << 8 | p[7];
eth_addsize(ether_type, packet_length);
length -= 8;
caplen -= 8;
p += 8;
switch (ether_type) {
case ETHERTYPE_IP:
ip_read(p, length, caplen);
break;
#ifdef IPV6
case ETHERTYPE_IPV6:
ipv6_read(p, length, caplen);
break;
#endif
}
}
#ifndef SLIP_HDRLEN
#define SLIP_HDRLEN 16
#endif
static void
sl_if_print(u_char *user, const struct pcap_pkthdr *h, const u_char *p)
{
int caplen = h->caplen;
int length = h->len;
packet_length = length; /* save data link level packet length */
if (caplen < SLIP_HDRLEN)
return;
length -= SLIP_HDRLEN;
caplen -= SLIP_HDRLEN;
p += SLIP_HDRLEN;
ip_read(p, length, caplen);
}
#ifndef PPP_HDRLEN
#define PPP_HDRLEN 4
#endif
#define PPP_IP 0x21 /* Internet Protocol */
#define PPP_IPV6 0x57 /* Internet Protocol Version 6 */
static void ppp_if_read(u_char *pcap, const struct pcap_pkthdr *h,
const u_char *p)
{
int caplen = h->caplen;
int length = h->len;
const struct ip *ip;
u_int proto;
packet_length = length; /* save data link level packet length */
if (caplen < PPP_HDRLEN)
return;
proto = ntohs(*(u_short *)&p[2]);
length -= PPP_HDRLEN;
caplen -= PPP_HDRLEN;
p += PPP_HDRLEN;
ip = (struct ip *)p;
switch (proto) {
case ETHERTYPE_IP:
case PPP_IP:
ip_read(p, length, caplen);
break;
#ifdef IPV6
case ETHERTYPE_IPV6:
case PPP_IPV6:
ipv6_read(p, length, caplen);
break;
#endif
}
}
#ifdef DLT_PPP_BSDOS
/* BSD/OS specific PPP printer */
#ifndef PPP_BSDI_HDRLEN
#define PPP_BSDI_HDRLEN 24
#endif
#define PPP_ADDRESS 0xff /* The address byte value */
#define PPP_CONTROL 0x03 /* The control byte value */
/* BSD/OS specific PPP printer */
static void ppp_bsdos_if_read(u_char *pcap, const struct pcap_pkthdr *h,
const u_char *p)
{
int caplen = h->caplen;
int length = h->len;
int hdrlength;
u_short ptype;
packet_length = length; /* save data link level packet length */
if (caplen < PPP_BSDI_HDRLEN)
return;
hdrlength = 0;
if (p[0] == PPP_ADDRESS && p[1] == PPP_CONTROL) {
p += 2;
hdrlength = 2;
}
/* Retrieve the protocol type */
if (*p & 01) {
/* Compressed protocol field */
ptype = *p;
p++;
hdrlength += 1;
} else {
/* Un-compressed protocol field */
ptype = ntohs(*(u_short *)p);
p += 2;
hdrlength += 2;
}
length -= hdrlength;
caplen -= hdrlength;
switch (ptype) {
case PPP_IP:
ip_read(p, length, caplen);
break;
#ifdef IPV6
case PPP_IPV6:
ipv6_read(p, length, caplen);
break;
#endif
}
}
#endif /* DLT_PPP_BSDOS */
#ifdef DLT_PPP_SERIAL /* netbsd specific */
/*
* NetBSD-specific PPP printers. Handles multiple PPP encaps, and
* Cisco frames.
*/
#define PPP_NETBSD_SERIAL_HDRLEN 4
/* Actual address byte values */
#define PPP_ADDR_ALLSTATIONS 0xff /* all stations broadcast addr */
#define PPP_ADDR_CISCO_MULTICAST 0x8f /* Cisco multicast address */
#define PPP_ADDR_CISCO_UNICAST 0x0f /* Cisco unicast address */
/*
* XXX Note, this is overloaded with VINESCP, but we can tell based on
* XXX the address byte if we're using Cisco protocol numbers (i.e.
* XXX Ethertypes).
*/
#define PPP_CISCO_KEEPALIVE 0x8035 /* Cisco keepalive protocol */
static void ppp_netbsd_serial_if_read(u_char *pcap,
const struct pcap_pkthdr *h, const u_char *p)
{
int caplen = h->caplen;
int length = h->len;
u_short ptype;
u_char addr, ctrl;
packet_length = length; /* save data link level packet length */
if (caplen < PPP_NETBSD_SERIAL_HDRLEN)
return;
addr = p[0];
ctrl = p[1];
switch (addr) {
case PPP_ADDR_ALLSTATIONS:
/*
* Regular serial PPP packet.
*/
ptype = (p[2] << 8) | p[3];
p += PPP_NETBSD_SERIAL_HDRLEN;
length -= PPP_NETBSD_SERIAL_HDRLEN;
caplen -= PPP_NETBSD_SERIAL_HDRLEN;
switch (ptype) {
case PPP_IP:
ip_read(p, length, caplen);
break;
#ifdef IPV6
case PPP_IPV6:
ipv6_read(p, length, caplen);
break;
#endif
}
break;
case PPP_ADDR_CISCO_MULTICAST:
case PPP_ADDR_CISCO_UNICAST:
ptype = (p[2] << 8) | p[3];
p += PPP_NETBSD_SERIAL_HDRLEN;
length -= PPP_NETBSD_SERIAL_HDRLEN;
caplen -= PPP_NETBSD_SERIAL_HDRLEN;
switch (ptype) {
case PPP_CISCO_KEEPALIVE:
break;
default:
if (ether_encap_read(ptype, p, length, caplen) == 0) {
/* ether_type not known */
}
}
break;
default:
/* Address not known, print raw packet. */
break;
}
}
#endif /* DLT_PPP_SERIAL */
#ifndef NULL_HDRLEN
#define NULL_HDRLEN 4 /* DLT_NULL header length */
#endif
static void null_if_read(u_char *user, const struct pcap_pkthdr *h, const u_char *p)
{
int length = h->len;
int caplen = h->caplen;
const struct ip *ip;
packet_length = length; /* save data link level packet length */
length -= NULL_HDRLEN;
caplen -= NULL_HDRLEN;
ip = (struct ip *)(p + NULL_HDRLEN);
switch (ip->ip_v) {
case 4:
ip_read((const u_char *)ip, length, caplen);
break;
#ifdef IPV6
case 6:
ipv6_read((const u_char *)ip, length, caplen);
break;
#endif
}
}
#ifdef DLT_RAW
static void raw_if_read(u_char *pcap, const struct pcap_pkthdr *h, const u_char *p)
{
packet_length = h->len; /* save data link level packet length */
switch (((struct ip *)p)->ip_v) {
case 4:
ip_read(p, h->len, h->caplen);
break;
#ifdef IPV6
case 6:
ipv6_read(p, h->len, h->caplen);
break;
#endif
}
}
#endif
#ifdef PFLOG_HDRLEN
static void
pflog_if_read(u_char *user, const struct pcap_pkthdr *h, const u_char *p)
{
int caplen = h->caplen;
int length = h->len;
const struct pfloghdr *pf;
if (caplen < PFLOG_HDRLEN) {
return;
}
pf = (struct pfloghdr *)p;
p += PFLOG_HDRLEN;
length -= PFLOG_HDRLEN;
caplen -= PFLOG_HDRLEN;
packet_length = length; /* save packet length */
switch (ntohl(pf->af)) {
case AF_INET:
ip_read((const u_char *)p, length, caplen);
break;
#ifdef INET6
case AF_INET6:
ip6_read((const u_char *)p, length, caplen);
break;
#endif
}
}
#endif /* PFLOG_HDRLEN */
#ifndef PPPOE_HDRLEN
#define PPPOE_HDRLEN 6
#endif
static int pppoe_read(const u_char *bp, const int length, const int caplen)
{
u_short version, type, code, ptype;
const u_char *p;
int hdrlen;
if (caplen < PPPOE_HDRLEN)
return (0);
p = bp;
version = p[0] & 0xf0;
type = p[0] & 0x0f;
code = p[1];
if (version != 1 || type != 1 || code != 0)
return (0);
hdrlen = PPPOE_HDRLEN;
p += PPPOE_HDRLEN;
if (p[0] & 0x1) {
ptype = p[0];
hdrlen += 1;
}
else if (p[1] & 0x1) {
ptype = ntohs(*(u_short *)p);
hdrlen += 2;
}
else
return (0);
if (caplen < hdrlen)
return (0);
switch (ptype) {
case PPP_IP:
ip_read(bp + hdrlen, length - hdrlen, caplen - hdrlen);
break;
#ifdef IPV6
case PPP_IPV6:
ipv6_read(bp + hdrlen, length - hdrlen, caplen - hdrlen);
break;
#endif
}
return (1);
}
static int ip_read(const u_char *bp, const int length, const int caplen)
{
struct ip *ip;
int hlen, len, proto, off;
u_long srcaddr, dstaddr;
u_short srcport, dstport;
struct tcphdr *tcp;
struct udphdr *udp;
ip = (struct ip *)bp;
if (length < sizeof (struct ip))
return 0;
#ifdef ALIGN_WORD
/*
* The IP header is not word aligned, so copy into abuf.
* This will never happen with BPF. It does happen raw packet
* dumps from -r.
*/
if ((int)ip & (sizeof(long)-1)) {
static u_char *abuf;
if (abuf == 0)
abuf = (u_char *)malloc(DEFAULT_SNAPLEN);
memcpy((char *)abuf, (char *)ip, caplen);
ip = (struct ip *)abuf;
}
#endif /* ALIGN_WORD */
hlen = ip->ip_hl * 4;
len = min(ntohs(ip->ip_len), length);
len -= hlen;
if (len < 0)
return 0;
bp = (u_char *)ip + hlen;
srcaddr = ntohl(ip->ip_src.s_addr);
dstaddr = ntohl(ip->ip_dst.s_addr);
proto = ip->ip_p;
if (!(ttt_filter & TTTFILTER_SRCHOST)) {
host_addsize(srcaddr, packet_length);
if (!(ttt_filter & TTTFILTER_DSTHOST) && srcaddr != dstaddr)
host_addsize(dstaddr, packet_length);
}
else if (!(ttt_filter & TTTFILTER_DSTHOST))
host_addsize(dstaddr, packet_length);
if (proto != IPPROTO_TCP && proto != IPPROTO_UDP)
ip_addsize(proto, packet_length);
else {
/* if this is fragment zero, hand it to the next higher
level protocol. */
off = ntohs(ip->ip_off);
if (off & 0x1fff) {
/* process fragments */
if ((bp = (u_char *)ip4f_lookup(ip)) == NULL)
/* lookup failed */
return 1;
}
if (proto == IPPROTO_TCP) {
if (len < sizeof (struct tcphdr))
return 0;
tcp = (struct tcphdr *)bp;
srcport = ntohs(tcp->th_sport);
dstport = ntohs(tcp->th_dport);
if (!(ttt_filter & TTTFILTER_SRCPORT)) {
tcp_addsize(srcport, packet_length);
if (dstport != srcport
&& !(ttt_filter & TTTFILTER_DSTPORT))
tcp_addsize(dstport, packet_length);
}
else if (!(ttt_filter & TTTFILTER_DSTPORT))
tcp_addsize(dstport, packet_length);
}
else {
if (len < sizeof (struct udphdr))
return 0;
udp = (struct udphdr *)bp;
srcport = ntohs(udp->uh_sport);
dstport = ntohs(udp->uh_dport);
if (!(ttt_filter & TTTFILTER_SRCPORT)) {
udp_addsize(srcport, packet_length);
if (dstport != srcport
&& !(ttt_filter & TTTFILTER_DSTPORT))
udp_addsize(dstport, packet_length);
}
else if (!(ttt_filter & TTTFILTER_DSTPORT))
udp_addsize(dstport, packet_length);
}
/* if this is a first fragment, cache it. */
if ((off & IP_MF) && (off & 0x1fff) == 0)
ip4f_cache(ip, (struct udphdr *)bp);
}
#ifdef IPV6
/* sould we do this? or is it better to see only that ipv6 being
encapsulated? another idea is to make another class
TTTTYPE_IPV6INIP. */
if (proto == IPPROTO_IPV6) {
/* ipv6 in ipv4 */
ipv6_read(bp, len, caplen-hlen);
}
#endif /* IPV6 */
return 1;
}
/*
* helper functions to handle IPv4 fragments.
* currently only in-sequence fragments are handled.
* - fragment info is cached in a LRU list.
* - when a first fragment is found, cache its flow info.
* - when a non-first fragment is found, lookup the cache.
*/
static void ip4f_cache(ip, udp)
struct ip *ip;
struct udphdr *udp;
{
struct ip4_frag *fp;
if (TAILQ_EMPTY(&ip4f_list)) {
/* first time call, allocate fragment cache entries. */
if (ip4f_init() < 0)
/* allocation failed! */
return;
}
fp = ip4f_alloc();
fp->ip4f_proto = ip->ip_p;
fp->ip4f_id = ip->ip_id;
fp->ip4f_src = ip->ip_src;
fp->ip4f_dst = ip->ip_dst;
fp->ip4f_udphdr.uh_sport = udp->uh_sport;
fp->ip4f_udphdr.uh_dport = udp->uh_dport;
}
static struct udphdr *ip4f_lookup(ip)
struct ip *ip;
{
struct ip4_frag *fp;
struct udphdr *udphdr;
for (fp = TAILQ_FIRST(&ip4f_list); fp != NULL && fp->ip4f_valid;
fp = TAILQ_NEXT(fp, ip4f_chain))
if (ip->ip_id == fp->ip4f_id &&
ip->ip_src.s_addr == fp->ip4f_src.s_addr &&
ip->ip_dst.s_addr == fp->ip4f_dst.s_addr &&
ip->ip_p == fp->ip4f_proto) {
/* found the matching entry */
udphdr = &fp->ip4f_udphdr;
if ((ntohs(ip->ip_off) & IP_MF) == 0)
/* this is the last fragment, release the entry. */
ip4f_free(fp);
return (udphdr);
}
/* no matching entry found */
return (NULL);
}
static int ip4f_init(void)
{
struct ip4_frag *fp;
int i;
TAILQ_INIT(&ip4f_list);
for (i=0; i<IP4F_TABSIZE; i++) {
fp = (struct ip4_frag *)malloc(sizeof(struct ip4_frag));
if (fp == NULL) {
printf("ip4f_initcache: can't alloc cache entry!\n");
return (-1);
}
fp->ip4f_valid = 0;
TAILQ_INSERT_TAIL(&ip4f_list, fp, ip4f_chain);
}
return (0);
}
static struct ip4_frag *ip4f_alloc(void)
{
struct ip4_frag *fp;
/* reclaim an entry at the tail, put it at the head */
fp = TAILQ_LAST(&ip4f_list, ip4f_list);
TAILQ_REMOVE(&ip4f_list, fp, ip4f_chain);
fp->ip4f_valid = 1;
TAILQ_INSERT_HEAD(&ip4f_list, fp, ip4f_chain);
return (fp);
}
static void ip4f_free(fp)
struct ip4_frag *fp;
{
TAILQ_REMOVE(&ip4f_list, fp, ip4f_chain);
fp->ip4f_valid = 0;
TAILQ_INSERT_TAIL(&ip4f_list, fp, ip4f_chain);
}
#ifdef IPV6
/* this version doesn't handle fragments */
static int ipv6_read(const u_char *bp, const int length, const int caplen)
{
struct ipv6 *ipv6;
int hlen, len, proto;
u_long srcaddr[4], dstaddr[4];
u_short srcport, dstport;
struct tcphdr *tcp;
struct udphdr *udp;
ipv6 = (struct ipv6 *)bp;
if (length < sizeof (struct ipv6))
return 0;
#ifdef ALIGN_WORD
/*
* The IP header is not word aligned, so copy into abuf.
* This will never happen with BPF. It does happen raw packet
* dumps from -r.
*/
if ((int)ipv6 & (sizeof(long)-1)) {
static u_char *abuf;
if (abuf == 0)
abuf = (u_char *)malloc(DEFAULT_SNAPLEN);
memcpy((char *)abuf, (char *)ipv6, caplen);
ipv6 = (struct ipv6 *)abuf;
}
#endif /* ALIGN_WORD */
hlen = read_ipv6hdr(ipv6, &proto, caplen);
len = min(ntohs(ipv6->ipv6_len) + sizeof(struct ipv6), length) - hlen;
if (len < 0)
return 0;
bp = (u_char *)ipv6 + hlen;
memcpy(srcaddr, &ipv6->ipv6_src, sizeof(struct in6_addr));
srcaddr[0] = ntohl(srcaddr[0]);
srcaddr[1] = ntohl(srcaddr[1]);
srcaddr[2] = ntohl(srcaddr[2]);
srcaddr[3] = ntohl(srcaddr[3]);
memcpy(dstaddr, &ipv6->ipv6_dst, sizeof(struct in6_addr));
dstaddr[0] = ntohl(dstaddr[0]);
dstaddr[1] = ntohl(dstaddr[1]);
dstaddr[2] = ntohl(dstaddr[2]);
dstaddr[3] = ntohl(dstaddr[3]);
if (!(ttt_filter & TTTFILTER_SRCHOST)) {
hostv6_addsize(srcaddr, packet_length);
if (!(ttt_filter & TTTFILTER_DSTHOST)
&& memcmp(srcaddr, dstaddr, sizeof(srcaddr)))
hostv6_addsize(dstaddr, packet_length);
}
else if (!(ttt_filter & TTTFILTER_DSTHOST))
hostv6_addsize(dstaddr, packet_length);
if (proto != IPPROTO_TCP && proto != IPPROTO_UDP)
ipv6_addsize(proto, packet_length);
else if (proto == IPPROTO_TCP) {
if (len < sizeof (struct tcphdr))
return 0;
tcp = (struct tcphdr *)bp;
srcport = ntohs(tcp->th_sport);
dstport = ntohs(tcp->th_dport);
if (!(ttt_filter & TTTFILTER_SRCPORT)) {
tcpv6_addsize(srcport, packet_length);
if (dstport != srcport
&& !(ttt_filter & TTTFILTER_DSTPORT))
tcpv6_addsize(dstport, packet_length);
}
else if (!(ttt_filter & TTTFILTER_DSTPORT))
tcpv6_addsize(dstport, packet_length);
}
else {
if (len < sizeof (struct udphdr))
return 0;
udp = (struct udphdr *)bp;
srcport = ntohs(udp->uh_sport);
dstport = ntohs(udp->uh_dport);
if (!(ttt_filter & TTTFILTER_SRCPORT)) {
udpv6_addsize(srcport, packet_length);
if (dstport != srcport
&& !(ttt_filter & TTTFILTER_DSTPORT))
udpv6_addsize(dstport, packet_length);
}
else if (!(ttt_filter & TTTFILTER_DSTPORT))
udpv6_addsize(dstport, packet_length);
}
return 1;
}
static int read_ipv6hdr(struct ipv6 *ipv6, int *proto, int caplen)
{
int hlen, opt_len;
struct ipv6_ext *ipv6ext;
u_char nh;
hlen = sizeof(struct ipv6);
caplen -= hlen;
nh = ipv6->ipv6_nh;
ipv6ext = (struct ipv6_ext *)(ipv6 + 1);
while (nh == IPV6_NH_HOP || nh == IPV6_NH_RT ||
nh == IPV6_NH_AUTH || nh == IPV6_NH_DST) {
if (nh == IPV6_NH_AUTH)
opt_len = 8 + (ipv6ext->i6ext_len * 4);
else
opt_len = (ipv6ext->i6ext_len + 1) * 8;
hlen += opt_len;
if ((caplen -= opt_len) < 0)
break;
nh = ipv6ext->i6ext_nh;
ipv6ext = (struct ipv6_ext *)((caddr_t)ipv6ext + opt_len);
}
*proto = (int)nh;
return hlen;
}
#endif /* IPV6 */
struct printer {
pcap_handler f;
int type;
};
static struct printer printers[] = {
{ ether_if_read, DLT_EN10MB },
{ fddi_if_read, DLT_FDDI },
#ifdef DLT_ATM_RFC1483
{ atm_if_read, DLT_ATM_RFC1483 },
#endif
{ sl_if_print, DLT_SLIP },
{ ppp_if_read, DLT_PPP },
#ifdef DLT_PPP_BSDOS
{ ppp_bsdos_if_read, DLT_PPP_BSDOS },
#endif
#ifdef DLT_PPP_SERIAL /* netbsd specific */
{ ppp_netbsd_serial_if_read, DLT_PPP_SERIAL },
#endif
{ null_if_read, DLT_NULL },
#ifdef DLT_RAW
{ raw_if_read, DLT_RAW },
#endif
#ifdef PFLOG_HDRLEN
{ pflog_if_read, DLT_PFLOG },
#endif
{ NULL, 0 },
};
static pcap_handler
lookup_printer(int type)
{
struct printer *p;
for (p = printers; p->f; ++p)
if (type == p->type)
return p->f;
fatal_error("lookup_printer: unknown data link type 0x%x", type);
/* NOTREACHED */
return NULL;
}
int open_pf(const char *interface)
{
int snaplen, fd;
struct bpf_program fcode;
u_int localnet, netmask;
struct in_addr inaddr;
char buf[128];
if (interface == NULL) {
device = pcap_lookupdev(errbuf);
if (device == NULL)
fatal_error(errbuf);
}
else {
strlcpy(buf, interface, sizeof(buf));
device = buf;
}
printf("packet filter: using device %s\n", device);
snaplen = DEFAULT_SNAPLEN;
pd = pcap_open_live(device, snaplen, 1, 0, errbuf);
if (pd == NULL)
fatal_error(errbuf);
if (pcap_lookupnet(device, &localnet, &netmask, errbuf) < 0)
fatal_error(errbuf);
netname_init(localnet, netmask);
inaddr.s_addr = localnet;
printf("local network is %s", inet_ntoa(inaddr));
inaddr.s_addr = netmask;
printf(" netmask is %s\n", inet_ntoa(inaddr));
if (pcap_compile(pd, &fcode, ttt_pcapcmd, 1, netmask) < 0)
fatal_error(pcap_geterr(pd));
if (pcap_setfilter(pd, &fcode) < 0)
fatal_error(pcap_geterr(pd));
/*
* Let user own process after socket has been opened.
*/
setuid(getuid());
ttt_netreader = lookup_printer(pcap_datalink(pd));
fd = pcap_fileno(pd);
#if defined(BSD) && defined(BPF_MAXBUFSIZE)
{
/* check the buffer size */
u_int bufsize;
if (ioctl(fd, BIOCGBLEN, (caddr_t)&bufsize) < 0)
perror("BIOCGBLEN");
else
printf("bpf buffer size is %d\n", bufsize);
}
#endif /* BSD */
return fd;
}
void close_pf(void)
{
pcap_close(pd);
}
int open_dump(const char *file, const char *interface)
{
int fd;
struct bpf_program fcode;
u_int localnet, netmask;
struct in_addr inaddr;
char buf[128];
printf("packet filter: using dump file %s\n", file);
pd = pcap_open_offline((char *)file, errbuf);
if (pd == NULL)
fatal_error(errbuf);
/* try to get local network address to print host names */
localnet = 0;
netmask = 0xffffffff;
if (interface == NULL) {
device = pcap_lookupdev(errbuf);
}
else {
strlcpy(buf, interface, sizeof(buf));
device = buf;
}
if (device != NULL)
(void)pcap_lookupnet(device, &localnet, &netmask, errbuf);
netname_init(localnet, netmask);
inaddr.s_addr = localnet;
printf("local network is %s", inet_ntoa(inaddr));
inaddr.s_addr = netmask;
printf(" netmask is %s\n", inet_ntoa(inaddr));
if (pcap_compile(pd, &fcode, ttt_pcapcmd, 1, netmask) < 0)
fatal_error(pcap_geterr(pd));
if (pcap_setfilter(pd, &fcode) < 0)
fatal_error(pcap_geterr(pd));
ttt_netreader = lookup_printer(pcap_datalink(pd));
fd = fileno(pcap_file(pd));
return fd;
}
int get_pcapstat(u_long *recvp, u_long *dropp, u_long *lostp)
{
struct pcap_stat pc_stat;
if (pcap_stats(pd, &pc_stat) == 0) {
*recvp = pc_stat.ps_recv;
*dropp = pc_stat.ps_drop;
*lostp = 0; /* no lost report for ttt */
return 0;
}
return (-1);
}
syntax highlighted by Code2HTML, v. 0.9.1