diff options
Diffstat (limited to 'net/802')
-rw-r--r-- | net/802/Kconfig | 7 | ||||
-rw-r--r-- | net/802/Makefile | 2 | ||||
-rw-r--r-- | net/802/garp.c | 636 | ||||
-rw-r--r-- | net/802/stp.c | 102 |
4 files changed, 747 insertions, 0 deletions
diff --git a/net/802/Kconfig b/net/802/Kconfig new file mode 100644 index 000000000000..be33d27c8e69 --- /dev/null +++ b/net/802/Kconfig | |||
@@ -0,0 +1,7 @@ | |||
1 | config STP | ||
2 | tristate | ||
3 | select LLC | ||
4 | |||
5 | config GARP | ||
6 | tristate | ||
7 | select STP | ||
diff --git a/net/802/Makefile b/net/802/Makefile index 68569ffddea1..7893d679910c 100644 --- a/net/802/Makefile +++ b/net/802/Makefile | |||
@@ -10,3 +10,5 @@ obj-$(CONFIG_FDDI) += fddi.o | |||
10 | obj-$(CONFIG_HIPPI) += hippi.o | 10 | obj-$(CONFIG_HIPPI) += hippi.o |
11 | obj-$(CONFIG_IPX) += p8022.o psnap.o p8023.o | 11 | obj-$(CONFIG_IPX) += p8022.o psnap.o p8023.o |
12 | obj-$(CONFIG_ATALK) += p8022.o psnap.o | 12 | obj-$(CONFIG_ATALK) += p8022.o psnap.o |
13 | obj-$(CONFIG_STP) += stp.o | ||
14 | obj-$(CONFIG_GARP) += garp.o | ||
diff --git a/net/802/garp.c b/net/802/garp.c new file mode 100644 index 000000000000..1dcb0660c49d --- /dev/null +++ b/net/802/garp.c | |||
@@ -0,0 +1,636 @@ | |||
1 | /* | ||
2 | * IEEE 802.1D Generic Attribute Registration Protocol (GARP) | ||
3 | * | ||
4 | * Copyright (c) 2008 Patrick McHardy <kaber@trash.net> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * version 2 as published by the Free Software Foundation. | ||
9 | */ | ||
10 | #include <linux/kernel.h> | ||
11 | #include <linux/timer.h> | ||
12 | #include <linux/skbuff.h> | ||
13 | #include <linux/netdevice.h> | ||
14 | #include <linux/etherdevice.h> | ||
15 | #include <linux/rtnetlink.h> | ||
16 | #include <linux/llc.h> | ||
17 | #include <net/llc.h> | ||
18 | #include <net/llc_pdu.h> | ||
19 | #include <net/garp.h> | ||
20 | #include <asm/unaligned.h> | ||
21 | |||
22 | static unsigned int garp_join_time __read_mostly = 200; | ||
23 | module_param(garp_join_time, uint, 0644); | ||
24 | MODULE_PARM_DESC(garp_join_time, "Join time in ms (default 200ms)"); | ||
25 | MODULE_LICENSE("GPL"); | ||
26 | |||
27 | static const struct garp_state_trans { | ||
28 | u8 state; | ||
29 | u8 action; | ||
30 | } garp_applicant_state_table[GARP_APPLICANT_MAX + 1][GARP_EVENT_MAX + 1] = { | ||
31 | [GARP_APPLICANT_VA] = { | ||
32 | [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_AA, | ||
33 | .action = GARP_ACTION_S_JOIN_IN }, | ||
34 | [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_AA }, | ||
35 | [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VA }, | ||
36 | [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VA }, | ||
37 | [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VA }, | ||
38 | [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP }, | ||
39 | [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID }, | ||
40 | [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_LA }, | ||
41 | }, | ||
42 | [GARP_APPLICANT_AA] = { | ||
43 | [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_QA, | ||
44 | .action = GARP_ACTION_S_JOIN_IN }, | ||
45 | [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QA }, | ||
46 | [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VA }, | ||
47 | [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VA }, | ||
48 | [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VA }, | ||
49 | [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP }, | ||
50 | [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID }, | ||
51 | [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_LA }, | ||
52 | }, | ||
53 | [GARP_APPLICANT_QA] = { | ||
54 | [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_INVALID }, | ||
55 | [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QA }, | ||
56 | [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VA }, | ||
57 | [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VA }, | ||
58 | [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VP }, | ||
59 | [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP }, | ||
60 | [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID }, | ||
61 | [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_LA }, | ||
62 | }, | ||
63 | [GARP_APPLICANT_LA] = { | ||
64 | [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_VO, | ||
65 | .action = GARP_ACTION_S_LEAVE_EMPTY }, | ||
66 | [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_LA }, | ||
67 | [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VO }, | ||
68 | [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_LA }, | ||
69 | [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_LA }, | ||
70 | [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VO }, | ||
71 | [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_VA }, | ||
72 | [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_INVALID }, | ||
73 | }, | ||
74 | [GARP_APPLICANT_VP] = { | ||
75 | [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_AA, | ||
76 | .action = GARP_ACTION_S_JOIN_IN }, | ||
77 | [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_AP }, | ||
78 | [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VP }, | ||
79 | [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VP }, | ||
80 | [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VP }, | ||
81 | [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP }, | ||
82 | [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID }, | ||
83 | [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_VO }, | ||
84 | }, | ||
85 | [GARP_APPLICANT_AP] = { | ||
86 | [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_QA, | ||
87 | .action = GARP_ACTION_S_JOIN_IN }, | ||
88 | [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QP }, | ||
89 | [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VP }, | ||
90 | [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VP }, | ||
91 | [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VP }, | ||
92 | [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP }, | ||
93 | [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID }, | ||
94 | [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_AO }, | ||
95 | }, | ||
96 | [GARP_APPLICANT_QP] = { | ||
97 | [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_INVALID }, | ||
98 | [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QP }, | ||
99 | [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VP }, | ||
100 | [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VP }, | ||
101 | [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VP }, | ||
102 | [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP }, | ||
103 | [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID }, | ||
104 | [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_QO }, | ||
105 | }, | ||
106 | [GARP_APPLICANT_VO] = { | ||
107 | [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_INVALID }, | ||
108 | [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_AO }, | ||
109 | [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VO }, | ||
110 | [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VO }, | ||
111 | [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VO }, | ||
112 | [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VO }, | ||
113 | [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_VP }, | ||
114 | [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_INVALID }, | ||
115 | }, | ||
116 | [GARP_APPLICANT_AO] = { | ||
117 | [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_INVALID }, | ||
118 | [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QO }, | ||
119 | [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VO }, | ||
120 | [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VO }, | ||
121 | [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VO }, | ||
122 | [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VO }, | ||
123 | [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_AP }, | ||
124 | [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_INVALID }, | ||
125 | }, | ||
126 | [GARP_APPLICANT_QO] = { | ||
127 | [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_INVALID }, | ||
128 | [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QO }, | ||
129 | [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VO }, | ||
130 | [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VO }, | ||
131 | [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VO }, | ||
132 | [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VO }, | ||
133 | [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_QP }, | ||
134 | [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_INVALID }, | ||
135 | }, | ||
136 | }; | ||
137 | |||
138 | static int garp_attr_cmp(const struct garp_attr *attr, | ||
139 | const void *data, u8 len, u8 type) | ||
140 | { | ||
141 | if (attr->type != type) | ||
142 | return attr->type - type; | ||
143 | if (attr->dlen != len) | ||
144 | return attr->dlen - len; | ||
145 | return memcmp(attr->data, data, len); | ||
146 | } | ||
147 | |||
148 | static struct garp_attr *garp_attr_lookup(const struct garp_applicant *app, | ||
149 | const void *data, u8 len, u8 type) | ||
150 | { | ||
151 | struct rb_node *parent = app->gid.rb_node; | ||
152 | struct garp_attr *attr; | ||
153 | int d; | ||
154 | |||
155 | while (parent) { | ||
156 | attr = rb_entry(parent, struct garp_attr, node); | ||
157 | d = garp_attr_cmp(attr, data, len, type); | ||
158 | if (d < 0) | ||
159 | parent = parent->rb_left; | ||
160 | else if (d > 0) | ||
161 | parent = parent->rb_right; | ||
162 | else | ||
163 | return attr; | ||
164 | } | ||
165 | return NULL; | ||
166 | } | ||
167 | |||
168 | static void garp_attr_insert(struct garp_applicant *app, struct garp_attr *new) | ||
169 | { | ||
170 | struct rb_node *parent = NULL, **p = &app->gid.rb_node; | ||
171 | struct garp_attr *attr; | ||
172 | int d; | ||
173 | |||
174 | while (*p) { | ||
175 | parent = *p; | ||
176 | attr = rb_entry(parent, struct garp_attr, node); | ||
177 | d = garp_attr_cmp(attr, new->data, new->dlen, new->type); | ||
178 | if (d < 0) | ||
179 | p = &parent->rb_left; | ||
180 | else if (d > 0) | ||
181 | p = &parent->rb_right; | ||
182 | } | ||
183 | rb_link_node(&new->node, parent, p); | ||
184 | rb_insert_color(&new->node, &app->gid); | ||
185 | } | ||
186 | |||
187 | static struct garp_attr *garp_attr_create(struct garp_applicant *app, | ||
188 | const void *data, u8 len, u8 type) | ||
189 | { | ||
190 | struct garp_attr *attr; | ||
191 | |||
192 | attr = kmalloc(sizeof(*attr) + len, GFP_ATOMIC); | ||
193 | if (!attr) | ||
194 | return attr; | ||
195 | attr->state = GARP_APPLICANT_VO; | ||
196 | attr->type = type; | ||
197 | attr->dlen = len; | ||
198 | memcpy(attr->data, data, len); | ||
199 | garp_attr_insert(app, attr); | ||
200 | return attr; | ||
201 | } | ||
202 | |||
203 | static void garp_attr_destroy(struct garp_applicant *app, struct garp_attr *attr) | ||
204 | { | ||
205 | rb_erase(&attr->node, &app->gid); | ||
206 | kfree(attr); | ||
207 | } | ||
208 | |||
209 | static int garp_pdu_init(struct garp_applicant *app) | ||
210 | { | ||
211 | struct sk_buff *skb; | ||
212 | struct garp_pdu_hdr *gp; | ||
213 | |||
214 | #define LLC_RESERVE sizeof(struct llc_pdu_un) | ||
215 | skb = alloc_skb(app->dev->mtu + LL_RESERVED_SPACE(app->dev), | ||
216 | GFP_ATOMIC); | ||
217 | if (!skb) | ||
218 | return -ENOMEM; | ||
219 | |||
220 | skb->dev = app->dev; | ||
221 | skb->protocol = htons(ETH_P_802_2); | ||
222 | skb_reserve(skb, LL_RESERVED_SPACE(app->dev) + LLC_RESERVE); | ||
223 | |||
224 | gp = (struct garp_pdu_hdr *)__skb_put(skb, sizeof(*gp)); | ||
225 | put_unaligned(htons(GARP_PROTOCOL_ID), &gp->protocol); | ||
226 | |||
227 | app->pdu = skb; | ||
228 | return 0; | ||
229 | } | ||
230 | |||
231 | static int garp_pdu_append_end_mark(struct garp_applicant *app) | ||
232 | { | ||
233 | if (skb_tailroom(app->pdu) < sizeof(u8)) | ||
234 | return -1; | ||
235 | *(u8 *)__skb_put(app->pdu, sizeof(u8)) = GARP_END_MARK; | ||
236 | return 0; | ||
237 | } | ||
238 | |||
239 | static void garp_pdu_queue(struct garp_applicant *app) | ||
240 | { | ||
241 | if (!app->pdu) | ||
242 | return; | ||
243 | |||
244 | garp_pdu_append_end_mark(app); | ||
245 | garp_pdu_append_end_mark(app); | ||
246 | |||
247 | llc_pdu_header_init(app->pdu, LLC_PDU_TYPE_U, LLC_SAP_BSPAN, | ||
248 | LLC_SAP_BSPAN, LLC_PDU_CMD); | ||
249 | llc_pdu_init_as_ui_cmd(app->pdu); | ||
250 | llc_mac_hdr_init(app->pdu, app->dev->dev_addr, | ||
251 | app->app->proto.group_address); | ||
252 | |||
253 | skb_queue_tail(&app->queue, app->pdu); | ||
254 | app->pdu = NULL; | ||
255 | } | ||
256 | |||
257 | static void garp_queue_xmit(struct garp_applicant *app) | ||
258 | { | ||
259 | struct sk_buff *skb; | ||
260 | |||
261 | while ((skb = skb_dequeue(&app->queue))) | ||
262 | dev_queue_xmit(skb); | ||
263 | } | ||
264 | |||
265 | static int garp_pdu_append_msg(struct garp_applicant *app, u8 attrtype) | ||
266 | { | ||
267 | struct garp_msg_hdr *gm; | ||
268 | |||
269 | if (skb_tailroom(app->pdu) < sizeof(*gm)) | ||
270 | return -1; | ||
271 | gm = (struct garp_msg_hdr *)__skb_put(app->pdu, sizeof(*gm)); | ||
272 | gm->attrtype = attrtype; | ||
273 | garp_cb(app->pdu)->cur_type = attrtype; | ||
274 | return 0; | ||
275 | } | ||
276 | |||
277 | static int garp_pdu_append_attr(struct garp_applicant *app, | ||
278 | const struct garp_attr *attr, | ||
279 | enum garp_attr_event event) | ||
280 | { | ||
281 | struct garp_attr_hdr *ga; | ||
282 | unsigned int len; | ||
283 | int err; | ||
284 | again: | ||
285 | if (!app->pdu) { | ||
286 | err = garp_pdu_init(app); | ||
287 | if (err < 0) | ||
288 | return err; | ||
289 | } | ||
290 | |||
291 | if (garp_cb(app->pdu)->cur_type != attr->type) { | ||
292 | if (garp_cb(app->pdu)->cur_type && | ||
293 | garp_pdu_append_end_mark(app) < 0) | ||
294 | goto queue; | ||
295 | if (garp_pdu_append_msg(app, attr->type) < 0) | ||
296 | goto queue; | ||
297 | } | ||
298 | |||
299 | len = sizeof(*ga) + attr->dlen; | ||
300 | if (skb_tailroom(app->pdu) < len) | ||
301 | goto queue; | ||
302 | ga = (struct garp_attr_hdr *)__skb_put(app->pdu, len); | ||
303 | ga->len = len; | ||
304 | ga->event = event; | ||
305 | memcpy(ga->data, attr->data, attr->dlen); | ||
306 | return 0; | ||
307 | |||
308 | queue: | ||
309 | garp_pdu_queue(app); | ||
310 | goto again; | ||
311 | } | ||
312 | |||
313 | static void garp_attr_event(struct garp_applicant *app, | ||
314 | struct garp_attr *attr, enum garp_event event) | ||
315 | { | ||
316 | enum garp_applicant_state state; | ||
317 | |||
318 | state = garp_applicant_state_table[attr->state][event].state; | ||
319 | if (state == GARP_APPLICANT_INVALID) | ||
320 | return; | ||
321 | |||
322 | switch (garp_applicant_state_table[attr->state][event].action) { | ||
323 | case GARP_ACTION_NONE: | ||
324 | break; | ||
325 | case GARP_ACTION_S_JOIN_IN: | ||
326 | /* When appending the attribute fails, don't update state in | ||
327 | * order to retry on next TRANSMIT_PDU event. */ | ||
328 | if (garp_pdu_append_attr(app, attr, GARP_JOIN_IN) < 0) | ||
329 | return; | ||
330 | break; | ||
331 | case GARP_ACTION_S_LEAVE_EMPTY: | ||
332 | garp_pdu_append_attr(app, attr, GARP_LEAVE_EMPTY); | ||
333 | /* As a pure applicant, sending a leave message implies that | ||
334 | * the attribute was unregistered and can be destroyed. */ | ||
335 | garp_attr_destroy(app, attr); | ||
336 | return; | ||
337 | default: | ||
338 | WARN_ON(1); | ||
339 | } | ||
340 | |||
341 | attr->state = state; | ||
342 | } | ||
343 | |||
344 | int garp_request_join(const struct net_device *dev, | ||
345 | const struct garp_application *appl, | ||
346 | const void *data, u8 len, u8 type) | ||
347 | { | ||
348 | struct garp_port *port = dev->garp_port; | ||
349 | struct garp_applicant *app = port->applicants[appl->type]; | ||
350 | struct garp_attr *attr; | ||
351 | |||
352 | spin_lock_bh(&app->lock); | ||
353 | attr = garp_attr_create(app, data, len, type); | ||
354 | if (!attr) { | ||
355 | spin_unlock_bh(&app->lock); | ||
356 | return -ENOMEM; | ||
357 | } | ||
358 | garp_attr_event(app, attr, GARP_EVENT_REQ_JOIN); | ||
359 | spin_unlock_bh(&app->lock); | ||
360 | return 0; | ||
361 | } | ||
362 | EXPORT_SYMBOL_GPL(garp_request_join); | ||
363 | |||
364 | void garp_request_leave(const struct net_device *dev, | ||
365 | const struct garp_application *appl, | ||
366 | const void *data, u8 len, u8 type) | ||
367 | { | ||
368 | struct garp_port *port = dev->garp_port; | ||
369 | struct garp_applicant *app = port->applicants[appl->type]; | ||
370 | struct garp_attr *attr; | ||
371 | |||
372 | spin_lock_bh(&app->lock); | ||
373 | attr = garp_attr_lookup(app, data, len, type); | ||
374 | if (!attr) { | ||
375 | spin_unlock_bh(&app->lock); | ||
376 | return; | ||
377 | } | ||
378 | garp_attr_event(app, attr, GARP_EVENT_REQ_LEAVE); | ||
379 | spin_unlock_bh(&app->lock); | ||
380 | } | ||
381 | EXPORT_SYMBOL_GPL(garp_request_leave); | ||
382 | |||
383 | static void garp_gid_event(struct garp_applicant *app, enum garp_event event) | ||
384 | { | ||
385 | struct rb_node *node, *next; | ||
386 | struct garp_attr *attr; | ||
387 | |||
388 | for (node = rb_first(&app->gid); | ||
389 | next = node ? rb_next(node) : NULL, node != NULL; | ||
390 | node = next) { | ||
391 | attr = rb_entry(node, struct garp_attr, node); | ||
392 | garp_attr_event(app, attr, event); | ||
393 | } | ||
394 | } | ||
395 | |||
396 | static void garp_join_timer_arm(struct garp_applicant *app) | ||
397 | { | ||
398 | unsigned long delay; | ||
399 | |||
400 | delay = (u64)msecs_to_jiffies(garp_join_time) * net_random() >> 32; | ||
401 | mod_timer(&app->join_timer, jiffies + delay); | ||
402 | } | ||
403 | |||
404 | static void garp_join_timer(unsigned long data) | ||
405 | { | ||
406 | struct garp_applicant *app = (struct garp_applicant *)data; | ||
407 | |||
408 | spin_lock(&app->lock); | ||
409 | garp_gid_event(app, GARP_EVENT_TRANSMIT_PDU); | ||
410 | garp_pdu_queue(app); | ||
411 | spin_unlock(&app->lock); | ||
412 | |||
413 | garp_queue_xmit(app); | ||
414 | garp_join_timer_arm(app); | ||
415 | } | ||
416 | |||
417 | static int garp_pdu_parse_end_mark(struct sk_buff *skb) | ||
418 | { | ||
419 | if (!pskb_may_pull(skb, sizeof(u8))) | ||
420 | return -1; | ||
421 | if (*skb->data == GARP_END_MARK) { | ||
422 | skb_pull(skb, sizeof(u8)); | ||
423 | return -1; | ||
424 | } | ||
425 | return 0; | ||
426 | } | ||
427 | |||
428 | static int garp_pdu_parse_attr(struct garp_applicant *app, struct sk_buff *skb, | ||
429 | u8 attrtype) | ||
430 | { | ||
431 | const struct garp_attr_hdr *ga; | ||
432 | struct garp_attr *attr; | ||
433 | enum garp_event event; | ||
434 | unsigned int dlen; | ||
435 | |||
436 | if (!pskb_may_pull(skb, sizeof(*ga))) | ||
437 | return -1; | ||
438 | ga = (struct garp_attr_hdr *)skb->data; | ||
439 | if (ga->len < sizeof(*ga)) | ||
440 | return -1; | ||
441 | |||
442 | if (!pskb_may_pull(skb, ga->len)) | ||
443 | return -1; | ||
444 | skb_pull(skb, ga->len); | ||
445 | dlen = sizeof(*ga) - ga->len; | ||
446 | |||
447 | if (attrtype > app->app->maxattr) | ||
448 | return 0; | ||
449 | |||
450 | switch (ga->event) { | ||
451 | case GARP_LEAVE_ALL: | ||
452 | if (dlen != 0) | ||
453 | return -1; | ||
454 | garp_gid_event(app, GARP_EVENT_R_LEAVE_EMPTY); | ||
455 | return 0; | ||
456 | case GARP_JOIN_EMPTY: | ||
457 | event = GARP_EVENT_R_JOIN_EMPTY; | ||
458 | break; | ||
459 | case GARP_JOIN_IN: | ||
460 | event = GARP_EVENT_R_JOIN_IN; | ||
461 | break; | ||
462 | case GARP_LEAVE_EMPTY: | ||
463 | event = GARP_EVENT_R_LEAVE_EMPTY; | ||
464 | break; | ||
465 | case GARP_EMPTY: | ||
466 | event = GARP_EVENT_R_EMPTY; | ||
467 | break; | ||
468 | default: | ||
469 | return 0; | ||
470 | } | ||
471 | |||
472 | if (dlen == 0) | ||
473 | return -1; | ||
474 | attr = garp_attr_lookup(app, ga->data, dlen, attrtype); | ||
475 | if (attr == NULL) | ||
476 | return 0; | ||
477 | garp_attr_event(app, attr, event); | ||
478 | return 0; | ||
479 | } | ||
480 | |||
481 | static int garp_pdu_parse_msg(struct garp_applicant *app, struct sk_buff *skb) | ||
482 | { | ||
483 | const struct garp_msg_hdr *gm; | ||
484 | |||
485 | if (!pskb_may_pull(skb, sizeof(*gm))) | ||
486 | return -1; | ||
487 | gm = (struct garp_msg_hdr *)skb->data; | ||
488 | if (gm->attrtype == 0) | ||
489 | return -1; | ||
490 | skb_pull(skb, sizeof(*gm)); | ||
491 | |||
492 | while (skb->len > 0) { | ||
493 | if (garp_pdu_parse_attr(app, skb, gm->attrtype) < 0) | ||
494 | return -1; | ||
495 | if (garp_pdu_parse_end_mark(skb) < 0) | ||
496 | break; | ||
497 | } | ||
498 | return 0; | ||
499 | } | ||
500 | |||
501 | static void garp_pdu_rcv(const struct stp_proto *proto, struct sk_buff *skb, | ||
502 | struct net_device *dev) | ||
503 | { | ||
504 | struct garp_application *appl = proto->data; | ||
505 | struct garp_port *port; | ||
506 | struct garp_applicant *app; | ||
507 | const struct garp_pdu_hdr *gp; | ||
508 | |||
509 | port = rcu_dereference(dev->garp_port); | ||
510 | if (!port) | ||
511 | goto err; | ||
512 | app = rcu_dereference(port->applicants[appl->type]); | ||
513 | if (!app) | ||
514 | goto err; | ||
515 | |||
516 | if (!pskb_may_pull(skb, sizeof(*gp))) | ||
517 | goto err; | ||
518 | gp = (struct garp_pdu_hdr *)skb->data; | ||
519 | if (get_unaligned(&gp->protocol) != htons(GARP_PROTOCOL_ID)) | ||
520 | goto err; | ||
521 | skb_pull(skb, sizeof(*gp)); | ||
522 | |||
523 | spin_lock(&app->lock); | ||
524 | while (skb->len > 0) { | ||
525 | if (garp_pdu_parse_msg(app, skb) < 0) | ||
526 | break; | ||
527 | if (garp_pdu_parse_end_mark(skb) < 0) | ||
528 | break; | ||
529 | } | ||
530 | spin_unlock(&app->lock); | ||
531 | err: | ||
532 | kfree_skb(skb); | ||
533 | } | ||
534 | |||
535 | static int garp_init_port(struct net_device *dev) | ||
536 | { | ||
537 | struct garp_port *port; | ||
538 | |||
539 | port = kzalloc(sizeof(*port), GFP_KERNEL); | ||
540 | if (!port) | ||
541 | return -ENOMEM; | ||
542 | rcu_assign_pointer(dev->garp_port, port); | ||
543 | return 0; | ||
544 | } | ||
545 | |||
546 | static void garp_release_port(struct net_device *dev) | ||
547 | { | ||
548 | struct garp_port *port = dev->garp_port; | ||
549 | unsigned int i; | ||
550 | |||
551 | for (i = 0; i <= GARP_APPLICATION_MAX; i++) { | ||
552 | if (port->applicants[i]) | ||
553 | return; | ||
554 | } | ||
555 | rcu_assign_pointer(dev->garp_port, NULL); | ||
556 | synchronize_rcu(); | ||
557 | kfree(port); | ||
558 | } | ||
559 | |||
560 | int garp_init_applicant(struct net_device *dev, struct garp_application *appl) | ||
561 | { | ||
562 | struct garp_applicant *app; | ||
563 | int err; | ||
564 | |||
565 | ASSERT_RTNL(); | ||
566 | |||
567 | if (!dev->garp_port) { | ||
568 | err = garp_init_port(dev); | ||
569 | if (err < 0) | ||
570 | goto err1; | ||
571 | } | ||
572 | |||
573 | err = -ENOMEM; | ||
574 | app = kzalloc(sizeof(*app), GFP_KERNEL); | ||
575 | if (!app) | ||
576 | goto err2; | ||
577 | |||
578 | err = dev_mc_add(dev, appl->proto.group_address, ETH_ALEN, 0); | ||
579 | if (err < 0) | ||
580 | goto err3; | ||
581 | |||
582 | app->dev = dev; | ||
583 | app->app = appl; | ||
584 | app->gid = RB_ROOT; | ||
585 | spin_lock_init(&app->lock); | ||
586 | skb_queue_head_init(&app->queue); | ||
587 | rcu_assign_pointer(dev->garp_port->applicants[appl->type], app); | ||
588 | setup_timer(&app->join_timer, garp_join_timer, (unsigned long)app); | ||
589 | garp_join_timer_arm(app); | ||
590 | return 0; | ||
591 | |||
592 | err3: | ||
593 | kfree(app); | ||
594 | err2: | ||
595 | garp_release_port(dev); | ||
596 | err1: | ||
597 | return err; | ||
598 | } | ||
599 | EXPORT_SYMBOL_GPL(garp_init_applicant); | ||
600 | |||
601 | void garp_uninit_applicant(struct net_device *dev, struct garp_application *appl) | ||
602 | { | ||
603 | struct garp_port *port = dev->garp_port; | ||
604 | struct garp_applicant *app = port->applicants[appl->type]; | ||
605 | |||
606 | ASSERT_RTNL(); | ||
607 | |||
608 | rcu_assign_pointer(port->applicants[appl->type], NULL); | ||
609 | synchronize_rcu(); | ||
610 | |||
611 | /* Delete timer and generate a final TRANSMIT_PDU event to flush out | ||
612 | * all pending messages before the applicant is gone. */ | ||
613 | del_timer_sync(&app->join_timer); | ||
614 | garp_gid_event(app, GARP_EVENT_TRANSMIT_PDU); | ||
615 | garp_pdu_queue(app); | ||
616 | garp_queue_xmit(app); | ||
617 | |||
618 | dev_mc_delete(dev, appl->proto.group_address, ETH_ALEN, 0); | ||
619 | kfree(app); | ||
620 | garp_release_port(dev); | ||
621 | } | ||
622 | EXPORT_SYMBOL_GPL(garp_uninit_applicant); | ||
623 | |||
624 | int garp_register_application(struct garp_application *appl) | ||
625 | { | ||
626 | appl->proto.rcv = garp_pdu_rcv; | ||
627 | appl->proto.data = appl; | ||
628 | return stp_proto_register(&appl->proto); | ||
629 | } | ||
630 | EXPORT_SYMBOL_GPL(garp_register_application); | ||
631 | |||
632 | void garp_unregister_application(struct garp_application *appl) | ||
633 | { | ||
634 | stp_proto_unregister(&appl->proto); | ||
635 | } | ||
636 | EXPORT_SYMBOL_GPL(garp_unregister_application); | ||
diff --git a/net/802/stp.c b/net/802/stp.c new file mode 100644 index 000000000000..0b7a24452d11 --- /dev/null +++ b/net/802/stp.c | |||
@@ -0,0 +1,102 @@ | |||
1 | /* | ||
2 | * STP SAP demux | ||
3 | * | ||
4 | * Copyright (c) 2008 Patrick McHardy <kaber@trash.net> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * version 2 as published by the Free Software Foundation. | ||
9 | */ | ||
10 | #include <linux/mutex.h> | ||
11 | #include <linux/skbuff.h> | ||
12 | #include <linux/etherdevice.h> | ||
13 | #include <linux/llc.h> | ||
14 | #include <net/llc.h> | ||
15 | #include <net/llc_pdu.h> | ||
16 | #include <net/stp.h> | ||
17 | |||
18 | /* 01:80:c2:00:00:20 - 01:80:c2:00:00:2F */ | ||
19 | #define GARP_ADDR_MIN 0x20 | ||
20 | #define GARP_ADDR_MAX 0x2F | ||
21 | #define GARP_ADDR_RANGE (GARP_ADDR_MAX - GARP_ADDR_MIN) | ||
22 | |||
23 | static const struct stp_proto *garp_protos[GARP_ADDR_RANGE + 1] __read_mostly; | ||
24 | static const struct stp_proto *stp_proto __read_mostly; | ||
25 | |||
26 | static struct llc_sap *sap __read_mostly; | ||
27 | static unsigned int sap_registered; | ||
28 | static DEFINE_MUTEX(stp_proto_mutex); | ||
29 | |||
30 | /* Called under rcu_read_lock from LLC */ | ||
31 | static int stp_pdu_rcv(struct sk_buff *skb, struct net_device *dev, | ||
32 | struct packet_type *pt, struct net_device *orig_dev) | ||
33 | { | ||
34 | const struct ethhdr *eh = eth_hdr(skb); | ||
35 | const struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb); | ||
36 | const struct stp_proto *proto; | ||
37 | |||
38 | if (pdu->ssap != LLC_SAP_BSPAN || | ||
39 | pdu->dsap != LLC_SAP_BSPAN || | ||
40 | pdu->ctrl_1 != LLC_PDU_TYPE_U) | ||
41 | goto err; | ||
42 | |||
43 | if (eh->h_dest[5] >= GARP_ADDR_MIN && eh->h_dest[5] <= GARP_ADDR_MAX) { | ||
44 | proto = rcu_dereference(garp_protos[eh->h_dest[5] - | ||
45 | GARP_ADDR_MIN]); | ||
46 | if (proto && | ||
47 | compare_ether_addr(eh->h_dest, proto->group_address)) | ||
48 | goto err; | ||
49 | } else | ||
50 | proto = rcu_dereference(stp_proto); | ||
51 | |||
52 | if (!proto) | ||
53 | goto err; | ||
54 | |||
55 | proto->rcv(proto, skb, dev); | ||
56 | return 0; | ||
57 | |||
58 | err: | ||
59 | kfree_skb(skb); | ||
60 | return 0; | ||
61 | } | ||
62 | |||
63 | int stp_proto_register(const struct stp_proto *proto) | ||
64 | { | ||
65 | int err = 0; | ||
66 | |||
67 | mutex_lock(&stp_proto_mutex); | ||
68 | if (sap_registered++ == 0) { | ||
69 | sap = llc_sap_open(LLC_SAP_BSPAN, stp_pdu_rcv); | ||
70 | if (!sap) { | ||
71 | err = -ENOMEM; | ||
72 | goto out; | ||
73 | } | ||
74 | } | ||
75 | if (is_zero_ether_addr(proto->group_address)) | ||
76 | rcu_assign_pointer(stp_proto, proto); | ||
77 | else | ||
78 | rcu_assign_pointer(garp_protos[proto->group_address[5] - | ||
79 | GARP_ADDR_MIN], proto); | ||
80 | out: | ||
81 | mutex_unlock(&stp_proto_mutex); | ||
82 | return err; | ||
83 | } | ||
84 | EXPORT_SYMBOL_GPL(stp_proto_register); | ||
85 | |||
86 | void stp_proto_unregister(const struct stp_proto *proto) | ||
87 | { | ||
88 | mutex_lock(&stp_proto_mutex); | ||
89 | if (is_zero_ether_addr(proto->group_address)) | ||
90 | rcu_assign_pointer(stp_proto, NULL); | ||
91 | else | ||
92 | rcu_assign_pointer(garp_protos[proto->group_address[5] - | ||
93 | GARP_ADDR_MIN], NULL); | ||
94 | synchronize_rcu(); | ||
95 | |||
96 | if (--sap_registered == 0) | ||
97 | llc_sap_put(sap); | ||
98 | mutex_unlock(&stp_proto_mutex); | ||
99 | } | ||
100 | EXPORT_SYMBOL_GPL(stp_proto_unregister); | ||
101 | |||
102 | MODULE_LICENSE("GPL"); | ||