xref: /OK3568_Linux_fs/kernel/net/can/gw.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
2*4882a593Smuzhiyun /* gw.c - CAN frame Gateway/Router/Bridge with netlink interface
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  * Copyright (c) 2019 Volkswagen Group Electronic Research
5*4882a593Smuzhiyun  * All rights reserved.
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  * Redistribution and use in source and binary forms, with or without
8*4882a593Smuzhiyun  * modification, are permitted provided that the following conditions
9*4882a593Smuzhiyun  * are met:
10*4882a593Smuzhiyun  * 1. Redistributions of source code must retain the above copyright
11*4882a593Smuzhiyun  *    notice, this list of conditions and the following disclaimer.
12*4882a593Smuzhiyun  * 2. Redistributions in binary form must reproduce the above copyright
13*4882a593Smuzhiyun  *    notice, this list of conditions and the following disclaimer in the
14*4882a593Smuzhiyun  *    documentation and/or other materials provided with the distribution.
15*4882a593Smuzhiyun  * 3. Neither the name of Volkswagen nor the names of its contributors
16*4882a593Smuzhiyun  *    may be used to endorse or promote products derived from this software
17*4882a593Smuzhiyun  *    without specific prior written permission.
18*4882a593Smuzhiyun  *
19*4882a593Smuzhiyun  * Alternatively, provided that this notice is retained in full, this
20*4882a593Smuzhiyun  * software may be distributed under the terms of the GNU General
21*4882a593Smuzhiyun  * Public License ("GPL") version 2, in which case the provisions of the
22*4882a593Smuzhiyun  * GPL apply INSTEAD OF those given above.
23*4882a593Smuzhiyun  *
24*4882a593Smuzhiyun  * The provided data structures and external interfaces from this code
25*4882a593Smuzhiyun  * are not restricted to be used by modules with a GPL compatible license.
26*4882a593Smuzhiyun  *
27*4882a593Smuzhiyun  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28*4882a593Smuzhiyun  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29*4882a593Smuzhiyun  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30*4882a593Smuzhiyun  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31*4882a593Smuzhiyun  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32*4882a593Smuzhiyun  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33*4882a593Smuzhiyun  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34*4882a593Smuzhiyun  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35*4882a593Smuzhiyun  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36*4882a593Smuzhiyun  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37*4882a593Smuzhiyun  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
38*4882a593Smuzhiyun  * DAMAGE.
39*4882a593Smuzhiyun  *
40*4882a593Smuzhiyun  */
41*4882a593Smuzhiyun 
42*4882a593Smuzhiyun #include <linux/module.h>
43*4882a593Smuzhiyun #include <linux/init.h>
44*4882a593Smuzhiyun #include <linux/types.h>
45*4882a593Smuzhiyun #include <linux/kernel.h>
46*4882a593Smuzhiyun #include <linux/list.h>
47*4882a593Smuzhiyun #include <linux/spinlock.h>
48*4882a593Smuzhiyun #include <linux/rcupdate.h>
49*4882a593Smuzhiyun #include <linux/rculist.h>
50*4882a593Smuzhiyun #include <linux/net.h>
51*4882a593Smuzhiyun #include <linux/netdevice.h>
52*4882a593Smuzhiyun #include <linux/if_arp.h>
53*4882a593Smuzhiyun #include <linux/skbuff.h>
54*4882a593Smuzhiyun #include <linux/can.h>
55*4882a593Smuzhiyun #include <linux/can/core.h>
56*4882a593Smuzhiyun #include <linux/can/skb.h>
57*4882a593Smuzhiyun #include <linux/can/gw.h>
58*4882a593Smuzhiyun #include <net/rtnetlink.h>
59*4882a593Smuzhiyun #include <net/net_namespace.h>
60*4882a593Smuzhiyun #include <net/sock.h>
61*4882a593Smuzhiyun 
62*4882a593Smuzhiyun #define CAN_GW_NAME "can-gw"
63*4882a593Smuzhiyun 
64*4882a593Smuzhiyun MODULE_DESCRIPTION("PF_CAN netlink gateway");
65*4882a593Smuzhiyun MODULE_LICENSE("Dual BSD/GPL");
66*4882a593Smuzhiyun MODULE_AUTHOR("Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
67*4882a593Smuzhiyun MODULE_ALIAS(CAN_GW_NAME);
68*4882a593Smuzhiyun 
69*4882a593Smuzhiyun #define CGW_MIN_HOPS 1
70*4882a593Smuzhiyun #define CGW_MAX_HOPS 6
71*4882a593Smuzhiyun #define CGW_DEFAULT_HOPS 1
72*4882a593Smuzhiyun 
73*4882a593Smuzhiyun static unsigned int max_hops __read_mostly = CGW_DEFAULT_HOPS;
74*4882a593Smuzhiyun module_param(max_hops, uint, 0444);
75*4882a593Smuzhiyun MODULE_PARM_DESC(max_hops,
76*4882a593Smuzhiyun 		 "maximum " CAN_GW_NAME " routing hops for CAN frames "
77*4882a593Smuzhiyun 		 "(valid values: " __stringify(CGW_MIN_HOPS) "-"
78*4882a593Smuzhiyun 		 __stringify(CGW_MAX_HOPS) " hops, "
79*4882a593Smuzhiyun 		 "default: " __stringify(CGW_DEFAULT_HOPS) ")");
80*4882a593Smuzhiyun 
81*4882a593Smuzhiyun static struct notifier_block notifier;
82*4882a593Smuzhiyun static struct kmem_cache *cgw_cache __read_mostly;
83*4882a593Smuzhiyun 
84*4882a593Smuzhiyun /* structure that contains the (on-the-fly) CAN frame modifications */
85*4882a593Smuzhiyun struct cf_mod {
86*4882a593Smuzhiyun 	struct {
87*4882a593Smuzhiyun 		struct canfd_frame and;
88*4882a593Smuzhiyun 		struct canfd_frame or;
89*4882a593Smuzhiyun 		struct canfd_frame xor;
90*4882a593Smuzhiyun 		struct canfd_frame set;
91*4882a593Smuzhiyun 	} modframe;
92*4882a593Smuzhiyun 	struct {
93*4882a593Smuzhiyun 		u8 and;
94*4882a593Smuzhiyun 		u8 or;
95*4882a593Smuzhiyun 		u8 xor;
96*4882a593Smuzhiyun 		u8 set;
97*4882a593Smuzhiyun 	} modtype;
98*4882a593Smuzhiyun 	void (*modfunc[MAX_MODFUNCTIONS])(struct canfd_frame *cf,
99*4882a593Smuzhiyun 					  struct cf_mod *mod);
100*4882a593Smuzhiyun 
101*4882a593Smuzhiyun 	/* CAN frame checksum calculation after CAN frame modifications */
102*4882a593Smuzhiyun 	struct {
103*4882a593Smuzhiyun 		struct cgw_csum_xor xor;
104*4882a593Smuzhiyun 		struct cgw_csum_crc8 crc8;
105*4882a593Smuzhiyun 	} csum;
106*4882a593Smuzhiyun 	struct {
107*4882a593Smuzhiyun 		void (*xor)(struct canfd_frame *cf,
108*4882a593Smuzhiyun 			    struct cgw_csum_xor *xor);
109*4882a593Smuzhiyun 		void (*crc8)(struct canfd_frame *cf,
110*4882a593Smuzhiyun 			     struct cgw_csum_crc8 *crc8);
111*4882a593Smuzhiyun 	} csumfunc;
112*4882a593Smuzhiyun 	u32 uid;
113*4882a593Smuzhiyun };
114*4882a593Smuzhiyun 
115*4882a593Smuzhiyun /* So far we just support CAN -> CAN routing and frame modifications.
116*4882a593Smuzhiyun  *
117*4882a593Smuzhiyun  * The internal can_can_gw structure contains data and attributes for
118*4882a593Smuzhiyun  * a CAN -> CAN gateway job.
119*4882a593Smuzhiyun  */
120*4882a593Smuzhiyun struct can_can_gw {
121*4882a593Smuzhiyun 	struct can_filter filter;
122*4882a593Smuzhiyun 	int src_idx;
123*4882a593Smuzhiyun 	int dst_idx;
124*4882a593Smuzhiyun };
125*4882a593Smuzhiyun 
126*4882a593Smuzhiyun /* list entry for CAN gateways jobs */
127*4882a593Smuzhiyun struct cgw_job {
128*4882a593Smuzhiyun 	struct hlist_node list;
129*4882a593Smuzhiyun 	struct rcu_head rcu;
130*4882a593Smuzhiyun 	u32 handled_frames;
131*4882a593Smuzhiyun 	u32 dropped_frames;
132*4882a593Smuzhiyun 	u32 deleted_frames;
133*4882a593Smuzhiyun 	struct cf_mod mod;
134*4882a593Smuzhiyun 	union {
135*4882a593Smuzhiyun 		/* CAN frame data source */
136*4882a593Smuzhiyun 		struct net_device *dev;
137*4882a593Smuzhiyun 	} src;
138*4882a593Smuzhiyun 	union {
139*4882a593Smuzhiyun 		/* CAN frame data destination */
140*4882a593Smuzhiyun 		struct net_device *dev;
141*4882a593Smuzhiyun 	} dst;
142*4882a593Smuzhiyun 	union {
143*4882a593Smuzhiyun 		struct can_can_gw ccgw;
144*4882a593Smuzhiyun 		/* tbc */
145*4882a593Smuzhiyun 	};
146*4882a593Smuzhiyun 	u8 gwtype;
147*4882a593Smuzhiyun 	u8 limit_hops;
148*4882a593Smuzhiyun 	u16 flags;
149*4882a593Smuzhiyun };
150*4882a593Smuzhiyun 
151*4882a593Smuzhiyun /* modification functions that are invoked in the hot path in can_can_gw_rcv */
152*4882a593Smuzhiyun 
153*4882a593Smuzhiyun #define MODFUNC(func, op) static void func(struct canfd_frame *cf, \
154*4882a593Smuzhiyun 					   struct cf_mod *mod) { op ; }
155*4882a593Smuzhiyun 
156*4882a593Smuzhiyun MODFUNC(mod_and_id, cf->can_id &= mod->modframe.and.can_id)
157*4882a593Smuzhiyun MODFUNC(mod_and_len, cf->len &= mod->modframe.and.len)
158*4882a593Smuzhiyun MODFUNC(mod_and_flags, cf->flags &= mod->modframe.and.flags)
159*4882a593Smuzhiyun MODFUNC(mod_and_data, *(u64 *)cf->data &= *(u64 *)mod->modframe.and.data)
160*4882a593Smuzhiyun MODFUNC(mod_or_id, cf->can_id |= mod->modframe.or.can_id)
161*4882a593Smuzhiyun MODFUNC(mod_or_len, cf->len |= mod->modframe.or.len)
162*4882a593Smuzhiyun MODFUNC(mod_or_flags, cf->flags |= mod->modframe.or.flags)
163*4882a593Smuzhiyun MODFUNC(mod_or_data, *(u64 *)cf->data |= *(u64 *)mod->modframe.or.data)
164*4882a593Smuzhiyun MODFUNC(mod_xor_id, cf->can_id ^= mod->modframe.xor.can_id)
165*4882a593Smuzhiyun MODFUNC(mod_xor_len, cf->len ^= mod->modframe.xor.len)
166*4882a593Smuzhiyun MODFUNC(mod_xor_flags, cf->flags ^= mod->modframe.xor.flags)
167*4882a593Smuzhiyun MODFUNC(mod_xor_data, *(u64 *)cf->data ^= *(u64 *)mod->modframe.xor.data)
168*4882a593Smuzhiyun MODFUNC(mod_set_id, cf->can_id = mod->modframe.set.can_id)
169*4882a593Smuzhiyun MODFUNC(mod_set_len, cf->len = mod->modframe.set.len)
170*4882a593Smuzhiyun MODFUNC(mod_set_flags, cf->flags = mod->modframe.set.flags)
171*4882a593Smuzhiyun MODFUNC(mod_set_data, *(u64 *)cf->data = *(u64 *)mod->modframe.set.data)
172*4882a593Smuzhiyun 
mod_and_fddata(struct canfd_frame * cf,struct cf_mod * mod)173*4882a593Smuzhiyun static void mod_and_fddata(struct canfd_frame *cf, struct cf_mod *mod)
174*4882a593Smuzhiyun {
175*4882a593Smuzhiyun 	int i;
176*4882a593Smuzhiyun 
177*4882a593Smuzhiyun 	for (i = 0; i < CANFD_MAX_DLEN; i += 8)
178*4882a593Smuzhiyun 		*(u64 *)(cf->data + i) &= *(u64 *)(mod->modframe.and.data + i);
179*4882a593Smuzhiyun }
180*4882a593Smuzhiyun 
mod_or_fddata(struct canfd_frame * cf,struct cf_mod * mod)181*4882a593Smuzhiyun static void mod_or_fddata(struct canfd_frame *cf, struct cf_mod *mod)
182*4882a593Smuzhiyun {
183*4882a593Smuzhiyun 	int i;
184*4882a593Smuzhiyun 
185*4882a593Smuzhiyun 	for (i = 0; i < CANFD_MAX_DLEN; i += 8)
186*4882a593Smuzhiyun 		*(u64 *)(cf->data + i) |= *(u64 *)(mod->modframe.or.data + i);
187*4882a593Smuzhiyun }
188*4882a593Smuzhiyun 
mod_xor_fddata(struct canfd_frame * cf,struct cf_mod * mod)189*4882a593Smuzhiyun static void mod_xor_fddata(struct canfd_frame *cf, struct cf_mod *mod)
190*4882a593Smuzhiyun {
191*4882a593Smuzhiyun 	int i;
192*4882a593Smuzhiyun 
193*4882a593Smuzhiyun 	for (i = 0; i < CANFD_MAX_DLEN; i += 8)
194*4882a593Smuzhiyun 		*(u64 *)(cf->data + i) ^= *(u64 *)(mod->modframe.xor.data + i);
195*4882a593Smuzhiyun }
196*4882a593Smuzhiyun 
mod_set_fddata(struct canfd_frame * cf,struct cf_mod * mod)197*4882a593Smuzhiyun static void mod_set_fddata(struct canfd_frame *cf, struct cf_mod *mod)
198*4882a593Smuzhiyun {
199*4882a593Smuzhiyun 	memcpy(cf->data, mod->modframe.set.data, CANFD_MAX_DLEN);
200*4882a593Smuzhiyun }
201*4882a593Smuzhiyun 
canframecpy(struct canfd_frame * dst,struct can_frame * src)202*4882a593Smuzhiyun static void canframecpy(struct canfd_frame *dst, struct can_frame *src)
203*4882a593Smuzhiyun {
204*4882a593Smuzhiyun 	/* Copy the struct members separately to ensure that no uninitialized
205*4882a593Smuzhiyun 	 * data are copied in the 3 bytes hole of the struct. This is needed
206*4882a593Smuzhiyun 	 * to make easy compares of the data in the struct cf_mod.
207*4882a593Smuzhiyun 	 */
208*4882a593Smuzhiyun 
209*4882a593Smuzhiyun 	dst->can_id = src->can_id;
210*4882a593Smuzhiyun 	dst->len = src->can_dlc;
211*4882a593Smuzhiyun 	*(u64 *)dst->data = *(u64 *)src->data;
212*4882a593Smuzhiyun }
213*4882a593Smuzhiyun 
canfdframecpy(struct canfd_frame * dst,struct canfd_frame * src)214*4882a593Smuzhiyun static void canfdframecpy(struct canfd_frame *dst, struct canfd_frame *src)
215*4882a593Smuzhiyun {
216*4882a593Smuzhiyun 	/* Copy the struct members separately to ensure that no uninitialized
217*4882a593Smuzhiyun 	 * data are copied in the 2 bytes hole of the struct. This is needed
218*4882a593Smuzhiyun 	 * to make easy compares of the data in the struct cf_mod.
219*4882a593Smuzhiyun 	 */
220*4882a593Smuzhiyun 
221*4882a593Smuzhiyun 	dst->can_id = src->can_id;
222*4882a593Smuzhiyun 	dst->flags = src->flags;
223*4882a593Smuzhiyun 	dst->len = src->len;
224*4882a593Smuzhiyun 	memcpy(dst->data, src->data, CANFD_MAX_DLEN);
225*4882a593Smuzhiyun }
226*4882a593Smuzhiyun 
cgw_chk_csum_parms(s8 fr,s8 to,s8 re,struct rtcanmsg * r)227*4882a593Smuzhiyun static int cgw_chk_csum_parms(s8 fr, s8 to, s8 re, struct rtcanmsg *r)
228*4882a593Smuzhiyun {
229*4882a593Smuzhiyun 	s8 dlen = CAN_MAX_DLEN;
230*4882a593Smuzhiyun 
231*4882a593Smuzhiyun 	if (r->flags & CGW_FLAGS_CAN_FD)
232*4882a593Smuzhiyun 		dlen = CANFD_MAX_DLEN;
233*4882a593Smuzhiyun 
234*4882a593Smuzhiyun 	/* absolute dlc values 0 .. 7 => 0 .. 7, e.g. data [0]
235*4882a593Smuzhiyun 	 * relative to received dlc -1 .. -8 :
236*4882a593Smuzhiyun 	 * e.g. for received dlc = 8
237*4882a593Smuzhiyun 	 * -1 => index = 7 (data[7])
238*4882a593Smuzhiyun 	 * -3 => index = 5 (data[5])
239*4882a593Smuzhiyun 	 * -8 => index = 0 (data[0])
240*4882a593Smuzhiyun 	 */
241*4882a593Smuzhiyun 
242*4882a593Smuzhiyun 	if (fr >= -dlen && fr < dlen &&
243*4882a593Smuzhiyun 	    to >= -dlen && to < dlen &&
244*4882a593Smuzhiyun 	    re >= -dlen && re < dlen)
245*4882a593Smuzhiyun 		return 0;
246*4882a593Smuzhiyun 	else
247*4882a593Smuzhiyun 		return -EINVAL;
248*4882a593Smuzhiyun }
249*4882a593Smuzhiyun 
calc_idx(int idx,int rx_len)250*4882a593Smuzhiyun static inline int calc_idx(int idx, int rx_len)
251*4882a593Smuzhiyun {
252*4882a593Smuzhiyun 	if (idx < 0)
253*4882a593Smuzhiyun 		return rx_len + idx;
254*4882a593Smuzhiyun 	else
255*4882a593Smuzhiyun 		return idx;
256*4882a593Smuzhiyun }
257*4882a593Smuzhiyun 
cgw_csum_xor_rel(struct canfd_frame * cf,struct cgw_csum_xor * xor)258*4882a593Smuzhiyun static void cgw_csum_xor_rel(struct canfd_frame *cf, struct cgw_csum_xor *xor)
259*4882a593Smuzhiyun {
260*4882a593Smuzhiyun 	int from = calc_idx(xor->from_idx, cf->len);
261*4882a593Smuzhiyun 	int to = calc_idx(xor->to_idx, cf->len);
262*4882a593Smuzhiyun 	int res = calc_idx(xor->result_idx, cf->len);
263*4882a593Smuzhiyun 	u8 val = xor->init_xor_val;
264*4882a593Smuzhiyun 	int i;
265*4882a593Smuzhiyun 
266*4882a593Smuzhiyun 	if (from < 0 || to < 0 || res < 0)
267*4882a593Smuzhiyun 		return;
268*4882a593Smuzhiyun 
269*4882a593Smuzhiyun 	if (from <= to) {
270*4882a593Smuzhiyun 		for (i = from; i <= to; i++)
271*4882a593Smuzhiyun 			val ^= cf->data[i];
272*4882a593Smuzhiyun 	} else {
273*4882a593Smuzhiyun 		for (i = from; i >= to; i--)
274*4882a593Smuzhiyun 			val ^= cf->data[i];
275*4882a593Smuzhiyun 	}
276*4882a593Smuzhiyun 
277*4882a593Smuzhiyun 	cf->data[res] = val;
278*4882a593Smuzhiyun }
279*4882a593Smuzhiyun 
cgw_csum_xor_pos(struct canfd_frame * cf,struct cgw_csum_xor * xor)280*4882a593Smuzhiyun static void cgw_csum_xor_pos(struct canfd_frame *cf, struct cgw_csum_xor *xor)
281*4882a593Smuzhiyun {
282*4882a593Smuzhiyun 	u8 val = xor->init_xor_val;
283*4882a593Smuzhiyun 	int i;
284*4882a593Smuzhiyun 
285*4882a593Smuzhiyun 	for (i = xor->from_idx; i <= xor->to_idx; i++)
286*4882a593Smuzhiyun 		val ^= cf->data[i];
287*4882a593Smuzhiyun 
288*4882a593Smuzhiyun 	cf->data[xor->result_idx] = val;
289*4882a593Smuzhiyun }
290*4882a593Smuzhiyun 
cgw_csum_xor_neg(struct canfd_frame * cf,struct cgw_csum_xor * xor)291*4882a593Smuzhiyun static void cgw_csum_xor_neg(struct canfd_frame *cf, struct cgw_csum_xor *xor)
292*4882a593Smuzhiyun {
293*4882a593Smuzhiyun 	u8 val = xor->init_xor_val;
294*4882a593Smuzhiyun 	int i;
295*4882a593Smuzhiyun 
296*4882a593Smuzhiyun 	for (i = xor->from_idx; i >= xor->to_idx; i--)
297*4882a593Smuzhiyun 		val ^= cf->data[i];
298*4882a593Smuzhiyun 
299*4882a593Smuzhiyun 	cf->data[xor->result_idx] = val;
300*4882a593Smuzhiyun }
301*4882a593Smuzhiyun 
cgw_csum_crc8_rel(struct canfd_frame * cf,struct cgw_csum_crc8 * crc8)302*4882a593Smuzhiyun static void cgw_csum_crc8_rel(struct canfd_frame *cf,
303*4882a593Smuzhiyun 			      struct cgw_csum_crc8 *crc8)
304*4882a593Smuzhiyun {
305*4882a593Smuzhiyun 	int from = calc_idx(crc8->from_idx, cf->len);
306*4882a593Smuzhiyun 	int to = calc_idx(crc8->to_idx, cf->len);
307*4882a593Smuzhiyun 	int res = calc_idx(crc8->result_idx, cf->len);
308*4882a593Smuzhiyun 	u8 crc = crc8->init_crc_val;
309*4882a593Smuzhiyun 	int i;
310*4882a593Smuzhiyun 
311*4882a593Smuzhiyun 	if (from < 0 || to < 0 || res < 0)
312*4882a593Smuzhiyun 		return;
313*4882a593Smuzhiyun 
314*4882a593Smuzhiyun 	if (from <= to) {
315*4882a593Smuzhiyun 		for (i = crc8->from_idx; i <= crc8->to_idx; i++)
316*4882a593Smuzhiyun 			crc = crc8->crctab[crc ^ cf->data[i]];
317*4882a593Smuzhiyun 	} else {
318*4882a593Smuzhiyun 		for (i = crc8->from_idx; i >= crc8->to_idx; i--)
319*4882a593Smuzhiyun 			crc = crc8->crctab[crc ^ cf->data[i]];
320*4882a593Smuzhiyun 	}
321*4882a593Smuzhiyun 
322*4882a593Smuzhiyun 	switch (crc8->profile) {
323*4882a593Smuzhiyun 	case CGW_CRC8PRF_1U8:
324*4882a593Smuzhiyun 		crc = crc8->crctab[crc ^ crc8->profile_data[0]];
325*4882a593Smuzhiyun 		break;
326*4882a593Smuzhiyun 
327*4882a593Smuzhiyun 	case  CGW_CRC8PRF_16U8:
328*4882a593Smuzhiyun 		crc = crc8->crctab[crc ^ crc8->profile_data[cf->data[1] & 0xF]];
329*4882a593Smuzhiyun 		break;
330*4882a593Smuzhiyun 
331*4882a593Smuzhiyun 	case CGW_CRC8PRF_SFFID_XOR:
332*4882a593Smuzhiyun 		crc = crc8->crctab[crc ^ (cf->can_id & 0xFF) ^
333*4882a593Smuzhiyun 				   (cf->can_id >> 8 & 0xFF)];
334*4882a593Smuzhiyun 		break;
335*4882a593Smuzhiyun 	}
336*4882a593Smuzhiyun 
337*4882a593Smuzhiyun 	cf->data[crc8->result_idx] = crc ^ crc8->final_xor_val;
338*4882a593Smuzhiyun }
339*4882a593Smuzhiyun 
cgw_csum_crc8_pos(struct canfd_frame * cf,struct cgw_csum_crc8 * crc8)340*4882a593Smuzhiyun static void cgw_csum_crc8_pos(struct canfd_frame *cf,
341*4882a593Smuzhiyun 			      struct cgw_csum_crc8 *crc8)
342*4882a593Smuzhiyun {
343*4882a593Smuzhiyun 	u8 crc = crc8->init_crc_val;
344*4882a593Smuzhiyun 	int i;
345*4882a593Smuzhiyun 
346*4882a593Smuzhiyun 	for (i = crc8->from_idx; i <= crc8->to_idx; i++)
347*4882a593Smuzhiyun 		crc = crc8->crctab[crc ^ cf->data[i]];
348*4882a593Smuzhiyun 
349*4882a593Smuzhiyun 	switch (crc8->profile) {
350*4882a593Smuzhiyun 	case CGW_CRC8PRF_1U8:
351*4882a593Smuzhiyun 		crc = crc8->crctab[crc ^ crc8->profile_data[0]];
352*4882a593Smuzhiyun 		break;
353*4882a593Smuzhiyun 
354*4882a593Smuzhiyun 	case  CGW_CRC8PRF_16U8:
355*4882a593Smuzhiyun 		crc = crc8->crctab[crc ^ crc8->profile_data[cf->data[1] & 0xF]];
356*4882a593Smuzhiyun 		break;
357*4882a593Smuzhiyun 
358*4882a593Smuzhiyun 	case CGW_CRC8PRF_SFFID_XOR:
359*4882a593Smuzhiyun 		crc = crc8->crctab[crc ^ (cf->can_id & 0xFF) ^
360*4882a593Smuzhiyun 				   (cf->can_id >> 8 & 0xFF)];
361*4882a593Smuzhiyun 		break;
362*4882a593Smuzhiyun 	}
363*4882a593Smuzhiyun 
364*4882a593Smuzhiyun 	cf->data[crc8->result_idx] = crc ^ crc8->final_xor_val;
365*4882a593Smuzhiyun }
366*4882a593Smuzhiyun 
cgw_csum_crc8_neg(struct canfd_frame * cf,struct cgw_csum_crc8 * crc8)367*4882a593Smuzhiyun static void cgw_csum_crc8_neg(struct canfd_frame *cf,
368*4882a593Smuzhiyun 			      struct cgw_csum_crc8 *crc8)
369*4882a593Smuzhiyun {
370*4882a593Smuzhiyun 	u8 crc = crc8->init_crc_val;
371*4882a593Smuzhiyun 	int i;
372*4882a593Smuzhiyun 
373*4882a593Smuzhiyun 	for (i = crc8->from_idx; i >= crc8->to_idx; i--)
374*4882a593Smuzhiyun 		crc = crc8->crctab[crc ^ cf->data[i]];
375*4882a593Smuzhiyun 
376*4882a593Smuzhiyun 	switch (crc8->profile) {
377*4882a593Smuzhiyun 	case CGW_CRC8PRF_1U8:
378*4882a593Smuzhiyun 		crc = crc8->crctab[crc ^ crc8->profile_data[0]];
379*4882a593Smuzhiyun 		break;
380*4882a593Smuzhiyun 
381*4882a593Smuzhiyun 	case  CGW_CRC8PRF_16U8:
382*4882a593Smuzhiyun 		crc = crc8->crctab[crc ^ crc8->profile_data[cf->data[1] & 0xF]];
383*4882a593Smuzhiyun 		break;
384*4882a593Smuzhiyun 
385*4882a593Smuzhiyun 	case CGW_CRC8PRF_SFFID_XOR:
386*4882a593Smuzhiyun 		crc = crc8->crctab[crc ^ (cf->can_id & 0xFF) ^
387*4882a593Smuzhiyun 				   (cf->can_id >> 8 & 0xFF)];
388*4882a593Smuzhiyun 		break;
389*4882a593Smuzhiyun 	}
390*4882a593Smuzhiyun 
391*4882a593Smuzhiyun 	cf->data[crc8->result_idx] = crc ^ crc8->final_xor_val;
392*4882a593Smuzhiyun }
393*4882a593Smuzhiyun 
394*4882a593Smuzhiyun /* the receive & process & send function */
can_can_gw_rcv(struct sk_buff * skb,void * data)395*4882a593Smuzhiyun static void can_can_gw_rcv(struct sk_buff *skb, void *data)
396*4882a593Smuzhiyun {
397*4882a593Smuzhiyun 	struct cgw_job *gwj = (struct cgw_job *)data;
398*4882a593Smuzhiyun 	struct canfd_frame *cf;
399*4882a593Smuzhiyun 	struct sk_buff *nskb;
400*4882a593Smuzhiyun 	int modidx = 0;
401*4882a593Smuzhiyun 
402*4882a593Smuzhiyun 	/* process strictly Classic CAN or CAN FD frames */
403*4882a593Smuzhiyun 	if (gwj->flags & CGW_FLAGS_CAN_FD) {
404*4882a593Smuzhiyun 		if (skb->len != CANFD_MTU)
405*4882a593Smuzhiyun 			return;
406*4882a593Smuzhiyun 	} else {
407*4882a593Smuzhiyun 		if (skb->len != CAN_MTU)
408*4882a593Smuzhiyun 			return;
409*4882a593Smuzhiyun 	}
410*4882a593Smuzhiyun 
411*4882a593Smuzhiyun 	/* Do not handle CAN frames routed more than 'max_hops' times.
412*4882a593Smuzhiyun 	 * In general we should never catch this delimiter which is intended
413*4882a593Smuzhiyun 	 * to cover a misconfiguration protection (e.g. circular CAN routes).
414*4882a593Smuzhiyun 	 *
415*4882a593Smuzhiyun 	 * The Controller Area Network controllers only accept CAN frames with
416*4882a593Smuzhiyun 	 * correct CRCs - which are not visible in the controller registers.
417*4882a593Smuzhiyun 	 * According to skbuff.h documentation the csum_start element for IP
418*4882a593Smuzhiyun 	 * checksums is undefined/unused when ip_summed == CHECKSUM_UNNECESSARY.
419*4882a593Smuzhiyun 	 * Only CAN skbs can be processed here which already have this property.
420*4882a593Smuzhiyun 	 */
421*4882a593Smuzhiyun 
422*4882a593Smuzhiyun #define cgw_hops(skb) ((skb)->csum_start)
423*4882a593Smuzhiyun 
424*4882a593Smuzhiyun 	BUG_ON(skb->ip_summed != CHECKSUM_UNNECESSARY);
425*4882a593Smuzhiyun 
426*4882a593Smuzhiyun 	if (cgw_hops(skb) >= max_hops) {
427*4882a593Smuzhiyun 		/* indicate deleted frames due to misconfiguration */
428*4882a593Smuzhiyun 		gwj->deleted_frames++;
429*4882a593Smuzhiyun 		return;
430*4882a593Smuzhiyun 	}
431*4882a593Smuzhiyun 
432*4882a593Smuzhiyun 	if (!(gwj->dst.dev->flags & IFF_UP)) {
433*4882a593Smuzhiyun 		gwj->dropped_frames++;
434*4882a593Smuzhiyun 		return;
435*4882a593Smuzhiyun 	}
436*4882a593Smuzhiyun 
437*4882a593Smuzhiyun 	/* is sending the skb back to the incoming interface not allowed? */
438*4882a593Smuzhiyun 	if (!(gwj->flags & CGW_FLAGS_CAN_IIF_TX_OK) &&
439*4882a593Smuzhiyun 	    can_skb_prv(skb)->ifindex == gwj->dst.dev->ifindex)
440*4882a593Smuzhiyun 		return;
441*4882a593Smuzhiyun 
442*4882a593Smuzhiyun 	/* clone the given skb, which has not been done in can_rcv()
443*4882a593Smuzhiyun 	 *
444*4882a593Smuzhiyun 	 * When there is at least one modification function activated,
445*4882a593Smuzhiyun 	 * we need to copy the skb as we want to modify skb->data.
446*4882a593Smuzhiyun 	 */
447*4882a593Smuzhiyun 	if (gwj->mod.modfunc[0])
448*4882a593Smuzhiyun 		nskb = skb_copy(skb, GFP_ATOMIC);
449*4882a593Smuzhiyun 	else
450*4882a593Smuzhiyun 		nskb = skb_clone(skb, GFP_ATOMIC);
451*4882a593Smuzhiyun 
452*4882a593Smuzhiyun 	if (!nskb) {
453*4882a593Smuzhiyun 		gwj->dropped_frames++;
454*4882a593Smuzhiyun 		return;
455*4882a593Smuzhiyun 	}
456*4882a593Smuzhiyun 
457*4882a593Smuzhiyun 	/* put the incremented hop counter in the cloned skb */
458*4882a593Smuzhiyun 	cgw_hops(nskb) = cgw_hops(skb) + 1;
459*4882a593Smuzhiyun 
460*4882a593Smuzhiyun 	/* first processing of this CAN frame -> adjust to private hop limit */
461*4882a593Smuzhiyun 	if (gwj->limit_hops && cgw_hops(nskb) == 1)
462*4882a593Smuzhiyun 		cgw_hops(nskb) = max_hops - gwj->limit_hops + 1;
463*4882a593Smuzhiyun 
464*4882a593Smuzhiyun 	nskb->dev = gwj->dst.dev;
465*4882a593Smuzhiyun 
466*4882a593Smuzhiyun 	/* pointer to modifiable CAN frame */
467*4882a593Smuzhiyun 	cf = (struct canfd_frame *)nskb->data;
468*4882a593Smuzhiyun 
469*4882a593Smuzhiyun 	/* perform preprocessed modification functions if there are any */
470*4882a593Smuzhiyun 	while (modidx < MAX_MODFUNCTIONS && gwj->mod.modfunc[modidx])
471*4882a593Smuzhiyun 		(*gwj->mod.modfunc[modidx++])(cf, &gwj->mod);
472*4882a593Smuzhiyun 
473*4882a593Smuzhiyun 	/* Has the CAN frame been modified? */
474*4882a593Smuzhiyun 	if (modidx) {
475*4882a593Smuzhiyun 		/* get available space for the processed CAN frame type */
476*4882a593Smuzhiyun 		int max_len = nskb->len - offsetof(struct canfd_frame, data);
477*4882a593Smuzhiyun 
478*4882a593Smuzhiyun 		/* dlc may have changed, make sure it fits to the CAN frame */
479*4882a593Smuzhiyun 		if (cf->len > max_len) {
480*4882a593Smuzhiyun 			/* delete frame due to misconfiguration */
481*4882a593Smuzhiyun 			gwj->deleted_frames++;
482*4882a593Smuzhiyun 			kfree_skb(nskb);
483*4882a593Smuzhiyun 			return;
484*4882a593Smuzhiyun 		}
485*4882a593Smuzhiyun 
486*4882a593Smuzhiyun 		/* check for checksum updates */
487*4882a593Smuzhiyun 		if (gwj->mod.csumfunc.crc8)
488*4882a593Smuzhiyun 			(*gwj->mod.csumfunc.crc8)(cf, &gwj->mod.csum.crc8);
489*4882a593Smuzhiyun 
490*4882a593Smuzhiyun 		if (gwj->mod.csumfunc.xor)
491*4882a593Smuzhiyun 			(*gwj->mod.csumfunc.xor)(cf, &gwj->mod.csum.xor);
492*4882a593Smuzhiyun 	}
493*4882a593Smuzhiyun 
494*4882a593Smuzhiyun 	/* clear the skb timestamp if not configured the other way */
495*4882a593Smuzhiyun 	if (!(gwj->flags & CGW_FLAGS_CAN_SRC_TSTAMP))
496*4882a593Smuzhiyun 		nskb->tstamp = 0;
497*4882a593Smuzhiyun 
498*4882a593Smuzhiyun 	/* send to netdevice */
499*4882a593Smuzhiyun 	if (can_send(nskb, gwj->flags & CGW_FLAGS_CAN_ECHO))
500*4882a593Smuzhiyun 		gwj->dropped_frames++;
501*4882a593Smuzhiyun 	else
502*4882a593Smuzhiyun 		gwj->handled_frames++;
503*4882a593Smuzhiyun }
504*4882a593Smuzhiyun 
cgw_register_filter(struct net * net,struct cgw_job * gwj)505*4882a593Smuzhiyun static inline int cgw_register_filter(struct net *net, struct cgw_job *gwj)
506*4882a593Smuzhiyun {
507*4882a593Smuzhiyun 	return can_rx_register(net, gwj->src.dev, gwj->ccgw.filter.can_id,
508*4882a593Smuzhiyun 			       gwj->ccgw.filter.can_mask, can_can_gw_rcv,
509*4882a593Smuzhiyun 			       gwj, "gw", NULL);
510*4882a593Smuzhiyun }
511*4882a593Smuzhiyun 
cgw_unregister_filter(struct net * net,struct cgw_job * gwj)512*4882a593Smuzhiyun static inline void cgw_unregister_filter(struct net *net, struct cgw_job *gwj)
513*4882a593Smuzhiyun {
514*4882a593Smuzhiyun 	can_rx_unregister(net, gwj->src.dev, gwj->ccgw.filter.can_id,
515*4882a593Smuzhiyun 			  gwj->ccgw.filter.can_mask, can_can_gw_rcv, gwj);
516*4882a593Smuzhiyun }
517*4882a593Smuzhiyun 
cgw_notifier(struct notifier_block * nb,unsigned long msg,void * ptr)518*4882a593Smuzhiyun static int cgw_notifier(struct notifier_block *nb,
519*4882a593Smuzhiyun 			unsigned long msg, void *ptr)
520*4882a593Smuzhiyun {
521*4882a593Smuzhiyun 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
522*4882a593Smuzhiyun 	struct net *net = dev_net(dev);
523*4882a593Smuzhiyun 
524*4882a593Smuzhiyun 	if (dev->type != ARPHRD_CAN)
525*4882a593Smuzhiyun 		return NOTIFY_DONE;
526*4882a593Smuzhiyun 
527*4882a593Smuzhiyun 	if (msg == NETDEV_UNREGISTER) {
528*4882a593Smuzhiyun 		struct cgw_job *gwj = NULL;
529*4882a593Smuzhiyun 		struct hlist_node *nx;
530*4882a593Smuzhiyun 
531*4882a593Smuzhiyun 		ASSERT_RTNL();
532*4882a593Smuzhiyun 
533*4882a593Smuzhiyun 		hlist_for_each_entry_safe(gwj, nx, &net->can.cgw_list, list) {
534*4882a593Smuzhiyun 			if (gwj->src.dev == dev || gwj->dst.dev == dev) {
535*4882a593Smuzhiyun 				hlist_del(&gwj->list);
536*4882a593Smuzhiyun 				cgw_unregister_filter(net, gwj);
537*4882a593Smuzhiyun 				synchronize_rcu();
538*4882a593Smuzhiyun 				kmem_cache_free(cgw_cache, gwj);
539*4882a593Smuzhiyun 			}
540*4882a593Smuzhiyun 		}
541*4882a593Smuzhiyun 	}
542*4882a593Smuzhiyun 
543*4882a593Smuzhiyun 	return NOTIFY_DONE;
544*4882a593Smuzhiyun }
545*4882a593Smuzhiyun 
cgw_put_job(struct sk_buff * skb,struct cgw_job * gwj,int type,u32 pid,u32 seq,int flags)546*4882a593Smuzhiyun static int cgw_put_job(struct sk_buff *skb, struct cgw_job *gwj, int type,
547*4882a593Smuzhiyun 		       u32 pid, u32 seq, int flags)
548*4882a593Smuzhiyun {
549*4882a593Smuzhiyun 	struct rtcanmsg *rtcan;
550*4882a593Smuzhiyun 	struct nlmsghdr *nlh;
551*4882a593Smuzhiyun 
552*4882a593Smuzhiyun 	nlh = nlmsg_put(skb, pid, seq, type, sizeof(*rtcan), flags);
553*4882a593Smuzhiyun 	if (!nlh)
554*4882a593Smuzhiyun 		return -EMSGSIZE;
555*4882a593Smuzhiyun 
556*4882a593Smuzhiyun 	rtcan = nlmsg_data(nlh);
557*4882a593Smuzhiyun 	rtcan->can_family = AF_CAN;
558*4882a593Smuzhiyun 	rtcan->gwtype = gwj->gwtype;
559*4882a593Smuzhiyun 	rtcan->flags = gwj->flags;
560*4882a593Smuzhiyun 
561*4882a593Smuzhiyun 	/* add statistics if available */
562*4882a593Smuzhiyun 
563*4882a593Smuzhiyun 	if (gwj->handled_frames) {
564*4882a593Smuzhiyun 		if (nla_put_u32(skb, CGW_HANDLED, gwj->handled_frames) < 0)
565*4882a593Smuzhiyun 			goto cancel;
566*4882a593Smuzhiyun 	}
567*4882a593Smuzhiyun 
568*4882a593Smuzhiyun 	if (gwj->dropped_frames) {
569*4882a593Smuzhiyun 		if (nla_put_u32(skb, CGW_DROPPED, gwj->dropped_frames) < 0)
570*4882a593Smuzhiyun 			goto cancel;
571*4882a593Smuzhiyun 	}
572*4882a593Smuzhiyun 
573*4882a593Smuzhiyun 	if (gwj->deleted_frames) {
574*4882a593Smuzhiyun 		if (nla_put_u32(skb, CGW_DELETED, gwj->deleted_frames) < 0)
575*4882a593Smuzhiyun 			goto cancel;
576*4882a593Smuzhiyun 	}
577*4882a593Smuzhiyun 
578*4882a593Smuzhiyun 	/* check non default settings of attributes */
579*4882a593Smuzhiyun 
580*4882a593Smuzhiyun 	if (gwj->limit_hops) {
581*4882a593Smuzhiyun 		if (nla_put_u8(skb, CGW_LIM_HOPS, gwj->limit_hops) < 0)
582*4882a593Smuzhiyun 			goto cancel;
583*4882a593Smuzhiyun 	}
584*4882a593Smuzhiyun 
585*4882a593Smuzhiyun 	if (gwj->flags & CGW_FLAGS_CAN_FD) {
586*4882a593Smuzhiyun 		struct cgw_fdframe_mod mb;
587*4882a593Smuzhiyun 
588*4882a593Smuzhiyun 		if (gwj->mod.modtype.and) {
589*4882a593Smuzhiyun 			memcpy(&mb.cf, &gwj->mod.modframe.and, sizeof(mb.cf));
590*4882a593Smuzhiyun 			mb.modtype = gwj->mod.modtype.and;
591*4882a593Smuzhiyun 			if (nla_put(skb, CGW_FDMOD_AND, sizeof(mb), &mb) < 0)
592*4882a593Smuzhiyun 				goto cancel;
593*4882a593Smuzhiyun 		}
594*4882a593Smuzhiyun 
595*4882a593Smuzhiyun 		if (gwj->mod.modtype.or) {
596*4882a593Smuzhiyun 			memcpy(&mb.cf, &gwj->mod.modframe.or, sizeof(mb.cf));
597*4882a593Smuzhiyun 			mb.modtype = gwj->mod.modtype.or;
598*4882a593Smuzhiyun 			if (nla_put(skb, CGW_FDMOD_OR, sizeof(mb), &mb) < 0)
599*4882a593Smuzhiyun 				goto cancel;
600*4882a593Smuzhiyun 		}
601*4882a593Smuzhiyun 
602*4882a593Smuzhiyun 		if (gwj->mod.modtype.xor) {
603*4882a593Smuzhiyun 			memcpy(&mb.cf, &gwj->mod.modframe.xor, sizeof(mb.cf));
604*4882a593Smuzhiyun 			mb.modtype = gwj->mod.modtype.xor;
605*4882a593Smuzhiyun 			if (nla_put(skb, CGW_FDMOD_XOR, sizeof(mb), &mb) < 0)
606*4882a593Smuzhiyun 				goto cancel;
607*4882a593Smuzhiyun 		}
608*4882a593Smuzhiyun 
609*4882a593Smuzhiyun 		if (gwj->mod.modtype.set) {
610*4882a593Smuzhiyun 			memcpy(&mb.cf, &gwj->mod.modframe.set, sizeof(mb.cf));
611*4882a593Smuzhiyun 			mb.modtype = gwj->mod.modtype.set;
612*4882a593Smuzhiyun 			if (nla_put(skb, CGW_FDMOD_SET, sizeof(mb), &mb) < 0)
613*4882a593Smuzhiyun 				goto cancel;
614*4882a593Smuzhiyun 		}
615*4882a593Smuzhiyun 	} else {
616*4882a593Smuzhiyun 		struct cgw_frame_mod mb;
617*4882a593Smuzhiyun 
618*4882a593Smuzhiyun 		if (gwj->mod.modtype.and) {
619*4882a593Smuzhiyun 			memcpy(&mb.cf, &gwj->mod.modframe.and, sizeof(mb.cf));
620*4882a593Smuzhiyun 			mb.modtype = gwj->mod.modtype.and;
621*4882a593Smuzhiyun 			if (nla_put(skb, CGW_MOD_AND, sizeof(mb), &mb) < 0)
622*4882a593Smuzhiyun 				goto cancel;
623*4882a593Smuzhiyun 		}
624*4882a593Smuzhiyun 
625*4882a593Smuzhiyun 		if (gwj->mod.modtype.or) {
626*4882a593Smuzhiyun 			memcpy(&mb.cf, &gwj->mod.modframe.or, sizeof(mb.cf));
627*4882a593Smuzhiyun 			mb.modtype = gwj->mod.modtype.or;
628*4882a593Smuzhiyun 			if (nla_put(skb, CGW_MOD_OR, sizeof(mb), &mb) < 0)
629*4882a593Smuzhiyun 				goto cancel;
630*4882a593Smuzhiyun 		}
631*4882a593Smuzhiyun 
632*4882a593Smuzhiyun 		if (gwj->mod.modtype.xor) {
633*4882a593Smuzhiyun 			memcpy(&mb.cf, &gwj->mod.modframe.xor, sizeof(mb.cf));
634*4882a593Smuzhiyun 			mb.modtype = gwj->mod.modtype.xor;
635*4882a593Smuzhiyun 			if (nla_put(skb, CGW_MOD_XOR, sizeof(mb), &mb) < 0)
636*4882a593Smuzhiyun 				goto cancel;
637*4882a593Smuzhiyun 		}
638*4882a593Smuzhiyun 
639*4882a593Smuzhiyun 		if (gwj->mod.modtype.set) {
640*4882a593Smuzhiyun 			memcpy(&mb.cf, &gwj->mod.modframe.set, sizeof(mb.cf));
641*4882a593Smuzhiyun 			mb.modtype = gwj->mod.modtype.set;
642*4882a593Smuzhiyun 			if (nla_put(skb, CGW_MOD_SET, sizeof(mb), &mb) < 0)
643*4882a593Smuzhiyun 				goto cancel;
644*4882a593Smuzhiyun 		}
645*4882a593Smuzhiyun 	}
646*4882a593Smuzhiyun 
647*4882a593Smuzhiyun 	if (gwj->mod.uid) {
648*4882a593Smuzhiyun 		if (nla_put_u32(skb, CGW_MOD_UID, gwj->mod.uid) < 0)
649*4882a593Smuzhiyun 			goto cancel;
650*4882a593Smuzhiyun 	}
651*4882a593Smuzhiyun 
652*4882a593Smuzhiyun 	if (gwj->mod.csumfunc.crc8) {
653*4882a593Smuzhiyun 		if (nla_put(skb, CGW_CS_CRC8, CGW_CS_CRC8_LEN,
654*4882a593Smuzhiyun 			    &gwj->mod.csum.crc8) < 0)
655*4882a593Smuzhiyun 			goto cancel;
656*4882a593Smuzhiyun 	}
657*4882a593Smuzhiyun 
658*4882a593Smuzhiyun 	if (gwj->mod.csumfunc.xor) {
659*4882a593Smuzhiyun 		if (nla_put(skb, CGW_CS_XOR, CGW_CS_XOR_LEN,
660*4882a593Smuzhiyun 			    &gwj->mod.csum.xor) < 0)
661*4882a593Smuzhiyun 			goto cancel;
662*4882a593Smuzhiyun 	}
663*4882a593Smuzhiyun 
664*4882a593Smuzhiyun 	if (gwj->gwtype == CGW_TYPE_CAN_CAN) {
665*4882a593Smuzhiyun 		if (gwj->ccgw.filter.can_id || gwj->ccgw.filter.can_mask) {
666*4882a593Smuzhiyun 			if (nla_put(skb, CGW_FILTER, sizeof(struct can_filter),
667*4882a593Smuzhiyun 				    &gwj->ccgw.filter) < 0)
668*4882a593Smuzhiyun 				goto cancel;
669*4882a593Smuzhiyun 		}
670*4882a593Smuzhiyun 
671*4882a593Smuzhiyun 		if (nla_put_u32(skb, CGW_SRC_IF, gwj->ccgw.src_idx) < 0)
672*4882a593Smuzhiyun 			goto cancel;
673*4882a593Smuzhiyun 
674*4882a593Smuzhiyun 		if (nla_put_u32(skb, CGW_DST_IF, gwj->ccgw.dst_idx) < 0)
675*4882a593Smuzhiyun 			goto cancel;
676*4882a593Smuzhiyun 	}
677*4882a593Smuzhiyun 
678*4882a593Smuzhiyun 	nlmsg_end(skb, nlh);
679*4882a593Smuzhiyun 	return 0;
680*4882a593Smuzhiyun 
681*4882a593Smuzhiyun cancel:
682*4882a593Smuzhiyun 	nlmsg_cancel(skb, nlh);
683*4882a593Smuzhiyun 	return -EMSGSIZE;
684*4882a593Smuzhiyun }
685*4882a593Smuzhiyun 
686*4882a593Smuzhiyun /* Dump information about all CAN gateway jobs, in response to RTM_GETROUTE */
cgw_dump_jobs(struct sk_buff * skb,struct netlink_callback * cb)687*4882a593Smuzhiyun static int cgw_dump_jobs(struct sk_buff *skb, struct netlink_callback *cb)
688*4882a593Smuzhiyun {
689*4882a593Smuzhiyun 	struct net *net = sock_net(skb->sk);
690*4882a593Smuzhiyun 	struct cgw_job *gwj = NULL;
691*4882a593Smuzhiyun 	int idx = 0;
692*4882a593Smuzhiyun 	int s_idx = cb->args[0];
693*4882a593Smuzhiyun 
694*4882a593Smuzhiyun 	rcu_read_lock();
695*4882a593Smuzhiyun 	hlist_for_each_entry_rcu(gwj, &net->can.cgw_list, list) {
696*4882a593Smuzhiyun 		if (idx < s_idx)
697*4882a593Smuzhiyun 			goto cont;
698*4882a593Smuzhiyun 
699*4882a593Smuzhiyun 		if (cgw_put_job(skb, gwj, RTM_NEWROUTE,
700*4882a593Smuzhiyun 				NETLINK_CB(cb->skb).portid,
701*4882a593Smuzhiyun 				cb->nlh->nlmsg_seq, NLM_F_MULTI) < 0)
702*4882a593Smuzhiyun 			break;
703*4882a593Smuzhiyun cont:
704*4882a593Smuzhiyun 		idx++;
705*4882a593Smuzhiyun 	}
706*4882a593Smuzhiyun 	rcu_read_unlock();
707*4882a593Smuzhiyun 
708*4882a593Smuzhiyun 	cb->args[0] = idx;
709*4882a593Smuzhiyun 
710*4882a593Smuzhiyun 	return skb->len;
711*4882a593Smuzhiyun }
712*4882a593Smuzhiyun 
713*4882a593Smuzhiyun static const struct nla_policy cgw_policy[CGW_MAX + 1] = {
714*4882a593Smuzhiyun 	[CGW_MOD_AND]	= { .len = sizeof(struct cgw_frame_mod) },
715*4882a593Smuzhiyun 	[CGW_MOD_OR]	= { .len = sizeof(struct cgw_frame_mod) },
716*4882a593Smuzhiyun 	[CGW_MOD_XOR]	= { .len = sizeof(struct cgw_frame_mod) },
717*4882a593Smuzhiyun 	[CGW_MOD_SET]	= { .len = sizeof(struct cgw_frame_mod) },
718*4882a593Smuzhiyun 	[CGW_CS_XOR]	= { .len = sizeof(struct cgw_csum_xor) },
719*4882a593Smuzhiyun 	[CGW_CS_CRC8]	= { .len = sizeof(struct cgw_csum_crc8) },
720*4882a593Smuzhiyun 	[CGW_SRC_IF]	= { .type = NLA_U32 },
721*4882a593Smuzhiyun 	[CGW_DST_IF]	= { .type = NLA_U32 },
722*4882a593Smuzhiyun 	[CGW_FILTER]	= { .len = sizeof(struct can_filter) },
723*4882a593Smuzhiyun 	[CGW_LIM_HOPS]	= { .type = NLA_U8 },
724*4882a593Smuzhiyun 	[CGW_MOD_UID]	= { .type = NLA_U32 },
725*4882a593Smuzhiyun 	[CGW_FDMOD_AND]	= { .len = sizeof(struct cgw_fdframe_mod) },
726*4882a593Smuzhiyun 	[CGW_FDMOD_OR]	= { .len = sizeof(struct cgw_fdframe_mod) },
727*4882a593Smuzhiyun 	[CGW_FDMOD_XOR]	= { .len = sizeof(struct cgw_fdframe_mod) },
728*4882a593Smuzhiyun 	[CGW_FDMOD_SET]	= { .len = sizeof(struct cgw_fdframe_mod) },
729*4882a593Smuzhiyun };
730*4882a593Smuzhiyun 
731*4882a593Smuzhiyun /* check for common and gwtype specific attributes */
cgw_parse_attr(struct nlmsghdr * nlh,struct cf_mod * mod,u8 gwtype,void * gwtypeattr,u8 * limhops)732*4882a593Smuzhiyun static int cgw_parse_attr(struct nlmsghdr *nlh, struct cf_mod *mod,
733*4882a593Smuzhiyun 			  u8 gwtype, void *gwtypeattr, u8 *limhops)
734*4882a593Smuzhiyun {
735*4882a593Smuzhiyun 	struct nlattr *tb[CGW_MAX + 1];
736*4882a593Smuzhiyun 	struct rtcanmsg *r = nlmsg_data(nlh);
737*4882a593Smuzhiyun 	int modidx = 0;
738*4882a593Smuzhiyun 	int err = 0;
739*4882a593Smuzhiyun 
740*4882a593Smuzhiyun 	/* initialize modification & checksum data space */
741*4882a593Smuzhiyun 	memset(mod, 0, sizeof(*mod));
742*4882a593Smuzhiyun 
743*4882a593Smuzhiyun 	err = nlmsg_parse_deprecated(nlh, sizeof(struct rtcanmsg), tb,
744*4882a593Smuzhiyun 				     CGW_MAX, cgw_policy, NULL);
745*4882a593Smuzhiyun 	if (err < 0)
746*4882a593Smuzhiyun 		return err;
747*4882a593Smuzhiyun 
748*4882a593Smuzhiyun 	if (tb[CGW_LIM_HOPS]) {
749*4882a593Smuzhiyun 		*limhops = nla_get_u8(tb[CGW_LIM_HOPS]);
750*4882a593Smuzhiyun 
751*4882a593Smuzhiyun 		if (*limhops < 1 || *limhops > max_hops)
752*4882a593Smuzhiyun 			return -EINVAL;
753*4882a593Smuzhiyun 	}
754*4882a593Smuzhiyun 
755*4882a593Smuzhiyun 	/* check for AND/OR/XOR/SET modifications */
756*4882a593Smuzhiyun 	if (r->flags & CGW_FLAGS_CAN_FD) {
757*4882a593Smuzhiyun 		struct cgw_fdframe_mod mb;
758*4882a593Smuzhiyun 
759*4882a593Smuzhiyun 		if (tb[CGW_FDMOD_AND]) {
760*4882a593Smuzhiyun 			nla_memcpy(&mb, tb[CGW_FDMOD_AND], CGW_FDMODATTR_LEN);
761*4882a593Smuzhiyun 
762*4882a593Smuzhiyun 			canfdframecpy(&mod->modframe.and, &mb.cf);
763*4882a593Smuzhiyun 			mod->modtype.and = mb.modtype;
764*4882a593Smuzhiyun 
765*4882a593Smuzhiyun 			if (mb.modtype & CGW_MOD_ID)
766*4882a593Smuzhiyun 				mod->modfunc[modidx++] = mod_and_id;
767*4882a593Smuzhiyun 
768*4882a593Smuzhiyun 			if (mb.modtype & CGW_MOD_LEN)
769*4882a593Smuzhiyun 				mod->modfunc[modidx++] = mod_and_len;
770*4882a593Smuzhiyun 
771*4882a593Smuzhiyun 			if (mb.modtype & CGW_MOD_FLAGS)
772*4882a593Smuzhiyun 				mod->modfunc[modidx++] = mod_and_flags;
773*4882a593Smuzhiyun 
774*4882a593Smuzhiyun 			if (mb.modtype & CGW_MOD_DATA)
775*4882a593Smuzhiyun 				mod->modfunc[modidx++] = mod_and_fddata;
776*4882a593Smuzhiyun 		}
777*4882a593Smuzhiyun 
778*4882a593Smuzhiyun 		if (tb[CGW_FDMOD_OR]) {
779*4882a593Smuzhiyun 			nla_memcpy(&mb, tb[CGW_FDMOD_OR], CGW_FDMODATTR_LEN);
780*4882a593Smuzhiyun 
781*4882a593Smuzhiyun 			canfdframecpy(&mod->modframe.or, &mb.cf);
782*4882a593Smuzhiyun 			mod->modtype.or = mb.modtype;
783*4882a593Smuzhiyun 
784*4882a593Smuzhiyun 			if (mb.modtype & CGW_MOD_ID)
785*4882a593Smuzhiyun 				mod->modfunc[modidx++] = mod_or_id;
786*4882a593Smuzhiyun 
787*4882a593Smuzhiyun 			if (mb.modtype & CGW_MOD_LEN)
788*4882a593Smuzhiyun 				mod->modfunc[modidx++] = mod_or_len;
789*4882a593Smuzhiyun 
790*4882a593Smuzhiyun 			if (mb.modtype & CGW_MOD_FLAGS)
791*4882a593Smuzhiyun 				mod->modfunc[modidx++] = mod_or_flags;
792*4882a593Smuzhiyun 
793*4882a593Smuzhiyun 			if (mb.modtype & CGW_MOD_DATA)
794*4882a593Smuzhiyun 				mod->modfunc[modidx++] = mod_or_fddata;
795*4882a593Smuzhiyun 		}
796*4882a593Smuzhiyun 
797*4882a593Smuzhiyun 		if (tb[CGW_FDMOD_XOR]) {
798*4882a593Smuzhiyun 			nla_memcpy(&mb, tb[CGW_FDMOD_XOR], CGW_FDMODATTR_LEN);
799*4882a593Smuzhiyun 
800*4882a593Smuzhiyun 			canfdframecpy(&mod->modframe.xor, &mb.cf);
801*4882a593Smuzhiyun 			mod->modtype.xor = mb.modtype;
802*4882a593Smuzhiyun 
803*4882a593Smuzhiyun 			if (mb.modtype & CGW_MOD_ID)
804*4882a593Smuzhiyun 				mod->modfunc[modidx++] = mod_xor_id;
805*4882a593Smuzhiyun 
806*4882a593Smuzhiyun 			if (mb.modtype & CGW_MOD_LEN)
807*4882a593Smuzhiyun 				mod->modfunc[modidx++] = mod_xor_len;
808*4882a593Smuzhiyun 
809*4882a593Smuzhiyun 			if (mb.modtype & CGW_MOD_FLAGS)
810*4882a593Smuzhiyun 				mod->modfunc[modidx++] = mod_xor_flags;
811*4882a593Smuzhiyun 
812*4882a593Smuzhiyun 			if (mb.modtype & CGW_MOD_DATA)
813*4882a593Smuzhiyun 				mod->modfunc[modidx++] = mod_xor_fddata;
814*4882a593Smuzhiyun 		}
815*4882a593Smuzhiyun 
816*4882a593Smuzhiyun 		if (tb[CGW_FDMOD_SET]) {
817*4882a593Smuzhiyun 			nla_memcpy(&mb, tb[CGW_FDMOD_SET], CGW_FDMODATTR_LEN);
818*4882a593Smuzhiyun 
819*4882a593Smuzhiyun 			canfdframecpy(&mod->modframe.set, &mb.cf);
820*4882a593Smuzhiyun 			mod->modtype.set = mb.modtype;
821*4882a593Smuzhiyun 
822*4882a593Smuzhiyun 			if (mb.modtype & CGW_MOD_ID)
823*4882a593Smuzhiyun 				mod->modfunc[modidx++] = mod_set_id;
824*4882a593Smuzhiyun 
825*4882a593Smuzhiyun 			if (mb.modtype & CGW_MOD_LEN)
826*4882a593Smuzhiyun 				mod->modfunc[modidx++] = mod_set_len;
827*4882a593Smuzhiyun 
828*4882a593Smuzhiyun 			if (mb.modtype & CGW_MOD_FLAGS)
829*4882a593Smuzhiyun 				mod->modfunc[modidx++] = mod_set_flags;
830*4882a593Smuzhiyun 
831*4882a593Smuzhiyun 			if (mb.modtype & CGW_MOD_DATA)
832*4882a593Smuzhiyun 				mod->modfunc[modidx++] = mod_set_fddata;
833*4882a593Smuzhiyun 		}
834*4882a593Smuzhiyun 	} else {
835*4882a593Smuzhiyun 		struct cgw_frame_mod mb;
836*4882a593Smuzhiyun 
837*4882a593Smuzhiyun 		if (tb[CGW_MOD_AND]) {
838*4882a593Smuzhiyun 			nla_memcpy(&mb, tb[CGW_MOD_AND], CGW_MODATTR_LEN);
839*4882a593Smuzhiyun 
840*4882a593Smuzhiyun 			canframecpy(&mod->modframe.and, &mb.cf);
841*4882a593Smuzhiyun 			mod->modtype.and = mb.modtype;
842*4882a593Smuzhiyun 
843*4882a593Smuzhiyun 			if (mb.modtype & CGW_MOD_ID)
844*4882a593Smuzhiyun 				mod->modfunc[modidx++] = mod_and_id;
845*4882a593Smuzhiyun 
846*4882a593Smuzhiyun 			if (mb.modtype & CGW_MOD_LEN)
847*4882a593Smuzhiyun 				mod->modfunc[modidx++] = mod_and_len;
848*4882a593Smuzhiyun 
849*4882a593Smuzhiyun 			if (mb.modtype & CGW_MOD_DATA)
850*4882a593Smuzhiyun 				mod->modfunc[modidx++] = mod_and_data;
851*4882a593Smuzhiyun 		}
852*4882a593Smuzhiyun 
853*4882a593Smuzhiyun 		if (tb[CGW_MOD_OR]) {
854*4882a593Smuzhiyun 			nla_memcpy(&mb, tb[CGW_MOD_OR], CGW_MODATTR_LEN);
855*4882a593Smuzhiyun 
856*4882a593Smuzhiyun 			canframecpy(&mod->modframe.or, &mb.cf);
857*4882a593Smuzhiyun 			mod->modtype.or = mb.modtype;
858*4882a593Smuzhiyun 
859*4882a593Smuzhiyun 			if (mb.modtype & CGW_MOD_ID)
860*4882a593Smuzhiyun 				mod->modfunc[modidx++] = mod_or_id;
861*4882a593Smuzhiyun 
862*4882a593Smuzhiyun 			if (mb.modtype & CGW_MOD_LEN)
863*4882a593Smuzhiyun 				mod->modfunc[modidx++] = mod_or_len;
864*4882a593Smuzhiyun 
865*4882a593Smuzhiyun 			if (mb.modtype & CGW_MOD_DATA)
866*4882a593Smuzhiyun 				mod->modfunc[modidx++] = mod_or_data;
867*4882a593Smuzhiyun 		}
868*4882a593Smuzhiyun 
869*4882a593Smuzhiyun 		if (tb[CGW_MOD_XOR]) {
870*4882a593Smuzhiyun 			nla_memcpy(&mb, tb[CGW_MOD_XOR], CGW_MODATTR_LEN);
871*4882a593Smuzhiyun 
872*4882a593Smuzhiyun 			canframecpy(&mod->modframe.xor, &mb.cf);
873*4882a593Smuzhiyun 			mod->modtype.xor = mb.modtype;
874*4882a593Smuzhiyun 
875*4882a593Smuzhiyun 			if (mb.modtype & CGW_MOD_ID)
876*4882a593Smuzhiyun 				mod->modfunc[modidx++] = mod_xor_id;
877*4882a593Smuzhiyun 
878*4882a593Smuzhiyun 			if (mb.modtype & CGW_MOD_LEN)
879*4882a593Smuzhiyun 				mod->modfunc[modidx++] = mod_xor_len;
880*4882a593Smuzhiyun 
881*4882a593Smuzhiyun 			if (mb.modtype & CGW_MOD_DATA)
882*4882a593Smuzhiyun 				mod->modfunc[modidx++] = mod_xor_data;
883*4882a593Smuzhiyun 		}
884*4882a593Smuzhiyun 
885*4882a593Smuzhiyun 		if (tb[CGW_MOD_SET]) {
886*4882a593Smuzhiyun 			nla_memcpy(&mb, tb[CGW_MOD_SET], CGW_MODATTR_LEN);
887*4882a593Smuzhiyun 
888*4882a593Smuzhiyun 			canframecpy(&mod->modframe.set, &mb.cf);
889*4882a593Smuzhiyun 			mod->modtype.set = mb.modtype;
890*4882a593Smuzhiyun 
891*4882a593Smuzhiyun 			if (mb.modtype & CGW_MOD_ID)
892*4882a593Smuzhiyun 				mod->modfunc[modidx++] = mod_set_id;
893*4882a593Smuzhiyun 
894*4882a593Smuzhiyun 			if (mb.modtype & CGW_MOD_LEN)
895*4882a593Smuzhiyun 				mod->modfunc[modidx++] = mod_set_len;
896*4882a593Smuzhiyun 
897*4882a593Smuzhiyun 			if (mb.modtype & CGW_MOD_DATA)
898*4882a593Smuzhiyun 				mod->modfunc[modidx++] = mod_set_data;
899*4882a593Smuzhiyun 		}
900*4882a593Smuzhiyun 	}
901*4882a593Smuzhiyun 
902*4882a593Smuzhiyun 	/* check for checksum operations after CAN frame modifications */
903*4882a593Smuzhiyun 	if (modidx) {
904*4882a593Smuzhiyun 		if (tb[CGW_CS_CRC8]) {
905*4882a593Smuzhiyun 			struct cgw_csum_crc8 *c = nla_data(tb[CGW_CS_CRC8]);
906*4882a593Smuzhiyun 
907*4882a593Smuzhiyun 			err = cgw_chk_csum_parms(c->from_idx, c->to_idx,
908*4882a593Smuzhiyun 						 c->result_idx, r);
909*4882a593Smuzhiyun 			if (err)
910*4882a593Smuzhiyun 				return err;
911*4882a593Smuzhiyun 
912*4882a593Smuzhiyun 			nla_memcpy(&mod->csum.crc8, tb[CGW_CS_CRC8],
913*4882a593Smuzhiyun 				   CGW_CS_CRC8_LEN);
914*4882a593Smuzhiyun 
915*4882a593Smuzhiyun 			/* select dedicated processing function to reduce
916*4882a593Smuzhiyun 			 * runtime operations in receive hot path.
917*4882a593Smuzhiyun 			 */
918*4882a593Smuzhiyun 			if (c->from_idx < 0 || c->to_idx < 0 ||
919*4882a593Smuzhiyun 			    c->result_idx < 0)
920*4882a593Smuzhiyun 				mod->csumfunc.crc8 = cgw_csum_crc8_rel;
921*4882a593Smuzhiyun 			else if (c->from_idx <= c->to_idx)
922*4882a593Smuzhiyun 				mod->csumfunc.crc8 = cgw_csum_crc8_pos;
923*4882a593Smuzhiyun 			else
924*4882a593Smuzhiyun 				mod->csumfunc.crc8 = cgw_csum_crc8_neg;
925*4882a593Smuzhiyun 		}
926*4882a593Smuzhiyun 
927*4882a593Smuzhiyun 		if (tb[CGW_CS_XOR]) {
928*4882a593Smuzhiyun 			struct cgw_csum_xor *c = nla_data(tb[CGW_CS_XOR]);
929*4882a593Smuzhiyun 
930*4882a593Smuzhiyun 			err = cgw_chk_csum_parms(c->from_idx, c->to_idx,
931*4882a593Smuzhiyun 						 c->result_idx, r);
932*4882a593Smuzhiyun 			if (err)
933*4882a593Smuzhiyun 				return err;
934*4882a593Smuzhiyun 
935*4882a593Smuzhiyun 			nla_memcpy(&mod->csum.xor, tb[CGW_CS_XOR],
936*4882a593Smuzhiyun 				   CGW_CS_XOR_LEN);
937*4882a593Smuzhiyun 
938*4882a593Smuzhiyun 			/* select dedicated processing function to reduce
939*4882a593Smuzhiyun 			 * runtime operations in receive hot path.
940*4882a593Smuzhiyun 			 */
941*4882a593Smuzhiyun 			if (c->from_idx < 0 || c->to_idx < 0 ||
942*4882a593Smuzhiyun 			    c->result_idx < 0)
943*4882a593Smuzhiyun 				mod->csumfunc.xor = cgw_csum_xor_rel;
944*4882a593Smuzhiyun 			else if (c->from_idx <= c->to_idx)
945*4882a593Smuzhiyun 				mod->csumfunc.xor = cgw_csum_xor_pos;
946*4882a593Smuzhiyun 			else
947*4882a593Smuzhiyun 				mod->csumfunc.xor = cgw_csum_xor_neg;
948*4882a593Smuzhiyun 		}
949*4882a593Smuzhiyun 
950*4882a593Smuzhiyun 		if (tb[CGW_MOD_UID])
951*4882a593Smuzhiyun 			nla_memcpy(&mod->uid, tb[CGW_MOD_UID], sizeof(u32));
952*4882a593Smuzhiyun 	}
953*4882a593Smuzhiyun 
954*4882a593Smuzhiyun 	if (gwtype == CGW_TYPE_CAN_CAN) {
955*4882a593Smuzhiyun 		/* check CGW_TYPE_CAN_CAN specific attributes */
956*4882a593Smuzhiyun 		struct can_can_gw *ccgw = (struct can_can_gw *)gwtypeattr;
957*4882a593Smuzhiyun 
958*4882a593Smuzhiyun 		memset(ccgw, 0, sizeof(*ccgw));
959*4882a593Smuzhiyun 
960*4882a593Smuzhiyun 		/* check for can_filter in attributes */
961*4882a593Smuzhiyun 		if (tb[CGW_FILTER])
962*4882a593Smuzhiyun 			nla_memcpy(&ccgw->filter, tb[CGW_FILTER],
963*4882a593Smuzhiyun 				   sizeof(struct can_filter));
964*4882a593Smuzhiyun 
965*4882a593Smuzhiyun 		err = -ENODEV;
966*4882a593Smuzhiyun 
967*4882a593Smuzhiyun 		/* specifying two interfaces is mandatory */
968*4882a593Smuzhiyun 		if (!tb[CGW_SRC_IF] || !tb[CGW_DST_IF])
969*4882a593Smuzhiyun 			return err;
970*4882a593Smuzhiyun 
971*4882a593Smuzhiyun 		ccgw->src_idx = nla_get_u32(tb[CGW_SRC_IF]);
972*4882a593Smuzhiyun 		ccgw->dst_idx = nla_get_u32(tb[CGW_DST_IF]);
973*4882a593Smuzhiyun 
974*4882a593Smuzhiyun 		/* both indices set to 0 for flushing all routing entries */
975*4882a593Smuzhiyun 		if (!ccgw->src_idx && !ccgw->dst_idx)
976*4882a593Smuzhiyun 			return 0;
977*4882a593Smuzhiyun 
978*4882a593Smuzhiyun 		/* only one index set to 0 is an error */
979*4882a593Smuzhiyun 		if (!ccgw->src_idx || !ccgw->dst_idx)
980*4882a593Smuzhiyun 			return err;
981*4882a593Smuzhiyun 	}
982*4882a593Smuzhiyun 
983*4882a593Smuzhiyun 	/* add the checks for other gwtypes here */
984*4882a593Smuzhiyun 
985*4882a593Smuzhiyun 	return 0;
986*4882a593Smuzhiyun }
987*4882a593Smuzhiyun 
cgw_create_job(struct sk_buff * skb,struct nlmsghdr * nlh,struct netlink_ext_ack * extack)988*4882a593Smuzhiyun static int cgw_create_job(struct sk_buff *skb,  struct nlmsghdr *nlh,
989*4882a593Smuzhiyun 			  struct netlink_ext_ack *extack)
990*4882a593Smuzhiyun {
991*4882a593Smuzhiyun 	struct net *net = sock_net(skb->sk);
992*4882a593Smuzhiyun 	struct rtcanmsg *r;
993*4882a593Smuzhiyun 	struct cgw_job *gwj;
994*4882a593Smuzhiyun 	struct cf_mod mod;
995*4882a593Smuzhiyun 	struct can_can_gw ccgw;
996*4882a593Smuzhiyun 	u8 limhops = 0;
997*4882a593Smuzhiyun 	int err = 0;
998*4882a593Smuzhiyun 
999*4882a593Smuzhiyun 	if (!netlink_capable(skb, CAP_NET_ADMIN))
1000*4882a593Smuzhiyun 		return -EPERM;
1001*4882a593Smuzhiyun 
1002*4882a593Smuzhiyun 	if (nlmsg_len(nlh) < sizeof(*r))
1003*4882a593Smuzhiyun 		return -EINVAL;
1004*4882a593Smuzhiyun 
1005*4882a593Smuzhiyun 	r = nlmsg_data(nlh);
1006*4882a593Smuzhiyun 	if (r->can_family != AF_CAN)
1007*4882a593Smuzhiyun 		return -EPFNOSUPPORT;
1008*4882a593Smuzhiyun 
1009*4882a593Smuzhiyun 	/* so far we only support CAN -> CAN routings */
1010*4882a593Smuzhiyun 	if (r->gwtype != CGW_TYPE_CAN_CAN)
1011*4882a593Smuzhiyun 		return -EINVAL;
1012*4882a593Smuzhiyun 
1013*4882a593Smuzhiyun 	err = cgw_parse_attr(nlh, &mod, CGW_TYPE_CAN_CAN, &ccgw, &limhops);
1014*4882a593Smuzhiyun 	if (err < 0)
1015*4882a593Smuzhiyun 		return err;
1016*4882a593Smuzhiyun 
1017*4882a593Smuzhiyun 	if (mod.uid) {
1018*4882a593Smuzhiyun 		ASSERT_RTNL();
1019*4882a593Smuzhiyun 
1020*4882a593Smuzhiyun 		/* check for updating an existing job with identical uid */
1021*4882a593Smuzhiyun 		hlist_for_each_entry(gwj, &net->can.cgw_list, list) {
1022*4882a593Smuzhiyun 			if (gwj->mod.uid != mod.uid)
1023*4882a593Smuzhiyun 				continue;
1024*4882a593Smuzhiyun 
1025*4882a593Smuzhiyun 			/* interfaces & filters must be identical */
1026*4882a593Smuzhiyun 			if (memcmp(&gwj->ccgw, &ccgw, sizeof(ccgw)))
1027*4882a593Smuzhiyun 				return -EINVAL;
1028*4882a593Smuzhiyun 
1029*4882a593Smuzhiyun 			/* update modifications with disabled softirq & quit */
1030*4882a593Smuzhiyun 			local_bh_disable();
1031*4882a593Smuzhiyun 			memcpy(&gwj->mod, &mod, sizeof(mod));
1032*4882a593Smuzhiyun 			local_bh_enable();
1033*4882a593Smuzhiyun 			return 0;
1034*4882a593Smuzhiyun 		}
1035*4882a593Smuzhiyun 	}
1036*4882a593Smuzhiyun 
1037*4882a593Smuzhiyun 	/* ifindex == 0 is not allowed for job creation */
1038*4882a593Smuzhiyun 	if (!ccgw.src_idx || !ccgw.dst_idx)
1039*4882a593Smuzhiyun 		return -ENODEV;
1040*4882a593Smuzhiyun 
1041*4882a593Smuzhiyun 	gwj = kmem_cache_alloc(cgw_cache, GFP_KERNEL);
1042*4882a593Smuzhiyun 	if (!gwj)
1043*4882a593Smuzhiyun 		return -ENOMEM;
1044*4882a593Smuzhiyun 
1045*4882a593Smuzhiyun 	gwj->handled_frames = 0;
1046*4882a593Smuzhiyun 	gwj->dropped_frames = 0;
1047*4882a593Smuzhiyun 	gwj->deleted_frames = 0;
1048*4882a593Smuzhiyun 	gwj->flags = r->flags;
1049*4882a593Smuzhiyun 	gwj->gwtype = r->gwtype;
1050*4882a593Smuzhiyun 	gwj->limit_hops = limhops;
1051*4882a593Smuzhiyun 
1052*4882a593Smuzhiyun 	/* insert already parsed information */
1053*4882a593Smuzhiyun 	memcpy(&gwj->mod, &mod, sizeof(mod));
1054*4882a593Smuzhiyun 	memcpy(&gwj->ccgw, &ccgw, sizeof(ccgw));
1055*4882a593Smuzhiyun 
1056*4882a593Smuzhiyun 	err = -ENODEV;
1057*4882a593Smuzhiyun 
1058*4882a593Smuzhiyun 	gwj->src.dev = __dev_get_by_index(net, gwj->ccgw.src_idx);
1059*4882a593Smuzhiyun 
1060*4882a593Smuzhiyun 	if (!gwj->src.dev)
1061*4882a593Smuzhiyun 		goto out;
1062*4882a593Smuzhiyun 
1063*4882a593Smuzhiyun 	if (gwj->src.dev->type != ARPHRD_CAN)
1064*4882a593Smuzhiyun 		goto out;
1065*4882a593Smuzhiyun 
1066*4882a593Smuzhiyun 	gwj->dst.dev = __dev_get_by_index(net, gwj->ccgw.dst_idx);
1067*4882a593Smuzhiyun 
1068*4882a593Smuzhiyun 	if (!gwj->dst.dev)
1069*4882a593Smuzhiyun 		goto out;
1070*4882a593Smuzhiyun 
1071*4882a593Smuzhiyun 	if (gwj->dst.dev->type != ARPHRD_CAN)
1072*4882a593Smuzhiyun 		goto out;
1073*4882a593Smuzhiyun 
1074*4882a593Smuzhiyun 	ASSERT_RTNL();
1075*4882a593Smuzhiyun 
1076*4882a593Smuzhiyun 	err = cgw_register_filter(net, gwj);
1077*4882a593Smuzhiyun 	if (!err)
1078*4882a593Smuzhiyun 		hlist_add_head_rcu(&gwj->list, &net->can.cgw_list);
1079*4882a593Smuzhiyun out:
1080*4882a593Smuzhiyun 	if (err)
1081*4882a593Smuzhiyun 		kmem_cache_free(cgw_cache, gwj);
1082*4882a593Smuzhiyun 
1083*4882a593Smuzhiyun 	return err;
1084*4882a593Smuzhiyun }
1085*4882a593Smuzhiyun 
cgw_remove_all_jobs(struct net * net)1086*4882a593Smuzhiyun static void cgw_remove_all_jobs(struct net *net)
1087*4882a593Smuzhiyun {
1088*4882a593Smuzhiyun 	struct cgw_job *gwj = NULL;
1089*4882a593Smuzhiyun 	struct hlist_node *nx;
1090*4882a593Smuzhiyun 
1091*4882a593Smuzhiyun 	ASSERT_RTNL();
1092*4882a593Smuzhiyun 
1093*4882a593Smuzhiyun 	hlist_for_each_entry_safe(gwj, nx, &net->can.cgw_list, list) {
1094*4882a593Smuzhiyun 		hlist_del(&gwj->list);
1095*4882a593Smuzhiyun 		cgw_unregister_filter(net, gwj);
1096*4882a593Smuzhiyun 		synchronize_rcu();
1097*4882a593Smuzhiyun 		kmem_cache_free(cgw_cache, gwj);
1098*4882a593Smuzhiyun 	}
1099*4882a593Smuzhiyun }
1100*4882a593Smuzhiyun 
cgw_remove_job(struct sk_buff * skb,struct nlmsghdr * nlh,struct netlink_ext_ack * extack)1101*4882a593Smuzhiyun static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh,
1102*4882a593Smuzhiyun 			  struct netlink_ext_ack *extack)
1103*4882a593Smuzhiyun {
1104*4882a593Smuzhiyun 	struct net *net = sock_net(skb->sk);
1105*4882a593Smuzhiyun 	struct cgw_job *gwj = NULL;
1106*4882a593Smuzhiyun 	struct hlist_node *nx;
1107*4882a593Smuzhiyun 	struct rtcanmsg *r;
1108*4882a593Smuzhiyun 	struct cf_mod mod;
1109*4882a593Smuzhiyun 	struct can_can_gw ccgw;
1110*4882a593Smuzhiyun 	u8 limhops = 0;
1111*4882a593Smuzhiyun 	int err = 0;
1112*4882a593Smuzhiyun 
1113*4882a593Smuzhiyun 	if (!netlink_capable(skb, CAP_NET_ADMIN))
1114*4882a593Smuzhiyun 		return -EPERM;
1115*4882a593Smuzhiyun 
1116*4882a593Smuzhiyun 	if (nlmsg_len(nlh) < sizeof(*r))
1117*4882a593Smuzhiyun 		return -EINVAL;
1118*4882a593Smuzhiyun 
1119*4882a593Smuzhiyun 	r = nlmsg_data(nlh);
1120*4882a593Smuzhiyun 	if (r->can_family != AF_CAN)
1121*4882a593Smuzhiyun 		return -EPFNOSUPPORT;
1122*4882a593Smuzhiyun 
1123*4882a593Smuzhiyun 	/* so far we only support CAN -> CAN routings */
1124*4882a593Smuzhiyun 	if (r->gwtype != CGW_TYPE_CAN_CAN)
1125*4882a593Smuzhiyun 		return -EINVAL;
1126*4882a593Smuzhiyun 
1127*4882a593Smuzhiyun 	err = cgw_parse_attr(nlh, &mod, CGW_TYPE_CAN_CAN, &ccgw, &limhops);
1128*4882a593Smuzhiyun 	if (err < 0)
1129*4882a593Smuzhiyun 		return err;
1130*4882a593Smuzhiyun 
1131*4882a593Smuzhiyun 	/* two interface indices both set to 0 => remove all entries */
1132*4882a593Smuzhiyun 	if (!ccgw.src_idx && !ccgw.dst_idx) {
1133*4882a593Smuzhiyun 		cgw_remove_all_jobs(net);
1134*4882a593Smuzhiyun 		return 0;
1135*4882a593Smuzhiyun 	}
1136*4882a593Smuzhiyun 
1137*4882a593Smuzhiyun 	err = -EINVAL;
1138*4882a593Smuzhiyun 
1139*4882a593Smuzhiyun 	ASSERT_RTNL();
1140*4882a593Smuzhiyun 
1141*4882a593Smuzhiyun 	/* remove only the first matching entry */
1142*4882a593Smuzhiyun 	hlist_for_each_entry_safe(gwj, nx, &net->can.cgw_list, list) {
1143*4882a593Smuzhiyun 		if (gwj->flags != r->flags)
1144*4882a593Smuzhiyun 			continue;
1145*4882a593Smuzhiyun 
1146*4882a593Smuzhiyun 		if (gwj->limit_hops != limhops)
1147*4882a593Smuzhiyun 			continue;
1148*4882a593Smuzhiyun 
1149*4882a593Smuzhiyun 		/* we have a match when uid is enabled and identical */
1150*4882a593Smuzhiyun 		if (gwj->mod.uid || mod.uid) {
1151*4882a593Smuzhiyun 			if (gwj->mod.uid != mod.uid)
1152*4882a593Smuzhiyun 				continue;
1153*4882a593Smuzhiyun 		} else {
1154*4882a593Smuzhiyun 			/* no uid => check for identical modifications */
1155*4882a593Smuzhiyun 			if (memcmp(&gwj->mod, &mod, sizeof(mod)))
1156*4882a593Smuzhiyun 				continue;
1157*4882a593Smuzhiyun 		}
1158*4882a593Smuzhiyun 
1159*4882a593Smuzhiyun 		/* if (r->gwtype == CGW_TYPE_CAN_CAN) - is made sure here */
1160*4882a593Smuzhiyun 		if (memcmp(&gwj->ccgw, &ccgw, sizeof(ccgw)))
1161*4882a593Smuzhiyun 			continue;
1162*4882a593Smuzhiyun 
1163*4882a593Smuzhiyun 		hlist_del(&gwj->list);
1164*4882a593Smuzhiyun 		cgw_unregister_filter(net, gwj);
1165*4882a593Smuzhiyun 		synchronize_rcu();
1166*4882a593Smuzhiyun 		kmem_cache_free(cgw_cache, gwj);
1167*4882a593Smuzhiyun 		err = 0;
1168*4882a593Smuzhiyun 		break;
1169*4882a593Smuzhiyun 	}
1170*4882a593Smuzhiyun 
1171*4882a593Smuzhiyun 	return err;
1172*4882a593Smuzhiyun }
1173*4882a593Smuzhiyun 
cangw_pernet_init(struct net * net)1174*4882a593Smuzhiyun static int __net_init cangw_pernet_init(struct net *net)
1175*4882a593Smuzhiyun {
1176*4882a593Smuzhiyun 	INIT_HLIST_HEAD(&net->can.cgw_list);
1177*4882a593Smuzhiyun 	return 0;
1178*4882a593Smuzhiyun }
1179*4882a593Smuzhiyun 
cangw_pernet_exit(struct net * net)1180*4882a593Smuzhiyun static void __net_exit cangw_pernet_exit(struct net *net)
1181*4882a593Smuzhiyun {
1182*4882a593Smuzhiyun 	rtnl_lock();
1183*4882a593Smuzhiyun 	cgw_remove_all_jobs(net);
1184*4882a593Smuzhiyun 	rtnl_unlock();
1185*4882a593Smuzhiyun }
1186*4882a593Smuzhiyun 
1187*4882a593Smuzhiyun static struct pernet_operations cangw_pernet_ops = {
1188*4882a593Smuzhiyun 	.init = cangw_pernet_init,
1189*4882a593Smuzhiyun 	.exit = cangw_pernet_exit,
1190*4882a593Smuzhiyun };
1191*4882a593Smuzhiyun 
cgw_module_init(void)1192*4882a593Smuzhiyun static __init int cgw_module_init(void)
1193*4882a593Smuzhiyun {
1194*4882a593Smuzhiyun 	int ret;
1195*4882a593Smuzhiyun 
1196*4882a593Smuzhiyun 	/* sanitize given module parameter */
1197*4882a593Smuzhiyun 	max_hops = clamp_t(unsigned int, max_hops, CGW_MIN_HOPS, CGW_MAX_HOPS);
1198*4882a593Smuzhiyun 
1199*4882a593Smuzhiyun 	pr_info("can: netlink gateway - max_hops=%d\n",	max_hops);
1200*4882a593Smuzhiyun 
1201*4882a593Smuzhiyun 	ret = register_pernet_subsys(&cangw_pernet_ops);
1202*4882a593Smuzhiyun 	if (ret)
1203*4882a593Smuzhiyun 		return ret;
1204*4882a593Smuzhiyun 
1205*4882a593Smuzhiyun 	ret = -ENOMEM;
1206*4882a593Smuzhiyun 	cgw_cache = kmem_cache_create("can_gw", sizeof(struct cgw_job),
1207*4882a593Smuzhiyun 				      0, 0, NULL);
1208*4882a593Smuzhiyun 	if (!cgw_cache)
1209*4882a593Smuzhiyun 		goto out_cache_create;
1210*4882a593Smuzhiyun 
1211*4882a593Smuzhiyun 	/* set notifier */
1212*4882a593Smuzhiyun 	notifier.notifier_call = cgw_notifier;
1213*4882a593Smuzhiyun 	ret = register_netdevice_notifier(&notifier);
1214*4882a593Smuzhiyun 	if (ret)
1215*4882a593Smuzhiyun 		goto out_register_notifier;
1216*4882a593Smuzhiyun 
1217*4882a593Smuzhiyun 	ret = rtnl_register_module(THIS_MODULE, PF_CAN, RTM_GETROUTE,
1218*4882a593Smuzhiyun 				   NULL, cgw_dump_jobs, 0);
1219*4882a593Smuzhiyun 	if (ret)
1220*4882a593Smuzhiyun 		goto out_rtnl_register1;
1221*4882a593Smuzhiyun 
1222*4882a593Smuzhiyun 	ret = rtnl_register_module(THIS_MODULE, PF_CAN, RTM_NEWROUTE,
1223*4882a593Smuzhiyun 				   cgw_create_job, NULL, 0);
1224*4882a593Smuzhiyun 	if (ret)
1225*4882a593Smuzhiyun 		goto out_rtnl_register2;
1226*4882a593Smuzhiyun 	ret = rtnl_register_module(THIS_MODULE, PF_CAN, RTM_DELROUTE,
1227*4882a593Smuzhiyun 				   cgw_remove_job, NULL, 0);
1228*4882a593Smuzhiyun 	if (ret)
1229*4882a593Smuzhiyun 		goto out_rtnl_register3;
1230*4882a593Smuzhiyun 
1231*4882a593Smuzhiyun 	return 0;
1232*4882a593Smuzhiyun 
1233*4882a593Smuzhiyun out_rtnl_register3:
1234*4882a593Smuzhiyun 	rtnl_unregister(PF_CAN, RTM_NEWROUTE);
1235*4882a593Smuzhiyun out_rtnl_register2:
1236*4882a593Smuzhiyun 	rtnl_unregister(PF_CAN, RTM_GETROUTE);
1237*4882a593Smuzhiyun out_rtnl_register1:
1238*4882a593Smuzhiyun 	unregister_netdevice_notifier(&notifier);
1239*4882a593Smuzhiyun out_register_notifier:
1240*4882a593Smuzhiyun 	kmem_cache_destroy(cgw_cache);
1241*4882a593Smuzhiyun out_cache_create:
1242*4882a593Smuzhiyun 	unregister_pernet_subsys(&cangw_pernet_ops);
1243*4882a593Smuzhiyun 
1244*4882a593Smuzhiyun 	return ret;
1245*4882a593Smuzhiyun }
1246*4882a593Smuzhiyun 
cgw_module_exit(void)1247*4882a593Smuzhiyun static __exit void cgw_module_exit(void)
1248*4882a593Smuzhiyun {
1249*4882a593Smuzhiyun 	rtnl_unregister_all(PF_CAN);
1250*4882a593Smuzhiyun 
1251*4882a593Smuzhiyun 	unregister_netdevice_notifier(&notifier);
1252*4882a593Smuzhiyun 
1253*4882a593Smuzhiyun 	unregister_pernet_subsys(&cangw_pernet_ops);
1254*4882a593Smuzhiyun 	rcu_barrier(); /* Wait for completion of call_rcu()'s */
1255*4882a593Smuzhiyun 
1256*4882a593Smuzhiyun 	kmem_cache_destroy(cgw_cache);
1257*4882a593Smuzhiyun }
1258*4882a593Smuzhiyun 
1259*4882a593Smuzhiyun module_init(cgw_module_init);
1260*4882a593Smuzhiyun module_exit(cgw_module_exit);
1261