xref: /OK3568_Linux_fs/kernel/net/802/mrp.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  *	IEEE 802.1Q Multiple Registration Protocol (MRP)
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  *	Copyright (c) 2012 Massachusetts Institute of Technology
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  *	Adapted from code in net/802/garp.c
8*4882a593Smuzhiyun  *	Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
9*4882a593Smuzhiyun  */
10*4882a593Smuzhiyun #include <linux/kernel.h>
11*4882a593Smuzhiyun #include <linux/timer.h>
12*4882a593Smuzhiyun #include <linux/skbuff.h>
13*4882a593Smuzhiyun #include <linux/netdevice.h>
14*4882a593Smuzhiyun #include <linux/etherdevice.h>
15*4882a593Smuzhiyun #include <linux/rtnetlink.h>
16*4882a593Smuzhiyun #include <linux/slab.h>
17*4882a593Smuzhiyun #include <linux/module.h>
18*4882a593Smuzhiyun #include <net/mrp.h>
19*4882a593Smuzhiyun #include <asm/unaligned.h>
20*4882a593Smuzhiyun 
21*4882a593Smuzhiyun static unsigned int mrp_join_time __read_mostly = 200;
22*4882a593Smuzhiyun module_param(mrp_join_time, uint, 0644);
23*4882a593Smuzhiyun MODULE_PARM_DESC(mrp_join_time, "Join time in ms (default 200ms)");
24*4882a593Smuzhiyun 
25*4882a593Smuzhiyun static unsigned int mrp_periodic_time __read_mostly = 1000;
26*4882a593Smuzhiyun module_param(mrp_periodic_time, uint, 0644);
27*4882a593Smuzhiyun MODULE_PARM_DESC(mrp_periodic_time, "Periodic time in ms (default 1s)");
28*4882a593Smuzhiyun 
29*4882a593Smuzhiyun MODULE_LICENSE("GPL");
30*4882a593Smuzhiyun 
31*4882a593Smuzhiyun static const u8
32*4882a593Smuzhiyun mrp_applicant_state_table[MRP_APPLICANT_MAX + 1][MRP_EVENT_MAX + 1] = {
33*4882a593Smuzhiyun 	[MRP_APPLICANT_VO] = {
34*4882a593Smuzhiyun 		[MRP_EVENT_NEW]		= MRP_APPLICANT_VN,
35*4882a593Smuzhiyun 		[MRP_EVENT_JOIN]	= MRP_APPLICANT_VP,
36*4882a593Smuzhiyun 		[MRP_EVENT_LV]		= MRP_APPLICANT_VO,
37*4882a593Smuzhiyun 		[MRP_EVENT_TX]		= MRP_APPLICANT_VO,
38*4882a593Smuzhiyun 		[MRP_EVENT_R_NEW]	= MRP_APPLICANT_VO,
39*4882a593Smuzhiyun 		[MRP_EVENT_R_JOIN_IN]	= MRP_APPLICANT_AO,
40*4882a593Smuzhiyun 		[MRP_EVENT_R_IN]	= MRP_APPLICANT_VO,
41*4882a593Smuzhiyun 		[MRP_EVENT_R_JOIN_MT]	= MRP_APPLICANT_VO,
42*4882a593Smuzhiyun 		[MRP_EVENT_R_MT]	= MRP_APPLICANT_VO,
43*4882a593Smuzhiyun 		[MRP_EVENT_R_LV]	= MRP_APPLICANT_VO,
44*4882a593Smuzhiyun 		[MRP_EVENT_R_LA]	= MRP_APPLICANT_VO,
45*4882a593Smuzhiyun 		[MRP_EVENT_REDECLARE]	= MRP_APPLICANT_VO,
46*4882a593Smuzhiyun 		[MRP_EVENT_PERIODIC]	= MRP_APPLICANT_VO,
47*4882a593Smuzhiyun 	},
48*4882a593Smuzhiyun 	[MRP_APPLICANT_VP] = {
49*4882a593Smuzhiyun 		[MRP_EVENT_NEW]		= MRP_APPLICANT_VN,
50*4882a593Smuzhiyun 		[MRP_EVENT_JOIN]	= MRP_APPLICANT_VP,
51*4882a593Smuzhiyun 		[MRP_EVENT_LV]		= MRP_APPLICANT_VO,
52*4882a593Smuzhiyun 		[MRP_EVENT_TX]		= MRP_APPLICANT_AA,
53*4882a593Smuzhiyun 		[MRP_EVENT_R_NEW]	= MRP_APPLICANT_VP,
54*4882a593Smuzhiyun 		[MRP_EVENT_R_JOIN_IN]	= MRP_APPLICANT_AP,
55*4882a593Smuzhiyun 		[MRP_EVENT_R_IN]	= MRP_APPLICANT_VP,
56*4882a593Smuzhiyun 		[MRP_EVENT_R_JOIN_MT]	= MRP_APPLICANT_VP,
57*4882a593Smuzhiyun 		[MRP_EVENT_R_MT]	= MRP_APPLICANT_VP,
58*4882a593Smuzhiyun 		[MRP_EVENT_R_LV]	= MRP_APPLICANT_VP,
59*4882a593Smuzhiyun 		[MRP_EVENT_R_LA]	= MRP_APPLICANT_VP,
60*4882a593Smuzhiyun 		[MRP_EVENT_REDECLARE]	= MRP_APPLICANT_VP,
61*4882a593Smuzhiyun 		[MRP_EVENT_PERIODIC]	= MRP_APPLICANT_VP,
62*4882a593Smuzhiyun 	},
63*4882a593Smuzhiyun 	[MRP_APPLICANT_VN] = {
64*4882a593Smuzhiyun 		[MRP_EVENT_NEW]		= MRP_APPLICANT_VN,
65*4882a593Smuzhiyun 		[MRP_EVENT_JOIN]	= MRP_APPLICANT_VN,
66*4882a593Smuzhiyun 		[MRP_EVENT_LV]		= MRP_APPLICANT_LA,
67*4882a593Smuzhiyun 		[MRP_EVENT_TX]		= MRP_APPLICANT_AN,
68*4882a593Smuzhiyun 		[MRP_EVENT_R_NEW]	= MRP_APPLICANT_VN,
69*4882a593Smuzhiyun 		[MRP_EVENT_R_JOIN_IN]	= MRP_APPLICANT_VN,
70*4882a593Smuzhiyun 		[MRP_EVENT_R_IN]	= MRP_APPLICANT_VN,
71*4882a593Smuzhiyun 		[MRP_EVENT_R_JOIN_MT]	= MRP_APPLICANT_VN,
72*4882a593Smuzhiyun 		[MRP_EVENT_R_MT]	= MRP_APPLICANT_VN,
73*4882a593Smuzhiyun 		[MRP_EVENT_R_LV]	= MRP_APPLICANT_VN,
74*4882a593Smuzhiyun 		[MRP_EVENT_R_LA]	= MRP_APPLICANT_VN,
75*4882a593Smuzhiyun 		[MRP_EVENT_REDECLARE]	= MRP_APPLICANT_VN,
76*4882a593Smuzhiyun 		[MRP_EVENT_PERIODIC]	= MRP_APPLICANT_VN,
77*4882a593Smuzhiyun 	},
78*4882a593Smuzhiyun 	[MRP_APPLICANT_AN] = {
79*4882a593Smuzhiyun 		[MRP_EVENT_NEW]		= MRP_APPLICANT_AN,
80*4882a593Smuzhiyun 		[MRP_EVENT_JOIN]	= MRP_APPLICANT_AN,
81*4882a593Smuzhiyun 		[MRP_EVENT_LV]		= MRP_APPLICANT_LA,
82*4882a593Smuzhiyun 		[MRP_EVENT_TX]		= MRP_APPLICANT_QA,
83*4882a593Smuzhiyun 		[MRP_EVENT_R_NEW]	= MRP_APPLICANT_AN,
84*4882a593Smuzhiyun 		[MRP_EVENT_R_JOIN_IN]	= MRP_APPLICANT_AN,
85*4882a593Smuzhiyun 		[MRP_EVENT_R_IN]	= MRP_APPLICANT_AN,
86*4882a593Smuzhiyun 		[MRP_EVENT_R_JOIN_MT]	= MRP_APPLICANT_AN,
87*4882a593Smuzhiyun 		[MRP_EVENT_R_MT]	= MRP_APPLICANT_AN,
88*4882a593Smuzhiyun 		[MRP_EVENT_R_LV]	= MRP_APPLICANT_VN,
89*4882a593Smuzhiyun 		[MRP_EVENT_R_LA]	= MRP_APPLICANT_VN,
90*4882a593Smuzhiyun 		[MRP_EVENT_REDECLARE]	= MRP_APPLICANT_VN,
91*4882a593Smuzhiyun 		[MRP_EVENT_PERIODIC]	= MRP_APPLICANT_AN,
92*4882a593Smuzhiyun 	},
93*4882a593Smuzhiyun 	[MRP_APPLICANT_AA] = {
94*4882a593Smuzhiyun 		[MRP_EVENT_NEW]		= MRP_APPLICANT_VN,
95*4882a593Smuzhiyun 		[MRP_EVENT_JOIN]	= MRP_APPLICANT_AA,
96*4882a593Smuzhiyun 		[MRP_EVENT_LV]		= MRP_APPLICANT_LA,
97*4882a593Smuzhiyun 		[MRP_EVENT_TX]		= MRP_APPLICANT_QA,
98*4882a593Smuzhiyun 		[MRP_EVENT_R_NEW]	= MRP_APPLICANT_AA,
99*4882a593Smuzhiyun 		[MRP_EVENT_R_JOIN_IN]	= MRP_APPLICANT_QA,
100*4882a593Smuzhiyun 		[MRP_EVENT_R_IN]	= MRP_APPLICANT_AA,
101*4882a593Smuzhiyun 		[MRP_EVENT_R_JOIN_MT]	= MRP_APPLICANT_AA,
102*4882a593Smuzhiyun 		[MRP_EVENT_R_MT]	= MRP_APPLICANT_AA,
103*4882a593Smuzhiyun 		[MRP_EVENT_R_LV]	= MRP_APPLICANT_VP,
104*4882a593Smuzhiyun 		[MRP_EVENT_R_LA]	= MRP_APPLICANT_VP,
105*4882a593Smuzhiyun 		[MRP_EVENT_REDECLARE]	= MRP_APPLICANT_VP,
106*4882a593Smuzhiyun 		[MRP_EVENT_PERIODIC]	= MRP_APPLICANT_AA,
107*4882a593Smuzhiyun 	},
108*4882a593Smuzhiyun 	[MRP_APPLICANT_QA] = {
109*4882a593Smuzhiyun 		[MRP_EVENT_NEW]		= MRP_APPLICANT_VN,
110*4882a593Smuzhiyun 		[MRP_EVENT_JOIN]	= MRP_APPLICANT_QA,
111*4882a593Smuzhiyun 		[MRP_EVENT_LV]		= MRP_APPLICANT_LA,
112*4882a593Smuzhiyun 		[MRP_EVENT_TX]		= MRP_APPLICANT_QA,
113*4882a593Smuzhiyun 		[MRP_EVENT_R_NEW]	= MRP_APPLICANT_QA,
114*4882a593Smuzhiyun 		[MRP_EVENT_R_JOIN_IN]	= MRP_APPLICANT_QA,
115*4882a593Smuzhiyun 		[MRP_EVENT_R_IN]	= MRP_APPLICANT_QA,
116*4882a593Smuzhiyun 		[MRP_EVENT_R_JOIN_MT]	= MRP_APPLICANT_AA,
117*4882a593Smuzhiyun 		[MRP_EVENT_R_MT]	= MRP_APPLICANT_AA,
118*4882a593Smuzhiyun 		[MRP_EVENT_R_LV]	= MRP_APPLICANT_VP,
119*4882a593Smuzhiyun 		[MRP_EVENT_R_LA]	= MRP_APPLICANT_VP,
120*4882a593Smuzhiyun 		[MRP_EVENT_REDECLARE]	= MRP_APPLICANT_VP,
121*4882a593Smuzhiyun 		[MRP_EVENT_PERIODIC]	= MRP_APPLICANT_AA,
122*4882a593Smuzhiyun 	},
123*4882a593Smuzhiyun 	[MRP_APPLICANT_LA] = {
124*4882a593Smuzhiyun 		[MRP_EVENT_NEW]		= MRP_APPLICANT_VN,
125*4882a593Smuzhiyun 		[MRP_EVENT_JOIN]	= MRP_APPLICANT_AA,
126*4882a593Smuzhiyun 		[MRP_EVENT_LV]		= MRP_APPLICANT_LA,
127*4882a593Smuzhiyun 		[MRP_EVENT_TX]		= MRP_APPLICANT_VO,
128*4882a593Smuzhiyun 		[MRP_EVENT_R_NEW]	= MRP_APPLICANT_LA,
129*4882a593Smuzhiyun 		[MRP_EVENT_R_JOIN_IN]	= MRP_APPLICANT_LA,
130*4882a593Smuzhiyun 		[MRP_EVENT_R_IN]	= MRP_APPLICANT_LA,
131*4882a593Smuzhiyun 		[MRP_EVENT_R_JOIN_MT]	= MRP_APPLICANT_LA,
132*4882a593Smuzhiyun 		[MRP_EVENT_R_MT]	= MRP_APPLICANT_LA,
133*4882a593Smuzhiyun 		[MRP_EVENT_R_LV]	= MRP_APPLICANT_LA,
134*4882a593Smuzhiyun 		[MRP_EVENT_R_LA]	= MRP_APPLICANT_LA,
135*4882a593Smuzhiyun 		[MRP_EVENT_REDECLARE]	= MRP_APPLICANT_LA,
136*4882a593Smuzhiyun 		[MRP_EVENT_PERIODIC]	= MRP_APPLICANT_LA,
137*4882a593Smuzhiyun 	},
138*4882a593Smuzhiyun 	[MRP_APPLICANT_AO] = {
139*4882a593Smuzhiyun 		[MRP_EVENT_NEW]		= MRP_APPLICANT_VN,
140*4882a593Smuzhiyun 		[MRP_EVENT_JOIN]	= MRP_APPLICANT_AP,
141*4882a593Smuzhiyun 		[MRP_EVENT_LV]		= MRP_APPLICANT_AO,
142*4882a593Smuzhiyun 		[MRP_EVENT_TX]		= MRP_APPLICANT_AO,
143*4882a593Smuzhiyun 		[MRP_EVENT_R_NEW]	= MRP_APPLICANT_AO,
144*4882a593Smuzhiyun 		[MRP_EVENT_R_JOIN_IN]	= MRP_APPLICANT_QO,
145*4882a593Smuzhiyun 		[MRP_EVENT_R_IN]	= MRP_APPLICANT_AO,
146*4882a593Smuzhiyun 		[MRP_EVENT_R_JOIN_MT]	= MRP_APPLICANT_AO,
147*4882a593Smuzhiyun 		[MRP_EVENT_R_MT]	= MRP_APPLICANT_AO,
148*4882a593Smuzhiyun 		[MRP_EVENT_R_LV]	= MRP_APPLICANT_VO,
149*4882a593Smuzhiyun 		[MRP_EVENT_R_LA]	= MRP_APPLICANT_VO,
150*4882a593Smuzhiyun 		[MRP_EVENT_REDECLARE]	= MRP_APPLICANT_VO,
151*4882a593Smuzhiyun 		[MRP_EVENT_PERIODIC]	= MRP_APPLICANT_AO,
152*4882a593Smuzhiyun 	},
153*4882a593Smuzhiyun 	[MRP_APPLICANT_QO] = {
154*4882a593Smuzhiyun 		[MRP_EVENT_NEW]		= MRP_APPLICANT_VN,
155*4882a593Smuzhiyun 		[MRP_EVENT_JOIN]	= MRP_APPLICANT_QP,
156*4882a593Smuzhiyun 		[MRP_EVENT_LV]		= MRP_APPLICANT_QO,
157*4882a593Smuzhiyun 		[MRP_EVENT_TX]		= MRP_APPLICANT_QO,
158*4882a593Smuzhiyun 		[MRP_EVENT_R_NEW]	= MRP_APPLICANT_QO,
159*4882a593Smuzhiyun 		[MRP_EVENT_R_JOIN_IN]	= MRP_APPLICANT_QO,
160*4882a593Smuzhiyun 		[MRP_EVENT_R_IN]	= MRP_APPLICANT_QO,
161*4882a593Smuzhiyun 		[MRP_EVENT_R_JOIN_MT]	= MRP_APPLICANT_AO,
162*4882a593Smuzhiyun 		[MRP_EVENT_R_MT]	= MRP_APPLICANT_AO,
163*4882a593Smuzhiyun 		[MRP_EVENT_R_LV]	= MRP_APPLICANT_VO,
164*4882a593Smuzhiyun 		[MRP_EVENT_R_LA]	= MRP_APPLICANT_VO,
165*4882a593Smuzhiyun 		[MRP_EVENT_REDECLARE]	= MRP_APPLICANT_VO,
166*4882a593Smuzhiyun 		[MRP_EVENT_PERIODIC]	= MRP_APPLICANT_QO,
167*4882a593Smuzhiyun 	},
168*4882a593Smuzhiyun 	[MRP_APPLICANT_AP] = {
169*4882a593Smuzhiyun 		[MRP_EVENT_NEW]		= MRP_APPLICANT_VN,
170*4882a593Smuzhiyun 		[MRP_EVENT_JOIN]	= MRP_APPLICANT_AP,
171*4882a593Smuzhiyun 		[MRP_EVENT_LV]		= MRP_APPLICANT_AO,
172*4882a593Smuzhiyun 		[MRP_EVENT_TX]		= MRP_APPLICANT_QA,
173*4882a593Smuzhiyun 		[MRP_EVENT_R_NEW]	= MRP_APPLICANT_AP,
174*4882a593Smuzhiyun 		[MRP_EVENT_R_JOIN_IN]	= MRP_APPLICANT_QP,
175*4882a593Smuzhiyun 		[MRP_EVENT_R_IN]	= MRP_APPLICANT_AP,
176*4882a593Smuzhiyun 		[MRP_EVENT_R_JOIN_MT]	= MRP_APPLICANT_AP,
177*4882a593Smuzhiyun 		[MRP_EVENT_R_MT]	= MRP_APPLICANT_AP,
178*4882a593Smuzhiyun 		[MRP_EVENT_R_LV]	= MRP_APPLICANT_VP,
179*4882a593Smuzhiyun 		[MRP_EVENT_R_LA]	= MRP_APPLICANT_VP,
180*4882a593Smuzhiyun 		[MRP_EVENT_REDECLARE]	= MRP_APPLICANT_VP,
181*4882a593Smuzhiyun 		[MRP_EVENT_PERIODIC]	= MRP_APPLICANT_AP,
182*4882a593Smuzhiyun 	},
183*4882a593Smuzhiyun 	[MRP_APPLICANT_QP] = {
184*4882a593Smuzhiyun 		[MRP_EVENT_NEW]		= MRP_APPLICANT_VN,
185*4882a593Smuzhiyun 		[MRP_EVENT_JOIN]	= MRP_APPLICANT_QP,
186*4882a593Smuzhiyun 		[MRP_EVENT_LV]		= MRP_APPLICANT_QO,
187*4882a593Smuzhiyun 		[MRP_EVENT_TX]		= MRP_APPLICANT_QP,
188*4882a593Smuzhiyun 		[MRP_EVENT_R_NEW]	= MRP_APPLICANT_QP,
189*4882a593Smuzhiyun 		[MRP_EVENT_R_JOIN_IN]	= MRP_APPLICANT_QP,
190*4882a593Smuzhiyun 		[MRP_EVENT_R_IN]	= MRP_APPLICANT_QP,
191*4882a593Smuzhiyun 		[MRP_EVENT_R_JOIN_MT]	= MRP_APPLICANT_AP,
192*4882a593Smuzhiyun 		[MRP_EVENT_R_MT]	= MRP_APPLICANT_AP,
193*4882a593Smuzhiyun 		[MRP_EVENT_R_LV]	= MRP_APPLICANT_VP,
194*4882a593Smuzhiyun 		[MRP_EVENT_R_LA]	= MRP_APPLICANT_VP,
195*4882a593Smuzhiyun 		[MRP_EVENT_REDECLARE]	= MRP_APPLICANT_VP,
196*4882a593Smuzhiyun 		[MRP_EVENT_PERIODIC]	= MRP_APPLICANT_AP,
197*4882a593Smuzhiyun 	},
198*4882a593Smuzhiyun };
199*4882a593Smuzhiyun 
200*4882a593Smuzhiyun static const u8
201*4882a593Smuzhiyun mrp_tx_action_table[MRP_APPLICANT_MAX + 1] = {
202*4882a593Smuzhiyun 	[MRP_APPLICANT_VO] = MRP_TX_ACTION_S_IN_OPTIONAL,
203*4882a593Smuzhiyun 	[MRP_APPLICANT_VP] = MRP_TX_ACTION_S_JOIN_IN,
204*4882a593Smuzhiyun 	[MRP_APPLICANT_VN] = MRP_TX_ACTION_S_NEW,
205*4882a593Smuzhiyun 	[MRP_APPLICANT_AN] = MRP_TX_ACTION_S_NEW,
206*4882a593Smuzhiyun 	[MRP_APPLICANT_AA] = MRP_TX_ACTION_S_JOIN_IN,
207*4882a593Smuzhiyun 	[MRP_APPLICANT_QA] = MRP_TX_ACTION_S_JOIN_IN_OPTIONAL,
208*4882a593Smuzhiyun 	[MRP_APPLICANT_LA] = MRP_TX_ACTION_S_LV,
209*4882a593Smuzhiyun 	[MRP_APPLICANT_AO] = MRP_TX_ACTION_S_IN_OPTIONAL,
210*4882a593Smuzhiyun 	[MRP_APPLICANT_QO] = MRP_TX_ACTION_S_IN_OPTIONAL,
211*4882a593Smuzhiyun 	[MRP_APPLICANT_AP] = MRP_TX_ACTION_S_JOIN_IN,
212*4882a593Smuzhiyun 	[MRP_APPLICANT_QP] = MRP_TX_ACTION_S_IN_OPTIONAL,
213*4882a593Smuzhiyun };
214*4882a593Smuzhiyun 
mrp_attrvalue_inc(void * value,u8 len)215*4882a593Smuzhiyun static void mrp_attrvalue_inc(void *value, u8 len)
216*4882a593Smuzhiyun {
217*4882a593Smuzhiyun 	u8 *v = (u8 *)value;
218*4882a593Smuzhiyun 
219*4882a593Smuzhiyun 	/* Add 1 to the last byte. If it becomes zero,
220*4882a593Smuzhiyun 	 * go to the previous byte and repeat.
221*4882a593Smuzhiyun 	 */
222*4882a593Smuzhiyun 	while (len > 0 && !++v[--len])
223*4882a593Smuzhiyun 		;
224*4882a593Smuzhiyun }
225*4882a593Smuzhiyun 
mrp_attr_cmp(const struct mrp_attr * attr,const void * value,u8 len,u8 type)226*4882a593Smuzhiyun static int mrp_attr_cmp(const struct mrp_attr *attr,
227*4882a593Smuzhiyun 			 const void *value, u8 len, u8 type)
228*4882a593Smuzhiyun {
229*4882a593Smuzhiyun 	if (attr->type != type)
230*4882a593Smuzhiyun 		return attr->type - type;
231*4882a593Smuzhiyun 	if (attr->len != len)
232*4882a593Smuzhiyun 		return attr->len - len;
233*4882a593Smuzhiyun 	return memcmp(attr->value, value, len);
234*4882a593Smuzhiyun }
235*4882a593Smuzhiyun 
mrp_attr_lookup(const struct mrp_applicant * app,const void * value,u8 len,u8 type)236*4882a593Smuzhiyun static struct mrp_attr *mrp_attr_lookup(const struct mrp_applicant *app,
237*4882a593Smuzhiyun 					const void *value, u8 len, u8 type)
238*4882a593Smuzhiyun {
239*4882a593Smuzhiyun 	struct rb_node *parent = app->mad.rb_node;
240*4882a593Smuzhiyun 	struct mrp_attr *attr;
241*4882a593Smuzhiyun 	int d;
242*4882a593Smuzhiyun 
243*4882a593Smuzhiyun 	while (parent) {
244*4882a593Smuzhiyun 		attr = rb_entry(parent, struct mrp_attr, node);
245*4882a593Smuzhiyun 		d = mrp_attr_cmp(attr, value, len, type);
246*4882a593Smuzhiyun 		if (d > 0)
247*4882a593Smuzhiyun 			parent = parent->rb_left;
248*4882a593Smuzhiyun 		else if (d < 0)
249*4882a593Smuzhiyun 			parent = parent->rb_right;
250*4882a593Smuzhiyun 		else
251*4882a593Smuzhiyun 			return attr;
252*4882a593Smuzhiyun 	}
253*4882a593Smuzhiyun 	return NULL;
254*4882a593Smuzhiyun }
255*4882a593Smuzhiyun 
mrp_attr_create(struct mrp_applicant * app,const void * value,u8 len,u8 type)256*4882a593Smuzhiyun static struct mrp_attr *mrp_attr_create(struct mrp_applicant *app,
257*4882a593Smuzhiyun 					const void *value, u8 len, u8 type)
258*4882a593Smuzhiyun {
259*4882a593Smuzhiyun 	struct rb_node *parent = NULL, **p = &app->mad.rb_node;
260*4882a593Smuzhiyun 	struct mrp_attr *attr;
261*4882a593Smuzhiyun 	int d;
262*4882a593Smuzhiyun 
263*4882a593Smuzhiyun 	while (*p) {
264*4882a593Smuzhiyun 		parent = *p;
265*4882a593Smuzhiyun 		attr = rb_entry(parent, struct mrp_attr, node);
266*4882a593Smuzhiyun 		d = mrp_attr_cmp(attr, value, len, type);
267*4882a593Smuzhiyun 		if (d > 0)
268*4882a593Smuzhiyun 			p = &parent->rb_left;
269*4882a593Smuzhiyun 		else if (d < 0)
270*4882a593Smuzhiyun 			p = &parent->rb_right;
271*4882a593Smuzhiyun 		else {
272*4882a593Smuzhiyun 			/* The attribute already exists; re-use it. */
273*4882a593Smuzhiyun 			return attr;
274*4882a593Smuzhiyun 		}
275*4882a593Smuzhiyun 	}
276*4882a593Smuzhiyun 	attr = kmalloc(sizeof(*attr) + len, GFP_ATOMIC);
277*4882a593Smuzhiyun 	if (!attr)
278*4882a593Smuzhiyun 		return attr;
279*4882a593Smuzhiyun 	attr->state = MRP_APPLICANT_VO;
280*4882a593Smuzhiyun 	attr->type  = type;
281*4882a593Smuzhiyun 	attr->len   = len;
282*4882a593Smuzhiyun 	memcpy(attr->value, value, len);
283*4882a593Smuzhiyun 
284*4882a593Smuzhiyun 	rb_link_node(&attr->node, parent, p);
285*4882a593Smuzhiyun 	rb_insert_color(&attr->node, &app->mad);
286*4882a593Smuzhiyun 	return attr;
287*4882a593Smuzhiyun }
288*4882a593Smuzhiyun 
mrp_attr_destroy(struct mrp_applicant * app,struct mrp_attr * attr)289*4882a593Smuzhiyun static void mrp_attr_destroy(struct mrp_applicant *app, struct mrp_attr *attr)
290*4882a593Smuzhiyun {
291*4882a593Smuzhiyun 	rb_erase(&attr->node, &app->mad);
292*4882a593Smuzhiyun 	kfree(attr);
293*4882a593Smuzhiyun }
294*4882a593Smuzhiyun 
mrp_attr_destroy_all(struct mrp_applicant * app)295*4882a593Smuzhiyun static void mrp_attr_destroy_all(struct mrp_applicant *app)
296*4882a593Smuzhiyun {
297*4882a593Smuzhiyun 	struct rb_node *node, *next;
298*4882a593Smuzhiyun 	struct mrp_attr *attr;
299*4882a593Smuzhiyun 
300*4882a593Smuzhiyun 	for (node = rb_first(&app->mad);
301*4882a593Smuzhiyun 	     next = node ? rb_next(node) : NULL, node != NULL;
302*4882a593Smuzhiyun 	     node = next) {
303*4882a593Smuzhiyun 		attr = rb_entry(node, struct mrp_attr, node);
304*4882a593Smuzhiyun 		mrp_attr_destroy(app, attr);
305*4882a593Smuzhiyun 	}
306*4882a593Smuzhiyun }
307*4882a593Smuzhiyun 
mrp_pdu_init(struct mrp_applicant * app)308*4882a593Smuzhiyun static int mrp_pdu_init(struct mrp_applicant *app)
309*4882a593Smuzhiyun {
310*4882a593Smuzhiyun 	struct sk_buff *skb;
311*4882a593Smuzhiyun 	struct mrp_pdu_hdr *ph;
312*4882a593Smuzhiyun 
313*4882a593Smuzhiyun 	skb = alloc_skb(app->dev->mtu + LL_RESERVED_SPACE(app->dev),
314*4882a593Smuzhiyun 			GFP_ATOMIC);
315*4882a593Smuzhiyun 	if (!skb)
316*4882a593Smuzhiyun 		return -ENOMEM;
317*4882a593Smuzhiyun 
318*4882a593Smuzhiyun 	skb->dev = app->dev;
319*4882a593Smuzhiyun 	skb->protocol = app->app->pkttype.type;
320*4882a593Smuzhiyun 	skb_reserve(skb, LL_RESERVED_SPACE(app->dev));
321*4882a593Smuzhiyun 	skb_reset_network_header(skb);
322*4882a593Smuzhiyun 	skb_reset_transport_header(skb);
323*4882a593Smuzhiyun 
324*4882a593Smuzhiyun 	ph = __skb_put(skb, sizeof(*ph));
325*4882a593Smuzhiyun 	ph->version = app->app->version;
326*4882a593Smuzhiyun 
327*4882a593Smuzhiyun 	app->pdu = skb;
328*4882a593Smuzhiyun 	return 0;
329*4882a593Smuzhiyun }
330*4882a593Smuzhiyun 
mrp_pdu_append_end_mark(struct mrp_applicant * app)331*4882a593Smuzhiyun static int mrp_pdu_append_end_mark(struct mrp_applicant *app)
332*4882a593Smuzhiyun {
333*4882a593Smuzhiyun 	__be16 *endmark;
334*4882a593Smuzhiyun 
335*4882a593Smuzhiyun 	if (skb_tailroom(app->pdu) < sizeof(*endmark))
336*4882a593Smuzhiyun 		return -1;
337*4882a593Smuzhiyun 	endmark = __skb_put(app->pdu, sizeof(*endmark));
338*4882a593Smuzhiyun 	put_unaligned(MRP_END_MARK, endmark);
339*4882a593Smuzhiyun 	return 0;
340*4882a593Smuzhiyun }
341*4882a593Smuzhiyun 
mrp_pdu_queue(struct mrp_applicant * app)342*4882a593Smuzhiyun static void mrp_pdu_queue(struct mrp_applicant *app)
343*4882a593Smuzhiyun {
344*4882a593Smuzhiyun 	if (!app->pdu)
345*4882a593Smuzhiyun 		return;
346*4882a593Smuzhiyun 
347*4882a593Smuzhiyun 	if (mrp_cb(app->pdu)->mh)
348*4882a593Smuzhiyun 		mrp_pdu_append_end_mark(app);
349*4882a593Smuzhiyun 	mrp_pdu_append_end_mark(app);
350*4882a593Smuzhiyun 
351*4882a593Smuzhiyun 	dev_hard_header(app->pdu, app->dev, ntohs(app->app->pkttype.type),
352*4882a593Smuzhiyun 			app->app->group_address, app->dev->dev_addr,
353*4882a593Smuzhiyun 			app->pdu->len);
354*4882a593Smuzhiyun 
355*4882a593Smuzhiyun 	skb_queue_tail(&app->queue, app->pdu);
356*4882a593Smuzhiyun 	app->pdu = NULL;
357*4882a593Smuzhiyun }
358*4882a593Smuzhiyun 
mrp_queue_xmit(struct mrp_applicant * app)359*4882a593Smuzhiyun static void mrp_queue_xmit(struct mrp_applicant *app)
360*4882a593Smuzhiyun {
361*4882a593Smuzhiyun 	struct sk_buff *skb;
362*4882a593Smuzhiyun 
363*4882a593Smuzhiyun 	while ((skb = skb_dequeue(&app->queue)))
364*4882a593Smuzhiyun 		dev_queue_xmit(skb);
365*4882a593Smuzhiyun }
366*4882a593Smuzhiyun 
mrp_pdu_append_msg_hdr(struct mrp_applicant * app,u8 attrtype,u8 attrlen)367*4882a593Smuzhiyun static int mrp_pdu_append_msg_hdr(struct mrp_applicant *app,
368*4882a593Smuzhiyun 				  u8 attrtype, u8 attrlen)
369*4882a593Smuzhiyun {
370*4882a593Smuzhiyun 	struct mrp_msg_hdr *mh;
371*4882a593Smuzhiyun 
372*4882a593Smuzhiyun 	if (mrp_cb(app->pdu)->mh) {
373*4882a593Smuzhiyun 		if (mrp_pdu_append_end_mark(app) < 0)
374*4882a593Smuzhiyun 			return -1;
375*4882a593Smuzhiyun 		mrp_cb(app->pdu)->mh = NULL;
376*4882a593Smuzhiyun 		mrp_cb(app->pdu)->vah = NULL;
377*4882a593Smuzhiyun 	}
378*4882a593Smuzhiyun 
379*4882a593Smuzhiyun 	if (skb_tailroom(app->pdu) < sizeof(*mh))
380*4882a593Smuzhiyun 		return -1;
381*4882a593Smuzhiyun 	mh = __skb_put(app->pdu, sizeof(*mh));
382*4882a593Smuzhiyun 	mh->attrtype = attrtype;
383*4882a593Smuzhiyun 	mh->attrlen = attrlen;
384*4882a593Smuzhiyun 	mrp_cb(app->pdu)->mh = mh;
385*4882a593Smuzhiyun 	return 0;
386*4882a593Smuzhiyun }
387*4882a593Smuzhiyun 
mrp_pdu_append_vecattr_hdr(struct mrp_applicant * app,const void * firstattrvalue,u8 attrlen)388*4882a593Smuzhiyun static int mrp_pdu_append_vecattr_hdr(struct mrp_applicant *app,
389*4882a593Smuzhiyun 				      const void *firstattrvalue, u8 attrlen)
390*4882a593Smuzhiyun {
391*4882a593Smuzhiyun 	struct mrp_vecattr_hdr *vah;
392*4882a593Smuzhiyun 
393*4882a593Smuzhiyun 	if (skb_tailroom(app->pdu) < sizeof(*vah) + attrlen)
394*4882a593Smuzhiyun 		return -1;
395*4882a593Smuzhiyun 	vah = __skb_put(app->pdu, sizeof(*vah) + attrlen);
396*4882a593Smuzhiyun 	put_unaligned(0, &vah->lenflags);
397*4882a593Smuzhiyun 	memcpy(vah->firstattrvalue, firstattrvalue, attrlen);
398*4882a593Smuzhiyun 	mrp_cb(app->pdu)->vah = vah;
399*4882a593Smuzhiyun 	memcpy(mrp_cb(app->pdu)->attrvalue, firstattrvalue, attrlen);
400*4882a593Smuzhiyun 	return 0;
401*4882a593Smuzhiyun }
402*4882a593Smuzhiyun 
mrp_pdu_append_vecattr_event(struct mrp_applicant * app,const struct mrp_attr * attr,enum mrp_vecattr_event vaevent)403*4882a593Smuzhiyun static int mrp_pdu_append_vecattr_event(struct mrp_applicant *app,
404*4882a593Smuzhiyun 					const struct mrp_attr *attr,
405*4882a593Smuzhiyun 					enum mrp_vecattr_event vaevent)
406*4882a593Smuzhiyun {
407*4882a593Smuzhiyun 	u16 len, pos;
408*4882a593Smuzhiyun 	u8 *vaevents;
409*4882a593Smuzhiyun 	int err;
410*4882a593Smuzhiyun again:
411*4882a593Smuzhiyun 	if (!app->pdu) {
412*4882a593Smuzhiyun 		err = mrp_pdu_init(app);
413*4882a593Smuzhiyun 		if (err < 0)
414*4882a593Smuzhiyun 			return err;
415*4882a593Smuzhiyun 	}
416*4882a593Smuzhiyun 
417*4882a593Smuzhiyun 	/* If there is no Message header in the PDU, or the Message header is
418*4882a593Smuzhiyun 	 * for a different attribute type, add an EndMark (if necessary) and a
419*4882a593Smuzhiyun 	 * new Message header to the PDU.
420*4882a593Smuzhiyun 	 */
421*4882a593Smuzhiyun 	if (!mrp_cb(app->pdu)->mh ||
422*4882a593Smuzhiyun 	    mrp_cb(app->pdu)->mh->attrtype != attr->type ||
423*4882a593Smuzhiyun 	    mrp_cb(app->pdu)->mh->attrlen != attr->len) {
424*4882a593Smuzhiyun 		if (mrp_pdu_append_msg_hdr(app, attr->type, attr->len) < 0)
425*4882a593Smuzhiyun 			goto queue;
426*4882a593Smuzhiyun 	}
427*4882a593Smuzhiyun 
428*4882a593Smuzhiyun 	/* If there is no VectorAttribute header for this Message in the PDU,
429*4882a593Smuzhiyun 	 * or this attribute's value does not sequentially follow the previous
430*4882a593Smuzhiyun 	 * attribute's value, add a new VectorAttribute header to the PDU.
431*4882a593Smuzhiyun 	 */
432*4882a593Smuzhiyun 	if (!mrp_cb(app->pdu)->vah ||
433*4882a593Smuzhiyun 	    memcmp(mrp_cb(app->pdu)->attrvalue, attr->value, attr->len)) {
434*4882a593Smuzhiyun 		if (mrp_pdu_append_vecattr_hdr(app, attr->value, attr->len) < 0)
435*4882a593Smuzhiyun 			goto queue;
436*4882a593Smuzhiyun 	}
437*4882a593Smuzhiyun 
438*4882a593Smuzhiyun 	len = be16_to_cpu(get_unaligned(&mrp_cb(app->pdu)->vah->lenflags));
439*4882a593Smuzhiyun 	pos = len % 3;
440*4882a593Smuzhiyun 
441*4882a593Smuzhiyun 	/* Events are packed into Vectors in the PDU, three to a byte. Add a
442*4882a593Smuzhiyun 	 * byte to the end of the Vector if necessary.
443*4882a593Smuzhiyun 	 */
444*4882a593Smuzhiyun 	if (!pos) {
445*4882a593Smuzhiyun 		if (skb_tailroom(app->pdu) < sizeof(u8))
446*4882a593Smuzhiyun 			goto queue;
447*4882a593Smuzhiyun 		vaevents = __skb_put(app->pdu, sizeof(u8));
448*4882a593Smuzhiyun 	} else {
449*4882a593Smuzhiyun 		vaevents = (u8 *)(skb_tail_pointer(app->pdu) - sizeof(u8));
450*4882a593Smuzhiyun 	}
451*4882a593Smuzhiyun 
452*4882a593Smuzhiyun 	switch (pos) {
453*4882a593Smuzhiyun 	case 0:
454*4882a593Smuzhiyun 		*vaevents = vaevent * (__MRP_VECATTR_EVENT_MAX *
455*4882a593Smuzhiyun 				       __MRP_VECATTR_EVENT_MAX);
456*4882a593Smuzhiyun 		break;
457*4882a593Smuzhiyun 	case 1:
458*4882a593Smuzhiyun 		*vaevents += vaevent * __MRP_VECATTR_EVENT_MAX;
459*4882a593Smuzhiyun 		break;
460*4882a593Smuzhiyun 	case 2:
461*4882a593Smuzhiyun 		*vaevents += vaevent;
462*4882a593Smuzhiyun 		break;
463*4882a593Smuzhiyun 	default:
464*4882a593Smuzhiyun 		WARN_ON(1);
465*4882a593Smuzhiyun 	}
466*4882a593Smuzhiyun 
467*4882a593Smuzhiyun 	/* Increment the length of the VectorAttribute in the PDU, as well as
468*4882a593Smuzhiyun 	 * the value of the next attribute that would continue its Vector.
469*4882a593Smuzhiyun 	 */
470*4882a593Smuzhiyun 	put_unaligned(cpu_to_be16(++len), &mrp_cb(app->pdu)->vah->lenflags);
471*4882a593Smuzhiyun 	mrp_attrvalue_inc(mrp_cb(app->pdu)->attrvalue, attr->len);
472*4882a593Smuzhiyun 
473*4882a593Smuzhiyun 	return 0;
474*4882a593Smuzhiyun 
475*4882a593Smuzhiyun queue:
476*4882a593Smuzhiyun 	mrp_pdu_queue(app);
477*4882a593Smuzhiyun 	goto again;
478*4882a593Smuzhiyun }
479*4882a593Smuzhiyun 
mrp_attr_event(struct mrp_applicant * app,struct mrp_attr * attr,enum mrp_event event)480*4882a593Smuzhiyun static void mrp_attr_event(struct mrp_applicant *app,
481*4882a593Smuzhiyun 			   struct mrp_attr *attr, enum mrp_event event)
482*4882a593Smuzhiyun {
483*4882a593Smuzhiyun 	enum mrp_applicant_state state;
484*4882a593Smuzhiyun 
485*4882a593Smuzhiyun 	state = mrp_applicant_state_table[attr->state][event];
486*4882a593Smuzhiyun 	if (state == MRP_APPLICANT_INVALID) {
487*4882a593Smuzhiyun 		WARN_ON(1);
488*4882a593Smuzhiyun 		return;
489*4882a593Smuzhiyun 	}
490*4882a593Smuzhiyun 
491*4882a593Smuzhiyun 	if (event == MRP_EVENT_TX) {
492*4882a593Smuzhiyun 		/* When appending the attribute fails, don't update its state
493*4882a593Smuzhiyun 		 * in order to retry at the next TX event.
494*4882a593Smuzhiyun 		 */
495*4882a593Smuzhiyun 
496*4882a593Smuzhiyun 		switch (mrp_tx_action_table[attr->state]) {
497*4882a593Smuzhiyun 		case MRP_TX_ACTION_NONE:
498*4882a593Smuzhiyun 		case MRP_TX_ACTION_S_JOIN_IN_OPTIONAL:
499*4882a593Smuzhiyun 		case MRP_TX_ACTION_S_IN_OPTIONAL:
500*4882a593Smuzhiyun 			break;
501*4882a593Smuzhiyun 		case MRP_TX_ACTION_S_NEW:
502*4882a593Smuzhiyun 			if (mrp_pdu_append_vecattr_event(
503*4882a593Smuzhiyun 				    app, attr, MRP_VECATTR_EVENT_NEW) < 0)
504*4882a593Smuzhiyun 				return;
505*4882a593Smuzhiyun 			break;
506*4882a593Smuzhiyun 		case MRP_TX_ACTION_S_JOIN_IN:
507*4882a593Smuzhiyun 			if (mrp_pdu_append_vecattr_event(
508*4882a593Smuzhiyun 				    app, attr, MRP_VECATTR_EVENT_JOIN_IN) < 0)
509*4882a593Smuzhiyun 				return;
510*4882a593Smuzhiyun 			break;
511*4882a593Smuzhiyun 		case MRP_TX_ACTION_S_LV:
512*4882a593Smuzhiyun 			if (mrp_pdu_append_vecattr_event(
513*4882a593Smuzhiyun 				    app, attr, MRP_VECATTR_EVENT_LV) < 0)
514*4882a593Smuzhiyun 				return;
515*4882a593Smuzhiyun 			/* As a pure applicant, sending a leave message
516*4882a593Smuzhiyun 			 * implies that the attribute was unregistered and
517*4882a593Smuzhiyun 			 * can be destroyed.
518*4882a593Smuzhiyun 			 */
519*4882a593Smuzhiyun 			mrp_attr_destroy(app, attr);
520*4882a593Smuzhiyun 			return;
521*4882a593Smuzhiyun 		default:
522*4882a593Smuzhiyun 			WARN_ON(1);
523*4882a593Smuzhiyun 		}
524*4882a593Smuzhiyun 	}
525*4882a593Smuzhiyun 
526*4882a593Smuzhiyun 	attr->state = state;
527*4882a593Smuzhiyun }
528*4882a593Smuzhiyun 
mrp_request_join(const struct net_device * dev,const struct mrp_application * appl,const void * value,u8 len,u8 type)529*4882a593Smuzhiyun int mrp_request_join(const struct net_device *dev,
530*4882a593Smuzhiyun 		     const struct mrp_application *appl,
531*4882a593Smuzhiyun 		     const void *value, u8 len, u8 type)
532*4882a593Smuzhiyun {
533*4882a593Smuzhiyun 	struct mrp_port *port = rtnl_dereference(dev->mrp_port);
534*4882a593Smuzhiyun 	struct mrp_applicant *app = rtnl_dereference(
535*4882a593Smuzhiyun 		port->applicants[appl->type]);
536*4882a593Smuzhiyun 	struct mrp_attr *attr;
537*4882a593Smuzhiyun 
538*4882a593Smuzhiyun 	if (sizeof(struct mrp_skb_cb) + len >
539*4882a593Smuzhiyun 	    sizeof_field(struct sk_buff, cb))
540*4882a593Smuzhiyun 		return -ENOMEM;
541*4882a593Smuzhiyun 
542*4882a593Smuzhiyun 	spin_lock_bh(&app->lock);
543*4882a593Smuzhiyun 	attr = mrp_attr_create(app, value, len, type);
544*4882a593Smuzhiyun 	if (!attr) {
545*4882a593Smuzhiyun 		spin_unlock_bh(&app->lock);
546*4882a593Smuzhiyun 		return -ENOMEM;
547*4882a593Smuzhiyun 	}
548*4882a593Smuzhiyun 	mrp_attr_event(app, attr, MRP_EVENT_JOIN);
549*4882a593Smuzhiyun 	spin_unlock_bh(&app->lock);
550*4882a593Smuzhiyun 	return 0;
551*4882a593Smuzhiyun }
552*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(mrp_request_join);
553*4882a593Smuzhiyun 
mrp_request_leave(const struct net_device * dev,const struct mrp_application * appl,const void * value,u8 len,u8 type)554*4882a593Smuzhiyun void mrp_request_leave(const struct net_device *dev,
555*4882a593Smuzhiyun 		       const struct mrp_application *appl,
556*4882a593Smuzhiyun 		       const void *value, u8 len, u8 type)
557*4882a593Smuzhiyun {
558*4882a593Smuzhiyun 	struct mrp_port *port = rtnl_dereference(dev->mrp_port);
559*4882a593Smuzhiyun 	struct mrp_applicant *app = rtnl_dereference(
560*4882a593Smuzhiyun 		port->applicants[appl->type]);
561*4882a593Smuzhiyun 	struct mrp_attr *attr;
562*4882a593Smuzhiyun 
563*4882a593Smuzhiyun 	if (sizeof(struct mrp_skb_cb) + len >
564*4882a593Smuzhiyun 	    sizeof_field(struct sk_buff, cb))
565*4882a593Smuzhiyun 		return;
566*4882a593Smuzhiyun 
567*4882a593Smuzhiyun 	spin_lock_bh(&app->lock);
568*4882a593Smuzhiyun 	attr = mrp_attr_lookup(app, value, len, type);
569*4882a593Smuzhiyun 	if (!attr) {
570*4882a593Smuzhiyun 		spin_unlock_bh(&app->lock);
571*4882a593Smuzhiyun 		return;
572*4882a593Smuzhiyun 	}
573*4882a593Smuzhiyun 	mrp_attr_event(app, attr, MRP_EVENT_LV);
574*4882a593Smuzhiyun 	spin_unlock_bh(&app->lock);
575*4882a593Smuzhiyun }
576*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(mrp_request_leave);
577*4882a593Smuzhiyun 
mrp_mad_event(struct mrp_applicant * app,enum mrp_event event)578*4882a593Smuzhiyun static void mrp_mad_event(struct mrp_applicant *app, enum mrp_event event)
579*4882a593Smuzhiyun {
580*4882a593Smuzhiyun 	struct rb_node *node, *next;
581*4882a593Smuzhiyun 	struct mrp_attr *attr;
582*4882a593Smuzhiyun 
583*4882a593Smuzhiyun 	for (node = rb_first(&app->mad);
584*4882a593Smuzhiyun 	     next = node ? rb_next(node) : NULL, node != NULL;
585*4882a593Smuzhiyun 	     node = next) {
586*4882a593Smuzhiyun 		attr = rb_entry(node, struct mrp_attr, node);
587*4882a593Smuzhiyun 		mrp_attr_event(app, attr, event);
588*4882a593Smuzhiyun 	}
589*4882a593Smuzhiyun }
590*4882a593Smuzhiyun 
mrp_join_timer_arm(struct mrp_applicant * app)591*4882a593Smuzhiyun static void mrp_join_timer_arm(struct mrp_applicant *app)
592*4882a593Smuzhiyun {
593*4882a593Smuzhiyun 	unsigned long delay;
594*4882a593Smuzhiyun 
595*4882a593Smuzhiyun 	delay = (u64)msecs_to_jiffies(mrp_join_time) * prandom_u32() >> 32;
596*4882a593Smuzhiyun 	mod_timer(&app->join_timer, jiffies + delay);
597*4882a593Smuzhiyun }
598*4882a593Smuzhiyun 
mrp_join_timer(struct timer_list * t)599*4882a593Smuzhiyun static void mrp_join_timer(struct timer_list *t)
600*4882a593Smuzhiyun {
601*4882a593Smuzhiyun 	struct mrp_applicant *app = from_timer(app, t, join_timer);
602*4882a593Smuzhiyun 
603*4882a593Smuzhiyun 	spin_lock(&app->lock);
604*4882a593Smuzhiyun 	mrp_mad_event(app, MRP_EVENT_TX);
605*4882a593Smuzhiyun 	mrp_pdu_queue(app);
606*4882a593Smuzhiyun 	spin_unlock(&app->lock);
607*4882a593Smuzhiyun 
608*4882a593Smuzhiyun 	mrp_queue_xmit(app);
609*4882a593Smuzhiyun 	mrp_join_timer_arm(app);
610*4882a593Smuzhiyun }
611*4882a593Smuzhiyun 
mrp_periodic_timer_arm(struct mrp_applicant * app)612*4882a593Smuzhiyun static void mrp_periodic_timer_arm(struct mrp_applicant *app)
613*4882a593Smuzhiyun {
614*4882a593Smuzhiyun 	mod_timer(&app->periodic_timer,
615*4882a593Smuzhiyun 		  jiffies + msecs_to_jiffies(mrp_periodic_time));
616*4882a593Smuzhiyun }
617*4882a593Smuzhiyun 
mrp_periodic_timer(struct timer_list * t)618*4882a593Smuzhiyun static void mrp_periodic_timer(struct timer_list *t)
619*4882a593Smuzhiyun {
620*4882a593Smuzhiyun 	struct mrp_applicant *app = from_timer(app, t, periodic_timer);
621*4882a593Smuzhiyun 
622*4882a593Smuzhiyun 	spin_lock(&app->lock);
623*4882a593Smuzhiyun 	mrp_mad_event(app, MRP_EVENT_PERIODIC);
624*4882a593Smuzhiyun 	mrp_pdu_queue(app);
625*4882a593Smuzhiyun 	spin_unlock(&app->lock);
626*4882a593Smuzhiyun 
627*4882a593Smuzhiyun 	mrp_periodic_timer_arm(app);
628*4882a593Smuzhiyun }
629*4882a593Smuzhiyun 
mrp_pdu_parse_end_mark(struct sk_buff * skb,int * offset)630*4882a593Smuzhiyun static int mrp_pdu_parse_end_mark(struct sk_buff *skb, int *offset)
631*4882a593Smuzhiyun {
632*4882a593Smuzhiyun 	__be16 endmark;
633*4882a593Smuzhiyun 
634*4882a593Smuzhiyun 	if (skb_copy_bits(skb, *offset, &endmark, sizeof(endmark)) < 0)
635*4882a593Smuzhiyun 		return -1;
636*4882a593Smuzhiyun 	if (endmark == MRP_END_MARK) {
637*4882a593Smuzhiyun 		*offset += sizeof(endmark);
638*4882a593Smuzhiyun 		return -1;
639*4882a593Smuzhiyun 	}
640*4882a593Smuzhiyun 	return 0;
641*4882a593Smuzhiyun }
642*4882a593Smuzhiyun 
mrp_pdu_parse_vecattr_event(struct mrp_applicant * app,struct sk_buff * skb,enum mrp_vecattr_event vaevent)643*4882a593Smuzhiyun static void mrp_pdu_parse_vecattr_event(struct mrp_applicant *app,
644*4882a593Smuzhiyun 					struct sk_buff *skb,
645*4882a593Smuzhiyun 					enum mrp_vecattr_event vaevent)
646*4882a593Smuzhiyun {
647*4882a593Smuzhiyun 	struct mrp_attr *attr;
648*4882a593Smuzhiyun 	enum mrp_event event;
649*4882a593Smuzhiyun 
650*4882a593Smuzhiyun 	attr = mrp_attr_lookup(app, mrp_cb(skb)->attrvalue,
651*4882a593Smuzhiyun 			       mrp_cb(skb)->mh->attrlen,
652*4882a593Smuzhiyun 			       mrp_cb(skb)->mh->attrtype);
653*4882a593Smuzhiyun 	if (attr == NULL)
654*4882a593Smuzhiyun 		return;
655*4882a593Smuzhiyun 
656*4882a593Smuzhiyun 	switch (vaevent) {
657*4882a593Smuzhiyun 	case MRP_VECATTR_EVENT_NEW:
658*4882a593Smuzhiyun 		event = MRP_EVENT_R_NEW;
659*4882a593Smuzhiyun 		break;
660*4882a593Smuzhiyun 	case MRP_VECATTR_EVENT_JOIN_IN:
661*4882a593Smuzhiyun 		event = MRP_EVENT_R_JOIN_IN;
662*4882a593Smuzhiyun 		break;
663*4882a593Smuzhiyun 	case MRP_VECATTR_EVENT_IN:
664*4882a593Smuzhiyun 		event = MRP_EVENT_R_IN;
665*4882a593Smuzhiyun 		break;
666*4882a593Smuzhiyun 	case MRP_VECATTR_EVENT_JOIN_MT:
667*4882a593Smuzhiyun 		event = MRP_EVENT_R_JOIN_MT;
668*4882a593Smuzhiyun 		break;
669*4882a593Smuzhiyun 	case MRP_VECATTR_EVENT_MT:
670*4882a593Smuzhiyun 		event = MRP_EVENT_R_MT;
671*4882a593Smuzhiyun 		break;
672*4882a593Smuzhiyun 	case MRP_VECATTR_EVENT_LV:
673*4882a593Smuzhiyun 		event = MRP_EVENT_R_LV;
674*4882a593Smuzhiyun 		break;
675*4882a593Smuzhiyun 	default:
676*4882a593Smuzhiyun 		return;
677*4882a593Smuzhiyun 	}
678*4882a593Smuzhiyun 
679*4882a593Smuzhiyun 	mrp_attr_event(app, attr, event);
680*4882a593Smuzhiyun }
681*4882a593Smuzhiyun 
mrp_pdu_parse_vecattr(struct mrp_applicant * app,struct sk_buff * skb,int * offset)682*4882a593Smuzhiyun static int mrp_pdu_parse_vecattr(struct mrp_applicant *app,
683*4882a593Smuzhiyun 				 struct sk_buff *skb, int *offset)
684*4882a593Smuzhiyun {
685*4882a593Smuzhiyun 	struct mrp_vecattr_hdr _vah;
686*4882a593Smuzhiyun 	u16 valen;
687*4882a593Smuzhiyun 	u8 vaevents, vaevent;
688*4882a593Smuzhiyun 
689*4882a593Smuzhiyun 	mrp_cb(skb)->vah = skb_header_pointer(skb, *offset, sizeof(_vah),
690*4882a593Smuzhiyun 					      &_vah);
691*4882a593Smuzhiyun 	if (!mrp_cb(skb)->vah)
692*4882a593Smuzhiyun 		return -1;
693*4882a593Smuzhiyun 	*offset += sizeof(_vah);
694*4882a593Smuzhiyun 
695*4882a593Smuzhiyun 	if (get_unaligned(&mrp_cb(skb)->vah->lenflags) &
696*4882a593Smuzhiyun 	    MRP_VECATTR_HDR_FLAG_LA)
697*4882a593Smuzhiyun 		mrp_mad_event(app, MRP_EVENT_R_LA);
698*4882a593Smuzhiyun 	valen = be16_to_cpu(get_unaligned(&mrp_cb(skb)->vah->lenflags) &
699*4882a593Smuzhiyun 			    MRP_VECATTR_HDR_LEN_MASK);
700*4882a593Smuzhiyun 
701*4882a593Smuzhiyun 	/* The VectorAttribute structure in a PDU carries event information
702*4882a593Smuzhiyun 	 * about one or more attributes having consecutive values. Only the
703*4882a593Smuzhiyun 	 * value for the first attribute is contained in the structure. So
704*4882a593Smuzhiyun 	 * we make a copy of that value, and then increment it each time we
705*4882a593Smuzhiyun 	 * advance to the next event in its Vector.
706*4882a593Smuzhiyun 	 */
707*4882a593Smuzhiyun 	if (sizeof(struct mrp_skb_cb) + mrp_cb(skb)->mh->attrlen >
708*4882a593Smuzhiyun 	    sizeof_field(struct sk_buff, cb))
709*4882a593Smuzhiyun 		return -1;
710*4882a593Smuzhiyun 	if (skb_copy_bits(skb, *offset, mrp_cb(skb)->attrvalue,
711*4882a593Smuzhiyun 			  mrp_cb(skb)->mh->attrlen) < 0)
712*4882a593Smuzhiyun 		return -1;
713*4882a593Smuzhiyun 	*offset += mrp_cb(skb)->mh->attrlen;
714*4882a593Smuzhiyun 
715*4882a593Smuzhiyun 	/* In a VectorAttribute, the Vector contains events which are packed
716*4882a593Smuzhiyun 	 * three to a byte. We process one byte of the Vector at a time.
717*4882a593Smuzhiyun 	 */
718*4882a593Smuzhiyun 	while (valen > 0) {
719*4882a593Smuzhiyun 		if (skb_copy_bits(skb, *offset, &vaevents,
720*4882a593Smuzhiyun 				  sizeof(vaevents)) < 0)
721*4882a593Smuzhiyun 			return -1;
722*4882a593Smuzhiyun 		*offset += sizeof(vaevents);
723*4882a593Smuzhiyun 
724*4882a593Smuzhiyun 		/* Extract and process the first event. */
725*4882a593Smuzhiyun 		vaevent = vaevents / (__MRP_VECATTR_EVENT_MAX *
726*4882a593Smuzhiyun 				      __MRP_VECATTR_EVENT_MAX);
727*4882a593Smuzhiyun 		if (vaevent >= __MRP_VECATTR_EVENT_MAX) {
728*4882a593Smuzhiyun 			/* The byte is malformed; stop processing. */
729*4882a593Smuzhiyun 			return -1;
730*4882a593Smuzhiyun 		}
731*4882a593Smuzhiyun 		mrp_pdu_parse_vecattr_event(app, skb, vaevent);
732*4882a593Smuzhiyun 
733*4882a593Smuzhiyun 		/* If present, extract and process the second event. */
734*4882a593Smuzhiyun 		if (!--valen)
735*4882a593Smuzhiyun 			break;
736*4882a593Smuzhiyun 		mrp_attrvalue_inc(mrp_cb(skb)->attrvalue,
737*4882a593Smuzhiyun 				  mrp_cb(skb)->mh->attrlen);
738*4882a593Smuzhiyun 		vaevents %= (__MRP_VECATTR_EVENT_MAX *
739*4882a593Smuzhiyun 			     __MRP_VECATTR_EVENT_MAX);
740*4882a593Smuzhiyun 		vaevent = vaevents / __MRP_VECATTR_EVENT_MAX;
741*4882a593Smuzhiyun 		mrp_pdu_parse_vecattr_event(app, skb, vaevent);
742*4882a593Smuzhiyun 
743*4882a593Smuzhiyun 		/* If present, extract and process the third event. */
744*4882a593Smuzhiyun 		if (!--valen)
745*4882a593Smuzhiyun 			break;
746*4882a593Smuzhiyun 		mrp_attrvalue_inc(mrp_cb(skb)->attrvalue,
747*4882a593Smuzhiyun 				  mrp_cb(skb)->mh->attrlen);
748*4882a593Smuzhiyun 		vaevents %= __MRP_VECATTR_EVENT_MAX;
749*4882a593Smuzhiyun 		vaevent = vaevents;
750*4882a593Smuzhiyun 		mrp_pdu_parse_vecattr_event(app, skb, vaevent);
751*4882a593Smuzhiyun 	}
752*4882a593Smuzhiyun 	return 0;
753*4882a593Smuzhiyun }
754*4882a593Smuzhiyun 
mrp_pdu_parse_msg(struct mrp_applicant * app,struct sk_buff * skb,int * offset)755*4882a593Smuzhiyun static int mrp_pdu_parse_msg(struct mrp_applicant *app, struct sk_buff *skb,
756*4882a593Smuzhiyun 			     int *offset)
757*4882a593Smuzhiyun {
758*4882a593Smuzhiyun 	struct mrp_msg_hdr _mh;
759*4882a593Smuzhiyun 
760*4882a593Smuzhiyun 	mrp_cb(skb)->mh = skb_header_pointer(skb, *offset, sizeof(_mh), &_mh);
761*4882a593Smuzhiyun 	if (!mrp_cb(skb)->mh)
762*4882a593Smuzhiyun 		return -1;
763*4882a593Smuzhiyun 	*offset += sizeof(_mh);
764*4882a593Smuzhiyun 
765*4882a593Smuzhiyun 	if (mrp_cb(skb)->mh->attrtype == 0 ||
766*4882a593Smuzhiyun 	    mrp_cb(skb)->mh->attrtype > app->app->maxattr ||
767*4882a593Smuzhiyun 	    mrp_cb(skb)->mh->attrlen == 0)
768*4882a593Smuzhiyun 		return -1;
769*4882a593Smuzhiyun 
770*4882a593Smuzhiyun 	while (skb->len > *offset) {
771*4882a593Smuzhiyun 		if (mrp_pdu_parse_end_mark(skb, offset) < 0)
772*4882a593Smuzhiyun 			break;
773*4882a593Smuzhiyun 		if (mrp_pdu_parse_vecattr(app, skb, offset) < 0)
774*4882a593Smuzhiyun 			return -1;
775*4882a593Smuzhiyun 	}
776*4882a593Smuzhiyun 	return 0;
777*4882a593Smuzhiyun }
778*4882a593Smuzhiyun 
mrp_rcv(struct sk_buff * skb,struct net_device * dev,struct packet_type * pt,struct net_device * orig_dev)779*4882a593Smuzhiyun static int mrp_rcv(struct sk_buff *skb, struct net_device *dev,
780*4882a593Smuzhiyun 		   struct packet_type *pt, struct net_device *orig_dev)
781*4882a593Smuzhiyun {
782*4882a593Smuzhiyun 	struct mrp_application *appl = container_of(pt, struct mrp_application,
783*4882a593Smuzhiyun 						    pkttype);
784*4882a593Smuzhiyun 	struct mrp_port *port;
785*4882a593Smuzhiyun 	struct mrp_applicant *app;
786*4882a593Smuzhiyun 	struct mrp_pdu_hdr _ph;
787*4882a593Smuzhiyun 	const struct mrp_pdu_hdr *ph;
788*4882a593Smuzhiyun 	int offset = skb_network_offset(skb);
789*4882a593Smuzhiyun 
790*4882a593Smuzhiyun 	/* If the interface is in promiscuous mode, drop the packet if
791*4882a593Smuzhiyun 	 * it was unicast to another host.
792*4882a593Smuzhiyun 	 */
793*4882a593Smuzhiyun 	if (unlikely(skb->pkt_type == PACKET_OTHERHOST))
794*4882a593Smuzhiyun 		goto out;
795*4882a593Smuzhiyun 	skb = skb_share_check(skb, GFP_ATOMIC);
796*4882a593Smuzhiyun 	if (unlikely(!skb))
797*4882a593Smuzhiyun 		goto out;
798*4882a593Smuzhiyun 	port = rcu_dereference(dev->mrp_port);
799*4882a593Smuzhiyun 	if (unlikely(!port))
800*4882a593Smuzhiyun 		goto out;
801*4882a593Smuzhiyun 	app = rcu_dereference(port->applicants[appl->type]);
802*4882a593Smuzhiyun 	if (unlikely(!app))
803*4882a593Smuzhiyun 		goto out;
804*4882a593Smuzhiyun 
805*4882a593Smuzhiyun 	ph = skb_header_pointer(skb, offset, sizeof(_ph), &_ph);
806*4882a593Smuzhiyun 	if (!ph)
807*4882a593Smuzhiyun 		goto out;
808*4882a593Smuzhiyun 	offset += sizeof(_ph);
809*4882a593Smuzhiyun 
810*4882a593Smuzhiyun 	if (ph->version != app->app->version)
811*4882a593Smuzhiyun 		goto out;
812*4882a593Smuzhiyun 
813*4882a593Smuzhiyun 	spin_lock(&app->lock);
814*4882a593Smuzhiyun 	while (skb->len > offset) {
815*4882a593Smuzhiyun 		if (mrp_pdu_parse_end_mark(skb, &offset) < 0)
816*4882a593Smuzhiyun 			break;
817*4882a593Smuzhiyun 		if (mrp_pdu_parse_msg(app, skb, &offset) < 0)
818*4882a593Smuzhiyun 			break;
819*4882a593Smuzhiyun 	}
820*4882a593Smuzhiyun 	spin_unlock(&app->lock);
821*4882a593Smuzhiyun out:
822*4882a593Smuzhiyun 	kfree_skb(skb);
823*4882a593Smuzhiyun 	return 0;
824*4882a593Smuzhiyun }
825*4882a593Smuzhiyun 
mrp_init_port(struct net_device * dev)826*4882a593Smuzhiyun static int mrp_init_port(struct net_device *dev)
827*4882a593Smuzhiyun {
828*4882a593Smuzhiyun 	struct mrp_port *port;
829*4882a593Smuzhiyun 
830*4882a593Smuzhiyun 	port = kzalloc(sizeof(*port), GFP_KERNEL);
831*4882a593Smuzhiyun 	if (!port)
832*4882a593Smuzhiyun 		return -ENOMEM;
833*4882a593Smuzhiyun 	rcu_assign_pointer(dev->mrp_port, port);
834*4882a593Smuzhiyun 	return 0;
835*4882a593Smuzhiyun }
836*4882a593Smuzhiyun 
mrp_release_port(struct net_device * dev)837*4882a593Smuzhiyun static void mrp_release_port(struct net_device *dev)
838*4882a593Smuzhiyun {
839*4882a593Smuzhiyun 	struct mrp_port *port = rtnl_dereference(dev->mrp_port);
840*4882a593Smuzhiyun 	unsigned int i;
841*4882a593Smuzhiyun 
842*4882a593Smuzhiyun 	for (i = 0; i <= MRP_APPLICATION_MAX; i++) {
843*4882a593Smuzhiyun 		if (rtnl_dereference(port->applicants[i]))
844*4882a593Smuzhiyun 			return;
845*4882a593Smuzhiyun 	}
846*4882a593Smuzhiyun 	RCU_INIT_POINTER(dev->mrp_port, NULL);
847*4882a593Smuzhiyun 	kfree_rcu(port, rcu);
848*4882a593Smuzhiyun }
849*4882a593Smuzhiyun 
mrp_init_applicant(struct net_device * dev,struct mrp_application * appl)850*4882a593Smuzhiyun int mrp_init_applicant(struct net_device *dev, struct mrp_application *appl)
851*4882a593Smuzhiyun {
852*4882a593Smuzhiyun 	struct mrp_applicant *app;
853*4882a593Smuzhiyun 	int err;
854*4882a593Smuzhiyun 
855*4882a593Smuzhiyun 	ASSERT_RTNL();
856*4882a593Smuzhiyun 
857*4882a593Smuzhiyun 	if (!rtnl_dereference(dev->mrp_port)) {
858*4882a593Smuzhiyun 		err = mrp_init_port(dev);
859*4882a593Smuzhiyun 		if (err < 0)
860*4882a593Smuzhiyun 			goto err1;
861*4882a593Smuzhiyun 	}
862*4882a593Smuzhiyun 
863*4882a593Smuzhiyun 	err = -ENOMEM;
864*4882a593Smuzhiyun 	app = kzalloc(sizeof(*app), GFP_KERNEL);
865*4882a593Smuzhiyun 	if (!app)
866*4882a593Smuzhiyun 		goto err2;
867*4882a593Smuzhiyun 
868*4882a593Smuzhiyun 	err = dev_mc_add(dev, appl->group_address);
869*4882a593Smuzhiyun 	if (err < 0)
870*4882a593Smuzhiyun 		goto err3;
871*4882a593Smuzhiyun 
872*4882a593Smuzhiyun 	app->dev = dev;
873*4882a593Smuzhiyun 	app->app = appl;
874*4882a593Smuzhiyun 	app->mad = RB_ROOT;
875*4882a593Smuzhiyun 	spin_lock_init(&app->lock);
876*4882a593Smuzhiyun 	skb_queue_head_init(&app->queue);
877*4882a593Smuzhiyun 	rcu_assign_pointer(dev->mrp_port->applicants[appl->type], app);
878*4882a593Smuzhiyun 	timer_setup(&app->join_timer, mrp_join_timer, 0);
879*4882a593Smuzhiyun 	mrp_join_timer_arm(app);
880*4882a593Smuzhiyun 	timer_setup(&app->periodic_timer, mrp_periodic_timer, 0);
881*4882a593Smuzhiyun 	mrp_periodic_timer_arm(app);
882*4882a593Smuzhiyun 	return 0;
883*4882a593Smuzhiyun 
884*4882a593Smuzhiyun err3:
885*4882a593Smuzhiyun 	kfree(app);
886*4882a593Smuzhiyun err2:
887*4882a593Smuzhiyun 	mrp_release_port(dev);
888*4882a593Smuzhiyun err1:
889*4882a593Smuzhiyun 	return err;
890*4882a593Smuzhiyun }
891*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(mrp_init_applicant);
892*4882a593Smuzhiyun 
mrp_uninit_applicant(struct net_device * dev,struct mrp_application * appl)893*4882a593Smuzhiyun void mrp_uninit_applicant(struct net_device *dev, struct mrp_application *appl)
894*4882a593Smuzhiyun {
895*4882a593Smuzhiyun 	struct mrp_port *port = rtnl_dereference(dev->mrp_port);
896*4882a593Smuzhiyun 	struct mrp_applicant *app = rtnl_dereference(
897*4882a593Smuzhiyun 		port->applicants[appl->type]);
898*4882a593Smuzhiyun 
899*4882a593Smuzhiyun 	ASSERT_RTNL();
900*4882a593Smuzhiyun 
901*4882a593Smuzhiyun 	RCU_INIT_POINTER(port->applicants[appl->type], NULL);
902*4882a593Smuzhiyun 
903*4882a593Smuzhiyun 	/* Delete timer and generate a final TX event to flush out
904*4882a593Smuzhiyun 	 * all pending messages before the applicant is gone.
905*4882a593Smuzhiyun 	 */
906*4882a593Smuzhiyun 	del_timer_sync(&app->join_timer);
907*4882a593Smuzhiyun 	del_timer_sync(&app->periodic_timer);
908*4882a593Smuzhiyun 
909*4882a593Smuzhiyun 	spin_lock_bh(&app->lock);
910*4882a593Smuzhiyun 	mrp_mad_event(app, MRP_EVENT_TX);
911*4882a593Smuzhiyun 	mrp_attr_destroy_all(app);
912*4882a593Smuzhiyun 	mrp_pdu_queue(app);
913*4882a593Smuzhiyun 	spin_unlock_bh(&app->lock);
914*4882a593Smuzhiyun 
915*4882a593Smuzhiyun 	mrp_queue_xmit(app);
916*4882a593Smuzhiyun 
917*4882a593Smuzhiyun 	dev_mc_del(dev, appl->group_address);
918*4882a593Smuzhiyun 	kfree_rcu(app, rcu);
919*4882a593Smuzhiyun 	mrp_release_port(dev);
920*4882a593Smuzhiyun }
921*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(mrp_uninit_applicant);
922*4882a593Smuzhiyun 
mrp_register_application(struct mrp_application * appl)923*4882a593Smuzhiyun int mrp_register_application(struct mrp_application *appl)
924*4882a593Smuzhiyun {
925*4882a593Smuzhiyun 	appl->pkttype.func = mrp_rcv;
926*4882a593Smuzhiyun 	dev_add_pack(&appl->pkttype);
927*4882a593Smuzhiyun 	return 0;
928*4882a593Smuzhiyun }
929*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(mrp_register_application);
930*4882a593Smuzhiyun 
mrp_unregister_application(struct mrp_application * appl)931*4882a593Smuzhiyun void mrp_unregister_application(struct mrp_application *appl)
932*4882a593Smuzhiyun {
933*4882a593Smuzhiyun 	dev_remove_pack(&appl->pkttype);
934*4882a593Smuzhiyun }
935*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(mrp_unregister_application);
936