aboutsummaryrefslogblamecommitdiff
path: root/contrib/ipfilter/ip_fil.c
blob: 327f90fc356c7fbc7fdb3475cfc9acafdfb839f5 (plain) (tree)
1
2
3
4
5
6
7
8


                          
                                     

                                                          

       


                                                                                       
                                       
      
 
                
                
                
 
                                
 


                                      
                
 





                                                                       
 


                                         
 


                                
 



                 


                                
 



                 


                          






                                     
 
                                     
 
                       



                   
                                                                         
                          


                             




                     



                                


                              


                                                                    

                                               

                                                                    

                                               

                                                                     

                                               

                                                                     

                                               


                                       


 
          
                        
                           
                          
                       
                           




                 
          
                           
                           


                           









                                                                       
                        














                                                                



                          
 
                           
 
                                               

                                               
                                     



                                                    
                                               

                                         
                               




                                        
                                                           





                                                                     












                                                                            







                                                               


         



                      



                                                                       
                        
 

                            
 









                                                            
                                                          













                                                            
                                                

                                            
                                                          


















                                                                    

                                                                         













                                                                        
                                               

                                      
                                                                       
                        

                                                                   








                                                     








                                                             
                                           

                           
                                          





                   


                          


                                      
                                               

                                             



                                                                    





                      

          





                                                                       
                        
                                                            
                                                      









                                                                        
                                                      










                                                                        




                               
 
                          
                               
                      


                      
                 
 

                            
                         
                       
 










                                                                       
                                

                                   
                                              



                                                          
                                                           

                 
                                                    











                                       

         


                                     
                                                   
     

                            
                     


 


                       
 
                                        



                 




                                 
 
                                                 



                 


                




               




                           




                                        




                                 



































                                                                         


                       





































                                                                              
                                                                              




                                                                              


                       

                                
                                                     

                   









                                                      




                  


                       
 




                                        
                                       


                          



                 


                       
 



                                        
                                       


                          



      
     


                                                                          



                                 











                                                                          



                         






                                            
      




                                                           





                                                 

                                  
                           
 
                                               

                                         
                               

                          

                                                      
 
                                                          
 
                                                                   
 















                                                                           


                 

 





                                                                 
 























































                                                                           

                 

 




                          
 


























                                                       

          
                                  
           




                                     
 
/*	$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(struct ifnet *, char *);
void	init_ifp(void);
static int 	no_output(struct ifnet *, struct mbuf *,
			       struct sockaddr *, struct rtentry *);
static int	write_output(struct ifnet *, struct mbuf *,
				  struct sockaddr *, struct rtentry *);

struct ifaddr {
	struct sockaddr_storage ifa_addr;
};

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
no_output(ifp, m, s, rt)
	struct rtentry *rt;
	struct ifnet *ifp;
	struct mbuf *m;
	struct sockaddr *s;
{
	return 0;
}


static int
write_output(ifp, m, s, rt)
	struct rtentry *rt;
	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(__FreeBSD__)
	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;
{
	struct ifaddr *ifa;

#if defined(__NetBSD__) || defined(__FreeBSD__)
	if (ifp->if_addrlist.tqh_first != NULL)
#else
	if (ifp->if_addrlist != NULL)
#endif
		return;

	ifa = (struct ifaddr *)malloc(sizeof(*ifa));
#if defined(__NetBSD__) || defined(__FreeBSD__)
	ifp->if_addrlist.tqh_first = ifa;
#else
	ifp->if_addrlist = ifa;
#endif

	if (ifa != NULL) {
		struct sockaddr_in *sin;

		sin = (struct sockaddr_in *)&ifa->ifa_addr;
#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(__FreeBSD__)

	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 **)reallocarray(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(__FreeBSD__)
	TAILQ_INIT(&ifp->if_addrlist);
#endif
#if (defined(NetBSD) && (NetBSD <= 1991011) && (NetBSD >= 199606)) || \
    defined(__FreeBSD__)
	(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(__NetBSD__) || defined(__FreeBSD__)
	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(__FreeBSD__)
	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);

	(*ifp->if_output)(ifp, (void *)m, NULL, 0);
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;
	struct ifaddr *ifa;

#if defined(__NetBSD__) || defined(__FreeBSD__)
	ifa = ifp->if_addrlist.tqh_first;
#else
	ifa = ifp->if_addrlist;
#endif
	if (ifa != NULL) {
		if (v == 4) {
			struct sockaddr_in *sin, mask;

			mask.sin_addr.s_addr = 0xffffffff;

			sin = (struct sockaddr_in *)&ifa->ifa_addr;

			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;
}