/*
** iplog_pcap.c - iplog pcap management routines.
** Copyright (C) 1999-2001 Ryan McCabe <odin@numb.org>
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License, version 2,
** as published by the Free Software Foundation.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
**
** $Id: iplog_pcap.c,v 1.33 2001/01/01 16:02:14 odin Exp $
*/
#include <config.h>
#include <unistd.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <pcap.h>
#ifdef HAVE_SYS_SOCKIO_H
# include <sys/sockio.h>
#endif
#include <iplog.h>
#include <iplog_pcap.h>
#include <iplog_options.h>
static int get_iflist(struct pcap_data **int_list);
static int get_ifflags(const u_char *ifname, short *ifflags);
static int get_pcap_datalink(pcap_t *pd);
static int open_pcap_device(struct pcap_data *pdata);
u_char *pcap_network;
static int get_iflist(struct pcap_data **int_list) {
int sock;
#ifdef HAVE_SOCKADDR_SA_LEN
u_int i;
#endif
struct ifreq buf[32], *ifr, *last, *next;
struct ifconf ifc;
struct pcap_data data;
struct sockaddr_in *isin;
u_char *p, *nlast = "\0";
sock = socket(PF_INET, SOCK_DGRAM, 0);
if (sock == -1) {
IDEBUG(("[%s:%d] socket: %s", __FILE__, __LINE__, strerror(errno)));
return (-1);
}
memset(&data, 0, sizeof(data));
ifc.ifc_len = sizeof(buf);
ifc.ifc_buf = (caddr_t) buf;
if (ioctl(sock, SIOCGIFCONF, &ifc) != 0 ||
(u_long) ifc.ifc_len < sizeof(struct ifreq))
{
IDEBUG(("[%s:%d] ioctl: %s", __FILE__, __LINE__, strerror(errno)));
close(sock);
return (-1);
}
ifr = buf;
last = (struct ifreq *) ((u_char *) buf + ifc.ifc_len);
for (; ifr < last ; ifr = next) {
#ifdef HAVE_SOCKADDR_SA_LEN
i = ifr->ifr_addr.sa_len + sizeof(ifr->ifr_name);
if (i < sizeof(struct ifreq))
next = ifr + 1;
else
next = (struct ifreq *) ((u_char *) ifr + i);
#else
next = ifr + 1;
#endif
if (ifr->ifr_addr.sa_family != AF_INET)
continue;
if (!ifflag_isset(ifr->ifr_name, IFF_UP))
continue;
p = strchr(ifr->ifr_name, ':');
if (p != NULL)
*p = '\0';
if (strcmp(nlast, ifr->ifr_name)) {
if (strcmp(nlast, "\0"))
dlist_copy_append(&data, int_list, sizeof(data));
nlast = ifr->ifr_name;
memset(&data, 0, sizeof(data));
xstrncpy(data.name, ifr->ifr_name, sizeof(data.name));
}
isin = (struct sockaddr_in *) &ifr->ifr_addr;
data.addr = xrealloc(data.addr, sizeof(ipaddr_t) * (data.num_addr + 1));
data.addr[data.num_addr++] = isin->sin_addr.s_addr;
}
if (data.num_addr != 0)
dlist_copy_append(&data, int_list, sizeof(data));
close(sock);
return (0);
}
/*
** Store the flags set on interface "ifname" in "ifflags."
** Returns 0 on success, -1 on failure.
*/
static int get_ifflags(const u_char *ifname, short *ifflags) {
struct ifreq ifr;
int ret, sock;
if (strlen(ifname) >= sizeof(ifr.ifr_name)) {
IDEBUG(("[%s:%d] iff name too long: %s", __FILE__, __LINE__, ifname));
return (-1);
}
sock = socket(PF_INET, SOCK_DGRAM, 0);
if (sock == -1) {
IDEBUG(("[%s:%d] socket: %s", __FILE__, __LINE__, strerror(errno)));
return (-1);
}
xstrncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
if (ioctl(sock, SIOCGIFFLAGS, &ifr) != 0) {
IDEBUG(("[%s:%d] %s: %s",
__FILE__, __LINE__, ifr.ifr_name, strerror(errno)));
ret = -1;
} else {
*(short *) ifflags = ifr.ifr_flags;
ret = 0;
}
close(sock);
return (ret);
}
/*
** Returns true if "test_flags" is set on interface "ifname,"
** false if it isn't.
*/
bool ifflag_isset(const u_char *ifname, short test_flag) {
short ifflags;
if (get_ifflags(ifname, &ifflags) == -1)
return (false);
return ((ifflags & test_flag) != 0);
}
/*
** Returns the datalink type of the interface specified in "pd."
** Returns -1 on failure.
*/
static int get_pcap_datalink(pcap_t *pd) {
int datalink = pcap_datalink(pd), dlt;
switch (datalink) {
case DLT_RAW:
dlt = 0;
break;
case DLT_ATM_RFC1483:
dlt = 8;
break;
case DLT_EN10MB:
case DLT_IEEE802:
dlt = sizeof(struct ethhdr);
break;
case DLT_SLIP_BSDOS:
case DLT_PPP_BSDOS:
dlt = 24;
break;
case DLT_SLIP:
dlt = 16;
break;
case DLT_PPP:
case DLT_NULL:
#ifdef __OpenBSD__
case DLT_LOOP:
#endif
dlt = 4;
break;
default:
dlt = -1;
break;
}
return (dlt);
}
static int open_pcap_device(struct pcap_data *pdata) {
u_char fstring[1024], *temp, errbuf[PCAP_ERRBUF_SIZE];
struct bpf_program filt;
u_int i = 0;
int b = 1;
pdata->pd =
pcap_open_live(pdata->name, SNAPLEN, opt_enabled(PROMISC), 0, errbuf);
if (pdata->pd == NULL) {
IDEBUG(("[%s:%d] pcap_open_live: %s (%s)",
__FILE__, __LINE__, errbuf, strerror(errno)));
return (-1);
}
pdata->dl = get_pcap_datalink(pdata->pd);
if (pdata->dl == -1) {
IDEBUG(("[%s:%d] Unknown datalink: %d",
__FILE__, __LINE__, pcap_datalink(pdata->pd)));
pcap_close(pdata->pd);
return (-1);
}
#ifdef __FreeBSD__
#include <osreldate.h>
#if __FreeBSD_version >= 420001
ioctl(pcap_fileno(pdata->pd), BIOCIMMEDIATE, &b);
#endif
#endif
#ifdef __linux__
/*
** pcap filters don't work on Linux loopback devices.
** I don't know why anybody would want to monitor loopback, anyway.
*/
if (ifflag_isset(pdata->name, IFF_LOOPBACK))
return (0);
#endif
xstrncpy(fstring, "ip and (", sizeof(fstring));
if (opt_enabled(LOG_TCP)) {
xstrncat(fstring, "tcp ", sizeof(fstring));
++i;
}
if (opt_enabled(LOG_UDP)) {
if (i++ == 0)
temp = "udp ";
else
temp = "or udp ";
xstrncat(fstring, temp, sizeof(fstring));
}
if (opt_enabled(LOG_ICMP)) {
if (i == 0)
temp = "icmp";
else
temp = "or icmp";
xstrncat(fstring, temp, sizeof(fstring));
}
if (opt_enabled(PROMISC)) {
u_char *p, *cur, *orig = xstrdup(pcap_network);
cur = orig;
xstrncat(fstring, ") and dst net (", sizeof(fstring));
while (1) {
p = strchr(cur, ',');
if (p != NULL)
*p++ = '\0';
xstrncat(fstring, cur, sizeof(fstring));
if (p == NULL)
break;
xstrncat(fstring, " or ", sizeof(fstring));
cur = p;
}
free(orig);
} else {
struct sockaddr_in opd_sin;
u_char nbuf[16];
xstrncat(fstring, ") and dst host (", sizeof(fstring));
for (i = 0 ; i < pdata->num_addr ; i++) {
opd_sin.sin_addr.s_addr = pdata->addr[i];
if (i > 0)
xstrncat(fstring, " or ", sizeof(fstring));
inet_ntoa_r(&opd_sin.sin_addr, nbuf, sizeof(nbuf));
xstrncat(fstring, nbuf, sizeof(fstring));
}
}
xstrncat(fstring, ")", sizeof(fstring));
IDEBUG(("[%s:%d] Filter: %s", __FILE__, __LINE__, fstring));
/*
** There seems to be a memory leak in pcap_compile()..
*/
if (pcap_compile(pdata->pd, &filt, fstring, 1, 0) == -1)
fatal("pcap_compile: %s", pcap_geterr(pdata->pd));
if (pcap_setfilter(pdata->pd, &filt) == -1)
fatal("pcap_setfilter: %s", pcap_geterr(pdata->pd));
return (0);
}
int setup_pcap(struct pcap_data **plist, const u_char *ifstring) {
struct pcap_data *cur;
u_char **vif = NULL;
size_t interfaces = 0;
int ret;
if (get_iflist(plist) == -1 || *plist == NULL)
return (-1);
cur = *plist;
if (ifstring != NULL) {
size_t i = 0, j;
bool found;
u_char *delim, *pos, *tstr;
tstr = xstrdup(ifstring);
for (pos = tstr ; (delim = strchr(pos, ',')) != NULL ; pos = delim) {
*delim++ = '\0';
vif = realloc(vif, sizeof(u_char *) * (i + 1));
vif[i++] = pos;
}
vif = realloc(vif, sizeof(u_char *) * (i + 1));
vif[i++] = pos;
while (cur != NULL) {
found = false;
for (j = 0 ; j < i ; j++) {
if (!strcmp(vif[j], cur->name)) {
found = true;
break;
}
}
if (found == false) {
free(cur->addr);
cur = dlist_delete(cur, plist);
} else
cur = cur->next;
}
free(vif);
free(tstr);
} else {
/*
** Ignore loopback interfaces by default.
*/
while (cur != NULL) {
if (ifflag_isset(cur->name, IFF_LOOPBACK)) {
free(cur->addr);
cur = dlist_delete(cur, plist);
} else
cur = cur->next;
}
}
for (cur = *plist ; cur != NULL ;) {
ret = open_pcap_device(cur);
free(cur->addr);
if (ret == -1) {
mysyslog("Warning: unable to open %s", cur->name);
cur = dlist_delete(cur, plist);
} else {
IDEBUG(("[%s:%d] got %s", __FILE__, __LINE__, cur->name));
interfaces++;
cur = cur->next;
}
}
if (interfaces == 0)
return (-1);
return (0);
}
/* vim:ts=4:sw=8:tw=0 */
syntax highlighted by Code2HTML, v. 0.9.1