Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/net/802/garp.c
26278 views
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 <[email protected]>
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 <linux/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(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 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(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(&attr->node, parent, 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(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(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, 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(app->pdu) < sizeof(u8))
245
return -1;
246
__skb_put_u8(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(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;
282
gm = __skb_put(app->pdu, sizeof(*gm));
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;
313
ga = __skb_put(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, GARP_JOIN_IN) < 0)
340
return;
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
{
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(&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
{
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(&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
411
delay = get_random_u32_below(msecs_to_jiffies(garp_join_time));
412
mod_timer(&app->join_timer, jiffies + delay);
413
}
414
415
static void garp_join_timer(struct timer_list *t)
416
{
417
struct garp_applicant *app = timer_container_of(app, t, join_timer);
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
{
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(sizeof(*app), GFP_KERNEL);
585
if (!app)
586
goto err2;
587
588
err = dev_mc_add(dev, 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(&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(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(&app->join_timer);
623
624
spin_lock_bh(&app->lock);
625
garp_gid_event(app, GARP_EVENT_TRANSMIT_PDU);
626
garp_attr_destroy_all(app);
627
garp_pdu_queue(app);
628
spin_unlock_bh(&app->lock);
629
630
garp_queue_xmit(app);
631
632
dev_mc_del(dev, 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(&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);
651
652