diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /net/bridge |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'net/bridge')
40 files changed, 8982 insertions, 0 deletions
diff --git a/net/bridge/Makefile b/net/bridge/Makefile new file mode 100644 index 000000000000..59556e40e143 --- /dev/null +++ b/net/bridge/Makefile | |||
@@ -0,0 +1,15 @@ | |||
1 | # | ||
2 | # Makefile for the IEEE 802.1d ethernet bridging layer. | ||
3 | # | ||
4 | |||
5 | obj-$(CONFIG_BRIDGE) += bridge.o | ||
6 | |||
7 | bridge-y := br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \ | ||
8 | br_ioctl.o br_notify.o br_stp.o br_stp_bpdu.o \ | ||
9 | br_stp_if.o br_stp_timer.o | ||
10 | |||
11 | bridge-$(CONFIG_SYSFS) += br_sysfs_if.o br_sysfs_br.o | ||
12 | |||
13 | bridge-$(CONFIG_BRIDGE_NETFILTER) += br_netfilter.o | ||
14 | |||
15 | obj-$(CONFIG_BRIDGE_NF_EBTABLES) += netfilter/ | ||
diff --git a/net/bridge/br.c b/net/bridge/br.c new file mode 100644 index 000000000000..f8f184942aaf --- /dev/null +++ b/net/bridge/br.c | |||
@@ -0,0 +1,69 @@ | |||
1 | /* | ||
2 | * Generic parts | ||
3 | * Linux ethernet bridge | ||
4 | * | ||
5 | * Authors: | ||
6 | * Lennert Buytenhek <buytenh@gnu.org> | ||
7 | * | ||
8 | * $Id: br.c,v 1.47 2001/12/24 00:56:41 davem Exp $ | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or | ||
11 | * modify it under the terms of the GNU General Public License | ||
12 | * as published by the Free Software Foundation; either version | ||
13 | * 2 of the License, or (at your option) any later version. | ||
14 | */ | ||
15 | |||
16 | #include <linux/config.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/kernel.h> | ||
19 | #include <linux/netdevice.h> | ||
20 | #include <linux/etherdevice.h> | ||
21 | #include <linux/init.h> | ||
22 | |||
23 | #include "br_private.h" | ||
24 | |||
25 | int (*br_should_route_hook) (struct sk_buff **pskb) = NULL; | ||
26 | |||
27 | static int __init br_init(void) | ||
28 | { | ||
29 | br_fdb_init(); | ||
30 | |||
31 | #ifdef CONFIG_BRIDGE_NETFILTER | ||
32 | if (br_netfilter_init()) | ||
33 | return 1; | ||
34 | #endif | ||
35 | brioctl_set(br_ioctl_deviceless_stub); | ||
36 | br_handle_frame_hook = br_handle_frame; | ||
37 | |||
38 | br_fdb_get_hook = br_fdb_get; | ||
39 | br_fdb_put_hook = br_fdb_put; | ||
40 | |||
41 | register_netdevice_notifier(&br_device_notifier); | ||
42 | |||
43 | return 0; | ||
44 | } | ||
45 | |||
46 | static void __exit br_deinit(void) | ||
47 | { | ||
48 | #ifdef CONFIG_BRIDGE_NETFILTER | ||
49 | br_netfilter_fini(); | ||
50 | #endif | ||
51 | unregister_netdevice_notifier(&br_device_notifier); | ||
52 | brioctl_set(NULL); | ||
53 | |||
54 | br_cleanup_bridges(); | ||
55 | |||
56 | synchronize_net(); | ||
57 | |||
58 | br_fdb_get_hook = NULL; | ||
59 | br_fdb_put_hook = NULL; | ||
60 | |||
61 | br_handle_frame_hook = NULL; | ||
62 | br_fdb_fini(); | ||
63 | } | ||
64 | |||
65 | EXPORT_SYMBOL(br_should_route_hook); | ||
66 | |||
67 | module_init(br_init) | ||
68 | module_exit(br_deinit) | ||
69 | MODULE_LICENSE("GPL"); | ||
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c new file mode 100644 index 000000000000..d9b72fde433c --- /dev/null +++ b/net/bridge/br_device.c | |||
@@ -0,0 +1,104 @@ | |||
1 | /* | ||
2 | * Device handling code | ||
3 | * Linux ethernet bridge | ||
4 | * | ||
5 | * Authors: | ||
6 | * Lennert Buytenhek <buytenh@gnu.org> | ||
7 | * | ||
8 | * $Id: br_device.c,v 1.6 2001/12/24 00:59:55 davem Exp $ | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or | ||
11 | * modify it under the terms of the GNU General Public License | ||
12 | * as published by the Free Software Foundation; either version | ||
13 | * 2 of the License, or (at your option) any later version. | ||
14 | */ | ||
15 | |||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/netdevice.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <asm/uaccess.h> | ||
20 | #include "br_private.h" | ||
21 | |||
22 | static struct net_device_stats *br_dev_get_stats(struct net_device *dev) | ||
23 | { | ||
24 | struct net_bridge *br; | ||
25 | |||
26 | br = dev->priv; | ||
27 | |||
28 | return &br->statistics; | ||
29 | } | ||
30 | |||
31 | int br_dev_xmit(struct sk_buff *skb, struct net_device *dev) | ||
32 | { | ||
33 | struct net_bridge *br = netdev_priv(dev); | ||
34 | const unsigned char *dest = skb->data; | ||
35 | struct net_bridge_fdb_entry *dst; | ||
36 | |||
37 | br->statistics.tx_packets++; | ||
38 | br->statistics.tx_bytes += skb->len; | ||
39 | |||
40 | skb->mac.raw = skb->data; | ||
41 | skb_pull(skb, ETH_HLEN); | ||
42 | |||
43 | rcu_read_lock(); | ||
44 | if (dest[0] & 1) | ||
45 | br_flood_deliver(br, skb, 0); | ||
46 | else if ((dst = __br_fdb_get(br, dest)) != NULL) | ||
47 | br_deliver(dst->dst, skb); | ||
48 | else | ||
49 | br_flood_deliver(br, skb, 0); | ||
50 | |||
51 | rcu_read_unlock(); | ||
52 | return 0; | ||
53 | } | ||
54 | |||
55 | static int br_dev_open(struct net_device *dev) | ||
56 | { | ||
57 | netif_start_queue(dev); | ||
58 | |||
59 | br_stp_enable_bridge(dev->priv); | ||
60 | |||
61 | return 0; | ||
62 | } | ||
63 | |||
64 | static void br_dev_set_multicast_list(struct net_device *dev) | ||
65 | { | ||
66 | } | ||
67 | |||
68 | static int br_dev_stop(struct net_device *dev) | ||
69 | { | ||
70 | br_stp_disable_bridge(dev->priv); | ||
71 | |||
72 | netif_stop_queue(dev); | ||
73 | |||
74 | return 0; | ||
75 | } | ||
76 | |||
77 | static int br_change_mtu(struct net_device *dev, int new_mtu) | ||
78 | { | ||
79 | if ((new_mtu < 68) || new_mtu > br_min_mtu(dev->priv)) | ||
80 | return -EINVAL; | ||
81 | |||
82 | dev->mtu = new_mtu; | ||
83 | return 0; | ||
84 | } | ||
85 | |||
86 | void br_dev_setup(struct net_device *dev) | ||
87 | { | ||
88 | memset(dev->dev_addr, 0, ETH_ALEN); | ||
89 | |||
90 | ether_setup(dev); | ||
91 | |||
92 | dev->do_ioctl = br_dev_ioctl; | ||
93 | dev->get_stats = br_dev_get_stats; | ||
94 | dev->hard_start_xmit = br_dev_xmit; | ||
95 | dev->open = br_dev_open; | ||
96 | dev->set_multicast_list = br_dev_set_multicast_list; | ||
97 | dev->change_mtu = br_change_mtu; | ||
98 | dev->destructor = free_netdev; | ||
99 | SET_MODULE_OWNER(dev); | ||
100 | dev->stop = br_dev_stop; | ||
101 | dev->tx_queue_len = 0; | ||
102 | dev->set_mac_address = NULL; | ||
103 | dev->priv_flags = IFF_EBRIDGE; | ||
104 | } | ||
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c new file mode 100644 index 000000000000..e6c2200b7ca3 --- /dev/null +++ b/net/bridge/br_fdb.c | |||
@@ -0,0 +1,368 @@ | |||
1 | /* | ||
2 | * Forwarding database | ||
3 | * Linux ethernet bridge | ||
4 | * | ||
5 | * Authors: | ||
6 | * Lennert Buytenhek <buytenh@gnu.org> | ||
7 | * | ||
8 | * $Id: br_fdb.c,v 1.6 2002/01/17 00:57:07 davem Exp $ | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or | ||
11 | * modify it under the terms of the GNU General Public License | ||
12 | * as published by the Free Software Foundation; either version | ||
13 | * 2 of the License, or (at your option) any later version. | ||
14 | */ | ||
15 | |||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/spinlock.h> | ||
19 | #include <linux/times.h> | ||
20 | #include <linux/netdevice.h> | ||
21 | #include <linux/etherdevice.h> | ||
22 | #include <linux/jhash.h> | ||
23 | #include <asm/atomic.h> | ||
24 | #include "br_private.h" | ||
25 | |||
26 | static kmem_cache_t *br_fdb_cache; | ||
27 | static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, | ||
28 | const unsigned char *addr); | ||
29 | |||
30 | void __init br_fdb_init(void) | ||
31 | { | ||
32 | br_fdb_cache = kmem_cache_create("bridge_fdb_cache", | ||
33 | sizeof(struct net_bridge_fdb_entry), | ||
34 | 0, | ||
35 | SLAB_HWCACHE_ALIGN, NULL, NULL); | ||
36 | } | ||
37 | |||
38 | void __exit br_fdb_fini(void) | ||
39 | { | ||
40 | kmem_cache_destroy(br_fdb_cache); | ||
41 | } | ||
42 | |||
43 | |||
44 | /* if topology_changing then use forward_delay (default 15 sec) | ||
45 | * otherwise keep longer (default 5 minutes) | ||
46 | */ | ||
47 | static __inline__ unsigned long hold_time(const struct net_bridge *br) | ||
48 | { | ||
49 | return br->topology_change ? br->forward_delay : br->ageing_time; | ||
50 | } | ||
51 | |||
52 | static __inline__ int has_expired(const struct net_bridge *br, | ||
53 | const struct net_bridge_fdb_entry *fdb) | ||
54 | { | ||
55 | return !fdb->is_static | ||
56 | && time_before_eq(fdb->ageing_timer + hold_time(br), jiffies); | ||
57 | } | ||
58 | |||
59 | static __inline__ int br_mac_hash(const unsigned char *mac) | ||
60 | { | ||
61 | return jhash(mac, ETH_ALEN, 0) & (BR_HASH_SIZE - 1); | ||
62 | } | ||
63 | |||
64 | static __inline__ void fdb_delete(struct net_bridge_fdb_entry *f) | ||
65 | { | ||
66 | hlist_del_rcu(&f->hlist); | ||
67 | br_fdb_put(f); | ||
68 | } | ||
69 | |||
70 | void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr) | ||
71 | { | ||
72 | struct net_bridge *br = p->br; | ||
73 | int i; | ||
74 | |||
75 | spin_lock_bh(&br->hash_lock); | ||
76 | |||
77 | /* Search all chains since old address/hash is unknown */ | ||
78 | for (i = 0; i < BR_HASH_SIZE; i++) { | ||
79 | struct hlist_node *h; | ||
80 | hlist_for_each(h, &br->hash[i]) { | ||
81 | struct net_bridge_fdb_entry *f; | ||
82 | |||
83 | f = hlist_entry(h, struct net_bridge_fdb_entry, hlist); | ||
84 | if (f->dst == p && f->is_local) { | ||
85 | /* maybe another port has same hw addr? */ | ||
86 | struct net_bridge_port *op; | ||
87 | list_for_each_entry(op, &br->port_list, list) { | ||
88 | if (op != p && | ||
89 | !memcmp(op->dev->dev_addr, | ||
90 | f->addr.addr, ETH_ALEN)) { | ||
91 | f->dst = op; | ||
92 | goto insert; | ||
93 | } | ||
94 | } | ||
95 | |||
96 | /* delete old one */ | ||
97 | fdb_delete(f); | ||
98 | goto insert; | ||
99 | } | ||
100 | } | ||
101 | } | ||
102 | insert: | ||
103 | /* insert new address, may fail if invalid address or dup. */ | ||
104 | fdb_insert(br, p, newaddr); | ||
105 | |||
106 | spin_unlock_bh(&br->hash_lock); | ||
107 | } | ||
108 | |||
109 | void br_fdb_cleanup(unsigned long _data) | ||
110 | { | ||
111 | struct net_bridge *br = (struct net_bridge *)_data; | ||
112 | unsigned long delay = hold_time(br); | ||
113 | int i; | ||
114 | |||
115 | spin_lock_bh(&br->hash_lock); | ||
116 | for (i = 0; i < BR_HASH_SIZE; i++) { | ||
117 | struct net_bridge_fdb_entry *f; | ||
118 | struct hlist_node *h, *n; | ||
119 | |||
120 | hlist_for_each_entry_safe(f, h, n, &br->hash[i], hlist) { | ||
121 | if (!f->is_static && | ||
122 | time_before_eq(f->ageing_timer + delay, jiffies)) | ||
123 | fdb_delete(f); | ||
124 | } | ||
125 | } | ||
126 | spin_unlock_bh(&br->hash_lock); | ||
127 | |||
128 | mod_timer(&br->gc_timer, jiffies + HZ/10); | ||
129 | } | ||
130 | |||
131 | void br_fdb_delete_by_port(struct net_bridge *br, struct net_bridge_port *p) | ||
132 | { | ||
133 | int i; | ||
134 | |||
135 | spin_lock_bh(&br->hash_lock); | ||
136 | for (i = 0; i < BR_HASH_SIZE; i++) { | ||
137 | struct hlist_node *h, *g; | ||
138 | |||
139 | hlist_for_each_safe(h, g, &br->hash[i]) { | ||
140 | struct net_bridge_fdb_entry *f | ||
141 | = hlist_entry(h, struct net_bridge_fdb_entry, hlist); | ||
142 | if (f->dst != p) | ||
143 | continue; | ||
144 | |||
145 | /* | ||
146 | * if multiple ports all have the same device address | ||
147 | * then when one port is deleted, assign | ||
148 | * the local entry to other port | ||
149 | */ | ||
150 | if (f->is_local) { | ||
151 | struct net_bridge_port *op; | ||
152 | list_for_each_entry(op, &br->port_list, list) { | ||
153 | if (op != p && | ||
154 | !memcmp(op->dev->dev_addr, | ||
155 | f->addr.addr, ETH_ALEN)) { | ||
156 | f->dst = op; | ||
157 | goto skip_delete; | ||
158 | } | ||
159 | } | ||
160 | } | ||
161 | |||
162 | fdb_delete(f); | ||
163 | skip_delete: ; | ||
164 | } | ||
165 | } | ||
166 | spin_unlock_bh(&br->hash_lock); | ||
167 | } | ||
168 | |||
169 | /* No locking or refcounting, assumes caller has no preempt (rcu_read_lock) */ | ||
170 | struct net_bridge_fdb_entry *__br_fdb_get(struct net_bridge *br, | ||
171 | const unsigned char *addr) | ||
172 | { | ||
173 | struct hlist_node *h; | ||
174 | struct net_bridge_fdb_entry *fdb; | ||
175 | |||
176 | hlist_for_each_entry_rcu(fdb, h, &br->hash[br_mac_hash(addr)], hlist) { | ||
177 | if (!memcmp(fdb->addr.addr, addr, ETH_ALEN)) { | ||
178 | if (unlikely(has_expired(br, fdb))) | ||
179 | break; | ||
180 | return fdb; | ||
181 | } | ||
182 | } | ||
183 | |||
184 | return NULL; | ||
185 | } | ||
186 | |||
187 | /* Interface used by ATM hook that keeps a ref count */ | ||
188 | struct net_bridge_fdb_entry *br_fdb_get(struct net_bridge *br, | ||
189 | unsigned char *addr) | ||
190 | { | ||
191 | struct net_bridge_fdb_entry *fdb; | ||
192 | |||
193 | rcu_read_lock(); | ||
194 | fdb = __br_fdb_get(br, addr); | ||
195 | if (fdb) | ||
196 | atomic_inc(&fdb->use_count); | ||
197 | rcu_read_unlock(); | ||
198 | return fdb; | ||
199 | } | ||
200 | |||
201 | static void fdb_rcu_free(struct rcu_head *head) | ||
202 | { | ||
203 | struct net_bridge_fdb_entry *ent | ||
204 | = container_of(head, struct net_bridge_fdb_entry, rcu); | ||
205 | kmem_cache_free(br_fdb_cache, ent); | ||
206 | } | ||
207 | |||
208 | /* Set entry up for deletion with RCU */ | ||
209 | void br_fdb_put(struct net_bridge_fdb_entry *ent) | ||
210 | { | ||
211 | if (atomic_dec_and_test(&ent->use_count)) | ||
212 | call_rcu(&ent->rcu, fdb_rcu_free); | ||
213 | } | ||
214 | |||
215 | /* | ||
216 | * Fill buffer with forwarding table records in | ||
217 | * the API format. | ||
218 | */ | ||
219 | int br_fdb_fillbuf(struct net_bridge *br, void *buf, | ||
220 | unsigned long maxnum, unsigned long skip) | ||
221 | { | ||
222 | struct __fdb_entry *fe = buf; | ||
223 | int i, num = 0; | ||
224 | struct hlist_node *h; | ||
225 | struct net_bridge_fdb_entry *f; | ||
226 | |||
227 | memset(buf, 0, maxnum*sizeof(struct __fdb_entry)); | ||
228 | |||
229 | rcu_read_lock(); | ||
230 | for (i = 0; i < BR_HASH_SIZE; i++) { | ||
231 | hlist_for_each_entry_rcu(f, h, &br->hash[i], hlist) { | ||
232 | if (num >= maxnum) | ||
233 | goto out; | ||
234 | |||
235 | if (has_expired(br, f)) | ||
236 | continue; | ||
237 | |||
238 | if (skip) { | ||
239 | --skip; | ||
240 | continue; | ||
241 | } | ||
242 | |||
243 | /* convert from internal format to API */ | ||
244 | memcpy(fe->mac_addr, f->addr.addr, ETH_ALEN); | ||
245 | fe->port_no = f->dst->port_no; | ||
246 | fe->is_local = f->is_local; | ||
247 | if (!f->is_static) | ||
248 | fe->ageing_timer_value = jiffies_to_clock_t(jiffies - f->ageing_timer); | ||
249 | ++fe; | ||
250 | ++num; | ||
251 | } | ||
252 | } | ||
253 | |||
254 | out: | ||
255 | rcu_read_unlock(); | ||
256 | |||
257 | return num; | ||
258 | } | ||
259 | |||
260 | static inline struct net_bridge_fdb_entry *fdb_find(struct hlist_head *head, | ||
261 | const unsigned char *addr) | ||
262 | { | ||
263 | struct hlist_node *h; | ||
264 | struct net_bridge_fdb_entry *fdb; | ||
265 | |||
266 | hlist_for_each_entry_rcu(fdb, h, head, hlist) { | ||
267 | if (!memcmp(fdb->addr.addr, addr, ETH_ALEN)) | ||
268 | return fdb; | ||
269 | } | ||
270 | return NULL; | ||
271 | } | ||
272 | |||
273 | static struct net_bridge_fdb_entry *fdb_create(struct hlist_head *head, | ||
274 | struct net_bridge_port *source, | ||
275 | const unsigned char *addr, | ||
276 | int is_local) | ||
277 | { | ||
278 | struct net_bridge_fdb_entry *fdb; | ||
279 | |||
280 | fdb = kmem_cache_alloc(br_fdb_cache, GFP_ATOMIC); | ||
281 | if (fdb) { | ||
282 | memcpy(fdb->addr.addr, addr, ETH_ALEN); | ||
283 | atomic_set(&fdb->use_count, 1); | ||
284 | hlist_add_head_rcu(&fdb->hlist, head); | ||
285 | |||
286 | fdb->dst = source; | ||
287 | fdb->is_local = is_local; | ||
288 | fdb->is_static = is_local; | ||
289 | fdb->ageing_timer = jiffies; | ||
290 | } | ||
291 | return fdb; | ||
292 | } | ||
293 | |||
294 | static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, | ||
295 | const unsigned char *addr) | ||
296 | { | ||
297 | struct hlist_head *head = &br->hash[br_mac_hash(addr)]; | ||
298 | struct net_bridge_fdb_entry *fdb; | ||
299 | |||
300 | if (!is_valid_ether_addr(addr)) | ||
301 | return -EINVAL; | ||
302 | |||
303 | fdb = fdb_find(head, addr); | ||
304 | if (fdb) { | ||
305 | /* it is okay to have multiple ports with same | ||
306 | * address, just use the first one. | ||
307 | */ | ||
308 | if (fdb->is_local) | ||
309 | return 0; | ||
310 | |||
311 | printk(KERN_WARNING "%s adding interface with same address " | ||
312 | "as a received packet\n", | ||
313 | source->dev->name); | ||
314 | fdb_delete(fdb); | ||
315 | } | ||
316 | |||
317 | if (!fdb_create(head, source, addr, 1)) | ||
318 | return -ENOMEM; | ||
319 | |||
320 | return 0; | ||
321 | } | ||
322 | |||
323 | int br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source, | ||
324 | const unsigned char *addr) | ||
325 | { | ||
326 | int ret; | ||
327 | |||
328 | spin_lock_bh(&br->hash_lock); | ||
329 | ret = fdb_insert(br, source, addr); | ||
330 | spin_unlock_bh(&br->hash_lock); | ||
331 | return ret; | ||
332 | } | ||
333 | |||
334 | void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, | ||
335 | const unsigned char *addr) | ||
336 | { | ||
337 | struct hlist_head *head = &br->hash[br_mac_hash(addr)]; | ||
338 | struct net_bridge_fdb_entry *fdb; | ||
339 | |||
340 | /* some users want to always flood. */ | ||
341 | if (hold_time(br) == 0) | ||
342 | return; | ||
343 | |||
344 | rcu_read_lock(); | ||
345 | fdb = fdb_find(head, addr); | ||
346 | if (likely(fdb)) { | ||
347 | /* attempt to update an entry for a local interface */ | ||
348 | if (unlikely(fdb->is_local)) { | ||
349 | if (net_ratelimit()) | ||
350 | printk(KERN_WARNING "%s: received packet with " | ||
351 | " own address as source address\n", | ||
352 | source->dev->name); | ||
353 | } else { | ||
354 | /* fastpath: update of existing entry */ | ||
355 | fdb->dst = source; | ||
356 | fdb->ageing_timer = jiffies; | ||
357 | } | ||
358 | } else { | ||
359 | spin_lock_bh(&br->hash_lock); | ||
360 | if (!fdb_find(head, addr)) | ||
361 | fdb_create(head, source, addr, 0); | ||
362 | /* else we lose race and someone else inserts | ||
363 | * it first, don't bother updating | ||
364 | */ | ||
365 | spin_unlock_bh(&br->hash_lock); | ||
366 | } | ||
367 | rcu_read_unlock(); | ||
368 | } | ||
diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c new file mode 100644 index 000000000000..ef9f2095f96e --- /dev/null +++ b/net/bridge/br_forward.c | |||
@@ -0,0 +1,159 @@ | |||
1 | /* | ||
2 | * Forwarding decision | ||
3 | * Linux ethernet bridge | ||
4 | * | ||
5 | * Authors: | ||
6 | * Lennert Buytenhek <buytenh@gnu.org> | ||
7 | * | ||
8 | * $Id: br_forward.c,v 1.4 2001/08/14 22:05:57 davem Exp $ | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or | ||
11 | * modify it under the terms of the GNU General Public License | ||
12 | * as published by the Free Software Foundation; either version | ||
13 | * 2 of the License, or (at your option) any later version. | ||
14 | */ | ||
15 | |||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/netdevice.h> | ||
18 | #include <linux/skbuff.h> | ||
19 | #include <linux/netfilter_bridge.h> | ||
20 | #include "br_private.h" | ||
21 | |||
22 | static inline int should_deliver(const struct net_bridge_port *p, | ||
23 | const struct sk_buff *skb) | ||
24 | { | ||
25 | if (skb->dev == p->dev || | ||
26 | p->state != BR_STATE_FORWARDING) | ||
27 | return 0; | ||
28 | |||
29 | return 1; | ||
30 | } | ||
31 | |||
32 | int br_dev_queue_push_xmit(struct sk_buff *skb) | ||
33 | { | ||
34 | if (skb->len > skb->dev->mtu) | ||
35 | kfree_skb(skb); | ||
36 | else { | ||
37 | #ifdef CONFIG_BRIDGE_NETFILTER | ||
38 | /* ip_refrag calls ip_fragment, doesn't copy the MAC header. */ | ||
39 | nf_bridge_maybe_copy_header(skb); | ||
40 | #endif | ||
41 | skb_push(skb, ETH_HLEN); | ||
42 | |||
43 | dev_queue_xmit(skb); | ||
44 | } | ||
45 | |||
46 | return 0; | ||
47 | } | ||
48 | |||
49 | int br_forward_finish(struct sk_buff *skb) | ||
50 | { | ||
51 | NF_HOOK(PF_BRIDGE, NF_BR_POST_ROUTING, skb, NULL, skb->dev, | ||
52 | br_dev_queue_push_xmit); | ||
53 | |||
54 | return 0; | ||
55 | } | ||
56 | |||
57 | static void __br_deliver(const struct net_bridge_port *to, struct sk_buff *skb) | ||
58 | { | ||
59 | skb->dev = to->dev; | ||
60 | #ifdef CONFIG_NETFILTER_DEBUG | ||
61 | skb->nf_debug = 0; | ||
62 | #endif | ||
63 | NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev, | ||
64 | br_forward_finish); | ||
65 | } | ||
66 | |||
67 | static void __br_forward(const struct net_bridge_port *to, struct sk_buff *skb) | ||
68 | { | ||
69 | struct net_device *indev; | ||
70 | |||
71 | indev = skb->dev; | ||
72 | skb->dev = to->dev; | ||
73 | skb->ip_summed = CHECKSUM_NONE; | ||
74 | |||
75 | NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, indev, skb->dev, | ||
76 | br_forward_finish); | ||
77 | } | ||
78 | |||
79 | /* called with rcu_read_lock */ | ||
80 | void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb) | ||
81 | { | ||
82 | if (should_deliver(to, skb)) { | ||
83 | __br_deliver(to, skb); | ||
84 | return; | ||
85 | } | ||
86 | |||
87 | kfree_skb(skb); | ||
88 | } | ||
89 | |||
90 | /* called with rcu_read_lock */ | ||
91 | void br_forward(const struct net_bridge_port *to, struct sk_buff *skb) | ||
92 | { | ||
93 | if (should_deliver(to, skb)) { | ||
94 | __br_forward(to, skb); | ||
95 | return; | ||
96 | } | ||
97 | |||
98 | kfree_skb(skb); | ||
99 | } | ||
100 | |||
101 | /* called under bridge lock */ | ||
102 | static void br_flood(struct net_bridge *br, struct sk_buff *skb, int clone, | ||
103 | void (*__packet_hook)(const struct net_bridge_port *p, | ||
104 | struct sk_buff *skb)) | ||
105 | { | ||
106 | struct net_bridge_port *p; | ||
107 | struct net_bridge_port *prev; | ||
108 | |||
109 | if (clone) { | ||
110 | struct sk_buff *skb2; | ||
111 | |||
112 | if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL) { | ||
113 | br->statistics.tx_dropped++; | ||
114 | return; | ||
115 | } | ||
116 | |||
117 | skb = skb2; | ||
118 | } | ||
119 | |||
120 | prev = NULL; | ||
121 | |||
122 | list_for_each_entry_rcu(p, &br->port_list, list) { | ||
123 | if (should_deliver(p, skb)) { | ||
124 | if (prev != NULL) { | ||
125 | struct sk_buff *skb2; | ||
126 | |||
127 | if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL) { | ||
128 | br->statistics.tx_dropped++; | ||
129 | kfree_skb(skb); | ||
130 | return; | ||
131 | } | ||
132 | |||
133 | __packet_hook(prev, skb2); | ||
134 | } | ||
135 | |||
136 | prev = p; | ||
137 | } | ||
138 | } | ||
139 | |||
140 | if (prev != NULL) { | ||
141 | __packet_hook(prev, skb); | ||
142 | return; | ||
143 | } | ||
144 | |||
145 | kfree_skb(skb); | ||
146 | } | ||
147 | |||
148 | |||
149 | /* called with rcu_read_lock */ | ||
150 | void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb, int clone) | ||
151 | { | ||
152 | br_flood(br, skb, clone, __br_deliver); | ||
153 | } | ||
154 | |||
155 | /* called under bridge lock */ | ||
156 | void br_flood_forward(struct net_bridge *br, struct sk_buff *skb, int clone) | ||
157 | { | ||
158 | br_flood(br, skb, clone, __br_forward); | ||
159 | } | ||
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c new file mode 100644 index 000000000000..69872bf3b87e --- /dev/null +++ b/net/bridge/br_if.c | |||
@@ -0,0 +1,388 @@ | |||
1 | /* | ||
2 | * Userspace interface | ||
3 | * Linux ethernet bridge | ||
4 | * | ||
5 | * Authors: | ||
6 | * Lennert Buytenhek <buytenh@gnu.org> | ||
7 | * | ||
8 | * $Id: br_if.c,v 1.7 2001/12/24 00:59:55 davem Exp $ | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or | ||
11 | * modify it under the terms of the GNU General Public License | ||
12 | * as published by the Free Software Foundation; either version | ||
13 | * 2 of the License, or (at your option) any later version. | ||
14 | */ | ||
15 | |||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/netdevice.h> | ||
18 | #include <linux/ethtool.h> | ||
19 | #include <linux/if_arp.h> | ||
20 | #include <linux/module.h> | ||
21 | #include <linux/init.h> | ||
22 | #include <linux/rtnetlink.h> | ||
23 | #include <net/sock.h> | ||
24 | |||
25 | #include "br_private.h" | ||
26 | |||
27 | /* | ||
28 | * Determine initial path cost based on speed. | ||
29 | * using recommendations from 802.1d standard | ||
30 | * | ||
31 | * Need to simulate user ioctl because not all device's that support | ||
32 | * ethtool, use ethtool_ops. Also, since driver might sleep need to | ||
33 | * not be holding any locks. | ||
34 | */ | ||
35 | static int br_initial_port_cost(struct net_device *dev) | ||
36 | { | ||
37 | |||
38 | struct ethtool_cmd ecmd = { ETHTOOL_GSET }; | ||
39 | struct ifreq ifr; | ||
40 | mm_segment_t old_fs; | ||
41 | int err; | ||
42 | |||
43 | strncpy(ifr.ifr_name, dev->name, IFNAMSIZ); | ||
44 | ifr.ifr_data = (void __user *) &ecmd; | ||
45 | |||
46 | old_fs = get_fs(); | ||
47 | set_fs(KERNEL_DS); | ||
48 | err = dev_ethtool(&ifr); | ||
49 | set_fs(old_fs); | ||
50 | |||
51 | if (!err) { | ||
52 | switch(ecmd.speed) { | ||
53 | case SPEED_100: | ||
54 | return 19; | ||
55 | case SPEED_1000: | ||
56 | return 4; | ||
57 | case SPEED_10000: | ||
58 | return 2; | ||
59 | case SPEED_10: | ||
60 | return 100; | ||
61 | default: | ||
62 | pr_info("bridge: can't decode speed from %s: %d\n", | ||
63 | dev->name, ecmd.speed); | ||
64 | return 100; | ||
65 | } | ||
66 | } | ||
67 | |||
68 | /* Old silly heuristics based on name */ | ||
69 | if (!strncmp(dev->name, "lec", 3)) | ||
70 | return 7; | ||
71 | |||
72 | if (!strncmp(dev->name, "plip", 4)) | ||
73 | return 2500; | ||
74 | |||
75 | return 100; /* assume old 10Mbps */ | ||
76 | } | ||
77 | |||
78 | static void destroy_nbp(struct net_bridge_port *p) | ||
79 | { | ||
80 | struct net_device *dev = p->dev; | ||
81 | |||
82 | dev->br_port = NULL; | ||
83 | p->br = NULL; | ||
84 | p->dev = NULL; | ||
85 | dev_put(dev); | ||
86 | |||
87 | br_sysfs_freeif(p); | ||
88 | } | ||
89 | |||
90 | static void destroy_nbp_rcu(struct rcu_head *head) | ||
91 | { | ||
92 | struct net_bridge_port *p = | ||
93 | container_of(head, struct net_bridge_port, rcu); | ||
94 | destroy_nbp(p); | ||
95 | } | ||
96 | |||
97 | /* called with RTNL */ | ||
98 | static void del_nbp(struct net_bridge_port *p) | ||
99 | { | ||
100 | struct net_bridge *br = p->br; | ||
101 | struct net_device *dev = p->dev; | ||
102 | |||
103 | dev_set_promiscuity(dev, -1); | ||
104 | |||
105 | spin_lock_bh(&br->lock); | ||
106 | br_stp_disable_port(p); | ||
107 | spin_unlock_bh(&br->lock); | ||
108 | |||
109 | br_fdb_delete_by_port(br, p); | ||
110 | |||
111 | list_del_rcu(&p->list); | ||
112 | |||
113 | del_timer_sync(&p->message_age_timer); | ||
114 | del_timer_sync(&p->forward_delay_timer); | ||
115 | del_timer_sync(&p->hold_timer); | ||
116 | |||
117 | call_rcu(&p->rcu, destroy_nbp_rcu); | ||
118 | } | ||
119 | |||
120 | /* called with RTNL */ | ||
121 | static void del_br(struct net_bridge *br) | ||
122 | { | ||
123 | struct net_bridge_port *p, *n; | ||
124 | |||
125 | list_for_each_entry_safe(p, n, &br->port_list, list) { | ||
126 | br_sysfs_removeif(p); | ||
127 | del_nbp(p); | ||
128 | } | ||
129 | |||
130 | del_timer_sync(&br->gc_timer); | ||
131 | |||
132 | br_sysfs_delbr(br->dev); | ||
133 | unregister_netdevice(br->dev); | ||
134 | } | ||
135 | |||
136 | static struct net_device *new_bridge_dev(const char *name) | ||
137 | { | ||
138 | struct net_bridge *br; | ||
139 | struct net_device *dev; | ||
140 | |||
141 | dev = alloc_netdev(sizeof(struct net_bridge), name, | ||
142 | br_dev_setup); | ||
143 | |||
144 | if (!dev) | ||
145 | return NULL; | ||
146 | |||
147 | br = netdev_priv(dev); | ||
148 | br->dev = dev; | ||
149 | |||
150 | spin_lock_init(&br->lock); | ||
151 | INIT_LIST_HEAD(&br->port_list); | ||
152 | spin_lock_init(&br->hash_lock); | ||
153 | |||
154 | br->bridge_id.prio[0] = 0x80; | ||
155 | br->bridge_id.prio[1] = 0x00; | ||
156 | memset(br->bridge_id.addr, 0, ETH_ALEN); | ||
157 | |||
158 | br->stp_enabled = 0; | ||
159 | br->designated_root = br->bridge_id; | ||
160 | br->root_path_cost = 0; | ||
161 | br->root_port = 0; | ||
162 | br->bridge_max_age = br->max_age = 20 * HZ; | ||
163 | br->bridge_hello_time = br->hello_time = 2 * HZ; | ||
164 | br->bridge_forward_delay = br->forward_delay = 15 * HZ; | ||
165 | br->topology_change = 0; | ||
166 | br->topology_change_detected = 0; | ||
167 | br->ageing_time = 300 * HZ; | ||
168 | INIT_LIST_HEAD(&br->age_list); | ||
169 | |||
170 | br_stp_timer_init(br); | ||
171 | |||
172 | return dev; | ||
173 | } | ||
174 | |||
175 | /* find an available port number */ | ||
176 | static int find_portno(struct net_bridge *br) | ||
177 | { | ||
178 | int index; | ||
179 | struct net_bridge_port *p; | ||
180 | unsigned long *inuse; | ||
181 | |||
182 | inuse = kmalloc(BITS_TO_LONGS(BR_MAX_PORTS)*sizeof(unsigned long), | ||
183 | GFP_KERNEL); | ||
184 | if (!inuse) | ||
185 | return -ENOMEM; | ||
186 | |||
187 | memset(inuse, 0, BITS_TO_LONGS(BR_MAX_PORTS)*sizeof(unsigned long)); | ||
188 | set_bit(0, inuse); /* zero is reserved */ | ||
189 | list_for_each_entry(p, &br->port_list, list) { | ||
190 | set_bit(p->port_no, inuse); | ||
191 | } | ||
192 | index = find_first_zero_bit(inuse, BR_MAX_PORTS); | ||
193 | kfree(inuse); | ||
194 | |||
195 | return (index >= BR_MAX_PORTS) ? -EXFULL : index; | ||
196 | } | ||
197 | |||
198 | /* called with RTNL */ | ||
199 | static struct net_bridge_port *new_nbp(struct net_bridge *br, | ||
200 | struct net_device *dev, | ||
201 | unsigned long cost) | ||
202 | { | ||
203 | int index; | ||
204 | struct net_bridge_port *p; | ||
205 | |||
206 | index = find_portno(br); | ||
207 | if (index < 0) | ||
208 | return ERR_PTR(index); | ||
209 | |||
210 | p = kmalloc(sizeof(*p), GFP_KERNEL); | ||
211 | if (p == NULL) | ||
212 | return ERR_PTR(-ENOMEM); | ||
213 | |||
214 | memset(p, 0, sizeof(*p)); | ||
215 | p->br = br; | ||
216 | dev_hold(dev); | ||
217 | p->dev = dev; | ||
218 | p->path_cost = cost; | ||
219 | p->priority = 0x8000 >> BR_PORT_BITS; | ||
220 | dev->br_port = p; | ||
221 | p->port_no = index; | ||
222 | br_init_port(p); | ||
223 | p->state = BR_STATE_DISABLED; | ||
224 | kobject_init(&p->kobj); | ||
225 | |||
226 | return p; | ||
227 | } | ||
228 | |||
229 | int br_add_bridge(const char *name) | ||
230 | { | ||
231 | struct net_device *dev; | ||
232 | int ret; | ||
233 | |||
234 | dev = new_bridge_dev(name); | ||
235 | if (!dev) | ||
236 | return -ENOMEM; | ||
237 | |||
238 | rtnl_lock(); | ||
239 | if (strchr(dev->name, '%')) { | ||
240 | ret = dev_alloc_name(dev, dev->name); | ||
241 | if (ret < 0) | ||
242 | goto err1; | ||
243 | } | ||
244 | |||
245 | ret = register_netdevice(dev); | ||
246 | if (ret) | ||
247 | goto err2; | ||
248 | |||
249 | /* network device kobject is not setup until | ||
250 | * after rtnl_unlock does it's hotplug magic. | ||
251 | * so hold reference to avoid race. | ||
252 | */ | ||
253 | dev_hold(dev); | ||
254 | rtnl_unlock(); | ||
255 | |||
256 | ret = br_sysfs_addbr(dev); | ||
257 | dev_put(dev); | ||
258 | |||
259 | if (ret) | ||
260 | unregister_netdev(dev); | ||
261 | out: | ||
262 | return ret; | ||
263 | |||
264 | err2: | ||
265 | free_netdev(dev); | ||
266 | err1: | ||
267 | rtnl_unlock(); | ||
268 | goto out; | ||
269 | } | ||
270 | |||
271 | int br_del_bridge(const char *name) | ||
272 | { | ||
273 | struct net_device *dev; | ||
274 | int ret = 0; | ||
275 | |||
276 | rtnl_lock(); | ||
277 | dev = __dev_get_by_name(name); | ||
278 | if (dev == NULL) | ||
279 | ret = -ENXIO; /* Could not find device */ | ||
280 | |||
281 | else if (!(dev->priv_flags & IFF_EBRIDGE)) { | ||
282 | /* Attempt to delete non bridge device! */ | ||
283 | ret = -EPERM; | ||
284 | } | ||
285 | |||
286 | else if (dev->flags & IFF_UP) { | ||
287 | /* Not shutdown yet. */ | ||
288 | ret = -EBUSY; | ||
289 | } | ||
290 | |||
291 | else | ||
292 | del_br(netdev_priv(dev)); | ||
293 | |||
294 | rtnl_unlock(); | ||
295 | return ret; | ||
296 | } | ||
297 | |||
298 | /* Mtu of the bridge pseudo-device 1500 or the minimum of the ports */ | ||
299 | int br_min_mtu(const struct net_bridge *br) | ||
300 | { | ||
301 | const struct net_bridge_port *p; | ||
302 | int mtu = 0; | ||
303 | |||
304 | ASSERT_RTNL(); | ||
305 | |||
306 | if (list_empty(&br->port_list)) | ||
307 | mtu = 1500; | ||
308 | else { | ||
309 | list_for_each_entry(p, &br->port_list, list) { | ||
310 | if (!mtu || p->dev->mtu < mtu) | ||
311 | mtu = p->dev->mtu; | ||
312 | } | ||
313 | } | ||
314 | return mtu; | ||
315 | } | ||
316 | |||
317 | /* called with RTNL */ | ||
318 | int br_add_if(struct net_bridge *br, struct net_device *dev) | ||
319 | { | ||
320 | struct net_bridge_port *p; | ||
321 | int err = 0; | ||
322 | |||
323 | if (dev->flags & IFF_LOOPBACK || dev->type != ARPHRD_ETHER) | ||
324 | return -EINVAL; | ||
325 | |||
326 | if (dev->hard_start_xmit == br_dev_xmit) | ||
327 | return -ELOOP; | ||
328 | |||
329 | if (dev->br_port != NULL) | ||
330 | return -EBUSY; | ||
331 | |||
332 | if (IS_ERR(p = new_nbp(br, dev, br_initial_port_cost(dev)))) | ||
333 | return PTR_ERR(p); | ||
334 | |||
335 | if ((err = br_fdb_insert(br, p, dev->dev_addr))) | ||
336 | destroy_nbp(p); | ||
337 | |||
338 | else if ((err = br_sysfs_addif(p))) | ||
339 | del_nbp(p); | ||
340 | else { | ||
341 | dev_set_promiscuity(dev, 1); | ||
342 | |||
343 | list_add_rcu(&p->list, &br->port_list); | ||
344 | |||
345 | spin_lock_bh(&br->lock); | ||
346 | br_stp_recalculate_bridge_id(br); | ||
347 | if ((br->dev->flags & IFF_UP) | ||
348 | && (dev->flags & IFF_UP) && netif_carrier_ok(dev)) | ||
349 | br_stp_enable_port(p); | ||
350 | spin_unlock_bh(&br->lock); | ||
351 | |||
352 | dev_set_mtu(br->dev, br_min_mtu(br)); | ||
353 | } | ||
354 | |||
355 | return err; | ||
356 | } | ||
357 | |||
358 | /* called with RTNL */ | ||
359 | int br_del_if(struct net_bridge *br, struct net_device *dev) | ||
360 | { | ||
361 | struct net_bridge_port *p = dev->br_port; | ||
362 | |||
363 | if (!p || p->br != br) | ||
364 | return -EINVAL; | ||
365 | |||
366 | br_sysfs_removeif(p); | ||
367 | del_nbp(p); | ||
368 | |||
369 | spin_lock_bh(&br->lock); | ||
370 | br_stp_recalculate_bridge_id(br); | ||
371 | spin_unlock_bh(&br->lock); | ||
372 | |||
373 | return 0; | ||
374 | } | ||
375 | |||
376 | void __exit br_cleanup_bridges(void) | ||
377 | { | ||
378 | struct net_device *dev, *nxt; | ||
379 | |||
380 | rtnl_lock(); | ||
381 | for (dev = dev_base; dev; dev = nxt) { | ||
382 | nxt = dev->next; | ||
383 | if (dev->priv_flags & IFF_EBRIDGE) | ||
384 | del_br(dev->priv); | ||
385 | } | ||
386 | rtnl_unlock(); | ||
387 | |||
388 | } | ||
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c new file mode 100644 index 000000000000..2b1cce46cab4 --- /dev/null +++ b/net/bridge/br_input.c | |||
@@ -0,0 +1,144 @@ | |||
1 | /* | ||
2 | * Handle incoming frames | ||
3 | * Linux ethernet bridge | ||
4 | * | ||
5 | * Authors: | ||
6 | * Lennert Buytenhek <buytenh@gnu.org> | ||
7 | * | ||
8 | * $Id: br_input.c,v 1.10 2001/12/24 04:50:20 davem Exp $ | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or | ||
11 | * modify it under the terms of the GNU General Public License | ||
12 | * as published by the Free Software Foundation; either version | ||
13 | * 2 of the License, or (at your option) any later version. | ||
14 | */ | ||
15 | |||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/netdevice.h> | ||
18 | #include <linux/etherdevice.h> | ||
19 | #include <linux/netfilter_bridge.h> | ||
20 | #include "br_private.h" | ||
21 | |||
22 | const unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }; | ||
23 | |||
24 | static int br_pass_frame_up_finish(struct sk_buff *skb) | ||
25 | { | ||
26 | #ifdef CONFIG_NETFILTER_DEBUG | ||
27 | skb->nf_debug = 0; | ||
28 | #endif | ||
29 | netif_rx(skb); | ||
30 | |||
31 | return 0; | ||
32 | } | ||
33 | |||
34 | static void br_pass_frame_up(struct net_bridge *br, struct sk_buff *skb) | ||
35 | { | ||
36 | struct net_device *indev; | ||
37 | |||
38 | br->statistics.rx_packets++; | ||
39 | br->statistics.rx_bytes += skb->len; | ||
40 | |||
41 | indev = skb->dev; | ||
42 | skb->dev = br->dev; | ||
43 | |||
44 | NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, indev, NULL, | ||
45 | br_pass_frame_up_finish); | ||
46 | } | ||
47 | |||
48 | /* note: already called with rcu_read_lock (preempt_disabled) */ | ||
49 | int br_handle_frame_finish(struct sk_buff *skb) | ||
50 | { | ||
51 | const unsigned char *dest = eth_hdr(skb)->h_dest; | ||
52 | struct net_bridge_port *p = skb->dev->br_port; | ||
53 | struct net_bridge *br = p->br; | ||
54 | struct net_bridge_fdb_entry *dst; | ||
55 | int passedup = 0; | ||
56 | |||
57 | if (br->dev->flags & IFF_PROMISC) { | ||
58 | struct sk_buff *skb2; | ||
59 | |||
60 | skb2 = skb_clone(skb, GFP_ATOMIC); | ||
61 | if (skb2 != NULL) { | ||
62 | passedup = 1; | ||
63 | br_pass_frame_up(br, skb2); | ||
64 | } | ||
65 | } | ||
66 | |||
67 | if (dest[0] & 1) { | ||
68 | br_flood_forward(br, skb, !passedup); | ||
69 | if (!passedup) | ||
70 | br_pass_frame_up(br, skb); | ||
71 | goto out; | ||
72 | } | ||
73 | |||
74 | dst = __br_fdb_get(br, dest); | ||
75 | if (dst != NULL && dst->is_local) { | ||
76 | if (!passedup) | ||
77 | br_pass_frame_up(br, skb); | ||
78 | else | ||
79 | kfree_skb(skb); | ||
80 | goto out; | ||
81 | } | ||
82 | |||
83 | if (dst != NULL) { | ||
84 | br_forward(dst->dst, skb); | ||
85 | goto out; | ||
86 | } | ||
87 | |||
88 | br_flood_forward(br, skb, 0); | ||
89 | |||
90 | out: | ||
91 | return 0; | ||
92 | } | ||
93 | |||
94 | /* | ||
95 | * Called via br_handle_frame_hook. | ||
96 | * Return 0 if *pskb should be processed furthur | ||
97 | * 1 if *pskb is handled | ||
98 | * note: already called with rcu_read_lock (preempt_disabled) | ||
99 | */ | ||
100 | int br_handle_frame(struct net_bridge_port *p, struct sk_buff **pskb) | ||
101 | { | ||
102 | struct sk_buff *skb = *pskb; | ||
103 | const unsigned char *dest = eth_hdr(skb)->h_dest; | ||
104 | |||
105 | if (p->state == BR_STATE_DISABLED) | ||
106 | goto err; | ||
107 | |||
108 | if (!is_valid_ether_addr(eth_hdr(skb)->h_source)) | ||
109 | goto err; | ||
110 | |||
111 | if (p->state == BR_STATE_LEARNING || | ||
112 | p->state == BR_STATE_FORWARDING) | ||
113 | br_fdb_update(p->br, p, eth_hdr(skb)->h_source); | ||
114 | |||
115 | if (p->br->stp_enabled && | ||
116 | !memcmp(dest, bridge_ula, 5) && | ||
117 | !(dest[5] & 0xF0)) { | ||
118 | if (!dest[5]) { | ||
119 | NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev, | ||
120 | NULL, br_stp_handle_bpdu); | ||
121 | return 1; | ||
122 | } | ||
123 | } | ||
124 | |||
125 | else if (p->state == BR_STATE_FORWARDING) { | ||
126 | if (br_should_route_hook) { | ||
127 | if (br_should_route_hook(pskb)) | ||
128 | return 0; | ||
129 | skb = *pskb; | ||
130 | dest = eth_hdr(skb)->h_dest; | ||
131 | } | ||
132 | |||
133 | if (!memcmp(p->br->dev->dev_addr, dest, ETH_ALEN)) | ||
134 | skb->pkt_type = PACKET_HOST; | ||
135 | |||
136 | NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL, | ||
137 | br_handle_frame_finish); | ||
138 | return 1; | ||
139 | } | ||
140 | |||
141 | err: | ||
142 | kfree_skb(skb); | ||
143 | return 1; | ||
144 | } | ||
diff --git a/net/bridge/br_ioctl.c b/net/bridge/br_ioctl.c new file mode 100644 index 000000000000..b8ce14b22181 --- /dev/null +++ b/net/bridge/br_ioctl.c | |||
@@ -0,0 +1,410 @@ | |||
1 | /* | ||
2 | * Ioctl handler | ||
3 | * Linux ethernet bridge | ||
4 | * | ||
5 | * Authors: | ||
6 | * Lennert Buytenhek <buytenh@gnu.org> | ||
7 | * | ||
8 | * $Id: br_ioctl.c,v 1.4 2000/11/08 05:16:40 davem Exp $ | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or | ||
11 | * modify it under the terms of the GNU General Public License | ||
12 | * as published by the Free Software Foundation; either version | ||
13 | * 2 of the License, or (at your option) any later version. | ||
14 | */ | ||
15 | |||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/if_bridge.h> | ||
18 | #include <linux/netdevice.h> | ||
19 | #include <linux/times.h> | ||
20 | #include <asm/uaccess.h> | ||
21 | #include "br_private.h" | ||
22 | |||
23 | /* called with RTNL */ | ||
24 | static int get_bridge_ifindices(int *indices, int num) | ||
25 | { | ||
26 | struct net_device *dev; | ||
27 | int i = 0; | ||
28 | |||
29 | for (dev = dev_base; dev && i < num; dev = dev->next) { | ||
30 | if (dev->priv_flags & IFF_EBRIDGE) | ||
31 | indices[i++] = dev->ifindex; | ||
32 | } | ||
33 | |||
34 | return i; | ||
35 | } | ||
36 | |||
37 | /* called with RTNL */ | ||
38 | static void get_port_ifindices(struct net_bridge *br, int *ifindices, int num) | ||
39 | { | ||
40 | struct net_bridge_port *p; | ||
41 | |||
42 | list_for_each_entry(p, &br->port_list, list) { | ||
43 | if (p->port_no < num) | ||
44 | ifindices[p->port_no] = p->dev->ifindex; | ||
45 | } | ||
46 | } | ||
47 | |||
48 | /* | ||
49 | * Format up to a page worth of forwarding table entries | ||
50 | * userbuf -- where to copy result | ||
51 | * maxnum -- maximum number of entries desired | ||
52 | * (limited to a page for sanity) | ||
53 | * offset -- number of records to skip | ||
54 | */ | ||
55 | static int get_fdb_entries(struct net_bridge *br, void __user *userbuf, | ||
56 | unsigned long maxnum, unsigned long offset) | ||
57 | { | ||
58 | int num; | ||
59 | void *buf; | ||
60 | size_t size = maxnum * sizeof(struct __fdb_entry); | ||
61 | |||
62 | if (size > PAGE_SIZE) { | ||
63 | size = PAGE_SIZE; | ||
64 | maxnum = PAGE_SIZE/sizeof(struct __fdb_entry); | ||
65 | } | ||
66 | |||
67 | buf = kmalloc(size, GFP_USER); | ||
68 | if (!buf) | ||
69 | return -ENOMEM; | ||
70 | |||
71 | num = br_fdb_fillbuf(br, buf, maxnum, offset); | ||
72 | if (num > 0) { | ||
73 | if (copy_to_user(userbuf, buf, num*sizeof(struct __fdb_entry))) | ||
74 | num = -EFAULT; | ||
75 | } | ||
76 | kfree(buf); | ||
77 | |||
78 | return num; | ||
79 | } | ||
80 | |||
81 | static int add_del_if(struct net_bridge *br, int ifindex, int isadd) | ||
82 | { | ||
83 | struct net_device *dev; | ||
84 | int ret; | ||
85 | |||
86 | if (!capable(CAP_NET_ADMIN)) | ||
87 | return -EPERM; | ||
88 | |||
89 | dev = dev_get_by_index(ifindex); | ||
90 | if (dev == NULL) | ||
91 | return -EINVAL; | ||
92 | |||
93 | if (isadd) | ||
94 | ret = br_add_if(br, dev); | ||
95 | else | ||
96 | ret = br_del_if(br, dev); | ||
97 | |||
98 | dev_put(dev); | ||
99 | return ret; | ||
100 | } | ||
101 | |||
102 | /* | ||
103 | * Legacy ioctl's through SIOCDEVPRIVATE | ||
104 | * This interface is deprecated because it was too difficult to | ||
105 | * to do the translation for 32/64bit ioctl compatability. | ||
106 | */ | ||
107 | static int old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) | ||
108 | { | ||
109 | struct net_bridge *br = netdev_priv(dev); | ||
110 | unsigned long args[4]; | ||
111 | |||
112 | if (copy_from_user(args, rq->ifr_data, sizeof(args))) | ||
113 | return -EFAULT; | ||
114 | |||
115 | switch (args[0]) { | ||
116 | case BRCTL_ADD_IF: | ||
117 | case BRCTL_DEL_IF: | ||
118 | return add_del_if(br, args[1], args[0] == BRCTL_ADD_IF); | ||
119 | |||
120 | case BRCTL_GET_BRIDGE_INFO: | ||
121 | { | ||
122 | struct __bridge_info b; | ||
123 | |||
124 | memset(&b, 0, sizeof(struct __bridge_info)); | ||
125 | rcu_read_lock(); | ||
126 | memcpy(&b.designated_root, &br->designated_root, 8); | ||
127 | memcpy(&b.bridge_id, &br->bridge_id, 8); | ||
128 | b.root_path_cost = br->root_path_cost; | ||
129 | b.max_age = jiffies_to_clock_t(br->max_age); | ||
130 | b.hello_time = jiffies_to_clock_t(br->hello_time); | ||
131 | b.forward_delay = br->forward_delay; | ||
132 | b.bridge_max_age = br->bridge_max_age; | ||
133 | b.bridge_hello_time = br->bridge_hello_time; | ||
134 | b.bridge_forward_delay = jiffies_to_clock_t(br->bridge_forward_delay); | ||
135 | b.topology_change = br->topology_change; | ||
136 | b.topology_change_detected = br->topology_change_detected; | ||
137 | b.root_port = br->root_port; | ||
138 | b.stp_enabled = br->stp_enabled; | ||
139 | b.ageing_time = jiffies_to_clock_t(br->ageing_time); | ||
140 | b.hello_timer_value = br_timer_value(&br->hello_timer); | ||
141 | b.tcn_timer_value = br_timer_value(&br->tcn_timer); | ||
142 | b.topology_change_timer_value = br_timer_value(&br->topology_change_timer); | ||
143 | b.gc_timer_value = br_timer_value(&br->gc_timer); | ||
144 | rcu_read_unlock(); | ||
145 | |||
146 | if (copy_to_user((void __user *)args[1], &b, sizeof(b))) | ||
147 | return -EFAULT; | ||
148 | |||
149 | return 0; | ||
150 | } | ||
151 | |||
152 | case BRCTL_GET_PORT_LIST: | ||
153 | { | ||
154 | int num, *indices; | ||
155 | |||
156 | num = args[2]; | ||
157 | if (num < 0) | ||
158 | return -EINVAL; | ||
159 | if (num == 0) | ||
160 | num = 256; | ||
161 | if (num > BR_MAX_PORTS) | ||
162 | num = BR_MAX_PORTS; | ||
163 | |||
164 | indices = kmalloc(num*sizeof(int), GFP_KERNEL); | ||
165 | if (indices == NULL) | ||
166 | return -ENOMEM; | ||
167 | |||
168 | memset(indices, 0, num*sizeof(int)); | ||
169 | |||
170 | get_port_ifindices(br, indices, num); | ||
171 | if (copy_to_user((void __user *)args[1], indices, num*sizeof(int))) | ||
172 | num = -EFAULT; | ||
173 | kfree(indices); | ||
174 | return num; | ||
175 | } | ||
176 | |||
177 | case BRCTL_SET_BRIDGE_FORWARD_DELAY: | ||
178 | if (!capable(CAP_NET_ADMIN)) | ||
179 | return -EPERM; | ||
180 | |||
181 | spin_lock_bh(&br->lock); | ||
182 | br->bridge_forward_delay = clock_t_to_jiffies(args[1]); | ||
183 | if (br_is_root_bridge(br)) | ||
184 | br->forward_delay = br->bridge_forward_delay; | ||
185 | spin_unlock_bh(&br->lock); | ||
186 | return 0; | ||
187 | |||
188 | case BRCTL_SET_BRIDGE_HELLO_TIME: | ||
189 | if (!capable(CAP_NET_ADMIN)) | ||
190 | return -EPERM; | ||
191 | |||
192 | spin_lock_bh(&br->lock); | ||
193 | br->bridge_hello_time = clock_t_to_jiffies(args[1]); | ||
194 | if (br_is_root_bridge(br)) | ||
195 | br->hello_time = br->bridge_hello_time; | ||
196 | spin_unlock_bh(&br->lock); | ||
197 | return 0; | ||
198 | |||
199 | case BRCTL_SET_BRIDGE_MAX_AGE: | ||
200 | if (!capable(CAP_NET_ADMIN)) | ||
201 | return -EPERM; | ||
202 | |||
203 | spin_lock_bh(&br->lock); | ||
204 | br->bridge_max_age = clock_t_to_jiffies(args[1]); | ||
205 | if (br_is_root_bridge(br)) | ||
206 | br->max_age = br->bridge_max_age; | ||
207 | spin_unlock_bh(&br->lock); | ||
208 | return 0; | ||
209 | |||
210 | case BRCTL_SET_AGEING_TIME: | ||
211 | if (!capable(CAP_NET_ADMIN)) | ||
212 | return -EPERM; | ||
213 | |||
214 | br->ageing_time = clock_t_to_jiffies(args[1]); | ||
215 | return 0; | ||
216 | |||
217 | case BRCTL_GET_PORT_INFO: | ||
218 | { | ||
219 | struct __port_info p; | ||
220 | struct net_bridge_port *pt; | ||
221 | |||
222 | rcu_read_lock(); | ||
223 | if ((pt = br_get_port(br, args[2])) == NULL) { | ||
224 | rcu_read_unlock(); | ||
225 | return -EINVAL; | ||
226 | } | ||
227 | |||
228 | memset(&p, 0, sizeof(struct __port_info)); | ||
229 | memcpy(&p.designated_root, &pt->designated_root, 8); | ||
230 | memcpy(&p.designated_bridge, &pt->designated_bridge, 8); | ||
231 | p.port_id = pt->port_id; | ||
232 | p.designated_port = pt->designated_port; | ||
233 | p.path_cost = pt->path_cost; | ||
234 | p.designated_cost = pt->designated_cost; | ||
235 | p.state = pt->state; | ||
236 | p.top_change_ack = pt->topology_change_ack; | ||
237 | p.config_pending = pt->config_pending; | ||
238 | p.message_age_timer_value = br_timer_value(&pt->message_age_timer); | ||
239 | p.forward_delay_timer_value = br_timer_value(&pt->forward_delay_timer); | ||
240 | p.hold_timer_value = br_timer_value(&pt->hold_timer); | ||
241 | |||
242 | rcu_read_unlock(); | ||
243 | |||
244 | if (copy_to_user((void __user *)args[1], &p, sizeof(p))) | ||
245 | return -EFAULT; | ||
246 | |||
247 | return 0; | ||
248 | } | ||
249 | |||
250 | case BRCTL_SET_BRIDGE_STP_STATE: | ||
251 | if (!capable(CAP_NET_ADMIN)) | ||
252 | return -EPERM; | ||
253 | |||
254 | br->stp_enabled = args[1]?1:0; | ||
255 | return 0; | ||
256 | |||
257 | case BRCTL_SET_BRIDGE_PRIORITY: | ||
258 | if (!capable(CAP_NET_ADMIN)) | ||
259 | return -EPERM; | ||
260 | |||
261 | spin_lock_bh(&br->lock); | ||
262 | br_stp_set_bridge_priority(br, args[1]); | ||
263 | spin_unlock_bh(&br->lock); | ||
264 | return 0; | ||
265 | |||
266 | case BRCTL_SET_PORT_PRIORITY: | ||
267 | { | ||
268 | struct net_bridge_port *p; | ||
269 | int ret = 0; | ||
270 | |||
271 | if (!capable(CAP_NET_ADMIN)) | ||
272 | return -EPERM; | ||
273 | |||
274 | if (args[2] >= (1<<(16-BR_PORT_BITS))) | ||
275 | return -ERANGE; | ||
276 | |||
277 | spin_lock_bh(&br->lock); | ||
278 | if ((p = br_get_port(br, args[1])) == NULL) | ||
279 | ret = -EINVAL; | ||
280 | else | ||
281 | br_stp_set_port_priority(p, args[2]); | ||
282 | spin_unlock_bh(&br->lock); | ||
283 | return ret; | ||
284 | } | ||
285 | |||
286 | case BRCTL_SET_PATH_COST: | ||
287 | { | ||
288 | struct net_bridge_port *p; | ||
289 | int ret = 0; | ||
290 | |||
291 | if (!capable(CAP_NET_ADMIN)) | ||
292 | return -EPERM; | ||
293 | |||
294 | spin_lock_bh(&br->lock); | ||
295 | if ((p = br_get_port(br, args[1])) == NULL) | ||
296 | ret = -EINVAL; | ||
297 | else | ||
298 | br_stp_set_path_cost(p, args[2]); | ||
299 | spin_unlock_bh(&br->lock); | ||
300 | return ret; | ||
301 | } | ||
302 | |||
303 | case BRCTL_GET_FDB_ENTRIES: | ||
304 | return get_fdb_entries(br, (void __user *)args[1], | ||
305 | args[2], args[3]); | ||
306 | } | ||
307 | |||
308 | return -EOPNOTSUPP; | ||
309 | } | ||
310 | |||
311 | static int old_deviceless(void __user *uarg) | ||
312 | { | ||
313 | unsigned long args[3]; | ||
314 | |||
315 | if (copy_from_user(args, uarg, sizeof(args))) | ||
316 | return -EFAULT; | ||
317 | |||
318 | switch (args[0]) { | ||
319 | case BRCTL_GET_VERSION: | ||
320 | return BRCTL_VERSION; | ||
321 | |||
322 | case BRCTL_GET_BRIDGES: | ||
323 | { | ||
324 | int *indices; | ||
325 | int ret = 0; | ||
326 | |||
327 | if (args[2] >= 2048) | ||
328 | return -ENOMEM; | ||
329 | indices = kmalloc(args[2]*sizeof(int), GFP_KERNEL); | ||
330 | if (indices == NULL) | ||
331 | return -ENOMEM; | ||
332 | |||
333 | memset(indices, 0, args[2]*sizeof(int)); | ||
334 | args[2] = get_bridge_ifindices(indices, args[2]); | ||
335 | |||
336 | ret = copy_to_user((void __user *)args[1], indices, args[2]*sizeof(int)) | ||
337 | ? -EFAULT : args[2]; | ||
338 | |||
339 | kfree(indices); | ||
340 | return ret; | ||
341 | } | ||
342 | |||
343 | case BRCTL_ADD_BRIDGE: | ||
344 | case BRCTL_DEL_BRIDGE: | ||
345 | { | ||
346 | char buf[IFNAMSIZ]; | ||
347 | |||
348 | if (!capable(CAP_NET_ADMIN)) | ||
349 | return -EPERM; | ||
350 | |||
351 | if (copy_from_user(buf, (void __user *)args[1], IFNAMSIZ)) | ||
352 | return -EFAULT; | ||
353 | |||
354 | buf[IFNAMSIZ-1] = 0; | ||
355 | |||
356 | if (args[0] == BRCTL_ADD_BRIDGE) | ||
357 | return br_add_bridge(buf); | ||
358 | |||
359 | return br_del_bridge(buf); | ||
360 | } | ||
361 | } | ||
362 | |||
363 | return -EOPNOTSUPP; | ||
364 | } | ||
365 | |||
366 | int br_ioctl_deviceless_stub(unsigned int cmd, void __user *uarg) | ||
367 | { | ||
368 | switch (cmd) { | ||
369 | case SIOCGIFBR: | ||
370 | case SIOCSIFBR: | ||
371 | return old_deviceless(uarg); | ||
372 | |||
373 | case SIOCBRADDBR: | ||
374 | case SIOCBRDELBR: | ||
375 | { | ||
376 | char buf[IFNAMSIZ]; | ||
377 | |||
378 | if (!capable(CAP_NET_ADMIN)) | ||
379 | return -EPERM; | ||
380 | |||
381 | if (copy_from_user(buf, uarg, IFNAMSIZ)) | ||
382 | return -EFAULT; | ||
383 | |||
384 | buf[IFNAMSIZ-1] = 0; | ||
385 | if (cmd == SIOCBRADDBR) | ||
386 | return br_add_bridge(buf); | ||
387 | |||
388 | return br_del_bridge(buf); | ||
389 | } | ||
390 | } | ||
391 | return -EOPNOTSUPP; | ||
392 | } | ||
393 | |||
394 | int br_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) | ||
395 | { | ||
396 | struct net_bridge *br = netdev_priv(dev); | ||
397 | |||
398 | switch(cmd) { | ||
399 | case SIOCDEVPRIVATE: | ||
400 | return old_dev_ioctl(dev, rq, cmd); | ||
401 | |||
402 | case SIOCBRADDIF: | ||
403 | case SIOCBRDELIF: | ||
404 | return add_del_if(br, rq->ifr_ifindex, cmd == SIOCBRADDIF); | ||
405 | |||
406 | } | ||
407 | |||
408 | pr_debug("Bridge does not support ioctl 0x%x\n", cmd); | ||
409 | return -EOPNOTSUPP; | ||
410 | } | ||
diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c new file mode 100644 index 000000000000..be03d3ad2648 --- /dev/null +++ b/net/bridge/br_netfilter.c | |||
@@ -0,0 +1,1087 @@ | |||
1 | /* | ||
2 | * Handle firewalling | ||
3 | * Linux ethernet bridge | ||
4 | * | ||
5 | * Authors: | ||
6 | * Lennert Buytenhek <buytenh@gnu.org> | ||
7 | * Bart De Schuymer (maintainer) <bdschuym@pandora.be> | ||
8 | * | ||
9 | * Changes: | ||
10 | * Apr 29 2003: physdev module support (bdschuym) | ||
11 | * Jun 19 2003: let arptables see bridged ARP traffic (bdschuym) | ||
12 | * Oct 06 2003: filter encapsulated IP/ARP VLAN traffic on untagged bridge | ||
13 | * (bdschuym) | ||
14 | * Sep 01 2004: add IPv6 filtering (bdschuym) | ||
15 | * | ||
16 | * This program is free software; you can redistribute it and/or | ||
17 | * modify it under the terms of the GNU General Public License | ||
18 | * as published by the Free Software Foundation; either version | ||
19 | * 2 of the License, or (at your option) any later version. | ||
20 | * | ||
21 | * Lennert dedicates this file to Kerstin Wurdinger. | ||
22 | */ | ||
23 | |||
24 | #include <linux/module.h> | ||
25 | #include <linux/kernel.h> | ||
26 | #include <linux/ip.h> | ||
27 | #include <linux/netdevice.h> | ||
28 | #include <linux/skbuff.h> | ||
29 | #include <linux/if_ether.h> | ||
30 | #include <linux/if_vlan.h> | ||
31 | #include <linux/netfilter_bridge.h> | ||
32 | #include <linux/netfilter_ipv4.h> | ||
33 | #include <linux/netfilter_ipv6.h> | ||
34 | #include <linux/netfilter_arp.h> | ||
35 | #include <linux/in_route.h> | ||
36 | #include <net/ip.h> | ||
37 | #include <net/ipv6.h> | ||
38 | #include <asm/uaccess.h> | ||
39 | #include <asm/checksum.h> | ||
40 | #include "br_private.h" | ||
41 | #ifdef CONFIG_SYSCTL | ||
42 | #include <linux/sysctl.h> | ||
43 | #endif | ||
44 | |||
45 | #define skb_origaddr(skb) (((struct bridge_skb_cb *) \ | ||
46 | (skb->nf_bridge->data))->daddr.ipv4) | ||
47 | #define store_orig_dstaddr(skb) (skb_origaddr(skb) = (skb)->nh.iph->daddr) | ||
48 | #define dnat_took_place(skb) (skb_origaddr(skb) != (skb)->nh.iph->daddr) | ||
49 | |||
50 | #define has_bridge_parent(device) ((device)->br_port != NULL) | ||
51 | #define bridge_parent(device) ((device)->br_port->br->dev) | ||
52 | |||
53 | #ifdef CONFIG_SYSCTL | ||
54 | static struct ctl_table_header *brnf_sysctl_header; | ||
55 | static int brnf_call_iptables = 1; | ||
56 | static int brnf_call_ip6tables = 1; | ||
57 | static int brnf_call_arptables = 1; | ||
58 | static int brnf_filter_vlan_tagged = 1; | ||
59 | #else | ||
60 | #define brnf_filter_vlan_tagged 1 | ||
61 | #endif | ||
62 | |||
63 | #define IS_VLAN_IP (skb->protocol == __constant_htons(ETH_P_8021Q) && \ | ||
64 | hdr->h_vlan_encapsulated_proto == __constant_htons(ETH_P_IP) && \ | ||
65 | brnf_filter_vlan_tagged) | ||
66 | #define IS_VLAN_IPV6 (skb->protocol == __constant_htons(ETH_P_8021Q) && \ | ||
67 | hdr->h_vlan_encapsulated_proto == __constant_htons(ETH_P_IPV6) && \ | ||
68 | brnf_filter_vlan_tagged) | ||
69 | #define IS_VLAN_ARP (skb->protocol == __constant_htons(ETH_P_8021Q) && \ | ||
70 | hdr->h_vlan_encapsulated_proto == __constant_htons(ETH_P_ARP) && \ | ||
71 | brnf_filter_vlan_tagged) | ||
72 | |||
73 | /* We need these fake structures to make netfilter happy -- | ||
74 | * lots of places assume that skb->dst != NULL, which isn't | ||
75 | * all that unreasonable. | ||
76 | * | ||
77 | * Currently, we fill in the PMTU entry because netfilter | ||
78 | * refragmentation needs it, and the rt_flags entry because | ||
79 | * ipt_REJECT needs it. Future netfilter modules might | ||
80 | * require us to fill additional fields. */ | ||
81 | static struct net_device __fake_net_device = { | ||
82 | .hard_header_len = ETH_HLEN | ||
83 | }; | ||
84 | |||
85 | static struct rtable __fake_rtable = { | ||
86 | .u = { | ||
87 | .dst = { | ||
88 | .__refcnt = ATOMIC_INIT(1), | ||
89 | .dev = &__fake_net_device, | ||
90 | .path = &__fake_rtable.u.dst, | ||
91 | .metrics = {[RTAX_MTU - 1] = 1500}, | ||
92 | } | ||
93 | }, | ||
94 | .rt_flags = 0, | ||
95 | }; | ||
96 | |||
97 | |||
98 | /* PF_BRIDGE/PRE_ROUTING *********************************************/ | ||
99 | /* Undo the changes made for ip6tables PREROUTING and continue the | ||
100 | * bridge PRE_ROUTING hook. */ | ||
101 | static int br_nf_pre_routing_finish_ipv6(struct sk_buff *skb) | ||
102 | { | ||
103 | struct nf_bridge_info *nf_bridge = skb->nf_bridge; | ||
104 | |||
105 | #ifdef CONFIG_NETFILTER_DEBUG | ||
106 | skb->nf_debug ^= (1 << NF_BR_PRE_ROUTING); | ||
107 | #endif | ||
108 | |||
109 | if (nf_bridge->mask & BRNF_PKT_TYPE) { | ||
110 | skb->pkt_type = PACKET_OTHERHOST; | ||
111 | nf_bridge->mask ^= BRNF_PKT_TYPE; | ||
112 | } | ||
113 | nf_bridge->mask ^= BRNF_NF_BRIDGE_PREROUTING; | ||
114 | |||
115 | skb->dst = (struct dst_entry *)&__fake_rtable; | ||
116 | dst_hold(skb->dst); | ||
117 | |||
118 | skb->dev = nf_bridge->physindev; | ||
119 | if (skb->protocol == __constant_htons(ETH_P_8021Q)) { | ||
120 | skb_push(skb, VLAN_HLEN); | ||
121 | skb->nh.raw -= VLAN_HLEN; | ||
122 | } | ||
123 | NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL, | ||
124 | br_handle_frame_finish, 1); | ||
125 | |||
126 | return 0; | ||
127 | } | ||
128 | |||
129 | static void __br_dnat_complain(void) | ||
130 | { | ||
131 | static unsigned long last_complaint; | ||
132 | |||
133 | if (jiffies - last_complaint >= 5 * HZ) { | ||
134 | printk(KERN_WARNING "Performing cross-bridge DNAT requires IP " | ||
135 | "forwarding to be enabled\n"); | ||
136 | last_complaint = jiffies; | ||
137 | } | ||
138 | } | ||
139 | |||
140 | /* This requires some explaining. If DNAT has taken place, | ||
141 | * we will need to fix up the destination Ethernet address, | ||
142 | * and this is a tricky process. | ||
143 | * | ||
144 | * There are two cases to consider: | ||
145 | * 1. The packet was DNAT'ed to a device in the same bridge | ||
146 | * port group as it was received on. We can still bridge | ||
147 | * the packet. | ||
148 | * 2. The packet was DNAT'ed to a different device, either | ||
149 | * a non-bridged device or another bridge port group. | ||
150 | * The packet will need to be routed. | ||
151 | * | ||
152 | * The correct way of distinguishing between these two cases is to | ||
153 | * call ip_route_input() and to look at skb->dst->dev, which is | ||
154 | * changed to the destination device if ip_route_input() succeeds. | ||
155 | * | ||
156 | * Let us first consider the case that ip_route_input() succeeds: | ||
157 | * | ||
158 | * If skb->dst->dev equals the logical bridge device the packet | ||
159 | * came in on, we can consider this bridging. We then call | ||
160 | * skb->dst->output() which will make the packet enter br_nf_local_out() | ||
161 | * not much later. In that function it is assured that the iptables | ||
162 | * FORWARD chain is traversed for the packet. | ||
163 | * | ||
164 | * Otherwise, the packet is considered to be routed and we just | ||
165 | * change the destination MAC address so that the packet will | ||
166 | * later be passed up to the IP stack to be routed. | ||
167 | * | ||
168 | * Let us now consider the case that ip_route_input() fails: | ||
169 | * | ||
170 | * After a "echo '0' > /proc/sys/net/ipv4/ip_forward" ip_route_input() | ||
171 | * will fail, while __ip_route_output_key() will return success. The source | ||
172 | * address for __ip_route_output_key() is set to zero, so __ip_route_output_key | ||
173 | * thinks we're handling a locally generated packet and won't care | ||
174 | * if IP forwarding is allowed. We send a warning message to the users's | ||
175 | * log telling her to put IP forwarding on. | ||
176 | * | ||
177 | * ip_route_input() will also fail if there is no route available. | ||
178 | * In that case we just drop the packet. | ||
179 | * | ||
180 | * --Lennert, 20020411 | ||
181 | * --Bart, 20020416 (updated) | ||
182 | * --Bart, 20021007 (updated) */ | ||
183 | static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb) | ||
184 | { | ||
185 | #ifdef CONFIG_NETFILTER_DEBUG | ||
186 | skb->nf_debug |= (1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_FORWARD); | ||
187 | #endif | ||
188 | |||
189 | if (skb->pkt_type == PACKET_OTHERHOST) { | ||
190 | skb->pkt_type = PACKET_HOST; | ||
191 | skb->nf_bridge->mask |= BRNF_PKT_TYPE; | ||
192 | } | ||
193 | skb->nf_bridge->mask ^= BRNF_NF_BRIDGE_PREROUTING; | ||
194 | |||
195 | skb->dev = bridge_parent(skb->dev); | ||
196 | if (skb->protocol == __constant_htons(ETH_P_8021Q)) { | ||
197 | skb_pull(skb, VLAN_HLEN); | ||
198 | skb->nh.raw += VLAN_HLEN; | ||
199 | } | ||
200 | skb->dst->output(skb); | ||
201 | return 0; | ||
202 | } | ||
203 | |||
204 | static int br_nf_pre_routing_finish(struct sk_buff *skb) | ||
205 | { | ||
206 | struct net_device *dev = skb->dev; | ||
207 | struct iphdr *iph = skb->nh.iph; | ||
208 | struct nf_bridge_info *nf_bridge = skb->nf_bridge; | ||
209 | |||
210 | #ifdef CONFIG_NETFILTER_DEBUG | ||
211 | skb->nf_debug ^= (1 << NF_BR_PRE_ROUTING); | ||
212 | #endif | ||
213 | |||
214 | if (nf_bridge->mask & BRNF_PKT_TYPE) { | ||
215 | skb->pkt_type = PACKET_OTHERHOST; | ||
216 | nf_bridge->mask ^= BRNF_PKT_TYPE; | ||
217 | } | ||
218 | nf_bridge->mask ^= BRNF_NF_BRIDGE_PREROUTING; | ||
219 | |||
220 | if (dnat_took_place(skb)) { | ||
221 | if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, | ||
222 | dev)) { | ||
223 | struct rtable *rt; | ||
224 | struct flowi fl = { .nl_u = | ||
225 | { .ip4_u = { .daddr = iph->daddr, .saddr = 0 , | ||
226 | .tos = RT_TOS(iph->tos)} }, .proto = 0}; | ||
227 | |||
228 | if (!ip_route_output_key(&rt, &fl)) { | ||
229 | /* Bridged-and-DNAT'ed traffic doesn't | ||
230 | * require ip_forwarding. */ | ||
231 | if (((struct dst_entry *)rt)->dev == dev) { | ||
232 | skb->dst = (struct dst_entry *)rt; | ||
233 | goto bridged_dnat; | ||
234 | } | ||
235 | __br_dnat_complain(); | ||
236 | dst_release((struct dst_entry *)rt); | ||
237 | } | ||
238 | kfree_skb(skb); | ||
239 | return 0; | ||
240 | } else { | ||
241 | if (skb->dst->dev == dev) { | ||
242 | bridged_dnat: | ||
243 | /* Tell br_nf_local_out this is a | ||
244 | * bridged frame */ | ||
245 | nf_bridge->mask |= BRNF_BRIDGED_DNAT; | ||
246 | skb->dev = nf_bridge->physindev; | ||
247 | if (skb->protocol == | ||
248 | __constant_htons(ETH_P_8021Q)) { | ||
249 | skb_push(skb, VLAN_HLEN); | ||
250 | skb->nh.raw -= VLAN_HLEN; | ||
251 | } | ||
252 | NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, | ||
253 | skb, skb->dev, NULL, | ||
254 | br_nf_pre_routing_finish_bridge, | ||
255 | 1); | ||
256 | return 0; | ||
257 | } | ||
258 | memcpy(eth_hdr(skb)->h_dest, dev->dev_addr, | ||
259 | ETH_ALEN); | ||
260 | skb->pkt_type = PACKET_HOST; | ||
261 | } | ||
262 | } else { | ||
263 | skb->dst = (struct dst_entry *)&__fake_rtable; | ||
264 | dst_hold(skb->dst); | ||
265 | } | ||
266 | |||
267 | skb->dev = nf_bridge->physindev; | ||
268 | if (skb->protocol == __constant_htons(ETH_P_8021Q)) { | ||
269 | skb_push(skb, VLAN_HLEN); | ||
270 | skb->nh.raw -= VLAN_HLEN; | ||
271 | } | ||
272 | NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL, | ||
273 | br_handle_frame_finish, 1); | ||
274 | |||
275 | return 0; | ||
276 | } | ||
277 | |||
278 | /* Some common code for IPv4/IPv6 */ | ||
279 | static void setup_pre_routing(struct sk_buff *skb) | ||
280 | { | ||
281 | struct nf_bridge_info *nf_bridge = skb->nf_bridge; | ||
282 | |||
283 | if (skb->pkt_type == PACKET_OTHERHOST) { | ||
284 | skb->pkt_type = PACKET_HOST; | ||
285 | nf_bridge->mask |= BRNF_PKT_TYPE; | ||
286 | } | ||
287 | |||
288 | nf_bridge->mask |= BRNF_NF_BRIDGE_PREROUTING; | ||
289 | nf_bridge->physindev = skb->dev; | ||
290 | skb->dev = bridge_parent(skb->dev); | ||
291 | } | ||
292 | |||
293 | /* We only check the length. A bridge shouldn't do any hop-by-hop stuff anyway */ | ||
294 | static int check_hbh_len(struct sk_buff *skb) | ||
295 | { | ||
296 | unsigned char *raw = (u8*)(skb->nh.ipv6h+1); | ||
297 | u32 pkt_len; | ||
298 | int off = raw - skb->nh.raw; | ||
299 | int len = (raw[1]+1)<<3; | ||
300 | |||
301 | if ((raw + len) - skb->data > skb_headlen(skb)) | ||
302 | goto bad; | ||
303 | |||
304 | off += 2; | ||
305 | len -= 2; | ||
306 | |||
307 | while (len > 0) { | ||
308 | int optlen = raw[off+1]+2; | ||
309 | |||
310 | switch (skb->nh.raw[off]) { | ||
311 | case IPV6_TLV_PAD0: | ||
312 | optlen = 1; | ||
313 | break; | ||
314 | |||
315 | case IPV6_TLV_PADN: | ||
316 | break; | ||
317 | |||
318 | case IPV6_TLV_JUMBO: | ||
319 | if (skb->nh.raw[off+1] != 4 || (off&3) != 2) | ||
320 | goto bad; | ||
321 | |||
322 | pkt_len = ntohl(*(u32*)(skb->nh.raw+off+2)); | ||
323 | |||
324 | if (pkt_len > skb->len - sizeof(struct ipv6hdr)) | ||
325 | goto bad; | ||
326 | if (pkt_len + sizeof(struct ipv6hdr) < skb->len) { | ||
327 | if (__pskb_trim(skb, | ||
328 | pkt_len + sizeof(struct ipv6hdr))) | ||
329 | goto bad; | ||
330 | if (skb->ip_summed == CHECKSUM_HW) | ||
331 | skb->ip_summed = CHECKSUM_NONE; | ||
332 | } | ||
333 | break; | ||
334 | default: | ||
335 | if (optlen > len) | ||
336 | goto bad; | ||
337 | break; | ||
338 | } | ||
339 | off += optlen; | ||
340 | len -= optlen; | ||
341 | } | ||
342 | if (len == 0) | ||
343 | return 0; | ||
344 | bad: | ||
345 | return -1; | ||
346 | |||
347 | } | ||
348 | |||
349 | /* Replicate the checks that IPv6 does on packet reception and pass the packet | ||
350 | * to ip6tables, which doesn't support NAT, so things are fairly simple. */ | ||
351 | static unsigned int br_nf_pre_routing_ipv6(unsigned int hook, | ||
352 | struct sk_buff *skb, const struct net_device *in, | ||
353 | const struct net_device *out, int (*okfn)(struct sk_buff *)) | ||
354 | { | ||
355 | struct ipv6hdr *hdr; | ||
356 | u32 pkt_len; | ||
357 | struct nf_bridge_info *nf_bridge; | ||
358 | |||
359 | if (skb->len < sizeof(struct ipv6hdr)) | ||
360 | goto inhdr_error; | ||
361 | |||
362 | if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) | ||
363 | goto inhdr_error; | ||
364 | |||
365 | hdr = skb->nh.ipv6h; | ||
366 | |||
367 | if (hdr->version != 6) | ||
368 | goto inhdr_error; | ||
369 | |||
370 | pkt_len = ntohs(hdr->payload_len); | ||
371 | |||
372 | if (pkt_len || hdr->nexthdr != NEXTHDR_HOP) { | ||
373 | if (pkt_len + sizeof(struct ipv6hdr) > skb->len) | ||
374 | goto inhdr_error; | ||
375 | if (pkt_len + sizeof(struct ipv6hdr) < skb->len) { | ||
376 | if (__pskb_trim(skb, pkt_len + sizeof(struct ipv6hdr))) | ||
377 | goto inhdr_error; | ||
378 | if (skb->ip_summed == CHECKSUM_HW) | ||
379 | skb->ip_summed = CHECKSUM_NONE; | ||
380 | } | ||
381 | } | ||
382 | if (hdr->nexthdr == NEXTHDR_HOP && check_hbh_len(skb)) | ||
383 | goto inhdr_error; | ||
384 | |||
385 | #ifdef CONFIG_NETFILTER_DEBUG | ||
386 | skb->nf_debug ^= (1 << NF_IP6_PRE_ROUTING); | ||
387 | #endif | ||
388 | if ((nf_bridge = nf_bridge_alloc(skb)) == NULL) | ||
389 | return NF_DROP; | ||
390 | setup_pre_routing(skb); | ||
391 | |||
392 | NF_HOOK(PF_INET6, NF_IP6_PRE_ROUTING, skb, skb->dev, NULL, | ||
393 | br_nf_pre_routing_finish_ipv6); | ||
394 | |||
395 | return NF_STOLEN; | ||
396 | |||
397 | inhdr_error: | ||
398 | return NF_DROP; | ||
399 | } | ||
400 | |||
401 | /* Direct IPv6 traffic to br_nf_pre_routing_ipv6. | ||
402 | * Replicate the checks that IPv4 does on packet reception. | ||
403 | * Set skb->dev to the bridge device (i.e. parent of the | ||
404 | * receiving device) to make netfilter happy, the REDIRECT | ||
405 | * target in particular. Save the original destination IP | ||
406 | * address to be able to detect DNAT afterwards. */ | ||
407 | static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb, | ||
408 | const struct net_device *in, const struct net_device *out, | ||
409 | int (*okfn)(struct sk_buff *)) | ||
410 | { | ||
411 | struct iphdr *iph; | ||
412 | __u32 len; | ||
413 | struct sk_buff *skb = *pskb; | ||
414 | struct nf_bridge_info *nf_bridge; | ||
415 | struct vlan_ethhdr *hdr = vlan_eth_hdr(*pskb); | ||
416 | |||
417 | if (skb->protocol == __constant_htons(ETH_P_IPV6) || IS_VLAN_IPV6) { | ||
418 | #ifdef CONFIG_SYSCTL | ||
419 | if (!brnf_call_ip6tables) | ||
420 | return NF_ACCEPT; | ||
421 | #endif | ||
422 | if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL) | ||
423 | goto out; | ||
424 | |||
425 | if (skb->protocol == __constant_htons(ETH_P_8021Q)) { | ||
426 | skb_pull(skb, VLAN_HLEN); | ||
427 | (skb)->nh.raw += VLAN_HLEN; | ||
428 | } | ||
429 | return br_nf_pre_routing_ipv6(hook, skb, in, out, okfn); | ||
430 | } | ||
431 | #ifdef CONFIG_SYSCTL | ||
432 | if (!brnf_call_iptables) | ||
433 | return NF_ACCEPT; | ||
434 | #endif | ||
435 | |||
436 | if (skb->protocol != __constant_htons(ETH_P_IP) && !IS_VLAN_IP) | ||
437 | return NF_ACCEPT; | ||
438 | |||
439 | if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL) | ||
440 | goto out; | ||
441 | |||
442 | if (skb->protocol == __constant_htons(ETH_P_8021Q)) { | ||
443 | skb_pull(skb, VLAN_HLEN); | ||
444 | (skb)->nh.raw += VLAN_HLEN; | ||
445 | } | ||
446 | |||
447 | if (!pskb_may_pull(skb, sizeof(struct iphdr))) | ||
448 | goto inhdr_error; | ||
449 | |||
450 | iph = skb->nh.iph; | ||
451 | if (iph->ihl < 5 || iph->version != 4) | ||
452 | goto inhdr_error; | ||
453 | |||
454 | if (!pskb_may_pull(skb, 4*iph->ihl)) | ||
455 | goto inhdr_error; | ||
456 | |||
457 | iph = skb->nh.iph; | ||
458 | if (ip_fast_csum((__u8 *)iph, iph->ihl) != 0) | ||
459 | goto inhdr_error; | ||
460 | |||
461 | len = ntohs(iph->tot_len); | ||
462 | if (skb->len < len || len < 4*iph->ihl) | ||
463 | goto inhdr_error; | ||
464 | |||
465 | if (skb->len > len) { | ||
466 | __pskb_trim(skb, len); | ||
467 | if (skb->ip_summed == CHECKSUM_HW) | ||
468 | skb->ip_summed = CHECKSUM_NONE; | ||
469 | } | ||
470 | |||
471 | #ifdef CONFIG_NETFILTER_DEBUG | ||
472 | skb->nf_debug ^= (1 << NF_IP_PRE_ROUTING); | ||
473 | #endif | ||
474 | if ((nf_bridge = nf_bridge_alloc(skb)) == NULL) | ||
475 | return NF_DROP; | ||
476 | setup_pre_routing(skb); | ||
477 | store_orig_dstaddr(skb); | ||
478 | |||
479 | NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL, | ||
480 | br_nf_pre_routing_finish); | ||
481 | |||
482 | return NF_STOLEN; | ||
483 | |||
484 | inhdr_error: | ||
485 | // IP_INC_STATS_BH(IpInHdrErrors); | ||
486 | out: | ||
487 | return NF_DROP; | ||
488 | } | ||
489 | |||
490 | |||
491 | /* PF_BRIDGE/LOCAL_IN ************************************************/ | ||
492 | /* The packet is locally destined, which requires a real | ||
493 | * dst_entry, so detach the fake one. On the way up, the | ||
494 | * packet would pass through PRE_ROUTING again (which already | ||
495 | * took place when the packet entered the bridge), but we | ||
496 | * register an IPv4 PRE_ROUTING 'sabotage' hook that will | ||
497 | * prevent this from happening. */ | ||
498 | static unsigned int br_nf_local_in(unsigned int hook, struct sk_buff **pskb, | ||
499 | const struct net_device *in, const struct net_device *out, | ||
500 | int (*okfn)(struct sk_buff *)) | ||
501 | { | ||
502 | struct sk_buff *skb = *pskb; | ||
503 | |||
504 | if (skb->dst == (struct dst_entry *)&__fake_rtable) { | ||
505 | dst_release(skb->dst); | ||
506 | skb->dst = NULL; | ||
507 | } | ||
508 | |||
509 | return NF_ACCEPT; | ||
510 | } | ||
511 | |||
512 | |||
513 | /* PF_BRIDGE/FORWARD *************************************************/ | ||
514 | static int br_nf_forward_finish(struct sk_buff *skb) | ||
515 | { | ||
516 | struct nf_bridge_info *nf_bridge = skb->nf_bridge; | ||
517 | struct net_device *in; | ||
518 | struct vlan_ethhdr *hdr = vlan_eth_hdr(skb); | ||
519 | |||
520 | #ifdef CONFIG_NETFILTER_DEBUG | ||
521 | skb->nf_debug ^= (1 << NF_BR_FORWARD); | ||
522 | #endif | ||
523 | |||
524 | if (skb->protocol != __constant_htons(ETH_P_ARP) && !IS_VLAN_ARP) { | ||
525 | in = nf_bridge->physindev; | ||
526 | if (nf_bridge->mask & BRNF_PKT_TYPE) { | ||
527 | skb->pkt_type = PACKET_OTHERHOST; | ||
528 | nf_bridge->mask ^= BRNF_PKT_TYPE; | ||
529 | } | ||
530 | } else { | ||
531 | in = *((struct net_device **)(skb->cb)); | ||
532 | } | ||
533 | if (skb->protocol == __constant_htons(ETH_P_8021Q)) { | ||
534 | skb_push(skb, VLAN_HLEN); | ||
535 | skb->nh.raw -= VLAN_HLEN; | ||
536 | } | ||
537 | NF_HOOK_THRESH(PF_BRIDGE, NF_BR_FORWARD, skb, in, | ||
538 | skb->dev, br_forward_finish, 1); | ||
539 | return 0; | ||
540 | } | ||
541 | |||
542 | /* This is the 'purely bridged' case. For IP, we pass the packet to | ||
543 | * netfilter with indev and outdev set to the bridge device, | ||
544 | * but we are still able to filter on the 'real' indev/outdev | ||
545 | * because of the physdev module. For ARP, indev and outdev are the | ||
546 | * bridge ports. */ | ||
547 | static unsigned int br_nf_forward_ip(unsigned int hook, struct sk_buff **pskb, | ||
548 | const struct net_device *in, const struct net_device *out, | ||
549 | int (*okfn)(struct sk_buff *)) | ||
550 | { | ||
551 | struct sk_buff *skb = *pskb; | ||
552 | struct nf_bridge_info *nf_bridge; | ||
553 | struct vlan_ethhdr *hdr = vlan_eth_hdr(skb); | ||
554 | int pf; | ||
555 | |||
556 | if (!skb->nf_bridge) | ||
557 | return NF_ACCEPT; | ||
558 | |||
559 | if (skb->protocol == __constant_htons(ETH_P_IP) || IS_VLAN_IP) | ||
560 | pf = PF_INET; | ||
561 | else | ||
562 | pf = PF_INET6; | ||
563 | |||
564 | if (skb->protocol == __constant_htons(ETH_P_8021Q)) { | ||
565 | skb_pull(*pskb, VLAN_HLEN); | ||
566 | (*pskb)->nh.raw += VLAN_HLEN; | ||
567 | } | ||
568 | |||
569 | #ifdef CONFIG_NETFILTER_DEBUG | ||
570 | skb->nf_debug ^= (1 << NF_BR_FORWARD); | ||
571 | #endif | ||
572 | nf_bridge = skb->nf_bridge; | ||
573 | if (skb->pkt_type == PACKET_OTHERHOST) { | ||
574 | skb->pkt_type = PACKET_HOST; | ||
575 | nf_bridge->mask |= BRNF_PKT_TYPE; | ||
576 | } | ||
577 | |||
578 | /* The physdev module checks on this */ | ||
579 | nf_bridge->mask |= BRNF_BRIDGED; | ||
580 | nf_bridge->physoutdev = skb->dev; | ||
581 | |||
582 | NF_HOOK(pf, NF_IP_FORWARD, skb, bridge_parent(in), | ||
583 | bridge_parent(out), br_nf_forward_finish); | ||
584 | |||
585 | return NF_STOLEN; | ||
586 | } | ||
587 | |||
588 | static unsigned int br_nf_forward_arp(unsigned int hook, struct sk_buff **pskb, | ||
589 | const struct net_device *in, const struct net_device *out, | ||
590 | int (*okfn)(struct sk_buff *)) | ||
591 | { | ||
592 | struct sk_buff *skb = *pskb; | ||
593 | struct vlan_ethhdr *hdr = vlan_eth_hdr(skb); | ||
594 | struct net_device **d = (struct net_device **)(skb->cb); | ||
595 | |||
596 | #ifdef CONFIG_SYSCTL | ||
597 | if (!brnf_call_arptables) | ||
598 | return NF_ACCEPT; | ||
599 | #endif | ||
600 | |||
601 | if (skb->protocol != __constant_htons(ETH_P_ARP)) { | ||
602 | if (!IS_VLAN_ARP) | ||
603 | return NF_ACCEPT; | ||
604 | skb_pull(*pskb, VLAN_HLEN); | ||
605 | (*pskb)->nh.raw += VLAN_HLEN; | ||
606 | } | ||
607 | |||
608 | #ifdef CONFIG_NETFILTER_DEBUG | ||
609 | skb->nf_debug ^= (1 << NF_BR_FORWARD); | ||
610 | #endif | ||
611 | |||
612 | if (skb->nh.arph->ar_pln != 4) { | ||
613 | if (IS_VLAN_ARP) { | ||
614 | skb_push(*pskb, VLAN_HLEN); | ||
615 | (*pskb)->nh.raw -= VLAN_HLEN; | ||
616 | } | ||
617 | return NF_ACCEPT; | ||
618 | } | ||
619 | *d = (struct net_device *)in; | ||
620 | NF_HOOK(NF_ARP, NF_ARP_FORWARD, skb, (struct net_device *)in, | ||
621 | (struct net_device *)out, br_nf_forward_finish); | ||
622 | |||
623 | return NF_STOLEN; | ||
624 | } | ||
625 | |||
626 | |||
627 | /* PF_BRIDGE/LOCAL_OUT ***********************************************/ | ||
628 | static int br_nf_local_out_finish(struct sk_buff *skb) | ||
629 | { | ||
630 | #ifdef CONFIG_NETFILTER_DEBUG | ||
631 | skb->nf_debug &= ~(1 << NF_BR_LOCAL_OUT); | ||
632 | #endif | ||
633 | if (skb->protocol == __constant_htons(ETH_P_8021Q)) { | ||
634 | skb_push(skb, VLAN_HLEN); | ||
635 | skb->nh.raw -= VLAN_HLEN; | ||
636 | } | ||
637 | |||
638 | NF_HOOK_THRESH(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev, | ||
639 | br_forward_finish, NF_BR_PRI_FIRST + 1); | ||
640 | |||
641 | return 0; | ||
642 | } | ||
643 | |||
644 | /* This function sees both locally originated IP packets and forwarded | ||
645 | * IP packets (in both cases the destination device is a bridge | ||
646 | * device). It also sees bridged-and-DNAT'ed packets. | ||
647 | * To be able to filter on the physical bridge devices (with the physdev | ||
648 | * module), we steal packets destined to a bridge device away from the | ||
649 | * PF_INET/FORWARD and PF_INET/OUTPUT hook functions, and give them back later, | ||
650 | * when we have determined the real output device. This is done in here. | ||
651 | * | ||
652 | * If (nf_bridge->mask & BRNF_BRIDGED_DNAT) then the packet is bridged | ||
653 | * and we fake the PF_BRIDGE/FORWARD hook. The function br_nf_forward() | ||
654 | * will then fake the PF_INET/FORWARD hook. br_nf_local_out() has priority | ||
655 | * NF_BR_PRI_FIRST, so no relevant PF_BRIDGE/INPUT functions have been nor | ||
656 | * will be executed. | ||
657 | * Otherwise, if nf_bridge->physindev is NULL, the bridge-nf code never touched | ||
658 | * this packet before, and so the packet was locally originated. We fake | ||
659 | * the PF_INET/LOCAL_OUT hook. | ||
660 | * Finally, if nf_bridge->physindev isn't NULL, then the packet was IP routed, | ||
661 | * so we fake the PF_INET/FORWARD hook. ip_sabotage_out() makes sure | ||
662 | * even routed packets that didn't arrive on a bridge interface have their | ||
663 | * nf_bridge->physindev set. */ | ||
664 | static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff **pskb, | ||
665 | const struct net_device *in, const struct net_device *out, | ||
666 | int (*okfn)(struct sk_buff *)) | ||
667 | { | ||
668 | struct net_device *realindev, *realoutdev; | ||
669 | struct sk_buff *skb = *pskb; | ||
670 | struct nf_bridge_info *nf_bridge; | ||
671 | struct vlan_ethhdr *hdr = vlan_eth_hdr(skb); | ||
672 | int pf; | ||
673 | |||
674 | if (!skb->nf_bridge) | ||
675 | return NF_ACCEPT; | ||
676 | |||
677 | if (skb->protocol == __constant_htons(ETH_P_IP) || IS_VLAN_IP) | ||
678 | pf = PF_INET; | ||
679 | else | ||
680 | pf = PF_INET6; | ||
681 | |||
682 | #ifdef CONFIG_NETFILTER_DEBUG | ||
683 | /* Sometimes we get packets with NULL ->dst here (for example, | ||
684 | * running a dhcp client daemon triggers this). This should now | ||
685 | * be fixed, but let's keep the check around. */ | ||
686 | if (skb->dst == NULL) { | ||
687 | printk(KERN_CRIT "br_netfilter: skb->dst == NULL."); | ||
688 | return NF_ACCEPT; | ||
689 | } | ||
690 | #endif | ||
691 | |||
692 | nf_bridge = skb->nf_bridge; | ||
693 | nf_bridge->physoutdev = skb->dev; | ||
694 | realindev = nf_bridge->physindev; | ||
695 | |||
696 | /* Bridged, take PF_BRIDGE/FORWARD. | ||
697 | * (see big note in front of br_nf_pre_routing_finish) */ | ||
698 | if (nf_bridge->mask & BRNF_BRIDGED_DNAT) { | ||
699 | if (nf_bridge->mask & BRNF_PKT_TYPE) { | ||
700 | skb->pkt_type = PACKET_OTHERHOST; | ||
701 | nf_bridge->mask ^= BRNF_PKT_TYPE; | ||
702 | } | ||
703 | if (skb->protocol == __constant_htons(ETH_P_8021Q)) { | ||
704 | skb_push(skb, VLAN_HLEN); | ||
705 | skb->nh.raw -= VLAN_HLEN; | ||
706 | } | ||
707 | |||
708 | NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, realindev, | ||
709 | skb->dev, br_forward_finish); | ||
710 | goto out; | ||
711 | } | ||
712 | realoutdev = bridge_parent(skb->dev); | ||
713 | |||
714 | #if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) | ||
715 | /* iptables should match -o br0.x */ | ||
716 | if (nf_bridge->netoutdev) | ||
717 | realoutdev = nf_bridge->netoutdev; | ||
718 | #endif | ||
719 | if (skb->protocol == __constant_htons(ETH_P_8021Q)) { | ||
720 | skb_pull(skb, VLAN_HLEN); | ||
721 | (*pskb)->nh.raw += VLAN_HLEN; | ||
722 | } | ||
723 | /* IP forwarded traffic has a physindev, locally | ||
724 | * generated traffic hasn't. */ | ||
725 | if (realindev != NULL) { | ||
726 | if (!(nf_bridge->mask & BRNF_DONT_TAKE_PARENT) && | ||
727 | has_bridge_parent(realindev)) | ||
728 | realindev = bridge_parent(realindev); | ||
729 | |||
730 | NF_HOOK_THRESH(pf, NF_IP_FORWARD, skb, realindev, | ||
731 | realoutdev, br_nf_local_out_finish, | ||
732 | NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD + 1); | ||
733 | } else { | ||
734 | #ifdef CONFIG_NETFILTER_DEBUG | ||
735 | skb->nf_debug ^= (1 << NF_IP_LOCAL_OUT); | ||
736 | #endif | ||
737 | |||
738 | NF_HOOK_THRESH(pf, NF_IP_LOCAL_OUT, skb, realindev, | ||
739 | realoutdev, br_nf_local_out_finish, | ||
740 | NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT + 1); | ||
741 | } | ||
742 | |||
743 | out: | ||
744 | return NF_STOLEN; | ||
745 | } | ||
746 | |||
747 | |||
748 | /* PF_BRIDGE/POST_ROUTING ********************************************/ | ||
749 | static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb, | ||
750 | const struct net_device *in, const struct net_device *out, | ||
751 | int (*okfn)(struct sk_buff *)) | ||
752 | { | ||
753 | struct sk_buff *skb = *pskb; | ||
754 | struct nf_bridge_info *nf_bridge = (*pskb)->nf_bridge; | ||
755 | struct vlan_ethhdr *hdr = vlan_eth_hdr(skb); | ||
756 | struct net_device *realoutdev = bridge_parent(skb->dev); | ||
757 | int pf; | ||
758 | |||
759 | #ifdef CONFIG_NETFILTER_DEBUG | ||
760 | /* Be very paranoid. This probably won't happen anymore, but let's | ||
761 | * keep the check just to be sure... */ | ||
762 | if (skb->mac.raw < skb->head || skb->mac.raw + ETH_HLEN > skb->data) { | ||
763 | printk(KERN_CRIT "br_netfilter: Argh!! br_nf_post_routing: " | ||
764 | "bad mac.raw pointer."); | ||
765 | goto print_error; | ||
766 | } | ||
767 | #endif | ||
768 | |||
769 | if (!nf_bridge) | ||
770 | return NF_ACCEPT; | ||
771 | |||
772 | if (skb->protocol == __constant_htons(ETH_P_IP) || IS_VLAN_IP) | ||
773 | pf = PF_INET; | ||
774 | else | ||
775 | pf = PF_INET6; | ||
776 | |||
777 | #ifdef CONFIG_NETFILTER_DEBUG | ||
778 | if (skb->dst == NULL) { | ||
779 | printk(KERN_CRIT "br_netfilter: skb->dst == NULL."); | ||
780 | goto print_error; | ||
781 | } | ||
782 | |||
783 | skb->nf_debug ^= (1 << NF_IP_POST_ROUTING); | ||
784 | #endif | ||
785 | |||
786 | /* We assume any code from br_dev_queue_push_xmit onwards doesn't care | ||
787 | * about the value of skb->pkt_type. */ | ||
788 | if (skb->pkt_type == PACKET_OTHERHOST) { | ||
789 | skb->pkt_type = PACKET_HOST; | ||
790 | nf_bridge->mask |= BRNF_PKT_TYPE; | ||
791 | } | ||
792 | |||
793 | if (skb->protocol == __constant_htons(ETH_P_8021Q)) { | ||
794 | skb_pull(skb, VLAN_HLEN); | ||
795 | skb->nh.raw += VLAN_HLEN; | ||
796 | } | ||
797 | |||
798 | nf_bridge_save_header(skb); | ||
799 | |||
800 | #if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) | ||
801 | if (nf_bridge->netoutdev) | ||
802 | realoutdev = nf_bridge->netoutdev; | ||
803 | #endif | ||
804 | NF_HOOK(pf, NF_IP_POST_ROUTING, skb, NULL, realoutdev, | ||
805 | br_dev_queue_push_xmit); | ||
806 | |||
807 | return NF_STOLEN; | ||
808 | |||
809 | #ifdef CONFIG_NETFILTER_DEBUG | ||
810 | print_error: | ||
811 | if (skb->dev != NULL) { | ||
812 | printk("[%s]", skb->dev->name); | ||
813 | if (has_bridge_parent(skb->dev)) | ||
814 | printk("[%s]", bridge_parent(skb->dev)->name); | ||
815 | } | ||
816 | printk(" head:%p, raw:%p, data:%p\n", skb->head, skb->mac.raw, | ||
817 | skb->data); | ||
818 | return NF_ACCEPT; | ||
819 | #endif | ||
820 | } | ||
821 | |||
822 | |||
823 | /* IP/SABOTAGE *****************************************************/ | ||
824 | /* Don't hand locally destined packets to PF_INET(6)/PRE_ROUTING | ||
825 | * for the second time. */ | ||
826 | static unsigned int ip_sabotage_in(unsigned int hook, struct sk_buff **pskb, | ||
827 | const struct net_device *in, const struct net_device *out, | ||
828 | int (*okfn)(struct sk_buff *)) | ||
829 | { | ||
830 | if ((*pskb)->nf_bridge && | ||
831 | !((*pskb)->nf_bridge->mask & BRNF_NF_BRIDGE_PREROUTING)) { | ||
832 | return NF_STOP; | ||
833 | } | ||
834 | |||
835 | return NF_ACCEPT; | ||
836 | } | ||
837 | |||
838 | /* Postpone execution of PF_INET(6)/FORWARD, PF_INET(6)/LOCAL_OUT | ||
839 | * and PF_INET(6)/POST_ROUTING until we have done the forwarding | ||
840 | * decision in the bridge code and have determined nf_bridge->physoutdev. */ | ||
841 | static unsigned int ip_sabotage_out(unsigned int hook, struct sk_buff **pskb, | ||
842 | const struct net_device *in, const struct net_device *out, | ||
843 | int (*okfn)(struct sk_buff *)) | ||
844 | { | ||
845 | struct sk_buff *skb = *pskb; | ||
846 | |||
847 | if ((out->hard_start_xmit == br_dev_xmit && | ||
848 | okfn != br_nf_forward_finish && | ||
849 | okfn != br_nf_local_out_finish && | ||
850 | okfn != br_dev_queue_push_xmit) | ||
851 | #if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) | ||
852 | || ((out->priv_flags & IFF_802_1Q_VLAN) && | ||
853 | VLAN_DEV_INFO(out)->real_dev->hard_start_xmit == br_dev_xmit) | ||
854 | #endif | ||
855 | ) { | ||
856 | struct nf_bridge_info *nf_bridge; | ||
857 | |||
858 | if (!skb->nf_bridge) { | ||
859 | #ifdef CONFIG_SYSCTL | ||
860 | /* This code is executed while in the IP(v6) stack, | ||
861 | the version should be 4 or 6. We can't use | ||
862 | skb->protocol because that isn't set on | ||
863 | PF_INET(6)/LOCAL_OUT. */ | ||
864 | struct iphdr *ip = skb->nh.iph; | ||
865 | |||
866 | if (ip->version == 4 && !brnf_call_iptables) | ||
867 | return NF_ACCEPT; | ||
868 | else if (ip->version == 6 && !brnf_call_ip6tables) | ||
869 | return NF_ACCEPT; | ||
870 | #endif | ||
871 | if (hook == NF_IP_POST_ROUTING) | ||
872 | return NF_ACCEPT; | ||
873 | if (!nf_bridge_alloc(skb)) | ||
874 | return NF_DROP; | ||
875 | } | ||
876 | |||
877 | nf_bridge = skb->nf_bridge; | ||
878 | |||
879 | /* This frame will arrive on PF_BRIDGE/LOCAL_OUT and we | ||
880 | * will need the indev then. For a brouter, the real indev | ||
881 | * can be a bridge port, so we make sure br_nf_local_out() | ||
882 | * doesn't use the bridge parent of the indev by using | ||
883 | * the BRNF_DONT_TAKE_PARENT mask. */ | ||
884 | if (hook == NF_IP_FORWARD && nf_bridge->physindev == NULL) { | ||
885 | nf_bridge->mask &= BRNF_DONT_TAKE_PARENT; | ||
886 | nf_bridge->physindev = (struct net_device *)in; | ||
887 | } | ||
888 | #if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) | ||
889 | /* the iptables outdev is br0.x, not br0 */ | ||
890 | if (out->priv_flags & IFF_802_1Q_VLAN) | ||
891 | nf_bridge->netoutdev = (struct net_device *)out; | ||
892 | #endif | ||
893 | return NF_STOP; | ||
894 | } | ||
895 | |||
896 | return NF_ACCEPT; | ||
897 | } | ||
898 | |||
899 | /* For br_nf_local_out we need (prio = NF_BR_PRI_FIRST), to insure that innocent | ||
900 | * PF_BRIDGE/NF_BR_LOCAL_OUT functions don't get bridged traffic as input. | ||
901 | * For br_nf_post_routing, we need (prio = NF_BR_PRI_LAST), because | ||
902 | * ip_refrag() can return NF_STOLEN. */ | ||
903 | static struct nf_hook_ops br_nf_ops[] = { | ||
904 | { .hook = br_nf_pre_routing, | ||
905 | .owner = THIS_MODULE, | ||
906 | .pf = PF_BRIDGE, | ||
907 | .hooknum = NF_BR_PRE_ROUTING, | ||
908 | .priority = NF_BR_PRI_BRNF, }, | ||
909 | { .hook = br_nf_local_in, | ||
910 | .owner = THIS_MODULE, | ||
911 | .pf = PF_BRIDGE, | ||
912 | .hooknum = NF_BR_LOCAL_IN, | ||
913 | .priority = NF_BR_PRI_BRNF, }, | ||
914 | { .hook = br_nf_forward_ip, | ||
915 | .owner = THIS_MODULE, | ||
916 | .pf = PF_BRIDGE, | ||
917 | .hooknum = NF_BR_FORWARD, | ||
918 | .priority = NF_BR_PRI_BRNF - 1, }, | ||
919 | { .hook = br_nf_forward_arp, | ||
920 | .owner = THIS_MODULE, | ||
921 | .pf = PF_BRIDGE, | ||
922 | .hooknum = NF_BR_FORWARD, | ||
923 | .priority = NF_BR_PRI_BRNF, }, | ||
924 | { .hook = br_nf_local_out, | ||
925 | .owner = THIS_MODULE, | ||
926 | .pf = PF_BRIDGE, | ||
927 | .hooknum = NF_BR_LOCAL_OUT, | ||
928 | .priority = NF_BR_PRI_FIRST, }, | ||
929 | { .hook = br_nf_post_routing, | ||
930 | .owner = THIS_MODULE, | ||
931 | .pf = PF_BRIDGE, | ||
932 | .hooknum = NF_BR_POST_ROUTING, | ||
933 | .priority = NF_BR_PRI_LAST, }, | ||
934 | { .hook = ip_sabotage_in, | ||
935 | .owner = THIS_MODULE, | ||
936 | .pf = PF_INET, | ||
937 | .hooknum = NF_IP_PRE_ROUTING, | ||
938 | .priority = NF_IP_PRI_FIRST, }, | ||
939 | { .hook = ip_sabotage_in, | ||
940 | .owner = THIS_MODULE, | ||
941 | .pf = PF_INET6, | ||
942 | .hooknum = NF_IP6_PRE_ROUTING, | ||
943 | .priority = NF_IP6_PRI_FIRST, }, | ||
944 | { .hook = ip_sabotage_out, | ||
945 | .owner = THIS_MODULE, | ||
946 | .pf = PF_INET, | ||
947 | .hooknum = NF_IP_FORWARD, | ||
948 | .priority = NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD, }, | ||
949 | { .hook = ip_sabotage_out, | ||
950 | .owner = THIS_MODULE, | ||
951 | .pf = PF_INET6, | ||
952 | .hooknum = NF_IP6_FORWARD, | ||
953 | .priority = NF_IP6_PRI_BRIDGE_SABOTAGE_FORWARD, }, | ||
954 | { .hook = ip_sabotage_out, | ||
955 | .owner = THIS_MODULE, | ||
956 | .pf = PF_INET, | ||
957 | .hooknum = NF_IP_LOCAL_OUT, | ||
958 | .priority = NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT, }, | ||
959 | { .hook = ip_sabotage_out, | ||
960 | .owner = THIS_MODULE, | ||
961 | .pf = PF_INET6, | ||
962 | .hooknum = NF_IP6_LOCAL_OUT, | ||
963 | .priority = NF_IP6_PRI_BRIDGE_SABOTAGE_LOCAL_OUT, }, | ||
964 | { .hook = ip_sabotage_out, | ||
965 | .owner = THIS_MODULE, | ||
966 | .pf = PF_INET, | ||
967 | .hooknum = NF_IP_POST_ROUTING, | ||
968 | .priority = NF_IP_PRI_FIRST, }, | ||
969 | { .hook = ip_sabotage_out, | ||
970 | .owner = THIS_MODULE, | ||
971 | .pf = PF_INET6, | ||
972 | .hooknum = NF_IP6_POST_ROUTING, | ||
973 | .priority = NF_IP6_PRI_FIRST, }, | ||
974 | }; | ||
975 | |||
976 | #ifdef CONFIG_SYSCTL | ||
977 | static | ||
978 | int brnf_sysctl_call_tables(ctl_table *ctl, int write, struct file * filp, | ||
979 | void __user *buffer, size_t *lenp, loff_t *ppos) | ||
980 | { | ||
981 | int ret; | ||
982 | |||
983 | ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos); | ||
984 | |||
985 | if (write && *(int *)(ctl->data)) | ||
986 | *(int *)(ctl->data) = 1; | ||
987 | return ret; | ||
988 | } | ||
989 | |||
990 | static ctl_table brnf_table[] = { | ||
991 | { | ||
992 | .ctl_name = NET_BRIDGE_NF_CALL_ARPTABLES, | ||
993 | .procname = "bridge-nf-call-arptables", | ||
994 | .data = &brnf_call_arptables, | ||
995 | .maxlen = sizeof(int), | ||
996 | .mode = 0644, | ||
997 | .proc_handler = &brnf_sysctl_call_tables, | ||
998 | }, | ||
999 | { | ||
1000 | .ctl_name = NET_BRIDGE_NF_CALL_IPTABLES, | ||
1001 | .procname = "bridge-nf-call-iptables", | ||
1002 | .data = &brnf_call_iptables, | ||
1003 | .maxlen = sizeof(int), | ||
1004 | .mode = 0644, | ||
1005 | .proc_handler = &brnf_sysctl_call_tables, | ||
1006 | }, | ||
1007 | { | ||
1008 | .ctl_name = NET_BRIDGE_NF_CALL_IP6TABLES, | ||
1009 | .procname = "bridge-nf-call-ip6tables", | ||
1010 | .data = &brnf_call_ip6tables, | ||
1011 | .maxlen = sizeof(int), | ||
1012 | .mode = 0644, | ||
1013 | .proc_handler = &brnf_sysctl_call_tables, | ||
1014 | }, | ||
1015 | { | ||
1016 | .ctl_name = NET_BRIDGE_NF_FILTER_VLAN_TAGGED, | ||
1017 | .procname = "bridge-nf-filter-vlan-tagged", | ||
1018 | .data = &brnf_filter_vlan_tagged, | ||
1019 | .maxlen = sizeof(int), | ||
1020 | .mode = 0644, | ||
1021 | .proc_handler = &brnf_sysctl_call_tables, | ||
1022 | }, | ||
1023 | { .ctl_name = 0 } | ||
1024 | }; | ||
1025 | |||
1026 | static ctl_table brnf_bridge_table[] = { | ||
1027 | { | ||
1028 | .ctl_name = NET_BRIDGE, | ||
1029 | .procname = "bridge", | ||
1030 | .mode = 0555, | ||
1031 | .child = brnf_table, | ||
1032 | }, | ||
1033 | { .ctl_name = 0 } | ||
1034 | }; | ||
1035 | |||
1036 | static ctl_table brnf_net_table[] = { | ||
1037 | { | ||
1038 | .ctl_name = CTL_NET, | ||
1039 | .procname = "net", | ||
1040 | .mode = 0555, | ||
1041 | .child = brnf_bridge_table, | ||
1042 | }, | ||
1043 | { .ctl_name = 0 } | ||
1044 | }; | ||
1045 | #endif | ||
1046 | |||
1047 | int br_netfilter_init(void) | ||
1048 | { | ||
1049 | int i; | ||
1050 | |||
1051 | for (i = 0; i < ARRAY_SIZE(br_nf_ops); i++) { | ||
1052 | int ret; | ||
1053 | |||
1054 | if ((ret = nf_register_hook(&br_nf_ops[i])) >= 0) | ||
1055 | continue; | ||
1056 | |||
1057 | while (i--) | ||
1058 | nf_unregister_hook(&br_nf_ops[i]); | ||
1059 | |||
1060 | return ret; | ||
1061 | } | ||
1062 | |||
1063 | #ifdef CONFIG_SYSCTL | ||
1064 | brnf_sysctl_header = register_sysctl_table(brnf_net_table, 0); | ||
1065 | if (brnf_sysctl_header == NULL) { | ||
1066 | printk(KERN_WARNING "br_netfilter: can't register to sysctl.\n"); | ||
1067 | for (i = 0; i < ARRAY_SIZE(br_nf_ops); i++) | ||
1068 | nf_unregister_hook(&br_nf_ops[i]); | ||
1069 | return -EFAULT; | ||
1070 | } | ||
1071 | #endif | ||
1072 | |||
1073 | printk(KERN_NOTICE "Bridge firewalling registered\n"); | ||
1074 | |||
1075 | return 0; | ||
1076 | } | ||
1077 | |||
1078 | void br_netfilter_fini(void) | ||
1079 | { | ||
1080 | int i; | ||
1081 | |||
1082 | for (i = ARRAY_SIZE(br_nf_ops) - 1; i >= 0; i--) | ||
1083 | nf_unregister_hook(&br_nf_ops[i]); | ||
1084 | #ifdef CONFIG_SYSCTL | ||
1085 | unregister_sysctl_table(brnf_sysctl_header); | ||
1086 | #endif | ||
1087 | } | ||
diff --git a/net/bridge/br_notify.c b/net/bridge/br_notify.c new file mode 100644 index 000000000000..f8fb49e34764 --- /dev/null +++ b/net/bridge/br_notify.c | |||
@@ -0,0 +1,87 @@ | |||
1 | /* | ||
2 | * Device event handling | ||
3 | * Linux ethernet bridge | ||
4 | * | ||
5 | * Authors: | ||
6 | * Lennert Buytenhek <buytenh@gnu.org> | ||
7 | * | ||
8 | * $Id: br_notify.c,v 1.2 2000/02/21 15:51:34 davem Exp $ | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or | ||
11 | * modify it under the terms of the GNU General Public License | ||
12 | * as published by the Free Software Foundation; either version | ||
13 | * 2 of the License, or (at your option) any later version. | ||
14 | */ | ||
15 | |||
16 | #include <linux/kernel.h> | ||
17 | |||
18 | #include "br_private.h" | ||
19 | |||
20 | static int br_device_event(struct notifier_block *unused, unsigned long event, void *ptr); | ||
21 | |||
22 | struct notifier_block br_device_notifier = { | ||
23 | .notifier_call = br_device_event | ||
24 | }; | ||
25 | |||
26 | /* | ||
27 | * Handle changes in state of network devices enslaved to a bridge. | ||
28 | * | ||
29 | * Note: don't care about up/down if bridge itself is down, because | ||
30 | * port state is checked when bridge is brought up. | ||
31 | */ | ||
32 | static int br_device_event(struct notifier_block *unused, unsigned long event, void *ptr) | ||
33 | { | ||
34 | struct net_device *dev = ptr; | ||
35 | struct net_bridge_port *p = dev->br_port; | ||
36 | struct net_bridge *br; | ||
37 | |||
38 | /* not a port of a bridge */ | ||
39 | if (p == NULL) | ||
40 | return NOTIFY_DONE; | ||
41 | |||
42 | br = p->br; | ||
43 | |||
44 | spin_lock_bh(&br->lock); | ||
45 | switch (event) { | ||
46 | case NETDEV_CHANGEMTU: | ||
47 | dev_set_mtu(br->dev, br_min_mtu(br)); | ||
48 | break; | ||
49 | |||
50 | case NETDEV_CHANGEADDR: | ||
51 | br_fdb_changeaddr(p, dev->dev_addr); | ||
52 | br_stp_recalculate_bridge_id(br); | ||
53 | break; | ||
54 | |||
55 | case NETDEV_CHANGE: /* device is up but carrier changed */ | ||
56 | if (!(br->dev->flags & IFF_UP)) | ||
57 | break; | ||
58 | |||
59 | if (netif_carrier_ok(dev)) { | ||
60 | if (p->state == BR_STATE_DISABLED) | ||
61 | br_stp_enable_port(p); | ||
62 | } else { | ||
63 | if (p->state != BR_STATE_DISABLED) | ||
64 | br_stp_disable_port(p); | ||
65 | } | ||
66 | break; | ||
67 | |||
68 | case NETDEV_DOWN: | ||
69 | if (br->dev->flags & IFF_UP) | ||
70 | br_stp_disable_port(p); | ||
71 | break; | ||
72 | |||
73 | case NETDEV_UP: | ||
74 | if (netif_carrier_ok(dev) && (br->dev->flags & IFF_UP)) | ||
75 | br_stp_enable_port(p); | ||
76 | break; | ||
77 | |||
78 | case NETDEV_UNREGISTER: | ||
79 | spin_unlock_bh(&br->lock); | ||
80 | br_del_if(br, dev); | ||
81 | goto done; | ||
82 | } | ||
83 | spin_unlock_bh(&br->lock); | ||
84 | |||
85 | done: | ||
86 | return NOTIFY_DONE; | ||
87 | } | ||
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h new file mode 100644 index 000000000000..54d63f1372a0 --- /dev/null +++ b/net/bridge/br_private.h | |||
@@ -0,0 +1,244 @@ | |||
1 | /* | ||
2 | * Linux ethernet bridge | ||
3 | * | ||
4 | * Authors: | ||
5 | * Lennert Buytenhek <buytenh@gnu.org> | ||
6 | * | ||
7 | * $Id: br_private.h,v 1.7 2001/12/24 00:59:55 davem Exp $ | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License | ||
11 | * as published by the Free Software Foundation; either version | ||
12 | * 2 of the License, or (at your option) any later version. | ||
13 | */ | ||
14 | |||
15 | #ifndef _BR_PRIVATE_H | ||
16 | #define _BR_PRIVATE_H | ||
17 | |||
18 | #include <linux/netdevice.h> | ||
19 | #include <linux/miscdevice.h> | ||
20 | #include <linux/if_bridge.h> | ||
21 | |||
22 | #define BR_HASH_BITS 8 | ||
23 | #define BR_HASH_SIZE (1 << BR_HASH_BITS) | ||
24 | |||
25 | #define BR_HOLD_TIME (1*HZ) | ||
26 | |||
27 | #define BR_PORT_BITS 10 | ||
28 | #define BR_MAX_PORTS (1<<BR_PORT_BITS) | ||
29 | |||
30 | typedef struct bridge_id bridge_id; | ||
31 | typedef struct mac_addr mac_addr; | ||
32 | typedef __u16 port_id; | ||
33 | |||
34 | struct bridge_id | ||
35 | { | ||
36 | unsigned char prio[2]; | ||
37 | unsigned char addr[6]; | ||
38 | }; | ||
39 | |||
40 | struct mac_addr | ||
41 | { | ||
42 | unsigned char addr[6]; | ||
43 | }; | ||
44 | |||
45 | struct net_bridge_fdb_entry | ||
46 | { | ||
47 | struct hlist_node hlist; | ||
48 | struct net_bridge_port *dst; | ||
49 | |||
50 | struct rcu_head rcu; | ||
51 | atomic_t use_count; | ||
52 | unsigned long ageing_timer; | ||
53 | mac_addr addr; | ||
54 | unsigned char is_local; | ||
55 | unsigned char is_static; | ||
56 | }; | ||
57 | |||
58 | struct net_bridge_port | ||
59 | { | ||
60 | struct net_bridge *br; | ||
61 | struct net_device *dev; | ||
62 | struct list_head list; | ||
63 | |||
64 | /* STP */ | ||
65 | u8 priority; | ||
66 | u8 state; | ||
67 | u16 port_no; | ||
68 | unsigned char topology_change_ack; | ||
69 | unsigned char config_pending; | ||
70 | port_id port_id; | ||
71 | port_id designated_port; | ||
72 | bridge_id designated_root; | ||
73 | bridge_id designated_bridge; | ||
74 | u32 path_cost; | ||
75 | u32 designated_cost; | ||
76 | |||
77 | struct timer_list forward_delay_timer; | ||
78 | struct timer_list hold_timer; | ||
79 | struct timer_list message_age_timer; | ||
80 | struct kobject kobj; | ||
81 | struct rcu_head rcu; | ||
82 | }; | ||
83 | |||
84 | struct net_bridge | ||
85 | { | ||
86 | spinlock_t lock; | ||
87 | struct list_head port_list; | ||
88 | struct net_device *dev; | ||
89 | struct net_device_stats statistics; | ||
90 | spinlock_t hash_lock; | ||
91 | struct hlist_head hash[BR_HASH_SIZE]; | ||
92 | struct list_head age_list; | ||
93 | |||
94 | /* STP */ | ||
95 | bridge_id designated_root; | ||
96 | bridge_id bridge_id; | ||
97 | u32 root_path_cost; | ||
98 | unsigned long max_age; | ||
99 | unsigned long hello_time; | ||
100 | unsigned long forward_delay; | ||
101 | unsigned long bridge_max_age; | ||
102 | unsigned long ageing_time; | ||
103 | unsigned long bridge_hello_time; | ||
104 | unsigned long bridge_forward_delay; | ||
105 | |||
106 | u16 root_port; | ||
107 | unsigned char stp_enabled; | ||
108 | unsigned char topology_change; | ||
109 | unsigned char topology_change_detected; | ||
110 | |||
111 | struct timer_list hello_timer; | ||
112 | struct timer_list tcn_timer; | ||
113 | struct timer_list topology_change_timer; | ||
114 | struct timer_list gc_timer; | ||
115 | struct kobject ifobj; | ||
116 | }; | ||
117 | |||
118 | extern struct notifier_block br_device_notifier; | ||
119 | extern const unsigned char bridge_ula[6]; | ||
120 | |||
121 | /* called under bridge lock */ | ||
122 | static inline int br_is_root_bridge(const struct net_bridge *br) | ||
123 | { | ||
124 | return !memcmp(&br->bridge_id, &br->designated_root, 8); | ||
125 | } | ||
126 | |||
127 | |||
128 | /* br_device.c */ | ||
129 | extern void br_dev_setup(struct net_device *dev); | ||
130 | extern int br_dev_xmit(struct sk_buff *skb, struct net_device *dev); | ||
131 | |||
132 | /* br_fdb.c */ | ||
133 | extern void br_fdb_init(void); | ||
134 | extern void br_fdb_fini(void); | ||
135 | extern void br_fdb_changeaddr(struct net_bridge_port *p, | ||
136 | const unsigned char *newaddr); | ||
137 | extern void br_fdb_cleanup(unsigned long arg); | ||
138 | extern void br_fdb_delete_by_port(struct net_bridge *br, | ||
139 | struct net_bridge_port *p); | ||
140 | extern struct net_bridge_fdb_entry *__br_fdb_get(struct net_bridge *br, | ||
141 | const unsigned char *addr); | ||
142 | extern struct net_bridge_fdb_entry *br_fdb_get(struct net_bridge *br, | ||
143 | unsigned char *addr); | ||
144 | extern void br_fdb_put(struct net_bridge_fdb_entry *ent); | ||
145 | extern int br_fdb_fillbuf(struct net_bridge *br, void *buf, | ||
146 | unsigned long count, unsigned long off); | ||
147 | extern int br_fdb_insert(struct net_bridge *br, | ||
148 | struct net_bridge_port *source, | ||
149 | const unsigned char *addr); | ||
150 | extern void br_fdb_update(struct net_bridge *br, | ||
151 | struct net_bridge_port *source, | ||
152 | const unsigned char *addr); | ||
153 | |||
154 | /* br_forward.c */ | ||
155 | extern void br_deliver(const struct net_bridge_port *to, | ||
156 | struct sk_buff *skb); | ||
157 | extern int br_dev_queue_push_xmit(struct sk_buff *skb); | ||
158 | extern void br_forward(const struct net_bridge_port *to, | ||
159 | struct sk_buff *skb); | ||
160 | extern int br_forward_finish(struct sk_buff *skb); | ||
161 | extern void br_flood_deliver(struct net_bridge *br, | ||
162 | struct sk_buff *skb, | ||
163 | int clone); | ||
164 | extern void br_flood_forward(struct net_bridge *br, | ||
165 | struct sk_buff *skb, | ||
166 | int clone); | ||
167 | |||
168 | /* br_if.c */ | ||
169 | extern int br_add_bridge(const char *name); | ||
170 | extern int br_del_bridge(const char *name); | ||
171 | extern void br_cleanup_bridges(void); | ||
172 | extern int br_add_if(struct net_bridge *br, | ||
173 | struct net_device *dev); | ||
174 | extern int br_del_if(struct net_bridge *br, | ||
175 | struct net_device *dev); | ||
176 | extern int br_min_mtu(const struct net_bridge *br); | ||
177 | |||
178 | /* br_input.c */ | ||
179 | extern int br_handle_frame_finish(struct sk_buff *skb); | ||
180 | extern int br_handle_frame(struct net_bridge_port *p, struct sk_buff **pskb); | ||
181 | |||
182 | /* br_ioctl.c */ | ||
183 | extern int br_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); | ||
184 | extern int br_ioctl_deviceless_stub(unsigned int cmd, void __user *arg); | ||
185 | |||
186 | /* br_netfilter.c */ | ||
187 | extern int br_netfilter_init(void); | ||
188 | extern void br_netfilter_fini(void); | ||
189 | |||
190 | /* br_stp.c */ | ||
191 | extern void br_log_state(const struct net_bridge_port *p); | ||
192 | extern struct net_bridge_port *br_get_port(struct net_bridge *br, | ||
193 | u16 port_no); | ||
194 | extern void br_init_port(struct net_bridge_port *p); | ||
195 | extern void br_become_designated_port(struct net_bridge_port *p); | ||
196 | |||
197 | /* br_stp_if.c */ | ||
198 | extern void br_stp_enable_bridge(struct net_bridge *br); | ||
199 | extern void br_stp_disable_bridge(struct net_bridge *br); | ||
200 | extern void br_stp_enable_port(struct net_bridge_port *p); | ||
201 | extern void br_stp_disable_port(struct net_bridge_port *p); | ||
202 | extern void br_stp_recalculate_bridge_id(struct net_bridge *br); | ||
203 | extern void br_stp_set_bridge_priority(struct net_bridge *br, | ||
204 | u16 newprio); | ||
205 | extern void br_stp_set_port_priority(struct net_bridge_port *p, | ||
206 | u8 newprio); | ||
207 | extern void br_stp_set_path_cost(struct net_bridge_port *p, | ||
208 | u32 path_cost); | ||
209 | extern ssize_t br_show_bridge_id(char *buf, const struct bridge_id *id); | ||
210 | |||
211 | /* br_stp_bpdu.c */ | ||
212 | extern int br_stp_handle_bpdu(struct sk_buff *skb); | ||
213 | |||
214 | /* br_stp_timer.c */ | ||
215 | extern void br_stp_timer_init(struct net_bridge *br); | ||
216 | extern void br_stp_port_timer_init(struct net_bridge_port *p); | ||
217 | extern unsigned long br_timer_value(const struct timer_list *timer); | ||
218 | |||
219 | /* br.c */ | ||
220 | extern struct net_bridge_fdb_entry *(*br_fdb_get_hook)(struct net_bridge *br, | ||
221 | unsigned char *addr); | ||
222 | extern void (*br_fdb_put_hook)(struct net_bridge_fdb_entry *ent); | ||
223 | |||
224 | |||
225 | #ifdef CONFIG_SYSFS | ||
226 | /* br_sysfs_if.c */ | ||
227 | extern int br_sysfs_addif(struct net_bridge_port *p); | ||
228 | extern void br_sysfs_removeif(struct net_bridge_port *p); | ||
229 | extern void br_sysfs_freeif(struct net_bridge_port *p); | ||
230 | |||
231 | /* br_sysfs_br.c */ | ||
232 | extern int br_sysfs_addbr(struct net_device *dev); | ||
233 | extern void br_sysfs_delbr(struct net_device *dev); | ||
234 | |||
235 | #else | ||
236 | |||
237 | #define br_sysfs_addif(p) (0) | ||
238 | #define br_sysfs_removeif(p) do { } while(0) | ||
239 | #define br_sysfs_freeif(p) kfree(p) | ||
240 | #define br_sysfs_addbr(dev) (0) | ||
241 | #define br_sysfs_delbr(dev) do { } while(0) | ||
242 | #endif /* CONFIG_SYSFS */ | ||
243 | |||
244 | #endif | ||
diff --git a/net/bridge/br_private_stp.h b/net/bridge/br_private_stp.h new file mode 100644 index 000000000000..e29f01ac1adf --- /dev/null +++ b/net/bridge/br_private_stp.h | |||
@@ -0,0 +1,58 @@ | |||
1 | /* | ||
2 | * Linux ethernet bridge | ||
3 | * | ||
4 | * Authors: | ||
5 | * Lennert Buytenhek <buytenh@gnu.org> | ||
6 | * | ||
7 | * $Id: br_private_stp.h,v 1.3 2001/02/05 06:03:47 davem Exp $ | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License | ||
11 | * as published by the Free Software Foundation; either version | ||
12 | * 2 of the License, or (at your option) any later version. | ||
13 | */ | ||
14 | |||
15 | #ifndef _BR_PRIVATE_STP_H | ||
16 | #define _BR_PRIVATE_STP_H | ||
17 | |||
18 | #define BPDU_TYPE_CONFIG 0 | ||
19 | #define BPDU_TYPE_TCN 0x80 | ||
20 | |||
21 | struct br_config_bpdu | ||
22 | { | ||
23 | unsigned topology_change:1; | ||
24 | unsigned topology_change_ack:1; | ||
25 | bridge_id root; | ||
26 | int root_path_cost; | ||
27 | bridge_id bridge_id; | ||
28 | port_id port_id; | ||
29 | int message_age; | ||
30 | int max_age; | ||
31 | int hello_time; | ||
32 | int forward_delay; | ||
33 | }; | ||
34 | |||
35 | /* called under bridge lock */ | ||
36 | static inline int br_is_designated_port(const struct net_bridge_port *p) | ||
37 | { | ||
38 | return !memcmp(&p->designated_bridge, &p->br->bridge_id, 8) && | ||
39 | (p->designated_port == p->port_id); | ||
40 | } | ||
41 | |||
42 | |||
43 | /* br_stp.c */ | ||
44 | extern void br_become_root_bridge(struct net_bridge *br); | ||
45 | extern void br_config_bpdu_generation(struct net_bridge *); | ||
46 | extern void br_configuration_update(struct net_bridge *); | ||
47 | extern void br_port_state_selection(struct net_bridge *); | ||
48 | extern void br_received_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *bpdu); | ||
49 | extern void br_received_tcn_bpdu(struct net_bridge_port *p); | ||
50 | extern void br_transmit_config(struct net_bridge_port *p); | ||
51 | extern void br_transmit_tcn(struct net_bridge *br); | ||
52 | extern void br_topology_change_detection(struct net_bridge *br); | ||
53 | |||
54 | /* br_stp_bpdu.c */ | ||
55 | extern void br_send_config_bpdu(struct net_bridge_port *, struct br_config_bpdu *); | ||
56 | extern void br_send_tcn_bpdu(struct net_bridge_port *); | ||
57 | |||
58 | #endif | ||
diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c new file mode 100644 index 000000000000..04ca0639a95a --- /dev/null +++ b/net/bridge/br_stp.c | |||
@@ -0,0 +1,459 @@ | |||
1 | /* | ||
2 | * Spanning tree protocol; generic parts | ||
3 | * Linux ethernet bridge | ||
4 | * | ||
5 | * Authors: | ||
6 | * Lennert Buytenhek <buytenh@gnu.org> | ||
7 | * | ||
8 | * $Id: br_stp.c,v 1.4 2000/06/19 10:13:35 davem Exp $ | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or | ||
11 | * modify it under the terms of the GNU General Public License | ||
12 | * as published by the Free Software Foundation; either version | ||
13 | * 2 of the License, or (at your option) any later version. | ||
14 | */ | ||
15 | #include <linux/kernel.h> | ||
16 | #include <linux/smp_lock.h> | ||
17 | |||
18 | #include "br_private.h" | ||
19 | #include "br_private_stp.h" | ||
20 | |||
21 | /* since time values in bpdu are in jiffies and then scaled (1/256) | ||
22 | * before sending, make sure that is at least one. | ||
23 | */ | ||
24 | #define MESSAGE_AGE_INCR ((HZ < 256) ? 1 : (HZ/256)) | ||
25 | |||
26 | static const char *br_port_state_names[] = { | ||
27 | [BR_STATE_DISABLED] = "disabled", | ||
28 | [BR_STATE_LISTENING] = "listening", | ||
29 | [BR_STATE_LEARNING] = "learning", | ||
30 | [BR_STATE_FORWARDING] = "forwarding", | ||
31 | [BR_STATE_BLOCKING] = "blocking", | ||
32 | }; | ||
33 | |||
34 | void br_log_state(const struct net_bridge_port *p) | ||
35 | { | ||
36 | pr_info("%s: port %d(%s) entering %s state\n", | ||
37 | p->br->dev->name, p->port_no, p->dev->name, | ||
38 | br_port_state_names[p->state]); | ||
39 | |||
40 | } | ||
41 | |||
42 | /* called under bridge lock */ | ||
43 | struct net_bridge_port *br_get_port(struct net_bridge *br, u16 port_no) | ||
44 | { | ||
45 | struct net_bridge_port *p; | ||
46 | |||
47 | list_for_each_entry_rcu(p, &br->port_list, list) { | ||
48 | if (p->port_no == port_no) | ||
49 | return p; | ||
50 | } | ||
51 | |||
52 | return NULL; | ||
53 | } | ||
54 | |||
55 | /* called under bridge lock */ | ||
56 | static int br_should_become_root_port(const struct net_bridge_port *p, | ||
57 | u16 root_port) | ||
58 | { | ||
59 | struct net_bridge *br; | ||
60 | struct net_bridge_port *rp; | ||
61 | int t; | ||
62 | |||
63 | br = p->br; | ||
64 | if (p->state == BR_STATE_DISABLED || | ||
65 | br_is_designated_port(p)) | ||
66 | return 0; | ||
67 | |||
68 | if (memcmp(&br->bridge_id, &p->designated_root, 8) <= 0) | ||
69 | return 0; | ||
70 | |||
71 | if (!root_port) | ||
72 | return 1; | ||
73 | |||
74 | rp = br_get_port(br, root_port); | ||
75 | |||
76 | t = memcmp(&p->designated_root, &rp->designated_root, 8); | ||
77 | if (t < 0) | ||
78 | return 1; | ||
79 | else if (t > 0) | ||
80 | return 0; | ||
81 | |||
82 | if (p->designated_cost + p->path_cost < | ||
83 | rp->designated_cost + rp->path_cost) | ||
84 | return 1; | ||
85 | else if (p->designated_cost + p->path_cost > | ||
86 | rp->designated_cost + rp->path_cost) | ||
87 | return 0; | ||
88 | |||
89 | t = memcmp(&p->designated_bridge, &rp->designated_bridge, 8); | ||
90 | if (t < 0) | ||
91 | return 1; | ||
92 | else if (t > 0) | ||
93 | return 0; | ||
94 | |||
95 | if (p->designated_port < rp->designated_port) | ||
96 | return 1; | ||
97 | else if (p->designated_port > rp->designated_port) | ||
98 | return 0; | ||
99 | |||
100 | if (p->port_id < rp->port_id) | ||
101 | return 1; | ||
102 | |||
103 | return 0; | ||
104 | } | ||
105 | |||
106 | /* called under bridge lock */ | ||
107 | static void br_root_selection(struct net_bridge *br) | ||
108 | { | ||
109 | struct net_bridge_port *p; | ||
110 | u16 root_port = 0; | ||
111 | |||
112 | list_for_each_entry(p, &br->port_list, list) { | ||
113 | if (br_should_become_root_port(p, root_port)) | ||
114 | root_port = p->port_no; | ||
115 | |||
116 | } | ||
117 | |||
118 | br->root_port = root_port; | ||
119 | |||
120 | if (!root_port) { | ||
121 | br->designated_root = br->bridge_id; | ||
122 | br->root_path_cost = 0; | ||
123 | } else { | ||
124 | p = br_get_port(br, root_port); | ||
125 | br->designated_root = p->designated_root; | ||
126 | br->root_path_cost = p->designated_cost + p->path_cost; | ||
127 | } | ||
128 | } | ||
129 | |||
130 | /* called under bridge lock */ | ||
131 | void br_become_root_bridge(struct net_bridge *br) | ||
132 | { | ||
133 | br->max_age = br->bridge_max_age; | ||
134 | br->hello_time = br->bridge_hello_time; | ||
135 | br->forward_delay = br->bridge_forward_delay; | ||
136 | br_topology_change_detection(br); | ||
137 | del_timer(&br->tcn_timer); | ||
138 | |||
139 | if (br->dev->flags & IFF_UP) { | ||
140 | br_config_bpdu_generation(br); | ||
141 | mod_timer(&br->hello_timer, jiffies + br->hello_time); | ||
142 | } | ||
143 | } | ||
144 | |||
145 | /* called under bridge lock */ | ||
146 | void br_transmit_config(struct net_bridge_port *p) | ||
147 | { | ||
148 | struct br_config_bpdu bpdu; | ||
149 | struct net_bridge *br; | ||
150 | |||
151 | |||
152 | if (timer_pending(&p->hold_timer)) { | ||
153 | p->config_pending = 1; | ||
154 | return; | ||
155 | } | ||
156 | |||
157 | br = p->br; | ||
158 | |||
159 | bpdu.topology_change = br->topology_change; | ||
160 | bpdu.topology_change_ack = p->topology_change_ack; | ||
161 | bpdu.root = br->designated_root; | ||
162 | bpdu.root_path_cost = br->root_path_cost; | ||
163 | bpdu.bridge_id = br->bridge_id; | ||
164 | bpdu.port_id = p->port_id; | ||
165 | if (br_is_root_bridge(br)) | ||
166 | bpdu.message_age = 0; | ||
167 | else { | ||
168 | struct net_bridge_port *root | ||
169 | = br_get_port(br, br->root_port); | ||
170 | bpdu.message_age = br->max_age | ||
171 | - (root->message_age_timer.expires - jiffies) | ||
172 | + MESSAGE_AGE_INCR; | ||
173 | } | ||
174 | bpdu.max_age = br->max_age; | ||
175 | bpdu.hello_time = br->hello_time; | ||
176 | bpdu.forward_delay = br->forward_delay; | ||
177 | |||
178 | if (bpdu.message_age < br->max_age) { | ||
179 | br_send_config_bpdu(p, &bpdu); | ||
180 | p->topology_change_ack = 0; | ||
181 | p->config_pending = 0; | ||
182 | mod_timer(&p->hold_timer, jiffies + BR_HOLD_TIME); | ||
183 | } | ||
184 | } | ||
185 | |||
186 | /* called under bridge lock */ | ||
187 | static inline void br_record_config_information(struct net_bridge_port *p, | ||
188 | const struct br_config_bpdu *bpdu) | ||
189 | { | ||
190 | p->designated_root = bpdu->root; | ||
191 | p->designated_cost = bpdu->root_path_cost; | ||
192 | p->designated_bridge = bpdu->bridge_id; | ||
193 | p->designated_port = bpdu->port_id; | ||
194 | |||
195 | mod_timer(&p->message_age_timer, jiffies | ||
196 | + (p->br->max_age - bpdu->message_age)); | ||
197 | } | ||
198 | |||
199 | /* called under bridge lock */ | ||
200 | static inline void br_record_config_timeout_values(struct net_bridge *br, | ||
201 | const struct br_config_bpdu *bpdu) | ||
202 | { | ||
203 | br->max_age = bpdu->max_age; | ||
204 | br->hello_time = bpdu->hello_time; | ||
205 | br->forward_delay = bpdu->forward_delay; | ||
206 | br->topology_change = bpdu->topology_change; | ||
207 | } | ||
208 | |||
209 | /* called under bridge lock */ | ||
210 | void br_transmit_tcn(struct net_bridge *br) | ||
211 | { | ||
212 | br_send_tcn_bpdu(br_get_port(br, br->root_port)); | ||
213 | } | ||
214 | |||
215 | /* called under bridge lock */ | ||
216 | static int br_should_become_designated_port(const struct net_bridge_port *p) | ||
217 | { | ||
218 | struct net_bridge *br; | ||
219 | int t; | ||
220 | |||
221 | br = p->br; | ||
222 | if (br_is_designated_port(p)) | ||
223 | return 1; | ||
224 | |||
225 | if (memcmp(&p->designated_root, &br->designated_root, 8)) | ||
226 | return 1; | ||
227 | |||
228 | if (br->root_path_cost < p->designated_cost) | ||
229 | return 1; | ||
230 | else if (br->root_path_cost > p->designated_cost) | ||
231 | return 0; | ||
232 | |||
233 | t = memcmp(&br->bridge_id, &p->designated_bridge, 8); | ||
234 | if (t < 0) | ||
235 | return 1; | ||
236 | else if (t > 0) | ||
237 | return 0; | ||
238 | |||
239 | if (p->port_id < p->designated_port) | ||
240 | return 1; | ||
241 | |||
242 | return 0; | ||
243 | } | ||
244 | |||
245 | /* called under bridge lock */ | ||
246 | static void br_designated_port_selection(struct net_bridge *br) | ||
247 | { | ||
248 | struct net_bridge_port *p; | ||
249 | |||
250 | list_for_each_entry(p, &br->port_list, list) { | ||
251 | if (p->state != BR_STATE_DISABLED && | ||
252 | br_should_become_designated_port(p)) | ||
253 | br_become_designated_port(p); | ||
254 | |||
255 | } | ||
256 | } | ||
257 | |||
258 | /* called under bridge lock */ | ||
259 | static int br_supersedes_port_info(struct net_bridge_port *p, struct br_config_bpdu *bpdu) | ||
260 | { | ||
261 | int t; | ||
262 | |||
263 | t = memcmp(&bpdu->root, &p->designated_root, 8); | ||
264 | if (t < 0) | ||
265 | return 1; | ||
266 | else if (t > 0) | ||
267 | return 0; | ||
268 | |||
269 | if (bpdu->root_path_cost < p->designated_cost) | ||
270 | return 1; | ||
271 | else if (bpdu->root_path_cost > p->designated_cost) | ||
272 | return 0; | ||
273 | |||
274 | t = memcmp(&bpdu->bridge_id, &p->designated_bridge, 8); | ||
275 | if (t < 0) | ||
276 | return 1; | ||
277 | else if (t > 0) | ||
278 | return 0; | ||
279 | |||
280 | if (memcmp(&bpdu->bridge_id, &p->br->bridge_id, 8)) | ||
281 | return 1; | ||
282 | |||
283 | if (bpdu->port_id <= p->designated_port) | ||
284 | return 1; | ||
285 | |||
286 | return 0; | ||
287 | } | ||
288 | |||
289 | /* called under bridge lock */ | ||
290 | static inline void br_topology_change_acknowledged(struct net_bridge *br) | ||
291 | { | ||
292 | br->topology_change_detected = 0; | ||
293 | del_timer(&br->tcn_timer); | ||
294 | } | ||
295 | |||
296 | /* called under bridge lock */ | ||
297 | void br_topology_change_detection(struct net_bridge *br) | ||
298 | { | ||
299 | int isroot = br_is_root_bridge(br); | ||
300 | |||
301 | pr_info("%s: topology change detected, %s\n", br->dev->name, | ||
302 | isroot ? "propagating" : "sending tcn bpdu"); | ||
303 | |||
304 | if (isroot) { | ||
305 | br->topology_change = 1; | ||
306 | mod_timer(&br->topology_change_timer, jiffies | ||
307 | + br->bridge_forward_delay + br->bridge_max_age); | ||
308 | } else if (!br->topology_change_detected) { | ||
309 | br_transmit_tcn(br); | ||
310 | mod_timer(&br->tcn_timer, jiffies + br->bridge_hello_time); | ||
311 | } | ||
312 | |||
313 | br->topology_change_detected = 1; | ||
314 | } | ||
315 | |||
316 | /* called under bridge lock */ | ||
317 | void br_config_bpdu_generation(struct net_bridge *br) | ||
318 | { | ||
319 | struct net_bridge_port *p; | ||
320 | |||
321 | list_for_each_entry(p, &br->port_list, list) { | ||
322 | if (p->state != BR_STATE_DISABLED && | ||
323 | br_is_designated_port(p)) | ||
324 | br_transmit_config(p); | ||
325 | } | ||
326 | } | ||
327 | |||
328 | /* called under bridge lock */ | ||
329 | static inline void br_reply(struct net_bridge_port *p) | ||
330 | { | ||
331 | br_transmit_config(p); | ||
332 | } | ||
333 | |||
334 | /* called under bridge lock */ | ||
335 | void br_configuration_update(struct net_bridge *br) | ||
336 | { | ||
337 | br_root_selection(br); | ||
338 | br_designated_port_selection(br); | ||
339 | } | ||
340 | |||
341 | /* called under bridge lock */ | ||
342 | void br_become_designated_port(struct net_bridge_port *p) | ||
343 | { | ||
344 | struct net_bridge *br; | ||
345 | |||
346 | br = p->br; | ||
347 | p->designated_root = br->designated_root; | ||
348 | p->designated_cost = br->root_path_cost; | ||
349 | p->designated_bridge = br->bridge_id; | ||
350 | p->designated_port = p->port_id; | ||
351 | } | ||
352 | |||
353 | |||
354 | /* called under bridge lock */ | ||
355 | static void br_make_blocking(struct net_bridge_port *p) | ||
356 | { | ||
357 | if (p->state != BR_STATE_DISABLED && | ||
358 | p->state != BR_STATE_BLOCKING) { | ||
359 | if (p->state == BR_STATE_FORWARDING || | ||
360 | p->state == BR_STATE_LEARNING) | ||
361 | br_topology_change_detection(p->br); | ||
362 | |||
363 | p->state = BR_STATE_BLOCKING; | ||
364 | br_log_state(p); | ||
365 | del_timer(&p->forward_delay_timer); | ||
366 | } | ||
367 | } | ||
368 | |||
369 | /* called under bridge lock */ | ||
370 | static void br_make_forwarding(struct net_bridge_port *p) | ||
371 | { | ||
372 | if (p->state == BR_STATE_BLOCKING) { | ||
373 | if (p->br->stp_enabled) { | ||
374 | p->state = BR_STATE_LISTENING; | ||
375 | } else { | ||
376 | p->state = BR_STATE_LEARNING; | ||
377 | } | ||
378 | br_log_state(p); | ||
379 | mod_timer(&p->forward_delay_timer, jiffies + p->br->forward_delay); } | ||
380 | } | ||
381 | |||
382 | /* called under bridge lock */ | ||
383 | void br_port_state_selection(struct net_bridge *br) | ||
384 | { | ||
385 | struct net_bridge_port *p; | ||
386 | |||
387 | list_for_each_entry(p, &br->port_list, list) { | ||
388 | if (p->state != BR_STATE_DISABLED) { | ||
389 | if (p->port_no == br->root_port) { | ||
390 | p->config_pending = 0; | ||
391 | p->topology_change_ack = 0; | ||
392 | br_make_forwarding(p); | ||
393 | } else if (br_is_designated_port(p)) { | ||
394 | del_timer(&p->message_age_timer); | ||
395 | br_make_forwarding(p); | ||
396 | } else { | ||
397 | p->config_pending = 0; | ||
398 | p->topology_change_ack = 0; | ||
399 | br_make_blocking(p); | ||
400 | } | ||
401 | } | ||
402 | |||
403 | } | ||
404 | } | ||
405 | |||
406 | /* called under bridge lock */ | ||
407 | static inline void br_topology_change_acknowledge(struct net_bridge_port *p) | ||
408 | { | ||
409 | p->topology_change_ack = 1; | ||
410 | br_transmit_config(p); | ||
411 | } | ||
412 | |||
413 | /* called under bridge lock */ | ||
414 | void br_received_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *bpdu) | ||
415 | { | ||
416 | struct net_bridge *br; | ||
417 | int was_root; | ||
418 | |||
419 | br = p->br; | ||
420 | was_root = br_is_root_bridge(br); | ||
421 | |||
422 | if (br_supersedes_port_info(p, bpdu)) { | ||
423 | br_record_config_information(p, bpdu); | ||
424 | br_configuration_update(br); | ||
425 | br_port_state_selection(br); | ||
426 | |||
427 | if (!br_is_root_bridge(br) && was_root) { | ||
428 | del_timer(&br->hello_timer); | ||
429 | if (br->topology_change_detected) { | ||
430 | del_timer(&br->topology_change_timer); | ||
431 | br_transmit_tcn(br); | ||
432 | |||
433 | mod_timer(&br->tcn_timer, | ||
434 | jiffies + br->bridge_hello_time); | ||
435 | } | ||
436 | } | ||
437 | |||
438 | if (p->port_no == br->root_port) { | ||
439 | br_record_config_timeout_values(br, bpdu); | ||
440 | br_config_bpdu_generation(br); | ||
441 | if (bpdu->topology_change_ack) | ||
442 | br_topology_change_acknowledged(br); | ||
443 | } | ||
444 | } else if (br_is_designated_port(p)) { | ||
445 | br_reply(p); | ||
446 | } | ||
447 | } | ||
448 | |||
449 | /* called under bridge lock */ | ||
450 | void br_received_tcn_bpdu(struct net_bridge_port *p) | ||
451 | { | ||
452 | if (br_is_designated_port(p)) { | ||
453 | pr_info("%s: received tcn bpdu on port %i(%s)\n", | ||
454 | p->br->dev->name, p->port_no, p->dev->name); | ||
455 | |||
456 | br_topology_change_detection(p->br); | ||
457 | br_topology_change_acknowledge(p); | ||
458 | } | ||
459 | } | ||
diff --git a/net/bridge/br_stp_bpdu.c b/net/bridge/br_stp_bpdu.c new file mode 100644 index 000000000000..b91a875aca01 --- /dev/null +++ b/net/bridge/br_stp_bpdu.c | |||
@@ -0,0 +1,205 @@ | |||
1 | /* | ||
2 | * Spanning tree protocol; BPDU handling | ||
3 | * Linux ethernet bridge | ||
4 | * | ||
5 | * Authors: | ||
6 | * Lennert Buytenhek <buytenh@gnu.org> | ||
7 | * | ||
8 | * $Id: br_stp_bpdu.c,v 1.3 2001/11/10 02:35:25 davem Exp $ | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or | ||
11 | * modify it under the terms of the GNU General Public License | ||
12 | * as published by the Free Software Foundation; either version | ||
13 | * 2 of the License, or (at your option) any later version. | ||
14 | */ | ||
15 | |||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/netfilter_bridge.h> | ||
18 | |||
19 | #include "br_private.h" | ||
20 | #include "br_private_stp.h" | ||
21 | |||
22 | #define JIFFIES_TO_TICKS(j) (((j) << 8) / HZ) | ||
23 | #define TICKS_TO_JIFFIES(j) (((j) * HZ) >> 8) | ||
24 | |||
25 | static void br_send_bpdu(struct net_bridge_port *p, unsigned char *data, int length) | ||
26 | { | ||
27 | struct net_device *dev; | ||
28 | struct sk_buff *skb; | ||
29 | int size; | ||
30 | |||
31 | if (!p->br->stp_enabled) | ||
32 | return; | ||
33 | |||
34 | size = length + 2*ETH_ALEN + 2; | ||
35 | if (size < 60) | ||
36 | size = 60; | ||
37 | |||
38 | dev = p->dev; | ||
39 | |||
40 | if ((skb = dev_alloc_skb(size)) == NULL) { | ||
41 | printk(KERN_INFO "br: memory squeeze!\n"); | ||
42 | return; | ||
43 | } | ||
44 | |||
45 | skb->dev = dev; | ||
46 | skb->protocol = htons(ETH_P_802_2); | ||
47 | skb->mac.raw = skb_put(skb, size); | ||
48 | memcpy(skb->mac.raw, bridge_ula, ETH_ALEN); | ||
49 | memcpy(skb->mac.raw+ETH_ALEN, dev->dev_addr, ETH_ALEN); | ||
50 | skb->mac.raw[2*ETH_ALEN] = 0; | ||
51 | skb->mac.raw[2*ETH_ALEN+1] = length; | ||
52 | skb->nh.raw = skb->mac.raw + 2*ETH_ALEN + 2; | ||
53 | memcpy(skb->nh.raw, data, length); | ||
54 | memset(skb->nh.raw + length, 0xa5, size - length - 2*ETH_ALEN - 2); | ||
55 | |||
56 | NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev, | ||
57 | dev_queue_xmit); | ||
58 | } | ||
59 | |||
60 | static __inline__ void br_set_ticks(unsigned char *dest, int jiff) | ||
61 | { | ||
62 | __u16 ticks; | ||
63 | |||
64 | ticks = JIFFIES_TO_TICKS(jiff); | ||
65 | dest[0] = (ticks >> 8) & 0xFF; | ||
66 | dest[1] = ticks & 0xFF; | ||
67 | } | ||
68 | |||
69 | static __inline__ int br_get_ticks(unsigned char *dest) | ||
70 | { | ||
71 | return TICKS_TO_JIFFIES((dest[0] << 8) | dest[1]); | ||
72 | } | ||
73 | |||
74 | /* called under bridge lock */ | ||
75 | void br_send_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *bpdu) | ||
76 | { | ||
77 | unsigned char buf[38]; | ||
78 | |||
79 | buf[0] = 0x42; | ||
80 | buf[1] = 0x42; | ||
81 | buf[2] = 0x03; | ||
82 | buf[3] = 0; | ||
83 | buf[4] = 0; | ||
84 | buf[5] = 0; | ||
85 | buf[6] = BPDU_TYPE_CONFIG; | ||
86 | buf[7] = (bpdu->topology_change ? 0x01 : 0) | | ||
87 | (bpdu->topology_change_ack ? 0x80 : 0); | ||
88 | buf[8] = bpdu->root.prio[0]; | ||
89 | buf[9] = bpdu->root.prio[1]; | ||
90 | buf[10] = bpdu->root.addr[0]; | ||
91 | buf[11] = bpdu->root.addr[1]; | ||
92 | buf[12] = bpdu->root.addr[2]; | ||
93 | buf[13] = bpdu->root.addr[3]; | ||
94 | buf[14] = bpdu->root.addr[4]; | ||
95 | buf[15] = bpdu->root.addr[5]; | ||
96 | buf[16] = (bpdu->root_path_cost >> 24) & 0xFF; | ||
97 | buf[17] = (bpdu->root_path_cost >> 16) & 0xFF; | ||
98 | buf[18] = (bpdu->root_path_cost >> 8) & 0xFF; | ||
99 | buf[19] = bpdu->root_path_cost & 0xFF; | ||
100 | buf[20] = bpdu->bridge_id.prio[0]; | ||
101 | buf[21] = bpdu->bridge_id.prio[1]; | ||
102 | buf[22] = bpdu->bridge_id.addr[0]; | ||
103 | buf[23] = bpdu->bridge_id.addr[1]; | ||
104 | buf[24] = bpdu->bridge_id.addr[2]; | ||
105 | buf[25] = bpdu->bridge_id.addr[3]; | ||
106 | buf[26] = bpdu->bridge_id.addr[4]; | ||
107 | buf[27] = bpdu->bridge_id.addr[5]; | ||
108 | buf[28] = (bpdu->port_id >> 8) & 0xFF; | ||
109 | buf[29] = bpdu->port_id & 0xFF; | ||
110 | |||
111 | br_set_ticks(buf+30, bpdu->message_age); | ||
112 | br_set_ticks(buf+32, bpdu->max_age); | ||
113 | br_set_ticks(buf+34, bpdu->hello_time); | ||
114 | br_set_ticks(buf+36, bpdu->forward_delay); | ||
115 | |||
116 | br_send_bpdu(p, buf, 38); | ||
117 | } | ||
118 | |||
119 | /* called under bridge lock */ | ||
120 | void br_send_tcn_bpdu(struct net_bridge_port *p) | ||
121 | { | ||
122 | unsigned char buf[7]; | ||
123 | |||
124 | buf[0] = 0x42; | ||
125 | buf[1] = 0x42; | ||
126 | buf[2] = 0x03; | ||
127 | buf[3] = 0; | ||
128 | buf[4] = 0; | ||
129 | buf[5] = 0; | ||
130 | buf[6] = BPDU_TYPE_TCN; | ||
131 | br_send_bpdu(p, buf, 7); | ||
132 | } | ||
133 | |||
134 | static const unsigned char header[6] = {0x42, 0x42, 0x03, 0x00, 0x00, 0x00}; | ||
135 | |||
136 | /* NO locks */ | ||
137 | int br_stp_handle_bpdu(struct sk_buff *skb) | ||
138 | { | ||
139 | struct net_bridge_port *p = skb->dev->br_port; | ||
140 | struct net_bridge *br = p->br; | ||
141 | unsigned char *buf; | ||
142 | |||
143 | /* need at least the 802 and STP headers */ | ||
144 | if (!pskb_may_pull(skb, sizeof(header)+1) || | ||
145 | memcmp(skb->data, header, sizeof(header))) | ||
146 | goto err; | ||
147 | |||
148 | buf = skb_pull(skb, sizeof(header)); | ||
149 | |||
150 | spin_lock_bh(&br->lock); | ||
151 | if (p->state == BR_STATE_DISABLED | ||
152 | || !(br->dev->flags & IFF_UP) | ||
153 | || !br->stp_enabled) | ||
154 | goto out; | ||
155 | |||
156 | if (buf[0] == BPDU_TYPE_CONFIG) { | ||
157 | struct br_config_bpdu bpdu; | ||
158 | |||
159 | if (!pskb_may_pull(skb, 32)) | ||
160 | goto out; | ||
161 | |||
162 | buf = skb->data; | ||
163 | bpdu.topology_change = (buf[1] & 0x01) ? 1 : 0; | ||
164 | bpdu.topology_change_ack = (buf[1] & 0x80) ? 1 : 0; | ||
165 | |||
166 | bpdu.root.prio[0] = buf[2]; | ||
167 | bpdu.root.prio[1] = buf[3]; | ||
168 | bpdu.root.addr[0] = buf[4]; | ||
169 | bpdu.root.addr[1] = buf[5]; | ||
170 | bpdu.root.addr[2] = buf[6]; | ||
171 | bpdu.root.addr[3] = buf[7]; | ||
172 | bpdu.root.addr[4] = buf[8]; | ||
173 | bpdu.root.addr[5] = buf[9]; | ||
174 | bpdu.root_path_cost = | ||
175 | (buf[10] << 24) | | ||
176 | (buf[11] << 16) | | ||
177 | (buf[12] << 8) | | ||
178 | buf[13]; | ||
179 | bpdu.bridge_id.prio[0] = buf[14]; | ||
180 | bpdu.bridge_id.prio[1] = buf[15]; | ||
181 | bpdu.bridge_id.addr[0] = buf[16]; | ||
182 | bpdu.bridge_id.addr[1] = buf[17]; | ||
183 | bpdu.bridge_id.addr[2] = buf[18]; | ||
184 | bpdu.bridge_id.addr[3] = buf[19]; | ||
185 | bpdu.bridge_id.addr[4] = buf[20]; | ||
186 | bpdu.bridge_id.addr[5] = buf[21]; | ||
187 | bpdu.port_id = (buf[22] << 8) | buf[23]; | ||
188 | |||
189 | bpdu.message_age = br_get_ticks(buf+24); | ||
190 | bpdu.max_age = br_get_ticks(buf+26); | ||
191 | bpdu.hello_time = br_get_ticks(buf+28); | ||
192 | bpdu.forward_delay = br_get_ticks(buf+30); | ||
193 | |||
194 | br_received_config_bpdu(p, &bpdu); | ||
195 | } | ||
196 | |||
197 | else if (buf[0] == BPDU_TYPE_TCN) { | ||
198 | br_received_tcn_bpdu(p); | ||
199 | } | ||
200 | out: | ||
201 | spin_unlock_bh(&br->lock); | ||
202 | err: | ||
203 | kfree_skb(skb); | ||
204 | return 0; | ||
205 | } | ||
diff --git a/net/bridge/br_stp_if.c b/net/bridge/br_stp_if.c new file mode 100644 index 000000000000..0da11ff05fa3 --- /dev/null +++ b/net/bridge/br_stp_if.c | |||
@@ -0,0 +1,225 @@ | |||
1 | /* | ||
2 | * Spanning tree protocol; interface code | ||
3 | * Linux ethernet bridge | ||
4 | * | ||
5 | * Authors: | ||
6 | * Lennert Buytenhek <buytenh@gnu.org> | ||
7 | * | ||
8 | * $Id: br_stp_if.c,v 1.4 2001/04/14 21:14:39 davem Exp $ | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or | ||
11 | * modify it under the terms of the GNU General Public License | ||
12 | * as published by the Free Software Foundation; either version | ||
13 | * 2 of the License, or (at your option) any later version. | ||
14 | */ | ||
15 | |||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/smp_lock.h> | ||
18 | |||
19 | #include "br_private.h" | ||
20 | #include "br_private_stp.h" | ||
21 | |||
22 | |||
23 | /* Port id is composed of priority and port number. | ||
24 | * NB: least significant bits of priority are dropped to | ||
25 | * make room for more ports. | ||
26 | */ | ||
27 | static inline port_id br_make_port_id(__u8 priority, __u16 port_no) | ||
28 | { | ||
29 | return ((u16)priority << BR_PORT_BITS) | ||
30 | | (port_no & ((1<<BR_PORT_BITS)-1)); | ||
31 | } | ||
32 | |||
33 | /* called under bridge lock */ | ||
34 | void br_init_port(struct net_bridge_port *p) | ||
35 | { | ||
36 | p->port_id = br_make_port_id(p->priority, p->port_no); | ||
37 | br_become_designated_port(p); | ||
38 | p->state = BR_STATE_BLOCKING; | ||
39 | p->topology_change_ack = 0; | ||
40 | p->config_pending = 0; | ||
41 | |||
42 | br_stp_port_timer_init(p); | ||
43 | } | ||
44 | |||
45 | /* called under bridge lock */ | ||
46 | void br_stp_enable_bridge(struct net_bridge *br) | ||
47 | { | ||
48 | struct net_bridge_port *p; | ||
49 | |||
50 | spin_lock_bh(&br->lock); | ||
51 | mod_timer(&br->hello_timer, jiffies + br->hello_time); | ||
52 | mod_timer(&br->gc_timer, jiffies + HZ/10); | ||
53 | |||
54 | br_config_bpdu_generation(br); | ||
55 | |||
56 | list_for_each_entry(p, &br->port_list, list) { | ||
57 | if ((p->dev->flags & IFF_UP) && netif_carrier_ok(p->dev)) | ||
58 | br_stp_enable_port(p); | ||
59 | |||
60 | } | ||
61 | spin_unlock_bh(&br->lock); | ||
62 | } | ||
63 | |||
64 | /* NO locks held */ | ||
65 | void br_stp_disable_bridge(struct net_bridge *br) | ||
66 | { | ||
67 | struct net_bridge_port *p; | ||
68 | |||
69 | spin_lock(&br->lock); | ||
70 | list_for_each_entry(p, &br->port_list, list) { | ||
71 | if (p->state != BR_STATE_DISABLED) | ||
72 | br_stp_disable_port(p); | ||
73 | |||
74 | } | ||
75 | |||
76 | br->topology_change = 0; | ||
77 | br->topology_change_detected = 0; | ||
78 | spin_unlock(&br->lock); | ||
79 | |||
80 | del_timer_sync(&br->hello_timer); | ||
81 | del_timer_sync(&br->topology_change_timer); | ||
82 | del_timer_sync(&br->tcn_timer); | ||
83 | del_timer_sync(&br->gc_timer); | ||
84 | } | ||
85 | |||
86 | /* called under bridge lock */ | ||
87 | void br_stp_enable_port(struct net_bridge_port *p) | ||
88 | { | ||
89 | br_init_port(p); | ||
90 | br_port_state_selection(p->br); | ||
91 | } | ||
92 | |||
93 | /* called under bridge lock */ | ||
94 | void br_stp_disable_port(struct net_bridge_port *p) | ||
95 | { | ||
96 | struct net_bridge *br; | ||
97 | int wasroot; | ||
98 | |||
99 | br = p->br; | ||
100 | printk(KERN_INFO "%s: port %i(%s) entering %s state\n", | ||
101 | br->dev->name, p->port_no, p->dev->name, "disabled"); | ||
102 | |||
103 | wasroot = br_is_root_bridge(br); | ||
104 | br_become_designated_port(p); | ||
105 | p->state = BR_STATE_DISABLED; | ||
106 | p->topology_change_ack = 0; | ||
107 | p->config_pending = 0; | ||
108 | |||
109 | del_timer(&p->message_age_timer); | ||
110 | del_timer(&p->forward_delay_timer); | ||
111 | del_timer(&p->hold_timer); | ||
112 | |||
113 | br_configuration_update(br); | ||
114 | |||
115 | br_port_state_selection(br); | ||
116 | |||
117 | if (br_is_root_bridge(br) && !wasroot) | ||
118 | br_become_root_bridge(br); | ||
119 | } | ||
120 | |||
121 | /* called under bridge lock */ | ||
122 | static void br_stp_change_bridge_id(struct net_bridge *br, | ||
123 | const unsigned char *addr) | ||
124 | { | ||
125 | unsigned char oldaddr[6]; | ||
126 | struct net_bridge_port *p; | ||
127 | int wasroot; | ||
128 | |||
129 | wasroot = br_is_root_bridge(br); | ||
130 | |||
131 | memcpy(oldaddr, br->bridge_id.addr, ETH_ALEN); | ||
132 | memcpy(br->bridge_id.addr, addr, ETH_ALEN); | ||
133 | memcpy(br->dev->dev_addr, addr, ETH_ALEN); | ||
134 | |||
135 | list_for_each_entry(p, &br->port_list, list) { | ||
136 | if (!memcmp(p->designated_bridge.addr, oldaddr, ETH_ALEN)) | ||
137 | memcpy(p->designated_bridge.addr, addr, ETH_ALEN); | ||
138 | |||
139 | if (!memcmp(p->designated_root.addr, oldaddr, ETH_ALEN)) | ||
140 | memcpy(p->designated_root.addr, addr, ETH_ALEN); | ||
141 | |||
142 | } | ||
143 | |||
144 | br_configuration_update(br); | ||
145 | br_port_state_selection(br); | ||
146 | if (br_is_root_bridge(br) && !wasroot) | ||
147 | br_become_root_bridge(br); | ||
148 | } | ||
149 | |||
150 | static const unsigned char br_mac_zero[6]; | ||
151 | |||
152 | /* called under bridge lock */ | ||
153 | void br_stp_recalculate_bridge_id(struct net_bridge *br) | ||
154 | { | ||
155 | const unsigned char *addr = br_mac_zero; | ||
156 | struct net_bridge_port *p; | ||
157 | |||
158 | list_for_each_entry(p, &br->port_list, list) { | ||
159 | if (addr == br_mac_zero || | ||
160 | memcmp(p->dev->dev_addr, addr, ETH_ALEN) < 0) | ||
161 | addr = p->dev->dev_addr; | ||
162 | |||
163 | } | ||
164 | |||
165 | if (memcmp(br->bridge_id.addr, addr, ETH_ALEN)) | ||
166 | br_stp_change_bridge_id(br, addr); | ||
167 | } | ||
168 | |||
169 | /* called under bridge lock */ | ||
170 | void br_stp_set_bridge_priority(struct net_bridge *br, u16 newprio) | ||
171 | { | ||
172 | struct net_bridge_port *p; | ||
173 | int wasroot; | ||
174 | |||
175 | wasroot = br_is_root_bridge(br); | ||
176 | |||
177 | list_for_each_entry(p, &br->port_list, list) { | ||
178 | if (p->state != BR_STATE_DISABLED && | ||
179 | br_is_designated_port(p)) { | ||
180 | p->designated_bridge.prio[0] = (newprio >> 8) & 0xFF; | ||
181 | p->designated_bridge.prio[1] = newprio & 0xFF; | ||
182 | } | ||
183 | |||
184 | } | ||
185 | |||
186 | br->bridge_id.prio[0] = (newprio >> 8) & 0xFF; | ||
187 | br->bridge_id.prio[1] = newprio & 0xFF; | ||
188 | br_configuration_update(br); | ||
189 | br_port_state_selection(br); | ||
190 | if (br_is_root_bridge(br) && !wasroot) | ||
191 | br_become_root_bridge(br); | ||
192 | } | ||
193 | |||
194 | /* called under bridge lock */ | ||
195 | void br_stp_set_port_priority(struct net_bridge_port *p, u8 newprio) | ||
196 | { | ||
197 | port_id new_port_id = br_make_port_id(newprio, p->port_no); | ||
198 | |||
199 | if (br_is_designated_port(p)) | ||
200 | p->designated_port = new_port_id; | ||
201 | |||
202 | p->port_id = new_port_id; | ||
203 | p->priority = newprio; | ||
204 | if (!memcmp(&p->br->bridge_id, &p->designated_bridge, 8) && | ||
205 | p->port_id < p->designated_port) { | ||
206 | br_become_designated_port(p); | ||
207 | br_port_state_selection(p->br); | ||
208 | } | ||
209 | } | ||
210 | |||
211 | /* called under bridge lock */ | ||
212 | void br_stp_set_path_cost(struct net_bridge_port *p, u32 path_cost) | ||
213 | { | ||
214 | p->path_cost = path_cost; | ||
215 | br_configuration_update(p->br); | ||
216 | br_port_state_selection(p->br); | ||
217 | } | ||
218 | |||
219 | ssize_t br_show_bridge_id(char *buf, const struct bridge_id *id) | ||
220 | { | ||
221 | return sprintf(buf, "%.2x%.2x.%.2x%.2x%.2x%.2x%.2x%.2x\n", | ||
222 | id->prio[0], id->prio[1], | ||
223 | id->addr[0], id->addr[1], id->addr[2], | ||
224 | id->addr[3], id->addr[4], id->addr[5]); | ||
225 | } | ||
diff --git a/net/bridge/br_stp_timer.c b/net/bridge/br_stp_timer.c new file mode 100644 index 000000000000..9bef55f56425 --- /dev/null +++ b/net/bridge/br_stp_timer.c | |||
@@ -0,0 +1,188 @@ | |||
1 | /* | ||
2 | * Spanning tree protocol; timer-related code | ||
3 | * Linux ethernet bridge | ||
4 | * | ||
5 | * Authors: | ||
6 | * Lennert Buytenhek <buytenh@gnu.org> | ||
7 | * | ||
8 | * $Id: br_stp_timer.c,v 1.3 2000/05/05 02:17:17 davem Exp $ | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or | ||
11 | * modify it under the terms of the GNU General Public License | ||
12 | * as published by the Free Software Foundation; either version | ||
13 | * 2 of the License, or (at your option) any later version. | ||
14 | */ | ||
15 | |||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/times.h> | ||
18 | #include <linux/smp_lock.h> | ||
19 | |||
20 | #include "br_private.h" | ||
21 | #include "br_private_stp.h" | ||
22 | |||
23 | /* called under bridge lock */ | ||
24 | static int br_is_designated_for_some_port(const struct net_bridge *br) | ||
25 | { | ||
26 | struct net_bridge_port *p; | ||
27 | |||
28 | list_for_each_entry(p, &br->port_list, list) { | ||
29 | if (p->state != BR_STATE_DISABLED && | ||
30 | !memcmp(&p->designated_bridge, &br->bridge_id, 8)) | ||
31 | return 1; | ||
32 | } | ||
33 | |||
34 | return 0; | ||
35 | } | ||
36 | |||
37 | static void br_hello_timer_expired(unsigned long arg) | ||
38 | { | ||
39 | struct net_bridge *br = (struct net_bridge *)arg; | ||
40 | |||
41 | pr_debug("%s: hello timer expired\n", br->dev->name); | ||
42 | spin_lock_bh(&br->lock); | ||
43 | if (br->dev->flags & IFF_UP) { | ||
44 | br_config_bpdu_generation(br); | ||
45 | |||
46 | mod_timer(&br->hello_timer, jiffies + br->hello_time); | ||
47 | } | ||
48 | spin_unlock_bh(&br->lock); | ||
49 | } | ||
50 | |||
51 | static void br_message_age_timer_expired(unsigned long arg) | ||
52 | { | ||
53 | struct net_bridge_port *p = (struct net_bridge_port *) arg; | ||
54 | struct net_bridge *br = p->br; | ||
55 | const bridge_id *id = &p->designated_bridge; | ||
56 | int was_root; | ||
57 | |||
58 | if (p->state == BR_STATE_DISABLED) | ||
59 | return; | ||
60 | |||
61 | |||
62 | pr_info("%s: neighbor %.2x%.2x.%.2x:%.2x:%.2x:%.2x:%.2x:%.2x lost on port %d(%s)\n", | ||
63 | br->dev->name, | ||
64 | id->prio[0], id->prio[1], | ||
65 | id->addr[0], id->addr[1], id->addr[2], | ||
66 | id->addr[3], id->addr[4], id->addr[5], | ||
67 | p->port_no, p->dev->name); | ||
68 | |||
69 | /* | ||
70 | * According to the spec, the message age timer cannot be | ||
71 | * running when we are the root bridge. So.. this was_root | ||
72 | * check is redundant. I'm leaving it in for now, though. | ||
73 | */ | ||
74 | spin_lock_bh(&br->lock); | ||
75 | if (p->state == BR_STATE_DISABLED) | ||
76 | goto unlock; | ||
77 | was_root = br_is_root_bridge(br); | ||
78 | |||
79 | br_become_designated_port(p); | ||
80 | br_configuration_update(br); | ||
81 | br_port_state_selection(br); | ||
82 | if (br_is_root_bridge(br) && !was_root) | ||
83 | br_become_root_bridge(br); | ||
84 | unlock: | ||
85 | spin_unlock_bh(&br->lock); | ||
86 | } | ||
87 | |||
88 | static void br_forward_delay_timer_expired(unsigned long arg) | ||
89 | { | ||
90 | struct net_bridge_port *p = (struct net_bridge_port *) arg; | ||
91 | struct net_bridge *br = p->br; | ||
92 | |||
93 | pr_debug("%s: %d(%s) forward delay timer\n", | ||
94 | br->dev->name, p->port_no, p->dev->name); | ||
95 | spin_lock_bh(&br->lock); | ||
96 | if (p->state == BR_STATE_LISTENING) { | ||
97 | p->state = BR_STATE_LEARNING; | ||
98 | mod_timer(&p->forward_delay_timer, | ||
99 | jiffies + br->forward_delay); | ||
100 | } else if (p->state == BR_STATE_LEARNING) { | ||
101 | p->state = BR_STATE_FORWARDING; | ||
102 | if (br_is_designated_for_some_port(br)) | ||
103 | br_topology_change_detection(br); | ||
104 | } | ||
105 | br_log_state(p); | ||
106 | spin_unlock_bh(&br->lock); | ||
107 | } | ||
108 | |||
109 | static void br_tcn_timer_expired(unsigned long arg) | ||
110 | { | ||
111 | struct net_bridge *br = (struct net_bridge *) arg; | ||
112 | |||
113 | pr_debug("%s: tcn timer expired\n", br->dev->name); | ||
114 | spin_lock_bh(&br->lock); | ||
115 | if (br->dev->flags & IFF_UP) { | ||
116 | br_transmit_tcn(br); | ||
117 | |||
118 | mod_timer(&br->tcn_timer,jiffies + br->bridge_hello_time); | ||
119 | } | ||
120 | spin_unlock_bh(&br->lock); | ||
121 | } | ||
122 | |||
123 | static void br_topology_change_timer_expired(unsigned long arg) | ||
124 | { | ||
125 | struct net_bridge *br = (struct net_bridge *) arg; | ||
126 | |||
127 | pr_debug("%s: topo change timer expired\n", br->dev->name); | ||
128 | spin_lock_bh(&br->lock); | ||
129 | br->topology_change_detected = 0; | ||
130 | br->topology_change = 0; | ||
131 | spin_unlock_bh(&br->lock); | ||
132 | } | ||
133 | |||
134 | static void br_hold_timer_expired(unsigned long arg) | ||
135 | { | ||
136 | struct net_bridge_port *p = (struct net_bridge_port *) arg; | ||
137 | |||
138 | pr_debug("%s: %d(%s) hold timer expired\n", | ||
139 | p->br->dev->name, p->port_no, p->dev->name); | ||
140 | |||
141 | spin_lock_bh(&p->br->lock); | ||
142 | if (p->config_pending) | ||
143 | br_transmit_config(p); | ||
144 | spin_unlock_bh(&p->br->lock); | ||
145 | } | ||
146 | |||
147 | static inline void br_timer_init(struct timer_list *timer, | ||
148 | void (*_function)(unsigned long), | ||
149 | unsigned long _data) | ||
150 | { | ||
151 | init_timer(timer); | ||
152 | timer->function = _function; | ||
153 | timer->data = _data; | ||
154 | } | ||
155 | |||
156 | void br_stp_timer_init(struct net_bridge *br) | ||
157 | { | ||
158 | br_timer_init(&br->hello_timer, br_hello_timer_expired, | ||
159 | (unsigned long) br); | ||
160 | |||
161 | br_timer_init(&br->tcn_timer, br_tcn_timer_expired, | ||
162 | (unsigned long) br); | ||
163 | |||
164 | br_timer_init(&br->topology_change_timer, | ||
165 | br_topology_change_timer_expired, | ||
166 | (unsigned long) br); | ||
167 | |||
168 | br_timer_init(&br->gc_timer, br_fdb_cleanup, (unsigned long) br); | ||
169 | } | ||
170 | |||
171 | void br_stp_port_timer_init(struct net_bridge_port *p) | ||
172 | { | ||
173 | br_timer_init(&p->message_age_timer, br_message_age_timer_expired, | ||
174 | (unsigned long) p); | ||
175 | |||
176 | br_timer_init(&p->forward_delay_timer, br_forward_delay_timer_expired, | ||
177 | (unsigned long) p); | ||
178 | |||
179 | br_timer_init(&p->hold_timer, br_hold_timer_expired, | ||
180 | (unsigned long) p); | ||
181 | } | ||
182 | |||
183 | /* Report ticks left (in USER_HZ) used for API */ | ||
184 | unsigned long br_timer_value(const struct timer_list *timer) | ||
185 | { | ||
186 | return timer_pending(timer) | ||
187 | ? jiffies_to_clock_t(timer->expires - jiffies) : 0; | ||
188 | } | ||
diff --git a/net/bridge/br_sysfs_br.c b/net/bridge/br_sysfs_br.c new file mode 100644 index 000000000000..98cf53c81fad --- /dev/null +++ b/net/bridge/br_sysfs_br.c | |||
@@ -0,0 +1,364 @@ | |||
1 | /* | ||
2 | * Sysfs attributes of bridge ports | ||
3 | * Linux ethernet bridge | ||
4 | * | ||
5 | * Authors: | ||
6 | * Stephen Hemminger <shemminger@osdl.org> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License | ||
10 | * as published by the Free Software Foundation; either version | ||
11 | * 2 of the License, or (at your option) any later version. | ||
12 | */ | ||
13 | |||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/netdevice.h> | ||
16 | #include <linux/if_bridge.h> | ||
17 | #include <linux/rtnetlink.h> | ||
18 | #include <linux/spinlock.h> | ||
19 | #include <linux/times.h> | ||
20 | |||
21 | #include "br_private.h" | ||
22 | |||
23 | #define to_class_dev(obj) container_of(obj,struct class_device,kobj) | ||
24 | #define to_net_dev(class) container_of(class, struct net_device, class_dev) | ||
25 | #define to_bridge(cd) ((struct net_bridge *)(to_net_dev(cd)->priv)) | ||
26 | |||
27 | /* | ||
28 | * Common code for storing bridge parameters. | ||
29 | */ | ||
30 | static ssize_t store_bridge_parm(struct class_device *cd, | ||
31 | const char *buf, size_t len, | ||
32 | void (*set)(struct net_bridge *, unsigned long)) | ||
33 | { | ||
34 | struct net_bridge *br = to_bridge(cd); | ||
35 | char *endp; | ||
36 | unsigned long val; | ||
37 | |||
38 | if (!capable(CAP_NET_ADMIN)) | ||
39 | return -EPERM; | ||
40 | |||
41 | val = simple_strtoul(buf, &endp, 0); | ||
42 | if (endp == buf) | ||
43 | return -EINVAL; | ||
44 | |||
45 | spin_lock_bh(&br->lock); | ||
46 | (*set)(br, val); | ||
47 | spin_unlock_bh(&br->lock); | ||
48 | return len; | ||
49 | } | ||
50 | |||
51 | |||
52 | static ssize_t show_forward_delay(struct class_device *cd, char *buf) | ||
53 | { | ||
54 | struct net_bridge *br = to_bridge(cd); | ||
55 | return sprintf(buf, "%lu\n", jiffies_to_clock_t(br->forward_delay)); | ||
56 | } | ||
57 | |||
58 | static void set_forward_delay(struct net_bridge *br, unsigned long val) | ||
59 | { | ||
60 | unsigned long delay = clock_t_to_jiffies(val); | ||
61 | br->forward_delay = delay; | ||
62 | if (br_is_root_bridge(br)) | ||
63 | br->bridge_forward_delay = delay; | ||
64 | } | ||
65 | |||
66 | static ssize_t store_forward_delay(struct class_device *cd, const char *buf, | ||
67 | size_t len) | ||
68 | { | ||
69 | return store_bridge_parm(cd, buf, len, set_forward_delay); | ||
70 | } | ||
71 | static CLASS_DEVICE_ATTR(forward_delay, S_IRUGO | S_IWUSR, | ||
72 | show_forward_delay, store_forward_delay); | ||
73 | |||
74 | static ssize_t show_hello_time(struct class_device *cd, char *buf) | ||
75 | { | ||
76 | return sprintf(buf, "%lu\n", | ||
77 | jiffies_to_clock_t(to_bridge(cd)->hello_time)); | ||
78 | } | ||
79 | |||
80 | static void set_hello_time(struct net_bridge *br, unsigned long val) | ||
81 | { | ||
82 | unsigned long t = clock_t_to_jiffies(val); | ||
83 | br->hello_time = t; | ||
84 | if (br_is_root_bridge(br)) | ||
85 | br->bridge_hello_time = t; | ||
86 | } | ||
87 | |||
88 | static ssize_t store_hello_time(struct class_device *cd, const char *buf, | ||
89 | size_t len) | ||
90 | { | ||
91 | return store_bridge_parm(cd, buf, len, set_hello_time); | ||
92 | } | ||
93 | |||
94 | static CLASS_DEVICE_ATTR(hello_time, S_IRUGO | S_IWUSR, show_hello_time, | ||
95 | store_hello_time); | ||
96 | |||
97 | static ssize_t show_max_age(struct class_device *cd, char *buf) | ||
98 | { | ||
99 | return sprintf(buf, "%lu\n", | ||
100 | jiffies_to_clock_t(to_bridge(cd)->max_age)); | ||
101 | } | ||
102 | |||
103 | static void set_max_age(struct net_bridge *br, unsigned long val) | ||
104 | { | ||
105 | unsigned long t = clock_t_to_jiffies(val); | ||
106 | br->max_age = t; | ||
107 | if (br_is_root_bridge(br)) | ||
108 | br->bridge_max_age = t; | ||
109 | } | ||
110 | |||
111 | static ssize_t store_max_age(struct class_device *cd, const char *buf, | ||
112 | size_t len) | ||
113 | { | ||
114 | return store_bridge_parm(cd, buf, len, set_max_age); | ||
115 | } | ||
116 | |||
117 | static CLASS_DEVICE_ATTR(max_age, S_IRUGO | S_IWUSR, show_max_age, | ||
118 | store_max_age); | ||
119 | |||
120 | static ssize_t show_ageing_time(struct class_device *cd, char *buf) | ||
121 | { | ||
122 | struct net_bridge *br = to_bridge(cd); | ||
123 | return sprintf(buf, "%lu\n", jiffies_to_clock_t(br->ageing_time)); | ||
124 | } | ||
125 | |||
126 | static void set_ageing_time(struct net_bridge *br, unsigned long val) | ||
127 | { | ||
128 | br->ageing_time = clock_t_to_jiffies(val); | ||
129 | } | ||
130 | |||
131 | static ssize_t store_ageing_time(struct class_device *cd, const char *buf, | ||
132 | size_t len) | ||
133 | { | ||
134 | return store_bridge_parm(cd, buf, len, set_ageing_time); | ||
135 | } | ||
136 | |||
137 | static CLASS_DEVICE_ATTR(ageing_time, S_IRUGO | S_IWUSR, show_ageing_time, | ||
138 | store_ageing_time); | ||
139 | static ssize_t show_stp_state(struct class_device *cd, char *buf) | ||
140 | { | ||
141 | struct net_bridge *br = to_bridge(cd); | ||
142 | return sprintf(buf, "%d\n", br->stp_enabled); | ||
143 | } | ||
144 | |||
145 | static void set_stp_state(struct net_bridge *br, unsigned long val) | ||
146 | { | ||
147 | br->stp_enabled = val; | ||
148 | } | ||
149 | |||
150 | static ssize_t store_stp_state(struct class_device *cd, | ||
151 | const char *buf, size_t len) | ||
152 | { | ||
153 | return store_bridge_parm(cd, buf, len, set_stp_state); | ||
154 | } | ||
155 | |||
156 | static CLASS_DEVICE_ATTR(stp_state, S_IRUGO | S_IWUSR, show_stp_state, | ||
157 | store_stp_state); | ||
158 | |||
159 | static ssize_t show_priority(struct class_device *cd, char *buf) | ||
160 | { | ||
161 | struct net_bridge *br = to_bridge(cd); | ||
162 | return sprintf(buf, "%d\n", | ||
163 | (br->bridge_id.prio[0] << 8) | br->bridge_id.prio[1]); | ||
164 | } | ||
165 | |||
166 | static void set_priority(struct net_bridge *br, unsigned long val) | ||
167 | { | ||
168 | br_stp_set_bridge_priority(br, (u16) val); | ||
169 | } | ||
170 | |||
171 | static ssize_t store_priority(struct class_device *cd, | ||
172 | const char *buf, size_t len) | ||
173 | { | ||
174 | return store_bridge_parm(cd, buf, len, set_priority); | ||
175 | } | ||
176 | static CLASS_DEVICE_ATTR(priority, S_IRUGO | S_IWUSR, show_priority, | ||
177 | store_priority); | ||
178 | |||
179 | static ssize_t show_root_id(struct class_device *cd, char *buf) | ||
180 | { | ||
181 | return br_show_bridge_id(buf, &to_bridge(cd)->designated_root); | ||
182 | } | ||
183 | static CLASS_DEVICE_ATTR(root_id, S_IRUGO, show_root_id, NULL); | ||
184 | |||
185 | static ssize_t show_bridge_id(struct class_device *cd, char *buf) | ||
186 | { | ||
187 | return br_show_bridge_id(buf, &to_bridge(cd)->bridge_id); | ||
188 | } | ||
189 | static CLASS_DEVICE_ATTR(bridge_id, S_IRUGO, show_bridge_id, NULL); | ||
190 | |||
191 | static ssize_t show_root_port(struct class_device *cd, char *buf) | ||
192 | { | ||
193 | return sprintf(buf, "%d\n", to_bridge(cd)->root_port); | ||
194 | } | ||
195 | static CLASS_DEVICE_ATTR(root_port, S_IRUGO, show_root_port, NULL); | ||
196 | |||
197 | static ssize_t show_root_path_cost(struct class_device *cd, char *buf) | ||
198 | { | ||
199 | return sprintf(buf, "%d\n", to_bridge(cd)->root_path_cost); | ||
200 | } | ||
201 | static CLASS_DEVICE_ATTR(root_path_cost, S_IRUGO, show_root_path_cost, NULL); | ||
202 | |||
203 | static ssize_t show_topology_change(struct class_device *cd, char *buf) | ||
204 | { | ||
205 | return sprintf(buf, "%d\n", to_bridge(cd)->topology_change); | ||
206 | } | ||
207 | static CLASS_DEVICE_ATTR(topology_change, S_IRUGO, show_topology_change, NULL); | ||
208 | |||
209 | static ssize_t show_topology_change_detected(struct class_device *cd, char *buf) | ||
210 | { | ||
211 | struct net_bridge *br = to_bridge(cd); | ||
212 | return sprintf(buf, "%d\n", br->topology_change_detected); | ||
213 | } | ||
214 | static CLASS_DEVICE_ATTR(topology_change_detected, S_IRUGO, show_topology_change_detected, NULL); | ||
215 | |||
216 | static ssize_t show_hello_timer(struct class_device *cd, char *buf) | ||
217 | { | ||
218 | struct net_bridge *br = to_bridge(cd); | ||
219 | return sprintf(buf, "%ld\n", br_timer_value(&br->hello_timer)); | ||
220 | } | ||
221 | static CLASS_DEVICE_ATTR(hello_timer, S_IRUGO, show_hello_timer, NULL); | ||
222 | |||
223 | static ssize_t show_tcn_timer(struct class_device *cd, char *buf) | ||
224 | { | ||
225 | struct net_bridge *br = to_bridge(cd); | ||
226 | return sprintf(buf, "%ld\n", br_timer_value(&br->tcn_timer)); | ||
227 | } | ||
228 | static CLASS_DEVICE_ATTR(tcn_timer, S_IRUGO, show_tcn_timer, NULL); | ||
229 | |||
230 | static ssize_t show_topology_change_timer(struct class_device *cd, char *buf) | ||
231 | { | ||
232 | struct net_bridge *br = to_bridge(cd); | ||
233 | return sprintf(buf, "%ld\n", br_timer_value(&br->topology_change_timer)); | ||
234 | } | ||
235 | static CLASS_DEVICE_ATTR(topology_change_timer, S_IRUGO, show_topology_change_timer, NULL); | ||
236 | |||
237 | static ssize_t show_gc_timer(struct class_device *cd, char *buf) | ||
238 | { | ||
239 | struct net_bridge *br = to_bridge(cd); | ||
240 | return sprintf(buf, "%ld\n", br_timer_value(&br->gc_timer)); | ||
241 | } | ||
242 | static CLASS_DEVICE_ATTR(gc_timer, S_IRUGO, show_gc_timer, NULL); | ||
243 | |||
244 | static struct attribute *bridge_attrs[] = { | ||
245 | &class_device_attr_forward_delay.attr, | ||
246 | &class_device_attr_hello_time.attr, | ||
247 | &class_device_attr_max_age.attr, | ||
248 | &class_device_attr_ageing_time.attr, | ||
249 | &class_device_attr_stp_state.attr, | ||
250 | &class_device_attr_priority.attr, | ||
251 | &class_device_attr_bridge_id.attr, | ||
252 | &class_device_attr_root_id.attr, | ||
253 | &class_device_attr_root_path_cost.attr, | ||
254 | &class_device_attr_root_port.attr, | ||
255 | &class_device_attr_topology_change.attr, | ||
256 | &class_device_attr_topology_change_detected.attr, | ||
257 | &class_device_attr_hello_timer.attr, | ||
258 | &class_device_attr_tcn_timer.attr, | ||
259 | &class_device_attr_topology_change_timer.attr, | ||
260 | &class_device_attr_gc_timer.attr, | ||
261 | NULL | ||
262 | }; | ||
263 | |||
264 | static struct attribute_group bridge_group = { | ||
265 | .name = SYSFS_BRIDGE_ATTR, | ||
266 | .attrs = bridge_attrs, | ||
267 | }; | ||
268 | |||
269 | /* | ||
270 | * Export the forwarding information table as a binary file | ||
271 | * The records are struct __fdb_entry. | ||
272 | * | ||
273 | * Returns the number of bytes read. | ||
274 | */ | ||
275 | static ssize_t brforward_read(struct kobject *kobj, char *buf, | ||
276 | loff_t off, size_t count) | ||
277 | { | ||
278 | struct class_device *cdev = to_class_dev(kobj); | ||
279 | struct net_bridge *br = to_bridge(cdev); | ||
280 | int n; | ||
281 | |||
282 | /* must read whole records */ | ||
283 | if (off % sizeof(struct __fdb_entry) != 0) | ||
284 | return -EINVAL; | ||
285 | |||
286 | n = br_fdb_fillbuf(br, buf, | ||
287 | count / sizeof(struct __fdb_entry), | ||
288 | off / sizeof(struct __fdb_entry)); | ||
289 | |||
290 | if (n > 0) | ||
291 | n *= sizeof(struct __fdb_entry); | ||
292 | |||
293 | return n; | ||
294 | } | ||
295 | |||
296 | static struct bin_attribute bridge_forward = { | ||
297 | .attr = { .name = SYSFS_BRIDGE_FDB, | ||
298 | .mode = S_IRUGO, | ||
299 | .owner = THIS_MODULE, }, | ||
300 | .read = brforward_read, | ||
301 | }; | ||
302 | |||
303 | /* | ||
304 | * Add entries in sysfs onto the existing network class device | ||
305 | * for the bridge. | ||
306 | * Adds a attribute group "bridge" containing tuning parameters. | ||
307 | * Binary attribute containing the forward table | ||
308 | * Sub directory to hold links to interfaces. | ||
309 | * | ||
310 | * Note: the ifobj exists only to be a subdirectory | ||
311 | * to hold links. The ifobj exists in same data structure | ||
312 | * as it's parent the bridge so reference counting works. | ||
313 | */ | ||
314 | int br_sysfs_addbr(struct net_device *dev) | ||
315 | { | ||
316 | struct kobject *brobj = &dev->class_dev.kobj; | ||
317 | struct net_bridge *br = netdev_priv(dev); | ||
318 | int err; | ||
319 | |||
320 | err = sysfs_create_group(brobj, &bridge_group); | ||
321 | if (err) { | ||
322 | pr_info("%s: can't create group %s/%s\n", | ||
323 | __FUNCTION__, dev->name, bridge_group.name); | ||
324 | goto out1; | ||
325 | } | ||
326 | |||
327 | err = sysfs_create_bin_file(brobj, &bridge_forward); | ||
328 | if (err) { | ||
329 | pr_info("%s: can't create attribue file %s/%s\n", | ||
330 | __FUNCTION__, dev->name, bridge_forward.attr.name); | ||
331 | goto out2; | ||
332 | } | ||
333 | |||
334 | |||
335 | kobject_set_name(&br->ifobj, SYSFS_BRIDGE_PORT_SUBDIR); | ||
336 | br->ifobj.ktype = NULL; | ||
337 | br->ifobj.kset = NULL; | ||
338 | br->ifobj.parent = brobj; | ||
339 | |||
340 | err = kobject_register(&br->ifobj); | ||
341 | if (err) { | ||
342 | pr_info("%s: can't add kobject (directory) %s/%s\n", | ||
343 | __FUNCTION__, dev->name, br->ifobj.name); | ||
344 | goto out3; | ||
345 | } | ||
346 | return 0; | ||
347 | out3: | ||
348 | sysfs_remove_bin_file(&dev->class_dev.kobj, &bridge_forward); | ||
349 | out2: | ||
350 | sysfs_remove_group(&dev->class_dev.kobj, &bridge_group); | ||
351 | out1: | ||
352 | return err; | ||
353 | |||
354 | } | ||
355 | |||
356 | void br_sysfs_delbr(struct net_device *dev) | ||
357 | { | ||
358 | struct kobject *kobj = &dev->class_dev.kobj; | ||
359 | struct net_bridge *br = netdev_priv(dev); | ||
360 | |||
361 | kobject_unregister(&br->ifobj); | ||
362 | sysfs_remove_bin_file(kobj, &bridge_forward); | ||
363 | sysfs_remove_group(kobj, &bridge_group); | ||
364 | } | ||
diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c new file mode 100644 index 000000000000..567249bf9331 --- /dev/null +++ b/net/bridge/br_sysfs_if.c | |||
@@ -0,0 +1,269 @@ | |||
1 | /* | ||
2 | * Sysfs attributes of bridge ports | ||
3 | * Linux ethernet bridge | ||
4 | * | ||
5 | * Authors: | ||
6 | * Stephen Hemminger <shemminger@osdl.org> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License | ||
10 | * as published by the Free Software Foundation; either version | ||
11 | * 2 of the License, or (at your option) any later version. | ||
12 | */ | ||
13 | |||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/netdevice.h> | ||
16 | #include <linux/if_bridge.h> | ||
17 | #include <linux/rtnetlink.h> | ||
18 | #include <linux/spinlock.h> | ||
19 | |||
20 | #include "br_private.h" | ||
21 | |||
22 | struct brport_attribute { | ||
23 | struct attribute attr; | ||
24 | ssize_t (*show)(struct net_bridge_port *, char *); | ||
25 | ssize_t (*store)(struct net_bridge_port *, unsigned long); | ||
26 | }; | ||
27 | |||
28 | #define BRPORT_ATTR(_name,_mode,_show,_store) \ | ||
29 | struct brport_attribute brport_attr_##_name = { \ | ||
30 | .attr = {.name = __stringify(_name), \ | ||
31 | .mode = _mode, \ | ||
32 | .owner = THIS_MODULE, }, \ | ||
33 | .show = _show, \ | ||
34 | .store = _store, \ | ||
35 | }; | ||
36 | |||
37 | static ssize_t show_path_cost(struct net_bridge_port *p, char *buf) | ||
38 | { | ||
39 | return sprintf(buf, "%d\n", p->path_cost); | ||
40 | } | ||
41 | static ssize_t store_path_cost(struct net_bridge_port *p, unsigned long v) | ||
42 | { | ||
43 | br_stp_set_path_cost(p, v); | ||
44 | return 0; | ||
45 | } | ||
46 | static BRPORT_ATTR(path_cost, S_IRUGO | S_IWUSR, | ||
47 | show_path_cost, store_path_cost); | ||
48 | |||
49 | static ssize_t show_priority(struct net_bridge_port *p, char *buf) | ||
50 | { | ||
51 | return sprintf(buf, "%d\n", p->priority); | ||
52 | } | ||
53 | static ssize_t store_priority(struct net_bridge_port *p, unsigned long v) | ||
54 | { | ||
55 | if (v >= (1<<(16-BR_PORT_BITS))) | ||
56 | return -ERANGE; | ||
57 | br_stp_set_port_priority(p, v); | ||
58 | return 0; | ||
59 | } | ||
60 | static BRPORT_ATTR(priority, S_IRUGO | S_IWUSR, | ||
61 | show_priority, store_priority); | ||
62 | |||
63 | static ssize_t show_designated_root(struct net_bridge_port *p, char *buf) | ||
64 | { | ||
65 | return br_show_bridge_id(buf, &p->designated_root); | ||
66 | } | ||
67 | static BRPORT_ATTR(designated_root, S_IRUGO, show_designated_root, NULL); | ||
68 | |||
69 | static ssize_t show_designated_bridge(struct net_bridge_port *p, char *buf) | ||
70 | { | ||
71 | return br_show_bridge_id(buf, &p->designated_bridge); | ||
72 | } | ||
73 | static BRPORT_ATTR(designated_bridge, S_IRUGO, show_designated_bridge, NULL); | ||
74 | |||
75 | static ssize_t show_designated_port(struct net_bridge_port *p, char *buf) | ||
76 | { | ||
77 | return sprintf(buf, "%d\n", p->designated_port); | ||
78 | } | ||
79 | static BRPORT_ATTR(designated_port, S_IRUGO, show_designated_port, NULL); | ||
80 | |||
81 | static ssize_t show_designated_cost(struct net_bridge_port *p, char *buf) | ||
82 | { | ||
83 | return sprintf(buf, "%d\n", p->designated_cost); | ||
84 | } | ||
85 | static BRPORT_ATTR(designated_cost, S_IRUGO, show_designated_cost, NULL); | ||
86 | |||
87 | static ssize_t show_port_id(struct net_bridge_port *p, char *buf) | ||
88 | { | ||
89 | return sprintf(buf, "0x%x\n", p->port_id); | ||
90 | } | ||
91 | static BRPORT_ATTR(port_id, S_IRUGO, show_port_id, NULL); | ||
92 | |||
93 | static ssize_t show_port_no(struct net_bridge_port *p, char *buf) | ||
94 | { | ||
95 | return sprintf(buf, "0x%x\n", p->port_no); | ||
96 | } | ||
97 | |||
98 | static BRPORT_ATTR(port_no, S_IRUGO, show_port_no, NULL); | ||
99 | |||
100 | static ssize_t show_change_ack(struct net_bridge_port *p, char *buf) | ||
101 | { | ||
102 | return sprintf(buf, "%d\n", p->topology_change_ack); | ||
103 | } | ||
104 | static BRPORT_ATTR(change_ack, S_IRUGO, show_change_ack, NULL); | ||
105 | |||
106 | static ssize_t show_config_pending(struct net_bridge_port *p, char *buf) | ||
107 | { | ||
108 | return sprintf(buf, "%d\n", p->config_pending); | ||
109 | } | ||
110 | static BRPORT_ATTR(config_pending, S_IRUGO, show_config_pending, NULL); | ||
111 | |||
112 | static ssize_t show_port_state(struct net_bridge_port *p, char *buf) | ||
113 | { | ||
114 | return sprintf(buf, "%d\n", p->state); | ||
115 | } | ||
116 | static BRPORT_ATTR(state, S_IRUGO, show_port_state, NULL); | ||
117 | |||
118 | static ssize_t show_message_age_timer(struct net_bridge_port *p, | ||
119 | char *buf) | ||
120 | { | ||
121 | return sprintf(buf, "%ld\n", br_timer_value(&p->message_age_timer)); | ||
122 | } | ||
123 | static BRPORT_ATTR(message_age_timer, S_IRUGO, show_message_age_timer, NULL); | ||
124 | |||
125 | static ssize_t show_forward_delay_timer(struct net_bridge_port *p, | ||
126 | char *buf) | ||
127 | { | ||
128 | return sprintf(buf, "%ld\n", br_timer_value(&p->forward_delay_timer)); | ||
129 | } | ||
130 | static BRPORT_ATTR(forward_delay_timer, S_IRUGO, show_forward_delay_timer, NULL); | ||
131 | |||
132 | static ssize_t show_hold_timer(struct net_bridge_port *p, | ||
133 | char *buf) | ||
134 | { | ||
135 | return sprintf(buf, "%ld\n", br_timer_value(&p->hold_timer)); | ||
136 | } | ||
137 | static BRPORT_ATTR(hold_timer, S_IRUGO, show_hold_timer, NULL); | ||
138 | |||
139 | static struct brport_attribute *brport_attrs[] = { | ||
140 | &brport_attr_path_cost, | ||
141 | &brport_attr_priority, | ||
142 | &brport_attr_port_id, | ||
143 | &brport_attr_port_no, | ||
144 | &brport_attr_designated_root, | ||
145 | &brport_attr_designated_bridge, | ||
146 | &brport_attr_designated_port, | ||
147 | &brport_attr_designated_cost, | ||
148 | &brport_attr_state, | ||
149 | &brport_attr_change_ack, | ||
150 | &brport_attr_config_pending, | ||
151 | &brport_attr_message_age_timer, | ||
152 | &brport_attr_forward_delay_timer, | ||
153 | &brport_attr_hold_timer, | ||
154 | NULL | ||
155 | }; | ||
156 | |||
157 | #define to_brport_attr(_at) container_of(_at, struct brport_attribute, attr) | ||
158 | #define to_brport(obj) container_of(obj, struct net_bridge_port, kobj) | ||
159 | |||
160 | static ssize_t brport_show(struct kobject * kobj, | ||
161 | struct attribute * attr, char * buf) | ||
162 | { | ||
163 | struct brport_attribute * brport_attr = to_brport_attr(attr); | ||
164 | struct net_bridge_port * p = to_brport(kobj); | ||
165 | |||
166 | return brport_attr->show(p, buf); | ||
167 | } | ||
168 | |||
169 | static ssize_t brport_store(struct kobject * kobj, | ||
170 | struct attribute * attr, | ||
171 | const char * buf, size_t count) | ||
172 | { | ||
173 | struct brport_attribute * brport_attr = to_brport_attr(attr); | ||
174 | struct net_bridge_port * p = to_brport(kobj); | ||
175 | ssize_t ret = -EINVAL; | ||
176 | char *endp; | ||
177 | unsigned long val; | ||
178 | |||
179 | if (!capable(CAP_NET_ADMIN)) | ||
180 | return -EPERM; | ||
181 | |||
182 | val = simple_strtoul(buf, &endp, 0); | ||
183 | if (endp != buf) { | ||
184 | rtnl_lock(); | ||
185 | if (p->dev && p->br && brport_attr->store) { | ||
186 | spin_lock_bh(&p->br->lock); | ||
187 | ret = brport_attr->store(p, val); | ||
188 | spin_unlock_bh(&p->br->lock); | ||
189 | if (ret == 0) | ||
190 | ret = count; | ||
191 | } | ||
192 | rtnl_unlock(); | ||
193 | } | ||
194 | return ret; | ||
195 | } | ||
196 | |||
197 | /* called from kobject_put when port ref count goes to zero. */ | ||
198 | static void brport_release(struct kobject *kobj) | ||
199 | { | ||
200 | kfree(container_of(kobj, struct net_bridge_port, kobj)); | ||
201 | } | ||
202 | |||
203 | static struct sysfs_ops brport_sysfs_ops = { | ||
204 | .show = brport_show, | ||
205 | .store = brport_store, | ||
206 | }; | ||
207 | |||
208 | static struct kobj_type brport_ktype = { | ||
209 | .sysfs_ops = &brport_sysfs_ops, | ||
210 | .release = brport_release, | ||
211 | }; | ||
212 | |||
213 | |||
214 | /* | ||
215 | * Add sysfs entries to ethernet device added to a bridge. | ||
216 | * Creates a brport subdirectory with bridge attributes. | ||
217 | * Puts symlink in bridge's brport subdirectory | ||
218 | */ | ||
219 | int br_sysfs_addif(struct net_bridge_port *p) | ||
220 | { | ||
221 | struct net_bridge *br = p->br; | ||
222 | struct brport_attribute **a; | ||
223 | int err; | ||
224 | |||
225 | ASSERT_RTNL(); | ||
226 | |||
227 | kobject_set_name(&p->kobj, SYSFS_BRIDGE_PORT_ATTR); | ||
228 | p->kobj.ktype = &brport_ktype; | ||
229 | p->kobj.parent = &(p->dev->class_dev.kobj); | ||
230 | p->kobj.kset = NULL; | ||
231 | |||
232 | err = kobject_add(&p->kobj); | ||
233 | if(err) | ||
234 | goto out1; | ||
235 | |||
236 | err = sysfs_create_link(&p->kobj, &br->dev->class_dev.kobj, | ||
237 | SYSFS_BRIDGE_PORT_LINK); | ||
238 | if (err) | ||
239 | goto out2; | ||
240 | |||
241 | for (a = brport_attrs; *a; ++a) { | ||
242 | err = sysfs_create_file(&p->kobj, &((*a)->attr)); | ||
243 | if (err) | ||
244 | goto out2; | ||
245 | } | ||
246 | |||
247 | err = sysfs_create_link(&br->ifobj, &p->kobj, p->dev->name); | ||
248 | if (err) | ||
249 | goto out2; | ||
250 | |||
251 | return 0; | ||
252 | out2: | ||
253 | kobject_del(&p->kobj); | ||
254 | out1: | ||
255 | return err; | ||
256 | } | ||
257 | |||
258 | void br_sysfs_removeif(struct net_bridge_port *p) | ||
259 | { | ||
260 | pr_debug("br_sysfs_removeif\n"); | ||
261 | sysfs_remove_link(&p->br->ifobj, p->dev->name); | ||
262 | kobject_del(&p->kobj); | ||
263 | } | ||
264 | |||
265 | void br_sysfs_freeif(struct net_bridge_port *p) | ||
266 | { | ||
267 | pr_debug("br_sysfs_freeif\n"); | ||
268 | kobject_put(&p->kobj); | ||
269 | } | ||
diff --git a/net/bridge/netfilter/Kconfig b/net/bridge/netfilter/Kconfig new file mode 100644 index 000000000000..68ccef507b49 --- /dev/null +++ b/net/bridge/netfilter/Kconfig | |||
@@ -0,0 +1,211 @@ | |||
1 | # | ||
2 | # Bridge netfilter configuration | ||
3 | # | ||
4 | |||
5 | menu "Bridge: Netfilter Configuration" | ||
6 | depends on BRIDGE && NETFILTER | ||
7 | |||
8 | config BRIDGE_NF_EBTABLES | ||
9 | tristate "Ethernet Bridge tables (ebtables) support" | ||
10 | help | ||
11 | ebtables is a general, extensible frame/packet identification | ||
12 | framework. Say 'Y' or 'M' here if you want to do Ethernet | ||
13 | filtering/NAT/brouting on the Ethernet bridge. | ||
14 | # | ||
15 | # tables | ||
16 | # | ||
17 | config BRIDGE_EBT_BROUTE | ||
18 | tristate "ebt: broute table support" | ||
19 | depends on BRIDGE_NF_EBTABLES | ||
20 | help | ||
21 | The ebtables broute table is used to define rules that decide between | ||
22 | bridging and routing frames, giving Linux the functionality of a | ||
23 | brouter. See the man page for ebtables(8) and examples on the ebtables | ||
24 | website. | ||
25 | |||
26 | To compile it as a module, choose M here. If unsure, say N. | ||
27 | |||
28 | config BRIDGE_EBT_T_FILTER | ||
29 | tristate "ebt: filter table support" | ||
30 | depends on BRIDGE_NF_EBTABLES | ||
31 | help | ||
32 | The ebtables filter table is used to define frame filtering rules at | ||
33 | local input, forwarding and local output. See the man page for | ||
34 | ebtables(8). | ||
35 | |||
36 | To compile it as a module, choose M here. If unsure, say N. | ||
37 | |||
38 | config BRIDGE_EBT_T_NAT | ||
39 | tristate "ebt: nat table support" | ||
40 | depends on BRIDGE_NF_EBTABLES | ||
41 | help | ||
42 | The ebtables nat table is used to define rules that alter the MAC | ||
43 | source address (MAC SNAT) or the MAC destination address (MAC DNAT). | ||
44 | See the man page for ebtables(8). | ||
45 | |||
46 | To compile it as a module, choose M here. If unsure, say N. | ||
47 | # | ||
48 | # matches | ||
49 | # | ||
50 | config BRIDGE_EBT_802_3 | ||
51 | tristate "ebt: 802.3 filter support" | ||
52 | depends on BRIDGE_NF_EBTABLES | ||
53 | help | ||
54 | This option adds matching support for 802.3 Ethernet frames. | ||
55 | |||
56 | To compile it as a module, choose M here. If unsure, say N. | ||
57 | |||
58 | config BRIDGE_EBT_AMONG | ||
59 | tristate "ebt: among filter support" | ||
60 | depends on BRIDGE_NF_EBTABLES | ||
61 | help | ||
62 | This option adds the among match, which allows matching the MAC source | ||
63 | and/or destination address on a list of addresses. Optionally, | ||
64 | MAC/IP address pairs can be matched, f.e. for anti-spoofing rules. | ||
65 | |||
66 | To compile it as a module, choose M here. If unsure, say N. | ||
67 | |||
68 | config BRIDGE_EBT_ARP | ||
69 | tristate "ebt: ARP filter support" | ||
70 | depends on BRIDGE_NF_EBTABLES | ||
71 | help | ||
72 | This option adds the ARP match, which allows ARP and RARP header field | ||
73 | filtering. | ||
74 | |||
75 | To compile it as a module, choose M here. If unsure, say N. | ||
76 | |||
77 | config BRIDGE_EBT_IP | ||
78 | tristate "ebt: IP filter support" | ||
79 | depends on BRIDGE_NF_EBTABLES | ||
80 | help | ||
81 | This option adds the IP match, which allows basic IP header field | ||
82 | filtering. | ||
83 | |||
84 | To compile it as a module, choose M here. If unsure, say N. | ||
85 | |||
86 | config BRIDGE_EBT_LIMIT | ||
87 | tristate "ebt: limit match support" | ||
88 | depends on BRIDGE_NF_EBTABLES | ||
89 | help | ||
90 | This option adds the limit match, which allows you to control | ||
91 | the rate at which a rule can be matched. This match is the | ||
92 | equivalent of the iptables limit match. | ||
93 | |||
94 | If you want to compile it as a module, say M here and read | ||
95 | <file:Documentation/kbuild/modules.txt>. If unsure, say `N'. | ||
96 | |||
97 | config BRIDGE_EBT_MARK | ||
98 | tristate "ebt: mark filter support" | ||
99 | depends on BRIDGE_NF_EBTABLES | ||
100 | help | ||
101 | This option adds the mark match, which allows matching frames based on | ||
102 | the 'nfmark' value in the frame. This can be set by the mark target. | ||
103 | This value is the same as the one used in the iptables mark match and | ||
104 | target. | ||
105 | |||
106 | To compile it as a module, choose M here. If unsure, say N. | ||
107 | |||
108 | config BRIDGE_EBT_PKTTYPE | ||
109 | tristate "ebt: packet type filter support" | ||
110 | depends on BRIDGE_NF_EBTABLES | ||
111 | help | ||
112 | This option adds the packet type match, which allows matching on the | ||
113 | type of packet based on its Ethernet "class" (as determined by | ||
114 | the generic networking code): broadcast, multicast, | ||
115 | for this host alone or for another host. | ||
116 | |||
117 | To compile it as a module, choose M here. If unsure, say N. | ||
118 | |||
119 | config BRIDGE_EBT_STP | ||
120 | tristate "ebt: STP filter support" | ||
121 | depends on BRIDGE_NF_EBTABLES | ||
122 | help | ||
123 | This option adds the Spanning Tree Protocol match, which | ||
124 | allows STP header field filtering. | ||
125 | |||
126 | To compile it as a module, choose M here. If unsure, say N. | ||
127 | |||
128 | config BRIDGE_EBT_VLAN | ||
129 | tristate "ebt: 802.1Q VLAN filter support" | ||
130 | depends on BRIDGE_NF_EBTABLES | ||
131 | help | ||
132 | This option adds the 802.1Q vlan match, which allows the filtering of | ||
133 | 802.1Q vlan fields. | ||
134 | |||
135 | To compile it as a module, choose M here. If unsure, say N. | ||
136 | # | ||
137 | # targets | ||
138 | # | ||
139 | config BRIDGE_EBT_ARPREPLY | ||
140 | tristate "ebt: arp reply target support" | ||
141 | depends on BRIDGE_NF_EBTABLES | ||
142 | help | ||
143 | This option adds the arp reply target, which allows | ||
144 | automatically sending arp replies to arp requests. | ||
145 | |||
146 | To compile it as a module, choose M here. If unsure, say N. | ||
147 | |||
148 | config BRIDGE_EBT_DNAT | ||
149 | tristate "ebt: dnat target support" | ||
150 | depends on BRIDGE_NF_EBTABLES | ||
151 | help | ||
152 | This option adds the MAC DNAT target, which allows altering the MAC | ||
153 | destination address of frames. | ||
154 | |||
155 | To compile it as a module, choose M here. If unsure, say N. | ||
156 | |||
157 | config BRIDGE_EBT_MARK_T | ||
158 | tristate "ebt: mark target support" | ||
159 | depends on BRIDGE_NF_EBTABLES | ||
160 | help | ||
161 | This option adds the mark target, which allows marking frames by | ||
162 | setting the 'nfmark' value in the frame. | ||
163 | This value is the same as the one used in the iptables mark match and | ||
164 | target. | ||
165 | |||
166 | To compile it as a module, choose M here. If unsure, say N. | ||
167 | |||
168 | config BRIDGE_EBT_REDIRECT | ||
169 | tristate "ebt: redirect target support" | ||
170 | depends on BRIDGE_NF_EBTABLES | ||
171 | help | ||
172 | This option adds the MAC redirect target, which allows altering the MAC | ||
173 | destination address of a frame to that of the device it arrived on. | ||
174 | |||
175 | To compile it as a module, choose M here. If unsure, say N. | ||
176 | |||
177 | config BRIDGE_EBT_SNAT | ||
178 | tristate "ebt: snat target support" | ||
179 | depends on BRIDGE_NF_EBTABLES | ||
180 | help | ||
181 | This option adds the MAC SNAT target, which allows altering the MAC | ||
182 | source address of frames. | ||
183 | |||
184 | To compile it as a module, choose M here. If unsure, say N. | ||
185 | # | ||
186 | # watchers | ||
187 | # | ||
188 | config BRIDGE_EBT_LOG | ||
189 | tristate "ebt: log support" | ||
190 | depends on BRIDGE_NF_EBTABLES | ||
191 | help | ||
192 | This option adds the log watcher, that you can use in any rule | ||
193 | in any ebtables table. It records info about the frame header | ||
194 | to the syslog. | ||
195 | |||
196 | To compile it as a module, choose M here. If unsure, say N. | ||
197 | |||
198 | config BRIDGE_EBT_ULOG | ||
199 | tristate "ebt: ulog support" | ||
200 | depends on BRIDGE_NF_EBTABLES | ||
201 | help | ||
202 | This option adds the ulog watcher, that you can use in any rule | ||
203 | in any ebtables table. The packet is passed to a userspace | ||
204 | logging daemon using netlink multicast sockets. This differs | ||
205 | from the log watcher in the sense that the complete packet is | ||
206 | sent to userspace instead of a descriptive text and that | ||
207 | netlink multicast sockets are used instead of the syslog. | ||
208 | |||
209 | To compile it as a module, choose M here. If unsure, say N. | ||
210 | |||
211 | endmenu | ||
diff --git a/net/bridge/netfilter/Makefile b/net/bridge/netfilter/Makefile new file mode 100644 index 000000000000..8bf6d9f6e9d3 --- /dev/null +++ b/net/bridge/netfilter/Makefile | |||
@@ -0,0 +1,32 @@ | |||
1 | # | ||
2 | # Makefile for the netfilter modules for Link Layer filtering on a bridge. | ||
3 | # | ||
4 | |||
5 | obj-$(CONFIG_BRIDGE_NF_EBTABLES) += ebtables.o | ||
6 | |||
7 | # tables | ||
8 | obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o | ||
9 | obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o | ||
10 | obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o | ||
11 | |||
12 | #matches | ||
13 | obj-$(CONFIG_BRIDGE_EBT_802_3) += ebt_802_3.o | ||
14 | obj-$(CONFIG_BRIDGE_EBT_AMONG) += ebt_among.o | ||
15 | obj-$(CONFIG_BRIDGE_EBT_ARP) += ebt_arp.o | ||
16 | obj-$(CONFIG_BRIDGE_EBT_IP) += ebt_ip.o | ||
17 | obj-$(CONFIG_BRIDGE_EBT_LIMIT) += ebt_limit.o | ||
18 | obj-$(CONFIG_BRIDGE_EBT_MARK) += ebt_mark_m.o | ||
19 | obj-$(CONFIG_BRIDGE_EBT_PKTTYPE) += ebt_pkttype.o | ||
20 | obj-$(CONFIG_BRIDGE_EBT_STP) += ebt_stp.o | ||
21 | obj-$(CONFIG_BRIDGE_EBT_VLAN) += ebt_vlan.o | ||
22 | |||
23 | # targets | ||
24 | obj-$(CONFIG_BRIDGE_EBT_ARPREPLY) += ebt_arpreply.o | ||
25 | obj-$(CONFIG_BRIDGE_EBT_MARK_T) += ebt_mark.o | ||
26 | obj-$(CONFIG_BRIDGE_EBT_DNAT) += ebt_dnat.o | ||
27 | obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o | ||
28 | obj-$(CONFIG_BRIDGE_EBT_SNAT) += ebt_snat.o | ||
29 | |||
30 | # watchers | ||
31 | obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o | ||
32 | obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_ulog.o | ||
diff --git a/net/bridge/netfilter/ebt_802_3.c b/net/bridge/netfilter/ebt_802_3.c new file mode 100644 index 000000000000..468ebdf4bc1c --- /dev/null +++ b/net/bridge/netfilter/ebt_802_3.c | |||
@@ -0,0 +1,73 @@ | |||
1 | /* | ||
2 | * 802_3 | ||
3 | * | ||
4 | * Author: | ||
5 | * Chris Vitale csv@bluetail.com | ||
6 | * | ||
7 | * May 2003 | ||
8 | * | ||
9 | */ | ||
10 | |||
11 | #include <linux/netfilter_bridge/ebtables.h> | ||
12 | #include <linux/netfilter_bridge/ebt_802_3.h> | ||
13 | #include <linux/module.h> | ||
14 | |||
15 | static int ebt_filter_802_3(const struct sk_buff *skb, const struct net_device *in, | ||
16 | const struct net_device *out, const void *data, unsigned int datalen) | ||
17 | { | ||
18 | struct ebt_802_3_info *info = (struct ebt_802_3_info *)data; | ||
19 | struct ebt_802_3_hdr *hdr = ebt_802_3_hdr(skb); | ||
20 | uint16_t type = hdr->llc.ui.ctrl & IS_UI ? hdr->llc.ui.type : hdr->llc.ni.type; | ||
21 | |||
22 | if (info->bitmask & EBT_802_3_SAP) { | ||
23 | if (FWINV(info->sap != hdr->llc.ui.ssap, EBT_802_3_SAP)) | ||
24 | return EBT_NOMATCH; | ||
25 | if (FWINV(info->sap != hdr->llc.ui.dsap, EBT_802_3_SAP)) | ||
26 | return EBT_NOMATCH; | ||
27 | } | ||
28 | |||
29 | if (info->bitmask & EBT_802_3_TYPE) { | ||
30 | if (!(hdr->llc.ui.dsap == CHECK_TYPE && hdr->llc.ui.ssap == CHECK_TYPE)) | ||
31 | return EBT_NOMATCH; | ||
32 | if (FWINV(info->type != type, EBT_802_3_TYPE)) | ||
33 | return EBT_NOMATCH; | ||
34 | } | ||
35 | |||
36 | return EBT_MATCH; | ||
37 | } | ||
38 | |||
39 | static struct ebt_match filter_802_3; | ||
40 | static int ebt_802_3_check(const char *tablename, unsigned int hookmask, | ||
41 | const struct ebt_entry *e, void *data, unsigned int datalen) | ||
42 | { | ||
43 | struct ebt_802_3_info *info = (struct ebt_802_3_info *)data; | ||
44 | |||
45 | if (datalen < sizeof(struct ebt_802_3_info)) | ||
46 | return -EINVAL; | ||
47 | if (info->bitmask & ~EBT_802_3_MASK || info->invflags & ~EBT_802_3_MASK) | ||
48 | return -EINVAL; | ||
49 | |||
50 | return 0; | ||
51 | } | ||
52 | |||
53 | static struct ebt_match filter_802_3 = | ||
54 | { | ||
55 | .name = EBT_802_3_MATCH, | ||
56 | .match = ebt_filter_802_3, | ||
57 | .check = ebt_802_3_check, | ||
58 | .me = THIS_MODULE, | ||
59 | }; | ||
60 | |||
61 | static int __init init(void) | ||
62 | { | ||
63 | return ebt_register_match(&filter_802_3); | ||
64 | } | ||
65 | |||
66 | static void __exit fini(void) | ||
67 | { | ||
68 | ebt_unregister_match(&filter_802_3); | ||
69 | } | ||
70 | |||
71 | module_init(init); | ||
72 | module_exit(fini); | ||
73 | MODULE_LICENSE("GPL"); | ||
diff --git a/net/bridge/netfilter/ebt_among.c b/net/bridge/netfilter/ebt_among.c new file mode 100644 index 000000000000..5a1f5e3bff15 --- /dev/null +++ b/net/bridge/netfilter/ebt_among.c | |||
@@ -0,0 +1,228 @@ | |||
1 | /* | ||
2 | * ebt_among | ||
3 | * | ||
4 | * Authors: | ||
5 | * Grzegorz Borowiak <grzes@gnu.univ.gda.pl> | ||
6 | * | ||
7 | * August, 2003 | ||
8 | * | ||
9 | */ | ||
10 | |||
11 | #include <linux/netfilter_bridge/ebtables.h> | ||
12 | #include <linux/netfilter_bridge/ebt_among.h> | ||
13 | #include <linux/ip.h> | ||
14 | #include <linux/if_arp.h> | ||
15 | #include <linux/module.h> | ||
16 | |||
17 | static int ebt_mac_wormhash_contains(const struct ebt_mac_wormhash *wh, | ||
18 | const char *mac, uint32_t ip) | ||
19 | { | ||
20 | /* You may be puzzled as to how this code works. | ||
21 | * Some tricks were used, refer to | ||
22 | * include/linux/netfilter_bridge/ebt_among.h | ||
23 | * as there you can find a solution of this mystery. | ||
24 | */ | ||
25 | const struct ebt_mac_wormhash_tuple *p; | ||
26 | int start, limit, i; | ||
27 | uint32_t cmp[2] = { 0, 0 }; | ||
28 | int key = (const unsigned char) mac[5]; | ||
29 | |||
30 | memcpy(((char *) cmp) + 2, mac, 6); | ||
31 | start = wh->table[key]; | ||
32 | limit = wh->table[key + 1]; | ||
33 | if (ip) { | ||
34 | for (i = start; i < limit; i++) { | ||
35 | p = &wh->pool[i]; | ||
36 | if (cmp[1] == p->cmp[1] && cmp[0] == p->cmp[0]) { | ||
37 | if (p->ip == 0 || p->ip == ip) { | ||
38 | return 1; | ||
39 | } | ||
40 | } | ||
41 | } | ||
42 | } else { | ||
43 | for (i = start; i < limit; i++) { | ||
44 | p = &wh->pool[i]; | ||
45 | if (cmp[1] == p->cmp[1] && cmp[0] == p->cmp[0]) { | ||
46 | if (p->ip == 0) { | ||
47 | return 1; | ||
48 | } | ||
49 | } | ||
50 | } | ||
51 | } | ||
52 | return 0; | ||
53 | } | ||
54 | |||
55 | static int ebt_mac_wormhash_check_integrity(const struct ebt_mac_wormhash | ||
56 | *wh) | ||
57 | { | ||
58 | int i; | ||
59 | |||
60 | for (i = 0; i < 256; i++) { | ||
61 | if (wh->table[i] > wh->table[i + 1]) | ||
62 | return -0x100 - i; | ||
63 | if (wh->table[i] < 0) | ||
64 | return -0x200 - i; | ||
65 | if (wh->table[i] > wh->poolsize) | ||
66 | return -0x300 - i; | ||
67 | } | ||
68 | if (wh->table[256] > wh->poolsize) | ||
69 | return -0xc00; | ||
70 | return 0; | ||
71 | } | ||
72 | |||
73 | static int get_ip_dst(const struct sk_buff *skb, uint32_t *addr) | ||
74 | { | ||
75 | if (eth_hdr(skb)->h_proto == htons(ETH_P_IP)) { | ||
76 | struct iphdr _iph, *ih; | ||
77 | |||
78 | ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph); | ||
79 | if (ih == NULL) | ||
80 | return -1; | ||
81 | *addr = ih->daddr; | ||
82 | } else if (eth_hdr(skb)->h_proto == htons(ETH_P_ARP)) { | ||
83 | struct arphdr _arph, *ah; | ||
84 | uint32_t buf, *bp; | ||
85 | |||
86 | ah = skb_header_pointer(skb, 0, sizeof(_arph), &_arph); | ||
87 | if (ah == NULL || | ||
88 | ah->ar_pln != sizeof(uint32_t) || | ||
89 | ah->ar_hln != ETH_ALEN) | ||
90 | return -1; | ||
91 | bp = skb_header_pointer(skb, sizeof(struct arphdr) + | ||
92 | 2 * ETH_ALEN + sizeof(uint32_t), | ||
93 | sizeof(uint32_t), &buf); | ||
94 | if (bp == NULL) | ||
95 | return -1; | ||
96 | *addr = *bp; | ||
97 | } | ||
98 | return 0; | ||
99 | } | ||
100 | |||
101 | static int get_ip_src(const struct sk_buff *skb, uint32_t *addr) | ||
102 | { | ||
103 | if (eth_hdr(skb)->h_proto == htons(ETH_P_IP)) { | ||
104 | struct iphdr _iph, *ih; | ||
105 | |||
106 | ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph); | ||
107 | if (ih == NULL) | ||
108 | return -1; | ||
109 | *addr = ih->saddr; | ||
110 | } else if (eth_hdr(skb)->h_proto == htons(ETH_P_ARP)) { | ||
111 | struct arphdr _arph, *ah; | ||
112 | uint32_t buf, *bp; | ||
113 | |||
114 | ah = skb_header_pointer(skb, 0, sizeof(_arph), &_arph); | ||
115 | if (ah == NULL || | ||
116 | ah->ar_pln != sizeof(uint32_t) || | ||
117 | ah->ar_hln != ETH_ALEN) | ||
118 | return -1; | ||
119 | bp = skb_header_pointer(skb, sizeof(struct arphdr) + | ||
120 | ETH_ALEN, sizeof(uint32_t), &buf); | ||
121 | if (bp == NULL) | ||
122 | return -1; | ||
123 | *addr = *bp; | ||
124 | } | ||
125 | return 0; | ||
126 | } | ||
127 | |||
128 | static int ebt_filter_among(const struct sk_buff *skb, | ||
129 | const struct net_device *in, | ||
130 | const struct net_device *out, const void *data, | ||
131 | unsigned int datalen) | ||
132 | { | ||
133 | struct ebt_among_info *info = (struct ebt_among_info *) data; | ||
134 | const char *dmac, *smac; | ||
135 | const struct ebt_mac_wormhash *wh_dst, *wh_src; | ||
136 | uint32_t dip = 0, sip = 0; | ||
137 | |||
138 | wh_dst = ebt_among_wh_dst(info); | ||
139 | wh_src = ebt_among_wh_src(info); | ||
140 | |||
141 | if (wh_src) { | ||
142 | smac = eth_hdr(skb)->h_source; | ||
143 | if (get_ip_src(skb, &sip)) | ||
144 | return EBT_NOMATCH; | ||
145 | if (!(info->bitmask & EBT_AMONG_SRC_NEG)) { | ||
146 | /* we match only if it contains */ | ||
147 | if (!ebt_mac_wormhash_contains(wh_src, smac, sip)) | ||
148 | return EBT_NOMATCH; | ||
149 | } else { | ||
150 | /* we match only if it DOES NOT contain */ | ||
151 | if (ebt_mac_wormhash_contains(wh_src, smac, sip)) | ||
152 | return EBT_NOMATCH; | ||
153 | } | ||
154 | } | ||
155 | |||
156 | if (wh_dst) { | ||
157 | dmac = eth_hdr(skb)->h_dest; | ||
158 | if (get_ip_dst(skb, &dip)) | ||
159 | return EBT_NOMATCH; | ||
160 | if (!(info->bitmask & EBT_AMONG_DST_NEG)) { | ||
161 | /* we match only if it contains */ | ||
162 | if (!ebt_mac_wormhash_contains(wh_dst, dmac, dip)) | ||
163 | return EBT_NOMATCH; | ||
164 | } else { | ||
165 | /* we match only if it DOES NOT contain */ | ||
166 | if (ebt_mac_wormhash_contains(wh_dst, dmac, dip)) | ||
167 | return EBT_NOMATCH; | ||
168 | } | ||
169 | } | ||
170 | |||
171 | return EBT_MATCH; | ||
172 | } | ||
173 | |||
174 | static int ebt_among_check(const char *tablename, unsigned int hookmask, | ||
175 | const struct ebt_entry *e, void *data, | ||
176 | unsigned int datalen) | ||
177 | { | ||
178 | struct ebt_among_info *info = (struct ebt_among_info *) data; | ||
179 | int expected_length = sizeof(struct ebt_among_info); | ||
180 | const struct ebt_mac_wormhash *wh_dst, *wh_src; | ||
181 | int err; | ||
182 | |||
183 | wh_dst = ebt_among_wh_dst(info); | ||
184 | wh_src = ebt_among_wh_src(info); | ||
185 | expected_length += ebt_mac_wormhash_size(wh_dst); | ||
186 | expected_length += ebt_mac_wormhash_size(wh_src); | ||
187 | |||
188 | if (datalen != EBT_ALIGN(expected_length)) { | ||
189 | printk(KERN_WARNING | ||
190 | "ebtables: among: wrong size: %d" | ||
191 | "against expected %d, rounded to %Zd\n", | ||
192 | datalen, expected_length, | ||
193 | EBT_ALIGN(expected_length)); | ||
194 | return -EINVAL; | ||
195 | } | ||
196 | if (wh_dst && (err = ebt_mac_wormhash_check_integrity(wh_dst))) { | ||
197 | printk(KERN_WARNING | ||
198 | "ebtables: among: dst integrity fail: %x\n", -err); | ||
199 | return -EINVAL; | ||
200 | } | ||
201 | if (wh_src && (err = ebt_mac_wormhash_check_integrity(wh_src))) { | ||
202 | printk(KERN_WARNING | ||
203 | "ebtables: among: src integrity fail: %x\n", -err); | ||
204 | return -EINVAL; | ||
205 | } | ||
206 | return 0; | ||
207 | } | ||
208 | |||
209 | static struct ebt_match filter_among = { | ||
210 | .name = EBT_AMONG_MATCH, | ||
211 | .match = ebt_filter_among, | ||
212 | .check = ebt_among_check, | ||
213 | .me = THIS_MODULE, | ||
214 | }; | ||
215 | |||
216 | static int __init init(void) | ||
217 | { | ||
218 | return ebt_register_match(&filter_among); | ||
219 | } | ||
220 | |||
221 | static void __exit fini(void) | ||
222 | { | ||
223 | ebt_unregister_match(&filter_among); | ||
224 | } | ||
225 | |||
226 | module_init(init); | ||
227 | module_exit(fini); | ||
228 | MODULE_LICENSE("GPL"); | ||
diff --git a/net/bridge/netfilter/ebt_arp.c b/net/bridge/netfilter/ebt_arp.c new file mode 100644 index 000000000000..b94c48cb6e4b --- /dev/null +++ b/net/bridge/netfilter/ebt_arp.c | |||
@@ -0,0 +1,140 @@ | |||
1 | /* | ||
2 | * ebt_arp | ||
3 | * | ||
4 | * Authors: | ||
5 | * Bart De Schuymer <bdschuym@pandora.be> | ||
6 | * Tim Gardner <timg@tpi.com> | ||
7 | * | ||
8 | * April, 2002 | ||
9 | * | ||
10 | */ | ||
11 | |||
12 | #include <linux/netfilter_bridge/ebtables.h> | ||
13 | #include <linux/netfilter_bridge/ebt_arp.h> | ||
14 | #include <linux/if_arp.h> | ||
15 | #include <linux/if_ether.h> | ||
16 | #include <linux/module.h> | ||
17 | |||
18 | static int ebt_filter_arp(const struct sk_buff *skb, const struct net_device *in, | ||
19 | const struct net_device *out, const void *data, unsigned int datalen) | ||
20 | { | ||
21 | struct ebt_arp_info *info = (struct ebt_arp_info *)data; | ||
22 | struct arphdr _arph, *ah; | ||
23 | |||
24 | ah = skb_header_pointer(skb, 0, sizeof(_arph), &_arph); | ||
25 | if (ah == NULL) | ||
26 | return EBT_NOMATCH; | ||
27 | if (info->bitmask & EBT_ARP_OPCODE && FWINV(info->opcode != | ||
28 | ah->ar_op, EBT_ARP_OPCODE)) | ||
29 | return EBT_NOMATCH; | ||
30 | if (info->bitmask & EBT_ARP_HTYPE && FWINV(info->htype != | ||
31 | ah->ar_hrd, EBT_ARP_HTYPE)) | ||
32 | return EBT_NOMATCH; | ||
33 | if (info->bitmask & EBT_ARP_PTYPE && FWINV(info->ptype != | ||
34 | ah->ar_pro, EBT_ARP_PTYPE)) | ||
35 | return EBT_NOMATCH; | ||
36 | |||
37 | if (info->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP)) { | ||
38 | uint32_t _addr, *ap; | ||
39 | |||
40 | /* IPv4 addresses are always 4 bytes */ | ||
41 | if (ah->ar_pln != sizeof(uint32_t)) | ||
42 | return EBT_NOMATCH; | ||
43 | if (info->bitmask & EBT_ARP_SRC_IP) { | ||
44 | ap = skb_header_pointer(skb, sizeof(struct arphdr) + | ||
45 | ah->ar_hln, sizeof(_addr), | ||
46 | &_addr); | ||
47 | if (ap == NULL) | ||
48 | return EBT_NOMATCH; | ||
49 | if (FWINV(info->saddr != (*ap & info->smsk), | ||
50 | EBT_ARP_SRC_IP)) | ||
51 | return EBT_NOMATCH; | ||
52 | } | ||
53 | |||
54 | if (info->bitmask & EBT_ARP_DST_IP) { | ||
55 | ap = skb_header_pointer(skb, sizeof(struct arphdr) + | ||
56 | 2*ah->ar_hln+sizeof(uint32_t), | ||
57 | sizeof(_addr), &_addr); | ||
58 | if (ap == NULL) | ||
59 | return EBT_NOMATCH; | ||
60 | if (FWINV(info->daddr != (*ap & info->dmsk), | ||
61 | EBT_ARP_DST_IP)) | ||
62 | return EBT_NOMATCH; | ||
63 | } | ||
64 | } | ||
65 | |||
66 | if (info->bitmask & (EBT_ARP_SRC_MAC | EBT_ARP_DST_MAC)) { | ||
67 | unsigned char _mac[ETH_ALEN], *mp; | ||
68 | uint8_t verdict, i; | ||
69 | |||
70 | /* MAC addresses are 6 bytes */ | ||
71 | if (ah->ar_hln != ETH_ALEN) | ||
72 | return EBT_NOMATCH; | ||
73 | if (info->bitmask & EBT_ARP_SRC_MAC) { | ||
74 | mp = skb_header_pointer(skb, sizeof(struct arphdr), | ||
75 | sizeof(_mac), &_mac); | ||
76 | if (mp == NULL) | ||
77 | return EBT_NOMATCH; | ||
78 | verdict = 0; | ||
79 | for (i = 0; i < 6; i++) | ||
80 | verdict |= (mp[i] ^ info->smaddr[i]) & | ||
81 | info->smmsk[i]; | ||
82 | if (FWINV(verdict != 0, EBT_ARP_SRC_MAC)) | ||
83 | return EBT_NOMATCH; | ||
84 | } | ||
85 | |||
86 | if (info->bitmask & EBT_ARP_DST_MAC) { | ||
87 | mp = skb_header_pointer(skb, sizeof(struct arphdr) + | ||
88 | ah->ar_hln + ah->ar_pln, | ||
89 | sizeof(_mac), &_mac); | ||
90 | if (mp == NULL) | ||
91 | return EBT_NOMATCH; | ||
92 | verdict = 0; | ||
93 | for (i = 0; i < 6; i++) | ||
94 | verdict |= (mp[i] ^ info->dmaddr[i]) & | ||
95 | info->dmmsk[i]; | ||
96 | if (FWINV(verdict != 0, EBT_ARP_DST_MAC)) | ||
97 | return EBT_NOMATCH; | ||
98 | } | ||
99 | } | ||
100 | |||
101 | return EBT_MATCH; | ||
102 | } | ||
103 | |||
104 | static int ebt_arp_check(const char *tablename, unsigned int hookmask, | ||
105 | const struct ebt_entry *e, void *data, unsigned int datalen) | ||
106 | { | ||
107 | struct ebt_arp_info *info = (struct ebt_arp_info *)data; | ||
108 | |||
109 | if (datalen != EBT_ALIGN(sizeof(struct ebt_arp_info))) | ||
110 | return -EINVAL; | ||
111 | if ((e->ethproto != htons(ETH_P_ARP) && | ||
112 | e->ethproto != htons(ETH_P_RARP)) || | ||
113 | e->invflags & EBT_IPROTO) | ||
114 | return -EINVAL; | ||
115 | if (info->bitmask & ~EBT_ARP_MASK || info->invflags & ~EBT_ARP_MASK) | ||
116 | return -EINVAL; | ||
117 | return 0; | ||
118 | } | ||
119 | |||
120 | static struct ebt_match filter_arp = | ||
121 | { | ||
122 | .name = EBT_ARP_MATCH, | ||
123 | .match = ebt_filter_arp, | ||
124 | .check = ebt_arp_check, | ||
125 | .me = THIS_MODULE, | ||
126 | }; | ||
127 | |||
128 | static int __init init(void) | ||
129 | { | ||
130 | return ebt_register_match(&filter_arp); | ||
131 | } | ||
132 | |||
133 | static void __exit fini(void) | ||
134 | { | ||
135 | ebt_unregister_match(&filter_arp); | ||
136 | } | ||
137 | |||
138 | module_init(init); | ||
139 | module_exit(fini); | ||
140 | MODULE_LICENSE("GPL"); | ||
diff --git a/net/bridge/netfilter/ebt_arpreply.c b/net/bridge/netfilter/ebt_arpreply.c new file mode 100644 index 000000000000..b934de90f7c5 --- /dev/null +++ b/net/bridge/netfilter/ebt_arpreply.c | |||
@@ -0,0 +1,97 @@ | |||
1 | /* | ||
2 | * ebt_arpreply | ||
3 | * | ||
4 | * Authors: | ||
5 | * Grzegorz Borowiak <grzes@gnu.univ.gda.pl> | ||
6 | * Bart De Schuymer <bdschuym@pandora.be> | ||
7 | * | ||
8 | * August, 2003 | ||
9 | * | ||
10 | */ | ||
11 | |||
12 | #include <linux/netfilter_bridge/ebtables.h> | ||
13 | #include <linux/netfilter_bridge/ebt_arpreply.h> | ||
14 | #include <linux/if_arp.h> | ||
15 | #include <net/arp.h> | ||
16 | #include <linux/module.h> | ||
17 | |||
18 | static int ebt_target_reply(struct sk_buff **pskb, unsigned int hooknr, | ||
19 | const struct net_device *in, const struct net_device *out, | ||
20 | const void *data, unsigned int datalen) | ||
21 | { | ||
22 | struct ebt_arpreply_info *info = (struct ebt_arpreply_info *)data; | ||
23 | u32 _sip, *siptr, _dip, *diptr; | ||
24 | struct arphdr _ah, *ap; | ||
25 | unsigned char _sha[ETH_ALEN], *shp; | ||
26 | struct sk_buff *skb = *pskb; | ||
27 | |||
28 | ap = skb_header_pointer(skb, 0, sizeof(_ah), &_ah); | ||
29 | if (ap == NULL) | ||
30 | return EBT_DROP; | ||
31 | |||
32 | if (ap->ar_op != htons(ARPOP_REQUEST) || | ||
33 | ap->ar_hln != ETH_ALEN || | ||
34 | ap->ar_pro != htons(ETH_P_IP) || | ||
35 | ap->ar_pln != 4) | ||
36 | return EBT_CONTINUE; | ||
37 | |||
38 | shp = skb_header_pointer(skb, sizeof(_ah), ETH_ALEN, &_sha); | ||
39 | if (shp == NULL) | ||
40 | return EBT_DROP; | ||
41 | |||
42 | siptr = skb_header_pointer(skb, sizeof(_ah) + ETH_ALEN, | ||
43 | sizeof(_sip), &_sip); | ||
44 | if (siptr == NULL) | ||
45 | return EBT_DROP; | ||
46 | |||
47 | diptr = skb_header_pointer(skb, | ||
48 | sizeof(_ah) + 2 * ETH_ALEN + sizeof(_sip), | ||
49 | sizeof(_dip), &_dip); | ||
50 | if (diptr == NULL) | ||
51 | return EBT_DROP; | ||
52 | |||
53 | arp_send(ARPOP_REPLY, ETH_P_ARP, *siptr, (struct net_device *)in, | ||
54 | *diptr, shp, info->mac, shp); | ||
55 | |||
56 | return info->target; | ||
57 | } | ||
58 | |||
59 | static int ebt_target_reply_check(const char *tablename, unsigned int hookmask, | ||
60 | const struct ebt_entry *e, void *data, unsigned int datalen) | ||
61 | { | ||
62 | struct ebt_arpreply_info *info = (struct ebt_arpreply_info *)data; | ||
63 | |||
64 | if (datalen != EBT_ALIGN(sizeof(struct ebt_arpreply_info))) | ||
65 | return -EINVAL; | ||
66 | if (BASE_CHAIN && info->target == EBT_RETURN) | ||
67 | return -EINVAL; | ||
68 | if (e->ethproto != htons(ETH_P_ARP) || | ||
69 | e->invflags & EBT_IPROTO) | ||
70 | return -EINVAL; | ||
71 | CLEAR_BASE_CHAIN_BIT; | ||
72 | if (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING)) | ||
73 | return -EINVAL; | ||
74 | return 0; | ||
75 | } | ||
76 | |||
77 | static struct ebt_target reply_target = | ||
78 | { | ||
79 | .name = EBT_ARPREPLY_TARGET, | ||
80 | .target = ebt_target_reply, | ||
81 | .check = ebt_target_reply_check, | ||
82 | .me = THIS_MODULE, | ||
83 | }; | ||
84 | |||
85 | static int __init init(void) | ||
86 | { | ||
87 | return ebt_register_target(&reply_target); | ||
88 | } | ||
89 | |||
90 | static void __exit fini(void) | ||
91 | { | ||
92 | ebt_unregister_target(&reply_target); | ||
93 | } | ||
94 | |||
95 | module_init(init); | ||
96 | module_exit(fini); | ||
97 | MODULE_LICENSE("GPL"); | ||
diff --git a/net/bridge/netfilter/ebt_dnat.c b/net/bridge/netfilter/ebt_dnat.c new file mode 100644 index 000000000000..f5463086c7bd --- /dev/null +++ b/net/bridge/netfilter/ebt_dnat.c | |||
@@ -0,0 +1,76 @@ | |||
1 | /* | ||
2 | * ebt_dnat | ||
3 | * | ||
4 | * Authors: | ||
5 | * Bart De Schuymer <bdschuym@pandora.be> | ||
6 | * | ||
7 | * June, 2002 | ||
8 | * | ||
9 | */ | ||
10 | |||
11 | #include <linux/netfilter_bridge/ebtables.h> | ||
12 | #include <linux/netfilter_bridge/ebt_nat.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <net/sock.h> | ||
15 | |||
16 | static int ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr, | ||
17 | const struct net_device *in, const struct net_device *out, | ||
18 | const void *data, unsigned int datalen) | ||
19 | { | ||
20 | struct ebt_nat_info *info = (struct ebt_nat_info *)data; | ||
21 | |||
22 | if (skb_shared(*pskb) || skb_cloned(*pskb)) { | ||
23 | struct sk_buff *nskb; | ||
24 | |||
25 | nskb = skb_copy(*pskb, GFP_ATOMIC); | ||
26 | if (!nskb) | ||
27 | return NF_DROP; | ||
28 | if ((*pskb)->sk) | ||
29 | skb_set_owner_w(nskb, (*pskb)->sk); | ||
30 | kfree_skb(*pskb); | ||
31 | *pskb = nskb; | ||
32 | } | ||
33 | memcpy(eth_hdr(*pskb)->h_dest, info->mac, ETH_ALEN); | ||
34 | return info->target; | ||
35 | } | ||
36 | |||
37 | static int ebt_target_dnat_check(const char *tablename, unsigned int hookmask, | ||
38 | const struct ebt_entry *e, void *data, unsigned int datalen) | ||
39 | { | ||
40 | struct ebt_nat_info *info = (struct ebt_nat_info *)data; | ||
41 | |||
42 | if (BASE_CHAIN && info->target == EBT_RETURN) | ||
43 | return -EINVAL; | ||
44 | CLEAR_BASE_CHAIN_BIT; | ||
45 | if ( (strcmp(tablename, "nat") || | ||
46 | (hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT)))) && | ||
47 | (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) ) | ||
48 | return -EINVAL; | ||
49 | if (datalen != EBT_ALIGN(sizeof(struct ebt_nat_info))) | ||
50 | return -EINVAL; | ||
51 | if (INVALID_TARGET) | ||
52 | return -EINVAL; | ||
53 | return 0; | ||
54 | } | ||
55 | |||
56 | static struct ebt_target dnat = | ||
57 | { | ||
58 | .name = EBT_DNAT_TARGET, | ||
59 | .target = ebt_target_dnat, | ||
60 | .check = ebt_target_dnat_check, | ||
61 | .me = THIS_MODULE, | ||
62 | }; | ||
63 | |||
64 | static int __init init(void) | ||
65 | { | ||
66 | return ebt_register_target(&dnat); | ||
67 | } | ||
68 | |||
69 | static void __exit fini(void) | ||
70 | { | ||
71 | ebt_unregister_target(&dnat); | ||
72 | } | ||
73 | |||
74 | module_init(init); | ||
75 | module_exit(fini); | ||
76 | MODULE_LICENSE("GPL"); | ||
diff --git a/net/bridge/netfilter/ebt_ip.c b/net/bridge/netfilter/ebt_ip.c new file mode 100644 index 000000000000..7323805b9726 --- /dev/null +++ b/net/bridge/netfilter/ebt_ip.c | |||
@@ -0,0 +1,122 @@ | |||
1 | /* | ||
2 | * ebt_ip | ||
3 | * | ||
4 | * Authors: | ||
5 | * Bart De Schuymer <bdschuym@pandora.be> | ||
6 | * | ||
7 | * April, 2002 | ||
8 | * | ||
9 | * Changes: | ||
10 | * added ip-sport and ip-dport | ||
11 | * Innominate Security Technologies AG <mhopf@innominate.com> | ||
12 | * September, 2002 | ||
13 | */ | ||
14 | |||
15 | #include <linux/netfilter_bridge/ebtables.h> | ||
16 | #include <linux/netfilter_bridge/ebt_ip.h> | ||
17 | #include <linux/ip.h> | ||
18 | #include <linux/in.h> | ||
19 | #include <linux/module.h> | ||
20 | |||
21 | struct tcpudphdr { | ||
22 | uint16_t src; | ||
23 | uint16_t dst; | ||
24 | }; | ||
25 | |||
26 | static int ebt_filter_ip(const struct sk_buff *skb, const struct net_device *in, | ||
27 | const struct net_device *out, const void *data, | ||
28 | unsigned int datalen) | ||
29 | { | ||
30 | struct ebt_ip_info *info = (struct ebt_ip_info *)data; | ||
31 | struct iphdr _iph, *ih; | ||
32 | struct tcpudphdr _ports, *pptr; | ||
33 | |||
34 | ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph); | ||
35 | if (ih == NULL) | ||
36 | return EBT_NOMATCH; | ||
37 | if (info->bitmask & EBT_IP_TOS && | ||
38 | FWINV(info->tos != ih->tos, EBT_IP_TOS)) | ||
39 | return EBT_NOMATCH; | ||
40 | if (info->bitmask & EBT_IP_SOURCE && | ||
41 | FWINV((ih->saddr & info->smsk) != | ||
42 | info->saddr, EBT_IP_SOURCE)) | ||
43 | return EBT_NOMATCH; | ||
44 | if ((info->bitmask & EBT_IP_DEST) && | ||
45 | FWINV((ih->daddr & info->dmsk) != | ||
46 | info->daddr, EBT_IP_DEST)) | ||
47 | return EBT_NOMATCH; | ||
48 | if (info->bitmask & EBT_IP_PROTO) { | ||
49 | if (FWINV(info->protocol != ih->protocol, EBT_IP_PROTO)) | ||
50 | return EBT_NOMATCH; | ||
51 | if (!(info->bitmask & EBT_IP_DPORT) && | ||
52 | !(info->bitmask & EBT_IP_SPORT)) | ||
53 | return EBT_MATCH; | ||
54 | pptr = skb_header_pointer(skb, ih->ihl*4, | ||
55 | sizeof(_ports), &_ports); | ||
56 | if (pptr == NULL) | ||
57 | return EBT_NOMATCH; | ||
58 | if (info->bitmask & EBT_IP_DPORT) { | ||
59 | u32 dst = ntohs(pptr->dst); | ||
60 | if (FWINV(dst < info->dport[0] || | ||
61 | dst > info->dport[1], | ||
62 | EBT_IP_DPORT)) | ||
63 | return EBT_NOMATCH; | ||
64 | } | ||
65 | if (info->bitmask & EBT_IP_SPORT) { | ||
66 | u32 src = ntohs(pptr->src); | ||
67 | if (FWINV(src < info->sport[0] || | ||
68 | src > info->sport[1], | ||
69 | EBT_IP_SPORT)) | ||
70 | return EBT_NOMATCH; | ||
71 | } | ||
72 | } | ||
73 | return EBT_MATCH; | ||
74 | } | ||
75 | |||
76 | static int ebt_ip_check(const char *tablename, unsigned int hookmask, | ||
77 | const struct ebt_entry *e, void *data, unsigned int datalen) | ||
78 | { | ||
79 | struct ebt_ip_info *info = (struct ebt_ip_info *)data; | ||
80 | |||
81 | if (datalen != EBT_ALIGN(sizeof(struct ebt_ip_info))) | ||
82 | return -EINVAL; | ||
83 | if (e->ethproto != htons(ETH_P_IP) || | ||
84 | e->invflags & EBT_IPROTO) | ||
85 | return -EINVAL; | ||
86 | if (info->bitmask & ~EBT_IP_MASK || info->invflags & ~EBT_IP_MASK) | ||
87 | return -EINVAL; | ||
88 | if (info->bitmask & (EBT_IP_DPORT | EBT_IP_SPORT)) { | ||
89 | if (info->invflags & EBT_IP_PROTO) | ||
90 | return -EINVAL; | ||
91 | if (info->protocol != IPPROTO_TCP && | ||
92 | info->protocol != IPPROTO_UDP) | ||
93 | return -EINVAL; | ||
94 | } | ||
95 | if (info->bitmask & EBT_IP_DPORT && info->dport[0] > info->dport[1]) | ||
96 | return -EINVAL; | ||
97 | if (info->bitmask & EBT_IP_SPORT && info->sport[0] > info->sport[1]) | ||
98 | return -EINVAL; | ||
99 | return 0; | ||
100 | } | ||
101 | |||
102 | static struct ebt_match filter_ip = | ||
103 | { | ||
104 | .name = EBT_IP_MATCH, | ||
105 | .match = ebt_filter_ip, | ||
106 | .check = ebt_ip_check, | ||
107 | .me = THIS_MODULE, | ||
108 | }; | ||
109 | |||
110 | static int __init init(void) | ||
111 | { | ||
112 | return ebt_register_match(&filter_ip); | ||
113 | } | ||
114 | |||
115 | static void __exit fini(void) | ||
116 | { | ||
117 | ebt_unregister_match(&filter_ip); | ||
118 | } | ||
119 | |||
120 | module_init(init); | ||
121 | module_exit(fini); | ||
122 | MODULE_LICENSE("GPL"); | ||
diff --git a/net/bridge/netfilter/ebt_limit.c b/net/bridge/netfilter/ebt_limit.c new file mode 100644 index 000000000000..637c8844cd5f --- /dev/null +++ b/net/bridge/netfilter/ebt_limit.c | |||
@@ -0,0 +1,113 @@ | |||
1 | /* | ||
2 | * ebt_limit | ||
3 | * | ||
4 | * Authors: | ||
5 | * Tom Marshall <tommy@home.tig-grr.com> | ||
6 | * | ||
7 | * Mostly copied from netfilter's ipt_limit.c, see that file for | ||
8 | * more explanation | ||
9 | * | ||
10 | * September, 2003 | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #include <linux/netfilter_bridge/ebtables.h> | ||
15 | #include <linux/netfilter_bridge/ebt_limit.h> | ||
16 | #include <linux/module.h> | ||
17 | |||
18 | #include <linux/netdevice.h> | ||
19 | #include <linux/spinlock.h> | ||
20 | |||
21 | static DEFINE_SPINLOCK(limit_lock); | ||
22 | |||
23 | #define MAX_CPJ (0xFFFFFFFF / (HZ*60*60*24)) | ||
24 | |||
25 | #define _POW2_BELOW2(x) ((x)|((x)>>1)) | ||
26 | #define _POW2_BELOW4(x) (_POW2_BELOW2(x)|_POW2_BELOW2((x)>>2)) | ||
27 | #define _POW2_BELOW8(x) (_POW2_BELOW4(x)|_POW2_BELOW4((x)>>4)) | ||
28 | #define _POW2_BELOW16(x) (_POW2_BELOW8(x)|_POW2_BELOW8((x)>>8)) | ||
29 | #define _POW2_BELOW32(x) (_POW2_BELOW16(x)|_POW2_BELOW16((x)>>16)) | ||
30 | #define POW2_BELOW32(x) ((_POW2_BELOW32(x)>>1) + 1) | ||
31 | |||
32 | #define CREDITS_PER_JIFFY POW2_BELOW32(MAX_CPJ) | ||
33 | |||
34 | static int ebt_limit_match(const struct sk_buff *skb, | ||
35 | const struct net_device *in, const struct net_device *out, | ||
36 | const void *data, unsigned int datalen) | ||
37 | { | ||
38 | struct ebt_limit_info *info = (struct ebt_limit_info *)data; | ||
39 | unsigned long now = jiffies; | ||
40 | |||
41 | spin_lock_bh(&limit_lock); | ||
42 | info->credit += (now - xchg(&info->prev, now)) * CREDITS_PER_JIFFY; | ||
43 | if (info->credit > info->credit_cap) | ||
44 | info->credit = info->credit_cap; | ||
45 | |||
46 | if (info->credit >= info->cost) { | ||
47 | /* We're not limited. */ | ||
48 | info->credit -= info->cost; | ||
49 | spin_unlock_bh(&limit_lock); | ||
50 | return EBT_MATCH; | ||
51 | } | ||
52 | |||
53 | spin_unlock_bh(&limit_lock); | ||
54 | return EBT_NOMATCH; | ||
55 | } | ||
56 | |||
57 | /* Precision saver. */ | ||
58 | static u_int32_t | ||
59 | user2credits(u_int32_t user) | ||
60 | { | ||
61 | /* If multiplying would overflow... */ | ||
62 | if (user > 0xFFFFFFFF / (HZ*CREDITS_PER_JIFFY)) | ||
63 | /* Divide first. */ | ||
64 | return (user / EBT_LIMIT_SCALE) * HZ * CREDITS_PER_JIFFY; | ||
65 | |||
66 | return (user * HZ * CREDITS_PER_JIFFY) / EBT_LIMIT_SCALE; | ||
67 | } | ||
68 | |||
69 | static int ebt_limit_check(const char *tablename, unsigned int hookmask, | ||
70 | const struct ebt_entry *e, void *data, unsigned int datalen) | ||
71 | { | ||
72 | struct ebt_limit_info *info = (struct ebt_limit_info *)data; | ||
73 | |||
74 | if (datalen != EBT_ALIGN(sizeof(struct ebt_limit_info))) | ||
75 | return -EINVAL; | ||
76 | |||
77 | /* Check for overflow. */ | ||
78 | if (info->burst == 0 || | ||
79 | user2credits(info->avg * info->burst) < user2credits(info->avg)) { | ||
80 | printk("Overflow in ebt_limit, try lower: %u/%u\n", | ||
81 | info->avg, info->burst); | ||
82 | return -EINVAL; | ||
83 | } | ||
84 | |||
85 | /* User avg in seconds * EBT_LIMIT_SCALE: convert to jiffies * 128. */ | ||
86 | info->prev = jiffies; | ||
87 | info->credit = user2credits(info->avg * info->burst); | ||
88 | info->credit_cap = user2credits(info->avg * info->burst); | ||
89 | info->cost = user2credits(info->avg); | ||
90 | return 0; | ||
91 | } | ||
92 | |||
93 | static struct ebt_match ebt_limit_reg = | ||
94 | { | ||
95 | .name = EBT_LIMIT_MATCH, | ||
96 | .match = ebt_limit_match, | ||
97 | .check = ebt_limit_check, | ||
98 | .me = THIS_MODULE, | ||
99 | }; | ||
100 | |||
101 | static int __init init(void) | ||
102 | { | ||
103 | return ebt_register_match(&ebt_limit_reg); | ||
104 | } | ||
105 | |||
106 | static void __exit fini(void) | ||
107 | { | ||
108 | ebt_unregister_match(&ebt_limit_reg); | ||
109 | } | ||
110 | |||
111 | module_init(init); | ||
112 | module_exit(fini); | ||
113 | MODULE_LICENSE("GPL"); | ||
diff --git a/net/bridge/netfilter/ebt_log.c b/net/bridge/netfilter/ebt_log.c new file mode 100644 index 000000000000..e4ae34b88925 --- /dev/null +++ b/net/bridge/netfilter/ebt_log.c | |||
@@ -0,0 +1,171 @@ | |||
1 | /* | ||
2 | * ebt_log | ||
3 | * | ||
4 | * Authors: | ||
5 | * Bart De Schuymer <bdschuym@pandora.be> | ||
6 | * | ||
7 | * April, 2002 | ||
8 | * | ||
9 | */ | ||
10 | |||
11 | #include <linux/netfilter_bridge/ebtables.h> | ||
12 | #include <linux/netfilter_bridge/ebt_log.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/ip.h> | ||
15 | #include <linux/if_arp.h> | ||
16 | #include <linux/spinlock.h> | ||
17 | |||
18 | static DEFINE_SPINLOCK(ebt_log_lock); | ||
19 | |||
20 | static int ebt_log_check(const char *tablename, unsigned int hookmask, | ||
21 | const struct ebt_entry *e, void *data, unsigned int datalen) | ||
22 | { | ||
23 | struct ebt_log_info *info = (struct ebt_log_info *)data; | ||
24 | |||
25 | if (datalen != EBT_ALIGN(sizeof(struct ebt_log_info))) | ||
26 | return -EINVAL; | ||
27 | if (info->bitmask & ~EBT_LOG_MASK) | ||
28 | return -EINVAL; | ||
29 | if (info->loglevel >= 8) | ||
30 | return -EINVAL; | ||
31 | info->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0'; | ||
32 | return 0; | ||
33 | } | ||
34 | |||
35 | struct tcpudphdr | ||
36 | { | ||
37 | uint16_t src; | ||
38 | uint16_t dst; | ||
39 | }; | ||
40 | |||
41 | struct arppayload | ||
42 | { | ||
43 | unsigned char mac_src[ETH_ALEN]; | ||
44 | unsigned char ip_src[4]; | ||
45 | unsigned char mac_dst[ETH_ALEN]; | ||
46 | unsigned char ip_dst[4]; | ||
47 | }; | ||
48 | |||
49 | static void print_MAC(unsigned char *p) | ||
50 | { | ||
51 | int i; | ||
52 | |||
53 | for (i = 0; i < ETH_ALEN; i++, p++) | ||
54 | printk("%02x%c", *p, i == ETH_ALEN - 1 ? ' ':':'); | ||
55 | } | ||
56 | |||
57 | #define myNIPQUAD(a) a[0], a[1], a[2], a[3] | ||
58 | static void ebt_log(const struct sk_buff *skb, unsigned int hooknr, | ||
59 | const struct net_device *in, const struct net_device *out, | ||
60 | const void *data, unsigned int datalen) | ||
61 | { | ||
62 | struct ebt_log_info *info = (struct ebt_log_info *)data; | ||
63 | char level_string[4] = "< >"; | ||
64 | union {struct iphdr iph; struct tcpudphdr ports; | ||
65 | struct arphdr arph; struct arppayload arpp;} u; | ||
66 | |||
67 | level_string[1] = '0' + info->loglevel; | ||
68 | spin_lock_bh(&ebt_log_lock); | ||
69 | printk(level_string); | ||
70 | printk("%s IN=%s OUT=%s ", info->prefix, in ? in->name : "", | ||
71 | out ? out->name : ""); | ||
72 | |||
73 | printk("MAC source = "); | ||
74 | print_MAC(eth_hdr(skb)->h_source); | ||
75 | printk("MAC dest = "); | ||
76 | print_MAC(eth_hdr(skb)->h_dest); | ||
77 | |||
78 | printk("proto = 0x%04x", ntohs(eth_hdr(skb)->h_proto)); | ||
79 | |||
80 | if ((info->bitmask & EBT_LOG_IP) && eth_hdr(skb)->h_proto == | ||
81 | htons(ETH_P_IP)){ | ||
82 | struct iphdr _iph, *ih; | ||
83 | |||
84 | ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph); | ||
85 | if (ih == NULL) { | ||
86 | printk(" INCOMPLETE IP header"); | ||
87 | goto out; | ||
88 | } | ||
89 | printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,", | ||
90 | NIPQUAD(ih->saddr), NIPQUAD(ih->daddr)); | ||
91 | printk(" IP tos=0x%02X, IP proto=%d", u.iph.tos, | ||
92 | ih->protocol); | ||
93 | if (ih->protocol == IPPROTO_TCP || | ||
94 | ih->protocol == IPPROTO_UDP) { | ||
95 | struct tcpudphdr _ports, *pptr; | ||
96 | |||
97 | pptr = skb_header_pointer(skb, ih->ihl*4, | ||
98 | sizeof(_ports), &_ports); | ||
99 | if (pptr == NULL) { | ||
100 | printk(" INCOMPLETE TCP/UDP header"); | ||
101 | goto out; | ||
102 | } | ||
103 | printk(" SPT=%u DPT=%u", ntohs(pptr->src), | ||
104 | ntohs(pptr->dst)); | ||
105 | } | ||
106 | goto out; | ||
107 | } | ||
108 | |||
109 | if ((info->bitmask & EBT_LOG_ARP) && | ||
110 | ((eth_hdr(skb)->h_proto == htons(ETH_P_ARP)) || | ||
111 | (eth_hdr(skb)->h_proto == htons(ETH_P_RARP)))) { | ||
112 | struct arphdr _arph, *ah; | ||
113 | |||
114 | ah = skb_header_pointer(skb, 0, sizeof(_arph), &_arph); | ||
115 | if (ah == NULL) { | ||
116 | printk(" INCOMPLETE ARP header"); | ||
117 | goto out; | ||
118 | } | ||
119 | printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d", | ||
120 | ntohs(ah->ar_hrd), ntohs(ah->ar_pro), | ||
121 | ntohs(ah->ar_op)); | ||
122 | |||
123 | /* If it's for Ethernet and the lengths are OK, | ||
124 | * then log the ARP payload */ | ||
125 | if (ah->ar_hrd == htons(1) && | ||
126 | ah->ar_hln == ETH_ALEN && | ||
127 | ah->ar_pln == sizeof(uint32_t)) { | ||
128 | struct arppayload _arpp, *ap; | ||
129 | |||
130 | ap = skb_header_pointer(skb, sizeof(u.arph), | ||
131 | sizeof(_arpp), &_arpp); | ||
132 | if (ap == NULL) { | ||
133 | printk(" INCOMPLETE ARP payload"); | ||
134 | goto out; | ||
135 | } | ||
136 | printk(" ARP MAC SRC="); | ||
137 | print_MAC(ap->mac_src); | ||
138 | printk(" ARP IP SRC=%u.%u.%u.%u", | ||
139 | myNIPQUAD(ap->ip_src)); | ||
140 | printk(" ARP MAC DST="); | ||
141 | print_MAC(ap->mac_dst); | ||
142 | printk(" ARP IP DST=%u.%u.%u.%u", | ||
143 | myNIPQUAD(ap->ip_dst)); | ||
144 | } | ||
145 | } | ||
146 | out: | ||
147 | printk("\n"); | ||
148 | spin_unlock_bh(&ebt_log_lock); | ||
149 | } | ||
150 | |||
151 | static struct ebt_watcher log = | ||
152 | { | ||
153 | .name = EBT_LOG_WATCHER, | ||
154 | .watcher = ebt_log, | ||
155 | .check = ebt_log_check, | ||
156 | .me = THIS_MODULE, | ||
157 | }; | ||
158 | |||
159 | static int __init init(void) | ||
160 | { | ||
161 | return ebt_register_watcher(&log); | ||
162 | } | ||
163 | |||
164 | static void __exit fini(void) | ||
165 | { | ||
166 | ebt_unregister_watcher(&log); | ||
167 | } | ||
168 | |||
169 | module_init(init); | ||
170 | module_exit(fini); | ||
171 | MODULE_LICENSE("GPL"); | ||
diff --git a/net/bridge/netfilter/ebt_mark.c b/net/bridge/netfilter/ebt_mark.c new file mode 100644 index 000000000000..02c632b4d325 --- /dev/null +++ b/net/bridge/netfilter/ebt_mark.c | |||
@@ -0,0 +1,68 @@ | |||
1 | /* | ||
2 | * ebt_mark | ||
3 | * | ||
4 | * Authors: | ||
5 | * Bart De Schuymer <bdschuym@pandora.be> | ||
6 | * | ||
7 | * July, 2002 | ||
8 | * | ||
9 | */ | ||
10 | |||
11 | /* The mark target can be used in any chain, | ||
12 | * I believe adding a mangle table just for marking is total overkill. | ||
13 | * Marking a frame doesn't really change anything in the frame anyway. | ||
14 | */ | ||
15 | |||
16 | #include <linux/netfilter_bridge/ebtables.h> | ||
17 | #include <linux/netfilter_bridge/ebt_mark_t.h> | ||
18 | #include <linux/module.h> | ||
19 | |||
20 | static int ebt_target_mark(struct sk_buff **pskb, unsigned int hooknr, | ||
21 | const struct net_device *in, const struct net_device *out, | ||
22 | const void *data, unsigned int datalen) | ||
23 | { | ||
24 | struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data; | ||
25 | |||
26 | if ((*pskb)->nfmark != info->mark) { | ||
27 | (*pskb)->nfmark = info->mark; | ||
28 | (*pskb)->nfcache |= NFC_ALTERED; | ||
29 | } | ||
30 | return info->target; | ||
31 | } | ||
32 | |||
33 | static int ebt_target_mark_check(const char *tablename, unsigned int hookmask, | ||
34 | const struct ebt_entry *e, void *data, unsigned int datalen) | ||
35 | { | ||
36 | struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data; | ||
37 | |||
38 | if (datalen != EBT_ALIGN(sizeof(struct ebt_mark_t_info))) | ||
39 | return -EINVAL; | ||
40 | if (BASE_CHAIN && info->target == EBT_RETURN) | ||
41 | return -EINVAL; | ||
42 | CLEAR_BASE_CHAIN_BIT; | ||
43 | if (INVALID_TARGET) | ||
44 | return -EINVAL; | ||
45 | return 0; | ||
46 | } | ||
47 | |||
48 | static struct ebt_target mark_target = | ||
49 | { | ||
50 | .name = EBT_MARK_TARGET, | ||
51 | .target = ebt_target_mark, | ||
52 | .check = ebt_target_mark_check, | ||
53 | .me = THIS_MODULE, | ||
54 | }; | ||
55 | |||
56 | static int __init init(void) | ||
57 | { | ||
58 | return ebt_register_target(&mark_target); | ||
59 | } | ||
60 | |||
61 | static void __exit fini(void) | ||
62 | { | ||
63 | ebt_unregister_target(&mark_target); | ||
64 | } | ||
65 | |||
66 | module_init(init); | ||
67 | module_exit(fini); | ||
68 | MODULE_LICENSE("GPL"); | ||
diff --git a/net/bridge/netfilter/ebt_mark_m.c b/net/bridge/netfilter/ebt_mark_m.c new file mode 100644 index 000000000000..625102de1495 --- /dev/null +++ b/net/bridge/netfilter/ebt_mark_m.c | |||
@@ -0,0 +1,62 @@ | |||
1 | /* | ||
2 | * ebt_mark_m | ||
3 | * | ||
4 | * Authors: | ||
5 | * Bart De Schuymer <bdschuym@pandora.be> | ||
6 | * | ||
7 | * July, 2002 | ||
8 | * | ||
9 | */ | ||
10 | |||
11 | #include <linux/netfilter_bridge/ebtables.h> | ||
12 | #include <linux/netfilter_bridge/ebt_mark_m.h> | ||
13 | #include <linux/module.h> | ||
14 | |||
15 | static int ebt_filter_mark(const struct sk_buff *skb, | ||
16 | const struct net_device *in, const struct net_device *out, const void *data, | ||
17 | unsigned int datalen) | ||
18 | { | ||
19 | struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data; | ||
20 | |||
21 | if (info->bitmask & EBT_MARK_OR) | ||
22 | return !(!!(skb->nfmark & info->mask) ^ info->invert); | ||
23 | return !(((skb->nfmark & info->mask) == info->mark) ^ info->invert); | ||
24 | } | ||
25 | |||
26 | static int ebt_mark_check(const char *tablename, unsigned int hookmask, | ||
27 | const struct ebt_entry *e, void *data, unsigned int datalen) | ||
28 | { | ||
29 | struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data; | ||
30 | |||
31 | if (datalen != EBT_ALIGN(sizeof(struct ebt_mark_m_info))) | ||
32 | return -EINVAL; | ||
33 | if (info->bitmask & ~EBT_MARK_MASK) | ||
34 | return -EINVAL; | ||
35 | if ((info->bitmask & EBT_MARK_OR) && (info->bitmask & EBT_MARK_AND)) | ||
36 | return -EINVAL; | ||
37 | if (!info->bitmask) | ||
38 | return -EINVAL; | ||
39 | return 0; | ||
40 | } | ||
41 | |||
42 | static struct ebt_match filter_mark = | ||
43 | { | ||
44 | .name = EBT_MARK_MATCH, | ||
45 | .match = ebt_filter_mark, | ||
46 | .check = ebt_mark_check, | ||
47 | .me = THIS_MODULE, | ||
48 | }; | ||
49 | |||
50 | static int __init init(void) | ||
51 | { | ||
52 | return ebt_register_match(&filter_mark); | ||
53 | } | ||
54 | |||
55 | static void __exit fini(void) | ||
56 | { | ||
57 | ebt_unregister_match(&filter_mark); | ||
58 | } | ||
59 | |||
60 | module_init(init); | ||
61 | module_exit(fini); | ||
62 | MODULE_LICENSE("GPL"); | ||
diff --git a/net/bridge/netfilter/ebt_pkttype.c b/net/bridge/netfilter/ebt_pkttype.c new file mode 100644 index 000000000000..ecd3b42b19b0 --- /dev/null +++ b/net/bridge/netfilter/ebt_pkttype.c | |||
@@ -0,0 +1,59 @@ | |||
1 | /* | ||
2 | * ebt_pkttype | ||
3 | * | ||
4 | * Authors: | ||
5 | * Bart De Schuymer <bdschuym@pandora.be> | ||
6 | * | ||
7 | * April, 2003 | ||
8 | * | ||
9 | */ | ||
10 | |||
11 | #include <linux/netfilter_bridge/ebtables.h> | ||
12 | #include <linux/netfilter_bridge/ebt_pkttype.h> | ||
13 | #include <linux/module.h> | ||
14 | |||
15 | static int ebt_filter_pkttype(const struct sk_buff *skb, | ||
16 | const struct net_device *in, | ||
17 | const struct net_device *out, | ||
18 | const void *data, | ||
19 | unsigned int datalen) | ||
20 | { | ||
21 | struct ebt_pkttype_info *info = (struct ebt_pkttype_info *)data; | ||
22 | |||
23 | return (skb->pkt_type != info->pkt_type) ^ info->invert; | ||
24 | } | ||
25 | |||
26 | static int ebt_pkttype_check(const char *tablename, unsigned int hookmask, | ||
27 | const struct ebt_entry *e, void *data, unsigned int datalen) | ||
28 | { | ||
29 | struct ebt_pkttype_info *info = (struct ebt_pkttype_info *)data; | ||
30 | |||
31 | if (datalen != EBT_ALIGN(sizeof(struct ebt_pkttype_info))) | ||
32 | return -EINVAL; | ||
33 | if (info->invert != 0 && info->invert != 1) | ||
34 | return -EINVAL; | ||
35 | /* Allow any pkt_type value */ | ||
36 | return 0; | ||
37 | } | ||
38 | |||
39 | static struct ebt_match filter_pkttype = | ||
40 | { | ||
41 | .name = EBT_PKTTYPE_MATCH, | ||
42 | .match = ebt_filter_pkttype, | ||
43 | .check = ebt_pkttype_check, | ||
44 | .me = THIS_MODULE, | ||
45 | }; | ||
46 | |||
47 | static int __init init(void) | ||
48 | { | ||
49 | return ebt_register_match(&filter_pkttype); | ||
50 | } | ||
51 | |||
52 | static void __exit fini(void) | ||
53 | { | ||
54 | ebt_unregister_match(&filter_pkttype); | ||
55 | } | ||
56 | |||
57 | module_init(init); | ||
58 | module_exit(fini); | ||
59 | MODULE_LICENSE("GPL"); | ||
diff --git a/net/bridge/netfilter/ebt_redirect.c b/net/bridge/netfilter/ebt_redirect.c new file mode 100644 index 000000000000..1538b4386662 --- /dev/null +++ b/net/bridge/netfilter/ebt_redirect.c | |||
@@ -0,0 +1,81 @@ | |||
1 | /* | ||
2 | * ebt_redirect | ||
3 | * | ||
4 | * Authors: | ||
5 | * Bart De Schuymer <bdschuym@pandora.be> | ||
6 | * | ||
7 | * April, 2002 | ||
8 | * | ||
9 | */ | ||
10 | |||
11 | #include <linux/netfilter_bridge/ebtables.h> | ||
12 | #include <linux/netfilter_bridge/ebt_redirect.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <net/sock.h> | ||
15 | #include "../br_private.h" | ||
16 | |||
17 | static int ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr, | ||
18 | const struct net_device *in, const struct net_device *out, | ||
19 | const void *data, unsigned int datalen) | ||
20 | { | ||
21 | struct ebt_redirect_info *info = (struct ebt_redirect_info *)data; | ||
22 | |||
23 | if (skb_shared(*pskb) || skb_cloned(*pskb)) { | ||
24 | struct sk_buff *nskb; | ||
25 | |||
26 | nskb = skb_copy(*pskb, GFP_ATOMIC); | ||
27 | if (!nskb) | ||
28 | return NF_DROP; | ||
29 | if ((*pskb)->sk) | ||
30 | skb_set_owner_w(nskb, (*pskb)->sk); | ||
31 | kfree_skb(*pskb); | ||
32 | *pskb = nskb; | ||
33 | } | ||
34 | if (hooknr != NF_BR_BROUTING) | ||
35 | memcpy(eth_hdr(*pskb)->h_dest, | ||
36 | in->br_port->br->dev->dev_addr, ETH_ALEN); | ||
37 | else | ||
38 | memcpy(eth_hdr(*pskb)->h_dest, in->dev_addr, ETH_ALEN); | ||
39 | (*pskb)->pkt_type = PACKET_HOST; | ||
40 | return info->target; | ||
41 | } | ||
42 | |||
43 | static int ebt_target_redirect_check(const char *tablename, unsigned int hookmask, | ||
44 | const struct ebt_entry *e, void *data, unsigned int datalen) | ||
45 | { | ||
46 | struct ebt_redirect_info *info = (struct ebt_redirect_info *)data; | ||
47 | |||
48 | if (datalen != EBT_ALIGN(sizeof(struct ebt_redirect_info))) | ||
49 | return -EINVAL; | ||
50 | if (BASE_CHAIN && info->target == EBT_RETURN) | ||
51 | return -EINVAL; | ||
52 | CLEAR_BASE_CHAIN_BIT; | ||
53 | if ( (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING)) && | ||
54 | (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) ) | ||
55 | return -EINVAL; | ||
56 | if (INVALID_TARGET) | ||
57 | return -EINVAL; | ||
58 | return 0; | ||
59 | } | ||
60 | |||
61 | static struct ebt_target redirect_target = | ||
62 | { | ||
63 | .name = EBT_REDIRECT_TARGET, | ||
64 | .target = ebt_target_redirect, | ||
65 | .check = ebt_target_redirect_check, | ||
66 | .me = THIS_MODULE, | ||
67 | }; | ||
68 | |||
69 | static int __init init(void) | ||
70 | { | ||
71 | return ebt_register_target(&redirect_target); | ||
72 | } | ||
73 | |||
74 | static void __exit fini(void) | ||
75 | { | ||
76 | ebt_unregister_target(&redirect_target); | ||
77 | } | ||
78 | |||
79 | module_init(init); | ||
80 | module_exit(fini); | ||
81 | MODULE_LICENSE("GPL"); | ||
diff --git a/net/bridge/netfilter/ebt_snat.c b/net/bridge/netfilter/ebt_snat.c new file mode 100644 index 000000000000..1529bdcb9a48 --- /dev/null +++ b/net/bridge/netfilter/ebt_snat.c | |||
@@ -0,0 +1,76 @@ | |||
1 | /* | ||
2 | * ebt_snat | ||
3 | * | ||
4 | * Authors: | ||
5 | * Bart De Schuymer <bdschuym@pandora.be> | ||
6 | * | ||
7 | * June, 2002 | ||
8 | * | ||
9 | */ | ||
10 | |||
11 | #include <linux/netfilter_bridge/ebtables.h> | ||
12 | #include <linux/netfilter_bridge/ebt_nat.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <net/sock.h> | ||
15 | |||
16 | static int ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr, | ||
17 | const struct net_device *in, const struct net_device *out, | ||
18 | const void *data, unsigned int datalen) | ||
19 | { | ||
20 | struct ebt_nat_info *info = (struct ebt_nat_info *) data; | ||
21 | |||
22 | if (skb_shared(*pskb) || skb_cloned(*pskb)) { | ||
23 | struct sk_buff *nskb; | ||
24 | |||
25 | nskb = skb_copy(*pskb, GFP_ATOMIC); | ||
26 | if (!nskb) | ||
27 | return NF_DROP; | ||
28 | if ((*pskb)->sk) | ||
29 | skb_set_owner_w(nskb, (*pskb)->sk); | ||
30 | kfree_skb(*pskb); | ||
31 | *pskb = nskb; | ||
32 | } | ||
33 | memcpy(eth_hdr(*pskb)->h_source, info->mac, ETH_ALEN); | ||
34 | return info->target; | ||
35 | } | ||
36 | |||
37 | static int ebt_target_snat_check(const char *tablename, unsigned int hookmask, | ||
38 | const struct ebt_entry *e, void *data, unsigned int datalen) | ||
39 | { | ||
40 | struct ebt_nat_info *info = (struct ebt_nat_info *) data; | ||
41 | |||
42 | if (datalen != EBT_ALIGN(sizeof(struct ebt_nat_info))) | ||
43 | return -EINVAL; | ||
44 | if (BASE_CHAIN && info->target == EBT_RETURN) | ||
45 | return -EINVAL; | ||
46 | CLEAR_BASE_CHAIN_BIT; | ||
47 | if (strcmp(tablename, "nat")) | ||
48 | return -EINVAL; | ||
49 | if (hookmask & ~(1 << NF_BR_POST_ROUTING)) | ||
50 | return -EINVAL; | ||
51 | if (INVALID_TARGET) | ||
52 | return -EINVAL; | ||
53 | return 0; | ||
54 | } | ||
55 | |||
56 | static struct ebt_target snat = | ||
57 | { | ||
58 | .name = EBT_SNAT_TARGET, | ||
59 | .target = ebt_target_snat, | ||
60 | .check = ebt_target_snat_check, | ||
61 | .me = THIS_MODULE, | ||
62 | }; | ||
63 | |||
64 | static int __init init(void) | ||
65 | { | ||
66 | return ebt_register_target(&snat); | ||
67 | } | ||
68 | |||
69 | static void __exit fini(void) | ||
70 | { | ||
71 | ebt_unregister_target(&snat); | ||
72 | } | ||
73 | |||
74 | module_init(init); | ||
75 | module_exit(fini); | ||
76 | MODULE_LICENSE("GPL"); | ||
diff --git a/net/bridge/netfilter/ebt_stp.c b/net/bridge/netfilter/ebt_stp.c new file mode 100644 index 000000000000..f8a8cdec16ee --- /dev/null +++ b/net/bridge/netfilter/ebt_stp.c | |||
@@ -0,0 +1,194 @@ | |||
1 | /* | ||
2 | * ebt_stp | ||
3 | * | ||
4 | * Authors: | ||
5 | * Bart De Schuymer <bdschuym@pandora.be> | ||
6 | * Stephen Hemminger <shemminger@osdl.org> | ||
7 | * | ||
8 | * July, 2003 | ||
9 | */ | ||
10 | |||
11 | #include <linux/netfilter_bridge/ebtables.h> | ||
12 | #include <linux/netfilter_bridge/ebt_stp.h> | ||
13 | #include <linux/module.h> | ||
14 | |||
15 | #define BPDU_TYPE_CONFIG 0 | ||
16 | #define BPDU_TYPE_TCN 0x80 | ||
17 | |||
18 | struct stp_header { | ||
19 | uint8_t dsap; | ||
20 | uint8_t ssap; | ||
21 | uint8_t ctrl; | ||
22 | uint8_t pid; | ||
23 | uint8_t vers; | ||
24 | uint8_t type; | ||
25 | }; | ||
26 | |||
27 | struct stp_config_pdu { | ||
28 | uint8_t flags; | ||
29 | uint8_t root[8]; | ||
30 | uint8_t root_cost[4]; | ||
31 | uint8_t sender[8]; | ||
32 | uint8_t port[2]; | ||
33 | uint8_t msg_age[2]; | ||
34 | uint8_t max_age[2]; | ||
35 | uint8_t hello_time[2]; | ||
36 | uint8_t forward_delay[2]; | ||
37 | }; | ||
38 | |||
39 | #define NR16(p) (p[0] << 8 | p[1]) | ||
40 | #define NR32(p) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]) | ||
41 | |||
42 | static int ebt_filter_config(struct ebt_stp_info *info, | ||
43 | struct stp_config_pdu *stpc) | ||
44 | { | ||
45 | struct ebt_stp_config_info *c; | ||
46 | uint16_t v16; | ||
47 | uint32_t v32; | ||
48 | int verdict, i; | ||
49 | |||
50 | c = &info->config; | ||
51 | if ((info->bitmask & EBT_STP_FLAGS) && | ||
52 | FWINV(c->flags != stpc->flags, EBT_STP_FLAGS)) | ||
53 | return EBT_NOMATCH; | ||
54 | if (info->bitmask & EBT_STP_ROOTPRIO) { | ||
55 | v16 = NR16(stpc->root); | ||
56 | if (FWINV(v16 < c->root_priol || | ||
57 | v16 > c->root_priou, EBT_STP_ROOTPRIO)) | ||
58 | return EBT_NOMATCH; | ||
59 | } | ||
60 | if (info->bitmask & EBT_STP_ROOTADDR) { | ||
61 | verdict = 0; | ||
62 | for (i = 0; i < 6; i++) | ||
63 | verdict |= (stpc->root[2+i] ^ c->root_addr[i]) & | ||
64 | c->root_addrmsk[i]; | ||
65 | if (FWINV(verdict != 0, EBT_STP_ROOTADDR)) | ||
66 | return EBT_NOMATCH; | ||
67 | } | ||
68 | if (info->bitmask & EBT_STP_ROOTCOST) { | ||
69 | v32 = NR32(stpc->root_cost); | ||
70 | if (FWINV(v32 < c->root_costl || | ||
71 | v32 > c->root_costu, EBT_STP_ROOTCOST)) | ||
72 | return EBT_NOMATCH; | ||
73 | } | ||
74 | if (info->bitmask & EBT_STP_SENDERPRIO) { | ||
75 | v16 = NR16(stpc->sender); | ||
76 | if (FWINV(v16 < c->sender_priol || | ||
77 | v16 > c->sender_priou, EBT_STP_SENDERPRIO)) | ||
78 | return EBT_NOMATCH; | ||
79 | } | ||
80 | if (info->bitmask & EBT_STP_SENDERADDR) { | ||
81 | verdict = 0; | ||
82 | for (i = 0; i < 6; i++) | ||
83 | verdict |= (stpc->sender[2+i] ^ c->sender_addr[i]) & | ||
84 | c->sender_addrmsk[i]; | ||
85 | if (FWINV(verdict != 0, EBT_STP_SENDERADDR)) | ||
86 | return EBT_NOMATCH; | ||
87 | } | ||
88 | if (info->bitmask & EBT_STP_PORT) { | ||
89 | v16 = NR16(stpc->port); | ||
90 | if (FWINV(v16 < c->portl || | ||
91 | v16 > c->portu, EBT_STP_PORT)) | ||
92 | return EBT_NOMATCH; | ||
93 | } | ||
94 | if (info->bitmask & EBT_STP_MSGAGE) { | ||
95 | v16 = NR16(stpc->msg_age); | ||
96 | if (FWINV(v16 < c->msg_agel || | ||
97 | v16 > c->msg_ageu, EBT_STP_MSGAGE)) | ||
98 | return EBT_NOMATCH; | ||
99 | } | ||
100 | if (info->bitmask & EBT_STP_MAXAGE) { | ||
101 | v16 = NR16(stpc->max_age); | ||
102 | if (FWINV(v16 < c->max_agel || | ||
103 | v16 > c->max_ageu, EBT_STP_MAXAGE)) | ||
104 | return EBT_NOMATCH; | ||
105 | } | ||
106 | if (info->bitmask & EBT_STP_HELLOTIME) { | ||
107 | v16 = NR16(stpc->hello_time); | ||
108 | if (FWINV(v16 < c->hello_timel || | ||
109 | v16 > c->hello_timeu, EBT_STP_HELLOTIME)) | ||
110 | return EBT_NOMATCH; | ||
111 | } | ||
112 | if (info->bitmask & EBT_STP_FWDD) { | ||
113 | v16 = NR16(stpc->forward_delay); | ||
114 | if (FWINV(v16 < c->forward_delayl || | ||
115 | v16 > c->forward_delayu, EBT_STP_FWDD)) | ||
116 | return EBT_NOMATCH; | ||
117 | } | ||
118 | return EBT_MATCH; | ||
119 | } | ||
120 | |||
121 | static int ebt_filter_stp(const struct sk_buff *skb, const struct net_device *in, | ||
122 | const struct net_device *out, const void *data, unsigned int datalen) | ||
123 | { | ||
124 | struct ebt_stp_info *info = (struct ebt_stp_info *)data; | ||
125 | struct stp_header _stph, *sp; | ||
126 | uint8_t header[6] = {0x42, 0x42, 0x03, 0x00, 0x00, 0x00}; | ||
127 | |||
128 | sp = skb_header_pointer(skb, 0, sizeof(_stph), &_stph); | ||
129 | if (sp == NULL) | ||
130 | return EBT_NOMATCH; | ||
131 | |||
132 | /* The stp code only considers these */ | ||
133 | if (memcmp(sp, header, sizeof(header))) | ||
134 | return EBT_NOMATCH; | ||
135 | |||
136 | if (info->bitmask & EBT_STP_TYPE | ||
137 | && FWINV(info->type != sp->type, EBT_STP_TYPE)) | ||
138 | return EBT_NOMATCH; | ||
139 | |||
140 | if (sp->type == BPDU_TYPE_CONFIG && | ||
141 | info->bitmask & EBT_STP_CONFIG_MASK) { | ||
142 | struct stp_config_pdu _stpc, *st; | ||
143 | |||
144 | st = skb_header_pointer(skb, sizeof(_stph), | ||
145 | sizeof(_stpc), &_stpc); | ||
146 | if (st == NULL) | ||
147 | return EBT_NOMATCH; | ||
148 | return ebt_filter_config(info, st); | ||
149 | } | ||
150 | return EBT_MATCH; | ||
151 | } | ||
152 | |||
153 | static int ebt_stp_check(const char *tablename, unsigned int hookmask, | ||
154 | const struct ebt_entry *e, void *data, unsigned int datalen) | ||
155 | { | ||
156 | struct ebt_stp_info *info = (struct ebt_stp_info *)data; | ||
157 | int len = EBT_ALIGN(sizeof(struct ebt_stp_info)); | ||
158 | uint8_t bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }; | ||
159 | uint8_t msk[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; | ||
160 | |||
161 | if (info->bitmask & ~EBT_STP_MASK || info->invflags & ~EBT_STP_MASK || | ||
162 | !(info->bitmask & EBT_STP_MASK)) | ||
163 | return -EINVAL; | ||
164 | if (datalen != len) | ||
165 | return -EINVAL; | ||
166 | /* Make sure the match only receives stp frames */ | ||
167 | if (memcmp(e->destmac, bridge_ula, ETH_ALEN) || | ||
168 | memcmp(e->destmsk, msk, ETH_ALEN) || !(e->bitmask & EBT_DESTMAC)) | ||
169 | return -EINVAL; | ||
170 | |||
171 | return 0; | ||
172 | } | ||
173 | |||
174 | static struct ebt_match filter_stp = | ||
175 | { | ||
176 | .name = EBT_STP_MATCH, | ||
177 | .match = ebt_filter_stp, | ||
178 | .check = ebt_stp_check, | ||
179 | .me = THIS_MODULE, | ||
180 | }; | ||
181 | |||
182 | static int __init init(void) | ||
183 | { | ||
184 | return ebt_register_match(&filter_stp); | ||
185 | } | ||
186 | |||
187 | static void __exit fini(void) | ||
188 | { | ||
189 | ebt_unregister_match(&filter_stp); | ||
190 | } | ||
191 | |||
192 | module_init(init); | ||
193 | module_exit(fini); | ||
194 | MODULE_LICENSE("GPL"); | ||
diff --git a/net/bridge/netfilter/ebt_ulog.c b/net/bridge/netfilter/ebt_ulog.c new file mode 100644 index 000000000000..01af4fcef26d --- /dev/null +++ b/net/bridge/netfilter/ebt_ulog.c | |||
@@ -0,0 +1,295 @@ | |||
1 | /* | ||
2 | * netfilter module for userspace bridged Ethernet frames logging daemons | ||
3 | * | ||
4 | * Authors: | ||
5 | * Bart De Schuymer <bdschuym@pandora.be> | ||
6 | * | ||
7 | * November, 2004 | ||
8 | * | ||
9 | * Based on ipt_ULOG.c, which is | ||
10 | * (C) 2000-2002 by Harald Welte <laforge@netfilter.org> | ||
11 | * | ||
12 | * This module accepts two parameters: | ||
13 | * | ||
14 | * nlbufsiz: | ||
15 | * The parameter specifies how big the buffer for each netlink multicast | ||
16 | * group is. e.g. If you say nlbufsiz=8192, up to eight kb of packets will | ||
17 | * get accumulated in the kernel until they are sent to userspace. It is | ||
18 | * NOT possible to allocate more than 128kB, and it is strongly discouraged, | ||
19 | * because atomically allocating 128kB inside the network rx softirq is not | ||
20 | * reliable. Please also keep in mind that this buffer size is allocated for | ||
21 | * each nlgroup you are using, so the total kernel memory usage increases | ||
22 | * by that factor. | ||
23 | * | ||
24 | * flushtimeout: | ||
25 | * Specify, after how many hundredths of a second the queue should be | ||
26 | * flushed even if it is not full yet. | ||
27 | * | ||
28 | */ | ||
29 | |||
30 | #include <linux/module.h> | ||
31 | #include <linux/config.h> | ||
32 | #include <linux/spinlock.h> | ||
33 | #include <linux/socket.h> | ||
34 | #include <linux/skbuff.h> | ||
35 | #include <linux/kernel.h> | ||
36 | #include <linux/timer.h> | ||
37 | #include <linux/netlink.h> | ||
38 | #include <linux/netdevice.h> | ||
39 | #include <linux/module.h> | ||
40 | #include <linux/netfilter_bridge/ebtables.h> | ||
41 | #include <linux/netfilter_bridge/ebt_ulog.h> | ||
42 | #include <net/sock.h> | ||
43 | #include "../br_private.h" | ||
44 | |||
45 | #define PRINTR(format, args...) do { if (net_ratelimit()) \ | ||
46 | printk(format , ## args); } while (0) | ||
47 | |||
48 | static unsigned int nlbufsiz = 4096; | ||
49 | module_param(nlbufsiz, uint, 0600); | ||
50 | MODULE_PARM_DESC(nlbufsiz, "netlink buffer size (number of bytes) " | ||
51 | "(defaults to 4096)"); | ||
52 | |||
53 | static unsigned int flushtimeout = 10; | ||
54 | module_param(flushtimeout, uint, 0600); | ||
55 | MODULE_PARM_DESC(flushtimeout, "buffer flush timeout (hundredths ofa second) " | ||
56 | "(defaults to 10)"); | ||
57 | |||
58 | typedef struct { | ||
59 | unsigned int qlen; /* number of nlmsgs' in the skb */ | ||
60 | struct nlmsghdr *lastnlh; /* netlink header of last msg in skb */ | ||
61 | struct sk_buff *skb; /* the pre-allocated skb */ | ||
62 | struct timer_list timer; /* the timer function */ | ||
63 | spinlock_t lock; /* the per-queue lock */ | ||
64 | } ebt_ulog_buff_t; | ||
65 | |||
66 | static ebt_ulog_buff_t ulog_buffers[EBT_ULOG_MAXNLGROUPS]; | ||
67 | static struct sock *ebtulognl; | ||
68 | |||
69 | /* send one ulog_buff_t to userspace */ | ||
70 | static void ulog_send(unsigned int nlgroup) | ||
71 | { | ||
72 | ebt_ulog_buff_t *ub = &ulog_buffers[nlgroup]; | ||
73 | |||
74 | if (timer_pending(&ub->timer)) | ||
75 | del_timer(&ub->timer); | ||
76 | |||
77 | /* last nlmsg needs NLMSG_DONE */ | ||
78 | if (ub->qlen > 1) | ||
79 | ub->lastnlh->nlmsg_type = NLMSG_DONE; | ||
80 | |||
81 | NETLINK_CB(ub->skb).dst_groups = 1 << nlgroup; | ||
82 | netlink_broadcast(ebtulognl, ub->skb, 0, 1 << nlgroup, GFP_ATOMIC); | ||
83 | |||
84 | ub->qlen = 0; | ||
85 | ub->skb = NULL; | ||
86 | } | ||
87 | |||
88 | /* timer function to flush queue in flushtimeout time */ | ||
89 | static void ulog_timer(unsigned long data) | ||
90 | { | ||
91 | spin_lock_bh(&ulog_buffers[data].lock); | ||
92 | if (ulog_buffers[data].skb) | ||
93 | ulog_send(data); | ||
94 | spin_unlock_bh(&ulog_buffers[data].lock); | ||
95 | } | ||
96 | |||
97 | static struct sk_buff *ulog_alloc_skb(unsigned int size) | ||
98 | { | ||
99 | struct sk_buff *skb; | ||
100 | |||
101 | skb = alloc_skb(nlbufsiz, GFP_ATOMIC); | ||
102 | if (!skb) { | ||
103 | PRINTR(KERN_ERR "ebt_ulog: can't alloc whole buffer " | ||
104 | "of size %ub!\n", nlbufsiz); | ||
105 | if (size < nlbufsiz) { | ||
106 | /* try to allocate only as much as we need for | ||
107 | * current packet */ | ||
108 | skb = alloc_skb(size, GFP_ATOMIC); | ||
109 | if (!skb) | ||
110 | PRINTR(KERN_ERR "ebt_ulog: can't even allocate " | ||
111 | "buffer of size %ub\n", size); | ||
112 | } | ||
113 | } | ||
114 | |||
115 | return skb; | ||
116 | } | ||
117 | |||
118 | static void ebt_ulog(const struct sk_buff *skb, unsigned int hooknr, | ||
119 | const struct net_device *in, const struct net_device *out, | ||
120 | const void *data, unsigned int datalen) | ||
121 | { | ||
122 | ebt_ulog_packet_msg_t *pm; | ||
123 | size_t size, copy_len; | ||
124 | struct nlmsghdr *nlh; | ||
125 | struct ebt_ulog_info *uloginfo = (struct ebt_ulog_info *)data; | ||
126 | unsigned int group = uloginfo->nlgroup; | ||
127 | ebt_ulog_buff_t *ub = &ulog_buffers[group]; | ||
128 | spinlock_t *lock = &ub->lock; | ||
129 | |||
130 | if ((uloginfo->cprange == 0) || | ||
131 | (uloginfo->cprange > skb->len + ETH_HLEN)) | ||
132 | copy_len = skb->len + ETH_HLEN; | ||
133 | else | ||
134 | copy_len = uloginfo->cprange; | ||
135 | |||
136 | size = NLMSG_SPACE(sizeof(*pm) + copy_len); | ||
137 | if (size > nlbufsiz) { | ||
138 | PRINTR("ebt_ulog: Size %Zd needed, but nlbufsiz=%d\n", | ||
139 | size, nlbufsiz); | ||
140 | return; | ||
141 | } | ||
142 | |||
143 | spin_lock_bh(lock); | ||
144 | |||
145 | if (!ub->skb) { | ||
146 | if (!(ub->skb = ulog_alloc_skb(size))) | ||
147 | goto alloc_failure; | ||
148 | } else if (size > skb_tailroom(ub->skb)) { | ||
149 | ulog_send(group); | ||
150 | |||
151 | if (!(ub->skb = ulog_alloc_skb(size))) | ||
152 | goto alloc_failure; | ||
153 | } | ||
154 | |||
155 | nlh = NLMSG_PUT(ub->skb, 0, ub->qlen, 0, | ||
156 | size - NLMSG_ALIGN(sizeof(*nlh))); | ||
157 | ub->qlen++; | ||
158 | |||
159 | pm = NLMSG_DATA(nlh); | ||
160 | |||
161 | /* Fill in the ulog data */ | ||
162 | pm->version = EBT_ULOG_VERSION; | ||
163 | do_gettimeofday(&pm->stamp); | ||
164 | if (ub->qlen == 1) | ||
165 | ub->skb->stamp = pm->stamp; | ||
166 | pm->data_len = copy_len; | ||
167 | pm->mark = skb->nfmark; | ||
168 | pm->hook = hooknr; | ||
169 | if (uloginfo->prefix != NULL) | ||
170 | strcpy(pm->prefix, uloginfo->prefix); | ||
171 | else | ||
172 | *(pm->prefix) = '\0'; | ||
173 | |||
174 | if (in) { | ||
175 | strcpy(pm->physindev, in->name); | ||
176 | /* If in isn't a bridge, then physindev==indev */ | ||
177 | if (in->br_port) | ||
178 | strcpy(pm->indev, in->br_port->br->dev->name); | ||
179 | else | ||
180 | strcpy(pm->indev, in->name); | ||
181 | } else | ||
182 | pm->indev[0] = pm->physindev[0] = '\0'; | ||
183 | |||
184 | if (out) { | ||
185 | /* If out exists, then out is a bridge port */ | ||
186 | strcpy(pm->physoutdev, out->name); | ||
187 | strcpy(pm->outdev, out->br_port->br->dev->name); | ||
188 | } else | ||
189 | pm->outdev[0] = pm->physoutdev[0] = '\0'; | ||
190 | |||
191 | if (skb_copy_bits(skb, -ETH_HLEN, pm->data, copy_len) < 0) | ||
192 | BUG(); | ||
193 | |||
194 | if (ub->qlen > 1) | ||
195 | ub->lastnlh->nlmsg_flags |= NLM_F_MULTI; | ||
196 | |||
197 | ub->lastnlh = nlh; | ||
198 | |||
199 | if (ub->qlen >= uloginfo->qthreshold) | ||
200 | ulog_send(group); | ||
201 | else if (!timer_pending(&ub->timer)) { | ||
202 | ub->timer.expires = jiffies + flushtimeout * HZ / 100; | ||
203 | add_timer(&ub->timer); | ||
204 | } | ||
205 | |||
206 | unlock: | ||
207 | spin_unlock_bh(lock); | ||
208 | |||
209 | return; | ||
210 | |||
211 | nlmsg_failure: | ||
212 | printk(KERN_CRIT "ebt_ulog: error during NLMSG_PUT. This should " | ||
213 | "not happen, please report to author.\n"); | ||
214 | goto unlock; | ||
215 | alloc_failure: | ||
216 | goto unlock; | ||
217 | } | ||
218 | |||
219 | static int ebt_ulog_check(const char *tablename, unsigned int hookmask, | ||
220 | const struct ebt_entry *e, void *data, unsigned int datalen) | ||
221 | { | ||
222 | struct ebt_ulog_info *uloginfo = (struct ebt_ulog_info *)data; | ||
223 | |||
224 | if (datalen != EBT_ALIGN(sizeof(struct ebt_ulog_info)) || | ||
225 | uloginfo->nlgroup > 31) | ||
226 | return -EINVAL; | ||
227 | |||
228 | uloginfo->prefix[EBT_ULOG_PREFIX_LEN - 1] = '\0'; | ||
229 | |||
230 | if (uloginfo->qthreshold > EBT_ULOG_MAX_QLEN) | ||
231 | uloginfo->qthreshold = EBT_ULOG_MAX_QLEN; | ||
232 | |||
233 | return 0; | ||
234 | } | ||
235 | |||
236 | static struct ebt_watcher ulog = { | ||
237 | .name = EBT_ULOG_WATCHER, | ||
238 | .watcher = ebt_ulog, | ||
239 | .check = ebt_ulog_check, | ||
240 | .me = THIS_MODULE, | ||
241 | }; | ||
242 | |||
243 | static int __init init(void) | ||
244 | { | ||
245 | int i, ret = 0; | ||
246 | |||
247 | if (nlbufsiz >= 128*1024) { | ||
248 | printk(KERN_NOTICE "ebt_ulog: Netlink buffer has to be <= 128kB," | ||
249 | " please try a smaller nlbufsiz parameter.\n"); | ||
250 | return -EINVAL; | ||
251 | } | ||
252 | |||
253 | /* initialize ulog_buffers */ | ||
254 | for (i = 0; i < EBT_ULOG_MAXNLGROUPS; i++) { | ||
255 | init_timer(&ulog_buffers[i].timer); | ||
256 | ulog_buffers[i].timer.function = ulog_timer; | ||
257 | ulog_buffers[i].timer.data = i; | ||
258 | spin_lock_init(&ulog_buffers[i].lock); | ||
259 | } | ||
260 | |||
261 | ebtulognl = netlink_kernel_create(NETLINK_NFLOG, NULL); | ||
262 | if (!ebtulognl) | ||
263 | ret = -ENOMEM; | ||
264 | else if ((ret = ebt_register_watcher(&ulog))) | ||
265 | sock_release(ebtulognl->sk_socket); | ||
266 | |||
267 | return ret; | ||
268 | } | ||
269 | |||
270 | static void __exit fini(void) | ||
271 | { | ||
272 | ebt_ulog_buff_t *ub; | ||
273 | int i; | ||
274 | |||
275 | ebt_unregister_watcher(&ulog); | ||
276 | for (i = 0; i < EBT_ULOG_MAXNLGROUPS; i++) { | ||
277 | ub = &ulog_buffers[i]; | ||
278 | if (timer_pending(&ub->timer)) | ||
279 | del_timer(&ub->timer); | ||
280 | spin_lock_bh(&ub->lock); | ||
281 | if (ub->skb) { | ||
282 | kfree_skb(ub->skb); | ||
283 | ub->skb = NULL; | ||
284 | } | ||
285 | spin_unlock_bh(&ub->lock); | ||
286 | } | ||
287 | sock_release(ebtulognl->sk_socket); | ||
288 | } | ||
289 | |||
290 | module_init(init); | ||
291 | module_exit(fini); | ||
292 | MODULE_LICENSE("GPL"); | ||
293 | MODULE_AUTHOR("Bart De Schuymer <bdschuym@pandora.be>"); | ||
294 | MODULE_DESCRIPTION("ebtables userspace logging module for bridged Ethernet" | ||
295 | " frames"); | ||
diff --git a/net/bridge/netfilter/ebt_vlan.c b/net/bridge/netfilter/ebt_vlan.c new file mode 100644 index 000000000000..db60d734908b --- /dev/null +++ b/net/bridge/netfilter/ebt_vlan.c | |||
@@ -0,0 +1,195 @@ | |||
1 | /* | ||
2 | * Description: EBTables 802.1Q match extension kernelspace module. | ||
3 | * Authors: Nick Fedchik <nick@fedchik.org.ua> | ||
4 | * 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 as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | */ | ||
20 | |||
21 | #include <linux/if_ether.h> | ||
22 | #include <linux/if_vlan.h> | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/moduleparam.h> | ||
25 | #include <linux/netfilter_bridge/ebtables.h> | ||
26 | #include <linux/netfilter_bridge/ebt_vlan.h> | ||
27 | |||
28 | static int debug; | ||
29 | #define MODULE_VERS "0.6" | ||
30 | |||
31 | module_param(debug, int, 0); | ||
32 | MODULE_PARM_DESC(debug, "debug=1 is turn on debug messages"); | ||
33 | MODULE_AUTHOR("Nick Fedchik <nick@fedchik.org.ua>"); | ||
34 | MODULE_DESCRIPTION("802.1Q match module (ebtables extension), v" | ||
35 | MODULE_VERS); | ||
36 | MODULE_LICENSE("GPL"); | ||
37 | |||
38 | |||
39 | #define DEBUG_MSG(args...) if (debug) printk (KERN_DEBUG "ebt_vlan: " args) | ||
40 | #define INV_FLAG(_inv_flag_) (info->invflags & _inv_flag_) ? "!" : "" | ||
41 | #define GET_BITMASK(_BIT_MASK_) info->bitmask & _BIT_MASK_ | ||
42 | #define SET_BITMASK(_BIT_MASK_) info->bitmask |= _BIT_MASK_ | ||
43 | #define EXIT_ON_MISMATCH(_MATCH_,_MASK_) {if (!((info->_MATCH_ == _MATCH_)^!!(info->invflags & _MASK_))) return EBT_NOMATCH;} | ||
44 | |||
45 | static int | ||
46 | ebt_filter_vlan(const struct sk_buff *skb, | ||
47 | const struct net_device *in, | ||
48 | const struct net_device *out, | ||
49 | const void *data, unsigned int datalen) | ||
50 | { | ||
51 | struct ebt_vlan_info *info = (struct ebt_vlan_info *) data; | ||
52 | struct vlan_hdr _frame, *fp; | ||
53 | |||
54 | unsigned short TCI; /* Whole TCI, given from parsed frame */ | ||
55 | unsigned short id; /* VLAN ID, given from frame TCI */ | ||
56 | unsigned char prio; /* user_priority, given from frame TCI */ | ||
57 | /* VLAN encapsulated Type/Length field, given from orig frame */ | ||
58 | unsigned short encap; | ||
59 | |||
60 | fp = skb_header_pointer(skb, 0, sizeof(_frame), &_frame); | ||
61 | if (fp == NULL) | ||
62 | return EBT_NOMATCH; | ||
63 | |||
64 | /* Tag Control Information (TCI) consists of the following elements: | ||
65 | * - User_priority. The user_priority field is three bits in length, | ||
66 | * interpreted as a binary number. | ||
67 | * - Canonical Format Indicator (CFI). The Canonical Format Indicator | ||
68 | * (CFI) is a single bit flag value. Currently ignored. | ||
69 | * - VLAN Identifier (VID). The VID is encoded as | ||
70 | * an unsigned binary number. */ | ||
71 | TCI = ntohs(fp->h_vlan_TCI); | ||
72 | id = TCI & VLAN_VID_MASK; | ||
73 | prio = (TCI >> 13) & 0x7; | ||
74 | encap = fp->h_vlan_encapsulated_proto; | ||
75 | |||
76 | /* Checking VLAN Identifier (VID) */ | ||
77 | if (GET_BITMASK(EBT_VLAN_ID)) | ||
78 | EXIT_ON_MISMATCH(id, EBT_VLAN_ID); | ||
79 | |||
80 | /* Checking user_priority */ | ||
81 | if (GET_BITMASK(EBT_VLAN_PRIO)) | ||
82 | EXIT_ON_MISMATCH(prio, EBT_VLAN_PRIO); | ||
83 | |||
84 | /* Checking Encapsulated Proto (Length/Type) field */ | ||
85 | if (GET_BITMASK(EBT_VLAN_ENCAP)) | ||
86 | EXIT_ON_MISMATCH(encap, EBT_VLAN_ENCAP); | ||
87 | |||
88 | return EBT_MATCH; | ||
89 | } | ||
90 | |||
91 | static int | ||
92 | ebt_check_vlan(const char *tablename, | ||
93 | unsigned int hooknr, | ||
94 | const struct ebt_entry *e, void *data, unsigned int datalen) | ||
95 | { | ||
96 | struct ebt_vlan_info *info = (struct ebt_vlan_info *) data; | ||
97 | |||
98 | /* Parameters buffer overflow check */ | ||
99 | if (datalen != EBT_ALIGN(sizeof(struct ebt_vlan_info))) { | ||
100 | DEBUG_MSG | ||
101 | ("passed size %d is not eq to ebt_vlan_info (%Zd)\n", | ||
102 | datalen, sizeof(struct ebt_vlan_info)); | ||
103 | return -EINVAL; | ||
104 | } | ||
105 | |||
106 | /* Is it 802.1Q frame checked? */ | ||
107 | if (e->ethproto != htons(ETH_P_8021Q)) { | ||
108 | DEBUG_MSG | ||
109 | ("passed entry proto %2.4X is not 802.1Q (8100)\n", | ||
110 | (unsigned short) ntohs(e->ethproto)); | ||
111 | return -EINVAL; | ||
112 | } | ||
113 | |||
114 | /* Check for bitmask range | ||
115 | * True if even one bit is out of mask */ | ||
116 | if (info->bitmask & ~EBT_VLAN_MASK) { | ||
117 | DEBUG_MSG("bitmask %2X is out of mask (%2X)\n", | ||
118 | info->bitmask, EBT_VLAN_MASK); | ||
119 | return -EINVAL; | ||
120 | } | ||
121 | |||
122 | /* Check for inversion flags range */ | ||
123 | if (info->invflags & ~EBT_VLAN_MASK) { | ||
124 | DEBUG_MSG("inversion flags %2X is out of mask (%2X)\n", | ||
125 | info->invflags, EBT_VLAN_MASK); | ||
126 | return -EINVAL; | ||
127 | } | ||
128 | |||
129 | /* Reserved VLAN ID (VID) values | ||
130 | * ----------------------------- | ||
131 | * 0 - The null VLAN ID. | ||
132 | * 1 - The default Port VID (PVID) | ||
133 | * 0x0FFF - Reserved for implementation use. | ||
134 | * if_vlan.h: VLAN_GROUP_ARRAY_LEN 4096. */ | ||
135 | if (GET_BITMASK(EBT_VLAN_ID)) { | ||
136 | if (!!info->id) { /* if id!=0 => check vid range */ | ||
137 | if (info->id > VLAN_GROUP_ARRAY_LEN) { | ||
138 | DEBUG_MSG | ||
139 | ("id %d is out of range (1-4096)\n", | ||
140 | info->id); | ||
141 | return -EINVAL; | ||
142 | } | ||
143 | /* Note: This is valid VLAN-tagged frame point. | ||
144 | * Any value of user_priority are acceptable, | ||
145 | * but should be ignored according to 802.1Q Std. | ||
146 | * So we just drop the prio flag. */ | ||
147 | info->bitmask &= ~EBT_VLAN_PRIO; | ||
148 | } | ||
149 | /* Else, id=0 (null VLAN ID) => user_priority range (any?) */ | ||
150 | } | ||
151 | |||
152 | if (GET_BITMASK(EBT_VLAN_PRIO)) { | ||
153 | if ((unsigned char) info->prio > 7) { | ||
154 | DEBUG_MSG("prio %d is out of range (0-7)\n", | ||
155 | info->prio); | ||
156 | return -EINVAL; | ||
157 | } | ||
158 | } | ||
159 | /* Check for encapsulated proto range - it is possible to be | ||
160 | * any value for u_short range. | ||
161 | * if_ether.h: ETH_ZLEN 60 - Min. octets in frame sans FCS */ | ||
162 | if (GET_BITMASK(EBT_VLAN_ENCAP)) { | ||
163 | if ((unsigned short) ntohs(info->encap) < ETH_ZLEN) { | ||
164 | DEBUG_MSG | ||
165 | ("encap frame length %d is less than minimal\n", | ||
166 | ntohs(info->encap)); | ||
167 | return -EINVAL; | ||
168 | } | ||
169 | } | ||
170 | |||
171 | return 0; | ||
172 | } | ||
173 | |||
174 | static struct ebt_match filter_vlan = { | ||
175 | .name = EBT_VLAN_MATCH, | ||
176 | .match = ebt_filter_vlan, | ||
177 | .check = ebt_check_vlan, | ||
178 | .me = THIS_MODULE, | ||
179 | }; | ||
180 | |||
181 | static int __init init(void) | ||
182 | { | ||
183 | DEBUG_MSG("ebtables 802.1Q extension module v" | ||
184 | MODULE_VERS "\n"); | ||
185 | DEBUG_MSG("module debug=%d\n", !!debug); | ||
186 | return ebt_register_match(&filter_vlan); | ||
187 | } | ||
188 | |||
189 | static void __exit fini(void) | ||
190 | { | ||
191 | ebt_unregister_match(&filter_vlan); | ||
192 | } | ||
193 | |||
194 | module_init(init); | ||
195 | module_exit(fini); | ||
diff --git a/net/bridge/netfilter/ebtable_broute.c b/net/bridge/netfilter/ebtable_broute.c new file mode 100644 index 000000000000..1767c94cd3de --- /dev/null +++ b/net/bridge/netfilter/ebtable_broute.c | |||
@@ -0,0 +1,86 @@ | |||
1 | /* | ||
2 | * ebtable_broute | ||
3 | * | ||
4 | * Authors: | ||
5 | * Bart De Schuymer <bdschuym@pandora.be> | ||
6 | * | ||
7 | * April, 2002 | ||
8 | * | ||
9 | * This table lets you choose between routing and bridging for frames | ||
10 | * entering on a bridge enslaved nic. This table is traversed before any | ||
11 | * other ebtables table. See net/bridge/br_input.c. | ||
12 | */ | ||
13 | |||
14 | #include <linux/netfilter_bridge/ebtables.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/if_bridge.h> | ||
17 | |||
18 | /* EBT_ACCEPT means the frame will be bridged | ||
19 | * EBT_DROP means the frame will be routed | ||
20 | */ | ||
21 | static struct ebt_entries initial_chain = { | ||
22 | .name = "BROUTING", | ||
23 | .policy = EBT_ACCEPT, | ||
24 | }; | ||
25 | |||
26 | static struct ebt_replace initial_table = | ||
27 | { | ||
28 | .name = "broute", | ||
29 | .valid_hooks = 1 << NF_BR_BROUTING, | ||
30 | .entries_size = sizeof(struct ebt_entries), | ||
31 | .hook_entry = { | ||
32 | [NF_BR_BROUTING] = &initial_chain, | ||
33 | }, | ||
34 | .entries = (char *)&initial_chain, | ||
35 | }; | ||
36 | |||
37 | static int check(const struct ebt_table_info *info, unsigned int valid_hooks) | ||
38 | { | ||
39 | if (valid_hooks & ~(1 << NF_BR_BROUTING)) | ||
40 | return -EINVAL; | ||
41 | return 0; | ||
42 | } | ||
43 | |||
44 | static struct ebt_table broute_table = | ||
45 | { | ||
46 | .name = "broute", | ||
47 | .table = &initial_table, | ||
48 | .valid_hooks = 1 << NF_BR_BROUTING, | ||
49 | .lock = RW_LOCK_UNLOCKED, | ||
50 | .check = check, | ||
51 | .me = THIS_MODULE, | ||
52 | }; | ||
53 | |||
54 | static int ebt_broute(struct sk_buff **pskb) | ||
55 | { | ||
56 | int ret; | ||
57 | |||
58 | ret = ebt_do_table(NF_BR_BROUTING, pskb, (*pskb)->dev, NULL, | ||
59 | &broute_table); | ||
60 | if (ret == NF_DROP) | ||
61 | return 1; /* route it */ | ||
62 | return 0; /* bridge it */ | ||
63 | } | ||
64 | |||
65 | static int __init init(void) | ||
66 | { | ||
67 | int ret; | ||
68 | |||
69 | ret = ebt_register_table(&broute_table); | ||
70 | if (ret < 0) | ||
71 | return ret; | ||
72 | /* see br_input.c */ | ||
73 | br_should_route_hook = ebt_broute; | ||
74 | return ret; | ||
75 | } | ||
76 | |||
77 | static void __exit fini(void) | ||
78 | { | ||
79 | br_should_route_hook = NULL; | ||
80 | synchronize_net(); | ||
81 | ebt_unregister_table(&broute_table); | ||
82 | } | ||
83 | |||
84 | module_init(init); | ||
85 | module_exit(fini); | ||
86 | MODULE_LICENSE("GPL"); | ||
diff --git a/net/bridge/netfilter/ebtable_filter.c b/net/bridge/netfilter/ebtable_filter.c new file mode 100644 index 000000000000..c18666e0392b --- /dev/null +++ b/net/bridge/netfilter/ebtable_filter.c | |||
@@ -0,0 +1,123 @@ | |||
1 | /* | ||
2 | * ebtable_filter | ||
3 | * | ||
4 | * Authors: | ||
5 | * Bart De Schuymer <bdschuym@pandora.be> | ||
6 | * | ||
7 | * April, 2002 | ||
8 | * | ||
9 | */ | ||
10 | |||
11 | #include <linux/netfilter_bridge/ebtables.h> | ||
12 | #include <linux/module.h> | ||
13 | |||
14 | #define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \ | ||
15 | (1 << NF_BR_LOCAL_OUT)) | ||
16 | |||
17 | static struct ebt_entries initial_chains[] = | ||
18 | { | ||
19 | { | ||
20 | .name = "INPUT", | ||
21 | .policy = EBT_ACCEPT, | ||
22 | }, | ||
23 | { | ||
24 | .name = "FORWARD", | ||
25 | .policy = EBT_ACCEPT, | ||
26 | }, | ||
27 | { | ||
28 | .name = "OUTPUT", | ||
29 | .policy = EBT_ACCEPT, | ||
30 | }, | ||
31 | }; | ||
32 | |||
33 | static struct ebt_replace initial_table = | ||
34 | { | ||
35 | .name = "filter", | ||
36 | .valid_hooks = FILTER_VALID_HOOKS, | ||
37 | .entries_size = 3 * sizeof(struct ebt_entries), | ||
38 | .hook_entry = { | ||
39 | [NF_BR_LOCAL_IN] = &initial_chains[0], | ||
40 | [NF_BR_FORWARD] = &initial_chains[1], | ||
41 | [NF_BR_LOCAL_OUT] = &initial_chains[2], | ||
42 | }, | ||
43 | .entries = (char *)initial_chains, | ||
44 | }; | ||
45 | |||
46 | static int check(const struct ebt_table_info *info, unsigned int valid_hooks) | ||
47 | { | ||
48 | if (valid_hooks & ~FILTER_VALID_HOOKS) | ||
49 | return -EINVAL; | ||
50 | return 0; | ||
51 | } | ||
52 | |||
53 | static struct ebt_table frame_filter = | ||
54 | { | ||
55 | .name = "filter", | ||
56 | .table = &initial_table, | ||
57 | .valid_hooks = FILTER_VALID_HOOKS, | ||
58 | .lock = RW_LOCK_UNLOCKED, | ||
59 | .check = check, | ||
60 | .me = THIS_MODULE, | ||
61 | }; | ||
62 | |||
63 | static unsigned int | ||
64 | ebt_hook (unsigned int hook, struct sk_buff **pskb, const struct net_device *in, | ||
65 | const struct net_device *out, int (*okfn)(struct sk_buff *)) | ||
66 | { | ||
67 | return ebt_do_table(hook, pskb, in, out, &frame_filter); | ||
68 | } | ||
69 | |||
70 | static struct nf_hook_ops ebt_ops_filter[] = { | ||
71 | { | ||
72 | .hook = ebt_hook, | ||
73 | .owner = THIS_MODULE, | ||
74 | .pf = PF_BRIDGE, | ||
75 | .hooknum = NF_BR_LOCAL_IN, | ||
76 | .priority = NF_BR_PRI_FILTER_BRIDGED, | ||
77 | }, | ||
78 | { | ||
79 | .hook = ebt_hook, | ||
80 | .owner = THIS_MODULE, | ||
81 | .pf = PF_BRIDGE, | ||
82 | .hooknum = NF_BR_FORWARD, | ||
83 | .priority = NF_BR_PRI_FILTER_BRIDGED, | ||
84 | }, | ||
85 | { | ||
86 | .hook = ebt_hook, | ||
87 | .owner = THIS_MODULE, | ||
88 | .pf = PF_BRIDGE, | ||
89 | .hooknum = NF_BR_LOCAL_OUT, | ||
90 | .priority = NF_BR_PRI_FILTER_OTHER, | ||
91 | }, | ||
92 | }; | ||
93 | |||
94 | static int __init init(void) | ||
95 | { | ||
96 | int i, j, ret; | ||
97 | |||
98 | ret = ebt_register_table(&frame_filter); | ||
99 | if (ret < 0) | ||
100 | return ret; | ||
101 | for (i = 0; i < ARRAY_SIZE(ebt_ops_filter); i++) | ||
102 | if ((ret = nf_register_hook(&ebt_ops_filter[i])) < 0) | ||
103 | goto cleanup; | ||
104 | return ret; | ||
105 | cleanup: | ||
106 | for (j = 0; j < i; j++) | ||
107 | nf_unregister_hook(&ebt_ops_filter[j]); | ||
108 | ebt_unregister_table(&frame_filter); | ||
109 | return ret; | ||
110 | } | ||
111 | |||
112 | static void __exit fini(void) | ||
113 | { | ||
114 | int i; | ||
115 | |||
116 | for (i = 0; i < ARRAY_SIZE(ebt_ops_filter); i++) | ||
117 | nf_unregister_hook(&ebt_ops_filter[i]); | ||
118 | ebt_unregister_table(&frame_filter); | ||
119 | } | ||
120 | |||
121 | module_init(init); | ||
122 | module_exit(fini); | ||
123 | MODULE_LICENSE("GPL"); | ||
diff --git a/net/bridge/netfilter/ebtable_nat.c b/net/bridge/netfilter/ebtable_nat.c new file mode 100644 index 000000000000..828cac2cc4a3 --- /dev/null +++ b/net/bridge/netfilter/ebtable_nat.c | |||
@@ -0,0 +1,130 @@ | |||
1 | /* | ||
2 | * ebtable_nat | ||
3 | * | ||
4 | * Authors: | ||
5 | * Bart De Schuymer <bdschuym@pandora.be> | ||
6 | * | ||
7 | * April, 2002 | ||
8 | * | ||
9 | */ | ||
10 | |||
11 | #include <linux/netfilter_bridge/ebtables.h> | ||
12 | #include <linux/module.h> | ||
13 | |||
14 | #define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \ | ||
15 | (1 << NF_BR_POST_ROUTING)) | ||
16 | |||
17 | static struct ebt_entries initial_chains[] = | ||
18 | { | ||
19 | { | ||
20 | .name = "PREROUTING", | ||
21 | .policy = EBT_ACCEPT, | ||
22 | }, | ||
23 | { | ||
24 | .name = "OUTPUT", | ||
25 | .policy = EBT_ACCEPT, | ||
26 | }, | ||
27 | { | ||
28 | .name = "POSTROUTING", | ||
29 | .policy = EBT_ACCEPT, | ||
30 | } | ||
31 | }; | ||
32 | |||
33 | static struct ebt_replace initial_table = | ||
34 | { | ||
35 | .name = "nat", | ||
36 | .valid_hooks = NAT_VALID_HOOKS, | ||
37 | .entries_size = 3 * sizeof(struct ebt_entries), | ||
38 | .hook_entry = { | ||
39 | [NF_BR_PRE_ROUTING] = &initial_chains[0], | ||
40 | [NF_BR_LOCAL_OUT] = &initial_chains[1], | ||
41 | [NF_BR_POST_ROUTING] = &initial_chains[2], | ||
42 | }, | ||
43 | .entries = (char *)initial_chains, | ||
44 | }; | ||
45 | |||
46 | static int check(const struct ebt_table_info *info, unsigned int valid_hooks) | ||
47 | { | ||
48 | if (valid_hooks & ~NAT_VALID_HOOKS) | ||
49 | return -EINVAL; | ||
50 | return 0; | ||
51 | } | ||
52 | |||
53 | static struct ebt_table frame_nat = | ||
54 | { | ||
55 | .name = "nat", | ||
56 | .table = &initial_table, | ||
57 | .valid_hooks = NAT_VALID_HOOKS, | ||
58 | .lock = RW_LOCK_UNLOCKED, | ||
59 | .check = check, | ||
60 | .me = THIS_MODULE, | ||
61 | }; | ||
62 | |||
63 | static unsigned int | ||
64 | ebt_nat_dst(unsigned int hook, struct sk_buff **pskb, const struct net_device *in | ||
65 | , const struct net_device *out, int (*okfn)(struct sk_buff *)) | ||
66 | { | ||
67 | return ebt_do_table(hook, pskb, in, out, &frame_nat); | ||
68 | } | ||
69 | |||
70 | static unsigned int | ||
71 | ebt_nat_src(unsigned int hook, struct sk_buff **pskb, const struct net_device *in | ||
72 | , const struct net_device *out, int (*okfn)(struct sk_buff *)) | ||
73 | { | ||
74 | return ebt_do_table(hook, pskb, in, out, &frame_nat); | ||
75 | } | ||
76 | |||
77 | static struct nf_hook_ops ebt_ops_nat[] = { | ||
78 | { | ||
79 | .hook = ebt_nat_dst, | ||
80 | .owner = THIS_MODULE, | ||
81 | .pf = PF_BRIDGE, | ||
82 | .hooknum = NF_BR_LOCAL_OUT, | ||
83 | .priority = NF_BR_PRI_NAT_DST_OTHER, | ||
84 | }, | ||
85 | { | ||
86 | .hook = ebt_nat_src, | ||
87 | .owner = THIS_MODULE, | ||
88 | .pf = PF_BRIDGE, | ||
89 | .hooknum = NF_BR_POST_ROUTING, | ||
90 | .priority = NF_BR_PRI_NAT_SRC, | ||
91 | }, | ||
92 | { | ||
93 | .hook = ebt_nat_dst, | ||
94 | .owner = THIS_MODULE, | ||
95 | .pf = PF_BRIDGE, | ||
96 | .hooknum = NF_BR_PRE_ROUTING, | ||
97 | .priority = NF_BR_PRI_NAT_DST_BRIDGED, | ||
98 | }, | ||
99 | }; | ||
100 | |||
101 | static int __init init(void) | ||
102 | { | ||
103 | int i, ret, j; | ||
104 | |||
105 | ret = ebt_register_table(&frame_nat); | ||
106 | if (ret < 0) | ||
107 | return ret; | ||
108 | for (i = 0; i < ARRAY_SIZE(ebt_ops_nat); i++) | ||
109 | if ((ret = nf_register_hook(&ebt_ops_nat[i])) < 0) | ||
110 | goto cleanup; | ||
111 | return ret; | ||
112 | cleanup: | ||
113 | for (j = 0; j < i; j++) | ||
114 | nf_unregister_hook(&ebt_ops_nat[j]); | ||
115 | ebt_unregister_table(&frame_nat); | ||
116 | return ret; | ||
117 | } | ||
118 | |||
119 | static void __exit fini(void) | ||
120 | { | ||
121 | int i; | ||
122 | |||
123 | for (i = 0; i < ARRAY_SIZE(ebt_ops_nat); i++) | ||
124 | nf_unregister_hook(&ebt_ops_nat[i]); | ||
125 | ebt_unregister_table(&frame_nat); | ||
126 | } | ||
127 | |||
128 | module_init(init); | ||
129 | module_exit(fini); | ||
130 | MODULE_LICENSE("GPL"); | ||
diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c new file mode 100644 index 000000000000..18ebc664769b --- /dev/null +++ b/net/bridge/netfilter/ebtables.c | |||
@@ -0,0 +1,1507 @@ | |||
1 | /* | ||
2 | * ebtables | ||
3 | * | ||
4 | * Author: | ||
5 | * Bart De Schuymer <bdschuym@pandora.be> | ||
6 | * | ||
7 | * ebtables.c,v 2.0, July, 2002 | ||
8 | * | ||
9 | * This code is stongly inspired on the iptables code which is | ||
10 | * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version | ||
15 | * 2 of the License, or (at your option) any later version. | ||
16 | */ | ||
17 | |||
18 | /* used for print_string */ | ||
19 | #include <linux/sched.h> | ||
20 | #include <linux/tty.h> | ||
21 | |||
22 | #include <linux/kmod.h> | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/vmalloc.h> | ||
25 | #include <linux/netfilter_bridge/ebtables.h> | ||
26 | #include <linux/spinlock.h> | ||
27 | #include <asm/uaccess.h> | ||
28 | #include <linux/smp.h> | ||
29 | #include <net/sock.h> | ||
30 | /* needed for logical [in,out]-dev filtering */ | ||
31 | #include "../br_private.h" | ||
32 | |||
33 | /* list_named_find */ | ||
34 | #define ASSERT_READ_LOCK(x) | ||
35 | #define ASSERT_WRITE_LOCK(x) | ||
36 | #include <linux/netfilter_ipv4/listhelp.h> | ||
37 | |||
38 | #if 0 | ||
39 | /* use this for remote debugging | ||
40 | * Copyright (C) 1998 by Ori Pomerantz | ||
41 | * Print the string to the appropriate tty, the one | ||
42 | * the current task uses | ||
43 | */ | ||
44 | static void print_string(char *str) | ||
45 | { | ||
46 | struct tty_struct *my_tty; | ||
47 | |||
48 | /* The tty for the current task */ | ||
49 | my_tty = current->signal->tty; | ||
50 | if (my_tty != NULL) { | ||
51 | my_tty->driver->write(my_tty, 0, str, strlen(str)); | ||
52 | my_tty->driver->write(my_tty, 0, "\015\012", 2); | ||
53 | } | ||
54 | } | ||
55 | |||
56 | #define BUGPRINT(args) print_string(args); | ||
57 | #else | ||
58 | #define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please "\ | ||
59 | "report to author: "format, ## args) | ||
60 | /* #define BUGPRINT(format, args...) */ | ||
61 | #endif | ||
62 | #define MEMPRINT(format, args...) printk("kernel msg: ebtables "\ | ||
63 | ": out of memory: "format, ## args) | ||
64 | /* #define MEMPRINT(format, args...) */ | ||
65 | |||
66 | |||
67 | |||
68 | /* | ||
69 | * Each cpu has its own set of counters, so there is no need for write_lock in | ||
70 | * the softirq | ||
71 | * For reading or updating the counters, the user context needs to | ||
72 | * get a write_lock | ||
73 | */ | ||
74 | |||
75 | /* The size of each set of counters is altered to get cache alignment */ | ||
76 | #define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1)) | ||
77 | #define COUNTER_OFFSET(n) (SMP_ALIGN(n * sizeof(struct ebt_counter))) | ||
78 | #define COUNTER_BASE(c, n, cpu) ((struct ebt_counter *)(((char *)c) + \ | ||
79 | COUNTER_OFFSET(n) * cpu)) | ||
80 | |||
81 | |||
82 | |||
83 | static DECLARE_MUTEX(ebt_mutex); | ||
84 | static LIST_HEAD(ebt_tables); | ||
85 | static LIST_HEAD(ebt_targets); | ||
86 | static LIST_HEAD(ebt_matches); | ||
87 | static LIST_HEAD(ebt_watchers); | ||
88 | |||
89 | static struct ebt_target ebt_standard_target = | ||
90 | { {NULL, NULL}, EBT_STANDARD_TARGET, NULL, NULL, NULL, NULL}; | ||
91 | |||
92 | static inline int ebt_do_watcher (struct ebt_entry_watcher *w, | ||
93 | const struct sk_buff *skb, unsigned int hooknr, const struct net_device *in, | ||
94 | const struct net_device *out) | ||
95 | { | ||
96 | w->u.watcher->watcher(skb, hooknr, in, out, w->data, | ||
97 | w->watcher_size); | ||
98 | /* watchers don't give a verdict */ | ||
99 | return 0; | ||
100 | } | ||
101 | |||
102 | static inline int ebt_do_match (struct ebt_entry_match *m, | ||
103 | const struct sk_buff *skb, const struct net_device *in, | ||
104 | const struct net_device *out) | ||
105 | { | ||
106 | return m->u.match->match(skb, in, out, m->data, | ||
107 | m->match_size); | ||
108 | } | ||
109 | |||
110 | static inline int ebt_dev_check(char *entry, const struct net_device *device) | ||
111 | { | ||
112 | int i = 0; | ||
113 | char *devname = device->name; | ||
114 | |||
115 | if (*entry == '\0') | ||
116 | return 0; | ||
117 | if (!device) | ||
118 | return 1; | ||
119 | /* 1 is the wildcard token */ | ||
120 | while (entry[i] != '\0' && entry[i] != 1 && entry[i] == devname[i]) | ||
121 | i++; | ||
122 | return (devname[i] != entry[i] && entry[i] != 1); | ||
123 | } | ||
124 | |||
125 | #define FWINV2(bool,invflg) ((bool) ^ !!(e->invflags & invflg)) | ||
126 | /* process standard matches */ | ||
127 | static inline int ebt_basic_match(struct ebt_entry *e, struct ethhdr *h, | ||
128 | const struct net_device *in, const struct net_device *out) | ||
129 | { | ||
130 | int verdict, i; | ||
131 | |||
132 | if (e->bitmask & EBT_802_3) { | ||
133 | if (FWINV2(ntohs(h->h_proto) >= 1536, EBT_IPROTO)) | ||
134 | return 1; | ||
135 | } else if (!(e->bitmask & EBT_NOPROTO) && | ||
136 | FWINV2(e->ethproto != h->h_proto, EBT_IPROTO)) | ||
137 | return 1; | ||
138 | |||
139 | if (FWINV2(ebt_dev_check(e->in, in), EBT_IIN)) | ||
140 | return 1; | ||
141 | if (FWINV2(ebt_dev_check(e->out, out), EBT_IOUT)) | ||
142 | return 1; | ||
143 | if ((!in || !in->br_port) ? 0 : FWINV2(ebt_dev_check( | ||
144 | e->logical_in, in->br_port->br->dev), EBT_ILOGICALIN)) | ||
145 | return 1; | ||
146 | if ((!out || !out->br_port) ? 0 : FWINV2(ebt_dev_check( | ||
147 | e->logical_out, out->br_port->br->dev), EBT_ILOGICALOUT)) | ||
148 | return 1; | ||
149 | |||
150 | if (e->bitmask & EBT_SOURCEMAC) { | ||
151 | verdict = 0; | ||
152 | for (i = 0; i < 6; i++) | ||
153 | verdict |= (h->h_source[i] ^ e->sourcemac[i]) & | ||
154 | e->sourcemsk[i]; | ||
155 | if (FWINV2(verdict != 0, EBT_ISOURCE) ) | ||
156 | return 1; | ||
157 | } | ||
158 | if (e->bitmask & EBT_DESTMAC) { | ||
159 | verdict = 0; | ||
160 | for (i = 0; i < 6; i++) | ||
161 | verdict |= (h->h_dest[i] ^ e->destmac[i]) & | ||
162 | e->destmsk[i]; | ||
163 | if (FWINV2(verdict != 0, EBT_IDEST) ) | ||
164 | return 1; | ||
165 | } | ||
166 | return 0; | ||
167 | } | ||
168 | |||
169 | /* Do some firewalling */ | ||
170 | unsigned int ebt_do_table (unsigned int hook, struct sk_buff **pskb, | ||
171 | const struct net_device *in, const struct net_device *out, | ||
172 | struct ebt_table *table) | ||
173 | { | ||
174 | int i, nentries; | ||
175 | struct ebt_entry *point; | ||
176 | struct ebt_counter *counter_base, *cb_base; | ||
177 | struct ebt_entry_target *t; | ||
178 | int verdict, sp = 0; | ||
179 | struct ebt_chainstack *cs; | ||
180 | struct ebt_entries *chaininfo; | ||
181 | char *base; | ||
182 | struct ebt_table_info *private; | ||
183 | |||
184 | read_lock_bh(&table->lock); | ||
185 | private = table->private; | ||
186 | cb_base = COUNTER_BASE(private->counters, private->nentries, | ||
187 | smp_processor_id()); | ||
188 | if (private->chainstack) | ||
189 | cs = private->chainstack[smp_processor_id()]; | ||
190 | else | ||
191 | cs = NULL; | ||
192 | chaininfo = private->hook_entry[hook]; | ||
193 | nentries = private->hook_entry[hook]->nentries; | ||
194 | point = (struct ebt_entry *)(private->hook_entry[hook]->data); | ||
195 | counter_base = cb_base + private->hook_entry[hook]->counter_offset; | ||
196 | /* base for chain jumps */ | ||
197 | base = private->entries; | ||
198 | i = 0; | ||
199 | while (i < nentries) { | ||
200 | if (ebt_basic_match(point, eth_hdr(*pskb), in, out)) | ||
201 | goto letscontinue; | ||
202 | |||
203 | if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in, out) != 0) | ||
204 | goto letscontinue; | ||
205 | |||
206 | /* increase counter */ | ||
207 | (*(counter_base + i)).pcnt++; | ||
208 | (*(counter_base + i)).bcnt+=(**pskb).len; | ||
209 | |||
210 | /* these should only watch: not modify, nor tell us | ||
211 | what to do with the packet */ | ||
212 | EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, hook, in, | ||
213 | out); | ||
214 | |||
215 | t = (struct ebt_entry_target *) | ||
216 | (((char *)point) + point->target_offset); | ||
217 | /* standard target */ | ||
218 | if (!t->u.target->target) | ||
219 | verdict = ((struct ebt_standard_target *)t)->verdict; | ||
220 | else | ||
221 | verdict = t->u.target->target(pskb, hook, | ||
222 | in, out, t->data, t->target_size); | ||
223 | if (verdict == EBT_ACCEPT) { | ||
224 | read_unlock_bh(&table->lock); | ||
225 | return NF_ACCEPT; | ||
226 | } | ||
227 | if (verdict == EBT_DROP) { | ||
228 | read_unlock_bh(&table->lock); | ||
229 | return NF_DROP; | ||
230 | } | ||
231 | if (verdict == EBT_RETURN) { | ||
232 | letsreturn: | ||
233 | #ifdef CONFIG_NETFILTER_DEBUG | ||
234 | if (sp == 0) { | ||
235 | BUGPRINT("RETURN on base chain"); | ||
236 | /* act like this is EBT_CONTINUE */ | ||
237 | goto letscontinue; | ||
238 | } | ||
239 | #endif | ||
240 | sp--; | ||
241 | /* put all the local variables right */ | ||
242 | i = cs[sp].n; | ||
243 | chaininfo = cs[sp].chaininfo; | ||
244 | nentries = chaininfo->nentries; | ||
245 | point = cs[sp].e; | ||
246 | counter_base = cb_base + | ||
247 | chaininfo->counter_offset; | ||
248 | continue; | ||
249 | } | ||
250 | if (verdict == EBT_CONTINUE) | ||
251 | goto letscontinue; | ||
252 | #ifdef CONFIG_NETFILTER_DEBUG | ||
253 | if (verdict < 0) { | ||
254 | BUGPRINT("bogus standard verdict\n"); | ||
255 | read_unlock_bh(&table->lock); | ||
256 | return NF_DROP; | ||
257 | } | ||
258 | #endif | ||
259 | /* jump to a udc */ | ||
260 | cs[sp].n = i + 1; | ||
261 | cs[sp].chaininfo = chaininfo; | ||
262 | cs[sp].e = (struct ebt_entry *) | ||
263 | (((char *)point) + point->next_offset); | ||
264 | i = 0; | ||
265 | chaininfo = (struct ebt_entries *) (base + verdict); | ||
266 | #ifdef CONFIG_NETFILTER_DEBUG | ||
267 | if (chaininfo->distinguisher) { | ||
268 | BUGPRINT("jump to non-chain\n"); | ||
269 | read_unlock_bh(&table->lock); | ||
270 | return NF_DROP; | ||
271 | } | ||
272 | #endif | ||
273 | nentries = chaininfo->nentries; | ||
274 | point = (struct ebt_entry *)chaininfo->data; | ||
275 | counter_base = cb_base + chaininfo->counter_offset; | ||
276 | sp++; | ||
277 | continue; | ||
278 | letscontinue: | ||
279 | point = (struct ebt_entry *) | ||
280 | (((char *)point) + point->next_offset); | ||
281 | i++; | ||
282 | } | ||
283 | |||
284 | /* I actually like this :) */ | ||
285 | if (chaininfo->policy == EBT_RETURN) | ||
286 | goto letsreturn; | ||
287 | if (chaininfo->policy == EBT_ACCEPT) { | ||
288 | read_unlock_bh(&table->lock); | ||
289 | return NF_ACCEPT; | ||
290 | } | ||
291 | read_unlock_bh(&table->lock); | ||
292 | return NF_DROP; | ||
293 | } | ||
294 | |||
295 | /* If it succeeds, returns element and locks mutex */ | ||
296 | static inline void * | ||
297 | find_inlist_lock_noload(struct list_head *head, const char *name, int *error, | ||
298 | struct semaphore *mutex) | ||
299 | { | ||
300 | void *ret; | ||
301 | |||
302 | *error = down_interruptible(mutex); | ||
303 | if (*error != 0) | ||
304 | return NULL; | ||
305 | |||
306 | ret = list_named_find(head, name); | ||
307 | if (!ret) { | ||
308 | *error = -ENOENT; | ||
309 | up(mutex); | ||
310 | } | ||
311 | return ret; | ||
312 | } | ||
313 | |||
314 | #ifndef CONFIG_KMOD | ||
315 | #define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m)) | ||
316 | #else | ||
317 | static void * | ||
318 | find_inlist_lock(struct list_head *head, const char *name, const char *prefix, | ||
319 | int *error, struct semaphore *mutex) | ||
320 | { | ||
321 | void *ret; | ||
322 | |||
323 | ret = find_inlist_lock_noload(head, name, error, mutex); | ||
324 | if (!ret) { | ||
325 | request_module("%s%s", prefix, name); | ||
326 | ret = find_inlist_lock_noload(head, name, error, mutex); | ||
327 | } | ||
328 | return ret; | ||
329 | } | ||
330 | #endif | ||
331 | |||
332 | static inline struct ebt_table * | ||
333 | find_table_lock(const char *name, int *error, struct semaphore *mutex) | ||
334 | { | ||
335 | return find_inlist_lock(&ebt_tables, name, "ebtable_", error, mutex); | ||
336 | } | ||
337 | |||
338 | static inline struct ebt_match * | ||
339 | find_match_lock(const char *name, int *error, struct semaphore *mutex) | ||
340 | { | ||
341 | return find_inlist_lock(&ebt_matches, name, "ebt_", error, mutex); | ||
342 | } | ||
343 | |||
344 | static inline struct ebt_watcher * | ||
345 | find_watcher_lock(const char *name, int *error, struct semaphore *mutex) | ||
346 | { | ||
347 | return find_inlist_lock(&ebt_watchers, name, "ebt_", error, mutex); | ||
348 | } | ||
349 | |||
350 | static inline struct ebt_target * | ||
351 | find_target_lock(const char *name, int *error, struct semaphore *mutex) | ||
352 | { | ||
353 | return find_inlist_lock(&ebt_targets, name, "ebt_", error, mutex); | ||
354 | } | ||
355 | |||
356 | static inline int | ||
357 | ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e, | ||
358 | const char *name, unsigned int hookmask, unsigned int *cnt) | ||
359 | { | ||
360 | struct ebt_match *match; | ||
361 | int ret; | ||
362 | |||
363 | if (((char *)m) + m->match_size + sizeof(struct ebt_entry_match) > | ||
364 | ((char *)e) + e->watchers_offset) | ||
365 | return -EINVAL; | ||
366 | match = find_match_lock(m->u.name, &ret, &ebt_mutex); | ||
367 | if (!match) | ||
368 | return ret; | ||
369 | m->u.match = match; | ||
370 | if (!try_module_get(match->me)) { | ||
371 | up(&ebt_mutex); | ||
372 | return -ENOENT; | ||
373 | } | ||
374 | up(&ebt_mutex); | ||
375 | if (match->check && | ||
376 | match->check(name, hookmask, e, m->data, m->match_size) != 0) { | ||
377 | BUGPRINT("match->check failed\n"); | ||
378 | module_put(match->me); | ||
379 | return -EINVAL; | ||
380 | } | ||
381 | (*cnt)++; | ||
382 | return 0; | ||
383 | } | ||
384 | |||
385 | static inline int | ||
386 | ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e, | ||
387 | const char *name, unsigned int hookmask, unsigned int *cnt) | ||
388 | { | ||
389 | struct ebt_watcher *watcher; | ||
390 | int ret; | ||
391 | |||
392 | if (((char *)w) + w->watcher_size + sizeof(struct ebt_entry_watcher) > | ||
393 | ((char *)e) + e->target_offset) | ||
394 | return -EINVAL; | ||
395 | watcher = find_watcher_lock(w->u.name, &ret, &ebt_mutex); | ||
396 | if (!watcher) | ||
397 | return ret; | ||
398 | w->u.watcher = watcher; | ||
399 | if (!try_module_get(watcher->me)) { | ||
400 | up(&ebt_mutex); | ||
401 | return -ENOENT; | ||
402 | } | ||
403 | up(&ebt_mutex); | ||
404 | if (watcher->check && | ||
405 | watcher->check(name, hookmask, e, w->data, w->watcher_size) != 0) { | ||
406 | BUGPRINT("watcher->check failed\n"); | ||
407 | module_put(watcher->me); | ||
408 | return -EINVAL; | ||
409 | } | ||
410 | (*cnt)++; | ||
411 | return 0; | ||
412 | } | ||
413 | |||
414 | /* | ||
415 | * this one is very careful, as it is the first function | ||
416 | * to parse the userspace data | ||
417 | */ | ||
418 | static inline int | ||
419 | ebt_check_entry_size_and_hooks(struct ebt_entry *e, | ||
420 | struct ebt_table_info *newinfo, char *base, char *limit, | ||
421 | struct ebt_entries **hook_entries, unsigned int *n, unsigned int *cnt, | ||
422 | unsigned int *totalcnt, unsigned int *udc_cnt, unsigned int valid_hooks) | ||
423 | { | ||
424 | int i; | ||
425 | |||
426 | for (i = 0; i < NF_BR_NUMHOOKS; i++) { | ||
427 | if ((valid_hooks & (1 << i)) == 0) | ||
428 | continue; | ||
429 | if ( (char *)hook_entries[i] - base == | ||
430 | (char *)e - newinfo->entries) | ||
431 | break; | ||
432 | } | ||
433 | /* beginning of a new chain | ||
434 | if i == NF_BR_NUMHOOKS it must be a user defined chain */ | ||
435 | if (i != NF_BR_NUMHOOKS || !(e->bitmask & EBT_ENTRY_OR_ENTRIES)) { | ||
436 | if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) != 0) { | ||
437 | /* we make userspace set this right, | ||
438 | so there is no misunderstanding */ | ||
439 | BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set " | ||
440 | "in distinguisher\n"); | ||
441 | return -EINVAL; | ||
442 | } | ||
443 | /* this checks if the previous chain has as many entries | ||
444 | as it said it has */ | ||
445 | if (*n != *cnt) { | ||
446 | BUGPRINT("nentries does not equal the nr of entries " | ||
447 | "in the chain\n"); | ||
448 | return -EINVAL; | ||
449 | } | ||
450 | /* before we look at the struct, be sure it is not too big */ | ||
451 | if ((char *)hook_entries[i] + sizeof(struct ebt_entries) | ||
452 | > limit) { | ||
453 | BUGPRINT("entries_size too small\n"); | ||
454 | return -EINVAL; | ||
455 | } | ||
456 | if (((struct ebt_entries *)e)->policy != EBT_DROP && | ||
457 | ((struct ebt_entries *)e)->policy != EBT_ACCEPT) { | ||
458 | /* only RETURN from udc */ | ||
459 | if (i != NF_BR_NUMHOOKS || | ||
460 | ((struct ebt_entries *)e)->policy != EBT_RETURN) { | ||
461 | BUGPRINT("bad policy\n"); | ||
462 | return -EINVAL; | ||
463 | } | ||
464 | } | ||
465 | if (i == NF_BR_NUMHOOKS) /* it's a user defined chain */ | ||
466 | (*udc_cnt)++; | ||
467 | else | ||
468 | newinfo->hook_entry[i] = (struct ebt_entries *)e; | ||
469 | if (((struct ebt_entries *)e)->counter_offset != *totalcnt) { | ||
470 | BUGPRINT("counter_offset != totalcnt"); | ||
471 | return -EINVAL; | ||
472 | } | ||
473 | *n = ((struct ebt_entries *)e)->nentries; | ||
474 | *cnt = 0; | ||
475 | return 0; | ||
476 | } | ||
477 | /* a plain old entry, heh */ | ||
478 | if (sizeof(struct ebt_entry) > e->watchers_offset || | ||
479 | e->watchers_offset > e->target_offset || | ||
480 | e->target_offset >= e->next_offset) { | ||
481 | BUGPRINT("entry offsets not in right order\n"); | ||
482 | return -EINVAL; | ||
483 | } | ||
484 | /* this is not checked anywhere else */ | ||
485 | if (e->next_offset - e->target_offset < sizeof(struct ebt_entry_target)) { | ||
486 | BUGPRINT("target size too small\n"); | ||
487 | return -EINVAL; | ||
488 | } | ||
489 | |||
490 | (*cnt)++; | ||
491 | (*totalcnt)++; | ||
492 | return 0; | ||
493 | } | ||
494 | |||
495 | struct ebt_cl_stack | ||
496 | { | ||
497 | struct ebt_chainstack cs; | ||
498 | int from; | ||
499 | unsigned int hookmask; | ||
500 | }; | ||
501 | |||
502 | /* | ||
503 | * we need these positions to check that the jumps to a different part of the | ||
504 | * entries is a jump to the beginning of a new chain. | ||
505 | */ | ||
506 | static inline int | ||
507 | ebt_get_udc_positions(struct ebt_entry *e, struct ebt_table_info *newinfo, | ||
508 | struct ebt_entries **hook_entries, unsigned int *n, unsigned int valid_hooks, | ||
509 | struct ebt_cl_stack *udc) | ||
510 | { | ||
511 | int i; | ||
512 | |||
513 | /* we're only interested in chain starts */ | ||
514 | if (e->bitmask & EBT_ENTRY_OR_ENTRIES) | ||
515 | return 0; | ||
516 | for (i = 0; i < NF_BR_NUMHOOKS; i++) { | ||
517 | if ((valid_hooks & (1 << i)) == 0) | ||
518 | continue; | ||
519 | if (newinfo->hook_entry[i] == (struct ebt_entries *)e) | ||
520 | break; | ||
521 | } | ||
522 | /* only care about udc */ | ||
523 | if (i != NF_BR_NUMHOOKS) | ||
524 | return 0; | ||
525 | |||
526 | udc[*n].cs.chaininfo = (struct ebt_entries *)e; | ||
527 | /* these initialisations are depended on later in check_chainloops() */ | ||
528 | udc[*n].cs.n = 0; | ||
529 | udc[*n].hookmask = 0; | ||
530 | |||
531 | (*n)++; | ||
532 | return 0; | ||
533 | } | ||
534 | |||
535 | static inline int | ||
536 | ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i) | ||
537 | { | ||
538 | if (i && (*i)-- == 0) | ||
539 | return 1; | ||
540 | if (m->u.match->destroy) | ||
541 | m->u.match->destroy(m->data, m->match_size); | ||
542 | module_put(m->u.match->me); | ||
543 | |||
544 | return 0; | ||
545 | } | ||
546 | |||
547 | static inline int | ||
548 | ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i) | ||
549 | { | ||
550 | if (i && (*i)-- == 0) | ||
551 | return 1; | ||
552 | if (w->u.watcher->destroy) | ||
553 | w->u.watcher->destroy(w->data, w->watcher_size); | ||
554 | module_put(w->u.watcher->me); | ||
555 | |||
556 | return 0; | ||
557 | } | ||
558 | |||
559 | static inline int | ||
560 | ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt) | ||
561 | { | ||
562 | struct ebt_entry_target *t; | ||
563 | |||
564 | if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0) | ||
565 | return 0; | ||
566 | /* we're done */ | ||
567 | if (cnt && (*cnt)-- == 0) | ||
568 | return 1; | ||
569 | EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL); | ||
570 | EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL); | ||
571 | t = (struct ebt_entry_target *)(((char *)e) + e->target_offset); | ||
572 | if (t->u.target->destroy) | ||
573 | t->u.target->destroy(t->data, t->target_size); | ||
574 | module_put(t->u.target->me); | ||
575 | |||
576 | return 0; | ||
577 | } | ||
578 | |||
579 | static inline int | ||
580 | ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo, | ||
581 | const char *name, unsigned int *cnt, unsigned int valid_hooks, | ||
582 | struct ebt_cl_stack *cl_s, unsigned int udc_cnt) | ||
583 | { | ||
584 | struct ebt_entry_target *t; | ||
585 | struct ebt_target *target; | ||
586 | unsigned int i, j, hook = 0, hookmask = 0; | ||
587 | int ret; | ||
588 | |||
589 | /* don't mess with the struct ebt_entries */ | ||
590 | if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0) | ||
591 | return 0; | ||
592 | |||
593 | if (e->bitmask & ~EBT_F_MASK) { | ||
594 | BUGPRINT("Unknown flag for bitmask\n"); | ||
595 | return -EINVAL; | ||
596 | } | ||
597 | if (e->invflags & ~EBT_INV_MASK) { | ||
598 | BUGPRINT("Unknown flag for inv bitmask\n"); | ||
599 | return -EINVAL; | ||
600 | } | ||
601 | if ( (e->bitmask & EBT_NOPROTO) && (e->bitmask & EBT_802_3) ) { | ||
602 | BUGPRINT("NOPROTO & 802_3 not allowed\n"); | ||
603 | return -EINVAL; | ||
604 | } | ||
605 | /* what hook do we belong to? */ | ||
606 | for (i = 0; i < NF_BR_NUMHOOKS; i++) { | ||
607 | if ((valid_hooks & (1 << i)) == 0) | ||
608 | continue; | ||
609 | if ((char *)newinfo->hook_entry[i] < (char *)e) | ||
610 | hook = i; | ||
611 | else | ||
612 | break; | ||
613 | } | ||
614 | /* (1 << NF_BR_NUMHOOKS) tells the check functions the rule is on | ||
615 | a base chain */ | ||
616 | if (i < NF_BR_NUMHOOKS) | ||
617 | hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS); | ||
618 | else { | ||
619 | for (i = 0; i < udc_cnt; i++) | ||
620 | if ((char *)(cl_s[i].cs.chaininfo) > (char *)e) | ||
621 | break; | ||
622 | if (i == 0) | ||
623 | hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS); | ||
624 | else | ||
625 | hookmask = cl_s[i - 1].hookmask; | ||
626 | } | ||
627 | i = 0; | ||
628 | ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hookmask, &i); | ||
629 | if (ret != 0) | ||
630 | goto cleanup_matches; | ||
631 | j = 0; | ||
632 | ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hookmask, &j); | ||
633 | if (ret != 0) | ||
634 | goto cleanup_watchers; | ||
635 | t = (struct ebt_entry_target *)(((char *)e) + e->target_offset); | ||
636 | target = find_target_lock(t->u.name, &ret, &ebt_mutex); | ||
637 | if (!target) | ||
638 | goto cleanup_watchers; | ||
639 | if (!try_module_get(target->me)) { | ||
640 | up(&ebt_mutex); | ||
641 | ret = -ENOENT; | ||
642 | goto cleanup_watchers; | ||
643 | } | ||
644 | up(&ebt_mutex); | ||
645 | |||
646 | t->u.target = target; | ||
647 | if (t->u.target == &ebt_standard_target) { | ||
648 | if (e->target_offset + sizeof(struct ebt_standard_target) > | ||
649 | e->next_offset) { | ||
650 | BUGPRINT("Standard target size too big\n"); | ||
651 | ret = -EFAULT; | ||
652 | goto cleanup_watchers; | ||
653 | } | ||
654 | if (((struct ebt_standard_target *)t)->verdict < | ||
655 | -NUM_STANDARD_TARGETS) { | ||
656 | BUGPRINT("Invalid standard target\n"); | ||
657 | ret = -EFAULT; | ||
658 | goto cleanup_watchers; | ||
659 | } | ||
660 | } else if ((e->target_offset + t->target_size + | ||
661 | sizeof(struct ebt_entry_target) > e->next_offset) || | ||
662 | (t->u.target->check && | ||
663 | t->u.target->check(name, hookmask, e, t->data, t->target_size) != 0)){ | ||
664 | module_put(t->u.target->me); | ||
665 | ret = -EFAULT; | ||
666 | goto cleanup_watchers; | ||
667 | } | ||
668 | (*cnt)++; | ||
669 | return 0; | ||
670 | cleanup_watchers: | ||
671 | EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, &j); | ||
672 | cleanup_matches: | ||
673 | EBT_MATCH_ITERATE(e, ebt_cleanup_match, &i); | ||
674 | return ret; | ||
675 | } | ||
676 | |||
677 | /* | ||
678 | * checks for loops and sets the hook mask for udc | ||
679 | * the hook mask for udc tells us from which base chains the udc can be | ||
680 | * accessed. This mask is a parameter to the check() functions of the extensions | ||
681 | */ | ||
682 | static int check_chainloops(struct ebt_entries *chain, struct ebt_cl_stack *cl_s, | ||
683 | unsigned int udc_cnt, unsigned int hooknr, char *base) | ||
684 | { | ||
685 | int i, chain_nr = -1, pos = 0, nentries = chain->nentries, verdict; | ||
686 | struct ebt_entry *e = (struct ebt_entry *)chain->data; | ||
687 | struct ebt_entry_target *t; | ||
688 | |||
689 | while (pos < nentries || chain_nr != -1) { | ||
690 | /* end of udc, go back one 'recursion' step */ | ||
691 | if (pos == nentries) { | ||
692 | /* put back values of the time when this chain was called */ | ||
693 | e = cl_s[chain_nr].cs.e; | ||
694 | if (cl_s[chain_nr].from != -1) | ||
695 | nentries = | ||
696 | cl_s[cl_s[chain_nr].from].cs.chaininfo->nentries; | ||
697 | else | ||
698 | nentries = chain->nentries; | ||
699 | pos = cl_s[chain_nr].cs.n; | ||
700 | /* make sure we won't see a loop that isn't one */ | ||
701 | cl_s[chain_nr].cs.n = 0; | ||
702 | chain_nr = cl_s[chain_nr].from; | ||
703 | if (pos == nentries) | ||
704 | continue; | ||
705 | } | ||
706 | t = (struct ebt_entry_target *) | ||
707 | (((char *)e) + e->target_offset); | ||
708 | if (strcmp(t->u.name, EBT_STANDARD_TARGET)) | ||
709 | goto letscontinue; | ||
710 | if (e->target_offset + sizeof(struct ebt_standard_target) > | ||
711 | e->next_offset) { | ||
712 | BUGPRINT("Standard target size too big\n"); | ||
713 | return -1; | ||
714 | } | ||
715 | verdict = ((struct ebt_standard_target *)t)->verdict; | ||
716 | if (verdict >= 0) { /* jump to another chain */ | ||
717 | struct ebt_entries *hlp2 = | ||
718 | (struct ebt_entries *)(base + verdict); | ||
719 | for (i = 0; i < udc_cnt; i++) | ||
720 | if (hlp2 == cl_s[i].cs.chaininfo) | ||
721 | break; | ||
722 | /* bad destination or loop */ | ||
723 | if (i == udc_cnt) { | ||
724 | BUGPRINT("bad destination\n"); | ||
725 | return -1; | ||
726 | } | ||
727 | if (cl_s[i].cs.n) { | ||
728 | BUGPRINT("loop\n"); | ||
729 | return -1; | ||
730 | } | ||
731 | /* this can't be 0, so the above test is correct */ | ||
732 | cl_s[i].cs.n = pos + 1; | ||
733 | pos = 0; | ||
734 | cl_s[i].cs.e = ((void *)e + e->next_offset); | ||
735 | e = (struct ebt_entry *)(hlp2->data); | ||
736 | nentries = hlp2->nentries; | ||
737 | cl_s[i].from = chain_nr; | ||
738 | chain_nr = i; | ||
739 | /* this udc is accessible from the base chain for hooknr */ | ||
740 | cl_s[i].hookmask |= (1 << hooknr); | ||
741 | continue; | ||
742 | } | ||
743 | letscontinue: | ||
744 | e = (void *)e + e->next_offset; | ||
745 | pos++; | ||
746 | } | ||
747 | return 0; | ||
748 | } | ||
749 | |||
750 | /* do the parsing of the table/chains/entries/matches/watchers/targets, heh */ | ||
751 | static int translate_table(struct ebt_replace *repl, | ||
752 | struct ebt_table_info *newinfo) | ||
753 | { | ||
754 | unsigned int i, j, k, udc_cnt; | ||
755 | int ret; | ||
756 | struct ebt_cl_stack *cl_s = NULL; /* used in the checking for chain loops */ | ||
757 | |||
758 | i = 0; | ||
759 | while (i < NF_BR_NUMHOOKS && !(repl->valid_hooks & (1 << i))) | ||
760 | i++; | ||
761 | if (i == NF_BR_NUMHOOKS) { | ||
762 | BUGPRINT("No valid hooks specified\n"); | ||
763 | return -EINVAL; | ||
764 | } | ||
765 | if (repl->hook_entry[i] != (struct ebt_entries *)repl->entries) { | ||
766 | BUGPRINT("Chains don't start at beginning\n"); | ||
767 | return -EINVAL; | ||
768 | } | ||
769 | /* make sure chains are ordered after each other in same order | ||
770 | as their corresponding hooks */ | ||
771 | for (j = i + 1; j < NF_BR_NUMHOOKS; j++) { | ||
772 | if (!(repl->valid_hooks & (1 << j))) | ||
773 | continue; | ||
774 | if ( repl->hook_entry[j] <= repl->hook_entry[i] ) { | ||
775 | BUGPRINT("Hook order must be followed\n"); | ||
776 | return -EINVAL; | ||
777 | } | ||
778 | i = j; | ||
779 | } | ||
780 | |||
781 | for (i = 0; i < NF_BR_NUMHOOKS; i++) | ||
782 | newinfo->hook_entry[i] = NULL; | ||
783 | |||
784 | newinfo->entries_size = repl->entries_size; | ||
785 | newinfo->nentries = repl->nentries; | ||
786 | |||
787 | /* do some early checkings and initialize some things */ | ||
788 | i = 0; /* holds the expected nr. of entries for the chain */ | ||
789 | j = 0; /* holds the up to now counted entries for the chain */ | ||
790 | k = 0; /* holds the total nr. of entries, should equal | ||
791 | newinfo->nentries afterwards */ | ||
792 | udc_cnt = 0; /* will hold the nr. of user defined chains (udc) */ | ||
793 | ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size, | ||
794 | ebt_check_entry_size_and_hooks, newinfo, repl->entries, | ||
795 | repl->entries + repl->entries_size, repl->hook_entry, &i, &j, &k, | ||
796 | &udc_cnt, repl->valid_hooks); | ||
797 | |||
798 | if (ret != 0) | ||
799 | return ret; | ||
800 | |||
801 | if (i != j) { | ||
802 | BUGPRINT("nentries does not equal the nr of entries in the " | ||
803 | "(last) chain\n"); | ||
804 | return -EINVAL; | ||
805 | } | ||
806 | if (k != newinfo->nentries) { | ||
807 | BUGPRINT("Total nentries is wrong\n"); | ||
808 | return -EINVAL; | ||
809 | } | ||
810 | |||
811 | /* check if all valid hooks have a chain */ | ||
812 | for (i = 0; i < NF_BR_NUMHOOKS; i++) { | ||
813 | if (newinfo->hook_entry[i] == NULL && | ||
814 | (repl->valid_hooks & (1 << i))) { | ||
815 | BUGPRINT("Valid hook without chain\n"); | ||
816 | return -EINVAL; | ||
817 | } | ||
818 | } | ||
819 | |||
820 | /* get the location of the udc, put them in an array | ||
821 | while we're at it, allocate the chainstack */ | ||
822 | if (udc_cnt) { | ||
823 | /* this will get free'd in do_replace()/ebt_register_table() | ||
824 | if an error occurs */ | ||
825 | newinfo->chainstack = (struct ebt_chainstack **) | ||
826 | vmalloc(num_possible_cpus() * sizeof(struct ebt_chainstack)); | ||
827 | if (!newinfo->chainstack) | ||
828 | return -ENOMEM; | ||
829 | for (i = 0; i < num_possible_cpus(); i++) { | ||
830 | newinfo->chainstack[i] = | ||
831 | vmalloc(udc_cnt * sizeof(struct ebt_chainstack)); | ||
832 | if (!newinfo->chainstack[i]) { | ||
833 | while (i) | ||
834 | vfree(newinfo->chainstack[--i]); | ||
835 | vfree(newinfo->chainstack); | ||
836 | newinfo->chainstack = NULL; | ||
837 | return -ENOMEM; | ||
838 | } | ||
839 | } | ||
840 | |||
841 | cl_s = (struct ebt_cl_stack *) | ||
842 | vmalloc(udc_cnt * sizeof(struct ebt_cl_stack)); | ||
843 | if (!cl_s) | ||
844 | return -ENOMEM; | ||
845 | i = 0; /* the i'th udc */ | ||
846 | EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size, | ||
847 | ebt_get_udc_positions, newinfo, repl->hook_entry, &i, | ||
848 | repl->valid_hooks, cl_s); | ||
849 | /* sanity check */ | ||
850 | if (i != udc_cnt) { | ||
851 | BUGPRINT("i != udc_cnt\n"); | ||
852 | vfree(cl_s); | ||
853 | return -EFAULT; | ||
854 | } | ||
855 | } | ||
856 | |||
857 | /* Check for loops */ | ||
858 | for (i = 0; i < NF_BR_NUMHOOKS; i++) | ||
859 | if (repl->valid_hooks & (1 << i)) | ||
860 | if (check_chainloops(newinfo->hook_entry[i], | ||
861 | cl_s, udc_cnt, i, newinfo->entries)) { | ||
862 | if (cl_s) | ||
863 | vfree(cl_s); | ||
864 | return -EINVAL; | ||
865 | } | ||
866 | |||
867 | /* we now know the following (along with E=mc²): | ||
868 | - the nr of entries in each chain is right | ||
869 | - the size of the allocated space is right | ||
870 | - all valid hooks have a corresponding chain | ||
871 | - there are no loops | ||
872 | - wrong data can still be on the level of a single entry | ||
873 | - could be there are jumps to places that are not the | ||
874 | beginning of a chain. This can only occur in chains that | ||
875 | are not accessible from any base chains, so we don't care. */ | ||
876 | |||
877 | /* used to know what we need to clean up if something goes wrong */ | ||
878 | i = 0; | ||
879 | ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size, | ||
880 | ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks, | ||
881 | cl_s, udc_cnt); | ||
882 | if (ret != 0) { | ||
883 | EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size, | ||
884 | ebt_cleanup_entry, &i); | ||
885 | } | ||
886 | if (cl_s) | ||
887 | vfree(cl_s); | ||
888 | return ret; | ||
889 | } | ||
890 | |||
891 | /* called under write_lock */ | ||
892 | static void get_counters(struct ebt_counter *oldcounters, | ||
893 | struct ebt_counter *counters, unsigned int nentries) | ||
894 | { | ||
895 | int i, cpu; | ||
896 | struct ebt_counter *counter_base; | ||
897 | |||
898 | /* counters of cpu 0 */ | ||
899 | memcpy(counters, oldcounters, | ||
900 | sizeof(struct ebt_counter) * nentries); | ||
901 | /* add other counters to those of cpu 0 */ | ||
902 | for (cpu = 1; cpu < num_possible_cpus(); cpu++) { | ||
903 | counter_base = COUNTER_BASE(oldcounters, nentries, cpu); | ||
904 | for (i = 0; i < nentries; i++) { | ||
905 | counters[i].pcnt += counter_base[i].pcnt; | ||
906 | counters[i].bcnt += counter_base[i].bcnt; | ||
907 | } | ||
908 | } | ||
909 | } | ||
910 | |||
911 | /* replace the table */ | ||
912 | static int do_replace(void __user *user, unsigned int len) | ||
913 | { | ||
914 | int ret, i, countersize; | ||
915 | struct ebt_table_info *newinfo; | ||
916 | struct ebt_replace tmp; | ||
917 | struct ebt_table *t; | ||
918 | struct ebt_counter *counterstmp = NULL; | ||
919 | /* used to be able to unlock earlier */ | ||
920 | struct ebt_table_info *table; | ||
921 | |||
922 | if (copy_from_user(&tmp, user, sizeof(tmp)) != 0) | ||
923 | return -EFAULT; | ||
924 | |||
925 | if (len != sizeof(tmp) + tmp.entries_size) { | ||
926 | BUGPRINT("Wrong len argument\n"); | ||
927 | return -EINVAL; | ||
928 | } | ||
929 | |||
930 | if (tmp.entries_size == 0) { | ||
931 | BUGPRINT("Entries_size never zero\n"); | ||
932 | return -EINVAL; | ||
933 | } | ||
934 | countersize = COUNTER_OFFSET(tmp.nentries) * num_possible_cpus(); | ||
935 | newinfo = (struct ebt_table_info *) | ||
936 | vmalloc(sizeof(struct ebt_table_info) + countersize); | ||
937 | if (!newinfo) | ||
938 | return -ENOMEM; | ||
939 | |||
940 | if (countersize) | ||
941 | memset(newinfo->counters, 0, countersize); | ||
942 | |||
943 | newinfo->entries = (char *)vmalloc(tmp.entries_size); | ||
944 | if (!newinfo->entries) { | ||
945 | ret = -ENOMEM; | ||
946 | goto free_newinfo; | ||
947 | } | ||
948 | if (copy_from_user( | ||
949 | newinfo->entries, tmp.entries, tmp.entries_size) != 0) { | ||
950 | BUGPRINT("Couldn't copy entries from userspace\n"); | ||
951 | ret = -EFAULT; | ||
952 | goto free_entries; | ||
953 | } | ||
954 | |||
955 | /* the user wants counters back | ||
956 | the check on the size is done later, when we have the lock */ | ||
957 | if (tmp.num_counters) { | ||
958 | counterstmp = (struct ebt_counter *) | ||
959 | vmalloc(tmp.num_counters * sizeof(struct ebt_counter)); | ||
960 | if (!counterstmp) { | ||
961 | ret = -ENOMEM; | ||
962 | goto free_entries; | ||
963 | } | ||
964 | } | ||
965 | else | ||
966 | counterstmp = NULL; | ||
967 | |||
968 | /* this can get initialized by translate_table() */ | ||
969 | newinfo->chainstack = NULL; | ||
970 | ret = translate_table(&tmp, newinfo); | ||
971 | |||
972 | if (ret != 0) | ||
973 | goto free_counterstmp; | ||
974 | |||
975 | t = find_table_lock(tmp.name, &ret, &ebt_mutex); | ||
976 | if (!t) { | ||
977 | ret = -ENOENT; | ||
978 | goto free_iterate; | ||
979 | } | ||
980 | |||
981 | /* the table doesn't like it */ | ||
982 | if (t->check && (ret = t->check(newinfo, tmp.valid_hooks))) | ||
983 | goto free_unlock; | ||
984 | |||
985 | if (tmp.num_counters && tmp.num_counters != t->private->nentries) { | ||
986 | BUGPRINT("Wrong nr. of counters requested\n"); | ||
987 | ret = -EINVAL; | ||
988 | goto free_unlock; | ||
989 | } | ||
990 | |||
991 | /* we have the mutex lock, so no danger in reading this pointer */ | ||
992 | table = t->private; | ||
993 | /* make sure the table can only be rmmod'ed if it contains no rules */ | ||
994 | if (!table->nentries && newinfo->nentries && !try_module_get(t->me)) { | ||
995 | ret = -ENOENT; | ||
996 | goto free_unlock; | ||
997 | } else if (table->nentries && !newinfo->nentries) | ||
998 | module_put(t->me); | ||
999 | /* we need an atomic snapshot of the counters */ | ||
1000 | write_lock_bh(&t->lock); | ||
1001 | if (tmp.num_counters) | ||
1002 | get_counters(t->private->counters, counterstmp, | ||
1003 | t->private->nentries); | ||
1004 | |||
1005 | t->private = newinfo; | ||
1006 | write_unlock_bh(&t->lock); | ||
1007 | up(&ebt_mutex); | ||
1008 | /* so, a user can change the chains while having messed up her counter | ||
1009 | allocation. Only reason why this is done is because this way the lock | ||
1010 | is held only once, while this doesn't bring the kernel into a | ||
1011 | dangerous state. */ | ||
1012 | if (tmp.num_counters && | ||
1013 | copy_to_user(tmp.counters, counterstmp, | ||
1014 | tmp.num_counters * sizeof(struct ebt_counter))) { | ||
1015 | BUGPRINT("Couldn't copy counters to userspace\n"); | ||
1016 | ret = -EFAULT; | ||
1017 | } | ||
1018 | else | ||
1019 | ret = 0; | ||
1020 | |||
1021 | /* decrease module count and free resources */ | ||
1022 | EBT_ENTRY_ITERATE(table->entries, table->entries_size, | ||
1023 | ebt_cleanup_entry, NULL); | ||
1024 | |||
1025 | vfree(table->entries); | ||
1026 | if (table->chainstack) { | ||
1027 | for (i = 0; i < num_possible_cpus(); i++) | ||
1028 | vfree(table->chainstack[i]); | ||
1029 | vfree(table->chainstack); | ||
1030 | } | ||
1031 | vfree(table); | ||
1032 | |||
1033 | if (counterstmp) | ||
1034 | vfree(counterstmp); | ||
1035 | return ret; | ||
1036 | |||
1037 | free_unlock: | ||
1038 | up(&ebt_mutex); | ||
1039 | free_iterate: | ||
1040 | EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size, | ||
1041 | ebt_cleanup_entry, NULL); | ||
1042 | free_counterstmp: | ||
1043 | if (counterstmp) | ||
1044 | vfree(counterstmp); | ||
1045 | /* can be initialized in translate_table() */ | ||
1046 | if (newinfo->chainstack) { | ||
1047 | for (i = 0; i < num_possible_cpus(); i++) | ||
1048 | vfree(newinfo->chainstack[i]); | ||
1049 | vfree(newinfo->chainstack); | ||
1050 | } | ||
1051 | free_entries: | ||
1052 | if (newinfo->entries) | ||
1053 | vfree(newinfo->entries); | ||
1054 | free_newinfo: | ||
1055 | if (newinfo) | ||
1056 | vfree(newinfo); | ||
1057 | return ret; | ||
1058 | } | ||
1059 | |||
1060 | int ebt_register_target(struct ebt_target *target) | ||
1061 | { | ||
1062 | int ret; | ||
1063 | |||
1064 | ret = down_interruptible(&ebt_mutex); | ||
1065 | if (ret != 0) | ||
1066 | return ret; | ||
1067 | if (!list_named_insert(&ebt_targets, target)) { | ||
1068 | up(&ebt_mutex); | ||
1069 | return -EEXIST; | ||
1070 | } | ||
1071 | up(&ebt_mutex); | ||
1072 | |||
1073 | return 0; | ||
1074 | } | ||
1075 | |||
1076 | void ebt_unregister_target(struct ebt_target *target) | ||
1077 | { | ||
1078 | down(&ebt_mutex); | ||
1079 | LIST_DELETE(&ebt_targets, target); | ||
1080 | up(&ebt_mutex); | ||
1081 | } | ||
1082 | |||
1083 | int ebt_register_match(struct ebt_match *match) | ||
1084 | { | ||
1085 | int ret; | ||
1086 | |||
1087 | ret = down_interruptible(&ebt_mutex); | ||
1088 | if (ret != 0) | ||
1089 | return ret; | ||
1090 | if (!list_named_insert(&ebt_matches, match)) { | ||
1091 | up(&ebt_mutex); | ||
1092 | return -EEXIST; | ||
1093 | } | ||
1094 | up(&ebt_mutex); | ||
1095 | |||
1096 | return 0; | ||
1097 | } | ||
1098 | |||
1099 | void ebt_unregister_match(struct ebt_match *match) | ||
1100 | { | ||
1101 | down(&ebt_mutex); | ||
1102 | LIST_DELETE(&ebt_matches, match); | ||
1103 | up(&ebt_mutex); | ||
1104 | } | ||
1105 | |||
1106 | int ebt_register_watcher(struct ebt_watcher *watcher) | ||
1107 | { | ||
1108 | int ret; | ||
1109 | |||
1110 | ret = down_interruptible(&ebt_mutex); | ||
1111 | if (ret != 0) | ||
1112 | return ret; | ||
1113 | if (!list_named_insert(&ebt_watchers, watcher)) { | ||
1114 | up(&ebt_mutex); | ||
1115 | return -EEXIST; | ||
1116 | } | ||
1117 | up(&ebt_mutex); | ||
1118 | |||
1119 | return 0; | ||
1120 | } | ||
1121 | |||
1122 | void ebt_unregister_watcher(struct ebt_watcher *watcher) | ||
1123 | { | ||
1124 | down(&ebt_mutex); | ||
1125 | LIST_DELETE(&ebt_watchers, watcher); | ||
1126 | up(&ebt_mutex); | ||
1127 | } | ||
1128 | |||
1129 | int ebt_register_table(struct ebt_table *table) | ||
1130 | { | ||
1131 | struct ebt_table_info *newinfo; | ||
1132 | int ret, i, countersize; | ||
1133 | |||
1134 | if (!table || !table->table ||!table->table->entries || | ||
1135 | table->table->entries_size == 0 || | ||
1136 | table->table->counters || table->private) { | ||
1137 | BUGPRINT("Bad table data for ebt_register_table!!!\n"); | ||
1138 | return -EINVAL; | ||
1139 | } | ||
1140 | |||
1141 | countersize = COUNTER_OFFSET(table->table->nentries) * num_possible_cpus(); | ||
1142 | newinfo = (struct ebt_table_info *) | ||
1143 | vmalloc(sizeof(struct ebt_table_info) + countersize); | ||
1144 | ret = -ENOMEM; | ||
1145 | if (!newinfo) | ||
1146 | return -ENOMEM; | ||
1147 | |||
1148 | newinfo->entries = (char *)vmalloc(table->table->entries_size); | ||
1149 | if (!(newinfo->entries)) | ||
1150 | goto free_newinfo; | ||
1151 | |||
1152 | memcpy(newinfo->entries, table->table->entries, | ||
1153 | table->table->entries_size); | ||
1154 | |||
1155 | if (countersize) | ||
1156 | memset(newinfo->counters, 0, countersize); | ||
1157 | |||
1158 | /* fill in newinfo and parse the entries */ | ||
1159 | newinfo->chainstack = NULL; | ||
1160 | ret = translate_table(table->table, newinfo); | ||
1161 | if (ret != 0) { | ||
1162 | BUGPRINT("Translate_table failed\n"); | ||
1163 | goto free_chainstack; | ||
1164 | } | ||
1165 | |||
1166 | if (table->check && table->check(newinfo, table->valid_hooks)) { | ||
1167 | BUGPRINT("The table doesn't like its own initial data, lol\n"); | ||
1168 | return -EINVAL; | ||
1169 | } | ||
1170 | |||
1171 | table->private = newinfo; | ||
1172 | rwlock_init(&table->lock); | ||
1173 | ret = down_interruptible(&ebt_mutex); | ||
1174 | if (ret != 0) | ||
1175 | goto free_chainstack; | ||
1176 | |||
1177 | if (list_named_find(&ebt_tables, table->name)) { | ||
1178 | ret = -EEXIST; | ||
1179 | BUGPRINT("Table name already exists\n"); | ||
1180 | goto free_unlock; | ||
1181 | } | ||
1182 | |||
1183 | /* Hold a reference count if the chains aren't empty */ | ||
1184 | if (newinfo->nentries && !try_module_get(table->me)) { | ||
1185 | ret = -ENOENT; | ||
1186 | goto free_unlock; | ||
1187 | } | ||
1188 | list_prepend(&ebt_tables, table); | ||
1189 | up(&ebt_mutex); | ||
1190 | return 0; | ||
1191 | free_unlock: | ||
1192 | up(&ebt_mutex); | ||
1193 | free_chainstack: | ||
1194 | if (newinfo->chainstack) { | ||
1195 | for (i = 0; i < num_possible_cpus(); i++) | ||
1196 | vfree(newinfo->chainstack[i]); | ||
1197 | vfree(newinfo->chainstack); | ||
1198 | } | ||
1199 | vfree(newinfo->entries); | ||
1200 | free_newinfo: | ||
1201 | vfree(newinfo); | ||
1202 | return ret; | ||
1203 | } | ||
1204 | |||
1205 | void ebt_unregister_table(struct ebt_table *table) | ||
1206 | { | ||
1207 | int i; | ||
1208 | |||
1209 | if (!table) { | ||
1210 | BUGPRINT("Request to unregister NULL table!!!\n"); | ||
1211 | return; | ||
1212 | } | ||
1213 | down(&ebt_mutex); | ||
1214 | LIST_DELETE(&ebt_tables, table); | ||
1215 | up(&ebt_mutex); | ||
1216 | if (table->private->entries) | ||
1217 | vfree(table->private->entries); | ||
1218 | if (table->private->chainstack) { | ||
1219 | for (i = 0; i < num_possible_cpus(); i++) | ||
1220 | vfree(table->private->chainstack[i]); | ||
1221 | vfree(table->private->chainstack); | ||
1222 | } | ||
1223 | vfree(table->private); | ||
1224 | } | ||
1225 | |||
1226 | /* userspace just supplied us with counters */ | ||
1227 | static int update_counters(void __user *user, unsigned int len) | ||
1228 | { | ||
1229 | int i, ret; | ||
1230 | struct ebt_counter *tmp; | ||
1231 | struct ebt_replace hlp; | ||
1232 | struct ebt_table *t; | ||
1233 | |||
1234 | if (copy_from_user(&hlp, user, sizeof(hlp))) | ||
1235 | return -EFAULT; | ||
1236 | |||
1237 | if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter)) | ||
1238 | return -EINVAL; | ||
1239 | if (hlp.num_counters == 0) | ||
1240 | return -EINVAL; | ||
1241 | |||
1242 | if ( !(tmp = (struct ebt_counter *) | ||
1243 | vmalloc(hlp.num_counters * sizeof(struct ebt_counter))) ){ | ||
1244 | MEMPRINT("Update_counters && nomemory\n"); | ||
1245 | return -ENOMEM; | ||
1246 | } | ||
1247 | |||
1248 | t = find_table_lock(hlp.name, &ret, &ebt_mutex); | ||
1249 | if (!t) | ||
1250 | goto free_tmp; | ||
1251 | |||
1252 | if (hlp.num_counters != t->private->nentries) { | ||
1253 | BUGPRINT("Wrong nr of counters\n"); | ||
1254 | ret = -EINVAL; | ||
1255 | goto unlock_mutex; | ||
1256 | } | ||
1257 | |||
1258 | if ( copy_from_user(tmp, hlp.counters, | ||
1259 | hlp.num_counters * sizeof(struct ebt_counter)) ) { | ||
1260 | BUGPRINT("Updata_counters && !cfu\n"); | ||
1261 | ret = -EFAULT; | ||
1262 | goto unlock_mutex; | ||
1263 | } | ||
1264 | |||
1265 | /* we want an atomic add of the counters */ | ||
1266 | write_lock_bh(&t->lock); | ||
1267 | |||
1268 | /* we add to the counters of the first cpu */ | ||
1269 | for (i = 0; i < hlp.num_counters; i++) { | ||
1270 | t->private->counters[i].pcnt += tmp[i].pcnt; | ||
1271 | t->private->counters[i].bcnt += tmp[i].bcnt; | ||
1272 | } | ||
1273 | |||
1274 | write_unlock_bh(&t->lock); | ||
1275 | ret = 0; | ||
1276 | unlock_mutex: | ||
1277 | up(&ebt_mutex); | ||
1278 | free_tmp: | ||
1279 | vfree(tmp); | ||
1280 | return ret; | ||
1281 | } | ||
1282 | |||
1283 | static inline int ebt_make_matchname(struct ebt_entry_match *m, | ||
1284 | char *base, char *ubase) | ||
1285 | { | ||
1286 | char *hlp = ubase - base + (char *)m; | ||
1287 | if (copy_to_user(hlp, m->u.match->name, EBT_FUNCTION_MAXNAMELEN)) | ||
1288 | return -EFAULT; | ||
1289 | return 0; | ||
1290 | } | ||
1291 | |||
1292 | static inline int ebt_make_watchername(struct ebt_entry_watcher *w, | ||
1293 | char *base, char *ubase) | ||
1294 | { | ||
1295 | char *hlp = ubase - base + (char *)w; | ||
1296 | if (copy_to_user(hlp , w->u.watcher->name, EBT_FUNCTION_MAXNAMELEN)) | ||
1297 | return -EFAULT; | ||
1298 | return 0; | ||
1299 | } | ||
1300 | |||
1301 | static inline int ebt_make_names(struct ebt_entry *e, char *base, char *ubase) | ||
1302 | { | ||
1303 | int ret; | ||
1304 | char *hlp; | ||
1305 | struct ebt_entry_target *t; | ||
1306 | |||
1307 | if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0) | ||
1308 | return 0; | ||
1309 | |||
1310 | hlp = ubase - base + (char *)e + e->target_offset; | ||
1311 | t = (struct ebt_entry_target *)(((char *)e) + e->target_offset); | ||
1312 | |||
1313 | ret = EBT_MATCH_ITERATE(e, ebt_make_matchname, base, ubase); | ||
1314 | if (ret != 0) | ||
1315 | return ret; | ||
1316 | ret = EBT_WATCHER_ITERATE(e, ebt_make_watchername, base, ubase); | ||
1317 | if (ret != 0) | ||
1318 | return ret; | ||
1319 | if (copy_to_user(hlp, t->u.target->name, EBT_FUNCTION_MAXNAMELEN)) | ||
1320 | return -EFAULT; | ||
1321 | return 0; | ||
1322 | } | ||
1323 | |||
1324 | /* called with ebt_mutex down */ | ||
1325 | static int copy_everything_to_user(struct ebt_table *t, void __user *user, | ||
1326 | int *len, int cmd) | ||
1327 | { | ||
1328 | struct ebt_replace tmp; | ||
1329 | struct ebt_counter *counterstmp, *oldcounters; | ||
1330 | unsigned int entries_size, nentries; | ||
1331 | char *entries; | ||
1332 | |||
1333 | if (cmd == EBT_SO_GET_ENTRIES) { | ||
1334 | entries_size = t->private->entries_size; | ||
1335 | nentries = t->private->nentries; | ||
1336 | entries = t->private->entries; | ||
1337 | oldcounters = t->private->counters; | ||
1338 | } else { | ||
1339 | entries_size = t->table->entries_size; | ||
1340 | nentries = t->table->nentries; | ||
1341 | entries = t->table->entries; | ||
1342 | oldcounters = t->table->counters; | ||
1343 | } | ||
1344 | |||
1345 | if (copy_from_user(&tmp, user, sizeof(tmp))) { | ||
1346 | BUGPRINT("Cfu didn't work\n"); | ||
1347 | return -EFAULT; | ||
1348 | } | ||
1349 | |||
1350 | if (*len != sizeof(struct ebt_replace) + entries_size + | ||
1351 | (tmp.num_counters? nentries * sizeof(struct ebt_counter): 0)) { | ||
1352 | BUGPRINT("Wrong size\n"); | ||
1353 | return -EINVAL; | ||
1354 | } | ||
1355 | |||
1356 | if (tmp.nentries != nentries) { | ||
1357 | BUGPRINT("Nentries wrong\n"); | ||
1358 | return -EINVAL; | ||
1359 | } | ||
1360 | |||
1361 | if (tmp.entries_size != entries_size) { | ||
1362 | BUGPRINT("Wrong size\n"); | ||
1363 | return -EINVAL; | ||
1364 | } | ||
1365 | |||
1366 | /* userspace might not need the counters */ | ||
1367 | if (tmp.num_counters) { | ||
1368 | if (tmp.num_counters != nentries) { | ||
1369 | BUGPRINT("Num_counters wrong\n"); | ||
1370 | return -EINVAL; | ||
1371 | } | ||
1372 | counterstmp = (struct ebt_counter *) | ||
1373 | vmalloc(nentries * sizeof(struct ebt_counter)); | ||
1374 | if (!counterstmp) { | ||
1375 | MEMPRINT("Couldn't copy counters, out of memory\n"); | ||
1376 | return -ENOMEM; | ||
1377 | } | ||
1378 | write_lock_bh(&t->lock); | ||
1379 | get_counters(oldcounters, counterstmp, nentries); | ||
1380 | write_unlock_bh(&t->lock); | ||
1381 | |||
1382 | if (copy_to_user(tmp.counters, counterstmp, | ||
1383 | nentries * sizeof(struct ebt_counter))) { | ||
1384 | BUGPRINT("Couldn't copy counters to userspace\n"); | ||
1385 | vfree(counterstmp); | ||
1386 | return -EFAULT; | ||
1387 | } | ||
1388 | vfree(counterstmp); | ||
1389 | } | ||
1390 | |||
1391 | if (copy_to_user(tmp.entries, entries, entries_size)) { | ||
1392 | BUGPRINT("Couldn't copy entries to userspace\n"); | ||
1393 | return -EFAULT; | ||
1394 | } | ||
1395 | /* set the match/watcher/target names right */ | ||
1396 | return EBT_ENTRY_ITERATE(entries, entries_size, | ||
1397 | ebt_make_names, entries, tmp.entries); | ||
1398 | } | ||
1399 | |||
1400 | static int do_ebt_set_ctl(struct sock *sk, | ||
1401 | int cmd, void __user *user, unsigned int len) | ||
1402 | { | ||
1403 | int ret; | ||
1404 | |||
1405 | switch(cmd) { | ||
1406 | case EBT_SO_SET_ENTRIES: | ||
1407 | ret = do_replace(user, len); | ||
1408 | break; | ||
1409 | case EBT_SO_SET_COUNTERS: | ||
1410 | ret = update_counters(user, len); | ||
1411 | break; | ||
1412 | default: | ||
1413 | ret = -EINVAL; | ||
1414 | } | ||
1415 | return ret; | ||
1416 | } | ||
1417 | |||
1418 | static int do_ebt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) | ||
1419 | { | ||
1420 | int ret; | ||
1421 | struct ebt_replace tmp; | ||
1422 | struct ebt_table *t; | ||
1423 | |||
1424 | if (copy_from_user(&tmp, user, sizeof(tmp))) | ||
1425 | return -EFAULT; | ||
1426 | |||
1427 | t = find_table_lock(tmp.name, &ret, &ebt_mutex); | ||
1428 | if (!t) | ||
1429 | return ret; | ||
1430 | |||
1431 | switch(cmd) { | ||
1432 | case EBT_SO_GET_INFO: | ||
1433 | case EBT_SO_GET_INIT_INFO: | ||
1434 | if (*len != sizeof(struct ebt_replace)){ | ||
1435 | ret = -EINVAL; | ||
1436 | up(&ebt_mutex); | ||
1437 | break; | ||
1438 | } | ||
1439 | if (cmd == EBT_SO_GET_INFO) { | ||
1440 | tmp.nentries = t->private->nentries; | ||
1441 | tmp.entries_size = t->private->entries_size; | ||
1442 | tmp.valid_hooks = t->valid_hooks; | ||
1443 | } else { | ||
1444 | tmp.nentries = t->table->nentries; | ||
1445 | tmp.entries_size = t->table->entries_size; | ||
1446 | tmp.valid_hooks = t->table->valid_hooks; | ||
1447 | } | ||
1448 | up(&ebt_mutex); | ||
1449 | if (copy_to_user(user, &tmp, *len) != 0){ | ||
1450 | BUGPRINT("c2u Didn't work\n"); | ||
1451 | ret = -EFAULT; | ||
1452 | break; | ||
1453 | } | ||
1454 | ret = 0; | ||
1455 | break; | ||
1456 | |||
1457 | case EBT_SO_GET_ENTRIES: | ||
1458 | case EBT_SO_GET_INIT_ENTRIES: | ||
1459 | ret = copy_everything_to_user(t, user, len, cmd); | ||
1460 | up(&ebt_mutex); | ||
1461 | break; | ||
1462 | |||
1463 | default: | ||
1464 | up(&ebt_mutex); | ||
1465 | ret = -EINVAL; | ||
1466 | } | ||
1467 | |||
1468 | return ret; | ||
1469 | } | ||
1470 | |||
1471 | static struct nf_sockopt_ops ebt_sockopts = | ||
1472 | { { NULL, NULL }, PF_INET, EBT_BASE_CTL, EBT_SO_SET_MAX + 1, do_ebt_set_ctl, | ||
1473 | EBT_BASE_CTL, EBT_SO_GET_MAX + 1, do_ebt_get_ctl, 0, NULL | ||
1474 | }; | ||
1475 | |||
1476 | static int __init init(void) | ||
1477 | { | ||
1478 | int ret; | ||
1479 | |||
1480 | down(&ebt_mutex); | ||
1481 | list_named_insert(&ebt_targets, &ebt_standard_target); | ||
1482 | up(&ebt_mutex); | ||
1483 | if ((ret = nf_register_sockopt(&ebt_sockopts)) < 0) | ||
1484 | return ret; | ||
1485 | |||
1486 | printk(KERN_NOTICE "Ebtables v2.0 registered\n"); | ||
1487 | return 0; | ||
1488 | } | ||
1489 | |||
1490 | static void __exit fini(void) | ||
1491 | { | ||
1492 | nf_unregister_sockopt(&ebt_sockopts); | ||
1493 | printk(KERN_NOTICE "Ebtables v2.0 unregistered\n"); | ||
1494 | } | ||
1495 | |||
1496 | EXPORT_SYMBOL(ebt_register_table); | ||
1497 | EXPORT_SYMBOL(ebt_unregister_table); | ||
1498 | EXPORT_SYMBOL(ebt_register_match); | ||
1499 | EXPORT_SYMBOL(ebt_unregister_match); | ||
1500 | EXPORT_SYMBOL(ebt_register_watcher); | ||
1501 | EXPORT_SYMBOL(ebt_unregister_watcher); | ||
1502 | EXPORT_SYMBOL(ebt_register_target); | ||
1503 | EXPORT_SYMBOL(ebt_unregister_target); | ||
1504 | EXPORT_SYMBOL(ebt_do_table); | ||
1505 | module_init(init); | ||
1506 | module_exit(fini); | ||
1507 | MODULE_LICENSE("GPL"); | ||