/* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * $Id$ */ #if !defined(lint) static const char sccsid[] = "@(#)ip_fil.c 2.41 6/5/96 (C) 1993-2000 Darren Reed"; static const char rcsid[] = "@(#)$Id$"; #endif #include "ipf.h" #include "md5.h" #include "ipt.h" ipf_main_softc_t ipfmain; static struct ifnet **ifneta = NULL; static int nifs = 0; struct rtentry; static void ipf_setifpaddr __P((struct ifnet *, char *)); void init_ifp __P((void)); #if defined(__sgi) && (IRIX < 60500) static int no_output __P((struct ifnet *, struct mbuf *, struct sockaddr *)); static int write_output __P((struct ifnet *, struct mbuf *, struct sockaddr *)); #else # if TRU64 >= 1885 static int no_output __P((struct ifnet *, struct mbuf *, struct sockaddr *, struct rtentry *, char *)); static int write_output __P((struct ifnet *, struct mbuf *, struct sockaddr *, struct rtentry *, char *)); # else static int no_output __P((struct ifnet *, struct mbuf *, struct sockaddr *, struct rtentry *)); static int write_output __P((struct ifnet *, struct mbuf *, struct sockaddr *, struct rtentry *)); # endif #endif int ipfattach(softc) ipf_main_softc_t *softc; { return 0; } int ipfdetach(softc) ipf_main_softc_t *softc; { return 0; } /* * Filter ioctl interface. */ int ipfioctl(softc, dev, cmd, data, mode) ipf_main_softc_t *softc; int dev; ioctlcmd_t cmd; caddr_t data; int mode; { int error = 0, unit = 0, uid; uid = getuid(); unit = dev; SPL_NET(s); error = ipf_ioctlswitch(softc, unit, data, cmd, mode, uid, NULL); if (error != -1) { SPL_X(s); return error; } SPL_X(s); return error; } void ipf_forgetifp(softc, ifp) ipf_main_softc_t *softc; void *ifp; { register frentry_t *f; WRITE_ENTER(&softc->ipf_mutex); for (f = softc->ipf_acct[0][softc->ipf_active]; (f != NULL); f = f->fr_next) if (f->fr_ifa == ifp) f->fr_ifa = (void *)-1; for (f = softc->ipf_acct[1][softc->ipf_active]; (f != NULL); f = f->fr_next) if (f->fr_ifa == ifp) f->fr_ifa = (void *)-1; for (f = softc->ipf_rules[0][softc->ipf_active]; (f != NULL); f = f->fr_next) if (f->fr_ifa == ifp) f->fr_ifa = (void *)-1; for (f = softc->ipf_rules[1][softc->ipf_active]; (f != NULL); f = f->fr_next) if (f->fr_ifa == ifp) f->fr_ifa = (void *)-1; RWLOCK_EXIT(&softc->ipf_mutex); ipf_nat_sync(softc, ifp); ipf_lookup_sync(softc, ifp); } static int #if defined(__sgi) && (IRIX < 60500) no_output(ifp, m, s) #else # if TRU64 >= 1885 no_output (ifp, m, s, rt, cp) char *cp; # else no_output(ifp, m, s, rt) # endif struct rtentry *rt; #endif struct ifnet *ifp; struct mbuf *m; struct sockaddr *s; { return 0; } static int #if defined(__sgi) && (IRIX < 60500) write_output(ifp, m, s) #else # if TRU64 >= 1885 write_output (ifp, m, s, rt, cp) char *cp; # else write_output(ifp, m, s, rt) # endif struct rtentry *rt; #endif struct ifnet *ifp; struct mbuf *m; struct sockaddr *s; { char fname[32]; mb_t *mb; ip_t *ip; int fd; mb = (mb_t *)m; ip = MTOD(mb, ip_t *); #if (defined(NetBSD) && (NetBSD <= 1991011) && (NetBSD >= 199606)) || \ (defined(OpenBSD) && (OpenBSD >= 199603)) || defined(linux) || \ (defined(__FreeBSD__) && (__FreeBSD_version >= 501113)) sprintf(fname, "/tmp/%s", ifp->if_xname); #else sprintf(fname, "/tmp/%s%d", ifp->if_name, ifp->if_unit); #endif fd = open(fname, O_WRONLY|O_APPEND); if (fd == -1) { perror("open"); return -1; } write(fd, (char *)ip, ntohs(ip->ip_len)); close(fd); return 0; } static void ipf_setifpaddr(ifp, addr) struct ifnet *ifp; char *addr; { #ifdef __sgi struct in_ifaddr *ifa; #else struct ifaddr *ifa; #endif #if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__) if (ifp->if_addrlist.tqh_first != NULL) #else # ifdef __sgi if (ifp->in_ifaddr != NULL) # else if (ifp->if_addrlist != NULL) # endif #endif return; ifa = (struct ifaddr *)malloc(sizeof(*ifa)); #if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__) ifp->if_addrlist.tqh_first = ifa; #else # ifdef __sgi ifp->in_ifaddr = ifa; # else ifp->if_addrlist = ifa; # endif #endif if (ifa != NULL) { struct sockaddr_in *sin; #ifdef __sgi sin = (struct sockaddr_in *)&ifa->ia_addr; #else sin = (struct sockaddr_in *)&ifa->ifa_addr; #endif #ifdef USE_INET6 if (index(addr, ':') != NULL) { struct sockaddr_in6 *sin6; sin6 = (struct sockaddr_in6 *)&ifa->ifa_addr; sin6->sin6_family = AF_INET6; /* Abort if bad address. */ switch (inet_pton(AF_INET6, addr, &sin6->sin6_addr)) { case 1: break; case -1: perror("inet_pton"); abort(); break; default: abort(); break; } } else #endif { sin->sin_family = AF_INET; sin->sin_addr.s_addr = inet_addr(addr); if (sin->sin_addr.s_addr == 0) abort(); } } } struct ifnet * get_unit(name, family) char *name; int family; { struct ifnet *ifp, **ifpp, **old_ifneta; char *addr; #if (defined(NetBSD) && (NetBSD <= 1991011) && (NetBSD >= 199606)) || \ (defined(OpenBSD) && (OpenBSD >= 199603)) || defined(linux) || \ (defined(__FreeBSD__) && (__FreeBSD_version >= 501113)) if (!*name) return NULL; if (name == NULL) name = "anon0"; addr = strchr(name, '='); if (addr != NULL) *addr++ = '\0'; for (ifpp = ifneta; ifpp && (ifp = *ifpp); ifpp++) { if (!strcmp(name, ifp->if_xname)) { if (addr != NULL) ipf_setifpaddr(ifp, addr); return ifp; } } #else char *s, ifname[LIFNAMSIZ+1]; if (name == NULL) name = "anon0"; addr = strchr(name, '='); if (addr != NULL) *addr++ = '\0'; for (ifpp = ifneta; ifpp && (ifp = *ifpp); ifpp++) { COPYIFNAME(family, ifp, ifname); if (!strcmp(name, ifname)) { if (addr != NULL) ipf_setifpaddr(ifp, addr); return ifp; } } #endif if (!ifneta) { ifneta = (struct ifnet **)malloc(sizeof(ifp) * 2); if (!ifneta) return NULL; ifneta[1] = NULL; ifneta[0] = (struct ifnet *)calloc(1, sizeof(*ifp)); if (!ifneta[0]) { free(ifneta); return NULL; } nifs = 1; } else { old_ifneta = ifneta; nifs++; ifneta = (struct ifnet **)realloc(ifneta, (nifs + 1) * sizeof(ifp)); if (!ifneta) { free(old_ifneta); nifs = 0; return NULL; } ifneta[nifs] = NULL; ifneta[nifs - 1] = (struct ifnet *)malloc(sizeof(*ifp)); if (!ifneta[nifs - 1]) { nifs--; return NULL; } } ifp = ifneta[nifs - 1]; #if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__) TAILQ_INIT(&ifp->if_addrlist); #endif #if (defined(NetBSD) && (NetBSD <= 1991011) && (NetBSD >= 199606)) || \ (defined(OpenBSD) && (OpenBSD >= 199603)) || defined(linux) || \ (defined(__FreeBSD__) && (__FreeBSD_version >= 501113)) (void) strncpy(ifp->if_xname, name, sizeof(ifp->if_xname)); #else s = name + strlen(name) - 1; for (; s > name; s--) { if (!ISDIGIT(*s)) { s++; break; } } if ((s > name) && (*s != 0) && ISDIGIT(*s)) { ifp->if_unit = atoi(s); ifp->if_name = (char *)malloc(s - name + 1); (void) strncpy(ifp->if_name, name, s - name); ifp->if_name[s - name] = '\0'; } else { ifp->if_name = strdup(name); ifp->if_unit = -1; } #endif ifp->if_output = (void *)no_output; if (addr != NULL) { ipf_setifpaddr(ifp, addr); } return ifp; } char * get_ifname(ifp) struct ifnet *ifp; { static char ifname[LIFNAMSIZ]; #if defined(__OpenBSD__) || defined(__NetBSD__) || defined(linux) || \ (defined(__FreeBSD__) && (__FreeBSD_version >= 501113)) sprintf(ifname, "%s", ifp->if_xname); #else if (ifp->if_unit != -1) sprintf(ifname, "%s%d", ifp->if_name, ifp->if_unit); else strcpy(ifname, ifp->if_name); #endif return ifname; } void init_ifp() { struct ifnet *ifp, **ifpp; char fname[32]; int fd; #if (defined(NetBSD) && (NetBSD <= 1991011) && (NetBSD >= 199606)) || \ (defined(OpenBSD) && (OpenBSD >= 199603)) || defined(linux) || \ (defined(__FreeBSD__) && (__FreeBSD_version >= 501113)) for (ifpp = ifneta; ifpp && (ifp = *ifpp); ifpp++) { ifp->if_output = (void *)write_output; sprintf(fname, "/tmp/%s", ifp->if_xname); fd = open(fname, O_WRONLY|O_CREAT|O_EXCL|O_TRUNC, 0600); if (fd == -1) perror("open"); else close(fd); } #else for (ifpp = ifneta; ifpp && (ifp = *ifpp); ifpp++) { ifp->if_output = (void *)write_output; sprintf(fname, "/tmp/%s%d", ifp->if_name, ifp->if_unit); fd = open(fname, O_WRONLY|O_CREAT|O_EXCL|O_TRUNC, 0600); if (fd == -1) perror("open"); else close(fd); } #endif } int ipf_fastroute(m, mpp, fin, fdp) mb_t *m, **mpp; fr_info_t *fin; frdest_t *fdp; { struct ifnet *ifp; ip_t *ip = fin->fin_ip; frdest_t node; int error = 0; frentry_t *fr; void *sifp; int sout; sifp = fin->fin_ifp; sout = fin->fin_out; fr = fin->fin_fr; ip->ip_sum = 0; if (!(fr->fr_flags & FR_KEEPSTATE) && (fdp != NULL) && (fdp->fd_type == FRD_DSTLIST)) { bzero(&node, sizeof(node)); ipf_dstlist_select_node(fin, fdp->fd_ptr, NULL, &node); fdp = &node; } ifp = fdp->fd_ptr; if (ifp == NULL) return 0; /* no routing table out here */ if (fin->fin_out == 0) { fin->fin_ifp = ifp; fin->fin_out = 1; (void) ipf_acctpkt(fin, NULL); fin->fin_fr = NULL; if (!fr || !(fr->fr_flags & FR_RETMASK)) { u_32_t pass; (void) ipf_state_check(fin, &pass); } switch (ipf_nat_checkout(fin, NULL)) { case 0 : break; case 1 : ip->ip_sum = 0; break; case -1 : error = -1; goto done; break; } } m->mb_ifp = ifp; printpacket(fin->fin_out, m); #if defined(__sgi) && (IRIX < 60500) (*ifp->if_output)(ifp, (void *)ip, NULL); # if TRU64 >= 1885 (*ifp->if_output)(ifp, (void *)m, NULL, 0, 0); # else (*ifp->if_output)(ifp, (void *)m, NULL, 0); # endif #endif done: fin->fin_ifp = sifp; fin->fin_out = sout; return error; } int ipf_send_reset(fin) fr_info_t *fin; { ipfkverbose("- TCP RST sent\n"); return 0; } int ipf_send_icmp_err(type, fin, dst) int type; fr_info_t *fin; int dst; { ipfkverbose("- ICMP unreachable sent\n"); return 0; } void m_freem(m) mb_t *m; { return; } void m_copydata(m, off, len, cp) mb_t *m; int off, len; caddr_t cp; { bcopy((char *)m + off, cp, len); } int ipfuiomove(buf, len, rwflag, uio) caddr_t buf; int len, rwflag; struct uio *uio; { int left, ioc, num, offset; struct iovec *io; char *start; if (rwflag == UIO_READ) { left = len; ioc = 0; offset = uio->uio_offset; while ((left > 0) && (ioc < uio->uio_iovcnt)) { io = uio->uio_iov + ioc; num = io->iov_len; if (num > left) num = left; start = (char *)io->iov_base + offset; if (start > (char *)io->iov_base + io->iov_len) { offset -= io->iov_len; ioc++; continue; } bcopy(buf, start, num); uio->uio_resid -= num; uio->uio_offset += num; left -= num; if (left > 0) ioc++; } if (left > 0) return EFAULT; } return 0; } u_32_t ipf_newisn(fin) fr_info_t *fin; { static int iss_seq_off = 0; u_char hash[16]; u_32_t newiss; 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; return newiss; } /* ------------------------------------------------------------------------ */ /* Function: ipf_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. */ /* ------------------------------------------------------------------------ */ INLINE u_short ipf_nextipid(fin) fr_info_t *fin; { static u_short ipid = 0; ipf_main_softc_t *softc = fin->fin_main_soft; u_short id; MUTEX_ENTER(&softc->ipf_rw); if (fin->fin_pktnum != 0) { /* * The -1 is for aligned test results. */ id = (fin->fin_pktnum - 1) & 0xffff; } else { } id = ipid++; MUTEX_EXIT(&softc->ipf_rw); return id; } INLINE int ipf_checkv4sum(fin) fr_info_t *fin; { if (fin->fin_flx & FI_SHORT) return 1; if (ipf_checkl4sum(fin) == -1) { fin->fin_flx |= FI_BAD; return -1; } return 0; } #ifdef USE_INET6 INLINE int ipf_checkv6sum(fin) fr_info_t *fin; { if (fin->fin_flx & FI_SHORT) return 1; if (ipf_checkl4sum(fin) == -1) { fin->fin_flx |= FI_BAD; return -1; } return 0; } #endif #if 0 /* * See above for description, except that all addressing is in user space. */ int copyoutptr(softc, src, dst, size) void *src, *dst; size_t size; { caddr_t ca; bcopy(dst, (char *)&ca, sizeof(ca)); bcopy(src, ca, size); return 0; } /* * See above for description, except that all addressing is in user space. */ int copyinptr(src, dst, size) void *src, *dst; size_t size; { caddr_t ca; bcopy(src, (char *)&ca, sizeof(ca)); bcopy(ca, dst, size); return 0; } #endif /* * return the first IP Address associated with an interface */ int ipf_ifpaddr(softc, v, atype, ifptr, inp, inpmask) ipf_main_softc_t *softc; int v, atype; void *ifptr; i6addr_t *inp, *inpmask; { struct ifnet *ifp = ifptr; #ifdef __sgi struct in_ifaddr *ifa; #else struct ifaddr *ifa; #endif #if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__) ifa = ifp->if_addrlist.tqh_first; #else # ifdef __sgi ifa = (struct in_ifaddr *)ifp->in_ifaddr; # else ifa = ifp->if_addrlist; # endif #endif if (ifa != NULL) { if (v == 4) { struct sockaddr_in *sin, mask; mask.sin_addr.s_addr = 0xffffffff; #ifdef __sgi sin = (struct sockaddr_in *)&ifa->ia_addr; #else sin = (struct sockaddr_in *)&ifa->ifa_addr; #endif return ipf_ifpfillv4addr(atype, sin, &mask, &inp->in4, &inpmask->in4); } #ifdef USE_INET6 if (v == 6) { struct sockaddr_in6 *sin6, mask; sin6 = (struct sockaddr_in6 *)&ifa->ifa_addr; ((i6addr_t *)&mask.sin6_addr)->i6[0] = 0xffffffff; ((i6addr_t *)&mask.sin6_addr)->i6[1] = 0xffffffff; ((i6addr_t *)&mask.sin6_addr)->i6[2] = 0xffffffff; ((i6addr_t *)&mask.sin6_addr)->i6[3] = 0xffffffff; return ipf_ifpfillv6addr(atype, sin6, &mask, inp, inpmask); } #endif } return 0; } /* * This function is not meant to be random, rather just produce a * sequence of numbers that isn't linear to show "randomness". */ u_32_t ipf_random() { static unsigned int last = 0xa5a5a5a5; static int calls = 0; int number; calls++; /* * These are deliberately chosen to ensure that there is some * attempt to test whether the output covers the range in test n18. */ switch (calls) { case 1 : number = 0; break; case 2 : number = 4; break; case 3 : number = 3999; break; case 4 : number = 4000; break; case 5 : number = 48999; break; case 6 : number = 49000; break; default : number = last; last *= calls; last++; number ^= last; break; } return number; } int ipf_verifysrc(fin) fr_info_t *fin; { return 1; } int ipf_inject(fin, m) fr_info_t *fin; mb_t *m; { FREE_MB_T(m); return 0; } u_int ipf_pcksum(fin, hlen, sum) fr_info_t *fin; int hlen; u_int sum; { u_short *sp; u_int sum2; int slen; slen = fin->fin_plen - hlen; sp = (u_short *)((u_char *)fin->fin_ip + hlen); for (; slen > 1; slen -= 2) sum += *sp++; if (slen) sum += ntohs(*(u_char *)sp << 8); while (sum > 0xffff) sum = (sum & 0xffff) + (sum >> 16); sum2 = (u_short)(~sum & 0xffff); return sum2; } void * ipf_pullup(m, fin, plen) mb_t *m; fr_info_t *fin; int plen; { if (M_LEN(m) >= plen) return fin->fin_ip; /* * Fake ipf_pullup failing */ fin->fin_reason = FRB_PULLUP; *fin->fin_mp = NULL; fin->fin_m = NULL; fin->fin_ip = NULL; return NULL; }