1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * IEEE 802.1D Generic Attribute Registration Protocol (GARP) |
4 | * |
5 | * Copyright (c) 2008 Patrick McHardy <kaber@trash.net> |
6 | */ |
7 | #include <linux/kernel.h> |
8 | #include <linux/timer.h> |
9 | #include <linux/skbuff.h> |
10 | #include <linux/netdevice.h> |
11 | #include <linux/etherdevice.h> |
12 | #include <linux/rtnetlink.h> |
13 | #include <linux/llc.h> |
14 | #include <linux/slab.h> |
15 | #include <linux/module.h> |
16 | #include <net/llc.h> |
17 | #include <net/llc_pdu.h> |
18 | #include <net/garp.h> |
19 | #include <asm/unaligned.h> |
20 | |
21 | static unsigned int garp_join_time __read_mostly = 200; |
22 | module_param(garp_join_time, uint, 0644); |
23 | MODULE_PARM_DESC(garp_join_time, "Join time in ms (default 200ms)" ); |
24 | MODULE_DESCRIPTION("IEEE 802.1D Generic Attribute Registration Protocol (GARP)" ); |
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(p: attr->data, q: data, size: 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 struct garp_attr *garp_attr_create(struct garp_applicant *app, |
169 | const void *data, u8 len, u8 type) |
170 | { |
171 | struct rb_node *parent = NULL, **p = &app->gid.rb_node; |
172 | struct garp_attr *attr; |
173 | int d; |
174 | |
175 | while (*p) { |
176 | parent = *p; |
177 | attr = rb_entry(parent, struct garp_attr, node); |
178 | d = garp_attr_cmp(attr, data, len, type); |
179 | if (d > 0) |
180 | p = &parent->rb_left; |
181 | else if (d < 0) |
182 | p = &parent->rb_right; |
183 | else { |
184 | /* The attribute already exists; re-use it. */ |
185 | return attr; |
186 | } |
187 | } |
188 | attr = kmalloc(size: sizeof(*attr) + len, GFP_ATOMIC); |
189 | if (!attr) |
190 | return attr; |
191 | attr->state = GARP_APPLICANT_VO; |
192 | attr->type = type; |
193 | attr->dlen = len; |
194 | memcpy(attr->data, data, len); |
195 | |
196 | rb_link_node(node: &attr->node, parent, rb_link: p); |
197 | rb_insert_color(&attr->node, &app->gid); |
198 | return attr; |
199 | } |
200 | |
201 | static void garp_attr_destroy(struct garp_applicant *app, struct garp_attr *attr) |
202 | { |
203 | rb_erase(&attr->node, &app->gid); |
204 | kfree(objp: attr); |
205 | } |
206 | |
207 | static void garp_attr_destroy_all(struct garp_applicant *app) |
208 | { |
209 | struct rb_node *node, *next; |
210 | struct garp_attr *attr; |
211 | |
212 | for (node = rb_first(&app->gid); |
213 | next = node ? rb_next(node) : NULL, node != NULL; |
214 | node = next) { |
215 | attr = rb_entry(node, struct garp_attr, node); |
216 | garp_attr_destroy(app, attr); |
217 | } |
218 | } |
219 | |
220 | static int garp_pdu_init(struct garp_applicant *app) |
221 | { |
222 | struct sk_buff *skb; |
223 | struct garp_pdu_hdr *gp; |
224 | |
225 | #define LLC_RESERVE sizeof(struct llc_pdu_un) |
226 | skb = alloc_skb(size: app->dev->mtu + LL_RESERVED_SPACE(app->dev), |
227 | GFP_ATOMIC); |
228 | if (!skb) |
229 | return -ENOMEM; |
230 | |
231 | skb->dev = app->dev; |
232 | skb->protocol = htons(ETH_P_802_2); |
233 | skb_reserve(skb, LL_RESERVED_SPACE(app->dev) + LLC_RESERVE); |
234 | |
235 | gp = __skb_put(skb, len: sizeof(*gp)); |
236 | put_unaligned(htons(GARP_PROTOCOL_ID), &gp->protocol); |
237 | |
238 | app->pdu = skb; |
239 | return 0; |
240 | } |
241 | |
242 | static int garp_pdu_append_end_mark(struct garp_applicant *app) |
243 | { |
244 | if (skb_tailroom(skb: app->pdu) < sizeof(u8)) |
245 | return -1; |
246 | __skb_put_u8(skb: app->pdu, GARP_END_MARK); |
247 | return 0; |
248 | } |
249 | |
250 | static void garp_pdu_queue(struct garp_applicant *app) |
251 | { |
252 | if (!app->pdu) |
253 | return; |
254 | |
255 | garp_pdu_append_end_mark(app); |
256 | garp_pdu_append_end_mark(app); |
257 | |
258 | llc_pdu_header_init(skb: app->pdu, LLC_PDU_TYPE_U, LLC_SAP_BSPAN, |
259 | LLC_SAP_BSPAN, LLC_PDU_CMD); |
260 | llc_pdu_init_as_ui_cmd(skb: app->pdu); |
261 | llc_mac_hdr_init(skb: app->pdu, sa: app->dev->dev_addr, |
262 | da: app->app->proto.group_address); |
263 | |
264 | skb_queue_tail(list: &app->queue, newsk: app->pdu); |
265 | app->pdu = NULL; |
266 | } |
267 | |
268 | static void garp_queue_xmit(struct garp_applicant *app) |
269 | { |
270 | struct sk_buff *skb; |
271 | |
272 | while ((skb = skb_dequeue(list: &app->queue))) |
273 | dev_queue_xmit(skb); |
274 | } |
275 | |
276 | static int garp_pdu_append_msg(struct garp_applicant *app, u8 attrtype) |
277 | { |
278 | struct garp_msg_hdr *gm; |
279 | |
280 | if (skb_tailroom(skb: app->pdu) < sizeof(*gm)) |
281 | return -1; |
282 | gm = __skb_put(skb: app->pdu, len: sizeof(*gm)); |
283 | gm->attrtype = attrtype; |
284 | garp_cb(skb: app->pdu)->cur_type = attrtype; |
285 | return 0; |
286 | } |
287 | |
288 | static int garp_pdu_append_attr(struct garp_applicant *app, |
289 | const struct garp_attr *attr, |
290 | enum garp_attr_event event) |
291 | { |
292 | struct garp_attr_hdr *ga; |
293 | unsigned int len; |
294 | int err; |
295 | again: |
296 | if (!app->pdu) { |
297 | err = garp_pdu_init(app); |
298 | if (err < 0) |
299 | return err; |
300 | } |
301 | |
302 | if (garp_cb(skb: app->pdu)->cur_type != attr->type) { |
303 | if (garp_cb(skb: app->pdu)->cur_type && |
304 | garp_pdu_append_end_mark(app) < 0) |
305 | goto queue; |
306 | if (garp_pdu_append_msg(app, attrtype: attr->type) < 0) |
307 | goto queue; |
308 | } |
309 | |
310 | len = sizeof(*ga) + attr->dlen; |
311 | if (skb_tailroom(skb: app->pdu) < len) |
312 | goto queue; |
313 | ga = __skb_put(skb: app->pdu, len); |
314 | ga->len = len; |
315 | ga->event = event; |
316 | memcpy(ga->data, attr->data, attr->dlen); |
317 | return 0; |
318 | |
319 | queue: |
320 | garp_pdu_queue(app); |
321 | goto again; |
322 | } |
323 | |
324 | static void garp_attr_event(struct garp_applicant *app, |
325 | struct garp_attr *attr, enum garp_event event) |
326 | { |
327 | enum garp_applicant_state state; |
328 | |
329 | state = garp_applicant_state_table[attr->state][event].state; |
330 | if (state == GARP_APPLICANT_INVALID) |
331 | return; |
332 | |
333 | switch (garp_applicant_state_table[attr->state][event].action) { |
334 | case GARP_ACTION_NONE: |
335 | break; |
336 | case GARP_ACTION_S_JOIN_IN: |
337 | /* When appending the attribute fails, don't update state in |
338 | * order to retry on next TRANSMIT_PDU event. */ |
339 | if (garp_pdu_append_attr(app, attr, event: GARP_JOIN_IN) < 0) |
340 | return; |
341 | break; |
342 | case GARP_ACTION_S_LEAVE_EMPTY: |
343 | garp_pdu_append_attr(app, attr, event: GARP_LEAVE_EMPTY); |
344 | /* As a pure applicant, sending a leave message implies that |
345 | * the attribute was unregistered and can be destroyed. */ |
346 | garp_attr_destroy(app, attr); |
347 | return; |
348 | default: |
349 | WARN_ON(1); |
350 | } |
351 | |
352 | attr->state = state; |
353 | } |
354 | |
355 | int garp_request_join(const struct net_device *dev, |
356 | const struct garp_application *appl, |
357 | const void *data, u8 len, u8 type) |
358 | { |
359 | struct garp_port *port = rtnl_dereference(dev->garp_port); |
360 | struct garp_applicant *app = rtnl_dereference(port->applicants[appl->type]); |
361 | struct garp_attr *attr; |
362 | |
363 | spin_lock_bh(lock: &app->lock); |
364 | attr = garp_attr_create(app, data, len, type); |
365 | if (!attr) { |
366 | spin_unlock_bh(lock: &app->lock); |
367 | return -ENOMEM; |
368 | } |
369 | garp_attr_event(app, attr, event: GARP_EVENT_REQ_JOIN); |
370 | spin_unlock_bh(lock: &app->lock); |
371 | return 0; |
372 | } |
373 | EXPORT_SYMBOL_GPL(garp_request_join); |
374 | |
375 | void garp_request_leave(const struct net_device *dev, |
376 | const struct garp_application *appl, |
377 | const void *data, u8 len, u8 type) |
378 | { |
379 | struct garp_port *port = rtnl_dereference(dev->garp_port); |
380 | struct garp_applicant *app = rtnl_dereference(port->applicants[appl->type]); |
381 | struct garp_attr *attr; |
382 | |
383 | spin_lock_bh(lock: &app->lock); |
384 | attr = garp_attr_lookup(app, data, len, type); |
385 | if (!attr) { |
386 | spin_unlock_bh(lock: &app->lock); |
387 | return; |
388 | } |
389 | garp_attr_event(app, attr, event: GARP_EVENT_REQ_LEAVE); |
390 | spin_unlock_bh(lock: &app->lock); |
391 | } |
392 | EXPORT_SYMBOL_GPL(garp_request_leave); |
393 | |
394 | static void garp_gid_event(struct garp_applicant *app, enum garp_event event) |
395 | { |
396 | struct rb_node *node, *next; |
397 | struct garp_attr *attr; |
398 | |
399 | for (node = rb_first(&app->gid); |
400 | next = node ? rb_next(node) : NULL, node != NULL; |
401 | node = next) { |
402 | attr = rb_entry(node, struct garp_attr, node); |
403 | garp_attr_event(app, attr, event); |
404 | } |
405 | } |
406 | |
407 | static void garp_join_timer_arm(struct garp_applicant *app) |
408 | { |
409 | unsigned long delay; |
410 | |
411 | delay = get_random_u32_below(ceil: msecs_to_jiffies(m: garp_join_time)); |
412 | mod_timer(timer: &app->join_timer, expires: jiffies + delay); |
413 | } |
414 | |
415 | static void garp_join_timer(struct timer_list *t) |
416 | { |
417 | struct garp_applicant *app = from_timer(app, t, join_timer); |
418 | |
419 | spin_lock(lock: &app->lock); |
420 | garp_gid_event(app, event: GARP_EVENT_TRANSMIT_PDU); |
421 | garp_pdu_queue(app); |
422 | spin_unlock(lock: &app->lock); |
423 | |
424 | garp_queue_xmit(app); |
425 | garp_join_timer_arm(app); |
426 | } |
427 | |
428 | static int garp_pdu_parse_end_mark(struct sk_buff *skb) |
429 | { |
430 | if (!pskb_may_pull(skb, len: sizeof(u8))) |
431 | return -1; |
432 | if (*skb->data == GARP_END_MARK) { |
433 | skb_pull(skb, len: sizeof(u8)); |
434 | return -1; |
435 | } |
436 | return 0; |
437 | } |
438 | |
439 | static int garp_pdu_parse_attr(struct garp_applicant *app, struct sk_buff *skb, |
440 | u8 attrtype) |
441 | { |
442 | const struct garp_attr_hdr *ga; |
443 | struct garp_attr *attr; |
444 | enum garp_event event; |
445 | unsigned int dlen; |
446 | |
447 | if (!pskb_may_pull(skb, len: sizeof(*ga))) |
448 | return -1; |
449 | ga = (struct garp_attr_hdr *)skb->data; |
450 | if (ga->len < sizeof(*ga)) |
451 | return -1; |
452 | |
453 | if (!pskb_may_pull(skb, len: ga->len)) |
454 | return -1; |
455 | skb_pull(skb, len: ga->len); |
456 | dlen = sizeof(*ga) - ga->len; |
457 | |
458 | if (attrtype > app->app->maxattr) |
459 | return 0; |
460 | |
461 | switch (ga->event) { |
462 | case GARP_LEAVE_ALL: |
463 | if (dlen != 0) |
464 | return -1; |
465 | garp_gid_event(app, event: GARP_EVENT_R_LEAVE_EMPTY); |
466 | return 0; |
467 | case GARP_JOIN_EMPTY: |
468 | event = GARP_EVENT_R_JOIN_EMPTY; |
469 | break; |
470 | case GARP_JOIN_IN: |
471 | event = GARP_EVENT_R_JOIN_IN; |
472 | break; |
473 | case GARP_LEAVE_EMPTY: |
474 | event = GARP_EVENT_R_LEAVE_EMPTY; |
475 | break; |
476 | case GARP_EMPTY: |
477 | event = GARP_EVENT_R_EMPTY; |
478 | break; |
479 | default: |
480 | return 0; |
481 | } |
482 | |
483 | if (dlen == 0) |
484 | return -1; |
485 | attr = garp_attr_lookup(app, data: ga->data, len: dlen, type: attrtype); |
486 | if (attr == NULL) |
487 | return 0; |
488 | garp_attr_event(app, attr, event); |
489 | return 0; |
490 | } |
491 | |
492 | static int garp_pdu_parse_msg(struct garp_applicant *app, struct sk_buff *skb) |
493 | { |
494 | const struct garp_msg_hdr *gm; |
495 | |
496 | if (!pskb_may_pull(skb, len: sizeof(*gm))) |
497 | return -1; |
498 | gm = (struct garp_msg_hdr *)skb->data; |
499 | if (gm->attrtype == 0) |
500 | return -1; |
501 | skb_pull(skb, len: sizeof(*gm)); |
502 | |
503 | while (skb->len > 0) { |
504 | if (garp_pdu_parse_attr(app, skb, attrtype: gm->attrtype) < 0) |
505 | return -1; |
506 | if (garp_pdu_parse_end_mark(skb) < 0) |
507 | break; |
508 | } |
509 | return 0; |
510 | } |
511 | |
512 | static void garp_pdu_rcv(const struct stp_proto *proto, struct sk_buff *skb, |
513 | struct net_device *dev) |
514 | { |
515 | struct garp_application *appl = proto->data; |
516 | struct garp_port *port; |
517 | struct garp_applicant *app; |
518 | const struct garp_pdu_hdr *gp; |
519 | |
520 | port = rcu_dereference(dev->garp_port); |
521 | if (!port) |
522 | goto err; |
523 | app = rcu_dereference(port->applicants[appl->type]); |
524 | if (!app) |
525 | goto err; |
526 | |
527 | if (!pskb_may_pull(skb, len: sizeof(*gp))) |
528 | goto err; |
529 | gp = (struct garp_pdu_hdr *)skb->data; |
530 | if (get_unaligned(&gp->protocol) != htons(GARP_PROTOCOL_ID)) |
531 | goto err; |
532 | skb_pull(skb, len: sizeof(*gp)); |
533 | |
534 | spin_lock(lock: &app->lock); |
535 | while (skb->len > 0) { |
536 | if (garp_pdu_parse_msg(app, skb) < 0) |
537 | break; |
538 | if (garp_pdu_parse_end_mark(skb) < 0) |
539 | break; |
540 | } |
541 | spin_unlock(lock: &app->lock); |
542 | err: |
543 | kfree_skb(skb); |
544 | } |
545 | |
546 | static int garp_init_port(struct net_device *dev) |
547 | { |
548 | struct garp_port *port; |
549 | |
550 | port = kzalloc(size: sizeof(*port), GFP_KERNEL); |
551 | if (!port) |
552 | return -ENOMEM; |
553 | rcu_assign_pointer(dev->garp_port, port); |
554 | return 0; |
555 | } |
556 | |
557 | static void garp_release_port(struct net_device *dev) |
558 | { |
559 | struct garp_port *port = rtnl_dereference(dev->garp_port); |
560 | unsigned int i; |
561 | |
562 | for (i = 0; i <= GARP_APPLICATION_MAX; i++) { |
563 | if (rtnl_dereference(port->applicants[i])) |
564 | return; |
565 | } |
566 | RCU_INIT_POINTER(dev->garp_port, NULL); |
567 | kfree_rcu(port, rcu); |
568 | } |
569 | |
570 | int garp_init_applicant(struct net_device *dev, struct garp_application *appl) |
571 | { |
572 | struct garp_applicant *app; |
573 | int err; |
574 | |
575 | ASSERT_RTNL(); |
576 | |
577 | if (!rtnl_dereference(dev->garp_port)) { |
578 | err = garp_init_port(dev); |
579 | if (err < 0) |
580 | goto err1; |
581 | } |
582 | |
583 | err = -ENOMEM; |
584 | app = kzalloc(size: sizeof(*app), GFP_KERNEL); |
585 | if (!app) |
586 | goto err2; |
587 | |
588 | err = dev_mc_add(dev, addr: appl->proto.group_address); |
589 | if (err < 0) |
590 | goto err3; |
591 | |
592 | app->dev = dev; |
593 | app->app = appl; |
594 | app->gid = RB_ROOT; |
595 | spin_lock_init(&app->lock); |
596 | skb_queue_head_init(list: &app->queue); |
597 | rcu_assign_pointer(dev->garp_port->applicants[appl->type], app); |
598 | timer_setup(&app->join_timer, garp_join_timer, 0); |
599 | garp_join_timer_arm(app); |
600 | return 0; |
601 | |
602 | err3: |
603 | kfree(objp: app); |
604 | err2: |
605 | garp_release_port(dev); |
606 | err1: |
607 | return err; |
608 | } |
609 | EXPORT_SYMBOL_GPL(garp_init_applicant); |
610 | |
611 | void garp_uninit_applicant(struct net_device *dev, struct garp_application *appl) |
612 | { |
613 | struct garp_port *port = rtnl_dereference(dev->garp_port); |
614 | struct garp_applicant *app = rtnl_dereference(port->applicants[appl->type]); |
615 | |
616 | ASSERT_RTNL(); |
617 | |
618 | RCU_INIT_POINTER(port->applicants[appl->type], NULL); |
619 | |
620 | /* Delete timer and generate a final TRANSMIT_PDU event to flush out |
621 | * all pending messages before the applicant is gone. */ |
622 | timer_shutdown_sync(timer: &app->join_timer); |
623 | |
624 | spin_lock_bh(lock: &app->lock); |
625 | garp_gid_event(app, event: GARP_EVENT_TRANSMIT_PDU); |
626 | garp_attr_destroy_all(app); |
627 | garp_pdu_queue(app); |
628 | spin_unlock_bh(lock: &app->lock); |
629 | |
630 | garp_queue_xmit(app); |
631 | |
632 | dev_mc_del(dev, addr: appl->proto.group_address); |
633 | kfree_rcu(app, rcu); |
634 | garp_release_port(dev); |
635 | } |
636 | EXPORT_SYMBOL_GPL(garp_uninit_applicant); |
637 | |
638 | int garp_register_application(struct garp_application *appl) |
639 | { |
640 | appl->proto.rcv = garp_pdu_rcv; |
641 | appl->proto.data = appl; |
642 | return stp_proto_register(proto: &appl->proto); |
643 | } |
644 | EXPORT_SYMBOL_GPL(garp_register_application); |
645 | |
646 | void garp_unregister_application(struct garp_application *appl) |
647 | { |
648 | stp_proto_unregister(proto: &appl->proto); |
649 | } |
650 | EXPORT_SYMBOL_GPL(garp_unregister_application); |
651 | |