ip_fil_freebsd.c revision 255332
1145516Sdarrenr/* $FreeBSD: head/sys/contrib/ipfilter/netinet/ip_fil_freebsd.c 255332 2013-09-06 23:11:19Z cy $ */ 2145516Sdarrenr 3145516Sdarrenr/* 4255332Scy * Copyright (C) 2012 by Darren Reed. 5145516Sdarrenr * 6145516Sdarrenr * See the IPFILTER.LICENCE file for details on licencing. 7145516Sdarrenr */ 8145516Sdarrenr#if !defined(lint) 9145516Sdarrenrstatic const char sccsid[] = "@(#)ip_fil.c 2.41 6/5/96 (C) 1993-2000 Darren Reed"; 10255332Scystatic const char rcsid[] = "@(#)$Id$"; 11145516Sdarrenr#endif 12145516Sdarrenr 13145516Sdarrenr#if defined(KERNEL) || defined(_KERNEL) 14145516Sdarrenr# undef KERNEL 15145516Sdarrenr# undef _KERNEL 16145516Sdarrenr# define KERNEL 1 17145516Sdarrenr# define _KERNEL 1 18145516Sdarrenr#endif 19145516Sdarrenr#if defined(__FreeBSD_version) && (__FreeBSD_version >= 400000) && \ 20145516Sdarrenr !defined(KLD_MODULE) && !defined(IPFILTER_LKM) 21145516Sdarrenr# include "opt_inet6.h" 22145516Sdarrenr#endif 23145516Sdarrenr#if defined(__FreeBSD_version) && (__FreeBSD_version >= 440000) && \ 24145516Sdarrenr !defined(KLD_MODULE) && !defined(IPFILTER_LKM) 25145516Sdarrenr# include "opt_random_ip_id.h" 26145516Sdarrenr#endif 27145516Sdarrenr#include <sys/param.h> 28145516Sdarrenr#include <sys/errno.h> 29145516Sdarrenr#include <sys/types.h> 30145516Sdarrenr#include <sys/file.h> 31145516Sdarrenr# include <sys/fcntl.h> 32145516Sdarrenr# include <sys/filio.h> 33145516Sdarrenr#include <sys/time.h> 34145516Sdarrenr#include <sys/systm.h> 35145516Sdarrenr# include <sys/dirent.h> 36255332Scy# include <sys/mbuf.h> 37255332Scy# include <sys/sockopt.h> 38145516Sdarrenr#if !defined(__hpux) 39145516Sdarrenr# include <sys/mbuf.h> 40145516Sdarrenr#endif 41145516Sdarrenr#include <sys/socket.h> 42170268Sdarrenr# include <sys/selinfo.h> 43195699Srwatson# include <netinet/tcp_var.h> 44145516Sdarrenr 45145516Sdarrenr#include <net/if.h> 46145516Sdarrenr# include <net/if_var.h> 47170268Sdarrenr# include <net/netisr.h> 48145516Sdarrenr#include <net/route.h> 49145516Sdarrenr#include <netinet/in.h> 50145516Sdarrenr#include <netinet/in_var.h> 51145516Sdarrenr#include <netinet/in_systm.h> 52145516Sdarrenr#include <netinet/ip.h> 53145516Sdarrenr#include <netinet/ip_var.h> 54145516Sdarrenr#include <netinet/tcp.h> 55145516Sdarrenr#if defined(__osf__) 56145516Sdarrenr# include <netinet/tcp_timer.h> 57145516Sdarrenr#endif 58145516Sdarrenr#include <netinet/udp.h> 59145516Sdarrenr#include <netinet/tcpip.h> 60145516Sdarrenr#include <netinet/ip_icmp.h> 61145516Sdarrenr#include "netinet/ip_compat.h" 62145516Sdarrenr#ifdef USE_INET6 63145516Sdarrenr# include <netinet/icmp6.h> 64145516Sdarrenr#endif 65145516Sdarrenr#include "netinet/ip_fil.h" 66145516Sdarrenr#include "netinet/ip_nat.h" 67145516Sdarrenr#include "netinet/ip_frag.h" 68145516Sdarrenr#include "netinet/ip_state.h" 69145516Sdarrenr#include "netinet/ip_proxy.h" 70145516Sdarrenr#include "netinet/ip_auth.h" 71145516Sdarrenr#include "netinet/ip_sync.h" 72255332Scy#include "netinet/ip_lookup.h" 73255332Scy#include "netinet/ip_dstlist.h" 74145516Sdarrenr#ifdef IPFILTER_SCAN 75145516Sdarrenr#include "netinet/ip_scan.h" 76145516Sdarrenr#endif 77145516Sdarrenr#include "netinet/ip_pool.h" 78145516Sdarrenr# include <sys/malloc.h> 79145516Sdarrenr#include <sys/kernel.h> 80145516Sdarrenr#ifdef CSUM_DATA_VALID 81145516Sdarrenr#include <machine/in_cksum.h> 82145516Sdarrenr#endif 83145516Sdarrenrextern int ip_optcopy __P((struct ip *, struct ip *)); 84145516Sdarrenr 85145516Sdarrenr 86145516Sdarrenr# ifdef IPFILTER_M_IPFILTER 87151897SrwatsonMALLOC_DEFINE(M_IPFILTER, "ipfilter", "IP Filter packet filter data structures"); 88145516Sdarrenr# endif 89145516Sdarrenr 90145516Sdarrenr 91255332Scystatic u_short ipid = 0; 92255332Scystatic int (*ipf_savep) __P((void *, ip_t *, int, void *, int, struct mbuf **)); 93255332Scystatic int ipf_send_ip __P((fr_info_t *, mb_t *)); 94255332Scystatic void ipf_timer_func __P((void *arg)); 95145516Sdarrenrint ipf_locks_done = 0; 96145516Sdarrenr 97255332Scyipf_main_softc_t ipfmain; 98145516Sdarrenr 99145516Sdarrenr# include <sys/conf.h> 100145516Sdarrenr# if defined(NETBSD_PF) 101145516Sdarrenr# include <net/pfil.h> 102255332Scy# endif /* NETBSD_PF */ 103145516Sdarrenr/* 104255332Scy * We provide the ipf_checkp name just to minimize changes later. 105145516Sdarrenr */ 106255332Scyint (*ipf_checkp) __P((void *, ip_t *ip, int hlen, void *ifp, int out, mb_t **mp)); 107145516Sdarrenr 108145516Sdarrenr 109153876Sguidostatic eventhandler_tag ipf_arrivetag, ipf_departtag, ipf_clonetag; 110153876Sguido 111153876Sguidostatic void ipf_ifevent(void *arg); 112153876Sguido 113153876Sguidostatic void ipf_ifevent(arg) 114255332Scy void *arg; 115153876Sguido{ 116255332Scy ipf_sync(arg, NULL); 117153876Sguido} 118153876Sguido 119153876Sguido 120145516Sdarrenr 121145516Sdarrenrstatic int 122255332Scyipf_check_wrapper(void *arg, struct mbuf **mp, struct ifnet *ifp, int dir) 123145516Sdarrenr{ 124145516Sdarrenr struct ip *ip = mtod(*mp, struct ip *); 125255332Scy int rv; 126255332Scy 127255332Scy /* 128255332Scy * IPFilter expects evreything in network byte order 129255332Scy */ 130255332Scy#if (__FreeBSD_version < 1000019) 131255332Scy ip->ip_len = htons(ip->ip_len); 132255332Scy ip->ip_off = htons(ip->ip_off); 133255332Scy#endif 134255332Scy rv = ipf_check(&ipfmain, ip, ip->ip_hl << 2, ifp, (dir == PFIL_OUT), 135255332Scy mp); 136255332Scy#if (__FreeBSD_version < 1000019) 137255332Scy if ((rv == 0) && (*mp != NULL)) { 138255332Scy ip = mtod(*mp, struct ip *); 139255332Scy ip->ip_len = ntohs(ip->ip_len); 140255332Scy ip->ip_off = ntohs(ip->ip_off); 141255332Scy } 142255332Scy#endif 143255332Scy return rv; 144145516Sdarrenr} 145145516Sdarrenr 146145516Sdarrenr# ifdef USE_INET6 147145516Sdarrenr# include <netinet/ip6.h> 148145516Sdarrenr 149145516Sdarrenrstatic int 150255332Scyipf_check_wrapper6(void *arg, struct mbuf **mp, struct ifnet *ifp, int dir) 151145516Sdarrenr{ 152255332Scy return (ipf_check(&ipfmain, mtod(*mp, struct ip *), 153255332Scy sizeof(struct ip6_hdr), ifp, (dir == PFIL_OUT), mp)); 154145516Sdarrenr} 155145516Sdarrenr# endif 156145516Sdarrenr#if defined(IPFILTER_LKM) 157255332Scyint ipf_identify(s) 158255332Scy char *s; 159145516Sdarrenr{ 160145516Sdarrenr if (strcmp(s, "ipl") == 0) 161145516Sdarrenr return 1; 162145516Sdarrenr return 0; 163145516Sdarrenr} 164145516Sdarrenr#endif /* IPFILTER_LKM */ 165145516Sdarrenr 166145516Sdarrenr 167255332Scystatic void 168255332Scyipf_timer_func(arg) 169255332Scy void *arg; 170145516Sdarrenr{ 171255332Scy ipf_main_softc_t *softc = arg; 172255332Scy SPL_INT(s); 173255332Scy 174255332Scy SPL_NET(s); 175255332Scy READ_ENTER(&softc->ipf_global); 176255332Scy 177255332Scy if (softc->ipf_running > 0) 178255332Scy ipf_slowtimer(softc); 179255332Scy 180255332Scy if (softc->ipf_running == -1 || softc->ipf_running == 1) { 181255332Scy#if FREEBSD_GE_REV(300000) 182255332Scy softc->ipf_slow_ch = timeout(ipf_timer_func, softc, hz/2); 183255332Scy#else 184255332Scy timeout(ipf_timer_func, softc, hz/2); 185255332Scy#endif 186255332Scy } 187255332Scy RWLOCK_EXIT(&softc->ipf_global); 188255332Scy SPL_X(s); 189255332Scy} 190255332Scy 191255332Scy 192255332Scyint 193255332Scyipfattach(softc) 194255332Scy ipf_main_softc_t *softc; 195255332Scy{ 196145516Sdarrenr#ifdef USE_SPL 197145516Sdarrenr int s; 198145516Sdarrenr#endif 199145516Sdarrenr 200145516Sdarrenr SPL_NET(s); 201255332Scy if (softc->ipf_running > 0) { 202145516Sdarrenr SPL_X(s); 203145516Sdarrenr return EBUSY; 204145516Sdarrenr } 205145516Sdarrenr 206255332Scy if (ipf_init_all(softc) < 0) { 207145516Sdarrenr SPL_X(s); 208145516Sdarrenr return EIO; 209145516Sdarrenr } 210145516Sdarrenr 211145516Sdarrenr 212255332Scy if (ipf_checkp != ipf_check) { 213255332Scy ipf_savep = ipf_checkp; 214255332Scy ipf_checkp = ipf_check; 215145516Sdarrenr } 216145516Sdarrenr 217255332Scy bzero((char *)ipfmain.ipf_selwait, sizeof(ipfmain.ipf_selwait)); 218255332Scy softc->ipf_running = 1; 219145516Sdarrenr 220255332Scy if (softc->ipf_control_forwarding & 1) 221181803Sbz V_ipforwarding = 1; 222145516Sdarrenr 223255332Scy ipid = 0; 224255332Scy 225145516Sdarrenr SPL_X(s); 226255332Scy softc->ipf_slow_ch = timeout(ipf_timer_func, softc, 227255332Scy (hz / IPF_HZ_DIVIDE) * IPF_HZ_MULT); 228145516Sdarrenr return 0; 229145516Sdarrenr} 230145516Sdarrenr 231145516Sdarrenr 232145516Sdarrenr/* 233145516Sdarrenr * Disable the filter by removing the hooks from the IP input/output 234145516Sdarrenr * stream. 235145516Sdarrenr */ 236255332Scyint 237255332Scyipfdetach(softc) 238255332Scy ipf_main_softc_t *softc; 239145516Sdarrenr{ 240145516Sdarrenr#ifdef USE_SPL 241145516Sdarrenr int s; 242145516Sdarrenr#endif 243255332Scy 244255332Scy if (softc->ipf_control_forwarding & 2) 245181803Sbz V_ipforwarding = 0; 246145516Sdarrenr 247145516Sdarrenr SPL_NET(s); 248145516Sdarrenr 249255332Scy if (softc->ipf_slow_ch.callout != NULL) 250255332Scy untimeout(ipf_timer_func, softc, softc->ipf_slow_ch); 251255332Scy bzero(&softc->ipf_slow, sizeof(softc->ipf_slow)); 252145516Sdarrenr 253145516Sdarrenr#ifndef NETBSD_PF 254255332Scy if (ipf_checkp != NULL) 255255332Scy ipf_checkp = ipf_savep; 256255332Scy ipf_savep = NULL; 257145516Sdarrenr#endif 258145516Sdarrenr 259255332Scy ipf_fini_all(softc); 260145516Sdarrenr 261255332Scy softc->ipf_running = -2; 262145516Sdarrenr 263145516Sdarrenr SPL_X(s); 264145516Sdarrenr 265145516Sdarrenr return 0; 266145516Sdarrenr} 267145516Sdarrenr 268145516Sdarrenr 269145516Sdarrenr/* 270145516Sdarrenr * Filter ioctl interface. 271145516Sdarrenr */ 272255332Scyint 273255332Scyipfioctl(dev, cmd, data, mode 274145516Sdarrenr, p) 275255332Scy struct thread *p; 276192895Sjamie# define p_cred td_ucred 277170268Sdarrenr# define p_uid td_ucred->cr_ruid 278255332Scy struct cdev *dev; 279255332Scy ioctlcmd_t cmd; 280255332Scy caddr_t data; 281255332Scy int mode; 282145516Sdarrenr{ 283170268Sdarrenr int error = 0, unit = 0; 284170268Sdarrenr SPL_INT(s); 285145516Sdarrenr 286255332Scy#if (BSD >= 199306) 287255332Scy if (securelevel_ge(p->p_cred, 3) && (mode & FWRITE)) 288255332Scy { 289255332Scy ipfmain.ipf_interror = 130001; 290145516Sdarrenr return EPERM; 291255332Scy } 292145516Sdarrenr#endif 293145516Sdarrenr 294145516Sdarrenr unit = GET_MINOR(dev); 295255332Scy if ((IPL_LOGMAX < unit) || (unit < 0)) { 296255332Scy ipfmain.ipf_interror = 130002; 297145516Sdarrenr return ENXIO; 298255332Scy } 299145516Sdarrenr 300255332Scy if (ipfmain.ipf_running <= 0) { 301255332Scy if (unit != IPL_LOGIPF && cmd != SIOCIPFINTERROR) { 302255332Scy ipfmain.ipf_interror = 130003; 303145516Sdarrenr return EIO; 304255332Scy } 305145516Sdarrenr if (cmd != SIOCIPFGETNEXT && cmd != SIOCIPFGET && 306173181Sdarrenr cmd != SIOCIPFSET && cmd != SIOCFRENB && 307255332Scy cmd != SIOCGETFS && cmd != SIOCGETFF && 308255332Scy cmd != SIOCIPFINTERROR) { 309255332Scy ipfmain.ipf_interror = 130004; 310145516Sdarrenr return EIO; 311255332Scy } 312145516Sdarrenr } 313145516Sdarrenr 314145516Sdarrenr SPL_NET(s); 315145516Sdarrenr 316255332Scy error = ipf_ioctlswitch(&ipfmain, unit, data, cmd, mode, p->p_uid, p); 317145516Sdarrenr if (error != -1) { 318145516Sdarrenr SPL_X(s); 319145516Sdarrenr return error; 320145516Sdarrenr } 321145516Sdarrenr 322145516Sdarrenr SPL_X(s); 323161356Sguido 324145516Sdarrenr return error; 325145516Sdarrenr} 326145516Sdarrenr 327145516Sdarrenr 328145516Sdarrenr/* 329255332Scy * ipf_send_reset - this could conceivably be a call to tcp_respond(), but that 330145516Sdarrenr * requires a large amount of setting up and isn't any more efficient. 331145516Sdarrenr */ 332255332Scyint 333255332Scyipf_send_reset(fin) 334255332Scy fr_info_t *fin; 335145516Sdarrenr{ 336145516Sdarrenr struct tcphdr *tcp, *tcp2; 337145516Sdarrenr int tlen = 0, hlen; 338145516Sdarrenr struct mbuf *m; 339145516Sdarrenr#ifdef USE_INET6 340145516Sdarrenr ip6_t *ip6; 341145516Sdarrenr#endif 342145516Sdarrenr ip_t *ip; 343145516Sdarrenr 344145516Sdarrenr tcp = fin->fin_dp; 345145516Sdarrenr if (tcp->th_flags & TH_RST) 346145516Sdarrenr return -1; /* feedback loop */ 347145516Sdarrenr 348255332Scy if (ipf_checkl4sum(fin) == -1) 349145516Sdarrenr return -1; 350145516Sdarrenr 351145516Sdarrenr tlen = fin->fin_dlen - (TCP_OFF(tcp) << 2) + 352145516Sdarrenr ((tcp->th_flags & TH_SYN) ? 1 : 0) + 353145516Sdarrenr ((tcp->th_flags & TH_FIN) ? 1 : 0); 354145516Sdarrenr 355145516Sdarrenr#ifdef USE_INET6 356145516Sdarrenr hlen = (fin->fin_v == 6) ? sizeof(ip6_t) : sizeof(ip_t); 357145516Sdarrenr#else 358145516Sdarrenr hlen = sizeof(ip_t); 359145516Sdarrenr#endif 360145516Sdarrenr#ifdef MGETHDR 361145516Sdarrenr MGETHDR(m, M_DONTWAIT, MT_HEADER); 362145516Sdarrenr#else 363145516Sdarrenr MGET(m, M_DONTWAIT, MT_HEADER); 364145516Sdarrenr#endif 365145516Sdarrenr if (m == NULL) 366145516Sdarrenr return -1; 367145516Sdarrenr if (sizeof(*tcp2) + hlen > MLEN) { 368145516Sdarrenr MCLGET(m, M_DONTWAIT); 369145516Sdarrenr if ((m->m_flags & M_EXT) == 0) { 370145516Sdarrenr FREE_MB_T(m); 371145516Sdarrenr return -1; 372145516Sdarrenr } 373145516Sdarrenr } 374145516Sdarrenr 375145516Sdarrenr m->m_len = sizeof(*tcp2) + hlen; 376145516Sdarrenr#if (BSD >= 199103) 377145516Sdarrenr m->m_data += max_linkhdr; 378145516Sdarrenr m->m_pkthdr.len = m->m_len; 379145516Sdarrenr m->m_pkthdr.rcvif = (struct ifnet *)0; 380145516Sdarrenr#endif 381145516Sdarrenr ip = mtod(m, struct ip *); 382145516Sdarrenr bzero((char *)ip, hlen); 383145516Sdarrenr#ifdef USE_INET6 384145516Sdarrenr ip6 = (ip6_t *)ip; 385145516Sdarrenr#endif 386145516Sdarrenr tcp2 = (struct tcphdr *)((char *)ip + hlen); 387145516Sdarrenr tcp2->th_sport = tcp->th_dport; 388145516Sdarrenr tcp2->th_dport = tcp->th_sport; 389145516Sdarrenr 390145516Sdarrenr if (tcp->th_flags & TH_ACK) { 391145516Sdarrenr tcp2->th_seq = tcp->th_ack; 392145516Sdarrenr tcp2->th_flags = TH_RST; 393145516Sdarrenr tcp2->th_ack = 0; 394145516Sdarrenr } else { 395145516Sdarrenr tcp2->th_seq = 0; 396145516Sdarrenr tcp2->th_ack = ntohl(tcp->th_seq); 397145516Sdarrenr tcp2->th_ack += tlen; 398145516Sdarrenr tcp2->th_ack = htonl(tcp2->th_ack); 399145516Sdarrenr tcp2->th_flags = TH_RST|TH_ACK; 400145516Sdarrenr } 401145516Sdarrenr TCP_X2_A(tcp2, 0); 402145516Sdarrenr TCP_OFF_A(tcp2, sizeof(*tcp2) >> 2); 403145516Sdarrenr tcp2->th_win = tcp->th_win; 404145516Sdarrenr tcp2->th_sum = 0; 405145516Sdarrenr tcp2->th_urp = 0; 406145516Sdarrenr 407145516Sdarrenr#ifdef USE_INET6 408145516Sdarrenr if (fin->fin_v == 6) { 409145516Sdarrenr ip6->ip6_flow = ((ip6_t *)fin->fin_ip)->ip6_flow; 410145516Sdarrenr ip6->ip6_plen = htons(sizeof(struct tcphdr)); 411145516Sdarrenr ip6->ip6_nxt = IPPROTO_TCP; 412145516Sdarrenr ip6->ip6_hlim = 0; 413255332Scy ip6->ip6_src = fin->fin_dst6.in6; 414255332Scy ip6->ip6_dst = fin->fin_src6.in6; 415145516Sdarrenr tcp2->th_sum = in6_cksum(m, IPPROTO_TCP, 416145516Sdarrenr sizeof(*ip6), sizeof(*tcp2)); 417255332Scy return ipf_send_ip(fin, m); 418145516Sdarrenr } 419145516Sdarrenr#endif 420145516Sdarrenr ip->ip_p = IPPROTO_TCP; 421145516Sdarrenr ip->ip_len = htons(sizeof(struct tcphdr)); 422145516Sdarrenr ip->ip_src.s_addr = fin->fin_daddr; 423145516Sdarrenr ip->ip_dst.s_addr = fin->fin_saddr; 424145516Sdarrenr tcp2->th_sum = in_cksum(m, hlen + sizeof(*tcp2)); 425255332Scy ip->ip_len = htons(hlen + sizeof(*tcp2)); 426255332Scy return ipf_send_ip(fin, m); 427145516Sdarrenr} 428145516Sdarrenr 429145516Sdarrenr 430255332Scy/* 431255332Scy * ip_len must be in network byte order when called. 432255332Scy */ 433255332Scystatic int 434255332Scyipf_send_ip(fin, m) 435255332Scy fr_info_t *fin; 436255332Scy mb_t *m; 437145516Sdarrenr{ 438145516Sdarrenr fr_info_t fnew; 439145516Sdarrenr ip_t *ip, *oip; 440145516Sdarrenr int hlen; 441145516Sdarrenr 442145516Sdarrenr ip = mtod(m, ip_t *); 443145516Sdarrenr bzero((char *)&fnew, sizeof(fnew)); 444255332Scy fnew.fin_main_soft = fin->fin_main_soft; 445145516Sdarrenr 446145516Sdarrenr IP_V_A(ip, fin->fin_v); 447145516Sdarrenr switch (fin->fin_v) 448145516Sdarrenr { 449145516Sdarrenr case 4 : 450255332Scy oip = fin->fin_ip; 451255332Scy hlen = sizeof(*oip); 452145516Sdarrenr fnew.fin_v = 4; 453255332Scy fnew.fin_p = ip->ip_p; 454255332Scy fnew.fin_plen = ntohs(ip->ip_len); 455145516Sdarrenr IP_HL_A(ip, sizeof(*oip) >> 2); 456145516Sdarrenr ip->ip_tos = oip->ip_tos; 457145516Sdarrenr ip->ip_id = fin->fin_ip->ip_id; 458255332Scy#if defined(FreeBSD) && (__FreeBSD_version > 460000) 459255332Scy ip->ip_off = htons(path_mtu_discovery ? IP_DF : 0); 460145516Sdarrenr#else 461145516Sdarrenr ip->ip_off = 0; 462145516Sdarrenr#endif 463181803Sbz ip->ip_ttl = V_ip_defttl; 464145516Sdarrenr ip->ip_sum = 0; 465145516Sdarrenr break; 466145516Sdarrenr#ifdef USE_INET6 467145516Sdarrenr case 6 : 468145516Sdarrenr { 469145516Sdarrenr ip6_t *ip6 = (ip6_t *)ip; 470145516Sdarrenr 471145516Sdarrenr ip6->ip6_vfc = 0x60; 472145516Sdarrenr ip6->ip6_hlim = IPDEFTTL; 473145516Sdarrenr 474255332Scy hlen = sizeof(*ip6); 475255332Scy fnew.fin_p = ip6->ip6_nxt; 476145516Sdarrenr fnew.fin_v = 6; 477255332Scy fnew.fin_plen = ntohs(ip6->ip6_plen) + hlen; 478145516Sdarrenr break; 479145516Sdarrenr } 480145516Sdarrenr#endif 481145516Sdarrenr default : 482145516Sdarrenr return EINVAL; 483145516Sdarrenr } 484145516Sdarrenr#ifdef IPSEC 485145516Sdarrenr m->m_pkthdr.rcvif = NULL; 486145516Sdarrenr#endif 487145516Sdarrenr 488145516Sdarrenr fnew.fin_ifp = fin->fin_ifp; 489145516Sdarrenr fnew.fin_flx = FI_NOCKSUM; 490145516Sdarrenr fnew.fin_m = m; 491145516Sdarrenr fnew.fin_ip = ip; 492255332Scy fnew.fin_mp = &m; 493145516Sdarrenr fnew.fin_hlen = hlen; 494145516Sdarrenr fnew.fin_dp = (char *)ip + hlen; 495255332Scy (void) ipf_makefrip(hlen, ip, &fnew); 496145516Sdarrenr 497255332Scy return ipf_fastroute(m, &m, &fnew, NULL); 498145516Sdarrenr} 499145516Sdarrenr 500145516Sdarrenr 501255332Scyint 502255332Scyipf_send_icmp_err(type, fin, dst) 503255332Scy int type; 504255332Scy fr_info_t *fin; 505255332Scy int dst; 506145516Sdarrenr{ 507145516Sdarrenr int err, hlen, xtra, iclen, ohlen, avail, code; 508145516Sdarrenr struct in_addr dst4; 509145516Sdarrenr struct icmp *icmp; 510145516Sdarrenr struct mbuf *m; 511255332Scy i6addr_t dst6; 512145516Sdarrenr void *ifp; 513145516Sdarrenr#ifdef USE_INET6 514145516Sdarrenr ip6_t *ip6; 515145516Sdarrenr#endif 516145516Sdarrenr ip_t *ip, *ip2; 517145516Sdarrenr 518172776Sdarrenr if ((type < 0) || (type >= ICMP_MAXTYPE)) 519145516Sdarrenr return -1; 520145516Sdarrenr 521145516Sdarrenr code = fin->fin_icode; 522145516Sdarrenr#ifdef USE_INET6 523255332Scy#if 0 524255332Scy /* XXX Fix an off by one error: s/>/>=/ 525255332Scy was: 526255332Scy if ((code < 0) || (code > sizeof(icmptoicmp6unreach)/sizeof(int))) 527255332Scy Fix obtained from NetBSD ip_fil_netbsd.c r1.4: */ 528255332Scy#endif 529255332Scy if ((code < 0) || (code >= sizeof(icmptoicmp6unreach)/sizeof(int))) 530145516Sdarrenr return -1; 531145516Sdarrenr#endif 532145516Sdarrenr 533255332Scy if (ipf_checkl4sum(fin) == -1) 534145516Sdarrenr return -1; 535145516Sdarrenr#ifdef MGETHDR 536145516Sdarrenr MGETHDR(m, M_DONTWAIT, MT_HEADER); 537145516Sdarrenr#else 538145516Sdarrenr MGET(m, M_DONTWAIT, MT_HEADER); 539145516Sdarrenr#endif 540145516Sdarrenr if (m == NULL) 541145516Sdarrenr return -1; 542145516Sdarrenr avail = MHLEN; 543145516Sdarrenr 544145516Sdarrenr xtra = 0; 545145516Sdarrenr hlen = 0; 546145516Sdarrenr ohlen = 0; 547255332Scy dst4.s_addr = 0; 548145516Sdarrenr ifp = fin->fin_ifp; 549145516Sdarrenr if (fin->fin_v == 4) { 550255332Scy if ((fin->fin_p == IPPROTO_ICMP) && !(fin->fin_flx & FI_SHORT)) 551145516Sdarrenr switch (ntohs(fin->fin_data[0]) >> 8) 552145516Sdarrenr { 553145516Sdarrenr case ICMP_ECHO : 554145516Sdarrenr case ICMP_TSTAMP : 555145516Sdarrenr case ICMP_IREQ : 556145516Sdarrenr case ICMP_MASKREQ : 557145516Sdarrenr break; 558145516Sdarrenr default : 559145516Sdarrenr FREE_MB_T(m); 560145516Sdarrenr return 0; 561145516Sdarrenr } 562145516Sdarrenr 563145516Sdarrenr if (dst == 0) { 564255332Scy if (ipf_ifpaddr(&ipfmain, 4, FRI_NORMAL, ifp, 565255332Scy &dst6, NULL) == -1) { 566145516Sdarrenr FREE_MB_T(m); 567145516Sdarrenr return -1; 568145516Sdarrenr } 569255332Scy dst4 = dst6.in4; 570145516Sdarrenr } else 571145516Sdarrenr dst4.s_addr = fin->fin_daddr; 572145516Sdarrenr 573145516Sdarrenr hlen = sizeof(ip_t); 574145516Sdarrenr ohlen = fin->fin_hlen; 575255332Scy iclen = hlen + offsetof(struct icmp, icmp_ip) + ohlen; 576145516Sdarrenr if (fin->fin_hlen < fin->fin_plen) 577145516Sdarrenr xtra = MIN(fin->fin_dlen, 8); 578145516Sdarrenr else 579145516Sdarrenr xtra = 0; 580145516Sdarrenr } 581145516Sdarrenr 582145516Sdarrenr#ifdef USE_INET6 583145516Sdarrenr else if (fin->fin_v == 6) { 584145516Sdarrenr hlen = sizeof(ip6_t); 585145516Sdarrenr ohlen = sizeof(ip6_t); 586255332Scy iclen = hlen + offsetof(struct icmp, icmp_ip) + ohlen; 587145516Sdarrenr type = icmptoicmp6types[type]; 588145516Sdarrenr if (type == ICMP6_DST_UNREACH) 589145516Sdarrenr code = icmptoicmp6unreach[code]; 590145516Sdarrenr 591255332Scy if (iclen + max_linkhdr + fin->fin_plen > avail) { 592145516Sdarrenr MCLGET(m, M_DONTWAIT); 593145516Sdarrenr if ((m->m_flags & M_EXT) == 0) { 594145516Sdarrenr FREE_MB_T(m); 595145516Sdarrenr return -1; 596145516Sdarrenr } 597145516Sdarrenr avail = MCLBYTES; 598145516Sdarrenr } 599255332Scy xtra = MIN(fin->fin_plen, avail - iclen - max_linkhdr); 600255332Scy xtra = MIN(xtra, IPV6_MMTU - iclen); 601145516Sdarrenr if (dst == 0) { 602255332Scy if (ipf_ifpaddr(&ipfmain, 6, FRI_NORMAL, ifp, 603255332Scy &dst6, NULL) == -1) { 604145516Sdarrenr FREE_MB_T(m); 605145516Sdarrenr return -1; 606145516Sdarrenr } 607145516Sdarrenr } else 608145516Sdarrenr dst6 = fin->fin_dst6; 609145516Sdarrenr } 610145516Sdarrenr#endif 611145516Sdarrenr else { 612145516Sdarrenr FREE_MB_T(m); 613145516Sdarrenr return -1; 614145516Sdarrenr } 615145516Sdarrenr 616145516Sdarrenr avail -= (max_linkhdr + iclen); 617145516Sdarrenr if (avail < 0) { 618145516Sdarrenr FREE_MB_T(m); 619145516Sdarrenr return -1; 620145516Sdarrenr } 621145516Sdarrenr if (xtra > avail) 622145516Sdarrenr xtra = avail; 623145516Sdarrenr iclen += xtra; 624145516Sdarrenr m->m_data += max_linkhdr; 625145516Sdarrenr m->m_pkthdr.rcvif = (struct ifnet *)0; 626145516Sdarrenr m->m_pkthdr.len = iclen; 627145516Sdarrenr m->m_len = iclen; 628145516Sdarrenr ip = mtod(m, ip_t *); 629145516Sdarrenr icmp = (struct icmp *)((char *)ip + hlen); 630145516Sdarrenr ip2 = (ip_t *)&icmp->icmp_ip; 631145516Sdarrenr 632145516Sdarrenr icmp->icmp_type = type; 633145516Sdarrenr icmp->icmp_code = fin->fin_icode; 634145516Sdarrenr icmp->icmp_cksum = 0; 635145516Sdarrenr#ifdef icmp_nextmtu 636255332Scy if (type == ICMP_UNREACH && fin->fin_icode == ICMP_UNREACH_NEEDFRAG) { 637255332Scy if (fin->fin_mtu != 0) { 638255332Scy icmp->icmp_nextmtu = htons(fin->fin_mtu); 639255332Scy 640255332Scy } else if (ifp != NULL) { 641255332Scy icmp->icmp_nextmtu = htons(GETIFMTU_4(ifp)); 642255332Scy 643255332Scy } else { /* make up a number... */ 644255332Scy icmp->icmp_nextmtu = htons(fin->fin_plen - 20); 645255332Scy } 646255332Scy } 647145516Sdarrenr#endif 648145516Sdarrenr 649145516Sdarrenr bcopy((char *)fin->fin_ip, (char *)ip2, ohlen); 650145516Sdarrenr 651145516Sdarrenr#ifdef USE_INET6 652145516Sdarrenr ip6 = (ip6_t *)ip; 653145516Sdarrenr if (fin->fin_v == 6) { 654145516Sdarrenr ip6->ip6_flow = ((ip6_t *)fin->fin_ip)->ip6_flow; 655145516Sdarrenr ip6->ip6_plen = htons(iclen - hlen); 656145516Sdarrenr ip6->ip6_nxt = IPPROTO_ICMPV6; 657145516Sdarrenr ip6->ip6_hlim = 0; 658255332Scy ip6->ip6_src = dst6.in6; 659255332Scy ip6->ip6_dst = fin->fin_src6.in6; 660145516Sdarrenr if (xtra > 0) 661145516Sdarrenr bcopy((char *)fin->fin_ip + ohlen, 662145516Sdarrenr (char *)&icmp->icmp_ip + ohlen, xtra); 663145516Sdarrenr icmp->icmp_cksum = in6_cksum(m, IPPROTO_ICMPV6, 664145516Sdarrenr sizeof(*ip6), iclen - hlen); 665145516Sdarrenr } else 666145516Sdarrenr#endif 667145516Sdarrenr { 668145516Sdarrenr ip->ip_p = IPPROTO_ICMP; 669145516Sdarrenr ip->ip_src.s_addr = dst4.s_addr; 670145516Sdarrenr ip->ip_dst.s_addr = fin->fin_saddr; 671145516Sdarrenr 672145516Sdarrenr if (xtra > 0) 673145516Sdarrenr bcopy((char *)fin->fin_ip + ohlen, 674145516Sdarrenr (char *)&icmp->icmp_ip + ohlen, xtra); 675145516Sdarrenr icmp->icmp_cksum = ipf_cksum((u_short *)icmp, 676145516Sdarrenr sizeof(*icmp) + 8); 677255332Scy ip->ip_len = htons(iclen); 678145516Sdarrenr ip->ip_p = IPPROTO_ICMP; 679145516Sdarrenr } 680255332Scy err = ipf_send_ip(fin, m); 681145516Sdarrenr return err; 682145516Sdarrenr} 683145516Sdarrenr 684145516Sdarrenr 685145516Sdarrenr 686145516Sdarrenr 687173181Sdarrenr/* 688173181Sdarrenr * m0 - pointer to mbuf where the IP packet starts 689173181Sdarrenr * mpp - pointer to the mbuf pointer that is the start of the mbuf chain 690173181Sdarrenr */ 691255332Scyint 692255332Scyipf_fastroute(m0, mpp, fin, fdp) 693255332Scy mb_t *m0, **mpp; 694255332Scy fr_info_t *fin; 695255332Scy frdest_t *fdp; 696145516Sdarrenr{ 697145516Sdarrenr register struct ip *ip, *mhip; 698173181Sdarrenr register struct mbuf *m = *mpp; 699145516Sdarrenr register struct route *ro; 700145516Sdarrenr int len, off, error = 0, hlen, code; 701145516Sdarrenr struct ifnet *ifp, *sifp; 702145516Sdarrenr struct sockaddr_in *dst; 703145516Sdarrenr struct route iproute; 704145516Sdarrenr u_short ip_off; 705255332Scy frdest_t node; 706145516Sdarrenr frentry_t *fr; 707145516Sdarrenr 708161356Sguido ro = NULL; 709161356Sguido 710145516Sdarrenr#ifdef M_WRITABLE 711145516Sdarrenr /* 712145516Sdarrenr * HOT FIX/KLUDGE: 713145516Sdarrenr * 714145516Sdarrenr * If the mbuf we're about to send is not writable (because of 715145516Sdarrenr * a cluster reference, for example) we'll need to make a copy 716145516Sdarrenr * of it since this routine modifies the contents. 717145516Sdarrenr * 718145516Sdarrenr * If you have non-crappy network hardware that can transmit data 719145516Sdarrenr * from the mbuf, rather than making a copy, this is gonna be a 720145516Sdarrenr * problem. 721145516Sdarrenr */ 722145516Sdarrenr if (M_WRITABLE(m) == 0) { 723161356Sguido m0 = m_dup(m, M_DONTWAIT); 724161356Sguido if (m0 != 0) { 725145516Sdarrenr FREE_MB_T(m); 726145516Sdarrenr m = m0; 727145516Sdarrenr *mpp = m; 728145516Sdarrenr } else { 729145516Sdarrenr error = ENOBUFS; 730145516Sdarrenr FREE_MB_T(m); 731161356Sguido goto done; 732145516Sdarrenr } 733145516Sdarrenr } 734145516Sdarrenr#endif 735145516Sdarrenr 736145516Sdarrenr#ifdef USE_INET6 737145516Sdarrenr if (fin->fin_v == 6) { 738145516Sdarrenr /* 739145516Sdarrenr * currently "to <if>" and "to <if>:ip#" are not supported 740145516Sdarrenr * for IPv6 741145516Sdarrenr */ 742255332Scy return ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL); 743145516Sdarrenr } 744145516Sdarrenr#endif 745145516Sdarrenr 746145516Sdarrenr hlen = fin->fin_hlen; 747145516Sdarrenr ip = mtod(m0, struct ip *); 748255332Scy ifp = NULL; 749145516Sdarrenr 750145516Sdarrenr /* 751145516Sdarrenr * Route packet. 752145516Sdarrenr */ 753145516Sdarrenr ro = &iproute; 754255332Scy bzero(ro, sizeof (*ro)); 755145516Sdarrenr dst = (struct sockaddr_in *)&ro->ro_dst; 756145516Sdarrenr dst->sin_family = AF_INET; 757145516Sdarrenr dst->sin_addr = ip->ip_dst; 758145516Sdarrenr 759145516Sdarrenr fr = fin->fin_fr; 760255332Scy if ((fr != NULL) && !(fr->fr_flags & FR_KEEPSTATE) && (fdp != NULL) && 761255332Scy (fdp->fd_type == FRD_DSTLIST)) { 762255332Scy if (ipf_dstlist_select_node(fin, fdp->fd_ptr, NULL, &node) == 0) 763255332Scy fdp = &node; 764255332Scy } 765255332Scy 766145516Sdarrenr if (fdp != NULL) 767255332Scy ifp = fdp->fd_ptr; 768145516Sdarrenr else 769145516Sdarrenr ifp = fin->fin_ifp; 770145516Sdarrenr 771255332Scy if ((ifp == NULL) && ((fr == NULL) || !(fr->fr_flags & FR_FASTROUTE))) { 772145516Sdarrenr error = -2; 773145516Sdarrenr goto bad; 774145516Sdarrenr } 775145516Sdarrenr 776161356Sguido if ((fdp != NULL) && (fdp->fd_ip.s_addr != 0)) 777161356Sguido dst->sin_addr = fdp->fd_ip; 778145516Sdarrenr 779145516Sdarrenr dst->sin_len = sizeof(*dst); 780178888Sjulian in_rtalloc(ro, 0); 781145516Sdarrenr 782145516Sdarrenr if ((ifp == NULL) && (ro->ro_rt != NULL)) 783145516Sdarrenr ifp = ro->ro_rt->rt_ifp; 784145516Sdarrenr 785145516Sdarrenr if ((ro->ro_rt == NULL) || (ifp == NULL)) { 786145516Sdarrenr if (in_localaddr(ip->ip_dst)) 787145516Sdarrenr error = EHOSTUNREACH; 788145516Sdarrenr else 789145516Sdarrenr error = ENETUNREACH; 790145516Sdarrenr goto bad; 791145516Sdarrenr } 792145516Sdarrenr if (ro->ro_rt->rt_flags & RTF_GATEWAY) 793145516Sdarrenr dst = (struct sockaddr_in *)ro->ro_rt->rt_gateway; 794145516Sdarrenr if (ro->ro_rt) 795145516Sdarrenr ro->ro_rt->rt_use++; 796145516Sdarrenr 797145516Sdarrenr /* 798145516Sdarrenr * For input packets which are being "fastrouted", they won't 799145516Sdarrenr * go back through output filtering and miss their chance to get 800170268Sdarrenr * NAT'd and counted. Duplicated packets aren't considered to be 801170268Sdarrenr * part of the normal packet stream, so do not NAT them or pass 802170268Sdarrenr * them through stateful checking, etc. 803145516Sdarrenr */ 804170268Sdarrenr if ((fdp != &fr->fr_dif) && (fin->fin_out == 0)) { 805145516Sdarrenr sifp = fin->fin_ifp; 806145516Sdarrenr fin->fin_ifp = ifp; 807145516Sdarrenr fin->fin_out = 1; 808255332Scy (void) ipf_acctpkt(fin, NULL); 809145516Sdarrenr fin->fin_fr = NULL; 810145516Sdarrenr if (!fr || !(fr->fr_flags & FR_RETMASK)) { 811145516Sdarrenr u_32_t pass; 812145516Sdarrenr 813255332Scy (void) ipf_state_check(fin, &pass); 814145516Sdarrenr } 815145516Sdarrenr 816255332Scy switch (ipf_nat_checkout(fin, NULL)) 817145516Sdarrenr { 818145516Sdarrenr case 0 : 819145516Sdarrenr break; 820145516Sdarrenr case 1 : 821145516Sdarrenr ip->ip_sum = 0; 822145516Sdarrenr break; 823145516Sdarrenr case -1 : 824145516Sdarrenr error = -1; 825173181Sdarrenr goto bad; 826145516Sdarrenr break; 827145516Sdarrenr } 828145516Sdarrenr 829145516Sdarrenr fin->fin_ifp = sifp; 830145516Sdarrenr fin->fin_out = 0; 831145516Sdarrenr } else 832145516Sdarrenr ip->ip_sum = 0; 833145516Sdarrenr /* 834145516Sdarrenr * If small enough for interface, can just send directly. 835145516Sdarrenr */ 836255332Scy if (ntohs(ip->ip_len) <= ifp->if_mtu) { 837145516Sdarrenr if (!ip->ip_sum) 838145516Sdarrenr ip->ip_sum = in_cksum(m, hlen); 839145516Sdarrenr error = (*ifp->if_output)(ifp, m, (struct sockaddr *)dst, 840255332Scy ro 841255332Scy ); 842145516Sdarrenr goto done; 843145516Sdarrenr } 844145516Sdarrenr /* 845145516Sdarrenr * Too large for interface; fragment if possible. 846145516Sdarrenr * Must be able to put at least 8 bytes per fragment. 847145516Sdarrenr */ 848145516Sdarrenr ip_off = ntohs(ip->ip_off); 849145516Sdarrenr if (ip_off & IP_DF) { 850145516Sdarrenr error = EMSGSIZE; 851145516Sdarrenr goto bad; 852145516Sdarrenr } 853145516Sdarrenr len = (ifp->if_mtu - hlen) &~ 7; 854145516Sdarrenr if (len < 8) { 855145516Sdarrenr error = EMSGSIZE; 856145516Sdarrenr goto bad; 857145516Sdarrenr } 858145516Sdarrenr 859145516Sdarrenr { 860145516Sdarrenr int mhlen, firstlen = len; 861145516Sdarrenr struct mbuf **mnext = &m->m_act; 862145516Sdarrenr 863145516Sdarrenr /* 864145516Sdarrenr * Loop through length of segment after first fragment, 865145516Sdarrenr * make new header and copy data of each part and link onto chain. 866145516Sdarrenr */ 867145516Sdarrenr m0 = m; 868145516Sdarrenr mhlen = sizeof (struct ip); 869255332Scy for (off = hlen + len; off < ntohs(ip->ip_len); off += len) { 870145516Sdarrenr#ifdef MGETHDR 871145516Sdarrenr MGETHDR(m, M_DONTWAIT, MT_HEADER); 872145516Sdarrenr#else 873145516Sdarrenr MGET(m, M_DONTWAIT, MT_HEADER); 874145516Sdarrenr#endif 875145516Sdarrenr if (m == 0) { 876145516Sdarrenr m = m0; 877145516Sdarrenr error = ENOBUFS; 878145516Sdarrenr goto bad; 879145516Sdarrenr } 880145516Sdarrenr m->m_data += max_linkhdr; 881145516Sdarrenr mhip = mtod(m, struct ip *); 882145516Sdarrenr bcopy((char *)ip, (char *)mhip, sizeof(*ip)); 883145516Sdarrenr if (hlen > sizeof (struct ip)) { 884145516Sdarrenr mhlen = ip_optcopy(ip, mhip) + sizeof (struct ip); 885145516Sdarrenr IP_HL_A(mhip, mhlen >> 2); 886145516Sdarrenr } 887145516Sdarrenr m->m_len = mhlen; 888145516Sdarrenr mhip->ip_off = ((off - hlen) >> 3) + ip_off; 889255332Scy if (off + len >= ntohs(ip->ip_len)) 890255332Scy len = ntohs(ip->ip_len) - off; 891145516Sdarrenr else 892145516Sdarrenr mhip->ip_off |= IP_MF; 893145516Sdarrenr mhip->ip_len = htons((u_short)(len + mhlen)); 894161356Sguido *mnext = m; 895145516Sdarrenr m->m_next = m_copy(m0, off, len); 896145516Sdarrenr if (m->m_next == 0) { 897145516Sdarrenr error = ENOBUFS; /* ??? */ 898145516Sdarrenr goto sendorfree; 899145516Sdarrenr } 900145516Sdarrenr m->m_pkthdr.len = mhlen + len; 901145516Sdarrenr m->m_pkthdr.rcvif = NULL; 902145516Sdarrenr mhip->ip_off = htons((u_short)mhip->ip_off); 903145516Sdarrenr mhip->ip_sum = 0; 904145516Sdarrenr mhip->ip_sum = in_cksum(m, mhlen); 905145516Sdarrenr mnext = &m->m_act; 906145516Sdarrenr } 907145516Sdarrenr /* 908145516Sdarrenr * Update first fragment by trimming what's been copied out 909145516Sdarrenr * and updating header, then send each fragment (in order). 910145516Sdarrenr */ 911145516Sdarrenr m_adj(m0, hlen + firstlen - ip->ip_len); 912145516Sdarrenr ip->ip_len = htons((u_short)(hlen + firstlen)); 913145516Sdarrenr ip->ip_off = htons((u_short)IP_MF); 914145516Sdarrenr ip->ip_sum = 0; 915145516Sdarrenr ip->ip_sum = in_cksum(m0, hlen); 916145516Sdarrenrsendorfree: 917145516Sdarrenr for (m = m0; m; m = m0) { 918145516Sdarrenr m0 = m->m_act; 919145516Sdarrenr m->m_act = 0; 920145516Sdarrenr if (error == 0) 921145516Sdarrenr error = (*ifp->if_output)(ifp, m, 922255332Scy (struct sockaddr *)dst, 923255332Scy ro 924255332Scy ); 925145516Sdarrenr else 926145516Sdarrenr FREE_MB_T(m); 927145516Sdarrenr } 928255332Scy } 929145516Sdarrenrdone: 930145516Sdarrenr if (!error) 931255332Scy ipfmain.ipf_frouteok[0]++; 932145516Sdarrenr else 933255332Scy ipfmain.ipf_frouteok[1]++; 934145516Sdarrenr 935161356Sguido if ((ro != NULL) && (ro->ro_rt != NULL)) { 936145516Sdarrenr RTFREE(ro->ro_rt); 937145516Sdarrenr } 938145516Sdarrenr return 0; 939145516Sdarrenrbad: 940145516Sdarrenr if (error == EMSGSIZE) { 941145516Sdarrenr sifp = fin->fin_ifp; 942145516Sdarrenr code = fin->fin_icode; 943145516Sdarrenr fin->fin_icode = ICMP_UNREACH_NEEDFRAG; 944145516Sdarrenr fin->fin_ifp = ifp; 945255332Scy (void) ipf_send_icmp_err(ICMP_UNREACH, fin, 1); 946145516Sdarrenr fin->fin_ifp = sifp; 947145516Sdarrenr fin->fin_icode = code; 948145516Sdarrenr } 949145516Sdarrenr FREE_MB_T(m); 950145516Sdarrenr goto done; 951145516Sdarrenr} 952145516Sdarrenr 953145516Sdarrenr 954255332Scyint 955255332Scyipf_verifysrc(fin) 956255332Scy fr_info_t *fin; 957145516Sdarrenr{ 958145516Sdarrenr struct sockaddr_in *dst; 959145516Sdarrenr struct route iproute; 960145516Sdarrenr 961145516Sdarrenr bzero((char *)&iproute, sizeof(iproute)); 962145516Sdarrenr dst = (struct sockaddr_in *)&iproute.ro_dst; 963145516Sdarrenr dst->sin_len = sizeof(*dst); 964145516Sdarrenr dst->sin_family = AF_INET; 965145516Sdarrenr dst->sin_addr = fin->fin_src; 966178888Sjulian in_rtalloc(&iproute, 0); 967145516Sdarrenr if (iproute.ro_rt == NULL) 968145516Sdarrenr return 0; 969145516Sdarrenr return (fin->fin_ifp == iproute.ro_rt->rt_ifp); 970145516Sdarrenr} 971145516Sdarrenr 972145516Sdarrenr 973145516Sdarrenr/* 974145516Sdarrenr * return the first IP Address associated with an interface 975145516Sdarrenr */ 976255332Scyint 977255332Scyipf_ifpaddr(softc, v, atype, ifptr, inp, inpmask) 978255332Scy ipf_main_softc_t *softc; 979255332Scy int v, atype; 980255332Scy void *ifptr; 981255332Scy i6addr_t *inp, *inpmask; 982145516Sdarrenr{ 983145516Sdarrenr#ifdef USE_INET6 984145516Sdarrenr struct in6_addr *inp6 = NULL; 985145516Sdarrenr#endif 986145516Sdarrenr struct sockaddr *sock, *mask; 987145516Sdarrenr struct sockaddr_in *sin; 988145516Sdarrenr struct ifaddr *ifa; 989145516Sdarrenr struct ifnet *ifp; 990145516Sdarrenr 991145516Sdarrenr if ((ifptr == NULL) || (ifptr == (void *)-1)) 992145516Sdarrenr return -1; 993145516Sdarrenr 994145516Sdarrenr sin = NULL; 995145516Sdarrenr ifp = ifptr; 996145516Sdarrenr 997145516Sdarrenr if (v == 4) 998255332Scy inp->in4.s_addr = 0; 999145516Sdarrenr#ifdef USE_INET6 1000145516Sdarrenr else if (v == 6) 1001255332Scy bzero((char *)inp, sizeof(*inp)); 1002145516Sdarrenr#endif 1003145516Sdarrenr ifa = TAILQ_FIRST(&ifp->if_addrhead); 1004145516Sdarrenr 1005145516Sdarrenr sock = ifa->ifa_addr; 1006145516Sdarrenr while (sock != NULL && ifa != NULL) { 1007145516Sdarrenr sin = (struct sockaddr_in *)sock; 1008145516Sdarrenr if ((v == 4) && (sin->sin_family == AF_INET)) 1009145516Sdarrenr break; 1010145516Sdarrenr#ifdef USE_INET6 1011145516Sdarrenr if ((v == 6) && (sin->sin_family == AF_INET6)) { 1012145516Sdarrenr inp6 = &((struct sockaddr_in6 *)sin)->sin6_addr; 1013145516Sdarrenr if (!IN6_IS_ADDR_LINKLOCAL(inp6) && 1014145516Sdarrenr !IN6_IS_ADDR_LOOPBACK(inp6)) 1015145516Sdarrenr break; 1016145516Sdarrenr } 1017145516Sdarrenr#endif 1018145516Sdarrenr ifa = TAILQ_NEXT(ifa, ifa_link); 1019145516Sdarrenr if (ifa != NULL) 1020145516Sdarrenr sock = ifa->ifa_addr; 1021145516Sdarrenr } 1022145516Sdarrenr 1023145516Sdarrenr if (ifa == NULL || sin == NULL) 1024145516Sdarrenr return -1; 1025145516Sdarrenr 1026145516Sdarrenr mask = ifa->ifa_netmask; 1027145516Sdarrenr if (atype == FRI_BROADCAST) 1028145516Sdarrenr sock = ifa->ifa_broadaddr; 1029145516Sdarrenr else if (atype == FRI_PEERADDR) 1030145516Sdarrenr sock = ifa->ifa_dstaddr; 1031145516Sdarrenr 1032161356Sguido if (sock == NULL) 1033161356Sguido return -1; 1034161356Sguido 1035145516Sdarrenr#ifdef USE_INET6 1036145516Sdarrenr if (v == 6) { 1037255332Scy return ipf_ifpfillv6addr(atype, (struct sockaddr_in6 *)sock, 1038255332Scy (struct sockaddr_in6 *)mask, 1039255332Scy inp, inpmask); 1040145516Sdarrenr } 1041145516Sdarrenr#endif 1042255332Scy return ipf_ifpfillv4addr(atype, (struct sockaddr_in *)sock, 1043255332Scy (struct sockaddr_in *)mask, 1044255332Scy &inp->in4, &inpmask->in4); 1045145516Sdarrenr} 1046145516Sdarrenr 1047145516Sdarrenr 1048255332Scyu_32_t 1049255332Scyipf_newisn(fin) 1050255332Scy fr_info_t *fin; 1051145516Sdarrenr{ 1052145516Sdarrenr u_32_t newiss; 1053145516Sdarrenr newiss = arc4random(); 1054145516Sdarrenr return newiss; 1055145516Sdarrenr} 1056145516Sdarrenr 1057145516Sdarrenr 1058145516Sdarrenr/* ------------------------------------------------------------------------ */ 1059255332Scy/* Function: ipf_nextipid */ 1060145516Sdarrenr/* Returns: int - 0 == success, -1 == error (packet should be droppped) */ 1061145516Sdarrenr/* Parameters: fin(I) - pointer to packet information */ 1062145516Sdarrenr/* */ 1063145516Sdarrenr/* Returns the next IPv4 ID to use for this packet. */ 1064145516Sdarrenr/* ------------------------------------------------------------------------ */ 1065255332Scyu_short 1066255332Scyipf_nextipid(fin) 1067255332Scy fr_info_t *fin; 1068145516Sdarrenr{ 1069145516Sdarrenr u_short id; 1070145516Sdarrenr 1071255332Scy#ifndef RANDOM_IP_ID 1072255332Scy MUTEX_ENTER(&ipfmain.ipf_rw); 1073145516Sdarrenr id = ipid++; 1074255332Scy MUTEX_EXIT(&ipfmain.ipf_rw); 1075145516Sdarrenr#else 1076145516Sdarrenr id = ip_randomid(); 1077145516Sdarrenr#endif 1078145516Sdarrenr 1079145516Sdarrenr return id; 1080145516Sdarrenr} 1081145516Sdarrenr 1082145516Sdarrenr 1083255332ScyINLINE int 1084255332Scyipf_checkv4sum(fin) 1085255332Scy fr_info_t *fin; 1086145516Sdarrenr{ 1087145516Sdarrenr#ifdef CSUM_DATA_VALID 1088145516Sdarrenr int manual = 0; 1089145516Sdarrenr u_short sum; 1090145516Sdarrenr ip_t *ip; 1091145516Sdarrenr mb_t *m; 1092145516Sdarrenr 1093145516Sdarrenr if ((fin->fin_flx & FI_NOCKSUM) != 0) 1094255332Scy return 0; 1095145516Sdarrenr 1096255332Scy if ((fin->fin_flx & FI_SHORT) != 0) 1097255332Scy return 1; 1098172776Sdarrenr 1099255332Scy if (fin->fin_cksum != FI_CK_NEEDED) 1100255332Scy return (fin->fin_cksum > FI_CK_NEEDED) ? 0 : -1; 1101255332Scy 1102145516Sdarrenr m = fin->fin_m; 1103145516Sdarrenr if (m == NULL) { 1104145516Sdarrenr manual = 1; 1105145516Sdarrenr goto skipauto; 1106145516Sdarrenr } 1107145516Sdarrenr ip = fin->fin_ip; 1108145516Sdarrenr 1109255332Scy if ((m->m_pkthdr.csum_flags & (CSUM_IP_CHECKED|CSUM_IP_VALID)) == 1110255332Scy CSUM_IP_CHECKED) { 1111255332Scy fin->fin_cksum = FI_CK_BAD; 1112255332Scy fin->fin_flx |= FI_BAD; 1113255332Scy return -1; 1114255332Scy } 1115145516Sdarrenr if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID) { 1116145516Sdarrenr if (m->m_pkthdr.csum_flags & CSUM_PSEUDO_HDR) 1117145516Sdarrenr sum = m->m_pkthdr.csum_data; 1118145516Sdarrenr else 1119145516Sdarrenr sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, 1120145516Sdarrenr htonl(m->m_pkthdr.csum_data + 1121255332Scy fin->fin_dlen + fin->fin_p)); 1122145516Sdarrenr sum ^= 0xffff; 1123172776Sdarrenr if (sum != 0) { 1124255332Scy fin->fin_cksum = FI_CK_BAD; 1125145516Sdarrenr fin->fin_flx |= FI_BAD; 1126172776Sdarrenr } else { 1127255332Scy fin->fin_cksum = FI_CK_SUMOK; 1128255332Scy return 0; 1129172776Sdarrenr } 1130255332Scy } else { 1131255332Scy if (m->m_pkthdr.csum_flags == CSUM_DELAY_DATA) { 1132255332Scy fin->fin_cksum = FI_CK_L4FULL; 1133255332Scy return 0; 1134255332Scy } else if (m->m_pkthdr.csum_flags == CSUM_TCP || 1135255332Scy m->m_pkthdr.csum_flags == CSUM_UDP) { 1136255332Scy fin->fin_cksum = FI_CK_L4PART; 1137255332Scy return 0; 1138255332Scy } else if (m->m_pkthdr.csum_flags == CSUM_IP) { 1139255332Scy fin->fin_cksum = FI_CK_L4PART; 1140255332Scy return 0; 1141255332Scy } else { 1142255332Scy manual = 1; 1143255332Scy } 1144255332Scy } 1145145516Sdarrenrskipauto: 1146255332Scy if (manual != 0) { 1147255332Scy if (ipf_checkl4sum(fin) == -1) { 1148145516Sdarrenr fin->fin_flx |= FI_BAD; 1149255332Scy return -1; 1150255332Scy } 1151255332Scy } 1152145516Sdarrenr#else 1153255332Scy if (ipf_checkl4sum(fin) == -1) { 1154145516Sdarrenr fin->fin_flx |= FI_BAD; 1155255332Scy return -1; 1156255332Scy } 1157145516Sdarrenr#endif 1158255332Scy return 0; 1159145516Sdarrenr} 1160145516Sdarrenr 1161145516Sdarrenr 1162145516Sdarrenr#ifdef USE_INET6 1163255332ScyINLINE int 1164255332Scyipf_checkv6sum(fin) 1165255332Scy fr_info_t *fin; 1166145516Sdarrenr{ 1167255332Scy if ((fin->fin_flx & FI_NOCKSUM) != 0) 1168255332Scy return 0; 1169255332Scy 1170255332Scy if ((fin->fin_flx & FI_SHORT) != 0) 1171255332Scy return 1; 1172255332Scy 1173255332Scy if (fin->fin_cksum != FI_CK_NEEDED) 1174255332Scy return (fin->fin_cksum > FI_CK_NEEDED) ? 0 : -1; 1175255332Scy 1176255332Scy if (ipf_checkl4sum(fin) == -1) { 1177145516Sdarrenr fin->fin_flx |= FI_BAD; 1178255332Scy return -1; 1179255332Scy } 1180255332Scy return 0; 1181145516Sdarrenr} 1182145516Sdarrenr#endif /* USE_INET6 */ 1183145516Sdarrenr 1184145516Sdarrenr 1185255332Scysize_t 1186255332Scymbufchainlen(m0) 1187255332Scy struct mbuf *m0; 1188255332Scy { 1189145516Sdarrenr size_t len; 1190145516Sdarrenr 1191145516Sdarrenr if ((m0->m_flags & M_PKTHDR) != 0) { 1192145516Sdarrenr len = m0->m_pkthdr.len; 1193145516Sdarrenr } else { 1194145516Sdarrenr struct mbuf *m; 1195145516Sdarrenr 1196145516Sdarrenr for (m = m0, len = 0; m != NULL; m = m->m_next) 1197145516Sdarrenr len += m->m_len; 1198145516Sdarrenr } 1199145516Sdarrenr return len; 1200145516Sdarrenr} 1201145516Sdarrenr 1202145516Sdarrenr 1203145516Sdarrenr/* ------------------------------------------------------------------------ */ 1204255332Scy/* Function: ipf_pullup */ 1205145516Sdarrenr/* Returns: NULL == pullup failed, else pointer to protocol header */ 1206255332Scy/* Parameters: xmin(I)- pointer to buffer where data packet starts */ 1207145516Sdarrenr/* fin(I) - pointer to packet information */ 1208145516Sdarrenr/* len(I) - number of bytes to pullup */ 1209145516Sdarrenr/* */ 1210145516Sdarrenr/* Attempt to move at least len bytes (from the start of the buffer) into a */ 1211145516Sdarrenr/* single buffer for ease of access. Operating system native functions are */ 1212145516Sdarrenr/* used to manage buffers - if necessary. If the entire packet ends up in */ 1213255332Scy/* a single buffer, set the FI_COALESCE flag even though ipf_coalesce() has */ 1214145516Sdarrenr/* not been called. Both fin_ip and fin_dp are updated before exiting _IF_ */ 1215145516Sdarrenr/* and ONLY if the pullup succeeds. */ 1216145516Sdarrenr/* */ 1217255332Scy/* We assume that 'xmin' is a pointer to a buffer that is part of the chain */ 1218145516Sdarrenr/* of buffers that starts at *fin->fin_mp. */ 1219145516Sdarrenr/* ------------------------------------------------------------------------ */ 1220255332Scyvoid * 1221255332Scyipf_pullup(xmin, fin, len) 1222255332Scy mb_t *xmin; 1223255332Scy fr_info_t *fin; 1224255332Scy int len; 1225145516Sdarrenr{ 1226255332Scy int dpoff, ipoff; 1227255332Scy mb_t *m = xmin; 1228145516Sdarrenr char *ip; 1229145516Sdarrenr 1230145516Sdarrenr if (m == NULL) 1231145516Sdarrenr return NULL; 1232145516Sdarrenr 1233145516Sdarrenr ip = (char *)fin->fin_ip; 1234145516Sdarrenr if ((fin->fin_flx & FI_COALESCE) != 0) 1235145516Sdarrenr return ip; 1236145516Sdarrenr 1237145516Sdarrenr ipoff = fin->fin_ipoff; 1238145516Sdarrenr if (fin->fin_dp != NULL) 1239145516Sdarrenr dpoff = (char *)fin->fin_dp - (char *)ip; 1240145516Sdarrenr else 1241145516Sdarrenr dpoff = 0; 1242145516Sdarrenr 1243145516Sdarrenr if (M_LEN(m) < len) { 1244255332Scy mb_t *n = *fin->fin_mp; 1245145516Sdarrenr /* 1246145516Sdarrenr * Assume that M_PKTHDR is set and just work with what is left 1247145516Sdarrenr * rather than check.. 1248145516Sdarrenr * Should not make any real difference, anyway. 1249145516Sdarrenr */ 1250255332Scy if (m != n) { 1251255332Scy /* 1252255332Scy * Record the mbuf that points to the mbuf that we're 1253255332Scy * about to go to work on so that we can update the 1254255332Scy * m_next appropriately later. 1255255332Scy */ 1256255332Scy for (; n->m_next != m; n = n->m_next) 1257255332Scy ; 1258255332Scy } else { 1259255332Scy n = NULL; 1260255332Scy } 1261255332Scy 1262255332Scy#ifdef MHLEN 1263145516Sdarrenr if (len > MHLEN) 1264145516Sdarrenr#else 1265145516Sdarrenr if (len > MLEN) 1266145516Sdarrenr#endif 1267145516Sdarrenr { 1268145516Sdarrenr#ifdef HAVE_M_PULLDOWN 1269145516Sdarrenr if (m_pulldown(m, 0, len, NULL) == NULL) 1270145516Sdarrenr m = NULL; 1271145516Sdarrenr#else 1272145516Sdarrenr FREE_MB_T(*fin->fin_mp); 1273145516Sdarrenr m = NULL; 1274255332Scy n = NULL; 1275145516Sdarrenr#endif 1276145516Sdarrenr } else 1277145516Sdarrenr { 1278145516Sdarrenr m = m_pullup(m, len); 1279145516Sdarrenr } 1280255332Scy if (n != NULL) 1281255332Scy n->m_next = m; 1282145516Sdarrenr if (m == NULL) { 1283255332Scy /* 1284255332Scy * When n is non-NULL, it indicates that m pointed to 1285255332Scy * a sub-chain (tail) of the mbuf and that the head 1286255332Scy * of this chain has not yet been free'd. 1287255332Scy */ 1288255332Scy if (n != NULL) { 1289255332Scy FREE_MB_T(*fin->fin_mp); 1290255332Scy } 1291255332Scy 1292255332Scy *fin->fin_mp = NULL; 1293172776Sdarrenr fin->fin_m = NULL; 1294145516Sdarrenr return NULL; 1295145516Sdarrenr } 1296172776Sdarrenr 1297255332Scy if (n == NULL) 1298255332Scy *fin->fin_mp = m; 1299255332Scy 1300172776Sdarrenr while (M_LEN(m) == 0) { 1301172776Sdarrenr m = m->m_next; 1302172776Sdarrenr } 1303172776Sdarrenr fin->fin_m = m; 1304145516Sdarrenr ip = MTOD(m, char *) + ipoff; 1305255332Scy 1306255332Scy fin->fin_ip = (ip_t *)ip; 1307255332Scy if (fin->fin_dp != NULL) 1308255332Scy fin->fin_dp = (char *)fin->fin_ip + dpoff; 1309255332Scy if (fin->fin_fraghdr != NULL) 1310255332Scy fin->fin_fraghdr = (char *)ip + 1311255332Scy ((char *)fin->fin_fraghdr - 1312255332Scy (char *)fin->fin_ip); 1313145516Sdarrenr } 1314145516Sdarrenr 1315145516Sdarrenr if (len == fin->fin_plen) 1316145516Sdarrenr fin->fin_flx |= FI_COALESCE; 1317145516Sdarrenr return ip; 1318145516Sdarrenr} 1319170268Sdarrenr 1320170268Sdarrenr 1321255332Scyint 1322255332Scyipf_inject(fin, m) 1323255332Scy fr_info_t *fin; 1324255332Scy mb_t *m; 1325170268Sdarrenr{ 1326170268Sdarrenr int error = 0; 1327170268Sdarrenr 1328170268Sdarrenr if (fin->fin_out == 0) { 1329170268Sdarrenr netisr_dispatch(NETISR_IP, m); 1330170268Sdarrenr } else { 1331173931Sdarrenr fin->fin_ip->ip_len = ntohs(fin->fin_ip->ip_len); 1332173931Sdarrenr fin->fin_ip->ip_off = ntohs(fin->fin_ip->ip_off); 1333170268Sdarrenr error = ip_output(m, NULL, NULL, IP_FORWARDING, NULL, NULL); 1334170268Sdarrenr } 1335170268Sdarrenr 1336170268Sdarrenr return error; 1337170268Sdarrenr} 1338172776Sdarrenr 1339172776Sdarrenrint ipf_pfil_unhook(void) { 1340172776Sdarrenr#if defined(NETBSD_PF) && (__FreeBSD_version >= 500011) 1341172776Sdarrenr struct pfil_head *ph_inet; 1342172776Sdarrenr# ifdef USE_INET6 1343172776Sdarrenr struct pfil_head *ph_inet6; 1344172776Sdarrenr# endif 1345172776Sdarrenr#endif 1346172776Sdarrenr 1347172776Sdarrenr#ifdef NETBSD_PF 1348172776Sdarrenr ph_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET); 1349172776Sdarrenr if (ph_inet != NULL) 1350255332Scy pfil_remove_hook((void *)ipf_check_wrapper, NULL, 1351172776Sdarrenr PFIL_IN|PFIL_OUT|PFIL_WAITOK, ph_inet); 1352172776Sdarrenr# ifdef USE_INET6 1353172776Sdarrenr ph_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6); 1354172776Sdarrenr if (ph_inet6 != NULL) 1355255332Scy pfil_remove_hook((void *)ipf_check_wrapper6, NULL, 1356172776Sdarrenr PFIL_IN|PFIL_OUT|PFIL_WAITOK, ph_inet6); 1357172776Sdarrenr# endif 1358172776Sdarrenr#endif 1359172776Sdarrenr 1360172776Sdarrenr return (0); 1361172776Sdarrenr} 1362172776Sdarrenr 1363172776Sdarrenrint ipf_pfil_hook(void) { 1364172776Sdarrenr#if defined(NETBSD_PF) && (__FreeBSD_version >= 500011) 1365172776Sdarrenr struct pfil_head *ph_inet; 1366172776Sdarrenr# ifdef USE_INET6 1367172776Sdarrenr struct pfil_head *ph_inet6; 1368172776Sdarrenr# endif 1369172776Sdarrenr#endif 1370172776Sdarrenr 1371172776Sdarrenr# ifdef NETBSD_PF 1372172776Sdarrenr ph_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET); 1373172776Sdarrenr# ifdef USE_INET6 1374172776Sdarrenr ph_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6); 1375172776Sdarrenr# endif 1376172776Sdarrenr if (ph_inet == NULL 1377172776Sdarrenr# ifdef USE_INET6 1378172776Sdarrenr && ph_inet6 == NULL 1379172776Sdarrenr# endif 1380255332Scy ) { 1381172776Sdarrenr return ENODEV; 1382255332Scy } 1383172776Sdarrenr 1384172776Sdarrenr if (ph_inet != NULL) 1385255332Scy pfil_add_hook((void *)ipf_check_wrapper, NULL, 1386172776Sdarrenr PFIL_IN|PFIL_OUT|PFIL_WAITOK, ph_inet); 1387172776Sdarrenr# ifdef USE_INET6 1388172776Sdarrenr if (ph_inet6 != NULL) 1389255332Scy pfil_add_hook((void *)ipf_check_wrapper6, NULL, 1390172776Sdarrenr PFIL_IN|PFIL_OUT|PFIL_WAITOK, ph_inet6); 1391172776Sdarrenr# endif 1392172776Sdarrenr# endif 1393172776Sdarrenr return (0); 1394172776Sdarrenr} 1395172776Sdarrenr 1396172776Sdarrenrvoid 1397172776Sdarrenripf_event_reg(void) 1398172776Sdarrenr{ 1399255332Scy ipf_arrivetag = EVENTHANDLER_REGISTER(ifnet_arrival_event, \ 1400255332Scy ipf_ifevent, &ipfmain, \ 1401172776Sdarrenr EVENTHANDLER_PRI_ANY); 1402255332Scy ipf_departtag = EVENTHANDLER_REGISTER(ifnet_departure_event, \ 1403255332Scy ipf_ifevent, &ipfmain, \ 1404172776Sdarrenr EVENTHANDLER_PRI_ANY); 1405255332Scy ipf_clonetag = EVENTHANDLER_REGISTER(if_clone_event, ipf_ifevent, \ 1406255332Scy &ipfmain, EVENTHANDLER_PRI_ANY); 1407172776Sdarrenr} 1408172776Sdarrenr 1409172776Sdarrenrvoid 1410172776Sdarrenripf_event_dereg(void) 1411172776Sdarrenr{ 1412172776Sdarrenr if (ipf_arrivetag != NULL) { 1413172776Sdarrenr EVENTHANDLER_DEREGISTER(ifnet_arrival_event, ipf_arrivetag); 1414172776Sdarrenr } 1415172776Sdarrenr if (ipf_departtag != NULL) { 1416172776Sdarrenr EVENTHANDLER_DEREGISTER(ifnet_departure_event, ipf_departtag); 1417172776Sdarrenr } 1418172776Sdarrenr if (ipf_clonetag != NULL) { 1419172776Sdarrenr EVENTHANDLER_DEREGISTER(if_clone_event, ipf_clonetag); 1420172776Sdarrenr } 1421172776Sdarrenr} 1422255332Scy 1423255332Scy 1424255332Scyu_32_t 1425255332Scyipf_random() 1426255332Scy{ 1427255332Scy return arc4random(); 1428255332Scy} 1429255332Scy 1430255332Scy 1431255332Scyu_int 1432255332Scyipf_pcksum(fin, hlen, sum) 1433255332Scy fr_info_t *fin; 1434255332Scy int hlen; 1435255332Scy u_int sum; 1436255332Scy{ 1437255332Scy struct mbuf *m; 1438255332Scy u_int sum2; 1439255332Scy int off; 1440255332Scy 1441255332Scy m = fin->fin_m; 1442255332Scy off = (char *)fin->fin_dp - (char *)fin->fin_ip; 1443255332Scy m->m_data += hlen; 1444255332Scy m->m_len -= hlen; 1445255332Scy sum2 = in_cksum(fin->fin_m, fin->fin_plen - off); 1446255332Scy m->m_len += hlen; 1447255332Scy m->m_data -= hlen; 1448255332Scy 1449255332Scy /* 1450255332Scy * Both sum and sum2 are partial sums, so combine them together. 1451255332Scy */ 1452255332Scy sum += ~sum2 & 0xffff; 1453255332Scy while (sum > 0xffff) 1454255332Scy sum = (sum & 0xffff) + (sum >> 16); 1455255332Scy sum2 = ~sum & 0xffff; 1456255332Scy return sum2; 1457255332Scy} 1458