fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / net / ipv4 / netfilter / ip_nat_snmp_basic.c
index 32d6e96..c3d9f3b 100644 (file)
  * 2000-08-06: Convert to new helper API (Harald Welte).
  *
  */
-#include <linux/config.h>
+#include <linux/in.h>
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/moduleparam.h>
 #include <linux/netfilter_ipv4.h>
 #include <linux/netfilter_ipv4/ip_nat.h>
+#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
 #include <linux/netfilter_ipv4/ip_nat_helper.h>
 #include <linux/ip.h>
+#include <linux/udp.h>
 #include <net/checksum.h>
 #include <net/udp.h>
 #include <asm/uaccess.h>
@@ -62,10 +64,10 @@ MODULE_DESCRIPTION("Basic SNMP Application Layer Gateway");
 
 #define SNMP_PORT 161
 #define SNMP_TRAP_PORT 162
-#define NOCT1(n) (u_int8_t )((n) & 0xff)
+#define NOCT1(n) (*(u8 *)n)
 
 static int debug;
-static spinlock_t snmp_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(snmp_lock);
 
 /* 
  * Application layer address mapping mimics the NAT mapping, but 
@@ -247,6 +249,7 @@ static unsigned char asn1_header_decode(struct asn1_ctx *ctx,
        if (!asn1_id_decode(ctx, cls, con, tag))
                return 0;
                
+       def = len = 0;
        if (!asn1_length_decode(ctx, &def, &len))
                return 0;
                
@@ -610,7 +613,7 @@ struct snmp_v1_trap
 static inline void mangle_address(unsigned char *begin,
                                   unsigned char *addr,
                                   const struct oct1_map *map,
-                                  u_int16_t *check);
+                                  __sum16 *check);
 struct snmp_cnv
 {
        unsigned int class;
@@ -666,7 +669,7 @@ static unsigned char snmp_object_decode(struct asn1_ctx *ctx,
        unsigned char *eoc, *end, *p;
        unsigned long *lp, *id;
        unsigned long ul;
-       long  l;
+       long l;
        
        *obj = NULL;
        id = NULL;
@@ -696,11 +699,13 @@ static unsigned char snmp_object_decode(struct asn1_ctx *ctx,
                return 0;
        }
        
+       type = 0;
        if (!snmp_tag_cls2syntax(tag, cls, &type)) {
                kfree(id);
                return 0;
        }
        
+       l = 0;
        switch (type) {
                case SNMP_INTEGER:
                        len = sizeof(long);
@@ -762,6 +767,7 @@ static unsigned char snmp_object_decode(struct asn1_ctx *ctx,
                        len *= sizeof(unsigned long);
                        *obj = kmalloc(sizeof(struct snmp_object) + len, GFP_ATOMIC);
                        if (*obj == NULL) {
+                               kfree(lp);
                                kfree(id);
                                if (net_ratelimit())
                                        printk("OOM in bsalg (%d)\n", __LINE__);
@@ -867,38 +873,24 @@ static unsigned char snmp_request_decode(struct asn1_ctx *ctx,
  * Fast checksum update for possibly oddly-aligned UDP byte, from the
  * code example in the draft.
  */
