OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [net/] [ipv6/] [netfilter/] [ip6_tables.c] - Diff between revs 1275 and 1765

Only display areas with differences | Details | Blame | View Log

Rev 1275 Rev 1765
/*
/*
 * Packet matching code.
 * Packet matching code.
 *
 *
 * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
 * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
 * Copyright (C) 2000-2002 Netfilter core team <coreteam@netfilter.org>
 * Copyright (C) 2000-2002 Netfilter core team <coreteam@netfilter.org>
 *
 *
 * 19 Jan 2002 Harald Welte <laforge@gnumonks.org>
 * 19 Jan 2002 Harald Welte <laforge@gnumonks.org>
 *      - increase module usage count as soon as we have rules inside
 *      - increase module usage count as soon as we have rules inside
 *        a table
 *        a table
 * 06 Jun 2002 Andras Kis-Szabo <kisza@sch.bme.hu>
 * 06 Jun 2002 Andras Kis-Szabo <kisza@sch.bme.hu>
 *      - new extension header parser code
 *      - new extension header parser code
 */
 */
#include <linux/config.h>
#include <linux/config.h>
#include <linux/skbuff.h>
#include <linux/skbuff.h>
#include <linux/kmod.h>
#include <linux/kmod.h>
#include <linux/vmalloc.h>
#include <linux/vmalloc.h>
#include <linux/netdevice.h>
#include <linux/netdevice.h>
#include <linux/module.h>
#include <linux/module.h>
#include <linux/tcp.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/udp.h>
#include <linux/icmpv6.h>
#include <linux/icmpv6.h>
#include <net/ip.h>
#include <net/ip.h>
#include <asm/uaccess.h>
#include <asm/uaccess.h>
#include <asm/semaphore.h>
#include <asm/semaphore.h>
#include <linux/proc_fs.h>
#include <linux/proc_fs.h>
 
 
#include <linux/netfilter_ipv6/ip6_tables.h>
#include <linux/netfilter_ipv6/ip6_tables.h>
 
 
#define IPV6_HDR_LEN    (sizeof(struct ipv6hdr))
#define IPV6_HDR_LEN    (sizeof(struct ipv6hdr))
#define IPV6_OPTHDR_LEN (sizeof(struct ipv6_opt_hdr))
#define IPV6_OPTHDR_LEN (sizeof(struct ipv6_opt_hdr))
 
 
/*#define DEBUG_IP_FIREWALL*/
/*#define DEBUG_IP_FIREWALL*/
/*#define DEBUG_ALLOW_ALL*/ /* Useful for remote debugging */
/*#define DEBUG_ALLOW_ALL*/ /* Useful for remote debugging */
/*#define DEBUG_IP_FIREWALL_USER*/
/*#define DEBUG_IP_FIREWALL_USER*/
 
 
#ifdef DEBUG_IP_FIREWALL
#ifdef DEBUG_IP_FIREWALL
#define dprintf(format, args...)  printk(format , ## args)
#define dprintf(format, args...)  printk(format , ## args)
#else
#else
#define dprintf(format, args...)
#define dprintf(format, args...)
#endif
#endif
 
 
#ifdef DEBUG_IP_FIREWALL_USER
#ifdef DEBUG_IP_FIREWALL_USER
#define duprintf(format, args...) printk(format , ## args)
#define duprintf(format, args...) printk(format , ## args)
#else
#else
#define duprintf(format, args...)
#define duprintf(format, args...)
#endif
#endif
 
 
#ifdef CONFIG_NETFILTER_DEBUG
#ifdef CONFIG_NETFILTER_DEBUG
#define IP_NF_ASSERT(x)                                         \
#define IP_NF_ASSERT(x)                                         \
do {                                                            \
do {                                                            \
        if (!(x))                                               \
        if (!(x))                                               \
                printk("IP_NF_ASSERT: %s:%s:%u\n",              \
                printk("IP_NF_ASSERT: %s:%s:%u\n",              \
                       __FUNCTION__, __FILE__, __LINE__);       \
                       __FUNCTION__, __FILE__, __LINE__);       \
} while(0)
} while(0)
#else
#else
#define IP_NF_ASSERT(x)
#define IP_NF_ASSERT(x)
#endif
#endif
#define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1))
#define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1))
 
 
/* Mutex protects lists (only traversed in user context). */
/* Mutex protects lists (only traversed in user context). */
static DECLARE_MUTEX(ip6t_mutex);
static DECLARE_MUTEX(ip6t_mutex);
 
 
/* Must have mutex */
/* Must have mutex */
#define ASSERT_READ_LOCK(x) IP_NF_ASSERT(down_trylock(&ip6t_mutex) != 0)
#define ASSERT_READ_LOCK(x) IP_NF_ASSERT(down_trylock(&ip6t_mutex) != 0)
#define ASSERT_WRITE_LOCK(x) IP_NF_ASSERT(down_trylock(&ip6t_mutex) != 0)
#define ASSERT_WRITE_LOCK(x) IP_NF_ASSERT(down_trylock(&ip6t_mutex) != 0)
#include <linux/netfilter_ipv4/lockhelp.h>
#include <linux/netfilter_ipv4/lockhelp.h>
#include <linux/netfilter_ipv4/listhelp.h>
#include <linux/netfilter_ipv4/listhelp.h>
 
 
#if 0
#if 0
/* All the better to debug you with... */
/* All the better to debug you with... */
#define static
#define static
#define inline
#define inline
#endif
#endif
 
 
/* Locking is simple: we assume at worst case there will be one packet
/* Locking is simple: we assume at worst case there will be one packet
   in user context and one from bottom halves (or soft irq if Alexey's
   in user context and one from bottom halves (or soft irq if Alexey's
   softnet patch was applied).
   softnet patch was applied).
 
 
   We keep a set of rules for each CPU, so we can avoid write-locking
   We keep a set of rules for each CPU, so we can avoid write-locking
   them; doing a readlock_bh() stops packets coming through if we're
   them; doing a readlock_bh() stops packets coming through if we're
   in user context.
   in user context.
 
 
   To be cache friendly on SMP, we arrange them like so:
   To be cache friendly on SMP, we arrange them like so:
   [ n-entries ]
   [ n-entries ]
   ... cache-align padding ...
   ... cache-align padding ...
   [ n-entries ]
   [ n-entries ]
 
 
   Hence the start of any table is given by get_table() below.  */
   Hence the start of any table is given by get_table() below.  */
 
 
/* The table itself */
/* The table itself */
struct ip6t_table_info
struct ip6t_table_info
{
{
        /* Size per table */
        /* Size per table */
        unsigned int size;
        unsigned int size;
        /* Number of entries: FIXME. --RR */
        /* Number of entries: FIXME. --RR */
        unsigned int number;
        unsigned int number;
        /* Initial number of entries. Needed for module usage count */
        /* Initial number of entries. Needed for module usage count */
        unsigned int initial_entries;
        unsigned int initial_entries;
 
 
        /* Entry points and underflows */
        /* Entry points and underflows */
        unsigned int hook_entry[NF_IP6_NUMHOOKS];
        unsigned int hook_entry[NF_IP6_NUMHOOKS];
        unsigned int underflow[NF_IP6_NUMHOOKS];
        unsigned int underflow[NF_IP6_NUMHOOKS];
 
 
        /* ip6t_entry tables: one per CPU */
        /* ip6t_entry tables: one per CPU */
        char entries[0] ____cacheline_aligned;
        char entries[0] ____cacheline_aligned;
};
};
 
 
static LIST_HEAD(ip6t_target);
static LIST_HEAD(ip6t_target);
static LIST_HEAD(ip6t_match);
static LIST_HEAD(ip6t_match);
static LIST_HEAD(ip6t_tables);
static LIST_HEAD(ip6t_tables);
#define ADD_COUNTER(c,b,p) do { (c).bcnt += (b); (c).pcnt += (p); } while(0)
#define ADD_COUNTER(c,b,p) do { (c).bcnt += (b); (c).pcnt += (p); } while(0)
 
 
#ifdef CONFIG_SMP
#ifdef CONFIG_SMP
#define TABLE_OFFSET(t,p) (SMP_ALIGN((t)->size)*(p))
#define TABLE_OFFSET(t,p) (SMP_ALIGN((t)->size)*(p))
#else
#else
#define TABLE_OFFSET(t,p) 0
#define TABLE_OFFSET(t,p) 0
#endif
#endif
 
 
#if 0
#if 0
#define down(x) do { printk("DOWN:%u:" #x "\n", __LINE__); down(x); } while(0)
#define down(x) do { printk("DOWN:%u:" #x "\n", __LINE__); down(x); } while(0)
#define down_interruptible(x) ({ int __r; printk("DOWNi:%u:" #x "\n", __LINE__); __r = down_interruptible(x); if (__r != 0) printk("ABORT-DOWNi:%u\n", __LINE__); __r; })
#define down_interruptible(x) ({ int __r; printk("DOWNi:%u:" #x "\n", __LINE__); __r = down_interruptible(x); if (__r != 0) printk("ABORT-DOWNi:%u\n", __LINE__); __r; })
#define up(x) do { printk("UP:%u:" #x "\n", __LINE__); up(x); } while(0)
#define up(x) do { printk("UP:%u:" #x "\n", __LINE__); up(x); } while(0)
#endif
#endif
 
 
static int ip6_masked_addrcmp(struct in6_addr addr1, struct in6_addr mask,
static int ip6_masked_addrcmp(struct in6_addr addr1, struct in6_addr mask,
                              struct in6_addr addr2)
                              struct in6_addr addr2)
{
{
        int i;
        int i;
        for( i = 0; i < 16; i++){
        for( i = 0; i < 16; i++){
                if((addr1.s6_addr[i] & mask.s6_addr[i]) !=
                if((addr1.s6_addr[i] & mask.s6_addr[i]) !=
                   (addr2.s6_addr[i] & mask.s6_addr[i]))
                   (addr2.s6_addr[i] & mask.s6_addr[i]))
                        return 1;
                        return 1;
        }
        }
        return 0;
        return 0;
}
}
 
 
/* Check for an extension */
/* Check for an extension */
int
int
ip6t_ext_hdr(u8 nexthdr)
ip6t_ext_hdr(u8 nexthdr)
{
{
        return ( (nexthdr == IPPROTO_HOPOPTS)   ||
        return ( (nexthdr == IPPROTO_HOPOPTS)   ||
                 (nexthdr == IPPROTO_ROUTING)   ||
                 (nexthdr == IPPROTO_ROUTING)   ||
                 (nexthdr == IPPROTO_FRAGMENT)  ||
                 (nexthdr == IPPROTO_FRAGMENT)  ||
                 (nexthdr == IPPROTO_ESP)       ||
                 (nexthdr == IPPROTO_ESP)       ||
                 (nexthdr == IPPROTO_AH)        ||
                 (nexthdr == IPPROTO_AH)        ||
                 (nexthdr == IPPROTO_NONE)      ||
                 (nexthdr == IPPROTO_NONE)      ||
                 (nexthdr == IPPROTO_DSTOPTS) );
                 (nexthdr == IPPROTO_DSTOPTS) );
}
}
 
 
/* Returns whether matches rule or not. */
/* Returns whether matches rule or not. */
static inline int
static inline int
ip6_packet_match(const struct sk_buff *skb,
ip6_packet_match(const struct sk_buff *skb,
                 const struct ipv6hdr *ipv6,
                 const struct ipv6hdr *ipv6,
                 const char *indev,
                 const char *indev,
                 const char *outdev,
                 const char *outdev,
                 const struct ip6t_ip6 *ip6info,
                 const struct ip6t_ip6 *ip6info,
                 int isfrag)
                 int isfrag)
{
{
        size_t i;
        size_t i;
        unsigned long ret;
        unsigned long ret;
 
 
#define FWINV(bool,invflg) ((bool) ^ !!(ip6info->invflags & invflg))
#define FWINV(bool,invflg) ((bool) ^ !!(ip6info->invflags & invflg))
 
 
        if (FWINV(ip6_masked_addrcmp(ipv6->saddr,ip6info->smsk,ip6info->src),
        if (FWINV(ip6_masked_addrcmp(ipv6->saddr,ip6info->smsk,ip6info->src),
                  IP6T_INV_SRCIP)
                  IP6T_INV_SRCIP)
            || FWINV(ip6_masked_addrcmp(ipv6->daddr,ip6info->dmsk,ip6info->dst),
            || FWINV(ip6_masked_addrcmp(ipv6->daddr,ip6info->dmsk,ip6info->dst),
                     IP6T_INV_DSTIP)) {
                     IP6T_INV_DSTIP)) {
                dprintf("Source or dest mismatch.\n");
                dprintf("Source or dest mismatch.\n");
/*
/*
                dprintf("SRC: %u. Mask: %u. Target: %u.%s\n", ip->saddr,
                dprintf("SRC: %u. Mask: %u. Target: %u.%s\n", ip->saddr,
                        ipinfo->smsk.s_addr, ipinfo->src.s_addr,
                        ipinfo->smsk.s_addr, ipinfo->src.s_addr,
                        ipinfo->invflags & IP6T_INV_SRCIP ? " (INV)" : "");
                        ipinfo->invflags & IP6T_INV_SRCIP ? " (INV)" : "");
                dprintf("DST: %u. Mask: %u. Target: %u.%s\n", ip->daddr,
                dprintf("DST: %u. Mask: %u. Target: %u.%s\n", ip->daddr,
                        ipinfo->dmsk.s_addr, ipinfo->dst.s_addr,
                        ipinfo->dmsk.s_addr, ipinfo->dst.s_addr,
                        ipinfo->invflags & IP6T_INV_DSTIP ? " (INV)" : "");*/
                        ipinfo->invflags & IP6T_INV_DSTIP ? " (INV)" : "");*/
                return 0;
                return 0;
        }
        }
 
 
        /* Look for ifname matches; this should unroll nicely. */
        /* Look for ifname matches; this should unroll nicely. */
        for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
        for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
                ret |= (((const unsigned long *)indev)[i]
                ret |= (((const unsigned long *)indev)[i]
                        ^ ((const unsigned long *)ip6info->iniface)[i])
                        ^ ((const unsigned long *)ip6info->iniface)[i])
                        & ((const unsigned long *)ip6info->iniface_mask)[i];
                        & ((const unsigned long *)ip6info->iniface_mask)[i];
        }
        }
 
 
        if (FWINV(ret != 0, IP6T_INV_VIA_IN)) {
        if (FWINV(ret != 0, IP6T_INV_VIA_IN)) {
                dprintf("VIA in mismatch (%s vs %s).%s\n",
                dprintf("VIA in mismatch (%s vs %s).%s\n",
                        indev, ip6info->iniface,
                        indev, ip6info->iniface,
                        ip6info->invflags&IP6T_INV_VIA_IN ?" (INV)":"");
                        ip6info->invflags&IP6T_INV_VIA_IN ?" (INV)":"");
                return 0;
                return 0;
        }
        }
 
 
        for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
        for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
                ret |= (((const unsigned long *)outdev)[i]
                ret |= (((const unsigned long *)outdev)[i]
                        ^ ((const unsigned long *)ip6info->outiface)[i])
                        ^ ((const unsigned long *)ip6info->outiface)[i])
                        & ((const unsigned long *)ip6info->outiface_mask)[i];
                        & ((const unsigned long *)ip6info->outiface_mask)[i];
        }
        }
 
 
        if (FWINV(ret != 0, IP6T_INV_VIA_OUT)) {
        if (FWINV(ret != 0, IP6T_INV_VIA_OUT)) {
                dprintf("VIA out mismatch (%s vs %s).%s\n",
                dprintf("VIA out mismatch (%s vs %s).%s\n",
                        outdev, ip6info->outiface,
                        outdev, ip6info->outiface,
                        ip6info->invflags&IP6T_INV_VIA_OUT ?" (INV)":"");
                        ip6info->invflags&IP6T_INV_VIA_OUT ?" (INV)":"");
                return 0;
                return 0;
        }
        }
 
 
