Index: projects/vnet/sys/contrib/ipfilter/netinet/ip_fil_freebsd.c =================================================================== --- projects/vnet/sys/contrib/ipfilter/netinet/ip_fil_freebsd.c (revision 302202) +++ projects/vnet/sys/contrib/ipfilter/netinet/ip_fil_freebsd.c (revision 302203) @@ -1,1459 +1,1462 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 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$"; #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 #include #include #include # include # include #include #include # include #if defined(__FreeBSD_version) && (__FreeBSD_version >= 800000) #include #endif # include # include # include #if !defined(__hpux) # include #endif #include # include # include #include # include # include #include #include #include #include #include #include #include #include #if defined(__FreeBSD_version) && (__FreeBSD_version >= 800000) #include #else #define CURVNET_SET(arg) #define CURVNET_RESTORE() +#define VNET_DEFINE(_t, _v) _t _v +#define VNET_DECLARE(_t, _v) extern _t _v +#define VNET(arg) arg #endif #if defined(__osf__) # include #endif #include #include #include #include "netinet/ip_compat.h" #ifdef USE_INET6 # include #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" #include "netinet/ip_sync.h" #include "netinet/ip_lookup.h" #include "netinet/ip_dstlist.h" #ifdef IPFILTER_SCAN #include "netinet/ip_scan.h" #endif #include "netinet/ip_pool.h" # include #include #ifdef CSUM_DATA_VALID #include #endif extern int ip_optcopy __P((struct ip *, struct ip *)); # ifdef IPFILTER_M_IPFILTER MALLOC_DEFINE(M_IPFILTER, "ipfilter", "IP Filter packet filter data structures"); # endif static int ipf_send_ip __P((fr_info_t *, mb_t *)); static void ipf_timer_func __P((void *arg)); VNET_DEFINE(ipf_main_softc_t, ipfmain); #define V_ipfmain VNET(ipfmain) # include # if defined(NETBSD_PF) # include # endif /* NETBSD_PF */ static eventhandler_tag ipf_arrivetag, ipf_departtag, ipf_clonetag; static void ipf_ifevent(void *arg, struct ifnet *ifp); static void ipf_ifevent(arg, ifp) void *arg; struct ifnet *ifp; { CURVNET_SET(ifp->if_vnet); ipf_sync(&V_ipfmain, NULL); CURVNET_RESTORE(); } static int ipf_check_wrapper(void *arg, struct mbuf **mp, struct ifnet *ifp, int dir) { struct ip *ip = mtod(*mp, struct ip *); int rv; /* * IPFilter expects evreything in network byte order */ #if (__FreeBSD_version < 1000019) ip->ip_len = htons(ip->ip_len); ip->ip_off = htons(ip->ip_off); #endif CURVNET_SET(ifp->if_vnet); rv = ipf_check(&V_ipfmain, ip, ip->ip_hl << 2, ifp, (dir == PFIL_OUT), mp); CURVNET_RESTORE(); #if (__FreeBSD_version < 1000019) if ((rv == 0) && (*mp != NULL)) { ip = mtod(*mp, struct ip *); ip->ip_len = ntohs(ip->ip_len); ip->ip_off = ntohs(ip->ip_off); } #endif return rv; } # ifdef USE_INET6 # include static int ipf_check_wrapper6(void *arg, struct mbuf **mp, struct ifnet *ifp, int dir) { int error; CURVNET_SET(ifp->if_vnet); error = ipf_check(&V_ipfmain, mtod(*mp, struct ip *), sizeof(struct ip6_hdr), ifp, (dir == PFIL_OUT), mp); CURVNET_RESTORE(); return (error); } # endif #if defined(IPFILTER_LKM) int ipf_identify(s) char *s; { if (strcmp(s, "ipl") == 0) return 1; return 0; } #endif /* IPFILTER_LKM */ static void ipf_timer_func(arg) void *arg; { ipf_main_softc_t *softc = arg; SPL_INT(s); SPL_NET(s); READ_ENTER(&softc->ipf_global); if (softc->ipf_running > 0) ipf_slowtimer(softc); if (softc->ipf_running == -1 || softc->ipf_running == 1) { #if 0 softc->ipf_slow_ch = timeout(ipf_timer_func, softc, hz/2); #endif callout_init(&softc->ipf_slow_ch, 1); callout_reset(&softc->ipf_slow_ch, (hz / IPF_HZ_DIVIDE) * IPF_HZ_MULT, ipf_timer_func, softc); } RWLOCK_EXIT(&softc->ipf_global); SPL_X(s); } int ipfattach(softc) ipf_main_softc_t *softc; { #ifdef USE_SPL int s; #endif SPL_NET(s); if (softc->ipf_running > 0) { SPL_X(s); return EBUSY; } if (ipf_init_all(softc) < 0) { SPL_X(s); return EIO; } bzero((char *)V_ipfmain.ipf_selwait, sizeof(V_ipfmain.ipf_selwait)); softc->ipf_running = 1; if (softc->ipf_control_forwarding & 1) V_ipforwarding = 1; SPL_X(s); #if 0 softc->ipf_slow_ch = timeout(ipf_timer_func, softc, (hz / IPF_HZ_DIVIDE) * IPF_HZ_MULT); #endif callout_init(&softc->ipf_slow_ch, 1); callout_reset(&softc->ipf_slow_ch, (hz / IPF_HZ_DIVIDE) * IPF_HZ_MULT, ipf_timer_func, softc); return 0; } /* * Disable the filter by removing the hooks from the IP input/output * stream. */ int ipfdetach(softc) ipf_main_softc_t *softc; { #ifdef USE_SPL int s; #endif if (softc->ipf_control_forwarding & 2) V_ipforwarding = 0; SPL_NET(s); #if 0 if (softc->ipf_slow_ch.callout != NULL) untimeout(ipf_timer_func, softc, softc->ipf_slow_ch); bzero(&softc->ipf_slow, sizeof(softc->ipf_slow)); #endif callout_drain(&softc->ipf_slow_ch); ipf_fini_all(softc); softc->ipf_running = -2; SPL_X(s); return 0; } /* * Filter ioctl interface. */ int ipfioctl(dev, cmd, data, mode , p) struct thread *p; # define p_cred td_ucred # define p_uid td_ucred->cr_ruid struct cdev *dev; ioctlcmd_t cmd; caddr_t data; int mode; { int error = 0, unit = 0; SPL_INT(s); #if (BSD >= 199306) if (securelevel_ge(p->p_cred, 3) && (mode & FWRITE)) { V_ipfmain.ipf_interror = 130001; return EPERM; } #endif unit = GET_MINOR(dev); if ((IPL_LOGMAX < unit) || (unit < 0)) { V_ipfmain.ipf_interror = 130002; return ENXIO; } if (V_ipfmain.ipf_running <= 0) { if (unit != IPL_LOGIPF && cmd != SIOCIPFINTERROR) { V_ipfmain.ipf_interror = 130003; return EIO; } if (cmd != SIOCIPFGETNEXT && cmd != SIOCIPFGET && cmd != SIOCIPFSET && cmd != SIOCFRENB && cmd != SIOCGETFS && cmd != SIOCGETFF && cmd != SIOCIPFINTERROR) { V_ipfmain.ipf_interror = 130004; return EIO; } } SPL_NET(s); CURVNET_SET(TD_TO_VNET(p)); error = ipf_ioctlswitch(&V_ipfmain, unit, data, cmd, mode, p->p_uid, p); CURVNET_RESTORE(); if (error != -1) { SPL_X(s); return error; } SPL_X(s); return error; } /* * ipf_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 ipf_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 */ if (ipf_checkl4sum(fin) == -1) return -1; 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_NOWAIT, MT_HEADER); #else MGET(m, M_NOWAIT, MT_HEADER); #endif if (m == NULL) return -1; if (sizeof(*tcp2) + hlen > MLEN) { if (!(MCLGET(m, M_NOWAIT))) { 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.in6; ip6->ip6_dst = fin->fin_src6.in6; tcp2->th_sum = in6_cksum(m, IPPROTO_TCP, sizeof(*ip6), sizeof(*tcp2)); return ipf_send_ip(fin, 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 = htons(hlen + sizeof(*tcp2)); return ipf_send_ip(fin, m); } /* * ip_len must be in network byte order when called. */ static int ipf_send_ip(fin, m) fr_info_t *fin; mb_t *m; { fr_info_t fnew; ip_t *ip, *oip; int hlen; ip = mtod(m, ip_t *); bzero((char *)&fnew, sizeof(fnew)); fnew.fin_main_soft = fin->fin_main_soft; IP_V_A(ip, fin->fin_v); switch (fin->fin_v) { case 4 : oip = fin->fin_ip; hlen = sizeof(*oip); fnew.fin_v = 4; fnew.fin_p = ip->ip_p; fnew.fin_plen = ntohs(ip->ip_len); IP_HL_A(ip, sizeof(*oip) >> 2); ip->ip_tos = oip->ip_tos; ip->ip_id = fin->fin_ip->ip_id; #if defined(FreeBSD) && (__FreeBSD_version > 460000) ip->ip_off = htons(path_mtu_discovery ? IP_DF : 0); #else ip->ip_off = 0; #endif ip->ip_ttl = V_ip_defttl; ip->ip_sum = 0; break; #ifdef USE_INET6 case 6 : { ip6_t *ip6 = (ip6_t *)ip; ip6->ip6_vfc = 0x60; ip6->ip6_hlim = IPDEFTTL; hlen = sizeof(*ip6); fnew.fin_p = ip6->ip6_nxt; fnew.fin_v = 6; fnew.fin_plen = ntohs(ip6->ip6_plen) + hlen; 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 = &m; fnew.fin_hlen = hlen; fnew.fin_dp = (char *)ip + hlen; (void) ipf_makefrip(hlen, ip, &fnew); return ipf_fastroute(m, &m, &fnew, NULL); } int ipf_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; i6addr_t dst6; void *ifp; #ifdef USE_INET6 ip6_t *ip6; #endif ip_t *ip, *ip2; if ((type < 0) || (type >= ICMP_MAXTYPE)) return -1; code = fin->fin_icode; #ifdef USE_INET6 #if 0 /* XXX Fix an off by one error: s/>/>=/ was: if ((code < 0) || (code > sizeof(icmptoicmp6unreach)/sizeof(int))) Fix obtained from NetBSD ip_fil_netbsd.c r1.4: */ #endif if ((code < 0) || (code >= sizeof(icmptoicmp6unreach)/sizeof(int))) return -1; #endif if (ipf_checkl4sum(fin) == -1) return -1; #ifdef MGETHDR MGETHDR(m, M_NOWAIT, MT_HEADER); #else MGET(m, M_NOWAIT, MT_HEADER); #endif if (m == NULL) return -1; avail = MHLEN; xtra = 0; hlen = 0; ohlen = 0; dst4.s_addr = 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 (ipf_ifpaddr(&V_ipfmain, 4, FRI_NORMAL, ifp, &dst6, NULL) == -1) { FREE_MB_T(m); return -1; } dst4 = dst6.in4; } else dst4.s_addr = fin->fin_daddr; hlen = sizeof(ip_t); ohlen = fin->fin_hlen; iclen = hlen + offsetof(struct icmp, icmp_ip) + ohlen; 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); iclen = hlen + offsetof(struct icmp, icmp_ip) + ohlen; type = icmptoicmp6types[type]; if (type == ICMP6_DST_UNREACH) code = icmptoicmp6unreach[code]; if (iclen + max_linkhdr + fin->fin_plen > avail) { if (!(MCLGET(m, M_NOWAIT))) { FREE_MB_T(m); return -1; } avail = MCLBYTES; } xtra = MIN(fin->fin_plen, avail - iclen - max_linkhdr); xtra = MIN(xtra, IPV6_MMTU - iclen); if (dst == 0) { if (ipf_ifpaddr(&V_ipfmain, 6, FRI_NORMAL, ifp, &dst6, NULL) == -1) { FREE_MB_T(m); return -1; } } else dst6 = fin->fin_dst6; } #endif else { FREE_MB_T(m); return -1; } 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) { if (fin->fin_mtu != 0) { icmp->icmp_nextmtu = htons(fin->fin_mtu); } else if (ifp != NULL) { icmp->icmp_nextmtu = htons(GETIFMTU_4(ifp)); } else { /* make up a number... */ icmp->icmp_nextmtu = htons(fin->fin_plen - 20); } } #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.in6; ip6->ip6_dst = fin->fin_src6.in6; 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 { 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 = htons(iclen); ip->ip_p = IPPROTO_ICMP; } err = ipf_send_ip(fin, m); return err; } /* * m0 - pointer to mbuf where the IP packet starts * mpp - pointer to the mbuf pointer that is the start of the mbuf chain */ int ipf_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 = *mpp; int len, off, error = 0, hlen, code; struct ifnet *ifp, *sifp; struct sockaddr_in dst; struct nhop4_extended nh4; int has_nhop = 0; u_long fibnum = 0; u_short ip_off; frdest_t node; 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) { m0 = m_dup(m, M_NOWAIT); if (m0 != NULL) { FREE_MB_T(m); m = m0; *mpp = m; } else { error = ENOBUFS; FREE_MB_T(m); goto done; } } #endif #ifdef USE_INET6 if (fin->fin_v == 6) { /* * currently "to " and "to :ip#" are not supported * for IPv6 */ return ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL); } #endif hlen = fin->fin_hlen; ip = mtod(m0, struct ip *); ifp = NULL; /* * Route packet. */ bzero(&dst, sizeof (dst)); dst.sin_family = AF_INET; dst.sin_addr = ip->ip_dst; dst.sin_len = sizeof(dst); fr = fin->fin_fr; if ((fr != NULL) && !(fr->fr_flags & FR_KEEPSTATE) && (fdp != NULL) && (fdp->fd_type == FRD_DSTLIST)) { if (ipf_dstlist_select_node(fin, fdp->fd_ptr, NULL, &node) == 0) fdp = &node; } if (fdp != NULL) ifp = fdp->fd_ptr; else ifp = fin->fin_ifp; if ((ifp == NULL) && ((fr == NULL) || !(fr->fr_flags & FR_FASTROUTE))) { error = -2; goto bad; } if ((fdp != NULL) && (fdp->fd_ip.s_addr != 0)) dst.sin_addr = fdp->fd_ip; fibnum = M_GETFIB(m0); if (fib4_lookup_nh_ext(fibnum, dst.sin_addr, NHR_REF, 0, &nh4) != 0) { if (in_localaddr(ip->ip_dst)) error = EHOSTUNREACH; else error = ENETUNREACH; goto bad; } has_nhop = 1; if (ifp == NULL) ifp = nh4.nh_ifp; if (nh4.nh_flags & NHF_GATEWAY) dst.sin_addr = nh4.nh_addr; /* * 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. Duplicated packets aren't considered to be * part of the normal packet stream, so do not NAT them or pass * them through stateful checking, etc. */ if ((fdp != &fr->fr_dif) && (fin->fin_out == 0)) { sifp = fin->fin_ifp; fin->fin_ifp = ifp; fin->fin_out = 1; (void) ipf_acctpkt(fin, NULL); fin->fin_fr = NULL; if (!fr || !(fr->fr_flags & FR_RETMASK)) { u_32_t pass; (void) ipf_state_check(fin, &pass); } switch (ipf_nat_checkout(fin, NULL)) { case 0 : break; case 1 : ip->ip_sum = 0; break; case -1 : error = -1; goto bad; break; } fin->fin_ifp = sifp; fin->fin_out = 0; } else ip->ip_sum = 0; /* * If small enough for interface, can just send directly. */ if (ntohs(ip->ip_len) <= ifp->if_mtu) { if (!ip->ip_sum) ip->ip_sum = in_cksum(m, hlen); error = (*ifp->if_output)(ifp, m, (struct sockaddr *)&dst, NULL ); 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 < ntohs(ip->ip_len); off += len) { #ifdef MGETHDR MGETHDR(m, M_NOWAIT, MT_HEADER); #else MGET(m, M_NOWAIT, MT_HEADER); #endif if (m == NULL) { 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 >= ntohs(ip->ip_len)) len = ntohs(ip->ip_len) - off; else mhip->ip_off |= IP_MF; mhip->ip_len = htons((u_short)(len + mhlen)); *mnext = m; 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->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, NULL ); else FREE_MB_T(m); } } done: if (!error) V_ipfmain.ipf_frouteok[0]++; else V_ipfmain.ipf_frouteok[1]++; if (has_nhop) fib4_free_nh_ext(fibnum, &nh4); 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) ipf_send_icmp_err(ICMP_UNREACH, fin, 1); fin->fin_ifp = sifp; fin->fin_icode = code; } FREE_MB_T(m); goto done; } int ipf_verifysrc(fin) fr_info_t *fin; { struct nhop4_basic nh4; if (fib4_lookup_nh_basic(0, fin->fin_src, 0, 0, &nh4) != 0) return (0); return (fin->fin_ifp == nh4.nh_ifp); } /* * return the first IP Address associated with an interface */ int ipf_ifpaddr(softc, v, atype, ifptr, inp, inpmask) ipf_main_softc_t *softc; int v, atype; void *ifptr; i6addr_t *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->in4.s_addr = 0; #ifdef USE_INET6 else if (v == 6) bzero((char *)inp, sizeof(*inp)); #endif ifa = TAILQ_FIRST(&ifp->if_addrhead); 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 ifa = TAILQ_NEXT(ifa, ifa_link); 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; if (sock == NULL) return -1; #ifdef USE_INET6 if (v == 6) { return ipf_ifpfillv6addr(atype, (struct sockaddr_in6 *)sock, (struct sockaddr_in6 *)mask, inp, inpmask); } #endif return ipf_ifpfillv4addr(atype, (struct sockaddr_in *)sock, (struct sockaddr_in *)mask, &inp->in4, &inpmask->in4); } u_32_t ipf_newisn(fin) fr_info_t *fin; { u_32_t newiss; newiss = arc4random(); return newiss; } INLINE int ipf_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 0; if ((fin->fin_flx & FI_SHORT) != 0) return 1; if (fin->fin_cksum != FI_CK_NEEDED) return (fin->fin_cksum > FI_CK_NEEDED) ? 0 : -1; m = fin->fin_m; if (m == NULL) { manual = 1; goto skipauto; } ip = fin->fin_ip; if ((m->m_pkthdr.csum_flags & (CSUM_IP_CHECKED|CSUM_IP_VALID)) == CSUM_IP_CHECKED) { fin->fin_cksum = FI_CK_BAD; fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_checkv4sum_csum_ip_checked, fr_info_t *, fin, u_int, m->m_pkthdr.csum_flags & (CSUM_IP_CHECKED|CSUM_IP_VALID)); return -1; } if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID) { /* Depending on the driver, UDP may have zero checksum */ if (fin->fin_p == IPPROTO_UDP && (fin->fin_flx & (FI_FRAG|FI_SHORT|FI_BAD)) == 0) { udphdr_t *udp = fin->fin_dp; if (udp->uh_sum == 0) { /* * we're good no matter what the hardware * checksum flags and csum_data say (handling * of csum_data for zero UDP checksum is not * consistent across all drivers) */ fin->fin_cksum = 1; return 0; } } 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_dlen + fin->fin_p)); sum ^= 0xffff; if (sum != 0) { fin->fin_cksum = FI_CK_BAD; fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_checkv4sum_sum, fr_info_t *, fin, u_int, sum); } else { fin->fin_cksum = FI_CK_SUMOK; return 0; } } else { if (m->m_pkthdr.csum_flags == CSUM_DELAY_DATA) { fin->fin_cksum = FI_CK_L4FULL; return 0; } else if (m->m_pkthdr.csum_flags == CSUM_TCP || m->m_pkthdr.csum_flags == CSUM_UDP) { fin->fin_cksum = FI_CK_L4PART; return 0; } else if (m->m_pkthdr.csum_flags == CSUM_IP) { fin->fin_cksum = FI_CK_L4PART; return 0; } else { manual = 1; } } skipauto: if (manual != 0) { if (ipf_checkl4sum(fin) == -1) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_checkv4sum_manual, fr_info_t *, fin, u_int, manual); return -1; } } #else if (ipf_checkl4sum(fin) == -1) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_checkv4sum_checkl4sum, fr_info_t *, fin, u_int, -1); return -1; } #endif return 0; } #ifdef USE_INET6 INLINE int ipf_checkv6sum(fin) fr_info_t *fin; { if ((fin->fin_flx & FI_NOCKSUM) != 0) { DT(ipf_checkv6sum_fi_nocksum); return 0; } if ((fin->fin_flx & FI_SHORT) != 0) { DT(ipf_checkv6sum_fi_short); return 1; } if (fin->fin_cksum != FI_CK_NEEDED) { DT(ipf_checkv6sum_fi_ck_needed); return (fin->fin_cksum > FI_CK_NEEDED) ? 0 : -1; } if (ipf_checkl4sum(fin) == -1) { fin->fin_flx |= FI_BAD; DT2(ipf_fi_bad_checkv6sum_checkl4sum, fr_info_t *, fin, u_int, -1); return -1; } return 0; } #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: ipf_pullup */ /* Returns: NULL == pullup failed, else pointer to protocol header */ /* Parameters: xmin(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 ipf_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 'xmin' is a pointer to a buffer that is part of the chain */ /* of buffers that starts at *fin->fin_mp. */ /* ------------------------------------------------------------------------ */ void * ipf_pullup(xmin, fin, len) mb_t *xmin; fr_info_t *fin; int len; { int dpoff, ipoff; mb_t *m = xmin; 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) { mb_t *n = *fin->fin_mp; /* * Assume that M_PKTHDR is set and just work with what is left * rather than check.. * Should not make any real difference, anyway. */ if (m != n) { /* * Record the mbuf that points to the mbuf that we're * about to go to work on so that we can update the * m_next appropriately later. */ for (; n->m_next != m; n = n->m_next) ; } else { n = NULL; } #ifdef MHLEN 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; n = NULL; #endif } else { m = m_pullup(m, len); } if (n != NULL) n->m_next = m; if (m == NULL) { /* * When n is non-NULL, it indicates that m pointed to * a sub-chain (tail) of the mbuf and that the head * of this chain has not yet been free'd. */ if (n != NULL) { FREE_MB_T(*fin->fin_mp); } *fin->fin_mp = NULL; fin->fin_m = NULL; return NULL; } if (n == NULL) *fin->fin_mp = m; while (M_LEN(m) == 0) { m = m->m_next; } fin->fin_m = m; ip = MTOD(m, char *) + ipoff; fin->fin_ip = (ip_t *)ip; if (fin->fin_dp != NULL) fin->fin_dp = (char *)fin->fin_ip + dpoff; if (fin->fin_fraghdr != NULL) fin->fin_fraghdr = (char *)ip + ((char *)fin->fin_fraghdr - (char *)fin->fin_ip); } if (len == fin->fin_plen) fin->fin_flx |= FI_COALESCE; return ip; } int ipf_inject(fin, m) fr_info_t *fin; mb_t *m; { int error = 0; if (fin->fin_out == 0) { netisr_dispatch(NETISR_IP, m); } else { fin->fin_ip->ip_len = ntohs(fin->fin_ip->ip_len); fin->fin_ip->ip_off = ntohs(fin->fin_ip->ip_off); error = ip_output(m, NULL, NULL, IP_FORWARDING, NULL, NULL); } return error; } int ipf_pfil_unhook(void) { #if defined(NETBSD_PF) && (__FreeBSD_version >= 500011) struct pfil_head *ph_inet; # ifdef USE_INET6 struct pfil_head *ph_inet6; # endif #endif #ifdef NETBSD_PF ph_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET); if (ph_inet != NULL) pfil_remove_hook((void *)ipf_check_wrapper, NULL, PFIL_IN|PFIL_OUT|PFIL_WAITOK, ph_inet); # ifdef USE_INET6 ph_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6); if (ph_inet6 != NULL) pfil_remove_hook((void *)ipf_check_wrapper6, NULL, PFIL_IN|PFIL_OUT|PFIL_WAITOK, ph_inet6); # endif #endif return (0); } int ipf_pfil_hook(void) { #if defined(NETBSD_PF) && (__FreeBSD_version >= 500011) struct pfil_head *ph_inet; # ifdef USE_INET6 struct pfil_head *ph_inet6; # endif #endif # ifdef NETBSD_PF 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) pfil_add_hook((void *)ipf_check_wrapper, NULL, PFIL_IN|PFIL_OUT|PFIL_WAITOK, ph_inet); # ifdef USE_INET6 if (ph_inet6 != NULL) pfil_add_hook((void *)ipf_check_wrapper6, NULL, PFIL_IN|PFIL_OUT|PFIL_WAITOK, ph_inet6); # endif # endif return (0); } void ipf_event_reg(void) { ipf_arrivetag = EVENTHANDLER_REGISTER(ifnet_arrival_event, \ ipf_ifevent, NULL, \ EVENTHANDLER_PRI_ANY); ipf_departtag = EVENTHANDLER_REGISTER(ifnet_departure_event, \ ipf_ifevent, NULL, \ EVENTHANDLER_PRI_ANY); ipf_clonetag = EVENTHANDLER_REGISTER(if_clone_event, ipf_ifevent, \ NULL, EVENTHANDLER_PRI_ANY); } void ipf_event_dereg(void) { if (ipf_arrivetag != NULL) { EVENTHANDLER_DEREGISTER(ifnet_arrival_event, ipf_arrivetag); } if (ipf_departtag != NULL) { EVENTHANDLER_DEREGISTER(ifnet_departure_event, ipf_departtag); } if (ipf_clonetag != NULL) { EVENTHANDLER_DEREGISTER(if_clone_event, ipf_clonetag); } } u_32_t ipf_random() { return arc4random(); } u_int ipf_pcksum(fin, hlen, sum) fr_info_t *fin; int hlen; u_int sum; { struct mbuf *m; u_int sum2; int off; m = fin->fin_m; off = (char *)fin->fin_dp - (char *)fin->fin_ip; m->m_data += hlen; m->m_len -= hlen; sum2 = in_cksum(fin->fin_m, fin->fin_plen - off); m->m_len += hlen; m->m_data -= hlen; /* * Both sum and sum2 are partial sums, so combine them together. */ sum += ~sum2 & 0xffff; while (sum > 0xffff) sum = (sum & 0xffff) + (sum >> 16); sum2 = ~sum & 0xffff; return sum2; } Index: projects/vnet/sys/contrib/ipfilter/netinet/ip_proxy.c =================================================================== --- projects/vnet/sys/contrib/ipfilter/netinet/ip_proxy.c (revision 302202) +++ projects/vnet/sys/contrib/ipfilter/netinet/ip_proxy.c (revision 302203) @@ -1,1468 +1,1477 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #if defined(KERNEL) || defined(_KERNEL) # undef KERNEL # undef _KERNEL # define KERNEL 1 # define _KERNEL 1 #endif #include #include #include #include #include #if !defined(AIX) # include #endif #if !defined(_KERNEL) && !defined(__KERNEL__) # include # include # include # include # define _KERNEL # ifdef __OpenBSD__ struct file; # endif # include # undef _KERNEL #endif #if !defined(linux) # include #endif #include #if defined(_KERNEL) # if !defined(__NetBSD__) && !defined(sun) && !defined(__osf__) && \ !defined(__OpenBSD__) && !defined(__hpux) && !defined(__sgi) && \ !defined(AIX) # include # endif # include # if !defined(__SVR4) && !defined(__svr4__) # include # endif #endif #if defined(_KERNEL) && (__FreeBSD_version >= 220000) # include # include #else # include #endif #if defined(__SVR4) || defined(__svr4__) # include # ifdef _KERNEL # include # endif # include # include #endif #if __FreeBSD_version >= 300000 # include #endif #include +#if defined(__FreeBSD_version) && (__FreeBSD_version >= 800000) && defined(_KERNEL) +#include +#else +#define CURVNET_SET(arg) +#define CURVNET_RESTORE() +#define VNET_DEFINE(_t, _v) _t _v +#define VNET_DECLARE(_t, _v) extern _t _v +#define VNET(arg) arg +#endif #ifdef sun # include #endif #include #include #include #ifndef linux # include #endif #include #include #include #include "netinet/ip_compat.h" #include #include "netinet/ip_fil.h" #include "netinet/ip_nat.h" #include "netinet/ip_state.h" #include "netinet/ip_proxy.h" #if (__FreeBSD_version >= 300000) # include #endif /* END OF INCLUDES */ #include "netinet/ip_ftp_pxy.c" #include "netinet/ip_tftp_pxy.c" #include "netinet/ip_rcmd_pxy.c" #include "netinet/ip_pptp_pxy.c" #if defined(_KERNEL) # include "netinet/ip_irc_pxy.c" # include "netinet/ip_raudio_pxy.c" # include "netinet/ip_netbios_pxy.c" #endif #include "netinet/ip_ipsec_pxy.c" #include "netinet/ip_rpcb_pxy.c" #if !defined(lint) static const char rcsid[] = "@(#)$Id$"; #endif #define AP_SESS_SIZE 53 static int ipf_proxy_fixseqack __P((fr_info_t *, ip_t *, ap_session_t *, int )); static aproxy_t *ipf_proxy_create_clone __P((ipf_main_softc_t *, aproxy_t *)); typedef struct ipf_proxy_softc_s { int ips_proxy_debug; int ips_proxy_session_size; ap_session_t **ips_sess_tab; ap_session_t *ips_sess_list; aproxy_t *ips_proxies; int ips_init_run; ipftuneable_t *ipf_proxy_tune; } ipf_proxy_softc_t; static ipftuneable_t ipf_proxy_tuneables[] = { { { (void *)offsetof(ipf_proxy_softc_t, ips_proxy_debug) }, "proxy_debug", 0, 0x1f, stsizeof(ipf_proxy_softc_t, ips_proxy_debug), 0, NULL, NULL }, { { NULL }, NULL, 0, 0, 0, 0, NULL, NULL} }; static aproxy_t *ap_proxylist = NULL; static aproxy_t ips_proxies[] = { #ifdef IPF_FTP_PROXY { NULL, NULL, "ftp", (char)IPPROTO_TCP, 0, 0, 0, ipf_p_ftp_main_load, ipf_p_ftp_main_unload, ipf_p_ftp_soft_create, ipf_p_ftp_soft_destroy, NULL, NULL, ipf_p_ftp_new, ipf_p_ftp_del, ipf_p_ftp_in, ipf_p_ftp_out, NULL, NULL, NULL, NULL, NULL }, #endif #ifdef IPF_TFTP_PROXY { NULL, NULL, "tftp", (char)IPPROTO_UDP, 0, 0, 0, ipf_p_tftp_main_load, ipf_p_tftp_main_unload, ipf_p_tftp_soft_create, ipf_p_tftp_soft_destroy, NULL, NULL, ipf_p_tftp_new, ipf_p_tftp_del, ipf_p_tftp_in, ipf_p_tftp_out, NULL, NULL, NULL, NULL, NULL }, #endif #ifdef IPF_IRC_PROXY { NULL, NULL, "irc", (char)IPPROTO_TCP, 0, 0, 0, ipf_p_irc_main_load, ipf_p_irc_main_unload, NULL, NULL, NULL, NULL, ipf_p_irc_new, NULL, NULL, ipf_p_irc_out, NULL, NULL, NULL, NULL, NULL }, #endif #ifdef IPF_RCMD_PROXY { NULL, NULL, "rcmd", (char)IPPROTO_TCP, 0, 0, 0, ipf_p_rcmd_main_load, ipf_p_rcmd_main_unload, NULL, NULL, NULL, NULL, ipf_p_rcmd_new, ipf_p_rcmd_del, ipf_p_rcmd_in, ipf_p_rcmd_out, NULL, NULL, NULL, NULL, NULL }, #endif #ifdef IPF_RAUDIO_PROXY { NULL, NULL, "raudio", (char)IPPROTO_TCP, 0, 0, 0, ipf_p_raudio_main_load, ipf_p_raudio_main_unload, NULL, NULL, NULL, NULL, ipf_p_raudio_new, NULL, ipf_p_raudio_in, ipf_p_raudio_out, NULL, NULL, NULL, NULL, NULL }, #endif #ifdef IPF_MSNRPC_PROXY { NULL, NULL, "msnrpc", (char)IPPROTO_TCP, 0, 0, 0, ipf_p_msnrpc_init, ipf_p_msnrpc_fini, NULL, NULL, NULL, NULL, ipf_p_msnrpc_new, NULL, ipf_p_msnrpc_in, ipf_p_msnrpc_out, NULL, NULL, NULL, NULL, NULL }, #endif #ifdef IPF_NETBIOS_PROXY { NULL, NULL, "netbios", (char)IPPROTO_UDP, 0, 0, 0, ipf_p_netbios_main_load, ipf_p_netbios_main_unload, NULL, NULL, NULL, NULL, NULL, NULL, NULL, ipf_p_netbios_out, NULL, NULL, NULL, NULL, NULL }, #endif #ifdef IPF_IPSEC_PROXY { NULL, NULL, "ipsec", (char)IPPROTO_UDP, 0, 0, 0, NULL, NULL, ipf_p_ipsec_soft_create, ipf_p_ipsec_soft_destroy, ipf_p_ipsec_soft_init, ipf_p_ipsec_soft_fini, ipf_p_ipsec_new, ipf_p_ipsec_del, ipf_p_ipsec_inout, ipf_p_ipsec_inout, ipf_p_ipsec_match, NULL, NULL, NULL, NULL }, #endif #ifdef IPF_DNS_PROXY { NULL, NULL, "dns", (char)IPPROTO_UDP, 0, 0, 0, NULL, NULL, ipf_p_dns_soft_create, ipf_p_dns_soft_destroy, NULL, NULL, ipf_p_dns_new, ipf_p_ipsec_del, ipf_p_dns_inout, ipf_p_dns_inout, ipf_p_dns_match, ipf_p_dns_ctl, NULL, NULL, NULL }, #endif #ifdef IPF_PPTP_PROXY { NULL, NULL, "pptp", (char)IPPROTO_TCP, 0, 0, 0, ipf_p_pptp_main_load, ipf_p_pptp_main_unload, NULL, NULL, NULL, NULL, ipf_p_pptp_new, ipf_p_pptp_del, ipf_p_pptp_inout, ipf_p_pptp_inout, NULL, NULL, NULL, NULL, NULL }, #endif #ifdef IPF_RPCB_PROXY # ifndef _KERNEL { NULL, NULL, "rpcbt", (char)IPPROTO_TCP, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, ipf_p_rpcb_new, ipf_p_rpcb_del, ipf_p_rpcb_in, ipf_p_rpcb_out, NULL, NULL, NULL, NULL, NULL }, # endif { NULL, NULL, "rpcbu", (char)IPPROTO_UDP, 0, 0, 0, ipf_p_rpcb_main_load, ipf_p_rpcb_main_unload, NULL, NULL, NULL, NULL, ipf_p_rpcb_new, ipf_p_rpcb_del, ipf_p_rpcb_in, ipf_p_rpcb_out, NULL, NULL, NULL, NULL, NULL }, #endif { NULL, NULL, "", '\0', 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } }; /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_main_load */ /* Returns: int - 0 == success, else failure. */ /* Parameters: Nil */ /* */ /* Initialise hook for kernel application proxies. */ /* Call the initialise routine for all the compiled in kernel proxies. */ /* ------------------------------------------------------------------------ */ int ipf_proxy_main_load() { aproxy_t *ap; for (ap = ips_proxies; ap->apr_p; ap++) { if (ap->apr_load != NULL) (*ap->apr_load)(); } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_main_unload */ /* Returns: int - 0 == success, else failure. */ /* Parameters: Nil */ /* */ /* Unload hook for kernel application proxies. */ /* Call the finialise routine for all the compiled in kernel proxies. */ /* ------------------------------------------------------------------------ */ int ipf_proxy_main_unload() { aproxy_t *ap; for (ap = ips_proxies; ap->apr_p; ap++) if (ap->apr_unload != NULL) (*ap->apr_unload)(); for (ap = ap_proxylist; ap; ap = ap->apr_next) if (ap->apr_unload != NULL) (*ap->apr_unload)(); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_soft_create */ /* Returns: void * - */ /* Parameters: softc(I) - pointer to soft context main structure */ /* */ /* Build the structure to hold all of the run time data to support proxies. */ /* ------------------------------------------------------------------------ */ void * ipf_proxy_soft_create(softc) ipf_main_softc_t *softc; { ipf_proxy_softc_t *softp; aproxy_t *last; aproxy_t *apn; aproxy_t *ap; KMALLOC(softp, ipf_proxy_softc_t *); if (softp == NULL) return softp; bzero((char *)softp, sizeof(*softp)); #if defined(_KERNEL) softp->ips_proxy_debug = 0; #else softp->ips_proxy_debug = 2; #endif softp->ips_proxy_session_size = AP_SESS_SIZE; softp->ipf_proxy_tune = ipf_tune_array_copy(softp, sizeof(ipf_proxy_tuneables), ipf_proxy_tuneables); if (softp->ipf_proxy_tune == NULL) { ipf_proxy_soft_destroy(softc, softp); return NULL; } if (ipf_tune_array_link(softc, softp->ipf_proxy_tune) == -1) { ipf_proxy_soft_destroy(softc, softp); return NULL; } last = NULL; for (ap = ips_proxies; ap->apr_p; ap++) { apn = ipf_proxy_create_clone(softc, ap); if (apn == NULL) goto failed; if (last != NULL) last->apr_next = apn; else softp->ips_proxies = apn; last = apn; } for (ap = ips_proxies; ap != NULL; ap = ap->apr_next) { apn = ipf_proxy_create_clone(softc, ap); if (apn == NULL) goto failed; if (last != NULL) last->apr_next = apn; else softp->ips_proxies = apn; last = apn; } return softp; failed: ipf_proxy_soft_destroy(softc, softp); return NULL; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_soft_create */ /* Returns: void * - */ /* Parameters: softc(I) - pointer to soft context main structure */ /* orig(I) - pointer to proxy definition to copy */ /* */ /* This function clones a proxy definition given by orig and returns a */ /* a pointer to that copy. */ /* ------------------------------------------------------------------------ */ static aproxy_t * ipf_proxy_create_clone(softc, orig) ipf_main_softc_t *softc; aproxy_t *orig; { aproxy_t *apn; KMALLOC(apn, aproxy_t *); if (apn == NULL) return NULL; bcopy((char *)orig, (char *)apn, sizeof(*apn)); apn->apr_next = NULL; apn->apr_soft = NULL; if (apn->apr_create != NULL) { apn->apr_soft = (*apn->apr_create)(softc); if (apn->apr_soft == NULL) { KFREE(apn); return NULL; } } apn->apr_parent = orig; orig->apr_clones++; return apn; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_soft_create */ /* Returns: int - 0 == success, else failure. */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to proxy contect data */ /* */ /* Initialise the proxy context and walk through each of the proxies and */ /* call its initialisation function. This allows for proxies to do any */ /* local setup prior to actual use. */ /* ------------------------------------------------------------------------ */ int ipf_proxy_soft_init(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_proxy_softc_t *softp; aproxy_t *ap; u_int size; int err; softp = arg; size = softp->ips_proxy_session_size * sizeof(ap_session_t *); KMALLOCS(softp->ips_sess_tab, ap_session_t **, size); if (softp->ips_sess_tab == NULL) return -1; bzero(softp->ips_sess_tab, size); for (ap = softp->ips_proxies; ap != NULL; ap = ap->apr_next) { if (ap->apr_init != NULL) { err = (*ap->apr_init)(softc, ap->apr_soft); if (err != 0) return -2; } } softp->ips_init_run = 1; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_soft_create */ /* Returns: int - 0 == success, else failure. */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to proxy contect data */ /* */ /* This function should always succeed. It is responsible for ensuring that */ /* the proxy context can be safely called when ipf_proxy_soft_destroy is */ /* called and suring all of the proxies have similarly been instructed. */ /* ------------------------------------------------------------------------ */ int ipf_proxy_soft_fini(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_proxy_softc_t *softp = arg; aproxy_t *ap; for (ap = softp->ips_proxies; ap != NULL; ap = ap->apr_next) { if (ap->apr_fini != NULL) { (*ap->apr_fini)(softc, ap->apr_soft); } } if (softp->ips_sess_tab != NULL) { KFREES(softp->ips_sess_tab, softp->ips_proxy_session_size * sizeof(ap_session_t *)); softp->ips_sess_tab = NULL; } softp->ips_init_run = 0; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_soft_destroy */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to proxy contect data */ /* */ /* Free up all of the local data structures allocated during creation. */ /* ------------------------------------------------------------------------ */ void ipf_proxy_soft_destroy(softc, arg) ipf_main_softc_t *softc; void *arg; { ipf_proxy_softc_t *softp = arg; aproxy_t *ap; while ((ap = softp->ips_proxies) != NULL) { softp->ips_proxies = ap->apr_next; if (ap->apr_destroy != NULL) (*ap->apr_destroy)(softc, ap->apr_soft); ap->apr_parent->apr_clones--; KFREE(ap); } if (softp->ipf_proxy_tune != NULL) { ipf_tune_array_unlink(softc, softp->ipf_proxy_tune); KFREES(softp->ipf_proxy_tune, sizeof(ipf_proxy_tuneables)); softp->ipf_proxy_tune = NULL; } KFREE(softp); } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_flush */ /* Returns: Nil */ /* Parameters: arg(I) - pointer to proxy contect data */ /* how(I) - indicates the type of flush operation */ /* */ /* Walk through all of the proxies and pass on the flush command as either */ /* a flush or a clear. */ /* ------------------------------------------------------------------------ */ void ipf_proxy_flush(arg, how) void *arg; int how; { ipf_proxy_softc_t *softp = arg; aproxy_t *ap; switch (how) { case 0 : for (ap = softp->ips_proxies; ap; ap = ap->apr_next) if (ap->apr_flush != NULL) (*ap->apr_flush)(ap, how); break; case 1 : for (ap = softp->ips_proxies; ap; ap = ap->apr_next) if (ap->apr_clear != NULL) (*ap->apr_clear)(ap); break; default : break; } } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_add */ /* Returns: int - 0 == success, else failure. */ /* Parameters: ap(I) - pointer to proxy structure */ /* */ /* Dynamically add a new kernel proxy. Ensure that it is unique in the */ /* collection compiled in and dynamically added. */ /* ------------------------------------------------------------------------ */ int ipf_proxy_add(arg, ap) void *arg; aproxy_t *ap; { ipf_proxy_softc_t *softp = arg; aproxy_t *a; for (a = ips_proxies; a->apr_p; a++) if ((a->apr_p == ap->apr_p) && !strncmp(a->apr_label, ap->apr_label, sizeof(ap->apr_label))) { if (softp->ips_proxy_debug & 0x01) printf("ipf_proxy_add: %s/%d present (B)\n", a->apr_label, a->apr_p); return -1; } for (a = ap_proxylist; (a != NULL); a = a->apr_next) if ((a->apr_p == ap->apr_p) && !strncmp(a->apr_label, ap->apr_label, sizeof(ap->apr_label))) { if (softp->ips_proxy_debug & 0x01) printf("ipf_proxy_add: %s/%d present (D)\n", a->apr_label, a->apr_p); return -1; } ap->apr_next = ap_proxylist; ap_proxylist = ap; if (ap->apr_load != NULL) (*ap->apr_load)(); return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_ctl */ /* Returns: int - 0 == success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* arg(I) - pointer to proxy context */ /* ctl(I) - pointer to proxy control structure */ /* */ /* Check to see if the proxy this control request has come through for */ /* exists, and if it does and it has a control function then invoke that */ /* control function. */ /* ------------------------------------------------------------------------ */ int ipf_proxy_ctl(softc, arg, ctl) ipf_main_softc_t *softc; void *arg; ap_ctl_t *ctl; { ipf_proxy_softc_t *softp = arg; aproxy_t *a; int error; a = ipf_proxy_lookup(arg, ctl->apc_p, ctl->apc_label); if (a == NULL) { if (softp->ips_proxy_debug & 0x01) printf("ipf_proxy_ctl: can't find %s/%d\n", ctl->apc_label, ctl->apc_p); IPFERROR(80001); error = ESRCH; } else if (a->apr_ctl == NULL) { if (softp->ips_proxy_debug & 0x01) printf("ipf_proxy_ctl: no ctl function for %s/%d\n", ctl->apc_label, ctl->apc_p); IPFERROR(80002); error = ENXIO; } else { error = (*a->apr_ctl)(softc, a->apr_soft, ctl); if ((error != 0) && (softp->ips_proxy_debug & 0x02)) printf("ipf_proxy_ctl: %s/%d ctl error %d\n", a->apr_label, a->apr_p, error); } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_del */ /* Returns: int - 0 == success, else failure. */ /* Parameters: ap(I) - pointer to proxy structure */ /* */ /* Delete a proxy that has been added dynamically from those available. */ /* If it is in use, return 1 (do not destroy NOW), not in use 0 or -1 */ /* if it cannot be matched. */ /* ------------------------------------------------------------------------ */ int ipf_proxy_del(ap) aproxy_t *ap; { aproxy_t *a, **app; for (app = &ap_proxylist; ((a = *app) != NULL); app = &a->apr_next) { if (a == ap) { a->apr_flags |= APR_DELETE; if (ap->apr_ref == 0 && ap->apr_clones == 0) { *app = a->apr_next; return 0; } return 1; } } return -1; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_ok */ /* Returns: int - 1 == good match else not. */ /* Parameters: fin(I) - pointer to packet information */ /* tcp(I) - pointer to TCP/UDP header */ /* nat(I) - pointer to current NAT session */ /* */ /* This function extends the NAT matching to ensure that a packet that has */ /* arrived matches the proxy information attached to the NAT rule. Notably, */ /* if the proxy is scheduled to be deleted then packets will not match the */ /* rule even if the rule is still active. */ /* ------------------------------------------------------------------------ */ int ipf_proxy_ok(fin, tcp, np) fr_info_t *fin; tcphdr_t *tcp; ipnat_t *np; { aproxy_t *apr = np->in_apr; u_short dport = np->in_odport; if ((apr == NULL) || (apr->apr_flags & APR_DELETE) || (fin->fin_p != apr->apr_p)) return 0; if ((tcp == NULL) && dport) return 0; return 1; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_ioctl */ /* Returns: int - 0 == success, else error */ /* Parameters: softc(I) - pointer to soft context main structure */ /* data(I) - pointer to ioctl data */ /* cmd(I) - ioctl command */ /* mode(I) - mode bits for device */ /* ctx(I) - pointer to context information */ /* */ /* ------------------------------------------------------------------------ */ int ipf_proxy_ioctl(softc, data, cmd, mode, ctx) ipf_main_softc_t *softc; caddr_t data; ioctlcmd_t cmd; int mode; void *ctx; { ap_ctl_t ctl; caddr_t ptr; int error; mode = mode; /* LINT */ switch (cmd) { case SIOCPROXY : error = ipf_inobj(softc, data, NULL, &ctl, IPFOBJ_PROXYCTL); if (error != 0) { return error; } ptr = NULL; if (ctl.apc_dsize > 0) { KMALLOCS(ptr, caddr_t, ctl.apc_dsize); if (ptr == NULL) { IPFERROR(80003); error = ENOMEM; } else { error = copyinptr(softc, ctl.apc_data, ptr, ctl.apc_dsize); if (error == 0) ctl.apc_data = ptr; } } else { ctl.apc_data = NULL; error = 0; } if (error == 0) error = ipf_proxy_ctl(softc, softc->ipf_proxy_soft, &ctl); if ((error != 0) && (ptr != NULL)) { KFREES(ptr, ctl.apc_dsize); } break; default : IPFERROR(80004); error = EINVAL; } return error; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_match */ /* Returns: int - 0 == success, else error */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to current NAT session */ /* */ /* If a proxy has a match function, call that to do extended packet */ /* matching. Whilst other parts of the NAT code are rather lenient when it */ /* comes to the quality of the packet that it will transform, the proxy */ /* matching is not because they need to work with data, not just headers. */ /* ------------------------------------------------------------------------ */ int ipf_proxy_match(fin, nat) fr_info_t *fin; nat_t *nat; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_proxy_softc_t *softp = softc->ipf_proxy_soft; aproxy_t *apr; ipnat_t *ipn; int result; ipn = nat->nat_ptr; if (softp->ips_proxy_debug & 0x04) printf("ipf_proxy_match(%lx,%lx) aps %lx ptr %lx\n", (u_long)fin, (u_long)nat, (u_long)nat->nat_aps, (u_long)ipn); if ((fin->fin_flx & (FI_SHORT|FI_BAD)) != 0) { if (softp->ips_proxy_debug & 0x08) printf("ipf_proxy_match: flx 0x%x (BAD|SHORT)\n", fin->fin_flx); return -1; } apr = ipn->in_apr; if ((apr == NULL) || (apr->apr_flags & APR_DELETE)) { if (softp->ips_proxy_debug & 0x08) printf("ipf_proxy_match:apr %lx apr_flags 0x%x\n", (u_long)apr, apr ? apr->apr_flags : 0); return -1; } if (apr->apr_match != NULL) { result = (*apr->apr_match)(fin, nat->nat_aps, nat); if (result != 0) { if (softp->ips_proxy_debug & 0x08) printf("ipf_proxy_match: result %d\n", result); return -1; } } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_new */ /* Returns: int - 0 == success, else error */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to current NAT session */ /* */ /* Allocate a new application proxy structure and fill it in with the */ /* relevant details. call the init function once complete, prior to */ /* returning. */ /* ------------------------------------------------------------------------ */ int ipf_proxy_new(fin, nat) fr_info_t *fin; nat_t *nat; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_proxy_softc_t *softp = softc->ipf_proxy_soft; register ap_session_t *aps; aproxy_t *apr; if (softp->ips_proxy_debug & 0x04) printf("ipf_proxy_new(%lx,%lx) \n", (u_long)fin, (u_long)nat); if ((nat->nat_ptr == NULL) || (nat->nat_aps != NULL)) { if (softp->ips_proxy_debug & 0x08) printf("ipf_proxy_new: nat_ptr %lx nat_aps %lx\n", (u_long)nat->nat_ptr, (u_long)nat->nat_aps); return -1; } apr = nat->nat_ptr->in_apr; if ((apr->apr_flags & APR_DELETE) || (fin->fin_p != apr->apr_p)) { if (softp->ips_proxy_debug & 0x08) printf("ipf_proxy_new: apr_flags 0x%x p %d/%d\n", apr->apr_flags, fin->fin_p, apr->apr_p); return -1; } KMALLOC(aps, ap_session_t *); if (!aps) { if (softp->ips_proxy_debug & 0x08) printf("ipf_proxy_new: malloc failed (%lu)\n", (u_long)sizeof(ap_session_t)); return -1; } bzero((char *)aps, sizeof(*aps)); aps->aps_data = NULL; aps->aps_apr = apr; aps->aps_psiz = 0; if (apr->apr_new != NULL) if ((*apr->apr_new)(apr->apr_soft, fin, aps, nat) == -1) { if ((aps->aps_data != NULL) && (aps->aps_psiz != 0)) { KFREES(aps->aps_data, aps->aps_psiz); } KFREE(aps); if (softp->ips_proxy_debug & 0x08) printf("ipf_proxy_new: new(%lx) failed\n", (u_long)apr->apr_new); return -1; } aps->aps_nat = nat; aps->aps_next = softp->ips_sess_list; softp->ips_sess_list = aps; nat->nat_aps = aps; return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_check */ /* Returns: int - -1 == error, 0 == success */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to current NAT session */ /* */ /* Check to see if a packet should be passed through an active proxy */ /* routine if one has been setup for it. We don't need to check the */ /* checksum here if IPFILTER_CKSUM is defined because if it is, a failed */ /* check causes FI_BAD to be set. */ /* ------------------------------------------------------------------------ */ int ipf_proxy_check(fin, nat) fr_info_t *fin; nat_t *nat; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_proxy_softc_t *softp = softc->ipf_proxy_soft; #if SOLARIS && defined(_KERNEL) && defined(ICK_VALID) mb_t *m; #endif tcphdr_t *tcp = NULL; udphdr_t *udp = NULL; ap_session_t *aps; aproxy_t *apr; short adjlen; int dosum; ip_t *ip; short rv; int err; #if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) u_32_t s1, s2, sd; #endif if (fin->fin_flx & FI_BAD) { if (softp->ips_proxy_debug & 0x08) printf("ipf_proxy_check: flx 0x%x (BAD)\n", fin->fin_flx); return -1; } #ifndef IPFILTER_CKSUM if ((fin->fin_out == 0) && (ipf_checkl4sum(fin) == -1)) { if (softp->ips_proxy_debug & 0x08) printf("ipf_proxy_check: l4 checksum failure %d\n", fin->fin_p); if (fin->fin_p == IPPROTO_TCP) softc->ipf_stats[fin->fin_out].fr_tcpbad++; return -1; } #endif aps = nat->nat_aps; if (aps != NULL) { /* * If there is data in this packet to be proxied then try and * get it all into the one buffer, else drop it. */ #if defined(MENTAT) || defined(HAVE_M_PULLDOWN) if ((fin->fin_dlen > 0) && !(fin->fin_flx & FI_COALESCE)) if (ipf_coalesce(fin) == -1) { if (softp->ips_proxy_debug & 0x08) printf("ipf_proxy_check: %s %x\n", "coalesce failed", fin->fin_flx); return -1; } #endif ip = fin->fin_ip; if (fin->fin_cksum > FI_CK_SUMOK) dosum = 0; else dosum = 1; switch (fin->fin_p) { case IPPROTO_TCP : tcp = (tcphdr_t *)fin->fin_dp; #if SOLARIS && defined(_KERNEL) && defined(ICK_VALID) m = fin->fin_qfm; if (dohwcksum && (m->b_ick_flag == ICK_VALID)) dosum = 0; #endif break; case IPPROTO_UDP : udp = (udphdr_t *)fin->fin_dp; break; default : break; } apr = aps->aps_apr; err = 0; if (fin->fin_out != 0) { if (apr->apr_outpkt != NULL) err = (*apr->apr_outpkt)(apr->apr_soft, fin, aps, nat); } else { if (apr->apr_inpkt != NULL) err = (*apr->apr_inpkt)(apr->apr_soft, fin, aps, nat); } rv = APR_EXIT(err); if (((softp->ips_proxy_debug & 0x08) && (rv != 0)) || (softp->ips_proxy_debug & 0x04)) printf("ipf_proxy_check: out %d err %x rv %d\n", fin->fin_out, err, rv); if (rv == 1) return -1; if (rv == 2) { ipf_proxy_deref(apr); nat->nat_aps = NULL; return -1; } /* * If err != 0 then the data size of the packet has changed * so we need to recalculate the header checksums for the * packet. */ adjlen = APR_INC(err); #if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) s1 = LONG_SUM(fin->fin_plen - adjlen); s2 = LONG_SUM(fin->fin_plen); CALC_SUMD(s1, s2, sd); if ((err != 0) && (fin->fin_cksum < FI_CK_L4PART) && fin->fin_v == 4) ipf_fix_outcksum(0, &ip->ip_sum, sd, 0); #endif if (fin->fin_flx & FI_DOCKSUM) dosum = 1; /* * For TCP packets, we may need to adjust the sequence and * acknowledgement numbers to reflect changes in size of the * data stream. * * For both TCP and UDP, recalculate the layer 4 checksum, * regardless, as we can't tell (here) if data has been * changed or not. */ if (tcp != NULL) { err = ipf_proxy_fixseqack(fin, ip, aps, adjlen); if (fin->fin_cksum == FI_CK_L4PART) { u_short sum = ntohs(tcp->th_sum); sum += adjlen; tcp->th_sum = htons(sum); } else if (fin->fin_cksum < FI_CK_L4PART) { tcp->th_sum = fr_cksum(fin, ip, IPPROTO_TCP, tcp); } } else if ((udp != NULL) && (udp->uh_sum != 0)) { if (fin->fin_cksum == FI_CK_L4PART) { u_short sum = ntohs(udp->uh_sum); sum += adjlen; udp->uh_sum = htons(sum); } else if (dosum) { udp->uh_sum = fr_cksum(fin, ip, IPPROTO_UDP, udp); } } aps->aps_bytes += fin->fin_plen; aps->aps_pkts++; return 1; } return 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_lookup */ /* Returns: int - -1 == error, 0 == success */ /* Parameters: arg(I) - pointer to proxy context information */ /* pr(I) - protocol number for proxy */ /* name(I) - proxy name */ /* */ /* Search for an proxy by the protocol it is being used with and its name. */ /* ------------------------------------------------------------------------ */ aproxy_t * ipf_proxy_lookup(arg, pr, name) void *arg; u_int pr; char *name; { ipf_proxy_softc_t *softp = arg; aproxy_t *ap; if (softp->ips_proxy_debug & 0x04) printf("ipf_proxy_lookup(%d,%s)\n", pr, name); for (ap = softp->ips_proxies; ap != NULL; ap = ap->apr_next) if ((ap->apr_p == pr) && !strncmp(name, ap->apr_label, sizeof(ap->apr_label))) { ap->apr_ref++; return ap; } if (softp->ips_proxy_debug & 0x08) printf("ipf_proxy_lookup: failed for %d/%s\n", pr, name); return NULL; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_deref */ /* Returns: Nil */ /* Parameters: ap(I) - pointer to proxy structure */ /* */ /* Drop the reference counter associated with the proxy. */ /* ------------------------------------------------------------------------ */ void ipf_proxy_deref(ap) aproxy_t *ap; { ap->apr_ref--; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_free */ /* Returns: Nil */ /* Parameters: softc(I) - pointer to soft context main structure */ /* aps(I) - pointer to current proxy session */ /* Locks Held: ipf_nat_new, ipf_nat(W) */ /* */ /* Free up proxy session information allocated to be used with a NAT */ /* session. */ /* ------------------------------------------------------------------------ */ void ipf_proxy_free(softc, aps) ipf_main_softc_t *softc; ap_session_t *aps; { ipf_proxy_softc_t *softp = softc->ipf_proxy_soft; ap_session_t *a, **ap; aproxy_t *apr; if (!aps) return; for (ap = &softp->ips_sess_list; ((a = *ap) != NULL); ap = &a->aps_next) if (a == aps) { *ap = a->aps_next; break; } apr = aps->aps_apr; if ((apr != NULL) && (apr->apr_del != NULL)) (*apr->apr_del)(softc, aps); if ((aps->aps_data != NULL) && (aps->aps_psiz != 0)) KFREES(aps->aps_data, aps->aps_psiz); KFREE(aps); } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_fixseqack */ /* Returns: int - 2 if TCP ack/seq is changed, else 0 */ /* Parameters: fin(I) - pointer to packet information */ /* ip(I) - pointer to IP header */ /* nat(I) - pointer to current NAT session */ /* inc(I) - delta to apply to TCP sequence numbering */ /* */ /* Adjust the TCP sequence/acknowledge numbers in the TCP header based on */ /* whether or not the new header is past the point at which an adjustment */ /* occurred. This might happen because of (say) an FTP string being changed */ /* and the new string being a different length to the old. */ /* ------------------------------------------------------------------------ */ static int ipf_proxy_fixseqack(fin, ip, aps, inc) fr_info_t *fin; ip_t *ip; ap_session_t *aps; int inc; { ipf_main_softc_t *softc = fin->fin_main_soft; ipf_proxy_softc_t *softp = softc->ipf_proxy_soft; int sel, ch = 0, out, nlen; u_32_t seq1, seq2; tcphdr_t *tcp; short inc2; tcp = (tcphdr_t *)fin->fin_dp; out = fin->fin_out; /* * ip_len has already been adjusted by 'inc'. */ nlen = fin->fin_dlen; nlen -= (TCP_OFF(tcp) << 2); inc2 = inc; inc = (int)inc2; if (out != 0) { seq1 = (u_32_t)ntohl(tcp->th_seq); sel = aps->aps_sel[out]; /* switch to other set ? */ if ((aps->aps_seqmin[!sel] > aps->aps_seqmin[sel]) && (seq1 > aps->aps_seqmin[!sel])) { if (softp->ips_proxy_debug & 0x10) printf("proxy out switch set seq %d -> %d %x > %x\n", sel, !sel, seq1, aps->aps_seqmin[!sel]); sel = aps->aps_sel[out] = !sel; } if (aps->aps_seqoff[sel]) { seq2 = aps->aps_seqmin[sel] - aps->aps_seqoff[sel]; if (seq1 > seq2) { seq2 = aps->aps_seqoff[sel]; seq1 += seq2; tcp->th_seq = htonl(seq1); ch = 1; } } if (inc && (seq1 > aps->aps_seqmin[!sel])) { aps->aps_seqmin[sel] = seq1 + nlen - 1; aps->aps_seqoff[sel] = aps->aps_seqoff[sel] + inc; if (softp->ips_proxy_debug & 0x10) printf("proxy seq set %d at %x to %d + %d\n", sel, aps->aps_seqmin[sel], aps->aps_seqoff[sel], inc); } /***/ seq1 = ntohl(tcp->th_ack); sel = aps->aps_sel[1 - out]; /* switch to other set ? */ if ((aps->aps_ackmin[!sel] > aps->aps_ackmin[sel]) && (seq1 > aps->aps_ackmin[!sel])) { if (softp->ips_proxy_debug & 0x10) printf("proxy out switch set ack %d -> %d %x > %x\n", sel, !sel, seq1, aps->aps_ackmin[!sel]); sel = aps->aps_sel[1 - out] = !sel; } if (aps->aps_ackoff[sel] && (seq1 > aps->aps_ackmin[sel])) { seq2 = aps->aps_ackoff[sel]; tcp->th_ack = htonl(seq1 - seq2); ch = 1; } } else { seq1 = ntohl(tcp->th_seq); sel = aps->aps_sel[out]; /* switch to other set ? */ if ((aps->aps_ackmin[!sel] > aps->aps_ackmin[sel]) && (seq1 > aps->aps_ackmin[!sel])) { if (softp->ips_proxy_debug & 0x10) printf("proxy in switch set ack %d -> %d %x > %x\n", sel, !sel, seq1, aps->aps_ackmin[!sel]); sel = aps->aps_sel[out] = !sel; } if (aps->aps_ackoff[sel]) { seq2 = aps->aps_ackmin[sel] - aps->aps_ackoff[sel]; if (seq1 > seq2) { seq2 = aps->aps_ackoff[sel]; seq1 += seq2; tcp->th_seq = htonl(seq1); ch = 1; } } if (inc && (seq1 > aps->aps_ackmin[!sel])) { aps->aps_ackmin[!sel] = seq1 + nlen - 1; aps->aps_ackoff[!sel] = aps->aps_ackoff[sel] + inc; if (softp->ips_proxy_debug & 0x10) printf("proxy ack set %d at %x to %d + %d\n", !sel, aps->aps_seqmin[!sel], aps->aps_seqoff[sel], inc); } /***/ seq1 = ntohl(tcp->th_ack); sel = aps->aps_sel[1 - out]; /* switch to other set ? */ if ((aps->aps_seqmin[!sel] > aps->aps_seqmin[sel]) && (seq1 > aps->aps_seqmin[!sel])) { if (softp->ips_proxy_debug & 0x10) printf("proxy in switch set seq %d -> %d %x > %x\n", sel, !sel, seq1, aps->aps_seqmin[!sel]); sel = aps->aps_sel[1 - out] = !sel; } if (aps->aps_seqoff[sel] != 0) { if (softp->ips_proxy_debug & 0x10) printf("sel %d seqoff %d seq1 %x seqmin %x\n", sel, aps->aps_seqoff[sel], seq1, aps->aps_seqmin[sel]); if (seq1 > aps->aps_seqmin[sel]) { seq2 = aps->aps_seqoff[sel]; tcp->th_ack = htonl(seq1 - seq2); ch = 1; } } } if (softp->ips_proxy_debug & 0x10) printf("ipf_proxy_fixseqack: seq %u ack %u\n", (u_32_t)ntohl(tcp->th_seq), (u_32_t)ntohl(tcp->th_ack)); return ch ? 2 : 0; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_rule_rev */ /* Returns: ipnat_t * - NULL = failure, else pointer to new rule */ /* Parameters: nat(I) - pointer to NAT session to create rule from */ /* */ /* This function creates a NAT rule that is based upon the reverse packet */ /* flow associated with this NAT session. Thus if this NAT session was */ /* created with a map rule then this function will create a rdr rule. */ /* Only address fields and network interfaces are assigned in this function */ /* and the address fields are formed such that an exact is required. If the */ /* original rule had a netmask, that is not replicated here not is it */ /* desired. The ultimate goal here is to create a NAT rule to support a NAT */ /* session being created that does not have a user configured rule. The */ /* classic example is supporting the FTP proxy, where a data channel needs */ /* to be setup, based on the addresses used for the control connection. In */ /* that case, this function is used to handle creating NAT rules to support */ /* data connections with the PORT and EPRT commands. */ /* ------------------------------------------------------------------------ */ ipnat_t * ipf_proxy_rule_rev(nat) nat_t *nat; { ipnat_t *old; ipnat_t *ipn; int size; old = nat->nat_ptr; size = old->in_size; KMALLOCS(ipn, ipnat_t *, size); if (ipn == NULL) return NULL; bzero((char *)ipn, size); ipn->in_use = 1; ipn->in_hits = 1; ipn->in_ippip = 1; ipn->in_apr = NULL; ipn->in_size = size; ipn->in_pr[0] = old->in_pr[1]; ipn->in_pr[1] = old->in_pr[0]; ipn->in_v[0] = old->in_v[1]; ipn->in_v[1] = old->in_v[0]; ipn->in_ifps[0] = old->in_ifps[1]; ipn->in_ifps[1] = old->in_ifps[0]; ipn->in_flags = (old->in_flags | IPN_PROXYRULE); ipn->in_nsrcip6 = nat->nat_odst6; ipn->in_osrcip6 = nat->nat_ndst6; if ((old->in_redir & NAT_REDIRECT) != 0) { ipn->in_redir = NAT_MAP; if (ipn->in_v[0] == 4) { ipn->in_snip = ntohl(nat->nat_odstaddr); ipn->in_dnip = ntohl(nat->nat_nsrcaddr); } else { #ifdef USE_INET6 ipn->in_snip6 = nat->nat_odst6; ipn->in_dnip6 = nat->nat_nsrc6; #endif } ipn->in_ndstip6 = nat->nat_nsrc6; ipn->in_odstip6 = nat->nat_osrc6; } else { ipn->in_redir = NAT_REDIRECT; if (ipn->in_v[0] == 4) { ipn->in_snip = ntohl(nat->nat_odstaddr); ipn->in_dnip = ntohl(nat->nat_osrcaddr); } else { #ifdef USE_INET6 ipn->in_snip6 = nat->nat_odst6; ipn->in_dnip6 = nat->nat_osrc6; #endif } ipn->in_ndstip6 = nat->nat_osrc6; ipn->in_odstip6 = nat->nat_nsrc6; } IP6_SETONES(&ipn->in_osrcmsk6); IP6_SETONES(&ipn->in_nsrcmsk6); IP6_SETONES(&ipn->in_odstmsk6); IP6_SETONES(&ipn->in_ndstmsk6); ipn->in_namelen = old->in_namelen; ipn->in_ifnames[0] = old->in_ifnames[1]; ipn->in_ifnames[1] = old->in_ifnames[0]; bcopy(old->in_names, ipn->in_names, ipn->in_namelen); MUTEX_INIT(&ipn->in_lock, "ipnat rev rule lock"); return ipn; } /* ------------------------------------------------------------------------ */ /* Function: ipf_proxy_rule_fwd */ /* Returns: ipnat_t * - NULL = failure, else pointer to new rule */ /* Parameters: nat(I) - pointer to NAT session to create rule from */ /* */ /* The purpose and rationale of this function is much the same as the above */ /* function, ipf_proxy_rule_rev, except that a rule is created that matches */ /* the same direction as that of the existing NAT session. Thus if this NAT */ /* session was created with a map rule then this function will also create */ /* a data structure to represent a map rule. Whereas ipf_proxy_rule_rev is */ /* used to support PORT/EPRT, this function supports PASV/EPSV. */ /* ------------------------------------------------------------------------ */ ipnat_t * ipf_proxy_rule_fwd(nat) nat_t *nat; { ipnat_t *old; ipnat_t *ipn; int size; old = nat->nat_ptr; size = old->in_size; KMALLOCS(ipn, ipnat_t *, size); if (ipn == NULL) return NULL; bzero((char *)ipn, size); ipn->in_use = 1; ipn->in_hits = 1; ipn->in_ippip = 1; ipn->in_apr = NULL; ipn->in_size = size; ipn->in_pr[0] = old->in_pr[0]; ipn->in_pr[1] = old->in_pr[1]; ipn->in_v[0] = old->in_v[0]; ipn->in_v[1] = old->in_v[1]; ipn->in_ifps[0] = nat->nat_ifps[0]; ipn->in_ifps[1] = nat->nat_ifps[1]; ipn->in_flags = (old->in_flags | IPN_PROXYRULE); ipn->in_nsrcip6 = nat->nat_nsrc6; ipn->in_osrcip6 = nat->nat_osrc6; ipn->in_ndstip6 = nat->nat_ndst6; ipn->in_odstip6 = nat->nat_odst6; ipn->in_redir = old->in_redir; if (ipn->in_v[0] == 4) { ipn->in_snip = ntohl(nat->nat_nsrcaddr); ipn->in_dnip = ntohl(nat->nat_ndstaddr); } else { #ifdef USE_INET6 ipn->in_snip6 = nat->nat_nsrc6; ipn->in_dnip6 = nat->nat_ndst6; #endif } IP6_SETONES(&ipn->in_osrcmsk6); IP6_SETONES(&ipn->in_nsrcmsk6); IP6_SETONES(&ipn->in_odstmsk6); IP6_SETONES(&ipn->in_ndstmsk6); ipn->in_namelen = old->in_namelen; ipn->in_ifnames[0] = old->in_ifnames[0]; ipn->in_ifnames[1] = old->in_ifnames[1]; bcopy(old->in_names, ipn->in_names, ipn->in_namelen); MUTEX_INIT(&ipn->in_lock, "ipnat fwd rule lock"); return ipn; } Index: projects/vnet/sys/contrib/ipfilter/netinet/ip_rules.c =================================================================== --- projects/vnet/sys/contrib/ipfilter/netinet/ip_rules.c (revision 302202) +++ projects/vnet/sys/contrib/ipfilter/netinet/ip_rules.c (revision 302203) @@ -1,265 +1,274 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 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. */ #include #include #include #include #if defined(__FreeBSD_version) && (__FreeBSD_version >= 40000) # if defined(_KERNEL) # include # else # include # endif #endif #if defined(__NetBSD_Version__) && (__NetBSD_Version__ >= 399000000) #else # if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__sgi) # include # endif #endif #include #include #if !defined(__SVR4) && !defined(__svr4__) && !defined(__hpux) # include #endif #if defined(__FreeBSD__) && (__FreeBSD_version > 220000) # include +#if defined(__FreeBSD_version) && (__FreeBSD_version >= 800000) && defined(_KERNEL) +#include +#else +#define CURVNET_SET(arg) +#define CURVNET_RESTORE() +#define VNET_DEFINE(_t, _v) _t _v +#define VNET_DECLARE(_t, _v) extern _t _v +#define VNET(arg) arg +#endif #else # include #endif /* FreeBSD */ #include #include #include #include #include #include "netinet/ip_compat.h" #include "netinet/ip_fil.h" #include "netinet/ip_rules.h" #ifndef _KERNEL # include #endif /* _KERNEL */ #ifdef IPFILTER_COMPILED VNET_DECLARE(ipf_main_softc_t, ipfmain); #define V_ipfmain VNET(ipfmain) static u_long in_rule__0[] = { 0, 0, 0, 0, 0, 0, 0, 0x8070d88, 0, 0, 0, 0xffffffff, 0, 0, 0, 0, 0, 0, 0, 0xffffffff, 0x1b0, 0x1, 0, 0, 0, 0x2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x40000000, 0x8002, 0, 0, 0, 0xffff, 0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xffffffff, 0, 0, 0, 0, 0, 0, 0, 0, 0xffffffff, 0, 0, 0, 0, 0, 0, 0, 0, 0xffffffff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xffffffff, 0, 0, 0, 0 }; static u_long out_rule__0[] = { 0, 0, 0, 0, 0, 0, 0, 0x8070d88, 0, 0, 0, 0xffffffff, 0, 0, 0, 0, 0, 0, 0, 0xffffffff, 0x1b0, 0x1, 0, 0, 0, 0x3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x40000000, 0x4002, 0, 0, 0, 0xffff, 0, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xffffffff, 0, 0, 0, 0, 0, 0, 0, 0, 0xffffffff, 0, 0, 0, 0, 0, 0, 0, 0, 0xffffffff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xffffffff, 0, 0, 0, 0 }; frentry_t *ipf_rules_in_[1] = { (frentry_t *)&in_rule__0 }; /* XXX This file (ip_rules.c) is not part of the ipfilter tarball, it is XXX generated by the ipfilter build process. Unfortunately the build XXX process did not generate the following lines so they are added XXX by hand here. This is a bit of a hack but it works for now. Future XXX imports/merges of ipfilter may generate this so the following will XXX need to be removed following some future merge. XXX */ frentry_t *ipf_rules_out_[1] = { (frentry_t *)&out_rule__0 }; frentry_t *ipfrule_match_in_(fin, passp) fr_info_t *fin; u_32_t *passp; { frentry_t *fr = NULL; fr = (frentry_t *)&in_rule__0; return fr; } frentry_t *ipfrule_match_out_(fin, passp) fr_info_t *fin; u_32_t *passp; { frentry_t *fr = NULL; fr = (frentry_t *)&out_rule__0; return fr; } static frentry_t ipfrule_out_; int ipfrule_add_out_() { int i, j, err = 0, max; frentry_t *fp; max = sizeof(ipf_rules_out_)/sizeof(frentry_t *); for (i = 0; i < max; i++) { fp = ipf_rules_out_[i]; fp->fr_next = NULL; for (j = i + 1; j < max; j++) if (strncmp(fp->fr_names + fp->fr_group, ipf_rules_out_[j]->fr_names + ipf_rules_out_[j]->fr_group, FR_GROUPLEN) == 0) { if (ipf_rules_out_[j] != NULL) ipf_rules_out_[j]->fr_pnext = &fp->fr_next; fp->fr_pnext = &ipf_rules_out_[j]; fp->fr_next = ipf_rules_out_[j]; break; } } fp = &ipfrule_out_; bzero((char *)fp, sizeof(*fp)); fp->fr_type = FR_T_CALLFUNC_BUILTIN; fp->fr_flags = FR_OUTQUE|FR_NOMATCH; fp->fr_data = (void *)ipf_rules_out_[0]; fp->fr_dsize = sizeof(ipf_rules_out_[0]); fp->fr_family = AF_INET; fp->fr_func = (ipfunc_t)ipfrule_match_out_; err = frrequest(&V_ipfmain, IPL_LOGIPF, SIOCADDFR, (caddr_t)fp, V_ipfmain.ipf_active, 0); return err; } int ipfrule_remove_out_() { int err = 0, i; frentry_t *fp; /* * Try to remove the outbound rule. */ if (ipfrule_out_.fr_ref > 0) { err = EBUSY; } else { i = sizeof(ipf_rules_out_)/sizeof(frentry_t *) - 1; for (; i >= 0; i--) { fp = ipf_rules_out_[i]; if (fp->fr_ref > 1) { err = EBUSY; break; } } } if (err == 0) err = frrequest(&V_ipfmain, IPL_LOGIPF, SIOCDELFR, (caddr_t)&ipfrule_out_, V_ipfmain.ipf_active, 0); if (err) return err; return err; } static frentry_t ipfrule_in_; int ipfrule_add_in_() { int i, j, err = 0, max; frentry_t *fp; max = sizeof(ipf_rules_in_)/sizeof(frentry_t *); for (i = 0; i < max; i++) { fp = ipf_rules_in_[i]; fp->fr_next = NULL; for (j = i + 1; j < max; j++) if (strncmp(fp->fr_names + fp->fr_group, ipf_rules_in_[j]->fr_names + ipf_rules_in_[j]->fr_group, FR_GROUPLEN) == 0) { if (ipf_rules_in_[j] != NULL) ipf_rules_in_[j]->fr_pnext = &fp->fr_next; fp->fr_pnext = &ipf_rules_in_[j]; fp->fr_next = ipf_rules_in_[j]; break; } } fp = &ipfrule_in_; bzero((char *)fp, sizeof(*fp)); fp->fr_type = FR_T_CALLFUNC_BUILTIN; fp->fr_flags = FR_INQUE|FR_NOMATCH; fp->fr_data = (void *)ipf_rules_in_[0]; fp->fr_dsize = sizeof(ipf_rules_in_[0]); fp->fr_family = AF_INET; fp->fr_func = (ipfunc_t)ipfrule_match_in_; err = frrequest(&V_ipfmain, IPL_LOGIPF, SIOCADDFR, (caddr_t)fp, V_ipfmain.ipf_active, 0); return err; } int ipfrule_remove_in_() { int err = 0, i; frentry_t *fp; /* * Try to remove the inbound rule. */ if (ipfrule_in_.fr_ref > 0) { err = EBUSY; } else { i = sizeof(ipf_rules_in_)/sizeof(frentry_t *) - 1; for (; i >= 0; i--) { fp = ipf_rules_in_[i]; if (fp->fr_ref > 1) { err = EBUSY; break; } } } if (err == 0) err = frrequest(&V_ipfmain, IPL_LOGIPF, SIOCDELFR, (caddr_t)&ipfrule_in_, V_ipfmain.ipf_active, 0); if (err) return err; return err; } int ipfrule_add() { int err; err = ipfrule_add_out_(); if (err != 0) return err; err = ipfrule_add_in_(); if (err != 0) return err; return 0; } int ipfrule_remove() { int err; err = ipfrule_remove_out_(); if (err != 0) return err; err = ipfrule_remove_in_(); if (err != 0) return err; return 0; } #endif /* IPFILTER_COMPILED */ Index: projects/vnet/sys/contrib/ipfilter/netinet/mlfk_ipl.c =================================================================== --- projects/vnet/sys/contrib/ipfilter/netinet/mlfk_ipl.c (revision 302202) +++ projects/vnet/sys/contrib/ipfilter/netinet/mlfk_ipl.c (revision 302203) @@ -1,611 +1,626 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * $FreeBSD$ * See the IPFILTER.LICENCE file for details on licencing. */ +#if defined(KERNEL) || defined(_KERNEL) +# undef KERNEL +# undef _KERNEL +# define KERNEL 1 +# define _KERNEL 1 +#endif #include #include #include #include #include #include #include #include #if __FreeBSD_version >= 500000 # include # include +#endif +#if defined(__FreeBSD_version) && (__FreeBSD_version >= 800000) && defined(_KERNEL) +#include +#else +#define CURVNET_SET(arg) +#define CURVNET_RESTORE() +#define VNET_DEFINE(_t, _v) _t _v +#define VNET_DECLARE(_t, _v) extern _t _v +#define VNET(arg) arg #endif #include #include #include #include "netinet/ipl.h" #include "netinet/ip_compat.h" #include "netinet/ip_fil.h" #include "netinet/ip_state.h" #include "netinet/ip_nat.h" #include "netinet/ip_auth.h" #include "netinet/ip_frag.h" #include "netinet/ip_sync.h" VNET_DECLARE(ipf_main_softc_t, ipfmain); #define V_ipfmain VNET(ipfmain) #if __FreeBSD_version >= 502116 static struct cdev *ipf_devs[IPL_LOGSIZE]; #else static dev_t ipf_devs[IPL_LOGSIZE]; #endif static int sysctl_ipf_int ( SYSCTL_HANDLER_ARGS ); static int ipf_modload(void); static int ipf_modunload(void); static int ipf_fbsd_sysctl_create(ipf_main_softc_t*); static int ipf_fbsd_sysctl_destroy(ipf_main_softc_t*); #if (__FreeBSD_version >= 500024) # if (__FreeBSD_version >= 502116) static int ipfopen __P((struct cdev*, int, int, struct thread *)); static int ipfclose __P((struct cdev*, int, int, struct thread *)); # else static int ipfopen __P((dev_t, int, int, struct thread *)); static int ipfclose __P((dev_t, int, int, struct thread *)); # endif /* __FreeBSD_version >= 502116 */ #else static int ipfopen __P((dev_t, int, int, struct proc *)); static int ipfclose __P((dev_t, int, int, struct proc *)); #endif #if (__FreeBSD_version >= 502116) static int ipfread __P((struct cdev*, struct uio *, int)); static int ipfwrite __P((struct cdev*, struct uio *, int)); #else static int ipfread __P((dev_t, struct uio *, int)); static int ipfwrite __P((dev_t, struct uio *, int)); #endif /* __FreeBSD_version >= 502116 */ SYSCTL_DECL(_net_inet); #define SYSCTL_IPF(parent, nbr, name, access, ptr, val, descr) \ SYSCTL_OID(parent, nbr, name, CTLTYPE_INT|CTLFLAG_VNET|access, \ ptr, val, sysctl_ipf_int, "I", descr) #define SYSCTL_DYN_IPF(parent, nbr, name, access,ptr, val, descr) \ SYSCTL_ADD_OID(&V_ipf_clist, SYSCTL_STATIC_CHILDREN(parent), nbr, name, \ CTLFLAG_DYN|CTLTYPE_INT|CTLFLAG_VNET|access, ptr, val, sysctl_ipf_int, "I", descr) static VNET_DEFINE(struct sysctl_ctx_list, ipf_clist); #define V_ipf_clist VNET(ipf_clist) #define CTLFLAG_OFF 0x00800000 /* IPFilter must be disabled */ #define CTLFLAG_RWO (CTLFLAG_RW|CTLFLAG_OFF) SYSCTL_NODE(_net_inet, OID_AUTO, ipf, CTLFLAG_RW, 0, "IPF"); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_flags, CTLFLAG_RW, &VNET_NAME(ipfmain.ipf_flags), 0, "IPF flags"); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, ipf_pass, CTLFLAG_RW, &VNET_NAME(ipfmain.ipf_pass), 0, "default pass/block"); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_active, CTLFLAG_RD, &VNET_NAME(ipfmain.ipf_active), 0, "IPF is active"); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_tcpidletimeout, CTLFLAG_RWO, &VNET_NAME(ipfmain.ipf_tcpidletimeout), 0, "TCP idle timeout in seconds"); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_tcphalfclosed, CTLFLAG_RWO, &VNET_NAME(ipfmain.ipf_tcphalfclosed), 0, "timeout for half closed TCP sessions"); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_tcpclosewait, CTLFLAG_RWO, &VNET_NAME(ipfmain.ipf_tcpclosewait), 0, "timeout for TCP sessions in closewait status"); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_tcplastack, CTLFLAG_RWO, &VNET_NAME(ipfmain.ipf_tcplastack), 0, "timeout for TCP sessions in last ack status"); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_tcptimeout, CTLFLAG_RWO, &VNET_NAME(ipfmain.ipf_tcptimeout), 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_tcpclosed, CTLFLAG_RWO, &VNET_NAME(ipfmain.ipf_tcpclosed), 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_udptimeout, CTLFLAG_RWO, &VNET_NAME(ipfmain.ipf_udptimeout), 0, "UDP timeout"); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_udpacktimeout, CTLFLAG_RWO, &VNET_NAME(ipfmain.ipf_udpacktimeout), 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_icmptimeout, CTLFLAG_RWO, &VNET_NAME(ipfmain.ipf_icmptimeout), 0, "ICMP timeout"); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_running, CTLFLAG_RD, &VNET_NAME(ipfmain.ipf_running), 0, "IPF is running"); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_chksrc, CTLFLAG_RW, &VNET_NAME(ipfmain.ipf_chksrc), 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_minttl, CTLFLAG_RW, &VNET_NAME(ipfmain.ipf_minttl), 0, ""); #define CDEV_MAJOR 79 #include #if __FreeBSD_version >= 500043 # include static int ipfpoll(struct cdev *dev, int events, struct thread *td); static struct cdevsw ipf_cdevsw = { #if __FreeBSD_version >= 502103 .d_version = D_VERSION, .d_flags = 0, /* D_NEEDGIANT - Should be SMP safe */ #endif .d_open = ipfopen, .d_close = ipfclose, .d_read = ipfread, .d_write = ipfwrite, .d_ioctl = ipfioctl, .d_poll = ipfpoll, .d_name = "ipf", #if __FreeBSD_version < 600000 .d_maj = CDEV_MAJOR, #endif }; #else static int ipfpoll(dev_t dev, int events, struct proc *td); static struct cdevsw ipf_cdevsw = { /* open */ ipfopen, /* close */ ipfclose, /* read */ ipfread, /* write */ ipfwrite, /* ioctl */ ipfioctl, /* poll */ ipfpoll, /* mmap */ nommap, /* strategy */ nostrategy, /* name */ "ipf", /* maj */ CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ 0, # if (__FreeBSD_version < 500043) /* bmaj */ -1, # endif # if (__FreeBSD_version >= 430000) /* kqfilter */ NULL # endif }; #endif static char *ipf_devfiles[] = { IPL_NAME, IPNAT_NAME, IPSTATE_NAME, IPAUTH_NAME, IPSYNC_NAME, IPSCAN_NAME, IPLOOKUP_NAME, NULL }; static int ipfilter_modevent(module_t mod, int type, void *unused) { int error = 0; switch (type) { case MOD_LOAD : error = ipf_modload(); break; case MOD_UNLOAD : error = ipf_modunload(); break; default: error = EINVAL; break; } return error; } static void vnet_ipf_init(void) { char *defpass; int error; if (ipf_create_all(&V_ipfmain) == NULL) return; if (ipf_fbsd_sysctl_create(&V_ipfmain) != 0) { ipf_destroy_all(&V_ipfmain); return; } error = ipfattach(&V_ipfmain); if (error) { (void)ipf_fbsd_sysctl_destroy(&V_ipfmain); ipf_destroy_all(&V_ipfmain); return; } if (FR_ISPASS(V_ipfmain.ipf_pass)) defpass = "pass"; else if (FR_ISBLOCK(V_ipfmain.ipf_pass)) defpass = "block"; else defpass = "no-match -> block"; if (IS_DEFAULT_VNET(curvnet)) printf("%s initialized. Default = %s all, Logging = %s%s\n", ipfilter_version, defpass, #ifdef IPFILTER_LOG "enabled", #else "disabled", #endif #ifdef IPFILTER_COMPILED " (COMPILED)" #else "" #endif ); } VNET_SYSINIT(vnet_ipf_init, SI_SUB_PROTO_FIREWALL, SI_ORDER_THIRD, vnet_ipf_init, NULL); static int ipf_modload() { char *c, *str; int i, j, error; if (ipf_load_all() != 0) return EIO; for (i = 0; i < IPL_LOGSIZE; i++) ipf_devs[i] = NULL; for (i = 0; (str = ipf_devfiles[i]); i++) { c = NULL; for(j = strlen(str); j > 0; j--) if (str[j] == '/') { c = str + j + 1; break; } if (!c) c = str; ipf_devs[i] = make_dev(&ipf_cdevsw, i, 0, 0, 0600, "%s", c); } error = ipf_pfil_hook(); if (error != 0) return error; ipf_event_reg(); return 0; } static void vnet_ipf_uninit(void) { if (V_ipfmain.ipf_refcnt) return; if (ipf_fbsd_sysctl_destroy(&V_ipfmain) != 0) return; if (V_ipfmain.ipf_running >= 0) { if (ipfdetach(&V_ipfmain) != 0) return; ipf_fbsd_sysctl_destroy(&V_ipfmain); ipf_destroy_all(&V_ipfmain); } V_ipfmain.ipf_running = -2; } VNET_SYSUNINIT(vnet_ipf_uninit, SI_SUB_PROTO_FIREWALL, SI_ORDER_THIRD, vnet_ipf_uninit, NULL); static int ipf_modunload() { int error, i; ipf_event_dereg(); error = ipf_pfil_unhook(); if (error != 0) return error; for (i = 0; ipf_devfiles[i]; i++) { if (ipf_devs[i] != NULL) destroy_dev(ipf_devs[i]); } ipf_unload_all(); printf("%s unloaded\n", ipfilter_version); return error; } static moduledata_t ipfiltermod = { "ipfilter", ipfilter_modevent, 0 }; DECLARE_MODULE(ipfilter, ipfiltermod, SI_SUB_PROTO_FIREWALL, SI_ORDER_SECOND); #ifdef MODULE_VERSION MODULE_VERSION(ipfilter, 1); #endif #ifdef SYSCTL_IPF int sysctl_ipf_int ( SYSCTL_HANDLER_ARGS ) { int error = 0; if (arg1) error = SYSCTL_OUT(req, arg1, sizeof(int)); else error = SYSCTL_OUT(req, &arg2, sizeof(int)); if (error || !req->newptr) return (error); if (!arg1) error = EPERM; else { if ((oidp->oid_kind & CTLFLAG_OFF) && (V_ipfmain.ipf_running > 0)) error = EBUSY; else error = SYSCTL_IN(req, arg1, sizeof(int)); } return (error); } #endif static int #if __FreeBSD_version >= 500043 ipfpoll(struct cdev *dev, int events, struct thread *td) #else ipfpoll(dev_t dev, int events, struct proc *td) #endif { int unit = GET_MINOR(dev); int revents; if (unit < 0 || unit > IPL_LOGMAX) return 0; revents = 0; CURVNET_SET(TD_TO_VNET(td)); switch (unit) { case IPL_LOGIPF : case IPL_LOGNAT : case IPL_LOGSTATE : #ifdef IPFILTER_LOG if ((events & (POLLIN | POLLRDNORM)) && ipf_log_canread(&V_ipfmain, unit)) revents |= events & (POLLIN | POLLRDNORM); #endif break; case IPL_LOGAUTH : if ((events & (POLLIN | POLLRDNORM)) && ipf_auth_waiting(&V_ipfmain)) revents |= events & (POLLIN | POLLRDNORM); break; case IPL_LOGSYNC : if ((events & (POLLIN | POLLRDNORM)) && ipf_sync_canread(&V_ipfmain)) revents |= events & (POLLIN | POLLRDNORM); if ((events & (POLLOUT | POLLWRNORM)) && ipf_sync_canwrite(&V_ipfmain)) revents |= events & (POLLOUT | POLLWRNORM); break; case IPL_LOGSCAN : case IPL_LOGLOOKUP : default : break; } if ((revents == 0) && ((events & (POLLIN|POLLRDNORM)) != 0)) selrecord(td, &V_ipfmain.ipf_selwait[unit]); CURVNET_RESTORE(); return revents; } /* * routines below for saving IP headers to buffer */ static int ipfopen(dev, flags #if ((BSD >= 199506) || (__FreeBSD_version >= 220000)) , devtype, p) int devtype; # if (__FreeBSD_version >= 500024) struct thread *p; # else struct proc *p; # endif /* __FreeBSD_version >= 500024 */ #else ) #endif #if (__FreeBSD_version >= 502116) struct cdev *dev; #else dev_t dev; #endif int flags; { int unit = GET_MINOR(dev); int error; if (IPL_LOGMAX < unit) error = ENXIO; else { switch (unit) { case IPL_LOGIPF : case IPL_LOGNAT : case IPL_LOGSTATE : case IPL_LOGAUTH : case IPL_LOGLOOKUP : case IPL_LOGSYNC : #ifdef IPFILTER_SCAN case IPL_LOGSCAN : #endif error = 0; break; default : error = ENXIO; break; } } return error; } static int ipfclose(dev, flags #if ((BSD >= 199506) || (__FreeBSD_version >= 220000)) , devtype, p) int devtype; # if (__FreeBSD_version >= 500024) struct thread *p; # else struct proc *p; # endif /* __FreeBSD_version >= 500024 */ #else ) #endif #if (__FreeBSD_version >= 502116) struct cdev *dev; #else dev_t dev; #endif int flags; { int unit = GET_MINOR(dev); if (IPL_LOGMAX < unit) unit = ENXIO; else unit = 0; return unit; } /* * ipfread/ipflog * 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) static int ipfread(dev, uio, ioflag) int ioflag; #else static int ipfread(dev, uio) #endif #if (__FreeBSD_version >= 502116) struct cdev *dev; #else dev_t dev; #endif struct uio *uio; { int error; int unit = GET_MINOR(dev); if (unit < 0) return ENXIO; CURVNET_SET(CRED_TO_VNET(dev->si_cred)); if (V_ipfmain.ipf_running < 1) { CURVNET_RESTORE(); return EIO; } if (unit == IPL_LOGSYNC) { error = ipf_sync_read(&V_ipfmain, uio); CURVNET_RESTORE(); return error; } #ifdef IPFILTER_LOG error = ipf_log_read(&V_ipfmain, unit, uio); #else error = ENXIO; #endif CURVNET_RESTORE(); return error; } /* * ipfwrite * 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) static int ipfwrite(dev, uio, ioflag) int ioflag; #else static int ipfwrite(dev, uio) #endif #if (__FreeBSD_version >= 502116) struct cdev *dev; #else dev_t dev; #endif struct uio *uio; { int error; CURVNET_SET(CRED_TO_VNET(dev->si_cred)); if (V_ipfmain.ipf_running < 1) { CURVNET_RESTORE(); return EIO; } if (GET_MINOR(dev) == IPL_LOGSYNC) { error = ipf_sync_write(&V_ipfmain, uio); CURVNET_RESTORE(); return error; } return ENXIO; } static int ipf_fbsd_sysctl_create(main_softc) ipf_main_softc_t *main_softc; { ipf_nat_softc_t *nat_softc; ipf_state_softc_t *state_softc; ipf_auth_softc_t *auth_softc; ipf_frag_softc_t *frag_softc; nat_softc = main_softc->ipf_nat_soft; state_softc = main_softc->ipf_state_soft; auth_softc = main_softc->ipf_auth_soft; frag_softc = main_softc->ipf_frag_soft; sysctl_ctx_init(&V_ipf_clist); SYSCTL_DYN_IPF(_net_inet_ipf, OID_AUTO, "fr_defnatage", CTLFLAG_RWO, &nat_softc->ipf_nat_defage, 0, ""); SYSCTL_DYN_IPF(_net_inet_ipf, OID_AUTO, "fr_statesize", CTLFLAG_RWO, &state_softc->ipf_state_size, 0, ""); SYSCTL_DYN_IPF(_net_inet_ipf, OID_AUTO, "fr_statemax", CTLFLAG_RWO, &state_softc->ipf_state_max, 0, ""); SYSCTL_DYN_IPF(_net_inet_ipf, OID_AUTO, "ipf_nattable_max", CTLFLAG_RWO, &nat_softc->ipf_nat_table_max, 0, ""); SYSCTL_DYN_IPF(_net_inet_ipf, OID_AUTO, "ipf_nattable_sz", CTLFLAG_RWO, &nat_softc->ipf_nat_table_sz, 0, ""); SYSCTL_DYN_IPF(_net_inet_ipf, OID_AUTO, "ipf_natrules_sz", CTLFLAG_RWO, &nat_softc->ipf_nat_maprules_sz, 0, ""); SYSCTL_DYN_IPF(_net_inet_ipf, OID_AUTO, "ipf_rdrrules_sz", CTLFLAG_RWO, &nat_softc->ipf_nat_rdrrules_sz, 0, ""); SYSCTL_DYN_IPF(_net_inet_ipf, OID_AUTO, "ipf_hostmap_sz", CTLFLAG_RWO, &nat_softc->ipf_nat_hostmap_sz, 0, ""); SYSCTL_DYN_IPF(_net_inet_ipf, OID_AUTO, "fr_authsize", CTLFLAG_RWO, &auth_softc->ipf_auth_size, 0, ""); SYSCTL_DYN_IPF(_net_inet_ipf, OID_AUTO, "fr_authused", CTLFLAG_RD, &auth_softc->ipf_auth_used, 0, ""); SYSCTL_DYN_IPF(_net_inet_ipf, OID_AUTO, "fr_defaultauthage", CTLFLAG_RW, &auth_softc->ipf_auth_defaultage, 0, ""); SYSCTL_DYN_IPF(_net_inet_ipf, OID_AUTO, "fr_ipfrttl", CTLFLAG_RW, &frag_softc->ipfr_ttl, 0, ""); return 0; } static int ipf_fbsd_sysctl_destroy(main_softc) ipf_main_softc_t *main_softc; { if (sysctl_ctx_free(&V_ipf_clist)) { printf("sysctl_ctx_free failed"); return(ENOTEMPTY); } return 0; }