Merge master.kernel.org:/pub/scm/linux/kernel/git/gregkh/driver-2.6
[linux-2.6] / net / netfilter / xt_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/xt_physdev.h>
14 #include <linux/netfilter/x_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 MODULE_ALIAS("ipt_physdev");
23 MODULE_ALIAS("ip6t_physdev");
24
25 static int
26 match(const struct sk_buff *skb,
27       const struct net_device *in,
28       const struct net_device *out,
29       const void *matchinfo,
30       int offset,
31       unsigned int protoff,
32       int *hotdrop)
33 {
34         int i;
35         static const char nulldevname[IFNAMSIZ];
36         const struct xt_physdev_info *info = matchinfo;
37         unsigned int ret;
38         const char *indev, *outdev;
39         struct nf_bridge_info *nf_bridge;
40
41         /* Not a bridged IP packet or no info available yet:
42          * LOCAL_OUT/mangle and LOCAL_OUT/nat don't know if
43          * the destination device will be a bridge. */
44         if (!(nf_bridge = skb->nf_bridge)) {
45                 /* Return MATCH if the invert flags of the used options are on */
46                 if ((info->bitmask & XT_PHYSDEV_OP_BRIDGED) &&
47                     !(info->invert & XT_PHYSDEV_OP_BRIDGED))
48                         return NOMATCH;
49                 if ((info->bitmask & XT_PHYSDEV_OP_ISIN) &&
50                     !(info->invert & XT_PHYSDEV_OP_ISIN))
51                         return NOMATCH;
52                 if ((info->bitmask & XT_PHYSDEV_OP_ISOUT) &&
53                     !(info->invert & XT_PHYSDEV_OP_ISOUT))
54                         return NOMATCH;
55                 if ((info->bitmask & XT_PHYSDEV_OP_IN) &&
56                     !(info->invert & XT_PHYSDEV_OP_IN))
57                         return NOMATCH;
58                 if ((info->bitmask & XT_PHYSDEV_OP_OUT) &&
59                     !(info->invert & XT_PHYSDEV_OP_OUT))
60                         return NOMATCH;
61                 return MATCH;
62         }
63
64         /* This only makes sense in the FORWARD and POSTROUTING chains */
65         if ((info->bitmask & XT_PHYSDEV_OP_BRIDGED) &&
66             (!!(nf_bridge->mask & BRNF_BRIDGED) ^
67             !(info->invert & XT_PHYSDEV_OP_BRIDGED)))
68                 return NOMATCH;
69
70         if ((info->bitmask & XT_PHYSDEV_OP_ISIN &&
71             (!nf_bridge->physindev ^ !!(info->invert & XT_PHYSDEV_OP_ISIN))) ||
72             (info->bitmask & XT_PHYSDEV_OP_ISOUT &&
73             (!nf_bridge->physoutdev ^ !!(info->invert & XT_PHYSDEV_OP_ISOUT))))
74                 return NOMATCH;
75
76         if (!(info->bitmask & XT_PHYSDEV_OP_IN))
77                 goto match_outdev;
78         indev = nf_bridge->physindev ? nf_bridge->physindev->name : nulldevname;
79         for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned int); i++) {
80                 ret |= (((const unsigned int *)indev)[i]
81                         ^ ((const unsigned int *)info->physindev)[i])
82                         & ((const unsigned int *)info->in_mask)[i];
83         }
84
85         if ((ret == 0) ^ !(info->invert & XT_PHYSDEV_OP_IN))
86                 return NOMATCH;
87
88 match_outdev:
89         if (!(info->bitmask & XT_PHYSDEV_OP_OUT))
90                 return MATCH;
91         outdev = nf_bridge->physoutdev ?
92                  nf_bridge->physoutdev->name : nulldevname;
93         for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned int); i++) {
94                 ret |= (((const unsigned int *)outdev)[i]
95                         ^ ((const unsigned int *)info->physoutdev)[i])
96                         & ((const unsigned int *)info->out_mask)[i];
97         }
98
99         return (ret != 0) ^ !(info->invert & XT_PHYSDEV_OP_OUT);
100 }
101
102 static int
103 checkentry(const char *tablename,
104                        const void *ip,
105                        void *matchinfo,
106                        unsigned int matchsize,
107                        unsigned int hook_mask)
108 {
109         const struct xt_physdev_info *info = matchinfo;
110
111         if (matchsize != XT_ALIGN(sizeof(struct xt_physdev_info)))
112                 return 0;
113         if (!(info->bitmask & XT_PHYSDEV_OP_MASK) ||
114             info->bitmask & ~XT_PHYSDEV_OP_MASK)
115                 return 0;
116         return 1;
117 }
118
119 static struct xt_match physdev_match = {
120         .name           = "physdev",
121         .match          = &match,
122         .checkentry     = &checkentry,
123         .me             = THIS_MODULE,
124 };
125
126 static struct xt_match physdev6_match = {
127         .name           = "physdev",
128         .match          = &match,
129         .checkentry     = &checkentry,
130         .me             = THIS_MODULE,
131 };
132
133 static int __init init(void)
134 {
135         int ret;
136
137         ret = xt_register_match(AF_INET, &physdev_match);
138         if (ret < 0)
139                 return ret;
140
141         ret = xt_register_match(AF_INET6, &physdev6_match);
142         if (ret < 0)
143                 xt_unregister_match(AF_INET, &physdev_match);
144
145         return ret;
146 }
147
148 static void __exit fini(void)
149 {
150         xt_unregister_match(AF_INET, &physdev_match);
151         xt_unregister_match(AF_INET6, &physdev6_match);
152 }
153
154 module_init(init);
155 module_exit(fini);