/* ... might want to do something with class and flowlabel here ... */
/* ... might want to do something with class and flowlabel here ... */
 
 
        /* look for the desired protocol header */
        /* look for the desired protocol header */
        if((ip6info->flags & IP6T_F_PROTO)) {
        if((ip6info->flags & IP6T_F_PROTO)) {
                u_int8_t currenthdr = ipv6->nexthdr;
                u_int8_t currenthdr = ipv6->nexthdr;
                struct ipv6_opt_hdr *hdrptr;
                struct ipv6_opt_hdr *hdrptr;
                u_int16_t ptr;          /* Header offset in skb */
                u_int16_t ptr;          /* Header offset in skb */
                u_int16_t hdrlen;       /* Header */
                u_int16_t hdrlen;       /* Header */
 
 
                ptr = IPV6_HDR_LEN;
                ptr = IPV6_HDR_LEN;
 
 
                while (ip6t_ext_hdr(currenthdr)) {
                while (ip6t_ext_hdr(currenthdr)) {
                        /* Is there enough space for the next ext header? */
                        /* Is there enough space for the next ext header? */
                        if (skb->len - ptr < IPV6_OPTHDR_LEN)
                        if (skb->len - ptr < IPV6_OPTHDR_LEN)
                                return 0;
                                return 0;
 
 
                        /* NONE or ESP: there isn't protocol part */
                        /* NONE or ESP: there isn't protocol part */
                        /* If we want to count these packets in '-p all',
                        /* If we want to count these packets in '-p all',
                         * we will change the return 0 to 1*/
                         * we will change the return 0 to 1*/
                        if ((currenthdr == IPPROTO_NONE) ||
                        if ((currenthdr == IPPROTO_NONE) ||
                                (currenthdr == IPPROTO_ESP))
                                (currenthdr == IPPROTO_ESP))
                                return 0;
                                return 0;
 
 
                        hdrptr = (struct ipv6_opt_hdr *)(skb->data + ptr);
                        hdrptr = (struct ipv6_opt_hdr *)(skb->data + ptr);
 
 
                        /* Size calculation */
                        /* Size calculation */
                        if (currenthdr == IPPROTO_FRAGMENT) {
                        if (currenthdr == IPPROTO_FRAGMENT) {
                                hdrlen = 8;
                                hdrlen = 8;
                        } else if (currenthdr == IPPROTO_AH)
                        } else if (currenthdr == IPPROTO_AH)
                                hdrlen = (hdrptr->hdrlen+2)<<2;
                                hdrlen = (hdrptr->hdrlen+2)<<2;
                        else
                        else
                                hdrlen = ipv6_optlen(hdrptr);
                                hdrlen = ipv6_optlen(hdrptr);
 
 
                        currenthdr = hdrptr->nexthdr;
                        currenthdr = hdrptr->nexthdr;
                        ptr += hdrlen;
                        ptr += hdrlen;
                        /* ptr is too large */
                        /* ptr is too large */
                        if ( ptr > skb->len )
                        if ( ptr > skb->len )
                                return 0;
                                return 0;
                }
                }
 
 
                /* currenthdr contains the protocol header */
                /* currenthdr contains the protocol header */
 
 
                dprintf("Packet protocol %hi ?= %s%hi.\n",
                dprintf("Packet protocol %hi ?= %s%hi.\n",
                                currenthdr,
                                currenthdr,
                                ip6info->invflags & IP6T_INV_PROTO ? "!":"",
                                ip6info->invflags & IP6T_INV_PROTO ? "!":"",
                                ip6info->proto);
                                ip6info->proto);
 
 
                if (ip6info->proto == currenthdr) {
                if (ip6info->proto == currenthdr) {
                        if(ip6info->invflags & IP6T_INV_PROTO) {
                        if(ip6info->invflags & IP6T_INV_PROTO) {
                                return 0;
                                return 0;
                        }
                        }
                        return 1;
                        return 1;
                }
                }
 
 
                /* We need match for the '-p all', too! */
                /* We need match for the '-p all', too! */
                if ((ip6info->proto != 0) &&
                if ((ip6info->proto != 0) &&
                        !(ip6info->invflags & IP6T_INV_PROTO))
                        !(ip6info->invflags & IP6T_INV_PROTO))
                        return 0;
                        return 0;
        }
        }
        return 1;
        return 1;
}
}
 
 
/* should be ip6 safe */
/* should be ip6 safe */
static inline int
static inline int
ip6_checkentry(const struct ip6t_ip6 *ipv6)
ip6_checkentry(const struct ip6t_ip6 *ipv6)
{
{
        if (ipv6->flags & ~IP6T_F_MASK) {
        if (ipv6->flags & ~IP6T_F_MASK) {
                duprintf("Unknown flag bits set: %08X\n",
                duprintf("Unknown flag bits set: %08X\n",
                         ipv6->flags & ~IP6T_F_MASK);
                         ipv6->flags & ~IP6T_F_MASK);
                return 0;
                return 0;
        }
        }
        if (ipv6->invflags & ~IP6T_INV_MASK) {
        if (ipv6->invflags & ~IP6T_INV_MASK) {
                duprintf("Unknown invflag bits set: %08X\n",
                duprintf("Unknown invflag bits set: %08X\n",
                         ipv6->invflags & ~IP6T_INV_MASK);
                         ipv6->invflags & ~IP6T_INV_MASK);
                return 0;
                return 0;
        }
        }
        return 1;
        return 1;
}
}
 
 
static unsigned int
static unsigned int
ip6t_error(struct sk_buff **pskb,
ip6t_error(struct sk_buff **pskb,
          unsigned int hooknum,
          unsigned int hooknum,
          const struct net_device *in,
          const struct net_device *in,
          const struct net_device *out,
          const struct net_device *out,
          const void *targinfo,
          const void *targinfo,
          void *userinfo)
          void *userinfo)
{
{
        if (net_ratelimit())
        if (net_ratelimit())
                printk("ip6_tables: error: `%s'\n", (char *)targinfo);
                printk("ip6_tables: error: `%s'\n", (char *)targinfo);
 
 
        return NF_DROP;
        return NF_DROP;
}
}
 
 
static inline
static inline
int do_match(struct ip6t_entry_match *m,
int do_match(struct ip6t_entry_match *m,
             const struct sk_buff *skb,
             const struct sk_buff *skb,
             const struct net_device *in,
             const struct net_device *in,
             const struct net_device *out,
             const struct net_device *out,
             int offset,
             int offset,
             const void *hdr,
             const void *hdr,
             u_int16_t datalen,
             u_int16_t datalen,
             int *hotdrop)
             int *hotdrop)
{
{
        /* Stop iteration if it doesn't match */
        /* Stop iteration if it doesn't match */
        if (!m->u.kernel.match->match(skb, in, out, m->data,
        if (!m->u.kernel.match->match(skb, in, out, m->data,
                                      offset, hdr, datalen, hotdrop))
                                      offset, hdr, datalen, hotdrop))
                return 1;
                return 1;
        else
        else
                return 0;
                return 0;
}
}
 
 
static inline struct ip6t_entry *
static inline struct ip6t_entry *
get_entry(void *base, unsigned int offset)
get_entry(void *base, unsigned int offset)
{
{
        return (struct ip6t_entry *)(base + offset);
        return (struct ip6t_entry *)(base + offset);
}
}
 
 
/* Returns one of the generic firewall policies, like NF_ACCEPT. */
/* Returns one of the generic firewall policies, like NF_ACCEPT. */
unsigned int
unsigned int
ip6t_do_table(struct sk_buff **pskb,
ip6t_do_table(struct sk_buff **pskb,
              unsigned int hook,
              unsigned int hook,
              const struct net_device *in,
              const struct net_device *in,
              const struct net_device *out,
              const struct net_device *out,
              struct ip6t_table *table,
              struct ip6t_table *table,
              void *userdata)
              void *userdata)
{
{
        static const char nulldevname[IFNAMSIZ] = { 0 };
        static const char nulldevname[IFNAMSIZ] = { 0 };
        u_int16_t offset = 0;
        u_int16_t offset = 0;
        struct ipv6hdr *ipv6;
        struct ipv6hdr *ipv6;
        void *protohdr;
        void *protohdr;
        u_int16_t datalen;
        u_int16_t datalen;
        int hotdrop = 0;
        int hotdrop = 0;
        /* Initializing verdict to NF_DROP keeps gcc happy. */
        /* Initializing verdict to NF_DROP keeps gcc happy. */
        unsigned int verdict = NF_DROP;
        unsigned int verdict = NF_DROP;
        const char *indev, *outdev;
        const char *indev, *outdev;
        void *table_base;
        void *table_base;
        struct ip6t_entry *e, *back;
        struct ip6t_entry *e, *back;
 
 
        /* Initialization */
        /* Initialization */
        ipv6 = (*pskb)->nh.ipv6h;
        ipv6 = (*pskb)->nh.ipv6h;
        protohdr = (u_int32_t *)((char *)ipv6 + IPV6_HDR_LEN);
        protohdr = (u_int32_t *)((char *)ipv6 + IPV6_HDR_LEN);
        datalen = (*pskb)->len - IPV6_HDR_LEN;
        datalen = (*pskb)->len - IPV6_HDR_LEN;
        indev = in ? in->name : nulldevname;
        indev = in ? in->name : nulldevname;
        outdev = out ? out->name : nulldevname;
        outdev = out ? out->name : nulldevname;
 
 
        /* We handle fragments by dealing with the first fragment as
        /* We handle fragments by dealing with the first fragment as
         * if it was a normal packet.  All other fragments are treated
         * if it was a normal packet.  All other fragments are treated
         * normally, except that they will NEVER match rules that ask
         * normally, except that they will NEVER match rules that ask
         * things we don't know, ie. tcp syn flag or ports).  If the
         * things we don't know, ie. tcp syn flag or ports).  If the
         * rule is also a fragment-specific rule, non-fragments won't
         * rule is also a fragment-specific rule, non-fragments won't
         * match it. */
         * match it. */
 
 
        read_lock_bh(&table->lock);
        read_lock_bh(&table->lock);
        IP_NF_ASSERT(table->valid_hooks & (1 << hook));
        IP_NF_ASSERT(table->valid_hooks & (1 << hook));
        table_base = (void *)table->private->entries
        table_base = (void *)table->private->entries
                + TABLE_OFFSET(table->private,
                + TABLE_OFFSET(table->private,
                                cpu_number_map(smp_processor_id()));
                                cpu_number_map(smp_processor_id()));
        e = get_entry(table_base, table->private->hook_entry[hook]);
        e = get_entry(table_base, table->private->hook_entry[hook]);
 
 
#ifdef CONFIG_NETFILTER_DEBUG
#ifdef CONFIG_NETFILTER_DEBUG
        /* Check noone else using our table */
        /* Check noone else using our table */
        if (((struct ip6t_entry *)table_base)->comefrom != 0xdead57ac
        if (((struct ip6t_entry *)table_base)->comefrom != 0xdead57ac
            && ((struct ip6t_entry *)table_base)->comefrom != 0xeeeeeeec) {
            && ((struct ip6t_entry *)table_base)->comefrom != 0xeeeeeeec) {
                printk("ASSERT: CPU #%u, %s comefrom(%p) = %X\n",
                printk("ASSERT: CPU #%u, %s comefrom(%p) = %X\n",
                       smp_processor_id(),
                       smp_processor_id(),
                       table->name,
                       table->name,
                       &((struct ip6t_entry *)table_base)->comefrom,
                       &((struct ip6t_entry *)table_base)->comefrom,
                       ((struct ip6t_entry *)table_base)->comefrom);
                       ((struct ip6t_entry *)table_base)->comefrom);
        }
        }
        ((struct ip6t_entry *)table_base)->comefrom = 0x57acc001;
        ((struct ip6t_entry *)table_base)->comefrom = 0x57acc001;
#endif
#endif
 
 
        /* For return from builtin chain */
        /* For return from builtin chain */
        back = get_entry(table_base, table->private->underflow[hook]);
        back = get_entry(table_base, table->private->underflow[hook]);
 
 
        do {
        do {
                IP_NF_ASSERT(e);
                IP_NF_ASSERT(e);
                IP_NF_ASSERT(back);
                IP_NF_ASSERT(back);
                (*pskb)->nfcache |= e->nfcache;
                (*pskb)->nfcache |= e->nfcache;
                if (ip6_packet_match(*pskb, ipv6, indev, outdev,
                if (ip6_packet_match(*pskb, ipv6, indev, outdev,
                        &e->ipv6, offset)) {
                        &e->ipv6, offset)) {
                        struct ip6t_entry_target *t;
                        struct ip6t_entry_target *t;
 
 
                        if (IP6T_MATCH_ITERATE(e, do_match,
                        if (IP6T_MATCH_ITERATE(e, do_match,
                                               *pskb, in, out,
                                               *pskb, in, out,
                                               offset, protohdr,
                                               offset, protohdr,
                                               datalen, &hotdrop) != 0)
                                               datalen, &hotdrop) != 0)
                                goto no_match;
                                goto no_match;
 
 
                        ADD_COUNTER(e->counters, ntohs(ipv6->payload_len) + IPV6_HDR_LEN, 1);
                        ADD_COUNTER(e->counters, ntohs(ipv6->payload_len) + IPV6_HDR_LEN, 1);
 
 
                        t = ip6t_get_target(e);
                        t = ip6t_get_target(e);
                        IP_NF_ASSERT(t->u.kernel.target);
                        IP_NF_ASSERT(t->u.kernel.target);
                        /* Standard target? */
                        /* Standard target? */
                        if (!t->u.kernel.target->target) {
                        if (!t->u.kernel.target->target) {
                                int v;
                                int v;
 
 
                                v = ((struct ip6t_standard_target *)t)->verdict;
                                v = ((struct ip6t_standard_target *)t)->verdict;
                                if (v < 0) {
                                if (v < 0) {
                                        /* Pop from stack? */
                                        /* Pop from stack? */
                                        if (v != IP6T_RETURN) {
                                        if (v != IP6T_RETURN) {
                                                verdict = (unsigned)(-v) - 1;
                                                verdict = (unsigned)(-v) - 1;
                                                break;
                                                break;
                                        }
                                        }
                                        e = back;
                                        e = back;
                                        back = get_entry(table_base,
                                        back = get_entry(table_base,
                                                         back->comefrom);
                                                         back->comefrom);
                                        continue;
                                        continue;
                                }
                                }
                                if (table_base + v
                                if (table_base + v
                                    != (void *)e + e->next_offset) {
                                    != (void *)e + e->next_offset) {
                                        /* Save old back ptr in next entry */
                                        /* Save old back ptr in next entry */
                                        struct ip6t_entry *next
                                        struct ip6t_entry *next
                                                = (void *)e + e->next_offset;
                                                = (void *)e + e->next_offset;
                                        next->comefrom
                                        next->comefrom
                                                = (void *)back - table_base;
                                                = (void *)back - table_base;
                                        /* set back pointer to next entry */
                                        /* set back pointer to next entry */
                                        back = next;
                                        back = next;
                                }
                                }
 
 
                                e = get_entry(table_base, v);
                                e = get_entry(table_base, v);
                        } else {
                        } else {
                                /* Targets which reenter must return
                                /* Targets which reenter must return
                                   abs. verdicts */
                                   abs. verdicts */
#ifdef CONFIG_NETFILTER_DEBUG
#ifdef CONFIG_NETFILTER_DEBUG
                                ((struct ip6t_entry *)table_base)->comefrom
                                ((struct ip6t_entry *)table_base)->comefrom
                                        = 0xeeeeeeec;
                                        = 0xeeeeeeec;
#endif
#endif
                                verdict = t->u.kernel.target->target(pskb,
                                verdict = t->u.kernel.target->target(pskb,
                                                                     hook,
                                                                     hook,
                                                                     in, out,
                                                                     in, out,
                                                                     t->data,
                                                                     t->data,
                                                                     userdata);
                                                                     userdata);
 
 
#ifdef CONFIG_NETFILTER_DEBUG
#ifdef CONFIG_NETFILTER_DEBUG
                                if (((struct ip6t_entry *)table_base)->comefrom
                                if (((struct ip6t_entry *)table_base)->comefrom
                                    != 0xeeeeeeec
                                    != 0xeeeeeeec
                                    && verdict == IP6T_CONTINUE) {
                                    && verdict == IP6T_CONTINUE) {
                                        printk("Target %s reentered!\n",
                                        printk("Target %s reentered!\n",
                                               t->u.kernel.target->name);
                                               t->u.kernel.target->name);
                                        verdict = NF_DROP;
                                        verdict = NF_DROP;
                                }
                                }
                                ((struct ip6t_entry *)table_base)->comefrom
                                ((struct ip6t_entry *)table_base)->comefrom
                                        = 0x57acc001;
                                        = 0x57acc001;
#endif
#endif
                                /* Target might have changed stuff. */
                                /* Target might have changed stuff. */
                                ipv6 = (*pskb)->nh.ipv6h;
                                ipv6 = (*pskb)->nh.ipv6h;
                                protohdr = (u_int32_t *)((void *)ipv6 + IPV6_HDR_LEN);
                                protohdr = (u_int32_t *)((void *)ipv6 + IPV6_HDR_LEN);
                                datalen = (*pskb)->len - IPV6_HDR_LEN;
                                datalen = (*pskb)->len - IPV6_HDR_LEN;
 
 
                                if (verdict == IP6T_CONTINUE)
                                if (verdict == IP6T_CONTINUE)
                                        e = (void *)e + e->next_offset;
                                        e = (void *)e + e->next_offset;
                                else
                                else
                                        /* Verdict */
                                        /* Verdict */
                                        break;
                                        break;
                        }
                        }
                } else {
                } else {
 
 
                no_match:
                no_match:
                        e = (void *)e + e->next_offset;
                        e = (void *)e + e->next_offset;
                }
                }
        } while (!hotdrop);
        } while (!hotdrop);
 
 
#ifdef CONFIG_NETFILTER_DEBUG
#ifdef CONFIG_NETFILTER_DEBUG
        ((struct ip6t_entry *)table_base)->comefrom = 0xdead57ac;
        ((struct ip6t_entry *)table_base)->comefrom = 0xdead57ac;
#endif
#endif
        read_unlock_bh(&table->lock);
        read_unlock_bh(&table->lock);
 
 
#ifdef DEBUG_ALLOW_ALL
#ifdef DEBUG_ALLOW_ALL
        return NF_ACCEPT;
        return NF_ACCEPT;
#else
#else
        if (hotdrop)
        if (hotdrop)
                return NF_DROP;
                return NF_DROP;
        else return verdict;
        else return verdict;
#endif
#endif
}
}
 
 
/* If it succeeds, returns element and locks mutex */
/* If it succeeds, returns element and locks mutex */
static inline void *
static inline void *
find_inlist_lock_noload(struct list_head *head,
find_inlist_lock_noload(struct list_head *head,
                        const char *name,
                        const char *name,
                        int *error,
                        int *error,
                        struct semaphore *mutex)
                        struct semaphore *mutex)
{
{
        void *ret;
        void *ret;
 
 
#if 1
#if 1
        duprintf("find_inlist: searching for `%s' in %s.\n",
        duprintf("find_inlist: searching for `%s' in %s.\n",
                 name, head == &ip6t_target ? "ip6t_target"
                 name, head == &ip6t_target ? "ip6t_target"
                 : head == &ip6t_match ? "ip6t_match"
                 : head == &ip6t_match ? "ip6t_match"
                 : head == &ip6t_tables ? "ip6t_tables" : "UNKNOWN");
                 : head == &ip6t_tables ? "ip6t_tables" : "UNKNOWN");
#endif
#endif
 
 
        *error = down_interruptible(mutex);
        *error = down_interruptible(mutex);
        if (*error != 0)
        if (*error != 0)
                return NULL;
                return NULL;
 
 
        ret = list_named_find(head, name);
        ret = list_named_find(head, name);
        if (!ret) {
        if (!ret) {
                *error = -ENOENT;
                *error = -ENOENT;
                up(mutex);
                up(mutex);
        }
        }
        return ret;
        return ret;
}
}
 
 
#ifndef CONFIG_KMOD
#ifndef CONFIG_KMOD
#define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m))
#define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m))
#else
#else
static void *
static void *
find_inlist_lock(struct list_head *head,
find_inlist_lock(struct list_head *head,
                 const char *name,
                 const char *name,
                 const char *prefix,
                 const char *prefix,
                 int *error,
                 int *error,
                 struct semaphore *mutex)
                 struct semaphore *mutex)
{
{
        void *ret;
        void *ret;
 
 
        ret = find_inlist_lock_noload(head, name, error, mutex);
        ret = find_inlist_lock_noload(head, name, error, mutex);
        if (!ret) {
        if (!ret) {
                char modulename[IP6T_FUNCTION_MAXNAMELEN + strlen(prefix) + 1];
                char modulename[IP6T_FUNCTION_MAXNAMELEN + strlen(prefix) + 1];
                strcpy(modulename, prefix);
                strcpy(modulename, prefix);
                strcat(modulename, name);
                strcat(modulename, name);
                duprintf("find_inlist: loading `%s'.\n", modulename);
                duprintf("find_inlist: loading `%s'.\n", modulename);
                request_module(modulename);
                request_module(modulename);
                ret = find_inlist_lock_noload(head, name, error, mutex);
                ret = find_inlist_lock_noload(head, name, error, mutex);
        }
        }
 
 
        return ret;
        return ret;
}
}
#endif
#endif
 
 
static inline struct ip6t_table *
static inline struct ip6t_table *
find_table_lock(const char *name, int *error, struct semaphore *mutex)
find_table_lock(const char *name, int *error, struct semaphore *mutex)
{
{
        return find_inlist_lock(&ip6t_tables, name, "ip6table_", error, mutex);
        return find_inlist_lock(&ip6t_tables, name, "ip6table_", error, mutex);
}
}
 
 
static inline struct ip6t_match *
static inline struct ip6t_match *
find_match_lock(const char *name, int *error, struct semaphore *mutex)
find_match_lock(const char *name, int *error, struct semaphore *mutex)
{
{
        return find_inlist_lock(&ip6t_match, name, "ip6t_", error, mutex);
        return find_inlist_lock(&ip6t_match, name, "ip6t_", error, mutex);
}
}
 
 
static inline struct ip6t_target *
static inline struct ip6t_target *
find_target_lock(const char *name, int *error, struct semaphore *mutex)
find_target_lock(const char *name, int *error, struct semaphore *mutex)
{
{
        return find_inlist_lock(&ip6t_target, name, "ip6t_", error, mutex);
        return find_inlist_lock(&ip6t_target, name, "ip6t_", error, mutex);
}
}
 
 
/* All zeroes == unconditional rule. */
/* All zeroes == unconditional rule. */
static inline int
static inline int
unconditional(const struct ip6t_ip6 *ipv6)
unconditional(const struct ip6t_ip6 *ipv6)
{
{
        unsigned int i;
        unsigned int i;
 
 
        for (i = 0; i < sizeof(*ipv6); i++)
        for (i = 0; i < sizeof(*ipv6); i++)
                if (((char *)ipv6)[i])
                if (((char *)ipv6)[i])
                        break;
                        break;
 
 
        return (i == sizeof(*ipv6));
        return (i == sizeof(*ipv6));
}
}
 
 
/* Figures out from what hook each rule can be called: returns 0 if
/* Figures out from what hook each rule can be called: returns 0 if
   there are loops.  Puts hook bitmask in comefrom. */
   there are loops.  Puts hook bitmask in comefrom. */
