Files
Richard Scheffenegger 347dd0539f tcp: add TH_AE capabilities to ppp and pf
Add support for the AE Flag in the TCP header to pf and ppp.
Commonalize to the use of "E"(ECE), "W"(CWR) and "e"(AE)
for the TCP header flags, in line with tcpdump.

Reviewers: kp, cc, tuexen, cy, #transport!
Sponsored by: NetApp, Inc.
Differential Revision: https://reviews.freebsd.org/D47106
2024-11-29 10:04:31 +01:00

1845 lines
39 KiB
C

/*
* Copyright (C) 2012 by Darren Reed.
*
* See the IPFILTER.LICENCE file for details on licencing.
*/
#include "ipf.h"
#include "ipmon.h"
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <syslog.h>
#include <ctype.h>
#include <fcntl.h>
#include <signal.h>
#define STRERROR(x) strerror(x)
extern int optind;
extern char *optarg;
extern ipmon_saver_t executesaver;
extern ipmon_saver_t filesaver;
extern ipmon_saver_t nothingsaver;
extern ipmon_saver_t snmpv1saver;
extern ipmon_saver_t snmpv2saver;
extern ipmon_saver_t syslogsaver;
struct flags {
int value;
char flag;
};
typedef struct logsource {
int fd;
int logtype;
char *file;
int regular;
size_t size;
} logsource_t;
typedef struct config {
int opts;
int maxfd;
logsource_t logsrc[3];
fd_set fdmr;
FILE *blog;
char *bfile;
FILE *log;
char *file;
char *cfile;
} config_t;
typedef struct icmp_subtype {
int ist_val;
char *ist_name;
} icmp_subtype_t;
typedef struct icmp_type {
int it_val;
struct icmp_subtype *it_subtable;
size_t it_stsize;
char *it_name;
} icmp_type_t;
#define IST_SZ(x) (sizeof(x)/sizeof(icmp_subtype_t))
struct flags tcpfl[] = {
{ TH_ACK, 'A' },
{ TH_RST, 'R' },
{ TH_SYN, 'S' },
{ TH_FIN, 'F' },
{ TH_URG, 'U' },
{ TH_PUSH,'P' },
{ TH_ECN, 'E' },
{ TH_CWR, 'W' },
{ TH_AE, 'e' },
{ 0, '\0' }
};
char *reasons[] = {
"filter-rule",
"log-or-block_1",
"pps-rate",
"jumbogram",
"makefrip-fail",
"state_add-fail",
"updateipid-fail",
"log-or-block_2",
"decap-fail",
"auth_new-fail",
"auth_captured",
"coalesce-fail",
"pullup-fail",
"auth-feedback",
"bad-frag",
"natv4_out-fail",
"natv4_in-fail",
"natv6_out-fail",
"natv6_in-fail",
};
#if SOLARIS
static char *pidfile = "/etc/opt/ipf/ipmon.pid";
#else
static char *pidfile = "/var/run/ipmon.pid";
#endif
static char line[2048];
static int donehup = 0;
static void usage(char *);
static void handlehup(int);
static void flushlogs(char *, FILE *);
static void print_log(config_t *, logsource_t *, char *, int);
static void print_ipflog(config_t *, char *, int);
static void print_natlog(config_t *, char *, int);
static void print_statelog(config_t *, char *, int);
static int read_log(int, int *, char *, int);
static void write_pid(char *);
static char *icmpname(u_int, u_int);
static char *icmpname6(u_int, u_int);
static icmp_type_t *find_icmptype(int, icmp_type_t *, size_t);
static icmp_subtype_t *find_icmpsubtype(int, icmp_subtype_t *, size_t);
static struct tm *get_tm(time_t);
char *portlocalname(int, char *, u_int);
int main(int, char *[]);
static void logopts(int, char *);
static void init_tabs(void);
static char *getlocalproto(u_int);
static void openlogs(config_t *conf);
static int read_loginfo(config_t *conf);
static void initconfig(config_t *conf);
static char **protocols = NULL;
static char **udp_ports = NULL;
static char **tcp_ports = NULL;
#define HOSTNAMEV4(b) hostname(AF_INET, (u_32_t *)&(b))
#ifndef LOGFAC
#define LOGFAC LOG_LOCAL0
#endif
int logfac = LOGFAC;
int ipmonopts = 0;
int opts = OPT_NORESOLVE;
int use_inet6 = 0;
static icmp_subtype_t icmpunreachnames[] = {
{ ICMP_UNREACH_NET, "net" },
{ ICMP_UNREACH_HOST, "host" },
{ ICMP_UNREACH_PROTOCOL, "protocol" },
{ ICMP_UNREACH_PORT, "port" },
{ ICMP_UNREACH_NEEDFRAG, "needfrag" },
{ ICMP_UNREACH_SRCFAIL, "srcfail" },
{ ICMP_UNREACH_NET_UNKNOWN, "net_unknown" },
{ ICMP_UNREACH_HOST_UNKNOWN, "host_unknown" },
{ ICMP_UNREACH_NET, "isolated" },
{ ICMP_UNREACH_NET_PROHIB, "net_prohib" },
{ ICMP_UNREACH_NET_PROHIB, "host_prohib" },
{ ICMP_UNREACH_TOSNET, "tosnet" },
{ ICMP_UNREACH_TOSHOST, "toshost" },
{ ICMP_UNREACH_ADMIN_PROHIBIT, "admin_prohibit" },
{ -2, NULL }
};
static icmp_subtype_t redirectnames[] = {
{ ICMP_REDIRECT_NET, "net" },
{ ICMP_REDIRECT_HOST, "host" },
{ ICMP_REDIRECT_TOSNET, "tosnet" },
{ ICMP_REDIRECT_TOSHOST, "toshost" },
{ -2, NULL }
};
static icmp_subtype_t timxceednames[] = {
{ ICMP_TIMXCEED_INTRANS, "transit" },
{ ICMP_TIMXCEED_REASS, "reassem" },
{ -2, NULL }
};
static icmp_subtype_t paramnames[] = {
{ ICMP_PARAMPROB_ERRATPTR, "errata_pointer" },
{ ICMP_PARAMPROB_OPTABSENT, "optmissing" },
{ ICMP_PARAMPROB_LENGTH, "length" },
{ -2, NULL }
};
static icmp_type_t icmptypes4[] = {
{ ICMP_ECHOREPLY, NULL, 0, "echoreply" },
{ -1, NULL, 0, NULL },
{ -1, NULL, 0, NULL },
{ ICMP_UNREACH, icmpunreachnames,
IST_SZ(icmpunreachnames),"unreach" },
{ ICMP_SOURCEQUENCH, NULL, 0, "sourcequench" },
{ ICMP_REDIRECT, redirectnames,
IST_SZ(redirectnames), "redirect" },
{ -1, NULL, 0, NULL },
{ -1, NULL, 0, NULL },
{ ICMP_ECHO, NULL, 0, "echo" },
{ ICMP_ROUTERADVERT, NULL, 0, "routeradvert" },
{ ICMP_ROUTERSOLICIT, NULL, 0, "routersolicit" },
{ ICMP_TIMXCEED, timxceednames,
IST_SZ(timxceednames), "timxceed" },
{ ICMP_PARAMPROB, paramnames,
IST_SZ(paramnames), "paramprob" },
{ ICMP_TSTAMP, NULL, 0, "timestamp" },
{ ICMP_TSTAMPREPLY, NULL, 0, "timestampreply" },
{ ICMP_IREQ, NULL, 0, "inforeq" },
{ ICMP_IREQREPLY, NULL, 0, "inforeply" },
{ ICMP_MASKREQ, NULL, 0, "maskreq" },
{ ICMP_MASKREPLY, NULL, 0, "maskreply" },
{ -2, NULL, 0, NULL }
};
static icmp_subtype_t icmpredirect6[] = {
{ ICMP6_DST_UNREACH_NOROUTE, "noroute" },
{ ICMP6_DST_UNREACH_ADMIN, "admin" },
{ ICMP6_DST_UNREACH_NOTNEIGHBOR, "neighbour" },
{ ICMP6_DST_UNREACH_ADDR, "address" },
{ ICMP6_DST_UNREACH_NOPORT, "noport" },
{ -2, NULL }
};
static icmp_subtype_t icmptimexceed6[] = {
{ ICMP6_TIME_EXCEED_TRANSIT, "intransit" },
{ ICMP6_TIME_EXCEED_REASSEMBLY, "reassem" },
{ -2, NULL }
};
static icmp_subtype_t icmpparamprob6[] = {
{ ICMP6_PARAMPROB_HEADER, "header" },
{ ICMP6_PARAMPROB_NEXTHEADER, "nextheader" },
{ ICMP6_PARAMPROB_OPTION, "option" },
{ -2, NULL }
};
static icmp_subtype_t icmpquerysubject6[] = {
{ ICMP6_NI_SUBJ_IPV6, "ipv6" },
{ ICMP6_NI_SUBJ_FQDN, "fqdn" },
{ ICMP6_NI_SUBJ_IPV4, "ipv4" },
{ -2, NULL },
};
static icmp_subtype_t icmpnodeinfo6[] = {
{ ICMP6_NI_SUCCESS, "success" },
{ ICMP6_NI_REFUSED, "refused" },
{ ICMP6_NI_UNKNOWN, "unknown" },
{ -2, NULL }
};
static icmp_subtype_t icmprenumber6[] = {
{ ICMP6_ROUTER_RENUMBERING_COMMAND, "command" },
{ ICMP6_ROUTER_RENUMBERING_RESULT, "result" },
{ ICMP6_ROUTER_RENUMBERING_SEQNUM_RESET, "seqnum_reset" },
{ -2, NULL }
};
static icmp_type_t icmptypes6[] = {
{ 0, NULL, 0, NULL },
{ ICMP6_DST_UNREACH, icmpredirect6,
IST_SZ(icmpredirect6), "unreach" },
{ ICMP6_PACKET_TOO_BIG, NULL, 0, "toobig" },
{ ICMP6_TIME_EXCEEDED, icmptimexceed6,
IST_SZ(icmptimexceed6), "timxceed" },
{ ICMP6_PARAM_PROB, icmpparamprob6,
IST_SZ(icmpparamprob6), "paramprob" },
{ ICMP6_ECHO_REQUEST, NULL, 0, "echo" },
{ ICMP6_ECHO_REPLY, NULL, 0, "echoreply" },
{ ICMP6_MEMBERSHIP_QUERY, icmpquerysubject6,
IST_SZ(icmpquerysubject6), "groupmemberquery" },
{ ICMP6_MEMBERSHIP_REPORT,NULL, 0, "groupmemberreport" },
{ ICMP6_MEMBERSHIP_REDUCTION,NULL, 0, "groupmemberterm" },
{ ND_ROUTER_SOLICIT, NULL, 0, "routersolicit" },
{ ND_ROUTER_ADVERT, NULL, 0, "routeradvert" },
{ ND_NEIGHBOR_SOLICIT, NULL, 0, "neighborsolicit" },
{ ND_NEIGHBOR_ADVERT, NULL, 0, "neighboradvert" },
{ ND_REDIRECT, NULL, 0, "redirect" },
{ ICMP6_ROUTER_RENUMBERING, icmprenumber6,
IST_SZ(icmprenumber6), "routerrenumber" },
{ ICMP6_WRUREQUEST, NULL, 0, "whoareyourequest" },
{ ICMP6_WRUREPLY, NULL, 0, "whoareyoureply" },
{ ICMP6_FQDN_QUERY, NULL, 0, "fqdnquery" },
{ ICMP6_FQDN_REPLY, NULL, 0, "fqdnreply" },
{ ICMP6_NI_QUERY, icmpnodeinfo6,
IST_SZ(icmpnodeinfo6), "nodeinforequest" },
{ ICMP6_NI_REPLY, NULL, 0, "nodeinforeply" },
{ MLD6_MTRACE_RESP, NULL, 0, "mtraceresponse" },
{ MLD6_MTRACE, NULL, 0, "mtracerequest" },
{ -2, NULL, 0, NULL }
};
static icmp_subtype_t *
find_icmpsubtype(int type, icmp_subtype_t *table, size_t tablesz)
{
icmp_subtype_t *ist;
if (tablesz < 2)
return (NULL);
if ((type < 0) || (type > table[tablesz - 2].ist_val))
return (NULL);
if (table[type].ist_val == type)
return (table + type);
for (ist = table; ist->ist_val != -2; ist++)
if (ist->ist_val == type)
return (ist);
return (NULL);
}
static icmp_type_t *
find_icmptype(int type, icmp_type_t *table, size_t tablesz)
{
icmp_type_t *it;
if (tablesz < 2)
return (NULL);
if ((type < 0) || (type > table[tablesz - 2].it_val))
return (NULL);
if (table[type].it_val == type)
return (table + type);
for (it = table; it->it_val != -2; it++)
if (it->it_val == type)
return (it);
return (NULL);
}
static void
handlehup(int sig)
{
signal(SIGHUP, handlehup);
donehup = 1;
}
static void
init_tabs(void)
{
struct protoent *p;
struct servent *s;
char *name, **tab;
int port, i;
if (protocols != NULL) {
for (i = 0; i < 256; i++)
if (protocols[i] != NULL) {
free(protocols[i]);
protocols[i] = NULL;
}
free(protocols);
protocols = NULL;
}
protocols = (char **)malloc(256 * sizeof(*protocols));
if (protocols != NULL) {
bzero((char *)protocols, 256 * sizeof(*protocols));
setprotoent(1);
while ((p = getprotoent()) != NULL)
if (p->p_proto >= 0 && p->p_proto <= 255 &&
p->p_name != NULL && protocols[p->p_proto] == NULL)
protocols[p->p_proto] = strdup(p->p_name);
endprotoent();
if (protocols[0])
free(protocols[0]);
protocols[0] = strdup("ip");
}
if (udp_ports != NULL) {
for (i = 0; i < 65536; i++)
if (udp_ports[i] != NULL) {
free(udp_ports[i]);
udp_ports[i] = NULL;
}
free(udp_ports);
udp_ports = NULL;
}
udp_ports = (char **)malloc(65536 * sizeof(*udp_ports));
if (udp_ports != NULL)
bzero((char *)udp_ports, 65536 * sizeof(*udp_ports));
if (tcp_ports != NULL) {
for (i = 0; i < 65536; i++)
if (tcp_ports[i] != NULL) {
free(tcp_ports[i]);
tcp_ports[i] = NULL;
}
free(tcp_ports);
tcp_ports = NULL;
}
tcp_ports = (char **)malloc(65536 * sizeof(*tcp_ports));
if (tcp_ports != NULL)
bzero((char *)tcp_ports, 65536 * sizeof(*tcp_ports));
setservent(1);
while ((s = getservent()) != NULL) {
if (s->s_proto == NULL)
continue;
else if (!strcmp(s->s_proto, "tcp")) {
port = ntohs(s->s_port);
name = s->s_name;
tab = tcp_ports;
} else if (!strcmp(s->s_proto, "udp")) {
port = ntohs(s->s_port);
name = s->s_name;
tab = udp_ports;
} else
continue;
if ((port < 0 || port > 65535) || (name == NULL))
continue;
if (tab != NULL)
tab[port] = strdup(name);
}
endservent();
}
static char *
getlocalproto(u_int p)
{
static char pnum[4];
char *s;
p &= 0xff;
s = protocols ? protocols[p] : NULL;
if (s == NULL) {
sprintf(pnum, "%u", p);
s = pnum;
}
return (s);
}
static int
read_log(int fd, int *lenp, char *buf, int bufsize)
{
int nr;
if (bufsize > IPFILTER_LOGSIZE)
bufsize = IPFILTER_LOGSIZE;
nr = read(fd, buf, bufsize);
if (!nr)
return (2);
if ((nr < 0) && (errno != EINTR))
return (-1);
*lenp = nr;
return (0);
}
char *
portlocalname(int res, char *proto, u_int port)
{
static char pname[8];
char *s;
port = ntohs(port);
port &= 0xffff;
sprintf(pname, "%u", port);
if (!res || (ipmonopts & IPMON_PORTNUM))
return (pname);
s = NULL;
if (!strcmp(proto, "tcp"))
s = tcp_ports[port];
else if (!strcmp(proto, "udp"))
s = udp_ports[port];
if (s == NULL)
s = pname;
return (s);
}
static char *
icmpname(u_int type, u_int code)
{
static char name[80];
icmp_subtype_t *ist;
icmp_type_t *it;
char *s;
s = NULL;
it = find_icmptype(type, icmptypes4, sizeof(icmptypes4) / sizeof(*it));
if (it != NULL)
s = it->it_name;
if (s == NULL)
sprintf(name, "icmptype(%d)/", type);
else
sprintf(name, "%s/", s);
ist = NULL;
if (it != NULL && it->it_subtable != NULL)
ist = find_icmpsubtype(code, it->it_subtable, it->it_stsize);
if (ist != NULL && ist->ist_name != NULL)
strcat(name, ist->ist_name);
else
sprintf(name + strlen(name), "%d", code);
return (name);
}
static char *
icmpname6(u_int type, u_int code)
{
static char name[80];
icmp_subtype_t *ist;
icmp_type_t *it;
char *s;
s = NULL;
it = find_icmptype(type, icmptypes6, sizeof(icmptypes6) / sizeof(*it));
if (it != NULL)
s = it->it_name;
if (s == NULL)
sprintf(name, "icmpv6type(%d)/", type);
else
sprintf(name, "%s/", s);
ist = NULL;
if (it != NULL && it->it_subtable != NULL)
ist = find_icmpsubtype(code, it->it_subtable, it->it_stsize);
if (ist != NULL && ist->ist_name != NULL)
strcat(name, ist->ist_name);
else
sprintf(name + strlen(name), "%d", code);
return (name);
}
void
dumphex(FILE *log, int dopts, char *buf, int len)
{
char hline[80];
int i, j, k;
u_char *s = (u_char *)buf, *t = (u_char *)hline;
if (buf == NULL || len == 0)
return;
*hline = '\0';
for (i = len, j = 0; i; i--, j++, s++) {
if (j && !(j & 0xf)) {
*t++ = '\n';
*t = '\0';
if ((dopts & IPMON_SYSLOG))
syslog(LOG_INFO, "%s", hline);
else if (log != NULL)
fputs(hline, log);
t = (u_char *)hline;
*t = '\0';
}
sprintf((char *)t, "%02x", *s & 0xff);
t += 2;
if (!((j + 1) & 0xf)) {
s -= 15;
sprintf((char *)t, " ");
t += 8;
for (k = 16; k; k--, s++)
*t++ = (isprint(*s) ? *s : '.');
s--;
}
if ((j + 1) & 0xf)
*t++ = ' ';
}
if (j & 0xf) {
for (k = 16 - (j & 0xf); k; k--) {
*t++ = ' ';
*t++ = ' ';
*t++ = ' ';
}
sprintf((char *)t, " ");
t += 7;
s -= j & 0xf;
for (k = j & 0xf; k; k--, s++)
*t++ = (isprint(*s) ? *s : '.');
*t++ = '\n';
*t = '\0';
}
if ((dopts & IPMON_SYSLOG) != 0)
syslog(LOG_INFO, "%s", hline);
else if (log != NULL) {
fputs(hline, log);
fflush(log);
}
}
static struct tm *
get_tm(time_t sec)
{
struct tm *tm;
time_t t;
t = sec;
tm = localtime(&t);
return (tm);
}
static void
print_natlog(config_t *conf, char *buf, int blen)
{
static u_32_t seqnum = 0;
int res, i, len, family;
struct natlog *nl;
struct tm *tm;
iplog_t *ipl;
char *proto;
int simple;
char *t;
t = line;
simple = 0;
ipl = (iplog_t *)buf;
if (ipl->ipl_seqnum != seqnum) {
if ((ipmonopts & IPMON_SYSLOG) != 0) {
syslog(LOG_WARNING,
"missed %u NAT log entries: %u %u",
ipl->ipl_seqnum - seqnum, seqnum,
ipl->ipl_seqnum);
} else {
(void) fprintf(conf->log,
"missed %u NAT log entries: %u %u\n",
ipl->ipl_seqnum - seqnum, seqnum,
ipl->ipl_seqnum);
}
}
seqnum = ipl->ipl_seqnum + ipl->ipl_count;
nl = (struct natlog *)((char *)ipl + sizeof(*ipl));
res = (ipmonopts & IPMON_RESOLVE) ? 1 : 0;
tm = get_tm(ipl->ipl_sec);
len = sizeof(line);
if (!(ipmonopts & IPMON_SYSLOG)) {
(void) strftime(t, len, "%d/%m/%Y ", tm);
i = strlen(t);
len -= i;
t += i;
}
(void) strftime(t, len, "%T", tm);
t += strlen(t);
sprintf(t, ".%-.6ld @%hd ", (long)ipl->ipl_usec, nl->nl_rule + 1);
t += strlen(t);
switch (nl->nl_action)
{
case NL_NEW :
strcpy(t, "NAT:NEW");
break;
case NL_FLUSH :
strcpy(t, "NAT:FLUSH");
break;
case NL_CLONE :
strcpy(t, "NAT:CLONE");
break;
case NL_EXPIRE :
strcpy(t, "NAT:EXPIRE");
break;
case NL_DESTROY :
strcpy(t, "NAT:DESTROY");
break;
case NL_PURGE :
strcpy(t, "NAT:PURGE");
break;
default :
sprintf(t, "NAT:Action(%d)", nl->nl_action);
break;
}
t += strlen(t);
switch (nl->nl_type)
{
case NAT_MAP :
strcpy(t, "-MAP ");
simple = 1;
break;
case NAT_REDIRECT :
strcpy(t, "-RDR ");
simple = 1;
break;
case NAT_BIMAP :
strcpy(t, "-BIMAP ");
simple = 1;
break;
case NAT_MAPBLK :
strcpy(t, "-MAPBLOCK ");
simple = 1;
break;
case NAT_REWRITE|NAT_MAP :
strcpy(t, "-RWR_MAP ");
break;
case NAT_REWRITE|NAT_REDIRECT :
strcpy(t, "-RWR_RDR ");
break;
case NAT_ENCAP|NAT_MAP :
strcpy(t, "-ENC_MAP ");
break;
case NAT_ENCAP|NAT_REDIRECT :
strcpy(t, "-ENC_RDR ");
break;
case NAT_DIVERTUDP|NAT_MAP :
strcpy(t, "-DIV_MAP ");
break;
case NAT_DIVERTUDP|NAT_REDIRECT :
strcpy(t, "-DIV_RDR ");
break;
default :
sprintf(t, "-Type(%d) ", nl->nl_type);
break;
}
t += strlen(t);
proto = getlocalproto(nl->nl_p[0]);
family = vtof(nl->nl_v[0]);
if (simple == 1) {
sprintf(t, "%s,%s <- -> ", hostname(family, nl->nl_osrcip.i6),
portlocalname(res, proto, (u_int)nl->nl_osrcport));
t += strlen(t);
sprintf(t, "%s,%s ", hostname(family, nl->nl_nsrcip.i6),
portlocalname(res, proto, (u_int)nl->nl_nsrcport));
t += strlen(t);
sprintf(t, "[%s,%s] ", hostname(family, nl->nl_odstip.i6),
portlocalname(res, proto, (u_int)nl->nl_odstport));
} else {
sprintf(t, "%s,%s ", hostname(family, nl->nl_osrcip.i6),
portlocalname(res, proto, (u_int)nl->nl_osrcport));
t += strlen(t);
sprintf(t, "%s,%s <- -> ", hostname(family, nl->nl_odstip.i6),
portlocalname(res, proto, (u_int)nl->nl_odstport));
t += strlen(t);
sprintf(t, "%s,%s ", hostname(family, nl->nl_nsrcip.i6),
portlocalname(res, proto, (u_int)nl->nl_nsrcport));
t += strlen(t);
sprintf(t, "%s,%s ", hostname(family, nl->nl_ndstip.i6),
portlocalname(res, proto, (u_int)nl->nl_ndstport));
}
t += strlen(t);
strcpy(t, getlocalproto(nl->nl_p[0]));
t += strlen(t);
if (nl->nl_action == NL_EXPIRE || nl->nl_action == NL_FLUSH) {
#ifdef USE_QUAD_T
# ifdef PRId64
sprintf(t, " Pkts %" PRId64 "/%" PRId64 " Bytes %" PRId64 "/%"
PRId64,
# else
sprintf(t, " Pkts %qd/%qd Bytes %qd/%qd",
# endif
#else
sprintf(t, " Pkts %ld/%ld Bytes %ld/%ld",
#endif
nl->nl_pkts[0], nl->nl_pkts[1],
nl->nl_bytes[0], nl->nl_bytes[1]);
t += strlen(t);
}
*t++ = '\n';
*t++ = '\0';
if (ipmonopts & IPMON_SYSLOG)
syslog(LOG_INFO, "%s", line);
else if (conf->log != NULL)
(void) fprintf(conf->log, "%s", line);
}
static void
print_statelog(config_t *conf, char *buf, int blen)
{
static u_32_t seqnum = 0;
int res, i, len, family;
struct ipslog *sl;
char *t, *proto;
struct tm *tm;
iplog_t *ipl;
t = line;
ipl = (iplog_t *)buf;
if (ipl->ipl_seqnum != seqnum) {
if ((ipmonopts & IPMON_SYSLOG) != 0) {
syslog(LOG_WARNING,
"missed %u state log entries: %u %u",
ipl->ipl_seqnum - seqnum, seqnum,
ipl->ipl_seqnum);
} else {
(void) fprintf(conf->log,
"missed %u state log entries: %u %u\n",
ipl->ipl_seqnum - seqnum, seqnum,
ipl->ipl_seqnum);
}
}
seqnum = ipl->ipl_seqnum + ipl->ipl_count;
sl = (struct ipslog *)((char *)ipl + sizeof(*ipl));
res = (ipmonopts & IPMON_RESOLVE) ? 1 : 0;
tm = get_tm(ipl->ipl_sec);
len = sizeof(line);
if (!(ipmonopts & IPMON_SYSLOG)) {
(void) strftime(t, len, "%d/%m/%Y ", tm);
i = strlen(t);
len -= i;
t += i;
}
(void) strftime(t, len, "%T", tm);
t += strlen(t);
sprintf(t, ".%-.6ld ", (long)ipl->ipl_usec);
t += strlen(t);
family = vtof(sl->isl_v);
switch (sl->isl_type)
{
case ISL_NEW :
strcpy(t, "STATE:NEW ");
break;
case ISL_CLONE :
strcpy(t, "STATE:CLONED ");
break;
case ISL_EXPIRE :
if ((sl->isl_p == IPPROTO_TCP) &&
(sl->isl_state[0] > IPF_TCPS_ESTABLISHED ||
sl->isl_state[1] > IPF_TCPS_ESTABLISHED))
strcpy(t, "STATE:CLOSE ");
else
strcpy(t, "STATE:EXPIRE ");
break;
case ISL_FLUSH :
strcpy(t, "STATE:FLUSH ");
break;
case ISL_INTERMEDIATE :
strcpy(t, "STATE:INTERMEDIATE ");
break;
case ISL_REMOVE :
strcpy(t, "STATE:REMOVE ");
break;
case ISL_KILLED :
strcpy(t, "STATE:KILLED ");
break;
case ISL_UNLOAD :
strcpy(t, "STATE:UNLOAD ");
break;
default :
sprintf(t, "Type: %d ", sl->isl_type);
break;
}
t += strlen(t);
proto = getlocalproto(sl->isl_p);
if (sl->isl_p == IPPROTO_TCP || sl->isl_p == IPPROTO_UDP) {
sprintf(t, "%s,%s -> ",
hostname(family, (u_32_t *)&sl->isl_src),
portlocalname(res, proto, (u_int)sl->isl_sport));
t += strlen(t);
sprintf(t, "%s,%s PR %s",
hostname(family, (u_32_t *)&sl->isl_dst),
portlocalname(res, proto, (u_int)sl->isl_dport), proto);
} else if (sl->isl_p == IPPROTO_ICMP) {
sprintf(t, "%s -> ", hostname(family, (u_32_t *)&sl->isl_src));
t += strlen(t);
sprintf(t, "%s PR icmp %d",
hostname(family, (u_32_t *)&sl->isl_dst),
sl->isl_itype);
} else if (sl->isl_p == IPPROTO_ICMPV6) {
sprintf(t, "%s -> ", hostname(family, (u_32_t *)&sl->isl_src));
t += strlen(t);
sprintf(t, "%s PR icmpv6 %d",
hostname(family, (u_32_t *)&sl->isl_dst),
sl->isl_itype);
} else {
sprintf(t, "%s -> ", hostname(family, (u_32_t *)&sl->isl_src));
t += strlen(t);
sprintf(t, "%s PR %s",
hostname(family, (u_32_t *)&sl->isl_dst), proto);
}
t += strlen(t);
if (sl->isl_tag != FR_NOLOGTAG) {
sprintf(t, " tag %u", sl->isl_tag);
t += strlen(t);
}
if (sl->isl_type != ISL_NEW) {
sprintf(t,
#ifdef USE_QUAD_T
#ifdef PRId64
" Forward: Pkts in %" PRId64 " Bytes in %" PRId64
" Pkts out %" PRId64 " Bytes out %" PRId64
" Backward: Pkts in %" PRId64 " Bytes in %" PRId64
" Pkts out %" PRId64 " Bytes out %" PRId64,
#else
" Forward: Pkts in %qd Bytes in %qd Pkts out %qd Bytes out %qd Backward: Pkts in %qd Bytes in %qd Pkts out %qd Bytes out %qd",
#endif /* PRId64 */
#else
" Forward: Pkts in %ld Bytes in %ld Pkts out %ld Bytes out %ld Backward: Pkts in %ld Bytes in %ld Pkts out %ld Bytes out %ld",
#endif
sl->isl_pkts[0], sl->isl_bytes[0],
sl->isl_pkts[1], sl->isl_bytes[1],
sl->isl_pkts[2], sl->isl_bytes[2],
sl->isl_pkts[3], sl->isl_bytes[3]);
t += strlen(t);
}
*t++ = '\n';
*t++ = '\0';
if (ipmonopts & IPMON_SYSLOG)
syslog(LOG_INFO, "%s", line);
else if (conf->log != NULL)
(void) fprintf(conf->log, "%s", line);
}
static void
print_log(config_t *conf, logsource_t *log, char *buf, int blen)
{
char *bp, *bpo;
iplog_t *ipl;
int psize;
bp = NULL;
bpo = NULL;
while (blen > 0) {
ipl = (iplog_t *)buf;
if ((u_long)ipl & (sizeof(long)-1)) {
if (bp)
bpo = bp;
bp = (char *)malloc(blen);
bcopy((char *)ipl, bp, blen);
if (bpo) {
free(bpo);
bpo = NULL;
}
buf = bp;
continue;
}
psize = ipl->ipl_dsize;
if (psize > blen)
break;
if (conf->blog != NULL) {
fwrite(buf, psize, 1, conf->blog);
fflush(conf->blog);
}
if (log->logtype == IPL_LOGIPF) {
if (ipl->ipl_magic == IPL_MAGIC)
print_ipflog(conf, buf, psize);
} else if (log->logtype == IPL_LOGNAT) {
if (ipl->ipl_magic == IPL_MAGIC_NAT)
print_natlog(conf, buf, psize);
} else if (log->logtype == IPL_LOGSTATE) {
if (ipl->ipl_magic == IPL_MAGIC_STATE)
print_statelog(conf, buf, psize);
}
blen -= psize;
buf += psize;
}
if (bp)
free(bp);
return;
}
static void
print_ipflog(config_t *conf, char *buf, int blen)
{
static u_32_t seqnum = 0;
int i, f, lvl, res, len, off, plen, ipoff, defaction;
struct icmp *icmp;
struct icmp *ic;
char *t, *proto;
ip_t *ipc, *ip;
struct tm *tm;
u_32_t *s, *d;
u_short hl, p;
ipflog_t *ipf;
iplog_t *ipl;
tcphdr_t *tp;
#ifdef USE_INET6
struct ip6_ext *ehp;
u_short ehl;
ip6_t *ip6;
int go;
#endif
ipl = (iplog_t *)buf;
if (ipl->ipl_seqnum != seqnum) {
if ((ipmonopts & IPMON_SYSLOG) != 0) {
syslog(LOG_WARNING,
"missed %u ipf log entries: %u %u",
ipl->ipl_seqnum - seqnum, seqnum,
ipl->ipl_seqnum);
} else {
(void) fprintf(conf->log,
"missed %u ipf log entries: %u %u\n",
ipl->ipl_seqnum - seqnum, seqnum,
ipl->ipl_seqnum);
}
}
seqnum = ipl->ipl_seqnum + ipl->ipl_count;
ipf = (ipflog_t *)((char *)buf + sizeof(*ipl));
ip = (ip_t *)((char *)ipf + sizeof(*ipf));
f = ipf->fl_family;
res = (ipmonopts & IPMON_RESOLVE) ? 1 : 0;
t = line;
*t = '\0';
tm = get_tm(ipl->ipl_sec);
len = sizeof(line);
if (!(ipmonopts & IPMON_SYSLOG)) {
(void) strftime(t, len, "%d/%m/%Y ", tm);
i = strlen(t);
len -= i;
t += i;
}
(void) strftime(t, len, "%T", tm);
t += strlen(t);
sprintf(t, ".%-.6ld ", (long)ipl->ipl_usec);
t += strlen(t);
if (ipl->ipl_count > 1) {
sprintf(t, "%dx ", ipl->ipl_count);
t += strlen(t);
}
{
char ifname[sizeof(ipf->fl_ifname) + 1];
strncpy(ifname, ipf->fl_ifname, sizeof(ipf->fl_ifname));
ifname[sizeof(ipf->fl_ifname)] = '\0';
sprintf(t, "%s", ifname);
t += strlen(t);
# if SOLARIS
if (ISALPHA(*(t - 1))) {
sprintf(t, "%d", ipf->fl_unit);
t += strlen(t);
}
# endif
}
if ((ipf->fl_group[0] == (char)~0) && (ipf->fl_group[1] == '\0'))
strcat(t, " @-1:");
else if (ipf->fl_group[0] == '\0')
(void) strcpy(t, " @0:");
else
sprintf(t, " @%s:", ipf->fl_group);
t += strlen(t);
if (ipf->fl_rule == 0xffffffff)
strcat(t, "-1 ");
else
sprintf(t, "%u ", ipf->fl_rule + 1);
t += strlen(t);
lvl = LOG_NOTICE;
if (ipf->fl_lflags & FI_SHORT) {
*t++ = 'S';
lvl = LOG_ERR;
}
if (FR_ISPASS(ipf->fl_flags)) {
if (ipf->fl_flags & FR_LOGP)
*t++ = 'p';
else
*t++ = 'P';
} else if (FR_ISBLOCK(ipf->fl_flags)) {
if (ipf->fl_flags & FR_LOGB)
*t++ = 'b';
else
*t++ = 'B';
lvl = LOG_WARNING;
} else if ((ipf->fl_flags & FR_LOGMASK) == FR_LOG) {
*t++ = 'L';
lvl = LOG_INFO;
} else if (ipf->fl_flags & FF_LOGNOMATCH) {
*t++ = 'n';
} else {
*t++ = '?';
lvl = LOG_EMERG;
}
if (ipf->fl_loglevel != 0xffff)
lvl = ipf->fl_loglevel;
*t++ = ' ';
*t = '\0';
if (f == AF_INET) {
hl = IP_HL(ip) << 2;
ipoff = ntohs(ip->ip_off);
off = ipoff & IP_OFFMASK;
p = (u_short)ip->ip_p;
s = (u_32_t *)&ip->ip_src;
d = (u_32_t *)&ip->ip_dst;
plen = ntohs(ip->ip_len);
} else
#ifdef USE_INET6
if (f == AF_INET6) {
off = 0;
ipoff = 0;
hl = sizeof(ip6_t);
ip6 = (ip6_t *)ip;
p = (u_short)ip6->ip6_nxt;
s = (u_32_t *)&ip6->ip6_src;
d = (u_32_t *)&ip6->ip6_dst;
plen = hl + ntohs(ip6->ip6_plen);
go = 1;
ehp = (struct ip6_ext *)((char *)ip6 + hl);
while (go == 1) {
switch (p)
{
case IPPROTO_HOPOPTS :
case IPPROTO_MOBILITY :
case IPPROTO_DSTOPTS :
case IPPROTO_ROUTING :
case IPPROTO_AH :
p = ehp->ip6e_nxt;
ehl = 8 + (ehp->ip6e_len << 3);
hl += ehl;
ehp = (struct ip6_ext *)((char *)ehp + ehl);
break;
case IPPROTO_FRAGMENT :
hl += sizeof(struct ip6_frag);
/* FALLTHROUGH */
default :
go = 0;
break;
}
}
} else
#endif
{
goto printipflog;
}
proto = getlocalproto(p);
if ((p == IPPROTO_TCP || p == IPPROTO_UDP) && !off) {
tp = (tcphdr_t *)((char *)ip + hl);
if (!(ipf->fl_lflags & FI_SHORT)) {
sprintf(t, "%s,%s -> ", hostname(f, s),
portlocalname(res, proto, (u_int)tp->th_sport));
t += strlen(t);
sprintf(t, "%s,%s PR %s len %hu %hu",
hostname(f, d),
portlocalname(res, proto, (u_int)tp->th_dport),
proto, hl, plen);
t += strlen(t);
if (p == IPPROTO_TCP) {
*t++ = ' ';
*t++ = '-';
for (i = 0; tcpfl[i].value; i++)
if (__tcp_get_flags(tp) & tcpfl[i].value)
*t++ = tcpfl[i].flag;
if (ipmonopts & IPMON_VERBOSE) {
sprintf(t, " %lu %lu %hu",
(u_long)(ntohl(tp->th_seq)),
(u_long)(ntohl(tp->th_ack)),
ntohs(tp->th_win));
t += strlen(t);
}
}
*t = '\0';
} else {
sprintf(t, "%s -> ", hostname(f, s));
t += strlen(t);
sprintf(t, "%s PR %s len %hu %hu",
hostname(f, d), proto, hl, plen);
}
#if defined(AF_INET6) && defined(IPPROTO_ICMPV6)
} else if ((p == IPPROTO_ICMPV6) && !off && (f == AF_INET6)) {
ic = (struct icmp *)((char *)ip + hl);
sprintf(t, "%s -> ", hostname(f, s));
t += strlen(t);
sprintf(t, "%s PR icmpv6 len %hu %hu icmpv6 %s",
hostname(f, d), hl, plen,
icmpname6(ic->icmp_type, ic->icmp_code));
#endif
} else if ((p == IPPROTO_ICMP) && !off && (f == AF_INET)) {
ic = (struct icmp *)((char *)ip + hl);
sprintf(t, "%s -> ", hostname(f, s));
t += strlen(t);
sprintf(t, "%s PR icmp len %hu %hu icmp %s",
hostname(f, d), hl, plen,
icmpname(ic->icmp_type, ic->icmp_code));
if (ic->icmp_type == ICMP_UNREACH ||
ic->icmp_type == ICMP_SOURCEQUENCH ||
ic->icmp_type == ICMP_PARAMPROB ||
ic->icmp_type == ICMP_REDIRECT ||
ic->icmp_type == ICMP_TIMXCEED) {
ipc = &ic->icmp_ip;
i = ntohs(ipc->ip_len);
/*
* XXX - try to guess endian of ip_len in ICMP
* returned data.
*/
if (i > 1500)
i = ipc->ip_len;
ipoff = ntohs(ipc->ip_off);
proto = getlocalproto(ipc->ip_p);
if (!(ipoff & IP_OFFMASK) &&
((ipc->ip_p == IPPROTO_TCP) ||
(ipc->ip_p == IPPROTO_UDP))) {
tp = (tcphdr_t *)((char *)ipc + hl);
t += strlen(t);
sprintf(t, " for %s,%s -",
HOSTNAMEV4(ipc->ip_src),
portlocalname(res, proto,
(u_int)tp->th_sport));
t += strlen(t);
sprintf(t, " %s,%s PR %s len %hu %hu",
HOSTNAMEV4(ipc->ip_dst),
portlocalname(res, proto,
(u_int)tp->th_dport),
proto, IP_HL(ipc) << 2, i);
} else if (!(ipoff & IP_OFFMASK) &&
(ipc->ip_p == IPPROTO_ICMP)) {
icmp = (icmphdr_t *)((char *)ipc + hl);
t += strlen(t);
sprintf(t, " for %s -",
HOSTNAMEV4(ipc->ip_src));
t += strlen(t);
sprintf(t,
" %s PR icmp len %hu %hu icmp %d/%d",
HOSTNAMEV4(ipc->ip_dst),
IP_HL(ipc) << 2, i,
icmp->icmp_type, icmp->icmp_code);
} else {
t += strlen(t);
sprintf(t, " for %s -",
HOSTNAMEV4(ipc->ip_src));
t += strlen(t);
sprintf(t, " %s PR %s len %hu (%hu)",
HOSTNAMEV4(ipc->ip_dst), proto,
IP_HL(ipc) << 2, i);
t += strlen(t);
if (ipoff & IP_OFFMASK) {
sprintf(t, "(frag %d:%hu@%hu%s%s)",
ntohs(ipc->ip_id),
i - (IP_HL(ipc) << 2),
(ipoff & IP_OFFMASK) << 3,
ipoff & IP_MF ? "+" : "",
ipoff & IP_DF ? "-" : "");
}
}
}
} else {
sprintf(t, "%s -> ", hostname(f, s));
t += strlen(t);
sprintf(t, "%s PR %s len %hu (%hu)",
hostname(f, d), proto, hl, plen);
t += strlen(t);
if (off & IP_OFFMASK)
sprintf(t, " (frag %d:%hu@%hu%s%s)",
ntohs(ip->ip_id),
plen - hl, (off & IP_OFFMASK) << 3,
ipoff & IP_MF ? "+" : "",
ipoff & IP_DF ? "-" : "");
}
t += strlen(t);
printipflog:
if (ipf->fl_flags & FR_KEEPSTATE) {
(void) strcpy(t, " K-S");
t += strlen(t);
}
if (ipf->fl_flags & FR_KEEPFRAG) {
(void) strcpy(t, " K-F");
t += strlen(t);
}
if (ipf->fl_dir == 0)
strcpy(t, " IN");
else if (ipf->fl_dir == 1)
strcpy(t, " OUT");
t += strlen(t);
if (ipf->fl_logtag != 0) {
sprintf(t, " log-tag %d", ipf->fl_logtag);
t += strlen(t);
}
if (ipf->fl_nattag.ipt_num[0] != 0) {
strcpy(t, " nat-tag ");
t += strlen(t);
strncpy(t, ipf->fl_nattag.ipt_tag, sizeof(ipf->fl_nattag));
t += strlen(t);
}
if ((ipf->fl_lflags & FI_LOWTTL) != 0) {
strcpy(t, " low-ttl");
t += 8;
}
if ((ipf->fl_lflags & FI_OOW) != 0) {
strcpy(t, " OOW");
t += 4;
}
if ((ipf->fl_lflags & FI_BAD) != 0) {
strcpy(t, " bad");
t += 4;
}
if ((ipf->fl_lflags & FI_NATED) != 0) {
strcpy(t, " NAT");
t += 4;
}
if ((ipf->fl_lflags & FI_BADNAT) != 0) {
strcpy(t, " bad-NAT");
t += 8;
}
if ((ipf->fl_lflags & FI_BADSRC) != 0) {
strcpy(t, " bad-src");
t += 8;
}
if ((ipf->fl_lflags & FI_MULTICAST) != 0) {
strcpy(t, " multicast");
t += 10;
}
if ((ipf->fl_lflags & FI_BROADCAST) != 0) {
strcpy(t, " broadcast");
t += 10;
}
if ((ipf->fl_lflags & (FI_MULTICAST|FI_BROADCAST|FI_MBCAST)) ==
FI_MBCAST) {
strcpy(t, " mbcast");
t += 7;
}
if (ipf->fl_breason != 0) {
strcpy(t, " reason:");
t += 8;
strcpy(t, reasons[ipf->fl_breason]);
t += strlen(reasons[ipf->fl_breason]);
}
*t++ = '\n';
*t++ = '\0';
defaction = 0;
if (conf->cfile != NULL)
defaction = check_action(buf, line, ipmonopts, lvl);
if (defaction == 0) {
if (ipmonopts & IPMON_SYSLOG) {
syslog(lvl, "%s", line);
} else if (conf->log != NULL) {
(void) fprintf(conf->log, "%s", line);
}
if (ipmonopts & IPMON_HEXHDR) {
dumphex(conf->log, ipmonopts, buf,
sizeof(iplog_t) + sizeof(*ipf));
}
if (ipmonopts & IPMON_HEXBODY) {
dumphex(conf->log, ipmonopts, (char *)ip,
ipf->fl_plen + ipf->fl_hlen);
} else if ((ipmonopts & IPMON_LOGBODY) &&
(ipf->fl_flags & FR_LOGBODY)) {
dumphex(conf->log, ipmonopts, (char *)ip + ipf->fl_hlen,
ipf->fl_plen);
}
}
}
static void
usage(char *prog)
{
fprintf(stderr, "Usage: %s [ -abDFhnpstvxX ] [ -B <binary-logfile> ] [ -C <config-file> ]\n"
"\t[ -f <device> ] [ -L <facility> ] [ -N <device> ]\n"
"\t[ -o [NSI] ] [ -O [NSI] ] [ -P <pidfile> ] [ -S <device> ]\n"
"\t[ <filename> ]\n", prog);
exit(1);
}
static void
write_pid(char *file)
{
FILE *fp = NULL;
int fd;
if ((fd = open(file, O_CREAT|O_TRUNC|O_WRONLY, 0644)) >= 0) {
fp = fdopen(fd, "w");
if (fp == NULL) {
close(fd);
fprintf(stderr,
"unable to open/create pid file: %s\n", file);
return;
}
fprintf(fp, "%d", getpid());
fclose(fp);
}
}
static void
flushlogs(char *file, FILE *log)
{
int fd, flushed = 0;
if ((fd = open(file, O_RDWR)) == -1) {
(void) fprintf(stderr, "%s: open: %s\n",
file, STRERROR(errno));
exit(1);
}
if (ioctl(fd, SIOCIPFFB, &flushed) == 0) {
printf("%d bytes flushed from log buffer\n",
flushed);
fflush(stdout);
} else
ipferror(fd, "SIOCIPFFB");
(void) close(fd);
if (flushed) {
if (ipmonopts & IPMON_SYSLOG) {
syslog(LOG_INFO, "%d bytes flushed from log\n",
flushed);
} else if ((log != stdout) && (log != NULL)) {
fprintf(log, "%d bytes flushed from log\n", flushed);
}
}
}
static void
logopts(int turnon, char *options)
{
int flags = 0;
char *s;
for (s = options; *s; s++)
{
switch (*s)
{
case 'N' :
flags |= IPMON_NAT;
break;
case 'S' :
flags |= IPMON_STATE;
break;
case 'I' :
flags |= IPMON_FILTER;
break;
default :
fprintf(stderr, "Unknown log option %c\n", *s);
exit(1);
}
}
if (turnon)
ipmonopts |= flags;
else
ipmonopts &= ~(flags);
}
static void
initconfig(config_t *conf)
{
int i;
memset(conf, 0, sizeof(*conf));
conf->log = stdout;
conf->maxfd = -1;
for (i = 0; i < 3; i++) {
conf->logsrc[i].fd = -1;
conf->logsrc[i].logtype = -1;
conf->logsrc[i].regular = -1;
}
conf->logsrc[0].file = IPL_NAME;
conf->logsrc[1].file = IPNAT_NAME;
conf->logsrc[2].file = IPSTATE_NAME;
add_doing(&executesaver);
add_doing(&snmpv1saver);
add_doing(&snmpv2saver);
add_doing(&syslogsaver);
add_doing(&filesaver);
add_doing(&nothingsaver);
}
int
main(int argc, char *argv[])
{
int doread, c, make_daemon = 0;
char *prog;
config_t config;
prog = strrchr(argv[0], '/');
if (prog == NULL)
prog = argv[0];
else
prog++;
initconfig(&config);
while ((c = getopt(argc, argv,
"?abB:C:Df:FhL:nN:o:O:pP:sS:tvxX")) != -1)
switch (c)
{
case 'a' :
ipmonopts |= IPMON_LOGALL;
config.logsrc[0].logtype = IPL_LOGIPF;
config.logsrc[1].logtype = IPL_LOGNAT;
config.logsrc[2].logtype = IPL_LOGSTATE;
break;
case 'b' :
ipmonopts |= IPMON_LOGBODY;
break;
case 'B' :
config.bfile = optarg;
config.blog = fopen(optarg, "a");
break;
case 'C' :
config.cfile = optarg;
break;
case 'D' :
make_daemon = 1;
break;
case 'f' : case 'I' :
ipmonopts |= IPMON_FILTER;
config.logsrc[0].logtype = IPL_LOGIPF;
config.logsrc[0].file = optarg;
break;
case 'F' :
flushlogs(config.logsrc[0].file, config.log);
flushlogs(config.logsrc[1].file, config.log);
flushlogs(config.logsrc[2].file, config.log);
break;
case 'L' :
logfac = fac_findname(optarg);
if (logfac == -1) {
fprintf(stderr,
"Unknown syslog facility '%s'\n",
optarg);
exit(1);
}
break;
case 'n' :
ipmonopts |= IPMON_RESOLVE;
opts &= ~OPT_NORESOLVE;
break;
case 'N' :
ipmonopts |= IPMON_NAT;
config.logsrc[1].logtype = IPL_LOGNAT;
config.logsrc[1].file = optarg;
break;
case 'o' : case 'O' :
logopts(c == 'o', optarg);
if (ipmonopts & IPMON_FILTER)
config.logsrc[0].logtype = IPL_LOGIPF;
if (ipmonopts & IPMON_NAT)
config.logsrc[1].logtype = IPL_LOGNAT;
if (ipmonopts & IPMON_STATE)
config.logsrc[2].logtype = IPL_LOGSTATE;
break;
case 'p' :
ipmonopts |= IPMON_PORTNUM;
break;
case 'P' :
pidfile = optarg;
break;
case 's' :
ipmonopts |= IPMON_SYSLOG;
config.log = NULL;
break;
case 'S' :
ipmonopts |= IPMON_STATE;
config.logsrc[2].logtype = IPL_LOGSTATE;
config.logsrc[2].file = optarg;
break;
case 't' :
ipmonopts |= IPMON_TAIL;
break;
case 'v' :
ipmonopts |= IPMON_VERBOSE;
break;
case 'x' :
ipmonopts |= IPMON_HEXBODY;
break;
case 'X' :
ipmonopts |= IPMON_HEXHDR;
break;
default :
case 'h' :
case '?' :
usage(argv[0]);
}
if (ipmonopts & IPMON_SYSLOG)
openlog(prog, LOG_NDELAY|LOG_PID, logfac);
init_tabs();
if (config.cfile)
if (load_config(config.cfile) == -1) {
unload_config();
exit(1);
}
/*
* Default action is to only open the filter log file.
*/
if ((config.logsrc[0].logtype == -1) &&
(config.logsrc[0].logtype == -1) &&
(config.logsrc[0].logtype == -1))
config.logsrc[0].logtype = IPL_LOGIPF;
openlogs(&config);
if (!(ipmonopts & IPMON_SYSLOG)) {
config.file = argv[optind];
config.log = config.file ? fopen(config.file, "a") : stdout;
if (config.log == NULL) {
(void) fprintf(stderr, "%s: fopen: %s\n",
argv[optind], STRERROR(errno));
exit(1);
/* NOTREACHED */
}
setvbuf(config.log, NULL, _IONBF, 0);
} else {
config.log = NULL;
}
if (make_daemon &&
((config.log != stdout) || (ipmonopts & IPMON_SYSLOG))) {
#ifdef BSD
daemon(0, !(ipmonopts & IPMON_SYSLOG));
#else
int pid;
switch (fork())
{
case -1 :
(void) fprintf(stderr, "%s: fork() failed: %s\n",
argv[0], STRERROR(errno));
exit(1);
/* NOTREACHED */
case 0 :
break;
default :
exit(0);
}
setsid();
if ((ipmonopts & IPMON_SYSLOG))
close(2);
#endif /* !BSD */
close(0);
close(1);
write_pid(pidfile);
}
signal(SIGHUP, handlehup);
for (doread = 1; doread; )
doread = read_loginfo(&config);
unload_config();
return (0);
/* NOTREACHED */
}
static void
openlogs(config_t *conf)
{
logsource_t *l;
struct stat sb;
int i;
for (i = 0; i < 3; i++) {
l = &conf->logsrc[i];
if (l->logtype == -1)
continue;
if (!strcmp(l->file, "-"))
l->fd = 0;
else {
if ((l->fd= open(l->file, O_RDONLY)) == -1) {
(void) fprintf(stderr,
"%s: open: %s\n", l->file,
STRERROR(errno));
exit(1);
/* NOTREACHED */
}
if (fstat(l->fd, &sb) == -1) {
(void) fprintf(stderr, "%d: fstat: %s\n",
l->fd, STRERROR(errno));
exit(1);
/* NOTREACHED */
}
l->regular = !S_ISCHR(sb.st_mode);
if (l->regular)
l->size = sb.st_size;
FD_SET(l->fd, &conf->fdmr);
if (l->fd > conf->maxfd)
conf->maxfd = l->fd;
}
}
}
static int
read_loginfo(config_t *conf)
{
iplog_t buf[DEFAULT_IPFLOGSIZE/sizeof(iplog_t)+1];
int n, tr, nr, i;
logsource_t *l;
fd_set fdr;
fdr = conf->fdmr;
n = select(conf->maxfd + 1, &fdr, NULL, NULL, NULL);
if (n == 0)
return (1);
if (n == -1) {
if (errno == EINTR)
return (1);
return (-1);
}
for (i = 0, nr = 0; i < 3; i++) {
l = &conf->logsrc[i];
if ((l->logtype == -1) || !FD_ISSET(l->fd, &fdr))
continue;
tr = 0;
if (l->regular) {
tr = (lseek(l->fd, 0, SEEK_CUR) < l->size);
if (!tr && !(ipmonopts & IPMON_TAIL))
return (0);
}
n = 0;
tr = read_log(l->fd, &n, (char *)buf, sizeof(buf));
if (donehup) {
if (conf->file != NULL) {
if (conf->log != NULL) {
fclose(conf->log);
conf->log = NULL;
}
conf->log = fopen(conf->file, "a");
}
if (conf->bfile != NULL) {
if (conf->blog != NULL) {
fclose(conf->blog);
conf->blog = NULL;
}
conf->blog = fopen(conf->bfile, "a");
}
init_tabs();
if (conf->cfile != NULL)
load_config(conf->cfile);
donehup = 0;
}
switch (tr)
{
case -1 :
if (ipmonopts & IPMON_SYSLOG)
syslog(LOG_CRIT, "read: %m\n");
else {
ipferror(l->fd, "read");
}
return (0);
case 1 :
if (ipmonopts & IPMON_SYSLOG)
syslog(LOG_CRIT, "aborting logging\n");
else if (conf->log != NULL)
fprintf(conf->log, "aborting logging\n");
return (0);
case 2 :
break;
case 0 :
nr += tr;
if (n > 0) {
print_log(conf, l, (char *)buf, n);
if (!(ipmonopts & IPMON_SYSLOG))
fflush(conf->log);
}
break;
}
}
if (!nr && (ipmonopts & IPMON_TAIL))
sleep(1);
return (1);
}