Index: head/sys/contrib/ipfilter/netinet/ip_fil_freebsd.c =================================================================== --- head/sys/contrib/ipfilter/netinet/ip_fil_freebsd.c (revision 302297) +++ head/sys/contrib/ipfilter/netinet/ip_fil_freebsd.c (revision 302298) @@ -1,1448 +1,1484 @@ /* $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)); -ipf_main_softc_t ipfmain; +VNET_DEFINE(ipf_main_softc_t, ipfmain) = { + .ipf_running = -2, +}; +#define V_ipfmain VNET(ipfmain) # include # if defined(NETBSD_PF) # include # endif /* NETBSD_PF */ +static eventhandler_tag ipf_arrivetag, ipf_departtag; +#if 0 +/* + * Disable the "cloner" event handler; we are getting interface + * events before the firewall is fully initiallized and also no vnet + * information thus leading to uninitialised memory accesses. + * In addition it is unclear why we need it in first place. + * If it turns out to be needed, well need a dedicated event handler + * for it to deal with the ifc and the correct vnet. + */ +static eventhandler_tag ipf_clonetag; +#endif -static eventhandler_tag ipf_arrivetag, ipf_departtag, ipf_clonetag; +static void ipf_ifevent(void *arg, struct ifnet *ifp); -static void ipf_ifevent(void *arg); - -static void ipf_ifevent(arg) +static void ipf_ifevent(arg, ifp) void *arg; + struct ifnet *ifp; { - ipf_sync(arg, NULL); + + CURVNET_SET(ifp->if_vnet); + if (V_ipfmain.ipf_running > 0) + 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 - rv = ipf_check(&ipfmain, ip, ip->ip_hl << 2, ifp, (dir == PFIL_OUT), + 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) { - return (ipf_check(&ipfmain, mtod(*mp, struct ip *), - sizeof(struct ip6_hdr), ifp, (dir == PFIL_OUT), mp)); + 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 *)ipfmain.ipf_selwait, sizeof(ipfmain.ipf_selwait)); + 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); + CURVNET_SET(TD_TO_VNET(p)); #if (BSD >= 199306) if (securelevel_ge(p->p_cred, 3) && (mode & FWRITE)) { - ipfmain.ipf_interror = 130001; + V_ipfmain.ipf_interror = 130001; + CURVNET_RESTORE(); return EPERM; } #endif unit = GET_MINOR(dev); if ((IPL_LOGMAX < unit) || (unit < 0)) { - ipfmain.ipf_interror = 130002; + V_ipfmain.ipf_interror = 130002; + CURVNET_RESTORE(); return ENXIO; } - if (ipfmain.ipf_running <= 0) { + if (V_ipfmain.ipf_running <= 0) { if (unit != IPL_LOGIPF && cmd != SIOCIPFINTERROR) { - ipfmain.ipf_interror = 130003; + V_ipfmain.ipf_interror = 130003; + CURVNET_RESTORE(); return EIO; } if (cmd != SIOCIPFGETNEXT && cmd != SIOCIPFGET && cmd != SIOCIPFSET && cmd != SIOCFRENB && cmd != SIOCGETFS && cmd != SIOCGETFF && cmd != SIOCIPFINTERROR) { - ipfmain.ipf_interror = 130004; + V_ipfmain.ipf_interror = 130004; + CURVNET_RESTORE(); return EIO; } } SPL_NET(s); - CURVNET_SET(TD_TO_VNET(p)); - error = ipf_ioctlswitch(&ipfmain, unit, data, cmd, mode, p->p_uid, 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(&ipfmain, 4, FRI_NORMAL, ifp, + 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(&ipfmain, 6, FRI_NORMAL, ifp, + 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) - ipfmain.ipf_frouteok[0]++; + V_ipfmain.ipf_frouteok[0]++; else - ipfmain.ipf_frouteok[1]++; + 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, &ipfmain, \ + ipf_ifevent, NULL, \ EVENTHANDLER_PRI_ANY); ipf_departtag = EVENTHANDLER_REGISTER(ifnet_departure_event, \ - ipf_ifevent, &ipfmain, \ + ipf_ifevent, NULL, \ EVENTHANDLER_PRI_ANY); +#if 0 ipf_clonetag = EVENTHANDLER_REGISTER(if_clone_event, ipf_ifevent, \ - &ipfmain, EVENTHANDLER_PRI_ANY); + NULL, EVENTHANDLER_PRI_ANY); +#endif } 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 0 if (ipf_clonetag != NULL) { EVENTHANDLER_DEREGISTER(if_clone_event, ipf_clonetag); } +#endif } 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: head/sys/contrib/ipfilter/netinet/ip_proxy.c =================================================================== --- head/sys/contrib/ipfilter/netinet/ip_proxy.c (revision 302297) +++ head/sys/contrib/ipfilter/netinet/ip_proxy.c (revision 302298) @@ -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: head/sys/contrib/ipfilter/netinet/ip_rpcb_pxy.c =================================================================== --- head/sys/contrib/ipfilter/netinet/ip_rpcb_pxy.c (revision 302297) +++ head/sys/contrib/ipfilter/netinet/ip_rpcb_pxy.c (revision 302298) @@ -1,1473 +1,1475 @@ /* * Copyright (C) 2002-2012 by Ryan Beasley * * See the IPFILTER.LICENCE file for details on licencing. */ /* * Overview: * This is an in-kernel application proxy for Sun's RPCBIND (nee portmap) * protocol as defined in RFC1833. It is far from complete, mostly * lacking in less-likely corner cases, but it's definitely functional. * * Invocation: * rdr /32 port -> port udp proxy rpcbu * * If the host running IP Filter is the same as the RPC server, it's * perfectly legal for both the internal and external addresses and ports * to match. * * When triggered by appropriate IP NAT rules, this proxy works by * examining data contained in received packets. Requests and replies are * modified, NAT and state table entries created, etc., as necessary. */ /* * TODO / NOTES * * o Must implement locking to protect proxy session data. * o Fragmentation isn't supported. * o Only supports UDP. * o Doesn't support multiple RPC records in a single request. * o Errors should be more fine-grained. (e.g., malloc failure vs. * illegal RPCB request / reply) * o Even with the limit on the total amount of recorded transactions, * should there be a timeout on transaction removal? * o There is a potential collision between cloning, wildcard NAT and * state entries. There should be an appr_getport routine for * to avoid this. * o The enclosed hack of STREAMS support is pretty sick and most likely * broken. * * $Id$ */ #define IPF_RPCB_PROXY /* * Function prototypes */ void ipf_p_rpcb_main_load __P((void)); void ipf_p_rpcb_main_unload __P((void)); int ipf_p_rpcb_new __P((void *, fr_info_t *, ap_session_t *, nat_t *)); void ipf_p_rpcb_del __P((ipf_main_softc_t *, ap_session_t *)); int ipf_p_rpcb_in __P((void *, fr_info_t *, ap_session_t *, nat_t *)); int ipf_p_rpcb_out __P((void *, fr_info_t *, ap_session_t *, nat_t *)); static void ipf_p_rpcb_flush __P((rpcb_session_t *)); static int ipf_p_rpcb_decodereq __P((fr_info_t *, nat_t *, rpcb_session_t *, rpc_msg_t *)); static int ipf_p_rpcb_skipauth __P((rpc_msg_t *, xdr_auth_t *, u_32_t **)); static int ipf_p_rpcb_insert __P((rpcb_session_t *, rpcb_xact_t *)); static int ipf_p_rpcb_xdrrpcb __P((rpc_msg_t *, u_32_t *, rpcb_args_t *)); static int ipf_p_rpcb_getuaddr __P((rpc_msg_t *, xdr_uaddr_t *, u_32_t **)); static u_int ipf_p_rpcb_atoi __P((char *)); static int ipf_p_rpcb_modreq __P((fr_info_t *, nat_t *, rpc_msg_t *, mb_t *, u_int)); static int ipf_p_rpcb_decoderep __P((fr_info_t *, nat_t *, rpcb_session_t *, rpc_msg_t *, rpcb_xact_t **)); static rpcb_xact_t * ipf_p_rpcb_lookup __P((rpcb_session_t *, u_32_t)); static void ipf_p_rpcb_deref __P((rpcb_session_t *, rpcb_xact_t *)); static int ipf_p_rpcb_getproto __P((rpc_msg_t *, xdr_proto_t *, u_32_t **)); static int ipf_p_rpcb_getnat __P((fr_info_t *, nat_t *, u_int, u_int)); static int ipf_p_rpcb_modv3 __P((fr_info_t *, nat_t *, rpc_msg_t *, mb_t *, u_int)); static int ipf_p_rpcb_modv4 __P((fr_info_t *, nat_t *, rpc_msg_t *, mb_t *, u_int)); static void ipf_p_rpcb_fixlen __P((fr_info_t *, int)); /* * Global variables */ static frentry_t rpcbfr; /* Skeleton rule for reference by entities this proxy creates. */ -static int rpcbcnt; /* Upper bound of allocated RPCB sessions. */ +static VNET_DEFINE(int, rpcbcnt); +#define V_rpcbcnt VNET(rpcbcnt) + /* Upper bound of allocated RPCB sessions. */ /* XXX rpcbcnt still requires locking. */ static int rpcb_proxy_init = 0; /* * Since rpc_msg contains only pointers, one should use this macro as a * handy way to get to the goods. (In case you're wondering about the name, * this started as BYTEREF -> BREF -> B.) */ #define B(r) (u_32_t)ntohl(*(r)) /* * Public subroutines */ /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_main_load */ /* Returns: void */ /* Parameters: (void) */ /* */ /* Initialize the filter rule entry and session limiter. */ /* -------------------------------------------------------------------- */ void ipf_p_rpcb_main_load() { - rpcbcnt = 0; + V_rpcbcnt = 0; bzero((char *)&rpcbfr, sizeof(rpcbfr)); rpcbfr.fr_ref = 1; rpcbfr.fr_flags = FR_PASS|FR_QUICK|FR_KEEPSTATE; MUTEX_INIT(&rpcbfr.fr_lock, "ipf Sun RPCB proxy rule lock"); rpcb_proxy_init = 1; } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_main_unload */ /* Returns: void */ /* Parameters: (void) */ /* */ /* Destroy rpcbfr's mutex to avoid a lock leak. */ /* -------------------------------------------------------------------- */ void ipf_p_rpcb_main_unload() { if (rpcb_proxy_init == 1) { MUTEX_DESTROY(&rpcbfr.fr_lock); rpcb_proxy_init = 0; } } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_new */ /* Returns: int - -1 == failure, 0 == success */ /* Parameters: fin(I) - pointer to packet information */ /* aps(I) - pointer to proxy session structure */ /* nat(I) - pointer to NAT session structure */ /* */ /* Allocate resources for per-session proxy structures. */ /* -------------------------------------------------------------------- */ int ipf_p_rpcb_new(arg, fin, aps, nat) void *arg; fr_info_t *fin; ap_session_t *aps; nat_t *nat; { rpcb_session_t *rs; nat = nat; /* LINT */ if (fin->fin_v != 4) return -1; KMALLOC(rs, rpcb_session_t *); if (rs == NULL) return(-1); bzero((char *)rs, sizeof(*rs)); MUTEX_INIT(&rs->rs_rxlock, "ipf Sun RPCB proxy session lock"); aps->aps_data = rs; return(0); } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_del */ /* Returns: void */ /* Parameters: aps(I) - pointer to proxy session structure */ /* */ /* Free up a session's list of RPCB requests. */ /* -------------------------------------------------------------------- */ void ipf_p_rpcb_del(softc, aps) ipf_main_softc_t *softc; ap_session_t *aps; { rpcb_session_t *rs; rs = (rpcb_session_t *)aps->aps_data; MUTEX_ENTER(&rs->rs_rxlock); ipf_p_rpcb_flush(rs); MUTEX_EXIT(&rs->rs_rxlock); MUTEX_DESTROY(&rs->rs_rxlock); } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_in */ /* Returns: int - APR_ERR(1) == drop the packet, */ /* APR_ERR(2) == kill the proxy session, */ /* else change in packet length (in bytes) */ /* Parameters: fin(I) - pointer to packet information */ /* ip(I) - pointer to packet header */ /* aps(I) - pointer to proxy session structure */ /* nat(I) - pointer to NAT session structure */ /* */ /* Given a presumed RPCB request, perform some minor tests and pass off */ /* for decoding. Also pass packet off for a rewrite if necessary. */ /* -------------------------------------------------------------------- */ int ipf_p_rpcb_in(arg, fin, aps, nat) void *arg; fr_info_t *fin; ap_session_t *aps; nat_t *nat; { rpc_msg_t rpcmsg, *rm; rpcb_session_t *rs; u_int off, dlen; mb_t *m; int rv; /* Disallow fragmented or illegally short packets. */ if ((fin->fin_flx & (FI_FRAG|FI_SHORT)) != 0) return(APR_ERR(1)); /* Perform basic variable initialization. */ rs = (rpcb_session_t *)aps->aps_data; m = fin->fin_m; off = (char *)fin->fin_dp - (char *)fin->fin_ip; off += sizeof(udphdr_t) + fin->fin_ipoff; dlen = fin->fin_dlen - sizeof(udphdr_t); /* Disallow packets outside legal range for supported requests. */ if ((dlen < RPCB_REQMIN) || (dlen > RPCB_REQMAX)) return(APR_ERR(1)); /* Copy packet over to convenience buffer. */ rm = &rpcmsg; bzero((char *)rm, sizeof(*rm)); COPYDATA(m, off, dlen, (caddr_t)&rm->rm_msgbuf); rm->rm_buflen = dlen; /* Send off to decode request. */ rv = ipf_p_rpcb_decodereq(fin, nat, rs, rm); switch(rv) { case -1: return(APR_ERR(1)); /*NOTREACHED*/ break; case 0: break; case 1: rv = ipf_p_rpcb_modreq(fin, nat, rm, m, off); break; default: /*CONSTANTCONDITION*/ IPF_PANIC(1, ("illegal rv %d (ipf_p_rpcb_req)", rv)); } return(rv); } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_out */ /* Returns: int - APR_ERR(1) == drop the packet, */ /* APR_ERR(2) == kill the proxy session, */ /* else change in packet length (in bytes) */ /* Parameters: fin(I) - pointer to packet information */ /* ip(I) - pointer to packet header */ /* aps(I) - pointer to proxy session structure */ /* nat(I) - pointer to NAT session structure */ /* */ /* Given a presumed RPCB reply, perform some minor tests and pass off */ /* for decoding. If the message indicates a successful request with */ /* valid addressing information, create NAT and state structures to */ /* allow direct communication between RPC client and server. */ /* -------------------------------------------------------------------- */ int ipf_p_rpcb_out(arg, fin, aps, nat) void *arg; fr_info_t *fin; ap_session_t *aps; nat_t *nat; { rpc_msg_t rpcmsg, *rm; rpcb_session_t *rs; rpcb_xact_t *rx; u_int off, dlen; int rv, diff; mb_t *m; /* Disallow fragmented or illegally short packets. */ if ((fin->fin_flx & (FI_FRAG|FI_SHORT)) != 0) return(APR_ERR(1)); /* Perform basic variable initialization. */ rs = (rpcb_session_t *)aps->aps_data; rx = NULL; m = fin->fin_m; off = (char *)fin->fin_dp - (char *)fin->fin_ip; off += sizeof(udphdr_t) + fin->fin_ipoff; dlen = fin->fin_dlen - sizeof(udphdr_t); diff = 0; /* Disallow packets outside legal range for supported requests. */ if ((dlen < RPCB_REPMIN) || (dlen > RPCB_REPMAX)) return(APR_ERR(1)); /* Copy packet over to convenience buffer. */ rm = &rpcmsg; bzero((char *)rm, sizeof(*rm)); COPYDATA(m, off, dlen, (caddr_t)&rm->rm_msgbuf); rm->rm_buflen = dlen; rx = NULL; /* XXX gcc */ /* Send off to decode reply. */ rv = ipf_p_rpcb_decoderep(fin, nat, rs, rm, &rx); switch(rv) { case -1: /* Bad packet */ if (rx != NULL) { MUTEX_ENTER(&rs->rs_rxlock); ipf_p_rpcb_deref(rs, rx); MUTEX_EXIT(&rs->rs_rxlock); } return(APR_ERR(1)); /*NOTREACHED*/ break; case 0: /* Negative reply / request rejected */ break; case 1: /* Positive reply */ /* * With the IP address embedded in a GETADDR(LIST) reply, * we'll need to rewrite the packet in the very possible * event that the internal & external addresses aren't the * same. (i.e., this box is either a router or rpcbind * only listens on loopback.) */ if (nat->nat_odstaddr != nat->nat_ndstaddr) { if (rx->rx_type == RPCB_RES_STRING) diff = ipf_p_rpcb_modv3(fin, nat, rm, m, off); else if (rx->rx_type == RPCB_RES_LIST) diff = ipf_p_rpcb_modv4(fin, nat, rm, m, off); } break; default: /*CONSTANTCONDITION*/ IPF_PANIC(1, ("illegal rv %d (ipf_p_rpcb_decoderep)", rv)); } if (rx != NULL) { MUTEX_ENTER(&rs->rs_rxlock); /* XXX Gross hack - I'm overloading the reference * counter to deal with both threads and retransmitted * requests. One deref signals that this thread is * finished with rx, and the other signals that we've * processed its reply. */ ipf_p_rpcb_deref(rs, rx); ipf_p_rpcb_deref(rs, rx); MUTEX_EXIT(&rs->rs_rxlock); } return(diff); } /* * Private support subroutines */ /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_flush */ /* Returns: void */ /* Parameters: rs(I) - pointer to RPCB session structure */ /* */ /* Simply flushes the list of outstanding transactions, if any. */ /* -------------------------------------------------------------------- */ static void ipf_p_rpcb_flush(rs) rpcb_session_t *rs; { rpcb_xact_t *r1, *r2; r1 = rs->rs_rxlist; if (r1 == NULL) return; while (r1 != NULL) { r2 = r1; r1 = r1->rx_next; KFREE(r2); } } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_decodereq */ /* Returns: int - -1 == bad request or critical failure, */ /* 0 == request successfully decoded, */ /* 1 == request successfully decoded; requires */ /* address rewrite/modification */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT session structure */ /* rs(I) - pointer to RPCB session structure */ /* rm(I) - pointer to RPC message structure */ /* */ /* Take a presumed RPCB request, decode it, and store the results in */ /* the transaction list. If the internal target address needs to be */ /* modified, store its location in ptr. */ /* WARNING: It's the responsibility of the caller to make sure there */ /* is enough room in rs_buf for the basic RPC message "preamble". */ /* -------------------------------------------------------------------- */ static int ipf_p_rpcb_decodereq(fin, nat, rs, rm) fr_info_t *fin; nat_t *nat; rpcb_session_t *rs; rpc_msg_t *rm; { rpcb_args_t *ra; u_32_t xdr, *p; rpc_call_t *rc; rpcb_xact_t rx; int mod; p = (u_32_t *)rm->rm_msgbuf; mod = 0; bzero((char *)&rx, sizeof(rx)); rc = &rm->rm_call; rm->rm_xid = p; rx.rx_xid = B(p++); /* Record this message's XID. */ /* Parse out and test the RPC header. */ if ((B(p++) != RPCB_CALL) || (B(p++) != RPCB_MSG_VERSION) || (B(p++) != RPCB_PROG)) return(-1); /* Record the RPCB version and procedure. */ rc->rc_vers = p++; rc->rc_proc = p++; /* Bypass RPC authentication stuff. */ if (ipf_p_rpcb_skipauth(rm, &rc->rc_authcred, &p) != 0) return(-1); if (ipf_p_rpcb_skipauth(rm, &rc->rc_authverf, &p) != 0) return(-1); /* Compare RPCB version and procedure numbers. */ switch(B(rc->rc_vers)) { case 2: /* This proxy only supports PMAP_GETPORT. */ if (B(rc->rc_proc) != RPCB_GETPORT) return(-1); /* Portmap requests contain four 4 byte parameters. */ if (RPCB_BUF_EQ(rm, p, 16) == 0) return(-1); p += 2; /* Skip requested program and version numbers. */ /* Sanity check the requested protocol. */ xdr = B(p); if (!(xdr == IPPROTO_UDP || xdr == IPPROTO_TCP)) return(-1); rx.rx_type = RPCB_RES_PMAP; rx.rx_proto = xdr; break; case 3: case 4: /* GETADDRLIST is exclusive to v4; GETADDR for v3 & v4 */ switch(B(rc->rc_proc)) { case RPCB_GETADDR: rx.rx_type = RPCB_RES_STRING; rx.rx_proto = (u_int)fin->fin_p; break; case RPCB_GETADDRLIST: if (B(rc->rc_vers) != 4) return(-1); rx.rx_type = RPCB_RES_LIST; break; default: return(-1); } ra = &rc->rc_rpcbargs; /* Decode the 'struct rpcb' request. */ if (ipf_p_rpcb_xdrrpcb(rm, p, ra) != 0) return(-1); /* Are the target address & port valid? */ if ((ra->ra_maddr.xu_ip != nat->nat_ndstaddr) || (ra->ra_maddr.xu_port != nat->nat_ndport)) return(-1); /* Do we need to rewrite this packet? */ if ((nat->nat_ndstaddr != nat->nat_odstaddr) || (nat->nat_ndport != nat->nat_odport)) mod = 1; break; default: return(-1); } MUTEX_ENTER(&rs->rs_rxlock); if (ipf_p_rpcb_insert(rs, &rx) != 0) { MUTEX_EXIT(&rs->rs_rxlock); return(-1); } MUTEX_EXIT(&rs->rs_rxlock); return(mod); } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_skipauth */ /* Returns: int -- -1 == illegal auth parameters (lengths) */ /* 0 == valid parameters, pointer advanced */ /* Parameters: rm(I) - pointer to RPC message structure */ /* auth(I) - pointer to RPC auth structure */ /* buf(IO) - pointer to location within convenience buffer */ /* */ /* Record auth data length & location of auth data, then advance past */ /* it. */ /* -------------------------------------------------------------------- */ static int ipf_p_rpcb_skipauth(rm, auth, buf) rpc_msg_t *rm; xdr_auth_t *auth; u_32_t **buf; { u_32_t *p, xdr; p = *buf; /* Make sure we have enough space for expected fixed auth parms. */ if (RPCB_BUF_GEQ(rm, p, 8) == 0) return(-1); p++; /* We don't care about auth_flavor. */ auth->xa_string.xs_len = p; xdr = B(p++); /* Length of auth_data */ /* Test for absurdity / illegality of auth_data length. */ if ((XDRALIGN(xdr) < xdr) || (RPCB_BUF_GEQ(rm, p, XDRALIGN(xdr)) == 0)) return(-1); auth->xa_string.xs_str = (char *)p; p += XDRALIGN(xdr); /* Advance our location. */ *buf = (u_32_t *)p; return(0); } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_insert */ /* Returns: int -- -1 == list insertion failed, */ /* 0 == item successfully added */ /* Parameters: rs(I) - pointer to RPCB session structure */ /* rx(I) - pointer to RPCB transaction structure */ /* -------------------------------------------------------------------- */ static int ipf_p_rpcb_insert(rs, rx) rpcb_session_t *rs; rpcb_xact_t *rx; { rpcb_xact_t *rxp; rxp = ipf_p_rpcb_lookup(rs, rx->rx_xid); if (rxp != NULL) { ++rxp->rx_ref; return(0); } - if (rpcbcnt == RPCB_MAXREQS) + if (V_rpcbcnt == RPCB_MAXREQS) return(-1); KMALLOC(rxp, rpcb_xact_t *); if (rxp == NULL) return(-1); bcopy((char *)rx, (char *)rxp, sizeof(*rx)); if (rs->rs_rxlist != NULL) rs->rs_rxlist->rx_pnext = &rxp->rx_next; rxp->rx_pnext = &rs->rs_rxlist; rxp->rx_next = rs->rs_rxlist; rs->rs_rxlist = rxp; rxp->rx_ref = 1; - ++rpcbcnt; + ++V_rpcbcnt; return(0); } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_xdrrpcb */ /* Returns: int -- -1 == failure to properly decode the request */ /* 0 == rpcb successfully decoded */ /* Parameters: rs(I) - pointer to RPCB session structure */ /* p(I) - pointer to location within session buffer */ /* rpcb(O) - pointer to rpcb (xdr type) structure */ /* */ /* Decode a XDR encoded rpcb structure and record its contents in rpcb */ /* within only the context of TCP/UDP over IP networks. */ /* -------------------------------------------------------------------- */ static int ipf_p_rpcb_xdrrpcb(rm, p, ra) rpc_msg_t *rm; u_32_t *p; rpcb_args_t *ra; { if (!RPCB_BUF_GEQ(rm, p, 20)) return(-1); /* Bypass target program & version. */ p += 2; /* Decode r_netid. Must be "tcp" or "udp". */ if (ipf_p_rpcb_getproto(rm, &ra->ra_netid, &p) != 0) return(-1); /* Decode r_maddr. */ if (ipf_p_rpcb_getuaddr(rm, &ra->ra_maddr, &p) != 0) return(-1); /* Advance to r_owner and make sure it's empty. */ if (!RPCB_BUF_EQ(rm, p, 4) || (B(p) != 0)) return(-1); return(0); } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_getuaddr */ /* Returns: int -- -1 == illegal string, */ /* 0 == string parsed; contents recorded */ /* Parameters: rm(I) - pointer to RPC message structure */ /* xu(I) - pointer to universal address structure */ /* p(IO) - pointer to location within message buffer */ /* */ /* Decode the IP address / port at p and record them in xu. */ /* -------------------------------------------------------------------- */ static int ipf_p_rpcb_getuaddr(rm, xu, p) rpc_msg_t *rm; xdr_uaddr_t *xu; u_32_t **p; { char *c, *i, *b, *pp; u_int d, dd, l, t; char uastr[24]; /* Test for string length. */ if (!RPCB_BUF_GEQ(rm, *p, 4)) return(-1); xu->xu_xslen = (*p)++; xu->xu_xsstr = (char *)*p; /* Length check */ l = B(xu->xu_xslen); if (l < 11 || l > 23 || !RPCB_BUF_GEQ(rm, *p, XDRALIGN(l))) return(-1); /* Advance p */ *(char **)p += XDRALIGN(l); /* Copy string to local buffer & terminate C style */ bcopy(xu->xu_xsstr, uastr, l); uastr[l] = '\0'; i = (char *)&xu->xu_ip; pp = (char *)&xu->xu_port; /* * Expected format: a.b.c.d.e.f where [a-d] correspond to bytes of * an IP address and [ef] are the bytes of a L4 port. */ if (!(ISDIGIT(uastr[0]) && ISDIGIT(uastr[l-1]))) return(-1); b = uastr; for (c = &uastr[1], d = 0, dd = 0; c < &uastr[l-1]; c++) { if (ISDIGIT(*c)) { dd = 0; continue; } if (*c == '.') { if (dd != 0) return(-1); /* Check for ASCII byte. */ *c = '\0'; t = ipf_p_rpcb_atoi(b); if (t > 255) return(-1); /* Aim b at beginning of the next byte. */ b = c + 1; /* Switch off IP addr vs port parsing. */ if (d < 4) i[d++] = t & 0xff; else pp[d++ - 4] = t & 0xff; dd = 1; continue; } return(-1); } if (d != 5) /* String must contain exactly 5 periods. */ return(-1); /* Handle the last byte (port low byte) */ t = ipf_p_rpcb_atoi(b); if (t > 255) return(-1); pp[d - 4] = t & 0xff; return(0); } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_atoi (XXX should be generic for all proxies) */ /* Returns: int -- integer representation of supplied string */ /* Parameters: ptr(I) - input string */ /* */ /* Simple version of atoi(3) ripped from ip_rcmd_pxy.c. */ /* -------------------------------------------------------------------- */ static u_int ipf_p_rpcb_atoi(ptr) char *ptr; { register char *s = ptr, c; register u_int i = 0; while (((c = *s++) != '\0') && ISDIGIT(c)) { i *= 10; i += c - '0'; } return i; } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_modreq */ /* Returns: int -- change in datagram length */ /* APR_ERR(2) - critical failure */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT session */ /* rm(I) - pointer to RPC message structure */ /* m(I) - pointer to mbuf chain */ /* off(I) - current offset within mbuf chain */ /* */ /* When external and internal addresses differ, we rewrite the former */ /* with the latter. (This is exclusive to protocol versions 3 & 4). */ /* -------------------------------------------------------------------- */ static int ipf_p_rpcb_modreq(fin, nat, rm, m, off) fr_info_t *fin; nat_t *nat; rpc_msg_t *rm; mb_t *m; u_int off; { u_int len, xlen, pos, bogo; rpcb_args_t *ra; char uaddr[24]; udphdr_t *udp; char *i, *p; int diff; ra = &rm->rm_call.rc_rpcbargs; i = (char *)&nat->nat_odstaddr; p = (char *)&nat->nat_odport; /* Form new string. */ bzero(uaddr, sizeof(uaddr)); /* Just in case we need padding. */ #if defined(SNPRINTF) && defined(_KERNEL) SNPRINTF(uaddr, sizeof(uaddr), #else (void) sprintf(uaddr, #endif "%u.%u.%u.%u.%u.%u", i[0] & 0xff, i[1] & 0xff, i[2] & 0xff, i[3] & 0xff, p[0] & 0xff, p[1] & 0xff); len = strlen(uaddr); xlen = XDRALIGN(len); /* Determine mbuf offset to start writing to. */ pos = (char *)ra->ra_maddr.xu_xslen - rm->rm_msgbuf; off += pos; /* Write new string length. */ bogo = htonl(len); COPYBACK(m, off, 4, (caddr_t)&bogo); off += 4; /* Write new string. */ COPYBACK(m, off, xlen, uaddr); off += xlen; /* Write in zero r_owner. */ bogo = 0; COPYBACK(m, off, 4, (caddr_t)&bogo); /* Determine difference in data lengths. */ diff = xlen - XDRALIGN(B(ra->ra_maddr.xu_xslen)); /* * If our new string has a different length, make necessary * adjustments. */ if (diff != 0) { udp = fin->fin_dp; udp->uh_ulen = htons(ntohs(udp->uh_ulen) + diff); fin->fin_plen += diff; fin->fin_ip->ip_len = htons(fin->fin_plen); fin->fin_dlen += diff; /* XXX Storage lengths. */ } return(diff); } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_decoderep */ /* Returns: int - -1 == bad request or critical failure, */ /* 0 == valid, negative reply */ /* 1 == vaddlid, positive reply; needs no changes */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT session structure */ /* rs(I) - pointer to RPCB session structure */ /* rm(I) - pointer to RPC message structure */ /* rxp(O) - pointer to RPCB transaction structure */ /* */ /* Take a presumed RPCB reply, extract the XID, search for the original */ /* request information, and determine whether the request was accepted */ /* or rejected. With a valid accepted reply, go ahead and create NAT */ /* and state entries, and finish up by rewriting the packet as */ /* required. */ /* */ /* WARNING: It's the responsibility of the caller to make sure there */ /* is enough room in rs_buf for the basic RPC message "preamble". */ /* -------------------------------------------------------------------- */ static int ipf_p_rpcb_decoderep(fin, nat, rs, rm, rxp) fr_info_t *fin; nat_t *nat; rpcb_session_t *rs; rpc_msg_t *rm; rpcb_xact_t **rxp; { rpcb_listp_t *rl; rpcb_entry_t *re; rpcb_xact_t *rx; u_32_t xdr, *p; rpc_resp_t *rr; int rv, cnt; p = (u_32_t *)rm->rm_msgbuf; bzero((char *)&rx, sizeof(rx)); rr = &rm->rm_resp; rm->rm_xid = p; xdr = B(p++); /* Record this message's XID. */ /* Lookup XID */ MUTEX_ENTER(&rs->rs_rxlock); if ((rx = ipf_p_rpcb_lookup(rs, xdr)) == NULL) { MUTEX_EXIT(&rs->rs_rxlock); return(-1); } ++rx->rx_ref; /* per thread reference */ MUTEX_EXIT(&rs->rs_rxlock); *rxp = rx; /* Test call vs reply */ if (B(p++) != RPCB_REPLY) return(-1); /* Test reply_stat */ switch(B(p++)) { case RPCB_MSG_DENIED: return(0); case RPCB_MSG_ACCEPTED: break; default: return(-1); } /* Bypass RPC authentication stuff. */ if (ipf_p_rpcb_skipauth(rm, &rr->rr_authverf, &p) != 0) return(-1); /* Test accept status */ if (!RPCB_BUF_GEQ(rm, p, 4)) return(-1); if (B(p++) != 0) return(0); /* Parse out the expected reply */ switch(rx->rx_type) { case RPCB_RES_PMAP: /* There must be only one 4 byte argument. */ if (!RPCB_BUF_EQ(rm, p, 4)) return(-1); rr->rr_v2 = p; xdr = B(rr->rr_v2); /* Reply w/ a 0 port indicates service isn't registered */ if (xdr == 0) return(0); /* Is the value sane? */ if (xdr > 65535) return(-1); /* Create NAT & state table entries. */ if (ipf_p_rpcb_getnat(fin, nat, rx->rx_proto, (u_int)xdr) != 0) return(-1); break; case RPCB_RES_STRING: /* Expecting a XDR string; need 4 bytes for length */ if (!RPCB_BUF_GEQ(rm, p, 4)) return(-1); rr->rr_v3.xu_str.xs_len = p++; rr->rr_v3.xu_str.xs_str = (char *)p; xdr = B(rr->rr_v3.xu_xslen); /* A null string indicates an unregistered service */ if ((xdr == 0) && RPCB_BUF_EQ(rm, p, 0)) return(0); /* Decode the target IP address / port. */ if (ipf_p_rpcb_getuaddr(rm, &rr->rr_v3, &p) != 0) return(-1); /* Validate the IP address and port contained. */ if (nat->nat_odstaddr != rr->rr_v3.xu_ip) return(-1); /* Create NAT & state table entries. */ if (ipf_p_rpcb_getnat(fin, nat, rx->rx_proto, (u_int)rr->rr_v3.xu_port) != 0) return(-1); break; case RPCB_RES_LIST: if (!RPCB_BUF_GEQ(rm, p, 4)) return(-1); /* rpcb_entry_list_ptr */ switch(B(p)) { case 0: return(0); /*NOTREACHED*/ break; case 1: break; default: return(-1); } rl = &rr->rr_v4; rl->rl_list = p++; cnt = 0; for(;;) { re = &rl->rl_entries[rl->rl_cnt]; if (ipf_p_rpcb_getuaddr(rm, &re->re_maddr, &p) != 0) return(-1); if (ipf_p_rpcb_getproto(rm, &re->re_netid, &p) != 0) return(-1); /* re_semantics & re_pfamily length */ if (!RPCB_BUF_GEQ(rm, p, 12)) return(-1); p++; /* Skipping re_semantics. */ xdr = B(p++); if ((xdr != 4) || strncmp((char *)p, "inet", 4)) return(-1); p++; if (ipf_p_rpcb_getproto(rm, &re->re_proto, &p) != 0) return(-1); if (!RPCB_BUF_GEQ(rm, p, 4)) return(-1); re->re_more = p; if (B(re->re_more) > 1) /* 0,1 only legal values */ return(-1); ++rl->rl_cnt; ++cnt; if (B(re->re_more) == 0) break; /* Replies in max out at 2; TCP and/or UDP */ if (cnt > 2) return(-1); p++; } for(rl->rl_cnt = 0; rl->rl_cnt < cnt; rl->rl_cnt++) { re = &rl->rl_entries[rl->rl_cnt]; rv = ipf_p_rpcb_getnat(fin, nat, re->re_proto.xp_proto, (u_int)re->re_maddr.xu_port); if (rv != 0) return(-1); } break; default: /*CONSTANTCONDITION*/ IPF_PANIC(1, ("illegal rx_type %d", rx->rx_type)); } return(1); } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_lookup */ /* Returns: rpcb_xact_t * - NULL == no matching record, */ /* else pointer to relevant entry */ /* Parameters: rs(I) - pointer to RPCB session */ /* xid(I) - XID to look for */ /* -------------------------------------------------------------------- */ static rpcb_xact_t * ipf_p_rpcb_lookup(rs, xid) rpcb_session_t *rs; u_32_t xid; { rpcb_xact_t *rx; if (rs->rs_rxlist == NULL) return(NULL); for (rx = rs->rs_rxlist; rx != NULL; rx = rx->rx_next) if (rx->rx_xid == xid) break; return(rx); } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_deref */ /* Returns: (void) */ /* Parameters: rs(I) - pointer to RPCB session */ /* rx(I) - pointer to RPC transaction struct to remove */ /* force(I) - indicates to delete entry regardless of */ /* reference count */ /* Locking: rs->rs_rxlock must be held write only */ /* */ /* Free the RPCB transaction record rx from the chain of entries. */ /* -------------------------------------------------------------------- */ static void ipf_p_rpcb_deref(rs, rx) rpcb_session_t *rs; rpcb_xact_t *rx; { rs = rs; /* LINT */ if (rx == NULL) return; if (--rx->rx_ref != 0) return; if (rx->rx_next != NULL) rx->rx_next->rx_pnext = rx->rx_pnext; *rx->rx_pnext = rx->rx_next; KFREE(rx); - --rpcbcnt; + --V_rpcbcnt; } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_getproto */ /* Returns: int - -1 == illegal protocol/netid, */ /* 0 == legal protocol/netid */ /* Parameters: rm(I) - pointer to RPC message structure */ /* xp(I) - pointer to netid structure */ /* p(IO) - pointer to location within packet buffer */ /* */ /* Decode netid/proto stored at p and record its numeric value. */ /* -------------------------------------------------------------------- */ static int ipf_p_rpcb_getproto(rm, xp, p) rpc_msg_t *rm; xdr_proto_t *xp; u_32_t **p; { u_int len; /* Must have 4 bytes for length & 4 bytes for "tcp" or "udp". */ if (!RPCB_BUF_GEQ(rm, p, 8)) return(-1); xp->xp_xslen = (*p)++; xp->xp_xsstr = (char *)*p; /* Test the string length. */ len = B(xp->xp_xslen); if (len != 3) return(-1); /* Test the actual string & record the protocol accordingly. */ if (!strncmp((char *)xp->xp_xsstr, "tcp\0", 4)) xp->xp_proto = IPPROTO_TCP; else if (!strncmp((char *)xp->xp_xsstr, "udp\0", 4)) xp->xp_proto = IPPROTO_UDP; else { return(-1); } /* Advance past the string. */ (*p)++; return(0); } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_getnat */ /* Returns: int -- -1 == failed to create table entries, */ /* 0 == success */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT table entry */ /* proto(I) - transport protocol for new entries */ /* port(I) - new port to use w/ wildcard table entries */ /* */ /* Create state and NAT entries to handle an anticipated connection */ /* attempt between RPC client and server. */ /* -------------------------------------------------------------------- */ static int ipf_p_rpcb_getnat(fin, nat, proto, port) fr_info_t *fin; nat_t *nat; u_int proto; u_int port; { ipf_main_softc_t *softc = fin->fin_main_soft; ipnat_t *ipn, ipnat; tcphdr_t tcp; ipstate_t *is; fr_info_t fi; nat_t *natl; int nflags; ipn = nat->nat_ptr; /* Generate dummy fr_info */ bcopy((char *)fin, (char *)&fi, sizeof(fi)); fi.fin_out = 0; fi.fin_p = proto; fi.fin_sport = 0; fi.fin_dport = port & 0xffff; fi.fin_flx |= FI_IGNORE; fi.fin_saddr = nat->nat_osrcaddr; fi.fin_daddr = nat->nat_odstaddr; bzero((char *)&tcp, sizeof(tcp)); tcp.th_dport = htons(port); if (proto == IPPROTO_TCP) { tcp.th_win = htons(8192); TCP_OFF_A(&tcp, sizeof(tcphdr_t) >> 2); fi.fin_dlen = sizeof(tcphdr_t); tcp.th_flags = TH_SYN; nflags = NAT_TCP; } else { fi.fin_dlen = sizeof(udphdr_t); nflags = NAT_UDP; } nflags |= SI_W_SPORT|NAT_SEARCH; fi.fin_dp = &tcp; fi.fin_plen = fi.fin_hlen + fi.fin_dlen; /* * Search for existing NAT & state entries. Pay close attention to * mutexes / locks grabbed from lookup routines, as not doing so could * lead to bad things. * * If successful, fr_stlookup returns with ipf_state locked. We have * no use for this lock, so simply unlock it if necessary. */ is = ipf_state_lookup(&fi, &tcp, NULL); if (is != NULL) { RWLOCK_EXIT(&softc->ipf_state); } RWLOCK_EXIT(&softc->ipf_nat); WRITE_ENTER(&softc->ipf_nat); natl = ipf_nat_inlookup(&fi, nflags, proto, fi.fin_src, fi.fin_dst); if ((natl != NULL) && (is != NULL)) { MUTEX_DOWNGRADE(&softc->ipf_nat); return(0); } /* Slightly modify the following structures for actual use in creating * NAT and/or state entries. We're primarily concerned with stripping * flags that may be detrimental to the creation process or simply * shouldn't be associated with a table entry. */ fi.fin_fr = &rpcbfr; fi.fin_flx &= ~FI_IGNORE; nflags &= ~NAT_SEARCH; if (natl == NULL) { #ifdef USE_MUTEXES ipf_nat_softc_t *softn = softc->ipf_nat_soft; #endif /* XXX Since we're just copying the original ipn contents * back, would we be better off just sending a pointer to * the 'temp' copy off to nat_new instead? */ /* Generate template/bogus NAT rule. */ bcopy((char *)ipn, (char *)&ipnat, sizeof(ipnat)); ipn->in_flags = nflags & IPN_TCPUDP; ipn->in_apr = NULL; ipn->in_pr[0] = proto; ipn->in_pr[1] = proto; ipn->in_dpmin = fi.fin_dport; ipn->in_dpmax = fi.fin_dport; ipn->in_dpnext = fi.fin_dport; ipn->in_space = 1; ipn->in_ippip = 1; if (ipn->in_flags & IPN_FILTER) { ipn->in_scmp = 0; ipn->in_dcmp = 0; } ipn->in_plabel = -1; /* Create NAT entry. return NULL if this fails. */ MUTEX_ENTER(&softn->ipf_nat_new); natl = ipf_nat_add(&fi, ipn, NULL, nflags|SI_CLONE|NAT_SLAVE, NAT_INBOUND); MUTEX_EXIT(&softn->ipf_nat_new); bcopy((char *)&ipnat, (char *)ipn, sizeof(ipnat)); if (natl == NULL) { MUTEX_DOWNGRADE(&softc->ipf_nat); return(-1); } natl->nat_ptr = ipn; fi.fin_saddr = natl->nat_nsrcaddr; fi.fin_daddr = natl->nat_ndstaddr; ipn->in_use++; (void) ipf_nat_proto(&fi, natl, nflags); MUTEX_ENTER(&natl->nat_lock); ipf_nat_update(&fi, natl); MUTEX_EXIT(&natl->nat_lock); } MUTEX_DOWNGRADE(&softc->ipf_nat); if (is == NULL) { /* Create state entry. Return NULL if this fails. */ fi.fin_flx |= FI_NATED; fi.fin_flx &= ~FI_STATE; nflags &= NAT_TCPUDP; nflags |= SI_W_SPORT|SI_CLONE; if (ipf_state_add(softc, &fi, NULL, nflags) != 0) { /* * XXX nat_delete is private to ip_nat.c. Should * check w/ Darren about this one. * * nat_delete(natl, NL_EXPIRE); */ return(-1); } } return(0); } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_modv3 */ /* Returns: int -- change in packet length */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT session */ /* rm(I) - pointer to RPC message structure */ /* m(I) - pointer to mbuf chain */ /* off(I) - offset within mbuf chain */ /* */ /* Write a new universal address string to this packet, adjusting */ /* lengths as necessary. */ /* -------------------------------------------------------------------- */ static int ipf_p_rpcb_modv3(fin, nat, rm, m, off) fr_info_t *fin; nat_t *nat; rpc_msg_t *rm; mb_t *m; u_int off; { u_int len, xlen, pos, bogo; rpc_resp_t *rr; char uaddr[24]; char *i, *p; int diff; rr = &rm->rm_resp; i = (char *)&nat->nat_ndstaddr; p = (char *)&rr->rr_v3.xu_port; /* Form new string. */ bzero(uaddr, sizeof(uaddr)); /* Just in case we need padding. */ #if defined(SNPRINTF) && defined(_KERNEL) SNPRINTF(uaddr, sizeof(uaddr), #else (void) sprintf(uaddr, #endif "%u.%u.%u.%u.%u.%u", i[0] & 0xff, i[1] & 0xff, i[2] & 0xff, i[3] & 0xff, p[0] & 0xff, p[1] & 0xff); len = strlen(uaddr); xlen = XDRALIGN(len); /* Determine mbuf offset to write to. */ pos = (char *)rr->rr_v3.xu_xslen - rm->rm_msgbuf; off += pos; /* Write new string length. */ bogo = htonl(len); COPYBACK(m, off, 4, (caddr_t)&bogo); off += 4; /* Write new string. */ COPYBACK(m, off, xlen, uaddr); /* Determine difference in data lengths. */ diff = xlen - XDRALIGN(B(rr->rr_v3.xu_xslen)); /* * If our new string has a different length, make necessary * adjustments. */ if (diff != 0) ipf_p_rpcb_fixlen(fin, diff); return(diff); } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_modv4 */ /* Returns: int -- change in packet length */ /* Parameters: fin(I) - pointer to packet information */ /* nat(I) - pointer to NAT session */ /* rm(I) - pointer to RPC message structure */ /* m(I) - pointer to mbuf chain */ /* off(I) - offset within mbuf chain */ /* */ /* Write new rpcb_entry list, adjusting lengths as necessary. */ /* -------------------------------------------------------------------- */ static int ipf_p_rpcb_modv4(fin, nat, rm, m, off) fr_info_t *fin; nat_t *nat; rpc_msg_t *rm; mb_t *m; u_int off; { u_int len, xlen, pos, bogo; rpcb_listp_t *rl; rpcb_entry_t *re; rpc_resp_t *rr; char uaddr[24]; int diff, cnt; char *i, *p; diff = 0; rr = &rm->rm_resp; rl = &rr->rr_v4; i = (char *)&nat->nat_ndstaddr; /* Determine mbuf offset to write to. */ re = &rl->rl_entries[0]; pos = (char *)re->re_maddr.xu_xslen - rm->rm_msgbuf; off += pos; for (cnt = 0; cnt < rl->rl_cnt; cnt++) { re = &rl->rl_entries[cnt]; p = (char *)&re->re_maddr.xu_port; /* Form new string. */ bzero(uaddr, sizeof(uaddr)); /* Just in case we need padding. */ #if defined(SNPRINTF) && defined(_KERNEL) SNPRINTF(uaddr, sizeof(uaddr), #else (void) sprintf(uaddr, #endif "%u.%u.%u.%u.%u.%u", i[0] & 0xff, i[1] & 0xff, i[2] & 0xff, i[3] & 0xff, p[0] & 0xff, p[1] & 0xff); len = strlen(uaddr); xlen = XDRALIGN(len); /* Write new string length. */ bogo = htonl(len); COPYBACK(m, off, 4, (caddr_t)&bogo); off += 4; /* Write new string. */ COPYBACK(m, off, xlen, uaddr); off += xlen; /* Record any change in length. */ diff += xlen - XDRALIGN(B(re->re_maddr.xu_xslen)); /* If the length changed, copy back the rest of this entry. */ len = ((char *)re->re_more + 4) - (char *)re->re_netid.xp_xslen; if (diff != 0) { COPYBACK(m, off, len, (caddr_t)re->re_netid.xp_xslen); } off += len; } /* * If our new string has a different length, make necessary * adjustments. */ if (diff != 0) ipf_p_rpcb_fixlen(fin, diff); return(diff); } /* -------------------------------------------------------------------- */ /* Function: ipf_p_rpcb_fixlen */ /* Returns: (void) */ /* Parameters: fin(I) - pointer to packet information */ /* len(I) - change in packet length */ /* */ /* Adjust various packet related lengths held in structure and packet */ /* header fields. */ /* -------------------------------------------------------------------- */ static void ipf_p_rpcb_fixlen(fin, len) fr_info_t *fin; int len; { udphdr_t *udp; udp = fin->fin_dp; udp->uh_ulen = htons(ntohs(udp->uh_ulen) + len); fin->fin_plen += len; fin->fin_ip->ip_len = htons(fin->fin_plen); fin->fin_dlen += len; } #undef B Index: head/sys/contrib/ipfilter/netinet/ip_rules.c =================================================================== --- head/sys/contrib/ipfilter/netinet/ip_rules.c (revision 302297) +++ head/sys/contrib/ipfilter/netinet/ip_rules.c (revision 302298) @@ -1,264 +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 -extern ipf_main_softc_t ipfmain; +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(&ipfmain, IPL_LOGIPF, SIOCADDFR, (caddr_t)fp, - ipfmain.ipf_active, 0); + 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(&ipfmain, IPL_LOGIPF, SIOCDELFR, + err = frrequest(&V_ipfmain, IPL_LOGIPF, SIOCDELFR, (caddr_t)&ipfrule_out_, - ipfmain.ipf_active, 0); + 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(&ipfmain, IPL_LOGIPF, SIOCADDFR, (caddr_t)fp, - ipfmain.ipf_active, 0); + 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(&ipfmain, IPL_LOGIPF, SIOCDELFR, + err = frrequest(&V_ipfmain, IPL_LOGIPF, SIOCDELFR, (caddr_t)&ipfrule_in_, - ipfmain.ipf_active, 0); + 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: head/sys/contrib/ipfilter/netinet/mlfk_ipl.c =================================================================== --- head/sys/contrib/ipfilter/netinet/mlfk_ipl.c (revision 302297) +++ head/sys/contrib/ipfilter/netinet/mlfk_ipl.c (revision 302298) @@ -1,568 +1,697 @@ /* $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" -extern ipf_main_softc_t ipfmain; +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 sysctl_ipf_int_nat ( SYSCTL_HANDLER_ARGS ); +static int sysctl_ipf_int_state ( SYSCTL_HANDLER_ARGS ); +static int sysctl_ipf_int_auth ( SYSCTL_HANDLER_ARGS ); +static int sysctl_ipf_int_frag ( 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*); +static int ipf_fbsd_sysctl_create(void); +static int ipf_fbsd_sysctl_destroy(void); #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|access, \ - ptr, val, sysctl_ipf_int, "I", descr); -#define SYSCTL_DYN_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_NAT(parent, nbr, name, access,ptr, val, descr) \ SYSCTL_ADD_OID(&ipf_clist, SYSCTL_STATIC_CHILDREN(parent), nbr, name, \ - CTLFLAG_DYN|CTLTYPE_INT|access, ptr, val, sysctl_ipf_int, "I", descr) + CTLFLAG_DYN|CTLTYPE_INT|CTLFLAG_VNET|access, ptr, val, sysctl_ipf_int_nat, "I", descr) +#define SYSCTL_DYN_IPF_STATE(parent, nbr, name, access,ptr, val, descr) \ + SYSCTL_ADD_OID(&ipf_clist, SYSCTL_STATIC_CHILDREN(parent), nbr, name, \ + CTLFLAG_DYN|CTLTYPE_INT|CTLFLAG_VNET|access, ptr, val, sysctl_ipf_int_state, "I", descr) +#define SYSCTL_DYN_IPF_FRAG(parent, nbr, name, access,ptr, val, descr) \ + SYSCTL_ADD_OID(&ipf_clist, SYSCTL_STATIC_CHILDREN(parent), nbr, name, \ + CTLFLAG_DYN|CTLTYPE_INT|CTLFLAG_VNET|access, ptr, val, sysctl_ipf_int_frag, "I", descr) +#define SYSCTL_DYN_IPF_AUTH(parent, nbr, name, access,ptr, val, descr) \ + SYSCTL_ADD_OID(&ipf_clist, SYSCTL_STATIC_CHILDREN(parent), nbr, name, \ + CTLFLAG_DYN|CTLTYPE_INT|CTLFLAG_VNET|access, ptr, val, sysctl_ipf_int_auth, "I", descr) static struct sysctl_ctx_list 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, &ipfmain.ipf_flags, 0, "IPF flags"); -SYSCTL_IPF(_net_inet_ipf, OID_AUTO, ipf_pass, CTLFLAG_RW, &ipfmain.ipf_pass, 0, "default pass/block"); -SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_active, CTLFLAG_RD, &ipfmain.ipf_active, 0, "IPF is active"); +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, - &ipfmain.ipf_tcpidletimeout, 0, "TCP idle timeout in seconds"); + &VNET_NAME(ipfmain.ipf_tcpidletimeout), 0, "TCP idle timeout in seconds"); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_tcphalfclosed, CTLFLAG_RWO, - &ipfmain.ipf_tcphalfclosed, 0, "timeout for half closed TCP sessions"); + &VNET_NAME(ipfmain.ipf_tcphalfclosed), 0, "timeout for half closed TCP sessions"); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_tcpclosewait, CTLFLAG_RWO, - &ipfmain.ipf_tcpclosewait, 0, "timeout for TCP sessions in closewait status"); + &VNET_NAME(ipfmain.ipf_tcpclosewait), 0, "timeout for TCP sessions in closewait status"); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_tcplastack, CTLFLAG_RWO, - &ipfmain.ipf_tcplastack, 0, "timeout for TCP sessions in last ack status"); + &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, - &ipfmain.ipf_tcptimeout, 0, ""); + &VNET_NAME(ipfmain.ipf_tcptimeout), 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_tcpclosed, CTLFLAG_RWO, - &ipfmain.ipf_tcpclosed, 0, ""); + &VNET_NAME(ipfmain.ipf_tcpclosed), 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_udptimeout, CTLFLAG_RWO, - &ipfmain.ipf_udptimeout, 0, "UDP timeout"); + &VNET_NAME(ipfmain.ipf_udptimeout), 0, "UDP timeout"); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_udpacktimeout, CTLFLAG_RWO, - &ipfmain.ipf_udpacktimeout, 0, ""); + &VNET_NAME(ipfmain.ipf_udpacktimeout), 0, ""); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_icmptimeout, CTLFLAG_RWO, - &ipfmain.ipf_icmptimeout, 0, "ICMP timeout"); + &VNET_NAME(ipfmain.ipf_icmptimeout), 0, "ICMP timeout"); SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_running, CTLFLAG_RD, - &ipfmain.ipf_running, 0, "IPF is running"); -SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_chksrc, CTLFLAG_RW, &ipfmain.ipf_chksrc, 0, ""); -SYSCTL_IPF(_net_inet_ipf, OID_AUTO, fr_minttl, CTLFLAG_RW, &ipfmain.ipf_minttl, 0, ""); + &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; + + error = ipfattach(&V_ipfmain); + if (error) { + 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 *defpass, *c, *str; + char *c, *str; int i, j, error; if (ipf_load_all() != 0) return EIO; - if (ipf_create_all(&ipfmain) == NULL) + if (ipf_fbsd_sysctl_create() != 0) { return EIO; + } - if (ipf_fbsd_sysctl_create(&ipfmain) != 0) - return EIO; - - error = ipfattach(&ipfmain); - if (error) - return error; - 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(); - if (FR_ISPASS(ipfmain.ipf_pass)) - defpass = "pass"; - else if (FR_ISBLOCK(ipfmain.ipf_pass)) - defpass = "block"; - else - defpass = "no-match -> block"; - - 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 - ); return 0; } +static void +vnet_ipf_uninit(void) +{ + if (V_ipfmain.ipf_refcnt) + return; + + if (V_ipfmain.ipf_running >= 0) { + if (ipfdetach(&V_ipfmain) != 0) + return; + + 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; - if (ipfmain.ipf_refcnt) - return EBUSY; + ipf_event_dereg(); - if (ipf_fbsd_sysctl_destroy(&ipfmain) != 0) - return EIO; + ipf_fbsd_sysctl_destroy(); error = ipf_pfil_unhook(); if (error != 0) return error; - if (ipfmain.ipf_running >= 0) { - error = ipfdetach(&ipfmain); - if (error != 0) - return error; - - ipf_fbsd_sysctl_destroy(&ipfmain); - ipf_destroy_all(&ipfmain); - ipf_unload_all(); - } else - error = 0; - - ipfmain.ipf_running = -2; - 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_ANY); +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) && (ipfmain.ipf_running > 0)) + if ((oidp->oid_kind & CTLFLAG_OFF) && (V_ipfmain.ipf_running > 0)) error = EBUSY; else error = SYSCTL_IN(req, arg1, sizeof(int)); } return (error); } + +/* + * In the VIMAGE case kern_sysctl.c already adds the vnet base address given + * we set CTLFLAG_VNET to get proper access checks. Have to undo this. + * Then we add the given offset to the specific malloced struct hanging off + * virtualized ipmain struct. + */ +static int +sysctl_ipf_int_nat ( SYSCTL_HANDLER_ARGS ) +{ + + if (arg1) { + ipf_nat_softc_t *nat_softc; + + nat_softc = V_ipfmain.ipf_nat_soft; +#ifdef VIMAGE + arg1 = (void *)((uintptr_t)arg1 - curvnet->vnet_data_base); #endif + arg1 = (void *)((uintptr_t)nat_softc + (uintptr_t)arg1); + } + return (sysctl_ipf_int(oidp, arg1, arg2, req)); +} static int +sysctl_ipf_int_state ( SYSCTL_HANDLER_ARGS ) +{ + + if (arg1) { + ipf_state_softc_t *state_softc; + + state_softc = V_ipfmain.ipf_state_soft; +#ifdef VIMAGE + arg1 = (void *)((uintptr_t)arg1 - curvnet->vnet_data_base); +#endif + arg1 = (void *)((uintptr_t)state_softc + (uintptr_t)arg1); + } + + return (sysctl_ipf_int(oidp, arg1, arg2, req)); +} + +static int +sysctl_ipf_int_auth ( SYSCTL_HANDLER_ARGS ) +{ + + if (arg1) { + ipf_auth_softc_t *auth_softc; + + auth_softc = V_ipfmain.ipf_auth_soft; +#ifdef VIMAGE + arg1 = (void *)((uintptr_t)arg1 - curvnet->vnet_data_base); +#endif + arg1 = (void *)((uintptr_t)auth_softc + (uintptr_t)arg1); + } + + return (sysctl_ipf_int(oidp, arg1, arg2, req)); +} + +static int +sysctl_ipf_int_frag ( SYSCTL_HANDLER_ARGS ) +{ + + if (arg1) { + ipf_frag_softc_t *frag_softc; + + frag_softc = V_ipfmain.ipf_frag_soft; +#ifdef VIMAGE + arg1 = (void *)((uintptr_t)arg1 - curvnet->vnet_data_base); +#endif + arg1 = (void *)((uintptr_t)frag_softc + (uintptr_t)arg1); + } + + return (sysctl_ipf_int(oidp, arg1, arg2, req)); +} +#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(&ipfmain, unit)) + 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(&ipfmain)) + if ((events & (POLLIN | POLLRDNORM)) && ipf_auth_waiting(&V_ipfmain)) revents |= events & (POLLIN | POLLRDNORM); break; case IPL_LOGSYNC : - if ((events & (POLLIN | POLLRDNORM)) && ipf_sync_canread(&ipfmain)) + if ((events & (POLLIN | POLLRDNORM)) && ipf_sync_canread(&V_ipfmain)) revents |= events & (POLLIN | POLLRDNORM); - if ((events & (POLLOUT | POLLWRNORM)) && ipf_sync_canwrite(&ipfmain)) + 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, &ipfmain.ipf_selwait[unit]); + 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; - if (ipfmain.ipf_running < 1) + CURVNET_SET(TD_TO_VNET(curthread)); + if (V_ipfmain.ipf_running < 1) { + CURVNET_RESTORE(); return EIO; + } - if (unit == IPL_LOGSYNC) - return ipf_sync_read(&ipfmain, uio); + if (unit == IPL_LOGSYNC) { + error = ipf_sync_read(&V_ipfmain, uio); + CURVNET_RESTORE(); + return error; + } #ifdef IPFILTER_LOG - return ipf_log_read(&ipfmain, unit, uio); + error = ipf_log_read(&V_ipfmain, unit, uio); #else - return ENXIO; + 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; - if (ipfmain.ipf_running < 1) + CURVNET_SET(TD_TO_VNET(curthread)); + if (V_ipfmain.ipf_running < 1) { + CURVNET_RESTORE(); return EIO; + } - if (GET_MINOR(dev) == IPL_LOGSYNC) - return ipf_sync_write(&ipfmain, uio); + 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_fbsd_sysctl_create(void) { - 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(&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, ""); + SYSCTL_DYN_IPF_NAT(_net_inet_ipf, OID_AUTO, "fr_defnatage", CTLFLAG_RWO, + (void *)offsetof(ipf_nat_softc_t, ipf_nat_defage), 0, ""); + SYSCTL_DYN_IPF_STATE(_net_inet_ipf, OID_AUTO, "fr_statesize", CTLFLAG_RWO, + (void *)offsetof(ipf_state_softc_t, ipf_state_size), 0, ""); + SYSCTL_DYN_IPF_STATE(_net_inet_ipf, OID_AUTO, "fr_statemax", CTLFLAG_RWO, + (void *)offsetof(ipf_state_softc_t, ipf_state_max), 0, ""); + SYSCTL_DYN_IPF_NAT(_net_inet_ipf, OID_AUTO, "ipf_nattable_max", CTLFLAG_RWO, + (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_max), 0, ""); + SYSCTL_DYN_IPF_NAT(_net_inet_ipf, OID_AUTO, "ipf_nattable_sz", CTLFLAG_RWO, + (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_sz), 0, ""); + SYSCTL_DYN_IPF_NAT(_net_inet_ipf, OID_AUTO, "ipf_natrules_sz", CTLFLAG_RWO, + (void *)offsetof(ipf_nat_softc_t, ipf_nat_maprules_sz), 0, ""); + SYSCTL_DYN_IPF_NAT(_net_inet_ipf, OID_AUTO, "ipf_rdrrules_sz", CTLFLAG_RWO, + (void *)offsetof(ipf_nat_softc_t, ipf_nat_rdrrules_sz), 0, ""); + SYSCTL_DYN_IPF_NAT(_net_inet_ipf, OID_AUTO, "ipf_hostmap_sz", CTLFLAG_RWO, + (void *)offsetof(ipf_nat_softc_t, ipf_nat_hostmap_sz), 0, ""); + SYSCTL_DYN_IPF_AUTH(_net_inet_ipf, OID_AUTO, "fr_authsize", CTLFLAG_RWO, + (void *)offsetof(ipf_auth_softc_t, ipf_auth_size), 0, ""); + SYSCTL_DYN_IPF_AUTH(_net_inet_ipf, OID_AUTO, "fr_authused", CTLFLAG_RD, + (void *)offsetof(ipf_auth_softc_t, ipf_auth_used), 0, ""); + SYSCTL_DYN_IPF_AUTH(_net_inet_ipf, OID_AUTO, "fr_defaultauthage", CTLFLAG_RW, + (void *)offsetof(ipf_auth_softc_t, ipf_auth_defaultage), 0, ""); + SYSCTL_DYN_IPF_FRAG(_net_inet_ipf, OID_AUTO, "fr_ipfrttl", CTLFLAG_RW, + (void *)offsetof(ipf_frag_softc_t, ipfr_ttl), 0, ""); return 0; } static int -ipf_fbsd_sysctl_destroy(main_softc) - ipf_main_softc_t *main_softc; +ipf_fbsd_sysctl_destroy(void) { if (sysctl_ctx_free(&ipf_clist)) { printf("sysctl_ctx_free failed"); return(ENOTEMPTY); } return 0; }