Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
eca9ebac PM |
2 | /* |
3 | * IEEE 802.1D Generic Attribute Registration Protocol (GARP) | |
4 | * | |
5 | * Copyright (c) 2008 Patrick McHardy <kaber@trash.net> | |
eca9ebac PM |
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> | |
5a0e3ad6 | 14 | #include <linux/slab.h> |
3a9a231d | 15 | #include <linux/module.h> |
eca9ebac PM |
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)"); | |
ce1afe28 | 24 | MODULE_DESCRIPTION("IEEE 802.1D Generic Attribute Registration Protocol (GARP)"); |
eca9ebac PM |
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); | |
6f66cdc3 | 158 | if (d > 0) |
eca9ebac | 159 | parent = parent->rb_left; |
6f66cdc3 | 160 | else if (d < 0) |
eca9ebac PM |
161 | parent = parent->rb_right; |
162 | else | |
163 | return attr; | |
164 | } | |
165 | return NULL; | |
166 | } | |
167 | ||
67378563 DW |
168 | static struct garp_attr *garp_attr_create(struct garp_applicant *app, |
169 | const void *data, u8 len, u8 type) | |
eca9ebac PM |
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); | |
67378563 | 178 | d = garp_attr_cmp(attr, data, len, type); |
6f66cdc3 | 179 | if (d > 0) |
eca9ebac | 180 | p = &parent->rb_left; |
6f66cdc3 | 181 | else if (d < 0) |
eca9ebac | 182 | p = &parent->rb_right; |
67378563 DW |
183 | else { |
184 | /* The attribute already exists; re-use it. */ | |
185 | return attr; | |
186 | } | |
eca9ebac | 187 | } |
eca9ebac PM |
188 | attr = kmalloc(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); | |
67378563 DW |
195 | |
196 | rb_link_node(&attr->node, parent, p); | |
197 | rb_insert_color(&attr->node, &app->gid); | |
eca9ebac PM |
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(attr); | |
205 | } | |
206 | ||
42ca63f9 YY |
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 | ||
eca9ebac PM |
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(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 | ||
4df864c1 | 235 | gp = __skb_put(skb, sizeof(*gp)); |
eca9ebac PM |
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(app->pdu) < sizeof(u8)) | |
245 | return -1; | |
de77b966 | 246 | __skb_put_u8(app->pdu, GARP_END_MARK); |
eca9ebac PM |
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(app->pdu, LLC_PDU_TYPE_U, LLC_SAP_BSPAN, | |
259 | LLC_SAP_BSPAN, LLC_PDU_CMD); | |
260 | llc_pdu_init_as_ui_cmd(app->pdu); | |
261 | llc_mac_hdr_init(app->pdu, app->dev->dev_addr, | |
262 | app->app->proto.group_address); | |
263 | ||
264 | skb_queue_tail(&app->queue, 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(&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(app->pdu) < sizeof(*gm)) | |
281 | return -1; | |
4df864c1 | 282 | gm = __skb_put(app->pdu, sizeof(*gm)); |
eca9ebac PM |
283 | gm->attrtype = attrtype; |
284 | garp_cb(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(app->pdu)->cur_type != attr->type) { | |
303 | if (garp_cb(app->pdu)->cur_type && | |
304 | garp_pdu_append_end_mark(app) < 0) | |
305 | goto queue; | |
306 | if (garp_pdu_append_msg(app, attr->type) < 0) | |
307 | goto queue; | |
308 | } | |
309 | ||
310 | len = sizeof(*ga) + attr->dlen; | |
311 | if (skb_tailroom(app->pdu) < len) | |
312 | goto queue; | |
4df864c1 | 313 | ga = __skb_put(app->pdu, len); |
eca9ebac PM |
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: | |
51ce7ec9 PM |
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, GARP_JOIN_IN) < 0) | |
340 | return; | |
eca9ebac PM |
341 | break; |
342 | case GARP_ACTION_S_LEAVE_EMPTY: | |
343 | garp_pdu_append_attr(app, attr, 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 | { | |
3cc77ec7 ED |
359 | struct garp_port *port = rtnl_dereference(dev->garp_port); |
360 | struct garp_applicant *app = rtnl_dereference(port->applicants[appl->type]); | |
eca9ebac PM |
361 | struct garp_attr *attr; |
362 | ||
363 | spin_lock_bh(&app->lock); | |
364 | attr = garp_attr_create(app, data, len, type); | |
365 | if (!attr) { | |
366 | spin_unlock_bh(&app->lock); | |
367 | return -ENOMEM; | |
368 | } | |
369 | garp_attr_event(app, attr, GARP_EVENT_REQ_JOIN); | |
370 | spin_unlock_bh(&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 | { | |
3cc77ec7 ED |
379 | struct garp_port *port = rtnl_dereference(dev->garp_port); |
380 | struct garp_applicant *app = rtnl_dereference(port->applicants[appl->type]); | |
eca9ebac PM |
381 | struct garp_attr *attr; |
382 | ||
383 | spin_lock_bh(&app->lock); | |
384 | attr = garp_attr_lookup(app, data, len, type); | |
385 | if (!attr) { | |
386 | spin_unlock_bh(&app->lock); | |
387 | return; | |
388 | } | |
389 | garp_attr_event(app, attr, GARP_EVENT_REQ_LEAVE); | |
390 | spin_unlock_bh(&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 | ||
8032bf12 | 411 | delay = get_random_u32_below(msecs_to_jiffies(garp_join_time)); |
eca9ebac PM |
412 | mod_timer(&app->join_timer, jiffies + delay); |
413 | } | |
414 | ||
e99e88a9 | 415 | static void garp_join_timer(struct timer_list *t) |
eca9ebac | 416 | { |
e99e88a9 | 417 | struct garp_applicant *app = from_timer(app, t, join_timer); |
eca9ebac PM |
418 | |
419 | spin_lock(&app->lock); | |
420 | garp_gid_event(app, GARP_EVENT_TRANSMIT_PDU); | |
421 | garp_pdu_queue(app); | |
422 | spin_unlock(&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, sizeof(u8))) | |
431 | return -1; | |
432 | if (*skb->data == GARP_END_MARK) { | |
433 | skb_pull(skb, 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, 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, ga->len)) | |
454 | return -1; | |
455 | skb_pull(skb, 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, 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, ga->data, dlen, 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, 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, sizeof(*gm)); | |
502 | ||
503 | while (skb->len > 0) { | |
504 | if (garp_pdu_parse_attr(app, skb, 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, 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, sizeof(*gp)); | |
533 | ||
534 | spin_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(&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(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 | { | |
3cc77ec7 | 559 | struct garp_port *port = rtnl_dereference(dev->garp_port); |
eca9ebac PM |
560 | unsigned int i; |
561 | ||
562 | for (i = 0; i <= GARP_APPLICATION_MAX; i++) { | |
3cc77ec7 | 563 | if (rtnl_dereference(port->applicants[i])) |
eca9ebac PM |
564 | return; |
565 | } | |
a9b3cd7f | 566 | RCU_INIT_POINTER(dev->garp_port, NULL); |
0a384b22 | 567 | kfree_rcu(port, rcu); |
eca9ebac PM |
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 | ||
3cc77ec7 | 577 | if (!rtnl_dereference(dev->garp_port)) { |
eca9ebac PM |
578 | err = garp_init_port(dev); |
579 | if (err < 0) | |
580 | goto err1; | |
581 | } | |
582 | ||
583 | err = -ENOMEM; | |
584 | app = kzalloc(sizeof(*app), GFP_KERNEL); | |
585 | if (!app) | |
586 | goto err2; | |
587 | ||
22bedad3 | 588 | err = dev_mc_add(dev, appl->proto.group_address); |
eca9ebac PM |
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(&app->queue); | |
597 | rcu_assign_pointer(dev->garp_port->applicants[appl->type], app); | |
e99e88a9 | 598 | timer_setup(&app->join_timer, garp_join_timer, 0); |
eca9ebac PM |
599 | garp_join_timer_arm(app); |
600 | return 0; | |
601 | ||
602 | err3: | |
603 | kfree(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 | { | |
3cc77ec7 ED |
613 | struct garp_port *port = rtnl_dereference(dev->garp_port); |
614 | struct garp_applicant *app = rtnl_dereference(port->applicants[appl->type]); | |
eca9ebac PM |
615 | |
616 | ASSERT_RTNL(); | |
617 | ||
a9b3cd7f | 618 | RCU_INIT_POINTER(port->applicants[appl->type], NULL); |
eca9ebac PM |
619 | |
620 | /* Delete timer and generate a final TRANSMIT_PDU event to flush out | |
621 | * all pending messages before the applicant is gone. */ | |
292a089d | 622 | timer_shutdown_sync(&app->join_timer); |
cfbe800b CW |
623 | |
624 | spin_lock_bh(&app->lock); | |
eca9ebac | 625 | garp_gid_event(app, GARP_EVENT_TRANSMIT_PDU); |
42ca63f9 | 626 | garp_attr_destroy_all(app); |
eca9ebac | 627 | garp_pdu_queue(app); |
cfbe800b CW |
628 | spin_unlock_bh(&app->lock); |
629 | ||
eca9ebac PM |
630 | garp_queue_xmit(app); |
631 | ||
22bedad3 | 632 | dev_mc_del(dev, appl->proto.group_address); |
0a384b22 | 633 | kfree_rcu(app, rcu); |
eca9ebac PM |
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(&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(&appl->proto); | |
649 | } | |
650 | EXPORT_SYMBOL_GPL(garp_unregister_application); |