ipfwadm_core.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:33k
- /* Minor modifications to fit on compatibility framework:
- Rusty.Russell@rustcorp.com.au
- */
- #include <linux/config.h>
- #define CONFIG_IP_FIREWALL
- #define CONFIG_IP_FIREWALL_VERBOSE
- #define CONFIG_IP_MASQUERADE
- #define CONFIG_IP_ACCT
- #define CONFIG_IP_TRANSPARENT_PROXY
- #if defined(CONFIG_NETLINK_DEV) || defined(CONFIG_NETLINK_DEV_MODULE)
- #define CONFIG_IP_FIREWALL_NETLINK
- #endif
- /*
- * 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.9.2.2 2002/01/24 15:50:42 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 <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/module.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("%u.%u.%u.%u", NIPQUAD(a));
- #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_t(unsigned int,
- 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(ftmp);
- MOD_DEC_USE_COUNT;
- }
- 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 non");
- #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);
- MOD_INC_USE_COUNT;
- 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 non");
- #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);
- MOD_INC_USE_COUNT;
- 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 emptyn");
- #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(ftmp);
- ftmp=ltmp->fw_next;
- }
- else
- {
- *chainptr=ftmp->fw_next;
- kfree(ftmp);
- ftmp=*chainptr;
- }
- }
- else
- {
- ltmp = ftmp;
- ftmp = ftmp->fw_next;
- }
- }
- restore_flags(flags);
- if (was_found) {
- MOD_DEC_USE_COUNT;
- 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 %dn",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_REDIRn");
- #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_MASQn");
- #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=%dn",
- 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=%dn",
- 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 %dn",stage);
- #endif
- return(EINVAL);
- }
- }
- #ifdef DEBUG_IP_FIREWALL
- printk("ip_acct_ctl: unknown request %dn",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 %dn",
- 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 %dn",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 %dn",stage);
- #endif
- return(EINVAL);
- }
- }
- #ifdef DEBUG_IP_FIREWALL
- printk("ip_fw_ctl: unknown request %dn",stage);
- #endif
- return(ENOPROTOOPT);
- }
- #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;
- 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 %dn",
- ip_fw_in_policy);
- break;
- case IP_FW_OUT:
- i = ip_fw_out_chain;
- len=sprintf(buffer, "IP firewall output rules, default %dn",
- ip_fw_out_policy);
- break;
- case IP_FW_FWD:
- i = ip_fw_fwd_chain;
- len=sprintf(buffer, "IP firewall forward rules, default %dn",
- ip_fw_fwd_policy);
- break;
- #endif
- #ifdef CONFIG_IP_ACCT
- case IP_FW_ACCT:
- i = ip_acct_chain;
- len=sprintf(buffer,"IP accounting rulesn");
- 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]='';
- 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
- #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;
- }