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