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