18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
28c2ecf20Sopenharmony_ci/* gw.c - CAN frame Gateway/Router/Bridge with netlink interface
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (c) 2019 Volkswagen Group Electronic Research
58c2ecf20Sopenharmony_ci * All rights reserved.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without
88c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions
98c2ecf20Sopenharmony_ci * are met:
108c2ecf20Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright
118c2ecf20Sopenharmony_ci *    notice, this list of conditions and the following disclaimer.
128c2ecf20Sopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright
138c2ecf20Sopenharmony_ci *    notice, this list of conditions and the following disclaimer in the
148c2ecf20Sopenharmony_ci *    documentation and/or other materials provided with the distribution.
158c2ecf20Sopenharmony_ci * 3. Neither the name of Volkswagen nor the names of its contributors
168c2ecf20Sopenharmony_ci *    may be used to endorse or promote products derived from this software
178c2ecf20Sopenharmony_ci *    without specific prior written permission.
188c2ecf20Sopenharmony_ci *
198c2ecf20Sopenharmony_ci * Alternatively, provided that this notice is retained in full, this
208c2ecf20Sopenharmony_ci * software may be distributed under the terms of the GNU General
218c2ecf20Sopenharmony_ci * Public License ("GPL") version 2, in which case the provisions of the
228c2ecf20Sopenharmony_ci * GPL apply INSTEAD OF those given above.
238c2ecf20Sopenharmony_ci *
248c2ecf20Sopenharmony_ci * The provided data structures and external interfaces from this code
258c2ecf20Sopenharmony_ci * are not restricted to be used by modules with a GPL compatible license.
268c2ecf20Sopenharmony_ci *
278c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
288c2ecf20Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
298c2ecf20Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
308c2ecf20Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
318c2ecf20Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
328c2ecf20Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
338c2ecf20Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
348c2ecf20Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
358c2ecf20Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
368c2ecf20Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
378c2ecf20Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
388c2ecf20Sopenharmony_ci * DAMAGE.
398c2ecf20Sopenharmony_ci *
408c2ecf20Sopenharmony_ci */
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#include <linux/module.h>
438c2ecf20Sopenharmony_ci#include <linux/init.h>
448c2ecf20Sopenharmony_ci#include <linux/types.h>
458c2ecf20Sopenharmony_ci#include <linux/kernel.h>
468c2ecf20Sopenharmony_ci#include <linux/list.h>
478c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
488c2ecf20Sopenharmony_ci#include <linux/rcupdate.h>
498c2ecf20Sopenharmony_ci#include <linux/rculist.h>
508c2ecf20Sopenharmony_ci#include <linux/net.h>
518c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
528c2ecf20Sopenharmony_ci#include <linux/if_arp.h>
538c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
548c2ecf20Sopenharmony_ci#include <linux/can.h>
558c2ecf20Sopenharmony_ci#include <linux/can/core.h>
568c2ecf20Sopenharmony_ci#include <linux/can/skb.h>
578c2ecf20Sopenharmony_ci#include <linux/can/gw.h>
588c2ecf20Sopenharmony_ci#include <net/rtnetlink.h>
598c2ecf20Sopenharmony_ci#include <net/net_namespace.h>
608c2ecf20Sopenharmony_ci#include <net/sock.h>
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci#define CAN_GW_NAME "can-gw"
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("PF_CAN netlink gateway");
658c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL");
668c2ecf20Sopenharmony_ciMODULE_AUTHOR("Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
678c2ecf20Sopenharmony_ciMODULE_ALIAS(CAN_GW_NAME);
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci#define CGW_MIN_HOPS 1
708c2ecf20Sopenharmony_ci#define CGW_MAX_HOPS 6
718c2ecf20Sopenharmony_ci#define CGW_DEFAULT_HOPS 1
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistatic unsigned int max_hops __read_mostly = CGW_DEFAULT_HOPS;
748c2ecf20Sopenharmony_cimodule_param(max_hops, uint, 0444);
758c2ecf20Sopenharmony_ciMODULE_PARM_DESC(max_hops,
768c2ecf20Sopenharmony_ci		 "maximum " CAN_GW_NAME " routing hops for CAN frames "
778c2ecf20Sopenharmony_ci		 "(valid values: " __stringify(CGW_MIN_HOPS) "-"
788c2ecf20Sopenharmony_ci		 __stringify(CGW_MAX_HOPS) " hops, "
798c2ecf20Sopenharmony_ci		 "default: " __stringify(CGW_DEFAULT_HOPS) ")");
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_cistatic struct notifier_block notifier;
828c2ecf20Sopenharmony_cistatic struct kmem_cache *cgw_cache __read_mostly;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci/* structure that contains the (on-the-fly) CAN frame modifications */
858c2ecf20Sopenharmony_cistruct cf_mod {
868c2ecf20Sopenharmony_ci	struct {
878c2ecf20Sopenharmony_ci		struct canfd_frame and;
888c2ecf20Sopenharmony_ci		struct canfd_frame or;
898c2ecf20Sopenharmony_ci		struct canfd_frame xor;
908c2ecf20Sopenharmony_ci		struct canfd_frame set;
918c2ecf20Sopenharmony_ci	} modframe;
928c2ecf20Sopenharmony_ci	struct {
938c2ecf20Sopenharmony_ci		u8 and;
948c2ecf20Sopenharmony_ci		u8 or;
958c2ecf20Sopenharmony_ci		u8 xor;
968c2ecf20Sopenharmony_ci		u8 set;
978c2ecf20Sopenharmony_ci	} modtype;
988c2ecf20Sopenharmony_ci	void (*modfunc[MAX_MODFUNCTIONS])(struct canfd_frame *cf,
998c2ecf20Sopenharmony_ci					  struct cf_mod *mod);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	/* CAN frame checksum calculation after CAN frame modifications */
1028c2ecf20Sopenharmony_ci	struct {
1038c2ecf20Sopenharmony_ci		struct cgw_csum_xor xor;
1048c2ecf20Sopenharmony_ci		struct cgw_csum_crc8 crc8;
1058c2ecf20Sopenharmony_ci	} csum;
1068c2ecf20Sopenharmony_ci	struct {
1078c2ecf20Sopenharmony_ci		void (*xor)(struct canfd_frame *cf,
1088c2ecf20Sopenharmony_ci			    struct cgw_csum_xor *xor);
1098c2ecf20Sopenharmony_ci		void (*crc8)(struct canfd_frame *cf,
1108c2ecf20Sopenharmony_ci			     struct cgw_csum_crc8 *crc8);
1118c2ecf20Sopenharmony_ci	} csumfunc;
1128c2ecf20Sopenharmony_ci	u32 uid;
1138c2ecf20Sopenharmony_ci};
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci/* So far we just support CAN -> CAN routing and frame modifications.
1168c2ecf20Sopenharmony_ci *
1178c2ecf20Sopenharmony_ci * The internal can_can_gw structure contains data and attributes for
1188c2ecf20Sopenharmony_ci * a CAN -> CAN gateway job.
1198c2ecf20Sopenharmony_ci */
1208c2ecf20Sopenharmony_cistruct can_can_gw {
1218c2ecf20Sopenharmony_ci	struct can_filter filter;
1228c2ecf20Sopenharmony_ci	int src_idx;
1238c2ecf20Sopenharmony_ci	int dst_idx;
1248c2ecf20Sopenharmony_ci};
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci/* list entry for CAN gateways jobs */
1278c2ecf20Sopenharmony_cistruct cgw_job {
1288c2ecf20Sopenharmony_ci	struct hlist_node list;
1298c2ecf20Sopenharmony_ci	struct rcu_head rcu;
1308c2ecf20Sopenharmony_ci	u32 handled_frames;
1318c2ecf20Sopenharmony_ci	u32 dropped_frames;
1328c2ecf20Sopenharmony_ci	u32 deleted_frames;
1338c2ecf20Sopenharmony_ci	struct cf_mod mod;
1348c2ecf20Sopenharmony_ci	union {
1358c2ecf20Sopenharmony_ci		/* CAN frame data source */
1368c2ecf20Sopenharmony_ci		struct net_device *dev;
1378c2ecf20Sopenharmony_ci	} src;
1388c2ecf20Sopenharmony_ci	union {
1398c2ecf20Sopenharmony_ci		/* CAN frame data destination */
1408c2ecf20Sopenharmony_ci		struct net_device *dev;
1418c2ecf20Sopenharmony_ci	} dst;
1428c2ecf20Sopenharmony_ci	union {
1438c2ecf20Sopenharmony_ci		struct can_can_gw ccgw;
1448c2ecf20Sopenharmony_ci		/* tbc */
1458c2ecf20Sopenharmony_ci	};
1468c2ecf20Sopenharmony_ci	u8 gwtype;
1478c2ecf20Sopenharmony_ci	u8 limit_hops;
1488c2ecf20Sopenharmony_ci	u16 flags;
1498c2ecf20Sopenharmony_ci};
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci/* modification functions that are invoked in the hot path in can_can_gw_rcv */
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci#define MODFUNC(func, op) static void func(struct canfd_frame *cf, \
1548c2ecf20Sopenharmony_ci					   struct cf_mod *mod) { op ; }
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ciMODFUNC(mod_and_id, cf->can_id &= mod->modframe.and.can_id)
1578c2ecf20Sopenharmony_ciMODFUNC(mod_and_len, cf->len &= mod->modframe.and.len)
1588c2ecf20Sopenharmony_ciMODFUNC(mod_and_flags, cf->flags &= mod->modframe.and.flags)
1598c2ecf20Sopenharmony_ciMODFUNC(mod_and_data, *(u64 *)cf->data &= *(u64 *)mod->modframe.and.data)
1608c2ecf20Sopenharmony_ciMODFUNC(mod_or_id, cf->can_id |= mod->modframe.or.can_id)
1618c2ecf20Sopenharmony_ciMODFUNC(mod_or_len, cf->len |= mod->modframe.or.len)
1628c2ecf20Sopenharmony_ciMODFUNC(mod_or_flags, cf->flags |= mod->modframe.or.flags)
1638c2ecf20Sopenharmony_ciMODFUNC(mod_or_data, *(u64 *)cf->data |= *(u64 *)mod->modframe.or.data)
1648c2ecf20Sopenharmony_ciMODFUNC(mod_xor_id, cf->can_id ^= mod->modframe.xor.can_id)
1658c2ecf20Sopenharmony_ciMODFUNC(mod_xor_len, cf->len ^= mod->modframe.xor.len)
1668c2ecf20Sopenharmony_ciMODFUNC(mod_xor_flags, cf->flags ^= mod->modframe.xor.flags)
1678c2ecf20Sopenharmony_ciMODFUNC(mod_xor_data, *(u64 *)cf->data ^= *(u64 *)mod->modframe.xor.data)
1688c2ecf20Sopenharmony_ciMODFUNC(mod_set_id, cf->can_id = mod->modframe.set.can_id)
1698c2ecf20Sopenharmony_ciMODFUNC(mod_set_len, cf->len = mod->modframe.set.len)
1708c2ecf20Sopenharmony_ciMODFUNC(mod_set_flags, cf->flags = mod->modframe.set.flags)
1718c2ecf20Sopenharmony_ciMODFUNC(mod_set_data, *(u64 *)cf->data = *(u64 *)mod->modframe.set.data)
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistatic void mod_and_fddata(struct canfd_frame *cf, struct cf_mod *mod)
1748c2ecf20Sopenharmony_ci{
1758c2ecf20Sopenharmony_ci	int i;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	for (i = 0; i < CANFD_MAX_DLEN; i += 8)
1788c2ecf20Sopenharmony_ci		*(u64 *)(cf->data + i) &= *(u64 *)(mod->modframe.and.data + i);
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_cistatic void mod_or_fddata(struct canfd_frame *cf, struct cf_mod *mod)
1828c2ecf20Sopenharmony_ci{
1838c2ecf20Sopenharmony_ci	int i;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	for (i = 0; i < CANFD_MAX_DLEN; i += 8)
1868c2ecf20Sopenharmony_ci		*(u64 *)(cf->data + i) |= *(u64 *)(mod->modframe.or.data + i);
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistatic void mod_xor_fddata(struct canfd_frame *cf, struct cf_mod *mod)
1908c2ecf20Sopenharmony_ci{
1918c2ecf20Sopenharmony_ci	int i;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	for (i = 0; i < CANFD_MAX_DLEN; i += 8)
1948c2ecf20Sopenharmony_ci		*(u64 *)(cf->data + i) ^= *(u64 *)(mod->modframe.xor.data + i);
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_cistatic void mod_set_fddata(struct canfd_frame *cf, struct cf_mod *mod)
1988c2ecf20Sopenharmony_ci{
1998c2ecf20Sopenharmony_ci	memcpy(cf->data, mod->modframe.set.data, CANFD_MAX_DLEN);
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_cistatic void canframecpy(struct canfd_frame *dst, struct can_frame *src)
2038c2ecf20Sopenharmony_ci{
2048c2ecf20Sopenharmony_ci	/* Copy the struct members separately to ensure that no uninitialized
2058c2ecf20Sopenharmony_ci	 * data are copied in the 3 bytes hole of the struct. This is needed
2068c2ecf20Sopenharmony_ci	 * to make easy compares of the data in the struct cf_mod.
2078c2ecf20Sopenharmony_ci	 */
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	dst->can_id = src->can_id;
2108c2ecf20Sopenharmony_ci	dst->len = src->can_dlc;
2118c2ecf20Sopenharmony_ci	*(u64 *)dst->data = *(u64 *)src->data;
2128c2ecf20Sopenharmony_ci}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_cistatic void canfdframecpy(struct canfd_frame *dst, struct canfd_frame *src)
2158c2ecf20Sopenharmony_ci{
2168c2ecf20Sopenharmony_ci	/* Copy the struct members separately to ensure that no uninitialized
2178c2ecf20Sopenharmony_ci	 * data are copied in the 2 bytes hole of the struct. This is needed
2188c2ecf20Sopenharmony_ci	 * to make easy compares of the data in the struct cf_mod.
2198c2ecf20Sopenharmony_ci	 */
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	dst->can_id = src->can_id;
2228c2ecf20Sopenharmony_ci	dst->flags = src->flags;
2238c2ecf20Sopenharmony_ci	dst->len = src->len;
2248c2ecf20Sopenharmony_ci	memcpy(dst->data, src->data, CANFD_MAX_DLEN);
2258c2ecf20Sopenharmony_ci}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_cistatic int cgw_chk_csum_parms(s8 fr, s8 to, s8 re, struct rtcanmsg *r)
2288c2ecf20Sopenharmony_ci{
2298c2ecf20Sopenharmony_ci	s8 dlen = CAN_MAX_DLEN;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	if (r->flags & CGW_FLAGS_CAN_FD)
2328c2ecf20Sopenharmony_ci		dlen = CANFD_MAX_DLEN;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	/* absolute dlc values 0 .. 7 => 0 .. 7, e.g. data [0]
2358c2ecf20Sopenharmony_ci	 * relative to received dlc -1 .. -8 :
2368c2ecf20Sopenharmony_ci	 * e.g. for received dlc = 8
2378c2ecf20Sopenharmony_ci	 * -1 => index = 7 (data[7])
2388c2ecf20Sopenharmony_ci	 * -3 => index = 5 (data[5])
2398c2ecf20Sopenharmony_ci	 * -8 => index = 0 (data[0])
2408c2ecf20Sopenharmony_ci	 */
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	if (fr >= -dlen && fr < dlen &&
2438c2ecf20Sopenharmony_ci	    to >= -dlen && to < dlen &&
2448c2ecf20Sopenharmony_ci	    re >= -dlen && re < dlen)
2458c2ecf20Sopenharmony_ci		return 0;
2468c2ecf20Sopenharmony_ci	else
2478c2ecf20Sopenharmony_ci		return -EINVAL;
2488c2ecf20Sopenharmony_ci}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_cistatic inline int calc_idx(int idx, int rx_len)
2518c2ecf20Sopenharmony_ci{
2528c2ecf20Sopenharmony_ci	if (idx < 0)
2538c2ecf20Sopenharmony_ci		return rx_len + idx;
2548c2ecf20Sopenharmony_ci	else
2558c2ecf20Sopenharmony_ci		return idx;
2568c2ecf20Sopenharmony_ci}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_cistatic void cgw_csum_xor_rel(struct canfd_frame *cf, struct cgw_csum_xor *xor)
2598c2ecf20Sopenharmony_ci{
2608c2ecf20Sopenharmony_ci	int from = calc_idx(xor->from_idx, cf->len);
2618c2ecf20Sopenharmony_ci	int to = calc_idx(xor->to_idx, cf->len);
2628c2ecf20Sopenharmony_ci	int res = calc_idx(xor->result_idx, cf->len);
2638c2ecf20Sopenharmony_ci	u8 val = xor->init_xor_val;
2648c2ecf20Sopenharmony_ci	int i;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	if (from < 0 || to < 0 || res < 0)
2678c2ecf20Sopenharmony_ci		return;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	if (from <= to) {
2708c2ecf20Sopenharmony_ci		for (i = from; i <= to; i++)
2718c2ecf20Sopenharmony_ci			val ^= cf->data[i];
2728c2ecf20Sopenharmony_ci	} else {
2738c2ecf20Sopenharmony_ci		for (i = from; i >= to; i--)
2748c2ecf20Sopenharmony_ci			val ^= cf->data[i];
2758c2ecf20Sopenharmony_ci	}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	cf->data[res] = val;
2788c2ecf20Sopenharmony_ci}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_cistatic void cgw_csum_xor_pos(struct canfd_frame *cf, struct cgw_csum_xor *xor)
2818c2ecf20Sopenharmony_ci{
2828c2ecf20Sopenharmony_ci	u8 val = xor->init_xor_val;
2838c2ecf20Sopenharmony_ci	int i;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	for (i = xor->from_idx; i <= xor->to_idx; i++)
2868c2ecf20Sopenharmony_ci		val ^= cf->data[i];
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	cf->data[xor->result_idx] = val;
2898c2ecf20Sopenharmony_ci}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_cistatic void cgw_csum_xor_neg(struct canfd_frame *cf, struct cgw_csum_xor *xor)
2928c2ecf20Sopenharmony_ci{
2938c2ecf20Sopenharmony_ci	u8 val = xor->init_xor_val;
2948c2ecf20Sopenharmony_ci	int i;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	for (i = xor->from_idx; i >= xor->to_idx; i--)
2978c2ecf20Sopenharmony_ci		val ^= cf->data[i];
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	cf->data[xor->result_idx] = val;
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_cistatic void cgw_csum_crc8_rel(struct canfd_frame *cf,
3038c2ecf20Sopenharmony_ci			      struct cgw_csum_crc8 *crc8)
3048c2ecf20Sopenharmony_ci{
3058c2ecf20Sopenharmony_ci	int from = calc_idx(crc8->from_idx, cf->len);
3068c2ecf20Sopenharmony_ci	int to = calc_idx(crc8->to_idx, cf->len);
3078c2ecf20Sopenharmony_ci	int res = calc_idx(crc8->result_idx, cf->len);
3088c2ecf20Sopenharmony_ci	u8 crc = crc8->init_crc_val;
3098c2ecf20Sopenharmony_ci	int i;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	if (from < 0 || to < 0 || res < 0)
3128c2ecf20Sopenharmony_ci		return;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	if (from <= to) {
3158c2ecf20Sopenharmony_ci		for (i = crc8->from_idx; i <= crc8->to_idx; i++)
3168c2ecf20Sopenharmony_ci			crc = crc8->crctab[crc ^ cf->data[i]];
3178c2ecf20Sopenharmony_ci	} else {
3188c2ecf20Sopenharmony_ci		for (i = crc8->from_idx; i >= crc8->to_idx; i--)
3198c2ecf20Sopenharmony_ci			crc = crc8->crctab[crc ^ cf->data[i]];
3208c2ecf20Sopenharmony_ci	}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	switch (crc8->profile) {
3238c2ecf20Sopenharmony_ci	case CGW_CRC8PRF_1U8:
3248c2ecf20Sopenharmony_ci		crc = crc8->crctab[crc ^ crc8->profile_data[0]];
3258c2ecf20Sopenharmony_ci		break;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	case  CGW_CRC8PRF_16U8:
3288c2ecf20Sopenharmony_ci		crc = crc8->crctab[crc ^ crc8->profile_data[cf->data[1] & 0xF]];
3298c2ecf20Sopenharmony_ci		break;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	case CGW_CRC8PRF_SFFID_XOR:
3328c2ecf20Sopenharmony_ci		crc = crc8->crctab[crc ^ (cf->can_id & 0xFF) ^
3338c2ecf20Sopenharmony_ci				   (cf->can_id >> 8 & 0xFF)];
3348c2ecf20Sopenharmony_ci		break;
3358c2ecf20Sopenharmony_ci	}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	cf->data[crc8->result_idx] = crc ^ crc8->final_xor_val;
3388c2ecf20Sopenharmony_ci}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_cistatic void cgw_csum_crc8_pos(struct canfd_frame *cf,
3418c2ecf20Sopenharmony_ci			      struct cgw_csum_crc8 *crc8)
3428c2ecf20Sopenharmony_ci{
3438c2ecf20Sopenharmony_ci	u8 crc = crc8->init_crc_val;
3448c2ecf20Sopenharmony_ci	int i;
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	for (i = crc8->from_idx; i <= crc8->to_idx; i++)
3478c2ecf20Sopenharmony_ci		crc = crc8->crctab[crc ^ cf->data[i]];
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	switch (crc8->profile) {
3508c2ecf20Sopenharmony_ci	case CGW_CRC8PRF_1U8:
3518c2ecf20Sopenharmony_ci		crc = crc8->crctab[crc ^ crc8->profile_data[0]];
3528c2ecf20Sopenharmony_ci		break;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	case  CGW_CRC8PRF_16U8:
3558c2ecf20Sopenharmony_ci		crc = crc8->crctab[crc ^ crc8->profile_data[cf->data[1] & 0xF]];
3568c2ecf20Sopenharmony_ci		break;
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	case CGW_CRC8PRF_SFFID_XOR:
3598c2ecf20Sopenharmony_ci		crc = crc8->crctab[crc ^ (cf->can_id & 0xFF) ^
3608c2ecf20Sopenharmony_ci				   (cf->can_id >> 8 & 0xFF)];
3618c2ecf20Sopenharmony_ci		break;
3628c2ecf20Sopenharmony_ci	}
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	cf->data[crc8->result_idx] = crc ^ crc8->final_xor_val;
3658c2ecf20Sopenharmony_ci}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_cistatic void cgw_csum_crc8_neg(struct canfd_frame *cf,
3688c2ecf20Sopenharmony_ci			      struct cgw_csum_crc8 *crc8)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci	u8 crc = crc8->init_crc_val;
3718c2ecf20Sopenharmony_ci	int i;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	for (i = crc8->from_idx; i >= crc8->to_idx; i--)
3748c2ecf20Sopenharmony_ci		crc = crc8->crctab[crc ^ cf->data[i]];
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	switch (crc8->profile) {
3778c2ecf20Sopenharmony_ci	case CGW_CRC8PRF_1U8:
3788c2ecf20Sopenharmony_ci		crc = crc8->crctab[crc ^ crc8->profile_data[0]];
3798c2ecf20Sopenharmony_ci		break;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	case  CGW_CRC8PRF_16U8:
3828c2ecf20Sopenharmony_ci		crc = crc8->crctab[crc ^ crc8->profile_data[cf->data[1] & 0xF]];
3838c2ecf20Sopenharmony_ci		break;
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	case CGW_CRC8PRF_SFFID_XOR:
3868c2ecf20Sopenharmony_ci		crc = crc8->crctab[crc ^ (cf->can_id & 0xFF) ^
3878c2ecf20Sopenharmony_ci				   (cf->can_id >> 8 & 0xFF)];
3888c2ecf20Sopenharmony_ci		break;
3898c2ecf20Sopenharmony_ci	}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	cf->data[crc8->result_idx] = crc ^ crc8->final_xor_val;
3928c2ecf20Sopenharmony_ci}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci/* the receive & process & send function */
3958c2ecf20Sopenharmony_cistatic void can_can_gw_rcv(struct sk_buff *skb, void *data)
3968c2ecf20Sopenharmony_ci{
3978c2ecf20Sopenharmony_ci	struct cgw_job *gwj = (struct cgw_job *)data;
3988c2ecf20Sopenharmony_ci	struct canfd_frame *cf;
3998c2ecf20Sopenharmony_ci	struct sk_buff *nskb;
4008c2ecf20Sopenharmony_ci	int modidx = 0;
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	/* process strictly Classic CAN or CAN FD frames */
4038c2ecf20Sopenharmony_ci	if (gwj->flags & CGW_FLAGS_CAN_FD) {
4048c2ecf20Sopenharmony_ci		if (skb->len != CANFD_MTU)
4058c2ecf20Sopenharmony_ci			return;
4068c2ecf20Sopenharmony_ci	} else {
4078c2ecf20Sopenharmony_ci		if (skb->len != CAN_MTU)
4088c2ecf20Sopenharmony_ci			return;
4098c2ecf20Sopenharmony_ci	}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	/* Do not handle CAN frames routed more than 'max_hops' times.
4128c2ecf20Sopenharmony_ci	 * In general we should never catch this delimiter which is intended
4138c2ecf20Sopenharmony_ci	 * to cover a misconfiguration protection (e.g. circular CAN routes).
4148c2ecf20Sopenharmony_ci	 *
4158c2ecf20Sopenharmony_ci	 * The Controller Area Network controllers only accept CAN frames with
4168c2ecf20Sopenharmony_ci	 * correct CRCs - which are not visible in the controller registers.
4178c2ecf20Sopenharmony_ci	 * According to skbuff.h documentation the csum_start element for IP
4188c2ecf20Sopenharmony_ci	 * checksums is undefined/unused when ip_summed == CHECKSUM_UNNECESSARY.
4198c2ecf20Sopenharmony_ci	 * Only CAN skbs can be processed here which already have this property.
4208c2ecf20Sopenharmony_ci	 */
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci#define cgw_hops(skb) ((skb)->csum_start)
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	BUG_ON(skb->ip_summed != CHECKSUM_UNNECESSARY);
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	if (cgw_hops(skb) >= max_hops) {
4278c2ecf20Sopenharmony_ci		/* indicate deleted frames due to misconfiguration */
4288c2ecf20Sopenharmony_ci		gwj->deleted_frames++;
4298c2ecf20Sopenharmony_ci		return;
4308c2ecf20Sopenharmony_ci	}
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	if (!(gwj->dst.dev->flags & IFF_UP)) {
4338c2ecf20Sopenharmony_ci		gwj->dropped_frames++;
4348c2ecf20Sopenharmony_ci		return;
4358c2ecf20Sopenharmony_ci	}
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	/* is sending the skb back to the incoming interface not allowed? */
4388c2ecf20Sopenharmony_ci	if (!(gwj->flags & CGW_FLAGS_CAN_IIF_TX_OK) &&
4398c2ecf20Sopenharmony_ci	    can_skb_prv(skb)->ifindex == gwj->dst.dev->ifindex)
4408c2ecf20Sopenharmony_ci		return;
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	/* clone the given skb, which has not been done in can_rcv()
4438c2ecf20Sopenharmony_ci	 *
4448c2ecf20Sopenharmony_ci	 * When there is at least one modification function activated,
4458c2ecf20Sopenharmony_ci	 * we need to copy the skb as we want to modify skb->data.
4468c2ecf20Sopenharmony_ci	 */
4478c2ecf20Sopenharmony_ci	if (gwj->mod.modfunc[0])
4488c2ecf20Sopenharmony_ci		nskb = skb_copy(skb, GFP_ATOMIC);
4498c2ecf20Sopenharmony_ci	else
4508c2ecf20Sopenharmony_ci		nskb = skb_clone(skb, GFP_ATOMIC);
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	if (!nskb) {
4538c2ecf20Sopenharmony_ci		gwj->dropped_frames++;
4548c2ecf20Sopenharmony_ci		return;
4558c2ecf20Sopenharmony_ci	}
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	/* put the incremented hop counter in the cloned skb */
4588c2ecf20Sopenharmony_ci	cgw_hops(nskb) = cgw_hops(skb) + 1;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	/* first processing of this CAN frame -> adjust to private hop limit */
4618c2ecf20Sopenharmony_ci	if (gwj->limit_hops && cgw_hops(nskb) == 1)
4628c2ecf20Sopenharmony_ci		cgw_hops(nskb) = max_hops - gwj->limit_hops + 1;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	nskb->dev = gwj->dst.dev;
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	/* pointer to modifiable CAN frame */
4678c2ecf20Sopenharmony_ci	cf = (struct canfd_frame *)nskb->data;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	/* perform preprocessed modification functions if there are any */
4708c2ecf20Sopenharmony_ci	while (modidx < MAX_MODFUNCTIONS && gwj->mod.modfunc[modidx])
4718c2ecf20Sopenharmony_ci		(*gwj->mod.modfunc[modidx++])(cf, &gwj->mod);
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	/* Has the CAN frame been modified? */
4748c2ecf20Sopenharmony_ci	if (modidx) {
4758c2ecf20Sopenharmony_ci		/* get available space for the processed CAN frame type */
4768c2ecf20Sopenharmony_ci		int max_len = nskb->len - offsetof(struct canfd_frame, data);
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci		/* dlc may have changed, make sure it fits to the CAN frame */
4798c2ecf20Sopenharmony_ci		if (cf->len > max_len) {
4808c2ecf20Sopenharmony_ci			/* delete frame due to misconfiguration */
4818c2ecf20Sopenharmony_ci			gwj->deleted_frames++;
4828c2ecf20Sopenharmony_ci			kfree_skb(nskb);
4838c2ecf20Sopenharmony_ci			return;
4848c2ecf20Sopenharmony_ci		}
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci		/* check for checksum updates */
4878c2ecf20Sopenharmony_ci		if (gwj->mod.csumfunc.crc8)
4888c2ecf20Sopenharmony_ci			(*gwj->mod.csumfunc.crc8)(cf, &gwj->mod.csum.crc8);
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci		if (gwj->mod.csumfunc.xor)
4918c2ecf20Sopenharmony_ci			(*gwj->mod.csumfunc.xor)(cf, &gwj->mod.csum.xor);
4928c2ecf20Sopenharmony_ci	}
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	/* clear the skb timestamp if not configured the other way */
4958c2ecf20Sopenharmony_ci	if (!(gwj->flags & CGW_FLAGS_CAN_SRC_TSTAMP))
4968c2ecf20Sopenharmony_ci		nskb->tstamp = 0;
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	/* send to netdevice */
4998c2ecf20Sopenharmony_ci	if (can_send(nskb, gwj->flags & CGW_FLAGS_CAN_ECHO))
5008c2ecf20Sopenharmony_ci		gwj->dropped_frames++;
5018c2ecf20Sopenharmony_ci	else
5028c2ecf20Sopenharmony_ci		gwj->handled_frames++;
5038c2ecf20Sopenharmony_ci}
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_cistatic inline int cgw_register_filter(struct net *net, struct cgw_job *gwj)
5068c2ecf20Sopenharmony_ci{
5078c2ecf20Sopenharmony_ci	return can_rx_register(net, gwj->src.dev, gwj->ccgw.filter.can_id,
5088c2ecf20Sopenharmony_ci			       gwj->ccgw.filter.can_mask, can_can_gw_rcv,
5098c2ecf20Sopenharmony_ci			       gwj, "gw", NULL);
5108c2ecf20Sopenharmony_ci}
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_cistatic inline void cgw_unregister_filter(struct net *net, struct cgw_job *gwj)
5138c2ecf20Sopenharmony_ci{
5148c2ecf20Sopenharmony_ci	can_rx_unregister(net, gwj->src.dev, gwj->ccgw.filter.can_id,
5158c2ecf20Sopenharmony_ci			  gwj->ccgw.filter.can_mask, can_can_gw_rcv, gwj);
5168c2ecf20Sopenharmony_ci}
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_cistatic int cgw_notifier(struct notifier_block *nb,
5198c2ecf20Sopenharmony_ci			unsigned long msg, void *ptr)
5208c2ecf20Sopenharmony_ci{
5218c2ecf20Sopenharmony_ci	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
5228c2ecf20Sopenharmony_ci	struct net *net = dev_net(dev);
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	if (dev->type != ARPHRD_CAN)
5258c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	if (msg == NETDEV_UNREGISTER) {
5288c2ecf20Sopenharmony_ci		struct cgw_job *gwj = NULL;
5298c2ecf20Sopenharmony_ci		struct hlist_node *nx;
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci		ASSERT_RTNL();
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci		hlist_for_each_entry_safe(gwj, nx, &net->can.cgw_list, list) {
5348c2ecf20Sopenharmony_ci			if (gwj->src.dev == dev || gwj->dst.dev == dev) {
5358c2ecf20Sopenharmony_ci				hlist_del(&gwj->list);
5368c2ecf20Sopenharmony_ci				cgw_unregister_filter(net, gwj);
5378c2ecf20Sopenharmony_ci				synchronize_rcu();
5388c2ecf20Sopenharmony_ci				kmem_cache_free(cgw_cache, gwj);
5398c2ecf20Sopenharmony_ci			}
5408c2ecf20Sopenharmony_ci		}
5418c2ecf20Sopenharmony_ci	}
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
5448c2ecf20Sopenharmony_ci}
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_cistatic int cgw_put_job(struct sk_buff *skb, struct cgw_job *gwj, int type,
5478c2ecf20Sopenharmony_ci		       u32 pid, u32 seq, int flags)
5488c2ecf20Sopenharmony_ci{
5498c2ecf20Sopenharmony_ci	struct rtcanmsg *rtcan;
5508c2ecf20Sopenharmony_ci	struct nlmsghdr *nlh;
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	nlh = nlmsg_put(skb, pid, seq, type, sizeof(*rtcan), flags);
5538c2ecf20Sopenharmony_ci	if (!nlh)
5548c2ecf20Sopenharmony_ci		return -EMSGSIZE;
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	rtcan = nlmsg_data(nlh);
5578c2ecf20Sopenharmony_ci	rtcan->can_family = AF_CAN;
5588c2ecf20Sopenharmony_ci	rtcan->gwtype = gwj->gwtype;
5598c2ecf20Sopenharmony_ci	rtcan->flags = gwj->flags;
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	/* add statistics if available */
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	if (gwj->handled_frames) {
5648c2ecf20Sopenharmony_ci		if (nla_put_u32(skb, CGW_HANDLED, gwj->handled_frames) < 0)
5658c2ecf20Sopenharmony_ci			goto cancel;
5668c2ecf20Sopenharmony_ci	}
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	if (gwj->dropped_frames) {
5698c2ecf20Sopenharmony_ci		if (nla_put_u32(skb, CGW_DROPPED, gwj->dropped_frames) < 0)
5708c2ecf20Sopenharmony_ci			goto cancel;
5718c2ecf20Sopenharmony_ci	}
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	if (gwj->deleted_frames) {
5748c2ecf20Sopenharmony_ci		if (nla_put_u32(skb, CGW_DELETED, gwj->deleted_frames) < 0)
5758c2ecf20Sopenharmony_ci			goto cancel;
5768c2ecf20Sopenharmony_ci	}
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	/* check non default settings of attributes */
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	if (gwj->limit_hops) {
5818c2ecf20Sopenharmony_ci		if (nla_put_u8(skb, CGW_LIM_HOPS, gwj->limit_hops) < 0)
5828c2ecf20Sopenharmony_ci			goto cancel;
5838c2ecf20Sopenharmony_ci	}
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	if (gwj->flags & CGW_FLAGS_CAN_FD) {
5868c2ecf20Sopenharmony_ci		struct cgw_fdframe_mod mb;
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci		if (gwj->mod.modtype.and) {
5898c2ecf20Sopenharmony_ci			memcpy(&mb.cf, &gwj->mod.modframe.and, sizeof(mb.cf));
5908c2ecf20Sopenharmony_ci			mb.modtype = gwj->mod.modtype.and;
5918c2ecf20Sopenharmony_ci			if (nla_put(skb, CGW_FDMOD_AND, sizeof(mb), &mb) < 0)
5928c2ecf20Sopenharmony_ci				goto cancel;
5938c2ecf20Sopenharmony_ci		}
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci		if (gwj->mod.modtype.or) {
5968c2ecf20Sopenharmony_ci			memcpy(&mb.cf, &gwj->mod.modframe.or, sizeof(mb.cf));
5978c2ecf20Sopenharmony_ci			mb.modtype = gwj->mod.modtype.or;
5988c2ecf20Sopenharmony_ci			if (nla_put(skb, CGW_FDMOD_OR, sizeof(mb), &mb) < 0)
5998c2ecf20Sopenharmony_ci				goto cancel;
6008c2ecf20Sopenharmony_ci		}
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci		if (gwj->mod.modtype.xor) {
6038c2ecf20Sopenharmony_ci			memcpy(&mb.cf, &gwj->mod.modframe.xor, sizeof(mb.cf));
6048c2ecf20Sopenharmony_ci			mb.modtype = gwj->mod.modtype.xor;
6058c2ecf20Sopenharmony_ci			if (nla_put(skb, CGW_FDMOD_XOR, sizeof(mb), &mb) < 0)
6068c2ecf20Sopenharmony_ci				goto cancel;
6078c2ecf20Sopenharmony_ci		}
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci		if (gwj->mod.modtype.set) {
6108c2ecf20Sopenharmony_ci			memcpy(&mb.cf, &gwj->mod.modframe.set, sizeof(mb.cf));
6118c2ecf20Sopenharmony_ci			mb.modtype = gwj->mod.modtype.set;
6128c2ecf20Sopenharmony_ci			if (nla_put(skb, CGW_FDMOD_SET, sizeof(mb), &mb) < 0)
6138c2ecf20Sopenharmony_ci				goto cancel;
6148c2ecf20Sopenharmony_ci		}
6158c2ecf20Sopenharmony_ci	} else {
6168c2ecf20Sopenharmony_ci		struct cgw_frame_mod mb;
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci		if (gwj->mod.modtype.and) {
6198c2ecf20Sopenharmony_ci			memcpy(&mb.cf, &gwj->mod.modframe.and, sizeof(mb.cf));
6208c2ecf20Sopenharmony_ci			mb.modtype = gwj->mod.modtype.and;
6218c2ecf20Sopenharmony_ci			if (nla_put(skb, CGW_MOD_AND, sizeof(mb), &mb) < 0)
6228c2ecf20Sopenharmony_ci				goto cancel;
6238c2ecf20Sopenharmony_ci		}
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci		if (gwj->mod.modtype.or) {
6268c2ecf20Sopenharmony_ci			memcpy(&mb.cf, &gwj->mod.modframe.or, sizeof(mb.cf));
6278c2ecf20Sopenharmony_ci			mb.modtype = gwj->mod.modtype.or;
6288c2ecf20Sopenharmony_ci			if (nla_put(skb, CGW_MOD_OR, sizeof(mb), &mb) < 0)
6298c2ecf20Sopenharmony_ci				goto cancel;
6308c2ecf20Sopenharmony_ci		}
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci		if (gwj->mod.modtype.xor) {
6338c2ecf20Sopenharmony_ci			memcpy(&mb.cf, &gwj->mod.modframe.xor, sizeof(mb.cf));
6348c2ecf20Sopenharmony_ci			mb.modtype = gwj->mod.modtype.xor;
6358c2ecf20Sopenharmony_ci			if (nla_put(skb, CGW_MOD_XOR, sizeof(mb), &mb) < 0)
6368c2ecf20Sopenharmony_ci				goto cancel;
6378c2ecf20Sopenharmony_ci		}
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci		if (gwj->mod.modtype.set) {
6408c2ecf20Sopenharmony_ci			memcpy(&mb.cf, &gwj->mod.modframe.set, sizeof(mb.cf));
6418c2ecf20Sopenharmony_ci			mb.modtype = gwj->mod.modtype.set;
6428c2ecf20Sopenharmony_ci			if (nla_put(skb, CGW_MOD_SET, sizeof(mb), &mb) < 0)
6438c2ecf20Sopenharmony_ci				goto cancel;
6448c2ecf20Sopenharmony_ci		}
6458c2ecf20Sopenharmony_ci	}
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	if (gwj->mod.uid) {
6488c2ecf20Sopenharmony_ci		if (nla_put_u32(skb, CGW_MOD_UID, gwj->mod.uid) < 0)
6498c2ecf20Sopenharmony_ci			goto cancel;
6508c2ecf20Sopenharmony_ci	}
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	if (gwj->mod.csumfunc.crc8) {
6538c2ecf20Sopenharmony_ci		if (nla_put(skb, CGW_CS_CRC8, CGW_CS_CRC8_LEN,
6548c2ecf20Sopenharmony_ci			    &gwj->mod.csum.crc8) < 0)
6558c2ecf20Sopenharmony_ci			goto cancel;
6568c2ecf20Sopenharmony_ci	}
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	if (gwj->mod.csumfunc.xor) {
6598c2ecf20Sopenharmony_ci		if (nla_put(skb, CGW_CS_XOR, CGW_CS_XOR_LEN,
6608c2ecf20Sopenharmony_ci			    &gwj->mod.csum.xor) < 0)
6618c2ecf20Sopenharmony_ci			goto cancel;
6628c2ecf20Sopenharmony_ci	}
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	if (gwj->gwtype == CGW_TYPE_CAN_CAN) {
6658c2ecf20Sopenharmony_ci		if (gwj->ccgw.filter.can_id || gwj->ccgw.filter.can_mask) {
6668c2ecf20Sopenharmony_ci			if (nla_put(skb, CGW_FILTER, sizeof(struct can_filter),
6678c2ecf20Sopenharmony_ci				    &gwj->ccgw.filter) < 0)
6688c2ecf20Sopenharmony_ci				goto cancel;
6698c2ecf20Sopenharmony_ci		}
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci		if (nla_put_u32(skb, CGW_SRC_IF, gwj->ccgw.src_idx) < 0)
6728c2ecf20Sopenharmony_ci			goto cancel;
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci		if (nla_put_u32(skb, CGW_DST_IF, gwj->ccgw.dst_idx) < 0)
6758c2ecf20Sopenharmony_ci			goto cancel;
6768c2ecf20Sopenharmony_ci	}
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	nlmsg_end(skb, nlh);
6798c2ecf20Sopenharmony_ci	return 0;
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_cicancel:
6828c2ecf20Sopenharmony_ci	nlmsg_cancel(skb, nlh);
6838c2ecf20Sopenharmony_ci	return -EMSGSIZE;
6848c2ecf20Sopenharmony_ci}
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci/* Dump information about all CAN gateway jobs, in response to RTM_GETROUTE */
6878c2ecf20Sopenharmony_cistatic int cgw_dump_jobs(struct sk_buff *skb, struct netlink_callback *cb)
6888c2ecf20Sopenharmony_ci{
6898c2ecf20Sopenharmony_ci	struct net *net = sock_net(skb->sk);
6908c2ecf20Sopenharmony_ci	struct cgw_job *gwj = NULL;
6918c2ecf20Sopenharmony_ci	int idx = 0;
6928c2ecf20Sopenharmony_ci	int s_idx = cb->args[0];
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci	rcu_read_lock();
6958c2ecf20Sopenharmony_ci	hlist_for_each_entry_rcu(gwj, &net->can.cgw_list, list) {
6968c2ecf20Sopenharmony_ci		if (idx < s_idx)
6978c2ecf20Sopenharmony_ci			goto cont;
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci		if (cgw_put_job(skb, gwj, RTM_NEWROUTE,
7008c2ecf20Sopenharmony_ci				NETLINK_CB(cb->skb).portid,
7018c2ecf20Sopenharmony_ci				cb->nlh->nlmsg_seq, NLM_F_MULTI) < 0)
7028c2ecf20Sopenharmony_ci			break;
7038c2ecf20Sopenharmony_cicont:
7048c2ecf20Sopenharmony_ci		idx++;
7058c2ecf20Sopenharmony_ci	}
7068c2ecf20Sopenharmony_ci	rcu_read_unlock();
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	cb->args[0] = idx;
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	return skb->len;
7118c2ecf20Sopenharmony_ci}
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_cistatic const struct nla_policy cgw_policy[CGW_MAX + 1] = {
7148c2ecf20Sopenharmony_ci	[CGW_MOD_AND]	= { .len = sizeof(struct cgw_frame_mod) },
7158c2ecf20Sopenharmony_ci	[CGW_MOD_OR]	= { .len = sizeof(struct cgw_frame_mod) },
7168c2ecf20Sopenharmony_ci	[CGW_MOD_XOR]	= { .len = sizeof(struct cgw_frame_mod) },
7178c2ecf20Sopenharmony_ci	[CGW_MOD_SET]	= { .len = sizeof(struct cgw_frame_mod) },
7188c2ecf20Sopenharmony_ci	[CGW_CS_XOR]	= { .len = sizeof(struct cgw_csum_xor) },
7198c2ecf20Sopenharmony_ci	[CGW_CS_CRC8]	= { .len = sizeof(struct cgw_csum_crc8) },
7208c2ecf20Sopenharmony_ci	[CGW_SRC_IF]	= { .type = NLA_U32 },
7218c2ecf20Sopenharmony_ci	[CGW_DST_IF]	= { .type = NLA_U32 },
7228c2ecf20Sopenharmony_ci	[CGW_FILTER]	= { .len = sizeof(struct can_filter) },
7238c2ecf20Sopenharmony_ci	[CGW_LIM_HOPS]	= { .type = NLA_U8 },
7248c2ecf20Sopenharmony_ci	[CGW_MOD_UID]	= { .type = NLA_U32 },
7258c2ecf20Sopenharmony_ci	[CGW_FDMOD_AND]	= { .len = sizeof(struct cgw_fdframe_mod) },
7268c2ecf20Sopenharmony_ci	[CGW_FDMOD_OR]	= { .len = sizeof(struct cgw_fdframe_mod) },
7278c2ecf20Sopenharmony_ci	[CGW_FDMOD_XOR]	= { .len = sizeof(struct cgw_fdframe_mod) },
7288c2ecf20Sopenharmony_ci	[CGW_FDMOD_SET]	= { .len = sizeof(struct cgw_fdframe_mod) },
7298c2ecf20Sopenharmony_ci};
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci/* check for common and gwtype specific attributes */
7328c2ecf20Sopenharmony_cistatic int cgw_parse_attr(struct nlmsghdr *nlh, struct cf_mod *mod,
7338c2ecf20Sopenharmony_ci			  u8 gwtype, void *gwtypeattr, u8 *limhops)
7348c2ecf20Sopenharmony_ci{
7358c2ecf20Sopenharmony_ci	struct nlattr *tb[CGW_MAX + 1];
7368c2ecf20Sopenharmony_ci	struct rtcanmsg *r = nlmsg_data(nlh);
7378c2ecf20Sopenharmony_ci	int modidx = 0;
7388c2ecf20Sopenharmony_ci	int err = 0;
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci	/* initialize modification & checksum data space */
7418c2ecf20Sopenharmony_ci	memset(mod, 0, sizeof(*mod));
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci	err = nlmsg_parse_deprecated(nlh, sizeof(struct rtcanmsg), tb,
7448c2ecf20Sopenharmony_ci				     CGW_MAX, cgw_policy, NULL);
7458c2ecf20Sopenharmony_ci	if (err < 0)
7468c2ecf20Sopenharmony_ci		return err;
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci	if (tb[CGW_LIM_HOPS]) {
7498c2ecf20Sopenharmony_ci		*limhops = nla_get_u8(tb[CGW_LIM_HOPS]);
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci		if (*limhops < 1 || *limhops > max_hops)
7528c2ecf20Sopenharmony_ci			return -EINVAL;
7538c2ecf20Sopenharmony_ci	}
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	/* check for AND/OR/XOR/SET modifications */
7568c2ecf20Sopenharmony_ci	if (r->flags & CGW_FLAGS_CAN_FD) {
7578c2ecf20Sopenharmony_ci		struct cgw_fdframe_mod mb;
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci		if (tb[CGW_FDMOD_AND]) {
7608c2ecf20Sopenharmony_ci			nla_memcpy(&mb, tb[CGW_FDMOD_AND], CGW_FDMODATTR_LEN);
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci			canfdframecpy(&mod->modframe.and, &mb.cf);
7638c2ecf20Sopenharmony_ci			mod->modtype.and = mb.modtype;
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci			if (mb.modtype & CGW_MOD_ID)
7668c2ecf20Sopenharmony_ci				mod->modfunc[modidx++] = mod_and_id;
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci			if (mb.modtype & CGW_MOD_LEN)
7698c2ecf20Sopenharmony_ci				mod->modfunc[modidx++] = mod_and_len;
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci			if (mb.modtype & CGW_MOD_FLAGS)
7728c2ecf20Sopenharmony_ci				mod->modfunc[modidx++] = mod_and_flags;
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci			if (mb.modtype & CGW_MOD_DATA)
7758c2ecf20Sopenharmony_ci				mod->modfunc[modidx++] = mod_and_fddata;
7768c2ecf20Sopenharmony_ci		}
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci		if (tb[CGW_FDMOD_OR]) {
7798c2ecf20Sopenharmony_ci			nla_memcpy(&mb, tb[CGW_FDMOD_OR], CGW_FDMODATTR_LEN);
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci			canfdframecpy(&mod->modframe.or, &mb.cf);
7828c2ecf20Sopenharmony_ci			mod->modtype.or = mb.modtype;
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci			if (mb.modtype & CGW_MOD_ID)
7858c2ecf20Sopenharmony_ci				mod->modfunc[modidx++] = mod_or_id;
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci			if (mb.modtype & CGW_MOD_LEN)
7888c2ecf20Sopenharmony_ci				mod->modfunc[modidx++] = mod_or_len;
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci			if (mb.modtype & CGW_MOD_FLAGS)
7918c2ecf20Sopenharmony_ci				mod->modfunc[modidx++] = mod_or_flags;
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci			if (mb.modtype & CGW_MOD_DATA)
7948c2ecf20Sopenharmony_ci				mod->modfunc[modidx++] = mod_or_fddata;
7958c2ecf20Sopenharmony_ci		}
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci		if (tb[CGW_FDMOD_XOR]) {
7988c2ecf20Sopenharmony_ci			nla_memcpy(&mb, tb[CGW_FDMOD_XOR], CGW_FDMODATTR_LEN);
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci			canfdframecpy(&mod->modframe.xor, &mb.cf);
8018c2ecf20Sopenharmony_ci			mod->modtype.xor = mb.modtype;
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci			if (mb.modtype & CGW_MOD_ID)
8048c2ecf20Sopenharmony_ci				mod->modfunc[modidx++] = mod_xor_id;
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci			if (mb.modtype & CGW_MOD_LEN)
8078c2ecf20Sopenharmony_ci				mod->modfunc[modidx++] = mod_xor_len;
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci			if (mb.modtype & CGW_MOD_FLAGS)
8108c2ecf20Sopenharmony_ci				mod->modfunc[modidx++] = mod_xor_flags;
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci			if (mb.modtype & CGW_MOD_DATA)
8138c2ecf20Sopenharmony_ci				mod->modfunc[modidx++] = mod_xor_fddata;
8148c2ecf20Sopenharmony_ci		}
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci		if (tb[CGW_FDMOD_SET]) {
8178c2ecf20Sopenharmony_ci			nla_memcpy(&mb, tb[CGW_FDMOD_SET], CGW_FDMODATTR_LEN);
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci			canfdframecpy(&mod->modframe.set, &mb.cf);
8208c2ecf20Sopenharmony_ci			mod->modtype.set = mb.modtype;
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci			if (mb.modtype & CGW_MOD_ID)
8238c2ecf20Sopenharmony_ci				mod->modfunc[modidx++] = mod_set_id;
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci			if (mb.modtype & CGW_MOD_LEN)
8268c2ecf20Sopenharmony_ci				mod->modfunc[modidx++] = mod_set_len;
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci			if (mb.modtype & CGW_MOD_FLAGS)
8298c2ecf20Sopenharmony_ci				mod->modfunc[modidx++] = mod_set_flags;
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci			if (mb.modtype & CGW_MOD_DATA)
8328c2ecf20Sopenharmony_ci				mod->modfunc[modidx++] = mod_set_fddata;
8338c2ecf20Sopenharmony_ci		}
8348c2ecf20Sopenharmony_ci	} else {
8358c2ecf20Sopenharmony_ci		struct cgw_frame_mod mb;
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci		if (tb[CGW_MOD_AND]) {
8388c2ecf20Sopenharmony_ci			nla_memcpy(&mb, tb[CGW_MOD_AND], CGW_MODATTR_LEN);
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci			canframecpy(&mod->modframe.and, &mb.cf);
8418c2ecf20Sopenharmony_ci			mod->modtype.and = mb.modtype;
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci			if (mb.modtype & CGW_MOD_ID)
8448c2ecf20Sopenharmony_ci				mod->modfunc[modidx++] = mod_and_id;
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci			if (mb.modtype & CGW_MOD_LEN)
8478c2ecf20Sopenharmony_ci				mod->modfunc[modidx++] = mod_and_len;
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci			if (mb.modtype & CGW_MOD_DATA)
8508c2ecf20Sopenharmony_ci				mod->modfunc[modidx++] = mod_and_data;
8518c2ecf20Sopenharmony_ci		}
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci		if (tb[CGW_MOD_OR]) {
8548c2ecf20Sopenharmony_ci			nla_memcpy(&mb, tb[CGW_MOD_OR], CGW_MODATTR_LEN);
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci			canframecpy(&mod->modframe.or, &mb.cf);
8578c2ecf20Sopenharmony_ci			mod->modtype.or = mb.modtype;
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci			if (mb.modtype & CGW_MOD_ID)
8608c2ecf20Sopenharmony_ci				mod->modfunc[modidx++] = mod_or_id;
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci			if (mb.modtype & CGW_MOD_LEN)
8638c2ecf20Sopenharmony_ci				mod->modfunc[modidx++] = mod_or_len;
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci			if (mb.modtype & CGW_MOD_DATA)
8668c2ecf20Sopenharmony_ci				mod->modfunc[modidx++] = mod_or_data;
8678c2ecf20Sopenharmony_ci		}
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci		if (tb[CGW_MOD_XOR]) {
8708c2ecf20Sopenharmony_ci			nla_memcpy(&mb, tb[CGW_MOD_XOR], CGW_MODATTR_LEN);
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci			canframecpy(&mod->modframe.xor, &mb.cf);
8738c2ecf20Sopenharmony_ci			mod->modtype.xor = mb.modtype;
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_ci			if (mb.modtype & CGW_MOD_ID)
8768c2ecf20Sopenharmony_ci				mod->modfunc[modidx++] = mod_xor_id;
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci			if (mb.modtype & CGW_MOD_LEN)
8798c2ecf20Sopenharmony_ci				mod->modfunc[modidx++] = mod_xor_len;
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_ci			if (mb.modtype & CGW_MOD_DATA)
8828c2ecf20Sopenharmony_ci				mod->modfunc[modidx++] = mod_xor_data;
8838c2ecf20Sopenharmony_ci		}
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_ci		if (tb[CGW_MOD_SET]) {
8868c2ecf20Sopenharmony_ci			nla_memcpy(&mb, tb[CGW_MOD_SET], CGW_MODATTR_LEN);
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci			canframecpy(&mod->modframe.set, &mb.cf);
8898c2ecf20Sopenharmony_ci			mod->modtype.set = mb.modtype;
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci			if (mb.modtype & CGW_MOD_ID)
8928c2ecf20Sopenharmony_ci				mod->modfunc[modidx++] = mod_set_id;
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci			if (mb.modtype & CGW_MOD_LEN)
8958c2ecf20Sopenharmony_ci				mod->modfunc[modidx++] = mod_set_len;
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ci			if (mb.modtype & CGW_MOD_DATA)
8988c2ecf20Sopenharmony_ci				mod->modfunc[modidx++] = mod_set_data;
8998c2ecf20Sopenharmony_ci		}
9008c2ecf20Sopenharmony_ci	}
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_ci	/* check for checksum operations after CAN frame modifications */
9038c2ecf20Sopenharmony_ci	if (modidx) {
9048c2ecf20Sopenharmony_ci		if (tb[CGW_CS_CRC8]) {
9058c2ecf20Sopenharmony_ci			struct cgw_csum_crc8 *c = nla_data(tb[CGW_CS_CRC8]);
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci			err = cgw_chk_csum_parms(c->from_idx, c->to_idx,
9088c2ecf20Sopenharmony_ci						 c->result_idx, r);
9098c2ecf20Sopenharmony_ci			if (err)
9108c2ecf20Sopenharmony_ci				return err;
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci			nla_memcpy(&mod->csum.crc8, tb[CGW_CS_CRC8],
9138c2ecf20Sopenharmony_ci				   CGW_CS_CRC8_LEN);
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci			/* select dedicated processing function to reduce
9168c2ecf20Sopenharmony_ci			 * runtime operations in receive hot path.
9178c2ecf20Sopenharmony_ci			 */
9188c2ecf20Sopenharmony_ci			if (c->from_idx < 0 || c->to_idx < 0 ||
9198c2ecf20Sopenharmony_ci			    c->result_idx < 0)
9208c2ecf20Sopenharmony_ci				mod->csumfunc.crc8 = cgw_csum_crc8_rel;
9218c2ecf20Sopenharmony_ci			else if (c->from_idx <= c->to_idx)
9228c2ecf20Sopenharmony_ci				mod->csumfunc.crc8 = cgw_csum_crc8_pos;
9238c2ecf20Sopenharmony_ci			else
9248c2ecf20Sopenharmony_ci				mod->csumfunc.crc8 = cgw_csum_crc8_neg;
9258c2ecf20Sopenharmony_ci		}
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_ci		if (tb[CGW_CS_XOR]) {
9288c2ecf20Sopenharmony_ci			struct cgw_csum_xor *c = nla_data(tb[CGW_CS_XOR]);
9298c2ecf20Sopenharmony_ci
9308c2ecf20Sopenharmony_ci			err = cgw_chk_csum_parms(c->from_idx, c->to_idx,
9318c2ecf20Sopenharmony_ci						 c->result_idx, r);
9328c2ecf20Sopenharmony_ci			if (err)
9338c2ecf20Sopenharmony_ci				return err;
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ci			nla_memcpy(&mod->csum.xor, tb[CGW_CS_XOR],
9368c2ecf20Sopenharmony_ci				   CGW_CS_XOR_LEN);
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci			/* select dedicated processing function to reduce
9398c2ecf20Sopenharmony_ci			 * runtime operations in receive hot path.
9408c2ecf20Sopenharmony_ci			 */
9418c2ecf20Sopenharmony_ci			if (c->from_idx < 0 || c->to_idx < 0 ||
9428c2ecf20Sopenharmony_ci			    c->result_idx < 0)
9438c2ecf20Sopenharmony_ci				mod->csumfunc.xor = cgw_csum_xor_rel;
9448c2ecf20Sopenharmony_ci			else if (c->from_idx <= c->to_idx)
9458c2ecf20Sopenharmony_ci				mod->csumfunc.xor = cgw_csum_xor_pos;
9468c2ecf20Sopenharmony_ci			else
9478c2ecf20Sopenharmony_ci				mod->csumfunc.xor = cgw_csum_xor_neg;
9488c2ecf20Sopenharmony_ci		}
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci		if (tb[CGW_MOD_UID])
9518c2ecf20Sopenharmony_ci			nla_memcpy(&mod->uid, tb[CGW_MOD_UID], sizeof(u32));
9528c2ecf20Sopenharmony_ci	}
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci	if (gwtype == CGW_TYPE_CAN_CAN) {
9558c2ecf20Sopenharmony_ci		/* check CGW_TYPE_CAN_CAN specific attributes */
9568c2ecf20Sopenharmony_ci		struct can_can_gw *ccgw = (struct can_can_gw *)gwtypeattr;
9578c2ecf20Sopenharmony_ci
9588c2ecf20Sopenharmony_ci		memset(ccgw, 0, sizeof(*ccgw));
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci		/* check for can_filter in attributes */
9618c2ecf20Sopenharmony_ci		if (tb[CGW_FILTER])
9628c2ecf20Sopenharmony_ci			nla_memcpy(&ccgw->filter, tb[CGW_FILTER],
9638c2ecf20Sopenharmony_ci				   sizeof(struct can_filter));
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_ci		err = -ENODEV;
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci		/* specifying two interfaces is mandatory */
9688c2ecf20Sopenharmony_ci		if (!tb[CGW_SRC_IF] || !tb[CGW_DST_IF])
9698c2ecf20Sopenharmony_ci			return err;
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ci		ccgw->src_idx = nla_get_u32(tb[CGW_SRC_IF]);
9728c2ecf20Sopenharmony_ci		ccgw->dst_idx = nla_get_u32(tb[CGW_DST_IF]);
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ci		/* both indices set to 0 for flushing all routing entries */
9758c2ecf20Sopenharmony_ci		if (!ccgw->src_idx && !ccgw->dst_idx)
9768c2ecf20Sopenharmony_ci			return 0;
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_ci		/* only one index set to 0 is an error */
9798c2ecf20Sopenharmony_ci		if (!ccgw->src_idx || !ccgw->dst_idx)
9808c2ecf20Sopenharmony_ci			return err;
9818c2ecf20Sopenharmony_ci	}
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	/* add the checks for other gwtypes here */
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_ci	return 0;
9868c2ecf20Sopenharmony_ci}
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_cistatic int cgw_create_job(struct sk_buff *skb,  struct nlmsghdr *nlh,
9898c2ecf20Sopenharmony_ci			  struct netlink_ext_ack *extack)
9908c2ecf20Sopenharmony_ci{
9918c2ecf20Sopenharmony_ci	struct net *net = sock_net(skb->sk);
9928c2ecf20Sopenharmony_ci	struct rtcanmsg *r;
9938c2ecf20Sopenharmony_ci	struct cgw_job *gwj;
9948c2ecf20Sopenharmony_ci	struct cf_mod mod;
9958c2ecf20Sopenharmony_ci	struct can_can_gw ccgw;
9968c2ecf20Sopenharmony_ci	u8 limhops = 0;
9978c2ecf20Sopenharmony_ci	int err = 0;
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci	if (!netlink_capable(skb, CAP_NET_ADMIN))
10008c2ecf20Sopenharmony_ci		return -EPERM;
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_ci	if (nlmsg_len(nlh) < sizeof(*r))
10038c2ecf20Sopenharmony_ci		return -EINVAL;
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_ci	r = nlmsg_data(nlh);
10068c2ecf20Sopenharmony_ci	if (r->can_family != AF_CAN)
10078c2ecf20Sopenharmony_ci		return -EPFNOSUPPORT;
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_ci	/* so far we only support CAN -> CAN routings */
10108c2ecf20Sopenharmony_ci	if (r->gwtype != CGW_TYPE_CAN_CAN)
10118c2ecf20Sopenharmony_ci		return -EINVAL;
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_ci	err = cgw_parse_attr(nlh, &mod, CGW_TYPE_CAN_CAN, &ccgw, &limhops);
10148c2ecf20Sopenharmony_ci	if (err < 0)
10158c2ecf20Sopenharmony_ci		return err;
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci	if (mod.uid) {
10188c2ecf20Sopenharmony_ci		ASSERT_RTNL();
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ci		/* check for updating an existing job with identical uid */
10218c2ecf20Sopenharmony_ci		hlist_for_each_entry(gwj, &net->can.cgw_list, list) {
10228c2ecf20Sopenharmony_ci			if (gwj->mod.uid != mod.uid)
10238c2ecf20Sopenharmony_ci				continue;
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_ci			/* interfaces & filters must be identical */
10268c2ecf20Sopenharmony_ci			if (memcmp(&gwj->ccgw, &ccgw, sizeof(ccgw)))
10278c2ecf20Sopenharmony_ci				return -EINVAL;
10288c2ecf20Sopenharmony_ci
10298c2ecf20Sopenharmony_ci			/* update modifications with disabled softirq & quit */
10308c2ecf20Sopenharmony_ci			local_bh_disable();
10318c2ecf20Sopenharmony_ci			memcpy(&gwj->mod, &mod, sizeof(mod));
10328c2ecf20Sopenharmony_ci			local_bh_enable();
10338c2ecf20Sopenharmony_ci			return 0;
10348c2ecf20Sopenharmony_ci		}
10358c2ecf20Sopenharmony_ci	}
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_ci	/* ifindex == 0 is not allowed for job creation */
10388c2ecf20Sopenharmony_ci	if (!ccgw.src_idx || !ccgw.dst_idx)
10398c2ecf20Sopenharmony_ci		return -ENODEV;
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_ci	gwj = kmem_cache_alloc(cgw_cache, GFP_KERNEL);
10428c2ecf20Sopenharmony_ci	if (!gwj)
10438c2ecf20Sopenharmony_ci		return -ENOMEM;
10448c2ecf20Sopenharmony_ci
10458c2ecf20Sopenharmony_ci	gwj->handled_frames = 0;
10468c2ecf20Sopenharmony_ci	gwj->dropped_frames = 0;
10478c2ecf20Sopenharmony_ci	gwj->deleted_frames = 0;
10488c2ecf20Sopenharmony_ci	gwj->flags = r->flags;
10498c2ecf20Sopenharmony_ci	gwj->gwtype = r->gwtype;
10508c2ecf20Sopenharmony_ci	gwj->limit_hops = limhops;
10518c2ecf20Sopenharmony_ci
10528c2ecf20Sopenharmony_ci	/* insert already parsed information */
10538c2ecf20Sopenharmony_ci	memcpy(&gwj->mod, &mod, sizeof(mod));
10548c2ecf20Sopenharmony_ci	memcpy(&gwj->ccgw, &ccgw, sizeof(ccgw));
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_ci	err = -ENODEV;
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_ci	gwj->src.dev = __dev_get_by_index(net, gwj->ccgw.src_idx);
10598c2ecf20Sopenharmony_ci
10608c2ecf20Sopenharmony_ci	if (!gwj->src.dev)
10618c2ecf20Sopenharmony_ci		goto out;
10628c2ecf20Sopenharmony_ci
10638c2ecf20Sopenharmony_ci	if (gwj->src.dev->type != ARPHRD_CAN)
10648c2ecf20Sopenharmony_ci		goto out;
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_ci	gwj->dst.dev = __dev_get_by_index(net, gwj->ccgw.dst_idx);
10678c2ecf20Sopenharmony_ci
10688c2ecf20Sopenharmony_ci	if (!gwj->dst.dev)
10698c2ecf20Sopenharmony_ci		goto out;
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ci	if (gwj->dst.dev->type != ARPHRD_CAN)
10728c2ecf20Sopenharmony_ci		goto out;
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_ci	ASSERT_RTNL();
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_ci	err = cgw_register_filter(net, gwj);
10778c2ecf20Sopenharmony_ci	if (!err)
10788c2ecf20Sopenharmony_ci		hlist_add_head_rcu(&gwj->list, &net->can.cgw_list);
10798c2ecf20Sopenharmony_ciout:
10808c2ecf20Sopenharmony_ci	if (err)
10818c2ecf20Sopenharmony_ci		kmem_cache_free(cgw_cache, gwj);
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci	return err;
10848c2ecf20Sopenharmony_ci}
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_cistatic void cgw_remove_all_jobs(struct net *net)
10878c2ecf20Sopenharmony_ci{
10888c2ecf20Sopenharmony_ci	struct cgw_job *gwj = NULL;
10898c2ecf20Sopenharmony_ci	struct hlist_node *nx;
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_ci	ASSERT_RTNL();
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_ci	hlist_for_each_entry_safe(gwj, nx, &net->can.cgw_list, list) {
10948c2ecf20Sopenharmony_ci		hlist_del(&gwj->list);
10958c2ecf20Sopenharmony_ci		cgw_unregister_filter(net, gwj);
10968c2ecf20Sopenharmony_ci		synchronize_rcu();
10978c2ecf20Sopenharmony_ci		kmem_cache_free(cgw_cache, gwj);
10988c2ecf20Sopenharmony_ci	}
10998c2ecf20Sopenharmony_ci}
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_cistatic int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh,
11028c2ecf20Sopenharmony_ci			  struct netlink_ext_ack *extack)
11038c2ecf20Sopenharmony_ci{
11048c2ecf20Sopenharmony_ci	struct net *net = sock_net(skb->sk);
11058c2ecf20Sopenharmony_ci	struct cgw_job *gwj = NULL;
11068c2ecf20Sopenharmony_ci	struct hlist_node *nx;
11078c2ecf20Sopenharmony_ci	struct rtcanmsg *r;
11088c2ecf20Sopenharmony_ci	struct cf_mod mod;
11098c2ecf20Sopenharmony_ci	struct can_can_gw ccgw;
11108c2ecf20Sopenharmony_ci	u8 limhops = 0;
11118c2ecf20Sopenharmony_ci	int err = 0;
11128c2ecf20Sopenharmony_ci
11138c2ecf20Sopenharmony_ci	if (!netlink_capable(skb, CAP_NET_ADMIN))
11148c2ecf20Sopenharmony_ci		return -EPERM;
11158c2ecf20Sopenharmony_ci
11168c2ecf20Sopenharmony_ci	if (nlmsg_len(nlh) < sizeof(*r))
11178c2ecf20Sopenharmony_ci		return -EINVAL;
11188c2ecf20Sopenharmony_ci
11198c2ecf20Sopenharmony_ci	r = nlmsg_data(nlh);
11208c2ecf20Sopenharmony_ci	if (r->can_family != AF_CAN)
11218c2ecf20Sopenharmony_ci		return -EPFNOSUPPORT;
11228c2ecf20Sopenharmony_ci
11238c2ecf20Sopenharmony_ci	/* so far we only support CAN -> CAN routings */
11248c2ecf20Sopenharmony_ci	if (r->gwtype != CGW_TYPE_CAN_CAN)
11258c2ecf20Sopenharmony_ci		return -EINVAL;
11268c2ecf20Sopenharmony_ci
11278c2ecf20Sopenharmony_ci	err = cgw_parse_attr(nlh, &mod, CGW_TYPE_CAN_CAN, &ccgw, &limhops);
11288c2ecf20Sopenharmony_ci	if (err < 0)
11298c2ecf20Sopenharmony_ci		return err;
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_ci	/* two interface indices both set to 0 => remove all entries */
11328c2ecf20Sopenharmony_ci	if (!ccgw.src_idx && !ccgw.dst_idx) {
11338c2ecf20Sopenharmony_ci		cgw_remove_all_jobs(net);
11348c2ecf20Sopenharmony_ci		return 0;
11358c2ecf20Sopenharmony_ci	}
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_ci	err = -EINVAL;
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_ci	ASSERT_RTNL();
11408c2ecf20Sopenharmony_ci
11418c2ecf20Sopenharmony_ci	/* remove only the first matching entry */
11428c2ecf20Sopenharmony_ci	hlist_for_each_entry_safe(gwj, nx, &net->can.cgw_list, list) {
11438c2ecf20Sopenharmony_ci		if (gwj->flags != r->flags)
11448c2ecf20Sopenharmony_ci			continue;
11458c2ecf20Sopenharmony_ci
11468c2ecf20Sopenharmony_ci		if (gwj->limit_hops != limhops)
11478c2ecf20Sopenharmony_ci			continue;
11488c2ecf20Sopenharmony_ci
11498c2ecf20Sopenharmony_ci		/* we have a match when uid is enabled and identical */
11508c2ecf20Sopenharmony_ci		if (gwj->mod.uid || mod.uid) {
11518c2ecf20Sopenharmony_ci			if (gwj->mod.uid != mod.uid)
11528c2ecf20Sopenharmony_ci				continue;
11538c2ecf20Sopenharmony_ci		} else {
11548c2ecf20Sopenharmony_ci			/* no uid => check for identical modifications */
11558c2ecf20Sopenharmony_ci			if (memcmp(&gwj->mod, &mod, sizeof(mod)))
11568c2ecf20Sopenharmony_ci				continue;
11578c2ecf20Sopenharmony_ci		}
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci		/* if (r->gwtype == CGW_TYPE_CAN_CAN) - is made sure here */
11608c2ecf20Sopenharmony_ci		if (memcmp(&gwj->ccgw, &ccgw, sizeof(ccgw)))
11618c2ecf20Sopenharmony_ci			continue;
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_ci		hlist_del(&gwj->list);
11648c2ecf20Sopenharmony_ci		cgw_unregister_filter(net, gwj);
11658c2ecf20Sopenharmony_ci		synchronize_rcu();
11668c2ecf20Sopenharmony_ci		kmem_cache_free(cgw_cache, gwj);
11678c2ecf20Sopenharmony_ci		err = 0;
11688c2ecf20Sopenharmony_ci		break;
11698c2ecf20Sopenharmony_ci	}
11708c2ecf20Sopenharmony_ci
11718c2ecf20Sopenharmony_ci	return err;
11728c2ecf20Sopenharmony_ci}
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_cistatic int __net_init cangw_pernet_init(struct net *net)
11758c2ecf20Sopenharmony_ci{
11768c2ecf20Sopenharmony_ci	INIT_HLIST_HEAD(&net->can.cgw_list);
11778c2ecf20Sopenharmony_ci	return 0;
11788c2ecf20Sopenharmony_ci}
11798c2ecf20Sopenharmony_ci
11808c2ecf20Sopenharmony_cistatic void __net_exit cangw_pernet_exit(struct net *net)
11818c2ecf20Sopenharmony_ci{
11828c2ecf20Sopenharmony_ci	rtnl_lock();
11838c2ecf20Sopenharmony_ci	cgw_remove_all_jobs(net);
11848c2ecf20Sopenharmony_ci	rtnl_unlock();
11858c2ecf20Sopenharmony_ci}
11868c2ecf20Sopenharmony_ci
11878c2ecf20Sopenharmony_cistatic struct pernet_operations cangw_pernet_ops = {
11888c2ecf20Sopenharmony_ci	.init = cangw_pernet_init,
11898c2ecf20Sopenharmony_ci	.exit = cangw_pernet_exit,
11908c2ecf20Sopenharmony_ci};
11918c2ecf20Sopenharmony_ci
11928c2ecf20Sopenharmony_cistatic __init int cgw_module_init(void)
11938c2ecf20Sopenharmony_ci{
11948c2ecf20Sopenharmony_ci	int ret;
11958c2ecf20Sopenharmony_ci
11968c2ecf20Sopenharmony_ci	/* sanitize given module parameter */
11978c2ecf20Sopenharmony_ci	max_hops = clamp_t(unsigned int, max_hops, CGW_MIN_HOPS, CGW_MAX_HOPS);
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_ci	pr_info("can: netlink gateway - max_hops=%d\n",	max_hops);
12008c2ecf20Sopenharmony_ci
12018c2ecf20Sopenharmony_ci	ret = register_pernet_subsys(&cangw_pernet_ops);
12028c2ecf20Sopenharmony_ci	if (ret)
12038c2ecf20Sopenharmony_ci		return ret;
12048c2ecf20Sopenharmony_ci
12058c2ecf20Sopenharmony_ci	ret = -ENOMEM;
12068c2ecf20Sopenharmony_ci	cgw_cache = kmem_cache_create("can_gw", sizeof(struct cgw_job),
12078c2ecf20Sopenharmony_ci				      0, 0, NULL);
12088c2ecf20Sopenharmony_ci	if (!cgw_cache)
12098c2ecf20Sopenharmony_ci		goto out_cache_create;
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci	/* set notifier */
12128c2ecf20Sopenharmony_ci	notifier.notifier_call = cgw_notifier;
12138c2ecf20Sopenharmony_ci	ret = register_netdevice_notifier(&notifier);
12148c2ecf20Sopenharmony_ci	if (ret)
12158c2ecf20Sopenharmony_ci		goto out_register_notifier;
12168c2ecf20Sopenharmony_ci
12178c2ecf20Sopenharmony_ci	ret = rtnl_register_module(THIS_MODULE, PF_CAN, RTM_GETROUTE,
12188c2ecf20Sopenharmony_ci				   NULL, cgw_dump_jobs, 0);
12198c2ecf20Sopenharmony_ci	if (ret)
12208c2ecf20Sopenharmony_ci		goto out_rtnl_register1;
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_ci	ret = rtnl_register_module(THIS_MODULE, PF_CAN, RTM_NEWROUTE,
12238c2ecf20Sopenharmony_ci				   cgw_create_job, NULL, 0);
12248c2ecf20Sopenharmony_ci	if (ret)
12258c2ecf20Sopenharmony_ci		goto out_rtnl_register2;
12268c2ecf20Sopenharmony_ci	ret = rtnl_register_module(THIS_MODULE, PF_CAN, RTM_DELROUTE,
12278c2ecf20Sopenharmony_ci				   cgw_remove_job, NULL, 0);
12288c2ecf20Sopenharmony_ci	if (ret)
12298c2ecf20Sopenharmony_ci		goto out_rtnl_register3;
12308c2ecf20Sopenharmony_ci
12318c2ecf20Sopenharmony_ci	return 0;
12328c2ecf20Sopenharmony_ci
12338c2ecf20Sopenharmony_ciout_rtnl_register3:
12348c2ecf20Sopenharmony_ci	rtnl_unregister(PF_CAN, RTM_NEWROUTE);
12358c2ecf20Sopenharmony_ciout_rtnl_register2:
12368c2ecf20Sopenharmony_ci	rtnl_unregister(PF_CAN, RTM_GETROUTE);
12378c2ecf20Sopenharmony_ciout_rtnl_register1:
12388c2ecf20Sopenharmony_ci	unregister_netdevice_notifier(&notifier);
12398c2ecf20Sopenharmony_ciout_register_notifier:
12408c2ecf20Sopenharmony_ci	kmem_cache_destroy(cgw_cache);
12418c2ecf20Sopenharmony_ciout_cache_create:
12428c2ecf20Sopenharmony_ci	unregister_pernet_subsys(&cangw_pernet_ops);
12438c2ecf20Sopenharmony_ci
12448c2ecf20Sopenharmony_ci	return ret;
12458c2ecf20Sopenharmony_ci}
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_cistatic __exit void cgw_module_exit(void)
12488c2ecf20Sopenharmony_ci{
12498c2ecf20Sopenharmony_ci	rtnl_unregister_all(PF_CAN);
12508c2ecf20Sopenharmony_ci
12518c2ecf20Sopenharmony_ci	unregister_netdevice_notifier(&notifier);
12528c2ecf20Sopenharmony_ci
12538c2ecf20Sopenharmony_ci	unregister_pernet_subsys(&cangw_pernet_ops);
12548c2ecf20Sopenharmony_ci	rcu_barrier(); /* Wait for completion of call_rcu()'s */
12558c2ecf20Sopenharmony_ci
12568c2ecf20Sopenharmony_ci	kmem_cache_destroy(cgw_cache);
12578c2ecf20Sopenharmony_ci}
12588c2ecf20Sopenharmony_ci
12598c2ecf20Sopenharmony_cimodule_init(cgw_module_init);
12608c2ecf20Sopenharmony_cimodule_exit(cgw_module_exit);
1261