diff options
Diffstat (limited to 'contrib/ipfilter/ip_fil_freebsd.c')
-rw-r--r-- | contrib/ipfilter/ip_fil_freebsd.c | 1692 |
1 files changed, 1692 insertions, 0 deletions
diff --git a/contrib/ipfilter/ip_fil_freebsd.c b/contrib/ipfilter/ip_fil_freebsd.c new file mode 100644 index 0000000..3b36b93 --- /dev/null +++ b/contrib/ipfilter/ip_fil_freebsd.c @@ -0,0 +1,1692 @@ +/* $NetBSD$ */ + +/* + * Copyright (C) 1993-2003 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + */ +#if !defined(lint) +static const char sccsid[] = "@(#)ip_fil.c 2.41 6/5/96 (C) 1993-2000 Darren Reed"; +static const char rcsid[] = "@(#)Id: ip_fil_freebsd.c,v 2.53.2.25 2005/02/01 03:15:56 darrenr Exp"; +#endif + +#if defined(KERNEL) || defined(_KERNEL) +# undef KERNEL +# undef _KERNEL +# define KERNEL 1 +# define _KERNEL 1 +#endif +#if defined(__FreeBSD_version) && (__FreeBSD_version >= 400000) && \ + !defined(KLD_MODULE) && !defined(IPFILTER_LKM) +# include "opt_inet6.h" +#endif +#if defined(__FreeBSD_version) && (__FreeBSD_version >= 440000) && \ + !defined(KLD_MODULE) && !defined(IPFILTER_LKM) +# include "opt_random_ip_id.h" +#endif +#include <sys/param.h> +#if defined(__FreeBSD__) && !defined(__FreeBSD_version) +# if defined(IPFILTER_LKM) +# ifndef __FreeBSD_cc_version +# include <osreldate.h> +# else +# if __FreeBSD_cc_version < 430000 +# include <osreldate.h> +# endif +# endif +# endif +#endif +#include <sys/errno.h> +#include <sys/types.h> +#include <sys/file.h> +#if __FreeBSD_version >= 220000 +# include <sys/fcntl.h> +# include <sys/filio.h> +#else +# include <sys/ioctl.h> +#endif +#include <sys/time.h> +#include <sys/systm.h> +#if (__FreeBSD_version >= 300000) +# include <sys/dirent.h> +#else +# include <sys/dir.h> +#endif +#if !defined(__hpux) +# include <sys/mbuf.h> +#endif +#include <sys/protosw.h> +#include <sys/socket.h> + +#include <net/if.h> +#if __FreeBSD_version >= 300000 +# include <net/if_var.h> +# if !defined(IPFILTER_LKM) +# include "opt_ipfilter.h" +# endif +#endif +#include <net/route.h> +#include <netinet/in.h> +#include <netinet/in_var.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/ip_var.h> +#include <netinet/tcp.h> +#if defined(__osf__) +# include <netinet/tcp_timer.h> +#endif +#include <netinet/udp.h> +#include <netinet/tcpip.h> +#include <netinet/ip_icmp.h> +#ifndef _KERNEL +# include "netinet/ipf.h" +#endif +#include "netinet/ip_compat.h" +#ifdef USE_INET6 +# include <netinet/icmp6.h> +#endif +#include "netinet/ip_fil.h" +#include "netinet/ip_nat.h" +#include "netinet/ip_frag.h" +#include "netinet/ip_state.h" +#include "netinet/ip_proxy.h" +#include "netinet/ip_auth.h" +#ifdef IPFILTER_SYNC +#include "netinet/ip_sync.h" +#endif +#ifdef IPFILTER_SCAN +#include "netinet/ip_scan.h" +#endif +#include "netinet/ip_pool.h" +#if defined(__FreeBSD_version) && (__FreeBSD_version >= 300000) +# include <sys/malloc.h> +#endif +#include <sys/kernel.h> +#ifdef CSUM_DATA_VALID +#include <machine/in_cksum.h> +#endif +extern int ip_optcopy __P((struct ip *, struct ip *)); + +#if (__FreeBSD_version > 460000) +extern int path_mtu_discovery; +#endif + +# ifdef IPFILTER_M_IPFILTER +MALLOC_DEFINE(M_IPFILTER, "IP Filter", "IP Filter packet filter data structures"); +# endif + + +#if !defined(__osf__) +extern struct protosw inetsw[]; +#endif + +static int (*fr_savep) __P((ip_t *, int, void *, int, struct mbuf **)); +static int fr_send_ip __P((fr_info_t *, mb_t *, mb_t **)); +# ifdef USE_MUTEXES +ipfmutex_t ipl_mutex, ipf_authmx, ipf_rw, ipf_stinsert; +ipfmutex_t ipf_nat_new, ipf_natio, ipf_timeoutlock; +ipfrwlock_t ipf_mutex, ipf_global, ipf_ipidfrag; +ipfrwlock_t ipf_frag, ipf_state, ipf_nat, ipf_natfrag, ipf_auth; +# endif +int ipf_locks_done = 0; + +#if (__FreeBSD_version >= 300000) +struct callout_handle fr_slowtimer_ch; +#endif + +#if (__FreeBSD_version >= 500011) +# include <sys/conf.h> +# if defined(NETBSD_PF) +# include <net/pfil.h> +# include <netinet/ipprotosw.h> +/* + * We provide the fr_checkp name just to minimize changes later. + */ +int (*fr_checkp) __P((ip_t *ip, int hlen, void *ifp, int out, mb_t **mp)); +# endif /* NETBSD_PF */ +#endif /* __FreeBSD_version >= 500011 */ + + +#if (__FreeBSD_version >= 501108) && defined(_KERNEL) + +static int +fr_check_wrapper(void *arg, struct mbuf **mp, struct ifnet *ifp, int dir) +{ + struct ip *ip = mtod(*mp, struct ip *); + return fr_check(ip, ip->ip_hl << 2, ifp, (dir == PFIL_OUT), mp); +} + +# ifdef USE_INET6 +# include <netinet/ip6.h> + +static int +fr_check_wrapper6(void *arg, struct mbuf **mp, struct ifnet *ifp, int dir) +{ + return (fr_check(mtod(*mp, struct ip *), sizeof(struct ip6_hdr), + ifp, (dir == PFIL_OUT), mp)); +} +# endif +#endif /* __FreeBSD_version >= 501108 */ +#if defined(IPFILTER_LKM) +int iplidentify(s) +char *s; +{ + if (strcmp(s, "ipl") == 0) + return 1; + return 0; +} +#endif /* IPFILTER_LKM */ + + +int iplattach() +{ +#ifdef USE_SPL + int s; +#endif +#if defined(NETBSD_PF) && (__FreeBSD_version >= 500011) + int error = 0; +# if __FreeBSD_version >= 501108 + struct pfil_head *ph_inet; +# ifdef USE_INET6 + struct pfil_head *ph_inet6; +# endif +# endif +#endif + + SPL_NET(s); + if (fr_running > 0) { + SPL_X(s); + return EBUSY; + } + + MUTEX_INIT(&ipf_rw, "ipf rw mutex"); + RWLOCK_INIT(&ipf_global, "ipf filter load/unload mutex"); + MUTEX_INIT(&ipf_timeoutlock, "ipf timeout queue mutex"); + RWLOCK_INIT(&ipf_mutex, "ipf filter rwlock"); + RWLOCK_INIT(&ipf_ipidfrag, "ipf IP NAT-Frag rwlock"); + ipf_locks_done = 1; + + if (fr_initialise() < 0) { + SPL_X(s); + return EIO; + } + + +# ifdef NETBSD_PF +# if __FreeBSD_version >= 500011 +# if __FreeBSD_version >= 501108 + ph_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET); +# ifdef USE_INET6 + ph_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6); +# endif + if (ph_inet == NULL +# ifdef USE_INET6 + && ph_inet6 == NULL +# endif + ) + return ENODEV; + + if (ph_inet != NULL) + error = pfil_add_hook((void *)fr_check_wrapper, NULL, + PFIL_IN|PFIL_OUT, ph_inet); + else + error = 0; +# else + error = pfil_add_hook((void *)fr_check, PFIL_IN|PFIL_OUT, + &inetsw[ip_protox[IPPROTO_IP]].pr_pfh); +# endif + if (error) { +# ifdef USE_INET6 + goto pfil_error; +# else + fr_deinitialise(); + SPL_X(s); + return error; +# endif + } +# else + pfil_add_hook((void *)fr_check, PFIL_IN|PFIL_OUT); +# endif +# ifdef USE_INET6 +# if __FreeBSD_version >= 501108 + if (ph_inet6 != NULL) + error = pfil_add_hook((void *)fr_check_wrapper6, NULL, + PFIL_IN|PFIL_OUT, ph_inet6); + else + error = 0; + if (error) { + pfil_remove_hook((void *)fr_check_wrapper6, NULL, + PFIL_IN|PFIL_OUT, ph_inet6); +# else + error = pfil_add_hook((void *)fr_check, PFIL_IN|PFIL_OUT, + &inet6sw[ip6_protox[IPPROTO_IPV6]].pr_pfh); + if (error) { + pfil_remove_hook((void *)fr_check, PFIL_IN|PFIL_OUT, + &inetsw[ip_protox[IPPROTO_IP]].pr_pfh); +# endif +pfil_error: + fr_deinitialise(); + SPL_X(s); + return error; + } +# endif +# endif + if (fr_checkp != fr_check) { + fr_savep = fr_checkp; + fr_checkp = fr_check; + } + + bzero((char *)frcache, sizeof(frcache)); + fr_running = 1; + + if (fr_control_forwarding & 1) + ipforwarding = 1; + + SPL_X(s); +#if (__FreeBSD_version >= 300000) + fr_slowtimer_ch = timeout(fr_slowtimer, NULL, + (hz / IPF_HZ_DIVIDE) * IPF_HZ_MULT); +#else + timeout(fr_slowtimer, NULL, (hz / IPF_HZ_DIVIDE) * IPF_HZ_MULT); +#endif + return 0; +} + + +/* + * Disable the filter by removing the hooks from the IP input/output + * stream. + */ +int ipldetach() +{ +#ifdef USE_SPL + int s; +#endif +#if defined(NETBSD_PF) && (__FreeBSD_version >= 500011) + int error = 0; +# if __FreeBSD_version >= 501108 + struct pfil_head *ph_inet; +# ifdef USE_INET6 + struct pfil_head *ph_inet6; +# endif +# endif +#endif + + if (fr_control_forwarding & 2) + ipforwarding = 0; + + SPL_NET(s); + +#if (__FreeBSD_version >= 300000) + if (fr_slowtimer_ch.callout != NULL) + untimeout(fr_slowtimer, NULL, fr_slowtimer_ch); + bzero(&fr_slowtimer_ch, sizeof(fr_slowtimer_ch)); +#else + untimeout(fr_slowtimer, NULL); +#endif /* FreeBSD */ + +#ifndef NETBSD_PF + if (fr_checkp != NULL) + fr_checkp = fr_savep; + fr_savep = NULL; +#endif + +#ifdef NETBSD_PF +# if (__FreeBSD_version >= 500011) +# if (__FreeBSD_version >= 501108) + ph_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET); + if (ph_inet != NULL) + error = pfil_remove_hook((void *)fr_check_wrapper, NULL, + PFIL_IN|PFIL_OUT, ph_inet); + else + error = 0; +# else + error = pfil_remove_hook((void *)fr_check, PFIL_IN|PFIL_OUT, + &inetsw[ip_protox[IPPROTO_IP]].pr_pfh); +# endif + if (error) { + SPL_X(s); + return error; + } +# else + pfil_remove_hook((void *)fr_check, PFIL_IN|PFIL_OUT); +# endif +# ifdef USE_INET6 +# if (__FreeBSD_version >= 501108) + ph_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6); + if (ph_inet6 != NULL) + error = pfil_remove_hook((void *)fr_check_wrapper6, NULL, + PFIL_IN|PFIL_OUT, ph_inet6); + else + error = 0; +# else + error = pfil_remove_hook((void *)fr_check, PFIL_IN|PFIL_OUT, + &inet6sw[ip6_protox[IPPROTO_IPV6]].pr_pfh); +# endif + if (error) { + SPL_X(s); + return error; + } +# endif +#endif + fr_deinitialise(); + + fr_running = -2; + + (void) frflush(IPL_LOGIPF, 0, FR_INQUE|FR_OUTQUE|FR_INACTIVE); + (void) frflush(IPL_LOGIPF, 0, FR_INQUE|FR_OUTQUE); + + if (ipf_locks_done == 1) { + MUTEX_DESTROY(&ipf_timeoutlock); + MUTEX_DESTROY(&ipf_rw); + RW_DESTROY(&ipf_mutex); + RW_DESTROY(&ipf_ipidfrag); + RW_DESTROY(&ipf_global); + ipf_locks_done = 0; + } + + SPL_X(s); + + return 0; +} + + +/* + * Filter ioctl interface. + */ +int iplioctl(dev, cmd, data, mode +# if defined(_KERNEL) && ((BSD >= 199506) || (__FreeBSD_version >= 220000)) +, p) +# if (__FreeBSD_version >= 500024) +struct thread *p; +# else +struct proc *p; +# endif /* __FreeBSD_version >= 500024 */ +# else +) +# endif +#if defined(_KERNEL) && (__FreeBSD_version >= 502116) +struct cdev *dev; +#else +dev_t dev; +#endif +ioctlcmd_t cmd; +caddr_t data; +int mode; +{ +#ifdef USE_SPL + int s; +#endif + int error = 0, unit = 0, tmp; + friostat_t fio; + +#if (BSD >= 199306) && defined(_KERNEL) + if ((securelevel >= 2) && (mode & FWRITE)) + return EPERM; +#endif + + unit = GET_MINOR(dev); + if ((IPL_LOGMAX < unit) || (unit < 0)) + return ENXIO; + + if (fr_running <= 0) { + if (unit != IPL_LOGIPF) + return EIO; + if (cmd != SIOCIPFGETNEXT && cmd != SIOCIPFGET && + cmd != SIOCIPFSET && cmd != SIOCFRENB && + cmd != SIOCGETFS && cmd != SIOCGETFF) + return EIO; + } + + SPL_NET(s); + + error = fr_ioctlswitch(unit, data, cmd, mode); + if (error != -1) { + SPL_X(s); + return error; + } + error = 0; + + switch (cmd) + { + case FIONREAD : +#ifdef IPFILTER_LOG + BCOPYOUT(&iplused[IPL_LOGIPF], (caddr_t)data, + sizeof(iplused[IPL_LOGIPF])); +#endif + break; + case SIOCFRENB : + if (!(mode & FWRITE)) + error = EPERM; + else { + BCOPYIN(data, &tmp, sizeof(tmp)); + if (tmp) { + if (fr_running > 0) + error = 0; + else + error = iplattach(); + if (error == 0) + fr_running = 1; + else + (void) ipldetach(); + } else { + error = ipldetach(); + if (error == 0) + fr_running = -1; + } + } + break; + case SIOCIPFSET : + if (!(mode & FWRITE)) { + error = EPERM; + break; + } + case SIOCIPFGETNEXT : + case SIOCIPFGET : + error = fr_ipftune(cmd, data); + break; + case SIOCSETFF : + if (!(mode & FWRITE)) + error = EPERM; + else + BCOPYIN(data, &fr_flags, sizeof(fr_flags)); + break; + case SIOCGETFF : + BCOPYOUT(&fr_flags, data, sizeof(fr_flags)); + break; + case SIOCFUNCL : + error = fr_resolvefunc(data); + break; + case SIOCINAFR : + case SIOCRMAFR : + case SIOCADAFR : + case SIOCZRLST : + if (!(mode & FWRITE)) + error = EPERM; + else + error = frrequest(unit, cmd, data, fr_active, 1); + break; + case SIOCINIFR : + case SIOCRMIFR : + case SIOCADIFR : + if (!(mode & FWRITE)) + error = EPERM; + else + error = frrequest(unit, cmd, data, 1 - fr_active, 1); + break; + case SIOCSWAPA : + if (!(mode & FWRITE)) + error = EPERM; + else { + bzero((char *)frcache, sizeof(frcache[0]) * 2); + *(u_int *)data = fr_active; + fr_active = 1 - fr_active; + } + break; + case SIOCGETFS : + fr_getstat(&fio); + error = fr_outobj(data, &fio, IPFOBJ_IPFSTAT); + break; + case SIOCFRZST : + if (!(mode & FWRITE)) + error = EPERM; + else + error = fr_zerostats(data); + break; + case SIOCIPFFL : + if (!(mode & FWRITE)) + error = EPERM; + else { + BCOPYIN(data, &tmp, sizeof(tmp)); + tmp = frflush(unit, 4, tmp); + BCOPYOUT(&tmp, data, sizeof(tmp)); + } + break; +#ifdef USE_INET6 + case SIOCIPFL6 : + if (!(mode & FWRITE)) + error = EPERM; + else { + BCOPYIN(data, &tmp, sizeof(tmp)); + tmp = frflush(unit, 6, tmp); + BCOPYOUT(&tmp, data, sizeof(tmp)); + } + break; +#endif + case SIOCSTLCK : + BCOPYIN(data, &tmp, sizeof(tmp)); + fr_state_lock = tmp; + fr_nat_lock = tmp; + fr_frag_lock = tmp; + fr_auth_lock = tmp; + break; +#ifdef IPFILTER_LOG + case SIOCIPFFB : + if (!(mode & FWRITE)) + error = EPERM; + else + *(int *)data = ipflog_clear(unit); + break; +#endif /* IPFILTER_LOG */ + case SIOCGFRST : + error = fr_outobj(data, fr_fragstats(), IPFOBJ_FRAGSTAT); + break; + case SIOCFRSYN : + if (!(mode & FWRITE)) + error = EPERM; + else { + frsync(NULL); + } + break; + default : + error = EINVAL; + break; + } + SPL_X(s); + return error; +} + + +#if 0 +void fr_forgetifp(ifp) +void *ifp; +{ + register frentry_t *f; + + WRITE_ENTER(&ipf_mutex); + for (f = ipacct[0][fr_active]; (f != NULL); f = f->fr_next) + if (f->fr_ifa == ifp) + f->fr_ifa = (void *)-1; + for (f = ipacct[1][fr_active]; (f != NULL); f = f->fr_next) + if (f->fr_ifa == ifp) + f->fr_ifa = (void *)-1; + for (f = ipfilter[0][fr_active]; (f != NULL); f = f->fr_next) + if (f->fr_ifa == ifp) + f->fr_ifa = (void *)-1; + for (f = ipfilter[1][fr_active]; (f != NULL); f = f->fr_next) + if (f->fr_ifa == ifp) + f->fr_ifa = (void *)-1; +#ifdef USE_INET6 + for (f = ipacct6[0][fr_active]; (f != NULL); f = f->fr_next) + if (f->fr_ifa == ifp) + f->fr_ifa = (void *)-1; + for (f = ipacct6[1][fr_active]; (f != NULL); f = f->fr_next) + if (f->fr_ifa == ifp) + f->fr_ifa = (void *)-1; + for (f = ipfilter6[0][fr_active]; (f != NULL); f = f->fr_next) + if (f->fr_ifa == ifp) + f->fr_ifa = (void *)-1; + for (f = ipfilter6[1][fr_active]; (f != NULL); f = f->fr_next) + if (f->fr_ifa == ifp) + f->fr_ifa = (void *)-1; +#endif + RWLOCK_EXIT(&ipf_mutex); + fr_natsync(ifp); +} +#endif + + +/* + * routines below for saving IP headers to buffer + */ +int iplopen(dev, flags +#if ((BSD >= 199506) || (__FreeBSD_version >= 220000)) && defined(_KERNEL) +, devtype, p) +int devtype; +# if (__FreeBSD_version >= 500024) +struct thread *p; +# else +struct proc *p; +# endif /* __FreeBSD_version >= 500024 */ +#else +) +#endif +#if defined(_KERNEL) && (__FreeBSD_version >= 502116) +struct cdev *dev; +#else +dev_t dev; +#endif +int flags; +{ + u_int min = GET_MINOR(dev); + + if (IPL_LOGMAX < min) + min = ENXIO; + else + min = 0; + return min; +} + + +int iplclose(dev, flags +#if ((BSD >= 199506) || (__FreeBSD_version >= 220000)) && defined(_KERNEL) +, devtype, p) +int devtype; +# if (__FreeBSD_version >= 500024) +struct thread *p; +# else +struct proc *p; +# endif /* __FreeBSD_version >= 500024 */ +#else +) +#endif +#if defined(_KERNEL) && (__FreeBSD_version >= 502116) +struct cdev *dev; +#else +dev_t dev; +#endif +int flags; +{ + u_int min = GET_MINOR(dev); + + if (IPL_LOGMAX < min) + min = ENXIO; + else + min = 0; + return min; +} + +/* + * iplread/ipllog + * both of these must operate with at least splnet() lest they be + * called during packet processing and cause an inconsistancy to appear in + * the filter lists. + */ +#if (BSD >= 199306) +int iplread(dev, uio, ioflag) +int ioflag; +#else +int iplread(dev, uio) +#endif +#if defined(_KERNEL) && (__FreeBSD_version >= 502116) +struct cdev *dev; +#else +dev_t dev; +#endif +register struct uio *uio; +{ + +# ifdef IPFILTER_SYNC + if (GET_MINOR(dev) == IPL_LOGSYNC) + return ipfsync_read(uio); +# endif + +#ifdef IPFILTER_LOG + return ipflog_read(GET_MINOR(dev), uio); +#else + return ENXIO; +#endif +} + + +/* + * iplwrite + * both of these must operate with at least splnet() lest they be + * called during packet processing and cause an inconsistancy to appear in + * the filter lists. + */ +#if (BSD >= 199306) +int iplwrite(dev, uio, ioflag) +int ioflag; +#else +int iplwrite(dev, uio) +#endif +#if defined(_KERNEL) && (__FreeBSD_version >= 502116) +struct cdev *dev; +#else +dev_t dev; +#endif +register struct uio *uio; +{ + +#ifdef IPFILTER_SYNC + if (GET_MINOR(dev) == IPL_LOGSYNC) + return ipfsync_write(uio); +#endif + return ENXIO; +} + + +/* + * fr_send_reset - this could conceivably be a call to tcp_respond(), but that + * requires a large amount of setting up and isn't any more efficient. + */ +int fr_send_reset(fin) +fr_info_t *fin; +{ + struct tcphdr *tcp, *tcp2; + int tlen = 0, hlen; + struct mbuf *m; +#ifdef USE_INET6 + ip6_t *ip6; +#endif + ip_t *ip; + + tcp = fin->fin_dp; + if (tcp->th_flags & TH_RST) + return -1; /* feedback loop */ + +#ifndef IPFILTER_CKSUM + if (fr_checkl4sum(fin) == -1) + return -1; +#endif + + tlen = fin->fin_dlen - (TCP_OFF(tcp) << 2) + + ((tcp->th_flags & TH_SYN) ? 1 : 0) + + ((tcp->th_flags & TH_FIN) ? 1 : 0); + +#ifdef USE_INET6 + hlen = (fin->fin_v == 6) ? sizeof(ip6_t) : sizeof(ip_t); +#else + hlen = sizeof(ip_t); +#endif +#ifdef MGETHDR + MGETHDR(m, M_DONTWAIT, MT_HEADER); +#else + MGET(m, M_DONTWAIT, MT_HEADER); +#endif + if (m == NULL) + return -1; + if (sizeof(*tcp2) + hlen > MLEN) { + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + FREE_MB_T(m); + return -1; + } + } + + m->m_len = sizeof(*tcp2) + hlen; +#if (BSD >= 199103) + m->m_data += max_linkhdr; + m->m_pkthdr.len = m->m_len; + m->m_pkthdr.rcvif = (struct ifnet *)0; +#endif + ip = mtod(m, struct ip *); + bzero((char *)ip, hlen); +#ifdef USE_INET6 + ip6 = (ip6_t *)ip; +#endif + tcp2 = (struct tcphdr *)((char *)ip + hlen); + tcp2->th_sport = tcp->th_dport; + tcp2->th_dport = tcp->th_sport; + + if (tcp->th_flags & TH_ACK) { + tcp2->th_seq = tcp->th_ack; + tcp2->th_flags = TH_RST; + tcp2->th_ack = 0; + } else { + tcp2->th_seq = 0; + tcp2->th_ack = ntohl(tcp->th_seq); + tcp2->th_ack += tlen; + tcp2->th_ack = htonl(tcp2->th_ack); + tcp2->th_flags = TH_RST|TH_ACK; + } + TCP_X2_A(tcp2, 0); + TCP_OFF_A(tcp2, sizeof(*tcp2) >> 2); + tcp2->th_win = tcp->th_win; + tcp2->th_sum = 0; + tcp2->th_urp = 0; + +#ifdef USE_INET6 + if (fin->fin_v == 6) { + ip6->ip6_flow = ((ip6_t *)fin->fin_ip)->ip6_flow; + ip6->ip6_plen = htons(sizeof(struct tcphdr)); + ip6->ip6_nxt = IPPROTO_TCP; + ip6->ip6_hlim = 0; + ip6->ip6_src = fin->fin_dst6; + ip6->ip6_dst = fin->fin_src6; + tcp2->th_sum = in6_cksum(m, IPPROTO_TCP, + sizeof(*ip6), sizeof(*tcp2)); + return fr_send_ip(fin, m, &m); + } +#endif + ip->ip_p = IPPROTO_TCP; + ip->ip_len = htons(sizeof(struct tcphdr)); + ip->ip_src.s_addr = fin->fin_daddr; + ip->ip_dst.s_addr = fin->fin_saddr; + tcp2->th_sum = in_cksum(m, hlen + sizeof(*tcp2)); + ip->ip_len = hlen + sizeof(*tcp2); + return fr_send_ip(fin, m, &m); +} + + +static int fr_send_ip(fin, m, mpp) +fr_info_t *fin; +mb_t *m, **mpp; +{ + fr_info_t fnew; + ip_t *ip, *oip; + int hlen; + + ip = mtod(m, ip_t *); + bzero((char *)&fnew, sizeof(fnew)); + + IP_V_A(ip, fin->fin_v); + switch (fin->fin_v) + { + case 4 : + fnew.fin_v = 4; + oip = fin->fin_ip; + IP_HL_A(ip, sizeof(*oip) >> 2); + ip->ip_tos = oip->ip_tos; + ip->ip_id = fin->fin_ip->ip_id; +#if (__FreeBSD_version > 460000) + ip->ip_off = path_mtu_discovery ? IP_DF : 0; +#else + ip->ip_off = 0; +#endif + ip->ip_ttl = ip_defttl; + ip->ip_sum = 0; + hlen = sizeof(*oip); + break; +#ifdef USE_INET6 + case 6 : + { + ip6_t *ip6 = (ip6_t *)ip; + + ip6->ip6_vfc = 0x60; + ip6->ip6_hlim = IPDEFTTL; + + fnew.fin_v = 6; + hlen = sizeof(*ip6); + break; + } +#endif + default : + return EINVAL; + } +#ifdef IPSEC + m->m_pkthdr.rcvif = NULL; +#endif + + fnew.fin_ifp = fin->fin_ifp; + fnew.fin_flx = FI_NOCKSUM; + fnew.fin_m = m; + fnew.fin_ip = ip; + fnew.fin_mp = mpp; + fnew.fin_hlen = hlen; + fnew.fin_dp = (char *)ip + hlen; + (void) fr_makefrip(hlen, ip, &fnew); + + return fr_fastroute(m, mpp, &fnew, NULL); +} + + +int fr_send_icmp_err(type, fin, dst) +int type; +fr_info_t *fin; +int dst; +{ + int err, hlen, xtra, iclen, ohlen, avail, code; + struct in_addr dst4; + struct icmp *icmp; + struct mbuf *m; + void *ifp; +#ifdef USE_INET6 + ip6_t *ip6; + struct in6_addr dst6; +#endif + ip_t *ip, *ip2; + + if ((type < 0) || (type > ICMP_MAXTYPE)) + return -1; + + code = fin->fin_icode; +#ifdef USE_INET6 + if ((code < 0) || (code > sizeof(icmptoicmp6unreach)/sizeof(int))) + return -1; +#endif + +#ifndef IPFILTER_CKSUM + if (fr_checkl4sum(fin) == -1) + return -1; +#endif +#ifdef MGETHDR + MGETHDR(m, M_DONTWAIT, MT_HEADER); +#else + MGET(m, M_DONTWAIT, MT_HEADER); +#endif + if (m == NULL) + return -1; + avail = MHLEN; + + xtra = 0; + hlen = 0; + ohlen = 0; + ifp = fin->fin_ifp; + if (fin->fin_v == 4) { + if ((fin->fin_p == IPPROTO_ICMP) && + !(fin->fin_flx & FI_SHORT)) + switch (ntohs(fin->fin_data[0]) >> 8) + { + case ICMP_ECHO : + case ICMP_TSTAMP : + case ICMP_IREQ : + case ICMP_MASKREQ : + break; + default : + FREE_MB_T(m); + return 0; + } + + if (dst == 0) { + if (fr_ifpaddr(4, FRI_NORMAL, ifp, + &dst4, NULL) == -1) { + FREE_MB_T(m); + return -1; + } + } else + dst4.s_addr = fin->fin_daddr; + + hlen = sizeof(ip_t); + ohlen = fin->fin_hlen; + if (fin->fin_hlen < fin->fin_plen) + xtra = MIN(fin->fin_dlen, 8); + else + xtra = 0; + } + +#ifdef USE_INET6 + else if (fin->fin_v == 6) { + hlen = sizeof(ip6_t); + ohlen = sizeof(ip6_t); + type = icmptoicmp6types[type]; + if (type == ICMP6_DST_UNREACH) + code = icmptoicmp6unreach[code]; + + if (hlen + sizeof(*icmp) + max_linkhdr + + fin->fin_plen > avail) { + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + FREE_MB_T(m); + return -1; + } + avail = MCLBYTES; + } + xtra = MIN(fin->fin_plen, + avail - hlen - sizeof(*icmp) - max_linkhdr); + if (dst == 0) { + if (fr_ifpaddr(6, FRI_NORMAL, ifp, + (struct in_addr *)&dst6, NULL) == -1) { + FREE_MB_T(m); + return -1; + } + } else + dst6 = fin->fin_dst6; + } +#endif + else { + FREE_MB_T(m); + return -1; + } + + iclen = hlen + sizeof(*icmp); + avail -= (max_linkhdr + iclen); + if (avail < 0) { + FREE_MB_T(m); + return -1; + } + if (xtra > avail) + xtra = avail; + iclen += xtra; + m->m_data += max_linkhdr; + m->m_pkthdr.rcvif = (struct ifnet *)0; + m->m_pkthdr.len = iclen; + m->m_len = iclen; + ip = mtod(m, ip_t *); + icmp = (struct icmp *)((char *)ip + hlen); + ip2 = (ip_t *)&icmp->icmp_ip; + + icmp->icmp_type = type; + icmp->icmp_code = fin->fin_icode; + icmp->icmp_cksum = 0; +#ifdef icmp_nextmtu + if (type == ICMP_UNREACH && + fin->fin_icode == ICMP_UNREACH_NEEDFRAG && ifp) + icmp->icmp_nextmtu = htons(((struct ifnet *)ifp)->if_mtu); +#endif + + bcopy((char *)fin->fin_ip, (char *)ip2, ohlen); + +#ifdef USE_INET6 + ip6 = (ip6_t *)ip; + if (fin->fin_v == 6) { + ip6->ip6_flow = ((ip6_t *)fin->fin_ip)->ip6_flow; + ip6->ip6_plen = htons(iclen - hlen); + ip6->ip6_nxt = IPPROTO_ICMPV6; + ip6->ip6_hlim = 0; + ip6->ip6_src = dst6; + ip6->ip6_dst = fin->fin_src6; + if (xtra > 0) + bcopy((char *)fin->fin_ip + ohlen, + (char *)&icmp->icmp_ip + ohlen, xtra); + icmp->icmp_cksum = in6_cksum(m, IPPROTO_ICMPV6, + sizeof(*ip6), iclen - hlen); + } else +#endif + { + ip2->ip_len = htons(ip2->ip_len); + ip2->ip_off = htons(ip2->ip_off); + ip->ip_p = IPPROTO_ICMP; + ip->ip_src.s_addr = dst4.s_addr; + ip->ip_dst.s_addr = fin->fin_saddr; + + if (xtra > 0) + bcopy((char *)fin->fin_ip + ohlen, + (char *)&icmp->icmp_ip + ohlen, xtra); + icmp->icmp_cksum = ipf_cksum((u_short *)icmp, + sizeof(*icmp) + 8); + ip->ip_len = iclen; + ip->ip_p = IPPROTO_ICMP; + } + err = fr_send_ip(fin, m, &m); + return err; +} + + +#if !defined(IPFILTER_LKM) && (__FreeBSD_version < 300000) +# if (BSD < 199306) +int iplinit __P((void)); + +int +# else +void iplinit __P((void)); + +void +# endif +iplinit() +{ + if (iplattach() != 0) + printf("IP Filter failed to attach\n"); + ip_init(); +} +#endif /* __FreeBSD_version < 300000 */ + + +int fr_fastroute(m0, mpp, fin, fdp) +mb_t *m0, **mpp; +fr_info_t *fin; +frdest_t *fdp; +{ + register struct ip *ip, *mhip; + register struct mbuf *m = m0; + register struct route *ro; + int len, off, error = 0, hlen, code; + struct ifnet *ifp, *sifp; + struct sockaddr_in *dst; + struct route iproute; + u_short ip_off; + frentry_t *fr; + +#ifdef M_WRITABLE + /* + * HOT FIX/KLUDGE: + * + * If the mbuf we're about to send is not writable (because of + * a cluster reference, for example) we'll need to make a copy + * of it since this routine modifies the contents. + * + * If you have non-crappy network hardware that can transmit data + * from the mbuf, rather than making a copy, this is gonna be a + * problem. + */ + if (M_WRITABLE(m) == 0) { + if ((m0 = m_dup(m, M_DONTWAIT)) != 0) { + FREE_MB_T(m); + m = m0; + *mpp = m; + } else { + error = ENOBUFS; + FREE_MB_T(m); + *mpp = NULL; + fr_frouteok[1]++; + } + } +#endif + +#ifdef USE_INET6 + if (fin->fin_v == 6) { + /* + * currently "to <if>" and "to <if>:ip#" are not supported + * for IPv6 + */ +#if (__FreeBSD_version >= 490000) + return ip6_output(m0, NULL, NULL, 0, NULL, NULL, NULL); +#else + return ip6_output(m0, NULL, NULL, 0, NULL, NULL); +#endif + } +#endif + + hlen = fin->fin_hlen; + ip = mtod(m0, struct ip *); + + /* + * Route packet. + */ + ro = &iproute; + bzero((caddr_t)ro, sizeof (*ro)); + dst = (struct sockaddr_in *)&ro->ro_dst; + dst->sin_family = AF_INET; + dst->sin_addr = ip->ip_dst; + + fr = fin->fin_fr; + if (fdp != NULL) + ifp = fdp->fd_ifp; + else + ifp = fin->fin_ifp; + + if ((ifp == NULL) && (!fr || !(fr->fr_flags & FR_FASTROUTE))) { + error = -2; + goto bad; + } + + /* + * In case we're here due to "to <if>" being used with "keep state", + * check that we're going in the correct direction. + */ + if ((fr != NULL) && (fin->fin_rev != 0)) { + if ((ifp != NULL) && (fdp == &fr->fr_tif)) + return -1; + } + if (fdp != NULL) { + if (fdp->fd_ip.s_addr != 0) + dst->sin_addr = fdp->fd_ip; + } + + dst->sin_len = sizeof(*dst); + rtalloc(ro); + + if ((ifp == NULL) && (ro->ro_rt != NULL)) + ifp = ro->ro_rt->rt_ifp; + + if ((ro->ro_rt == NULL) || (ifp == NULL)) { + if (in_localaddr(ip->ip_dst)) + error = EHOSTUNREACH; + else + error = ENETUNREACH; + goto bad; + } + if (ro->ro_rt->rt_flags & RTF_GATEWAY) + dst = (struct sockaddr_in *)ro->ro_rt->rt_gateway; + if (ro->ro_rt) + ro->ro_rt->rt_use++; + + /* + * For input packets which are being "fastrouted", they won't + * go back through output filtering and miss their chance to get + * NAT'd and counted. + */ + if (fin->fin_out == 0) { + sifp = fin->fin_ifp; + fin->fin_ifp = ifp; + fin->fin_out = 1; + (void) fr_acctpkt(fin, NULL); + fin->fin_fr = NULL; + if (!fr || !(fr->fr_flags & FR_RETMASK)) { + u_32_t pass; + + (void) fr_checkstate(fin, &pass); + } + + switch (fr_checknatout(fin, NULL)) + { + case 0 : + break; + case 1 : + ip->ip_sum = 0; + break; + case -1 : + error = -1; + goto done; + break; + } + + fin->fin_ifp = sifp; + fin->fin_out = 0; + } else + ip->ip_sum = 0; + /* + * If small enough for interface, can just send directly. + */ + if (ip->ip_len <= ifp->if_mtu) { + ip->ip_len = htons(ip->ip_len); + ip->ip_off = htons(ip->ip_off); + + if (!ip->ip_sum) + ip->ip_sum = in_cksum(m, hlen); + error = (*ifp->if_output)(ifp, m, (struct sockaddr *)dst, + ro->ro_rt); + goto done; + } + /* + * Too large for interface; fragment if possible. + * Must be able to put at least 8 bytes per fragment. + */ + ip_off = ntohs(ip->ip_off); + if (ip_off & IP_DF) { + error = EMSGSIZE; + goto bad; + } + len = (ifp->if_mtu - hlen) &~ 7; + if (len < 8) { + error = EMSGSIZE; + goto bad; + } + + { + int mhlen, firstlen = len; + struct mbuf **mnext = &m->m_act; + + /* + * Loop through length of segment after first fragment, + * make new header and copy data of each part and link onto chain. + */ + m0 = m; + mhlen = sizeof (struct ip); + for (off = hlen + len; off < ip->ip_len; off += len) { +#ifdef MGETHDR + MGETHDR(m, M_DONTWAIT, MT_HEADER); +#else + MGET(m, M_DONTWAIT, MT_HEADER); +#endif + if (m == 0) { + m = m0; + error = ENOBUFS; + goto bad; + } + m->m_data += max_linkhdr; + mhip = mtod(m, struct ip *); + bcopy((char *)ip, (char *)mhip, sizeof(*ip)); + if (hlen > sizeof (struct ip)) { + mhlen = ip_optcopy(ip, mhip) + sizeof (struct ip); + IP_HL_A(mhip, mhlen >> 2); + } + m->m_len = mhlen; + mhip->ip_off = ((off - hlen) >> 3) + ip_off; + if (off + len >= ip->ip_len) + len = ip->ip_len - off; + else + mhip->ip_off |= IP_MF; + mhip->ip_len = htons((u_short)(len + mhlen)); + m->m_next = m_copy(m0, off, len); + if (m->m_next == 0) { + error = ENOBUFS; /* ??? */ + goto sendorfree; + } + m->m_pkthdr.len = mhlen + len; + m->m_pkthdr.rcvif = NULL; + mhip->ip_off = htons((u_short)mhip->ip_off); + mhip->ip_sum = 0; + mhip->ip_sum = in_cksum(m, mhlen); + *mnext = m; + mnext = &m->m_act; + } + /* + * Update first fragment by trimming what's been copied out + * and updating header, then send each fragment (in order). + */ + m_adj(m0, hlen + firstlen - ip->ip_len); + ip->ip_len = htons((u_short)(hlen + firstlen)); + ip->ip_off = htons((u_short)IP_MF); + ip->ip_sum = 0; + ip->ip_sum = in_cksum(m0, hlen); +sendorfree: + for (m = m0; m; m = m0) { + m0 = m->m_act; + m->m_act = 0; + if (error == 0) + error = (*ifp->if_output)(ifp, m, + (struct sockaddr *)dst, ro->ro_rt); + else + FREE_MB_T(m); + } + } +done: + if (!error) + fr_frouteok[0]++; + else + fr_frouteok[1]++; + + if (ro->ro_rt) { + RTFREE(ro->ro_rt); + } + *mpp = NULL; + return 0; +bad: + if (error == EMSGSIZE) { + sifp = fin->fin_ifp; + code = fin->fin_icode; + fin->fin_icode = ICMP_UNREACH_NEEDFRAG; + fin->fin_ifp = ifp; + (void) fr_send_icmp_err(ICMP_UNREACH, fin, 1); + fin->fin_ifp = sifp; + fin->fin_icode = code; + } + FREE_MB_T(m); + goto done; +} + + +int fr_verifysrc(fin) +fr_info_t *fin; +{ + struct sockaddr_in *dst; + struct route iproute; + + bzero((char *)&iproute, sizeof(iproute)); + dst = (struct sockaddr_in *)&iproute.ro_dst; + dst->sin_len = sizeof(*dst); + dst->sin_family = AF_INET; + dst->sin_addr = fin->fin_src; + rtalloc(&iproute); + if (iproute.ro_rt == NULL) + return 0; + return (fin->fin_ifp == iproute.ro_rt->rt_ifp); +} + + +/* + * return the first IP Address associated with an interface + */ +int fr_ifpaddr(v, atype, ifptr, inp, inpmask) +int v, atype; +void *ifptr; +struct in_addr *inp, *inpmask; +{ +#ifdef USE_INET6 + struct in6_addr *inp6 = NULL; +#endif + struct sockaddr *sock, *mask; + struct sockaddr_in *sin; + struct ifaddr *ifa; + struct ifnet *ifp; + + if ((ifptr == NULL) || (ifptr == (void *)-1)) + return -1; + + sin = NULL; + ifp = ifptr; + + if (v == 4) + inp->s_addr = 0; +#ifdef USE_INET6 + else if (v == 6) + bzero((char *)inp, sizeof(struct in6_addr)); +#endif +#if (__FreeBSD_version >= 300000) + ifa = TAILQ_FIRST(&ifp->if_addrhead); +#else + ifa = ifp->if_addrlist; +#endif /* __FreeBSD_version >= 300000 */ + + sock = ifa->ifa_addr; + while (sock != NULL && ifa != NULL) { + sin = (struct sockaddr_in *)sock; + if ((v == 4) && (sin->sin_family == AF_INET)) + break; +#ifdef USE_INET6 + if ((v == 6) && (sin->sin_family == AF_INET6)) { + inp6 = &((struct sockaddr_in6 *)sin)->sin6_addr; + if (!IN6_IS_ADDR_LINKLOCAL(inp6) && + !IN6_IS_ADDR_LOOPBACK(inp6)) + break; + } +#endif +#if (__FreeBSD_version >= 300000) + ifa = TAILQ_NEXT(ifa, ifa_link); +#else + ifa = ifa->ifa_next; +#endif /* __FreeBSD_version >= 300000 */ + if (ifa != NULL) + sock = ifa->ifa_addr; + } + + if (ifa == NULL || sin == NULL) + return -1; + + mask = ifa->ifa_netmask; + if (atype == FRI_BROADCAST) + sock = ifa->ifa_broadaddr; + else if (atype == FRI_PEERADDR) + sock = ifa->ifa_dstaddr; + +#ifdef USE_INET6 + if (v == 6) { + return fr_ifpfillv6addr(atype, (struct sockaddr_in6 *)sock, + (struct sockaddr_in6 *)mask, + inp, inpmask); + } +#endif + return fr_ifpfillv4addr(atype, (struct sockaddr_in *)sock, + (struct sockaddr_in *)mask, inp, inpmask); +} + + +u_32_t fr_newisn(fin) +fr_info_t *fin; +{ + u_32_t newiss; +#if (__FreeBSD_version >= 400000) + newiss = arc4random(); +#else + static iss_seq_off = 0; + u_char hash[16]; + MD5_CTX ctx; + + /* + * Compute the base value of the ISS. It is a hash + * of (saddr, sport, daddr, dport, secret). + */ + MD5Init(&ctx); + + MD5Update(&ctx, (u_char *) &fin->fin_fi.fi_src, + sizeof(fin->fin_fi.fi_src)); + MD5Update(&ctx, (u_char *) &fin->fin_fi.fi_dst, + sizeof(fin->fin_fi.fi_dst)); + MD5Update(&ctx, (u_char *) &fin->fin_dat, sizeof(fin->fin_dat)); + + MD5Update(&ctx, ipf_iss_secret, sizeof(ipf_iss_secret)); + + MD5Final(hash, &ctx); + + memcpy(&newiss, hash, sizeof(newiss)); + + /* + * Now increment our "timer", and add it in to + * the computed value. + * + * XXX Use `addin'? + * XXX TCP_ISSINCR too large to use? + */ + iss_seq_off += 0x00010000; + newiss += iss_seq_off; +#endif + return newiss; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: fr_nextipid */ +/* Returns: int - 0 == success, -1 == error (packet should be droppped) */ +/* Parameters: fin(I) - pointer to packet information */ +/* */ +/* Returns the next IPv4 ID to use for this packet. */ +/* ------------------------------------------------------------------------ */ +u_short fr_nextipid(fin) +fr_info_t *fin; +{ +#ifndef RANDOM_IP_ID + static u_short ipid = 0; + u_short id; + + MUTEX_ENTER(&ipf_rw); + id = ipid++; + MUTEX_EXIT(&ipf_rw); +#else + u_short id; + + id = ip_randomid(); +#endif + + return id; +} + + +INLINE void fr_checkv4sum(fin) +fr_info_t *fin; +{ +#ifdef CSUM_DATA_VALID + int manual = 0; + u_short sum; + ip_t *ip; + mb_t *m; + + if ((fin->fin_flx & FI_NOCKSUM) != 0) + return; + + m = fin->fin_m; + if (m == NULL) { + manual = 1; + goto skipauto; + } + ip = fin->fin_ip; + + if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID) { + if (m->m_pkthdr.csum_flags & CSUM_PSEUDO_HDR) + sum = m->m_pkthdr.csum_data; + else + sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, + htonl(m->m_pkthdr.csum_data + + fin->fin_ip->ip_len + fin->fin_p)); + sum ^= 0xffff; + if (sum != 0) + fin->fin_flx |= FI_BAD; + } else + manual = 1; +skipauto: +# ifdef IPFILTER_CKSUM + if (manual != 0) + if (fr_checkl4sum(fin) == -1) + fin->fin_flx |= FI_BAD; +# else + ; +# endif +#else +# ifdef IPFILTER_CKSUM + if (fr_checkl4sum(fin) == -1) + fin->fin_flx |= FI_BAD; +# endif +#endif +} + + +#ifdef USE_INET6 +INLINE void fr_checkv6sum(fin) +fr_info_t *fin; +{ +# ifdef IPFILTER_CKSUM + if (fr_checkl4sum(fin) == -1) + fin->fin_flx |= FI_BAD; +# endif +} +#endif /* USE_INET6 */ + + +size_t mbufchainlen(m0) +struct mbuf *m0; +{ + size_t len; + + if ((m0->m_flags & M_PKTHDR) != 0) { + len = m0->m_pkthdr.len; + } else { + struct mbuf *m; + + for (m = m0, len = 0; m != NULL; m = m->m_next) + len += m->m_len; + } + return len; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: fr_pullup */ +/* Returns: NULL == pullup failed, else pointer to protocol header */ +/* Parameters: m(I) - pointer to buffer where data packet starts */ +/* fin(I) - pointer to packet information */ +/* len(I) - number of bytes to pullup */ +/* */ +/* Attempt to move at least len bytes (from the start of the buffer) into a */ +/* single buffer for ease of access. Operating system native functions are */ +/* used to manage buffers - if necessary. If the entire packet ends up in */ +/* a single buffer, set the FI_COALESCE flag even though fr_coalesce() has */ +/* not been called. Both fin_ip and fin_dp are updated before exiting _IF_ */ +/* and ONLY if the pullup succeeds. */ +/* */ +/* We assume that 'min' is a pointer to a buffer that is part of the chain */ +/* of buffers that starts at *fin->fin_mp. */ +/* ------------------------------------------------------------------------ */ +void *fr_pullup(min, fin, len) +mb_t *min; +fr_info_t *fin; +int len; +{ + int out = fin->fin_out, dpoff, ipoff; + mb_t *m = min; + char *ip; + + if (m == NULL) + return NULL; + + ip = (char *)fin->fin_ip; + if ((fin->fin_flx & FI_COALESCE) != 0) + return ip; + + ipoff = fin->fin_ipoff; + if (fin->fin_dp != NULL) + dpoff = (char *)fin->fin_dp - (char *)ip; + else + dpoff = 0; + + if (M_LEN(m) < len) { +#ifdef MHLEN + /* + * Assume that M_PKTHDR is set and just work with what is left + * rather than check.. + * Should not make any real difference, anyway. + */ + if (len > MHLEN) +#else + if (len > MLEN) +#endif + { +#ifdef HAVE_M_PULLDOWN + if (m_pulldown(m, 0, len, NULL) == NULL) + m = NULL; +#else + FREE_MB_T(*fin->fin_mp); + m = NULL; +#endif + } else + { + m = m_pullup(m, len); + } + *fin->fin_mp = m; + fin->fin_m = m; + if (m == NULL) { + ATOMIC_INCL(frstats[out].fr_pull[1]); + return NULL; + } + ip = MTOD(m, char *) + ipoff; + } + + ATOMIC_INCL(frstats[out].fr_pull[0]); + fin->fin_ip = (ip_t *)ip; + if (fin->fin_dp != NULL) + fin->fin_dp = (char *)fin->fin_ip + dpoff; + + if (len == fin->fin_plen) + fin->fin_flx |= FI_COALESCE; + return ip; +} |