static int
static int
mark_source_chains(struct ip6t_table_info *newinfo, unsigned int valid_hooks)
mark_source_chains(struct ip6t_table_info *newinfo, unsigned int valid_hooks)
{
{
        unsigned int hook;
        unsigned int hook;
 
 
        /* No recursion; use packet counter to save back ptrs (reset
        /* No recursion; use packet counter to save back ptrs (reset
           to 0 as we leave), and comefrom to save source hook bitmask */
           to 0 as we leave), and comefrom to save source hook bitmask */
        for (hook = 0; hook < NF_IP6_NUMHOOKS; hook++) {
        for (hook = 0; hook < NF_IP6_NUMHOOKS; hook++) {
                unsigned int pos = newinfo->hook_entry[hook];
                unsigned int pos = newinfo->hook_entry[hook];
                struct ip6t_entry *e
                struct ip6t_entry *e
                        = (struct ip6t_entry *)(newinfo->entries + pos);
                        = (struct ip6t_entry *)(newinfo->entries + pos);
 
 
                if (!(valid_hooks & (1 << hook)))
                if (!(valid_hooks & (1 << hook)))
                        continue;
                        continue;
 
 
                /* Set initial back pointer. */
                /* Set initial back pointer. */
                e->counters.pcnt = pos;
                e->counters.pcnt = pos;
 
 
                for (;;) {
                for (;;) {
                        struct ip6t_standard_target *t
                        struct ip6t_standard_target *t
                                = (void *)ip6t_get_target(e);
                                = (void *)ip6t_get_target(e);
 
 
                        if (e->comefrom & (1 << NF_IP6_NUMHOOKS)) {
                        if (e->comefrom & (1 << NF_IP6_NUMHOOKS)) {
                                printk("iptables: loop hook %u pos %u %08X.\n",
                                printk("iptables: loop hook %u pos %u %08X.\n",
                                       hook, pos, e->comefrom);
                                       hook, pos, e->comefrom);
                                return 0;
                                return 0;
                        }
                        }
                        e->comefrom
                        e->comefrom
                                |= ((1 << hook) | (1 << NF_IP6_NUMHOOKS));
                                |= ((1 << hook) | (1 << NF_IP6_NUMHOOKS));
 
 
                        /* Unconditional return/END. */
                        /* Unconditional return/END. */
                        if (e->target_offset == sizeof(struct ip6t_entry)
                        if (e->target_offset == sizeof(struct ip6t_entry)
                            && (strcmp(t->target.u.user.name,
                            && (strcmp(t->target.u.user.name,
                                       IP6T_STANDARD_TARGET) == 0)
                                       IP6T_STANDARD_TARGET) == 0)
                            && t->verdict < 0
                            && t->verdict < 0
                            && unconditional(&e->ipv6)) {
                            && unconditional(&e->ipv6)) {
                                unsigned int oldpos, size;
                                unsigned int oldpos, size;
 
 
                                /* Return: backtrack through the last
                                /* Return: backtrack through the last
                                   big jump. */
                                   big jump. */
                                do {
                                do {
                                        e->comefrom ^= (1<<NF_IP6_NUMHOOKS);
                                        e->comefrom ^= (1<<NF_IP6_NUMHOOKS);
#ifdef DEBUG_IP_FIREWALL_USER
#ifdef DEBUG_IP_FIREWALL_USER
                                        if (e->comefrom
                                        if (e->comefrom
                                            & (1 << NF_IP6_NUMHOOKS)) {
                                            & (1 << NF_IP6_NUMHOOKS)) {
                                                duprintf("Back unset "
                                                duprintf("Back unset "
                                                         "on hook %u "
                                                         "on hook %u "
                                                         "rule %u\n",
                                                         "rule %u\n",
                                                         hook, pos);
                                                         hook, pos);
                                        }
                                        }
#endif
#endif
                                        oldpos = pos;
                                        oldpos = pos;
                                        pos = e->counters.pcnt;
                                        pos = e->counters.pcnt;
                                        e->counters.pcnt = 0;
                                        e->counters.pcnt = 0;
 
 
                                        /* We're at the start. */
                                        /* We're at the start. */
                                        if (pos == oldpos)
                                        if (pos == oldpos)
                                                goto next;
                                                goto next;
 
 
                                        e = (struct ip6t_entry *)
                                        e = (struct ip6t_entry *)
                                                (newinfo->entries + pos);
                                                (newinfo->entries + pos);
                                } while (oldpos == pos + e->next_offset);
                                } while (oldpos == pos + e->next_offset);
 
 
                                /* Move along one */
                                /* Move along one */
                                size = e->next_offset;
                                size = e->next_offset;
                                e = (struct ip6t_entry *)
                                e = (struct ip6t_entry *)
                                        (newinfo->entries + pos + size);
                                        (newinfo->entries + pos + size);
                                e->counters.pcnt = pos;
                                e->counters.pcnt = pos;
                                pos += size;
                                pos += size;
                        } else {
                        } else {
                                int newpos = t->verdict;
                                int newpos = t->verdict;
 
 
                                if (strcmp(t->target.u.user.name,
                                if (strcmp(t->target.u.user.name,
                                           IP6T_STANDARD_TARGET) == 0
                                           IP6T_STANDARD_TARGET) == 0
                                    && newpos >= 0) {
                                    && newpos >= 0) {
                                        /* This a jump; chase it. */
                                        /* This a jump; chase it. */
                                        duprintf("Jump rule %u -> %u\n",
                                        duprintf("Jump rule %u -> %u\n",
                                                 pos, newpos);
                                                 pos, newpos);
                                } else {
                                } else {
                                        /* ... this is a fallthru */
                                        /* ... this is a fallthru */
                                        newpos = pos + e->next_offset;
                                        newpos = pos + e->next_offset;
                                }
                                }
                                e = (struct ip6t_entry *)
                                e = (struct ip6t_entry *)
                                        (newinfo->entries + newpos);
                                        (newinfo->entries + newpos);
                                e->counters.pcnt = pos;
                                e->counters.pcnt = pos;
                                pos = newpos;
                                pos = newpos;
                        }
                        }
                }
                }
                next:
                next:
                duprintf("Finished chain %u\n", hook);
                duprintf("Finished chain %u\n", hook);
        }
        }
        return 1;
        return 1;
}
}
 
 
static inline int
static inline int
cleanup_match(struct ip6t_entry_match *m, unsigned int *i)
cleanup_match(struct ip6t_entry_match *m, unsigned int *i)
{
{
        if (i && (*i)-- == 0)
        if (i && (*i)-- == 0)
                return 1;
                return 1;
 
 
        if (m->u.kernel.match->destroy)
        if (m->u.kernel.match->destroy)
                m->u.kernel.match->destroy(m->data,
                m->u.kernel.match->destroy(m->data,
                                           m->u.match_size - sizeof(*m));
                                           m->u.match_size - sizeof(*m));
 
 
        if (m->u.kernel.match->me)
        if (m->u.kernel.match->me)
                __MOD_DEC_USE_COUNT(m->u.kernel.match->me);
                __MOD_DEC_USE_COUNT(m->u.kernel.match->me);
 
 
        return 0;
        return 0;
}
}
 
 
static inline int
static inline int
standard_check(const struct ip6t_entry_target *t,
standard_check(const struct ip6t_entry_target *t,
               unsigned int max_offset)
               unsigned int max_offset)
{
{
        struct ip6t_standard_target *targ = (void *)t;
        struct ip6t_standard_target *targ = (void *)t;
 
 
        /* Check standard info. */
        /* Check standard info. */
        if (t->u.target_size
        if (t->u.target_size
            != IP6T_ALIGN(sizeof(struct ip6t_standard_target))) {
            != IP6T_ALIGN(sizeof(struct ip6t_standard_target))) {
                duprintf("standard_check: target size %u != %u\n",
                duprintf("standard_check: target size %u != %u\n",
                         t->u.target_size,
                         t->u.target_size,
                         IP6T_ALIGN(sizeof(struct ip6t_standard_target)));
                         IP6T_ALIGN(sizeof(struct ip6t_standard_target)));
                return 0;
                return 0;
        }
        }
 
 
        if (targ->verdict >= 0
        if (targ->verdict >= 0
            && targ->verdict > max_offset - sizeof(struct ip6t_entry)) {
            && targ->verdict > max_offset - sizeof(struct ip6t_entry)) {
                duprintf("ip6t_standard_check: bad verdict (%i)\n",
                duprintf("ip6t_standard_check: bad verdict (%i)\n",
                         targ->verdict);
                         targ->verdict);
                return 0;
                return 0;
        }
        }
 
 
        if (targ->verdict < -NF_MAX_VERDICT - 1) {
        if (targ->verdict < -NF_MAX_VERDICT - 1) {
                duprintf("ip6t_standard_check: bad negative verdict (%i)\n",
                duprintf("ip6t_standard_check: bad negative verdict (%i)\n",
                         targ->verdict);
                         targ->verdict);
                return 0;
                return 0;
        }
        }
        return 1;
        return 1;
}
}
 
 
static inline int
static inline int
check_match(struct ip6t_entry_match *m,
check_match(struct ip6t_entry_match *m,
            const char *name,
            const char *name,
            const struct ip6t_ip6 *ipv6,
            const struct ip6t_ip6 *ipv6,
            unsigned int hookmask,
            unsigned int hookmask,
            unsigned int *i)
            unsigned int *i)
{
{
        int ret;
        int ret;
        struct ip6t_match *match;
        struct ip6t_match *match;
 
 
        match = find_match_lock(m->u.user.name, &ret, &ip6t_mutex);
        match = find_match_lock(m->u.user.name, &ret, &ip6t_mutex);
        if (!match) {
        if (!match) {
          //            duprintf("check_match: `%s' not found\n", m->u.name);
          //            duprintf("check_match: `%s' not found\n", m->u.name);
                return ret;
                return ret;
        }
        }
        if (match->me)
        if (match->me)
                __MOD_INC_USE_COUNT(match->me);
                __MOD_INC_USE_COUNT(match->me);
        m->u.kernel.match = match;
        m->u.kernel.match = match;
        up(&ip6t_mutex);
        up(&ip6t_mutex);
 
 
        if (m->u.kernel.match->checkentry
        if (m->u.kernel.match->checkentry
            && !m->u.kernel.match->checkentry(name, ipv6, m->data,
            && !m->u.kernel.match->checkentry(name, ipv6, m->data,
                                              m->u.match_size - sizeof(*m),
                                              m->u.match_size - sizeof(*m),
                                              hookmask)) {
                                              hookmask)) {
                if (m->u.kernel.match->me)
                if (m->u.kernel.match->me)
                        __MOD_DEC_USE_COUNT(m->u.kernel.match->me);
                        __MOD_DEC_USE_COUNT(m->u.kernel.match->me);
                duprintf("ip_tables: check failed for `%s'.\n",
                duprintf("ip_tables: check failed for `%s'.\n",
                         m->u.kernel.match->name);
                         m->u.kernel.match->name);
                return -EINVAL;
                return -EINVAL;
        }
        }
 
 
        (*i)++;
        (*i)++;
        return 0;
        return 0;
}
}
 
 
static struct ip6t_target ip6t_standard_target;
static struct ip6t_target ip6t_standard_target;
 
 
static inline int
static inline int
check_entry(struct ip6t_entry *e, const char *name, unsigned int size,
check_entry(struct ip6t_entry *e, const char *name, unsigned int size,
            unsigned int *i)
            unsigned int *i)
{
{
        struct ip6t_entry_target *t;
        struct ip6t_entry_target *t;
        struct ip6t_target *target;
        struct ip6t_target *target;
        int ret;
        int ret;
        unsigned int j;
        unsigned int j;
 
 
        if (!ip6_checkentry(&e->ipv6)) {
        if (!ip6_checkentry(&e->ipv6)) {
                duprintf("ip_tables: ip check failed %p %s.\n", e, name);
                duprintf("ip_tables: ip check failed %p %s.\n", e, name);
                return -EINVAL;
                return -EINVAL;
        }
        }
 
 
        j = 0;
        j = 0;
        ret = IP6T_MATCH_ITERATE(e, check_match, name, &e->ipv6, e->comefrom, &j);
        ret = IP6T_MATCH_ITERATE(e, check_match, name, &e->ipv6, e->comefrom, &j);
        if (ret != 0)
        if (ret != 0)
                goto cleanup_matches;
                goto cleanup_matches;
 
 
        t = ip6t_get_target(e);
        t = ip6t_get_target(e);
        target = find_target_lock(t->u.user.name, &ret, &ip6t_mutex);
        target = find_target_lock(t->u.user.name, &ret, &ip6t_mutex);
        if (!target) {
        if (!target) {
                duprintf("check_entry: `%s' not found\n", t->u.user.name);
                duprintf("check_entry: `%s' not found\n", t->u.user.name);
                goto cleanup_matches;
                goto cleanup_matches;
        }
        }
        if (target->me)
        if (target->me)
                __MOD_INC_USE_COUNT(target->me);
                __MOD_INC_USE_COUNT(target->me);
        t->u.kernel.target = target;
        t->u.kernel.target = target;
        up(&ip6t_mutex);
        up(&ip6t_mutex);
 
 
        if (t->u.kernel.target == &ip6t_standard_target) {
        if (t->u.kernel.target == &ip6t_standard_target) {
                if (!standard_check(t, size)) {
                if (!standard_check(t, size)) {
                        ret = -EINVAL;
                        ret = -EINVAL;
                        goto cleanup_matches;
                        goto cleanup_matches;
                }
                }
        } else if (t->u.kernel.target->checkentry
        } else if (t->u.kernel.target->checkentry
                   && !t->u.kernel.target->checkentry(name, e, t->data,
                   && !t->u.kernel.target->checkentry(name, e, t->data,
                                                      t->u.target_size
                                                      t->u.target_size
                                                      - sizeof(*t),
                                                      - sizeof(*t),
                                                      e->comefrom)) {
                                                      e->comefrom)) {
                if (t->u.kernel.target->me)
                if (t->u.kernel.target->me)
                        __MOD_DEC_USE_COUNT(t->u.kernel.target->me);
                        __MOD_DEC_USE_COUNT(t->u.kernel.target->me);
                duprintf("ip_tables: check failed for `%s'.\n",
                duprintf("ip_tables: check failed for `%s'.\n",
                         t->u.kernel.target->name);
                         t->u.kernel.target->name);
                ret = -EINVAL;
                ret = -EINVAL;
                goto cleanup_matches;
                goto cleanup_matches;
        }
        }
 
 
        (*i)++;
        (*i)++;
        return 0;
        return 0;
 
 
 cleanup_matches:
 cleanup_matches:
        IP6T_MATCH_ITERATE(e, cleanup_match, &j);
        IP6T_MATCH_ITERATE(e, cleanup_match, &j);
        return ret;
        return ret;
}
}
 
 
static inline int
static inline int
check_entry_size_and_hooks(struct ip6t_entry *e,
check_entry_size_and_hooks(struct ip6t_entry *e,
                           struct ip6t_table_info *newinfo,
                           struct ip6t_table_info *newinfo,
                           unsigned char *base,
                           unsigned char *base,
                           unsigned char *limit,
                           unsigned char *limit,
                           const unsigned int *hook_entries,
                           const unsigned int *hook_entries,
                           const unsigned int *underflows,
                           const unsigned int *underflows,
                           unsigned int *i)
                           unsigned int *i)
{
{
        unsigned int h;
        unsigned int h;
 
 
        if ((unsigned long)e % __alignof__(struct ip6t_entry) != 0
        if ((unsigned long)e % __alignof__(struct ip6t_entry) != 0
            || (unsigned char *)e + sizeof(struct ip6t_entry) >= limit) {
            || (unsigned char *)e + sizeof(struct ip6t_entry) >= limit) {
                duprintf("Bad offset %p\n", e);
                duprintf("Bad offset %p\n", e);
                return -EINVAL;
                return -EINVAL;
        }
        }
 
 
        if (e->next_offset
        if (e->next_offset
            < sizeof(struct ip6t_entry) + sizeof(struct ip6t_entry_target)) {
            < sizeof(struct ip6t_entry) + sizeof(struct ip6t_entry_target)) {
                duprintf("checking: element %p size %u\n",
                duprintf("checking: element %p size %u\n",
                         e, e->next_offset);
                         e, e->next_offset);
                return -EINVAL;
                return -EINVAL;
        }
        }
 
 
        /* Check hooks & underflows */
        /* Check hooks & underflows */
        for (h = 0; h < NF_IP6_NUMHOOKS; h++) {
        for (h = 0; h < NF_IP6_NUMHOOKS; h++) {
                if ((unsigned char *)e - base == hook_entries[h])
                if ((unsigned char *)e - base == hook_entries[h])
                        newinfo->hook_entry[h] = hook_entries[h];
                        newinfo->hook_entry[h] = hook_entries[h];
                if ((unsigned char *)e - base == underflows[h])
                if ((unsigned char *)e - base == underflows[h])
                        newinfo->underflow[h] = underflows[h];
                        newinfo->underflow[h] = underflows[h];
        }
        }
 
 
        /* FIXME: underflows must be unconditional, standard verdicts
        /* FIXME: underflows must be unconditional, standard verdicts
           < 0 (not IP6T_RETURN). --RR */
           < 0 (not IP6T_RETURN). --RR */
 
 
        /* Clear counters and comefrom */
        /* Clear counters and comefrom */
        e->counters = ((struct ip6t_counters) { 0, 0 });
        e->counters = ((struct ip6t_counters) { 0, 0 });
        e->comefrom = 0;
        e->comefrom = 0;
 
 
        (*i)++;
        (*i)++;
        return 0;
        return 0;
}
}
 
 
static inline int
static inline int
cleanup_entry(struct ip6t_entry *e, unsigned int *i)
cleanup_entry(struct ip6t_entry *e, unsigned int *i)
{
{
        struct ip6t_entry_target *t;
        struct ip6t_entry_target *t;
 
 
        if (i && (*i)-- == 0)
        if (i && (*i)-- == 0)
                return 1;
                return 1;
 
 
        /* Cleanup all matches */
        /* Cleanup all matches */
        IP6T_MATCH_ITERATE(e, cleanup_match, NULL);
        IP6T_MATCH_ITERATE(e, cleanup_match, NULL);
        t = ip6t_get_target(e);
        t = ip6t_get_target(e);
        if (t->u.kernel.target->destroy)
        if (t->u.kernel.target->destroy)
                t->u.kernel.target->destroy(t->data,
                t->u.kernel.target->destroy(t->data,
                                            t->u.target_size - sizeof(*t));
                                            t->u.target_size - sizeof(*t));
        if (t->u.kernel.target->me)
        if (t->u.kernel.target->me)
                __MOD_DEC_USE_COUNT(t->u.kernel.target->me);
                __MOD_DEC_USE_COUNT(t->u.kernel.target->me);
 
 
        return 0;
        return 0;
}
}
 
 
/* Checks and translates the user-supplied table segment (held in
/* Checks and translates the user-supplied table segment (held in
   newinfo) */
   newinfo) */
