xref: /openbmc/linux/net/802/garp.c (revision d2912cb15bdda8ba4a5dd73396ad62641af2f520)
1*d2912cb1SThomas 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 
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 
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 
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 
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 
206eca9ebacSPatrick McHardy static int garp_pdu_init(struct garp_applicant *app)
207eca9ebacSPatrick McHardy {
208eca9ebacSPatrick McHardy 	struct sk_buff *skb;
209eca9ebacSPatrick McHardy 	struct garp_pdu_hdr *gp;
210eca9ebacSPatrick McHardy 
211eca9ebacSPatrick McHardy #define LLC_RESERVE	sizeof(struct llc_pdu_un)
212eca9ebacSPatrick McHardy 	skb = alloc_skb(app->dev->mtu + LL_RESERVED_SPACE(app->dev),
213eca9ebacSPatrick McHardy 			GFP_ATOMIC);
214eca9ebacSPatrick McHardy 	if (!skb)
215eca9ebacSPatrick McHardy 		return -ENOMEM;
216eca9ebacSPatrick McHardy 
217eca9ebacSPatrick McHardy 	skb->dev = app->dev;
218eca9ebacSPatrick McHardy 	skb->protocol = htons(ETH_P_802_2);
219eca9ebacSPatrick McHardy 	skb_reserve(skb, LL_RESERVED_SPACE(app->dev) + LLC_RESERVE);
220eca9ebacSPatrick McHardy 
2214df864c1SJohannes Berg 	gp = __skb_put(skb, sizeof(*gp));
222eca9ebacSPatrick McHardy 	put_unaligned(htons(GARP_PROTOCOL_ID), &gp->protocol);
223eca9ebacSPatrick McHardy 
224eca9ebacSPatrick McHardy 	app->pdu = skb;
225eca9ebacSPatrick McHardy 	return 0;
226eca9ebacSPatrick McHardy }
227eca9ebacSPatrick McHardy 
228eca9ebacSPatrick McHardy static int garp_pdu_append_end_mark(struct garp_applicant *app)
229eca9ebacSPatrick McHardy {
230eca9ebacSPatrick McHardy 	if (skb_tailroom(app->pdu) < sizeof(u8))
231eca9ebacSPatrick McHardy 		return -1;
232de77b966Syuan linyu 	__skb_put_u8(app->pdu, GARP_END_MARK);
233eca9ebacSPatrick McHardy 	return 0;
234eca9ebacSPatrick McHardy }
235eca9ebacSPatrick McHardy 
236eca9ebacSPatrick McHardy static void garp_pdu_queue(struct garp_applicant *app)
237eca9ebacSPatrick McHardy {
238eca9ebacSPatrick McHardy 	if (!app->pdu)
239eca9ebacSPatrick McHardy 		return;
240eca9ebacSPatrick McHardy 
241eca9ebacSPatrick McHardy 	garp_pdu_append_end_mark(app);
242eca9ebacSPatrick McHardy 	garp_pdu_append_end_mark(app);
243eca9ebacSPatrick McHardy 
244eca9ebacSPatrick McHardy 	llc_pdu_header_init(app->pdu, LLC_PDU_TYPE_U, LLC_SAP_BSPAN,
245eca9ebacSPatrick McHardy 			    LLC_SAP_BSPAN, LLC_PDU_CMD);
246eca9ebacSPatrick McHardy 	llc_pdu_init_as_ui_cmd(app->pdu);
247eca9ebacSPatrick McHardy 	llc_mac_hdr_init(app->pdu, app->dev->dev_addr,
248eca9ebacSPatrick McHardy 			 app->app->proto.group_address);
249eca9ebacSPatrick McHardy 
250eca9ebacSPatrick McHardy 	skb_queue_tail(&app->queue, app->pdu);
251eca9ebacSPatrick McHardy 	app->pdu = NULL;
252eca9ebacSPatrick McHardy }
253eca9ebacSPatrick McHardy 
254eca9ebacSPatrick McHardy static void garp_queue_xmit(struct garp_applicant *app)
255eca9ebacSPatrick McHardy {
256eca9ebacSPatrick McHardy 	struct sk_buff *skb;
257eca9ebacSPatrick McHardy 
258eca9ebacSPatrick McHardy 	while ((skb = skb_dequeue(&app->queue)))
259eca9ebacSPatrick McHardy 		dev_queue_xmit(skb);
260eca9ebacSPatrick McHardy }
261eca9ebacSPatrick McHardy 
262eca9ebacSPatrick McHardy static int garp_pdu_append_msg(struct garp_applicant *app, u8 attrtype)
263eca9ebacSPatrick McHardy {
264eca9ebacSPatrick McHardy 	struct garp_msg_hdr *gm;
265eca9ebacSPatrick McHardy 
266eca9ebacSPatrick McHardy 	if (skb_tailroom(app->pdu) < sizeof(*gm))
267eca9ebacSPatrick McHardy 		return -1;
2684df864c1SJohannes Berg 	gm = __skb_put(app->pdu, sizeof(*gm));
269eca9ebacSPatrick McHardy 	gm->attrtype = attrtype;
270eca9ebacSPatrick McHardy 	garp_cb(app->pdu)->cur_type = attrtype;
271eca9ebacSPatrick McHardy 	return 0;
272eca9ebacSPatrick McHardy }
273eca9ebacSPatrick McHardy 
274eca9ebacSPatrick McHardy static int garp_pdu_append_attr(struct garp_applicant *app,
275eca9ebacSPatrick McHardy 				const struct garp_attr *attr,
276eca9ebacSPatrick McHardy 				enum garp_attr_event event)
277eca9ebacSPatrick McHardy {
278eca9ebacSPatrick McHardy 	struct garp_attr_hdr *ga;
279eca9ebacSPatrick McHardy 	unsigned int len;
280eca9ebacSPatrick McHardy 	int err;
281eca9ebacSPatrick McHardy again:
282eca9ebacSPatrick McHardy 	if (!app->pdu) {
283eca9ebacSPatrick McHardy 		err = garp_pdu_init(app);
284eca9ebacSPatrick McHardy 		if (err < 0)
285eca9ebacSPatrick McHardy 			return err;
286eca9ebacSPatrick McHardy 	}
287eca9ebacSPatrick McHardy 
288eca9ebacSPatrick McHardy 	if (garp_cb(app->pdu)->cur_type != attr->type) {
289eca9ebacSPatrick McHardy 		if (garp_cb(app->pdu)->cur_type &&
290eca9ebacSPatrick McHardy 		    garp_pdu_append_end_mark(app) < 0)
291eca9ebacSPatrick McHardy 			goto queue;
292eca9ebacSPatrick McHardy 		if (garp_pdu_append_msg(app, attr->type) < 0)
293eca9ebacSPatrick McHardy 			goto queue;
294eca9ebacSPatrick McHardy 	}
295eca9ebacSPatrick McHardy 
296eca9ebacSPatrick McHardy 	len = sizeof(*ga) + attr->dlen;
297eca9ebacSPatrick McHardy 	if (skb_tailroom(app->pdu) < len)
298eca9ebacSPatrick McHardy 		goto queue;
2994df864c1SJohannes Berg 	ga = __skb_put(app->pdu, len);
300eca9ebacSPatrick McHardy 	ga->len   = len;
301eca9ebacSPatrick McHardy 	ga->event = event;
302eca9ebacSPatrick McHardy 	memcpy(ga->data, attr->data, attr->dlen);
303eca9ebacSPatrick McHardy 	return 0;
304eca9ebacSPatrick McHardy 
305eca9ebacSPatrick McHardy queue:
306eca9ebacSPatrick McHardy 	garp_pdu_queue(app);
307eca9ebacSPatrick McHardy 	goto again;
308eca9ebacSPatrick McHardy }
309eca9ebacSPatrick McHardy 
310eca9ebacSPatrick McHardy static void garp_attr_event(struct garp_applicant *app,
311eca9ebacSPatrick McHardy 			    struct garp_attr *attr, enum garp_event event)
312eca9ebacSPatrick McHardy {
313eca9ebacSPatrick McHardy 	enum garp_applicant_state state;
314eca9ebacSPatrick McHardy 
315eca9ebacSPatrick McHardy 	state = garp_applicant_state_table[attr->state][event].state;
316eca9ebacSPatrick McHardy 	if (state == GARP_APPLICANT_INVALID)
317eca9ebacSPatrick McHardy 		return;
318eca9ebacSPatrick McHardy 
319eca9ebacSPatrick McHardy 	switch (garp_applicant_state_table[attr->state][event].action) {
320eca9ebacSPatrick McHardy 	case GARP_ACTION_NONE:
321eca9ebacSPatrick McHardy 		break;
322eca9ebacSPatrick McHardy 	case GARP_ACTION_S_JOIN_IN:
32351ce7ec9SPatrick McHardy 		/* When appending the attribute fails, don't update state in
32451ce7ec9SPatrick McHardy 		 * order to retry on next TRANSMIT_PDU event. */
32551ce7ec9SPatrick McHardy 		if (garp_pdu_append_attr(app, attr, GARP_JOIN_IN) < 0)
32651ce7ec9SPatrick McHardy 			return;
327eca9ebacSPatrick McHardy 		break;
328eca9ebacSPatrick McHardy 	case GARP_ACTION_S_LEAVE_EMPTY:
329eca9ebacSPatrick McHardy 		garp_pdu_append_attr(app, attr, GARP_LEAVE_EMPTY);
330eca9ebacSPatrick McHardy 		/* As a pure applicant, sending a leave message implies that
331eca9ebacSPatrick McHardy 		 * the attribute was unregistered and can be destroyed. */
332eca9ebacSPatrick McHardy 		garp_attr_destroy(app, attr);
333eca9ebacSPatrick McHardy 		return;
334eca9ebacSPatrick McHardy 	default:
335eca9ebacSPatrick McHardy 		WARN_ON(1);
336eca9ebacSPatrick McHardy 	}
337eca9ebacSPatrick McHardy 
338eca9ebacSPatrick McHardy 	attr->state = state;
339eca9ebacSPatrick McHardy }
340eca9ebacSPatrick McHardy 
341eca9ebacSPatrick McHardy int garp_request_join(const struct net_device *dev,
342eca9ebacSPatrick McHardy 		      const struct garp_application *appl,
343eca9ebacSPatrick McHardy 		      const void *data, u8 len, u8 type)
344eca9ebacSPatrick McHardy {
3453cc77ec7SEric Dumazet 	struct garp_port *port = rtnl_dereference(dev->garp_port);
3463cc77ec7SEric Dumazet 	struct garp_applicant *app = rtnl_dereference(port->applicants[appl->type]);
347eca9ebacSPatrick McHardy 	struct garp_attr *attr;
348eca9ebacSPatrick McHardy 
349eca9ebacSPatrick McHardy 	spin_lock_bh(&app->lock);
350eca9ebacSPatrick McHardy 	attr = garp_attr_create(app, data, len, type);
351eca9ebacSPatrick McHardy 	if (!attr) {
352eca9ebacSPatrick McHardy 		spin_unlock_bh(&app->lock);
353eca9ebacSPatrick McHardy 		return -ENOMEM;
354eca9ebacSPatrick McHardy 	}
355eca9ebacSPatrick McHardy 	garp_attr_event(app, attr, GARP_EVENT_REQ_JOIN);
356eca9ebacSPatrick McHardy 	spin_unlock_bh(&app->lock);
357eca9ebacSPatrick McHardy 	return 0;
358eca9ebacSPatrick McHardy }
359eca9ebacSPatrick McHardy EXPORT_SYMBOL_GPL(garp_request_join);
360eca9ebacSPatrick McHardy 
361eca9ebacSPatrick McHardy void garp_request_leave(const struct net_device *dev,
362eca9ebacSPatrick McHardy 			const struct garp_application *appl,
363eca9ebacSPatrick McHardy 			const void *data, u8 len, u8 type)
364eca9ebacSPatrick McHardy {
3653cc77ec7SEric Dumazet 	struct garp_port *port = rtnl_dereference(dev->garp_port);
3663cc77ec7SEric Dumazet 	struct garp_applicant *app = rtnl_dereference(port->applicants[appl->type]);
367eca9ebacSPatrick McHardy 	struct garp_attr *attr;
368eca9ebacSPatrick McHardy 
369eca9ebacSPatrick McHardy 	spin_lock_bh(&app->lock);
370eca9ebacSPatrick McHardy 	attr = garp_attr_lookup(app, data, len, type);
371eca9ebacSPatrick McHardy 	if (!attr) {
372eca9ebacSPatrick McHardy 		spin_unlock_bh(&app->lock);
373eca9ebacSPatrick McHardy 		return;
374eca9ebacSPatrick McHardy 	}
375eca9ebacSPatrick McHardy 	garp_attr_event(app, attr, GARP_EVENT_REQ_LEAVE);
376eca9ebacSPatrick McHardy 	spin_unlock_bh(&app->lock);
377eca9ebacSPatrick McHardy }
378eca9ebacSPatrick McHardy EXPORT_SYMBOL_GPL(garp_request_leave);
379eca9ebacSPatrick McHardy 
380eca9ebacSPatrick McHardy static void garp_gid_event(struct garp_applicant *app, enum garp_event event)
381eca9ebacSPatrick McHardy {
382eca9ebacSPatrick McHardy 	struct rb_node *node, *next;
383eca9ebacSPatrick McHardy 	struct garp_attr *attr;
384eca9ebacSPatrick McHardy 
385eca9ebacSPatrick McHardy 	for (node = rb_first(&app->gid);
386eca9ebacSPatrick McHardy 	     next = node ? rb_next(node) : NULL, node != NULL;
387eca9ebacSPatrick McHardy 	     node = next) {
388eca9ebacSPatrick McHardy 		attr = rb_entry(node, struct garp_attr, node);
389eca9ebacSPatrick McHardy 		garp_attr_event(app, attr, event);
390eca9ebacSPatrick McHardy 	}
391eca9ebacSPatrick McHardy }
392eca9ebacSPatrick McHardy 
393eca9ebacSPatrick McHardy static void garp_join_timer_arm(struct garp_applicant *app)
394eca9ebacSPatrick McHardy {
395eca9ebacSPatrick McHardy 	unsigned long delay;
396eca9ebacSPatrick McHardy 
39763862b5bSAruna-Hewapathirane 	delay = (u64)msecs_to_jiffies(garp_join_time) * prandom_u32() >> 32;
398eca9ebacSPatrick McHardy 	mod_timer(&app->join_timer, jiffies + delay);
399eca9ebacSPatrick McHardy }
400eca9ebacSPatrick McHardy 
401e99e88a9SKees Cook static void garp_join_timer(struct timer_list *t)
402eca9ebacSPatrick McHardy {
403e99e88a9SKees Cook 	struct garp_applicant *app = from_timer(app, t, join_timer);
404eca9ebacSPatrick McHardy 
405eca9ebacSPatrick McHardy 	spin_lock(&app->lock);
406eca9ebacSPatrick McHardy 	garp_gid_event(app, GARP_EVENT_TRANSMIT_PDU);
407eca9ebacSPatrick McHardy 	garp_pdu_queue(app);
408eca9ebacSPatrick McHardy 	spin_unlock(&app->lock);
409eca9ebacSPatrick McHardy 
410eca9ebacSPatrick McHardy 	garp_queue_xmit(app);
411eca9ebacSPatrick McHardy 	garp_join_timer_arm(app);
412eca9ebacSPatrick McHardy }
413eca9ebacSPatrick McHardy 
414eca9ebacSPatrick McHardy static int garp_pdu_parse_end_mark(struct sk_buff *skb)
415eca9ebacSPatrick McHardy {
416eca9ebacSPatrick McHardy 	if (!pskb_may_pull(skb, sizeof(u8)))
417eca9ebacSPatrick McHardy 		return -1;
418eca9ebacSPatrick McHardy 	if (*skb->data == GARP_END_MARK) {
419eca9ebacSPatrick McHardy 		skb_pull(skb, sizeof(u8));
420eca9ebacSPatrick McHardy 		return -1;
421eca9ebacSPatrick McHardy 	}
422eca9ebacSPatrick McHardy 	return 0;
423eca9ebacSPatrick McHardy }
424eca9ebacSPatrick McHardy 
425eca9ebacSPatrick McHardy static int garp_pdu_parse_attr(struct garp_applicant *app, struct sk_buff *skb,
426eca9ebacSPatrick McHardy 			       u8 attrtype)
427eca9ebacSPatrick McHardy {
428eca9ebacSPatrick McHardy 	const struct garp_attr_hdr *ga;
429eca9ebacSPatrick McHardy 	struct garp_attr *attr;
430eca9ebacSPatrick McHardy 	enum garp_event event;
431eca9ebacSPatrick McHardy 	unsigned int dlen;
432eca9ebacSPatrick McHardy 
433eca9ebacSPatrick McHardy 	if (!pskb_may_pull(skb, sizeof(*ga)))
434eca9ebacSPatrick McHardy 		return -1;
435eca9ebacSPatrick McHardy 	ga = (struct garp_attr_hdr *)skb->data;
436eca9ebacSPatrick McHardy 	if (ga->len < sizeof(*ga))
437eca9ebacSPatrick McHardy 		return -1;
438eca9ebacSPatrick McHardy 
439eca9ebacSPatrick McHardy 	if (!pskb_may_pull(skb, ga->len))
440eca9ebacSPatrick McHardy 		return -1;
441eca9ebacSPatrick McHardy 	skb_pull(skb, ga->len);
442eca9ebacSPatrick McHardy 	dlen = sizeof(*ga) - ga->len;
443eca9ebacSPatrick McHardy 
444eca9ebacSPatrick McHardy 	if (attrtype > app->app->maxattr)
445eca9ebacSPatrick McHardy 		return 0;
446eca9ebacSPatrick McHardy 
447eca9ebacSPatrick McHardy 	switch (ga->event) {
448eca9ebacSPatrick McHardy 	case GARP_LEAVE_ALL:
449eca9ebacSPatrick McHardy 		if (dlen != 0)
450eca9ebacSPatrick McHardy 			return -1;
451eca9ebacSPatrick McHardy 		garp_gid_event(app, GARP_EVENT_R_LEAVE_EMPTY);
452eca9ebacSPatrick McHardy 		return 0;
453eca9ebacSPatrick McHardy 	case GARP_JOIN_EMPTY:
454eca9ebacSPatrick McHardy 		event = GARP_EVENT_R_JOIN_EMPTY;
455eca9ebacSPatrick McHardy 		break;
456eca9ebacSPatrick McHardy 	case GARP_JOIN_IN:
457eca9ebacSPatrick McHardy 		event = GARP_EVENT_R_JOIN_IN;
458eca9ebacSPatrick McHardy 		break;
459eca9ebacSPatrick McHardy 	case GARP_LEAVE_EMPTY:
460eca9ebacSPatrick McHardy 		event = GARP_EVENT_R_LEAVE_EMPTY;
461eca9ebacSPatrick McHardy 		break;
462eca9ebacSPatrick McHardy 	case GARP_EMPTY:
463eca9ebacSPatrick McHardy 		event = GARP_EVENT_R_EMPTY;
464eca9ebacSPatrick McHardy 		break;
465eca9ebacSPatrick McHardy 	default:
466eca9ebacSPatrick McHardy 		return 0;
467eca9ebacSPatrick McHardy 	}
468eca9ebacSPatrick McHardy 
469eca9ebacSPatrick McHardy 	if (dlen == 0)
470eca9ebacSPatrick McHardy 		return -1;
471eca9ebacSPatrick McHardy 	attr = garp_attr_lookup(app, ga->data, dlen, attrtype);
472eca9ebacSPatrick McHardy 	if (attr == NULL)
473eca9ebacSPatrick McHardy 		return 0;
474eca9ebacSPatrick McHardy 	garp_attr_event(app, attr, event);
475eca9ebacSPatrick McHardy 	return 0;
476eca9ebacSPatrick McHardy }
477eca9ebacSPatrick McHardy 
478eca9ebacSPatrick McHardy static int garp_pdu_parse_msg(struct garp_applicant *app, struct sk_buff *skb)
479eca9ebacSPatrick McHardy {
480eca9ebacSPatrick McHardy 	const struct garp_msg_hdr *gm;
481eca9ebacSPatrick McHardy 
482eca9ebacSPatrick McHardy 	if (!pskb_may_pull(skb, sizeof(*gm)))
483eca9ebacSPatrick McHardy 		return -1;
484eca9ebacSPatrick McHardy 	gm = (struct garp_msg_hdr *)skb->data;
485eca9ebacSPatrick McHardy 	if (gm->attrtype == 0)
486eca9ebacSPatrick McHardy 		return -1;
487eca9ebacSPatrick McHardy 	skb_pull(skb, sizeof(*gm));
488eca9ebacSPatrick McHardy 
489eca9ebacSPatrick McHardy 	while (skb->len > 0) {
490eca9ebacSPatrick McHardy 		if (garp_pdu_parse_attr(app, skb, gm->attrtype) < 0)
491eca9ebacSPatrick McHardy 			return -1;
492eca9ebacSPatrick McHardy 		if (garp_pdu_parse_end_mark(skb) < 0)
493eca9ebacSPatrick McHardy 			break;
494eca9ebacSPatrick McHardy 	}
495eca9ebacSPatrick McHardy 	return 0;
496eca9ebacSPatrick McHardy }
497eca9ebacSPatrick McHardy 
498eca9ebacSPatrick McHardy static void garp_pdu_rcv(const struct stp_proto *proto, struct sk_buff *skb,
499eca9ebacSPatrick McHardy 			 struct net_device *dev)
500eca9ebacSPatrick McHardy {
501eca9ebacSPatrick McHardy 	struct garp_application *appl = proto->data;
502eca9ebacSPatrick McHardy 	struct garp_port *port;
503eca9ebacSPatrick McHardy 	struct garp_applicant *app;
504eca9ebacSPatrick McHardy 	const struct garp_pdu_hdr *gp;
505eca9ebacSPatrick McHardy 
506eca9ebacSPatrick McHardy 	port = rcu_dereference(dev->garp_port);
507eca9ebacSPatrick McHardy 	if (!port)
508eca9ebacSPatrick McHardy 		goto err;
509eca9ebacSPatrick McHardy 	app = rcu_dereference(port->applicants[appl->type]);
510eca9ebacSPatrick McHardy 	if (!app)
511eca9ebacSPatrick McHardy 		goto err;
512eca9ebacSPatrick McHardy 
513eca9ebacSPatrick McHardy 	if (!pskb_may_pull(skb, sizeof(*gp)))
514eca9ebacSPatrick McHardy 		goto err;
515eca9ebacSPatrick McHardy 	gp = (struct garp_pdu_hdr *)skb->data;
516eca9ebacSPatrick McHardy 	if (get_unaligned(&gp->protocol) != htons(GARP_PROTOCOL_ID))
517eca9ebacSPatrick McHardy 		goto err;
518eca9ebacSPatrick McHardy 	skb_pull(skb, sizeof(*gp));
519eca9ebacSPatrick McHardy 
520eca9ebacSPatrick McHardy 	spin_lock(&app->lock);
521eca9ebacSPatrick McHardy 	while (skb->len > 0) {
522eca9ebacSPatrick McHardy 		if (garp_pdu_parse_msg(app, skb) < 0)
523eca9ebacSPatrick McHardy 			break;
524eca9ebacSPatrick McHardy 		if (garp_pdu_parse_end_mark(skb) < 0)
525eca9ebacSPatrick McHardy 			break;
526eca9ebacSPatrick McHardy 	}
527eca9ebacSPatrick McHardy 	spin_unlock(&app->lock);
528eca9ebacSPatrick McHardy err:
529eca9ebacSPatrick McHardy 	kfree_skb(skb);
530eca9ebacSPatrick McHardy }
531eca9ebacSPatrick McHardy 
532eca9ebacSPatrick McHardy static int garp_init_port(struct net_device *dev)
533eca9ebacSPatrick McHardy {
534eca9ebacSPatrick McHardy 	struct garp_port *port;
535eca9ebacSPatrick McHardy 
536eca9ebacSPatrick McHardy 	port = kzalloc(sizeof(*port), GFP_KERNEL);
537eca9ebacSPatrick McHardy 	if (!port)
538eca9ebacSPatrick McHardy 		return -ENOMEM;
539eca9ebacSPatrick McHardy 	rcu_assign_pointer(dev->garp_port, port);
540eca9ebacSPatrick McHardy 	return 0;
541eca9ebacSPatrick McHardy }
542eca9ebacSPatrick McHardy 
543eca9ebacSPatrick McHardy static void garp_release_port(struct net_device *dev)
544eca9ebacSPatrick McHardy {
5453cc77ec7SEric Dumazet 	struct garp_port *port = rtnl_dereference(dev->garp_port);
546eca9ebacSPatrick McHardy 	unsigned int i;
547eca9ebacSPatrick McHardy 
548eca9ebacSPatrick McHardy 	for (i = 0; i <= GARP_APPLICATION_MAX; i++) {
5493cc77ec7SEric Dumazet 		if (rtnl_dereference(port->applicants[i]))
550eca9ebacSPatrick McHardy 			return;
551eca9ebacSPatrick McHardy 	}
552a9b3cd7fSStephen Hemminger 	RCU_INIT_POINTER(dev->garp_port, NULL);
5530a384b22SEric Dumazet 	kfree_rcu(port, rcu);
554eca9ebacSPatrick McHardy }
555eca9ebacSPatrick McHardy 
556eca9ebacSPatrick McHardy int garp_init_applicant(struct net_device *dev, struct garp_application *appl)
557eca9ebacSPatrick McHardy {
558eca9ebacSPatrick McHardy 	struct garp_applicant *app;
559eca9ebacSPatrick McHardy 	int err;
560eca9ebacSPatrick McHardy 
561eca9ebacSPatrick McHardy 	ASSERT_RTNL();
562eca9ebacSPatrick McHardy 
5633cc77ec7SEric Dumazet 	if (!rtnl_dereference(dev->garp_port)) {
564eca9ebacSPatrick McHardy 		err = garp_init_port(dev);
565eca9ebacSPatrick McHardy 		if (err < 0)
566eca9ebacSPatrick McHardy 			goto err1;
567eca9ebacSPatrick McHardy 	}
568eca9ebacSPatrick McHardy 
569eca9ebacSPatrick McHardy 	err = -ENOMEM;
570eca9ebacSPatrick McHardy 	app = kzalloc(sizeof(*app), GFP_KERNEL);
571eca9ebacSPatrick McHardy 	if (!app)
572eca9ebacSPatrick McHardy 		goto err2;
573eca9ebacSPatrick McHardy 
57422bedad3SJiri Pirko 	err = dev_mc_add(dev, appl->proto.group_address);
575eca9ebacSPatrick McHardy 	if (err < 0)
576eca9ebacSPatrick McHardy 		goto err3;
577eca9ebacSPatrick McHardy 
578eca9ebacSPatrick McHardy 	app->dev = dev;
579eca9ebacSPatrick McHardy 	app->app = appl;
580eca9ebacSPatrick McHardy 	app->gid = RB_ROOT;
581eca9ebacSPatrick McHardy 	spin_lock_init(&app->lock);
582eca9ebacSPatrick McHardy 	skb_queue_head_init(&app->queue);
583eca9ebacSPatrick McHardy 	rcu_assign_pointer(dev->garp_port->applicants[appl->type], app);
584e99e88a9SKees Cook 	timer_setup(&app->join_timer, garp_join_timer, 0);
585eca9ebacSPatrick McHardy 	garp_join_timer_arm(app);
586eca9ebacSPatrick McHardy 	return 0;
587eca9ebacSPatrick McHardy 
588eca9ebacSPatrick McHardy err3:
589eca9ebacSPatrick McHardy 	kfree(app);
590eca9ebacSPatrick McHardy err2:
591eca9ebacSPatrick McHardy 	garp_release_port(dev);
592eca9ebacSPatrick McHardy err1:
593eca9ebacSPatrick McHardy 	return err;
594eca9ebacSPatrick McHardy }
595eca9ebacSPatrick McHardy EXPORT_SYMBOL_GPL(garp_init_applicant);
596eca9ebacSPatrick McHardy 
597eca9ebacSPatrick McHardy void garp_uninit_applicant(struct net_device *dev, struct garp_application *appl)
598eca9ebacSPatrick McHardy {
5993cc77ec7SEric Dumazet 	struct garp_port *port = rtnl_dereference(dev->garp_port);
6003cc77ec7SEric Dumazet 	struct garp_applicant *app = rtnl_dereference(port->applicants[appl->type]);
601eca9ebacSPatrick McHardy 
602eca9ebacSPatrick McHardy 	ASSERT_RTNL();
603eca9ebacSPatrick McHardy 
604a9b3cd7fSStephen Hemminger 	RCU_INIT_POINTER(port->applicants[appl->type], NULL);
605eca9ebacSPatrick McHardy 
606eca9ebacSPatrick McHardy 	/* Delete timer and generate a final TRANSMIT_PDU event to flush out
607eca9ebacSPatrick McHardy 	 * all pending messages before the applicant is gone. */
608eca9ebacSPatrick McHardy 	del_timer_sync(&app->join_timer);
609cfbe800bSCong Wang 
610cfbe800bSCong Wang 	spin_lock_bh(&app->lock);
611eca9ebacSPatrick McHardy 	garp_gid_event(app, GARP_EVENT_TRANSMIT_PDU);
612eca9ebacSPatrick McHardy 	garp_pdu_queue(app);
613cfbe800bSCong Wang 	spin_unlock_bh(&app->lock);
614cfbe800bSCong Wang 
615eca9ebacSPatrick McHardy 	garp_queue_xmit(app);
616eca9ebacSPatrick McHardy 
61722bedad3SJiri Pirko 	dev_mc_del(dev, appl->proto.group_address);
6180a384b22SEric Dumazet 	kfree_rcu(app, rcu);
619eca9ebacSPatrick McHardy 	garp_release_port(dev);
620eca9ebacSPatrick McHardy }
621eca9ebacSPatrick McHardy EXPORT_SYMBOL_GPL(garp_uninit_applicant);
622eca9ebacSPatrick McHardy 
623eca9ebacSPatrick McHardy int garp_register_application(struct garp_application *appl)
624eca9ebacSPatrick McHardy {
625eca9ebacSPatrick McHardy 	appl->proto.rcv = garp_pdu_rcv;
626eca9ebacSPatrick McHardy 	appl->proto.data = appl;
627eca9ebacSPatrick McHardy 	return stp_proto_register(&appl->proto);
628eca9ebacSPatrick McHardy }
629eca9ebacSPatrick McHardy EXPORT_SYMBOL_GPL(garp_register_application);
630eca9ebacSPatrick McHardy 
631eca9ebacSPatrick McHardy void garp_unregister_application(struct garp_application *appl)
632eca9ebacSPatrick McHardy {
633eca9ebacSPatrick McHardy 	stp_proto_unregister(&appl->proto);
634eca9ebacSPatrick McHardy }
635eca9ebacSPatrick McHardy EXPORT_SYMBOL_GPL(garp_unregister_application);
636