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