static int
static int
translate_table(const char *name,
translate_table(const char *name,
                unsigned int valid_hooks,
                unsigned int valid_hooks,
                struct ip6t_table_info *newinfo,
                struct ip6t_table_info *newinfo,
                unsigned int size,
                unsigned int size,
                unsigned int number,
                unsigned int number,
                const unsigned int *hook_entries,
                const unsigned int *hook_entries,
                const unsigned int *underflows)
                const unsigned int *underflows)
{
{
        unsigned int i;
        unsigned int i;
        int ret;
        int ret;
 
 
        newinfo->size = size;
        newinfo->size = size;
        newinfo->number = number;
        newinfo->number = number;
 
 
        /* Init all hooks to impossible value. */
        /* Init all hooks to impossible value. */
        for (i = 0; i < NF_IP6_NUMHOOKS; i++) {
        for (i = 0; i < NF_IP6_NUMHOOKS; i++) {
                newinfo->hook_entry[i] = 0xFFFFFFFF;
                newinfo->hook_entry[i] = 0xFFFFFFFF;
                newinfo->underflow[i] = 0xFFFFFFFF;
                newinfo->underflow[i] = 0xFFFFFFFF;
        }
        }
 
 
        duprintf("translate_table: size %u\n", newinfo->size);
        duprintf("translate_table: size %u\n", newinfo->size);
        i = 0;
        i = 0;
        /* Walk through entries, checking offsets. */
        /* Walk through entries, checking offsets. */
        ret = IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size,
        ret = IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size,
                                check_entry_size_and_hooks,
                                check_entry_size_and_hooks,
                                newinfo,
                                newinfo,
                                newinfo->entries,
                                newinfo->entries,
                                newinfo->entries + size,
                                newinfo->entries + size,
                                hook_entries, underflows, &i);
                                hook_entries, underflows, &i);
        if (ret != 0)
        if (ret != 0)
                return ret;
                return ret;
 
 
        if (i != number) {
        if (i != number) {
                duprintf("translate_table: %u not %u entries\n",
                duprintf("translate_table: %u not %u entries\n",
                         i, number);
                         i, number);
                return -EINVAL;
                return -EINVAL;
        }
        }
 
 
        /* Check hooks all assigned */
        /* Check hooks all assigned */
        for (i = 0; i < NF_IP6_NUMHOOKS; i++) {
        for (i = 0; i < NF_IP6_NUMHOOKS; i++) {
                /* Only hooks which are valid */
                /* Only hooks which are valid */
                if (!(valid_hooks & (1 << i)))
                if (!(valid_hooks & (1 << i)))
                        continue;
                        continue;
                if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
                if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
                        duprintf("Invalid hook entry %u %u\n",
                        duprintf("Invalid hook entry %u %u\n",
                                 i, hook_entries[i]);
                                 i, hook_entries[i]);
                        return -EINVAL;
                        return -EINVAL;
                }
                }
                if (newinfo->underflow[i] == 0xFFFFFFFF) {
                if (newinfo->underflow[i] == 0xFFFFFFFF) {
                        duprintf("Invalid underflow %u %u\n",
                        duprintf("Invalid underflow %u %u\n",
                                 i, underflows[i]);
                                 i, underflows[i]);
                        return -EINVAL;
                        return -EINVAL;
                }
                }
        }
        }
 
 
        if (!mark_source_chains(newinfo, valid_hooks))
        if (!mark_source_chains(newinfo, valid_hooks))
                return -ELOOP;
                return -ELOOP;
 
 
        /* Finally, each sanity check must pass */
        /* Finally, each sanity check must pass */
        i = 0;
        i = 0;
        ret = IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size,
        ret = IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size,
                                check_entry, name, size, &i);
                                check_entry, name, size, &i);
 
 
        if (ret != 0) {
        if (ret != 0) {
                IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size,
                IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size,
                                  cleanup_entry, &i);
                                  cleanup_entry, &i);
                return ret;
                return ret;
        }
        }
 
 
        /* And one copy for every other CPU */
        /* And one copy for every other CPU */
        for (i = 1; i < smp_num_cpus; i++) {
        for (i = 1; i < smp_num_cpus; i++) {
                memcpy(newinfo->entries + SMP_ALIGN(newinfo->size)*i,
                memcpy(newinfo->entries + SMP_ALIGN(newinfo->size)*i,
                       newinfo->entries,
                       newinfo->entries,
                       SMP_ALIGN(newinfo->size));
                       SMP_ALIGN(newinfo->size));
        }
        }
 
 
        return ret;
        return ret;
}
}
 
 
static struct ip6t_table_info *
static struct ip6t_table_info *
replace_table(struct ip6t_table *table,
replace_table(struct ip6t_table *table,
              unsigned int num_counters,
              unsigned int num_counters,
              struct ip6t_table_info *newinfo,
              struct ip6t_table_info *newinfo,
              int *error)
              int *error)
{
{
        struct ip6t_table_info *oldinfo;
        struct ip6t_table_info *oldinfo;
 
 
#ifdef CONFIG_NETFILTER_DEBUG
#ifdef CONFIG_NETFILTER_DEBUG
        {
        {
                struct ip6t_entry *table_base;
                struct ip6t_entry *table_base;
                unsigned int i;
                unsigned int i;
 
 
                for (i = 0; i < smp_num_cpus; i++) {
                for (i = 0; i < smp_num_cpus; i++) {
                        table_base =
                        table_base =
                                (void *)newinfo->entries
                                (void *)newinfo->entries
                                + TABLE_OFFSET(newinfo, i);
                                + TABLE_OFFSET(newinfo, i);
 
 
                        table_base->comefrom = 0xdead57ac;
                        table_base->comefrom = 0xdead57ac;
                }
                }
        }
        }
#endif
#endif
 
 
        /* Do the substitution. */
        /* Do the substitution. */
        write_lock_bh(&table->lock);
        write_lock_bh(&table->lock);
        /* Check inside lock: is the old number correct? */
        /* Check inside lock: is the old number correct? */
        if (num_counters != table->private->number) {
        if (num_counters != table->private->number) {
                duprintf("num_counters != table->private->number (%u/%u)\n",
                duprintf("num_counters != table->private->number (%u/%u)\n",
                         num_counters, table->private->number);
                         num_counters, table->private->number);
                write_unlock_bh(&table->lock);
                write_unlock_bh(&table->lock);
                *error = -EAGAIN;
                *error = -EAGAIN;
                return NULL;
                return NULL;
        }
        }
        oldinfo = table->private;
        oldinfo = table->private;
        table->private = newinfo;
        table->private = newinfo;
        newinfo->initial_entries = oldinfo->initial_entries;
        newinfo->initial_entries = oldinfo->initial_entries;
        write_unlock_bh(&table->lock);
        write_unlock_bh(&table->lock);
 
 
        return oldinfo;
        return oldinfo;
}
}
 
 
/* Gets counters. */
/* Gets counters. */
static inline int
static inline int
add_entry_to_counter(const struct ip6t_entry *e,
add_entry_to_counter(const struct ip6t_entry *e,
                     struct ip6t_counters total[],
                     struct ip6t_counters total[],
                     unsigned int *i)
                     unsigned int *i)
{
{
        ADD_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
        ADD_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
 
 
        (*i)++;
        (*i)++;
        return 0;
        return 0;
}
}
 
 
static void
static void
get_counters(const struct ip6t_table_info *t,
get_counters(const struct ip6t_table_info *t,
             struct ip6t_counters counters[])
             struct ip6t_counters counters[])
{
{
        unsigned int cpu;
        unsigned int cpu;
        unsigned int i;
        unsigned int i;
 
 
        for (cpu = 0; cpu < smp_num_cpus; cpu++) {
        for (cpu = 0; cpu < smp_num_cpus; cpu++) {
                i = 0;
                i = 0;
                IP6T_ENTRY_ITERATE(t->entries + TABLE_OFFSET(t, cpu),
                IP6T_ENTRY_ITERATE(t->entries + TABLE_OFFSET(t, cpu),
                                  t->size,
                                  t->size,
                                  add_entry_to_counter,
                                  add_entry_to_counter,
                                  counters,
                                  counters,
                                  &i);
                                  &i);
        }
        }
}
}
 
 
static int
static int
copy_entries_to_user(unsigned int total_size,
copy_entries_to_user(unsigned int total_size,
                     struct ip6t_table *table,
                     struct ip6t_table *table,
                     void *userptr)
                     void *userptr)
{
{
        unsigned int off, num, countersize;
        unsigned int off, num, countersize;
        struct ip6t_entry *e;
        struct ip6t_entry *e;
        struct ip6t_counters *counters;
        struct ip6t_counters *counters;
        int ret = 0;
        int ret = 0;
 
 
        /* We need atomic snapshot of counters: rest doesn't change
        /* We need atomic snapshot of counters: rest doesn't change
           (other than comefrom, which userspace doesn't care
           (other than comefrom, which userspace doesn't care
           about). */
           about). */
        countersize = sizeof(struct ip6t_counters) * table->private->number;
        countersize = sizeof(struct ip6t_counters) * table->private->number;
        counters = vmalloc(countersize);
        counters = vmalloc(countersize);
 
 
        if (counters == NULL)
        if (counters == NULL)
                return -ENOMEM;
                return -ENOMEM;
 
 
        /* First, sum counters... */
        /* First, sum counters... */
        memset(counters, 0, countersize);
        memset(counters, 0, countersize);
        write_lock_bh(&table->lock);
        write_lock_bh(&table->lock);
        get_counters(table->private, counters);
        get_counters(table->private, counters);
        write_unlock_bh(&table->lock);
        write_unlock_bh(&table->lock);
 
 
        /* ... then copy entire thing from CPU 0... */
        /* ... then copy entire thing from CPU 0... */
        if (copy_to_user(userptr, table->private->entries, total_size) != 0) {
        if (copy_to_user(userptr, table->private->entries, total_size) != 0) {
                ret = -EFAULT;
                ret = -EFAULT;
                goto free_counters;
                goto free_counters;
        }
        }
 
 
        /* FIXME: use iterator macros --RR */
        /* FIXME: use iterator macros --RR */
        /* ... then go back and fix counters and names */
        /* ... then go back and fix counters and names */
        for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){
        for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){
                unsigned int i;
                unsigned int i;
                struct ip6t_entry_match *m;
                struct ip6t_entry_match *m;
                struct ip6t_entry_target *t;
                struct ip6t_entry_target *t;
 
 
                e = (struct ip6t_entry *)(table->private->entries + off);
                e = (struct ip6t_entry *)(table->private->entries + off);
                if (copy_to_user(userptr + off
                if (copy_to_user(userptr + off
                                 + offsetof(struct ip6t_entry, counters),
                                 + offsetof(struct ip6t_entry, counters),
                                 &counters[num],
                                 &counters[num],
                                 sizeof(counters[num])) != 0) {
                                 sizeof(counters[num])) != 0) {
                        ret = -EFAULT;
                        ret = -EFAULT;
                        goto free_counters;
                        goto free_counters;
                }
                }
 
 
                for (i = sizeof(struct ip6t_entry);
                for (i = sizeof(struct ip6t_entry);
                     i < e->target_offset;
                     i < e->target_offset;
                     i += m->u.match_size) {
                     i += m->u.match_size) {
                        m = (void *)e + i;
                        m = (void *)e + i;
 
 
                        if (copy_to_user(userptr + off + i
                        if (copy_to_user(userptr + off + i
                                         + offsetof(struct ip6t_entry_match,
                                         + offsetof(struct ip6t_entry_match,
                                                    u.user.name),
                                                    u.user.name),
                                         m->u.kernel.match->name,
                                         m->u.kernel.match->name,
                                         strlen(m->u.kernel.match->name)+1)
                                         strlen(m->u.kernel.match->name)+1)
                            != 0) {
                            != 0) {
                                ret = -EFAULT;
                                ret = -EFAULT;
                                goto free_counters;
                                goto free_counters;
                        }
                        }
                }
                }
 
 
                t = ip6t_get_target(e);
                t = ip6t_get_target(e);
                if (copy_to_user(userptr + off + e->target_offset
                if (copy_to_user(userptr + off + e->target_offset
                                 + offsetof(struct ip6t_entry_target,
                                 + offsetof(struct ip6t_entry_target,
                                            u.user.name),
                                            u.user.name),
                                 t->u.kernel.target->name,
                                 t->u.kernel.target->name,
                                 strlen(t->u.kernel.target->name)+1) != 0) {
                                 strlen(t->u.kernel.target->name)+1) != 0) {
                        ret = -EFAULT;
                        ret = -EFAULT;
                        goto free_counters;
                        goto free_counters;
                }
                }
        }
        }
 
 
 free_counters:
 free_counters:
        vfree(counters);
        vfree(counters);
        return ret;
        return ret;
}
}
 
 
static int
static int
get_entries(const struct ip6t_get_entries *entries,
get_entries(const struct ip6t_get_entries *entries,
            struct ip6t_get_entries *uptr)
            struct ip6t_get_entries *uptr)
{
{
        int ret;
        int ret;
        struct ip6t_table *t;
        struct ip6t_table *t;
 
 
        t = find_table_lock(entries->name, &ret, &ip6t_mutex);
        t = find_table_lock(entries->name, &ret, &ip6t_mutex);
        if (t) {
        if (t) {
                duprintf("t->private->number = %u\n",
                duprintf("t->private->number = %u\n",
                         t->private->number);
                         t->private->number);
                if (entries->size == t->private->size)
                if (entries->size == t->private->size)
                        ret = copy_entries_to_user(t->private->size,
                        ret = copy_entries_to_user(t->private->size,
                                                   t, uptr->entrytable);
                                                   t, uptr->entrytable);
                else {
                else {
                        duprintf("get_entries: I've got %u not %u!\n",
                        duprintf("get_entries: I've got %u not %u!\n",
                                 t->private->size,
                                 t->private->size,
                                 entries->size);
                                 entries->size);
                        ret = -EINVAL;
                        ret = -EINVAL;
                }
                }
                up(&ip6t_mutex);
                up(&ip6t_mutex);
        } else
        } else
                duprintf("get_entries: Can't find %s!\n",
                duprintf("get_entries: Can't find %s!\n",
                         entries->name);
                         entries->name);
 
 
        return ret;
        return ret;
}
}
 
 
static int
static int
do_replace(void *user, unsigned int len)
do_replace(void *user, unsigned int len)
{
{
        int ret;
        int ret;
        struct ip6t_replace tmp;
        struct ip6t_replace tmp;
        struct ip6t_table *t;
        struct ip6t_table *t;
        struct ip6t_table_info *newinfo, *oldinfo;
        struct ip6t_table_info *newinfo, *oldinfo;
        struct ip6t_counters *counters;
        struct ip6t_counters *counters;
 
 
        if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
        if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
                return -EFAULT;
                return -EFAULT;
 
 
        /* Pedantry: prevent them from hitting BUG() in vmalloc.c --RR */
        /* Pedantry: prevent them from hitting BUG() in vmalloc.c --RR */
        if ((SMP_ALIGN(tmp.size) >> PAGE_SHIFT) + 2 > num_physpages)
        if ((SMP_ALIGN(tmp.size) >> PAGE_SHIFT) + 2 > num_physpages)
                return -ENOMEM;
                return -ENOMEM;
 
 
        newinfo = vmalloc(sizeof(struct ip6t_table_info)
        newinfo = vmalloc(sizeof(struct ip6t_table_info)
                          + SMP_ALIGN(tmp.size) * smp_num_cpus);
                          + SMP_ALIGN(tmp.size) * smp_num_cpus);
        if (!newinfo)
        if (!newinfo)
                return -ENOMEM;
                return -ENOMEM;
 
 
        if (copy_from_user(newinfo->entries, user + sizeof(tmp),
        if (copy_from_user(newinfo->entries, user + sizeof(tmp),
                           tmp.size) != 0) {
                           tmp.size) != 0) {
                ret = -EFAULT;
                ret = -EFAULT;
                goto free_newinfo;
                goto free_newinfo;
        }
        }
 
 
        counters = vmalloc(tmp.num_counters * sizeof(struct ip6t_counters));
        counters = vmalloc(tmp.num_counters * sizeof(struct ip6t_counters));
        if (!counters) {
        if (!counters) {
                ret = -ENOMEM;
                ret = -ENOMEM;
                goto free_newinfo;
                goto free_newinfo;
        }
        }
        memset(counters, 0, tmp.num_counters * sizeof(struct ip6t_counters));
        memset(counters, 0, tmp.num_counters * sizeof(struct ip6t_counters));
 
 
        ret = translate_table(tmp.name, tmp.valid_hooks,
        ret = translate_table(tmp.name, tmp.valid_hooks,
                              newinfo, tmp.size, tmp.num_entries,
                              newinfo, tmp.size, tmp.num_entries,
                              tmp.hook_entry, tmp.underflow);
                              tmp.hook_entry, tmp.underflow);
        if (ret != 0)
        if (ret != 0)
                goto free_newinfo_counters;
                goto free_newinfo_counters;
 
 
        duprintf("ip_tables: Translated table\n");
        duprintf("ip_tables: Translated table\n");
 
 
        t = find_table_lock(tmp.name, &ret, &ip6t_mutex);
        t = find_table_lock(tmp.name, &ret, &ip6t_mutex);
        if (!t)
        if (!t)
                goto free_newinfo_counters_untrans;
                goto free_newinfo_counters_untrans;
 
 
        /* You lied! */
        /* You lied! */
        if (tmp.valid_hooks != t->valid_hooks) {
        if (tmp.valid_hooks != t->valid_hooks) {
                duprintf("Valid hook crap: %08X vs %08X\n",
                duprintf("Valid hook crap: %08X vs %08X\n",
                         tmp.valid_hooks, t->valid_hooks);
                         tmp.valid_hooks, t->valid_hooks);
                ret = -EINVAL;
                ret = -EINVAL;
                goto free_newinfo_counters_untrans_unlock;
                goto free_newinfo_counters_untrans_unlock;
        }
        }
 
 
        oldinfo = replace_table(t, tmp.num_counters, newinfo, &ret);
        oldinfo = replace_table(t, tmp.num_counters, newinfo, &ret);
        if (!oldinfo)
        if (!oldinfo)
                goto free_newinfo_counters_untrans_unlock;
                goto free_newinfo_counters_untrans_unlock;
 
 
        /* Update module usage count based on number of rules */
        /* Update module usage count based on number of rules */
        duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
        duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
                oldinfo->number, oldinfo->initial_entries, newinfo->number);
                oldinfo->number, oldinfo->initial_entries, newinfo->number);
        if (t->me && (oldinfo->number <= oldinfo->initial_entries) &&
        if (t->me && (oldinfo->number <= oldinfo->initial_entries) &&
            (newinfo->number > oldinfo->initial_entries))
            (newinfo->number > oldinfo->initial_entries))
                __MOD_INC_USE_COUNT(t->me);
                __MOD_INC_USE_COUNT(t->me);
        else if (t->me && (oldinfo->number > oldinfo->initial_entries) &&
        else if (t->me && (oldinfo->number > oldinfo->initial_entries) &&
                 (newinfo->number <= oldinfo->initial_entries))
                 (newinfo->number <= oldinfo->initial_entries))
                __MOD_DEC_USE_COUNT(t->me);
                __MOD_DEC_USE_COUNT(t->me);
 
 
        /* Get the old counters. */
        /* Get the old counters. */
        get_counters(oldinfo, counters);
        get_counters(oldinfo, counters);
        /* Decrease module usage counts and free resource */
        /* Decrease module usage counts and free resource */
        IP6T_ENTRY_ITERATE(oldinfo->entries, oldinfo->size, cleanup_entry,NULL);
        IP6T_ENTRY_ITERATE(oldinfo->entries, oldinfo->size, cleanup_entry,NULL);
        vfree(oldinfo);
        vfree(oldinfo);
        /* Silent error: too late now. */
        /* Silent error: too late now. */
        copy_to_user(tmp.counters, counters,
        copy_to_user(tmp.counters, counters,
                     sizeof(struct ip6t_counters) * tmp.num_counters);
                     sizeof(struct ip6t_counters) * tmp.num_counters);
        vfree(counters);
        vfree(counters);
        up(&ip6t_mutex);
        up(&ip6t_mutex);
        return 0;
        return 0;
 
 
 free_newinfo_counters_untrans_unlock:
 free_newinfo_counters_untrans_unlock:
        up(&ip6t_mutex);
        up(&ip6t_mutex);
 free_newinfo_counters_untrans:
 free_newinfo_counters_untrans:
        IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size, cleanup_entry,NULL);
        IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size, cleanup_entry,NULL);
 free_newinfo_counters:
 free_newinfo_counters:
        vfree(counters);
        vfree(counters);
 free_newinfo:
 free_newinfo:
        vfree(newinfo);
        vfree(newinfo);
        return ret;
        return ret;
}
}
 
 
/* We're lazy, and add to the first CPU; overflow works its fey magic
/* We're lazy, and add to the first CPU; overflow works its fey magic
 * and everything is OK. */
 * and everything is OK. */
