1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2eca9ebacSPatrick McHardy /*
3eca9ebacSPatrick McHardy * IEEE 802.1D Generic Attribute Registration Protocol (GARP)
4eca9ebacSPatrick McHardy *
5eca9ebacSPatrick McHardy * Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
6eca9ebacSPatrick McHardy */
7eca9ebacSPatrick McHardy #include <linux/kernel.h>
8eca9ebacSPatrick McHardy #include <linux/timer.h>
9eca9ebacSPatrick McHardy #include <linux/skbuff.h>
10eca9ebacSPatrick McHardy #include <linux/netdevice.h>
11eca9ebacSPatrick McHardy #include <linux/etherdevice.h>
12eca9ebacSPatrick McHardy #include <linux/rtnetlink.h>
13eca9ebacSPatrick McHardy #include <linux/llc.h>
145a0e3ad6STejun Heo #include <linux/slab.h>
153a9a231dSPaul Gortmaker #include <linux/module.h>
16eca9ebacSPatrick McHardy #include <net/llc.h>
17eca9ebacSPatrick McHardy #include <net/llc_pdu.h>
18eca9ebacSPatrick McHardy #include <net/garp.h>
19eca9ebacSPatrick McHardy #include <asm/unaligned.h>
20eca9ebacSPatrick McHardy
21eca9ebacSPatrick McHardy static unsigned int garp_join_time __read_mostly = 200;
22eca9ebacSPatrick McHardy module_param(garp_join_time, uint, 0644);
23eca9ebacSPatrick McHardy MODULE_PARM_DESC(garp_join_time, "Join time in ms (default 200ms)");
24eca9ebacSPatrick McHardy MODULE_LICENSE("GPL");
25eca9ebacSPatrick McHardy
26eca9ebacSPatrick McHardy static const struct garp_state_trans {
27eca9ebacSPatrick McHardy u8 state;
28eca9ebacSPatrick McHardy u8 action;
29eca9ebacSPatrick McHardy } garp_applicant_state_table[GARP_APPLICANT_MAX + 1][GARP_EVENT_MAX + 1] = {
30eca9ebacSPatrick McHardy [GARP_APPLICANT_VA] = {
31eca9ebacSPatrick McHardy [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_AA,
32eca9ebacSPatrick McHardy .action = GARP_ACTION_S_JOIN_IN },
33eca9ebacSPatrick McHardy [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_AA },
34eca9ebacSPatrick McHardy [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VA },
35eca9ebacSPatrick McHardy [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VA },
36eca9ebacSPatrick McHardy [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VA },
37eca9ebacSPatrick McHardy [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP },
38eca9ebacSPatrick McHardy [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID },
39eca9ebacSPatrick McHardy [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_LA },
40eca9ebacSPatrick McHardy },
41eca9ebacSPatrick McHardy [GARP_APPLICANT_AA] = {
42eca9ebacSPatrick McHardy [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_QA,
43eca9ebacSPatrick McHardy .action = GARP_ACTION_S_JOIN_IN },
44eca9ebacSPatrick McHardy [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QA },
45eca9ebacSPatrick McHardy [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VA },
46eca9ebacSPatrick McHardy [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VA },
47eca9ebacSPatrick McHardy [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VA },
48eca9ebacSPatrick McHardy [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP },
49eca9ebacSPatrick McHardy [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID },
50eca9ebacSPatrick McHardy [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_LA },
51eca9ebacSPatrick McHardy },
52eca9ebacSPatrick McHardy [GARP_APPLICANT_QA] = {
53eca9ebacSPatrick McHardy [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_INVALID },
54eca9ebacSPatrick McHardy [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QA },
55eca9ebacSPatrick McHardy [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VA },
56eca9ebacSPatrick McHardy [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VA },
57eca9ebacSPatrick McHardy [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VP },
58eca9ebacSPatrick McHardy [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP },
59eca9ebacSPatrick McHardy [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID },
60eca9ebacSPatrick McHardy [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_LA },
61eca9ebacSPatrick McHardy },
62eca9ebacSPatrick McHardy [GARP_APPLICANT_LA] = {
63eca9ebacSPatrick McHardy [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_VO,
64eca9ebacSPatrick McHardy .action = GARP_ACTION_S_LEAVE_EMPTY },
65eca9ebacSPatrick McHardy [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_LA },
66eca9ebacSPatrick McHardy [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VO },
67eca9ebacSPatrick McHardy [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_LA },
68eca9ebacSPatrick McHardy [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_LA },
69eca9ebacSPatrick McHardy [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VO },
70eca9ebacSPatrick McHardy [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_VA },
71eca9ebacSPatrick McHardy [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_INVALID },
72eca9ebacSPatrick McHardy },
73eca9ebacSPatrick McHardy [GARP_APPLICANT_VP] = {
74eca9ebacSPatrick McHardy [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_AA,
75eca9ebacSPatrick McHardy .action = GARP_ACTION_S_JOIN_IN },
76eca9ebacSPatrick McHardy [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_AP },
77eca9ebacSPatrick McHardy [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VP },
78eca9ebacSPatrick McHardy [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VP },
79eca9ebacSPatrick McHardy [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VP },
80eca9ebacSPatrick McHardy [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP },
81eca9ebacSPatrick McHardy [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID },
82eca9ebacSPatrick McHardy [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_VO },
83eca9ebacSPatrick McHardy },
84eca9ebacSPatrick McHardy [GARP_APPLICANT_AP] = {
85eca9ebacSPatrick McHardy [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_QA,
86eca9ebacSPatrick McHardy .action = GARP_ACTION_S_JOIN_IN },
87eca9ebacSPatrick McHardy [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QP },
88eca9ebacSPatrick McHardy [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VP },
89eca9ebacSPatrick McHardy [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VP },
90eca9ebacSPatrick McHardy [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VP },
91eca9ebacSPatrick McHardy [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP },
92eca9ebacSPatrick McHardy [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID },
93eca9ebacSPatrick McHardy [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_AO },
94eca9ebacSPatrick McHardy },
95eca9ebacSPatrick McHardy [GARP_APPLICANT_QP] = {
96eca9ebacSPatrick McHardy [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_INVALID },
97eca9ebacSPatrick McHardy [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QP },
98eca9ebacSPatrick McHardy [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VP },
99eca9ebacSPatrick McHardy [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VP },
100eca9ebacSPatrick McHardy [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VP },
101eca9ebacSPatrick McHardy [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP },
102eca9ebacSPatrick McHardy [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID },
103eca9ebacSPatrick McHardy [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_QO },
104eca9ebacSPatrick McHardy },
105eca9ebacSPatrick McHardy [GARP_APPLICANT_VO] = {
106eca9ebacSPatrick McHardy [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_INVALID },
107eca9ebacSPatrick McHardy [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_AO },
108eca9ebacSPatrick McHardy [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VO },
109eca9ebacSPatrick McHardy [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VO },
110eca9ebacSPatrick McHardy [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VO },
111eca9ebacSPatrick McHardy [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VO },
112eca9ebacSPatrick McHardy [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_VP },
113eca9ebacSPatrick McHardy [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_INVALID },
114eca9ebacSPatrick McHardy },
115eca9ebacSPatrick McHardy [GARP_APPLICANT_AO] = {
116eca9ebacSPatrick McHardy [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_INVALID },
117eca9ebacSPatrick McHardy [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QO },
118eca9ebacSPatrick McHardy [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VO },
119eca9ebacSPatrick McHardy [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VO },
120eca9ebacSPatrick McHardy [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VO },
121eca9ebacSPatrick McHardy [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VO },
122eca9ebacSPatrick McHardy [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_AP },
123eca9ebacSPatrick McHardy [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_INVALID },
124eca9ebacSPatrick McHardy },
125eca9ebacSPatrick McHardy [GARP_APPLICANT_QO] = {
126eca9ebacSPatrick McHardy [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_INVALID },
127eca9ebacSPatrick McHardy [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QO },
128eca9ebacSPatrick McHardy [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VO },
129eca9ebacSPatrick McHardy [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VO },
130eca9ebacSPatrick McHardy [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VO },
131eca9ebacSPatrick McHardy [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VO },
132eca9ebacSPatrick McHardy [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_QP },
133eca9ebacSPatrick McHardy [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_INVALID },
134eca9ebacSPatrick McHardy },
135eca9ebacSPatrick McHardy };
136eca9ebacSPatrick McHardy
garp_attr_cmp(const struct garp_attr * attr,const void * data,u8 len,u8 type)137eca9ebacSPatrick McHardy static int garp_attr_cmp(const struct garp_attr *attr,
138eca9ebacSPatrick McHardy const void *data, u8 len, u8 type)
139eca9ebacSPatrick McHardy {
140eca9ebacSPatrick McHardy if (attr->type != type)
141eca9ebacSPatrick McHardy return attr->type - type;
142eca9ebacSPatrick McHardy if (attr->dlen != len)
143eca9ebacSPatrick McHardy return attr->dlen - len;
144eca9ebacSPatrick McHardy return memcmp(attr->data, data, len);
145eca9ebacSPatrick McHardy }
146eca9ebacSPatrick McHardy
garp_attr_lookup(const struct garp_applicant * app,const void * data,u8 len,u8 type)147eca9ebacSPatrick McHardy static struct garp_attr *garp_attr_lookup(const struct garp_applicant *app,
148eca9ebacSPatrick McHardy const void *data, u8 len, u8 type)
149eca9ebacSPatrick McHardy {
150eca9ebacSPatrick McHardy struct rb_node *parent = app->gid.rb_node;
151eca9ebacSPatrick McHardy struct garp_attr *attr;
152eca9ebacSPatrick McHardy int d;
153eca9ebacSPatrick McHardy
154eca9ebacSPatrick McHardy while (parent) {
155eca9ebacSPatrick McHardy attr = rb_entry(parent, struct garp_attr, node);
156eca9ebacSPatrick McHardy d = garp_attr_cmp(attr, data, len, type);
1576f66cdc3SDavid Ward if (d > 0)
158eca9ebacSPatrick McHardy parent = parent->rb_left;
1596f66cdc3SDavid Ward else if (d < 0)
160eca9ebacSPatrick McHardy parent = parent->rb_right;
161eca9ebacSPatrick McHardy else
162eca9ebacSPatrick McHardy return attr;
163eca9ebacSPatrick McHardy }
164eca9ebacSPatrick McHardy return NULL;
165eca9ebacSPatrick McHardy }
166eca9ebacSPatrick McHardy
garp_attr_create(struct garp_applicant * app,const void * data,u8 len,u8 type)16767378563SDavid Ward static struct garp_attr *garp_attr_create(struct garp_applicant *app,
16867378563SDavid Ward const void *data, u8 len, u8 type)
169eca9ebacSPatrick McHardy {
170eca9ebacSPatrick McHardy struct rb_node *parent = NULL, **p = &app->gid.rb_node;
171eca9ebacSPatrick McHardy struct garp_attr *attr;
172eca9ebacSPatrick McHardy int d;
173eca9ebacSPatrick McHardy
174eca9ebacSPatrick McHardy while (*p) {
175eca9ebacSPatrick McHardy parent = *p;
176eca9ebacSPatrick McHardy attr = rb_entry(parent, struct garp_attr, node);
17767378563SDavid Ward d = garp_attr_cmp(attr, data, len, type);
1786f66cdc3SDavid Ward if (d > 0)
179eca9ebacSPatrick McHardy p = &parent->rb_left;
1806f66cdc3SDavid Ward else if (d < 0)
181eca9ebacSPatrick McHardy p = &parent->rb_right;
18267378563SDavid Ward else {
18367378563SDavid Ward /* The attribute already exists; re-use it. */
18467378563SDavid Ward return attr;
185eca9ebacSPatrick McHardy }
186eca9ebacSPatrick McHardy }
187eca9ebacSPatrick McHardy attr = kmalloc(sizeof(*attr) + len, GFP_ATOMIC);
188eca9ebacSPatrick McHardy if (!attr)
189eca9ebacSPatrick McHardy return attr;
190eca9ebacSPatrick McHardy attr->state = GARP_APPLICANT_VO;
191eca9ebacSPatrick McHardy attr->type = type;
192eca9ebacSPatrick McHardy attr->dlen = len;
193eca9ebacSPatrick McHardy memcpy(attr->data, data, len);
19467378563SDavid Ward
19567378563SDavid Ward rb_link_node(&attr->node, parent, p);
19667378563SDavid Ward rb_insert_color(&attr->node, &app->gid);
197eca9ebacSPatrick McHardy return attr;
198eca9ebacSPatrick McHardy }
199eca9ebacSPatrick McHardy
garp_attr_destroy(struct garp_applicant * app,struct garp_attr * attr)200eca9ebacSPatrick McHardy static void garp_attr_destroy(struct garp_applicant *app, struct garp_attr *attr)
201eca9ebacSPatrick McHardy {
202eca9ebacSPatrick McHardy rb_erase(&attr->node, &app->gid);
203eca9ebacSPatrick McHardy kfree(attr);
204eca9ebacSPatrick McHardy }
205eca9ebacSPatrick McHardy
garp_attr_destroy_all(struct garp_applicant * app)20642ca63f9SYang Yingliang static void garp_attr_destroy_all(struct garp_applicant *app)
20742ca63f9SYang Yingliang {
20842ca63f9SYang Yingliang struct rb_node *node, *next;
20942ca63f9SYang Yingliang struct garp_attr *attr;
21042ca63f9SYang Yingliang
21142ca63f9SYang Yingliang for (node = rb_first(&app->gid);
21242ca63f9SYang Yingliang next = node ? rb_next(node) : NULL, node != NULL;
21342ca63f9SYang Yingliang node = next) {
21442ca63f9SYang Yingliang attr = rb_entry(node, struct garp_attr, node);
21542ca63f9SYang Yingliang garp_attr_destroy(app, attr);
21642ca63f9SYang Yingliang }
21742ca63f9SYang Yingliang }
21842ca63f9SYang Yingliang
garp_pdu_init(struct garp_applicant * app)219eca9ebacSPatrick McHardy static int garp_pdu_init(struct garp_applicant *app)
220eca9ebacSPatrick McHardy {
221eca9ebacSPatrick McHardy struct sk_buff *skb;
222eca9ebacSPatrick McHardy struct garp_pdu_hdr *gp;
223eca9ebacSPatrick McHardy
224eca9ebacSPatrick McHardy #define LLC_RESERVE sizeof(struct llc_pdu_un)
225eca9ebacSPatrick McHardy skb = alloc_skb(app->dev->mtu + LL_RESERVED_SPACE(app->dev),
226eca9ebacSPatrick McHardy GFP_ATOMIC);
227eca9ebacSPatrick McHardy if (!skb)
228eca9ebacSPatrick McHardy return -ENOMEM;
229eca9ebacSPatrick McHardy
230eca9ebacSPatrick McHardy skb->dev = app->dev;
231eca9ebacSPatrick McHardy skb->protocol = htons(ETH_P_802_2);
232eca9ebacSPatrick McHardy skb_reserve(skb, LL_RESERVED_SPACE(app->dev) + LLC_RESERVE);
233eca9ebacSPatrick McHardy
2344df864c1SJohannes Berg gp = __skb_put(skb, sizeof(*gp));
235eca9ebacSPatrick McHardy put_unaligned(htons(GARP_PROTOCOL_ID), &gp->protocol);
236eca9ebacSPatrick McHardy
237eca9ebacSPatrick McHardy app->pdu = skb;
238eca9ebacSPatrick McHardy return 0;
239eca9ebacSPatrick McHardy }
240eca9ebacSPatrick McHardy
garp_pdu_append_end_mark(struct garp_applicant * app)241eca9ebacSPatrick McHardy static int garp_pdu_append_end_mark(struct garp_applicant *app)
242eca9ebacSPatrick McHardy {
243eca9ebacSPatrick McHardy if (skb_tailroom(app->pdu) < sizeof(u8))
244eca9ebacSPatrick McHardy return -1;
245de77b966Syuan linyu __skb_put_u8(app->pdu, GARP_END_MARK);
246eca9ebacSPatrick McHardy return 0;
247eca9ebacSPatrick McHardy }
248eca9ebacSPatrick McHardy
garp_pdu_queue(struct garp_applicant * app)249eca9ebacSPatrick McHardy static void garp_pdu_queue(struct garp_applicant *app)
250eca9ebacSPatrick McHardy {
251eca9ebacSPatrick McHardy if (!app->pdu)
252eca9ebacSPatrick McHardy return;
253eca9ebacSPatrick McHardy
254eca9ebacSPatrick McHardy garp_pdu_append_end_mark(app);
255eca9ebacSPatrick McHardy garp_pdu_append_end_mark(app);
256eca9ebacSPatrick McHardy
257eca9ebacSPatrick McHardy llc_pdu_header_init(app->pdu, LLC_PDU_TYPE_U, LLC_SAP_BSPAN,
258eca9ebacSPatrick McHardy LLC_SAP_BSPAN, LLC_PDU_CMD);
259eca9ebacSPatrick McHardy llc_pdu_init_as_ui_cmd(app->pdu);
260eca9ebacSPatrick McHardy llc_mac_hdr_init(app->pdu, app->dev->dev_addr,
261eca9ebacSPatrick McHardy app->app->proto.group_address);
262eca9ebacSPatrick McHardy
263eca9ebacSPatrick McHardy skb_queue_tail(&app->queue, app->pdu);
264eca9ebacSPatrick McHardy app->pdu = NULL;
265eca9ebacSPatrick McHardy }
266eca9ebacSPatrick McHardy
garp_queue_xmit(struct garp_applicant * app)267eca9ebacSPatrick McHardy static void garp_queue_xmit(struct garp_applicant *app)
268eca9ebacSPatrick McHardy {
269eca9ebacSPatrick McHardy struct sk_buff *skb;
270eca9ebacSPatrick McHardy
271eca9ebacSPatrick McHardy while ((skb = skb_dequeue(&app->queue)))
272eca9ebacSPatrick McHardy dev_queue_xmit(skb);
273eca9ebacSPatrick McHardy }
274eca9ebacSPatrick McHardy
garp_pdu_append_msg(struct garp_applicant * app,u8 attrtype)275eca9ebacSPatrick McHardy static int garp_pdu_append_msg(struct garp_applicant *app, u8 attrtype)
276eca9ebacSPatrick McHardy {
277eca9ebacSPatrick McHardy struct garp_msg_hdr *gm;
278eca9ebacSPatrick McHardy
279eca9ebacSPatrick McHardy if (skb_tailroom(app->pdu) < sizeof(*gm))
280eca9ebacSPatrick McHardy return -1;
2814df864c1SJohannes Berg gm = __skb_put(app->pdu, sizeof(*gm));
282eca9ebacSPatrick McHardy gm->attrtype = attrtype;
283eca9ebacSPatrick McHardy garp_cb(app->pdu)->cur_type = attrtype;
284eca9ebacSPatrick McHardy return 0;
285eca9ebacSPatrick McHardy }
286eca9ebacSPatrick McHardy
garp_pdu_append_attr(struct garp_applicant * app,const struct garp_attr * attr,enum garp_attr_event event)287eca9ebacSPatrick McHardy static int garp_pdu_append_attr(struct garp_applicant *app,
288eca9ebacSPatrick McHardy const struct garp_attr *attr,
289eca9ebacSPatrick McHardy enum garp_attr_event event)
290eca9ebacSPatrick McHardy {
291eca9ebacSPatrick McHardy struct garp_attr_hdr *ga;
292eca9ebacSPatrick McHardy unsigned int len;
293eca9ebacSPatrick McHardy int err;
294eca9ebacSPatrick McHardy again:
295eca9ebacSPatrick McHardy if (!app->pdu) {
296eca9ebacSPatrick McHardy err = garp_pdu_init(app);
297eca9ebacSPatrick McHardy if (err < 0)
298eca9ebacSPatrick McHardy return err;
299eca9ebacSPatrick McHardy }
300eca9ebacSPatrick McHardy
301eca9ebacSPatrick McHardy if (garp_cb(app->pdu)->cur_type != attr->type) {
302eca9ebacSPatrick McHardy if (garp_cb(app->pdu)->cur_type &&
303eca9ebacSPatrick McHardy garp_pdu_append_end_mark(app) < 0)
304eca9ebacSPatrick McHardy goto queue;
305eca9ebacSPatrick McHardy if (garp_pdu_append_msg(app, attr->type) < 0)
306eca9ebacSPatrick McHardy goto queue;
307eca9ebacSPatrick McHardy }
308eca9ebacSPatrick McHardy
309eca9ebacSPatrick McHardy len = sizeof(*ga) + attr->dlen;
310eca9ebacSPatrick McHardy if (skb_tailroom(app->pdu) < len)
311eca9ebacSPatrick McHardy goto queue;
3124df864c1SJohannes Berg ga = __skb_put(app->pdu, len);
313eca9ebacSPatrick McHardy ga->len = len;
314eca9ebacSPatrick McHardy ga->event = event;
315eca9ebacSPatrick McHardy memcpy(ga->data, attr->data, attr->dlen);
316eca9ebacSPatrick McHardy return 0;
317eca9ebacSPatrick McHardy
318eca9ebacSPatrick McHardy queue:
319eca9ebacSPatrick McHardy garp_pdu_queue(app);
320eca9ebacSPatrick McHardy goto again;
321eca9ebacSPatrick McHardy }
322eca9ebacSPatrick McHardy
garp_attr_event(struct garp_applicant * app,struct garp_attr * attr,enum garp_event event)323eca9ebacSPatrick McHardy static void garp_attr_event(struct garp_applicant *app,
324eca9ebacSPatrick McHardy struct garp_attr *attr, enum garp_event event)
325eca9ebacSPatrick McHardy {
326eca9ebacSPatrick McHardy enum garp_applicant_state state;
327eca9ebacSPatrick McHardy
328eca9ebacSPatrick McHardy state = garp_applicant_state_table[attr->state][event].state;
329eca9ebacSPatrick McHardy if (state == GARP_APPLICANT_INVALID)
330eca9ebacSPatrick McHardy return;
331eca9ebacSPatrick McHardy
332eca9ebacSPatrick McHardy switch (garp_applicant_state_table[attr->state][event].action) {
333eca9ebacSPatrick McHardy case GARP_ACTION_NONE:
334eca9ebacSPatrick McHardy break;
335eca9ebacSPatrick McHardy case GARP_ACTION_S_JOIN_IN:
33651ce7ec9SPatrick McHardy /* When appending the attribute fails, don't update state in
33751ce7ec9SPatrick McHardy * order to retry on next TRANSMIT_PDU event. */
33851ce7ec9SPatrick McHardy if (garp_pdu_append_attr(app, attr, GARP_JOIN_IN) < 0)
33951ce7ec9SPatrick McHardy return;
340eca9ebacSPatrick McHardy break;
341eca9ebacSPatrick McHardy case GARP_ACTION_S_LEAVE_EMPTY:
342eca9ebacSPatrick McHardy garp_pdu_append_attr(app, attr, GARP_LEAVE_EMPTY);
343eca9ebacSPatrick McHardy /* As a pure applicant, sending a leave message implies that
344eca9ebacSPatrick McHardy * the attribute was unregistered and can be destroyed. */
345eca9ebacSPatrick McHardy garp_attr_destroy(app, attr);
346eca9ebacSPatrick McHardy return;
347eca9ebacSPatrick McHardy default:
348eca9ebacSPatrick McHardy WARN_ON(1);
349eca9ebacSPatrick McHardy }
350eca9ebacSPatrick McHardy
351eca9ebacSPatrick McHardy attr->state = state;
352eca9ebacSPatrick McHardy }
353eca9ebacSPatrick McHardy
garp_request_join(const struct net_device * dev,const struct garp_application * appl,const void * data,u8 len,u8 type)354eca9ebacSPatrick McHardy int garp_request_join(const struct net_device *dev,
355eca9ebacSPatrick McHardy const struct garp_application *appl,
356eca9ebacSPatrick McHardy const void *data, u8 len, u8 type)
357eca9ebacSPatrick McHardy {
3583cc77ec7SEric Dumazet struct garp_port *port = rtnl_dereference(dev->garp_port);
3593cc77ec7SEric Dumazet struct garp_applicant *app = rtnl_dereference(port->applicants[appl->type]);
360eca9ebacSPatrick McHardy struct garp_attr *attr;
361eca9ebacSPatrick McHardy
362eca9ebacSPatrick McHardy spin_lock_bh(&app->lock);
363eca9ebacSPatrick McHardy attr = garp_attr_create(app, data, len, type);
364eca9ebacSPatrick McHardy if (!attr) {
365eca9ebacSPatrick McHardy spin_unlock_bh(&app->lock);
366eca9ebacSPatrick McHardy return -ENOMEM;
367eca9ebacSPatrick McHardy }
368eca9ebacSPatrick McHardy garp_attr_event(app, attr, GARP_EVENT_REQ_JOIN);
369eca9ebacSPatrick McHardy spin_unlock_bh(&app->lock);
370eca9ebacSPatrick McHardy return 0;
371eca9ebacSPatrick McHardy }
372eca9ebacSPatrick McHardy EXPORT_SYMBOL_GPL(garp_request_join);
373eca9ebacSPatrick McHardy
garp_request_leave(const struct net_device * dev,const struct garp_application * appl,const void * data,u8 len,u8 type)374eca9ebacSPatrick McHardy void garp_request_leave(const struct net_device *dev,
375eca9ebacSPatrick McHardy const struct garp_application *appl,
376eca9ebacSPatrick McHardy const void *data, u8 len, u8 type)
377eca9ebacSPatrick McHardy {
3783cc77ec7SEric Dumazet struct garp_port *port = rtnl_dereference(dev->garp_port);
3793cc77ec7SEric Dumazet struct garp_applicant *app = rtnl_dereference(port->applicants[appl->type]);
380eca9ebacSPatrick McHardy struct garp_attr *attr;
381eca9ebacSPatrick McHardy
382eca9ebacSPatrick McHardy spin_lock_bh(&app->lock);
383eca9ebacSPatrick McHardy attr = garp_attr_lookup(app, data, len, type);
384eca9ebacSPatrick McHardy if (!attr) {
385eca9ebacSPatrick McHardy spin_unlock_bh(&app->lock);
386eca9ebacSPatrick McHardy return;
387eca9ebacSPatrick McHardy }
388eca9ebacSPatrick McHardy garp_attr_event(app, attr, GARP_EVENT_REQ_LEAVE);
389eca9ebacSPatrick McHardy spin_unlock_bh(&app->lock);
390eca9ebacSPatrick McHardy }
391eca9ebacSPatrick McHardy EXPORT_SYMBOL_GPL(garp_request_leave);
392eca9ebacSPatrick McHardy
garp_gid_event(struct garp_applicant * app,enum garp_event event)393eca9ebacSPatrick McHardy static void garp_gid_event(struct garp_applicant *app, enum garp_event event)
394eca9ebacSPatrick McHardy {
395eca9ebacSPatrick McHardy struct rb_node *node, *next;
396eca9ebacSPatrick McHardy struct garp_attr *attr;
397eca9ebacSPatrick McHardy
398eca9ebacSPatrick McHardy for (node = rb_first(&app->gid);
399eca9ebacSPatrick McHardy next = node ? rb_next(node) : NULL, node != NULL;
400eca9ebacSPatrick McHardy node = next) {
401eca9ebacSPatrick McHardy attr = rb_entry(node, struct garp_attr, node);
402eca9ebacSPatrick McHardy garp_attr_event(app, attr, event);
403eca9ebacSPatrick McHardy }
404eca9ebacSPatrick McHardy }
405eca9ebacSPatrick McHardy
garp_join_timer_arm(struct garp_applicant * app)406eca9ebacSPatrick McHardy static void garp_join_timer_arm(struct garp_applicant *app)
407eca9ebacSPatrick McHardy {
408eca9ebacSPatrick McHardy unsigned long delay;
409eca9ebacSPatrick McHardy
4108032bf12SJason A. Donenfeld delay = get_random_u32_below(msecs_to_jiffies(garp_join_time));
411eca9ebacSPatrick McHardy mod_timer(&app->join_timer, jiffies + delay);
412eca9ebacSPatrick McHardy }
413eca9ebacSPatrick McHardy
garp_join_timer(struct timer_list * t)414e99e88a9SKees Cook static void garp_join_timer(struct timer_list *t)
415eca9ebacSPatrick McHardy {
416e99e88a9SKees Cook struct garp_applicant *app = from_timer(app, t, join_timer);
417eca9ebacSPatrick McHardy
418eca9ebacSPatrick McHardy spin_lock(&app->lock);
419eca9ebacSPatrick McHardy garp_gid_event(app, GARP_EVENT_TRANSMIT_PDU);
420eca9ebacSPatrick McHardy garp_pdu_queue(app);
421eca9ebacSPatrick McHardy spin_unlock(&app->lock);
422eca9ebacSPatrick McHardy
423eca9ebacSPatrick McHardy garp_queue_xmit(app);
424eca9ebacSPatrick McHardy garp_join_timer_arm(app);
425eca9ebacSPatrick McHardy }
426eca9ebacSPatrick McHardy
garp_pdu_parse_end_mark(struct sk_buff * skb)427eca9ebacSPatrick McHardy static int garp_pdu_parse_end_mark(struct sk_buff *skb)
428eca9ebacSPatrick McHardy {
429eca9ebacSPatrick McHardy if (!pskb_may_pull(skb, sizeof(u8)))
430eca9ebacSPatrick McHardy return -1;
431eca9ebacSPatrick McHardy if (*skb->data == GARP_END_MARK) {
432eca9ebacSPatrick McHardy skb_pull(skb, sizeof(u8));
433eca9ebacSPatrick McHardy return -1;
434eca9ebacSPatrick McHardy }
435eca9ebacSPatrick McHardy return 0;
436eca9ebacSPatrick McHardy }
437eca9ebacSPatrick McHardy
garp_pdu_parse_attr(struct garp_applicant * app,struct sk_buff * skb,u8 attrtype)438eca9ebacSPatrick McHardy static int garp_pdu_parse_attr(struct garp_applicant *app, struct sk_buff *skb,
439eca9ebacSPatrick McHardy u8 attrtype)
440eca9ebacSPatrick McHardy {
441eca9ebacSPatrick McHardy const struct garp_attr_hdr *ga;
442eca9ebacSPatrick McHardy struct garp_attr *attr;
443eca9ebacSPatrick McHardy enum garp_event event;
444eca9ebacSPatrick McHardy unsigned int dlen;
445eca9ebacSPatrick McHardy
446eca9ebacSPatrick McHardy if (!pskb_may_pull(skb, sizeof(*ga)))
447eca9ebacSPatrick McHardy return -1;
448eca9ebacSPatrick McHardy ga = (struct garp_attr_hdr *)skb->data;
449eca9ebacSPatrick McHardy if (ga->len < sizeof(*ga))
450eca9ebacSPatrick McHardy return -1;
451eca9ebacSPatrick McHardy
452eca9ebacSPatrick McHardy if (!pskb_may_pull(skb, ga->len))
453eca9ebacSPatrick McHardy return -1;
454eca9ebacSPatrick McHardy skb_pull(skb, ga->len);
455eca9ebacSPatrick McHardy dlen = sizeof(*ga) - ga->len;
456eca9ebacSPatrick McHardy
457eca9ebacSPatrick McHardy if (attrtype > app->app->maxattr)
458eca9ebacSPatrick McHardy return 0;
459eca9ebacSPatrick McHardy
460eca9ebacSPatrick McHardy switch (ga->event) {
461eca9ebacSPatrick McHardy case GARP_LEAVE_ALL:
462eca9ebacSPatrick McHardy if (dlen != 0)
463eca9ebacSPatrick McHardy return -1;
464eca9ebacSPatrick McHardy garp_gid_event(app, GARP_EVENT_R_LEAVE_EMPTY);
465eca9ebacSPatrick McHardy return 0;
466eca9ebacSPatrick McHardy case GARP_JOIN_EMPTY:
467eca9ebacSPatrick McHardy event = GARP_EVENT_R_JOIN_EMPTY;
468eca9ebacSPatrick McHardy break;
469eca9ebacSPatrick McHardy case GARP_JOIN_IN:
470eca9ebacSPatrick McHardy event = GARP_EVENT_R_JOIN_IN;
471eca9ebacSPatrick McHardy break;
472eca9ebacSPatrick McHardy case GARP_LEAVE_EMPTY:
473eca9ebacSPatrick McHardy event = GARP_EVENT_R_LEAVE_EMPTY;
474eca9ebacSPatrick McHardy break;
475eca9ebacSPatrick McHardy case GARP_EMPTY:
476eca9ebacSPatrick McHardy event = GARP_EVENT_R_EMPTY;
477eca9ebacSPatrick McHardy break;
478eca9ebacSPatrick McHardy default:
479eca9ebacSPatrick McHardy return 0;
480eca9ebacSPatrick McHardy }
481eca9ebacSPatrick McHardy
482eca9ebacSPatrick McHardy if (dlen == 0)
483eca9ebacSPatrick McHardy return -1;
484eca9ebacSPatrick McHardy attr = garp_attr_lookup(app, ga->data, dlen, attrtype);
485eca9ebacSPatrick McHardy if (attr == NULL)
486eca9ebacSPatrick McHardy return 0;
487eca9ebacSPatrick McHardy garp_attr_event(app, attr, event);
488eca9ebacSPatrick McHardy return 0;
489eca9ebacSPatrick McHardy }
490eca9ebacSPatrick McHardy
garp_pdu_parse_msg(struct garp_applicant * app,struct sk_buff * skb)491eca9ebacSPatrick McHardy static int garp_pdu_parse_msg(struct garp_applicant *app, struct sk_buff *skb)
492eca9ebacSPatrick McHardy {
493eca9ebacSPatrick McHardy const struct garp_msg_hdr *gm;
494eca9ebacSPatrick McHardy
495eca9ebacSPatrick McHardy if (!pskb_may_pull(skb, sizeof(*gm)))
496eca9ebacSPatrick McHardy return -1;
497eca9ebacSPatrick McHardy gm = (struct garp_msg_hdr *)skb->data;
498eca9ebacSPatrick McHardy if (gm->attrtype == 0)
499eca9ebacSPatrick McHardy return -1;
500eca9ebacSPatrick McHardy skb_pull(skb, sizeof(*gm));
501eca9ebacSPatrick McHardy
502eca9ebacSPatrick McHardy while (skb->len > 0) {
503eca9ebacSPatrick McHardy if (garp_pdu_parse_attr(app, skb, gm->attrtype) < 0)
504eca9ebacSPatrick McHardy return -1;
505eca9ebacSPatrick McHardy if (garp_pdu_parse_end_mark(skb) < 0)
506eca9ebacSPatrick McHardy break;
507eca9ebacSPatrick McHardy }
508eca9ebacSPatrick McHardy return 0;
509eca9ebacSPatrick McHardy }
510eca9ebacSPatrick McHardy
garp_pdu_rcv(const struct stp_proto * proto,struct sk_buff * skb,struct net_device * dev)511eca9ebacSPatrick McHardy static void garp_pdu_rcv(const struct stp_proto *proto, struct sk_buff *skb,
512eca9ebacSPatrick McHardy struct net_device *dev)
513eca9ebacSPatrick McHardy {
514eca9ebacSPatrick McHardy struct garp_application *appl = proto->data;
515eca9ebacSPatrick McHardy struct garp_port *port;
516eca9ebacSPatrick McHardy struct garp_applicant *app;
517eca9ebacSPatrick McHardy const struct garp_pdu_hdr *gp;
518eca9ebacSPatrick McHardy
519eca9ebacSPatrick McHardy port = rcu_dereference(dev->garp_port);
520eca9ebacSPatrick McHardy if (!port)
521eca9ebacSPatrick McHardy goto err;
522eca9ebacSPatrick McHardy app = rcu_dereference(port->applicants[appl->type]);
523eca9ebacSPatrick McHardy if (!app)
524eca9ebacSPatrick McHardy goto err;
525eca9ebacSPatrick McHardy
526eca9ebacSPatrick McHardy if (!pskb_may_pull(skb, sizeof(*gp)))
527eca9ebacSPatrick McHardy goto err;
528eca9ebacSPatrick McHardy gp = (struct garp_pdu_hdr *)skb->data;
529eca9ebacSPatrick McHardy if (get_unaligned(&gp->protocol) != htons(GARP_PROTOCOL_ID))
530eca9ebacSPatrick McHardy goto err;
531eca9ebacSPatrick McHardy skb_pull(skb, sizeof(*gp));
532eca9ebacSPatrick McHardy
533eca9ebacSPatrick McHardy spin_lock(&app->lock);
534eca9ebacSPatrick McHardy while (skb->len > 0) {
535eca9ebacSPatrick McHardy if (garp_pdu_parse_msg(app, skb) < 0)
536eca9ebacSPatrick McHardy break;
537eca9ebacSPatrick McHardy if (garp_pdu_parse_end_mark(skb) < 0)
538eca9ebacSPatrick McHardy break;
539eca9ebacSPatrick McHardy }
540eca9ebacSPatrick McHardy spin_unlock(&app->lock);
541eca9ebacSPatrick McHardy err:
542eca9ebacSPatrick McHardy kfree_skb(skb);
543eca9ebacSPatrick McHardy }
544eca9ebacSPatrick McHardy
garp_init_port(struct net_device * dev)545eca9ebacSPatrick McHardy static int garp_init_port(struct net_device *dev)
546eca9ebacSPatrick McHardy {
547eca9ebacSPatrick McHardy struct garp_port *port;
548eca9ebacSPatrick McHardy
549eca9ebacSPatrick McHardy port = kzalloc(sizeof(*port), GFP_KERNEL);
550eca9ebacSPatrick McHardy if (!port)
551eca9ebacSPatrick McHardy return -ENOMEM;
552eca9ebacSPatrick McHardy rcu_assign_pointer(dev->garp_port, port);
553eca9ebacSPatrick McHardy return 0;
554eca9ebacSPatrick McHardy }
555eca9ebacSPatrick McHardy
garp_release_port(struct net_device * dev)556eca9ebacSPatrick McHardy static void garp_release_port(struct net_device *dev)
557eca9ebacSPatrick McHardy {
5583cc77ec7SEric Dumazet struct garp_port *port = rtnl_dereference(dev->garp_port);
559eca9ebacSPatrick McHardy unsigned int i;
560eca9ebacSPatrick McHardy
561eca9ebacSPatrick McHardy for (i = 0; i <= GARP_APPLICATION_MAX; i++) {
5623cc77ec7SEric Dumazet if (rtnl_dereference(port->applicants[i]))
563eca9ebacSPatrick McHardy return;
564eca9ebacSPatrick McHardy }
565a9b3cd7fSStephen Hemminger RCU_INIT_POINTER(dev->garp_port, NULL);
5660a384b22SEric Dumazet kfree_rcu(port, rcu);
567eca9ebacSPatrick McHardy }
568eca9ebacSPatrick McHardy
garp_init_applicant(struct net_device * dev,struct garp_application * appl)569eca9ebacSPatrick McHardy int garp_init_applicant(struct net_device *dev, struct garp_application *appl)
570eca9ebacSPatrick McHardy {
571eca9ebacSPatrick McHardy struct garp_applicant *app;
572eca9ebacSPatrick McHardy int err;
573eca9ebacSPatrick McHardy
574eca9ebacSPatrick McHardy ASSERT_RTNL();
575eca9ebacSPatrick McHardy
5763cc77ec7SEric Dumazet if (!rtnl_dereference(dev->garp_port)) {
577eca9ebacSPatrick McHardy err = garp_init_port(dev);
578eca9ebacSPatrick McHardy if (err < 0)
579eca9ebacSPatrick McHardy goto err1;
580eca9ebacSPatrick McHardy }
581eca9ebacSPatrick McHardy
582eca9ebacSPatrick McHardy err = -ENOMEM;
583eca9ebacSPatrick McHardy app = kzalloc(sizeof(*app), GFP_KERNEL);
584eca9ebacSPatrick McHardy if (!app)
585eca9ebacSPatrick McHardy goto err2;
586eca9ebacSPatrick McHardy
58722bedad3SJiri Pirko err = dev_mc_add(dev, appl->proto.group_address);
588eca9ebacSPatrick McHardy if (err < 0)
589eca9ebacSPatrick McHardy goto err3;
590eca9ebacSPatrick McHardy
591eca9ebacSPatrick McHardy app->dev = dev;
592eca9ebacSPatrick McHardy app->app = appl;
593eca9ebacSPatrick McHardy app->gid = RB_ROOT;
594eca9ebacSPatrick McHardy spin_lock_init(&app->lock);
595eca9ebacSPatrick McHardy skb_queue_head_init(&app->queue);
596eca9ebacSPatrick McHardy rcu_assign_pointer(dev->garp_port->applicants[appl->type], app);
597e99e88a9SKees Cook timer_setup(&app->join_timer, garp_join_timer, 0);
598eca9ebacSPatrick McHardy garp_join_timer_arm(app);
599eca9ebacSPatrick McHardy return 0;
600eca9ebacSPatrick McHardy
601eca9ebacSPatrick McHardy err3:
602eca9ebacSPatrick McHardy kfree(app);
603eca9ebacSPatrick McHardy err2:
604eca9ebacSPatrick McHardy garp_release_port(dev);
605eca9ebacSPatrick McHardy err1:
606eca9ebacSPatrick McHardy return err;
607eca9ebacSPatrick McHardy }
608eca9ebacSPatrick McHardy EXPORT_SYMBOL_GPL(garp_init_applicant);
609eca9ebacSPatrick McHardy
garp_uninit_applicant(struct net_device * dev,struct garp_application * appl)610eca9ebacSPatrick McHardy void garp_uninit_applicant(struct net_device *dev, struct garp_application *appl)
611eca9ebacSPatrick McHardy {
6123cc77ec7SEric Dumazet struct garp_port *port = rtnl_dereference(dev->garp_port);
6133cc77ec7SEric Dumazet struct garp_applicant *app = rtnl_dereference(port->applicants[appl->type]);
614eca9ebacSPatrick McHardy
615eca9ebacSPatrick McHardy ASSERT_RTNL();
616eca9ebacSPatrick McHardy
617a9b3cd7fSStephen Hemminger RCU_INIT_POINTER(port->applicants[appl->type], NULL);
618eca9ebacSPatrick McHardy
619eca9ebacSPatrick McHardy /* Delete timer and generate a final TRANSMIT_PDU event to flush out
620eca9ebacSPatrick McHardy * all pending messages before the applicant is gone. */
621*292a089dSSteven Rostedt (Google) timer_shutdown_sync(&app->join_timer);
622cfbe800bSCong Wang
623cfbe800bSCong Wang spin_lock_bh(&app->lock);
624eca9ebacSPatrick McHardy garp_gid_event(app, GARP_EVENT_TRANSMIT_PDU);
62542ca63f9SYang Yingliang garp_attr_destroy_all(app);
626eca9ebacSPatrick McHardy garp_pdu_queue(app);
627cfbe800bSCong Wang spin_unlock_bh(&app->lock);
628cfbe800bSCong Wang
629eca9ebacSPatrick McHardy garp_queue_xmit(app);
630eca9ebacSPatrick McHardy
63122bedad3SJiri Pirko dev_mc_del(dev, appl->proto.group_address);
6320a384b22SEric Dumazet kfree_rcu(app, rcu);
633eca9ebacSPatrick McHardy garp_release_port(dev);
634eca9ebacSPatrick McHardy }
635eca9ebacSPatrick McHardy EXPORT_SYMBOL_GPL(garp_uninit_applicant);
636eca9ebacSPatrick McHardy
garp_register_application(struct garp_application * appl)637eca9ebacSPatrick McHardy int garp_register_application(struct garp_application *appl)
638eca9ebacSPatrick McHardy {
639eca9ebacSPatrick McHardy appl->proto.rcv = garp_pdu_rcv;
640eca9ebacSPatrick McHardy appl->proto.data = appl;
641eca9ebacSPatrick McHardy return stp_proto_register(&appl->proto);
642eca9ebacSPatrick McHardy }
643eca9ebacSPatrick McHardy EXPORT_SYMBOL_GPL(garp_register_application);
644eca9ebacSPatrick McHardy
garp_unregister_application(struct garp_application * appl)645eca9ebacSPatrick McHardy void garp_unregister_application(struct garp_application *appl)
646eca9ebacSPatrick McHardy {
647eca9ebacSPatrick McHardy stp_proto_unregister(&appl->proto);
648eca9ebacSPatrick McHardy }
649eca9ebacSPatrick McHardy EXPORT_SYMBOL_GPL(garp_unregister_application);
650