41d4822677
silly differences between the NetBSD copy of the code and the vendor branch, keeping only those which are necessary. Of those differences that currently exist, several "portability to NetBSD" issues, which will be fed back to the ipfilter author.
904 lines
21 KiB
C
904 lines
21 KiB
C
/* $NetBSD: fil.c,v 1.9 1997/05/28 00:17:11 thorpej Exp $ */
|
|
|
|
/*
|
|
* (C)opyright 1993-1996 by Darren Reed.
|
|
*
|
|
* Redistribution and use in source and binary forms are permitted
|
|
* provided that this notice is preserved and due credit is given
|
|
* to the original author and the contributors.
|
|
*/
|
|
#if !defined(lint) && defined(LIBC_SCCS)
|
|
static char sccsid[] = "@(#)fil.c 1.36 6/5/96 (C) 1993-1996 Darren Reed";
|
|
static char rcsid[] = "Id: fil.c,v 2.0.2.13 1997/05/24 07:33:37 darrenr Exp ";
|
|
#endif
|
|
|
|
#include <sys/errno.h>
|
|
#include <sys/types.h>
|
|
#include <sys/param.h>
|
|
#include <sys/time.h>
|
|
#include <sys/file.h>
|
|
#include <sys/ioctl.h>
|
|
#if defined(_KERNEL) || defined(KERNEL)
|
|
# include <sys/systm.h>
|
|
#else
|
|
# include <stdio.h>
|
|
# include <string.h>
|
|
#endif
|
|
#include <sys/uio.h>
|
|
#if !defined(__SVR4) && !defined(__svr4__)
|
|
# include <sys/mbuf.h>
|
|
#else
|
|
# include <sys/byteorder.h>
|
|
# include <sys/dditypes.h>
|
|
# include <sys/stream.h>
|
|
#endif
|
|
#include <sys/protosw.h>
|
|
#include <sys/socket.h>
|
|
#include <net/if.h>
|
|
#ifdef sun
|
|
# include <net/af.h>
|
|
#endif
|
|
#include <net/route.h>
|
|
#include <netinet/in.h>
|
|
#include <netinet/in_systm.h>
|
|
#include <netinet/ip.h>
|
|
#include <netinet/ip_var.h>
|
|
#include <netinet/tcp.h>
|
|
#include <netinet/udp.h>
|
|
#include <netinet/tcpip.h>
|
|
#include <netinet/ip_icmp.h>
|
|
#include "netinet/ip_compat.h"
|
|
#include "netinet/ip_fil.h"
|
|
#include "netinet/ip_proxy.h"
|
|
#include "netinet/ip_nat.h"
|
|
#include "netinet/ip_frag.h"
|
|
#include "netinet/ip_state.h"
|
|
#ifndef MIN
|
|
#define MIN(a,b) (((a)<(b))?(a):(b))
|
|
#endif
|
|
|
|
#ifndef _KERNEL
|
|
# include "ipf.h"
|
|
# include "ipt.h"
|
|
extern int opts;
|
|
|
|
# define FR_IFVERBOSE(ex,second,verb_pr) if (ex) { verbose verb_pr; \
|
|
second; }
|
|
# define FR_IFDEBUG(ex,second,verb_pr) if (ex) { debug verb_pr; \
|
|
second; }
|
|
# define FR_VERBOSE(verb_pr) verbose verb_pr
|
|
# define FR_DEBUG(verb_pr) debug verb_pr
|
|
# define FR_SCANLIST(p, ip, fi, m) fr_scanlist(p, ip, fi, m)
|
|
# define SEND_RESET(ip, qif, q, if) send_reset(ip, if)
|
|
# define IPLLOG(a, c, d, e) ipllog()
|
|
# if SOLARIS
|
|
# define ICMP_ERROR(b, ip, t, c, if, src) icmp_error(ip)
|
|
# else
|
|
# define ICMP_ERROR(b, ip, t, c, if, src) icmp_error(b, ip, if)
|
|
# endif
|
|
|
|
#else /* #ifndef _KERNEL */
|
|
# define FR_IFVERBOSE(ex,second,verb_pr) ;
|
|
# define FR_IFDEBUG(ex,second,verb_pr) ;
|
|
# define FR_VERBOSE(verb_pr)
|
|
# define FR_DEBUG(verb_pr)
|
|
# define FR_SCANLIST(p, ip, fi, m) fr_scanlist(p, ip, fi, m)
|
|
# define IPLLOG(a, c, d, e) ipllog(a, IPL_LOGIPF, c, d, e)
|
|
# if SOLARIS
|
|
extern kmutex_t ipf_mutex;
|
|
# define SEND_RESET(ip, qif, q, if) send_reset(ip, qif, q)
|
|
# define ICMP_ERROR(b, ip, t, c, if, src) \
|
|
icmp_error(b, ip, t, c, if, src)
|
|
# else
|
|
# define FR_SCANLIST(p, ip, fi, m) fr_scanlist(p, ip, fi, m)
|
|
# define SEND_RESET(ip, qif, q, if) send_reset((struct tcpiphdr *)ip)
|
|
# if BSD < 199103
|
|
# define ICMP_ERROR(b, ip, t, c, if, src) \
|
|
icmp_error(mtod(b, ip_t *), t, c, if, src)
|
|
# else
|
|
# define ICMP_ERROR(b, ip, t, c, if, src) \
|
|
icmp_error(b, t, c, (src).s_addr, if)
|
|
# endif
|
|
# endif
|
|
#endif
|
|
|
|
|
|
struct filterstats frstats[2] = {{0,0,0,0,0},{0,0,0,0,0}};
|
|
struct frentry *ipfilter[2][2] = { { NULL, NULL }, { NULL, NULL } },
|
|
*ipacct[2][2] = { { NULL, NULL }, { NULL, NULL } };
|
|
int fr_flags = IPF_LOGGING, fr_active = 0;
|
|
int fr_pass = (IPF_DEFAULT_PASS|FR_NOMATCH);
|
|
|
|
fr_info_t frcache[2];
|
|
|
|
static void fr_makefrip __P((int, ip_t *, fr_info_t *));
|
|
static int fr_tcpudpchk __P((frentry_t *, fr_info_t *));
|
|
static int fr_scanlist __P((int, ip_t *, fr_info_t *, void *));
|
|
|
|
|
|
/*
|
|
* bit values for identifying presence of individual IP options
|
|
*/
|
|
struct optlist ipopts[20] = {
|
|
{ IPOPT_NOP, 0x000001 },
|
|
{ IPOPT_RR, 0x000002 },
|
|
{ IPOPT_ZSU, 0x000004 },
|
|
{ IPOPT_MTUP, 0x000008 },
|
|
{ IPOPT_MTUR, 0x000010 },
|
|
{ IPOPT_ENCODE, 0x000020 },
|
|
{ IPOPT_TS, 0x000040 },
|
|
{ IPOPT_TR, 0x000080 },
|
|
{ IPOPT_SECURITY, 0x000100 },
|
|
{ IPOPT_LSRR, 0x000200 },
|
|
{ IPOPT_E_SEC, 0x000400 },
|
|
{ IPOPT_CIPSO, 0x000800 },
|
|
{ IPOPT_SATID, 0x001000 },
|
|
{ IPOPT_SSRR, 0x002000 },
|
|
{ IPOPT_ADDEXT, 0x004000 },
|
|
{ IPOPT_VISA, 0x008000 },
|
|
{ IPOPT_IMITD, 0x010000 },
|
|
{ IPOPT_EIP, 0x020000 },
|
|
{ IPOPT_FINN, 0x040000 },
|
|
{ 0, 0x000000 }
|
|
};
|
|
|
|
/*
|
|
* bit values for identifying presence of individual IP security options
|
|
*/
|
|
struct optlist secopt[8] = {
|
|
{ IPSO_CLASS_RES4, 0x01 },
|
|
{ IPSO_CLASS_TOPS, 0x02 },
|
|
{ IPSO_CLASS_SECR, 0x04 },
|
|
{ IPSO_CLASS_RES3, 0x08 },
|
|
{ IPSO_CLASS_CONF, 0x10 },
|
|
{ IPSO_CLASS_UNCL, 0x20 },
|
|
{ IPSO_CLASS_RES2, 0x40 },
|
|
{ IPSO_CLASS_RES1, 0x80 }
|
|
};
|
|
|
|
|
|
/*
|
|
* compact the IP header into a structure which contains just the info.
|
|
* which is useful for comparing IP headers with.
|
|
*/
|
|
static void fr_makefrip(hlen, ip, fin)
|
|
int hlen;
|
|
ip_t *ip;
|
|
fr_info_t *fin;
|
|
{
|
|
struct optlist *op;
|
|
tcphdr_t *tcp;
|
|
fr_ip_t *fi = &fin->fin_fi;
|
|
u_short optmsk = 0, secmsk = 0, auth = 0;
|
|
int i, mv, ol, off;
|
|
u_char *s, opt;
|
|
|
|
fin->fin_fr = NULL;
|
|
fin->fin_tcpf = 0;
|
|
fin->fin_data[0] = 0;
|
|
fin->fin_data[1] = 0;
|
|
fin->fin_rule = -1;
|
|
#ifdef _KERNEL
|
|
fin->fin_icode = ipl_unreach;
|
|
#endif
|
|
fi->fi_v = ip->ip_v;
|
|
fi->fi_tos = ip->ip_tos;
|
|
fin->fin_hlen = hlen;
|
|
fin->fin_dlen = ip->ip_len - hlen;
|
|
tcp = (tcphdr_t *)((char *)ip + hlen);
|
|
fin->fin_dp = (void *)tcp;
|
|
(*(((u_short *)fi) + 1)) = (*(((u_short *)ip) + 4));
|
|
(*(((u_long *)fi) + 1)) = (*(((u_long *)ip) + 3));
|
|
(*(((u_long *)fi) + 2)) = (*(((u_long *)ip) + 4));
|
|
|
|
fi->fi_fl = (hlen > sizeof(struct ip)) ? FI_OPTIONS : 0;
|
|
off = (ip->ip_off & 0x1fff) << 3;
|
|
if (ip->ip_off & 0x3fff)
|
|
fi->fi_fl |= FI_FRAG;
|
|
switch (ip->ip_p)
|
|
{
|
|
case IPPROTO_ICMP :
|
|
if ((!IPMINLEN(ip, icmp) && !off) ||
|
|
(off && off < sizeof(struct icmp)))
|
|
fi->fi_fl |= FI_SHORT;
|
|
if (fin->fin_dlen > 1)
|
|
fin->fin_data[0] = *(u_short *)tcp;
|
|
break;
|
|
case IPPROTO_TCP :
|
|
fi->fi_fl |= FI_TCPUDP;
|
|
if ((!IPMINLEN(ip, tcphdr) && !off) ||
|
|
(off && off < sizeof(struct tcphdr)))
|
|
fi->fi_fl |= FI_SHORT;
|
|
if (!(fi->fi_fl & FI_SHORT) && !off)
|
|
fin->fin_tcpf = tcp->th_flags;
|
|
goto getports;
|
|
case IPPROTO_UDP :
|
|
fi->fi_fl |= FI_TCPUDP;
|
|
if ((!IPMINLEN(ip, udphdr) && !off) ||
|
|
(off && off < sizeof(struct udphdr)))
|
|
fi->fi_fl |= FI_SHORT;
|
|
getports:
|
|
if (!off && (fin->fin_dlen > 3)) {
|
|
fin->fin_data[0] = ntohs(tcp->th_sport);
|
|
fin->fin_data[1] = ntohs(tcp->th_dport);
|
|
}
|
|
break;
|
|
default :
|
|
break;
|
|
}
|
|
|
|
|
|
for (s = (u_char *)(ip + 1), hlen -= sizeof(*ip); hlen; ) {
|
|
if (!(opt = *s))
|
|
break;
|
|
ol = (opt == IPOPT_NOP) ? 1 : (int)*(s+1);
|
|
if (opt > 1 && (ol < 2 || ol > hlen))
|
|
break;
|
|
for (i = 9, mv = 4; mv >= 0; ) {
|
|
op = ipopts + i;
|
|
if (opt == (u_char)op->ol_val) {
|
|
optmsk |= op->ol_bit;
|
|
if (opt == IPOPT_SECURITY) {
|
|
struct optlist *sp;
|
|
u_char sec;
|
|
int j, m;
|
|
|
|
sec = *(s + 2); /* classification */
|
|
for (j = 3, m = 2; m >= 0; ) {
|
|
sp = secopt + j;
|
|
if (sec == sp->ol_val) {
|
|
secmsk |= sp->ol_bit;
|
|
auth = *(s + 3);
|
|
auth *= 256;
|
|
auth += *(s + 4);
|
|
break;
|
|
}
|
|
if (sec < sp->ol_val)
|
|
j -= m--;
|
|
else
|
|
j += m--;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
if (opt < op->ol_val)
|
|
i -= mv--;
|
|
else
|
|
i += mv--;
|
|
}
|
|
hlen -= ol;
|
|
s += ol;
|
|
}
|
|
if (auth && !(auth & 0x0100))
|
|
auth &= 0xff00;
|
|
fi->fi_optmsk = optmsk;
|
|
fi->fi_secmsk = secmsk;
|
|
fi->fi_auth = auth;
|
|
}
|
|
|
|
|
|
/*
|
|
* check an IP packet for TCP/UDP characteristics such as ports and flags.
|
|
*/
|
|
static int fr_tcpudpchk(fr, fin)
|
|
frentry_t *fr;
|
|
fr_info_t *fin;
|
|
{
|
|
register u_short po, tup;
|
|
register char i;
|
|
register int err = 1;
|
|
|
|
/*
|
|
* Both ports should *always* be in the first fragment.
|
|
* So far, I cannot find any cases where they can not be.
|
|
*
|
|
* compare destination ports
|
|
*/
|
|
if ((i = (int)fr->fr_dcmp)) {
|
|
po = fr->fr_dport;
|
|
tup = fin->fin_data[1];
|
|
/*
|
|
* Do opposite test to that required and
|
|
* continue if that succeeds.
|
|
*/
|
|
if (!--i && tup != po) /* EQUAL */
|
|
err = 0;
|
|
else if (!--i && tup == po) /* NOTEQUAL */
|
|
err = 0;
|
|
else if (!--i && tup >= po) /* LESSTHAN */
|
|
err = 0;
|
|
else if (!--i && tup <= po) /* GREATERTHAN */
|
|
err = 0;
|
|
else if (!--i && tup > po) /* LT or EQ */
|
|
err = 0;
|
|
else if (!--i && tup < po) /* GT or EQ */
|
|
err = 0;
|
|
else if (!--i && /* Out of range */
|
|
(tup >= po && tup <= fr->fr_dtop))
|
|
err = 0;
|
|
else if (!--i && /* In range */
|
|
(tup <= po || tup >= fr->fr_dtop))
|
|
err = 0;
|
|
}
|
|
/*
|
|
* compare source ports
|
|
*/
|
|
if (err && (i = (int)fr->fr_scmp)) {
|
|
po = fr->fr_sport;
|
|
tup = fin->fin_data[0];
|
|
if (!--i && tup != po)
|
|
err = 0;
|
|
else if (!--i && tup == po)
|
|
err = 0;
|
|
else if (!--i && tup >= po)
|
|
err = 0;
|
|
else if (!--i && tup <= po)
|
|
err = 0;
|
|
else if (!--i && tup > po)
|
|
err = 0;
|
|
else if (!--i && tup < po)
|
|
err = 0;
|
|
else if (!--i && /* Out of range */
|
|
(tup >= po && tup <= fr->fr_stop))
|
|
err = 0;
|
|
else if (!--i && /* In range */
|
|
(tup <= po || tup >= fr->fr_stop))
|
|
err = 0;
|
|
}
|
|
|
|
/*
|
|
* If we don't have all the TCP/UDP header, then how can we
|
|
* expect to do any sort of match on it ? If we were looking for
|
|
* TCP flags, then NO match. If not, then match (which should
|
|
* satisfy the "short" class too).
|
|
*/
|
|
if (err && (fin->fin_fi.fi_p == IPPROTO_TCP)) {
|
|
if (fin->fin_fi.fi_fl & FI_SHORT)
|
|
return !(fr->fr_tcpf | fr->fr_tcpfm);
|
|
/*
|
|
* Match the flags ? If not, abort this match.
|
|
*/
|
|
if (fr->fr_tcpf &&
|
|
fr->fr_tcpf != (fin->fin_tcpf & fr->fr_tcpfm)) {
|
|
FR_DEBUG(("f. %#x & %#x != %#x\n", fin->fin_tcpf,
|
|
fr->fr_tcpfm, fr->fr_tcpf));
|
|
err = 0;
|
|
}
|
|
}
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
* Check the input/output list of rules for a match and result.
|
|
* Could be per interface, but this gets real nasty when you don't have
|
|
* kernel sauce.
|
|
*/
|
|
static int fr_scanlist(pass, ip, fin, m)
|
|
int pass;
|
|
ip_t *ip;
|
|
register fr_info_t *fin;
|
|
void *m;
|
|
{
|
|
register struct frentry *fr;
|
|
register fr_ip_t *fi = &fin->fin_fi;
|
|
int rulen, portcmp = 0, off;
|
|
|
|
fr = fin->fin_fr;
|
|
fin->fin_fr = NULL;
|
|
fin->fin_rule = 0;
|
|
off = ip->ip_off & 0x1fff;
|
|
pass |= (fi->fi_fl << 20);
|
|
|
|
if ((fi->fi_fl & FI_TCPUDP) && (fin->fin_dlen > 3) && !off)
|
|
portcmp = 1;
|
|
|
|
for (rulen = 0; fr; fr = fr->fr_next, rulen++) {
|
|
/*
|
|
* In all checks below, a null (zero) value in the
|
|
* filter struture is taken to mean a wildcard.
|
|
*
|
|
* check that we are working for the right interface
|
|
*/
|
|
#ifdef _KERNEL
|
|
if (fr->fr_ifa && fr->fr_ifa != fin->fin_ifp)
|
|
continue;
|
|
#else
|
|
if (opts & (OPT_VERBOSE|OPT_DEBUG))
|
|
printf("\n");
|
|
FR_VERBOSE(("%c", (pass & FR_PASS) ? 'p' : 'b'));
|
|
if (fr->fr_ifa && fr->fr_ifa != fin->fin_ifp)
|
|
continue;
|
|
FR_VERBOSE((":i"));
|
|
#endif
|
|
{
|
|
register u_long *ld, *lm, *lip;
|
|
register int i;
|
|
|
|
lip = (u_long *)fi;
|
|
lm = (u_long *)&fr->fr_mip;
|
|
ld = (u_long *)&fr->fr_ip;
|
|
i = ((lip[0] & lm[0]) != ld[0]);
|
|
FR_IFDEBUG(i,continue,("0. %#08x & %#08x != %#08x\n",
|
|
lip[0], lm[0], ld[0]));
|
|
i |= ((lip[1] & lm[1]) != ld[1]) << 21;
|
|
FR_IFDEBUG(i,continue,("1. %#08x & %#08x != %#08x\n",
|
|
lip[1], lm[1], ld[1]));
|
|
i |= ((lip[2] & lm[2]) != ld[2]) << 22;
|
|
FR_IFDEBUG(i,continue,("2. %#08x & %#08x != %#08x\n",
|
|
lip[2], lm[2], ld[2]));
|
|
i |= ((lip[3] & lm[3]) != ld[3]);
|
|
FR_IFDEBUG(i,continue,("3. %#08x & %#08x != %#08x\n",
|
|
lip[3], lm[3], ld[3]));
|
|
i |= ((lip[4] & lm[4]) != ld[4]);
|
|
FR_IFDEBUG(i,continue,("4. %#08x & %#08x != %#08x\n",
|
|
lip[4], lm[4], ld[4]));
|
|
i ^= (fi->fi_fl & (FR_NOTSRCIP|FR_NOTDSTIP));
|
|
if (i)
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* If a fragment, then only the first has what we're looking
|
|
* for here...
|
|
*/
|
|
if (fi->fi_fl & FI_TCPUDP) {
|
|
if (portcmp) {
|
|
if (!fr_tcpudpchk(fr, fin))
|
|
continue;
|
|
} else if (fr->fr_dcmp || fr->fr_scmp || fr->fr_tcpf ||
|
|
fr->fr_tcpfm)
|
|
continue;
|
|
} else if (fi->fi_p == IPPROTO_ICMP) {
|
|
if (!off && (fin->fin_dlen > 1)) {
|
|
if ((fin->fin_data[0] & fr->fr_icmpm) !=
|
|
fr->fr_icmp) {
|
|
FR_DEBUG(("i. %#x & %#x != %#x\n",
|
|
fin->fin_data[0],
|
|
fr->fr_icmpm, fr->fr_icmp));
|
|
continue;
|
|
}
|
|
} else if (fr->fr_icmpm || fr->fr_icmp)
|
|
continue;
|
|
}
|
|
FR_VERBOSE(("*"));
|
|
/*
|
|
* Just log this packet...
|
|
*/
|
|
pass = fr->fr_flags;
|
|
if ((pass & FR_CALLNOW) && fr->fr_func)
|
|
pass = (*fr->fr_func)(pass, ip, fin);
|
|
#ifdef IPFILTER_LOG
|
|
if ((pass & FR_LOGMASK) == FR_LOG) {
|
|
if (!IPLLOG(fr->fr_flags, ip, fin, m))
|
|
frstats[fin->fin_out].fr_skip++;
|
|
frstats[fin->fin_out].fr_pkl++;
|
|
}
|
|
#endif /* IPFILTER_LOG */
|
|
FR_DEBUG(("pass %#x\n", pass));
|
|
fr->fr_hits++;
|
|
if (pass & FR_ACCOUNT)
|
|
fr->fr_bytes += (U_QUAD_T)ip->ip_len;
|
|
else
|
|
fin->fin_icode = fr->fr_icode;
|
|
fin->fin_rule = rulen;
|
|
fin->fin_fr = fr;
|
|
if (pass & FR_QUICK)
|
|
break;
|
|
}
|
|
return pass;
|
|
}
|
|
|
|
|
|
/*
|
|
* frcheck - filter check
|
|
* check using source and destination addresses/pors in a packet whether
|
|
* or not to pass it on or not.
|
|
*/
|
|
int fr_check(ip, hlen, ifp, out
|
|
#ifdef _KERNEL
|
|
# if SOLARIS
|
|
, qif, q, mp)
|
|
qif_t *qif;
|
|
queue_t *q;
|
|
mblk_t **mp;
|
|
# else
|
|
, mp)
|
|
struct mbuf **mp;
|
|
# endif
|
|
#else
|
|
, mp)
|
|
char *mp;
|
|
#endif
|
|
ip_t *ip;
|
|
int hlen;
|
|
struct ifnet *ifp;
|
|
int out;
|
|
{
|
|
/*
|
|
* The above really sucks, but short of writing a diff
|
|
*/
|
|
fr_info_t frinfo, *fc;
|
|
register fr_info_t *fin = &frinfo;
|
|
frentry_t *fr = NULL;
|
|
int pass, changed;
|
|
#ifndef _KERNEL
|
|
char *mc = mp, *m = mp;
|
|
#endif
|
|
|
|
#ifdef _KERNEL
|
|
# if !defined(__SVR4) && !defined(__svr4__)
|
|
register struct mbuf *m = *mp;
|
|
struct mbuf *mc = NULL;
|
|
|
|
if ((ip->ip_p == IPPROTO_TCP || ip->ip_p == IPPROTO_UDP ||
|
|
ip->ip_p == IPPROTO_ICMP)) {
|
|
register int up = MIN(hlen + 8, ip->ip_len);
|
|
|
|
if (up > m->m_len) {
|
|
if ((*mp = m_pullup(m, up)) == 0) {
|
|
frstats[out].fr_pull[1]++;
|
|
return -1;
|
|
} else {
|
|
frstats[out].fr_pull[0]++;
|
|
m = *mp;
|
|
ip = mtod(m, struct ip *);
|
|
}
|
|
}
|
|
}
|
|
# endif
|
|
# if SOLARIS
|
|
mblk_t *mc = NULL, *m = qif->qf_m;
|
|
# endif
|
|
#endif
|
|
fr_makefrip(hlen, ip, fin);
|
|
fin->fin_ifp = ifp;
|
|
fin->fin_out = out;
|
|
fin->fin_mp = mp;
|
|
|
|
MUTEX_ENTER(&ipf_mutex);
|
|
if (!out) {
|
|
changed = ip_natin(ip, hlen, fin);
|
|
if ((fin->fin_fr = ipacct[0][fr_active]) &&
|
|
(FR_SCANLIST(FR_NOMATCH, ip, fin, m) & FR_ACCOUNT))
|
|
frstats[0].fr_acct++;
|
|
}
|
|
|
|
if (!(pass = ipfr_knownfrag(ip, fin)) &&
|
|
!(pass = fr_checkstate(ip, fin))) {
|
|
fc = frcache + out;
|
|
if (fc->fin_fr && !bcmp((char *)fin, (char *)fc, FI_CSIZE)) {
|
|
/*
|
|
* copy cached data so we can unlock the mutex
|
|
* earlier.
|
|
*/
|
|
bcopy((char *)fc, (char *)fin, sizeof(*fin));
|
|
frstats[out].fr_chit++;
|
|
pass = fin->fin_fr->fr_flags;
|
|
} else {
|
|
pass = fr_pass;
|
|
if ((fin->fin_fr = ipfilter[out][fr_active]))
|
|
pass = FR_SCANLIST(fr_pass, ip, fin, m);
|
|
bcopy((char *)fin, (char *)fc, FI_CSIZE);
|
|
if (pass & FR_NOMATCH)
|
|
frstats[out].fr_nom++;
|
|
}
|
|
fr = fin->fin_fr;
|
|
|
|
if (pass & FR_KEEPFRAG) {
|
|
if (fin->fin_fi.fi_fl & FI_FRAG) {
|
|
if (ipfr_newfrag(ip, fin, pass) == -1)
|
|
frstats[out].fr_bnfr++;
|
|
else
|
|
frstats[out].fr_nfr++;
|
|
} else
|
|
frstats[out].fr_cfr++;
|
|
}
|
|
if (pass & FR_KEEPSTATE) {
|
|
if (fr_addstate(ip, fin, pass) == -1)
|
|
frstats[out].fr_bads++;
|
|
else
|
|
frstats[out].fr_ads++;
|
|
}
|
|
}
|
|
|
|
if (fr && fr->fr_func && !(pass & FR_CALLNOW))
|
|
pass = (*fr->fr_func)(pass, ip, fin);
|
|
|
|
if (out) {
|
|
if ((fin->fin_fr = ipacct[1][fr_active]) &&
|
|
(FR_SCANLIST(FR_NOMATCH, ip, fin, m) & FR_ACCOUNT))
|
|
frstats[1].fr_acct++;
|
|
fin->fin_fr = NULL;
|
|
changed = ip_natout(ip, hlen, fin);
|
|
}
|
|
fin->fin_fr = fr;
|
|
MUTEX_EXIT(&ipf_mutex);
|
|
|
|
#ifdef IPFILTER_LOG
|
|
if ((fr_flags & FF_LOGGING) || (pass & FR_LOGMASK)) {
|
|
if ((fr_flags & FF_LOGNOMATCH) && (pass & FR_NOMATCH)) {
|
|
pass |= FF_LOGNOMATCH;
|
|
frstats[out].fr_npkl++;
|
|
goto logit;
|
|
} else if (((pass & FR_LOGMASK) == FR_LOGP) ||
|
|
((pass & FR_PASS) && (fr_flags & FF_LOGPASS))) {
|
|
if ((pass & FR_LOGMASK) != FR_LOGP)
|
|
pass |= FF_LOGPASS;
|
|
frstats[out].fr_ppkl++;
|
|
goto logit;
|
|
} else if (((pass & FR_LOGMASK) == FR_LOGB) ||
|
|
((pass & FR_BLOCK) && (fr_flags & FF_LOGBLOCK))) {
|
|
if ((pass & FR_LOGMASK) != FR_LOGB)
|
|
pass |= FF_LOGBLOCK;
|
|
frstats[out].fr_bpkl++;
|
|
logit:
|
|
if (!IPLLOG(pass, ip, fin, m)) {
|
|
frstats[out].fr_skip++;
|
|
if ((pass & (FR_PASS|FR_LOGORBLOCK)) ==
|
|
(FR_PASS|FR_LOGORBLOCK))
|
|
pass ^= FR_PASS|FR_BLOCK;
|
|
}
|
|
}
|
|
}
|
|
#endif /* IPFILTER_LOG */
|
|
#ifdef _KERNEL
|
|
/*
|
|
* Only allow FR_DUP to work if a rule matched - it makes no sense to
|
|
* set FR_DUP as a "default" as there are no instructions about where
|
|
* to send the packet.
|
|
*/
|
|
if (fr && (pass & FR_DUP))
|
|
# if SOLARIS
|
|
mc = dupmsg(m);
|
|
# else
|
|
mc = m_copy(m, 0, M_COPYALL);
|
|
# endif
|
|
#endif
|
|
|
|
if (pass & FR_PASS)
|
|
frstats[out].fr_pass++;
|
|
else if (pass & FR_BLOCK) {
|
|
frstats[out].fr_block++;
|
|
/*
|
|
* Should we return an ICMP packet to indicate error
|
|
* status passing through the packet filter ?
|
|
* WARNING: ICMP error packets AND TCP RST packets should
|
|
* ONLY be sent in repsonse to incoming packets. Sending them
|
|
* in response to outbound packets can result in a panic on
|
|
* some operating systems.
|
|
*/
|
|
if (!out) {
|
|
#ifdef _KERNEL
|
|
if (pass & FR_RETICMP) {
|
|
# if SOLARIS
|
|
ICMP_ERROR(q, ip, ICMP_UNREACH, fin->fin_icode,
|
|
qif, ip->ip_src);
|
|
# else
|
|
ICMP_ERROR(m, ip, ICMP_UNREACH, fin->fin_icode,
|
|
ifp, ip->ip_src);
|
|
m = *mp = NULL; /* freed by icmp_error() */
|
|
# endif
|
|
|
|
frstats[0].fr_ret++;
|
|
} else if ((pass & FR_RETRST) &&
|
|
!(fin->fin_fi.fi_fl & FI_SHORT)) {
|
|
if (SEND_RESET(ip, qif, q, ifp) == 0)
|
|
frstats[1].fr_ret++;
|
|
}
|
|
#else
|
|
if (pass & FR_RETICMP) {
|
|
verbose("- ICMP unreachable sent\n");
|
|
frstats[0].fr_ret++;
|
|
} else if ((pass & FR_RETRST) &&
|
|
!(fin->fin_fi.fi_fl & FI_SHORT)) {
|
|
verbose("- TCP RST sent\n");
|
|
frstats[1].fr_ret++;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If we didn't drop off the bottom of the list of rules (and thus
|
|
* the 'current' rule fr is not NULL), then we may have some extra
|
|
* instructions about what to do with a packet.
|
|
* Once we're finished return to our caller, freeing the packet if
|
|
* we are dropping it (* BSD ONLY *).
|
|
*/
|
|
#ifdef _KERNEL
|
|
# if !SOLARIS
|
|
if (fr) {
|
|
frdest_t *fdp = &fr->fr_tif;
|
|
|
|
if ((pass & FR_FASTROUTE) ||
|
|
(fdp->fd_ifp && fdp->fd_ifp != (struct ifnet *)-1)) {
|
|
ipfr_fastroute(m, fin, fdp);
|
|
m = *mp = NULL;
|
|
}
|
|
if (mc)
|
|
ipfr_fastroute(mc, fin, &fr->fr_dif);
|
|
}
|
|
if (!(pass & FR_PASS) && m)
|
|
m_freem(m);
|
|
return (pass & FR_PASS) ? 0 : -1;
|
|
# else
|
|
if (fr) {
|
|
frdest_t *fdp = &fr->fr_tif;
|
|
|
|
if ((pass & FR_FASTROUTE) ||
|
|
(fdp->fd_ifp && fdp->fd_ifp != (struct ifnet *)-1)) {
|
|
ipfr_fastroute(qif, ip, m, mp, fin, fdp);
|
|
m = *mp = NULL;
|
|
}
|
|
if (mc)
|
|
ipfr_fastroute(qif, ip, mc, mp, fin, &fr->fr_dif);
|
|
}
|
|
return (pass & FR_PASS) ? changed : -1;
|
|
# endif
|
|
#else
|
|
if (pass & FR_NOMATCH)
|
|
return 1;
|
|
if (pass & FR_PASS)
|
|
return 0;
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
|
|
#ifdef IPFILTER_LOG
|
|
int fr_copytolog(dev, buf, len)
|
|
int dev;
|
|
char *buf;
|
|
int len;
|
|
{
|
|
register char *bufp = iplbuf[dev], *tp = iplt[dev], *hp = iplh[dev];
|
|
register int clen, tail;
|
|
|
|
tail = (hp >= tp) ? (bufp + IPLLOGSIZE - hp) : (tp - hp);
|
|
clen = MIN(tail, len);
|
|
bcopy(buf, hp, clen);
|
|
len -= clen;
|
|
tail -= clen;
|
|
hp += clen;
|
|
buf += clen;
|
|
if (hp == bufp + IPLLOGSIZE) {
|
|
hp = bufp;
|
|
tail = tp - hp;
|
|
}
|
|
if (len && tail) {
|
|
clen = MIN(tail, len);
|
|
bcopy(buf, hp, clen);
|
|
len -= clen;
|
|
hp += clen;
|
|
}
|
|
iplh[dev] = hp;
|
|
return len;
|
|
}
|
|
#endif
|
|
|
|
|
|
u_short ipf_cksum(addr, len)
|
|
register u_short *addr;
|
|
register int len;
|
|
{
|
|
register u_long sum = 0;
|
|
|
|
for (sum = 0; len > 1; len -= 2)
|
|
sum += *addr++;
|
|
|
|
/* mop up an odd byte, if necessary */
|
|
if (len == 1)
|
|
sum += *(u_char *)addr;
|
|
|
|
/*
|
|
* add back carry outs from top 16 bits to low 16 bits
|
|
*/
|
|
sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
|
|
sum += (sum >> 16); /* add carry */
|
|
return (u_short)(~sum);
|
|
}
|
|
|
|
|
|
/*
|
|
* NB: This function assumes we've pullup'd enough for all of the IP header
|
|
* and the TCP header. We also assume that data blocks aren't allocated in
|
|
* odd sizes.
|
|
*/
|
|
u_short fr_tcpsum(m, ip, tcp)
|
|
#if SOLARIS
|
|
mblk_t *m;
|
|
#else
|
|
struct mbuf *m;
|
|
#endif
|
|
ip_t *ip;
|
|
tcphdr_t *tcp;
|
|
{
|
|
union {
|
|
u_char c[2];
|
|
u_short s;
|
|
} bytes;
|
|
u_long sum;
|
|
u_short *sp;
|
|
int len, ilen;
|
|
# if SOLARIS
|
|
int add, hlen;
|
|
# endif
|
|
|
|
/*
|
|
* Add up IP Header portion
|
|
*/
|
|
ilen = len = ip->ip_len - (ip->ip_hl << 2);
|
|
bytes.c[0] = 0;
|
|
bytes.c[1] = IPPROTO_TCP;
|
|
sum = bytes.s;
|
|
sum += htons((u_short)len);
|
|
sp = (u_short *)&ip->ip_src;
|
|
sum += *sp++;
|
|
sum += *sp++;
|
|
sum += *sp++;
|
|
sum += *sp++;
|
|
if (sp != (u_short *)tcp)
|
|
sp = (u_short *)tcp;
|
|
sum += *sp++;
|
|
sum += *sp++;
|
|
sum += *sp++;
|
|
sum += *sp++;
|
|
sum += *sp++;
|
|
sum += *sp++;
|
|
sum += *sp++;
|
|
sum += *sp;
|
|
sp += 2; /* Skip over checksum */
|
|
sum += *sp++;
|
|
|
|
#if SOLARIS
|
|
/*
|
|
* In case we had to copy the IP & TCP header out of mblks,
|
|
* skip over the mblk bits which are the header
|
|
*/
|
|
if ((caddr_t)ip != (caddr_t)m->b_rptr) {
|
|
hlen = (caddr_t)sp - (caddr_t)ip;
|
|
while (hlen) {
|
|
add = MIN(hlen, m->b_wptr - m->b_rptr);
|
|
sp = (u_short *)((caddr_t)m->b_rptr + add);
|
|
if ((hlen -= add))
|
|
m = m->b_cont;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (!(len -= sizeof(*tcp)))
|
|
goto nodata;
|
|
while (len > 1) {
|
|
sum += *sp++;
|
|
len -= 2;
|
|
#if SOLARIS
|
|
if ((caddr_t)sp > (caddr_t)m->b_wptr) {
|
|
m = m->b_cont;
|
|
PANIC((!m),("fr_tcpsum: not enough data"));
|
|
sp = (u_short *)m->b_rptr;
|
|
}
|
|
#else
|
|
# ifdef m_data
|
|
if ((caddr_t)sp > (m->m_data + m->m_len))
|
|
# else
|
|
if ((caddr_t)sp > (caddr_t)(m->m_dat + m->m_off + m->m_len))
|
|
# endif
|
|
{
|
|
m = m->m_next;
|
|
PANIC((!m),("fr_tcpsum: not enough data"));
|
|
sp = mtod(m, u_short *);
|
|
}
|
|
#endif /* SOLARIS */
|
|
}
|
|
if (len) {
|
|
bytes.c[1] = 0;
|
|
bytes.c[0] = *(u_char *)sp;
|
|
sum += bytes.s;
|
|
}
|
|
nodata:
|
|
sum = (sum >> 16) + (sum & 0xffff);
|
|
sum += (sum >> 16);
|
|
sum = (u_short)((~sum) & 0xffff);
|
|
return sum;
|
|
}
|