static inline int
static inline int
add_counter_to_entry(struct ip6t_entry *e,
add_counter_to_entry(struct ip6t_entry *e,
                     const struct ip6t_counters addme[],
                     const struct ip6t_counters addme[],
                     unsigned int *i)
                     unsigned int *i)
{
{
#if 0
#if 0
        duprintf("add_counter: Entry %u %lu/%lu + %lu/%lu\n",
        duprintf("add_counter: Entry %u %lu/%lu + %lu/%lu\n",
                 *i,
                 *i,
                 (long unsigned int)e->counters.pcnt,
                 (long unsigned int)e->counters.pcnt,
                 (long unsigned int)e->counters.bcnt,
                 (long unsigned int)e->counters.bcnt,
                 (long unsigned int)addme[*i].pcnt,
                 (long unsigned int)addme[*i].pcnt,
                 (long unsigned int)addme[*i].bcnt);
                 (long unsigned int)addme[*i].bcnt);
#endif
#endif
 
 
        ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
        ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
 
 
        (*i)++;
        (*i)++;
        return 0;
        return 0;
}
}
 
 
static int
static int
do_add_counters(void *user, unsigned int len)
do_add_counters(void *user, unsigned int len)
{
{
        unsigned int i;
        unsigned int i;
        struct ip6t_counters_info tmp, *paddc;
        struct ip6t_counters_info tmp, *paddc;
        struct ip6t_table *t;
        struct ip6t_table *t;
        int ret;
        int ret;
 
 
        if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
        if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
                return -EFAULT;
                return -EFAULT;
 
 
        if (len != sizeof(tmp) + tmp.num_counters*sizeof(struct ip6t_counters))
        if (len != sizeof(tmp) + tmp.num_counters*sizeof(struct ip6t_counters))
                return -EINVAL;
                return -EINVAL;
 
 
        paddc = vmalloc(len);
        paddc = vmalloc(len);
        if (!paddc)
        if (!paddc)
                return -ENOMEM;
                return -ENOMEM;
 
 
        if (copy_from_user(paddc, user, len) != 0) {
        if (copy_from_user(paddc, user, len) != 0) {
                ret = -EFAULT;
                ret = -EFAULT;
                goto free;
                goto free;
        }
        }
 
 
        t = find_table_lock(tmp.name, &ret, &ip6t_mutex);
        t = find_table_lock(tmp.name, &ret, &ip6t_mutex);
        if (!t)
        if (!t)
                goto free;
                goto free;
 
 
        write_lock_bh(&t->lock);
        write_lock_bh(&t->lock);
        if (t->private->number != paddc->num_counters) {
        if (t->private->number != paddc->num_counters) {
                ret = -EINVAL;
                ret = -EINVAL;
                goto unlock_up_free;
                goto unlock_up_free;
        }
        }
 
 
        i = 0;
        i = 0;
        IP6T_ENTRY_ITERATE(t->private->entries,
        IP6T_ENTRY_ITERATE(t->private->entries,
                          t->private->size,
                          t->private->size,
                          add_counter_to_entry,
                          add_counter_to_entry,
                          paddc->counters,
                          paddc->counters,
                          &i);
                          &i);
 unlock_up_free:
 unlock_up_free:
        write_unlock_bh(&t->lock);
        write_unlock_bh(&t->lock);
        up(&ip6t_mutex);
        up(&ip6t_mutex);
 free:
 free:
        vfree(paddc);
        vfree(paddc);
 
 
        return ret;
        return ret;
}
}
 
 
static int
static int
do_ip6t_set_ctl(struct sock *sk,        int cmd, void *user, unsigned int len)
do_ip6t_set_ctl(struct sock *sk,        int cmd, void *user, unsigned int len)
{
{
        int ret;
        int ret;
 
 
        if (!capable(CAP_NET_ADMIN))
        if (!capable(CAP_NET_ADMIN))
                return -EPERM;
                return -EPERM;
 
 
        switch (cmd) {
        switch (cmd) {
        case IP6T_SO_SET_REPLACE:
        case IP6T_SO_SET_REPLACE:
                ret = do_replace(user, len);
                ret = do_replace(user, len);
                break;
                break;
 
 
        case IP6T_SO_SET_ADD_COUNTERS:
        case IP6T_SO_SET_ADD_COUNTERS:
                ret = do_add_counters(user, len);
                ret = do_add_counters(user, len);
                break;
                break;
 
 
        default:
        default:
                duprintf("do_ip6t_set_ctl:  unknown request %i\n", cmd);
                duprintf("do_ip6t_set_ctl:  unknown request %i\n", cmd);
                ret = -EINVAL;
                ret = -EINVAL;
        }
        }
 
 
        return ret;
        return ret;
}
}
 
 
static int
static int
do_ip6t_get_ctl(struct sock *sk, int cmd, void *user, int *len)
do_ip6t_get_ctl(struct sock *sk, int cmd, void *user, int *len)
{
{
        int ret;
        int ret;
 
 
        if (!capable(CAP_NET_ADMIN))
        if (!capable(CAP_NET_ADMIN))
                return -EPERM;
                return -EPERM;
 
 
        switch (cmd) {
        switch (cmd) {
        case IP6T_SO_GET_INFO: {
        case IP6T_SO_GET_INFO: {
                char name[IP6T_TABLE_MAXNAMELEN];
                char name[IP6T_TABLE_MAXNAMELEN];
                struct ip6t_table *t;
                struct ip6t_table *t;
 
 
                if (*len != sizeof(struct ip6t_getinfo)) {
                if (*len != sizeof(struct ip6t_getinfo)) {
                        duprintf("length %u != %u\n", *len,
                        duprintf("length %u != %u\n", *len,
                                 sizeof(struct ip6t_getinfo));
                                 sizeof(struct ip6t_getinfo));
                        ret = -EINVAL;
                        ret = -EINVAL;
                        break;
                        break;
                }
                }
 
 
                if (copy_from_user(name, user, sizeof(name)) != 0) {
                if (copy_from_user(name, user, sizeof(name)) != 0) {
                        ret = -EFAULT;
                        ret = -EFAULT;
                        break;
                        break;
                }
                }
                name[IP6T_TABLE_MAXNAMELEN-1] = '\0';
                name[IP6T_TABLE_MAXNAMELEN-1] = '\0';
                t = find_table_lock(name, &ret, &ip6t_mutex);
                t = find_table_lock(name, &ret, &ip6t_mutex);
                if (t) {
                if (t) {
                        struct ip6t_getinfo info;
                        struct ip6t_getinfo info;
 
 
                        info.valid_hooks = t->valid_hooks;
                        info.valid_hooks = t->valid_hooks;
                        memcpy(info.hook_entry, t->private->hook_entry,
                        memcpy(info.hook_entry, t->private->hook_entry,
                               sizeof(info.hook_entry));
                               sizeof(info.hook_entry));
                        memcpy(info.underflow, t->private->underflow,
                        memcpy(info.underflow, t->private->underflow,
                               sizeof(info.underflow));
                               sizeof(info.underflow));
                        info.num_entries = t->private->number;
                        info.num_entries = t->private->number;
                        info.size = t->private->size;
                        info.size = t->private->size;
                        strcpy(info.name, name);
                        strcpy(info.name, name);
 
 
                        if (copy_to_user(user, &info, *len) != 0)
                        if (copy_to_user(user, &info, *len) != 0)
                                ret = -EFAULT;
                                ret = -EFAULT;
                        else
                        else
                                ret = 0;
                                ret = 0;
 
 
                        up(&ip6t_mutex);
                        up(&ip6t_mutex);
                }
                }
        }
        }
        break;
        break;
 
 
        case IP6T_SO_GET_ENTRIES: {
        case IP6T_SO_GET_ENTRIES: {
                struct ip6t_get_entries get;
                struct ip6t_get_entries get;
 
 
                if (*len < sizeof(get)) {
                if (*len < sizeof(get)) {
                        duprintf("get_entries: %u < %u\n", *len, sizeof(get));
                        duprintf("get_entries: %u < %u\n", *len, sizeof(get));
                        ret = -EINVAL;
                        ret = -EINVAL;
                } else if (copy_from_user(&get, user, sizeof(get)) != 0) {
                } else if (copy_from_user(&get, user, sizeof(get)) != 0) {
                        ret = -EFAULT;
                        ret = -EFAULT;
                } else if (*len != sizeof(struct ip6t_get_entries) + get.size) {
                } else if (*len != sizeof(struct ip6t_get_entries) + get.size) {
                        duprintf("get_entries: %u != %u\n", *len,
                        duprintf("get_entries: %u != %u\n", *len,
                                 sizeof(struct ip6t_get_entries) + get.size);
                                 sizeof(struct ip6t_get_entries) + get.size);
                        ret = -EINVAL;
                        ret = -EINVAL;
                } else
                } else
                        ret = get_entries(&get, user);
                        ret = get_entries(&get, user);
                break;
                break;
        }
        }
 
 
        default:
        default:
                duprintf("do_ip6t_get_ctl: unknown request %i\n", cmd);
                duprintf("do_ip6t_get_ctl: unknown request %i\n", cmd);
                ret = -EINVAL;
                ret = -EINVAL;
        }
        }
 
 
        return ret;
        return ret;
}
}
 
 
/* Registration hooks for targets. */
/* Registration hooks for targets. */
int
int
ip6t_register_target(struct ip6t_target *target)
ip6t_register_target(struct ip6t_target *target)
{
{
        int ret;
        int ret;
 
 
        MOD_INC_USE_COUNT;
        MOD_INC_USE_COUNT;
        ret = down_interruptible(&ip6t_mutex);
        ret = down_interruptible(&ip6t_mutex);
        if (ret != 0) {
        if (ret != 0) {
                MOD_DEC_USE_COUNT;
                MOD_DEC_USE_COUNT;
                return ret;
                return ret;
        }
        }
        if (!list_named_insert(&ip6t_target, target)) {
        if (!list_named_insert(&ip6t_target, target)) {
                duprintf("ip6t_register_target: `%s' already in list!\n",
                duprintf("ip6t_register_target: `%s' already in list!\n",
                         target->name);
                         target->name);
                ret = -EINVAL;
                ret = -EINVAL;
                MOD_DEC_USE_COUNT;
                MOD_DEC_USE_COUNT;
        }
        }
        up(&ip6t_mutex);
        up(&ip6t_mutex);
        return ret;
        return ret;
}
}
 
 
void
void
ip6t_unregister_target(struct ip6t_target *target)
ip6t_unregister_target(struct ip6t_target *target)
{
{
        down(&ip6t_mutex);
        down(&ip6t_mutex);
        LIST_DELETE(&ip6t_target, target);
        LIST_DELETE(&ip6t_target, target);
        up(&ip6t_mutex);
        up(&ip6t_mutex);
        MOD_DEC_USE_COUNT;
        MOD_DEC_USE_COUNT;
}
}
 
 
int
int
ip6t_register_match(struct ip6t_match *match)
ip6t_register_match(struct ip6t_match *match)
{
{
        int ret;
        int ret;
 
 
        MOD_INC_USE_COUNT;
        MOD_INC_USE_COUNT;
        ret = down_interruptible(&ip6t_mutex);
        ret = down_interruptible(&ip6t_mutex);
        if (ret != 0) {
        if (ret != 0) {
                MOD_DEC_USE_COUNT;
                MOD_DEC_USE_COUNT;
                return ret;
                return ret;
        }
        }
        if (!list_named_insert(&ip6t_match, match)) {
        if (!list_named_insert(&ip6t_match, match)) {
                duprintf("ip6t_register_match: `%s' already in list!\n",
                duprintf("ip6t_register_match: `%s' already in list!\n",
                         match->name);
                         match->name);
                MOD_DEC_USE_COUNT;
                MOD_DEC_USE_COUNT;
                ret = -EINVAL;
                ret = -EINVAL;
        }
        }
        up(&ip6t_mutex);
        up(&ip6t_mutex);
 
 
        return ret;
        return ret;
}
}
 
 
void
void
ip6t_unregister_match(struct ip6t_match *match)
ip6t_unregister_match(struct ip6t_match *match)
{
{
        down(&ip6t_mutex);
        down(&ip6t_mutex);
        LIST_DELETE(&ip6t_match, match);
        LIST_DELETE(&ip6t_match, match);
        up(&ip6t_mutex);
        up(&ip6t_mutex);
        MOD_DEC_USE_COUNT;
        MOD_DEC_USE_COUNT;
}
}
 
 
int ip6t_register_table(struct ip6t_table *table)
int ip6t_register_table(struct ip6t_table *table)
{
{
        int ret;
        int ret;
        struct ip6t_table_info *newinfo;
        struct ip6t_table_info *newinfo;
        static struct ip6t_table_info bootstrap
        static struct ip6t_table_info bootstrap
                = { 0, 0, 0, { 0 }, { 0 }, { } };
                = { 0, 0, 0, { 0 }, { 0 }, { } };
 
 
        MOD_INC_USE_COUNT;
        MOD_INC_USE_COUNT;
        newinfo = vmalloc(sizeof(struct ip6t_table_info)
        newinfo = vmalloc(sizeof(struct ip6t_table_info)
                          + SMP_ALIGN(table->table->size) * smp_num_cpus);
                          + SMP_ALIGN(table->table->size) * smp_num_cpus);
        if (!newinfo) {
        if (!newinfo) {
                ret = -ENOMEM;
                ret = -ENOMEM;
                MOD_DEC_USE_COUNT;
                MOD_DEC_USE_COUNT;
                return ret;
                return ret;
        }
        }
        memcpy(newinfo->entries, table->table->entries, table->table->size);
        memcpy(newinfo->entries, table->table->entries, table->table->size);
 
 
        ret = translate_table(table->name, table->valid_hooks,
        ret = translate_table(table->name, table->valid_hooks,
                              newinfo, table->table->size,
                              newinfo, table->table->size,
                              table->table->num_entries,
                              table->table->num_entries,
                              table->table->hook_entry,
                              table->table->hook_entry,
                              table->table->underflow);
                              table->table->underflow);
        if (ret != 0) {
        if (ret != 0) {
                vfree(newinfo);
                vfree(newinfo);
                MOD_DEC_USE_COUNT;
                MOD_DEC_USE_COUNT;
                return ret;
                return ret;
        }
        }
 
 
        ret = down_interruptible(&ip6t_mutex);
        ret = down_interruptible(&ip6t_mutex);
        if (ret != 0) {
        if (ret != 0) {
                vfree(newinfo);
                vfree(newinfo);
                MOD_DEC_USE_COUNT;
                MOD_DEC_USE_COUNT;
                return ret;
                return ret;
        }
        }
 
 
        /* Don't autoload: we'd eat our tail... */
        /* Don't autoload: we'd eat our tail... */
        if (list_named_find(&ip6t_tables, table->name)) {
        if (list_named_find(&ip6t_tables, table->name)) {
                ret = -EEXIST;
                ret = -EEXIST;
                goto free_unlock;
                goto free_unlock;
        }
        }
 
 
        /* Simplifies replace_table code. */
        /* Simplifies replace_table code. */
        table->private = &bootstrap;
        table->private = &bootstrap;
        if (!replace_table(table, 0, newinfo, &ret))
        if (!replace_table(table, 0, newinfo, &ret))
                goto free_unlock;
                goto free_unlock;
 
 
        duprintf("table->private->number = %u\n",
        duprintf("table->private->number = %u\n",
                 table->private->number);
                 table->private->number);
 
 
        /* save number of initial entries */
        /* save number of initial entries */
        table->private->initial_entries = table->private->number;
        table->private->initial_entries = table->private->number;
 
 
        table->lock = RW_LOCK_UNLOCKED;
        table->lock = RW_LOCK_UNLOCKED;
        list_prepend(&ip6t_tables, table);
        list_prepend(&ip6t_tables, table);
 
 
 unlock:
 unlock:
        up(&ip6t_mutex);
        up(&ip6t_mutex);
        return ret;
        return ret;
 
 
 free_unlock:
 free_unlock:
        vfree(newinfo);
        vfree(newinfo);
        MOD_DEC_USE_COUNT;
        MOD_DEC_USE_COUNT;
        goto unlock;
        goto unlock;
}
}
 
 
void ip6t_unregister_table(struct ip6t_table *table)
void ip6t_unregister_table(struct ip6t_table *table)
{
{
        down(&ip6t_mutex);
        down(&ip6t_mutex);
        LIST_DELETE(&ip6t_tables, table);
        LIST_DELETE(&ip6t_tables, table);
        up(&ip6t_mutex);
        up(&ip6t_mutex);
 
 
        /* Decrease module usage counts and free resources */
        /* Decrease module usage counts and free resources */
        IP6T_ENTRY_ITERATE(table->private->entries, table->private->size,
        IP6T_ENTRY_ITERATE(table->private->entries, table->private->size,
                          cleanup_entry, NULL);
                          cleanup_entry, NULL);
        vfree(table->private);
        vfree(table->private);
        MOD_DEC_USE_COUNT;
        MOD_DEC_USE_COUNT;
}
}
 
 
/* Returns 1 if the port is matched by the range, 0 otherwise */
/* Returns 1 if the port is matched by the range, 0 otherwise */
static inline int
static inline int
port_match(u_int16_t min, u_int16_t max, u_int16_t port, int invert)
port_match(u_int16_t min, u_int16_t max, u_int16_t port, int invert)
{
{
        int ret;
        int ret;
 
 
        ret = (port >= min && port <= max) ^ invert;
        ret = (port >= min && port <= max) ^ invert;
        return ret;
        return ret;
}
}
 
 
static int
static int
tcp_find_option(u_int8_t option,
tcp_find_option(u_int8_t option,
                const struct tcphdr *tcp,
                const struct tcphdr *tcp,
                u_int16_t datalen,
                u_int16_t datalen,
                int invert,
                int invert,
                int *hotdrop)
                int *hotdrop)
{
{
        unsigned int i = sizeof(struct tcphdr);
        unsigned int i = sizeof(struct tcphdr);
        const u_int8_t *opt = (u_int8_t *)tcp;
        const u_int8_t *opt = (u_int8_t *)tcp;
 
 
        duprintf("tcp_match: finding option\n");
        duprintf("tcp_match: finding option\n");
        /* If we don't have the whole header, drop packet. */
        /* If we don't have the whole header, drop packet. */
        if (tcp->doff * 4 < sizeof(struct tcphdr) ||
        if (tcp->doff * 4 < sizeof(struct tcphdr) ||
            tcp->doff * 4 > datalen) {
            tcp->doff * 4 > datalen) {
                *hotdrop = 1;
                *hotdrop = 1;
                return 0;
                return 0;
        }
        }
 
 
        while (i < tcp->doff * 4) {
        while (i < tcp->doff * 4) {
                if (opt[i] == option) return !invert;
                if (opt[i] == option) return !invert;
                if (opt[i] < 2) i++;
                if (opt[i] < 2) i++;
                else i += opt[i+1]?:1;
                else i += opt[i+1]?:1;
        }
        }
 
 
        return invert;
        return invert;
}
}
 
 
static int
static int
tcp_match(const struct sk_buff *skb,
tcp_match(const struct sk_buff *skb,
          const struct net_device *in,
          const struct net_device *in,
          const struct net_device *out,
          const struct net_device *out,
          const void *matchinfo,
          const void *matchinfo,
          int offset,
          int offset,
          const void *hdr,
          const void *hdr,
          u_int16_t datalen,
          u_int16_t datalen,
          int *hotdrop)
          int *hotdrop)
{
{
        const struct tcphdr *tcp;
        const struct tcphdr *tcp;
        const struct ip6t_tcp *tcpinfo = matchinfo;
        const struct ip6t_tcp *tcpinfo = matchinfo;
        int tcpoff;
        int tcpoff;
        u8 nexthdr = skb->nh.ipv6h->nexthdr;
        u8 nexthdr = skb->nh.ipv6h->nexthdr;
 
 
        /* To quote Alan:
        /* To quote Alan:
 
 
           Don't allow a fragment of TCP 8 bytes in. Nobody normal
           Don't allow a fragment of TCP 8 bytes in. Nobody normal
           causes this. Its a cracker trying to break in by doing a
           causes this. Its a cracker trying to break in by doing a
           flag overwrite to pass the direction checks.
           flag overwrite to pass the direction checks.
        */
        */
 
 
        if (offset == 1) {
        if (offset == 1) {
                duprintf("Dropping evil TCP offset=1 frag.\n");
                duprintf("Dropping evil TCP offset=1 frag.\n");
                *hotdrop = 1;
                *hotdrop = 1;
                return 0;
                return 0;
        } else if (offset == 0 && datalen < sizeof(struct tcphdr)) {
        } else if (offset == 0 && datalen < sizeof(struct tcphdr)) {
                /* We've been asked to examine this packet, and we
                /* We've been asked to examine this packet, and we
                   can't.  Hence, no choice but to drop. */
                   can't.  Hence, no choice but to drop. */
                duprintf("Dropping evil TCP offset=0 tinygram.\n");
                duprintf("Dropping evil TCP offset=0 tinygram.\n");
                *hotdrop = 1;
                *hotdrop = 1;
                return 0;
                return 0;
        }
        }
 
 
        tcpoff = (u8*)(skb->nh.ipv6h + 1) - skb->data;
        tcpoff = (u8*)(skb->nh.ipv6h + 1) - skb->data;
        tcpoff = ipv6_skip_exthdr(skb, tcpoff, &nexthdr, skb->len - tcpoff);
        tcpoff = ipv6_skip_exthdr(skb, tcpoff, &nexthdr, skb->len - tcpoff);
        if (tcpoff < 0 || tcpoff > skb->len) {
        if (tcpoff < 0 || tcpoff > skb->len) {
                duprintf("tcp_match: cannot skip exthdr. Dropping.\n");
                duprintf("tcp_match: cannot skip exthdr. Dropping.\n");
                *hotdrop = 1;
                *hotdrop = 1;
                return 0;
                return 0;
        } else if (nexthdr == IPPROTO_FRAGMENT)
        } else if (nexthdr == IPPROTO_FRAGMENT)
                return 0;
                return 0;
        else if (nexthdr != IPPROTO_TCP ||
        else if (nexthdr != IPPROTO_TCP ||
                 skb->len - tcpoff < sizeof(struct tcphdr)) {
                 skb->len - tcpoff < sizeof(struct tcphdr)) {
                /* cannot be occured */
                /* cannot be occured */
                duprintf("tcp_match: cannot get TCP header. Dropping.\n");
                duprintf("tcp_match: cannot get TCP header. Dropping.\n");
                *hotdrop = 1;
                *hotdrop = 1;
                return 0;
                return 0;
        }
        }
 
 
        tcp = (struct tcphdr *)(skb->data + tcpoff);
        tcp = (struct tcphdr *)(skb->data + tcpoff);
 
 
        /* FIXME: Try tcp doff >> packet len against various stacks --RR */
        /* FIXME: Try tcp doff >> packet len against various stacks --RR */
 
 
#define FWINVTCP(bool,invflg) ((bool) ^ !!(tcpinfo->invflags & invflg))
#define FWINVTCP(bool,invflg) ((bool) ^ !!(tcpinfo->invflags & invflg))
 
 
        /* Must not be a fragment. */
        /* Must not be a fragment. */
        return !offset
        return !offset
                && port_match(tcpinfo->spts[0], tcpinfo->spts[1],
                && port_match(tcpinfo->spts[0], tcpinfo->spts[1],
                              ntohs(tcp->source),
                              ntohs(tcp->source),
                              !!(tcpinfo->invflags & IP6T_TCP_INV_SRCPT))
                              !!(tcpinfo->invflags & IP6T_TCP_INV_SRCPT))
                && port_match(tcpinfo->dpts[0], tcpinfo->dpts[1],
                && port_match(tcpinfo->dpts[0], tcpinfo->dpts[1],
                              ntohs(tcp->dest),
                              ntohs(tcp->dest),
                              !!(tcpinfo->invflags & IP6T_TCP_INV_DSTPT))
                              !!(tcpinfo->invflags & IP6T_TCP_INV_DSTPT))
                && FWINVTCP((((unsigned char *)tcp)[13]
                && FWINVTCP((((unsigned char *)tcp)[13]
                             & tcpinfo->flg_mask)
                             & tcpinfo->flg_mask)
                            == tcpinfo->flg_cmp,
                            == tcpinfo->flg_cmp,
                            IP6T_TCP_INV_FLAGS)
                            IP6T_TCP_INV_FLAGS)
                && (!tcpinfo->option
                && (!tcpinfo->option
                    || tcp_find_option(tcpinfo->option, tcp, datalen,
                    || tcp_find_option(tcpinfo->option, tcp, datalen,
                                       tcpinfo->invflags
                                       tcpinfo->invflags
                                       & IP6T_TCP_INV_OPTION,
                                       & IP6T_TCP_INV_OPTION,
                                       hotdrop));
                                       hotdrop));
}
}
 
 
/* Called when user tries to insert an entry of this type. */
/* Called when user tries to insert an entry of this type. */
static int
static int
tcp_checkentry(const char *tablename,
tcp_checkentry(const char *tablename,
               const struct ip6t_ip6 *ipv6,
               const struct ip6t_ip6 *ipv6,
               void *matchinfo,
               void *matchinfo,
               unsigned int matchsize,
               unsigned int matchsize,
               unsigned int hook_mask)
               unsigned int hook_mask)
{
{
        const struct ip6t_tcp *tcpinfo = matchinfo;
        const struct ip6t_tcp *tcpinfo = matchinfo;
 
 
        /* Must specify proto == TCP, and no unknown invflags */
        /* Must specify proto == TCP, and no unknown invflags */
        return ipv6->proto == IPPROTO_TCP
        return ipv6->proto == IPPROTO_TCP
                && !(ipv6->invflags & IP6T_INV_PROTO)
                && !(ipv6->invflags & IP6T_INV_PROTO)
                && matchsize == IP6T_ALIGN(sizeof(struct ip6t_tcp))
                && matchsize == IP6T_ALIGN(sizeof(struct ip6t_tcp))
                && !(tcpinfo->invflags & ~IP6T_TCP_INV_MASK);
                && !(tcpinfo->invflags & ~IP6T_TCP_INV_MASK);
}
}
 
 
static int
static int
udp_match(const struct sk_buff *skb,
udp_match(const struct sk_buff *skb,
          const struct net_device *in,
          const struct net_device *in,
          const struct net_device *out,
          const struct net_device *out,
          const void *matchinfo,
          const void *matchinfo,
          int offset,
          int offset,
          const void *hdr,
          const void *hdr,
          u_int16_t datalen,
          u_int16_t datalen,
          int *hotdrop)
          int *hotdrop)
{
{
        const struct udphdr *udp;
        const struct udphdr *udp;
        const struct ip6t_udp *udpinfo = matchinfo;
        const struct ip6t_udp *udpinfo = matchinfo;
        int udpoff;
        int udpoff;
        u8 nexthdr = skb->nh.ipv6h->nexthdr;
        u8 nexthdr = skb->nh.ipv6h->nexthdr;
 
 
        if (offset == 0 && datalen < sizeof(struct udphdr)) {
        if (offset == 0 && datalen < sizeof(struct udphdr)) {
                /* We've been asked to examine this packet, and we
                /* We've been asked to examine this packet, and we
                   can't.  Hence, no choice but to drop. */
                   can't.  Hence, no choice but to drop. */
                duprintf("Dropping evil UDP tinygram.\n");
                duprintf("Dropping evil UDP tinygram.\n");
                *hotdrop = 1;
                *hotdrop = 1;
                return 0;
                return 0;
        }
        }
 
 
        udpoff = (u8*)(skb->nh.ipv6h + 1) - skb->data;
        udpoff = (u8*)(skb->nh.ipv6h + 1) - skb->data;
        udpoff = ipv6_skip_exthdr(skb, udpoff, &nexthdr, skb->len - udpoff);
        udpoff = ipv6_skip_exthdr(skb, udpoff, &nexthdr, skb->len - udpoff);
        if (udpoff < 0 || udpoff > skb->len) {
        if (udpoff < 0 || udpoff > skb->len) {
                duprintf("udp_match: cannot skip exthdr. Dropping.\n");
                duprintf("udp_match: cannot skip exthdr. Dropping.\n");
                *hotdrop = 1;
                *hotdrop = 1;
                return 0;
                return 0;
        } else if (nexthdr == IPPROTO_FRAGMENT)
        } else if (nexthdr == IPPROTO_FRAGMENT)
                return 0;
                return 0;
        else if (nexthdr != IPPROTO_UDP ||
        else if (nexthdr != IPPROTO_UDP ||
                 skb->len - udpoff < sizeof(struct udphdr)) {
                 skb->len - udpoff < sizeof(struct udphdr)) {
                duprintf("udp_match: cannot get UDP header. Dropping.\n");
                duprintf("udp_match: cannot get UDP header. Dropping.\n");
                *hotdrop = 1;
                *hotdrop = 1;
                return 0;
                return 0;
        }
        }
 
 
        udp = (struct udphdr *)(skb->data + udpoff);
        udp = (struct udphdr *)(skb->data + udpoff);
 
 
        /* Must not be a fragment. */
        /* Must not be a fragment. */
        return !offset
        return !offset
                && port_match(udpinfo->spts[0], udpinfo->spts[1],
                && port_match(udpinfo->spts[0], udpinfo->spts[1],
                              ntohs(udp->source),
                              ntohs(udp->source),
                              !!(udpinfo->invflags & IP6T_UDP_INV_SRCPT))
                              !!(udpinfo->invflags & IP6T_UDP_INV_SRCPT))
                && port_match(udpinfo->dpts[0], udpinfo->dpts[1],
                && port_match(udpinfo->dpts[0], udpinfo->dpts[1],
                              ntohs(udp->dest),
                              ntohs(udp->dest),
                              !!(udpinfo->invflags & IP6T_UDP_INV_DSTPT));
                              !!(udpinfo->invflags & IP6T_UDP_INV_DSTPT));
}
}
 
 
/* Called when user tries to insert an entry of this type. */
/* Called when user tries to insert an entry of this type. */
static int
static int
udp_checkentry(const char *tablename,
udp_checkentry(const char *tablename,
               const struct ip6t_ip6 *ipv6,
               const struct ip6t_ip6 *ipv6,
               void *matchinfo,
               void *matchinfo,
               unsigned int matchinfosize,
               unsigned int matchinfosize,
               unsigned int hook_mask)
               unsigned int hook_mask)
{
{
        const struct ip6t_udp *udpinfo = matchinfo;
        const struct ip6t_udp *udpinfo = matchinfo;
 
 
        /* Must specify proto == UDP, and no unknown invflags */
        /* Must specify proto == UDP, and no unknown invflags */
        if (ipv6->proto != IPPROTO_UDP || (ipv6->invflags & IP6T_INV_PROTO)) {
        if (ipv6->proto != IPPROTO_UDP || (ipv6->invflags & IP6T_INV_PROTO)) {
                duprintf("ip6t_udp: Protocol %u != %u\n", ipv6->proto,
                duprintf("ip6t_udp: Protocol %u != %u\n", ipv6->proto,
                         IPPROTO_UDP);
                         IPPROTO_UDP);
                return 0;
                return 0;
        }
        }
        if (matchinfosize != IP6T_ALIGN(sizeof(struct ip6t_udp))) {
        if (matchinfosize != IP6T_ALIGN(sizeof(struct ip6t_udp))) {
                duprintf("ip6t_udp: matchsize %u != %u\n",
                duprintf("ip6t_udp: matchsize %u != %u\n",
                         matchinfosize, IP6T_ALIGN(sizeof(struct ip6t_udp)));
                         matchinfosize, IP6T_ALIGN(sizeof(struct ip6t_udp)));
                return 0;
                return 0;
        }
        }
        if (udpinfo->invflags & ~IP6T_UDP_INV_MASK) {
        if (udpinfo->invflags & ~IP6T_UDP_INV_MASK) {
                duprintf("ip6t_udp: unknown flags %X\n",
                duprintf("ip6t_udp: unknown flags %X\n",
                         udpinfo->invflags);
                         udpinfo->invflags);
                return 0;
                return 0;
        }
        }
 
 
        return 1;
        return 1;
}
}
 
 
/* Returns 1 if the type and code is matched by the range, 0 otherwise */
/* Returns 1 if the type and code is matched by the range, 0 otherwise */
static inline int
static inline int
icmp6_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
icmp6_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
                     u_int8_t type, u_int8_t code,
                     u_int8_t type, u_int8_t code,
                     int invert)
                     int invert)
{
{
        return (type == test_type && code >= min_code && code <= max_code)
        return (type == test_type && code >= min_code && code <= max_code)
                ^ invert;
                ^ invert;
}
}
 
 
static int
static int
icmp6_match(const struct sk_buff *skb,
icmp6_match(const struct sk_buff *skb,
           const struct net_device *in,
           const struct net_device *in,
           const struct net_device *out,
           const struct net_device *out,
           const void *matchinfo,
           const void *matchinfo,
           int offset,
           int offset,
           const void *hdr,
           const void *hdr,
           u_int16_t datalen,
           u_int16_t datalen,
           int *hotdrop)
           int *hotdrop)
{
{
        const struct icmp6hdr *icmp = hdr;
        const struct icmp6hdr *icmp = hdr;
        const struct ip6t_icmp *icmpinfo = matchinfo;
        const struct ip6t_icmp *icmpinfo = matchinfo;
 
 
        if (offset == 0 && datalen < 2) {
        if (offset == 0 && datalen < 2) {
                /* We've been asked to examine this packet, and we
                /* We've been asked to examine this packet, and we
                   can't.  Hence, no choice but to drop. */
                   can't.  Hence, no choice but to drop. */
                duprintf("Dropping evil ICMP tinygram.\n");
                duprintf("Dropping evil ICMP tinygram.\n");
                *hotdrop = 1;
                *hotdrop = 1;
                return 0;
                return 0;
        }
        }
 
 
        /* Must not be a fragment. */
        /* Must not be a fragment. */
        return !offset
        return !offset
                && icmp6_type_code_match(icmpinfo->type,
                && icmp6_type_code_match(icmpinfo->type,
                                        icmpinfo->code[0],
                                        icmpinfo->code[0],
                                        icmpinfo->code[1],
                                        icmpinfo->code[1],
                                        icmp->icmp6_type, icmp->icmp6_code,
                                        icmp->icmp6_type, icmp->icmp6_code,
                                        !!(icmpinfo->invflags&IP6T_ICMP_INV));
                                        !!(icmpinfo->invflags&IP6T_ICMP_INV));
}
}
 
 
/* Called when user tries to insert an entry of this type. */
/* Called when user tries to insert an entry of this type. */
static int
static int
icmp6_checkentry(const char *tablename,
icmp6_checkentry(const char *tablename,
           const struct ip6t_ip6 *ipv6,
           const struct ip6t_ip6 *ipv6,
           void *matchinfo,
           void *matchinfo,
           unsigned int matchsize,
           unsigned int matchsize,
           unsigned int hook_mask)
           unsigned int hook_mask)
{
{
        const struct ip6t_icmp *icmpinfo = matchinfo;
        const struct ip6t_icmp *icmpinfo = matchinfo;
 
 
        /* Must specify proto == ICMP, and no unknown invflags */
        /* Must specify proto == ICMP, and no unknown invflags */
        return ipv6->proto == IPPROTO_ICMPV6
        return ipv6->proto == IPPROTO_ICMPV6
                && !(ipv6->invflags & IP6T_INV_PROTO)
                && !(ipv6->invflags & IP6T_INV_PROTO)
                && matchsize == IP6T_ALIGN(sizeof(struct ip6t_icmp))
                && matchsize == IP6T_ALIGN(sizeof(struct ip6t_icmp))
                && !(icmpinfo->invflags & ~IP6T_ICMP_INV);
                && !(icmpinfo->invflags & ~IP6T_ICMP_INV);
}
}
 
 
/* The built-in targets: standard (NULL) and error. */
/* The built-in targets: standard (NULL) and error. */
static struct ip6t_target ip6t_standard_target
static struct ip6t_target ip6t_standard_target
= { { NULL, NULL }, IP6T_STANDARD_TARGET, NULL, NULL, NULL };
= { { NULL, NULL }, IP6T_STANDARD_TARGET, NULL, NULL, NULL };
static struct ip6t_target ip6t_error_target
static struct ip6t_target ip6t_error_target
= { { NULL, NULL }, IP6T_ERROR_TARGET, ip6t_error, NULL, NULL };
= { { NULL, NULL }, IP6T_ERROR_TARGET, ip6t_error, NULL, NULL };
 
 
static struct nf_sockopt_ops ip6t_sockopts
static struct nf_sockopt_ops ip6t_sockopts
= { { NULL, NULL }, PF_INET6, IP6T_BASE_CTL, IP6T_SO_SET_MAX+1, do_ip6t_set_ctl,
= { { NULL, NULL }, PF_INET6, IP6T_BASE_CTL, IP6T_SO_SET_MAX+1, do_ip6t_set_ctl,
    IP6T_BASE_CTL, IP6T_SO_GET_MAX+1, do_ip6t_get_ctl, 0, NULL  };
    IP6T_BASE_CTL, IP6T_SO_GET_MAX+1, do_ip6t_get_ctl, 0, NULL  };
 
 
