diff options
-rw-r--r-- | sys/contrib/ipfilter/netinet/ip_fil_freebsd.c | 1692 | ||||
-rw-r--r-- | sys/contrib/ipfilter/netinet/ip_htable.c | 455 | ||||
-rw-r--r-- | sys/contrib/ipfilter/netinet/ip_htable.h | 71 | ||||
-rw-r--r-- | sys/contrib/ipfilter/netinet/ip_ipsec_pxy.c | 233 | ||||
-rw-r--r-- | sys/contrib/ipfilter/netinet/ip_irc_pxy.c | 435 | ||||
-rw-r--r-- | sys/contrib/ipfilter/netinet/ip_lookup.c | 530 | ||||
-rw-r--r-- | sys/contrib/ipfilter/netinet/ip_lookup.h | 65 | ||||
-rw-r--r-- | sys/contrib/ipfilter/netinet/ip_netbios_pxy.c | 53 | ||||
-rw-r--r-- | sys/contrib/ipfilter/netinet/ip_pool.c | 786 | ||||
-rw-r--r-- | sys/contrib/ipfilter/netinet/ip_pool.h | 87 | ||||
-rw-r--r-- | sys/contrib/ipfilter/netinet/ip_pptp_pxy.c | 527 | ||||
-rw-r--r-- | sys/contrib/ipfilter/netinet/ip_rpcb_pxy.c | 1460 | ||||
-rw-r--r-- | sys/contrib/ipfilter/netinet/ip_rules.c | 229 | ||||
-rw-r--r-- | sys/contrib/ipfilter/netinet/ip_rules.h | 16 | ||||
-rw-r--r-- | sys/contrib/ipfilter/netinet/ip_scan.c | 594 | ||||
-rw-r--r-- | sys/contrib/ipfilter/netinet/ip_scan.h | 108 | ||||
-rw-r--r-- | sys/contrib/ipfilter/netinet/ip_sync.c | 1001 | ||||
-rw-r--r-- | sys/contrib/ipfilter/netinet/ip_sync.h | 117 |
18 files changed, 8348 insertions, 111 deletions
diff --git a/sys/contrib/ipfilter/netinet/ip_fil_freebsd.c b/sys/contrib/ipfilter/netinet/ip_fil_freebsd.c new file mode 100644 index 0000000..4ee0d3b --- /dev/null +++ b/sys/contrib/ipfilter/netinet/ip_fil_freebsd.c @@ -0,0 +1,1692 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 1993-2003 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + */ +#if !defined(lint) +static const char sccsid[] = "@(#)ip_fil.c 2.41 6/5/96 (C) 1993-2000 Darren Reed"; +static const char rcsid[] = "@(#)Id: ip_fil_freebsd.c,v 2.53.2.25 2005/02/01 03:15:56 darrenr Exp"; +#endif + +#if defined(KERNEL) || defined(_KERNEL) +# undef KERNEL +# undef _KERNEL +# define KERNEL 1 +# define _KERNEL 1 +#endif +#if defined(__FreeBSD_version) && (__FreeBSD_version >= 400000) && \ + !defined(KLD_MODULE) && !defined(IPFILTER_LKM) +# include "opt_inet6.h" +#endif +#if defined(__FreeBSD_version) && (__FreeBSD_version >= 440000) && \ + !defined(KLD_MODULE) && !defined(IPFILTER_LKM) +# include "opt_random_ip_id.h" +#endif +#include <sys/param.h> +#if defined(__FreeBSD__) && !defined(__FreeBSD_version) +# if defined(IPFILTER_LKM) +# ifndef __FreeBSD_cc_version +# include <osreldate.h> +# else +# if __FreeBSD_cc_version < 430000 +# include <osreldate.h> +# endif +# endif +# endif +#endif +#include <sys/errno.h> +#include <sys/types.h> +#include <sys/file.h> +#if __FreeBSD_version >= 220000 +# include <sys/fcntl.h> +# include <sys/filio.h> +#else +# include <sys/ioctl.h> +#endif +#include <sys/time.h> +#include <sys/systm.h> +#if (__FreeBSD_version >= 300000) +# include <sys/dirent.h> +#else +# include <sys/dir.h> +#endif +#if !defined(__hpux) +# include <sys/mbuf.h> +#endif +#include <sys/protosw.h> +#include <sys/socket.h> + +#include <net/if.h> +#if __FreeBSD_version >= 300000 +# include <net/if_var.h> +# if !defined(IPFILTER_LKM) +# include "opt_ipfilter.h" +# endif +#endif +#include <net/route.h> +#include <netinet/in.h> +#include <netinet/in_var.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/ip_var.h> +#include <netinet/tcp.h> +#if defined(__osf__) +# include <netinet/tcp_timer.h> +#endif +#include <netinet/udp.h> +#include <netinet/tcpip.h> +#include <netinet/ip_icmp.h> +#ifndef _KERNEL +# include "netinet/ipf.h" +#endif +#include "netinet/ip_compat.h" +#ifdef USE_INET6 +# include <netinet/icmp6.h> +#endif +#include "netinet/ip_fil.h" +#include "netinet/ip_nat.h" +#include "netinet/ip_frag.h" +#include "netinet/ip_state.h" +#include "netinet/ip_proxy.h" +#include "netinet/ip_auth.h" +#ifdef IPFILTER_SYNC +#include "netinet/ip_sync.h" +#endif +#ifdef IPFILTER_SCAN +#include "netinet/ip_scan.h" +#endif +#include "netinet/ip_pool.h" +#if defined(__FreeBSD_version) && (__FreeBSD_version >= 300000) +# include <sys/malloc.h> +#endif +#include <sys/kernel.h> +#ifdef CSUM_DATA_VALID +#include <machine/in_cksum.h> +#endif +extern int ip_optcopy __P((struct ip *, struct ip *)); + +#if (__FreeBSD_version > 460000) +extern int path_mtu_discovery; +#endif + +# ifdef IPFILTER_M_IPFILTER +MALLOC_DEFINE(M_IPFILTER, "IP Filter", "IP Filter packet filter data structures"); +# endif + + +#if !defined(__osf__) +extern struct protosw inetsw[]; +#endif + +static int (*fr_savep) __P((ip_t *, int, void *, int, struct mbuf **)); +static int fr_send_ip __P((fr_info_t *, mb_t *, mb_t **)); +# ifdef USE_MUTEXES +ipfmutex_t ipl_mutex, ipf_authmx, ipf_rw, ipf_stinsert; +ipfmutex_t ipf_nat_new, ipf_natio, ipf_timeoutlock; +ipfrwlock_t ipf_mutex, ipf_global, ipf_ipidfrag; +ipfrwlock_t ipf_frag, ipf_state, ipf_nat, ipf_natfrag, ipf_auth; +# endif +int ipf_locks_done = 0; + +#if (__FreeBSD_version >= 300000) +struct callout_handle fr_slowtimer_ch; +#endif + +#if (__FreeBSD_version >= 500011) +# include <sys/conf.h> +# if defined(NETBSD_PF) +# include <net/pfil.h> +# include <netinet/ipprotosw.h> +/* + * We provide the fr_checkp name just to minimize changes later. + */ +int (*fr_checkp) __P((ip_t *ip, int hlen, void *ifp, int out, mb_t **mp)); +# endif /* NETBSD_PF */ +#endif /* __FreeBSD_version >= 500011 */ + + +#if (__FreeBSD_version >= 501108) && defined(_KERNEL) + +static int +fr_check_wrapper(void *arg, struct mbuf **mp, struct ifnet *ifp, int dir) +{ + struct ip *ip = mtod(*mp, struct ip *); + return fr_check(ip, ip->ip_hl << 2, ifp, (dir == PFIL_OUT), mp); +} + +# ifdef USE_INET6 +# include <netinet/ip6.h> + +static int +fr_check_wrapper6(void *arg, struct mbuf **mp, struct ifnet *ifp, int dir) +{ + return (fr_check(mtod(*mp, struct ip *), sizeof(struct ip6_hdr), + ifp, (dir == PFIL_OUT), mp)); +} +# endif +#endif /* __FreeBSD_version >= 501108 */ +#if defined(IPFILTER_LKM) +int iplidentify(s) +char *s; +{ + if (strcmp(s, "ipl") == 0) + return 1; + return 0; +} +#endif /* IPFILTER_LKM */ + + +int iplattach() +{ +#ifdef USE_SPL + int s; +#endif +#if defined(NETBSD_PF) && (__FreeBSD_version >= 500011) + int error = 0; +# if __FreeBSD_version >= 501108 + struct pfil_head *ph_inet; +# ifdef USE_INET6 + struct pfil_head *ph_inet6; +# endif +# endif +#endif + + SPL_NET(s); + if (fr_running > 0) { + SPL_X(s); + return EBUSY; + } + + MUTEX_INIT(&ipf_rw, "ipf rw mutex"); + RWLOCK_INIT(&ipf_global, "ipf filter load/unload mutex"); + MUTEX_INIT(&ipf_timeoutlock, "ipf timeout queue mutex"); + RWLOCK_INIT(&ipf_mutex, "ipf filter rwlock"); + RWLOCK_INIT(&ipf_ipidfrag, "ipf IP NAT-Frag rwlock"); + ipf_locks_done = 1; + + if (fr_initialise() < 0) { + SPL_X(s); + return EIO; + } + + +# ifdef NETBSD_PF +# if __FreeBSD_version >= 500011 +# if __FreeBSD_version >= 501108 + ph_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET); +# ifdef USE_INET6 + ph_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6); +# endif + if (ph_inet == NULL +# ifdef USE_INET6 + && ph_inet6 == NULL +# endif + ) + return ENODEV; + + if (ph_inet != NULL) + error = pfil_add_hook((void *)fr_check_wrapper, NULL, + PFIL_IN|PFIL_OUT, ph_inet); + else + error = 0; +# else + error = pfil_add_hook((void *)fr_check, PFIL_IN|PFIL_OUT, + &inetsw[ip_protox[IPPROTO_IP]].pr_pfh); +# endif + if (error) { +# ifdef USE_INET6 + goto pfil_error; +# else + fr_deinitialise(); + SPL_X(s); + return error; +# endif + } +# else + pfil_add_hook((void *)fr_check, PFIL_IN|PFIL_OUT); +# endif +# ifdef USE_INET6 +# if __FreeBSD_version >= 501108 + if (ph_inet6 != NULL) + error = pfil_add_hook((void *)fr_check_wrapper6, NULL, + PFIL_IN|PFIL_OUT, ph_inet6); + else + error = 0; + if (error) { + pfil_remove_hook((void *)fr_check_wrapper6, NULL, + PFIL_IN|PFIL_OUT, ph_inet6); +# else + error = pfil_add_hook((void *)fr_check, PFIL_IN|PFIL_OUT, + &inet6sw[ip6_protox[IPPROTO_IPV6]].pr_pfh); + if (error) { + pfil_remove_hook((void *)fr_check, PFIL_IN|PFIL_OUT, + &inetsw[ip_protox[IPPROTO_IP]].pr_pfh); +# endif +pfil_error: + fr_deinitialise(); + SPL_X(s); + return error; + } +# endif +# endif + if (fr_checkp != fr_check) { + fr_savep = fr_checkp; + fr_checkp = fr_check; + } + + bzero((char *)frcache, sizeof(frcache)); + fr_running = 1; + + if (fr_control_forwarding & 1) + ipforwarding = 1; + + SPL_X(s); +#if (__FreeBSD_version >= 300000) + fr_slowtimer_ch = timeout(fr_slowtimer, NULL, + (hz / IPF_HZ_DIVIDE) * IPF_HZ_MULT); +#else + timeout(fr_slowtimer, NULL, (hz / IPF_HZ_DIVIDE) * IPF_HZ_MULT); +#endif + return 0; +} + + +/* + * Disable the filter by removing the hooks from the IP input/output + * stream. + */ +int ipldetach() +{ +#ifdef USE_SPL + int s; +#endif +#if defined(NETBSD_PF) && (__FreeBSD_version >= 500011) + int error = 0; +# if __FreeBSD_version >= 501108 + struct pfil_head *ph_inet; +# ifdef USE_INET6 + struct pfil_head *ph_inet6; +# endif +# endif +#endif + + if (fr_control_forwarding & 2) + ipforwarding = 0; + + SPL_NET(s); + +#if (__FreeBSD_version >= 300000) + if (fr_slowtimer_ch.callout != NULL) + untimeout(fr_slowtimer, NULL, fr_slowtimer_ch); + bzero(&fr_slowtimer_ch, sizeof(fr_slowtimer_ch)); +#else + untimeout(fr_slowtimer, NULL); +#endif /* FreeBSD */ + +#ifndef NETBSD_PF + if (fr_checkp != NULL) + fr_checkp = fr_savep; + fr_savep = NULL; +#endif + +#ifdef NETBSD_PF +# if (__FreeBSD_version >= 500011) +# if (__FreeBSD_version >= 501108) + ph_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET); + if (ph_inet != NULL) + error = pfil_remove_hook((void *)fr_check_wrapper, NULL, + PFIL_IN|PFIL_OUT, ph_inet); + else + error = 0; +# else + error = pfil_remove_hook((void *)fr_check, PFIL_IN|PFIL_OUT, + &inetsw[ip_protox[IPPROTO_IP]].pr_pfh); +# endif + if (error) { + SPL_X(s); + return error; + } +# else + pfil_remove_hook((void *)fr_check, PFIL_IN|PFIL_OUT); +# endif +# ifdef USE_INET6 +# if (__FreeBSD_version >= 501108) + ph_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6); + if (ph_inet6 != NULL) + error = pfil_remove_hook((void *)fr_check_wrapper6, NULL, + PFIL_IN|PFIL_OUT, ph_inet6); + else + error = 0; +# else + error = pfil_remove_hook((void *)fr_check, PFIL_IN|PFIL_OUT, + &inet6sw[ip6_protox[IPPROTO_IPV6]].pr_pfh); +# endif + if (error) { + SPL_X(s); + return error; + } +# endif +#endif + fr_deinitialise(); + + fr_running = -2; + + (void) frflush(IPL_LOGIPF, 0, FR_INQUE|FR_OUTQUE|FR_INACTIVE); + (void) frflush(IPL_LOGIPF, 0, FR_INQUE|FR_OUTQUE); + + if (ipf_locks_done == 1) { + MUTEX_DESTROY(&ipf_timeoutlock); + MUTEX_DESTROY(&ipf_rw); + RW_DESTROY(&ipf_mutex); + RW_DESTROY(&ipf_ipidfrag); + RW_DESTROY(&ipf_global); + ipf_locks_done = 0; + } + + SPL_X(s); + + return 0; +} + + +/* + * Filter ioctl interface. + */ +int iplioctl(dev, cmd, data, mode +# if defined(_KERNEL) && ((BSD >= 199506) || (__FreeBSD_version >= 220000)) +, p) +# if (__FreeBSD_version >= 500024) +struct thread *p; +# else +struct proc *p; +# endif /* __FreeBSD_version >= 500024 */ +# else +) +# endif +#if defined(_KERNEL) && (__FreeBSD_version >= 502116) +struct cdev *dev; +#else +dev_t dev; +#endif +ioctlcmd_t cmd; +caddr_t data; +int mode; +{ +#ifdef USE_SPL + int s; +#endif + int error = 0, unit = 0, tmp; + friostat_t fio; + +#if (BSD >= 199306) && defined(_KERNEL) + if ((securelevel >= 2) && (mode & FWRITE)) + return EPERM; +#endif + + unit = GET_MINOR(dev); + if ((IPL_LOGMAX < unit) || (unit < 0)) + return ENXIO; + + if (fr_running <= 0) { + if (unit != IPL_LOGIPF) + return EIO; + if (cmd != SIOCIPFGETNEXT && cmd != SIOCIPFGET && + cmd != SIOCIPFSET && cmd != SIOCFRENB && + cmd != SIOCGETFS && cmd != SIOCGETFF) + return EIO; + } + + SPL_NET(s); + + error = fr_ioctlswitch(unit, data, cmd, mode); + if (error != -1) { + SPL_X(s); + return error; + } + error = 0; + + switch (cmd) + { + case FIONREAD : +#ifdef IPFILTER_LOG + BCOPYOUT(&iplused[IPL_LOGIPF], (caddr_t)data, + sizeof(iplused[IPL_LOGIPF])); +#endif + break; + case SIOCFRENB : + if (!(mode & FWRITE)) + error = EPERM; + else { + BCOPYIN(data, &tmp, sizeof(tmp)); + if (tmp) { + if (fr_running > 0) + error = 0; + else + error = iplattach(); + if (error == 0) + fr_running = 1; + else + (void) ipldetach(); + } else { + error = ipldetach(); + if (error == 0) + fr_running = -1; + } + } + break; + case SIOCIPFSET : + if (!(mode & FWRITE)) { + error = EPERM; + break; + } + case SIOCIPFGETNEXT : + case SIOCIPFGET : + error = fr_ipftune(cmd, data); + break; + case SIOCSETFF : + if (!(mode & FWRITE)) + error = EPERM; + else + BCOPYIN(data, &fr_flags, sizeof(fr_flags)); + break; + case SIOCGETFF : + BCOPYOUT(&fr_flags, data, sizeof(fr_flags)); + break; + case SIOCFUNCL : + error = fr_resolvefunc(data); + break; + case SIOCINAFR : + case SIOCRMAFR : + case SIOCADAFR : + case SIOCZRLST : + if (!(mode & FWRITE)) + error = EPERM; + else + error = frrequest(unit, cmd, data, fr_active, 1); + break; + case SIOCINIFR : + case SIOCRMIFR : + case SIOCADIFR : + if (!(mode & FWRITE)) + error = EPERM; + else + error = frrequest(unit, cmd, data, 1 - fr_active, 1); + break; + case SIOCSWAPA : + if (!(mode & FWRITE)) + error = EPERM; + else { + bzero((char *)frcache, sizeof(frcache[0]) * 2); + *(u_int *)data = fr_active; + fr_active = 1 - fr_active; + } + break; + case SIOCGETFS : + fr_getstat(&fio); + error = fr_outobj(data, &fio, IPFOBJ_IPFSTAT); + break; + case SIOCFRZST : + if (!(mode & FWRITE)) + error = EPERM; + else + error = fr_zerostats(data); + break; + case SIOCIPFFL : + if (!(mode & FWRITE)) + error = EPERM; + else { + BCOPYIN(data, &tmp, sizeof(tmp)); + tmp = frflush(unit, 4, tmp); + BCOPYOUT(&tmp, data, sizeof(tmp)); + } + break; +#ifdef USE_INET6 + case SIOCIPFL6 : + if (!(mode & FWRITE)) + error = EPERM; + else { + BCOPYIN(data, &tmp, sizeof(tmp)); + tmp = frflush(unit, 6, tmp); + BCOPYOUT(&tmp, data, sizeof(tmp)); + } + break; +#endif + case SIOCSTLCK : + BCOPYIN(data, &tmp, sizeof(tmp)); + fr_state_lock = tmp; + fr_nat_lock = tmp; + fr_frag_lock = tmp; + fr_auth_lock = tmp; + break; +#ifdef IPFILTER_LOG + case SIOCIPFFB : + if (!(mode & FWRITE)) + error = EPERM; + else + *(int *)data = ipflog_clear(unit); + break; +#endif /* IPFILTER_LOG */ + case SIOCGFRST : + error = fr_outobj(data, fr_fragstats(), IPFOBJ_FRAGSTAT); + break; + case SIOCFRSYN : + if (!(mode & FWRITE)) + error = EPERM; + else { + frsync(NULL); + } + break; + default : + error = EINVAL; + break; + } + SPL_X(s); + return error; +} + + +#if 0 +void fr_forgetifp(ifp) +void *ifp; +{ + register frentry_t *f; + + WRITE_ENTER(&ipf_mutex); + for (f = ipacct[0][fr_active]; (f != NULL); f = f->fr_next) + if (f->fr_ifa == ifp) + f->fr_ifa = (void *)-1; + for (f = ipacct[1][fr_active]; (f != NULL); f = f->fr_next) + if (f->fr_ifa == ifp) + f->fr_ifa = (void *)-1; + for (f = ipfilter[0][fr_active]; (f != NULL); f = f->fr_next) + if (f->fr_ifa == ifp) + f->fr_ifa = (void *)-1; + for (f = ipfilter[1][fr_active]; (f != NULL); f = f->fr_next) + if (f->fr_ifa == ifp) + f->fr_ifa = (void *)-1; +#ifdef USE_INET6 + for (f = ipacct6[0][fr_active]; (f != NULL); f = f->fr_next) + if (f->fr_ifa == ifp) + f->fr_ifa = (void *)-1; + for (f = ipacct6[1][fr_active]; (f != NULL); f = f->fr_next) + if (f->fr_ifa == ifp) + f->fr_ifa = (void *)-1; + for (f = ipfilter6[0][fr_active]; (f != NULL); f = f->fr_next) + if (f->fr_ifa == ifp) + f->fr_ifa = (void *)-1; + for (f = ipfilter6[1][fr_active]; (f != NULL); f = f->fr_next) + if (f->fr_ifa == ifp) + f->fr_ifa = (void *)-1; +#endif + RWLOCK_EXIT(&ipf_mutex); + fr_natsync(ifp); +} +#endif + + +/* + * routines below for saving IP headers to buffer + */ +int iplopen(dev, flags +#if ((BSD >= 199506) || (__FreeBSD_version >= 220000)) && defined(_KERNEL) +, devtype, p) +int devtype; +# if (__FreeBSD_version >= 500024) +struct thread *p; +# else +struct proc *p; +# endif /* __FreeBSD_version >= 500024 */ +#else +) +#endif +#if defined(_KERNEL) && (__FreeBSD_version >= 502116) +struct cdev *dev; +#else +dev_t dev; +#endif +int flags; +{ + u_int min = GET_MINOR(dev); + + if (IPL_LOGMAX < min) + min = ENXIO; + else + min = 0; + return min; +} + + +int iplclose(dev, flags +#if ((BSD >= 199506) || (__FreeBSD_version >= 220000)) && defined(_KERNEL) +, devtype, p) +int devtype; +# if (__FreeBSD_version >= 500024) +struct thread *p; +# else +struct proc *p; +# endif /* __FreeBSD_version >= 500024 */ +#else +) +#endif +#if defined(_KERNEL) && (__FreeBSD_version >= 502116) +struct cdev *dev; +#else +dev_t dev; +#endif +int flags; +{ + u_int min = GET_MINOR(dev); + + if (IPL_LOGMAX < min) + min = ENXIO; + else + min = 0; + return min; +} + +/* + * iplread/ipllog + * both of these must operate with at least splnet() lest they be + * called during packet processing and cause an inconsistancy to appear in + * the filter lists. + */ +#if (BSD >= 199306) +int iplread(dev, uio, ioflag) +int ioflag; +#else +int iplread(dev, uio) +#endif +#if defined(_KERNEL) && (__FreeBSD_version >= 502116) +struct cdev *dev; +#else +dev_t dev; +#endif +register struct uio *uio; +{ + +# ifdef IPFILTER_SYNC + if (GET_MINOR(dev) == IPL_LOGSYNC) + return ipfsync_read(uio); +# endif + +#ifdef IPFILTER_LOG + return ipflog_read(GET_MINOR(dev), uio); +#else + return ENXIO; +#endif +} + + +/* + * iplwrite + * both of these must operate with at least splnet() lest they be + * called during packet processing and cause an inconsistancy to appear in + * the filter lists. + */ +#if (BSD >= 199306) +int iplwrite(dev, uio, ioflag) +int ioflag; +#else +int iplwrite(dev, uio) +#endif +#if defined(_KERNEL) && (__FreeBSD_version >= 502116) +struct cdev *dev; +#else +dev_t dev; +#endif +register struct uio *uio; +{ + +#ifdef IPFILTER_SYNC + if (GET_MINOR(dev) == IPL_LOGSYNC) + return ipfsync_write(uio); +#endif + return ENXIO; +} + + +/* + * fr_send_reset - this could conceivably be a call to tcp_respond(), but that + * requires a large amount of setting up and isn't any more efficient. + */ +int fr_send_reset(fin) +fr_info_t *fin; +{ + struct tcphdr *tcp, *tcp2; + int tlen = 0, hlen; + struct mbuf *m; +#ifdef USE_INET6 + ip6_t *ip6; +#endif + ip_t *ip; + + tcp = fin->fin_dp; + if (tcp->th_flags & TH_RST) + return -1; /* feedback loop */ + +#ifndef IPFILTER_CKSUM + if (fr_checkl4sum(fin) == -1) + return -1; +#endif + + tlen = fin->fin_dlen - (TCP_OFF(tcp) << 2) + + ((tcp->th_flags & TH_SYN) ? 1 : 0) + + ((tcp->th_flags & TH_FIN) ? 1 : 0); + +#ifdef USE_INET6 + hlen = (fin->fin_v == 6) ? sizeof(ip6_t) : sizeof(ip_t); +#else + hlen = sizeof(ip_t); +#endif +#ifdef MGETHDR + MGETHDR(m, M_DONTWAIT, MT_HEADER); +#else + MGET(m, M_DONTWAIT, MT_HEADER); +#endif + if (m == NULL) + return -1; + if (sizeof(*tcp2) + hlen > MLEN) { + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + FREE_MB_T(m); + return -1; + } + } + + m->m_len = sizeof(*tcp2) + hlen; +#if (BSD >= 199103) + m->m_data += max_linkhdr; + m->m_pkthdr.len = m->m_len; + m->m_pkthdr.rcvif = (struct ifnet *)0; +#endif + ip = mtod(m, struct ip *); + bzero((char *)ip, hlen); +#ifdef USE_INET6 + ip6 = (ip6_t *)ip; +#endif + tcp2 = (struct tcphdr *)((char *)ip + hlen); + tcp2->th_sport = tcp->th_dport; + tcp2->th_dport = tcp->th_sport; + + if (tcp->th_flags & TH_ACK) { + tcp2->th_seq = tcp->th_ack; + tcp2->th_flags = TH_RST; + tcp2->th_ack = 0; + } else { + tcp2->th_seq = 0; + tcp2->th_ack = ntohl(tcp->th_seq); + tcp2->th_ack += tlen; + tcp2->th_ack = htonl(tcp2->th_ack); + tcp2->th_flags = TH_RST|TH_ACK; + } + TCP_X2_A(tcp2, 0); + TCP_OFF_A(tcp2, sizeof(*tcp2) >> 2); + tcp2->th_win = tcp->th_win; + tcp2->th_sum = 0; + tcp2->th_urp = 0; + +#ifdef USE_INET6 + if (fin->fin_v == 6) { + ip6->ip6_flow = ((ip6_t *)fin->fin_ip)->ip6_flow; + ip6->ip6_plen = htons(sizeof(struct tcphdr)); + ip6->ip6_nxt = IPPROTO_TCP; + ip6->ip6_hlim = 0; + ip6->ip6_src = fin->fin_dst6; + ip6->ip6_dst = fin->fin_src6; + tcp2->th_sum = in6_cksum(m, IPPROTO_TCP, + sizeof(*ip6), sizeof(*tcp2)); + return fr_send_ip(fin, m, &m); + } +#endif + ip->ip_p = IPPROTO_TCP; + ip->ip_len = htons(sizeof(struct tcphdr)); + ip->ip_src.s_addr = fin->fin_daddr; + ip->ip_dst.s_addr = fin->fin_saddr; + tcp2->th_sum = in_cksum(m, hlen + sizeof(*tcp2)); + ip->ip_len = hlen + sizeof(*tcp2); + return fr_send_ip(fin, m, &m); +} + + +static int fr_send_ip(fin, m, mpp) +fr_info_t *fin; +mb_t *m, **mpp; +{ + fr_info_t fnew; + ip_t *ip, *oip; + int hlen; + + ip = mtod(m, ip_t *); + bzero((char *)&fnew, sizeof(fnew)); + + IP_V_A(ip, fin->fin_v); + switch (fin->fin_v) + { + case 4 : + fnew.fin_v = 4; + oip = fin->fin_ip; + IP_HL_A(ip, sizeof(*oip) >> 2); + ip->ip_tos = oip->ip_tos; + ip->ip_id = fin->fin_ip->ip_id; +#if (__FreeBSD_version > 460000) + ip->ip_off = path_mtu_discovery ? IP_DF : 0; +#else + ip->ip_off = 0; +#endif + ip->ip_ttl = ip_defttl; + ip->ip_sum = 0; + hlen = sizeof(*oip); + break; +#ifdef USE_INET6 + case 6 : + { + ip6_t *ip6 = (ip6_t *)ip; + + ip6->ip6_vfc = 0x60; + ip6->ip6_hlim = IPDEFTTL; + + fnew.fin_v = 6; + hlen = sizeof(*ip6); + break; + } +#endif + default : + return EINVAL; + } +#ifdef IPSEC + m->m_pkthdr.rcvif = NULL; +#endif + + fnew.fin_ifp = fin->fin_ifp; + fnew.fin_flx = FI_NOCKSUM; + fnew.fin_m = m; + fnew.fin_ip = ip; + fnew.fin_mp = mpp; + fnew.fin_hlen = hlen; + fnew.fin_dp = (char *)ip + hlen; + (void) fr_makefrip(hlen, ip, &fnew); + + return fr_fastroute(m, mpp, &fnew, NULL); +} + + +int fr_send_icmp_err(type, fin, dst) +int type; +fr_info_t *fin; +int dst; +{ + int err, hlen, xtra, iclen, ohlen, avail, code; + struct in_addr dst4; + struct icmp *icmp; + struct mbuf *m; + void *ifp; +#ifdef USE_INET6 + ip6_t *ip6; + struct in6_addr dst6; +#endif + ip_t *ip, *ip2; + + if ((type < 0) || (type > ICMP_MAXTYPE)) + return -1; + + code = fin->fin_icode; +#ifdef USE_INET6 + if ((code < 0) || (code > sizeof(icmptoicmp6unreach)/sizeof(int))) + return -1; +#endif + +#ifndef IPFILTER_CKSUM + if (fr_checkl4sum(fin) == -1) + return -1; +#endif +#ifdef MGETHDR + MGETHDR(m, M_DONTWAIT, MT_HEADER); +#else + MGET(m, M_DONTWAIT, MT_HEADER); +#endif + if (m == NULL) + return -1; + avail = MHLEN; + + xtra = 0; + hlen = 0; + ohlen = 0; + ifp = fin->fin_ifp; + if (fin->fin_v == 4) { + if ((fin->fin_p == IPPROTO_ICMP) && + !(fin->fin_flx & FI_SHORT)) + switch (ntohs(fin->fin_data[0]) >> 8) + { + case ICMP_ECHO : + case ICMP_TSTAMP : + case ICMP_IREQ : + case ICMP_MASKREQ : + break; + default : + FREE_MB_T(m); + return 0; + } + + if (dst == 0) { + if (fr_ifpaddr(4, FRI_NORMAL, ifp, + &dst4, NULL) == -1) { + FREE_MB_T(m); + return -1; + } + } else + dst4.s_addr = fin->fin_daddr; + + hlen = sizeof(ip_t); + ohlen = fin->fin_hlen; + if (fin->fin_hlen < fin->fin_plen) + xtra = MIN(fin->fin_dlen, 8); + else + xtra = 0; + } + +#ifdef USE_INET6 + else if (fin->fin_v == 6) { + hlen = sizeof(ip6_t); + ohlen = sizeof(ip6_t); + type = icmptoicmp6types[type]; + if (type == ICMP6_DST_UNREACH) + code = icmptoicmp6unreach[code]; + + if (hlen + sizeof(*icmp) + max_linkhdr + + fin->fin_plen > avail) { + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + FREE_MB_T(m); + return -1; + } + avail = MCLBYTES; + } + xtra = MIN(fin->fin_plen, + avail - hlen - sizeof(*icmp) - max_linkhdr); + if (dst == 0) { + if (fr_ifpaddr(6, FRI_NORMAL, ifp, + (struct in_addr *)&dst6, NULL) == -1) { + FREE_MB_T(m); + return -1; + } + } else + dst6 = fin->fin_dst6; + } +#endif + else { + FREE_MB_T(m); + return -1; + } + + iclen = hlen + sizeof(*icmp); + avail -= (max_linkhdr + iclen); + if (avail < 0) { + FREE_MB_T(m); + return -1; + } + if (xtra > avail) + xtra = avail; + iclen += xtra; + m->m_data += max_linkhdr; + m->m_pkthdr.rcvif = (struct ifnet *)0; + m->m_pkthdr.len = iclen; + m->m_len = iclen; + ip = mtod(m, ip_t *); + icmp = (struct icmp *)((char *)ip + hlen); + ip2 = (ip_t *)&icmp->icmp_ip; + + icmp->icmp_type = type; + icmp->icmp_code = fin->fin_icode; + icmp->icmp_cksum = 0; +#ifdef icmp_nextmtu + if (type == ICMP_UNREACH && + fin->fin_icode == ICMP_UNREACH_NEEDFRAG && ifp) + icmp->icmp_nextmtu = htons(((struct ifnet *)ifp)->if_mtu); +#endif + + bcopy((char *)fin->fin_ip, (char *)ip2, ohlen); + +#ifdef USE_INET6 + ip6 = (ip6_t *)ip; + if (fin->fin_v == 6) { + ip6->ip6_flow = ((ip6_t *)fin->fin_ip)->ip6_flow; + ip6->ip6_plen = htons(iclen - hlen); + ip6->ip6_nxt = IPPROTO_ICMPV6; + ip6->ip6_hlim = 0; + ip6->ip6_src = dst6; + ip6->ip6_dst = fin->fin_src6; + if (xtra > 0) + bcopy((char *)fin->fin_ip + ohlen, + (char *)&icmp->icmp_ip + ohlen, xtra); + icmp->icmp_cksum = in6_cksum(m, IPPROTO_ICMPV6, + sizeof(*ip6), iclen - hlen); + } else +#endif + { + ip2->ip_len = htons(ip2->ip_len); + ip2->ip_off = htons(ip2->ip_off); + ip->ip_p = IPPROTO_ICMP; + ip->ip_src.s_addr = dst4.s_addr; + ip->ip_dst.s_addr = fin->fin_saddr; + + if (xtra > 0) + bcopy((char *)fin->fin_ip + ohlen, + (char *)&icmp->icmp_ip + ohlen, xtra); + icmp->icmp_cksum = ipf_cksum((u_short *)icmp, + sizeof(*icmp) + 8); + ip->ip_len = iclen; + ip->ip_p = IPPROTO_ICMP; + } + err = fr_send_ip(fin, m, &m); + return err; +} + + +#if !defined(IPFILTER_LKM) && (__FreeBSD_version < 300000) +# if (BSD < 199306) +int iplinit __P((void)); + +int +# else +void iplinit __P((void)); + +void +# endif +iplinit() +{ + if (iplattach() != 0) + printf("IP Filter failed to attach\n"); + ip_init(); +} +#endif /* __FreeBSD_version < 300000 */ + + +int fr_fastroute(m0, mpp, fin, fdp) +mb_t *m0, **mpp; +fr_info_t *fin; +frdest_t *fdp; +{ + register struct ip *ip, *mhip; + register struct mbuf *m = m0; + register struct route *ro; + int len, off, error = 0, hlen, code; + struct ifnet *ifp, *sifp; + struct sockaddr_in *dst; + struct route iproute; + u_short ip_off; + frentry_t *fr; + +#ifdef M_WRITABLE + /* + * HOT FIX/KLUDGE: + * + * If the mbuf we're about to send is not writable (because of + * a cluster reference, for example) we'll need to make a copy + * of it since this routine modifies the contents. + * + * If you have non-crappy network hardware that can transmit data + * from the mbuf, rather than making a copy, this is gonna be a + * problem. + */ + if (M_WRITABLE(m) == 0) { + if ((m0 = m_dup(m, M_DONTWAIT)) != 0) { + FREE_MB_T(m); + m = m0; + *mpp = m; + } else { + error = ENOBUFS; + FREE_MB_T(m); + *mpp = NULL; + fr_frouteok[1]++; + } + } +#endif + +#ifdef USE_INET6 + if (fin->fin_v == 6) { + /* + * currently "to <if>" and "to <if>:ip#" are not supported + * for IPv6 + */ +#if (__FreeBSD_version >= 490000) + return ip6_output(m0, NULL, NULL, 0, NULL, NULL, NULL); +#else + return ip6_output(m0, NULL, NULL, 0, NULL, NULL); +#endif + } +#endif + + hlen = fin->fin_hlen; + ip = mtod(m0, struct ip *); + + /* + * Route packet. + */ + ro = &iproute; + bzero((caddr_t)ro, sizeof (*ro)); + dst = (struct sockaddr_in *)&ro->ro_dst; + dst->sin_family = AF_INET; + dst->sin_addr = ip->ip_dst; + + fr = fin->fin_fr; + if (fdp != NULL) + ifp = fdp->fd_ifp; + else + ifp = fin->fin_ifp; + + if ((ifp == NULL) && (!fr || !(fr->fr_flags & FR_FASTROUTE))) { + error = -2; + goto bad; + } + + /* + * In case we're here due to "to <if>" being used with "keep state", + * check that we're going in the correct direction. + */ + if ((fr != NULL) && (fin->fin_rev != 0)) { + if ((ifp != NULL) && (fdp == &fr->fr_tif)) + return -1; + } + if (fdp != NULL) { + if (fdp->fd_ip.s_addr != 0) + dst->sin_addr = fdp->fd_ip; + } + + dst->sin_len = sizeof(*dst); + rtalloc(ro); + + if ((ifp == NULL) && (ro->ro_rt != NULL)) + ifp = ro->ro_rt->rt_ifp; + + if ((ro->ro_rt == NULL) || (ifp == NULL)) { + if (in_localaddr(ip->ip_dst)) + error = EHOSTUNREACH; + else + error = ENETUNREACH; + goto bad; + } + if (ro->ro_rt->rt_flags & RTF_GATEWAY) + dst = (struct sockaddr_in *)ro->ro_rt->rt_gateway; + if (ro->ro_rt) + ro->ro_rt->rt_use++; + + /* + * For input packets which are being "fastrouted", they won't + * go back through output filtering and miss their chance to get + * NAT'd and counted. + */ + if (fin->fin_out == 0) { + sifp = fin->fin_ifp; + fin->fin_ifp = ifp; + fin->fin_out = 1; + (void) fr_acctpkt(fin, NULL); + fin->fin_fr = NULL; + if (!fr || !(fr->fr_flags & FR_RETMASK)) { + u_32_t pass; + + (void) fr_checkstate(fin, &pass); + } + + switch (fr_checknatout(fin, NULL)) + { + case 0 : + break; + case 1 : + ip->ip_sum = 0; + break; + case -1 : + error = -1; + goto done; + break; + } + + fin->fin_ifp = sifp; + fin->fin_out = 0; + } else + ip->ip_sum = 0; + /* + * If small enough for interface, can just send directly. + */ + if (ip->ip_len <= ifp->if_mtu) { + ip->ip_len = htons(ip->ip_len); + ip->ip_off = htons(ip->ip_off); + + if (!ip->ip_sum) + ip->ip_sum = in_cksum(m, hlen); + error = (*ifp->if_output)(ifp, m, (struct sockaddr *)dst, + ro->ro_rt); + goto done; + } + /* + * Too large for interface; fragment if possible. + * Must be able to put at least 8 bytes per fragment. + */ + ip_off = ntohs(ip->ip_off); + if (ip_off & IP_DF) { + error = EMSGSIZE; + goto bad; + } + len = (ifp->if_mtu - hlen) &~ 7; + if (len < 8) { + error = EMSGSIZE; + goto bad; + } + + { + int mhlen, firstlen = len; + struct mbuf **mnext = &m->m_act; + + /* + * Loop through length of segment after first fragment, + * make new header and copy data of each part and link onto chain. + */ + m0 = m; + mhlen = sizeof (struct ip); + for (off = hlen + len; off < ip->ip_len; off += len) { +#ifdef MGETHDR + MGETHDR(m, M_DONTWAIT, MT_HEADER); +#else + MGET(m, M_DONTWAIT, MT_HEADER); +#endif + if (m == 0) { + m = m0; + error = ENOBUFS; + goto bad; + } + m->m_data += max_linkhdr; + mhip = mtod(m, struct ip *); + bcopy((char *)ip, (char *)mhip, sizeof(*ip)); + if (hlen > sizeof (struct ip)) { + mhlen = ip_optcopy(ip, mhip) + sizeof (struct ip); + IP_HL_A(mhip, mhlen >> 2); + } + m->m_len = mhlen; + mhip->ip_off = ((off - hlen) >> 3) + ip_off; + if (off + len >= ip->ip_len) + len = ip->ip_len - off; + else + mhip->ip_off |= IP_MF; + mhip->ip_len = htons((u_short)(len + mhlen)); + m->m_next = m_copy(m0, off, len); + if (m->m_next == 0) { + error = ENOBUFS; /* ??? */ + goto sendorfree; + } + m->m_pkthdr.len = mhlen + len; + m->m_pkthdr.rcvif = NULL; + mhip->ip_off = htons((u_short)mhip->ip_off); + mhip->ip_sum = 0; + mhip->ip_sum = in_cksum(m, mhlen); + *mnext = m; + mnext = &m->m_act; + } + /* + * Update first fragment by trimming what's been copied out + * and updating header, then send each fragment (in order). + */ + m_adj(m0, hlen + firstlen - ip->ip_len); + ip->ip_len = htons((u_short)(hlen + firstlen)); + ip->ip_off = htons((u_short)IP_MF); + ip->ip_sum = 0; + ip->ip_sum = in_cksum(m0, hlen); +sendorfree: + for (m = m0; m; m = m0) { + m0 = m->m_act; + m->m_act = 0; + if (error == 0) + error = (*ifp->if_output)(ifp, m, + (struct sockaddr *)dst, ro->ro_rt); + else + FREE_MB_T(m); + } + } +done: + if (!error) + fr_frouteok[0]++; + else + fr_frouteok[1]++; + + if (ro->ro_rt) { + RTFREE(ro->ro_rt); + } + *mpp = NULL; + return 0; +bad: + if (error == EMSGSIZE) { + sifp = fin->fin_ifp; + code = fin->fin_icode; + fin->fin_icode = ICMP_UNREACH_NEEDFRAG; + fin->fin_ifp = ifp; + (void) fr_send_icmp_err(ICMP_UNREACH, fin, 1); + fin->fin_ifp = sifp; + fin->fin_icode = code; + } + FREE_MB_T(m); + goto done; +} + + +int fr_verifysrc(fin) +fr_info_t *fin; +{ + struct sockaddr_in *dst; + struct route iproute; + + bzero((char *)&iproute, sizeof(iproute)); + dst = (struct sockaddr_in *)&iproute.ro_dst; + dst->sin_len = sizeof(*dst); + dst->sin_family = AF_INET; + dst->sin_addr = fin->fin_src; + rtalloc(&iproute); + if (iproute.ro_rt == NULL) + return 0; + return (fin->fin_ifp == iproute.ro_rt->rt_ifp); +} + + +/* + * return the first IP Address associated with an interface + */ +int fr_ifpaddr(v, atype, ifptr, inp, inpmask) +int v, atype; +void *ifptr; +struct in_addr *inp, *inpmask; +{ +#ifdef USE_INET6 + struct in6_addr *inp6 = NULL; +#endif + struct sockaddr *sock, *mask; + struct sockaddr_in *sin; + struct ifaddr *ifa; + struct ifnet *ifp; + + if ((ifptr == NULL) || (ifptr == (void *)-1)) + return -1; + + sin = NULL; + ifp = ifptr; + + if (v == 4) + inp->s_addr = 0; +#ifdef USE_INET6 + else if (v == 6) + bzero((char *)inp, sizeof(struct in6_addr)); +#endif +#if (__FreeBSD_version >= 300000) + ifa = TAILQ_FIRST(&ifp->if_addrhead); +#else + ifa = ifp->if_addrlist; +#endif /* __FreeBSD_version >= 300000 */ + + sock = ifa->ifa_addr; + while (sock != NULL && ifa != NULL) { + sin = (struct sockaddr_in *)sock; + if ((v == 4) && (sin->sin_family == AF_INET)) + break; +#ifdef USE_INET6 + if ((v == 6) && (sin->sin_family == AF_INET6)) { + inp6 = &((struct sockaddr_in6 *)sin)->sin6_addr; + if (!IN6_IS_ADDR_LINKLOCAL(inp6) && + !IN6_IS_ADDR_LOOPBACK(inp6)) + break; + } +#endif +#if (__FreeBSD_version >= 300000) + ifa = TAILQ_NEXT(ifa, ifa_link); +#else + ifa = ifa->ifa_next; +#endif /* __FreeBSD_version >= 300000 */ + if (ifa != NULL) + sock = ifa->ifa_addr; + } + + if (ifa == NULL || sin == NULL) + return -1; + + mask = ifa->ifa_netmask; + if (atype == FRI_BROADCAST) + sock = ifa->ifa_broadaddr; + else if (atype == FRI_PEERADDR) + sock = ifa->ifa_dstaddr; + +#ifdef USE_INET6 + if (v == 6) { + return fr_ifpfillv6addr(atype, (struct sockaddr_in6 *)sock, + (struct sockaddr_in6 *)mask, + inp, inpmask); + } +#endif + return fr_ifpfillv4addr(atype, (struct sockaddr_in *)sock, + (struct sockaddr_in *)mask, inp, inpmask); +} + + +u_32_t fr_newisn(fin) +fr_info_t *fin; +{ + u_32_t newiss; +#if (__FreeBSD_version >= 400000) + newiss = arc4random(); +#else + static iss_seq_off = 0; + u_char hash[16]; + MD5_CTX ctx; + + /* + * Compute the base value of the ISS. It is a hash + * of (saddr, sport, daddr, dport, secret). + */ + MD5Init(&ctx); + + MD5Update(&ctx, (u_char *) &fin->fin_fi.fi_src, + sizeof(fin->fin_fi.fi_src)); + MD5Update(&ctx, (u_char *) &fin->fin_fi.fi_dst, + sizeof(fin->fin_fi.fi_dst)); + MD5Update(&ctx, (u_char *) &fin->fin_dat, sizeof(fin->fin_dat)); + + MD5Update(&ctx, ipf_iss_secret, sizeof(ipf_iss_secret)); + + MD5Final(hash, &ctx); + + memcpy(&newiss, hash, sizeof(newiss)); + + /* + * Now increment our "timer", and add it in to + * the computed value. + * + * XXX Use `addin'? + * XXX TCP_ISSINCR too large to use? + */ + iss_seq_off += 0x00010000; + newiss += iss_seq_off; +#endif + return newiss; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: fr_nextipid */ +/* Returns: int - 0 == success, -1 == error (packet should be droppped) */ +/* Parameters: fin(I) - pointer to packet information */ +/* */ +/* Returns the next IPv4 ID to use for this packet. */ +/* ------------------------------------------------------------------------ */ +u_short fr_nextipid(fin) +fr_info_t *fin; +{ +#ifndef RANDOM_IP_ID + static u_short ipid = 0; + u_short id; + + MUTEX_ENTER(&ipf_rw); + id = ipid++; + MUTEX_EXIT(&ipf_rw); +#else + u_short id; + + id = ip_randomid(); +#endif + + return id; +} + + +INLINE void fr_checkv4sum(fin) +fr_info_t *fin; +{ +#ifdef CSUM_DATA_VALID + int manual = 0; + u_short sum; + ip_t *ip; + mb_t *m; + + if ((fin->fin_flx & FI_NOCKSUM) != 0) + return; + + m = fin->fin_m; + if (m == NULL) { + manual = 1; + goto skipauto; + } + ip = fin->fin_ip; + + if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID) { + if (m->m_pkthdr.csum_flags & CSUM_PSEUDO_HDR) + sum = m->m_pkthdr.csum_data; + else + sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, + htonl(m->m_pkthdr.csum_data + + fin->fin_ip->ip_len + fin->fin_p)); + sum ^= 0xffff; + if (sum != 0) + fin->fin_flx |= FI_BAD; + } else + manual = 1; +skipauto: +# ifdef IPFILTER_CKSUM + if (manual != 0) + if (fr_checkl4sum(fin) == -1) + fin->fin_flx |= FI_BAD; +# else + ; +# endif +#else +# ifdef IPFILTER_CKSUM + if (fr_checkl4sum(fin) == -1) + fin->fin_flx |= FI_BAD; +# endif +#endif +} + + +#ifdef USE_INET6 +INLINE void fr_checkv6sum(fin) +fr_info_t *fin; +{ +# ifdef IPFILTER_CKSUM + if (fr_checkl4sum(fin) == -1) + fin->fin_flx |= FI_BAD; +# endif +} +#endif /* USE_INET6 */ + + +size_t mbufchainlen(m0) +struct mbuf *m0; +{ + size_t len; + + if ((m0->m_flags & M_PKTHDR) != 0) { + len = m0->m_pkthdr.len; + } else { + struct mbuf *m; + + for (m = m0, len = 0; m != NULL; m = m->m_next) + len += m->m_len; + } + return len; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: fr_pullup */ +/* Returns: NULL == pullup failed, else pointer to protocol header */ +/* Parameters: m(I) - pointer to buffer where data packet starts */ +/* fin(I) - pointer to packet information */ +/* len(I) - number of bytes to pullup */ +/* */ +/* Attempt to move at least len bytes (from the start of the buffer) into a */ +/* single buffer for ease of access. Operating system native functions are */ +/* used to manage buffers - if necessary. If the entire packet ends up in */ +/* a single buffer, set the FI_COALESCE flag even though fr_coalesce() has */ +/* not been called. Both fin_ip and fin_dp are updated before exiting _IF_ */ +/* and ONLY if the pullup succeeds. */ +/* */ +/* We assume that 'min' is a pointer to a buffer that is part of the chain */ +/* of buffers that starts at *fin->fin_mp. */ +/* ------------------------------------------------------------------------ */ +void *fr_pullup(min, fin, len) +mb_t *min; +fr_info_t *fin; +int len; +{ + int out = fin->fin_out, dpoff, ipoff; + mb_t *m = min; + char *ip; + + if (m == NULL) + return NULL; + + ip = (char *)fin->fin_ip; + if ((fin->fin_flx & FI_COALESCE) != 0) + return ip; + + ipoff = fin->fin_ipoff; + if (fin->fin_dp != NULL) + dpoff = (char *)fin->fin_dp - (char *)ip; + else + dpoff = 0; + + if (M_LEN(m) < len) { +#ifdef MHLEN + /* + * Assume that M_PKTHDR is set and just work with what is left + * rather than check.. + * Should not make any real difference, anyway. + */ + if (len > MHLEN) +#else + if (len > MLEN) +#endif + { +#ifdef HAVE_M_PULLDOWN + if (m_pulldown(m, 0, len, NULL) == NULL) + m = NULL; +#else + FREE_MB_T(*fin->fin_mp); + m = NULL; +#endif + } else + { + m = m_pullup(m, len); + } + *fin->fin_mp = m; + fin->fin_m = m; + if (m == NULL) { + ATOMIC_INCL(frstats[out].fr_pull[1]); + return NULL; + } + ip = MTOD(m, char *) + ipoff; + } + + ATOMIC_INCL(frstats[out].fr_pull[0]); + fin->fin_ip = (ip_t *)ip; + if (fin->fin_dp != NULL) + fin->fin_dp = (char *)fin->fin_ip + dpoff; + + if (len == fin->fin_plen) + fin->fin_flx |= FI_COALESCE; + return ip; +} diff --git a/sys/contrib/ipfilter/netinet/ip_htable.c b/sys/contrib/ipfilter/netinet/ip_htable.c new file mode 100644 index 0000000..22acbec --- /dev/null +++ b/sys/contrib/ipfilter/netinet/ip_htable.c @@ -0,0 +1,455 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 1993-2001, 2003 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 <sys/param.h> +#include <sys/types.h> +#include <sys/errno.h> +#include <sys/time.h> +#include <sys/file.h> +#if !defined(_KERNEL) +# include <stdlib.h> +# include <string.h> +# define _KERNEL +# ifdef __OpenBSD__ +struct file; +# endif +# include <sys/uio.h> +# undef _KERNEL +#endif +#include <sys/socket.h> +#if defined(__FreeBSD_version) && (__FreeBSD_version >= 300000) +# include <sys/malloc.h> +#endif +#if defined(__FreeBSD__) +# include <sys/cdefs.h> +# include <sys/proc.h> +#endif +#if !defined(__svr4__) && !defined(__SVR4) && !defined(__hpux) && \ + !defined(linux) +# include <sys/mbuf.h> +#endif +#if defined(_KERNEL) +# include <sys/systm.h> +#else +# include <stdio.h> +#endif +#include <netinet/in.h> +#include <net/if.h> + +#include "netinet/ip_compat.h" +#include "netinet/ip_fil.h" +#include "netinet/ip_lookup.h" +#include "netinet/ip_htable.h" +/* END OF INCLUDES */ + +#if !defined(lint) +static const char rcsid[] = "@(#)Id: ip_htable.c,v 2.34.2.2 2004/10/17 15:49:15 darrenr Exp"; +#endif + +#ifdef IPFILTER_LOOKUP +static iphtent_t *fr_iphmfind __P((iphtable_t *, struct in_addr *)); +static u_long ipht_nomem[IPL_LOGSIZE] = { 0, 0, 0, 0, 0, 0, 0, 0 }; +static u_long ipf_nhtables[IPL_LOGSIZE] = { 0, 0, 0, 0, 0, 0, 0, 0 }; +static u_long ipf_nhtnodes[IPL_LOGSIZE] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + +iphtable_t *ipf_htables[IPL_LOGSIZE] = { NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL }; + + +void fr_htable_unload() +{ + iplookupflush_t fop; + + fop.iplf_unit = IPL_LOGALL; + (void)fr_flushhtable(&fop); +} + + +int fr_gethtablestat(op) +iplookupop_t *op; +{ + iphtstat_t stats; + + if (op->iplo_size != sizeof(stats)) + return EINVAL; + + stats.iphs_tables = ipf_htables[op->iplo_unit]; + stats.iphs_numtables = ipf_nhtables[op->iplo_unit]; + stats.iphs_numnodes = ipf_nhtnodes[op->iplo_unit]; + stats.iphs_nomem = ipht_nomem[op->iplo_unit]; + + return COPYOUT(&stats, op->iplo_struct, sizeof(stats)); + +} + + +/* + * Create a new hash table using the template passed. + */ +int fr_newhtable(op) +iplookupop_t *op; +{ + iphtable_t *iph, *oiph; + char name[FR_GROUPLEN]; + int err, i, unit; + + KMALLOC(iph, iphtable_t *); + if (iph == NULL) + return ENOMEM; + + err = COPYIN(op->iplo_struct, iph, sizeof(*iph)); + if (err != 0) { + KFREE(iph); + return EFAULT; + } + + unit = op->iplo_unit; + if (iph->iph_unit != unit) { + KFREE(iph); + return EINVAL; + } + + if ((op->iplo_arg & IPHASH_ANON) == 0) { + if (fr_findhtable(op->iplo_unit, op->iplo_name) != NULL) { + KFREE(iph); + return EEXIST; + } + } else { + i = IPHASH_ANON; + do { + i++; +#if defined(SNPRINTF) && defined(_KERNEL) + SNPRINTF(name, sizeof(name), "%u", i); +#else + (void)sprintf(name, "%u", i); +#endif + for (oiph = ipf_htables[unit]; oiph != NULL; + oiph = oiph->iph_next) + if (strncmp(oiph->iph_name, name, + sizeof(oiph->iph_name)) == 0) + break; + } while (oiph != NULL); + (void)strncpy(iph->iph_name, name, sizeof(iph->iph_name)); + err = COPYOUT(iph, op->iplo_struct, sizeof(*iph)); + if (err != 0) { + KFREE(iph); + return EFAULT; + } + iph->iph_type |= IPHASH_ANON; + } + + KMALLOCS(iph->iph_table, iphtent_t **, + iph->iph_size * sizeof(*iph->iph_table)); + if (iph->iph_table == NULL) { + KFREE(iph); + ipht_nomem[unit]++; + return ENOMEM; + } + + bzero((char *)iph->iph_table, iph->iph_size * sizeof(*iph->iph_table)); + iph->iph_masks = 0; + + iph->iph_next = ipf_htables[unit]; + iph->iph_pnext = &ipf_htables[unit]; + if (ipf_htables[unit] != NULL) + ipf_htables[unit]->iph_pnext = &iph->iph_next; + ipf_htables[unit] = iph; + + ipf_nhtables[unit]++; + + return 0; +} + + +/* + */ +int fr_removehtable(op) +iplookupop_t *op; +{ + iphtable_t *iph; + + + iph = fr_findhtable(op->iplo_unit, op->iplo_name); + if (iph == NULL) + return ESRCH; + + if (iph->iph_unit != op->iplo_unit) { + return EINVAL; + } + + if (iph->iph_ref != 0) { + return EBUSY; + } + + fr_delhtable(iph); + + return 0; +} + + +void fr_delhtable(iph) +iphtable_t *iph; +{ + iphtent_t *ipe; + int i; + + for (i = 0; i < iph->iph_size; i++) + while ((ipe = iph->iph_table[i]) != NULL) + if (fr_delhtent(iph, ipe) != 0) + return; + + *iph->iph_pnext = iph->iph_next; + if (iph->iph_next != NULL) + iph->iph_next->iph_pnext = iph->iph_pnext; + + ipf_nhtables[iph->iph_unit]--; + + if (iph->iph_ref == 0) { + KFREES(iph->iph_table, iph->iph_size * sizeof(*iph->iph_table)); + KFREE(iph); + } +} + + +void fr_derefhtable(iph) +iphtable_t *iph; +{ + iph->iph_ref--; + if (iph->iph_ref == 0) + fr_delhtable(iph); +} + + +iphtable_t *fr_findhtable(unit, name) +int unit; +char *name; +{ + iphtable_t *iph; + + for (iph = ipf_htables[unit]; iph != NULL; iph = iph->iph_next) + if (strncmp(iph->iph_name, name, sizeof(iph->iph_name)) == 0) + break; + return iph; +} + + +size_t fr_flushhtable(op) +iplookupflush_t *op; +{ + iphtable_t *iph; + size_t freed; + int i; + + freed = 0; + + for (i = 0; i <= IPL_LOGMAX; i++) { + if (op->iplf_unit == i || op->iplf_unit == IPL_LOGALL) { + while ((iph = ipf_htables[i]) != NULL) { + fr_delhtable(iph); + freed++; + } + } + } + + return freed; +} + + +/* + * Add an entry to a hash table. + */ +int fr_addhtent(iph, ipeo) +iphtable_t *iph; +iphtent_t *ipeo; +{ + iphtent_t *ipe; + u_int hv; + int bits; + + KMALLOC(ipe, iphtent_t *); + if (ipe == NULL) + return -1; + + bcopy((char *)ipeo, (char *)ipe, sizeof(*ipe)); + ipe->ipe_addr.in4_addr &= ipe->ipe_mask.in4_addr; + ipe->ipe_addr.in4_addr = ntohl(ipe->ipe_addr.in4_addr); + bits = count4bits(ipe->ipe_mask.in4_addr); + ipe->ipe_mask.in4_addr = ntohl(ipe->ipe_mask.in4_addr); + + hv = IPE_HASH_FN(ipe->ipe_addr.in4_addr, ipe->ipe_mask.in4_addr, + iph->iph_size); + ipe->ipe_ref = 0; + ipe->ipe_next = iph->iph_table[hv]; + ipe->ipe_pnext = iph->iph_table + hv; + + if (iph->iph_table[hv] != NULL) + iph->iph_table[hv]->ipe_pnext = &ipe->ipe_next; + iph->iph_table[hv] = ipe; + if ((bits >= 0) && (bits != 32)) + iph->iph_masks |= 1 << bits; + + switch (iph->iph_type & ~IPHASH_ANON) + { + case IPHASH_GROUPMAP : + ipe->ipe_ptr = fr_addgroup(ipe->ipe_group, NULL, + iph->iph_flags, IPL_LOGIPF, + fr_active); + break; + + default : + ipe->ipe_ptr = NULL; + ipe->ipe_value = 0; + break; + } + + ipf_nhtnodes[iph->iph_unit]++; + + return 0; +} + + +/* + * Delete an entry from a hash table. + */ +int fr_delhtent(iph, ipe) +iphtable_t *iph; +iphtent_t *ipe; +{ + + if (ipe->ipe_ref != 0) + return EBUSY; + + + *ipe->ipe_pnext = ipe->ipe_next; + if (ipe->ipe_next != NULL) + ipe->ipe_next->ipe_pnext = ipe->ipe_pnext; + + switch (iph->iph_type & ~IPHASH_ANON) + { + case IPHASH_GROUPMAP : + if (ipe->ipe_group != NULL) + fr_delgroup(ipe->ipe_group, IPL_LOGIPF, fr_active); + break; + + default : + ipe->ipe_ptr = NULL; + ipe->ipe_value = 0; + break; + } + + KFREE(ipe); + + ipf_nhtnodes[iph->iph_unit]--; + + return 0; +} + + +void *fr_iphmfindgroup(tptr, aptr) +void *tptr, *aptr; +{ + struct in_addr *addr; + iphtable_t *iph; + iphtent_t *ipe; + void *rval; + + READ_ENTER(&ip_poolrw); + iph = tptr; + addr = aptr; + + ipe = fr_iphmfind(iph, addr); + if (ipe != NULL) + rval = ipe->ipe_ptr; + else + rval = NULL; + RWLOCK_EXIT(&ip_poolrw); + return rval; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: fr_iphmfindip */ +/* Returns: int - 0 == +ve match, -1 == error, 1 == -ve/no match */ +/* Parameters: tptr(I) - pointer to the pool to search */ +/* version(I) - IP protocol version (4 or 6) */ +/* aptr(I) - pointer to address information */ +/* */ +/* Search the hash table for a given address and return a search result. */ +/* ------------------------------------------------------------------------ */ +int fr_iphmfindip(tptr, version, aptr) +void *tptr, *aptr; +int version; +{ + struct in_addr *addr; + iphtable_t *iph; + iphtent_t *ipe; + int rval; + + if (version != 4) + return -1; + + if (tptr == NULL || aptr == NULL) + return -1; + + iph = tptr; + addr = aptr; + + READ_ENTER(&ip_poolrw); + ipe = fr_iphmfind(iph, addr); + if (ipe != NULL) + rval = 0; + else + rval = 1; + RWLOCK_EXIT(&ip_poolrw); + return rval; +} + + +/* Locks: ip_poolrw */ +static iphtent_t *fr_iphmfind(iph, addr) +iphtable_t *iph; +struct in_addr *addr; +{ + u_32_t hmsk, msk, ips; + iphtent_t *ipe; + u_int hv; + + hmsk = iph->iph_masks; + msk = 0xffffffff; +maskloop: + ips = ntohl(addr->s_addr) & msk; + hv = IPE_HASH_FN(ips, msk, iph->iph_size); + for (ipe = iph->iph_table[hv]; (ipe != NULL); ipe = ipe->ipe_next) { + if (ipe->ipe_mask.in4_addr != msk || + ipe->ipe_addr.in4_addr != ips) { + continue; + } + break; + } + + if ((ipe == NULL) && (hmsk != 0)) { + while (hmsk != 0) { + msk <<= 1; + if (hmsk & 0x80000000) + break; + hmsk <<= 1; + } + if (hmsk != 0) { + hmsk <<= 1; + goto maskloop; + } + } + return ipe; +} + +#endif /* IPFILTER_LOOKUP */ diff --git a/sys/contrib/ipfilter/netinet/ip_htable.h b/sys/contrib/ipfilter/netinet/ip_htable.h new file mode 100644 index 0000000..1bc4087 --- /dev/null +++ b/sys/contrib/ipfilter/netinet/ip_htable.h @@ -0,0 +1,71 @@ +/* $FreeBSD$ */ + +#ifndef __IP_HTABLE_H__ +#define __IP_HTABLE_H__ + +#include "netinet/ip_lookup.h" + +typedef struct iphtent_s { + struct iphtent_s *ipe_next, **ipe_pnext; + void *ipe_ptr; + i6addr_t ipe_addr; + i6addr_t ipe_mask; + int ipe_ref; + union { + char ipeu_char[16]; + u_long ipeu_long; + u_int ipeu_int; + }ipe_un; +} iphtent_t; + +#define ipe_value ipe_un.ipeu_int +#define ipe_group ipe_un.ipeu_char + +#define IPE_HASH_FN(a, m, s) (((a) * (m)) % (s)) + + +typedef struct iphtable_s { + ipfrwlock_t iph_rwlock; + struct iphtable_s *iph_next, **iph_pnext; + struct iphtent_s **iph_table; + size_t iph_size; /* size of hash table */ + u_long iph_seed; /* hashing seed */ + u_32_t iph_flags; + u_int iph_unit; /* IPL_LOG* */ + u_int iph_ref; + u_int iph_type; /* lookup or group map - IPHASH_* */ + u_int iph_masks; /* IPv4 netmasks in use */ + char iph_name[FR_GROUPLEN]; /* hash table number */ +} iphtable_t; + +/* iph_type */ +#define IPHASH_LOOKUP 0 +#define IPHASH_GROUPMAP 1 +#define IPHASH_ANON 0x80000000 + + +typedef struct iphtstat_s { + iphtable_t *iphs_tables; + u_long iphs_numtables; + u_long iphs_numnodes; + u_long iphs_nomem; + u_long iphs_pad[16]; +} iphtstat_t; + + +extern iphtable_t *ipf_htables[IPL_LOGSIZE]; + +extern void fr_htable_unload __P((void)); +extern int fr_newhtable __P((iplookupop_t *)); +extern iphtable_t *fr_findhtable __P((int, char *)); +extern int fr_removehtable __P((iplookupop_t *)); +extern size_t fr_flushhtable __P((iplookupflush_t *)); +extern int fr_addhtent __P((iphtable_t *, iphtent_t *)); +extern int fr_delhtent __P((iphtable_t *, iphtent_t *)); +extern void fr_derefhtable __P((iphtable_t *)); +extern void fr_delhtable __P((iphtable_t *)); +extern void *fr_iphmfindgroup __P((void *, void *)); +extern int fr_iphmfindip __P((void *, int, void *)); +extern int fr_gethtablestat __P((iplookupop_t *)); + +#endif /* __IP_HTABLE_H__ */ diff --git a/sys/contrib/ipfilter/netinet/ip_ipsec_pxy.c b/sys/contrib/ipfilter/netinet/ip_ipsec_pxy.c index 40ce131..b549611 100644 --- a/sys/contrib/ipfilter/netinet/ip_ipsec_pxy.c +++ b/sys/contrib/ipfilter/netinet/ip_ipsec_pxy.c @@ -1,42 +1,90 @@ +/* $FreeBSD$ */ + /* + * Copyright (C) 2001-2003 by Darren Reed + * + * See the IPFILTER.LICENCE file for details on licencing. + * * Simple ISAKMP transparent proxy for in-kernel use. For use with the NAT * code. * - * $Id: ip_ipsec_pxy.c,v 1.1.2.10 2002/01/13 04:58:29 darrenr Exp $ + * Id: ip_ipsec_pxy.c,v 2.20.2.6 2005/03/28 10:47:53 darrenr Exp * */ #define IPF_IPSEC_PROXY int ippr_ipsec_init __P((void)); -int ippr_ipsec_new __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *)); +void ippr_ipsec_fini __P((void)); +int ippr_ipsec_new __P((fr_info_t *, ap_session_t *, nat_t *)); void ippr_ipsec_del __P((ap_session_t *)); -int ippr_ipsec_out __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *)); +int ippr_ipsec_inout __P((fr_info_t *, ap_session_t *, nat_t *)); int ippr_ipsec_match __P((fr_info_t *, ap_session_t *, nat_t *)); static frentry_t ipsecfr; - - +static ipftq_t *ipsecnattqe; +static ipftq_t *ipsecstatetqe; static char ipsec_buffer[1500]; +int ipsec_proxy_init = 0; +int ipsec_proxy_ttl = 60; + /* - * RCMD application proxy initialization. + * IPSec application proxy initialization. */ int ippr_ipsec_init() { bzero((char *)&ipsecfr, sizeof(ipsecfr)); ipsecfr.fr_ref = 1; ipsecfr.fr_flags = FR_OUTQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; + MUTEX_INIT(&ipsecfr.fr_lock, "IPsec proxy rule lock"); + ipsec_proxy_init = 1; + + ipsecnattqe = fr_addtimeoutqueue(&nat_utqe, ipsec_proxy_ttl); + if (ipsecnattqe == NULL) + return -1; + ipsecstatetqe = fr_addtimeoutqueue(&ips_utqe, ipsec_proxy_ttl); + if (ipsecstatetqe == NULL) { + if (fr_deletetimeoutqueue(ipsecnattqe) == 0) + fr_freetimeoutqueue(ipsecnattqe); + ipsecnattqe = NULL; + return -1; + } + + ipsecnattqe->ifq_flags |= IFQF_PROXY; + ipsecstatetqe->ifq_flags |= IFQF_PROXY; + + ipsecfr.fr_age[0] = ipsec_proxy_ttl; + ipsecfr.fr_age[1] = ipsec_proxy_ttl; return 0; } +void ippr_ipsec_fini() +{ + if (ipsecnattqe != NULL) { + if (fr_deletetimeoutqueue(ipsecnattqe) == 0) + fr_freetimeoutqueue(ipsecnattqe); + } + ipsecnattqe = NULL; + if (ipsecstatetqe != NULL) { + if (fr_deletetimeoutqueue(ipsecstatetqe) == 0) + fr_freetimeoutqueue(ipsecstatetqe); + } + ipsecstatetqe = NULL; + + if (ipsec_proxy_init == 1) { + MUTEX_DESTROY(&ipsecfr.fr_lock); + ipsec_proxy_init = 0; + } +} + + /* * Setup for a new IPSEC proxy. */ -int ippr_ipsec_new(fin, ip, aps, nat) +int ippr_ipsec_new(fin, aps, nat) fr_info_t *fin; -ip_t *ip; ap_session_t *aps; nat_t *nat; { @@ -44,40 +92,22 @@ nat_t *nat; fr_info_t fi; ipnat_t *ipn; char *ptr; - int p, off, dlen; + int p, off, dlen, ttl; mb_t *m; + ip_t *ip; bzero(ipsec_buffer, sizeof(ipsec_buffer)); off = fin->fin_hlen + sizeof(udphdr_t); -#ifdef _KERNEL -# if SOLARIS - m = fin->fin_qfm; + ip = fin->fin_ip; + m = fin->fin_m; - dlen = msgdsize(m) - off; + dlen = M_LEN(m) - off; if (dlen < 16) return -1; - copyout_mblk(m, off, MIN(sizeof(ipsec_buffer), dlen), ipsec_buffer); -# else - m = *(mb_t **)fin->fin_mp; - dlen = mbufchainlen(m) - off; - if (dlen < 16) - return -1; - m_copydata(m, off, MIN(sizeof(ipsec_buffer), dlen), ipsec_buffer); -# endif -#else - m = *(mb_t **)fin->fin_mp; - dlen = ip->ip_len - off; - ptr = (char *)m; - ptr += off; - bcopy(ptr, ipsec_buffer, MIN(sizeof(ipsec_buffer), dlen)); -#endif + COPYDATA(m, off, MIN(sizeof(ipsec_buffer), dlen), ipsec_buffer); - /* - * Because _new() gets called from nat_new(), ipf_nat is held with a - * write lock so pass rw=1 to nat_outlookup(). - */ if (nat_outlookup(fin, 0, IPPROTO_ESP, nat->nat_inip, - ip->ip_dst, 1) != NULL) + ip->ip_dst) != NULL) return -1; aps->aps_psiz = sizeof(*ipsec); @@ -94,7 +124,10 @@ nat_t *nat; * describe ESP but UDP instead. */ ipn = &ipsec->ipsc_rule; - ipn->in_ifp = fin->fin_ifp; + ttl = IPF_TTLVAL(ipsecnattqe->ifq_ttl); + ipn->in_tqehead[0] = fr_addtimeoutqueue(&nat_utqe, ttl); + ipn->in_tqehead[1] = fr_addtimeoutqueue(&nat_utqe, ttl); + ipn->in_ifps[0] = fin->fin_ifp; ipn->in_apr = NULL; ipn->in_use = 1; ipn->in_hits = 1; @@ -102,27 +135,31 @@ nat_t *nat; ipn->in_ippip = 1; ipn->in_inip = nat->nat_inip.s_addr; ipn->in_inmsk = 0xffffffff; - ipn->in_outip = nat->nat_outip.s_addr; - ipn->in_outmsk = 0xffffffff; + ipn->in_outip = fin->fin_saddr; + ipn->in_outmsk = nat->nat_outip.s_addr; ipn->in_srcip = fin->fin_saddr; ipn->in_srcmsk = 0xffffffff; ipn->in_redir = NAT_MAP; - bcopy(nat->nat_ptr->in_ifname, ipn->in_ifname, sizeof(ipn->in_ifname)); + bcopy(nat->nat_ptr->in_ifnames[0], ipn->in_ifnames[0], + sizeof(ipn->in_ifnames[0])); ipn->in_p = IPPROTO_ESP; bcopy((char *)fin, (char *)&fi, sizeof(fi)); + fi.fin_state = NULL; + fi.fin_nat = NULL; fi.fin_fi.fi_p = IPPROTO_ESP; fi.fin_fr = &ipsecfr; fi.fin_data[0] = 0; fi.fin_data[1] = 0; p = ip->ip_p; ip->ip_p = IPPROTO_ESP; - fi.fin_fl &= ~FI_TCPUDP; + fi.fin_flx &= ~(FI_TCPUDP|FI_STATE|FI_FRAG); + fi.fin_flx |= FI_IGNORE; ptr = ipsec_buffer; - bcopy(ptr, ipsec->ipsc_icookie, sizeof(ipsec_cookie_t)); + bcopy(ptr, (char *)ipsec->ipsc_icookie, sizeof(ipsec_cookie_t)); ptr += sizeof(ipsec_cookie_t); - bcopy(ptr, ipsec->ipsc_rcookie, sizeof(ipsec_cookie_t)); + bcopy(ptr, (char *)ipsec->ipsc_rcookie, sizeof(ipsec_cookie_t)); /* * The responder cookie should only be non-zero if the initiator * cookie is non-zero. Therefore, it is safe to assume(!) that the @@ -130,76 +167,101 @@ nat_t *nat; */ if ((ipsec->ipsc_rcookie[0]|ipsec->ipsc_rcookie[1]) != 0) ipsec->ipsc_rckset = 1; - else - nat->nat_age = 60; /* 30 seconds */ - ipsec->ipsc_nat = nat_new(&fi, ip, ipn, &ipsec->ipsc_nat, FI_IGNOREPKT, - NAT_OUTBOUND); + ipsec->ipsc_nat = nat_new(&fi, ipn, &ipsec->ipsc_nat, + NAT_SLAVE|SI_WILDP, NAT_OUTBOUND); if (ipsec->ipsc_nat != NULL) { + (void) nat_proto(&fi, ipsec->ipsc_nat, 0); + nat_update(&fi, ipsec->ipsc_nat, ipn); + fi.fin_data[0] = 0; fi.fin_data[1] = 0; - ipsec->ipsc_state = fr_addstate(ip, &fi, &ipsec->ipsc_state, - FI_IGNOREPKT|FI_NORULE); + ipsec->ipsc_state = fr_addstate(&fi, &ipsec->ipsc_state, + SI_WILDP); + if (fi.fin_state != NULL) + fr_statederef(&fi, (ipstate_t **)&fi.fin_state); } - ip->ip_p = p; + ip->ip_p = p & 0xff; return 0; } /* - * For outgoing IKE packets. refresh timeouts for NAT & stat entries, if + * For outgoing IKE packets. refresh timeouts for NAT & state entries, if * we can. If they have disappeared, recreate them. */ -int ippr_ipsec_out(fin, ip, aps, nat) +int ippr_ipsec_inout(fin, aps, nat) fr_info_t *fin; -ip_t *ip; ap_session_t *aps; nat_t *nat; { ipsec_pxy_t *ipsec; fr_info_t fi; + ip_t *ip; int p; - bcopy((char *)fin, (char *)&fi, sizeof(fi)); - fi.fin_fi.fi_p = IPPROTO_ESP; - fi.fin_fr = &ipsecfr; - fi.fin_data[0] = 0; - fi.fin_data[1] = 0; - p = ip->ip_p; - ip->ip_p = IPPROTO_ESP; - fi.fin_fl &= ~FI_TCPUDP; + if ((fin->fin_out == 1) && (nat->nat_dir == NAT_INBOUND)) + return 0; + + if ((fin->fin_out == 0) && (nat->nat_dir == NAT_OUTBOUND)) + return 0; ipsec = aps->aps_data; + if (ipsec != NULL) { + ip = fin->fin_ip; + p = ip->ip_p; + + if ((ipsec->ipsc_nat == NULL) || (ipsec->ipsc_state == NULL)) { + bcopy((char *)fin, (char *)&fi, sizeof(fi)); + fi.fin_state = NULL; + fi.fin_nat = NULL; + fi.fin_fi.fi_p = IPPROTO_ESP; + fi.fin_fr = &ipsecfr; + fi.fin_data[0] = 0; + fi.fin_data[1] = 0; + ip->ip_p = IPPROTO_ESP; + fi.fin_flx &= ~(FI_TCPUDP|FI_STATE|FI_FRAG); + fi.fin_flx |= FI_IGNORE; + } + /* * Update NAT timeout/create NAT if missing. */ - if (ipsec->ipsc_rckset == 0) - nat->nat_age = 60; /* 30 seconds */ if (ipsec->ipsc_nat != NULL) - ipsec->ipsc_nat->nat_age = nat->nat_age; - else - ipsec->ipsc_nat = nat_new(&fi, ip, &ipsec->ipsc_rule, + fr_queueback(&ipsec->ipsc_nat->nat_tqe); + else { + ipsec->ipsc_nat = nat_new(&fi, &ipsec->ipsc_rule, &ipsec->ipsc_nat, - FI_IGNOREPKT, NAT_OUTBOUND); + NAT_SLAVE|SI_WILDP, + nat->nat_dir); + if (ipsec->ipsc_nat != NULL) { + (void) nat_proto(&fi, ipsec->ipsc_nat, 0); + nat_update(&fi, ipsec->ipsc_nat, + &ipsec->ipsc_rule); + } + } /* * Update state timeout/create state if missing. */ READ_ENTER(&ipf_state); if (ipsec->ipsc_state != NULL) { - ipsec->ipsc_state->is_age = nat->nat_age; + fr_queueback(&ipsec->ipsc_state->is_sti); + ipsec->ipsc_state->is_die = nat->nat_age; RWLOCK_EXIT(&ipf_state); } else { RWLOCK_EXIT(&ipf_state); fi.fin_data[0] = 0; fi.fin_data[1] = 0; - ipsec->ipsc_state = fr_addstate(ip, &fi, + ipsec->ipsc_state = fr_addstate(&fi, &ipsec->ipsc_state, - FI_IGNOREPKT|FI_NORULE); + SI_WILDP); + if (fi.fin_state != NULL) + fr_statederef(&fi, (ipstate_t **)&fi.fin_state); } + ip->ip_p = p; } - ip->ip_p = p; return 0; } @@ -220,24 +282,15 @@ nat_t *nat; mb_t *m; int off; - if ((fin->fin_dlen < sizeof(cookies)) || (fin->fin_fl & FI_FRAG)) + nat = nat; /* LINT */ + + if ((fin->fin_dlen < sizeof(cookies)) || (fin->fin_flx & FI_FRAG)) return -1; ipsec = aps->aps_data; off = fin->fin_hlen + sizeof(udphdr_t); -#ifdef _KERNEL -# if SOLARIS - m = fin->fin_qfm; - - copyout_mblk(m, off, sizeof(cookies), (char *)cookies); -# else - m = *(mb_t **)fin->fin_mp; - m_copydata(m, off, sizeof(cookies), (char *)cookies); -# endif -#else - m = *(mb_t **)fin->fin_mp; - bcopy((char *)m + off, cookies, sizeof(cookies)); -#endif + m = fin->fin_m; + COPYDATA(m, off, sizeof(cookies), (char *)cookies); if ((cookies[0] != ipsec->ipsc_icookie[0]) || (cookies[1] != ipsec->ipsc_icookie[1])) @@ -245,7 +298,6 @@ nat_t *nat; if (ipsec->ipsc_rckset == 0) { if ((cookies[2]|cookies[3]) == 0) { - nat->nat_age = 60; /* 30 seconds */ return 0; } ipsec->ipsc_rckset = 1; @@ -273,17 +325,16 @@ ap_session_t *aps; if (ipsec != NULL) { /* - * Don't delete it from here, just schedule it to be - * deleted ASAP. + * Don't bother changing any of the NAT structure details, + * *_del() is on a callback from aps_free(), from nat_delete() */ - if (ipsec->ipsc_nat != NULL) { - ipsec->ipsc_nat->nat_age = 1; - ipsec->ipsc_nat->nat_ptr = NULL; - } READ_ENTER(&ipf_state); - if (ipsec->ipsc_state != NULL) - ipsec->ipsc_state->is_age = 1; + if (ipsec->ipsc_state != NULL) { + ipsec->ipsc_state->is_die = fr_ticks + 1; + ipsec->ipsc_state->is_me = NULL; + fr_queuefront(&ipsec->ipsc_state->is_sti); + } RWLOCK_EXIT(&ipf_state); ipsec->ipsc_state = NULL; diff --git a/sys/contrib/ipfilter/netinet/ip_irc_pxy.c b/sys/contrib/ipfilter/netinet/ip_irc_pxy.c new file mode 100644 index 0000000..0f61d76 --- /dev/null +++ b/sys/contrib/ipfilter/netinet/ip_irc_pxy.c @@ -0,0 +1,435 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2000-2003 Darren Reed + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * Id: ip_irc_pxy.c,v 2.39.2.4 2005/02/04 10:22:55 darrenr Exp + */ + +#define IPF_IRC_PROXY + +#define IPF_IRCBUFSZ 96 /* This *MUST* be >= 64! */ + + +int ippr_irc_init __P((void)); +void ippr_irc_fini __P((void)); +int ippr_irc_new __P((fr_info_t *, ap_session_t *, nat_t *)); +int ippr_irc_out __P((fr_info_t *, ap_session_t *, nat_t *)); +int ippr_irc_send __P((fr_info_t *, nat_t *)); +int ippr_irc_complete __P((ircinfo_t *, char *, size_t)); +u_short ipf_irc_atoi __P((char **)); + +static frentry_t ircnatfr; + +int irc_proxy_init = 0; + + +/* + * Initialize local structures. + */ +int ippr_irc_init() +{ + bzero((char *)&ircnatfr, sizeof(ircnatfr)); + ircnatfr.fr_ref = 1; + ircnatfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; + MUTEX_INIT(&ircnatfr.fr_lock, "IRC proxy rule lock"); + irc_proxy_init = 1; + + return 0; +} + + +void ippr_irc_fini() +{ + if (irc_proxy_init == 1) { + MUTEX_DESTROY(&ircnatfr.fr_lock); + irc_proxy_init = 0; + } +} + + +char *ippr_irc_dcctypes[] = { + "CHAT ", /* CHAT chat ipnumber portnumber */ + "SEND ", /* SEND filename ipnumber portnumber */ + "MOVE ", + "TSEND ", + "SCHAT ", + NULL, +}; + + +/* + * :A PRIVMSG B :^ADCC CHAT chat 0 0^A\r\n + * PRIVMSG B ^ADCC CHAT chat 0 0^A\r\n + */ + + +int ippr_irc_complete(ircp, buf, len) +ircinfo_t *ircp; +char *buf; +size_t len; +{ + register char *s, c; + register size_t i; + u_32_t l; + int j, k; + + ircp->irc_ipnum = 0; + ircp->irc_port = 0; + + if (len < 31) + return 0; + s = buf; + c = *s++; + i = len - 1; + + if ((c != ':') && (c != 'P')) + return 0; + + if (c == ':') { + /* + * Loosely check that the source is a nickname of some sort + */ + s++; + c = *s; + ircp->irc_snick = s; + if (!ISALPHA(c)) + return 0; + i--; + for (c = *s; !ISSPACE(c) && (i > 0); i--) + c = *s++; + if (i < 31) + return 0; + if (c != 'P') + return 0; + } else + ircp->irc_snick = NULL; + + /* + * Check command string + */ + if (strncmp(s, "PRIVMSG ", 8)) + return 0; + i -= 8; + s += 8; + c = *s; + ircp->irc_dnick = s; + + /* + * Loosely check that the destination is a nickname of some sort + */ + if (!ISALPHA(c)) + return 0; + for (; !ISSPACE(c) && (i > 0); i--) + c = *s++; + if (i < 20) + return 0; + s++, + i--; + + /* + * Look for a ^A to start the DCC + */ + c = *s; + if (c == ':') { + s++; + c = *s; + } + + if (strncmp(s, "\001DCC ", 4)) + return 0; + + i -= 4; + s += 4; + + /* + * Check for a recognised DCC command + */ + for (j = 0, k = 0; ippr_irc_dcctypes[j]; j++) { + k = MIN(strlen(ippr_irc_dcctypes[j]), i); + if (!strncmp(ippr_irc_dcctypes[j], s, k)) + break; + } + if (!ippr_irc_dcctypes[j]) + return 0; + + ircp->irc_type = s; + i -= k; + s += k; + + if (i < 11) + return 0; + + /* + * Check for the arg + */ + c = *s; + if (ISSPACE(c)) + return 0; + ircp->irc_arg = s; + for (; (c != ' ') && (c != '\001') && (i > 0); i--) + c = *s++; + + if (c == '\001') /* In reality a ^A can quote another ^A...*/ + return 0; + + if (i < 5) + return 0; + + s++; + i--; + c = *s; + if (!ISDIGIT(c)) + return 0; + ircp->irc_addr = s; + /* + * Get the IP# + */ + for (l = 0; ISDIGIT(c) && (i > 0); i--) { + l *= 10; + l += c - '0'; + c = *s++; + } + + if (i < 4) + return 0; + + if (c != ' ') + return 0; + + ircp->irc_ipnum = l; + s++; + i--; + c = *s; + if (!ISDIGIT(c)) + return 0; + /* + * Get the port# + */ + for (l = 0; ISDIGIT(c) && (i > 0); i--) { + l *= 10; + l += c - '0'; + c = *s++; + } + if (i < 3) + return 0; + if (strncmp(s, "\001\r\n", 3)) + return 0; + s += 3; + ircp->irc_len = s - buf; + ircp->irc_port = l; + return 1; +} + + +int ippr_irc_new(fin, aps, nat) +fr_info_t *fin; +ap_session_t *aps; +nat_t *nat; +{ + ircinfo_t *irc; + + KMALLOC(irc, ircinfo_t *); + if (irc == NULL) + return -1; + + fin = fin; /* LINT */ + nat = nat; /* LINT */ + + aps->aps_data = irc; + aps->aps_psiz = sizeof(ircinfo_t); + + bzero((char *)irc, sizeof(*irc)); + return 0; +} + + +int ippr_irc_send(fin, nat) +fr_info_t *fin; +nat_t *nat; +{ + char ctcpbuf[IPF_IRCBUFSZ], newbuf[IPF_IRCBUFSZ]; + tcphdr_t *tcp, tcph, *tcp2 = &tcph; + int off, inc = 0, i, dlen; + size_t nlen = 0, olen; + struct in_addr swip; + u_short a5, sp; + ircinfo_t *irc; + fr_info_t fi; + nat_t *nat2; + u_int a1; + ip_t *ip; + mb_t *m; +#ifdef MENTAT + mb_t *m1; +#endif + + m = fin->fin_m; + ip = fin->fin_ip; + tcp = (tcphdr_t *)fin->fin_dp; + bzero(ctcpbuf, sizeof(ctcpbuf)); + off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; + +#ifdef __sgi + dlen = fin->fin_plen - off; +#else + dlen = MSGDSIZE(m) - off; +#endif + if (dlen <= 0) + return 0; + COPYDATA(m, off, MIN(sizeof(ctcpbuf), dlen), ctcpbuf); + + if (dlen <= 0) + return 0; + ctcpbuf[sizeof(ctcpbuf) - 1] = '\0'; + *newbuf = '\0'; + + irc = nat->nat_aps->aps_data; + if (ippr_irc_complete(irc, ctcpbuf, dlen) == 0) + return 0; + + /* + * check that IP address in the PORT/PASV reply is the same as the + * sender of the command - prevents using PORT for port scanning. + */ + if (irc->irc_ipnum != ntohl(nat->nat_inip.s_addr)) + return 0; + + a5 = irc->irc_port; + + /* + * Calculate new address parts for the DCC command + */ + a1 = ntohl(ip->ip_src.s_addr); + olen = irc->irc_len; + i = irc->irc_addr - ctcpbuf; + i++; + (void) strncpy(newbuf, ctcpbuf, i); + /* DO NOT change these! */ +#if defined(SNPRINTF) && defined(KERNEL) + SNPRINTF(newbuf, sizeof(newbuf) - i, "%u %u\001\r\n", a1, a5); +#else + (void) sprintf(newbuf, "%u %u\001\r\n", a1, a5); +#endif + + nlen = strlen(newbuf); + inc = nlen - olen; + + if ((inc + ip->ip_len) > 65535) + return 0; + +#ifdef MENTAT + for (m1 = m; m1->b_cont; m1 = m1->b_cont) + ; + if ((inc > 0) && (m1->b_datap->db_lim - m1->b_wptr < inc)) { + mblk_t *nm; + + /* alloc enough to keep same trailer space for lower driver */ + nm = allocb(nlen, BPRI_MED); + PANIC((!nm),("ippr_irc_out: allocb failed")); + + nm->b_band = m1->b_band; + nm->b_wptr += nlen; + + m1->b_wptr -= olen; + PANIC((m1->b_wptr < m1->b_rptr), + ("ippr_irc_out: cannot handle fragmented data block")); + + linkb(m1, nm); + } else { +# if SOLARIS && defined(ICK_VALID) + if (m1->b_datap->db_struiolim == m1->b_wptr) + m1->b_datap->db_struiolim += inc; + m1->b_datap->db_struioflag &= ~STRUIO_IP; +# endif + m1->b_wptr += inc; + } +#else + if (inc < 0) + m_adj(m, inc); + /* the mbuf chain will be extended if necessary by m_copyback() */ +#endif + COPYBACK(m, off, nlen, newbuf); + + if (inc != 0) { +#if defined(MENTAT) || defined(__sgi) + register u_32_t sum1, sum2; + + sum1 = ip->ip_len; + sum2 = ip->ip_len + inc; + + /* Because ~1 == -2, We really need ~1 == -1 */ + if (sum1 > sum2) + sum2--; + sum2 -= sum1; + sum2 = (sum2 & 0xffff) + (sum2 >> 16); + + fix_outcksum(fin, &ip->ip_sum, sum2); +#endif + ip->ip_len += inc; + } + + /* + * Add skeleton NAT entry for connection which will come back the + * other way. + */ + sp = htons(a5); + /* + * Don't allow the PORT command to specify a port < 1024 due to + * security crap. + */ + if (ntohs(sp) < 1024) + return 0; + + /* + * The server may not make the connection back from port 20, but + * it is the most likely so use it here to check for a conflicting + * mapping. + */ + bcopy((caddr_t)fin, (caddr_t)&fi, sizeof(fi)); + fi.fin_data[0] = sp; + fi.fin_data[1] = fin->fin_data[1]; + nat2 = nat_outlookup(fin, IPN_TCP, nat->nat_p, nat->nat_inip, + ip->ip_dst); + if (nat2 == NULL) { + bcopy((caddr_t)fin, (caddr_t)&fi, sizeof(fi)); + bzero((char *)tcp2, sizeof(*tcp2)); + tcp2->th_win = htons(8192); + tcp2->th_sport = sp; + tcp2->th_dport = 0; /* XXX - don't specify remote port */ + fi.fin_state = NULL; + fi.fin_nat = NULL; + fi.fin_data[0] = ntohs(sp); + fi.fin_data[1] = 0; + fi.fin_dp = (char *)tcp2; + fi.fin_fr = &ircnatfr; + fi.fin_dlen = sizeof(*tcp2); + fi.fin_plen = fi.fin_hlen + sizeof(*tcp2); + swip = ip->ip_src; + ip->ip_src = nat->nat_inip; + nat2 = nat_new(&fi, nat->nat_ptr, NULL, + NAT_SLAVE|IPN_TCP|SI_W_DPORT, NAT_OUTBOUND); + if (nat2 != NULL) { + (void) nat_proto(&fi, nat2, 0); + nat_update(&fi, nat2, nat2->nat_ptr); + + (void) fr_addstate(&fi, NULL, SI_W_DPORT); + if (fi.fin_state != NULL) + fr_statederef(&fi, (ipstate_t **)&fi.fin_state); + } + ip->ip_src = swip; + } + return inc; +} + + +int ippr_irc_out(fin, aps, nat) +fr_info_t *fin; +ap_session_t *aps; +nat_t *nat; +{ + aps = aps; /* LINT */ + return ippr_irc_send(fin, nat); +} diff --git a/sys/contrib/ipfilter/netinet/ip_lookup.c b/sys/contrib/ipfilter/netinet/ip_lookup.c new file mode 100644 index 0000000..2f404ee --- /dev/null +++ b/sys/contrib/ipfilter/netinet/ip_lookup.c @@ -0,0 +1,530 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2002-2003 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 +#if defined(__osf__) +# define _PROTO_NET_H_ +#endif +#include <sys/param.h> +#include <sys/errno.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/file.h> +#if __FreeBSD_version >= 220000 && defined(_KERNEL) +# include <sys/fcntl.h> +# include <sys/filio.h> +#else +# include <sys/ioctl.h> +#endif +#if !defined(_KERNEL) +# include <string.h> +# define _KERNEL +# ifdef __OpenBSD__ +struct file; +# endif +# include <sys/uio.h> +# undef _KERNEL +#endif +#include <sys/socket.h> +#if (defined(__osf__) || defined(__hpux) || defined(__sgi)) && defined(_KERNEL) +# ifdef __osf__ +# include <net/radix.h> +# endif +# include "radix_ipf_local.h" +# define _RADIX_H_ +#endif +#include <net/if.h> +#if defined(__FreeBSD__) +# include <sys/cdefs.h> +# include <sys/proc.h> +#endif +#if defined(_KERNEL) +# include <sys/systm.h> +# if !defined(__SVR4) && !defined(__svr4__) +# include <sys/mbuf.h> +# endif +#endif +#include <netinet/in.h> + +#include "netinet/ip_compat.h" +#include "netinet/ip_fil.h" +#include "netinet/ip_pool.h" +#include "netinet/ip_htable.h" +#include "netinet/ip_lookup.h" +/* END OF INCLUDES */ + +#if !defined(lint) +static const char rcsid[] = "@(#)Id: ip_lookup.c,v 2.35.2.5 2004/07/06 11:16:25 darrenr Exp"; +#endif + +#ifdef IPFILTER_LOOKUP +int ip_lookup_inited = 0; + +static int iplookup_addnode __P((caddr_t)); +static int iplookup_delnode __P((caddr_t data)); +static int iplookup_addtable __P((caddr_t)); +static int iplookup_deltable __P((caddr_t)); +static int iplookup_stats __P((caddr_t)); +static int iplookup_flush __P((caddr_t)); + + +/* ------------------------------------------------------------------------ */ +/* Function: iplookup_init */ +/* Returns: int - 0 = success, else error */ +/* Parameters: Nil */ +/* */ +/* Initialise all of the subcomponents of the lookup infrstructure. */ +/* ------------------------------------------------------------------------ */ +int ip_lookup_init() +{ + + if (ip_pool_init() == -1) + return -1; + + RWLOCK_INIT(&ip_poolrw, "ip pool rwlock"); + + ip_lookup_inited = 1; + + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: iplookup_unload */ +/* Returns: int - 0 = success, else error */ +/* Parameters: Nil */ +/* */ +/* Free up all pool related memory that has been allocated whilst IPFilter */ +/* has been running. Also, do any other deinitialisation required such */ +/* ip_lookup_init() can be called again, safely. */ +/* ------------------------------------------------------------------------ */ +void ip_lookup_unload() +{ + ip_pool_fini(); + fr_htable_unload(); + + if (ip_lookup_inited == 1) { + RW_DESTROY(&ip_poolrw); + ip_lookup_inited = 0; + } +} + + +/* ------------------------------------------------------------------------ */ +/* Function: iplookup_ioctl */ +/* Returns: int - 0 = success, else error */ +/* Parameters: data(IO) - pointer to ioctl data to be copied to/from user */ +/* space. */ +/* cmd(I) - ioctl command number */ +/* mode(I) - file mode bits used with open */ +/* */ +/* Handle ioctl commands sent to the ioctl device. For the most part, this */ +/* involves just calling another function to handle the specifics of each */ +/* command. */ +/* ------------------------------------------------------------------------ */ +int ip_lookup_ioctl(data, cmd, mode) +caddr_t data; +ioctlcmd_t cmd; +int mode; +{ + int err; +# if defined(_KERNEL) && !defined(MENTAT) && defined(USE_SPL) + int s; +# endif + + mode = mode; /* LINT */ + + SPL_NET(s); + + switch (cmd) + { + case SIOCLOOKUPADDNODE : + case SIOCLOOKUPADDNODEW : + WRITE_ENTER(&ip_poolrw); + err = iplookup_addnode(data); + RWLOCK_EXIT(&ip_poolrw); + break; + + case SIOCLOOKUPDELNODE : + case SIOCLOOKUPDELNODEW : + WRITE_ENTER(&ip_poolrw); + err = iplookup_delnode(data); + RWLOCK_EXIT(&ip_poolrw); + break; + + case SIOCLOOKUPADDTABLE : + WRITE_ENTER(&ip_poolrw); + err = iplookup_addtable(data); + RWLOCK_EXIT(&ip_poolrw); + break; + + case SIOCLOOKUPDELTABLE : + WRITE_ENTER(&ip_poolrw); + err = iplookup_deltable(data); + RWLOCK_EXIT(&ip_poolrw); + break; + + case SIOCLOOKUPSTAT : + case SIOCLOOKUPSTATW : + WRITE_ENTER(&ip_poolrw); + err = iplookup_stats(data); + RWLOCK_EXIT(&ip_poolrw); + break; + + case SIOCLOOKUPFLUSH : + WRITE_ENTER(&ip_poolrw); + err = iplookup_flush(data); + RWLOCK_EXIT(&ip_poolrw); + break; + + default : + err = EINVAL; + break; + } + SPL_X(s); + return err; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: iplookup_addnode */ +/* Returns: int - 0 = success, else error */ +/* Parameters: data(I) - pointer to data from ioctl call */ +/* */ +/* Add a new data node to a lookup structure. First, check to see if the */ +/* parent structure refered to by name exists and if it does, then go on to */ +/* add a node to it. */ +/* ------------------------------------------------------------------------ */ +static int iplookup_addnode(data) +caddr_t data; +{ + ip_pool_node_t node, *m; + iplookupop_t op; + iphtable_t *iph; + iphtent_t hte; + ip_pool_t *p; + int err; + + err = 0; + BCOPYIN(data, &op, sizeof(op)); + op.iplo_name[sizeof(op.iplo_name) - 1] = '\0'; + + switch (op.iplo_type) + { + case IPLT_POOL : + if (op.iplo_size != sizeof(node)) + return EINVAL; + + err = COPYIN(op.iplo_struct, &node, sizeof(node)); + if (err != 0) + return EFAULT; + + p = ip_pool_find(op.iplo_unit, op.iplo_name); + if (p == NULL) + return ESRCH; + + /* + * add an entry to a pool - return an error if it already + * exists remove an entry from a pool - if it exists + * - in both cases, the pool *must* exist! + */ + m = ip_pool_findeq(p, &node.ipn_addr, &node.ipn_mask); + if (m) + return EEXIST; + err = ip_pool_insert(p, &node.ipn_addr.adf_addr, + &node.ipn_mask.adf_addr, node.ipn_info); + break; + + case IPLT_HASH : + if (op.iplo_size != sizeof(hte)) + return EINVAL; + + err = COPYIN(op.iplo_struct, &hte, sizeof(hte)); + if (err != 0) + return EFAULT; + + iph = fr_findhtable(op.iplo_unit, op.iplo_name); + if (iph == NULL) + return ESRCH; + err = fr_addhtent(iph, &hte); + break; + + default : + err = EINVAL; + break; + } + return err; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: iplookup_delnode */ +/* Returns: int - 0 = success, else error */ +/* Parameters: data(I) - pointer to data from ioctl call */ +/* */ +/* Delete a node from a lookup table by first looking for the table it is */ +/* in and then deleting the entry that gets found. */ +/* ------------------------------------------------------------------------ */ +static int iplookup_delnode(data) +caddr_t data; +{ + ip_pool_node_t node, *m; + iplookupop_t op; + iphtable_t *iph; + iphtent_t hte; + ip_pool_t *p; + int err; + + err = 0; + BCOPYIN(data, &op, sizeof(op)); + + op.iplo_name[sizeof(op.iplo_name) - 1] = '\0'; + + switch (op.iplo_type) + { + case IPLT_POOL : + if (op.iplo_size != sizeof(node)) + return EINVAL; + + err = COPYIN(op.iplo_struct, &node, sizeof(node)); + if (err != 0) + return EFAULT; + + p = ip_pool_find(op.iplo_unit, op.iplo_name); + if (!p) + return ESRCH; + + m = ip_pool_findeq(p, &node.ipn_addr, &node.ipn_mask); + if (m == NULL) + return ENOENT; + err = ip_pool_remove(p, m); + break; + + case IPLT_HASH : + if (op.iplo_size != sizeof(hte)) + return EINVAL; + + err = COPYIN(op.iplo_struct, &hte, sizeof(hte)); + if (err != 0) + return EFAULT; + + iph = fr_findhtable(op.iplo_unit, op.iplo_name); + if (iph == NULL) + return ESRCH; + err = fr_delhtent(iph, &hte); + break; + + default : + err = EINVAL; + break; + } + return err; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: iplookup_addtable */ +/* Returns: int - 0 = success, else error */ +/* Parameters: data(I) - pointer to data from ioctl call */ +/* */ +/* Create a new lookup table, if one doesn't already exist using the name */ +/* for this one. */ +/* ------------------------------------------------------------------------ */ +static int iplookup_addtable(data) +caddr_t data; +{ + iplookupop_t op; + int err; + + err = 0; + BCOPYIN(data, &op, sizeof(op)); + + op.iplo_name[sizeof(op.iplo_name) - 1] = '\0'; + + switch (op.iplo_type) + { + case IPLT_POOL : + if (ip_pool_find(op.iplo_unit, op.iplo_name) != NULL) + err = EEXIST; + else + err = ip_pool_create(&op); + break; + + case IPLT_HASH : + if (fr_findhtable(op.iplo_unit, op.iplo_name) != NULL) + err = EEXIST; + else + err = fr_newhtable(&op); + break; + + default : + err = EINVAL; + break; + } + return err; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: iplookup_deltable */ +/* Returns: int - 0 = success, else error */ +/* Parameters: data(I) - pointer to data from ioctl call */ +/* */ +/* Decodes ioctl request to remove a particular hash table or pool and */ +/* calls the relevant function to do the cleanup. */ +/* ------------------------------------------------------------------------ */ +static int iplookup_deltable(data) +caddr_t data; +{ + iplookupop_t op; + int err; + + BCOPYIN(data, &op, sizeof(op)); + op.iplo_name[sizeof(op.iplo_name) - 1] = '\0'; + + if (op.iplo_arg & IPLT_ANON) + op.iplo_arg &= IPLT_ANON; + + /* + * create a new pool - fail if one already exists with + * the same # + */ + switch (op.iplo_type) + { + case IPLT_POOL : + err = ip_pool_destroy(&op); + break; + + case IPLT_HASH : + err = fr_removehtable(&op); + break; + + default : + err = EINVAL; + break; + } + return err; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: iplookup_stats */ +/* Returns: int - 0 = success, else error */ +/* Parameters: data(I) - pointer to data from ioctl call */ +/* */ +/* Copy statistical information from inside the kernel back to user space. */ +/* ------------------------------------------------------------------------ */ +static int iplookup_stats(data) +caddr_t data; +{ + iplookupop_t op; + int err; + + err = 0; + BCOPYIN(data, &op, sizeof(op)); + + switch (op.iplo_type) + { + case IPLT_POOL : + err = ip_pool_statistics(&op); + break; + + case IPLT_HASH : + err = fr_gethtablestat(&op); + break; + + default : + err = EINVAL; + break; + } + return err; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: iplookup_flush */ +/* Returns: int - 0 = success, else error */ +/* Parameters: data(I) - pointer to data from ioctl call */ +/* */ +/* A flush is called when we want to flush all the nodes from a particular */ +/* entry in the hash table/pool or want to remove all groups from those. */ +/* ------------------------------------------------------------------------ */ +static int iplookup_flush(data) +caddr_t data; +{ + int err, unit, num, type; + iplookupflush_t flush; + + err = 0; + BCOPYIN(data, &flush, sizeof(flush)); + + flush.iplf_name[sizeof(flush.iplf_name) - 1] = '\0'; + + unit = flush.iplf_unit; + if ((unit < 0 || unit > IPL_LOGMAX) && (unit != IPLT_ALL)) + return EINVAL; + + type = flush.iplf_type; + err = EINVAL; + num = 0; + + if (type == IPLT_POOL || type == IPLT_ALL) { + err = 0; + num = ip_pool_flush(&flush); + } + + if (type == IPLT_HASH || type == IPLT_ALL) { + err = 0; + num += fr_flushhtable(&flush); + } + + if (err == 0) { + flush.iplf_count = num; + err = COPYOUT(&flush, data, sizeof(flush)); + } + return err; +} + + +void ip_lookup_deref(type, ptr) +int type; +void *ptr; +{ + if (ptr == NULL) + return; + + WRITE_ENTER(&ip_poolrw); + switch (type) + { + case IPLT_POOL : + ip_pool_deref(ptr); + break; + + case IPLT_HASH : + fr_derefhtable(ptr); + break; + } + RWLOCK_EXIT(&ip_poolrw); +} + + +#else /* IPFILTER_LOOKUP */ + +/*ARGSUSED*/ +int ip_lookup_ioctl(data, cmd, mode) +caddr_t data; +ioctlcmd_t cmd; +int mode; +{ + return EIO; +} +#endif /* IPFILTER_LOOKUP */ diff --git a/sys/contrib/ipfilter/netinet/ip_lookup.h b/sys/contrib/ipfilter/netinet/ip_lookup.h new file mode 100644 index 0000000..7d9acad --- /dev/null +++ b/sys/contrib/ipfilter/netinet/ip_lookup.h @@ -0,0 +1,65 @@ +/* $FreeBSD$ */ + + +#ifndef __IP_LOOKUP_H__ +#define __IP_LOOKUP_H__ + +#if defined(__STDC__) || defined(__GNUC__) +# define SIOCLOOKUPADDTABLE _IOWR('r', 60, struct iplookupop) +# define SIOCLOOKUPDELTABLE _IOWR('r', 61, struct iplookupop) +# define SIOCLOOKUPSTAT _IOWR('r', 64, struct iplookupop) +# define SIOCLOOKUPSTATW _IOW('r', 64, struct iplookupop) +# define SIOCLOOKUPFLUSH _IOWR('r', 65, struct iplookupflush) +# define SIOCLOOKUPADDNODE _IOWR('r', 67, struct iplookupop) +# define SIOCLOOKUPADDNODEW _IOW('r', 67, struct iplookupop) +# define SIOCLOOKUPDELNODE _IOWR('r', 68, struct iplookupop) +# define SIOCLOOKUPDELNODEW _IOW('r', 68, struct iplookupop) +#else +# define SIOCLOOKUPADDTABLE _IOWR(r, 60, struct iplookupop) +# define SIOCLOOKUPDELTABLE _IOWR(r, 61, struct iplookupop) +# define SIOCLOOKUPSTAT _IOWR(r, 64, struct iplookupop) +# define SIOCLOOKUPSTATW _IOW(r, 64, struct iplookupop) +# define SIOCLOOKUPFLUSH _IOWR(r, 65, struct iplookupflush) +# define SIOCLOOKUPADDNODE _IOWR(r, 67, struct iplookupop) +# define SIOCLOOKUPADDNODEW _IOW(r, 67, struct iplookupop) +# define SIOCLOOKUPDELNODE _IOWR(r, 68, struct iplookupop) +# define SIOCLOOKUPDELNODEW _IOW(r, 68, struct iplookupop) +#endif + +typedef struct iplookupop { + int iplo_type; /* IPLT_* */ + int iplo_unit; /* IPL_LOG* */ + u_int iplo_arg; + char iplo_name[FR_GROUPLEN]; + size_t iplo_size; /* sizeof struct at iplo_struct */ + void *iplo_struct; +} iplookupop_t; + +typedef struct iplookupflush { + int iplf_type; /* IPLT_* */ + int iplf_unit; /* IPL_LOG* */ + u_int iplf_arg; + size_t iplf_count; + char iplf_name[FR_GROUPLEN]; +} iplookupflush_t; + +typedef struct iplookuplink { + int ipll_type; /* IPLT_* */ + int ipll_unit; /* IPL_LOG* */ + u_int ipll_num; + char ipll_group[FR_GROUPLEN]; +} iplookuplink_t; + +#define IPLT_ALL -1 +#define IPLT_NONE 0 +#define IPLT_POOL 1 +#define IPLT_HASH 2 + +#define IPLT_ANON 0x80000000 + +extern int ip_lookup_init __P((void)); +extern int ip_lookup_ioctl __P((caddr_t, ioctlcmd_t, int)); +extern void ip_lookup_unload __P((void)); +extern void ip_lookup_deref __P((int, void *)); + +#endif /* __IP_LOOKUP_H__ */ diff --git a/sys/contrib/ipfilter/netinet/ip_netbios_pxy.c b/sys/contrib/ipfilter/netinet/ip_netbios_pxy.c index ee9b0c4..a55cc33 100644 --- a/sys/contrib/ipfilter/netinet/ip_netbios_pxy.c +++ b/sys/contrib/ipfilter/netinet/ip_netbios_pxy.c @@ -1,11 +1,13 @@ +/* $FreeBSD$ */ + /* * Simple netbios-dgm transparent proxy for in-kernel use. * For use with the NAT code. - * $Id: ip_netbios_pxy.c,v 1.1.2.3 2002/01/09 09:28:37 darrenr Exp $ + * Id: ip_netbios_pxy.c,v 2.8 2003/12/01 02:52:16 darrenr Exp */ /*- - * Copyright (c) 2002 Paul J. Ledbetter III + * Copyright (c) 2002-2003 Paul J. Ledbetter III * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,16 +31,19 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: ip_netbios_pxy.c,v 1.1.2.3 2002/01/09 09:28:37 darrenr Exp $ + * Id: ip_netbios_pxy.c,v 2.8 2003/12/01 02:52:16 darrenr Exp */ #define IPF_NETBIOS_PROXY int ippr_netbios_init __P((void)); -int ippr_netbios_out __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *)); +void ippr_netbios_fini __P((void)); +int ippr_netbios_out __P((fr_info_t *, ap_session_t *, nat_t *)); static frentry_t netbiosfr; +int netbios_proxy_init = 0; + /* * Initialize local structures. */ @@ -47,43 +52,55 @@ int ippr_netbios_init() bzero((char *)&netbiosfr, sizeof(netbiosfr)); netbiosfr.fr_ref = 1; netbiosfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; + MUTEX_INIT(&netbiosfr.fr_lock, "NETBIOS proxy rule lock"); + netbios_proxy_init = 1; + return 0; } -int ippr_netbios_out(fin, ip, aps, nat) + +void ippr_netbios_fini() +{ + if (netbios_proxy_init == 1) { + MUTEX_DESTROY(&netbiosfr.fr_lock); + netbios_proxy_init = 0; + } +} + + +int ippr_netbios_out(fin, aps, nat) fr_info_t *fin; -ip_t *ip; ap_session_t *aps; nat_t *nat; { char dgmbuf[6]; - int off, dlen; udphdr_t *udp; + ip_t *ip; mb_t *m; + aps = aps; /* LINT */ + nat = nat; /* LINT */ + + ip = fin->fin_ip; m = *(mb_t **)fin->fin_mp; off = fin->fin_hlen + sizeof(udphdr_t); -#if SOLARIS - dlen = msgdsize(m); -#else - dlen = mbufchainlen(m); -#endif + dlen = M_LEN(m); dlen -= off; /* * no net bios datagram could possibly be shorter than this */ - if (dlen < 11) + if (dlen < 11) return 0; udp = (udphdr_t *)fin->fin_dp; - /* + /* * move past the * ip header; * udp header; - * 4 bytes into the net bios dgm header. + * 4 bytes into the net bios dgm header. * According to rfc1002, this should be the exact location of * the source address/port */ @@ -99,11 +116,7 @@ nat_t *nat; dgmbuf[5] = (char)((udp->uh_sport >> 8)&0xFF); /* replace data in packet */ -#if SOLARIS - copyin_mblk(m, off, sizeof(dgmbuf), dgmbuf); -#else - m_copyback(m, off, sizeof(dgmbuf), dgmbuf); -#endif + COPYBACK(m, off, sizeof(dgmbuf), dgmbuf); return 0; } diff --git a/sys/contrib/ipfilter/netinet/ip_pool.c b/sys/contrib/ipfilter/netinet/ip_pool.c new file mode 100644 index 0000000..9880c9d --- /dev/null +++ b/sys/contrib/ipfilter/netinet/ip_pool.c @@ -0,0 +1,786 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 1993-2001, 2003 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 +#if defined(__osf__) +# define _PROTO_NET_H_ +#endif +#include <sys/errno.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/file.h> +#if !defined(_KERNEL) && !defined(__KERNEL__) +# include <stdio.h> +# include <stdlib.h> +# include <string.h> +# define _KERNEL +# ifdef __OpenBSD__ +struct file; +# endif +# include <sys/uio.h> +# undef _KERNEL +#else +# include <sys/systm.h> +# if defined(NetBSD) && (__NetBSD_Version__ >= 104000000) +# include <sys/proc.h> +# endif +#endif +#include <sys/time.h> +#if !defined(linux) +# include <sys/protosw.h> +#endif +#include <sys/socket.h> +#if defined(_KERNEL) && (!defined(__SVR4) && !defined(__svr4__)) +# include <sys/mbuf.h> +#endif +#if defined(__SVR4) || defined(__svr4__) +# include <sys/filio.h> +# include <sys/byteorder.h> +# ifdef _KERNEL +# include <sys/dditypes.h> +# endif +# include <sys/stream.h> +# include <sys/kmem.h> +#endif +#if defined(__FreeBSD_version) && (__FreeBSD_version >= 300000) +# include <sys/malloc.h> +#endif + +#if (defined(__osf__) || defined(__hpux) || defined(__sgi)) && defined(_KERNEL) +# ifdef __osf__ +# include <net/radix.h> +# endif +# include "radix_ipf_local.h" +# define _RADIX_H_ +#endif +#include <net/if.h> +#include <netinet/in.h> + +#include "netinet/ip_compat.h" +#include "netinet/ip_fil.h" +#include "netinet/ip_pool.h" + +#if defined(IPFILTER_LOOKUP) && defined(_KERNEL) && \ + ((BSD >= 198911) && !defined(__osf__) && \ + !defined(__hpux) && !defined(__sgi)) +static int rn_freenode __P((struct radix_node *, void *)); +#endif + +/* END OF INCLUDES */ + +#if !defined(lint) +static const char sccsid[] = "@(#)ip_fil.c 2.41 6/5/96 (C) 1993-2000 Darren Reed"; +static const char rcsid[] = "@(#)Id: ip_pool.c,v 2.55.2.12 2005/02/01 04:04:46 darrenr Exp"; +#endif + +#ifdef IPFILTER_LOOKUP + +# ifndef RADIX_NODE_HEAD_LOCK +# define RADIX_NODE_HEAD_LOCK(x) ; +# endif +# ifndef RADIX_NODE_HEAD_UNLOCK +# define RADIX_NODE_HEAD_UNLOCK(x) ; +# endif + +ip_pool_stat_t ipoolstat; +ipfrwlock_t ip_poolrw; + +/* + * Binary tree routines from Sedgewick and enhanced to do ranges of addresses. + * NOTE: Insertion *MUST* be from greatest range to least for it to work! + * These should be replaced, eventually, by something else - most notably a + * interval searching method. The important feature is to be able to find + * the best match. + * + * So why not use a radix tree for this? As the first line implies, it + * has been written to work with a _range_ of addresses. A range is not + * necessarily a match with any given netmask so what we end up dealing + * with is an interval tree. Implementations of these are hard to find + * and the one herein is far from bug free. + * + * Sigh, in the end I became convinced that the bugs the code contained did + * not make it worthwhile not using radix trees. For now the radix tree from + * 4.4 BSD is used, but this is not viewed as a long term solution. + */ +ip_pool_t *ip_pool_list[IPL_LOGSIZE] = { NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL }; + + +#ifdef TEST_POOL +void treeprint __P((ip_pool_t *)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + addrfamily_t a, b; + iplookupop_t op; + ip_pool_t *ipo; + i6addr_t ip; + + RWLOCK_INIT(&ip_poolrw, "poolrw"); + ip_pool_init(); + + bzero((char *)&a, sizeof(a)); + bzero((char *)&b, sizeof(b)); + bzero((char *)&ip, sizeof(ip)); + bzero((char *)&op, sizeof(op)); + strcpy(op.iplo_name, "0"); + + if (ip_pool_create(&op) == 0) + ipo = ip_pool_find(0, "0"); + + a.adf_addr.in4.s_addr = 0x0a010203; + b.adf_addr.in4.s_addr = 0xffffffff; + ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 1); + ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 1); + + a.adf_addr.in4.s_addr = 0x0a000000; + b.adf_addr.in4.s_addr = 0xff000000; + ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 0); + ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 0); + + a.adf_addr.in4.s_addr = 0x0a010100; + b.adf_addr.in4.s_addr = 0xffffff00; + ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 1); + ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 1); + + a.adf_addr.in4.s_addr = 0x0a010200; + b.adf_addr.in4.s_addr = 0xffffff00; + ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 0); + ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 0); + + a.adf_addr.in4.s_addr = 0x0a010000; + b.adf_addr.in4.s_addr = 0xffff0000; + ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 1); + ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 1); + + a.adf_addr.in4.s_addr = 0x0a01020f; + b.adf_addr.in4.s_addr = 0xffffffff; + ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 1); + ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 1); +#ifdef DEBUG_POOL +treeprint(ipo); +#endif + ip.in4.s_addr = 0x0a00aabb; + printf("search(%#x) = %d (0)\n", ip.in4.s_addr, + ip_pool_search(ipo, 4, &ip)); + + ip.in4.s_addr = 0x0a000001; + printf("search(%#x) = %d (0)\n", ip.in4.s_addr, + ip_pool_search(ipo, 4, &ip)); + + ip.in4.s_addr = 0x0a000101; + printf("search(%#x) = %d (0)\n", ip.in4.s_addr, + ip_pool_search(ipo, 4, &ip)); + + ip.in4.s_addr = 0x0a010001; + printf("search(%#x) = %d (1)\n", ip.in4.s_addr, + ip_pool_search(ipo, 4, &ip)); + + ip.in4.s_addr = 0x0a010101; + printf("search(%#x) = %d (1)\n", ip.in4.s_addr, + ip_pool_search(ipo, 4, &ip)); + + ip.in4.s_addr = 0x0a010201; + printf("search(%#x) = %d (0)\n", ip.in4.s_addr, + ip_pool_search(ipo, 4, &ip)); + + ip.in4.s_addr = 0x0a010203; + printf("search(%#x) = %d (1)\n", ip.in4.s_addr, + ip_pool_search(ipo, 4, &ip)); + + ip.in4.s_addr = 0x0a01020f; + printf("search(%#x) = %d (1)\n", ip.in4.s_addr, + ip_pool_search(ipo, 4, &ip)); + + ip.in4.s_addr = 0x0b00aabb; + printf("search(%#x) = %d (-1)\n", ip.in4.s_addr, + ip_pool_search(ipo, 4, &ip)); + +#ifdef DEBUG_POOL +treeprint(ipo); +#endif + + ip_pool_fini(); + + return 0; +} + + +void +treeprint(ipo) +ip_pool_t *ipo; +{ + ip_pool_node_t *c; + + for (c = ipo->ipo_list; c != NULL; c = c->ipn_next) + printf("Node %p(%s) (%#x/%#x) = %d hits %lu\n", + c, c->ipn_name, c->ipn_addr.adf_addr.in4.s_addr, + c->ipn_mask.adf_addr.in4.s_addr, + c->ipn_info, c->ipn_hits); +} +#endif /* TEST_POOL */ + + +/* ------------------------------------------------------------------------ */ +/* Function: ip_pool_init */ +/* Returns: int - 0 = success, else error */ +/* */ +/* Initialise the routing table data structures where required. */ +/* ------------------------------------------------------------------------ */ +int ip_pool_init() +{ + + bzero((char *)&ipoolstat, sizeof(ipoolstat)); + +#if (!defined(_KERNEL) || (BSD < 199306)) + rn_init(); +#endif + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ip_pool_fini */ +/* Returns: int - 0 = success, else error */ +/* Locks: WRITE(ipf_global) */ +/* */ +/* Clean up all the pool data structures allocated and call the cleanup */ +/* function for the radix tree that supports the pools. ip_pool_destroy() is*/ +/* used to delete the pools one by one to ensure they're properly freed up. */ +/* ------------------------------------------------------------------------ */ +void ip_pool_fini() +{ + ip_pool_t *p, *q; + iplookupop_t op; + int i; + + ASSERT(rw_read_locked(&ipf_global.ipf_lk) == 0); + + for (i = 0; i <= IPL_LOGMAX; i++) { + for (q = ip_pool_list[i]; (p = q) != NULL; ) { + op.iplo_unit = i; + (void)strncpy(op.iplo_name, p->ipo_name, + sizeof(op.iplo_name)); + q = p->ipo_next; + (void) ip_pool_destroy(&op); + } + } + +#if (!defined(_KERNEL) || (BSD < 199306)) + rn_fini(); +#endif +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ip_pool_statistics */ +/* Returns: int - 0 = success, else error */ +/* Parameters: op(I) - pointer to lookup operation arguments */ +/* */ +/* Copy the current statistics out into user space, collecting pool list */ +/* pointers as appropriate for later use. */ +/* ------------------------------------------------------------------------ */ +int ip_pool_statistics(op) +iplookupop_t *op; +{ + ip_pool_stat_t stats; + int unit, i, err = 0; + + if (op->iplo_size != sizeof(ipoolstat)) + return EINVAL; + + bcopy((char *)&ipoolstat, (char *)&stats, sizeof(stats)); + unit = op->iplo_unit; + if (unit == IPL_LOGALL) { + for (i = 0; i < IPL_LOGSIZE; i++) + stats.ipls_list[i] = ip_pool_list[i]; + } else if (unit >= 0 && unit < IPL_LOGSIZE) { + if (op->iplo_name[0] != '\0') + stats.ipls_list[unit] = ip_pool_find(unit, + op->iplo_name); + else + stats.ipls_list[unit] = ip_pool_list[unit]; + } else + err = EINVAL; + if (err == 0) + err = COPYOUT(&stats, op->iplo_struct, sizeof(stats)); + return err; +} + + + +/* ------------------------------------------------------------------------ */ +/* Function: ip_pool_find */ +/* Returns: int - 0 = success, else error */ +/* Parameters: ipo(I) - pointer to the pool getting the new node. */ +/* */ +/* Find a matching pool inside the collection of pools for a particular */ +/* device, indicated by the unit number. */ +/* ------------------------------------------------------------------------ */ +void *ip_pool_find(unit, name) +int unit; +char *name; +{ + ip_pool_t *p; + + for (p = ip_pool_list[unit]; p != NULL; p = p->ipo_next) + if (strncmp(p->ipo_name, name, sizeof(p->ipo_name)) == 0) + break; + return p; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ip_pool_findeq */ +/* Returns: int - 0 = success, else error */ +/* Parameters: ipo(I) - pointer to the pool getting the new node. */ +/* addr(I) - pointer to address information to delete */ +/* mask(I) - */ +/* */ +/* Searches for an exact match of an entry in the pool. */ +/* ------------------------------------------------------------------------ */ +ip_pool_node_t *ip_pool_findeq(ipo, addr, mask) +ip_pool_t *ipo; +addrfamily_t *addr, *mask; +{ + struct radix_node *n; +#ifdef USE_SPL + int s; + + SPL_NET(s); +#endif + RADIX_NODE_HEAD_LOCK(ipo->ipo_head); + n = ipo->ipo_head->rnh_lookup(addr, mask, ipo->ipo_head); + RADIX_NODE_HEAD_UNLOCK(ipo->ipo_head); + SPL_X(s); + return (ip_pool_node_t *)n; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ip_pool_search */ +/* Returns: int - 0 == +ve match, -1 == error, 1 == -ve/no match */ +/* Parameters: tptr(I) - pointer to the pool to search */ +/* version(I) - IP protocol version (4 or 6) */ +/* dptr(I) - pointer to address information */ +/* */ +/* Search the pool for a given address and return a search result. */ +/* ------------------------------------------------------------------------ */ +int ip_pool_search(tptr, version, dptr) +void *tptr; +int version; +void *dptr; +{ + struct radix_node *rn; + ip_pool_node_t *m; + i6addr_t *addr; + addrfamily_t v; + ip_pool_t *ipo; + int rv; + + ipo = tptr; + if (ipo == NULL) + return -1; + + rv = 1; + m = NULL; + addr = (i6addr_t *)dptr; + bzero(&v, sizeof(v)); + v.adf_len = offsetof(addrfamily_t, adf_addr); + + if (version == 4) { + v.adf_len += sizeof(addr->in4); + v.adf_addr.in4 = addr->in4; +#ifdef USE_INET6 + } else if (version == 6) { + v.adf_len += sizeof(addr->in6); + v.adf_addr.in6 = addr->in6; +#endif + } else + return -1; + + READ_ENTER(&ip_poolrw); + + RADIX_NODE_HEAD_LOCK(ipo->ipo_head); + rn = ipo->ipo_head->rnh_matchaddr(&v, ipo->ipo_head); + RADIX_NODE_HEAD_UNLOCK(ipo->ipo_head); + + if ((rn != NULL) && ((rn->rn_flags & RNF_ROOT) == 0)) { + m = (ip_pool_node_t *)rn; + ipo->ipo_hits++; + m->ipn_hits++; + rv = m->ipn_info; + } + RWLOCK_EXIT(&ip_poolrw); + return rv; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ip_pool_insert */ +/* Returns: int - 0 = success, else error */ +/* Parameters: ipo(I) - pointer to the pool getting the new node. */ +/* addr(I) - address being added as a node */ +/* mask(I) - netmask to with the node being added */ +/* info(I) - extra information to store in this node. */ +/* Locks: WRITE(ip_poolrw) */ +/* */ +/* Add another node to the pool given by ipo. The three parameters passed */ +/* in (addr, mask, info) shold all be stored in the node. */ +/* ------------------------------------------------------------------------ */ +int ip_pool_insert(ipo, addr, mask, info) +ip_pool_t *ipo; +i6addr_t *addr, *mask; +int info; +{ + struct radix_node *rn; + ip_pool_node_t *x; + + ASSERT(rw_read_locked(&ip_poolrw.ipf_lk) == 0); + + KMALLOC(x, ip_pool_node_t *); + if (x == NULL) { + return ENOMEM; + } + + bzero(x, sizeof(*x)); + + x->ipn_info = info; + (void)strncpy(x->ipn_name, ipo->ipo_name, sizeof(x->ipn_name)); + + bcopy(addr, &x->ipn_addr.adf_addr, sizeof(*addr)); + x->ipn_addr.adf_len = sizeof(x->ipn_addr); + bcopy(mask, &x->ipn_mask.adf_addr, sizeof(*mask)); + x->ipn_mask.adf_len = sizeof(x->ipn_mask); + + RADIX_NODE_HEAD_LOCK(ipo->ipo_head); + rn = ipo->ipo_head->rnh_addaddr(&x->ipn_addr, &x->ipn_mask, + ipo->ipo_head, x->ipn_nodes); + RADIX_NODE_HEAD_UNLOCK(ipo->ipo_head); +#ifdef DEBUG_POOL + printf("Added %p at %p\n", x, rn); +#endif + + if (rn == NULL) { + KFREE(x); + return ENOMEM; + } + + x->ipn_next = ipo->ipo_list; + x->ipn_pnext = &ipo->ipo_list; + if (ipo->ipo_list != NULL) + ipo->ipo_list->ipn_pnext = &x->ipn_next; + ipo->ipo_list = x; + + ipoolstat.ipls_nodes++; + + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ip_pool_create */ +/* Returns: int - 0 = success, else error */ +/* Parameters: op(I) - pointer to iplookup struct with call details */ +/* Locks: WRITE(ip_poolrw) */ +/* */ +/* Creates a new group according to the paramters passed in via the */ +/* iplookupop structure. Does not check to see if the group already exists */ +/* when being inserted - assume this has already been done. If the pool is */ +/* marked as being anonymous, give it a new, unique, identifier. Call any */ +/* other functions required to initialise the structure. */ +/* ------------------------------------------------------------------------ */ +int ip_pool_create(op) +iplookupop_t *op; +{ + char name[FR_GROUPLEN]; + int poolnum, unit; + ip_pool_t *h; + + ASSERT(rw_read_locked(&ip_poolrw.ipf_lk) == 0); + + KMALLOC(h, ip_pool_t *); + if (h == NULL) + return ENOMEM; + bzero(h, sizeof(*h)); + + if (rn_inithead((void **)&h->ipo_head, + offsetof(addrfamily_t, adf_addr) << 3) == 0) { + KFREE(h); + return ENOMEM; + } + + unit = op->iplo_unit; + + if ((op->iplo_arg & IPOOL_ANON) != 0) { + ip_pool_t *p; + + poolnum = IPOOL_ANON; + +#if defined(SNPRINTF) && defined(_KERNEL) + SNPRINTF(name, sizeof(name), "%x", poolnum); +#else + (void)sprintf(name, "%x", poolnum); +#endif + + for (p = ip_pool_list[unit]; p != NULL; ) { + if (strncmp(name, p->ipo_name, + sizeof(p->ipo_name)) == 0) { + poolnum++; +#if defined(SNPRINTF) && defined(_KERNEL) + SNPRINTF(name, sizeof(name), "%x", poolnum); +#else + (void)sprintf(name, "%x", poolnum); +#endif + p = ip_pool_list[unit]; + } else + p = p->ipo_next; + } + + (void)strncpy(h->ipo_name, name, sizeof(h->ipo_name)); + } else { + (void) strncpy(h->ipo_name, op->iplo_name, sizeof(h->ipo_name)); + } + + h->ipo_ref = 1; + h->ipo_list = NULL; + h->ipo_unit = unit; + h->ipo_next = ip_pool_list[unit]; + if (ip_pool_list[unit] != NULL) + ip_pool_list[unit]->ipo_pnext = &h->ipo_next; + h->ipo_pnext = &ip_pool_list[unit]; + ip_pool_list[unit] = h; + + ipoolstat.ipls_pools++; + + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ip_pool_remove */ +/* Returns: int - 0 = success, else error */ +/* Parameters: ipo(I) - pointer to the pool to remove the node from. */ +/* ipe(I) - address being deleted as a node */ +/* Locks: WRITE(ip_poolrw) */ +/* */ +/* Add another node to the pool given by ipo. The three parameters passed */ +/* in (addr, mask, info) shold all be stored in the node. */ +/* ------------------------------------------------------------------------ */ +int ip_pool_remove(ipo, ipe) +ip_pool_t *ipo; +ip_pool_node_t *ipe; +{ + ip_pool_node_t **ipp, *n; + + ASSERT(rw_read_locked(&ip_poolrw.ipf_lk) == 0); + + for (ipp = &ipo->ipo_list; (n = *ipp) != NULL; ipp = &n->ipn_next) { + if (ipe == n) { + *n->ipn_pnext = n->ipn_next; + if (n->ipn_next) + n->ipn_next->ipn_pnext = n->ipn_pnext; + break; + } + } + + if (n == NULL) + return ENOENT; + + RADIX_NODE_HEAD_LOCK(ipo->ipo_head); + ipo->ipo_head->rnh_deladdr(&n->ipn_addr, &n->ipn_mask, + ipo->ipo_head); + RADIX_NODE_HEAD_UNLOCK(ipo->ipo_head); + KFREE(n); + + ipoolstat.ipls_nodes--; + + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ip_pool_destroy */ +/* Returns: int - 0 = success, else error */ +/* Parameters: op(I) - information about the pool to remove */ +/* Locks: WRITE(ip_poolrw) or WRITE(ipf_global) */ +/* */ +/* Search for a pool using paramters passed in and if it's not otherwise */ +/* busy, free it. */ +/* */ +/* NOTE: Because this function is called out of ipldetach() where ip_poolrw */ +/* may not be initialised, we can't use an ASSERT to enforce the locking */ +/* assertion that one of the two (ip_poolrw,ipf_global) is held. */ +/* ------------------------------------------------------------------------ */ +int ip_pool_destroy(op) +iplookupop_t *op; +{ + ip_pool_t *ipo; + + ipo = ip_pool_find(op->iplo_unit, op->iplo_name); + if (ipo == NULL) + return ESRCH; + + if (ipo->ipo_ref != 1) + return EBUSY; + + ip_pool_free(ipo); + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ip_pool_flush */ +/* Returns: int - number of pools deleted */ +/* Parameters: fp(I) - which pool(s) to flush */ +/* Locks: WRITE(ip_poolrw) or WRITE(ipf_global) */ +/* */ +/* Free all pools associated with the device that matches the unit number */ +/* passed in with operation. */ +/* */ +/* NOTE: Because this function is called out of ipldetach() where ip_poolrw */ +/* may not be initialised, we can't use an ASSERT to enforce the locking */ +/* assertion that one of the two (ip_poolrw,ipf_global) is held. */ +/* ------------------------------------------------------------------------ */ +int ip_pool_flush(fp) +iplookupflush_t *fp; +{ + int i, num = 0, unit, err; + ip_pool_t *p, *q; + iplookupop_t op; + + unit = fp->iplf_unit; + + for (i = 0; i <= IPL_LOGMAX; i++) { + if (unit != IPLT_ALL && i != unit) + continue; + for (q = ip_pool_list[i]; (p = q) != NULL; ) { + op.iplo_unit = i; + (void)strncpy(op.iplo_name, p->ipo_name, + sizeof(op.iplo_name)); + q = p->ipo_next; + err = ip_pool_destroy(&op); + if (err == 0) + num++; + else + break; + } + } + return num; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ip_pool_free */ +/* Returns: void */ +/* Parameters: ipo(I) - pointer to pool structure */ +/* Locks: WRITE(ip_poolrw) or WRITE(ipf_global) */ +/* */ +/* Deletes the pool strucutre passed in from the list of pools and deletes */ +/* all of the address information stored in it, including any tree data */ +/* structures also allocated. */ +/* */ +/* NOTE: Because this function is called out of ipldetach() where ip_poolrw */ +/* may not be initialised, we can't use an ASSERT to enforce the locking */ +/* assertion that one of the two (ip_poolrw,ipf_global) is held. */ +/* ------------------------------------------------------------------------ */ +void ip_pool_free(ipo) +ip_pool_t *ipo; +{ + ip_pool_node_t *n; + + RADIX_NODE_HEAD_LOCK(ipo->ipo_head); + while ((n = ipo->ipo_list) != NULL) { + ipo->ipo_head->rnh_deladdr(&n->ipn_addr, &n->ipn_mask, + ipo->ipo_head); + + *n->ipn_pnext = n->ipn_next; + if (n->ipn_next) + n->ipn_next->ipn_pnext = n->ipn_pnext; + + KFREE(n); + + ipoolstat.ipls_nodes--; + } + RADIX_NODE_HEAD_UNLOCK(ipo->ipo_head); + + ipo->ipo_list = NULL; + if (ipo->ipo_next != NULL) + ipo->ipo_next->ipo_pnext = ipo->ipo_pnext; + *ipo->ipo_pnext = ipo->ipo_next; + rn_freehead(ipo->ipo_head); + KFREE(ipo); + + ipoolstat.ipls_pools--; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ip_pool_deref */ +/* Returns: void */ +/* Parameters: ipo(I) - pointer to pool structure */ +/* Locks: WRITE(ip_poolrw) */ +/* */ +/* Drop the number of known references to this pool structure by one and if */ +/* we arrive at zero known references, free it. */ +/* ------------------------------------------------------------------------ */ +void ip_pool_deref(ipo) +ip_pool_t *ipo; +{ + + ASSERT(rw_read_locked(&ip_poolrw.ipf_lk) == 0); + + ipo->ipo_ref--; + if (ipo->ipo_ref == 0) + ip_pool_free(ipo); +} + + +# if defined(_KERNEL) && ((BSD >= 198911) && !defined(__osf__) && \ + !defined(__hpux) && !defined(__sgi)) +static int +rn_freenode(struct radix_node *n, void *p) +{ + struct radix_node_head *rnh = p; + struct radix_node *d; + + d = rnh->rnh_deladdr(n->rn_key, NULL, rnh); + if (d != NULL) { + FreeS(d, max_keylen + 2 * sizeof (*d)); + } + return 0; +} + + +void +rn_freehead(rnh) + struct radix_node_head *rnh; +{ + + RADIX_NODE_HEAD_LOCK(rnh); + (*rnh->rnh_walktree)(rnh, rn_freenode, rnh); + + rnh->rnh_addaddr = NULL; + rnh->rnh_deladdr = NULL; + rnh->rnh_matchaddr = NULL; + rnh->rnh_lookup = NULL; + rnh->rnh_walktree = NULL; + RADIX_NODE_HEAD_UNLOCK(rnh); + + Free(rnh); +} +# endif + +#endif /* IPFILTER_LOOKUP */ diff --git a/sys/contrib/ipfilter/netinet/ip_pool.h b/sys/contrib/ipfilter/netinet/ip_pool.h new file mode 100644 index 0000000..5ddc74e --- /dev/null +++ b/sys/contrib/ipfilter/netinet/ip_pool.h @@ -0,0 +1,87 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 1993-2001, 2003 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * Id: ip_pool.h,v 2.26.2.2 2004/03/23 12:44:34 darrenr Exp + */ + +#ifndef __IP_POOL_H__ +#define __IP_POOL_H__ + +#if defined(_KERNEL) && !defined(__osf__) && !defined(__hpux) && \ + !defined(linux) && !defined(sun) +# include <net/radix.h> +extern void rn_freehead __P((struct radix_node_head *)); +# define FreeS(p, z) KFREES(p, z) +extern int max_keylen; +#else +# if defined(__osf__) || defined(__hpux) +# include "radix_ipf_local.h" +# define radix_mask ipf_radix_mask +# define radix_node ipf_radix_node +# define radix_node_head ipf_radix_node_head +# else +# include "radix_ipf.h" +# endif +#endif +#include "netinet/ip_lookup.h" + +#define IP_POOL_NOMATCH 0 +#define IP_POOL_POSITIVE 1 + +typedef struct ip_pool_node { + struct radix_node ipn_nodes[2]; + addrfamily_t ipn_addr; + addrfamily_t ipn_mask; + int ipn_info; + char ipn_name[FR_GROUPLEN]; + u_long ipn_hits; + struct ip_pool_node *ipn_next, **ipn_pnext; +} ip_pool_node_t; + + +typedef struct ip_pool_s { + struct ip_pool_s *ipo_next; + struct ip_pool_s **ipo_pnext; + struct radix_node_head *ipo_head; + ip_pool_node_t *ipo_list; + u_long ipo_hits; + int ipo_unit; + int ipo_flags; + int ipo_ref; + char ipo_name[FR_GROUPLEN]; +} ip_pool_t; + +#define IPOOL_ANON 0x80000000 + + +typedef struct ip_pool_stat { + u_long ipls_pools; + u_long ipls_tables; + u_long ipls_nodes; + ip_pool_t *ipls_list[IPL_LOGSIZE]; +} ip_pool_stat_t; + + +extern ip_pool_stat_t ipoolstat; +extern ip_pool_t *ip_pool_list[IPL_LOGSIZE]; + +extern int ip_pool_search __P((void *, int, void *)); +extern int ip_pool_init __P((void)); +extern void ip_pool_fini __P((void)); +extern int ip_pool_create __P((iplookupop_t *)); +extern int ip_pool_insert __P((ip_pool_t *, i6addr_t *, i6addr_t *, int)); +extern int ip_pool_remove __P((ip_pool_t *, ip_pool_node_t *)); +extern int ip_pool_destroy __P((iplookupop_t *)); +extern void ip_pool_free __P((ip_pool_t *)); +extern void ip_pool_deref __P((ip_pool_t *)); +extern void *ip_pool_find __P((int, char *)); +extern ip_pool_node_t *ip_pool_findeq __P((ip_pool_t *, + addrfamily_t *, addrfamily_t *)); +extern int ip_pool_flush __P((iplookupflush_t *)); +extern int ip_pool_statistics __P((iplookupop_t *)); + +#endif /* __IP_POOL_H__ */ diff --git a/sys/contrib/ipfilter/netinet/ip_pptp_pxy.c b/sys/contrib/ipfilter/netinet/ip_pptp_pxy.c new file mode 100644 index 0000000..b7ec697 --- /dev/null +++ b/sys/contrib/ipfilter/netinet/ip_pptp_pxy.c @@ -0,0 +1,527 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2002-2003 by Darren Reed + * + * Simple PPTP transparent proxy for in-kernel use. For use with the NAT + * code. + * + * Id: ip_pptp_pxy.c,v 2.10.2.9 2005/03/16 18:17:34 darrenr Exp + * + */ +#define IPF_PPTP_PROXY + +typedef struct pptp_hdr { + u_short pptph_len; + u_short pptph_type; + u_32_t pptph_cookie; +} pptp_hdr_t; + +#define PPTP_MSGTYPE_CTL 1 +#define PPTP_MTCTL_STARTREQ 1 +#define PPTP_MTCTL_STARTREP 2 +#define PPTP_MTCTL_STOPREQ 3 +#define PPTP_MTCTL_STOPREP 4 +#define PPTP_MTCTL_ECHOREQ 5 +#define PPTP_MTCTL_ECHOREP 6 +#define PPTP_MTCTL_OUTREQ 7 +#define PPTP_MTCTL_OUTREP 8 +#define PPTP_MTCTL_INREQ 9 +#define PPTP_MTCTL_INREP 10 +#define PPTP_MTCTL_INCONNECT 11 +#define PPTP_MTCTL_CLEAR 12 +#define PPTP_MTCTL_DISCONNECT 13 +#define PPTP_MTCTL_WANERROR 14 +#define PPTP_MTCTL_LINKINFO 15 + + +int ippr_pptp_init __P((void)); +void ippr_pptp_fini __P((void)); +int ippr_pptp_new __P((fr_info_t *, ap_session_t *, nat_t *)); +void ippr_pptp_del __P((ap_session_t *)); +int ippr_pptp_inout __P((fr_info_t *, ap_session_t *, nat_t *)); +void ippr_pptp_donatstate __P((fr_info_t *, nat_t *, pptp_pxy_t *)); +int ippr_pptp_message __P((fr_info_t *, nat_t *, pptp_pxy_t *, pptp_side_t *)); +int ippr_pptp_nextmessage __P((fr_info_t *, nat_t *, pptp_pxy_t *, int)); +int ippr_pptp_mctl __P((fr_info_t *, nat_t *, pptp_pxy_t *, pptp_side_t *)); + +static frentry_t pptpfr; + +int pptp_proxy_init = 0; +int ippr_pptp_debug = 0; +int ippr_pptp_gretimeout = IPF_TTLVAL(120); /* 2 minutes */ + + +/* + * PPTP application proxy initialization. + */ +int ippr_pptp_init() +{ + bzero((char *)&pptpfr, sizeof(pptpfr)); + pptpfr.fr_ref = 1; + pptpfr.fr_age[0] = ippr_pptp_gretimeout; + pptpfr.fr_age[1] = ippr_pptp_gretimeout; + pptpfr.fr_flags = FR_OUTQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; + MUTEX_INIT(&pptpfr.fr_lock, "PPTP proxy rule lock"); + pptp_proxy_init = 1; + + return 0; +} + + +void ippr_pptp_fini() +{ + if (pptp_proxy_init == 1) { + MUTEX_DESTROY(&pptpfr.fr_lock); + pptp_proxy_init = 0; + } +} + + +/* + * Setup for a new PPTP proxy. + */ +int ippr_pptp_new(fin, aps, nat) +fr_info_t *fin; +ap_session_t *aps; +nat_t *nat; +{ + pptp_pxy_t *pptp; + ipnat_t *ipn; + ip_t *ip; + int off; + + ip = fin->fin_ip; + off = fin->fin_hlen + sizeof(udphdr_t); + + if (nat_outlookup(fin, 0, IPPROTO_GRE, nat->nat_inip, + ip->ip_dst) != NULL) { + if (ippr_pptp_debug > 0) + printf("ippr_pptp_new: GRE session already exists\n"); + return -1; + } + + aps->aps_psiz = sizeof(*pptp); + KMALLOCS(aps->aps_data, pptp_pxy_t *, sizeof(*pptp)); + if (aps->aps_data == NULL) { + if (ippr_pptp_debug > 0) + printf("ippr_pptp_new: malloc for aps_data failed\n"); + return -1; + } + + /* + * Create NAT rule against which the tunnel/transport mapping is + * created. This is required because the current NAT rule does not + * describe GRE but TCP instead. + */ + pptp = aps->aps_data; + bzero((char *)pptp, sizeof(*pptp)); + ipn = &pptp->pptp_rule; + ipn->in_ifps[0] = fin->fin_ifp; + ipn->in_apr = NULL; + ipn->in_use = 1; + ipn->in_hits = 1; + ipn->in_ippip = 1; + if (nat->nat_dir == NAT_OUTBOUND) { + ipn->in_nip = ntohl(nat->nat_outip.s_addr); + ipn->in_outip = fin->fin_saddr; + ipn->in_redir = NAT_MAP; + } else if (nat->nat_dir == NAT_INBOUND) { + ipn->in_nip = 0; + ipn->in_outip = nat->nat_outip.s_addr; + ipn->in_redir = NAT_REDIRECT; + } + ipn->in_inip = nat->nat_inip.s_addr; + ipn->in_inmsk = 0xffffffff; + ipn->in_outmsk = 0xffffffff; + ipn->in_srcip = fin->fin_saddr; + ipn->in_srcmsk = 0xffffffff; + bcopy(nat->nat_ptr->in_ifnames[0], ipn->in_ifnames[0], + sizeof(ipn->in_ifnames[0])); + ipn->in_p = IPPROTO_GRE; + + pptp->pptp_side[0].pptps_wptr = pptp->pptp_side[0].pptps_buffer; + pptp->pptp_side[1].pptps_wptr = pptp->pptp_side[1].pptps_buffer; + return 0; +} + + +void ippr_pptp_donatstate(fin, nat, pptp) +fr_info_t *fin; +nat_t *nat; +pptp_pxy_t *pptp; +{ + fr_info_t fi; + grehdr_t gre; + nat_t *nat2; + u_char p; + ip_t *ip; + + ip = fin->fin_ip; + p = ip->ip_p; + + nat2 = pptp->pptp_nat; + if ((nat2 == NULL) || (pptp->pptp_state == NULL)) { + bcopy((char *)fin, (char *)&fi, sizeof(fi)); + bzero((char *)&gre, sizeof(gre)); + fi.fin_state = NULL; + fi.fin_nat = NULL; + fi.fin_fi.fi_p = IPPROTO_GRE; + fi.fin_fr = &pptpfr; + if ((nat->nat_dir == NAT_OUTBOUND && fin->fin_out) || + (nat->nat_dir == NAT_INBOUND && !fin->fin_out)) { + fi.fin_data[0] = pptp->pptp_call[0]; + fi.fin_data[1] = pptp->pptp_call[1]; + } else { + fi.fin_data[0] = pptp->pptp_call[1]; + fi.fin_data[1] = pptp->pptp_call[0]; + } + ip = fin->fin_ip; + ip->ip_p = IPPROTO_GRE; + fi.fin_flx &= ~(FI_TCPUDP|FI_STATE|FI_FRAG); + fi.fin_flx |= FI_IGNORE; + fi.fin_dp = &gre; + gre.gr_flags = htons(1 << 13); + if (fin->fin_out && nat->nat_dir == NAT_INBOUND) { + fi.fin_fi.fi_saddr = fin->fin_fi.fi_daddr; + fi.fin_fi.fi_daddr = nat->nat_outip.s_addr; + } else if (!fin->fin_out && nat->nat_dir == NAT_OUTBOUND) { + fi.fin_fi.fi_saddr = nat->nat_inip.s_addr; + fi.fin_fi.fi_daddr = fin->fin_fi.fi_saddr; + } + } + + /* + * Update NAT timeout/create NAT if missing. + */ + if (nat2 != NULL) + fr_queueback(&nat2->nat_tqe); + else { + nat2 = nat_new(&fi, &pptp->pptp_rule, &pptp->pptp_nat, + NAT_SLAVE, nat->nat_dir); + pptp->pptp_nat = nat2; + if (nat2 != NULL) { + (void) nat_proto(&fi, nat2, 0); + nat_update(&fi, nat2, nat2->nat_ptr); + } + } + + READ_ENTER(&ipf_state); + if (pptp->pptp_state != NULL) { + fr_queueback(&pptp->pptp_state->is_sti); + RWLOCK_EXIT(&ipf_state); + } else { + RWLOCK_EXIT(&ipf_state); + if (nat->nat_dir == NAT_INBOUND) + fi.fin_fi.fi_daddr = nat2->nat_inip.s_addr; + else + fi.fin_fi.fi_saddr = nat2->nat_inip.s_addr; + fi.fin_ifp = NULL; + pptp->pptp_state = fr_addstate(&fi, &pptp->pptp_state, + 0); + if (fi.fin_state != NULL) + fr_statederef(&fi, (ipstate_t **)&fi.fin_state); + } + ip->ip_p = p; + return; +} + + +/* + * Try and build up the next PPTP message in the TCP stream and if we can + * build it up completely (fits in our buffer) then pass it off to the message + * parsing function. + */ +int ippr_pptp_nextmessage(fin, nat, pptp, rev) +fr_info_t *fin; +nat_t *nat; +pptp_pxy_t *pptp; +int rev; +{ + static char *funcname = "ippr_pptp_nextmessage"; + pptp_side_t *pptps; + u_32_t start, end; + pptp_hdr_t *hdr; + tcphdr_t *tcp; + int dlen, off; + u_short len; + char *msg; + + tcp = fin->fin_dp; + dlen = fin->fin_dlen - (TCP_OFF(tcp) << 2); + start = ntohl(tcp->th_seq); + pptps = &pptp->pptp_side[rev]; + off = (char *)tcp - (char *)fin->fin_ip + (TCP_OFF(tcp) << 2) + + fin->fin_ipoff; + + if (dlen <= 0) + return 0; + /* + * If the complete data packet is before what we expect to see + * "next", just ignore it as the chances are we've already seen it. + * The next if statement following this one really just causes packets + * ahead of what we've seen to be dropped, implying that something in + * the middle went missing and we want to see that first. + */ + end = start + dlen; + if (pptps->pptps_next > end && pptps->pptps_next > start) + return 0; + + if (pptps->pptps_next != start) { + if (ippr_pptp_debug > 5) + printf("%s: next (%x) != start (%x)\n", funcname, + pptps->pptps_next, start); + return -1; + } + + msg = (char *)fin->fin_dp + (TCP_OFF(tcp) << 2); + + while (dlen > 0) { + off += pptps->pptps_bytes; + if (pptps->pptps_gothdr == 0) { + /* + * PPTP has an 8 byte header that inclues the cookie. + * The start of every message should include one and + * it should match 1a2b3c4d. Byte order is ignored, + * deliberately, when printing out the error. + */ + len = MIN(8 - pptps->pptps_bytes, dlen); + COPYDATA(fin->fin_m, off, len, pptps->pptps_wptr); + pptps->pptps_bytes += len; + pptps->pptps_wptr += len; + hdr = (pptp_hdr_t *)pptps->pptps_buffer; + if (pptps->pptps_bytes == 8) { + pptps->pptps_next += 8; + if (ntohl(hdr->pptph_cookie) != 0x1a2b3c4d) { + if (ippr_pptp_debug > 1) + printf("%s: bad cookie (%x)\n", + funcname, + hdr->pptph_cookie); + return -1; + } + } + dlen -= len; + msg += len; + off += len; + + pptps->pptps_gothdr = 1; + len = ntohs(hdr->pptph_len); + pptps->pptps_len = len; + pptps->pptps_nexthdr += len; + + /* + * If a message is too big for the buffer, just set + * the fields for the next message to come along. + * The messages defined in RFC 2637 will not exceed + * 512 bytes (in total length) so this is likely a + * bad data packet, anyway. + */ + if (len > sizeof(pptps->pptps_buffer)) { + if (ippr_pptp_debug > 3) + printf("%s: message too big (%d)\n", + funcname, len); + pptps->pptps_next = pptps->pptps_nexthdr; + pptps->pptps_wptr = pptps->pptps_buffer; + pptps->pptps_gothdr = 0; + pptps->pptps_bytes = 0; + pptps->pptps_len = 0; + break; + } + } + + len = MIN(pptps->pptps_len - pptps->pptps_bytes, dlen); + COPYDATA(fin->fin_m, off, len, pptps->pptps_wptr); + pptps->pptps_bytes += len; + pptps->pptps_wptr += len; + pptps->pptps_next += len; + + if (pptps->pptps_len > pptps->pptps_bytes) + break; + + ippr_pptp_message(fin, nat, pptp, pptps); + pptps->pptps_wptr = pptps->pptps_buffer; + pptps->pptps_gothdr = 0; + pptps->pptps_bytes = 0; + pptps->pptps_len = 0; + + start += len; + msg += len; + dlen -= len; + } + + return 0; +} + + +/* + * handle a complete PPTP message + */ +int ippr_pptp_message(fin, nat, pptp, pptps) +fr_info_t *fin; +nat_t *nat; +pptp_pxy_t *pptp; +pptp_side_t *pptps; +{ + pptp_hdr_t *hdr = (pptp_hdr_t *)pptps->pptps_buffer; + + switch (ntohs(hdr->pptph_type)) + { + case PPTP_MSGTYPE_CTL : + ippr_pptp_mctl(fin, nat, pptp, pptps); + break; + + default : + break; + } + return 0; +} + + +/* + * handle a complete PPTP control message + */ +int ippr_pptp_mctl(fin, nat, pptp, pptps) +fr_info_t *fin; +nat_t *nat; +pptp_pxy_t *pptp; +pptp_side_t *pptps; +{ + u_short *buffer = (u_short *)(pptps->pptps_buffer); + pptp_side_t *pptpo; + + if (pptps == &pptp->pptp_side[0]) + pptpo = &pptp->pptp_side[1]; + else + pptpo = &pptp->pptp_side[0]; + + /* + * Breakout to handle all the various messages. Most are just state + * transition. + */ + switch (ntohs(buffer[4])) + { + case PPTP_MTCTL_STARTREQ : + pptps->pptps_state = PPTP_MTCTL_STARTREQ; + break; + case PPTP_MTCTL_STARTREP : + if (pptpo->pptps_state == PPTP_MTCTL_STARTREQ) + pptps->pptps_state = PPTP_MTCTL_STARTREP; + break; + case PPTP_MTCTL_STOPREQ : + pptps->pptps_state = PPTP_MTCTL_STOPREQ; + break; + case PPTP_MTCTL_STOPREP : + if (pptpo->pptps_state == PPTP_MTCTL_STOPREQ) + pptps->pptps_state = PPTP_MTCTL_STOPREP; + break; + case PPTP_MTCTL_ECHOREQ : + pptps->pptps_state = PPTP_MTCTL_ECHOREQ; + break; + case PPTP_MTCTL_ECHOREP : + if (pptpo->pptps_state == PPTP_MTCTL_ECHOREQ) + pptps->pptps_state = PPTP_MTCTL_ECHOREP; + break; + case PPTP_MTCTL_OUTREQ : + pptps->pptps_state = PPTP_MTCTL_OUTREQ; + break; + case PPTP_MTCTL_OUTREP : + if (pptpo->pptps_state == PPTP_MTCTL_OUTREQ) { + pptps->pptps_state = PPTP_MTCTL_OUTREP; + pptp->pptp_call[0] = buffer[7]; + pptp->pptp_call[1] = buffer[6]; + ippr_pptp_donatstate(fin, nat, pptp); + } + break; + case PPTP_MTCTL_INREQ : + pptps->pptps_state = PPTP_MTCTL_INREQ; + break; + case PPTP_MTCTL_INREP : + if (pptpo->pptps_state == PPTP_MTCTL_INREQ) { + pptps->pptps_state = PPTP_MTCTL_INREP; + pptp->pptp_call[0] = buffer[7]; + pptp->pptp_call[1] = buffer[6]; + ippr_pptp_donatstate(fin, nat, pptp); + } + break; + case PPTP_MTCTL_INCONNECT : + pptps->pptps_state = PPTP_MTCTL_INCONNECT; + break; + case PPTP_MTCTL_CLEAR : + pptps->pptps_state = PPTP_MTCTL_CLEAR; + break; + case PPTP_MTCTL_DISCONNECT : + pptps->pptps_state = PPTP_MTCTL_DISCONNECT; + break; + case PPTP_MTCTL_WANERROR : + pptps->pptps_state = PPTP_MTCTL_WANERROR; + break; + case PPTP_MTCTL_LINKINFO : + pptps->pptps_state = PPTP_MTCTL_LINKINFO; + break; + } + + return 0; +} + + +/* + * For outgoing PPTP packets. refresh timeouts for NAT & state entries, if + * we can. If they have disappeared, recreate them. + */ +int ippr_pptp_inout(fin, aps, nat) +fr_info_t *fin; +ap_session_t *aps; +nat_t *nat; +{ + pptp_pxy_t *pptp; + tcphdr_t *tcp; + int rev; + + if ((fin->fin_out == 1) && (nat->nat_dir == NAT_INBOUND)) + rev = 1; + else if ((fin->fin_out == 0) && (nat->nat_dir == NAT_OUTBOUND)) + rev = 1; + else + rev = 0; + + tcp = (tcphdr_t *)fin->fin_dp; + if ((tcp->th_flags & TH_OPENING) == TH_OPENING) { + pptp = (pptp_pxy_t *)aps->aps_data; + pptp->pptp_side[1 - rev].pptps_next = ntohl(tcp->th_ack); + pptp->pptp_side[1 - rev].pptps_nexthdr = ntohl(tcp->th_ack); + pptp->pptp_side[rev].pptps_next = ntohl(tcp->th_seq) + 1; + pptp->pptp_side[rev].pptps_nexthdr = ntohl(tcp->th_seq) + 1; + } + return ippr_pptp_nextmessage(fin, nat, (pptp_pxy_t *)aps->aps_data, + rev); +} + + +/* + * clean up after ourselves. + */ +void ippr_pptp_del(aps) +ap_session_t *aps; +{ + pptp_pxy_t *pptp; + + pptp = aps->aps_data; + + if (pptp != NULL) { + /* + * Don't bother changing any of the NAT structure details, + * *_del() is on a callback from aps_free(), from nat_delete() + */ + + READ_ENTER(&ipf_state); + if (pptp->pptp_state != NULL) { + pptp->pptp_state->is_die = fr_ticks + 1; + pptp->pptp_state->is_me = NULL; + fr_queuefront(&pptp->pptp_state->is_sti); + } + RWLOCK_EXIT(&ipf_state); + + pptp->pptp_state = NULL; + pptp->pptp_nat = NULL; + } +} diff --git a/sys/contrib/ipfilter/netinet/ip_rpcb_pxy.c b/sys/contrib/ipfilter/netinet/ip_rpcb_pxy.c new file mode 100644 index 0000000..4c01223 --- /dev/null +++ b/sys/contrib/ipfilter/netinet/ip_rpcb_pxy.c @@ -0,0 +1,1460 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 2002-2003 by Ryan Beasley <ryanb@goddamnbastard.org> + * + * 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 <int> <e_ip>/32 port <e_p> -> <i_ip> port <i_p> 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: ip_rpcb_pxy.c,v 2.25.2.3 2005/02/04 10:22:56 darrenr Exp + */ + +#define IPF_RPCB_PROXY + +/* + * Function prototypes + */ +int ippr_rpcb_init __P((void)); +void ippr_rpcb_fini __P((void)); +int ippr_rpcb_new __P((fr_info_t *, ap_session_t *, nat_t *)); +void ippr_rpcb_del __P((ap_session_t *)); +int ippr_rpcb_in __P((fr_info_t *, ap_session_t *, nat_t *)); +int ippr_rpcb_out __P((fr_info_t *, ap_session_t *, nat_t *)); + +static void ippr_rpcb_flush __P((rpcb_session_t *)); +static int ippr_rpcb_decodereq __P((fr_info_t *, nat_t *, + rpcb_session_t *, rpc_msg_t *)); +static int ippr_rpcb_skipauth __P((rpc_msg_t *, xdr_auth_t *, u_32_t **)); +static int ippr_rpcb_insert __P((rpcb_session_t *, rpcb_xact_t *)); +static int ippr_rpcb_xdrrpcb __P((rpc_msg_t *, u_32_t *, rpcb_args_t *)); +static int ippr_rpcb_getuaddr __P((rpc_msg_t *, xdr_uaddr_t *, + u_32_t **)); +static u_int ippr_rpcb_atoi __P((char *)); +static int ippr_rpcb_modreq __P((fr_info_t *, nat_t *, rpc_msg_t *, + mb_t *, u_int)); +static int ippr_rpcb_decoderep __P((fr_info_t *, nat_t *, + rpcb_session_t *, rpc_msg_t *, rpcb_xact_t **)); +static rpcb_xact_t * ippr_rpcb_lookup __P((rpcb_session_t *, u_32_t)); +static void ippr_rpcb_deref __P((rpcb_session_t *, rpcb_xact_t *)); +static int ippr_rpcb_getproto __P((rpc_msg_t *, xdr_proto_t *, + u_32_t **)); +static int ippr_rpcb_getnat __P((fr_info_t *, nat_t *, u_int, u_int)); +static int ippr_rpcb_modv3 __P((fr_info_t *, nat_t *, rpc_msg_t *, + mb_t *, u_int)); +static int ippr_rpcb_modv4 __P((fr_info_t *, nat_t *, rpc_msg_t *, + mb_t *, u_int)); +static void ippr_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. */ + /* XXX rpcbcnt still requires locking. */ + +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: ippr_rpcb_init */ +/* Returns: int - 0 == success */ +/* Parameters: (void) */ +/* */ +/* Initialize the filter rule entry and session limiter. */ +/* -------------------------------------------------------------------- */ +int +ippr_rpcb_init() +{ + 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; + + return(0); +} + +/* -------------------------------------------------------------------- */ +/* Function: ippr_rpcb_fini */ +/* Returns: void */ +/* Parameters: (void) */ +/* */ +/* Destroy rpcbfr's mutex to avoid a lock leak. */ +/* -------------------------------------------------------------------- */ +void +ippr_rpcb_fini() +{ + if (rpcb_proxy_init == 1) { + MUTEX_DESTROY(&rpcbfr.fr_lock); + rpcb_proxy_init = 0; + } +} + +/* -------------------------------------------------------------------- */ +/* Function: ippr_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 +ippr_rpcb_new(fin, aps, nat) + fr_info_t *fin; + ap_session_t *aps; + nat_t *nat; +{ + rpcb_session_t *rs; + + fin = fin; /* LINT */ + nat = nat; /* LINT */ + + 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: ippr_rpcb_del */ +/* Returns: void */ +/* Parameters: aps(I) - pointer to proxy session structure */ +/* */ +/* Free up a session's list of RPCB requests. */ +/* -------------------------------------------------------------------- */ +void +ippr_rpcb_del(aps) + ap_session_t *aps; +{ + rpcb_session_t *rs; + rs = (rpcb_session_t *)aps->aps_data; + + MUTEX_ENTER(&rs->rs_rxlock); + ippr_rpcb_flush(rs); + MUTEX_EXIT(&rs->rs_rxlock); + MUTEX_DESTROY(&rs->rs_rxlock); +} + +/* -------------------------------------------------------------------- */ +/* Function: ippr_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 +ippr_rpcb_in(fin, aps, nat) + 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 = ippr_rpcb_decodereq(fin, nat, rs, rm); + + switch(rv) + { + case -1: + return(APR_ERR(1)); + /*NOTREACHED*/ + break; + case 0: + break; + case 1: + rv = ippr_rpcb_modreq(fin, nat, rm, m, off); + break; + default: + /*CONSTANTCONDITION*/ + IPF_PANIC(1, ("illegal rv %d (ippr_rpcb_req)", rv)); + } + + return(rv); +} + +/* -------------------------------------------------------------------- */ +/* Function: ippr_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 +ippr_rpcb_out(fin, aps, nat) + 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; + + 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; + + /* Send off to decode reply. */ + rv = ippr_rpcb_decoderep(fin, nat, rs, rm, &rx); + + switch(rv) + { + case -1: /* Bad packet */ + if (rx != NULL) { + MUTEX_ENTER(&rs->rs_rxlock); + ippr_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_inip.s_addr != nat->nat_outip.s_addr) { + if (rx->rx_type == RPCB_RES_STRING) + diff = ippr_rpcb_modv3(fin, nat, rm, m, off); + else if (rx->rx_type == RPCB_RES_LIST) + diff = ippr_rpcb_modv4(fin, nat, rm, m, off); + } + break; + default: + /*CONSTANTCONDITION*/ + IPF_PANIC(1, ("illegal rv %d (ippr_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. + */ + ippr_rpcb_deref(rs, rx); + ippr_rpcb_deref(rs, rx); + MUTEX_EXIT(&rs->rs_rxlock); + } + + return(diff); +} + +/* + * Private support subroutines + */ + +/* -------------------------------------------------------------------- */ +/* Function: ippr_rpcb_flush */ +/* Returns: void */ +/* Parameters: rs(I) - pointer to RPCB session structure */ +/* */ +/* Simply flushes the list of outstanding transactions, if any. */ +/* -------------------------------------------------------------------- */ +static void +ippr_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: ippr_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 +ippr_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 (ippr_rpcb_skipauth(rm, &rc->rc_authcred, &p) != 0) + return(-1); + if (ippr_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 (ippr_rpcb_xdrrpcb(rm, p, ra) != 0) + return(-1); + + /* Are the target address & port valid? */ + if ((ra->ra_maddr.xu_ip != nat->nat_outip.s_addr) || + (ra->ra_maddr.xu_port != nat->nat_outport)) + return(-1); + + /* Do we need to rewrite this packet? */ + if ((nat->nat_outip.s_addr != nat->nat_inip.s_addr) || + (nat->nat_outport != nat->nat_inport)) + mod = 1; + break; + default: + return(-1); + } + + MUTEX_ENTER(&rs->rs_rxlock); + if (ippr_rpcb_insert(rs, &rx) != 0) { + MUTEX_EXIT(&rs->rs_rxlock); + return(-1); + } + MUTEX_EXIT(&rs->rs_rxlock); + + return(mod); +} + +/* -------------------------------------------------------------------- */ +/* Function: ippr_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 +ippr_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: ippr_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 +ippr_rpcb_insert(rs, rx) + rpcb_session_t *rs; + rpcb_xact_t *rx; +{ + rpcb_xact_t *rxp; + + rxp = ippr_rpcb_lookup(rs, rx->rx_xid); + if (rxp != NULL) { + ++rxp->rx_ref; + return(0); + } + + if (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; + + return(0); +} + +/* -------------------------------------------------------------------- */ +/* Function: ippr_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 +ippr_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 (ippr_rpcb_getproto(rm, &ra->ra_netid, &p) != 0) + return(-1); + + /* Decode r_maddr. */ + if (ippr_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: ippr_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 +ippr_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 = ippr_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 = ippr_rpcb_atoi(b); + if (t > 255) + return(-1); + pp[d - 4] = t & 0xff; + + return(0); +} + +/* -------------------------------------------------------------------- */ +/* Function: ippr_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 +ippr_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: ippr_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 +ippr_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_inip.s_addr; + p = (char *)&nat->nat_inport; + + /* 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_ip->ip_len += diff; + fin->fin_dlen += diff; + fin->fin_plen += diff; + /* XXX Storage lengths. */ + } + + return(diff); +} + +/* -------------------------------------------------------------------- */ +/* Function: ippr_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 +ippr_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 = ippr_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 (ippr_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 (ippr_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 (ippr_rpcb_getuaddr(rm, &rr->rr_v3, &p) != 0) + return(-1); + + /* Validate the IP address and port contained. */ + if (nat->nat_inip.s_addr != rr->rr_v3.xu_ip) + return(-1); + + /* Create NAT & state table entries. */ + if (ippr_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 (ippr_rpcb_getuaddr(rm, &re->re_maddr, &p) != 0) + return(-1); + if (ippr_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 (ippr_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 = ippr_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: ippr_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 * +ippr_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: ippr_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 +ippr_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; +} + +/* -------------------------------------------------------------------- */ +/* Function: ippr_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 +ippr_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: ippr_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 +ippr_rpcb_getnat(fin, nat, proto, port) + fr_info_t *fin; + nat_t *nat; + u_int proto; + u_int port; +{ + 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_src = fin->fin_dst; + fi.fin_dst = nat->nat_outip; + fi.fin_p = proto; + fi.fin_sport = 0; + fi.fin_dport = port & 0xffff; + fi.fin_flx |= FI_IGNORE; + + 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 = fr_stlookup(&fi, &tcp, NULL); + if (is != NULL) + RWLOCK_EXIT(&ipf_state); + + RWLOCK_EXIT(&ipf_nat); + + WRITE_ENTER(&ipf_nat); + natl = nat_inlookup(&fi, nflags, proto, fi.fin_src, fi.fin_dst); + + if ((natl != NULL) && (is != NULL)) { + MUTEX_DOWNGRADE(&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) { + /* 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_p = proto; + ipn->in_pmin = htons(fi.fin_dport); + ipn->in_pmax = htons(fi.fin_dport); + ipn->in_pnext = htons(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 = '\0'; + + /* Create NAT entry. return NULL if this fails. */ + natl = nat_new(&fi, ipn, NULL, nflags|SI_CLONE|NAT_SLAVE, + NAT_INBOUND); + + bcopy((char *)&ipnat, (char *)ipn, sizeof(ipnat)); + + if (natl == NULL) { + MUTEX_DOWNGRADE(&ipf_nat); + return(-1); + } + + ipn->in_use++; + (void) nat_proto(&fi, natl, nflags); + nat_update(&fi, natl, natl->nat_ptr); + } + MUTEX_DOWNGRADE(&ipf_nat); + + if (is == NULL) { + /* Create state entry. Return NULL if this fails. */ + fi.fin_dst = nat->nat_inip; + fi.fin_nat = (void *)natl; + fi.fin_flx |= FI_NATED; + fi.fin_flx &= ~FI_STATE; + nflags &= NAT_TCPUDP; + nflags |= SI_W_SPORT|SI_CLONE; + + is = fr_addstate(&fi, NULL, nflags); + if (is == NULL) { + /* + * XXX nat_delete is private to ip_nat.c. Should + * check w/ Darren about this one. + * + * nat_delete(natl, NL_EXPIRE); + */ + return(-1); + } + if (fi.fin_state != NULL) + fr_statederef(&fi, (ipstate_t **)&fi.fin_state); + } + + return(0); +} + +/* -------------------------------------------------------------------- */ +/* Function: ippr_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 +ippr_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_outip.s_addr; + 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) + ippr_rpcb_fixlen(fin, diff); + + return(diff); +} + +/* -------------------------------------------------------------------- */ +/* Function: ippr_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 +ippr_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_outip.s_addr; + + /* 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) + ippr_rpcb_fixlen(fin, diff); + + return(diff); +} + + +/* -------------------------------------------------------------------- */ +/* Function: ippr_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 +ippr_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_ip->ip_len += len; + fin->fin_dlen += len; + fin->fin_plen += len; +} + +#undef B diff --git a/sys/contrib/ipfilter/netinet/ip_rules.c b/sys/contrib/ipfilter/netinet/ip_rules.c new file mode 100644 index 0000000..f080ec5b --- /dev/null +++ b/sys/contrib/ipfilter/netinet/ip_rules.c @@ -0,0 +1,229 @@ +/* $FreeBSD$ */ + +/* +* Copyright (C) 1993-2000 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 <sys/types.h> +#include <sys/time.h> +#include <sys/socket.h> +#if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__sgi) +# include <sys/systm.h> +#endif +#include <sys/errno.h> +#include <sys/param.h> +#if !defined(__SVR4) && !defined(__svr4__) && !defined(__hpux) +# include <sys/mbuf.h> +#endif +#if defined(__FreeBSD__) && (__FreeBSD_version > 220000) +# include <sys/sockio.h> +#else +# include <sys/ioctl.h> +#endif /* FreeBSD */ +#include <net/if.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/tcp.h> +#include "netinet/ip_compat.h" +#include "netinet/ip_fil.h" + +#include "netinet/ip_rules.h" + +#ifndef _KERNEL +# include <string.h> +#endif /* _KERNEL */ + +#ifdef IPFILTER_COMPILED + +static u_long in_rule__0[] = { +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xffffffff, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80000000, 0x8002, 0, 0, 0, 0xffff, 0, 0, 0x4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static u_long out_rule__0[] = { +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xffffffff, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80000000, 0x4002, 0, 0, 0, 0xffff, 0, 0, 0x4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +frentry_t *ipf_rules_in_[1] = { + (frentry_t *)&in_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 *ipf_rules_out_[1] = { + (frentry_t *)&out_rule__0 +}; + +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_group, + ipf_rules_out_[j]->fr_group, + FR_GROUPLEN) == 0) { + fp->fr_next = ipf_rules_out_[j]; + break; + } + } + + fp = &ipfrule_out_; + bzero((char *)fp, sizeof(*fp)); + fp->fr_type = FR_T_CALLFUNC|FR_T_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_v = 4; + fp->fr_func = (ipfunc_t)ipfrule_match_out_; + err = frrequest(IPL_LOGIPF, SIOCADDFR, (caddr_t)fp, fr_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(IPL_LOGIPF, SIOCDELFR, + (caddr_t)&ipfrule_out_, fr_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_group, + ipf_rules_in_[j]->fr_group, + FR_GROUPLEN) == 0) { + fp->fr_next = ipf_rules_in_[j]; + break; + } + } + + fp = &ipfrule_in_; + bzero((char *)fp, sizeof(*fp)); + fp->fr_type = FR_T_CALLFUNC|FR_T_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_v = 4; + fp->fr_func = (ipfunc_t)ipfrule_match_in_; + err = frrequest(IPL_LOGIPF, SIOCADDFR, (caddr_t)fp, fr_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(IPL_LOGIPF, SIOCDELFR, + (caddr_t)&ipfrule_in_, fr_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 */ diff --git a/sys/contrib/ipfilter/netinet/ip_rules.h b/sys/contrib/ipfilter/netinet/ip_rules.h new file mode 100644 index 0000000..37e3646 --- /dev/null +++ b/sys/contrib/ipfilter/netinet/ip_rules.h @@ -0,0 +1,16 @@ +/* $FreeBSD$ */ + +extern int ipfrule_add __P((void)); +extern int ipfrule_remove __P((void)); + +extern frentry_t *ipfrule_match_out_ __P((fr_info_t *, u_32_t *)); +extern frentry_t *ipf_rules_out_[1]; + +extern int ipfrule_add_out_ __P((void)); +extern int ipfrule_remove_out_ __P((void)); + +extern frentry_t *ipfrule_match_in_ __P((fr_info_t *, u_32_t *)); +extern frentry_t *ipf_rules_in_[1]; + +extern int ipfrule_add_in_ __P((void)); +extern int ipfrule_remove_in_ __P((void)); diff --git a/sys/contrib/ipfilter/netinet/ip_scan.c b/sys/contrib/ipfilter/netinet/ip_scan.c new file mode 100644 index 0000000..b36fccf --- /dev/null +++ b/sys/contrib/ipfilter/netinet/ip_scan.c @@ -0,0 +1,594 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 1995-2001 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 <sys/param.h> +#if defined(__hpux) && (HPUXREV >= 1111) && !defined(_KERNEL) +# include <sys/kern_svcs.h> +#endif +#include <sys/types.h> +#include <sys/time.h> +#include <sys/errno.h> +#if !defined(_KERNEL) +# include <stdlib.h> +# include <string.h> +# define _KERNEL +# ifdef __OpenBSD__ +struct file; +# endif +# include <sys/uio.h> +# undef _KERNEL +#else +# include <sys/systm.h> +# if !defined(__svr4__) && !defined(__SVR4) +# include <sys/mbuf.h> +# endif +#endif +#include <sys/socket.h> +#if !defined(__hpux) && !defined(__osf__) && !defined(linux) +# include <sys/ioccom.h> +#endif +#ifdef __FreeBSD__ +# include <sys/filio.h> +# include <sys/malloc.h> +#else +# include <sys/ioctl.h> +#endif + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/tcp.h> + +#include <net/if.h> + + +#include "netinet/ip_compat.h" +#include "netinet/ip_fil.h" +#include "netinet/ip_state.h" +#include "netinet/ip_scan.h" +/* END OF INCLUDES */ + +#if !defined(lint) +static const char sccsid[] = "@(#)ip_state.c 1.8 6/5/96 (C) 1993-2000 Darren Reed"; +static const char rcsid[] = "@(#)Id: ip_scan.c,v 2.40.2.2 2005/01/18 10:13:16 darrenr Exp"; +#endif + +#ifdef IPFILTER_SCAN /* endif at bottom of file */ + + +ipscan_t *ipsc_list = NULL, + *ipsc_tail = NULL; +ipscanstat_t ipsc_stat; +# ifdef USE_MUTEXES +ipfrwlock_t ipsc_rwlock; +# endif + +# ifndef isalpha +# define isalpha(x) (((x) >= 'A' && 'Z' >= (x)) || \ + ((x) >= 'a' && 'z' >= (x))) +# endif + + +int ipsc_add __P((caddr_t)); +int ipsc_delete __P((caddr_t)); +struct ipscan *ipsc_lookup __P((char *)); +int ipsc_matchstr __P((sinfo_t *, char *, int)); +int ipsc_matchisc __P((ipscan_t *, ipstate_t *, int, int, int *)); +int ipsc_match __P((ipstate_t *)); + + + +int ipsc_init() +{ + RWLOCK_INIT(&ipsc_rwlock, "ip scan rwlock"); + return 0; +} + + +void fr_scanunload() +{ + RW_DESTROY(&ipsc_rwlock); +} + + +int ipsc_add(data) +caddr_t data; +{ + ipscan_t *i, *isc; + int err; + + KMALLOC(isc, ipscan_t *); + if (!isc) + return ENOMEM; + + err = copyinptr(data, isc, sizeof(*isc)); + if (err) + return err; + + WRITE_ENTER(&ipsc_rwlock); + + i = ipsc_lookup(isc->ipsc_tag); + if (i) { + RWLOCK_EXIT(&ipsc_rwlock); + KFREE(isc); + return EEXIST; + } + + if (ipsc_tail) { + ipsc_tail->ipsc_next = isc; + isc->ipsc_pnext = &ipsc_tail->ipsc_next; + ipsc_tail = isc; + } else { + ipsc_list = isc; + ipsc_tail = isc; + isc->ipsc_pnext = &ipsc_list; + } + isc->ipsc_next = NULL; + + isc->ipsc_hits = 0; + isc->ipsc_fref = 0; + isc->ipsc_sref = 0; + isc->ipsc_active = 0; + + ipsc_stat.iscs_entries++; + RWLOCK_EXIT(&ipsc_rwlock); + return 0; +} + + +int ipsc_delete(data) +caddr_t data; +{ + ipscan_t isc, *i; + int err; + + err = copyinptr(data, &isc, sizeof(isc)); + if (err) + return err; + + WRITE_ENTER(&ipsc_rwlock); + + i = ipsc_lookup(isc.ipsc_tag); + if (i == NULL) + err = ENOENT; + else { + if (i->ipsc_fref) { + RWLOCK_EXIT(&ipsc_rwlock); + return EBUSY; + } + + *i->ipsc_pnext = i->ipsc_next; + if (i->ipsc_next) + i->ipsc_next->ipsc_pnext = i->ipsc_pnext; + else { + if (i->ipsc_pnext == &ipsc_list) + ipsc_tail = NULL; + else + ipsc_tail = *(*i->ipsc_pnext)->ipsc_pnext; + } + + ipsc_stat.iscs_entries--; + KFREE(i); + } + RWLOCK_EXIT(&ipsc_rwlock); + return err; +} + + +struct ipscan *ipsc_lookup(tag) +char *tag; +{ + ipscan_t *i; + + for (i = ipsc_list; i; i = i->ipsc_next) + if (!strcmp(i->ipsc_tag, tag)) + return i; + return NULL; +} + + +int ipsc_attachfr(fr) +struct frentry *fr; +{ + ipscan_t *i; + + if (fr->fr_isctag[0]) { + READ_ENTER(&ipsc_rwlock); + i = ipsc_lookup(fr->fr_isctag); + if (i != NULL) { + ATOMIC_INC32(i->ipsc_fref); + } + RWLOCK_EXIT(&ipsc_rwlock); + if (i == NULL) + return ENOENT; + fr->fr_isc = i; + } + return 0; +} + + +int ipsc_attachis(is) +struct ipstate *is; +{ + frentry_t *fr; + ipscan_t *i; + + READ_ENTER(&ipsc_rwlock); + fr = is->is_rule; + if (fr) { + i = fr->fr_isc; + if (!i || (i != (ipscan_t *)-1)) { + is->is_isc = i; + if (i) { + ATOMIC_INC32(i->ipsc_sref); + if (i->ipsc_clen) + is->is_flags |= IS_SC_CLIENT; + else + is->is_flags |= IS_SC_MATCHC; + if (i->ipsc_slen) + is->is_flags |= IS_SC_SERVER; + else + is->is_flags |= IS_SC_MATCHS; + } else + is->is_flags |= (IS_SC_CLIENT|IS_SC_SERVER); + } + } + RWLOCK_EXIT(&ipsc_rwlock); + return 0; +} + + +int ipsc_detachfr(fr) +struct frentry *fr; +{ + ipscan_t *i; + + i = fr->fr_isc; + if (i != NULL) { + ATOMIC_DEC32(i->ipsc_fref); + } + return 0; +} + + +int ipsc_detachis(is) +struct ipstate *is; +{ + ipscan_t *i; + + READ_ENTER(&ipsc_rwlock); + if ((i = is->is_isc) && (i != (ipscan_t *)-1)) { + ATOMIC_DEC32(i->ipsc_sref); + is->is_isc = NULL; + is->is_flags &= ~(IS_SC_CLIENT|IS_SC_SERVER); + } + RWLOCK_EXIT(&ipsc_rwlock); + return 0; +} + + +/* + * 'string' compare for scanning + */ +int ipsc_matchstr(sp, str, n) +sinfo_t *sp; +char *str; +int n; +{ + char *s, *t, *up; + int i = n; + + if (i > sp->s_len) + i = sp->s_len; + up = str; + + for (s = sp->s_txt, t = sp->s_msk; i; i--, s++, t++, up++) + switch ((int)*t) + { + case '.' : + if (*s != *up) + return 1; + break; + case '?' : + if (!ISALPHA(*up) || ((*s & 0x5f) != (*up & 0x5f))) + return 1; + break; + case '*' : + break; + } + return 0; +} + + +/* + * Returns 3 if both server and client match, 2 if just server, + * 1 if just client + */ +int ipsc_matchisc(isc, is, cl, sl, maxm) +ipscan_t *isc; +ipstate_t *is; +int cl, sl, maxm[2]; +{ + int i, j, k, n, ret = 0, flags; + + flags = is->is_flags; + + /* + * If we've already matched more than what is on offer, then + * assume we have a better match already and forget this one. + */ + if (maxm != NULL) { + if (isc->ipsc_clen < maxm[0]) + return 0; + if (isc->ipsc_slen < maxm[1]) + return 0; + j = maxm[0]; + k = maxm[1]; + } else { + j = 0; + k = 0; + } + + if (!isc->ipsc_clen) + ret = 1; + else if (((flags & (IS_SC_MATCHC|IS_SC_CLIENT)) == IS_SC_CLIENT) && + cl && isc->ipsc_clen) { + i = 0; + n = MIN(cl, isc->ipsc_clen); + if ((n > 0) && (!maxm || (n >= maxm[1]))) { + if (!ipsc_matchstr(&isc->ipsc_cl, is->is_sbuf[0], n)) { + i++; + ret |= 1; + if (n > j) + j = n; + } + } + } + + if (!isc->ipsc_slen) + ret |= 2; + else if (((flags & (IS_SC_MATCHS|IS_SC_SERVER)) == IS_SC_SERVER) && + sl && isc->ipsc_slen) { + i = 0; + n = MIN(cl, isc->ipsc_slen); + if ((n > 0) && (!maxm || (n >= maxm[1]))) { + if (!ipsc_matchstr(&isc->ipsc_sl, is->is_sbuf[1], n)) { + i++; + ret |= 2; + if (n > k) + k = n; + } + } + } + + if (maxm && (ret == 3)) { + maxm[0] = j; + maxm[1] = k; + } + return ret; +} + + +int ipsc_match(is) +ipstate_t *is; +{ + int i, j, k, n, cl, sl, maxm[2]; + ipscan_t *isc, *lm; + tcpdata_t *t; + + for (cl = 0, n = is->is_smsk[0]; n & 1; n >>= 1) + cl++; + for (sl = 0, n = is->is_smsk[1]; n & 1; n >>= 1) + sl++; + + j = 0; + isc = is->is_isc; + if (isc != NULL) { + /* + * Known object to scan for. + */ + i = ipsc_matchisc(isc, is, cl, sl, NULL); + if (i & 1) { + is->is_flags |= IS_SC_MATCHC; + is->is_flags &= ~IS_SC_CLIENT; + } else if (cl >= isc->ipsc_clen) + is->is_flags &= ~IS_SC_CLIENT; + if (i & 2) { + is->is_flags |= IS_SC_MATCHS; + is->is_flags &= ~IS_SC_SERVER; + } else if (sl >= isc->ipsc_slen) + is->is_flags &= ~IS_SC_SERVER; + } else { + i = 0; + lm = NULL; + maxm[0] = 0; + maxm[1] = 0; + for (k = 0, isc = ipsc_list; isc; isc = isc->ipsc_next) { + i = ipsc_matchisc(isc, is, cl, sl, maxm); + if (i) { + /* + * We only want to remember the best match + * and the number of times we get a best + * match. + */ + if ((j == 3) && (i < 3)) + continue; + if ((i == 3) && (j != 3)) + k = 1; + else + k++; + j = i; + lm = isc; + } + } + if (k == 1) + isc = lm; + + /* + * No matches or partial matches, so reset the respective + * search flag. + */ + if (!(j & 1)) + is->is_flags &= ~IS_SC_CLIENT; + + if (!(j & 2)) + is->is_flags &= ~IS_SC_SERVER; + + /* + * If we found the best match, then set flags appropriately. + */ + if ((j == 3) && (k == 1)) { + is->is_flags &= ~(IS_SC_SERVER|IS_SC_CLIENT); + is->is_flags |= (IS_SC_MATCHS|IS_SC_MATCHC); + } + } + + /* + * If the acknowledged side of a connection has moved past the data in + * which we are interested, then reset respective flag. + */ + t = &is->is_tcp.ts_data[0]; + if (t->td_end > is->is_s0[0] + 15) + is->is_flags &= ~IS_SC_CLIENT; + + t = &is->is_tcp.ts_data[1]; + if (t->td_end > is->is_s0[1] + 15) + is->is_flags &= ~IS_SC_SERVER; + + /* + * Matching complete ? + */ + j = ISC_A_NONE; + if ((is->is_flags & IS_SC_MATCHALL) == IS_SC_MATCHALL) { + j = isc->ipsc_action; + ipsc_stat.iscs_acted++; + } else if ((is->is_isc != NULL) && + ((is->is_flags & IS_SC_MATCHALL) != IS_SC_MATCHALL) && + !(is->is_flags & (IS_SC_CLIENT|IS_SC_SERVER))) { + /* + * Matching failed... + */ + j = isc->ipsc_else; + ipsc_stat.iscs_else++; + } + + switch (j) + { + case ISC_A_CLOSE : + /* + * If as a result of a successful match we are to + * close a connection, change the "keep state" info. + * to block packets and generate TCP RST's. + */ + is->is_pass &= ~FR_RETICMP; + is->is_pass |= FR_RETRST; + break; + default : + break; + } + + return i; +} + + +/* + * check if a packet matches what we're scanning for + */ +int ipsc_packet(fin, is) +fr_info_t *fin; +ipstate_t *is; +{ + int i, j, rv, dlen, off, thoff; + u_32_t seq, s0; + tcphdr_t *tcp; + + rv = !IP6_EQ(&fin->fin_fi.fi_src, &is->is_src); + tcp = fin->fin_dp; + seq = ntohl(tcp->th_seq); + + if (!is->is_s0[rv]) + return 1; + + /* + * check if this packet has more data that falls within the first + * 16 bytes sent in either direction. + */ + s0 = is->is_s0[rv]; + off = seq - s0; + if ((off > 15) || (off < 0)) + return 1; + thoff = TCP_OFF(tcp) << 2; + dlen = fin->fin_dlen - thoff; + if (dlen <= 0) + return 1; + if (dlen > 16) + dlen = 16; + if (off + dlen > 16) + dlen = 16 - off; + + j = 0xffff >> (16 - dlen); + i = (0xffff & j) << off; +#ifdef _KERNEL + COPYDATA(*(mb_t **)fin->fin_mp, fin->fin_hlen + thoff, dlen, + (caddr_t)is->is_sbuf[rv] + off); +#endif + is->is_smsk[rv] |= i; + for (j = 0, i = is->is_smsk[rv]; i & 1; i >>= 1) + j++; + if (j == 0) + return 1; + + (void) ipsc_match(is); +#if 0 + /* + * There is the potential here for plain text passwords to get + * buffered and stored for some time... + */ + if (!(is->is_flags & IS_SC_CLIENT)) + bzero(is->is_sbuf[0], sizeof(is->is_sbuf[0])); + if (!(is->is_flags & IS_SC_SERVER)) + bzero(is->is_sbuf[1], sizeof(is->is_sbuf[1])); +#endif + return 0; +} + + +int fr_scan_ioctl(data, cmd, mode) +caddr_t data; +ioctlcmd_t cmd; +int mode; +{ + ipscanstat_t ipscs; + int err = 0; + + switch (cmd) + { + case SIOCADSCA : + err = ipsc_add(data); + break; + case SIOCRMSCA : + err = ipsc_delete(data); + break; + case SIOCGSCST : + bcopy((char *)&ipsc_stat, (char *)&ipscs, sizeof(ipscs)); + ipscs.iscs_list = ipsc_list; + BCOPYOUT(&ipscs, data, sizeof(ipscs)); + break; + default : + err = EINVAL; + break; + } + + return err; +} +#endif /* IPFILTER_SCAN */ diff --git a/sys/contrib/ipfilter/netinet/ip_scan.h b/sys/contrib/ipfilter/netinet/ip_scan.h new file mode 100644 index 0000000..8891367 --- /dev/null +++ b/sys/contrib/ipfilter/netinet/ip_scan.h @@ -0,0 +1,108 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 1993-2001 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * @(#)ip_fil.h 1.35 6/5/96 + * Id: ip_scan.h,v 2.9 2003/07/25 22:05:01 darrenr Exp + */ + +#ifndef __IP_SCAN_H__ +#define __IP_SCAN_H__ 1 + +#ifdef sun +# include <sys/ioccom.h> +#endif + +#define IPSCAN_NAME "/dev/ipscan" +#define IPL_SCAN IPSCAN_NAME +#define ISC_TLEN 16 + + +struct fr_info; +struct frentry; +struct ip; +struct ipstate; + + +#if defined(__STDC__) || defined(__GNUC__) +# define SIOCADSCA _IOWR('r', 60, struct ipscan *) +# define SIOCRMSCA _IOWR('r', 61, struct ipscan *) +# define SIOCGSCST _IOWR('r', 62, struct ipscan *) +#else +# define SIOCADSCA _IOWR(r, 60, struct ipscan *) +# define SIOCRMSCA _IOWR(r, 61, struct ipscan *) +# define SIOCGSCST _IOWR(r, 62, struct ipscan *) +#endif + +struct action { + int act_val; /* what to do */ + struct in_addr act_ip; /* redirect IP# */ + u_short act_port; /* redirect port number */ + int act_else; /* what to do */ + struct in_addr act_eip; /* redirect IP# */ + u_short act_eport; /* redirect port number */ +}; + + +typedef struct sinfo { + char s_txt[ISC_TLEN]; /* text to match */ + char s_msk[ISC_TLEN]; /* mask of the above to check */ + int s_len; /* length of server text */ +} sinfo_t; + + +typedef struct ipscan { + struct ipscan *ipsc_next; + struct ipscan **ipsc_pnext; + char ipsc_tag[ISC_TLEN]; /* table entry protocol tag */ + sinfo_t ipsc_si[2]; /* client/server side information */ + int ipsc_hits; /* times this has been matched */ + int ipsc_active; /* # of active matches */ + int ipsc_fref; /* # of references from filter rules */ + int ipsc_sref; /* # of references from state entries */ + struct action ipsc_act; +} ipscan_t; + + +#define ipsc_cl ipsc_si[0] +#define ipsc_sl ipsc_si[1] +#define ipsc_ctxt ipsc_cl.s_txt +#define ipsc_cmsk ipsc_cl.s_msk +#define ipsc_clen ipsc_cl.s_len +#define ipsc_stxt ipsc_sl.s_txt +#define ipsc_smsk ipsc_sl.s_msk +#define ipsc_slen ipsc_sl.s_len +#define ipsc_action ipsc_act.act_val +#define ipsc_ip ipsc_act.act_ip +#define ipsc_port ipsc_act.act_port +#define ipsc_else ipsc_act.act_else +#define ipsc_eip ipsc_act.act_eip +#define ipsc_eport ipsc_act.act_eport + +#define ISC_A_NONE 0 +#define ISC_A_TRACK 1 +#define ISC_A_CLOSE 2 +#define ISC_A_REDIRECT 3 + + +typedef struct ipscanstat { + struct ipscan *iscs_list; + u_long iscs_acted; + u_long iscs_else; + int iscs_entries; +} ipscanstat_t; + + +extern int fr_scan_ioctl __P((caddr_t, ioctlcmd_t, int)); +extern int ipsc_init __P((void)); +extern int ipsc_attachis __P((struct ipstate *)); +extern int ipsc_attachfr __P((struct frentry *)); +extern int ipsc_detachis __P((struct ipstate *)); +extern int ipsc_detachfr __P((struct frentry *)); +extern int ipsc_packet __P((struct fr_info *, struct ipstate *)); +extern void fr_scanunload __P((void)); + +#endif /* __IP_SCAN_H__ */ diff --git a/sys/contrib/ipfilter/netinet/ip_sync.c b/sys/contrib/ipfilter/netinet/ip_sync.c new file mode 100644 index 0000000..40a027e --- /dev/null +++ b/sys/contrib/ipfilter/netinet/ip_sync.c @@ -0,0 +1,1001 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 1995-1998 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 <sys/errno.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/file.h> +#if !defined(_KERNEL) && !defined(__KERNEL__) +# include <stdio.h> +# include <stdlib.h> +# include <string.h> +# define _KERNEL +# define KERNEL +# ifdef __OpenBSD__ +struct file; +# endif +# include <sys/uio.h> +# undef _KERNEL +# undef KERNEL +#else +# include <sys/systm.h> +# if !defined(__SVR4) && !defined(__svr4__) +# include <sys/mbuf.h> +# endif +#endif +#if defined(__NetBSD__) && (__NetBSD_Version__ >= 104000000) +# include <sys/proc.h> +#endif +#if defined(_KERNEL) && (__FreeBSD_version >= 220000) +# include <sys/filio.h> +# include <sys/fcntl.h> +# if (__FreeBSD_version >= 300000) && !defined(IPFILTER_LKM) +# include "opt_ipfilter.h" +# endif +#else +# include <sys/ioctl.h> +#endif +#include <sys/time.h> +#if !defined(linux) +# include <sys/protosw.h> +#endif +#include <sys/socket.h> +#if defined(__SVR4) || defined(__svr4__) +# include <sys/filio.h> +# include <sys/byteorder.h> +# ifdef _KERNEL +# include <sys/dditypes.h> +# endif +# include <sys/stream.h> +# include <sys/kmem.h> +#endif + +#include <net/if.h> +#ifdef sun +# include <net/af.h> +#endif +#include <net/route.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/tcp.h> +#if !defined(linux) +# include <netinet/ip_var.h> +#endif +#if !defined(__hpux) && !defined(linux) +# include <netinet/tcp_fsm.h> +#endif +#include <netinet/udp.h> +#include <netinet/ip_icmp.h> +#include "netinet/ip_compat.h" +#include <netinet/tcpip.h> +#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_sync.h" +#ifdef USE_INET6 +#include <netinet/icmp6.h> +#endif +#if (__FreeBSD_version >= 300000) +# include <sys/malloc.h> +# if defined(_KERNEL) && !defined(IPFILTER_LKM) +# include <sys/libkern.h> +# include <sys/systm.h> +# endif +#endif +/* END OF INCLUDES */ + +#if !defined(lint) +static const char rcsid[] = "@(#)Id: ip_sync.c,v 2.40.2.3 2005/02/18 13:06:29 darrenr Exp"; +#endif + +#define SYNC_STATETABSZ 256 +#define SYNC_NATTABSZ 256 + +#ifdef IPFILTER_SYNC +ipfmutex_t ipf_syncadd, ipsl_mutex; +ipfrwlock_t ipf_syncstate, ipf_syncnat; +#if SOLARIS && defined(_KERNEL) +kcondvar_t ipslwait; +#endif +synclist_t *syncstatetab[SYNC_STATETABSZ]; +synclist_t *syncnattab[SYNC_NATTABSZ]; +synclogent_t synclog[SYNCLOG_SZ]; +syncupdent_t syncupd[SYNCLOG_SZ]; +u_int ipf_syncnum = 1; +u_int ipf_syncwrap = 0; +u_int sl_idx = 0, /* next available sync log entry */ + su_idx = 0, /* next available sync update entry */ + sl_tail = 0, /* next sync log entry to read */ + su_tail = 0; /* next sync update entry to read */ +int ipf_sync_debug = 0; + + +# if !defined(sparc) && !defined(__hppa) +void ipfsync_tcporder __P((int, struct tcpdata *)); +void ipfsync_natorder __P((int, struct nat *)); +void ipfsync_storder __P((int, struct ipstate *)); +# endif + + +/* ------------------------------------------------------------------------ */ +/* Function: ipfsync_init */ +/* Returns: int - 0 == success, -1 == failure */ +/* Parameters: Nil */ +/* */ +/* Initialise all of the locks required for the sync code and initialise */ +/* any data structures, as required. */ +/* ------------------------------------------------------------------------ */ +int ipfsync_init() +{ + RWLOCK_INIT(&ipf_syncstate, "add things to state sync table"); + RWLOCK_INIT(&ipf_syncnat, "add things to nat sync table"); + MUTEX_INIT(&ipf_syncadd, "add things to sync table"); + MUTEX_INIT(&ipsl_mutex, "add things to sync table"); +# if SOLARIS && defined(_KERNEL) + cv_init(&ipslwait, "ipsl condvar", CV_DRIVER, NULL); +# endif + + bzero((char *)syncnattab, sizeof(syncnattab)); + bzero((char *)syncstatetab, sizeof(syncstatetab)); + + return 0; +} + + +# if !defined(sparc) && !defined(__hppa) +/* ------------------------------------------------------------------------ */ +/* Function: ipfsync_tcporder */ +/* Returns: Nil */ +/* Parameters: way(I) - direction of byte order conversion. */ +/* td(IO) - pointer to data to be converted. */ +/* */ +/* Do byte swapping on values in the TCP state information structure that */ +/* need to be used at both ends by the host in their native byte order. */ +/* ------------------------------------------------------------------------ */ +void ipfsync_tcporder(way, td) +int way; +tcpdata_t *td; +{ + if (way) { + td->td_maxwin = htons(td->td_maxwin); + td->td_end = htonl(td->td_end); + td->td_maxend = htonl(td->td_maxend); + } else { + td->td_maxwin = ntohs(td->td_maxwin); + td->td_end = ntohl(td->td_end); + td->td_maxend = ntohl(td->td_maxend); + } +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipfsync_natorder */ +/* Returns: Nil */ +/* Parameters: way(I) - direction of byte order conversion. */ +/* nat(IO) - pointer to data to be converted. */ +/* */ +/* Do byte swapping on values in the NAT data structure that need to be */ +/* used at both ends by the host in their native byte order. */ +/* ------------------------------------------------------------------------ */ +void ipfsync_natorder(way, n) +int way; +nat_t *n; +{ + if (way) { + n->nat_age = htonl(n->nat_age); + n->nat_flags = htonl(n->nat_flags); + n->nat_ipsumd = htonl(n->nat_ipsumd); + n->nat_use = htonl(n->nat_use); + n->nat_dir = htonl(n->nat_dir); + } else { + n->nat_age = ntohl(n->nat_age); + n->nat_flags = ntohl(n->nat_flags); + n->nat_ipsumd = ntohl(n->nat_ipsumd); + n->nat_use = ntohl(n->nat_use); + n->nat_dir = ntohl(n->nat_dir); + } +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipfsync_storder */ +/* Returns: Nil */ +/* Parameters: way(I) - direction of byte order conversion. */ +/* ips(IO) - pointer to data to be converted. */ +/* */ +/* Do byte swapping on values in the IP state data structure that need to */ +/* be used at both ends by the host in their native byte order. */ +/* ------------------------------------------------------------------------ */ +void ipfsync_storder(way, ips) +int way; +ipstate_t *ips; +{ + ipfsync_tcporder(way, &ips->is_tcp.ts_data[0]); + ipfsync_tcporder(way, &ips->is_tcp.ts_data[1]); + + if (way) { + ips->is_hv = htonl(ips->is_hv); + ips->is_die = htonl(ips->is_die); + ips->is_pass = htonl(ips->is_pass); + ips->is_flags = htonl(ips->is_flags); + ips->is_opt = htonl(ips->is_opt); + ips->is_optmsk = htonl(ips->is_optmsk); + ips->is_sec = htons(ips->is_sec); + ips->is_secmsk = htons(ips->is_secmsk); + ips->is_auth = htons(ips->is_auth); + ips->is_authmsk = htons(ips->is_authmsk); + ips->is_s0[0] = htonl(ips->is_s0[0]); + ips->is_s0[1] = htonl(ips->is_s0[1]); + ips->is_smsk[0] = htons(ips->is_smsk[0]); + ips->is_smsk[1] = htons(ips->is_smsk[1]); + } else { + ips->is_hv = ntohl(ips->is_hv); + ips->is_die = ntohl(ips->is_die); + ips->is_pass = ntohl(ips->is_pass); + ips->is_flags = ntohl(ips->is_flags); + ips->is_opt = ntohl(ips->is_opt); + ips->is_optmsk = ntohl(ips->is_optmsk); + ips->is_sec = ntohs(ips->is_sec); + ips->is_secmsk = ntohs(ips->is_secmsk); + ips->is_auth = ntohs(ips->is_auth); + ips->is_authmsk = ntohs(ips->is_authmsk); + ips->is_s0[0] = ntohl(ips->is_s0[0]); + ips->is_s0[1] = ntohl(ips->is_s0[1]); + ips->is_smsk[0] = ntohl(ips->is_smsk[0]); + ips->is_smsk[1] = ntohl(ips->is_smsk[1]); + } +} +# else /* !defined(sparc) && !defined(__hppa) */ +# define ipfsync_tcporder(x,y) +# define ipfsync_natorder(x,y) +# define ipfsync_storder(x,y) +# endif /* !defined(sparc) && !defined(__hppa) */ + +/* enable this for debugging */ + +# ifdef _KERNEL +/* ------------------------------------------------------------------------ */ +/* Function: ipfsync_write */ +/* Returns: int - 0 == success, else error value. */ +/* Parameters: uio(I) - pointer to information about data to write */ +/* */ +/* Moves data from user space into the kernel and uses it for updating data */ +/* structures in the state/NAT tables. */ +/* ------------------------------------------------------------------------ */ +int ipfsync_write(uio) +struct uio *uio; +{ + synchdr_t sh; + + /* + * THIS MUST BE SUFFICIENT LARGE TO STORE + * ANY POSSIBLE DATA TYPE + */ + char data[2048]; + + int err = 0; + +# if (BSD >= 199306) || defined(__FreeBSD__) || defined(__osf__) + uio->uio_rw = UIO_WRITE; +# endif + + /* Try to get bytes */ + while (uio->uio_resid > 0) { + + if (uio->uio_resid >= sizeof(sh)) { + + err = UIOMOVE((caddr_t)&sh, sizeof(sh), UIO_WRITE, uio); + + if (err) { + if (ipf_sync_debug > 2) + printf("uiomove(header) failed: %d\n", + err); + return err; + } + + /* convert to host order */ + sh.sm_magic = ntohl(sh.sm_magic); + sh.sm_len = ntohl(sh.sm_len); + sh.sm_num = ntohl(sh.sm_num); + + if (ipf_sync_debug > 8) + printf("[%d] Read v:%d p:%d cmd:%d table:%d rev:%d len:%d magic:%x\n", + sh.sm_num, sh.sm_v, sh.sm_p, sh.sm_cmd, + sh.sm_table, sh.sm_rev, sh.sm_len, + sh.sm_magic); + + if (sh.sm_magic != SYNHDRMAGIC) { + if (ipf_sync_debug > 2) + printf("uiomove(header) invalud %s\n", + "magic"); + return EINVAL; + } + + if (sh.sm_v != 4 && sh.sm_v != 6) { + if (ipf_sync_debug > 2) + printf("uiomove(header) invalid %s\n", + "protocol"); + return EINVAL; + } + + if (sh.sm_cmd > SMC_MAXCMD) { + if (ipf_sync_debug > 2) + printf("uiomove(header) invalid %s\n", + "command"); + return EINVAL; + } + + + if (sh.sm_table > SMC_MAXTBL) { + if (ipf_sync_debug > 2) + printf("uiomove(header) invalid %s\n", + "table"); + return EINVAL; + } + + } else { + /* unsufficient data, wait until next call */ + if (ipf_sync_debug > 2) + printf("uiomove(header) insufficient data"); + return EAGAIN; + } + + + /* + * We have a header, so try to read the amount of data + * needed for the request + */ + + /* not supported */ + if (sh.sm_len == 0) { + if (ipf_sync_debug > 2) + printf("uiomove(data zero length %s\n", + "not supported"); + return EINVAL; + } + + if (uio->uio_resid >= sh.sm_len) { + + err = UIOMOVE((caddr_t)data, sh.sm_len, UIO_WRITE, uio); + + if (err) { + if (ipf_sync_debug > 2) + printf("uiomove(data) failed: %d\n", + err); + return err; + } + + if (ipf_sync_debug > 7) + printf("uiomove(data) %d bytes read\n", + sh.sm_len); + + if (sh.sm_table == SMC_STATE) + err = ipfsync_state(&sh, data); + else if (sh.sm_table == SMC_NAT) + err = ipfsync_nat(&sh, data); + if (ipf_sync_debug > 7) + printf("[%d] Finished with error %d\n", + sh.sm_num, err); + + } else { + /* insufficient data, wait until next call */ + if (ipf_sync_debug > 2) + printf("uiomove(data) %s %d bytes, got %d\n", + "insufficient data, need", + sh.sm_len, uio->uio_resid); + return EAGAIN; + } + } + + /* no more data */ + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipfsync_read */ +/* Returns: int - 0 == success, else error value. */ +/* Parameters: uio(O) - pointer to information about where to store data */ +/* */ +/* This function is called when a user program wants to read some data */ +/* for pending state/NAT updates. If no data is available, the caller is */ +/* put to sleep, pending a wakeup from the "lower half" of this code. */ +/* ------------------------------------------------------------------------ */ +int ipfsync_read(uio) +struct uio *uio; +{ + syncupdent_t *su; + synclogent_t *sl; + int err = 0; + + if ((uio->uio_resid & 3) || (uio->uio_resid < 8)) + return EINVAL; + +# if (BSD >= 199306) || defined(__FreeBSD__) || defined(__osf__) + uio->uio_rw = UIO_READ; +# endif + + MUTEX_ENTER(&ipsl_mutex); + while ((sl_tail == sl_idx) && (su_tail == su_idx)) { +# if SOLARIS && defined(_KERNEL) + if (!cv_wait_sig(&ipslwait, &ipsl_mutex)) { + MUTEX_EXIT(&ipsl_mutex); + return EINTR; + } +# else +# ifdef __hpux + { + lock_t *l; + + l = get_sleep_lock(&sl_tail); + err = sleep(&sl_tail, PZERO+1); + spinunlock(l); + } +# else /* __hpux */ +# ifdef __osf__ + err = mpsleep(&sl_tail, PSUSP|PCATCH, "ipl sleep", 0, + &ipsl_mutex, MS_LOCK_SIMPLE); +# else + MUTEX_EXIT(&ipsl_mutex); + err = SLEEP(&sl_tail, "ipl sleep"); +# endif /* __osf__ */ +# endif /* __hpux */ + if (err) { + MUTEX_EXIT(&ipsl_mutex); + return err; + } +# endif /* SOLARIS */ + } + MUTEX_EXIT(&ipsl_mutex); + + READ_ENTER(&ipf_syncstate); + while ((sl_tail < sl_idx) && (uio->uio_resid > sizeof(*sl))) { + sl = synclog + sl_tail++; + err = UIOMOVE((caddr_t)sl, sizeof(*sl), UIO_READ, uio); + if (err != 0) + break; + } + + while ((su_tail < su_idx) && (uio->uio_resid > sizeof(*su))) { + su = syncupd + su_tail; + su_tail++; + err = UIOMOVE((caddr_t)su, sizeof(*su), UIO_READ, uio); + if (err != 0) + break; + if (su->sup_hdr.sm_sl != NULL) + su->sup_hdr.sm_sl->sl_idx = -1; + } + + MUTEX_ENTER(&ipf_syncadd); + if (su_tail == su_idx) + su_tail = su_idx = 0; + if (sl_tail == sl_idx) + sl_tail = sl_idx = 0; + MUTEX_EXIT(&ipf_syncadd); + RWLOCK_EXIT(&ipf_syncstate); + return err; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipfsync_state */ +/* Returns: int - 0 == success, else error value. */ +/* Parameters: sp(I) - pointer to sync packet data header */ +/* uio(I) - pointer to user data for further information */ +/* */ +/* Updates the state table according to information passed in the sync */ +/* header. As required, more data is fetched from the uio structure but */ +/* varies depending on the contents of the sync header. This function can */ +/* create a new state entry or update one. Deletion is left to the state */ +/* structures being timed out correctly. */ +/* ------------------------------------------------------------------------ */ +int ipfsync_state(sp, data) +synchdr_t *sp; +void *data; +{ + synctcp_update_t su; + ipstate_t *is, sn; + synclist_t *sl; + frentry_t *fr; + u_int hv; + int err = 0; + + hv = sp->sm_num & (SYNC_STATETABSZ - 1); + + switch (sp->sm_cmd) + { + case SMC_CREATE : + + bcopy(data, &sn, sizeof(sn)); + KMALLOC(is, ipstate_t *); + if (is == NULL) { + err = ENOMEM; + break; + } + + KMALLOC(sl, synclist_t *); + if (sl == NULL) { + err = ENOMEM; + KFREE(is); + break; + } + + bzero((char *)is, offsetof(ipstate_t, is_die)); + bcopy((char *)&sn.is_die, (char *)&is->is_die, + sizeof(*is) - offsetof(ipstate_t, is_die)); + ipfsync_storder(0, is); + + /* + * We need to find the same rule on the slave as was used on + * the master to create this state entry. + */ + READ_ENTER(&ipf_mutex); + fr = fr_getrulen(IPL_LOGIPF, sn.is_group, sn.is_rulen); + if (fr != NULL) { + MUTEX_ENTER(&fr->fr_lock); + fr->fr_ref++; + fr->fr_statecnt++; + MUTEX_EXIT(&fr->fr_lock); + } + RWLOCK_EXIT(&ipf_mutex); + + if (ipf_sync_debug > 4) + printf("[%d] Filter rules = %p\n", sp->sm_num, fr); + + is->is_rule = fr; + is->is_sync = sl; + + sl->sl_idx = -1; + sl->sl_ips = is; + bcopy(sp, &sl->sl_hdr, sizeof(struct synchdr)); + + WRITE_ENTER(&ipf_syncstate); + WRITE_ENTER(&ipf_state); + + sl->sl_pnext = syncstatetab + hv; + sl->sl_next = syncstatetab[hv]; + if (syncstatetab[hv] != NULL) + syncstatetab[hv]->sl_pnext = &sl->sl_next; + syncstatetab[hv] = sl; + MUTEX_DOWNGRADE(&ipf_syncstate); + fr_stinsert(is, sp->sm_rev); + /* + * Do not initialise the interface pointers for the state + * entry as the full complement of interface names may not + * be present. + * + * Put this state entry on its timeout queue. + */ + /*fr_setstatequeue(is, sp->sm_rev);*/ + break; + + case SMC_UPDATE : + bcopy(data, &su, sizeof(su)); + + if (ipf_sync_debug > 4) + printf("[%d] Update age %lu state %d/%d \n", + sp->sm_num, su.stu_age, su.stu_state[0], + su.stu_state[1]); + + READ_ENTER(&ipf_syncstate); + for (sl = syncstatetab[hv]; (sl != NULL); sl = sl->sl_next) + if (sl->sl_hdr.sm_num == sp->sm_num) + break; + if (sl == NULL) { + if (ipf_sync_debug > 1) + printf("[%d] State not found - can't update\n", + sp->sm_num); + RWLOCK_EXIT(&ipf_syncstate); + err = ENOENT; + break; + } + + READ_ENTER(&ipf_state); + + if (ipf_sync_debug > 6) + printf("[%d] Data from state v:%d p:%d cmd:%d table:%d rev:%d\n", + sp->sm_num, sl->sl_hdr.sm_v, sl->sl_hdr.sm_p, + sl->sl_hdr.sm_cmd, sl->sl_hdr.sm_table, + sl->sl_hdr.sm_rev); + + is = sl->sl_ips; + + MUTEX_ENTER(&is->is_lock); + switch (sp->sm_p) + { + case IPPROTO_TCP : + /* XXX FV --- shouldn't we do ntohl/htonl???? XXX */ + is->is_send = su.stu_data[0].td_end; + is->is_maxsend = su.stu_data[0].td_maxend; + is->is_maxswin = su.stu_data[0].td_maxwin; + is->is_state[0] = su.stu_state[0]; + is->is_dend = su.stu_data[1].td_end; + is->is_maxdend = su.stu_data[1].td_maxend; + is->is_maxdwin = su.stu_data[1].td_maxwin; + is->is_state[1] = su.stu_state[1]; + break; + default : + break; + } + + if (ipf_sync_debug > 6) + printf("[%d] Setting timers for state\n", sp->sm_num); + + fr_setstatequeue(is, sp->sm_rev); + + MUTEX_EXIT(&is->is_lock); + break; + + default : + err = EINVAL; + break; + } + + if (err == 0) { + RWLOCK_EXIT(&ipf_state); + RWLOCK_EXIT(&ipf_syncstate); + } + + if (ipf_sync_debug > 6) + printf("[%d] Update completed with error %d\n", + sp->sm_num, err); + + return err; +} +# endif /* _KERNEL */ + + +/* ------------------------------------------------------------------------ */ +/* Function: ipfsync_del */ +/* Returns: Nil */ +/* Parameters: sl(I) - pointer to synclist object to delete */ +/* */ +/* Deletes an object from the synclist table and free's its memory. */ +/* ------------------------------------------------------------------------ */ +void ipfsync_del(sl) +synclist_t *sl; +{ + WRITE_ENTER(&ipf_syncstate); + *sl->sl_pnext = sl->sl_next; + if (sl->sl_next != NULL) + sl->sl_next->sl_pnext = sl->sl_pnext; + if (sl->sl_idx != -1) + syncupd[sl->sl_idx].sup_hdr.sm_sl = NULL; + RWLOCK_EXIT(&ipf_syncstate); + KFREE(sl); +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipfsync_nat */ +/* Returns: int - 0 == success, else error value. */ +/* Parameters: sp(I) - pointer to sync packet data header */ +/* uio(I) - pointer to user data for further information */ +/* */ +/* Updates the NAT table according to information passed in the sync */ +/* header. As required, more data is fetched from the uio structure but */ +/* varies depending on the contents of the sync header. This function can */ +/* create a new NAT entry or update one. Deletion is left to the NAT */ +/* structures being timed out correctly. */ +/* ------------------------------------------------------------------------ */ +int ipfsync_nat(sp, data) +synchdr_t *sp; +void *data; +{ + synclogent_t sle; + syncupdent_t su; + nat_t *n, *nat; + synclist_t *sl; + u_int hv = 0; + int err; + + READ_ENTER(&ipf_syncstate); + + switch (sp->sm_cmd) + { + case SMC_CREATE : + bcopy(data, &sle, sizeof(sle)); + + KMALLOC(n, nat_t *); + if (n == NULL) { + err = ENOMEM; + break; + } + + KMALLOC(sl, synclist_t *); + if (sl == NULL) { + err = ENOMEM; + KFREE(n); + break; + } + + WRITE_ENTER(&ipf_nat); + + nat = &sle.sle_un.sleu_ipn; + bzero((char *)n, offsetof(nat_t, nat_age)); + bcopy((char *)&nat->nat_age, (char *)&n->nat_age, + sizeof(*n) - offsetof(nat_t, nat_age)); + ipfsync_natorder(0, n); + n->nat_sync = sl; + + sl->sl_idx = -1; + sl->sl_ipn = n; + sl->sl_num = ntohl(sp->sm_num); + sl->sl_pnext = syncstatetab + hv; + sl->sl_next = syncstatetab[hv]; + if (syncstatetab[hv] != NULL) + syncstatetab[hv]->sl_pnext = &sl->sl_next; + syncstatetab[hv] = sl; + nat_insert(n, sl->sl_rev); + RWLOCK_EXIT(&ipf_nat); + break; + + case SMC_UPDATE : + bcopy(data, &su, sizeof(su)); + + READ_ENTER(&ipf_syncstate); + for (sl = syncstatetab[hv]; (sl != NULL); sl = sl->sl_next) + if (sl->sl_hdr.sm_num == sp->sm_num) + break; + if (sl == NULL) { + err = ENOENT; + break; + } + + READ_ENTER(&ipf_nat); + + nat = sl->sl_ipn; + + MUTEX_ENTER(&nat->nat_lock); + fr_setnatqueue(nat, sl->sl_rev); + MUTEX_EXIT(&nat->nat_lock); + + RWLOCK_EXIT(&ipf_nat); + + break; + + default : + err = EINVAL; + break; + } + + RWLOCK_EXIT(&ipf_syncstate); + return 0; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipfsync_new */ +/* Returns: synclist_t* - NULL == failure, else pointer to new synclist */ +/* data structure. */ +/* Parameters: tab(I) - type of synclist_t to create */ +/* fin(I) - pointer to packet information */ +/* ptr(I) - pointer to owning object */ +/* */ +/* Creates a new sync table entry and notifies any sleepers that it's there */ +/* waiting to be processed. */ +/* ------------------------------------------------------------------------ */ +synclist_t *ipfsync_new(tab, fin, ptr) +int tab; +fr_info_t *fin; +void *ptr; +{ + synclist_t *sl, *ss; + synclogent_t *sle; + u_int hv, sz; + + if (sl_idx == SYNCLOG_SZ) + return NULL; + KMALLOC(sl, synclist_t *); + if (sl == NULL) + return NULL; + + MUTEX_ENTER(&ipf_syncadd); + /* + * Get a unique number for this synclist_t. The number is only meant + * to be unique for the lifetime of the structure and may be reused + * later. + */ + ipf_syncnum++; + if (ipf_syncnum == 0) { + ipf_syncnum = 1; + ipf_syncwrap = 1; + } + + hv = ipf_syncnum & (SYNC_STATETABSZ - 1); + while (ipf_syncwrap != 0) { + for (ss = syncstatetab[hv]; ss; ss = ss->sl_next) + if (ss->sl_hdr.sm_num == ipf_syncnum) + break; + if (ss == NULL) + break; + ipf_syncnum++; + hv = ipf_syncnum & (SYNC_STATETABSZ - 1); + } + /* + * Use the synch number of the object as the hash key. Should end up + * with relatively even distribution over time. + * XXX - an attacker could lunch an DoS attack, of sorts, if they are + * the only one causing new table entries by only keeping open every + * nth connection they make, where n is a value in the interval + * [0, SYNC_STATETABSZ-1]. + */ + sl->sl_pnext = syncstatetab + hv; + sl->sl_next = syncstatetab[hv]; + syncstatetab[hv] = sl; + sl->sl_num = ipf_syncnum; + MUTEX_EXIT(&ipf_syncadd); + + sl->sl_magic = htonl(SYNHDRMAGIC); + sl->sl_v = fin->fin_v; + sl->sl_p = fin->fin_p; + sl->sl_cmd = SMC_CREATE; + sl->sl_idx = -1; + sl->sl_table = tab; + sl->sl_rev = fin->fin_rev; + if (tab == SMC_STATE) { + sl->sl_ips = ptr; + sz = sizeof(*sl->sl_ips); + } else if (tab == SMC_NAT) { + sl->sl_ipn = ptr; + sz = sizeof(*sl->sl_ipn); + } else { + ptr = NULL; + sz = 0; + } + sl->sl_len = sz; + + /* + * Create the log entry to be read by a user daemon. When it has been + * finished and put on the queue, send a signal to wakeup any waiters. + */ + MUTEX_ENTER(&ipf_syncadd); + sle = synclog + sl_idx++; + bcopy((char *)&sl->sl_hdr, (char *)&sle->sle_hdr, + sizeof(sle->sle_hdr)); + sle->sle_hdr.sm_num = htonl(sle->sle_hdr.sm_num); + sle->sle_hdr.sm_len = htonl(sle->sle_hdr.sm_len); + if (ptr != NULL) { + bcopy((char *)ptr, (char *)&sle->sle_un, sz); + if (tab == SMC_STATE) { + ipfsync_storder(1, &sle->sle_un.sleu_ips); + } else if (tab == SMC_NAT) { + ipfsync_natorder(1, &sle->sle_un.sleu_ipn); + } + } + MUTEX_EXIT(&ipf_syncadd); + + MUTEX_ENTER(&ipsl_mutex); +# if SOLARIS +# ifdef _KERNEL + cv_signal(&ipslwait); +# endif + MUTEX_EXIT(&ipsl_mutex); +# else + MUTEX_EXIT(&ipsl_mutex); +# ifdef _KERNEL + wakeup(&sl_tail); +# endif +# endif + return sl; +} + + +/* ------------------------------------------------------------------------ */ +/* Function: ipfsync_update */ +/* Returns: Nil */ +/* Parameters: tab(I) - type of synclist_t to create */ +/* fin(I) - pointer to packet information */ +/* sl(I) - pointer to synchronisation object */ +/* */ +/* For outbound packets, only, create an sync update record for the user */ +/* process to read. */ +/* ------------------------------------------------------------------------ */ +void ipfsync_update(tab, fin, sl) +int tab; +fr_info_t *fin; +synclist_t *sl; +{ + synctcp_update_t *st; + syncupdent_t *slu; + ipstate_t *ips; + nat_t *nat; + + if (fin->fin_out == 0 || sl == NULL) + return; + + WRITE_ENTER(&ipf_syncstate); + MUTEX_ENTER(&ipf_syncadd); + if (sl->sl_idx == -1) { + slu = syncupd + su_idx; + sl->sl_idx = su_idx++; + bcopy((char *)&sl->sl_hdr, (char *)&slu->sup_hdr, + sizeof(slu->sup_hdr)); + slu->sup_hdr.sm_magic = htonl(SYNHDRMAGIC); + slu->sup_hdr.sm_sl = sl; + slu->sup_hdr.sm_cmd = SMC_UPDATE; + slu->sup_hdr.sm_table = tab; + slu->sup_hdr.sm_num = htonl(sl->sl_num); + slu->sup_hdr.sm_len = htonl(sizeof(struct synctcp_update)); + slu->sup_hdr.sm_rev = fin->fin_rev; +# if 0 + if (fin->fin_p == IPPROTO_TCP) { + st->stu_len[0] = 0; + st->stu_len[1] = 0; + } +# endif + } else + slu = syncupd + sl->sl_idx; + MUTEX_EXIT(&ipf_syncadd); + MUTEX_DOWNGRADE(&ipf_syncstate); + + /* + * Only TCP has complex timeouts, others just use default timeouts. + * For TCP, we only need to track the connection state and window. + */ + if (fin->fin_p == IPPROTO_TCP) { + st = &slu->sup_tcp; + if (tab == SMC_STATE) { + ips = sl->sl_ips; + st->stu_age = htonl(ips->is_die); + st->stu_data[0].td_end = ips->is_send; + st->stu_data[0].td_maxend = ips->is_maxsend; + st->stu_data[0].td_maxwin = ips->is_maxswin; + st->stu_state[0] = ips->is_state[0]; + st->stu_data[1].td_end = ips->is_dend; + st->stu_data[1].td_maxend = ips->is_maxdend; + st->stu_data[1].td_maxwin = ips->is_maxdwin; + st->stu_state[1] = ips->is_state[1]; + } else if (tab == SMC_NAT) { + nat = sl->sl_ipn; + st->stu_age = htonl(nat->nat_age); + } + } + RWLOCK_EXIT(&ipf_syncstate); + + MUTEX_ENTER(&ipsl_mutex); +# if SOLARIS +# ifdef _KERNEL + cv_signal(&ipslwait); +# endif + MUTEX_EXIT(&ipsl_mutex); +# else + MUTEX_EXIT(&ipsl_mutex); +# ifdef _KERNEL + wakeup(&sl_tail); +# endif +# endif +} + + +/* ------------------------------------------------------------------------ */ +/* Function: fr_sync_ioctl */ +/* Returns: int - 0 == success, != 0 == failure */ +/* Parameters: data(I) - pointer to ioctl data */ +/* cmd(I) - ioctl command integer */ +/* mode(I) - file mode bits used with open */ +/* */ +/* This function currently does not handle any ioctls and so just returns */ +/* EINVAL on all occasions. */ +/* ------------------------------------------------------------------------ */ +int fr_sync_ioctl(data, cmd, mode) +caddr_t data; +ioctlcmd_t cmd; +int mode; +{ + return EINVAL; +} +#endif /* IPFILTER_SYNC */ diff --git a/sys/contrib/ipfilter/netinet/ip_sync.h b/sys/contrib/ipfilter/netinet/ip_sync.h new file mode 100644 index 0000000..25ad708 --- /dev/null +++ b/sys/contrib/ipfilter/netinet/ip_sync.h @@ -0,0 +1,117 @@ +/* $FreeBSD$ */ + +/* + * Copyright (C) 1993-2001 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + * + * @(#)ip_fil.h 1.35 6/5/96 + * Id: ip_sync.h,v 2.11.2.2 2004/11/04 19:29:07 darrenr Exp + */ + +#ifndef __IP_SYNC_H__ +#define __IP_SYNC_H__ + +typedef struct synchdr { + u_32_t sm_magic; /* magic */ + u_char sm_v; /* version: 4,6 */ + u_char sm_p; /* protocol */ + u_char sm_cmd; /* command */ + u_char sm_table; /* NAT, STATE, etc */ + u_int sm_num; /* table entry number */ + int sm_rev; /* forward/reverse */ + int sm_len; /* length of the data section */ + struct synclist *sm_sl; /* back pointer to parent */ +} synchdr_t; + + +#define SYNHDRMAGIC 0x0FF51DE5 + +/* + * Commands + * No delete required as expirey will take care of that! + */ +#define SMC_CREATE 0 /* pass ipstate_t after synchdr_t */ +#define SMC_UPDATE 1 +#define SMC_MAXCMD 1 + +/* + * Tables + */ +#define SMC_NAT 0 +#define SMC_STATE 1 +#define SMC_MAXTBL 1 + + +/* + * Only TCP requires "more" information than just a reference to the entry + * for which an update is being made. + */ +typedef struct synctcp_update { + u_long stu_age; + tcpdata_t stu_data[2]; + int stu_state[2]; +} synctcp_update_t; + + +typedef struct synclist { + struct synclist *sl_next; + struct synclist **sl_pnext; + int sl_idx; /* update index */ + struct synchdr sl_hdr; + union { + struct ipstate *slu_ips; + struct nat *slu_ipn; + void *slu_ptr; + } sl_un; +} synclist_t; + +#define sl_ptr sl_un.slu_ptr +#define sl_ips sl_un.slu_ips +#define sl_ipn sl_un.slu_ipn +#define sl_magic sl_hdr.sm_magic +#define sl_v sl_hdr.sm_v +#define sl_p sl_hdr.sm_p +#define sl_cmd sl_hdr.sm_cmd +#define sl_rev sl_hdr.sm_rev +#define sl_table sl_hdr.sm_table +#define sl_num sl_hdr.sm_num +#define sl_len sl_hdr.sm_len + +/* + * NOTE: SYNCLOG_SZ is defined *low*. It should be the next power of two + * up for whatever number of packets per second you expect to see. Be + * warned: this index's a table of large elements (upto 272 bytes in size + * each), and thus a size of 8192, for example, results in a 2MB table. + * The lesson here is not to use small machines for running fast firewalls + * (100BaseT) in sync, where you might have upwards of 10k pps. + */ +#define SYNCLOG_SZ 256 + +typedef struct synclogent { + struct synchdr sle_hdr; + union { + struct ipstate sleu_ips; + struct nat sleu_ipn; + } sle_un; +} synclogent_t; + +typedef struct syncupdent { /* 28 or 32 bytes */ + struct synchdr sup_hdr; + struct synctcp_update sup_tcp; +} syncupdent_t; + +extern synclogent_t synclog[SYNCLOG_SZ]; + + +extern int fr_sync_ioctl __P((caddr_t, ioctlcmd_t, int)); +extern synclist_t *ipfsync_new __P((int, fr_info_t *, void *)); +extern void ipfsync_del __P((synclist_t *)); +extern void ipfsync_update __P((int, fr_info_t *, synclist_t *)); +extern int ipfsync_init __P((void)); +extern int ipfsync_nat __P((synchdr_t *sp, void *data)); +extern int ipfsync_state __P((synchdr_t *sp, void *data)); +extern int ipfsync_read __P((struct uio *uio)); +extern int ipfsync_write __P((struct uio *uio)); + +#endif /* IP_SYNC */ |