diff options
author | Patrick McHardy <kaber@trash.net> | 2008-07-06 00:26:13 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-07-06 00:26:13 -0400 |
commit | eca9ebac651f774d8b10fce7c5d173c3c3d3394f (patch) | |
tree | 58b4117a0820dff43d4faa7fdcae5cd7723674c7 /net/802/garp.c | |
parent | 7c85fbf0657f216557b0c9c4a2e4e07f37d8bb8c (diff) |
net: Add GARP applicant-only participant
Add an implementation of the GARP (Generic Attribute Registration Protocol)
applicant-only participant. This will be used by the following patch to
add GVRP support to the VLAN code.
Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/802/garp.c')
-rw-r--r-- | net/802/garp.c | 633 |
1 files changed, 633 insertions, 0 deletions
diff --git a/net/802/garp.c b/net/802/garp.c new file mode 100644 index 000000000000..3b78f7b74fd4 --- /dev/null +++ b/net/802/garp.c | |||
@@ -0,0 +1,633 @@ | |||
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 | garp_pdu_append_attr(app, attr, GARP_JOIN_IN); | ||
327 | break; | ||
328 | case GARP_ACTION_S_LEAVE_EMPTY: | ||
329 | garp_pdu_append_attr(app, attr, GARP_LEAVE_EMPTY); | ||
330 | /* As a pure applicant, sending a leave message implies that | ||
331 | * the attribute was unregistered and can be destroyed. */ | ||
332 | garp_attr_destroy(app, attr); | ||
333 | return; | ||
334 | default: | ||
335 | WARN_ON(1); | ||
336 | } | ||
337 | |||
338 | attr->state = state; | ||
339 | } | ||
340 | |||
341 | int garp_request_join(const struct net_device *dev, | ||
342 | const struct garp_application *appl, | ||
343 | const void *data, u8 len, u8 type) | ||
344 | { | ||
345 | struct garp_port *port = dev->garp_port; | ||
346 | struct garp_applicant *app = port->applicants[appl->type]; | ||
347 | struct garp_attr *attr; | ||
348 | |||
349 | spin_lock_bh(&app->lock); | ||
350 | attr = garp_attr_create(app, data, len, type); | ||
351 | if (!attr) { | ||
352 | spin_unlock_bh(&app->lock); | ||
353 | return -ENOMEM; | ||
354 | } | ||
355 | garp_attr_event(app, attr, GARP_EVENT_REQ_JOIN); | ||
356 | spin_unlock_bh(&app->lock); | ||
357 | return 0; | ||
358 | } | ||
359 | EXPORT_SYMBOL_GPL(garp_request_join); | ||
360 | |||
361 | void garp_request_leave(const struct net_device *dev, | ||
362 | const struct garp_application *appl, | ||
363 | const void *data, u8 len, u8 type) | ||
364 | { | ||
365 | struct garp_port *port = dev->garp_port; | ||
366 | struct garp_applicant *app = port->applicants[appl->type]; | ||
367 | struct garp_attr *attr; | ||
368 | |||
369 | spin_lock_bh(&app->lock); | ||
370 | attr = garp_attr_lookup(app, data, len, type); | ||
371 | if (!attr) { | ||
372 | spin_unlock_bh(&app->lock); | ||
373 | return; | ||
374 | } | ||
375 | garp_attr_event(app, attr, GARP_EVENT_REQ_LEAVE); | ||
376 | spin_unlock_bh(&app->lock); | ||
377 | } | ||
378 | EXPORT_SYMBOL_GPL(garp_request_leave); | ||
379 | |||
380 | static void garp_gid_event(struct garp_applicant *app, enum garp_event event) | ||
381 | { | ||
382 | struct rb_node *node, *next; | ||
383 | struct garp_attr *attr; | ||
384 | |||
385 | for (node = rb_first(&app->gid); | ||
386 | next = node ? rb_next(node) : NULL, node != NULL; | ||
387 | node = next) { | ||
388 | attr = rb_entry(node, struct garp_attr, node); | ||
389 | garp_attr_event(app, attr, event); | ||
390 | } | ||
391 | } | ||
392 | |||
393 | static void garp_join_timer_arm(struct garp_applicant *app) | ||
394 | { | ||
395 | unsigned long delay; | ||
396 | |||
397 | delay = (u64)msecs_to_jiffies(garp_join_time) * net_random() >> 32; | ||
398 | mod_timer(&app->join_timer, jiffies + delay); | ||
399 | } | ||
400 | |||
401 | static void garp_join_timer(unsigned long data) | ||
402 | { | ||
403 | struct garp_applicant *app = (struct garp_applicant *)data; | ||
404 | |||
405 | spin_lock(&app->lock); | ||
406 | garp_gid_event(app, GARP_EVENT_TRANSMIT_PDU); | ||
407 | garp_pdu_queue(app); | ||
408 | spin_unlock(&app->lock); | ||
409 | |||
410 | garp_queue_xmit(app); | ||
411 | garp_join_timer_arm(app); | ||
412 | } | ||
413 | |||
414 | static int garp_pdu_parse_end_mark(struct sk_buff *skb) | ||
415 | { | ||
416 | if (!pskb_may_pull(skb, sizeof(u8))) | ||
417 | return -1; | ||
418 | if (*skb->data == GARP_END_MARK) { | ||
419 | skb_pull(skb, sizeof(u8)); | ||
420 | return -1; | ||
421 | } | ||
422 | return 0; | ||
423 | } | ||
424 | |||
425 | static int garp_pdu_parse_attr(struct garp_applicant *app, struct sk_buff *skb, | ||
426 | u8 attrtype) | ||
427 | { | ||
428 | const struct garp_attr_hdr *ga; | ||
429 | struct garp_attr *attr; | ||
430 | enum garp_event event; | ||
431 | unsigned int dlen; | ||
432 | |||
433 | if (!pskb_may_pull(skb, sizeof(*ga))) | ||
434 | return -1; | ||
435 | ga = (struct garp_attr_hdr *)skb->data; | ||
436 | if (ga->len < sizeof(*ga)) | ||
437 | return -1; | ||
438 | |||
439 | if (!pskb_may_pull(skb, ga->len)) | ||
440 | return -1; | ||
441 | skb_pull(skb, ga->len); | ||
442 | dlen = sizeof(*ga) - ga->len; | ||
443 | |||
444 | if (attrtype > app->app->maxattr) | ||
445 | return 0; | ||
446 | |||
447 | switch (ga->event) { | ||
448 | case GARP_LEAVE_ALL: | ||
449 | if (dlen != 0) | ||
450 | return -1; | ||
451 | garp_gid_event(app, GARP_EVENT_R_LEAVE_EMPTY); | ||
452 | return 0; | ||
453 | case GARP_JOIN_EMPTY: | ||
454 | event = GARP_EVENT_R_JOIN_EMPTY; | ||
455 | break; | ||
456 | case GARP_JOIN_IN: | ||
457 | event = GARP_EVENT_R_JOIN_IN; | ||
458 | break; | ||
459 | case GARP_LEAVE_EMPTY: | ||
460 | event = GARP_EVENT_R_LEAVE_EMPTY; | ||
461 | break; | ||
462 | case GARP_EMPTY: | ||
463 | event = GARP_EVENT_R_EMPTY; | ||
464 | break; | ||
465 | default: | ||
466 | return 0; | ||
467 | } | ||
468 | |||
469 | if (dlen == 0) | ||
470 | return -1; | ||
471 | attr = garp_attr_lookup(app, ga->data, dlen, attrtype); | ||
472 | if (attr == NULL) | ||
473 | return 0; | ||
474 | garp_attr_event(app, attr, event); | ||
475 | return 0; | ||
476 | } | ||
477 | |||
478 | static int garp_pdu_parse_msg(struct garp_applicant *app, struct sk_buff *skb) | ||
479 | { | ||
480 | const struct garp_msg_hdr *gm; | ||
481 | |||
482 | if (!pskb_may_pull(skb, sizeof(*gm))) | ||
483 | return -1; | ||
484 | gm = (struct garp_msg_hdr *)skb->data; | ||
485 | if (gm->attrtype == 0) | ||
486 | return -1; | ||
487 | skb_pull(skb, sizeof(*gm)); | ||
488 | |||
489 | while (skb->len > 0) { | ||
490 | if (garp_pdu_parse_attr(app, skb, gm->attrtype) < 0) | ||
491 | return -1; | ||
492 | if (garp_pdu_parse_end_mark(skb) < 0) | ||
493 | break; | ||
494 | } | ||
495 | return 0; | ||
496 | } | ||
497 | |||
498 | static void garp_pdu_rcv(const struct stp_proto *proto, struct sk_buff *skb, | ||
499 | struct net_device *dev) | ||
500 | { | ||
501 | struct garp_application *appl = proto->data; | ||
502 | struct garp_port *port; | ||
503 | struct garp_applicant *app; | ||
504 | const struct garp_pdu_hdr *gp; | ||
505 | |||
506 | port = rcu_dereference(dev->garp_port); | ||
507 | if (!port) | ||
508 | goto err; | ||
509 | app = rcu_dereference(port->applicants[appl->type]); | ||
510 | if (!app) | ||
511 | goto err; | ||
512 | |||
513 | if (!pskb_may_pull(skb, sizeof(*gp))) | ||
514 | goto err; | ||
515 | gp = (struct garp_pdu_hdr *)skb->data; | ||
516 | if (get_unaligned(&gp->protocol) != htons(GARP_PROTOCOL_ID)) | ||
517 | goto err; | ||
518 | skb_pull(skb, sizeof(*gp)); | ||
519 | |||
520 | spin_lock(&app->lock); | ||
521 | while (skb->len > 0) { | ||
522 | if (garp_pdu_parse_msg(app, skb) < 0) | ||
523 | break; | ||
524 | if (garp_pdu_parse_end_mark(skb) < 0) | ||
525 | break; | ||
526 | } | ||
527 | spin_unlock(&app->lock); | ||
528 | err: | ||
529 | kfree_skb(skb); | ||
530 | } | ||
531 | |||
532 | static int garp_init_port(struct net_device *dev) | ||
533 | { | ||
534 | struct garp_port *port; | ||
535 | |||
536 | port = kzalloc(sizeof(*port), GFP_KERNEL); | ||
537 | if (!port) | ||
538 | return -ENOMEM; | ||
539 | rcu_assign_pointer(dev->garp_port, port); | ||
540 | return 0; | ||
541 | } | ||
542 | |||
543 | static void garp_release_port(struct net_device *dev) | ||
544 | { | ||
545 | struct garp_port *port = dev->garp_port; | ||
546 | unsigned int i; | ||
547 | |||
548 | for (i = 0; i <= GARP_APPLICATION_MAX; i++) { | ||
549 | if (port->applicants[i]) | ||
550 | return; | ||
551 | } | ||
552 | rcu_assign_pointer(dev->garp_port, NULL); | ||
553 | synchronize_rcu(); | ||
554 | kfree(port); | ||
555 | } | ||
556 | |||
557 | int garp_init_applicant(struct net_device *dev, struct garp_application *appl) | ||
558 | { | ||
559 | struct garp_applicant *app; | ||
560 | int err; | ||
561 | |||
562 | ASSERT_RTNL(); | ||
563 | |||
564 | if (!dev->garp_port) { | ||
565 | err = garp_init_port(dev); | ||
566 | if (err < 0) | ||
567 | goto err1; | ||
568 | } | ||
569 | |||
570 | err = -ENOMEM; | ||
571 | app = kzalloc(sizeof(*app), GFP_KERNEL); | ||
572 | if (!app) | ||
573 | goto err2; | ||
574 | |||
575 | err = dev_mc_add(dev, appl->proto.group_address, ETH_ALEN, 0); | ||
576 | if (err < 0) | ||
577 | goto err3; | ||
578 | |||
579 | app->dev = dev; | ||
580 | app->app = appl; | ||
581 | app->gid = RB_ROOT; | ||
582 | spin_lock_init(&app->lock); | ||
583 | skb_queue_head_init(&app->queue); | ||
584 | rcu_assign_pointer(dev->garp_port->applicants[appl->type], app); | ||
585 | setup_timer(&app->join_timer, garp_join_timer, (unsigned long)app); | ||
586 | garp_join_timer_arm(app); | ||
587 | return 0; | ||
588 | |||
589 | err3: | ||
590 | kfree(app); | ||
591 | err2: | ||
592 | garp_release_port(dev); | ||
593 | err1: | ||
594 | return err; | ||
595 | } | ||
596 | EXPORT_SYMBOL_GPL(garp_init_applicant); | ||
597 | |||
598 | void garp_uninit_applicant(struct net_device *dev, struct garp_application *appl) | ||
599 | { | ||
600 | struct garp_port *port = dev->garp_port; | ||
601 | struct garp_applicant *app = port->applicants[appl->type]; | ||
602 | |||
603 | ASSERT_RTNL(); | ||
604 | |||
605 | rcu_assign_pointer(port->applicants[appl->type], NULL); | ||
606 | synchronize_rcu(); | ||
607 | |||
608 | /* Delete timer and generate a final TRANSMIT_PDU event to flush out | ||
609 | * all pending messages before the applicant is gone. */ | ||
610 | del_timer_sync(&app->join_timer); | ||
611 | garp_gid_event(app, GARP_EVENT_TRANSMIT_PDU); | ||
612 | garp_pdu_queue(app); | ||
613 | garp_queue_xmit(app); | ||
614 | |||
615 | dev_mc_delete(dev, appl->proto.group_address, ETH_ALEN, 0); | ||
616 | kfree(app); | ||
617 | garp_release_port(dev); | ||
618 | } | ||
619 | EXPORT_SYMBOL_GPL(garp_uninit_applicant); | ||
620 | |||
621 | int garp_register_application(struct garp_application *appl) | ||
622 | { | ||
623 | appl->proto.rcv = garp_pdu_rcv; | ||
624 | appl->proto.data = appl; | ||
625 | return stp_proto_register(&appl->proto); | ||
626 | } | ||
627 | EXPORT_SYMBOL_GPL(garp_register_application); | ||
628 | |||
629 | void garp_unregister_application(struct garp_application *appl) | ||
630 | { | ||
631 | stp_proto_unregister(&appl->proto); | ||
632 | } | ||
633 | EXPORT_SYMBOL_GPL(garp_unregister_application); | ||