-static void fast_csum(unsigned char *csum,
+static void fast_csum(__sum16 *csum,
                       const unsigned char *optr,
                       const unsigned char *nptr,
-                      int odd)
+                      int offset)
 {
-       long x, old, new;
-       
-       x = csum[0] * 256 + csum[1];
-       
-       x =~ x & 0xFFFF;
-       
-       if (odd) old = optr[0] * 256;
-       else old = optr[0];
-       
-       x -= old & 0xFFFF;
-       if (x <= 0) {
-               x--;
-               x &= 0xFFFF;
-       }
-       
-       if (odd) new = nptr[0] * 256;
-       else new = nptr[0];
-       
-       x += new & 0xFFFF;
-       if (x & 0x10000) {
-               x++;
-               x &= 0xFFFF;
+       unsigned char s[4];
+
+       if (offset & 1) {
+               s[0] = s[2] = 0;
+               s[1] = ~*optr;
+               s[3] = *nptr;
+       } else {
+               s[1] = s[3] = 0;
+               s[0] = ~*optr;
+               s[2] = *nptr;
        }
-       
-       x =~ x & 0xFFFF;
-       csum[0] = x / 256;
-       csum[1] = x & 0xFF;
+
+       *csum = csum_fold(csum_partial(s, 4, ~csum_unfold(*csum)));
 }
 
 /* 
@@ -909,9 +901,9 @@ static void fast_csum(unsigned char *csum,
 static inline void mangle_address(unsigned char *begin,
                                   unsigned char *addr,
                                   const struct oct1_map *map,
-                                  u_int16_t *check)
+                                  __sum16 *check)
 {
-       if (map->from == NOCT1(*addr)) {
+       if (map->from == NOCT1(addr)) {
                u_int32_t old;
                
                if (debug)
@@ -921,11 +913,8 @@ static inline void mangle_address(unsigned char *begin,
                
                /* Update UDP checksum if being used */
                if (*check) {
-                       unsigned char odd = !((addr - begin) % 2);
-                       
-                       fast_csum((unsigned char *)check,
-                                 &map->from, &map->to, odd);
-                                 
+                       fast_csum(check,
+                                 &map->from, &map->to, addr - begin);
                }
                
                if (debug)
@@ -937,7 +926,7 @@ static inline void mangle_address(unsigned char *begin,
 static unsigned char snmp_trap_decode(struct asn1_ctx *ctx,
                                       struct snmp_v1_trap *trap,
                                       const struct oct1_map *map,
-                                      u_int16_t *check)
+                                      __sum16 *check)
 {
        unsigned int cls, con, tag, len;
        unsigned char *end;
@@ -997,12 +986,12 @@ static unsigned char snmp_trap_decode(struct asn1_ctx *ctx,
                
        return 1;
 
+err_addr_free:
+       kfree((unsigned long *)trap->ip_address);
+
 err_id_free:
        kfree(trap->id);
 
-err_addr_free:
-       kfree((unsigned long *)trap->ip_address);
-       
        return 0;
 }
 
@@ -1031,7 +1020,7 @@ static void hex_dump(unsigned char *buf, size_t len)
 static int snmp_parse_mangle(unsigned char *msg,
                              u_int16_t len,
                              const struct oct1_map *map,
-                             u_int16_t *check)
+                             __sum16 *check)
 {
        unsigned char *eoc, *end;
        unsigned int cls, con, tag, vers, pdutype;
@@ -1120,11 +1109,10 @@ static int snmp_parse_mangle(unsigned char *msg,
                struct snmp_v1_trap trap;
                unsigned char ret = snmp_trap_decode(&ctx, &trap, map, check);
                
-               /* Discard trap allocations regardless */
-               kfree(trap.id);
-               kfree((unsigned long *)trap.ip_address);
-               
-               if (!ret)
+               if (ret) {
+                       kfree(trap.id);
+                       kfree((unsigned long *)trap.ip_address);
+               } else 
                        return ret;
                
        } else {
@@ -1160,8 +1148,7 @@ static int snmp_parse_mangle(unsigned char *msg,
                
                if (!snmp_object_decode(&ctx, obj)) {
                        if (*obj) {
-                               if ((*obj)->id)
-                                       kfree((*obj)->id);
+                               kfree((*obj)->id);
                                kfree(*obj);
                        }       
                        kfree(obj);
@@ -1203,13 +1190,11 @@ static int snmp_parse_mangle(unsigned char *msg,
  * SNMP translation routine.
  */
 static int snmp_translate(struct ip_conntrack *ct,
-                          struct ip_nat_info *info,
                           enum ip_conntrack_info ctinfo,
-                          unsigned int hooknum,
                           struct sk_buff **pskb)
 {
        struct iphdr *iph = (*pskb)->nh.iph;
-       struct udphdr *udph = (struct udphdr *)((u_int32_t *)iph + iph->ihl);
+       struct udphdr *udph = (struct udphdr *)((__be32 *)iph + iph->ihl);
        u_int16_t udplen = ntohs(udph->len);
        u_int16_t paylen = udplen - sizeof(struct udphdr);
        int dir = CTINFO2DIR(ctinfo);
@@ -1221,12 +1206,12 @@ static int snmp_translate(struct ip_conntrack *ct,
         */
        if (dir == IP_CT_DIR_ORIGINAL) {
                /* SNAT traps */
-               map.from = NOCT1(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip);
-               map.to = NOCT1(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip);
+               map.from = NOCT1(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip);
+               map.to = NOCT1(&ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip);
        } else {
                /* DNAT replies */
-               map.from = NOCT1(ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip);
-               map.to = NOCT1(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip);
+               map.from = NOCT1(&ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip);
+               map.to = NOCT1(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip);
        }
        
        if (map.from == map.to)
@@ -1234,101 +1219,86 @@ static int snmp_translate(struct ip_conntrack *ct,
        
        if (!snmp_parse_mangle((unsigned char *)udph + sizeof(struct udphdr),
                               paylen, &map, &udph->check)) {
-               printk(KERN_WARNING "bsalg: parser failed\n");
+               if (net_ratelimit())
+                       printk(KERN_WARNING "bsalg: parser failed\n");
                return NF_DROP;
        }
        return NF_ACCEPT;
 }
 
-/* 
- * NAT helper function, packets arrive here from NAT code.
- */
-static unsigned int nat_help(struct ip_conntrack *ct,
-                            struct ip_conntrack_expect *exp,
-                             struct ip_nat_info *info,
-                             enum ip_conntrack_info ctinfo,
-                             unsigned int hooknum,
-                             struct sk_buff **pskb)
+/* We don't actually set up expectations, just adjust internal IP
+ * addresses if this is being NATted */
+static int help(struct sk_buff **pskb,
+               struct ip_conntrack *ct,
+               enum ip_conntrack_info ctinfo)
 {
        int dir = CTINFO2DIR(ctinfo);
+       unsigned int ret;
        struct iphdr *iph = (*pskb)->nh.iph;
        struct udphdr *udph = (struct udphdr *)((u_int32_t *)iph + iph->ihl);
-       
-       if (!skb_ip_make_writable(pskb, (*pskb)->len))
-               return NF_DROP;
 
-       spin_lock_bh(&snmp_lock);
-       
-       /*
-        * Translate snmp replies on pre-routing (DNAT) and snmp traps
-        * on post routing (SNAT).
-        */
-       if (!((dir == IP_CT_DIR_REPLY && hooknum == NF_IP_PRE_ROUTING &&
-                       udph->source == ntohs(SNMP_PORT)) ||
-             (dir == IP_CT_DIR_ORIGINAL && hooknum == NF_IP_POST_ROUTING &&
-                       udph->dest == ntohs(SNMP_TRAP_PORT)))) {
-               spin_unlock_bh(&snmp_lock);
+       /* SNMP replies and originating SNMP traps get mangled */
+       if (udph->source == htons(SNMP_PORT) && dir != IP_CT_DIR_REPLY)
+               return NF_ACCEPT;
+       if (udph->dest == htons(SNMP_TRAP_PORT) && dir != IP_CT_DIR_ORIGINAL)
+               return NF_ACCEPT;
+
+       /* No NAT? */
+       if (!(ct->status & IPS_NAT_MASK))
                return NF_ACCEPT;
-       }
 
-       if (debug > 1) {
-               printk(KERN_DEBUG "bsalg: dir=%s hook=%d manip=%s len=%d "
-                      "src=%u.%u.%u.%u:%u dst=%u.%u.%u.%u:%u "
-                      "osrc=%u.%u.%u.%u odst=%u.%u.%u.%u "
-                      "rsrc=%u.%u.%u.%u rdst=%u.%u.%u.%u "
-                      "\n", 
-                      dir == IP_CT_DIR_REPLY ? "reply" : "orig", hooknum, 
-                      HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC ? "snat" :
-                      "dnat", (*pskb)->len,
-                      NIPQUAD(iph->saddr), ntohs(udph->source),
-                      NIPQUAD(iph->daddr), ntohs(udph->dest),
-                      NIPQUAD(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip),
-                      NIPQUAD(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip),
-                      NIPQUAD(ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip),
-                      NIPQUAD(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip));
-       }
-       
        /* 
         * Make sure the packet length is ok.  So far, we were only guaranteed
         * to have a valid length IP header plus 8 bytes, which means we have
         * enough room for a UDP header.  Just verify the UDP length field so we
         * can mess around with the payload.
         */
-        if (ntohs(udph->len) == (*pskb)->len - (iph->ihl << 2)) {
-               int ret = snmp_translate(ct, info, ctinfo, hooknum, pskb);
-               spin_unlock_bh(&snmp_lock);
-               return ret;
+       if (ntohs(udph->len) != (*pskb)->len - (iph->ihl << 2)) {
+                if (net_ratelimit())
+                        printk(KERN_WARNING "SNMP: dropping malformed packet "
+                               "src=%u.%u.%u.%u dst=%u.%u.%u.%u\n",
+                               NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
+                return NF_DROP;
        }
-       
-       if (net_ratelimit())
-               printk(KERN_WARNING "bsalg: dropping malformed packet "
-                      "src=%u.%u.%u.%u dst=%u.%u.%u.%u\n",
-                      NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
+
+       if (!skb_make_writable(pskb, (*pskb)->len))
+               return NF_DROP;
+
+       spin_lock_bh(&snmp_lock);
+       ret = snmp_translate(ct, ctinfo, pskb);
        spin_unlock_bh(&snmp_lock);
-       return NF_DROP;
+       return ret;
 }
 
-static struct ip_nat_helper snmp = { 
-       { NULL, NULL },
-       "snmp",
-       0,
-       THIS_MODULE,
-       { { 0, { .udp = { __constant_htons(SNMP_PORT) } } },
-         { 0, { 0 }, IPPROTO_UDP } },
-       { { 0, { .udp = { 0xFFFF } } },
-         { 0, { 0 }, 0xFFFF } },
-       nat_help, NULL };
-static struct ip_nat_helper snmp_trap = { 
-       { NULL, NULL },
-       "snmp_trap",
-       0,
-       THIS_MODULE,
-       { { 0, { .udp = { __constant_htons(SNMP_TRAP_PORT) } } },
-         { 0, { 0 }, IPPROTO_UDP } },
-       { { 0, { .udp = { 0xFFFF } } },
-         { 0, { 0 }, 0xFFFF } },
-       nat_help, NULL };
+static struct ip_conntrack_helper snmp_helper = {
+       .max_expected = 0,
+       .timeout = 180,
+       .me = THIS_MODULE,
+       .help = help,
+       .name = "snmp",
+
+       .tuple = {.src = {.u = {.udp = {.port = __constant_htons(SNMP_PORT)}}},
+                 .dst = {.protonum = IPPROTO_UDP},
+       },
+       .mask = {.src = {.u = {0xFFFF}},
+                .dst = {.protonum = 0xFF},
+       },
+};
+
+static struct ip_conntrack_helper snmp_trap_helper = {
+       .max_expected = 0,
+       .timeout = 180,
+       .me = THIS_MODULE,
+       .help = help,
+       .name = "snmp_trap",
+
+       .tuple = {.src = {.u = {.udp = {.port = __constant_htons(SNMP_TRAP_PORT)}}},
+                 .dst = {.protonum = IPPROTO_UDP},
+       },
+       .mask = {.src = {.u = {0xFFFF}},
+                .dst = {.protonum = 0xFF},
+       },
+};
 
 /*****************************************************************************
  *
@@ -1336,29 +1306,28 @@ static struct ip_nat_helper snmp_trap = {
  *
  *****************************************************************************/
  
-static int __init init(void)
+static int __init ip_nat_snmp_basic_init(void)
 {
        int ret = 0;
 
-       ret = ip_nat_helper_register(&snmp);
+       ret = ip_conntrack_helper_register(&snmp_helper);
        if (ret < 0)
                return ret;
-       ret = ip_nat_helper_register(&snmp_trap);
+       ret = ip_conntrack_helper_register(&snmp_trap_helper);
        if (ret < 0) {
-               ip_nat_helper_unregister(&snmp);
+               ip_conntrack_helper_unregister(&snmp_helper);
                return ret;
        }
        return ret;
 }
 
-static void __exit fini(void)
+static void __exit ip_nat_snmp_basic_fini(void)
 {
-       ip_nat_helper_unregister(&snmp);
-       ip_nat_helper_unregister(&snmp_trap);
-       synchronize_net();
+       ip_conntrack_helper_unregister(&snmp_helper);
+       ip_conntrack_helper_unregister(&snmp_trap_helper);
 }
 
-module_init(init);
-module_exit(fini);
+module_init(ip_nat_snmp_basic_init);
+module_exit(ip_nat_snmp_basic_fini);
 
-module_param(debug, bool, 0600);
+module_param(debug, int, 0600);