[NETFILTER] NAT: Fix module refcount dropping too far
[linux-2.6] / net / ipv4 / netfilter / ipt_physdev.c
1 /* Kernel module to match the bridge port in and
2  * out device for IP packets coming into contact with a bridge. */
3
4 /* (C) 2001-2003 Bart De Schuymer <bdschuym@pandora.be>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  */
10
11 #include <linux/module.h>
12 #include <linux/skbuff.h>
13 #include <linux/netfilter_ipv4/ipt_physdev.h>
14 #include <linux/netfilter_ipv4/ip_tables.h>
15 #include <linux/netfilter_bridge.h>
16 #define MATCH   1
17 #define NOMATCH 0
18
19 MODULE_LICENSE("GPL");
20 MODULE_AUTHOR("Bart De Schuymer <bdschuym@pandora.be>");
21 MODULE_DESCRIPTION("iptables bridge physical device match module");
22
23 static int
24 match(const struct sk_buff *skb,
25       const struct net_device *in,
26       const struct net_device *out,
27       const void *matchinfo,
28       int offset,
29       int *hotdrop)
30 {
31         int i;
32         static const char nulldevname[IFNAMSIZ];
33         const struct ipt_physdev_info *info = matchinfo;
34         unsigned int ret;
35         const char *indev, *outdev;
36         struct nf_bridge_info *nf_bridge;
37
38         /* Not a bridged IP packet or no info available yet:
39          * LOCAL_OUT/mangle and LOCAL_OUT/nat don't know if
40          * the destination device will be a bridge. */
41         if (!(nf_bridge = skb->nf_bridge)) {
42                 /* Return MATCH if the invert flags of the used options are on */
43                 if ((info->bitmask & IPT_PHYSDEV_OP_BRIDGED) &&
44                     !(info->invert & IPT_PHYSDEV_OP_BRIDGED))
45                         return NOMATCH;
46                 if ((info->bitmask & IPT_PHYSDEV_OP_ISIN) &&
47                     !(info->invert & IPT_PHYSDEV_OP_ISIN))
48                         return NOMATCH;
49                 if ((info->bitmask & IPT_PHYSDEV_OP_ISOUT) &&
50                     !(info->invert & IPT_PHYSDEV_OP_ISOUT))
51                         return NOMATCH;
52                 if ((info->bitmask & IPT_PHYSDEV_OP_IN) &&
53                     !(info->invert & IPT_PHYSDEV_OP_IN))
54                         return NOMATCH;
55                 if ((info->bitmask & IPT_PHYSDEV_OP_OUT) &&
56                     !(info->invert & IPT_PHYSDEV_OP_OUT))
57                         return NOMATCH;
58                 return MATCH;
59         }
60
61         /* This only makes sense in the FORWARD and POSTROUTING chains */
62         if ((info->bitmask & IPT_PHYSDEV_OP_BRIDGED) &&
63             (!!(nf_bridge->mask & BRNF_BRIDGED) ^
64             !(info->invert & IPT_PHYSDEV_OP_BRIDGED)))
65                 return NOMATCH;
66
67         if ((info->bitmask & IPT_PHYSDEV_OP_ISIN &&
68             (!nf_bridge->physindev ^ !!(info->invert & IPT_PHYSDEV_OP_ISIN))) ||
69             (info->bitmask & IPT_PHYSDEV_OP_ISOUT &&
70             (!nf_bridge->physoutdev ^ !!(info->invert & IPT_PHYSDEV_OP_ISOUT))))
71                 return NOMATCH;
72
73         if (!(info->bitmask & IPT_PHYSDEV_OP_IN))
74                 goto match_outdev;
75         indev = nf_bridge->physindev ? nf_bridge->physindev->name : nulldevname;
76         for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned int); i++) {
77                 ret |= (((const unsigned int *)indev)[i]
78                         ^ ((const unsigned int *)info->physindev)[i])
79                         & ((const unsigned int *)info->in_mask)[i];
80         }
81
82         if ((ret == 0) ^ !(info->invert & IPT_PHYSDEV_OP_IN))
83                 return NOMATCH;
84
85 match_outdev:
86         if (!(info->bitmask & IPT_PHYSDEV_OP_OUT))
87                 return MATCH;
88         outdev = nf_bridge->physoutdev ?
89                  nf_bridge->physoutdev->name : nulldevname;
90         for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned int); i++) {
91                 ret |= (((const unsigned int *)outdev)[i]
92                         ^ ((const unsigned int *)info->physoutdev)[i])
93                         & ((const unsigned int *)info->out_mask)[i];
94         }
95
96         return (ret != 0) ^ !(info->invert & IPT_PHYSDEV_OP_OUT);
97 }
98
99 static int
100 checkentry(const char *tablename,
101                        const struct ipt_ip *ip,
102                        void *matchinfo,
103                        unsigned int matchsize,
104                        unsigned int hook_mask)
105 {
106         const struct ipt_physdev_info *info = matchinfo;
107
108         if (matchsize != IPT_ALIGN(sizeof(struct ipt_physdev_info)))
109                 return 0;
110         if (!(info->bitmask & IPT_PHYSDEV_OP_MASK) ||
111             info->bitmask & ~IPT_PHYSDEV_OP_MASK)
112                 return 0;
113         return 1;
114 }
115
116 static struct ipt_match physdev_match = {
117         .name           = "physdev",
118         .match          = &match,
119         .checkentry     = &checkentry,
120         .me             = THIS_MODULE,
121 };
122
123 static int __init init(void)
124 {
125         return ipt_register_match(&physdev_match);
126 }
127
128 static void __exit fini(void)
129 {
130         ipt_unregister_match(&physdev_match);
131 }
132
133 module_init(init);
134 module_exit(fini);