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