patch-2.3.99-pre2 linux/net/ipv4/netfilter/ipfwadm_core.c
Next file: linux/net/ipv4/netfilter/ipt_LOG.c
Previous file: linux/net/ipv4/netfilter/ipchains_core.c
Back to the patch index
Back to the overall index
- Lines: 1411
- Date:
Fri Mar 17 10:56:20 2000
- Orig file:
v2.3.99-pre1/linux/net/ipv4/netfilter/ipfwadm_core.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.3.99-pre1/linux/net/ipv4/netfilter/ipfwadm_core.c linux/net/ipv4/netfilter/ipfwadm_core.c
@@ -0,0 +1,1410 @@
+/* Minor modifications to fit on compatibility framework:
+ Rusty.Russell@rustcorp.com.au
+*/
+
+#define CONFIG_IP_FIREWALL
+#define CONFIG_IP_FIREWALL_VERBOSE
+#define CONFIG_IP_MASQUERADE
+#define CONFIG_IP_ACCT
+#define CONFIG_IP_TRANSPARENT_PROXY
+#define CONFIG_IP_FIREWALL_NETLINK
+
+/*
+ * 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.
+ *
+ * $Id: ipfwadm_core.c,v 1.1 2000/03/17 14:42:00 davem Exp $
+ *
+ * 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.
+ * Established connections (ACK check), ACK check on bidirectional rules,
+ * ICMP type check.
+ * Wilfred Mollenvanger 7/7/1995.
+ * TCP attack protection.
+ * Alan Cox 25/8/95, based on information from bugtraq.
+ * ICMP type printk, IP_FW_F_APPEND
+ * Bernd Eckenfels 1996-01-31
+ * Split blocking chain into input and output chains, add new "insert" and
+ * "append" commands to replace semi-intelligent "add" command, let "delete".
+ * only delete the first matching entry, use 0xFFFF (0xFF) as ports (ICMP
+ * types) when counting packets being 2nd and further fragments.
+ * Jos Vos <jos@xos.nl> 8/2/1996.
+ * Add support for matching on device names.
+ * Jos Vos <jos@xos.nl> 15/2/1996.
+ * Transparent proxying support.
+ * Willy Konynenberg <willy@xos.nl> 10/5/96.
+ * Make separate accounting on incoming and outgoing packets possible.
+ * Jos Vos <jos@xos.nl> 18/5/1996.
+ * Added trap out of bad frames.
+ * Alan Cox <alan@cymru.net> 17/11/1996
+ *
+ *
+ * Masquerading functionality
+ *
+ * Copyright (c) 1994 Pauline Middelink
+ *
+ * The pieces which added masquerading functionality are totally
+ * my responsibility and have nothing to with the original authors
+ * copyright or doing.
+ *
+ * Parts distributed under GPL.
+ *
+ * Fixes:
+ * Pauline Middelink : Added masquerading.
+ * Alan Cox : Fixed an error in the merge.
+ * Thomas Quinot : Fixed port spoofing.
+ * Alan Cox : Cleaned up retransmits in spoofing.
+ * Alan Cox : Cleaned up length setting.
+ * Wouter Gadeyne : Fixed masquerading support of ftp PORT commands
+ *
+ * Juan Jose Ciarlante : Masquerading code moved to ip_masq.c
+ * Andi Kleen : Print frag_offsets and the ip flags properly.
+ *
+ * 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/uaccess.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/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 <net/ip.h>
+#include <net/protocol.h>
+#include <net/route.h>
+#include <net/tcp.h>
+#include <net/udp.h>
+#include <net/sock.h>
+#include <net/icmp.h>
+#include <linux/netlink.h>
+#include <linux/init.h>
+#include <linux/netfilter_ipv4/ipfwadm_core.h>
+#include <linux/netfilter_ipv4/compat_firewall.h>
+
+#include <net/checksum.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/version.h>
+
+/*
+ * Implement IP packet firewall
+ */
+
+#ifdef DEBUG_IP_FIREWALL
+#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) printk("%d.%d.%d.%d",(ntohl(a)>>24)&0xFF,\
+ (ntohl(a)>>16)&0xFF,\
+ (ntohl(a)>>8)&0xFF,\
+ (ntohl(a))&0xFF);
+
+#ifdef DEBUG_IP_FIREWALL
+#define dprint_ip(a) print_ip(a)
+#else
+#define dprint_ip(a)
+#endif
+
+#if defined(CONFIG_IP_ACCT) || defined(CONFIG_IP_FIREWALL)
+
+struct ip_fw *ip_fw_fwd_chain;
+struct ip_fw *ip_fw_in_chain;
+struct ip_fw *ip_fw_out_chain;
+struct ip_fw *ip_acct_chain;
+struct ip_fw *ip_masq_chain;
+
+static struct ip_fw **chains[] =
+ {&ip_fw_fwd_chain, &ip_fw_in_chain, &ip_fw_out_chain, &ip_acct_chain,
+ &ip_masq_chain
+ };
+#endif /* CONFIG_IP_ACCT || CONFIG_IP_FIREWALL */
+
+#ifdef CONFIG_IP_FIREWALL
+int ip_fw_fwd_policy=IP_FW_F_ACCEPT;
+int ip_fw_in_policy=IP_FW_F_ACCEPT;
+int ip_fw_out_policy=IP_FW_F_ACCEPT;
+
+static int *policies[] =
+ {&ip_fw_fwd_policy, &ip_fw_in_policy, &ip_fw_out_policy};
+
+#endif
+
+#ifdef CONFIG_IP_FIREWALL_NETLINK
+struct sock *ipfwsk;
+#endif
+
+/*
+ * 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)
+
+#ifdef CONFIG_IP_FIREWALL_VERBOSE
+
+/*
+ * VERY ugly piece of code which actually makes kernel printf for
+ * matching packets.
+ */
+
+static char *chain_name(struct ip_fw *chain, int mode)
+{
+ switch (mode) {
+ case IP_FW_MODE_ACCT_IN: return "acct in";
+ case IP_FW_MODE_ACCT_OUT: return "acct out";
+ default:
+ if (chain == ip_fw_fwd_chain)
+ return "fw-fwd";
+ else if (chain == ip_fw_in_chain)
+ return "fw-in";
+ else
+ return "fw-out";
+ }
+}
+
+static char *rule_name(struct ip_fw *f, int mode, char *buf)
+{
+ if (mode == IP_FW_MODE_ACCT_IN || mode == IP_FW_MODE_ACCT_OUT)
+ return "";
+
+ if(f->fw_flg&IP_FW_F_ACCEPT) {
+ if(f->fw_flg&IP_FW_F_REDIR) {
+ sprintf(buf, "acc/r%d ", f->fw_pts[f->fw_nsp+f->fw_ndp]);
+ return buf;
+ } else if(f->fw_flg&IP_FW_F_MASQ)
+ return "acc/masq ";
+ else
+ return "acc ";
+ } else if(f->fw_flg&IP_FW_F_ICMPRPL) {
+ return "rej ";
+ } else {
+ return "deny ";
+ }
+}
+
+static void print_packet(struct iphdr *ip,
+ u16 src_port, u16 dst_port, u16 icmp_type,
+ char *chain, char *rule, char *devname)
+{
+ __u32 *opt = (__u32 *) (ip + 1);
+ int opti;
+ __u16 foff = ntohs(ip->frag_off);
+
+ printk(KERN_INFO "IP %s %s%s", chain, rule, devname);
+
+ switch(ip->protocol)
+ {
+ case IPPROTO_TCP:
+ printk(" TCP ");
+ break;
+ case IPPROTO_UDP:
+ printk(" UDP ");
+ break;
+ case IPPROTO_ICMP:
+ printk(" ICMP/%d ", icmp_type);
+ break;
+ default:
+ printk(" PROTO=%d ", ip->protocol);
+ break;
+ }
+ print_ip(ip->saddr);
+ if(ip->protocol == IPPROTO_TCP || ip->protocol == IPPROTO_UDP)
+ printk(":%hu", src_port);
+ printk(" ");
+ print_ip(ip->daddr);
+ if(ip->protocol == IPPROTO_TCP || ip->protocol == IPPROTO_UDP)
+ printk(":%hu", dst_port);
+ printk(" L=%hu S=0x%2.2hX I=%hu FO=0x%4.4hX T=%hu",
+ ntohs(ip->tot_len), ip->tos, ntohs(ip->id),
+ foff & IP_OFFSET, ip->ttl);
+ if (foff & IP_DF) printk(" DF=1");
+ if (foff & IP_MF) printk(" MF=1");
+ for (opti = 0; opti < (ip->ihl - sizeof(struct iphdr) / 4); opti++)
+ printk(" O=0x%8.8X", *opt++);
+ printk("\n");
+}
+#endif
+
+/*
+ * Returns one of the generic firewall policies, like FW_ACCEPT.
+ * Also does accounting so you can feed it the accounting chain.
+ *
+ * The modes is either IP_FW_MODE_FW (normal firewall mode),
+ * IP_FW_MODE_ACCT_IN or IP_FW_MODE_ACCT_OUT (accounting mode,
+ * steps through the entire chain and handles fragments
+ * differently), or IP_FW_MODE_CHK (handles user-level check,
+ * counters are not updated).
+ */
+
+
+int ip_fw_chk(struct iphdr *ip, struct net_device *rif, __u16 *redirport,
+ struct ip_fw *chain, int policy, int mode)
+{
+ struct ip_fw *f;
+ struct tcphdr *tcp=(struct tcphdr *)((__u32 *)ip+ip->ihl);
+ struct udphdr *udp=(struct udphdr *)((__u32 *)ip+ip->ihl);
+ struct icmphdr *icmp=(struct icmphdr *)((__u32 *)ip+ip->ihl);
+ __u32 src, dst;
+ __u16 src_port=0xFFFF, dst_port=0xFFFF, icmp_type=0xFF;
+ unsigned short f_prt=0, prt;
+ char notcpsyn=0, notcpack=0, match;
+ unsigned short offset;
+ int answer;
+ unsigned char tosand, tosxor;
+
+ /*
+ * 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.
+ */
+
+ offset = ntohs(ip->frag_off) & IP_OFFSET;
+
+ /*
+ * Don't allow a fragment of TCP 8 bytes in. Nobody
+ * normal causes this. Its a cracker trying to break
+ * in by doing a flag overwrite to pass the direction
+ * checks.
+ */
+
+ if (offset == 1 && ip->protocol == IPPROTO_TCP)
+ return FW_BLOCK;
+
+ if (offset!=0 && !(mode & (IP_FW_MODE_ACCT_IN|IP_FW_MODE_ACCT_OUT)) &&
+ (ip->protocol == IPPROTO_TCP || ip->protocol == IPPROTO_UDP ||
+ ip->protocol == IPPROTO_ICMP))
+ return FW_ACCEPT;
+
+ /*
+ * Header fragment for TCP is too small to check the bits.
+ */
+
+ if(ip->protocol==IPPROTO_TCP && (ip->ihl<<2)+16 > ntohs(ip->tot_len))
+ return FW_BLOCK;
+
+ /*
+ * Too short.
+ *
+ * But only too short for a packet with ports...
+ */
+
+ else if((ntohs(ip->tot_len)<8+(ip->ihl<<2))&&(ip->protocol==IPPROTO_TCP || ip->protocol==IPPROTO_UDP))
+ return FW_BLOCK;
+
+ 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 0xFFFF if it is not the first fragment */
+ if (!offset) {
+ src_port=ntohs(tcp->source);
+ dst_port=ntohs(tcp->dest);
+ if(!tcp->ack && !tcp->rst)
+ /* We do NOT have ACK, value TRUE */
+ notcpack=1;
+ if(!tcp->syn || !notcpack)
+ /* We do NOT have SYN, value TRUE */
+ notcpsyn=1;
+ }
+ prt=IP_FW_F_TCP;
+ break;
+ case IPPROTO_UDP:
+ dprintf1("UDP ");
+ /* ports stay 0xFFFF if it is not the first fragment */
+ if (!offset) {
+ src_port=ntohs(udp->source);
+ dst_port=ntohs(udp->dest);
+ }
+ prt=IP_FW_F_UDP;
+ break;
+ case IPPROTO_ICMP:
+ /* icmp_type stays 255 if it is not the first fragment */
+ if (!offset)
+ icmp_type=(__u16)(icmp->type);
+ dprintf2("ICMP:%d ",icmp_type);
+ prt=IP_FW_F_ICMP;
+ break;
+ default:
+ dprintf2("p=%d ",ip->protocol);
+ prt=IP_FW_F_ALL;
+ break;
+ }
+#ifdef DEBUG_IP_FIREWALL
+ dprint_ip(ip->saddr);
+
+ if (ip->protocol==IPPROTO_TCP || ip->protocol==IPPROTO_UDP)
+ /* This will print 65535 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 65535 when it is not the first fragment! */
+ dprintf2(":%d ",dst_port);
+ dprintf1("\n");
+#endif
+
+ 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)
+ continue;
+
+ /*
+ * Look for a VIA device match
+ */
+ if(f->fw_viadev)
+ {
+ if(rif!=f->fw_viadev)
+ continue; /* Mismatch */
+ }
+
+ /* This looks stupid, because we scan almost static
+ list, searching for static key. However, this way seems
+ to be only reasonable way of handling fw_via rules
+ (btw bsd makes the same thing).
+
+ It will not affect performance if you will follow
+ the following simple rules:
+
+ - if inteface is aliased, ALWAYS specify fw_viadev,
+ so that previous check will guarantee, that we will
+ not waste time when packet arrive on another interface.
+
+ - avoid using fw_via.s_addr if fw_via.s_addr is owned
+ by an aliased interface.
+
+ --ANK
+ */
+ if (f->fw_via.s_addr && rif) {
+ struct in_ifaddr *ifa;
+
+ if (rif->ip_ptr == NULL)
+ continue; /* Mismatch */
+
+ for (ifa = ((struct in_device*)(rif->ip_ptr))->ifa_list;
+ ifa; ifa = ifa->ifa_next) {
+ if (ifa->ifa_local == f->fw_via.s_addr)
+ goto ifa_ok;
+ }
+ continue; /* Mismatch */
+
+ ifa_ok:
+ }
+
+ /*
+ * Ok the chain addresses match.
+ */
+
+#ifdef CONFIG_IP_ACCT
+ /*
+ * See if we're in accounting mode and only want to
+ * count incoming or outgoing packets.
+ */
+
+ if (mode & (IP_FW_MODE_ACCT_IN|IP_FW_MODE_ACCT_OUT) &&
+ ((mode == IP_FW_MODE_ACCT_IN && f->fw_flg&IP_FW_F_ACCTOUT) ||
+ (mode == IP_FW_MODE_ACCT_OUT && f->fw_flg&IP_FW_F_ACCTIN)))
+ continue;
+
+#endif
+ /*
+ * For all non-TCP packets and/or non-first fragments,
+ * notcpsyn and notcpack will always be FALSE,
+ * so the IP_FW_F_TCPSYN and IP_FW_F_TCPACK flags
+ * are actually ignored for these packets.
+ */
+
+ if((f->fw_flg&IP_FW_F_TCPSYN) && notcpsyn)
+ continue;
+
+ if((f->fw_flg&IP_FW_F_TCPACK) && notcpack)
+ continue;
+
+ f_prt=f->fw_flg&IP_FW_F_KIND;
+ if (f_prt!=IP_FW_F_ALL)
+ {
+ /*
+ * Specific firewall - packet's protocol
+ * must match firewall's.
+ */
+
+ if(prt!=f_prt)
+ continue;
+
+ if((prt==IP_FW_F_ICMP &&
+ ! port_match(&f->fw_pts[0], f->fw_nsp,
+ icmp_type,f->fw_flg&IP_FW_F_SRNG)) ||
+ !(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
+ if (f->fw_flg & IP_FW_F_PRN)
+ {
+ char buf[16];
+
+ print_packet(ip, src_port, dst_port, icmp_type,
+ chain_name(chain, mode),
+ rule_name(f, mode, buf),
+ rif ? rif->name : "-");
+ }
+#endif
+ if (mode != IP_FW_MODE_CHK) {
+ f->fw_bcnt+=ntohs(ip->tot_len);
+ f->fw_pcnt++;
+ }
+ if (!(mode & (IP_FW_MODE_ACCT_IN|IP_FW_MODE_ACCT_OUT)))
+ break;
+ } /* Loop */
+
+ if (!(mode & (IP_FW_MODE_ACCT_IN|IP_FW_MODE_ACCT_OUT))) {
+
+ /*
+ * 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) {
+ policy=f->fw_flg;
+ tosand=f->fw_tosand;
+ tosxor=f->fw_tosxor;
+ } else {
+ tosand=0xFF;
+ tosxor=0x00;
+ }
+
+ if (policy&IP_FW_F_ACCEPT) {
+ /* Adjust priority and recompute checksum */
+ __u8 old_tos = ip->tos;
+ ip->tos = (old_tos & tosand) ^ tosxor;
+ if (ip->tos != old_tos)
+ ip_send_check(ip);
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+ if (policy&IP_FW_F_REDIR) {
+ if (redirport)
+ if ((*redirport = htons(f->fw_pts[f->fw_nsp+f->fw_ndp])) == 0) {
+ /* Wildcard redirection.
+ * Note that redirport will become
+ * 0xFFFF for non-TCP/UDP packets.
+ */
+ *redirport = htons(dst_port);
+ }
+ answer = FW_REDIRECT;
+ } else
+#endif
+#ifdef CONFIG_IP_MASQUERADE
+ if (policy&IP_FW_F_MASQ)
+ answer = FW_MASQUERADE;
+ else
+#endif
+ answer = FW_ACCEPT;
+
+ } else if(policy&IP_FW_F_ICMPRPL)
+ answer = FW_REJECT;
+ else
+ answer = FW_BLOCK;
+
+#ifdef CONFIG_IP_FIREWALL_NETLINK
+ if((policy&IP_FW_F_PRN) && (answer == FW_REJECT || answer == FW_BLOCK))
+ {
+ struct sk_buff *skb=alloc_skb(128, GFP_ATOMIC);
+ if(skb)
+ {
+ int len=min(128,ntohs(ip->tot_len));
+ skb_put(skb,len);
+ memcpy(skb->data,ip,len);
+ if(netlink_post(NETLINK_FIREWALL, skb))
+ kfree_skb(skb);
+ }
+ }
+#endif
+ return answer;
+ } else
+ /* we're doing accounting, always ok */
+ 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 insert_in_chain(struct ip_fw *volatile* chainptr, struct ip_fw *frwl,int len)
+{
+ struct ip_fw *ftmp;
+ unsigned long flags;
+
+ save_flags(flags);
+
+ ftmp = kmalloc( sizeof(struct ip_fw), GFP_ATOMIC );
+ if ( ftmp == NULL )
+ {
+#ifdef DEBUG_IP_FIREWALL
+ printk("ip_fw_ctl: malloc said no\n");
+#endif
+ return( ENOMEM );
+ }
+
+ memcpy(ftmp, frwl, len);
+ /*
+ * Allow the more recent "minimise cost" flag to be
+ * set. [Rob van Nieuwkerk]
+ */
+ ftmp->fw_tosand |= 0x01;
+ ftmp->fw_tosxor &= 0xFE;
+ ftmp->fw_pcnt=0L;
+ ftmp->fw_bcnt=0L;
+
+ cli();
+
+ if ((ftmp->fw_vianame)[0]) {
+ if (!(ftmp->fw_viadev = dev_get_by_name(ftmp->fw_vianame)))
+ ftmp->fw_viadev = (struct net_device *) -1;
+ } else
+ ftmp->fw_viadev = NULL;
+
+ ftmp->fw_next = *chainptr;
+ *chainptr=ftmp;
+ restore_flags(flags);
+ return(0);
+}
+
+static int append_to_chain(struct ip_fw *volatile* chainptr, struct ip_fw *frwl,int len)
+{
+ struct ip_fw *ftmp;
+ struct ip_fw *chtmp=NULL;
+ struct ip_fw *volatile chtmp_prev=NULL;
+ unsigned long flags;
+
+ save_flags(flags);
+
+ ftmp = kmalloc( sizeof(struct ip_fw), GFP_ATOMIC );
+ if ( ftmp == NULL )
+ {
+#ifdef DEBUG_IP_FIREWALL
+ printk("ip_fw_ctl: malloc said no\n");
+#endif
+ return( ENOMEM );
+ }
+
+ memcpy(ftmp, frwl, len);
+ /*
+ * Allow the more recent "minimise cost" flag to be
+ * set. [Rob van Nieuwkerk]
+ */
+ ftmp->fw_tosand |= 0x01;
+ ftmp->fw_tosxor &= 0xFE;
+ ftmp->fw_pcnt=0L;
+ ftmp->fw_bcnt=0L;
+
+ ftmp->fw_next = NULL;
+
+ cli();
+
+ if ((ftmp->fw_vianame)[0]) {
+ if (!(ftmp->fw_viadev = dev_get_by_name(ftmp->fw_vianame)))
+ ftmp->fw_viadev = (struct net_device *) -1;
+ } else
+ ftmp->fw_viadev = NULL;
+
+ chtmp_prev=NULL;
+ for (chtmp=*chainptr;chtmp!=NULL;chtmp=chtmp->fw_next)
+ 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_IP_FIREWALL
+ printk("ip_fw_ctl: chain is empty\n");
+#endif
+ restore_flags(flags);
+ return( EINVAL );
+ }
+
+ ltmp=NULL;
+ was_found=0;
+
+ while( !was_found && 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 (strncmp(ftmp->fw_vianame, frwl->fw_vianame, IFNAMSIZ))
+ 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_IP_FIREWALL
+ printk("ip_fw_ctl: len=%d, want %d\n",len, sizeof(struct ip_fw));
+#endif
+ return(NULL);
+ }
+
+ if ( (frwl->fw_flg & ~IP_FW_F_MASK) != 0 )
+ {
+#ifdef DEBUG_IP_FIREWALL
+ printk("ip_fw_ctl: undefined flag bits set (flags=%x)\n",
+ frwl->fw_flg);
+#endif
+ return(NULL);
+ }
+
+#ifndef CONFIG_IP_TRANSPARENT_PROXY
+ if (frwl->fw_flg & IP_FW_F_REDIR) {
+#ifdef DEBUG_IP_FIREWALL
+ printk("ip_fw_ctl: unsupported flag IP_FW_F_REDIR\n");
+#endif
+ return(NULL);
+ }
+#endif
+
+#ifndef CONFIG_IP_MASQUERADE
+ if (frwl->fw_flg & IP_FW_F_MASQ) {
+#ifdef DEBUG_IP_FIREWALL
+ printk("ip_fw_ctl: unsupported flag IP_FW_F_MASQ\n");
+#endif
+ return(NULL);
+ }
+#endif
+
+ if ( (frwl->fw_flg & IP_FW_F_SRNG) && frwl->fw_nsp < 2 )
+ {
+#ifdef DEBUG_IP_FIREWALL
+ printk("ip_fw_ctl: src range set but fw_nsp=%d\n",
+ frwl->fw_nsp);
+#endif
+ return(NULL);
+ }
+
+ if ( (frwl->fw_flg & IP_FW_F_DRNG) && frwl->fw_ndp < 2 )
+ {
+#ifdef DEBUG_IP_FIREWALL
+ printk("ip_fw_ctl: dst range set but fw_ndp=%d\n",
+ frwl->fw_ndp);
+#endif
+ return(NULL);
+ }
+
+ if ( frwl->fw_nsp + frwl->fw_ndp > (frwl->fw_flg & IP_FW_F_REDIR ? IP_FW_MAX_PORTS - 1 : IP_FW_MAX_PORTS) )
+ {
+#ifdef DEBUG_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
+
+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_INSERT || stage == IP_ACCT_APPEND ||
+ stage == IP_ACCT_DELETE )
+ {
+ struct ip_fw *frwl;
+
+ if (!(frwl=check_ipfw_struct(m,len)))
+ return (EINVAL);
+
+ switch (stage)
+ {
+ case IP_ACCT_INSERT:
+ return( insert_in_chain(&ip_acct_chain,frwl,len));
+ case IP_ACCT_APPEND:
+ return( append_to_chain(&ip_acct_chain,frwl,len));
+ case IP_ACCT_DELETE:
+ return( del_from_chain(&ip_acct_chain,frwl));
+ default:
+ /*
+ * Should be panic but... (Why ??? - AC)
+ */
+#ifdef DEBUG_IP_FIREWALL
+ printk("ip_acct_ctl: unknown request %d\n",stage);
+#endif
+ return(EINVAL);
+ }
+ }
+#ifdef DEBUG_IP_FIREWALL
+ printk("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 cmd, fwtype;
+
+ cmd = stage & IP_FW_COMMAND;
+ fwtype = (stage & IP_FW_TYPE) >> IP_FW_SHIFT;
+
+ if ( cmd == IP_FW_FLUSH )
+ {
+ free_fw_chain(chains[fwtype]);
+ return(0);
+ }
+
+ if ( cmd == IP_FW_ZERO )
+ {
+ zero_fw_chain(*chains[fwtype]);
+ return(0);
+ }
+
+ if ( cmd == IP_FW_POLICY )
+ {
+ int *tmp_policy_ptr;
+ tmp_policy_ptr=(int *)m;
+ *policies[fwtype] = *tmp_policy_ptr;
+ return 0;
+ }
+
+ if ( cmd == IP_FW_CHECK )
+ {
+ struct net_device *viadev;
+ struct ip_fwpkt *ipfwp;
+ struct iphdr *ip;
+
+ if ( len != sizeof(struct ip_fwpkt) )
+ {
+#ifdef DEBUG_IP_FIREWALL
+ printk("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 ( !(viadev = dev_get_by_name(ipfwp->fwp_vianame)) ) {
+#ifdef DEBUG_IP_FIREWALL
+ printk("ip_fw_ctl: invalid device \"%s\"\n", ipfwp->fwp_vianame);
+#endif
+ return(EINVAL);
+ } else if ( ip->ihl != sizeof(struct iphdr) / sizeof(int)) {
+#ifdef DEBUG_IP_FIREWALL
+ printk("ip_fw_ctl: ip->ihl=%d, want %d\n",ip->ihl,
+ sizeof(struct iphdr)/sizeof(int));
+#endif
+ return(EINVAL);
+ }
+
+ switch (ip_fw_chk(ip, viadev, NULL, *chains[fwtype],
+ *policies[fwtype], IP_FW_MODE_CHK))
+ {
+ case FW_ACCEPT:
+ return(0);
+ case FW_REDIRECT:
+ return(ECONNABORTED);
+ case FW_MASQUERADE:
+ return(ECONNRESET);
+ case FW_REJECT:
+ return(ECONNREFUSED);
+ default: /* FW_BLOCK */
+ return(ETIMEDOUT);
+ }
+ }
+
+ if ( cmd == IP_FW_MASQ_TIMEOUTS )
+ return ip_fw_masq_timeouts(m, len);
+
+/*
+ * Here we really working hard-adding new elements
+ * to blocking/forwarding chains or deleting 'em
+ */
+
+ if ( cmd == IP_FW_INSERT || cmd == IP_FW_APPEND || cmd == IP_FW_DELETE )
+ {
+ struct ip_fw *frwl;
+ int fwtype;
+
+ frwl=check_ipfw_struct(m,len);
+ if (frwl==NULL)
+ return (EINVAL);
+ fwtype = (stage & IP_FW_TYPE) >> IP_FW_SHIFT;
+
+ switch (cmd)
+ {
+ case IP_FW_INSERT:
+ return(insert_in_chain(chains[fwtype],frwl,len));
+ case IP_FW_APPEND:
+ return(append_to_chain(chains[fwtype],frwl,len));
+ case IP_FW_DELETE:
+ return(del_from_chain(chains[fwtype],frwl));
+ default:
+ /*
+ * Should be panic but... (Why are BSD people panic obsessed ??)
+ */
+#ifdef DEBUG_IP_FIREWALL
+ printk("ip_fw_ctl: unknown request %d\n",stage);
+#endif
+ return(EINVAL);
+ }
+ }
+
+#ifdef DEBUG_IP_FIREWALL
+ printk("ip_fw_ctl: unknown request %d\n",stage);
+#endif
+ return(ENOPROTOOPT);
+}
+#endif /* CONFIG_IP_FIREWALL */
+
+#ifdef CONFIG_PROC_FS
+#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;
+ int last_len = 0;
+
+
+ switch(stage)
+ {
+#ifdef CONFIG_IP_FIREWALL
+ case IP_FW_IN:
+ i = ip_fw_in_chain;
+ len=sprintf(buffer, "IP firewall input rules, default %d\n",
+ ip_fw_in_policy);
+ break;
+ case IP_FW_OUT:
+ i = ip_fw_out_chain;
+ len=sprintf(buffer, "IP firewall output rules, default %d\n",
+ ip_fw_out_policy);
+ break;
+ case IP_FW_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_FW_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,"%08X/%08X->%08X/%08X %.16s %08X %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),
+ (i->fw_vianame)[0] ? i->fw_vianame : "-",
+ ntohl(i->fw_via.s_addr), i->fw_flg);
+ /* 10 is enough for a 32 bit box but the counters are 64bit on
+ the Alpha and Ultrapenguin */
+ len+=sprintf(buffer+len,"%u %u %-20lu %-20lu",
+ 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]);
+ len+=sprintf(buffer+len, " A%02X X%02X", i->fw_tosand, i->fw_tosxor);
+ buffer[len++]='\n';
+ buffer[len]='\0';
+ pos=begin+len;
+ if(pos<offset)
+ {
+ len=0;
+ begin=pos;
+ }
+ else if(pos>offset+length)
+ {
+ len = last_len;
+ break;
+ }
+ else if(reset)
+ {
+ /* This needs to be done at this specific place! */
+ i->fw_pcnt=0L;
+ i->fw_bcnt=0L;
+ }
+ last_len = len;
+ 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
+
+static int ip_acct_procinfo(char *buffer, char **start, off_t offset,
+ int length
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,29)
+ , int reset
+#endif
+ )
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,29)
+ /* FIXME: No more `atomic' read and reset. Wonderful 8-( --RR */
+ int reset = 0;
+#endif
+ return ip_chain_procinfo(IP_FW_ACCT, buffer,start, offset,length,
+ reset);
+}
+
+#endif
+
+#ifdef CONFIG_IP_FIREWALL
+
+static int ip_fw_in_procinfo(char *buffer, char **start, off_t offset,
+ int length
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,29)
+ , int reset
+#endif
+ )
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,29)
+ /* FIXME: No more `atomic' read and reset. Wonderful 8-( --RR */
+ int reset = 0;
+#endif
+ return ip_chain_procinfo(IP_FW_IN, buffer,start,offset,length,
+ reset);
+}
+
+static int ip_fw_out_procinfo(char *buffer, char **start, off_t offset,
+ int length
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,29)
+ , int reset
+#endif
+ )
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,29)
+ /* FIXME: No more `atomic' read and reset. Wonderful 8-( --RR */
+ int reset = 0;
+#endif
+ return ip_chain_procinfo(IP_FW_OUT, buffer,start,offset,length,
+ reset);
+}
+
+static int ip_fw_fwd_procinfo(char *buffer, char **start, off_t offset,
+ int length
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,29)
+ , int reset
+#endif
+ )
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,29)
+ /* FIXME: No more `atomic' read and reset. Wonderful 8-( --RR */
+ int reset = 0;
+#endif
+ return ip_chain_procinfo(IP_FW_FWD, buffer,start,offset,length,
+ reset);
+}
+#endif
+#endif
+
+
+#ifdef CONFIG_IP_FIREWALL
+/*
+ * Interface to the generic firewall chains.
+ */
+
+int ipfw_input_check(struct firewall_ops *this, int pf,
+ struct net_device *dev, void *phdr, void *arg,
+ struct sk_buff **pskb)
+{
+ return ip_fw_chk(phdr, dev, arg, ip_fw_in_chain, ip_fw_in_policy,
+ IP_FW_MODE_FW);
+}
+
+int ipfw_output_check(struct firewall_ops *this, int pf,
+ struct net_device *dev, void *phdr, void *arg,
+ struct sk_buff **pskb)
+{
+ return ip_fw_chk(phdr, dev, arg, ip_fw_out_chain, ip_fw_out_policy,
+ IP_FW_MODE_FW);
+}
+
+int ipfw_forward_check(struct firewall_ops *this, int pf,
+ struct net_device *dev, void *phdr, void *arg,
+ struct sk_buff **pskb)
+{
+ return ip_fw_chk(phdr, dev, arg, ip_fw_fwd_chain, ip_fw_fwd_policy,
+ IP_FW_MODE_FW);
+}
+
+#ifdef CONFIG_IP_ACCT
+int ipfw_acct_in(struct firewall_ops *this, int pf, struct net_device *dev,
+ void *phdr, void *arg, struct sk_buff **pskb)
+{
+ return ip_fw_chk(phdr,dev,NULL,ip_acct_chain,0,IP_FW_MODE_ACCT_IN);
+}
+
+int ipfw_acct_out(struct firewall_ops *this, int pf, struct net_device *dev,
+ void *phdr, void *arg, struct sk_buff **pskb)
+{
+ return ip_fw_chk(phdr,dev,NULL,ip_acct_chain,0,IP_FW_MODE_ACCT_OUT);
+}
+#endif
+
+struct firewall_ops ipfw_ops=
+{
+ NULL,
+ ipfw_forward_check,
+ ipfw_input_check,
+ ipfw_output_check,
+#ifdef CONFIG_IP_ACCT
+ ipfw_acct_in,
+ ipfw_acct_out
+#else
+ NULL,
+ NULL
+#endif
+};
+
+#endif
+
+#if defined(CONFIG_IP_ACCT) || defined(CONFIG_IP_FIREWALL)
+
+int ipfw_device_event(struct notifier_block *this, unsigned long event, void *ptr)
+{
+ struct net_device *dev=ptr;
+ char *devname = dev->name;
+ unsigned long flags;
+ struct ip_fw *fw;
+ int chn;
+
+ save_flags(flags);
+ cli();
+
+ if (event == NETDEV_UP) {
+ for (chn = 0; chn < IP_FW_CHAINS; chn++)
+ for (fw = *chains[chn]; fw; fw = fw->fw_next)
+ if ((fw->fw_vianame)[0] && !strncmp(devname,
+ fw->fw_vianame, IFNAMSIZ))
+ fw->fw_viadev = dev;
+ } else if (event == NETDEV_DOWN) {
+ for (chn = 0; chn < IP_FW_CHAINS; chn++)
+ for (fw = *chains[chn]; fw; fw = fw->fw_next)
+ /* we could compare just the pointers ... */
+ if ((fw->fw_vianame)[0] && !strncmp(devname,
+ fw->fw_vianame, IFNAMSIZ))
+ fw->fw_viadev = (struct net_device*)-1;
+ }
+
+ restore_flags(flags);
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block ipfw_dev_notifier={
+ ipfw_device_event,
+ NULL,
+ 0
+};
+
+#endif
+
+int ipfw_init_or_cleanup(int init)
+{
+ int ret = 0;
+
+ if (!init)
+ goto cleanup;
+
+ ret = register_firewall(PF_INET, &ipfw_ops);
+ if (ret < 0)
+ goto cleanup_nothing;
+
+#ifdef CONFIG_IP_ACCT
+ proc_net_create("ip_acct", S_IFREG | S_IRUGO | S_IWUSR, ip_acct_procinfo);
+#endif
+ proc_net_create("ip_input", S_IFREG | S_IRUGO | S_IWUSR, ip_fw_in_procinfo);
+ proc_net_create("ip_output", S_IFREG | S_IRUGO | S_IWUSR, ip_fw_out_procinfo);
+ proc_net_create("ip_forward", S_IFREG | S_IRUGO | S_IWUSR, ip_fw_fwd_procinfo);
+
+ /* Register for device up/down reports */
+ register_netdevice_notifier(&ipfw_dev_notifier);
+
+#ifdef CONFIG_IP_FIREWALL_NETLINK
+ ipfwsk = netlink_kernel_create(NETLINK_FIREWALL, NULL);
+#endif
+ return ret;
+
+ cleanup:
+#ifdef CONFIG_IP_FIREWALL_NETLINK
+ sock_release(ipfwsk->socket);
+#endif
+ unregister_netdevice_notifier(&ipfw_dev_notifier);
+
+#ifdef CONFIG_IP_ACCT
+ proc_net_remove("ip_acct");
+#endif
+ proc_net_remove("ip_input");
+ proc_net_remove("ip_output");
+ proc_net_remove("ip_forward");
+
+ free_fw_chain(chains[IP_FW_FWD]);
+ free_fw_chain(chains[IP_FW_IN]);
+ free_fw_chain(chains[IP_FW_OUT]);
+ free_fw_chain(chains[IP_FW_ACCT]);
+
+ unregister_firewall(PF_INET, &ipfw_ops);
+
+ cleanup_nothing:
+ return ret;
+}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)