xref: /openbmc/linux/net/can/gw.c (revision 2a30b2bd)
1d77cd7feSMasahiro Yamada // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
2465c0debSMarc Kleine-Budde /* gw.c - CAN frame Gateway/Router/Bridge with netlink interface
3c1aabdf3SOliver Hartkopp  *
4456a8a64SOliver Hartkopp  * Copyright (c) 2019 Volkswagen Group Electronic Research
5c1aabdf3SOliver Hartkopp  * All rights reserved.
6c1aabdf3SOliver Hartkopp  *
7c1aabdf3SOliver Hartkopp  * Redistribution and use in source and binary forms, with or without
8c1aabdf3SOliver Hartkopp  * modification, are permitted provided that the following conditions
9c1aabdf3SOliver Hartkopp  * are met:
10c1aabdf3SOliver Hartkopp  * 1. Redistributions of source code must retain the above copyright
11c1aabdf3SOliver Hartkopp  *    notice, this list of conditions and the following disclaimer.
12c1aabdf3SOliver Hartkopp  * 2. Redistributions in binary form must reproduce the above copyright
13c1aabdf3SOliver Hartkopp  *    notice, this list of conditions and the following disclaimer in the
14c1aabdf3SOliver Hartkopp  *    documentation and/or other materials provided with the distribution.
15c1aabdf3SOliver Hartkopp  * 3. Neither the name of Volkswagen nor the names of its contributors
16c1aabdf3SOliver Hartkopp  *    may be used to endorse or promote products derived from this software
17c1aabdf3SOliver Hartkopp  *    without specific prior written permission.
18c1aabdf3SOliver Hartkopp  *
19c1aabdf3SOliver Hartkopp  * Alternatively, provided that this notice is retained in full, this
20c1aabdf3SOliver Hartkopp  * software may be distributed under the terms of the GNU General
21c1aabdf3SOliver Hartkopp  * Public License ("GPL") version 2, in which case the provisions of the
22c1aabdf3SOliver Hartkopp  * GPL apply INSTEAD OF those given above.
23c1aabdf3SOliver Hartkopp  *
24c1aabdf3SOliver Hartkopp  * The provided data structures and external interfaces from this code
25c1aabdf3SOliver Hartkopp  * are not restricted to be used by modules with a GPL compatible license.
26c1aabdf3SOliver Hartkopp  *
27c1aabdf3SOliver Hartkopp  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28c1aabdf3SOliver Hartkopp  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29c1aabdf3SOliver Hartkopp  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30c1aabdf3SOliver Hartkopp  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31c1aabdf3SOliver Hartkopp  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32c1aabdf3SOliver Hartkopp  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33c1aabdf3SOliver Hartkopp  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34c1aabdf3SOliver Hartkopp  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35c1aabdf3SOliver Hartkopp  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36c1aabdf3SOliver Hartkopp  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37c1aabdf3SOliver Hartkopp  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
38c1aabdf3SOliver Hartkopp  * DAMAGE.
39c1aabdf3SOliver Hartkopp  *
40c1aabdf3SOliver Hartkopp  */
41c1aabdf3SOliver Hartkopp 
42c1aabdf3SOliver Hartkopp #include <linux/module.h>
43c1aabdf3SOliver Hartkopp #include <linux/init.h>
44c1aabdf3SOliver Hartkopp #include <linux/types.h>
45be286bafSOliver Hartkopp #include <linux/kernel.h>
46c1aabdf3SOliver Hartkopp #include <linux/list.h>
47c1aabdf3SOliver Hartkopp #include <linux/spinlock.h>
48c1aabdf3SOliver Hartkopp #include <linux/rcupdate.h>
49c1aabdf3SOliver Hartkopp #include <linux/rculist.h>
50c1aabdf3SOliver Hartkopp #include <linux/net.h>
51c1aabdf3SOliver Hartkopp #include <linux/netdevice.h>
52c1aabdf3SOliver Hartkopp #include <linux/if_arp.h>
53c1aabdf3SOliver Hartkopp #include <linux/skbuff.h>
54c1aabdf3SOliver Hartkopp #include <linux/can.h>
55c1aabdf3SOliver Hartkopp #include <linux/can/core.h>
56d904d3edSOliver Hartkopp #include <linux/can/skb.h>
57c1aabdf3SOliver Hartkopp #include <linux/can/gw.h>
58c1aabdf3SOliver Hartkopp #include <net/rtnetlink.h>
59c1aabdf3SOliver Hartkopp #include <net/net_namespace.h>
60c1aabdf3SOliver Hartkopp #include <net/sock.h>
61c1aabdf3SOliver Hartkopp 
62be286bafSOliver Hartkopp #define CAN_GW_NAME "can-gw"
63c1aabdf3SOliver Hartkopp 
64c1aabdf3SOliver Hartkopp MODULE_DESCRIPTION("PF_CAN netlink gateway");
65c1aabdf3SOliver Hartkopp MODULE_LICENSE("Dual BSD/GPL");
66c1aabdf3SOliver Hartkopp MODULE_AUTHOR("Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
67be286bafSOliver Hartkopp MODULE_ALIAS(CAN_GW_NAME);
68be286bafSOliver Hartkopp 
69be286bafSOliver Hartkopp #define CGW_MIN_HOPS 1
70be286bafSOliver Hartkopp #define CGW_MAX_HOPS 6
71be286bafSOliver Hartkopp #define CGW_DEFAULT_HOPS 1
72be286bafSOliver Hartkopp 
73be286bafSOliver Hartkopp static unsigned int max_hops __read_mostly = CGW_DEFAULT_HOPS;
74d6444062SJoe Perches module_param(max_hops, uint, 0444);
75be286bafSOliver Hartkopp MODULE_PARM_DESC(max_hops,
76be286bafSOliver Hartkopp 		 "maximum " CAN_GW_NAME " routing hops for CAN frames "
77be286bafSOliver Hartkopp 		 "(valid values: " __stringify(CGW_MIN_HOPS) "-"
78be286bafSOliver Hartkopp 		 __stringify(CGW_MAX_HOPS) " hops, "
79be286bafSOliver Hartkopp 		 "default: " __stringify(CGW_DEFAULT_HOPS) ")");
80c1aabdf3SOliver Hartkopp 
81c1aabdf3SOliver Hartkopp static struct notifier_block notifier;
82c1aabdf3SOliver Hartkopp static struct kmem_cache *cgw_cache __read_mostly;
83c1aabdf3SOliver Hartkopp 
84c1aabdf3SOliver Hartkopp /* structure that contains the (on-the-fly) CAN frame modifications */
85c1aabdf3SOliver Hartkopp struct cf_mod {
86c1aabdf3SOliver Hartkopp 	struct {
87e9dc7c60SOliver Hartkopp 		struct canfd_frame and;
88e9dc7c60SOliver Hartkopp 		struct canfd_frame or;
89e9dc7c60SOliver Hartkopp 		struct canfd_frame xor;
90e9dc7c60SOliver Hartkopp 		struct canfd_frame set;
91c1aabdf3SOliver Hartkopp 	} modframe;
92c1aabdf3SOliver Hartkopp 	struct {
93c1aabdf3SOliver Hartkopp 		u8 and;
94c1aabdf3SOliver Hartkopp 		u8 or;
95c1aabdf3SOliver Hartkopp 		u8 xor;
96c1aabdf3SOliver Hartkopp 		u8 set;
97c1aabdf3SOliver Hartkopp 	} modtype;
98e9dc7c60SOliver Hartkopp 	void (*modfunc[MAX_MODFUNCTIONS])(struct canfd_frame *cf,
99c1aabdf3SOliver Hartkopp 					  struct cf_mod *mod);
100c1aabdf3SOliver Hartkopp 
101c1aabdf3SOliver Hartkopp 	/* CAN frame checksum calculation after CAN frame modifications */
102c1aabdf3SOliver Hartkopp 	struct {
103c1aabdf3SOliver Hartkopp 		struct cgw_csum_xor xor;
104c1aabdf3SOliver Hartkopp 		struct cgw_csum_crc8 crc8;
105c1aabdf3SOliver Hartkopp 	} csum;
106c1aabdf3SOliver Hartkopp 	struct {
107e9dc7c60SOliver Hartkopp 		void (*xor)(struct canfd_frame *cf,
108e9dc7c60SOliver Hartkopp 			    struct cgw_csum_xor *xor);
109e9dc7c60SOliver Hartkopp 		void (*crc8)(struct canfd_frame *cf,
110e9dc7c60SOliver Hartkopp 			     struct cgw_csum_crc8 *crc8);
111c1aabdf3SOliver Hartkopp 	} csumfunc;
112dd895d7fSOliver Hartkopp 	u32 uid;
113c1aabdf3SOliver Hartkopp };
114c1aabdf3SOliver Hartkopp 
115465c0debSMarc Kleine-Budde /* So far we just support CAN -> CAN routing and frame modifications.
116c1aabdf3SOliver Hartkopp  *
117c1aabdf3SOliver Hartkopp  * The internal can_can_gw structure contains data and attributes for
118c1aabdf3SOliver Hartkopp  * a CAN -> CAN gateway job.
119c1aabdf3SOliver Hartkopp  */
120c1aabdf3SOliver Hartkopp struct can_can_gw {
121c1aabdf3SOliver Hartkopp 	struct can_filter filter;
122c1aabdf3SOliver Hartkopp 	int src_idx;
123c1aabdf3SOliver Hartkopp 	int dst_idx;
124c1aabdf3SOliver Hartkopp };
125c1aabdf3SOliver Hartkopp 
126c1aabdf3SOliver Hartkopp /* list entry for CAN gateways jobs */
127c1aabdf3SOliver Hartkopp struct cgw_job {
128c1aabdf3SOliver Hartkopp 	struct hlist_node list;
129c1aabdf3SOliver Hartkopp 	struct rcu_head rcu;
130c1aabdf3SOliver Hartkopp 	u32 handled_frames;
131c1aabdf3SOliver Hartkopp 	u32 dropped_frames;
132e6afa00aSOliver Hartkopp 	u32 deleted_frames;
133c1aabdf3SOliver Hartkopp 	struct cf_mod mod;
134c1aabdf3SOliver Hartkopp 	union {
135c1aabdf3SOliver Hartkopp 		/* CAN frame data source */
136c1aabdf3SOliver Hartkopp 		struct net_device *dev;
137c1aabdf3SOliver Hartkopp 	} src;
138c1aabdf3SOliver Hartkopp 	union {
139c1aabdf3SOliver Hartkopp 		/* CAN frame data destination */
140c1aabdf3SOliver Hartkopp 		struct net_device *dev;
141c1aabdf3SOliver Hartkopp 	} dst;
142c1aabdf3SOliver Hartkopp 	union {
143c1aabdf3SOliver Hartkopp 		struct can_can_gw ccgw;
144c1aabdf3SOliver Hartkopp 		/* tbc */
145c1aabdf3SOliver Hartkopp 	};
146c1aabdf3SOliver Hartkopp 	u8 gwtype;
147391ac128SOliver Hartkopp 	u8 limit_hops;
148c1aabdf3SOliver Hartkopp 	u16 flags;
149c1aabdf3SOliver Hartkopp };
150c1aabdf3SOliver Hartkopp 
151c1aabdf3SOliver Hartkopp /* modification functions that are invoked in the hot path in can_can_gw_rcv */
152c1aabdf3SOliver Hartkopp 
153e9dc7c60SOliver Hartkopp #define MODFUNC(func, op) static void func(struct canfd_frame *cf, \
154c1aabdf3SOliver Hartkopp 					   struct cf_mod *mod) { op ; }
155c1aabdf3SOliver Hartkopp 
156c1aabdf3SOliver Hartkopp MODFUNC(mod_and_id, cf->can_id &= mod->modframe.and.can_id)
157e9dc7c60SOliver Hartkopp MODFUNC(mod_and_len, cf->len &= mod->modframe.and.len)
158456a8a64SOliver Hartkopp MODFUNC(mod_and_flags, cf->flags &= mod->modframe.and.flags)
159c1aabdf3SOliver Hartkopp MODFUNC(mod_and_data, *(u64 *)cf->data &= *(u64 *)mod->modframe.and.data)
160c1aabdf3SOliver Hartkopp MODFUNC(mod_or_id, cf->can_id |= mod->modframe.or.can_id)
161e9dc7c60SOliver Hartkopp MODFUNC(mod_or_len, cf->len |= mod->modframe.or.len)
162456a8a64SOliver Hartkopp MODFUNC(mod_or_flags, cf->flags |= mod->modframe.or.flags)
163c1aabdf3SOliver Hartkopp MODFUNC(mod_or_data, *(u64 *)cf->data |= *(u64 *)mod->modframe.or.data)
164c1aabdf3SOliver Hartkopp MODFUNC(mod_xor_id, cf->can_id ^= mod->modframe.xor.can_id)
165e9dc7c60SOliver Hartkopp MODFUNC(mod_xor_len, cf->len ^= mod->modframe.xor.len)
166456a8a64SOliver Hartkopp MODFUNC(mod_xor_flags, cf->flags ^= mod->modframe.xor.flags)
167c1aabdf3SOliver Hartkopp MODFUNC(mod_xor_data, *(u64 *)cf->data ^= *(u64 *)mod->modframe.xor.data)
168c1aabdf3SOliver Hartkopp MODFUNC(mod_set_id, cf->can_id = mod->modframe.set.can_id)
169e9dc7c60SOliver Hartkopp MODFUNC(mod_set_len, cf->len = mod->modframe.set.len)
170456a8a64SOliver Hartkopp MODFUNC(mod_set_flags, cf->flags = mod->modframe.set.flags)
171c1aabdf3SOliver Hartkopp MODFUNC(mod_set_data, *(u64 *)cf->data = *(u64 *)mod->modframe.set.data)
172c1aabdf3SOliver Hartkopp 
mod_and_fddata(struct canfd_frame * cf,struct cf_mod * mod)173456a8a64SOliver Hartkopp static void mod_and_fddata(struct canfd_frame *cf, struct cf_mod *mod)
174456a8a64SOliver Hartkopp {
175456a8a64SOliver Hartkopp 	int i;
176456a8a64SOliver Hartkopp 
177456a8a64SOliver Hartkopp 	for (i = 0; i < CANFD_MAX_DLEN; i += 8)
178456a8a64SOliver Hartkopp 		*(u64 *)(cf->data + i) &= *(u64 *)(mod->modframe.and.data + i);
179456a8a64SOliver Hartkopp }
180456a8a64SOliver Hartkopp 
mod_or_fddata(struct canfd_frame * cf,struct cf_mod * mod)181456a8a64SOliver Hartkopp static void mod_or_fddata(struct canfd_frame *cf, struct cf_mod *mod)
182456a8a64SOliver Hartkopp {
183456a8a64SOliver Hartkopp 	int i;
184456a8a64SOliver Hartkopp 
185456a8a64SOliver Hartkopp 	for (i = 0; i < CANFD_MAX_DLEN; i += 8)
186456a8a64SOliver Hartkopp 		*(u64 *)(cf->data + i) |= *(u64 *)(mod->modframe.or.data + i);
187456a8a64SOliver Hartkopp }
188456a8a64SOliver Hartkopp 
mod_xor_fddata(struct canfd_frame * cf,struct cf_mod * mod)189456a8a64SOliver Hartkopp static void mod_xor_fddata(struct canfd_frame *cf, struct cf_mod *mod)
190456a8a64SOliver Hartkopp {
191456a8a64SOliver Hartkopp 	int i;
192456a8a64SOliver Hartkopp 
193456a8a64SOliver Hartkopp 	for (i = 0; i < CANFD_MAX_DLEN; i += 8)
194456a8a64SOliver Hartkopp 		*(u64 *)(cf->data + i) ^= *(u64 *)(mod->modframe.xor.data + i);
195456a8a64SOliver Hartkopp }
196456a8a64SOliver Hartkopp 
mod_set_fddata(struct canfd_frame * cf,struct cf_mod * mod)197456a8a64SOliver Hartkopp static void mod_set_fddata(struct canfd_frame *cf, struct cf_mod *mod)
198456a8a64SOliver Hartkopp {
199456a8a64SOliver Hartkopp 	memcpy(cf->data, mod->modframe.set.data, CANFD_MAX_DLEN);
200456a8a64SOliver Hartkopp }
201456a8a64SOliver Hartkopp 
20294c23097SOliver Hartkopp /* retrieve valid CC DLC value and store it into 'len' */
mod_retrieve_ccdlc(struct canfd_frame * cf)20394c23097SOliver Hartkopp static void mod_retrieve_ccdlc(struct canfd_frame *cf)
20494c23097SOliver Hartkopp {
20594c23097SOliver Hartkopp 	struct can_frame *ccf = (struct can_frame *)cf;
20694c23097SOliver Hartkopp 
20794c23097SOliver Hartkopp 	/* len8_dlc is only valid if len == CAN_MAX_DLEN */
20894c23097SOliver Hartkopp 	if (ccf->len != CAN_MAX_DLEN)
20994c23097SOliver Hartkopp 		return;
21094c23097SOliver Hartkopp 
21194c23097SOliver Hartkopp 	/* do we have a valid len8_dlc value from 9 .. 15 ? */
21294c23097SOliver Hartkopp 	if (ccf->len8_dlc > CAN_MAX_DLEN && ccf->len8_dlc <= CAN_MAX_RAW_DLC)
21394c23097SOliver Hartkopp 		ccf->len = ccf->len8_dlc;
21494c23097SOliver Hartkopp }
21594c23097SOliver Hartkopp 
21694c23097SOliver Hartkopp /* convert valid CC DLC value in 'len' into struct can_frame elements */
mod_store_ccdlc(struct canfd_frame * cf)21794c23097SOliver Hartkopp static void mod_store_ccdlc(struct canfd_frame *cf)
21894c23097SOliver Hartkopp {
21994c23097SOliver Hartkopp 	struct can_frame *ccf = (struct can_frame *)cf;
22094c23097SOliver Hartkopp 
22194c23097SOliver Hartkopp 	/* clear potential leftovers */
22294c23097SOliver Hartkopp 	ccf->len8_dlc = 0;
22394c23097SOliver Hartkopp 
22494c23097SOliver Hartkopp 	/* plain data length 0 .. 8 - that was easy */
22594c23097SOliver Hartkopp 	if (ccf->len <= CAN_MAX_DLEN)
22694c23097SOliver Hartkopp 		return;
22794c23097SOliver Hartkopp 
22812da7a1fSMarc Kleine-Budde 	/* potentially broken values are caught in can_can_gw_rcv() */
22994c23097SOliver Hartkopp 	if (ccf->len > CAN_MAX_RAW_DLC)
23094c23097SOliver Hartkopp 		return;
23194c23097SOliver Hartkopp 
23294c23097SOliver Hartkopp 	/* we have a valid dlc value from 9 .. 15 in ccf->len */
23394c23097SOliver Hartkopp 	ccf->len8_dlc = ccf->len;
23494c23097SOliver Hartkopp 	ccf->len = CAN_MAX_DLEN;
23594c23097SOliver Hartkopp }
23694c23097SOliver Hartkopp 
mod_and_ccdlc(struct canfd_frame * cf,struct cf_mod * mod)23794c23097SOliver Hartkopp static void mod_and_ccdlc(struct canfd_frame *cf, struct cf_mod *mod)
23894c23097SOliver Hartkopp {
23994c23097SOliver Hartkopp 	mod_retrieve_ccdlc(cf);
24094c23097SOliver Hartkopp 	mod_and_len(cf, mod);
24194c23097SOliver Hartkopp 	mod_store_ccdlc(cf);
24294c23097SOliver Hartkopp }
24394c23097SOliver Hartkopp 
mod_or_ccdlc(struct canfd_frame * cf,struct cf_mod * mod)24494c23097SOliver Hartkopp static void mod_or_ccdlc(struct canfd_frame *cf, struct cf_mod *mod)
24594c23097SOliver Hartkopp {
24694c23097SOliver Hartkopp 	mod_retrieve_ccdlc(cf);
24794c23097SOliver Hartkopp 	mod_or_len(cf, mod);
24894c23097SOliver Hartkopp 	mod_store_ccdlc(cf);
24994c23097SOliver Hartkopp }
25094c23097SOliver Hartkopp 
mod_xor_ccdlc(struct canfd_frame * cf,struct cf_mod * mod)25194c23097SOliver Hartkopp static void mod_xor_ccdlc(struct canfd_frame *cf, struct cf_mod *mod)
25294c23097SOliver Hartkopp {
25394c23097SOliver Hartkopp 	mod_retrieve_ccdlc(cf);
25494c23097SOliver Hartkopp 	mod_xor_len(cf, mod);
25594c23097SOliver Hartkopp 	mod_store_ccdlc(cf);
25694c23097SOliver Hartkopp }
25794c23097SOliver Hartkopp 
mod_set_ccdlc(struct canfd_frame * cf,struct cf_mod * mod)25894c23097SOliver Hartkopp static void mod_set_ccdlc(struct canfd_frame *cf, struct cf_mod *mod)
25994c23097SOliver Hartkopp {
26094c23097SOliver Hartkopp 	mod_set_len(cf, mod);
26194c23097SOliver Hartkopp 	mod_store_ccdlc(cf);
26294c23097SOliver Hartkopp }
26394c23097SOliver Hartkopp 
canframecpy(struct canfd_frame * dst,struct can_frame * src)264e9dc7c60SOliver Hartkopp static void canframecpy(struct canfd_frame *dst, struct can_frame *src)
265c1aabdf3SOliver Hartkopp {
266465c0debSMarc Kleine-Budde 	/* Copy the struct members separately to ensure that no uninitialized
267c1aabdf3SOliver Hartkopp 	 * data are copied in the 3 bytes hole of the struct. This is needed
268c1aabdf3SOliver Hartkopp 	 * to make easy compares of the data in the struct cf_mod.
269c1aabdf3SOliver Hartkopp 	 */
270c1aabdf3SOliver Hartkopp 
271c1aabdf3SOliver Hartkopp 	dst->can_id = src->can_id;
272c7b74967SOliver Hartkopp 	dst->len = src->len;
273c1aabdf3SOliver Hartkopp 	*(u64 *)dst->data = *(u64 *)src->data;
274c1aabdf3SOliver Hartkopp }
275c1aabdf3SOliver Hartkopp 
canfdframecpy(struct canfd_frame * dst,struct canfd_frame * src)276456a8a64SOliver Hartkopp static void canfdframecpy(struct canfd_frame *dst, struct canfd_frame *src)
277456a8a64SOliver Hartkopp {
278456a8a64SOliver Hartkopp 	/* Copy the struct members separately to ensure that no uninitialized
279456a8a64SOliver Hartkopp 	 * data are copied in the 2 bytes hole of the struct. This is needed
280456a8a64SOliver Hartkopp 	 * to make easy compares of the data in the struct cf_mod.
281456a8a64SOliver Hartkopp 	 */
282456a8a64SOliver Hartkopp 
283456a8a64SOliver Hartkopp 	dst->can_id = src->can_id;
284456a8a64SOliver Hartkopp 	dst->flags = src->flags;
285456a8a64SOliver Hartkopp 	dst->len = src->len;
286456a8a64SOliver Hartkopp 	memcpy(dst->data, src->data, CANFD_MAX_DLEN);
287456a8a64SOliver Hartkopp }
288456a8a64SOliver Hartkopp 
cgw_chk_csum_parms(s8 fr,s8 to,s8 re,struct rtcanmsg * r)289456a8a64SOliver Hartkopp static int cgw_chk_csum_parms(s8 fr, s8 to, s8 re, struct rtcanmsg *r)
290c1aabdf3SOliver Hartkopp {
291e9dc7c60SOliver Hartkopp 	s8 dlen = CAN_MAX_DLEN;
292e9dc7c60SOliver Hartkopp 
293456a8a64SOliver Hartkopp 	if (r->flags & CGW_FLAGS_CAN_FD)
294456a8a64SOliver Hartkopp 		dlen = CANFD_MAX_DLEN;
295456a8a64SOliver Hartkopp 
296465c0debSMarc Kleine-Budde 	/* absolute dlc values 0 .. 7 => 0 .. 7, e.g. data [0]
297c1aabdf3SOliver Hartkopp 	 * relative to received dlc -1 .. -8 :
298c1aabdf3SOliver Hartkopp 	 * e.g. for received dlc = 8
299c1aabdf3SOliver Hartkopp 	 * -1 => index = 7 (data[7])
300c1aabdf3SOliver Hartkopp 	 * -3 => index = 5 (data[5])
301c1aabdf3SOliver Hartkopp 	 * -8 => index = 0 (data[0])
302c1aabdf3SOliver Hartkopp 	 */
303c1aabdf3SOliver Hartkopp 
304e9dc7c60SOliver Hartkopp 	if (fr >= -dlen && fr < dlen &&
305e9dc7c60SOliver Hartkopp 	    to >= -dlen && to < dlen &&
306e9dc7c60SOliver Hartkopp 	    re >= -dlen && re < dlen)
307c1aabdf3SOliver Hartkopp 		return 0;
308c1aabdf3SOliver Hartkopp 	else
309c1aabdf3SOliver Hartkopp 		return -EINVAL;
310c1aabdf3SOliver Hartkopp }
311c1aabdf3SOliver Hartkopp 
calc_idx(int idx,int rx_len)312e9dc7c60SOliver Hartkopp static inline int calc_idx(int idx, int rx_len)
313c1aabdf3SOliver Hartkopp {
314c1aabdf3SOliver Hartkopp 	if (idx < 0)
315e9dc7c60SOliver Hartkopp 		return rx_len + idx;
316c1aabdf3SOliver Hartkopp 	else
317c1aabdf3SOliver Hartkopp 		return idx;
318c1aabdf3SOliver Hartkopp }
319c1aabdf3SOliver Hartkopp 
cgw_csum_xor_rel(struct canfd_frame * cf,struct cgw_csum_xor * xor)320e9dc7c60SOliver Hartkopp static void cgw_csum_xor_rel(struct canfd_frame *cf, struct cgw_csum_xor *xor)
321c1aabdf3SOliver Hartkopp {
322e9dc7c60SOliver Hartkopp 	int from = calc_idx(xor->from_idx, cf->len);
323e9dc7c60SOliver Hartkopp 	int to = calc_idx(xor->to_idx, cf->len);
324e9dc7c60SOliver Hartkopp 	int res = calc_idx(xor->result_idx, cf->len);
325c1aabdf3SOliver Hartkopp 	u8 val = xor->init_xor_val;
326c1aabdf3SOliver Hartkopp 	int i;
327c1aabdf3SOliver Hartkopp 
328c1aabdf3SOliver Hartkopp 	if (from < 0 || to < 0 || res < 0)
329c1aabdf3SOliver Hartkopp 		return;
330c1aabdf3SOliver Hartkopp 
331c1aabdf3SOliver Hartkopp 	if (from <= to) {
332c1aabdf3SOliver Hartkopp 		for (i = from; i <= to; i++)
333c1aabdf3SOliver Hartkopp 			val ^= cf->data[i];
334c1aabdf3SOliver Hartkopp 	} else {
335c1aabdf3SOliver Hartkopp 		for (i = from; i >= to; i--)
336c1aabdf3SOliver Hartkopp 			val ^= cf->data[i];
337c1aabdf3SOliver Hartkopp 	}
338c1aabdf3SOliver Hartkopp 
339c1aabdf3SOliver Hartkopp 	cf->data[res] = val;
340c1aabdf3SOliver Hartkopp }
341c1aabdf3SOliver Hartkopp 
cgw_csum_xor_pos(struct canfd_frame * cf,struct cgw_csum_xor * xor)342e9dc7c60SOliver Hartkopp static void cgw_csum_xor_pos(struct canfd_frame *cf, struct cgw_csum_xor *xor)
343c1aabdf3SOliver Hartkopp {
344c1aabdf3SOliver Hartkopp 	u8 val = xor->init_xor_val;
345c1aabdf3SOliver Hartkopp 	int i;
346c1aabdf3SOliver Hartkopp 
347c1aabdf3SOliver Hartkopp 	for (i = xor->from_idx; i <= xor->to_idx; i++)
348c1aabdf3SOliver Hartkopp 		val ^= cf->data[i];
349c1aabdf3SOliver Hartkopp 
350c1aabdf3SOliver Hartkopp 	cf->data[xor->result_idx] = val;
351c1aabdf3SOliver Hartkopp }
352c1aabdf3SOliver Hartkopp 
cgw_csum_xor_neg(struct canfd_frame * cf,struct cgw_csum_xor * xor)353e9dc7c60SOliver Hartkopp static void cgw_csum_xor_neg(struct canfd_frame *cf, struct cgw_csum_xor *xor)
354c1aabdf3SOliver Hartkopp {
355c1aabdf3SOliver Hartkopp 	u8 val = xor->init_xor_val;
356c1aabdf3SOliver Hartkopp 	int i;
357c1aabdf3SOliver Hartkopp 
358c1aabdf3SOliver Hartkopp 	for (i = xor->from_idx; i >= xor->to_idx; i--)
359c1aabdf3SOliver Hartkopp 		val ^= cf->data[i];
360c1aabdf3SOliver Hartkopp 
361c1aabdf3SOliver Hartkopp 	cf->data[xor->result_idx] = val;
362c1aabdf3SOliver Hartkopp }
363c1aabdf3SOliver Hartkopp 
cgw_csum_crc8_rel(struct canfd_frame * cf,struct cgw_csum_crc8 * crc8)364e9dc7c60SOliver Hartkopp static void cgw_csum_crc8_rel(struct canfd_frame *cf,
365e9dc7c60SOliver Hartkopp 			      struct cgw_csum_crc8 *crc8)
366c1aabdf3SOliver Hartkopp {
367e9dc7c60SOliver Hartkopp 	int from = calc_idx(crc8->from_idx, cf->len);
368e9dc7c60SOliver Hartkopp 	int to = calc_idx(crc8->to_idx, cf->len);
369e9dc7c60SOliver Hartkopp 	int res = calc_idx(crc8->result_idx, cf->len);
370c1aabdf3SOliver Hartkopp 	u8 crc = crc8->init_crc_val;
371c1aabdf3SOliver Hartkopp 	int i;
372c1aabdf3SOliver Hartkopp 
373c1aabdf3SOliver Hartkopp 	if (from < 0 || to < 0 || res < 0)
374c1aabdf3SOliver Hartkopp 		return;
375c1aabdf3SOliver Hartkopp 
376c1aabdf3SOliver Hartkopp 	if (from <= to) {
377c1aabdf3SOliver Hartkopp 		for (i = crc8->from_idx; i <= crc8->to_idx; i++)
378c1aabdf3SOliver Hartkopp 			crc = crc8->crctab[crc ^ cf->data[i]];
379c1aabdf3SOliver Hartkopp 	} else {
380c1aabdf3SOliver Hartkopp 		for (i = crc8->from_idx; i >= crc8->to_idx; i--)
381c1aabdf3SOliver Hartkopp 			crc = crc8->crctab[crc ^ cf->data[i]];
382c1aabdf3SOliver Hartkopp 	}
383c1aabdf3SOliver Hartkopp 
384c1aabdf3SOliver Hartkopp 	switch (crc8->profile) {
385c1aabdf3SOliver Hartkopp 	case CGW_CRC8PRF_1U8:
386c1aabdf3SOliver Hartkopp 		crc = crc8->crctab[crc ^ crc8->profile_data[0]];
387c1aabdf3SOliver Hartkopp 		break;
388c1aabdf3SOliver Hartkopp 
389c1aabdf3SOliver Hartkopp 	case  CGW_CRC8PRF_16U8:
390c1aabdf3SOliver Hartkopp 		crc = crc8->crctab[crc ^ crc8->profile_data[cf->data[1] & 0xF]];
391c1aabdf3SOliver Hartkopp 		break;
392c1aabdf3SOliver Hartkopp 
393c1aabdf3SOliver Hartkopp 	case CGW_CRC8PRF_SFFID_XOR:
394c1aabdf3SOliver Hartkopp 		crc = crc8->crctab[crc ^ (cf->can_id & 0xFF) ^
395c1aabdf3SOliver Hartkopp 				   (cf->can_id >> 8 & 0xFF)];
396c1aabdf3SOliver Hartkopp 		break;
397c1aabdf3SOliver Hartkopp 	}
398c1aabdf3SOliver Hartkopp 
399c1aabdf3SOliver Hartkopp 	cf->data[crc8->result_idx] = crc ^ crc8->final_xor_val;
400c1aabdf3SOliver Hartkopp }
401c1aabdf3SOliver Hartkopp 
cgw_csum_crc8_pos(struct canfd_frame * cf,struct cgw_csum_crc8 * crc8)402e9dc7c60SOliver Hartkopp static void cgw_csum_crc8_pos(struct canfd_frame *cf,
403e9dc7c60SOliver Hartkopp 			      struct cgw_csum_crc8 *crc8)
404c1aabdf3SOliver Hartkopp {
405c1aabdf3SOliver Hartkopp 	u8 crc = crc8->init_crc_val;
406c1aabdf3SOliver Hartkopp 	int i;
407c1aabdf3SOliver Hartkopp 
408c1aabdf3SOliver Hartkopp 	for (i = crc8->from_idx; i <= crc8->to_idx; i++)
409c1aabdf3SOliver Hartkopp 		crc = crc8->crctab[crc ^ cf->data[i]];
410c1aabdf3SOliver Hartkopp 
411c1aabdf3SOliver Hartkopp 	switch (crc8->profile) {
412c1aabdf3SOliver Hartkopp 	case CGW_CRC8PRF_1U8:
413c1aabdf3SOliver Hartkopp 		crc = crc8->crctab[crc ^ crc8->profile_data[0]];
414c1aabdf3SOliver Hartkopp 		break;
415c1aabdf3SOliver Hartkopp 
416c1aabdf3SOliver Hartkopp 	case  CGW_CRC8PRF_16U8:
417c1aabdf3SOliver Hartkopp 		crc = crc8->crctab[crc ^ crc8->profile_data[cf->data[1] & 0xF]];
418c1aabdf3SOliver Hartkopp 		break;
419c1aabdf3SOliver Hartkopp 
420c1aabdf3SOliver Hartkopp 	case CGW_CRC8PRF_SFFID_XOR:
421c1aabdf3SOliver Hartkopp 		crc = crc8->crctab[crc ^ (cf->can_id & 0xFF) ^
422c1aabdf3SOliver Hartkopp 				   (cf->can_id >> 8 & 0xFF)];
423c1aabdf3SOliver Hartkopp 		break;
424c1aabdf3SOliver Hartkopp 	}
425c1aabdf3SOliver Hartkopp 
426c1aabdf3SOliver Hartkopp 	cf->data[crc8->result_idx] = crc ^ crc8->final_xor_val;
427c1aabdf3SOliver Hartkopp }
428c1aabdf3SOliver Hartkopp 
cgw_csum_crc8_neg(struct canfd_frame * cf,struct cgw_csum_crc8 * crc8)429e9dc7c60SOliver Hartkopp static void cgw_csum_crc8_neg(struct canfd_frame *cf,
430e9dc7c60SOliver Hartkopp 			      struct cgw_csum_crc8 *crc8)
431c1aabdf3SOliver Hartkopp {
432c1aabdf3SOliver Hartkopp 	u8 crc = crc8->init_crc_val;
433c1aabdf3SOliver Hartkopp 	int i;
434c1aabdf3SOliver Hartkopp 
435c1aabdf3SOliver Hartkopp 	for (i = crc8->from_idx; i >= crc8->to_idx; i--)
436c1aabdf3SOliver Hartkopp 		crc = crc8->crctab[crc ^ cf->data[i]];
437c1aabdf3SOliver Hartkopp 
438c1aabdf3SOliver Hartkopp 	switch (crc8->profile) {
439c1aabdf3SOliver Hartkopp 	case CGW_CRC8PRF_1U8:
440c1aabdf3SOliver Hartkopp 		crc = crc8->crctab[crc ^ crc8->profile_data[0]];
441c1aabdf3SOliver Hartkopp 		break;
442c1aabdf3SOliver Hartkopp 
443c1aabdf3SOliver Hartkopp 	case  CGW_CRC8PRF_16U8:
444c1aabdf3SOliver Hartkopp 		crc = crc8->crctab[crc ^ crc8->profile_data[cf->data[1] & 0xF]];
445c1aabdf3SOliver Hartkopp 		break;
446c1aabdf3SOliver Hartkopp 
447c1aabdf3SOliver Hartkopp 	case CGW_CRC8PRF_SFFID_XOR:
448c1aabdf3SOliver Hartkopp 		crc = crc8->crctab[crc ^ (cf->can_id & 0xFF) ^
449c1aabdf3SOliver Hartkopp 				   (cf->can_id >> 8 & 0xFF)];
450c1aabdf3SOliver Hartkopp 		break;
451c1aabdf3SOliver Hartkopp 	}
452c1aabdf3SOliver Hartkopp 
453c1aabdf3SOliver Hartkopp 	cf->data[crc8->result_idx] = crc ^ crc8->final_xor_val;
454c1aabdf3SOliver Hartkopp }
455c1aabdf3SOliver Hartkopp 
456c1aabdf3SOliver Hartkopp /* the receive & process & send function */
can_can_gw_rcv(struct sk_buff * skb,void * data)457c1aabdf3SOliver Hartkopp static void can_can_gw_rcv(struct sk_buff *skb, void *data)
458c1aabdf3SOliver Hartkopp {
459c1aabdf3SOliver Hartkopp 	struct cgw_job *gwj = (struct cgw_job *)data;
460e9dc7c60SOliver Hartkopp 	struct canfd_frame *cf;
461c1aabdf3SOliver Hartkopp 	struct sk_buff *nskb;
462c1aabdf3SOliver Hartkopp 	int modidx = 0;
463c1aabdf3SOliver Hartkopp 
464456a8a64SOliver Hartkopp 	/* process strictly Classic CAN or CAN FD frames */
465456a8a64SOliver Hartkopp 	if (gwj->flags & CGW_FLAGS_CAN_FD) {
46696a7457aSOliver Hartkopp 		if (!can_is_canfd_skb(skb))
467456a8a64SOliver Hartkopp 			return;
468456a8a64SOliver Hartkopp 	} else {
46996a7457aSOliver Hartkopp 		if (!can_is_can_skb(skb))
470456a8a64SOliver Hartkopp 			return;
471456a8a64SOliver Hartkopp 	}
472456a8a64SOliver Hartkopp 
473465c0debSMarc Kleine-Budde 	/* Do not handle CAN frames routed more than 'max_hops' times.
474be286bafSOliver Hartkopp 	 * In general we should never catch this delimiter which is intended
475be286bafSOliver Hartkopp 	 * to cover a misconfiguration protection (e.g. circular CAN routes).
476be286bafSOliver Hartkopp 	 *
477be286bafSOliver Hartkopp 	 * The Controller Area Network controllers only accept CAN frames with
478be286bafSOliver Hartkopp 	 * correct CRCs - which are not visible in the controller registers.
479be286bafSOliver Hartkopp 	 * According to skbuff.h documentation the csum_start element for IP
480069f8457SJeremiah Mahler 	 * checksums is undefined/unused when ip_summed == CHECKSUM_UNNECESSARY.
481be286bafSOliver Hartkopp 	 * Only CAN skbs can be processed here which already have this property.
482be286bafSOliver Hartkopp 	 */
483be286bafSOliver Hartkopp 
484be286bafSOliver Hartkopp #define cgw_hops(skb) ((skb)->csum_start)
485be286bafSOliver Hartkopp 
486be286bafSOliver Hartkopp 	BUG_ON(skb->ip_summed != CHECKSUM_UNNECESSARY);
487be286bafSOliver Hartkopp 
488e6afa00aSOliver Hartkopp 	if (cgw_hops(skb) >= max_hops) {
489e6afa00aSOliver Hartkopp 		/* indicate deleted frames due to misconfiguration */
490e6afa00aSOliver Hartkopp 		gwj->deleted_frames++;
491c1aabdf3SOliver Hartkopp 		return;
492e6afa00aSOliver Hartkopp 	}
493c1aabdf3SOliver Hartkopp 
494c1aabdf3SOliver Hartkopp 	if (!(gwj->dst.dev->flags & IFF_UP)) {
495c1aabdf3SOliver Hartkopp 		gwj->dropped_frames++;
496c1aabdf3SOliver Hartkopp 		return;
497c1aabdf3SOliver Hartkopp 	}
498c1aabdf3SOliver Hartkopp 
499d904d3edSOliver Hartkopp 	/* is sending the skb back to the incoming interface not allowed? */
500d904d3edSOliver Hartkopp 	if (!(gwj->flags & CGW_FLAGS_CAN_IIF_TX_OK) &&
5012bf3440dSOliver Hartkopp 	    can_skb_prv(skb)->ifindex == gwj->dst.dev->ifindex)
502d904d3edSOliver Hartkopp 		return;
503d904d3edSOliver Hartkopp 
504465c0debSMarc Kleine-Budde 	/* clone the given skb, which has not been done in can_rcv()
505c1aabdf3SOliver Hartkopp 	 *
506c1aabdf3SOliver Hartkopp 	 * When there is at least one modification function activated,
507c1aabdf3SOliver Hartkopp 	 * we need to copy the skb as we want to modify skb->data.
508c1aabdf3SOliver Hartkopp 	 */
509c1aabdf3SOliver Hartkopp 	if (gwj->mod.modfunc[0])
510c1aabdf3SOliver Hartkopp 		nskb = skb_copy(skb, GFP_ATOMIC);
511c1aabdf3SOliver Hartkopp 	else
512c1aabdf3SOliver Hartkopp 		nskb = skb_clone(skb, GFP_ATOMIC);
513c1aabdf3SOliver Hartkopp 
514c1aabdf3SOliver Hartkopp 	if (!nskb) {
515c1aabdf3SOliver Hartkopp 		gwj->dropped_frames++;
516c1aabdf3SOliver Hartkopp 		return;
517c1aabdf3SOliver Hartkopp 	}
518c1aabdf3SOliver Hartkopp 
519be286bafSOliver Hartkopp 	/* put the incremented hop counter in the cloned skb */
520be286bafSOliver Hartkopp 	cgw_hops(nskb) = cgw_hops(skb) + 1;
521391ac128SOliver Hartkopp 
522391ac128SOliver Hartkopp 	/* first processing of this CAN frame -> adjust to private hop limit */
523391ac128SOliver Hartkopp 	if (gwj->limit_hops && cgw_hops(nskb) == 1)
524391ac128SOliver Hartkopp 		cgw_hops(nskb) = max_hops - gwj->limit_hops + 1;
525391ac128SOliver Hartkopp 
526c1aabdf3SOliver Hartkopp 	nskb->dev = gwj->dst.dev;
527c1aabdf3SOliver Hartkopp 
528c1aabdf3SOliver Hartkopp 	/* pointer to modifiable CAN frame */
529e9dc7c60SOliver Hartkopp 	cf = (struct canfd_frame *)nskb->data;
530c1aabdf3SOliver Hartkopp 
531c1aabdf3SOliver Hartkopp 	/* perform preprocessed modification functions if there are any */
532c1aabdf3SOliver Hartkopp 	while (modidx < MAX_MODFUNCTIONS && gwj->mod.modfunc[modidx])
533c1aabdf3SOliver Hartkopp 		(*gwj->mod.modfunc[modidx++])(cf, &gwj->mod);
534c1aabdf3SOliver Hartkopp 
5350aaa8137SOliver Hartkopp 	/* Has the CAN frame been modified? */
536c1aabdf3SOliver Hartkopp 	if (modidx) {
5370aaa8137SOliver Hartkopp 		/* get available space for the processed CAN frame type */
538e9dc7c60SOliver Hartkopp 		int max_len = nskb->len - offsetof(struct canfd_frame, data);
539c1aabdf3SOliver Hartkopp 
5400aaa8137SOliver Hartkopp 		/* dlc may have changed, make sure it fits to the CAN frame */
541456a8a64SOliver Hartkopp 		if (cf->len > max_len) {
542456a8a64SOliver Hartkopp 			/* delete frame due to misconfiguration */
543456a8a64SOliver Hartkopp 			gwj->deleted_frames++;
544456a8a64SOliver Hartkopp 			kfree_skb(nskb);
545456a8a64SOliver Hartkopp 			return;
546456a8a64SOliver Hartkopp 		}
5470aaa8137SOliver Hartkopp 
548456a8a64SOliver Hartkopp 		/* check for checksum updates */
549456a8a64SOliver Hartkopp 		if (gwj->mod.csumfunc.crc8)
5500aaa8137SOliver Hartkopp 			(*gwj->mod.csumfunc.crc8)(cf, &gwj->mod.csum.crc8);
5510aaa8137SOliver Hartkopp 
552456a8a64SOliver Hartkopp 		if (gwj->mod.csumfunc.xor)
553c1aabdf3SOliver Hartkopp 			(*gwj->mod.csumfunc.xor)(cf, &gwj->mod.csum.xor);
554c1aabdf3SOliver Hartkopp 	}
555c1aabdf3SOliver Hartkopp 
556c1aabdf3SOliver Hartkopp 	/* clear the skb timestamp if not configured the other way */
557c1aabdf3SOliver Hartkopp 	if (!(gwj->flags & CGW_FLAGS_CAN_SRC_TSTAMP))
5582456e855SThomas Gleixner 		nskb->tstamp = 0;
559c1aabdf3SOliver Hartkopp 
560c1aabdf3SOliver Hartkopp 	/* send to netdevice */
561c1aabdf3SOliver Hartkopp 	if (can_send(nskb, gwj->flags & CGW_FLAGS_CAN_ECHO))
562c1aabdf3SOliver Hartkopp 		gwj->dropped_frames++;
563c1aabdf3SOliver Hartkopp 	else
564c1aabdf3SOliver Hartkopp 		gwj->handled_frames++;
565c1aabdf3SOliver Hartkopp }
566c1aabdf3SOliver Hartkopp 
cgw_register_filter(struct net * net,struct cgw_job * gwj)5671ef83310SOliver Hartkopp static inline int cgw_register_filter(struct net *net, struct cgw_job *gwj)
568c1aabdf3SOliver Hartkopp {
5691ef83310SOliver Hartkopp 	return can_rx_register(net, gwj->src.dev, gwj->ccgw.filter.can_id,
570c1aabdf3SOliver Hartkopp 			       gwj->ccgw.filter.can_mask, can_can_gw_rcv,
571f1712c73SEric Dumazet 			       gwj, "gw", NULL);
572c1aabdf3SOliver Hartkopp }
573c1aabdf3SOliver Hartkopp 
cgw_unregister_filter(struct net * net,struct cgw_job * gwj)5741ef83310SOliver Hartkopp static inline void cgw_unregister_filter(struct net *net, struct cgw_job *gwj)
575c1aabdf3SOliver Hartkopp {
5761ef83310SOliver Hartkopp 	can_rx_unregister(net, gwj->src.dev, gwj->ccgw.filter.can_id,
577c1aabdf3SOliver Hartkopp 			  gwj->ccgw.filter.can_mask, can_can_gw_rcv, gwj);
578c1aabdf3SOliver Hartkopp }
579c1aabdf3SOliver Hartkopp 
cgw_job_free_rcu(struct rcu_head * rcu_head)580181d4447SEric Dumazet static void cgw_job_free_rcu(struct rcu_head *rcu_head)
581181d4447SEric Dumazet {
582181d4447SEric Dumazet 	struct cgw_job *gwj = container_of(rcu_head, struct cgw_job, rcu);
583181d4447SEric Dumazet 
584181d4447SEric Dumazet 	kmem_cache_free(cgw_cache, gwj);
585181d4447SEric Dumazet }
586181d4447SEric Dumazet 
cgw_notifier(struct notifier_block * nb,unsigned long msg,void * ptr)587c1aabdf3SOliver Hartkopp static int cgw_notifier(struct notifier_block *nb,
588351638e7SJiri Pirko 			unsigned long msg, void *ptr)
589c1aabdf3SOliver Hartkopp {
590351638e7SJiri Pirko 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
5911ef83310SOliver Hartkopp 	struct net *net = dev_net(dev);
592c1aabdf3SOliver Hartkopp 
593c1aabdf3SOliver Hartkopp 	if (dev->type != ARPHRD_CAN)
594c1aabdf3SOliver Hartkopp 		return NOTIFY_DONE;
595c1aabdf3SOliver Hartkopp 
596c1aabdf3SOliver Hartkopp 	if (msg == NETDEV_UNREGISTER) {
597c1aabdf3SOliver Hartkopp 		struct cgw_job *gwj = NULL;
598b67bfe0dSSasha Levin 		struct hlist_node *nx;
599c1aabdf3SOliver Hartkopp 
600c1aabdf3SOliver Hartkopp 		ASSERT_RTNL();
601c1aabdf3SOliver Hartkopp 
6021ef83310SOliver Hartkopp 		hlist_for_each_entry_safe(gwj, nx, &net->can.cgw_list, list) {
603c1aabdf3SOliver Hartkopp 			if (gwj->src.dev == dev || gwj->dst.dev == dev) {
604c1aabdf3SOliver Hartkopp 				hlist_del(&gwj->list);
6051ef83310SOliver Hartkopp 				cgw_unregister_filter(net, gwj);
606181d4447SEric Dumazet 				call_rcu(&gwj->rcu, cgw_job_free_rcu);
607c1aabdf3SOliver Hartkopp 			}
608c1aabdf3SOliver Hartkopp 		}
609c1aabdf3SOliver Hartkopp 	}
610c1aabdf3SOliver Hartkopp 
611c1aabdf3SOliver Hartkopp 	return NOTIFY_DONE;
612c1aabdf3SOliver Hartkopp }
613c1aabdf3SOliver Hartkopp 
cgw_put_job(struct sk_buff * skb,struct cgw_job * gwj,int type,u32 pid,u32 seq,int flags)6141da0faa3SThomas Graf static int cgw_put_job(struct sk_buff *skb, struct cgw_job *gwj, int type,
6151da0faa3SThomas Graf 		       u32 pid, u32 seq, int flags)
616c1aabdf3SOliver Hartkopp {
617c1aabdf3SOliver Hartkopp 	struct rtcanmsg *rtcan;
6181da0faa3SThomas Graf 	struct nlmsghdr *nlh;
6191da0faa3SThomas Graf 
6201da0faa3SThomas Graf 	nlh = nlmsg_put(skb, pid, seq, type, sizeof(*rtcan), flags);
621c1aabdf3SOliver Hartkopp 	if (!nlh)
622c1aabdf3SOliver Hartkopp 		return -EMSGSIZE;
623c1aabdf3SOliver Hartkopp 
624c1aabdf3SOliver Hartkopp 	rtcan = nlmsg_data(nlh);
625c1aabdf3SOliver Hartkopp 	rtcan->can_family = AF_CAN;
626c1aabdf3SOliver Hartkopp 	rtcan->gwtype = gwj->gwtype;
627c1aabdf3SOliver Hartkopp 	rtcan->flags = gwj->flags;
628c1aabdf3SOliver Hartkopp 
629c1aabdf3SOliver Hartkopp 	/* add statistics if available */
630c1aabdf3SOliver Hartkopp 
631c1aabdf3SOliver Hartkopp 	if (gwj->handled_frames) {
632c1aabdf3SOliver Hartkopp 		if (nla_put_u32(skb, CGW_HANDLED, gwj->handled_frames) < 0)
633c1aabdf3SOliver Hartkopp 			goto cancel;
634c1aabdf3SOliver Hartkopp 	}
635c1aabdf3SOliver Hartkopp 
636c1aabdf3SOliver Hartkopp 	if (gwj->dropped_frames) {
637c1aabdf3SOliver Hartkopp 		if (nla_put_u32(skb, CGW_DROPPED, gwj->dropped_frames) < 0)
638c1aabdf3SOliver Hartkopp 			goto cancel;
639c1aabdf3SOliver Hartkopp 	}
640c1aabdf3SOliver Hartkopp 
641e6afa00aSOliver Hartkopp 	if (gwj->deleted_frames) {
642e6afa00aSOliver Hartkopp 		if (nla_put_u32(skb, CGW_DELETED, gwj->deleted_frames) < 0)
643e6afa00aSOliver Hartkopp 			goto cancel;
644e6afa00aSOliver Hartkopp 	}
645e6afa00aSOliver Hartkopp 
646c1aabdf3SOliver Hartkopp 	/* check non default settings of attributes */
647c1aabdf3SOliver Hartkopp 
648391ac128SOliver Hartkopp 	if (gwj->limit_hops) {
649391ac128SOliver Hartkopp 		if (nla_put_u8(skb, CGW_LIM_HOPS, gwj->limit_hops) < 0)
650391ac128SOliver Hartkopp 			goto cancel;
651391ac128SOliver Hartkopp 	}
652391ac128SOliver Hartkopp 
653456a8a64SOliver Hartkopp 	if (gwj->flags & CGW_FLAGS_CAN_FD) {
654456a8a64SOliver Hartkopp 		struct cgw_fdframe_mod mb;
655456a8a64SOliver Hartkopp 
656456a8a64SOliver Hartkopp 		if (gwj->mod.modtype.and) {
657456a8a64SOliver Hartkopp 			memcpy(&mb.cf, &gwj->mod.modframe.and, sizeof(mb.cf));
658456a8a64SOliver Hartkopp 			mb.modtype = gwj->mod.modtype.and;
659456a8a64SOliver Hartkopp 			if (nla_put(skb, CGW_FDMOD_AND, sizeof(mb), &mb) < 0)
660456a8a64SOliver Hartkopp 				goto cancel;
661456a8a64SOliver Hartkopp 		}
662456a8a64SOliver Hartkopp 
663456a8a64SOliver Hartkopp 		if (gwj->mod.modtype.or) {
664456a8a64SOliver Hartkopp 			memcpy(&mb.cf, &gwj->mod.modframe.or, sizeof(mb.cf));
665456a8a64SOliver Hartkopp 			mb.modtype = gwj->mod.modtype.or;
666456a8a64SOliver Hartkopp 			if (nla_put(skb, CGW_FDMOD_OR, sizeof(mb), &mb) < 0)
667456a8a64SOliver Hartkopp 				goto cancel;
668456a8a64SOliver Hartkopp 		}
669456a8a64SOliver Hartkopp 
670456a8a64SOliver Hartkopp 		if (gwj->mod.modtype.xor) {
671456a8a64SOliver Hartkopp 			memcpy(&mb.cf, &gwj->mod.modframe.xor, sizeof(mb.cf));
672456a8a64SOliver Hartkopp 			mb.modtype = gwj->mod.modtype.xor;
673456a8a64SOliver Hartkopp 			if (nla_put(skb, CGW_FDMOD_XOR, sizeof(mb), &mb) < 0)
674456a8a64SOliver Hartkopp 				goto cancel;
675456a8a64SOliver Hartkopp 		}
676456a8a64SOliver Hartkopp 
677456a8a64SOliver Hartkopp 		if (gwj->mod.modtype.set) {
678456a8a64SOliver Hartkopp 			memcpy(&mb.cf, &gwj->mod.modframe.set, sizeof(mb.cf));
679456a8a64SOliver Hartkopp 			mb.modtype = gwj->mod.modtype.set;
680456a8a64SOliver Hartkopp 			if (nla_put(skb, CGW_FDMOD_SET, sizeof(mb), &mb) < 0)
681456a8a64SOliver Hartkopp 				goto cancel;
682456a8a64SOliver Hartkopp 		}
683456a8a64SOliver Hartkopp 	} else {
684e9dc7c60SOliver Hartkopp 		struct cgw_frame_mod mb;
685e9dc7c60SOliver Hartkopp 
686c1aabdf3SOliver Hartkopp 		if (gwj->mod.modtype.and) {
687c1aabdf3SOliver Hartkopp 			memcpy(&mb.cf, &gwj->mod.modframe.and, sizeof(mb.cf));
688c1aabdf3SOliver Hartkopp 			mb.modtype = gwj->mod.modtype.and;
689c1aabdf3SOliver Hartkopp 			if (nla_put(skb, CGW_MOD_AND, sizeof(mb), &mb) < 0)
690c1aabdf3SOliver Hartkopp 				goto cancel;
691c1aabdf3SOliver Hartkopp 		}
692c1aabdf3SOliver Hartkopp 
693c1aabdf3SOliver Hartkopp 		if (gwj->mod.modtype.or) {
694c1aabdf3SOliver Hartkopp 			memcpy(&mb.cf, &gwj->mod.modframe.or, sizeof(mb.cf));
695c1aabdf3SOliver Hartkopp 			mb.modtype = gwj->mod.modtype.or;
696c1aabdf3SOliver Hartkopp 			if (nla_put(skb, CGW_MOD_OR, sizeof(mb), &mb) < 0)
697c1aabdf3SOliver Hartkopp 				goto cancel;
698c1aabdf3SOliver Hartkopp 		}
699c1aabdf3SOliver Hartkopp 
700c1aabdf3SOliver Hartkopp 		if (gwj->mod.modtype.xor) {
701c1aabdf3SOliver Hartkopp 			memcpy(&mb.cf, &gwj->mod.modframe.xor, sizeof(mb.cf));
702c1aabdf3SOliver Hartkopp 			mb.modtype = gwj->mod.modtype.xor;
703c1aabdf3SOliver Hartkopp 			if (nla_put(skb, CGW_MOD_XOR, sizeof(mb), &mb) < 0)
704c1aabdf3SOliver Hartkopp 				goto cancel;
705c1aabdf3SOliver Hartkopp 		}
706c1aabdf3SOliver Hartkopp 
707c1aabdf3SOliver Hartkopp 		if (gwj->mod.modtype.set) {
708c1aabdf3SOliver Hartkopp 			memcpy(&mb.cf, &gwj->mod.modframe.set, sizeof(mb.cf));
709c1aabdf3SOliver Hartkopp 			mb.modtype = gwj->mod.modtype.set;
710c1aabdf3SOliver Hartkopp 			if (nla_put(skb, CGW_MOD_SET, sizeof(mb), &mb) < 0)
711c1aabdf3SOliver Hartkopp 				goto cancel;
712c1aabdf3SOliver Hartkopp 		}
713e9dc7c60SOliver Hartkopp 	}
714c1aabdf3SOliver Hartkopp 
715dd895d7fSOliver Hartkopp 	if (gwj->mod.uid) {
716dd895d7fSOliver Hartkopp 		if (nla_put_u32(skb, CGW_MOD_UID, gwj->mod.uid) < 0)
717dd895d7fSOliver Hartkopp 			goto cancel;
718dd895d7fSOliver Hartkopp 	}
719dd895d7fSOliver Hartkopp 
720c1aabdf3SOliver Hartkopp 	if (gwj->mod.csumfunc.crc8) {
721c1aabdf3SOliver Hartkopp 		if (nla_put(skb, CGW_CS_CRC8, CGW_CS_CRC8_LEN,
722c1aabdf3SOliver Hartkopp 			    &gwj->mod.csum.crc8) < 0)
723c1aabdf3SOliver Hartkopp 			goto cancel;
724c1aabdf3SOliver Hartkopp 	}
725c1aabdf3SOliver Hartkopp 
726c1aabdf3SOliver Hartkopp 	if (gwj->mod.csumfunc.xor) {
727c1aabdf3SOliver Hartkopp 		if (nla_put(skb, CGW_CS_XOR, CGW_CS_XOR_LEN,
728c1aabdf3SOliver Hartkopp 			    &gwj->mod.csum.xor) < 0)
729c1aabdf3SOliver Hartkopp 			goto cancel;
730c1aabdf3SOliver Hartkopp 	}
731c1aabdf3SOliver Hartkopp 
732c1aabdf3SOliver Hartkopp 	if (gwj->gwtype == CGW_TYPE_CAN_CAN) {
733c1aabdf3SOliver Hartkopp 		if (gwj->ccgw.filter.can_id || gwj->ccgw.filter.can_mask) {
734c1aabdf3SOliver Hartkopp 			if (nla_put(skb, CGW_FILTER, sizeof(struct can_filter),
735c1aabdf3SOliver Hartkopp 				    &gwj->ccgw.filter) < 0)
736c1aabdf3SOliver Hartkopp 				goto cancel;
737c1aabdf3SOliver Hartkopp 		}
738c1aabdf3SOliver Hartkopp 
739c1aabdf3SOliver Hartkopp 		if (nla_put_u32(skb, CGW_SRC_IF, gwj->ccgw.src_idx) < 0)
740c1aabdf3SOliver Hartkopp 			goto cancel;
741c1aabdf3SOliver Hartkopp 
742c1aabdf3SOliver Hartkopp 		if (nla_put_u32(skb, CGW_DST_IF, gwj->ccgw.dst_idx) < 0)
743c1aabdf3SOliver Hartkopp 			goto cancel;
744c1aabdf3SOliver Hartkopp 	}
745c1aabdf3SOliver Hartkopp 
746053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
747053c095aSJohannes Berg 	return 0;
748c1aabdf3SOliver Hartkopp 
749c1aabdf3SOliver Hartkopp cancel:
750c1aabdf3SOliver Hartkopp 	nlmsg_cancel(skb, nlh);
751c1aabdf3SOliver Hartkopp 	return -EMSGSIZE;
752c1aabdf3SOliver Hartkopp }
753c1aabdf3SOliver Hartkopp 
754c1aabdf3SOliver Hartkopp /* Dump information about all CAN gateway jobs, in response to RTM_GETROUTE */
cgw_dump_jobs(struct sk_buff * skb,struct netlink_callback * cb)755c1aabdf3SOliver Hartkopp static int cgw_dump_jobs(struct sk_buff *skb, struct netlink_callback *cb)
756c1aabdf3SOliver Hartkopp {
7571ef83310SOliver Hartkopp 	struct net *net = sock_net(skb->sk);
758c1aabdf3SOliver Hartkopp 	struct cgw_job *gwj = NULL;
759c1aabdf3SOliver Hartkopp 	int idx = 0;
760c1aabdf3SOliver Hartkopp 	int s_idx = cb->args[0];
761c1aabdf3SOliver Hartkopp 
762c1aabdf3SOliver Hartkopp 	rcu_read_lock();
7631ef83310SOliver Hartkopp 	hlist_for_each_entry_rcu(gwj, &net->can.cgw_list, list) {
764c1aabdf3SOliver Hartkopp 		if (idx < s_idx)
765c1aabdf3SOliver Hartkopp 			goto cont;
766c1aabdf3SOliver Hartkopp 
76778f8a326SMarc Kleine-Budde 		if (cgw_put_job(skb, gwj, RTM_NEWROUTE,
76878f8a326SMarc Kleine-Budde 				NETLINK_CB(cb->skb).portid,
7691da0faa3SThomas Graf 				cb->nlh->nlmsg_seq, NLM_F_MULTI) < 0)
770c1aabdf3SOliver Hartkopp 			break;
771c1aabdf3SOliver Hartkopp cont:
772c1aabdf3SOliver Hartkopp 		idx++;
773c1aabdf3SOliver Hartkopp 	}
774c1aabdf3SOliver Hartkopp 	rcu_read_unlock();
775c1aabdf3SOliver Hartkopp 
776c1aabdf3SOliver Hartkopp 	cb->args[0] = idx;
777c1aabdf3SOliver Hartkopp 
778c1aabdf3SOliver Hartkopp 	return skb->len;
779c1aabdf3SOliver Hartkopp }
780c1aabdf3SOliver Hartkopp 
781732d35fdSThomas Graf static const struct nla_policy cgw_policy[CGW_MAX + 1] = {
782732d35fdSThomas Graf 	[CGW_MOD_AND]	= { .len = sizeof(struct cgw_frame_mod) },
783732d35fdSThomas Graf 	[CGW_MOD_OR]	= { .len = sizeof(struct cgw_frame_mod) },
784732d35fdSThomas Graf 	[CGW_MOD_XOR]	= { .len = sizeof(struct cgw_frame_mod) },
785732d35fdSThomas Graf 	[CGW_MOD_SET]	= { .len = sizeof(struct cgw_frame_mod) },
786732d35fdSThomas Graf 	[CGW_CS_XOR]	= { .len = sizeof(struct cgw_csum_xor) },
787732d35fdSThomas Graf 	[CGW_CS_CRC8]	= { .len = sizeof(struct cgw_csum_crc8) },
788732d35fdSThomas Graf 	[CGW_SRC_IF]	= { .type = NLA_U32 },
789732d35fdSThomas Graf 	[CGW_DST_IF]	= { .type = NLA_U32 },
790732d35fdSThomas Graf 	[CGW_FILTER]	= { .len = sizeof(struct can_filter) },
791391ac128SOliver Hartkopp 	[CGW_LIM_HOPS]	= { .type = NLA_U8 },
792dd895d7fSOliver Hartkopp 	[CGW_MOD_UID]	= { .type = NLA_U32 },
793456a8a64SOliver Hartkopp 	[CGW_FDMOD_AND]	= { .len = sizeof(struct cgw_fdframe_mod) },
794456a8a64SOliver Hartkopp 	[CGW_FDMOD_OR]	= { .len = sizeof(struct cgw_fdframe_mod) },
795456a8a64SOliver Hartkopp 	[CGW_FDMOD_XOR]	= { .len = sizeof(struct cgw_fdframe_mod) },
796456a8a64SOliver Hartkopp 	[CGW_FDMOD_SET]	= { .len = sizeof(struct cgw_fdframe_mod) },
797732d35fdSThomas Graf };
798732d35fdSThomas Graf 
799c1aabdf3SOliver Hartkopp /* check for common and gwtype specific attributes */
cgw_parse_attr(struct nlmsghdr * nlh,struct cf_mod * mod,u8 gwtype,void * gwtypeattr,u8 * limhops)800c1aabdf3SOliver Hartkopp static int cgw_parse_attr(struct nlmsghdr *nlh, struct cf_mod *mod,
801391ac128SOliver Hartkopp 			  u8 gwtype, void *gwtypeattr, u8 *limhops)
802c1aabdf3SOliver Hartkopp {
803c1aabdf3SOliver Hartkopp 	struct nlattr *tb[CGW_MAX + 1];
804456a8a64SOliver Hartkopp 	struct rtcanmsg *r = nlmsg_data(nlh);
805c1aabdf3SOliver Hartkopp 	int modidx = 0;
806c1aabdf3SOliver Hartkopp 	int err = 0;
807c1aabdf3SOliver Hartkopp 
808c1aabdf3SOliver Hartkopp 	/* initialize modification & checksum data space */
809c1aabdf3SOliver Hartkopp 	memset(mod, 0, sizeof(*mod));
810c1aabdf3SOliver Hartkopp 
8118cb08174SJohannes Berg 	err = nlmsg_parse_deprecated(nlh, sizeof(struct rtcanmsg), tb,
8128cb08174SJohannes Berg 				     CGW_MAX, cgw_policy, NULL);
813c1aabdf3SOliver Hartkopp 	if (err < 0)
814c1aabdf3SOliver Hartkopp 		return err;
815c1aabdf3SOliver Hartkopp 
816391ac128SOliver Hartkopp 	if (tb[CGW_LIM_HOPS]) {
817391ac128SOliver Hartkopp 		*limhops = nla_get_u8(tb[CGW_LIM_HOPS]);
818391ac128SOliver Hartkopp 
819391ac128SOliver Hartkopp 		if (*limhops < 1 || *limhops > max_hops)
820391ac128SOliver Hartkopp 			return -EINVAL;
821391ac128SOliver Hartkopp 	}
822391ac128SOliver Hartkopp 
823c1aabdf3SOliver Hartkopp 	/* check for AND/OR/XOR/SET modifications */
824456a8a64SOliver Hartkopp 	if (r->flags & CGW_FLAGS_CAN_FD) {
825456a8a64SOliver Hartkopp 		struct cgw_fdframe_mod mb;
826456a8a64SOliver Hartkopp 
827456a8a64SOliver Hartkopp 		if (tb[CGW_FDMOD_AND]) {
828456a8a64SOliver Hartkopp 			nla_memcpy(&mb, tb[CGW_FDMOD_AND], CGW_FDMODATTR_LEN);
829456a8a64SOliver Hartkopp 
830456a8a64SOliver Hartkopp 			canfdframecpy(&mod->modframe.and, &mb.cf);
831456a8a64SOliver Hartkopp 			mod->modtype.and = mb.modtype;
832456a8a64SOliver Hartkopp 
833456a8a64SOliver Hartkopp 			if (mb.modtype & CGW_MOD_ID)
834456a8a64SOliver Hartkopp 				mod->modfunc[modidx++] = mod_and_id;
835456a8a64SOliver Hartkopp 
836456a8a64SOliver Hartkopp 			if (mb.modtype & CGW_MOD_LEN)
837456a8a64SOliver Hartkopp 				mod->modfunc[modidx++] = mod_and_len;
838456a8a64SOliver Hartkopp 
839456a8a64SOliver Hartkopp 			if (mb.modtype & CGW_MOD_FLAGS)
840456a8a64SOliver Hartkopp 				mod->modfunc[modidx++] = mod_and_flags;
841456a8a64SOliver Hartkopp 
842456a8a64SOliver Hartkopp 			if (mb.modtype & CGW_MOD_DATA)
843456a8a64SOliver Hartkopp 				mod->modfunc[modidx++] = mod_and_fddata;
844456a8a64SOliver Hartkopp 		}
845456a8a64SOliver Hartkopp 
846456a8a64SOliver Hartkopp 		if (tb[CGW_FDMOD_OR]) {
847456a8a64SOliver Hartkopp 			nla_memcpy(&mb, tb[CGW_FDMOD_OR], CGW_FDMODATTR_LEN);
848456a8a64SOliver Hartkopp 
849456a8a64SOliver Hartkopp 			canfdframecpy(&mod->modframe.or, &mb.cf);
850456a8a64SOliver Hartkopp 			mod->modtype.or = mb.modtype;
851456a8a64SOliver Hartkopp 
852456a8a64SOliver Hartkopp 			if (mb.modtype & CGW_MOD_ID)
853456a8a64SOliver Hartkopp 				mod->modfunc[modidx++] = mod_or_id;
854456a8a64SOliver Hartkopp 
855456a8a64SOliver Hartkopp 			if (mb.modtype & CGW_MOD_LEN)
856456a8a64SOliver Hartkopp 				mod->modfunc[modidx++] = mod_or_len;
857456a8a64SOliver Hartkopp 
858456a8a64SOliver Hartkopp 			if (mb.modtype & CGW_MOD_FLAGS)
859456a8a64SOliver Hartkopp 				mod->modfunc[modidx++] = mod_or_flags;
860456a8a64SOliver Hartkopp 
861456a8a64SOliver Hartkopp 			if (mb.modtype & CGW_MOD_DATA)
862456a8a64SOliver Hartkopp 				mod->modfunc[modidx++] = mod_or_fddata;
863456a8a64SOliver Hartkopp 		}
864456a8a64SOliver Hartkopp 
865456a8a64SOliver Hartkopp 		if (tb[CGW_FDMOD_XOR]) {
866456a8a64SOliver Hartkopp 			nla_memcpy(&mb, tb[CGW_FDMOD_XOR], CGW_FDMODATTR_LEN);
867456a8a64SOliver Hartkopp 
868456a8a64SOliver Hartkopp 			canfdframecpy(&mod->modframe.xor, &mb.cf);
869456a8a64SOliver Hartkopp 			mod->modtype.xor = mb.modtype;
870456a8a64SOliver Hartkopp 
871456a8a64SOliver Hartkopp 			if (mb.modtype & CGW_MOD_ID)
872456a8a64SOliver Hartkopp 				mod->modfunc[modidx++] = mod_xor_id;
873456a8a64SOliver Hartkopp 
874456a8a64SOliver Hartkopp 			if (mb.modtype & CGW_MOD_LEN)
875456a8a64SOliver Hartkopp 				mod->modfunc[modidx++] = mod_xor_len;
876456a8a64SOliver Hartkopp 
877456a8a64SOliver Hartkopp 			if (mb.modtype & CGW_MOD_FLAGS)
878456a8a64SOliver Hartkopp 				mod->modfunc[modidx++] = mod_xor_flags;
879456a8a64SOliver Hartkopp 
880456a8a64SOliver Hartkopp 			if (mb.modtype & CGW_MOD_DATA)
881456a8a64SOliver Hartkopp 				mod->modfunc[modidx++] = mod_xor_fddata;
882456a8a64SOliver Hartkopp 		}
883456a8a64SOliver Hartkopp 
884456a8a64SOliver Hartkopp 		if (tb[CGW_FDMOD_SET]) {
885456a8a64SOliver Hartkopp 			nla_memcpy(&mb, tb[CGW_FDMOD_SET], CGW_FDMODATTR_LEN);
886456a8a64SOliver Hartkopp 
887456a8a64SOliver Hartkopp 			canfdframecpy(&mod->modframe.set, &mb.cf);
888456a8a64SOliver Hartkopp 			mod->modtype.set = mb.modtype;
889456a8a64SOliver Hartkopp 
890456a8a64SOliver Hartkopp 			if (mb.modtype & CGW_MOD_ID)
891456a8a64SOliver Hartkopp 				mod->modfunc[modidx++] = mod_set_id;
892456a8a64SOliver Hartkopp 
893456a8a64SOliver Hartkopp 			if (mb.modtype & CGW_MOD_LEN)
894456a8a64SOliver Hartkopp 				mod->modfunc[modidx++] = mod_set_len;
895456a8a64SOliver Hartkopp 
896456a8a64SOliver Hartkopp 			if (mb.modtype & CGW_MOD_FLAGS)
897456a8a64SOliver Hartkopp 				mod->modfunc[modidx++] = mod_set_flags;
898456a8a64SOliver Hartkopp 
899456a8a64SOliver Hartkopp 			if (mb.modtype & CGW_MOD_DATA)
900456a8a64SOliver Hartkopp 				mod->modfunc[modidx++] = mod_set_fddata;
901456a8a64SOliver Hartkopp 		}
902456a8a64SOliver Hartkopp 	} else {
903e9dc7c60SOliver Hartkopp 		struct cgw_frame_mod mb;
904c1aabdf3SOliver Hartkopp 
905732d35fdSThomas Graf 		if (tb[CGW_MOD_AND]) {
906c1aabdf3SOliver Hartkopp 			nla_memcpy(&mb, tb[CGW_MOD_AND], CGW_MODATTR_LEN);
907c1aabdf3SOliver Hartkopp 
908c1aabdf3SOliver Hartkopp 			canframecpy(&mod->modframe.and, &mb.cf);
909c1aabdf3SOliver Hartkopp 			mod->modtype.and = mb.modtype;
910c1aabdf3SOliver Hartkopp 
911c1aabdf3SOliver Hartkopp 			if (mb.modtype & CGW_MOD_ID)
912c1aabdf3SOliver Hartkopp 				mod->modfunc[modidx++] = mod_and_id;
913c1aabdf3SOliver Hartkopp 
91494c23097SOliver Hartkopp 			if (mb.modtype & CGW_MOD_DLC)
91594c23097SOliver Hartkopp 				mod->modfunc[modidx++] = mod_and_ccdlc;
916c1aabdf3SOliver Hartkopp 
917c1aabdf3SOliver Hartkopp 			if (mb.modtype & CGW_MOD_DATA)
918c1aabdf3SOliver Hartkopp 				mod->modfunc[modidx++] = mod_and_data;
919c1aabdf3SOliver Hartkopp 		}
920c1aabdf3SOliver Hartkopp 
921732d35fdSThomas Graf 		if (tb[CGW_MOD_OR]) {
922c1aabdf3SOliver Hartkopp 			nla_memcpy(&mb, tb[CGW_MOD_OR], CGW_MODATTR_LEN);
923c1aabdf3SOliver Hartkopp 
924c1aabdf3SOliver Hartkopp 			canframecpy(&mod->modframe.or, &mb.cf);
925c1aabdf3SOliver Hartkopp 			mod->modtype.or = mb.modtype;
926c1aabdf3SOliver Hartkopp 
927c1aabdf3SOliver Hartkopp 			if (mb.modtype & CGW_MOD_ID)
928c1aabdf3SOliver Hartkopp 				mod->modfunc[modidx++] = mod_or_id;
929c1aabdf3SOliver Hartkopp 
93094c23097SOliver Hartkopp 			if (mb.modtype & CGW_MOD_DLC)
93194c23097SOliver Hartkopp 				mod->modfunc[modidx++] = mod_or_ccdlc;
932c1aabdf3SOliver Hartkopp 
933c1aabdf3SOliver Hartkopp 			if (mb.modtype & CGW_MOD_DATA)
934c1aabdf3SOliver Hartkopp 				mod->modfunc[modidx++] = mod_or_data;
935c1aabdf3SOliver Hartkopp 		}
936c1aabdf3SOliver Hartkopp 
937732d35fdSThomas Graf 		if (tb[CGW_MOD_XOR]) {
938c1aabdf3SOliver Hartkopp 			nla_memcpy(&mb, tb[CGW_MOD_XOR], CGW_MODATTR_LEN);
939c1aabdf3SOliver Hartkopp 
940c1aabdf3SOliver Hartkopp 			canframecpy(&mod->modframe.xor, &mb.cf);
941c1aabdf3SOliver Hartkopp 			mod->modtype.xor = mb.modtype;
942c1aabdf3SOliver Hartkopp 
943c1aabdf3SOliver Hartkopp 			if (mb.modtype & CGW_MOD_ID)
944c1aabdf3SOliver Hartkopp 				mod->modfunc[modidx++] = mod_xor_id;
945c1aabdf3SOliver Hartkopp 
94694c23097SOliver Hartkopp 			if (mb.modtype & CGW_MOD_DLC)
94794c23097SOliver Hartkopp 				mod->modfunc[modidx++] = mod_xor_ccdlc;
948c1aabdf3SOliver Hartkopp 
949c1aabdf3SOliver Hartkopp 			if (mb.modtype & CGW_MOD_DATA)
950c1aabdf3SOliver Hartkopp 				mod->modfunc[modidx++] = mod_xor_data;
951c1aabdf3SOliver Hartkopp 		}
952c1aabdf3SOliver Hartkopp 
953732d35fdSThomas Graf 		if (tb[CGW_MOD_SET]) {
954c1aabdf3SOliver Hartkopp 			nla_memcpy(&mb, tb[CGW_MOD_SET], CGW_MODATTR_LEN);
955c1aabdf3SOliver Hartkopp 
956c1aabdf3SOliver Hartkopp 			canframecpy(&mod->modframe.set, &mb.cf);
957c1aabdf3SOliver Hartkopp 			mod->modtype.set = mb.modtype;
958c1aabdf3SOliver Hartkopp 
959c1aabdf3SOliver Hartkopp 			if (mb.modtype & CGW_MOD_ID)
960c1aabdf3SOliver Hartkopp 				mod->modfunc[modidx++] = mod_set_id;
961c1aabdf3SOliver Hartkopp 
96294c23097SOliver Hartkopp 			if (mb.modtype & CGW_MOD_DLC)
96394c23097SOliver Hartkopp 				mod->modfunc[modidx++] = mod_set_ccdlc;
964c1aabdf3SOliver Hartkopp 
965c1aabdf3SOliver Hartkopp 			if (mb.modtype & CGW_MOD_DATA)
966c1aabdf3SOliver Hartkopp 				mod->modfunc[modidx++] = mod_set_data;
967c1aabdf3SOliver Hartkopp 		}
968e9dc7c60SOliver Hartkopp 	}
969c1aabdf3SOliver Hartkopp 
970c1aabdf3SOliver Hartkopp 	/* check for checksum operations after CAN frame modifications */
971c1aabdf3SOliver Hartkopp 	if (modidx) {
972732d35fdSThomas Graf 		if (tb[CGW_CS_CRC8]) {
9735d91efa8SThomas Graf 			struct cgw_csum_crc8 *c = nla_data(tb[CGW_CS_CRC8]);
974c1aabdf3SOliver Hartkopp 
975c1aabdf3SOliver Hartkopp 			err = cgw_chk_csum_parms(c->from_idx, c->to_idx,
976456a8a64SOliver Hartkopp 						 c->result_idx, r);
977c1aabdf3SOliver Hartkopp 			if (err)
978c1aabdf3SOliver Hartkopp 				return err;
979c1aabdf3SOliver Hartkopp 
980c1aabdf3SOliver Hartkopp 			nla_memcpy(&mod->csum.crc8, tb[CGW_CS_CRC8],
981c1aabdf3SOliver Hartkopp 				   CGW_CS_CRC8_LEN);
982c1aabdf3SOliver Hartkopp 
983465c0debSMarc Kleine-Budde 			/* select dedicated processing function to reduce
984c1aabdf3SOliver Hartkopp 			 * runtime operations in receive hot path.
985c1aabdf3SOliver Hartkopp 			 */
986c1aabdf3SOliver Hartkopp 			if (c->from_idx < 0 || c->to_idx < 0 ||
987c1aabdf3SOliver Hartkopp 			    c->result_idx < 0)
988c1aabdf3SOliver Hartkopp 				mod->csumfunc.crc8 = cgw_csum_crc8_rel;
989c1aabdf3SOliver Hartkopp 			else if (c->from_idx <= c->to_idx)
990c1aabdf3SOliver Hartkopp 				mod->csumfunc.crc8 = cgw_csum_crc8_pos;
991c1aabdf3SOliver Hartkopp 			else
992c1aabdf3SOliver Hartkopp 				mod->csumfunc.crc8 = cgw_csum_crc8_neg;
993c1aabdf3SOliver Hartkopp 		}
994c1aabdf3SOliver Hartkopp 
995732d35fdSThomas Graf 		if (tb[CGW_CS_XOR]) {
9965d91efa8SThomas Graf 			struct cgw_csum_xor *c = nla_data(tb[CGW_CS_XOR]);
997c1aabdf3SOliver Hartkopp 
998c1aabdf3SOliver Hartkopp 			err = cgw_chk_csum_parms(c->from_idx, c->to_idx,
999456a8a64SOliver Hartkopp 						 c->result_idx, r);
1000c1aabdf3SOliver Hartkopp 			if (err)
1001c1aabdf3SOliver Hartkopp 				return err;
1002c1aabdf3SOliver Hartkopp 
1003c1aabdf3SOliver Hartkopp 			nla_memcpy(&mod->csum.xor, tb[CGW_CS_XOR],
1004c1aabdf3SOliver Hartkopp 				   CGW_CS_XOR_LEN);
1005c1aabdf3SOliver Hartkopp 
1006465c0debSMarc Kleine-Budde 			/* select dedicated processing function to reduce
1007c1aabdf3SOliver Hartkopp 			 * runtime operations in receive hot path.
1008c1aabdf3SOliver Hartkopp 			 */
1009c1aabdf3SOliver Hartkopp 			if (c->from_idx < 0 || c->to_idx < 0 ||
1010c1aabdf3SOliver Hartkopp 			    c->result_idx < 0)
1011c1aabdf3SOliver Hartkopp 				mod->csumfunc.xor = cgw_csum_xor_rel;
1012c1aabdf3SOliver Hartkopp 			else if (c->from_idx <= c->to_idx)
1013c1aabdf3SOliver Hartkopp 				mod->csumfunc.xor = cgw_csum_xor_pos;
1014c1aabdf3SOliver Hartkopp 			else
1015c1aabdf3SOliver Hartkopp 				mod->csumfunc.xor = cgw_csum_xor_neg;
1016c1aabdf3SOliver Hartkopp 		}
1017dd895d7fSOliver Hartkopp 
101821468e6dSMarc Kleine-Budde 		if (tb[CGW_MOD_UID])
1019dd895d7fSOliver Hartkopp 			nla_memcpy(&mod->uid, tb[CGW_MOD_UID], sizeof(u32));
1020dd895d7fSOliver Hartkopp 	}
1021c1aabdf3SOliver Hartkopp 
1022c1aabdf3SOliver Hartkopp 	if (gwtype == CGW_TYPE_CAN_CAN) {
1023c1aabdf3SOliver Hartkopp 		/* check CGW_TYPE_CAN_CAN specific attributes */
1024c1aabdf3SOliver Hartkopp 		struct can_can_gw *ccgw = (struct can_can_gw *)gwtypeattr;
1025f62564f5SMarc Kleine-Budde 
1026c1aabdf3SOliver Hartkopp 		memset(ccgw, 0, sizeof(*ccgw));
1027c1aabdf3SOliver Hartkopp 
1028c1aabdf3SOliver Hartkopp 		/* check for can_filter in attributes */
1029732d35fdSThomas Graf 		if (tb[CGW_FILTER])
1030c1aabdf3SOliver Hartkopp 			nla_memcpy(&ccgw->filter, tb[CGW_FILTER],
1031c1aabdf3SOliver Hartkopp 				   sizeof(struct can_filter));
1032c1aabdf3SOliver Hartkopp 
1033c1aabdf3SOliver Hartkopp 		err = -ENODEV;
1034c1aabdf3SOliver Hartkopp 
1035c1aabdf3SOliver Hartkopp 		/* specifying two interfaces is mandatory */
1036c1aabdf3SOliver Hartkopp 		if (!tb[CGW_SRC_IF] || !tb[CGW_DST_IF])
1037c1aabdf3SOliver Hartkopp 			return err;
1038c1aabdf3SOliver Hartkopp 
1039732d35fdSThomas Graf 		ccgw->src_idx = nla_get_u32(tb[CGW_SRC_IF]);
1040732d35fdSThomas Graf 		ccgw->dst_idx = nla_get_u32(tb[CGW_DST_IF]);
1041c1aabdf3SOliver Hartkopp 
1042c1aabdf3SOliver Hartkopp 		/* both indices set to 0 for flushing all routing entries */
1043c1aabdf3SOliver Hartkopp 		if (!ccgw->src_idx && !ccgw->dst_idx)
1044c1aabdf3SOliver Hartkopp 			return 0;
1045c1aabdf3SOliver Hartkopp 
1046c1aabdf3SOliver Hartkopp 		/* only one index set to 0 is an error */
1047c1aabdf3SOliver Hartkopp 		if (!ccgw->src_idx || !ccgw->dst_idx)
1048c1aabdf3SOliver Hartkopp 			return err;
1049c1aabdf3SOliver Hartkopp 	}
1050c1aabdf3SOliver Hartkopp 
1051c1aabdf3SOliver Hartkopp 	/* add the checks for other gwtypes here */
1052c1aabdf3SOliver Hartkopp 
1053c1aabdf3SOliver Hartkopp 	return 0;
1054c1aabdf3SOliver Hartkopp }
1055c1aabdf3SOliver Hartkopp 
cgw_create_job(struct sk_buff * skb,struct nlmsghdr * nlh,struct netlink_ext_ack * extack)1056c21ef3e3SDavid Ahern static int cgw_create_job(struct sk_buff *skb,  struct nlmsghdr *nlh,
1057c21ef3e3SDavid Ahern 			  struct netlink_ext_ack *extack)
1058c1aabdf3SOliver Hartkopp {
10591ef83310SOliver Hartkopp 	struct net *net = sock_net(skb->sk);
1060c1aabdf3SOliver Hartkopp 	struct rtcanmsg *r;
1061c1aabdf3SOliver Hartkopp 	struct cgw_job *gwj;
1062dd895d7fSOliver Hartkopp 	struct cf_mod mod;
1063dd895d7fSOliver Hartkopp 	struct can_can_gw ccgw;
1064391ac128SOliver Hartkopp 	u8 limhops = 0;
1065c1aabdf3SOliver Hartkopp 	int err = 0;
1066c1aabdf3SOliver Hartkopp 
106790f62cf3SEric W. Biederman 	if (!netlink_capable(skb, CAP_NET_ADMIN))
1068dfc47ef8SEric W. Biederman 		return -EPERM;
1069dfc47ef8SEric W. Biederman 
1070c1aabdf3SOliver Hartkopp 	if (nlmsg_len(nlh) < sizeof(*r))
1071c1aabdf3SOliver Hartkopp 		return -EINVAL;
1072c1aabdf3SOliver Hartkopp 
1073c1aabdf3SOliver Hartkopp 	r = nlmsg_data(nlh);
1074c1aabdf3SOliver Hartkopp 	if (r->can_family != AF_CAN)
1075c1aabdf3SOliver Hartkopp 		return -EPFNOSUPPORT;
1076c1aabdf3SOliver Hartkopp 
1077c1aabdf3SOliver Hartkopp 	/* so far we only support CAN -> CAN routings */
1078c1aabdf3SOliver Hartkopp 	if (r->gwtype != CGW_TYPE_CAN_CAN)
1079c1aabdf3SOliver Hartkopp 		return -EINVAL;
1080c1aabdf3SOliver Hartkopp 
1081dd895d7fSOliver Hartkopp 	err = cgw_parse_attr(nlh, &mod, CGW_TYPE_CAN_CAN, &ccgw, &limhops);
1082dd895d7fSOliver Hartkopp 	if (err < 0)
1083dd895d7fSOliver Hartkopp 		return err;
1084dd895d7fSOliver Hartkopp 
1085dd895d7fSOliver Hartkopp 	if (mod.uid) {
1086dd895d7fSOliver Hartkopp 		ASSERT_RTNL();
1087dd895d7fSOliver Hartkopp 
1088dd895d7fSOliver Hartkopp 		/* check for updating an existing job with identical uid */
10891ef83310SOliver Hartkopp 		hlist_for_each_entry(gwj, &net->can.cgw_list, list) {
1090dd895d7fSOliver Hartkopp 			if (gwj->mod.uid != mod.uid)
1091dd895d7fSOliver Hartkopp 				continue;
1092dd895d7fSOliver Hartkopp 
1093dd895d7fSOliver Hartkopp 			/* interfaces & filters must be identical */
1094dd895d7fSOliver Hartkopp 			if (memcmp(&gwj->ccgw, &ccgw, sizeof(ccgw)))
1095dd895d7fSOliver Hartkopp 				return -EINVAL;
1096dd895d7fSOliver Hartkopp 
1097dd895d7fSOliver Hartkopp 			/* update modifications with disabled softirq & quit */
1098dd895d7fSOliver Hartkopp 			local_bh_disable();
1099dd895d7fSOliver Hartkopp 			memcpy(&gwj->mod, &mod, sizeof(mod));
1100dd895d7fSOliver Hartkopp 			local_bh_enable();
1101dd895d7fSOliver Hartkopp 			return 0;
1102dd895d7fSOliver Hartkopp 		}
1103dd895d7fSOliver Hartkopp 	}
1104dd895d7fSOliver Hartkopp 
1105dd895d7fSOliver Hartkopp 	/* ifindex == 0 is not allowed for job creation */
1106dd895d7fSOliver Hartkopp 	if (!ccgw.src_idx || !ccgw.dst_idx)
1107dd895d7fSOliver Hartkopp 		return -ENODEV;
1108dd895d7fSOliver Hartkopp 
1109c1aabdf3SOliver Hartkopp 	gwj = kmem_cache_alloc(cgw_cache, GFP_KERNEL);
1110c1aabdf3SOliver Hartkopp 	if (!gwj)
1111c1aabdf3SOliver Hartkopp 		return -ENOMEM;
1112c1aabdf3SOliver Hartkopp 
1113c1aabdf3SOliver Hartkopp 	gwj->handled_frames = 0;
1114c1aabdf3SOliver Hartkopp 	gwj->dropped_frames = 0;
1115e6afa00aSOliver Hartkopp 	gwj->deleted_frames = 0;
1116c1aabdf3SOliver Hartkopp 	gwj->flags = r->flags;
1117c1aabdf3SOliver Hartkopp 	gwj->gwtype = r->gwtype;
1118dd895d7fSOliver Hartkopp 	gwj->limit_hops = limhops;
1119c1aabdf3SOliver Hartkopp 
1120dd895d7fSOliver Hartkopp 	/* insert already parsed information */
1121dd895d7fSOliver Hartkopp 	memcpy(&gwj->mod, &mod, sizeof(mod));
1122dd895d7fSOliver Hartkopp 	memcpy(&gwj->ccgw, &ccgw, sizeof(ccgw));
1123c1aabdf3SOliver Hartkopp 
1124c1aabdf3SOliver Hartkopp 	err = -ENODEV;
1125c1aabdf3SOliver Hartkopp 
11261ef83310SOliver Hartkopp 	gwj->src.dev = __dev_get_by_index(net, gwj->ccgw.src_idx);
1127c1aabdf3SOliver Hartkopp 
1128c1aabdf3SOliver Hartkopp 	if (!gwj->src.dev)
1129c1aabdf3SOliver Hartkopp 		goto out;
1130c1aabdf3SOliver Hartkopp 
1131c0ebbdd6SOliver Hartkopp 	if (gwj->src.dev->type != ARPHRD_CAN)
11325af28de3SYing Xue 		goto out;
1133c1aabdf3SOliver Hartkopp 
11341ef83310SOliver Hartkopp 	gwj->dst.dev = __dev_get_by_index(net, gwj->ccgw.dst_idx);
1135c1aabdf3SOliver Hartkopp 
1136c1aabdf3SOliver Hartkopp 	if (!gwj->dst.dev)
11375af28de3SYing Xue 		goto out;
1138c1aabdf3SOliver Hartkopp 
1139c0ebbdd6SOliver Hartkopp 	if (gwj->dst.dev->type != ARPHRD_CAN)
11405af28de3SYing Xue 		goto out;
1141c1aabdf3SOliver Hartkopp 
1142*2a30b2bdSOliver Hartkopp 	/* is sending the skb back to the incoming interface intended? */
1143*2a30b2bdSOliver Hartkopp 	if (gwj->src.dev == gwj->dst.dev &&
1144*2a30b2bdSOliver Hartkopp 	    !(gwj->flags & CGW_FLAGS_CAN_IIF_TX_OK)) {
1145*2a30b2bdSOliver Hartkopp 		err = -EINVAL;
1146*2a30b2bdSOliver Hartkopp 		goto out;
1147*2a30b2bdSOliver Hartkopp 	}
1148*2a30b2bdSOliver Hartkopp 
1149c1aabdf3SOliver Hartkopp 	ASSERT_RTNL();
1150c1aabdf3SOliver Hartkopp 
11511ef83310SOliver Hartkopp 	err = cgw_register_filter(net, gwj);
1152c1aabdf3SOliver Hartkopp 	if (!err)
11531ef83310SOliver Hartkopp 		hlist_add_head_rcu(&gwj->list, &net->can.cgw_list);
1154c1aabdf3SOliver Hartkopp out:
1155c1aabdf3SOliver Hartkopp 	if (err)
1156c1aabdf3SOliver Hartkopp 		kmem_cache_free(cgw_cache, gwj);
1157c1aabdf3SOliver Hartkopp 
1158c1aabdf3SOliver Hartkopp 	return err;
1159c1aabdf3SOliver Hartkopp }
1160c1aabdf3SOliver Hartkopp 
cgw_remove_all_jobs(struct net * net)11611ef83310SOliver Hartkopp static void cgw_remove_all_jobs(struct net *net)
1162c1aabdf3SOliver Hartkopp {
1163c1aabdf3SOliver Hartkopp 	struct cgw_job *gwj = NULL;
1164b67bfe0dSSasha Levin 	struct hlist_node *nx;
1165c1aabdf3SOliver Hartkopp 
1166c1aabdf3SOliver Hartkopp 	ASSERT_RTNL();
1167c1aabdf3SOliver Hartkopp 
11681ef83310SOliver Hartkopp 	hlist_for_each_entry_safe(gwj, nx, &net->can.cgw_list, list) {
1169c1aabdf3SOliver Hartkopp 		hlist_del(&gwj->list);
11701ef83310SOliver Hartkopp 		cgw_unregister_filter(net, gwj);
1171181d4447SEric Dumazet 		call_rcu(&gwj->rcu, cgw_job_free_rcu);
1172c1aabdf3SOliver Hartkopp 	}
1173c1aabdf3SOliver Hartkopp }
1174c1aabdf3SOliver Hartkopp 
cgw_remove_job(struct sk_buff * skb,struct nlmsghdr * nlh,struct netlink_ext_ack * extack)1175c21ef3e3SDavid Ahern static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh,
1176c21ef3e3SDavid Ahern 			  struct netlink_ext_ack *extack)
1177c1aabdf3SOliver Hartkopp {
11781ef83310SOliver Hartkopp 	struct net *net = sock_net(skb->sk);
1179c1aabdf3SOliver Hartkopp 	struct cgw_job *gwj = NULL;
1180b67bfe0dSSasha Levin 	struct hlist_node *nx;
1181c1aabdf3SOliver Hartkopp 	struct rtcanmsg *r;
1182c1aabdf3SOliver Hartkopp 	struct cf_mod mod;
1183c1aabdf3SOliver Hartkopp 	struct can_can_gw ccgw;
1184391ac128SOliver Hartkopp 	u8 limhops = 0;
1185c1aabdf3SOliver Hartkopp 	int err = 0;
1186c1aabdf3SOliver Hartkopp 
118790f62cf3SEric W. Biederman 	if (!netlink_capable(skb, CAP_NET_ADMIN))
1188dfc47ef8SEric W. Biederman 		return -EPERM;
1189dfc47ef8SEric W. Biederman 
1190c1aabdf3SOliver Hartkopp 	if (nlmsg_len(nlh) < sizeof(*r))
1191c1aabdf3SOliver Hartkopp 		return -EINVAL;
1192c1aabdf3SOliver Hartkopp 
1193c1aabdf3SOliver Hartkopp 	r = nlmsg_data(nlh);
1194c1aabdf3SOliver Hartkopp 	if (r->can_family != AF_CAN)
1195c1aabdf3SOliver Hartkopp 		return -EPFNOSUPPORT;
1196c1aabdf3SOliver Hartkopp 
1197c1aabdf3SOliver Hartkopp 	/* so far we only support CAN -> CAN routings */
1198c1aabdf3SOliver Hartkopp 	if (r->gwtype != CGW_TYPE_CAN_CAN)
1199c1aabdf3SOliver Hartkopp 		return -EINVAL;
1200c1aabdf3SOliver Hartkopp 
1201391ac128SOliver Hartkopp 	err = cgw_parse_attr(nlh, &mod, CGW_TYPE_CAN_CAN, &ccgw, &limhops);
1202c1aabdf3SOliver Hartkopp 	if (err < 0)
1203c1aabdf3SOliver Hartkopp 		return err;
1204c1aabdf3SOliver Hartkopp 
1205c1aabdf3SOliver Hartkopp 	/* two interface indices both set to 0 => remove all entries */
1206c1aabdf3SOliver Hartkopp 	if (!ccgw.src_idx && !ccgw.dst_idx) {
12071ef83310SOliver Hartkopp 		cgw_remove_all_jobs(net);
1208c1aabdf3SOliver Hartkopp 		return 0;
1209c1aabdf3SOliver Hartkopp 	}
1210c1aabdf3SOliver Hartkopp 
1211c1aabdf3SOliver Hartkopp 	err = -EINVAL;
1212c1aabdf3SOliver Hartkopp 
1213c1aabdf3SOliver Hartkopp 	ASSERT_RTNL();
1214c1aabdf3SOliver Hartkopp 
1215c1aabdf3SOliver Hartkopp 	/* remove only the first matching entry */
12161ef83310SOliver Hartkopp 	hlist_for_each_entry_safe(gwj, nx, &net->can.cgw_list, list) {
1217c1aabdf3SOliver Hartkopp 		if (gwj->flags != r->flags)
1218c1aabdf3SOliver Hartkopp 			continue;
1219c1aabdf3SOliver Hartkopp 
1220391ac128SOliver Hartkopp 		if (gwj->limit_hops != limhops)
1221391ac128SOliver Hartkopp 			continue;
1222391ac128SOliver Hartkopp 
1223dd895d7fSOliver Hartkopp 		/* we have a match when uid is enabled and identical */
1224dd895d7fSOliver Hartkopp 		if (gwj->mod.uid || mod.uid) {
1225dd895d7fSOliver Hartkopp 			if (gwj->mod.uid != mod.uid)
1226dd895d7fSOliver Hartkopp 				continue;
1227dd895d7fSOliver Hartkopp 		} else {
1228dd895d7fSOliver Hartkopp 			/* no uid => check for identical modifications */
1229c1aabdf3SOliver Hartkopp 			if (memcmp(&gwj->mod, &mod, sizeof(mod)))
1230c1aabdf3SOliver Hartkopp 				continue;
1231dd895d7fSOliver Hartkopp 		}
1232c1aabdf3SOliver Hartkopp 
1233c1aabdf3SOliver Hartkopp 		/* if (r->gwtype == CGW_TYPE_CAN_CAN) - is made sure here */
1234c1aabdf3SOliver Hartkopp 		if (memcmp(&gwj->ccgw, &ccgw, sizeof(ccgw)))
1235c1aabdf3SOliver Hartkopp 			continue;
1236c1aabdf3SOliver Hartkopp 
1237c1aabdf3SOliver Hartkopp 		hlist_del(&gwj->list);
12381ef83310SOliver Hartkopp 		cgw_unregister_filter(net, gwj);
1239181d4447SEric Dumazet 		call_rcu(&gwj->rcu, cgw_job_free_rcu);
1240c1aabdf3SOliver Hartkopp 		err = 0;
1241c1aabdf3SOliver Hartkopp 		break;
1242c1aabdf3SOliver Hartkopp 	}
1243c1aabdf3SOliver Hartkopp 
1244c1aabdf3SOliver Hartkopp 	return err;
1245c1aabdf3SOliver Hartkopp }
1246c1aabdf3SOliver Hartkopp 
cangw_pernet_init(struct net * net)12471ef83310SOliver Hartkopp static int __net_init cangw_pernet_init(struct net *net)
12481ef83310SOliver Hartkopp {
12491ef83310SOliver Hartkopp 	INIT_HLIST_HEAD(&net->can.cgw_list);
12501ef83310SOliver Hartkopp 	return 0;
12511ef83310SOliver Hartkopp }
12521ef83310SOliver Hartkopp 
cangw_pernet_exit_batch(struct list_head * net_list)1253ef0de669SEric Dumazet static void __net_exit cangw_pernet_exit_batch(struct list_head *net_list)
12541ef83310SOliver Hartkopp {
1255ef0de669SEric Dumazet 	struct net *net;
1256ef0de669SEric Dumazet 
12571ef83310SOliver Hartkopp 	rtnl_lock();
1258ef0de669SEric Dumazet 	list_for_each_entry(net, net_list, exit_list)
12591ef83310SOliver Hartkopp 		cgw_remove_all_jobs(net);
12601ef83310SOliver Hartkopp 	rtnl_unlock();
12611ef83310SOliver Hartkopp }
12621ef83310SOliver Hartkopp 
12631ef83310SOliver Hartkopp static struct pernet_operations cangw_pernet_ops = {
12641ef83310SOliver Hartkopp 	.init = cangw_pernet_init,
1265ef0de669SEric Dumazet 	.exit_batch = cangw_pernet_exit_batch,
12661ef83310SOliver Hartkopp };
12671ef83310SOliver Hartkopp 
cgw_module_init(void)1268c1aabdf3SOliver Hartkopp static __init int cgw_module_init(void)
1269c1aabdf3SOliver Hartkopp {
1270c1c502b5SFlorian Westphal 	int ret;
1271c1c502b5SFlorian Westphal 
1272be286bafSOliver Hartkopp 	/* sanitize given module parameter */
1273be286bafSOliver Hartkopp 	max_hops = clamp_t(unsigned int, max_hops, CGW_MIN_HOPS, CGW_MAX_HOPS);
1274be286bafSOliver Hartkopp 
1275f726f3d3SOliver Hartkopp 	pr_info("can: netlink gateway - max_hops=%d\n",	max_hops);
1276c1aabdf3SOliver Hartkopp 
1277b7a14297SYueHaibing 	ret = register_pernet_subsys(&cangw_pernet_ops);
1278b7a14297SYueHaibing 	if (ret)
1279b7a14297SYueHaibing 		return ret;
1280b7a14297SYueHaibing 
1281b7a14297SYueHaibing 	ret = -ENOMEM;
1282c1aabdf3SOliver Hartkopp 	cgw_cache = kmem_cache_create("can_gw", sizeof(struct cgw_job),
1283c1aabdf3SOliver Hartkopp 				      0, 0, NULL);
1284c1aabdf3SOliver Hartkopp 	if (!cgw_cache)
1285b7a14297SYueHaibing 		goto out_cache_create;
1286c1aabdf3SOliver Hartkopp 
1287c1aabdf3SOliver Hartkopp 	/* set notifier */
1288c1aabdf3SOliver Hartkopp 	notifier.notifier_call = cgw_notifier;
1289b7a14297SYueHaibing 	ret = register_netdevice_notifier(&notifier);
1290b7a14297SYueHaibing 	if (ret)
1291b7a14297SYueHaibing 		goto out_register_notifier;
1292c1aabdf3SOliver Hartkopp 
1293c1c502b5SFlorian Westphal 	ret = rtnl_register_module(THIS_MODULE, PF_CAN, RTM_GETROUTE,
1294c1c502b5SFlorian Westphal 				   NULL, cgw_dump_jobs, 0);
1295b7a14297SYueHaibing 	if (ret)
1296b7a14297SYueHaibing 		goto out_rtnl_register1;
1297c1aabdf3SOliver Hartkopp 
1298b7a14297SYueHaibing 	ret = rtnl_register_module(THIS_MODULE, PF_CAN, RTM_NEWROUTE,
1299c1c502b5SFlorian Westphal 				   cgw_create_job, NULL, 0);
1300b7a14297SYueHaibing 	if (ret)
1301b7a14297SYueHaibing 		goto out_rtnl_register2;
1302b7a14297SYueHaibing 	ret = rtnl_register_module(THIS_MODULE, PF_CAN, RTM_DELROUTE,
1303c1c502b5SFlorian Westphal 				   cgw_remove_job, NULL, 0);
1304b7a14297SYueHaibing 	if (ret)
1305b7a14297SYueHaibing 		goto out_rtnl_register3;
1306c1aabdf3SOliver Hartkopp 
1307c1aabdf3SOliver Hartkopp 	return 0;
1308b7a14297SYueHaibing 
1309b7a14297SYueHaibing out_rtnl_register3:
1310b7a14297SYueHaibing 	rtnl_unregister(PF_CAN, RTM_NEWROUTE);
1311b7a14297SYueHaibing out_rtnl_register2:
1312b7a14297SYueHaibing 	rtnl_unregister(PF_CAN, RTM_GETROUTE);
1313b7a14297SYueHaibing out_rtnl_register1:
1314b7a14297SYueHaibing 	unregister_netdevice_notifier(&notifier);
1315b7a14297SYueHaibing out_register_notifier:
1316b7a14297SYueHaibing 	kmem_cache_destroy(cgw_cache);
1317b7a14297SYueHaibing out_cache_create:
1318b7a14297SYueHaibing 	unregister_pernet_subsys(&cangw_pernet_ops);
1319b7a14297SYueHaibing 
1320b7a14297SYueHaibing 	return ret;
1321c1aabdf3SOliver Hartkopp }
1322c1aabdf3SOliver Hartkopp 
cgw_module_exit(void)1323c1aabdf3SOliver Hartkopp static __exit void cgw_module_exit(void)
1324c1aabdf3SOliver Hartkopp {
1325c1aabdf3SOliver Hartkopp 	rtnl_unregister_all(PF_CAN);
1326c1aabdf3SOliver Hartkopp 
1327c1aabdf3SOliver Hartkopp 	unregister_netdevice_notifier(&notifier);
1328c1aabdf3SOliver Hartkopp 
13291ef83310SOliver Hartkopp 	unregister_pernet_subsys(&cangw_pernet_ops);
1330c1aabdf3SOliver Hartkopp 	rcu_barrier(); /* Wait for completion of call_rcu()'s */
1331c1aabdf3SOliver Hartkopp 
1332c1aabdf3SOliver Hartkopp 	kmem_cache_destroy(cgw_cache);
1333c1aabdf3SOliver Hartkopp }
1334c1aabdf3SOliver Hartkopp 
1335c1aabdf3SOliver Hartkopp module_init(cgw_module_init);
1336c1aabdf3SOliver Hartkopp module_exit(cgw_module_exit);
1337