static struct ip6t_match tcp_matchstruct
static struct ip6t_match tcp_matchstruct
= { { NULL, NULL }, "tcp", &tcp_match, &tcp_checkentry, NULL };
= { { NULL, NULL }, "tcp", &tcp_match, &tcp_checkentry, NULL };
static struct ip6t_match udp_matchstruct
static struct ip6t_match udp_matchstruct
= { { NULL, NULL }, "udp", &udp_match, &udp_checkentry, NULL };
= { { NULL, NULL }, "udp", &udp_match, &udp_checkentry, NULL };
static struct ip6t_match icmp6_matchstruct
static struct ip6t_match icmp6_matchstruct
= { { NULL, NULL }, "icmp6", &icmp6_match, &icmp6_checkentry, NULL };
= { { NULL, NULL }, "icmp6", &icmp6_match, &icmp6_checkentry, NULL };
 
 
#ifdef CONFIG_PROC_FS
#ifdef CONFIG_PROC_FS
static inline int print_name(const char *i,
static inline int print_name(const char *i,
                             off_t start_offset, char *buffer, int length,
                             off_t start_offset, char *buffer, int length,
                             off_t *pos, unsigned int *count)
                             off_t *pos, unsigned int *count)
{
{
        if ((*count)++ >= start_offset) {
        if ((*count)++ >= start_offset) {
                unsigned int namelen;
                unsigned int namelen;
 
 
                namelen = sprintf(buffer + *pos, "%s\n",
                namelen = sprintf(buffer + *pos, "%s\n",
                                  i + sizeof(struct list_head));
                                  i + sizeof(struct list_head));
                if (*pos + namelen > length) {
                if (*pos + namelen > length) {
                        /* Stop iterating */
                        /* Stop iterating */
                        return 1;
                        return 1;
                }
                }
                *pos += namelen;
                *pos += namelen;
        }
        }
        return 0;
        return 0;
}
}
 
 
static int ip6t_get_tables(char *buffer, char **start, off_t offset, int length)
static int ip6t_get_tables(char *buffer, char **start, off_t offset, int length)
{
{
        off_t pos = 0;
        off_t pos = 0;
        unsigned int count = 0;
        unsigned int count = 0;
 
 
        if (down_interruptible(&ip6t_mutex) != 0)
        if (down_interruptible(&ip6t_mutex) != 0)
                return 0;
                return 0;
 
 
        LIST_FIND(&ip6t_tables, print_name, char *,
        LIST_FIND(&ip6t_tables, print_name, char *,
                  offset, buffer, length, &pos, &count);
                  offset, buffer, length, &pos, &count);
 
 
        up(&ip6t_mutex);
        up(&ip6t_mutex);
 
 
        /* `start' hack - see fs/proc/generic.c line ~105 */
        /* `start' hack - see fs/proc/generic.c line ~105 */
        *start=(char *)((unsigned long)count-offset);
        *start=(char *)((unsigned long)count-offset);
        return pos;
        return pos;
}
}
 
 
static int ip6t_get_targets(char *buffer, char **start, off_t offset, int length)
static int ip6t_get_targets(char *buffer, char **start, off_t offset, int length)
{
{
        off_t pos = 0;
        off_t pos = 0;
        unsigned int count = 0;
        unsigned int count = 0;
 
 
        if (down_interruptible(&ip6t_mutex) != 0)
        if (down_interruptible(&ip6t_mutex) != 0)
                return 0;
                return 0;
 
 
        LIST_FIND(&ip6t_target, print_name, char *,
        LIST_FIND(&ip6t_target, print_name, char *,
                  offset, buffer, length, &pos, &count);
                  offset, buffer, length, &pos, &count);
 
 
        up(&ip6t_mutex);
        up(&ip6t_mutex);
 
 
        *start = (char *)((unsigned long)count - offset);
        *start = (char *)((unsigned long)count - offset);
        return pos;
        return pos;
}
}
 
 
static int ip6t_get_matches(char *buffer, char **start, off_t offset, int length)
static int ip6t_get_matches(char *buffer, char **start, off_t offset, int length)
{
{
        off_t pos = 0;
        off_t pos = 0;
        unsigned int count = 0;
        unsigned int count = 0;
 
 
        if (down_interruptible(&ip6t_mutex) != 0)
        if (down_interruptible(&ip6t_mutex) != 0)
                return 0;
                return 0;
 
 
        LIST_FIND(&ip6t_match, print_name, char *,
        LIST_FIND(&ip6t_match, print_name, char *,
                  offset, buffer, length, &pos, &count);
                  offset, buffer, length, &pos, &count);
 
 
        up(&ip6t_mutex);
        up(&ip6t_mutex);
 
 
        *start = (char *)((unsigned long)count - offset);
        *start = (char *)((unsigned long)count - offset);
        return pos;
        return pos;
}
}
 
 
static struct { char *name; get_info_t *get_info; } ip6t_proc_entry[] =
static struct { char *name; get_info_t *get_info; } ip6t_proc_entry[] =
{ { "ip6_tables_names", ip6t_get_tables },
{ { "ip6_tables_names", ip6t_get_tables },
  { "ip6_tables_targets", ip6t_get_targets },
  { "ip6_tables_targets", ip6t_get_targets },
  { "ip6_tables_matches", ip6t_get_matches },
  { "ip6_tables_matches", ip6t_get_matches },
  { NULL, NULL} };
  { NULL, NULL} };
