Root/
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 <linux/slab.h> |
18 | #include <linux/module.h> |
19 | #include <net/llc.h> |
20 | #include <net/llc_pdu.h> |
21 | #include <net/garp.h> |
22 | #include <asm/unaligned.h> |
23 | |
24 | static unsigned int garp_join_time __read_mostly = 200; |
25 | module_param(garp_join_time, uint, 0644); |
26 | MODULE_PARM_DESC(garp_join_time, "Join time in ms (default 200ms)"); |
27 | MODULE_LICENSE("GPL"); |
28 | |
29 | static const struct garp_state_trans { |
30 | u8 state; |
31 | u8 action; |
32 | } garp_applicant_state_table[GARP_APPLICANT_MAX + 1][GARP_EVENT_MAX + 1] = { |
33 | [GARP_APPLICANT_VA] = { |
34 | [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_AA, |
35 | .action = GARP_ACTION_S_JOIN_IN }, |
36 | [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_AA }, |
37 | [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VA }, |
38 | [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VA }, |
39 | [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VA }, |
40 | [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP }, |
41 | [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID }, |
42 | [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_LA }, |
43 | }, |
44 | [GARP_APPLICANT_AA] = { |
45 | [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_QA, |
46 | .action = GARP_ACTION_S_JOIN_IN }, |
47 | [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QA }, |
48 | [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VA }, |
49 | [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VA }, |
50 | [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VA }, |
51 | [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP }, |
52 | [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID }, |
53 | [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_LA }, |
54 | }, |
55 | [GARP_APPLICANT_QA] = { |
56 | [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_INVALID }, |
57 | [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QA }, |
58 | [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VA }, |
59 | [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VA }, |
60 | [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VP }, |
61 | [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP }, |
62 | [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID }, |
63 | [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_LA }, |
64 | }, |
65 | [GARP_APPLICANT_LA] = { |
66 | [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_VO, |
67 | .action = GARP_ACTION_S_LEAVE_EMPTY }, |
68 | [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_LA }, |
69 | [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VO }, |
70 | [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_LA }, |
71 | [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_LA }, |
72 | [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VO }, |
73 | [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_VA }, |
74 | [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_INVALID }, |
75 | }, |
76 | [GARP_APPLICANT_VP] = { |
77 | [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_AA, |
78 | .action = GARP_ACTION_S_JOIN_IN }, |
79 | [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_AP }, |
80 | [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VP }, |
81 | [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VP }, |
82 | [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VP }, |
83 | [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP }, |
84 | [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID }, |
85 | [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_VO }, |
86 | }, |
87 | [GARP_APPLICANT_AP] = { |
88 | [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_QA, |
89 | .action = GARP_ACTION_S_JOIN_IN }, |
90 | [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QP }, |
91 | [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VP }, |
92 | [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VP }, |
93 | [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VP }, |
94 | [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP }, |
95 | [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID }, |
96 | [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_AO }, |
97 | }, |
98 | [GARP_APPLICANT_QP] = { |
99 | [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_INVALID }, |
100 | [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QP }, |
101 | [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VP }, |
102 | [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VP }, |
103 | [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VP }, |
104 | [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP }, |
105 | [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID }, |
106 | [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_QO }, |
107 | }, |
108 | [GARP_APPLICANT_VO] = { |
109 | [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_INVALID }, |
110 | [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_AO }, |
111 | [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VO }, |
112 | [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VO }, |
113 | [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VO }, |
114 | [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VO }, |
115 | [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_VP }, |
116 | [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_INVALID }, |
117 | }, |
118 | [GARP_APPLICANT_AO] = { |
119 | [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_INVALID }, |
120 | [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QO }, |
121 | [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VO }, |
122 | [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VO }, |
123 | [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VO }, |
124 | [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VO }, |
125 | [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_AP }, |
126 | [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_INVALID }, |
127 | }, |
128 | [GARP_APPLICANT_QO] = { |
129 | [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_INVALID }, |
130 | [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QO }, |
131 | [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VO }, |
132 | [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VO }, |
133 | [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VO }, |
134 | [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VO }, |
135 | [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_QP }, |
136 | [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_INVALID }, |
137 | }, |
138 | }; |
139 | |
140 | static int garp_attr_cmp(const struct garp_attr *attr, |
141 | const void *data, u8 len, u8 type) |
142 | { |
143 | if (attr->type != type) |
144 | return attr->type - type; |
145 | if (attr->dlen != len) |
146 | return attr->dlen - len; |
147 | return memcmp(attr->data, data, len); |
148 | } |
149 | |
150 | static struct garp_attr *garp_attr_lookup(const struct garp_applicant *app, |
151 | const void *data, u8 len, u8 type) |
152 | { |
153 | struct rb_node *parent = app->gid.rb_node; |
154 | struct garp_attr *attr; |
155 | int d; |
156 | |
157 | while (parent) { |
158 | attr = rb_entry(parent, struct garp_attr, node); |
159 | d = garp_attr_cmp(attr, data, len, type); |
160 | if (d > 0) |
161 | parent = parent->rb_left; |
162 | else if (d < 0) |
163 | parent = parent->rb_right; |
164 | else |
165 | return attr; |
166 | } |
167 | return NULL; |
168 | } |
169 | |
170 | static struct garp_attr *garp_attr_create(struct garp_applicant *app, |
171 | const void *data, u8 len, u8 type) |
172 | { |
173 | struct rb_node *parent = NULL, **p = &app->gid.rb_node; |
174 | struct garp_attr *attr; |
175 | int d; |
176 | |
177 | while (*p) { |
178 | parent = *p; |
179 | attr = rb_entry(parent, struct garp_attr, node); |
180 | d = garp_attr_cmp(attr, data, len, type); |
181 | if (d > 0) |
182 | p = &parent->rb_left; |
183 | else if (d < 0) |
184 | p = &parent->rb_right; |
185 | else { |
186 | /* The attribute already exists; re-use it. */ |
187 | return attr; |
188 | } |
189 | } |
190 | attr = kmalloc(sizeof(*attr) + len, GFP_ATOMIC); |
191 | if (!attr) |
192 | return attr; |
193 | attr->state = GARP_APPLICANT_VO; |
194 | attr->type = type; |
195 | attr->dlen = len; |
196 | memcpy(attr->data, data, len); |
197 | |
198 | rb_link_node(&attr->node, parent, p); |
199 | rb_insert_color(&attr->node, &app->gid); |
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 = rtnl_dereference(dev->garp_port); |
349 | struct garp_applicant *app = rtnl_dereference(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 = rtnl_dereference(dev->garp_port); |
369 | struct garp_applicant *app = rtnl_dereference(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) * prandom_u32() >> 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 = rtnl_dereference(dev->garp_port); |
549 | unsigned int i; |
550 | |
551 | for (i = 0; i <= GARP_APPLICATION_MAX; i++) { |
552 | if (rtnl_dereference(port->applicants[i])) |
553 | return; |
554 | } |
555 | RCU_INIT_POINTER(dev->garp_port, NULL); |
556 | kfree_rcu(port, rcu); |
557 | } |
558 | |
559 | int garp_init_applicant(struct net_device *dev, struct garp_application *appl) |
560 | { |
561 | struct garp_applicant *app; |
562 | int err; |
563 | |
564 | ASSERT_RTNL(); |
565 | |
566 | if (!rtnl_dereference(dev->garp_port)) { |
567 | err = garp_init_port(dev); |
568 | if (err < 0) |
569 | goto err1; |
570 | } |
571 | |
572 | err = -ENOMEM; |
573 | app = kzalloc(sizeof(*app), GFP_KERNEL); |
574 | if (!app) |
575 | goto err2; |
576 | |
577 | err = dev_mc_add(dev, appl->proto.group_address); |
578 | if (err < 0) |
579 | goto err3; |
580 | |
581 | app->dev = dev; |
582 | app->app = appl; |
583 | app->gid = RB_ROOT; |
584 | spin_lock_init(&app->lock); |
585 | skb_queue_head_init(&app->queue); |
586 | rcu_assign_pointer(dev->garp_port->applicants[appl->type], app); |
587 | setup_timer(&app->join_timer, garp_join_timer, (unsigned long)app); |
588 | garp_join_timer_arm(app); |
589 | return 0; |
590 | |
591 | err3: |
592 | kfree(app); |
593 | err2: |
594 | garp_release_port(dev); |
595 | err1: |
596 | return err; |
597 | } |
598 | EXPORT_SYMBOL_GPL(garp_init_applicant); |
599 | |
600 | void garp_uninit_applicant(struct net_device *dev, struct garp_application *appl) |
601 | { |
602 | struct garp_port *port = rtnl_dereference(dev->garp_port); |
603 | struct garp_applicant *app = rtnl_dereference(port->applicants[appl->type]); |
604 | |
605 | ASSERT_RTNL(); |
606 | |
607 | RCU_INIT_POINTER(port->applicants[appl->type], NULL); |
608 | |
609 | /* Delete timer and generate a final TRANSMIT_PDU event to flush out |
610 | * all pending messages before the applicant is gone. */ |
611 | del_timer_sync(&app->join_timer); |
612 | |
613 | spin_lock_bh(&app->lock); |
614 | garp_gid_event(app, GARP_EVENT_TRANSMIT_PDU); |
615 | garp_pdu_queue(app); |
616 | spin_unlock_bh(&app->lock); |
617 | |
618 | garp_queue_xmit(app); |
619 | |
620 | dev_mc_del(dev, appl->proto.group_address); |
621 | kfree_rcu(app, rcu); |
622 | garp_release_port(dev); |
623 | } |
624 | EXPORT_SYMBOL_GPL(garp_uninit_applicant); |
625 | |
626 | int garp_register_application(struct garp_application *appl) |
627 | { |
628 | appl->proto.rcv = garp_pdu_rcv; |
629 | appl->proto.data = appl; |
630 | return stp_proto_register(&appl->proto); |
631 | } |
632 | EXPORT_SYMBOL_GPL(garp_register_application); |
633 | |
634 | void garp_unregister_application(struct garp_application *appl) |
635 | { |
636 | stp_proto_unregister(&appl->proto); |
637 | } |
638 | EXPORT_SYMBOL_GPL(garp_unregister_application); |
639 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9