summaryrefslogtreecommitdiff
path: root/pfinet/linux-inet/ip_fw.c
diff options
context:
space:
mode:
authorMichael I. Bushnell <mib@gnu.org>1995-07-12 15:42:50 +0000
committerMichael I. Bushnell <mib@gnu.org>1995-07-12 15:42:50 +0000
commita81cd86c8d93236ffccfbee44b5818ba21523463 (patch)
treef098368d79e9ed9b39907730c28ed3182b69519d /pfinet/linux-inet/ip_fw.c
parentc7923f6aa252a29ccb4f16bd91469c9000a2bd94 (diff)
entered into RCS
Diffstat (limited to 'pfinet/linux-inet/ip_fw.c')
-rw-r--r--pfinet/linux-inet/ip_fw.c1016
1 files changed, 1016 insertions, 0 deletions
diff --git a/pfinet/linux-inet/ip_fw.c b/pfinet/linux-inet/ip_fw.c
new file mode 100644
index 00000000..0572c8f1
--- /dev/null
+++ b/pfinet/linux-inet/ip_fw.c
@@ -0,0 +1,1016 @@
+/*
+ * IP firewalling code. This is taken from 4.4BSD. Please note the
+ * copyright message below. As per the GPL it must be maintained
+ * and the licenses thus do not conflict. While this port is subject
+ * to the GPL I also place my modifications under the original
+ * license in recognition of the original copyright.
+ * -- Alan Cox.
+ *
+ * Ported from BSD to Linux,
+ * Alan Cox 22/Nov/1994.
+ * Zeroing /proc and other additions
+ * Jos Vos 4/Feb/1995.
+ * Merged and included the FreeBSD-Current changes at Ugen's request
+ * (but hey it's a lot cleaner now). Ugen would prefer in some ways
+ * we waited for his final product but since Linux 1.2.0 is about to
+ * appear it's not practical - Read: It works, it's not clean but please
+ * don't consider it to be his standard of finished work.
+ * Alan Cox 12/Feb/1995
+ * Porting bidirectional entries from BSD, fixing accounting issues,
+ * adding struct ip_fwpkt for checking packets with interface address
+ * Jos Vos 5/Mar/1995.
+ *
+ * All the real work was done by .....
+ */
+
+/*
+ * Copyright (c) 1993 Daniel Boulet
+ * Copyright (c) 1994 Ugen J.S.Antsilevich
+ *
+ * Redistribution and use in source forms, with and without modification,
+ * are permitted provided that this entire comment appears intact.
+ *
+ * Redistribution in binary form may occur without any restrictions.
+ * Obviously, it would be nice if you gave credit where credit is due
+ * but requiring it would be too onerous.
+ *
+ * This software is provided ``AS IS'' without any warranties of any kind.
+ */
+
+#include <linux/config.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/config.h>
+
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/icmp.h>
+#include <linux/udp.h>
+#include "ip.h"
+#include "protocol.h"
+#include "route.h"
+#include "tcp.h"
+#include <linux/skbuff.h>
+#include "sock.h"
+#include "icmp.h"
+#include <linux/ip_fw.h>
+
+/*
+ * Implement IP packet firewall
+ */
+
+#ifdef CONFIG_IPFIREWALL_DEBUG
+#define dprintf1(a) printk(a)
+#define dprintf2(a1,a2) printk(a1,a2)
+#define dprintf3(a1,a2,a3) printk(a1,a2,a3)
+#define dprintf4(a1,a2,a3,a4) printk(a1,a2,a3,a4)
+#else
+#define dprintf1(a)
+#define dprintf2(a1,a2)
+#define dprintf3(a1,a2,a3)
+#define dprintf4(a1,a2,a3,a4)
+#endif
+
+#define print_ip(a) printf("%d.%d.%d.%d",(ntohl(a.s_addr)>>24)&0xFF,\
+ (ntohl(a.s_addr)>>16)&0xFF,\
+ (ntohl(a.s_addr)>>8)&0xFF,\
+ (ntohl(a.s_addr))&0xFF);
+
+#ifdef IPFIREWALL_DEBUG
+#define dprint_ip(a) print_ip(a)
+#else
+#define dprint_ip(a)
+#endif
+
+#ifdef CONFIG_IP_FIREWALL
+struct ip_fw *ip_fw_fwd_chain;
+struct ip_fw *ip_fw_blk_chain;
+int ip_fw_blk_policy=IP_FW_F_ACCEPT;
+int ip_fw_fwd_policy=IP_FW_F_ACCEPT;
+#endif
+#ifdef CONFIG_IP_ACCT
+struct ip_fw *ip_acct_chain;
+#endif
+
+#define IP_INFO_BLK 0
+#define IP_INFO_FWD 1
+#define IP_INFO_ACCT 2
+
+
+/*
+ * Returns 1 if the port is matched by the vector, 0 otherwise
+ */
+
+extern inline int port_match(unsigned short *portptr,int nports,unsigned short port,int range_flag)
+{
+ if (!nports)
+ return 1;
+ if ( range_flag )
+ {
+ if ( portptr[0] <= port && port <= portptr[1] )
+ {
+ return( 1 );
+ }
+ nports -= 2;
+ portptr += 2;
+ }
+ while ( nports-- > 0 )
+ {
+ if ( *portptr++ == port )
+ {
+ return( 1 );
+ }
+ }
+ return(0);
+}
+
+#if defined(CONFIG_IP_ACCT) || defined(CONFIG_IP_FIREWALL)
+
+
+/*
+ * Returns 0 if packet should be dropped, 1 if it should be accepted,
+ * and -1 if an ICMP host unreachable packet should be sent.
+ * Also does accounting so you can feed it the accounting chain.
+ * If opt is set to 1, it means that we do this for accounting
+ * purposes (searches all entries and handles fragments different).
+ * If opt is set to 2, it doesn't count a matching packet, which
+ * is used when calling this for checking purposes (IP_FW_CHK_*).
+ */
+
+
+int ip_fw_chk(struct iphdr *ip, struct device *rif, struct ip_fw *chain, int policy, int opt)
+{
+ struct ip_fw *f;
+ struct tcphdr *tcp=(struct tcphdr *)((unsigned long *)ip+ip->ihl);
+ struct udphdr *udp=(struct udphdr *)((unsigned long *)ip+ip->ihl);
+ __u32 src, dst;
+ __u16 src_port=0, dst_port=0;
+ unsigned short f_prt=0, prt;
+ char notcpsyn=1, frag1, match;
+ unsigned short f_flag;
+
+ /*
+ * If the chain is empty follow policy. The BSD one
+ * accepts anything giving you a time window while
+ * flushing and rebuilding the tables.
+ */
+
+ src = ip->saddr;
+ dst = ip->daddr;
+
+ /*
+ * This way we handle fragmented packets.
+ * we ignore all fragments but the first one
+ * so the whole packet can't be reassembled.
+ * This way we relay on the full info which
+ * stored only in first packet.
+ *
+ * Note that this theoretically allows partial packet
+ * spoofing. Not very dangerous but paranoid people may
+ * wish to play with this. It also allows the so called
+ * "fragment bomb" denial of service attack on some types
+ * of system.
+ */
+
+ frag1 = ((ntohs(ip->frag_off) & IP_OFFSET) == 0);
+ if (!frag1 && (opt != 1) && (ip->protocol == IPPROTO_TCP ||
+ ip->protocol == IPPROTO_UDP))
+ return(1);
+
+ src = ip->saddr;
+ dst = ip->daddr;
+
+ /*
+ * If we got interface from which packet came
+ * we can use the address directly. This is unlike
+ * 4.4BSD derived systems that have an address chain
+ * per device. We have a device per address with dummy
+ * devices instead.
+ */
+
+ dprintf1("Packet ");
+ switch(ip->protocol)
+ {
+ case IPPROTO_TCP:
+ dprintf1("TCP ");
+ /* ports stay 0 if it is not the first fragment */
+ if (frag1) {
+ src_port=ntohs(tcp->source);
+ dst_port=ntohs(tcp->dest);
+ if(tcp->syn && !tcp->ack)
+ /* We *DO* have SYN, value FALSE */
+ notcpsyn=0;
+ }
+ prt=IP_FW_F_TCP;
+ break;
+ case IPPROTO_UDP:
+ dprintf1("UDP ");
+ /* ports stay 0 if it is not the first fragment */
+ if (frag1) {
+ src_port=ntohs(udp->source);
+ dst_port=ntohs(udp->dest);
+ }
+ prt=IP_FW_F_UDP;
+ break;
+ case IPPROTO_ICMP:
+ dprintf2("ICMP:%d ",((char *)portptr)[0]&0xff);
+ prt=IP_FW_F_ICMP;
+ break;
+ default:
+ dprintf2("p=%d ",ip->protocol);
+ prt=IP_FW_F_ALL;
+ break;
+ }
+ dprint_ip(ip->saddr);
+
+ if (ip->protocol==IPPROTO_TCP || ip->protocol==IPPROTO_UDP)
+ /* This will print 0 when it is not the first fragment! */
+ dprintf2(":%d ", src_port);
+ dprint_ip(ip->daddr);
+ if (ip->protocol==IPPROTO_TCP || ip->protocol==IPPROTO_UDP)
+ /* This will print 0 when it is not the first fragment! */
+ dprintf2(":%d ",dst_port);
+ dprintf1("\n");
+
+ for (f=chain;f;f=f->fw_next)
+ {
+ /*
+ * This is a bit simpler as we don't have to walk
+ * an interface chain as you do in BSD - same logic
+ * however.
+ */
+
+ /*
+ * Match can become 0x01 (a "normal" match was found),
+ * 0x02 (a reverse match was found), and 0x03 (the
+ * IP addresses match in both directions).
+ * Now we know in which direction(s) we should look
+ * for a match for the TCP/UDP ports. Both directions
+ * might match (e.g., when both addresses are on the
+ * same network for which an address/mask is given), but
+ * the ports might only match in one direction.
+ * This was obviously wrong in the original BSD code.
+ */
+ match = 0x00;
+
+ if ((src&f->fw_smsk.s_addr)==f->fw_src.s_addr
+ && (dst&f->fw_dmsk.s_addr)==f->fw_dst.s_addr)
+ /* normal direction */
+ match |= 0x01;
+
+ if ((f->fw_flg & IP_FW_F_BIDIR) &&
+ (dst&f->fw_smsk.s_addr)==f->fw_src.s_addr
+ && (src&f->fw_dmsk.s_addr)==f->fw_dst.s_addr)
+ /* reverse direction */
+ match |= 0x02;
+
+ if (match)
+ {
+ /*
+ * Look for a VIA match
+ */
+ if(f->fw_via.s_addr && rif)
+ {
+ if(rif->pa_addr!=f->fw_via.s_addr)
+ continue; /* Mismatch */
+ }
+ /*
+ * Drop through - this is a match
+ */
+ }
+ else
+ continue;
+
+ /*
+ * Ok the chain addresses match.
+ */
+
+ f_prt=f->fw_flg&IP_FW_F_KIND;
+ if (f_prt!=IP_FW_F_ALL)
+ {
+ /*
+ * This is actually buggy as if you set SYN flag
+ * on UDP or ICMP firewall it will never work,but
+ * actually it is a concern of software which sets
+ * firewall entries.
+ */
+
+ if((f->fw_flg&IP_FW_F_TCPSYN) && notcpsyn)
+ continue;
+ /*
+ * Specific firewall - packet's protocol
+ * must match firewall's.
+ */
+
+ if(prt!=f_prt)
+ continue;
+
+ if(!(prt==IP_FW_F_ICMP || ((match & 0x01) &&
+ port_match(&f->fw_pts[0], f->fw_nsp, src_port,
+ f->fw_flg&IP_FW_F_SRNG) &&
+ port_match(&f->fw_pts[f->fw_nsp], f->fw_ndp, dst_port,
+ f->fw_flg&IP_FW_F_DRNG)) || ((match & 0x02) &&
+ port_match(&f->fw_pts[0], f->fw_nsp, dst_port,
+ f->fw_flg&IP_FW_F_SRNG) &&
+ port_match(&f->fw_pts[f->fw_nsp], f->fw_ndp, src_port,
+ f->fw_flg&IP_FW_F_DRNG))))
+ {
+ continue;
+ }
+ }
+#ifdef CONFIG_IP_FIREWALL_VERBOSE
+ /*
+ * VERY ugly piece of code which actually
+ * makes kernel printf for denied packets...
+ */
+
+ if (f->fw_flg & IP_FW_F_PRN)
+ {
+ if(opt != 1) {
+ if(f->fw_flg&IP_FW_F_ACCEPT)
+ printk("Accept ");
+ else if(f->fw_flg&IP_FW_F_ICMPRPL)
+ printk("Reject ");
+ else
+ printk("Deny ");
+ }
+ switch(ip->protocol)
+ {
+ case IPPROTO_TCP:
+ printk("TCP ");
+ break;
+ case IPPROTO_UDP:
+ printk("UDP ");
+ case IPPROTO_ICMP:
+ printk("ICMP ");
+ break;
+ default:
+ printk("p=%d ",ip->protocol);
+ break;
+ }
+ print_ip(ip->saddr);
+ if(ip->protocol == IPPROTO_TCP || ip->protocol == IPPROTO_UDP)
+ printk(":%d", src_port);
+ printk(" ");
+ print_ip(ip->daddr);
+ if(ip->protocol == IPPROTO_TCP || ip->protocol == IPPROTO_UDP)
+ printk(":%d",dst_port);
+ printk("\n");
+ }
+#endif
+ if (opt != 2) {
+ f->fw_bcnt+=ntohs(ip->tot_len);
+ f->fw_pcnt++;
+ }
+ if (opt != 1)
+ break;
+ } /* Loop */
+
+ if(opt == 1)
+ return 0;
+
+ /*
+ * We rely on policy defined in the rejecting entry or, if no match
+ * was found, we rely on the general policy variable for this type
+ * of firewall.
+ */
+
+ if(f!=NULL) /* A match was found */
+ f_flag=f->fw_flg;
+ else
+ f_flag=policy;
+ if(f_flag&IP_FW_F_ACCEPT)
+ return 1;
+ if(f_flag&IP_FW_F_ICMPRPL)
+ return -1;
+ return 0;
+}
+
+
+static void zero_fw_chain(struct ip_fw *chainptr)
+{
+ struct ip_fw *ctmp=chainptr;
+ while(ctmp)
+ {
+ ctmp->fw_pcnt=0L;
+ ctmp->fw_bcnt=0L;
+ ctmp=ctmp->fw_next;
+ }
+}
+
+static void free_fw_chain(struct ip_fw *volatile* chainptr)
+{
+ unsigned long flags;
+ save_flags(flags);
+ cli();
+ while ( *chainptr != NULL )
+ {
+ struct ip_fw *ftmp;
+ ftmp = *chainptr;
+ *chainptr = ftmp->fw_next;
+ kfree_s(ftmp,sizeof(*ftmp));
+ }
+ restore_flags(flags);
+}
+
+/* Volatiles to keep some of the compiler versions amused */
+
+static int add_to_chain(struct ip_fw *volatile* chainptr, struct ip_fw *frwl)
+{
+ struct ip_fw *ftmp;
+ struct ip_fw *chtmp=NULL;
+ struct ip_fw *volatile chtmp_prev=NULL;
+ unsigned long flags;
+ unsigned long m_src_mask,m_dst_mask;
+ unsigned long n_sa,n_da,o_sa,o_da,o_sm,o_dm,n_sm,n_dm;
+ unsigned short n_sr,n_dr,o_sr,o_dr;
+ unsigned short oldkind,newkind;
+ int addb4=0;
+ int n_o,n_n;
+
+ save_flags(flags);
+
+ ftmp = kmalloc( sizeof(struct ip_fw), GFP_ATOMIC );
+ if ( ftmp == NULL )
+ {
+#ifdef DEBUG_CONFIG_IP_FIREWALL
+ printf("ip_fw_ctl: malloc said no\n");
+#endif
+ return( ENOMEM );
+ }
+
+ memcpy(ftmp, frwl, sizeof( struct ip_fw ) );
+
+ ftmp->fw_pcnt=0L;
+ ftmp->fw_bcnt=0L;
+
+ ftmp->fw_next = NULL;
+
+ cli();
+
+ if (*chainptr==NULL)
+ {
+ *chainptr=ftmp;
+ }
+ else
+ {
+ chtmp_prev=NULL;
+ for (chtmp=*chainptr;chtmp!=NULL;chtmp=chtmp->fw_next)
+ {
+ addb4=0;
+ newkind=ftmp->fw_flg & IP_FW_F_KIND;
+ oldkind=chtmp->fw_flg & IP_FW_F_KIND;
+
+ if (newkind!=IP_FW_F_ALL
+ && oldkind!=IP_FW_F_ALL
+ && oldkind!=newkind)
+ {
+ chtmp_prev=chtmp;
+ continue;
+ }
+
+ /*
+ * Very very *UGLY* code...
+ * Sorry,but i had to do this....
+ */
+
+ n_sa=ntohl(ftmp->fw_src.s_addr);
+ n_da=ntohl(ftmp->fw_dst.s_addr);
+ n_sm=ntohl(ftmp->fw_smsk.s_addr);
+ n_dm=ntohl(ftmp->fw_dmsk.s_addr);
+
+ o_sa=ntohl(chtmp->fw_src.s_addr);
+ o_da=ntohl(chtmp->fw_dst.s_addr);
+ o_sm=ntohl(chtmp->fw_smsk.s_addr);
+ o_dm=ntohl(chtmp->fw_dmsk.s_addr);
+
+ m_src_mask = o_sm & n_sm;
+ m_dst_mask = o_dm & n_dm;
+
+ if ((o_sa & m_src_mask) == (n_sa & m_src_mask))
+ {
+ if (n_sm > o_sm)
+ addb4++;
+ if (n_sm < o_sm)
+ addb4--;
+ }
+
+ if ((o_da & m_dst_mask) == (n_da & m_dst_mask))
+ {
+ if (n_dm > o_dm)
+ addb4++;
+ if (n_dm < o_dm)
+ addb4--;
+ }
+
+ if (((o_da & o_dm) == (n_da & n_dm))
+ &&((o_sa & o_sm) == (n_sa & n_sm)))
+ {
+ if (newkind!=IP_FW_F_ALL &&
+ oldkind==IP_FW_F_ALL)
+ addb4++;
+ if (newkind==oldkind && (oldkind==IP_FW_F_TCP
+ || oldkind==IP_FW_F_UDP))
+ {
+
+ /*
+ * Here the main idea is to check the size
+ * of port range which the frwl covers
+ * We actually don't check their values but
+ * just the wideness of range they have
+ * so that less wide ranges or single ports
+ * go first and wide ranges go later. No ports
+ * at all treated as a range of maximum number
+ * of ports.
+ */
+
+ if (ftmp->fw_flg & IP_FW_F_SRNG)
+ n_sr=ftmp->fw_pts[1]-ftmp->fw_pts[0];
+ else
+ n_sr=(ftmp->fw_nsp)?
+ ftmp->fw_nsp : 0xFFFF;
+
+ if (chtmp->fw_flg & IP_FW_F_SRNG)
+ o_sr=chtmp->fw_pts[1]-chtmp->fw_pts[0];
+ else
+ o_sr=(chtmp->fw_nsp)?chtmp->fw_nsp : 0xFFFF;
+
+ if (n_sr<o_sr)
+ addb4++;
+ if (n_sr>o_sr)
+ addb4--;
+
+ n_n=ftmp->fw_nsp;
+ n_o=chtmp->fw_nsp;
+
+ /*
+ * Actually this cannot happen as the frwl control
+ * procedure checks for number of ports in source and
+ * destination range but we will try to be more safe.
+ */
+
+ if ((n_n>(IP_FW_MAX_PORTS-2)) ||
+ (n_o>(IP_FW_MAX_PORTS-2)))
+ goto skip_check;
+
+ if (ftmp->fw_flg & IP_FW_F_DRNG)
+ n_dr=ftmp->fw_pts[n_n+1]-ftmp->fw_pts[n_n];
+ else
+ n_dr=(ftmp->fw_ndp)? ftmp->fw_ndp : 0xFFFF;
+
+ if (chtmp->fw_flg & IP_FW_F_DRNG)
+ o_dr=chtmp->fw_pts[n_o+1]-chtmp->fw_pts[n_o];
+ else
+ o_dr=(chtmp->fw_ndp)? chtmp->fw_ndp : 0xFFFF;
+ if (n_dr<o_dr)
+ addb4++;
+ if (n_dr>o_dr)
+ addb4--;
+skip_check:
+ }
+ /* finally look at the interface address */
+ if ((addb4 == 0) && ftmp->fw_via.s_addr &&
+ !(chtmp->fw_via.s_addr))
+ addb4++;
+ }
+ if (addb4>0)
+ {
+ if (chtmp_prev)
+ {
+ chtmp_prev->fw_next=ftmp;
+ ftmp->fw_next=chtmp;
+ }
+ else
+ {
+ *chainptr=ftmp;
+ ftmp->fw_next=chtmp;
+ }
+ restore_flags(flags);
+ return 0;
+ }
+ chtmp_prev=chtmp;
+ }
+ }
+
+ if (chtmp_prev)
+ chtmp_prev->fw_next=ftmp;
+ else
+ *chainptr=ftmp;
+ restore_flags(flags);
+ return(0);
+}
+
+static int del_from_chain(struct ip_fw *volatile*chainptr, struct ip_fw *frwl)
+{
+ struct ip_fw *ftmp,*ltmp;
+ unsigned short tport1,tport2,tmpnum;
+ char matches,was_found;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ ftmp=*chainptr;
+
+ if ( ftmp == NULL )
+ {
+#ifdef DEBUG_CONFIG_IP_FIREWALL
+ printk("ip_fw_ctl: chain is empty\n");
+#endif
+ restore_flags(flags);
+ return( EINVAL );
+ }
+
+ ltmp=NULL;
+ was_found=0;
+
+ while( ftmp != NULL )
+ {
+ matches=1;
+ if (ftmp->fw_src.s_addr!=frwl->fw_src.s_addr
+ || ftmp->fw_dst.s_addr!=frwl->fw_dst.s_addr
+ || ftmp->fw_smsk.s_addr!=frwl->fw_smsk.s_addr
+ || ftmp->fw_dmsk.s_addr!=frwl->fw_dmsk.s_addr
+ || ftmp->fw_via.s_addr!=frwl->fw_via.s_addr
+ || ftmp->fw_flg!=frwl->fw_flg)
+ matches=0;
+
+ tport1=ftmp->fw_nsp+ftmp->fw_ndp;
+ tport2=frwl->fw_nsp+frwl->fw_ndp;
+ if (tport1!=tport2)
+ matches=0;
+ else if (tport1!=0)
+ {
+ for (tmpnum=0;tmpnum < tport1 && tmpnum < IP_FW_MAX_PORTS;tmpnum++)
+ if (ftmp->fw_pts[tmpnum]!=frwl->fw_pts[tmpnum])
+ matches=0;
+ }
+ if(matches)
+ {
+ was_found=1;
+ if (ltmp)
+ {
+ ltmp->fw_next=ftmp->fw_next;
+ kfree_s(ftmp,sizeof(*ftmp));
+ ftmp=ltmp->fw_next;
+ }
+ else
+ {
+ *chainptr=ftmp->fw_next;
+ kfree_s(ftmp,sizeof(*ftmp));
+ ftmp=*chainptr;
+ }
+ }
+ else
+ {
+ ltmp = ftmp;
+ ftmp = ftmp->fw_next;
+ }
+ }
+ restore_flags(flags);
+ if (was_found)
+ return 0;
+ else
+ return(EINVAL);
+}
+
+#endif /* CONFIG_IP_ACCT || CONFIG_IP_FIREWALL */
+
+struct ip_fw *check_ipfw_struct(struct ip_fw *frwl, int len)
+{
+
+ if ( len != sizeof(struct ip_fw) )
+ {
+#ifdef DEBUG_CONFIG_IP_FIREWALL
+ printk("ip_fw_ctl: len=%d, want %d\n",m->m_len,
+ sizeof(struct ip_fw));
+#endif
+ return(NULL);
+ }
+
+ if ( (frwl->fw_flg & ~IP_FW_F_MASK) != 0 )
+ {
+#ifdef DEBUG_CONFIG_IP_FIREWALL
+ printk("ip_fw_ctl: undefined flag bits set (flags=%x)\n",
+ frwl->fw_flg);
+#endif
+ return(NULL);
+ }
+
+ if ( (frwl->fw_flg & IP_FW_F_SRNG) && frwl->fw_nsp < 2 )
+ {
+#ifdef DEBUG_CONFIG_IP_FIREWALL
+ printk("ip_fw_ctl: src range set but n_src_p=%d\n",
+ frwl->fw_nsp);
+#endif
+ return(NULL);
+ }
+
+ if ( (frwl->fw_flg & IP_FW_F_DRNG) && frwl->fw_ndp < 2 )
+ {
+#ifdef DEBUG_CONFIG_IP_FIREWALL
+ printk("ip_fw_ctl: dst range set but n_dst_p=%d\n",
+ frwl->fw_ndp);
+#endif
+ return(NULL);
+ }
+
+ if ( frwl->fw_nsp + frwl->fw_ndp > IP_FW_MAX_PORTS )
+ {
+#ifdef DEBUG_CONFIG_IP_FIREWALL
+ printk("ip_fw_ctl: too many ports (%d+%d)\n",
+ frwl->fw_nsp,frwl->fw_ndp);
+#endif
+ return(NULL);
+ }
+
+ return frwl;
+}
+
+
+
+
+#ifdef CONFIG_IP_ACCT
+
+void ip_acct_cnt(struct iphdr *iph, struct device *dev, struct ip_fw *f)
+{
+ (void) ip_fw_chk(iph, dev, f, 0, 1);
+ return;
+}
+
+int ip_acct_ctl(int stage, void *m, int len)
+{
+ if ( stage == IP_ACCT_FLUSH )
+ {
+ free_fw_chain(&ip_acct_chain);
+ return(0);
+ }
+ if ( stage == IP_ACCT_ZERO )
+ {
+ zero_fw_chain(ip_acct_chain);
+ return(0);
+ }
+ if ( stage == IP_ACCT_ADD
+ || stage == IP_ACCT_DEL
+ )
+ {
+ struct ip_fw *frwl;
+
+ if (!(frwl=check_ipfw_struct(m,len)))
+ return (EINVAL);
+
+ switch (stage)
+ {
+ case IP_ACCT_ADD:
+ return( add_to_chain(&ip_acct_chain,frwl));
+ case IP_ACCT_DEL:
+ return( del_from_chain(&ip_acct_chain,frwl));
+ default:
+ /*
+ * Should be panic but... (Why ??? - AC)
+ */
+#ifdef DEBUG_CONFIG_IP_FIREWALL
+ printf("ip_acct_ctl: unknown request %d\n",stage);
+#endif
+ return(EINVAL);
+ }
+ }
+#ifdef DEBUG_CONFIG_IP_FIREWALL
+ printf("ip_acct_ctl: unknown request %d\n",stage);
+#endif
+ return(EINVAL);
+}
+#endif
+
+#ifdef CONFIG_IP_FIREWALL
+int ip_fw_ctl(int stage, void *m, int len)
+{
+ int ret;
+
+ if ( stage == IP_FW_FLUSH_BLK )
+ {
+ free_fw_chain(&ip_fw_blk_chain);
+ return(0);
+ }
+
+ if ( stage == IP_FW_FLUSH_FWD )
+ {
+ free_fw_chain(&ip_fw_fwd_chain);
+ return(0);
+ }
+
+ if ( stage == IP_FW_ZERO_BLK )
+ {
+ zero_fw_chain(ip_fw_blk_chain);
+ return(0);
+ }
+
+ if ( stage == IP_FW_ZERO_FWD )
+ {
+ zero_fw_chain(ip_fw_fwd_chain);
+ return(0);
+ }
+
+ if ( stage == IP_FW_POLICY_BLK || stage == IP_FW_POLICY_FWD )
+ {
+ int *tmp_policy_ptr;
+ tmp_policy_ptr=(int *)m;
+ if ( stage == IP_FW_POLICY_BLK )
+ ip_fw_blk_policy=*tmp_policy_ptr;
+ else
+ ip_fw_fwd_policy=*tmp_policy_ptr;
+ return 0;
+ }
+
+ if ( stage == IP_FW_CHK_BLK || stage == IP_FW_CHK_FWD )
+ {
+ struct device viadev;
+ struct ip_fwpkt *ipfwp;
+ struct iphdr *ip;
+
+ if ( len < sizeof(struct ip_fwpkt) )
+ {
+#ifdef DEBUG_CONFIG_IP_FIREWALL
+ printf("ip_fw_ctl: length=%d, expected %d\n",
+ len, sizeof(struct ip_fwpkt));
+#endif
+ return( EINVAL );
+ }
+
+ ipfwp = (struct ip_fwpkt *)m;
+ ip = &(ipfwp->fwp_iph);
+
+ if ( ip->ihl != sizeof(struct iphdr) / sizeof(int))
+ {
+#ifdef DEBUG_CONFIG_IP_FIREWALL
+ printf("ip_fw_ctl: ip->ihl=%d, want %d\n",ip->ihl,
+ sizeof(struct ip)/sizeof(int));
+#endif
+ return(EINVAL);
+ }
+
+ viadev.pa_addr = ipfwp->fwp_via.s_addr;
+
+ if ((ret = ip_fw_chk(ip, &viadev,
+ stage == IP_FW_CHK_BLK ?
+ ip_fw_blk_chain : ip_fw_fwd_chain,
+ stage == IP_FW_CHK_BLK ?
+ ip_fw_blk_policy : ip_fw_fwd_policy, 2 )) > 0
+ )
+ return(0);
+ else if (ret == -1)
+ return(ECONNREFUSED);
+ else
+ return(ETIMEDOUT);
+ }
+
+/*
+ * Here we really working hard-adding new elements
+ * to blocking/forwarding chains or deleting 'em
+ */
+
+ if ( stage == IP_FW_ADD_BLK || stage == IP_FW_ADD_FWD
+ || stage == IP_FW_DEL_BLK || stage == IP_FW_DEL_FWD
+ )
+ {
+ struct ip_fw *frwl;
+ frwl=check_ipfw_struct(m,len);
+ if (frwl==NULL)
+ return (EINVAL);
+
+ switch (stage)
+ {
+ case IP_FW_ADD_BLK:
+ return(add_to_chain(&ip_fw_blk_chain,frwl));
+ case IP_FW_ADD_FWD:
+ return(add_to_chain(&ip_fw_fwd_chain,frwl));
+ case IP_FW_DEL_BLK:
+ return(del_from_chain(&ip_fw_blk_chain,frwl));
+ case IP_FW_DEL_FWD:
+ return(del_from_chain(&ip_fw_fwd_chain,frwl));
+ default:
+ /*
+ * Should be panic but... (Why are BSD people panic obsessed ??)
+ */
+#ifdef DEBUG_CONFIG_IP_FIREWALL
+ printk("ip_fw_ctl: unknown request %d\n",stage);
+#endif
+ return(EINVAL);
+ }
+ }
+
+#ifdef DEBUG_CONFIG_IP_FIREWALL
+ printf("ip_fw_ctl: unknown request %d\n",stage);
+#endif
+ return(EINVAL);
+}
+#endif /* CONFIG_IP_FIREWALL */
+
+#if defined(CONFIG_IP_FIREWALL) || defined(CONFIG_IP_ACCT)
+
+static int ip_chain_procinfo(int stage, char *buffer, char **start,
+ off_t offset, int length, int reset)
+{
+ off_t pos=0, begin=0;
+ struct ip_fw *i;
+ unsigned long flags;
+ int len, p;
+
+
+ switch(stage)
+ {
+#ifdef CONFIG_IP_FIREWALL
+ case IP_INFO_BLK:
+ i = ip_fw_blk_chain;
+ len=sprintf(buffer, "IP firewall block rules, default %d\n",
+ ip_fw_blk_policy);
+ break;
+ case IP_INFO_FWD:
+ i = ip_fw_fwd_chain;
+ len=sprintf(buffer, "IP firewall forward rules, default %d\n",
+ ip_fw_fwd_policy);
+ break;
+#endif
+#ifdef CONFIG_IP_ACCT
+ case IP_INFO_ACCT:
+ i = ip_acct_chain;
+ len=sprintf(buffer,"IP accounting rules\n");
+ break;
+#endif
+ default:
+ /* this should never be reached, but safety first... */
+ i = NULL;
+ len=0;
+ break;
+ }
+
+ save_flags(flags);
+ cli();
+
+ while(i!=NULL)
+ {
+ len+=sprintf(buffer+len,"%08lX/%08lX->%08lX/%08lX %08lX %X ",
+ ntohl(i->fw_src.s_addr),ntohl(i->fw_smsk.s_addr),
+ ntohl(i->fw_dst.s_addr),ntohl(i->fw_dmsk.s_addr),
+ ntohl(i->fw_via.s_addr),i->fw_flg);
+ len+=sprintf(buffer+len,"%u %u %lu %lu",
+ i->fw_nsp,i->fw_ndp, i->fw_pcnt,i->fw_bcnt);
+ for (p = 0; p < IP_FW_MAX_PORTS; p++)
+ len+=sprintf(buffer+len, " %u", i->fw_pts[p]);
+ buffer[len++]='\n';
+ buffer[len]='\0';
+ pos=begin+len;
+ if(pos<offset)
+ {
+ len=0;
+ begin=pos;
+ }
+ else if(reset)
+ {
+ /* This needs to be done at this specific place! */
+ i->fw_pcnt=0L;
+ i->fw_bcnt=0L;
+ }
+ if(pos>offset+length)
+ break;
+ i=i->fw_next;
+ }
+ restore_flags(flags);
+ *start=buffer+(offset-begin);
+ len-=(offset-begin);
+ if(len>length)
+ len=length;
+ return len;
+}
+#endif
+
+#ifdef CONFIG_IP_ACCT
+
+int ip_acct_procinfo(char *buffer, char **start, off_t offset, int length, int reset)
+{
+ return ip_chain_procinfo(IP_INFO_ACCT, buffer,start,offset,length,reset);
+}
+
+#endif
+
+#ifdef CONFIG_IP_FIREWALL
+
+int ip_fw_blk_procinfo(char *buffer, char **start, off_t offset, int length, int reset)
+{
+ return ip_chain_procinfo(IP_INFO_BLK, buffer,start,offset,length,reset);
+}
+
+int ip_fw_fwd_procinfo(char *buffer, char **start, off_t offset, int length, int reset)
+{
+ return ip_chain_procinfo(IP_INFO_FWD, buffer,start,offset,length,reset);
+}
+
+#endif