#endif /*CONFIG_PROC_FS*/
#endif /*CONFIG_PROC_FS*/
 
 
static int __init init(void)
static int __init init(void)
{
{
        int ret;
        int ret;
 
 
        /* Noone else will be downing sem now, so we won't sleep */
        /* Noone else will be downing sem now, so we won't sleep */
        down(&ip6t_mutex);
        down(&ip6t_mutex);
        list_append(&ip6t_target, &ip6t_standard_target);
        list_append(&ip6t_target, &ip6t_standard_target);
        list_append(&ip6t_target, &ip6t_error_target);
        list_append(&ip6t_target, &ip6t_error_target);
        list_append(&ip6t_match, &tcp_matchstruct);
        list_append(&ip6t_match, &tcp_matchstruct);
        list_append(&ip6t_match, &udp_matchstruct);
        list_append(&ip6t_match, &udp_matchstruct);
        list_append(&ip6t_match, &icmp6_matchstruct);
        list_append(&ip6t_match, &icmp6_matchstruct);
        up(&ip6t_mutex);
        up(&ip6t_mutex);
 
 
        /* Register setsockopt */
        /* Register setsockopt */
        ret = nf_register_sockopt(&ip6t_sockopts);
        ret = nf_register_sockopt(&ip6t_sockopts);
        if (ret < 0) {
        if (ret < 0) {
                duprintf("Unable to register sockopts.\n");
                duprintf("Unable to register sockopts.\n");
                return ret;
                return ret;
        }
        }
 
 
#ifdef CONFIG_PROC_FS
#ifdef CONFIG_PROC_FS
        {
        {
                struct proc_dir_entry *proc;
                struct proc_dir_entry *proc;
                int i;
                int i;
 
 
                for (i = 0; ip6t_proc_entry[i].name; i++) {
                for (i = 0; ip6t_proc_entry[i].name; i++) {
                        proc = proc_net_create(ip6t_proc_entry[i].name, 0,
                        proc = proc_net_create(ip6t_proc_entry[i].name, 0,
                                               ip6t_proc_entry[i].get_info);
                                               ip6t_proc_entry[i].get_info);
                        if (!proc) {
                        if (!proc) {
                                while (--i >= 0)
                                while (--i >= 0)
                                       proc_net_remove(ip6t_proc_entry[i].name);
                                       proc_net_remove(ip6t_proc_entry[i].name);
                                nf_unregister_sockopt(&ip6t_sockopts);
                                nf_unregister_sockopt(&ip6t_sockopts);
                                return -ENOMEM;
                                return -ENOMEM;
                        }
                        }
                        proc->owner = THIS_MODULE;
                        proc->owner = THIS_MODULE;
                }
                }
        }
        }
#endif
#endif
 
 
        printk("ip6_tables: (C) 2000-2002 Netfilter core team\n");
        printk("ip6_tables: (C) 2000-2002 Netfilter core team\n");
        return 0;
        return 0;
}
}
 
 
static void __exit fini(void)
static void __exit fini(void)
{
{
        nf_unregister_sockopt(&ip6t_sockopts);
        nf_unregister_sockopt(&ip6t_sockopts);
#ifdef CONFIG_PROC_FS
#ifdef CONFIG_PROC_FS
        {
        {
                int i;
                int i;
                for (i = 0; ip6t_proc_entry[i].name; i++)
                for (i = 0; ip6t_proc_entry[i].name; i++)
                        proc_net_remove(ip6t_proc_entry[i].name);
                        proc_net_remove(ip6t_proc_entry[i].name);
        }
        }
#endif
#endif
}
}
 
 
EXPORT_SYMBOL(ip6t_register_table);
EXPORT_SYMBOL(ip6t_register_table);
EXPORT_SYMBOL(ip6t_unregister_table);
EXPORT_SYMBOL(ip6t_unregister_table);
EXPORT_SYMBOL(ip6t_do_table);
EXPORT_SYMBOL(ip6t_do_table);
EXPORT_SYMBOL(ip6t_register_match);
EXPORT_SYMBOL(ip6t_register_match);
EXPORT_SYMBOL(ip6t_unregister_match);
EXPORT_SYMBOL(ip6t_unregister_match);
EXPORT_SYMBOL(ip6t_register_target);
EXPORT_SYMBOL(ip6t_register_target);
EXPORT_SYMBOL(ip6t_unregister_target);
EXPORT_SYMBOL(ip6t_unregister_target);
EXPORT_SYMBOL(ip6t_ext_hdr);
EXPORT_SYMBOL(ip6t_ext_hdr);
 
 
module_init(init);
module_init(init);
module_exit(fini);
module_exit(fini);
MODULE_LICENSE("GPL");
MODULE_LICENSE("GPL");
 
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.