📄 ipchains_core.c
字号:
/* Minor modifications to fit on compatibility framework: Rusty.Russell@rustcorp.com.au*//* * This code is heavily based on the code on the old ip_fw.c code; see below for * copyrights and attributions of the old code. This code is basically GPL. * * 15-Aug-1997: Major changes to allow graphs for firewall rules. * Paul Russell <Paul.Russell@rustcorp.com.au> and * Michael Neuling <Michael.Neuling@rustcorp.com.au> * 24-Aug-1997: Generalised protocol handling (not just TCP/UDP/ICMP). * Added explicit RETURN from chains. * Removed TOS mangling (done in ipchains 1.0.1). * Fixed read & reset bug by reworking proc handling. * Paul Russell <Paul.Russell@rustcorp.com.au> * 28-Sep-1997: Added packet marking for net sched code. * Removed fw_via comparisons: all done on device name now, * similar to changes in ip_fw.c in DaveM's CVS970924 tree. * Paul Russell <Paul.Russell@rustcorp.com.au> * 2-Nov-1997: Moved types across to __u16, etc. * Added inverse flags. * Fixed fragment bug (in args to port_match). * Changed mark to only one flag (MARKABS). * 21-Nov-1997: Added ability to test ICMP code. * 19-Jan-1998: Added wildcard interfaces. * 6-Feb-1998: Merged 2.0 and 2.1 versions. * Initialised ip_masq for 2.0.x version. * Added explicit NETLINK option for 2.1.x version. * Added packet and byte counters for policy matches. * 26-Feb-1998: Fixed race conditions, added SMP support. * 18-Mar-1998: Fix SMP, fix race condition fix. * 1-May-1998: Remove caching of device pointer. * 12-May-1998: Allow tiny fragment case for TCP/UDP. * 15-May-1998: Treat short packets as fragments, don't just block. * 3-Jan-1999: Fixed serious procfs security hole -- users should never * be allowed to view the chains! * Marc Santoro <ultima@snicker.emoti.com> * 29-Jan-1999: Locally generated bogus IPs dealt with, rather than crash * during dump_packet. --RR. * 19-May-1999: Star Wars: The Phantom Menace opened. Rule num * printed in log (modified from Michael Hasenstein's patch). * Added SYN in log message. --RR * 23-Jul-1999: Fixed small fragment security exposure opened on 15-May-1998. * John McDonald <jm@dataprotect.com> * Thomas Lopatic <tl@dataprotect.com> *//* * * The origina Linux port was done Alan Cox, with changes/fixes from * Pauline Middlelink, Jos Vos, Thomas Quinot, Wouter Gadeyne, Juan * Jose Ciarlante, Bernd Eckenfels, Keith Owens and others. * * Copyright from the original FreeBSD version follows: * * 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/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/netfilter.h>#include <linux/netfilter_ipv4/compat_firewall.h>#include <linux/netfilter_ipv4/ipchains_core.h>#include <net/checksum.h>#include <linux/proc_fs.h>#include <linux/stat.h>/* Understanding locking in this code: (thanks to Alan Cox for using * little words to explain this to me). -- PR * * In UP, there can be two packets traversing the chains: * 1) A packet from the current userspace context * 2) A packet off the bh handlers (timer or net). * * For SMP (kernel v2.1+), multiply this by # CPUs. * * [Note that this in not correct for 2.2 - because the socket code always * uses lock_kernel() to serialize, and bottom halves (timers and net_bhs) * only run on one CPU at a time. This will probably change for 2.3. * It is still good to use spinlocks because that avoids the global cli() * for updating the tables, which is rather costly in SMP kernels -AK] * * This means counters and backchains can get corrupted if no precautions * are taken. * * To actually alter a chain on UP, we need only do a cli(), as this will * stop a bh handler firing, as we are in the current userspace context * (coming from a setsockopt()). * * On SMP, we need a write_lock_irqsave(), which is a simple cli() in * UP. * * For backchains and counters, we use an array, indexed by * [cpu_number_map[smp_processor_id()]*2 + !in_interrupt()]; the array is of * size [smp_num_cpus*2]. For v2.0, smp_num_cpus is effectively 1. So, * confident of uniqueness, we modify counters even though we only * have a read lock (to read the counters, you need a write lock, * though). *//* Why I didn't use straight locking... -- PR * * The backchains can be separated out of the ip_chains structure, and * allocated as needed inside ip_fw_check(). * * The counters, however, can't. Trying to lock these means blocking * interrupts every time we want to access them. This would suck HARD * performance-wise. Not locking them leads to possible corruption, * made worse on 32-bit machines (counters are 64-bit). *//*#define DEBUG_IP_FIREWALL*//*#define DEBUG_ALLOW_ALL*/ /* Useful for remote debugging *//*#define DEBUG_IP_FIREWALL_USER*//*#define DEBUG_IP_FIREWALL_LOCKING*/#if defined(CONFIG_NETLINK_DEV) || defined(CONFIG_NETLINK_DEV_MODULE)static struct sock *ipfwsk;#endif#ifdef CONFIG_SMP#define SLOT_NUMBER() (cpu_number_map(smp_processor_id())*2 + !in_interrupt())#else /* !SMP */#define SLOT_NUMBER() (!in_interrupt())#endif /* CONFIG_SMP */#define NUM_SLOTS (smp_num_cpus*2)#define SIZEOF_STRUCT_IP_CHAIN (sizeof(struct ip_chain) \ + NUM_SLOTS*sizeof(struct ip_reent))#define SIZEOF_STRUCT_IP_FW_KERNEL (sizeof(struct ip_fwkernel) \ + NUM_SLOTS*sizeof(struct ip_counters))#ifdef DEBUG_IP_FIREWALL_LOCKINGstatic unsigned int fwc_rlocks, fwc_wlocks;#define FWC_DEBUG_LOCK(d) \do { \ FWC_DONT_HAVE_LOCK(d); \ d |= (1 << SLOT_NUMBER()); \} while (0)#define FWC_DEBUG_UNLOCK(d) \do { \ FWC_HAVE_LOCK(d); \ d &= ~(1 << SLOT_NUMBER()); \} while (0)#define FWC_DONT_HAVE_LOCK(d) \do { \ if ((d) & (1 << SLOT_NUMBER())) \ printk("%s:%i: Got lock on %i already!\n", \ __FILE__, __LINE__, SLOT_NUMBER()); \} while(0)#define FWC_HAVE_LOCK(d) \do { \ if (!((d) & (1 << SLOT_NUMBER()))) \ printk("%s:%i:No lock on %i!\n", \ __FILE__, __LINE__, SLOT_NUMBER()); \} while (0)#else#define FWC_DEBUG_LOCK(d) do { } while(0)#define FWC_DEBUG_UNLOCK(d) do { } while(0)#define FWC_DONT_HAVE_LOCK(d) do { } while(0)#define FWC_HAVE_LOCK(d) do { } while(0)#endif /*DEBUG_IP_FIRWALL_LOCKING*/#define FWC_READ_LOCK(l) do { FWC_DEBUG_LOCK(fwc_rlocks); read_lock(l); } while (0)#define FWC_WRITE_LOCK(l) do { FWC_DEBUG_LOCK(fwc_wlocks); write_lock(l); } while (0)#define FWC_READ_LOCK_IRQ(l,f) do { FWC_DEBUG_LOCK(fwc_rlocks); read_lock_irqsave(l,f); } while (0)#define FWC_WRITE_LOCK_IRQ(l,f) do { FWC_DEBUG_LOCK(fwc_wlocks); write_lock_irqsave(l,f); } while (0)#define FWC_READ_UNLOCK(l) do { FWC_DEBUG_UNLOCK(fwc_rlocks); read_unlock(l); } while (0)#define FWC_WRITE_UNLOCK(l) do { FWC_DEBUG_UNLOCK(fwc_wlocks); write_unlock(l); } while (0)#define FWC_READ_UNLOCK_IRQ(l,f) do { FWC_DEBUG_UNLOCK(fwc_rlocks); read_unlock_irqrestore(l,f); } while (0)#define FWC_WRITE_UNLOCK_IRQ(l,f) do { FWC_DEBUG_UNLOCK(fwc_wlocks); write_unlock_irqrestore(l,f); } while (0)struct ip_chain;struct ip_counters{ __u64 pcnt, bcnt; /* Packet and byte counters */};struct ip_fwkernel{ struct ip_fw ipfw; struct ip_fwkernel *next; /* where to go next if current * rule doesn't match */ struct ip_chain *branch; /* which branch to jump to if * current rule matches */ int simplebranch; /* Use this if branch == NULL */ struct ip_counters counters[0]; /* Actually several of these */};struct ip_reent{ struct ip_chain *prevchain; /* Pointer to referencing chain */ struct ip_fwkernel *prevrule; /* Pointer to referencing rule */ struct ip_counters counters;};struct ip_chain{ ip_chainlabel label; /* Defines the label for each block */ struct ip_chain *next; /* Pointer to next block */ struct ip_fwkernel *chain; /* Pointer to first rule in block */ __u32 refcount; /* Number of refernces to block */ int policy; /* Default rule for chain. Only * * used in built in chains */ struct ip_reent reent[0]; /* Actually several of these */};/* * Implement IP packet firewall */#ifdef DEBUG_IP_FIREWALL#define dprintf(format, args...) printk(format , ## args)#else#define dprintf(format, args...)#endif#ifdef DEBUG_IP_FIREWALL_USER#define duprintf(format, args...) printk(format , ## args)#else#define duprintf(format, args...)#endif/* Lock around ip_fw_chains linked list structure */rwlock_t ip_fw_lock = RW_LOCK_UNLOCKED;/* Head of linked list of fw rules */static struct ip_chain *ip_fw_chains;#define IP_FW_INPUT_CHAIN ip_fw_chains#define IP_FW_FORWARD_CHAIN (ip_fw_chains->next)#define IP_FW_OUTPUT_CHAIN (ip_fw_chains->next->next)/* Returns 1 if the port is matched by the range, 0 otherwise */extern inline int port_match(__u16 min, __u16 max, __u16 port, int frag, int invert){ if (frag) /* Fragments fail ANY port test. */ return (min == 0 && max == 0xFFFF); else return (port >= min && port <= max) ^ invert;}/* Returns whether matches rule or not. */static int ip_rule_match(struct ip_fwkernel *f, const char *ifname, struct iphdr *ip, char tcpsyn, __u16 src_port, __u16 dst_port, char isfrag){#define FWINV(bool,invflg) ((bool) ^ !!(f->ipfw.fw_invflg & invflg)) /* * This is a bit simpler as we don't have to walk * an interface chain as you do in BSD - same logic * however. */ if (FWINV((ip->saddr&f->ipfw.fw_smsk.s_addr) != f->ipfw.fw_src.s_addr, IP_FW_INV_SRCIP) || FWINV((ip->daddr&f->ipfw.fw_dmsk.s_addr)!=f->ipfw.fw_dst.s_addr, IP_FW_INV_DSTIP)) { dprintf("Source or dest mismatch.\n"); dprintf("SRC: %u. Mask: %u. Target: %u.%s\n", ip->saddr, f->ipfw.fw_smsk.s_addr, f->ipfw.fw_src.s_addr, f->ipfw.fw_invflg & IP_FW_INV_SRCIP ? " (INV)" : ""); dprintf("DST: %u. Mask: %u. Target: %u.%s\n", ip->daddr, f->ipfw.fw_dmsk.s_addr, f->ipfw.fw_dst.s_addr, f->ipfw.fw_invflg & IP_FW_INV_DSTIP ? " (INV)" : ""); return 0; } /* * Look for a VIA device match */ if (f->ipfw.fw_flg & IP_FW_F_WILDIF) { if (FWINV(strncmp(ifname, f->ipfw.fw_vianame, strlen(f->ipfw.fw_vianame)) != 0, IP_FW_INV_VIA)) { dprintf("Wildcard interface mismatch.%s\n", f->ipfw.fw_invflg & IP_FW_INV_VIA ? " (INV)" : ""); return 0; /* Mismatch */ } } else if (FWINV(strcmp(ifname, f->ipfw.fw_vianame) != 0, IP_FW_INV_VIA)) { dprintf("Interface name does not match.%s\n", f->ipfw.fw_invflg & IP_FW_INV_VIA ? " (INV)" : ""); return 0; /* Mismatch */ } /* * Ok the chain addresses match. */ /* If we have a fragment rule but the packet is not a fragment * the we return zero */ if (FWINV((f->ipfw.fw_flg&IP_FW_F_FRAG) && !isfrag, IP_FW_INV_FRAG)) { dprintf("Fragment rule but not fragment.%s\n", f->ipfw.fw_invflg & IP_FW_INV_FRAG ? " (INV)" : ""); return 0; } /* Fragment NEVER passes a SYN test, even an inverted one. */ if (FWINV((f->ipfw.fw_flg&IP_FW_F_TCPSYN) && !tcpsyn, IP_FW_INV_SYN) || (isfrag && (f->ipfw.fw_flg&IP_FW_F_TCPSYN))) { dprintf("Rule requires SYN and packet has no SYN.%s\n", f->ipfw.fw_invflg & IP_FW_INV_SYN ? " (INV)" : ""); return 0; } if (f->ipfw.fw_proto) { /* * Specific firewall - packet's protocol * must match firewall's. */ if (FWINV(ip->protocol!=f->ipfw.fw_proto, IP_FW_INV_PROTO)) { dprintf("Packet protocol %hi does not match %hi.%s\n", ip->protocol, f->ipfw.fw_proto, f->ipfw.fw_invflg&IP_FW_INV_PROTO ? " (INV)":""); return 0; } /* For non TCP/UDP/ICMP, port range is max anyway. */ if (!port_match(f->ipfw.fw_spts[0], f->ipfw.fw_spts[1], src_port, isfrag, !!(f->ipfw.fw_invflg&IP_FW_INV_SRCPT)) || !port_match(f->ipfw.fw_dpts[0], f->ipfw.fw_dpts[1], dst_port, isfrag, !!(f->ipfw.fw_invflg &IP_FW_INV_DSTPT))) { dprintf("Port match failed.\n"); return 0; } } dprintf("Match succeeded.\n"); return 1;}static const char *branchname(struct ip_chain *branch,int simplebranch){ if (branch) return branch->label; switch (simplebranch) { case FW_BLOCK: return IP_FW_LABEL_BLOCK; case FW_ACCEPT: return IP_FW_LABEL_ACCEPT; case FW_REJECT: return IP_FW_LABEL_REJECT; case FW_REDIRECT: return IP_FW_LABEL_REDIRECT; case FW_MASQUERADE: return IP_FW_LABEL_MASQUERADE; case FW_SKIP: return "-"; case FW_SKIP+1: return IP_FW_LABEL_RETURN; default: return "UNKNOWN"; }}/* * VERY ugly piece of code which actually * makes kernel printf for matching packets... */static void dump_packet(const struct iphdr *ip, const char *ifname, struct ip_fwkernel *f, const ip_chainlabel chainlabel, __u16 src_port, __u16 dst_port, unsigned int count, int syn){ __u32 *opt = (__u32 *) (ip + 1); int opti; if (f) { printk(KERN_INFO "Packet log: %s ",chainlabel); printk("%s ",branchname(f->branch,f->simplebranch)); if (f->simplebranch==FW_REDIRECT) printk("%d ",f->ipfw.fw_redirpt); } printk("%s PROTO=%d %u.%u.%u.%u:%hu %u.%u.%u.%u:%hu" " L=%hu S=0x%2.2hX I=%hu F=0x%4.4hX T=%hu", ifname, ip->protocol, NIPQUAD(ip->saddr), src_port, NIPQUAD(ip->daddr), dst_port, ntohs(ip->tot_len), ip->tos, ntohs(ip->id), ntohs(ip->frag_off), ip->ttl); for (opti = 0; opti < (ip->ihl - sizeof(struct iphdr) / 4); opti++) printk(" O=0x%8.8X", *opt++); printk(" %s(#%d)\n", syn ? "SYN " : /* "PENANCE" */ "", count);}/* function for checking chain labels for user space. */static int check_label(ip_chainlabel label){ unsigned int i; /* strlen must be < IP_FW_MAX_LABEL_LENGTH. */ for (i = 0; i < IP_FW_MAX_LABEL_LENGTH + 1; i++) if (label[i] == '\0') return 1; return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -