162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
262306a36Sopenharmony_ci/* gw.c - CAN frame Gateway/Router/Bridge with netlink interface
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (c) 2019 Volkswagen Group Electronic Research
562306a36Sopenharmony_ci * All rights reserved.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or without
862306a36Sopenharmony_ci * modification, are permitted provided that the following conditions
962306a36Sopenharmony_ci * are met:
1062306a36Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright
1162306a36Sopenharmony_ci *    notice, this list of conditions and the following disclaimer.
1262306a36Sopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright
1362306a36Sopenharmony_ci *    notice, this list of conditions and the following disclaimer in the
1462306a36Sopenharmony_ci *    documentation and/or other materials provided with the distribution.
1562306a36Sopenharmony_ci * 3. Neither the name of Volkswagen nor the names of its contributors
1662306a36Sopenharmony_ci *    may be used to endorse or promote products derived from this software
1762306a36Sopenharmony_ci *    without specific prior written permission.
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci * Alternatively, provided that this notice is retained in full, this
2062306a36Sopenharmony_ci * software may be distributed under the terms of the GNU General
2162306a36Sopenharmony_ci * Public License ("GPL") version 2, in which case the provisions of the
2262306a36Sopenharmony_ci * GPL apply INSTEAD OF those given above.
2362306a36Sopenharmony_ci *
2462306a36Sopenharmony_ci * The provided data structures and external interfaces from this code
2562306a36Sopenharmony_ci * are not restricted to be used by modules with a GPL compatible license.
2662306a36Sopenharmony_ci *
2762306a36Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2862306a36Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2962306a36Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
3062306a36Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
3162306a36Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
3262306a36Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
3362306a36Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
3462306a36Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3562306a36Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3662306a36Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
3762306a36Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
3862306a36Sopenharmony_ci * DAMAGE.
3962306a36Sopenharmony_ci *
4062306a36Sopenharmony_ci */
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#include <linux/module.h>
4362306a36Sopenharmony_ci#include <linux/init.h>
4462306a36Sopenharmony_ci#include <linux/types.h>
4562306a36Sopenharmony_ci#include <linux/kernel.h>
4662306a36Sopenharmony_ci#include <linux/list.h>
4762306a36Sopenharmony_ci#include <linux/spinlock.h>
4862306a36Sopenharmony_ci#include <linux/rcupdate.h>
4962306a36Sopenharmony_ci#include <linux/rculist.h>
5062306a36Sopenharmony_ci#include <linux/net.h>
5162306a36Sopenharmony_ci#include <linux/netdevice.h>
5262306a36Sopenharmony_ci#include <linux/if_arp.h>
5362306a36Sopenharmony_ci#include <linux/skbuff.h>
5462306a36Sopenharmony_ci#include <linux/can.h>
5562306a36Sopenharmony_ci#include <linux/can/core.h>
5662306a36Sopenharmony_ci#include <linux/can/skb.h>
5762306a36Sopenharmony_ci#include <linux/can/gw.h>
5862306a36Sopenharmony_ci#include <net/rtnetlink.h>
5962306a36Sopenharmony_ci#include <net/net_namespace.h>
6062306a36Sopenharmony_ci#include <net/sock.h>
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci#define CAN_GW_NAME "can-gw"
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ciMODULE_DESCRIPTION("PF_CAN netlink gateway");
6562306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL");
6662306a36Sopenharmony_ciMODULE_AUTHOR("Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
6762306a36Sopenharmony_ciMODULE_ALIAS(CAN_GW_NAME);
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci#define CGW_MIN_HOPS 1
7062306a36Sopenharmony_ci#define CGW_MAX_HOPS 6
7162306a36Sopenharmony_ci#define CGW_DEFAULT_HOPS 1
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic unsigned int max_hops __read_mostly = CGW_DEFAULT_HOPS;
7462306a36Sopenharmony_cimodule_param(max_hops, uint, 0444);
7562306a36Sopenharmony_ciMODULE_PARM_DESC(max_hops,
7662306a36Sopenharmony_ci		 "maximum " CAN_GW_NAME " routing hops for CAN frames "
7762306a36Sopenharmony_ci		 "(valid values: " __stringify(CGW_MIN_HOPS) "-"
7862306a36Sopenharmony_ci		 __stringify(CGW_MAX_HOPS) " hops, "
7962306a36Sopenharmony_ci		 "default: " __stringify(CGW_DEFAULT_HOPS) ")");
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic struct notifier_block notifier;
8262306a36Sopenharmony_cistatic struct kmem_cache *cgw_cache __read_mostly;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci/* structure that contains the (on-the-fly) CAN frame modifications */
8562306a36Sopenharmony_cistruct cf_mod {
8662306a36Sopenharmony_ci	struct {
8762306a36Sopenharmony_ci		struct canfd_frame and;
8862306a36Sopenharmony_ci		struct canfd_frame or;
8962306a36Sopenharmony_ci		struct canfd_frame xor;
9062306a36Sopenharmony_ci		struct canfd_frame set;
9162306a36Sopenharmony_ci	} modframe;
9262306a36Sopenharmony_ci	struct {
9362306a36Sopenharmony_ci		u8 and;
9462306a36Sopenharmony_ci		u8 or;
9562306a36Sopenharmony_ci		u8 xor;
9662306a36Sopenharmony_ci		u8 set;
9762306a36Sopenharmony_ci	} modtype;
9862306a36Sopenharmony_ci	void (*modfunc[MAX_MODFUNCTIONS])(struct canfd_frame *cf,
9962306a36Sopenharmony_ci					  struct cf_mod *mod);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	/* CAN frame checksum calculation after CAN frame modifications */
10262306a36Sopenharmony_ci	struct {
10362306a36Sopenharmony_ci		struct cgw_csum_xor xor;
10462306a36Sopenharmony_ci		struct cgw_csum_crc8 crc8;
10562306a36Sopenharmony_ci	} csum;
10662306a36Sopenharmony_ci	struct {
10762306a36Sopenharmony_ci		void (*xor)(struct canfd_frame *cf,
10862306a36Sopenharmony_ci			    struct cgw_csum_xor *xor);
10962306a36Sopenharmony_ci		void (*crc8)(struct canfd_frame *cf,
11062306a36Sopenharmony_ci			     struct cgw_csum_crc8 *crc8);
11162306a36Sopenharmony_ci	} csumfunc;
11262306a36Sopenharmony_ci	u32 uid;
11362306a36Sopenharmony_ci};
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci/* So far we just support CAN -> CAN routing and frame modifications.
11662306a36Sopenharmony_ci *
11762306a36Sopenharmony_ci * The internal can_can_gw structure contains data and attributes for
11862306a36Sopenharmony_ci * a CAN -> CAN gateway job.
11962306a36Sopenharmony_ci */
12062306a36Sopenharmony_cistruct can_can_gw {
12162306a36Sopenharmony_ci	struct can_filter filter;
12262306a36Sopenharmony_ci	int src_idx;
12362306a36Sopenharmony_ci	int dst_idx;
12462306a36Sopenharmony_ci};
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci/* list entry for CAN gateways jobs */
12762306a36Sopenharmony_cistruct cgw_job {
12862306a36Sopenharmony_ci	struct hlist_node list;
12962306a36Sopenharmony_ci	struct rcu_head rcu;
13062306a36Sopenharmony_ci	u32 handled_frames;
13162306a36Sopenharmony_ci	u32 dropped_frames;
13262306a36Sopenharmony_ci	u32 deleted_frames;
13362306a36Sopenharmony_ci	struct cf_mod mod;
13462306a36Sopenharmony_ci	union {
13562306a36Sopenharmony_ci		/* CAN frame data source */
13662306a36Sopenharmony_ci		struct net_device *dev;
13762306a36Sopenharmony_ci	} src;
13862306a36Sopenharmony_ci	union {
13962306a36Sopenharmony_ci		/* CAN frame data destination */
14062306a36Sopenharmony_ci		struct net_device *dev;
14162306a36Sopenharmony_ci	} dst;
14262306a36Sopenharmony_ci	union {
14362306a36Sopenharmony_ci		struct can_can_gw ccgw;
14462306a36Sopenharmony_ci		/* tbc */
14562306a36Sopenharmony_ci	};
14662306a36Sopenharmony_ci	u8 gwtype;
14762306a36Sopenharmony_ci	u8 limit_hops;
14862306a36Sopenharmony_ci	u16 flags;
14962306a36Sopenharmony_ci};
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci/* modification functions that are invoked in the hot path in can_can_gw_rcv */
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci#define MODFUNC(func, op) static void func(struct canfd_frame *cf, \
15462306a36Sopenharmony_ci					   struct cf_mod *mod) { op ; }
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ciMODFUNC(mod_and_id, cf->can_id &= mod->modframe.and.can_id)
15762306a36Sopenharmony_ciMODFUNC(mod_and_len, cf->len &= mod->modframe.and.len)
15862306a36Sopenharmony_ciMODFUNC(mod_and_flags, cf->flags &= mod->modframe.and.flags)
15962306a36Sopenharmony_ciMODFUNC(mod_and_data, *(u64 *)cf->data &= *(u64 *)mod->modframe.and.data)
16062306a36Sopenharmony_ciMODFUNC(mod_or_id, cf->can_id |= mod->modframe.or.can_id)
16162306a36Sopenharmony_ciMODFUNC(mod_or_len, cf->len |= mod->modframe.or.len)
16262306a36Sopenharmony_ciMODFUNC(mod_or_flags, cf->flags |= mod->modframe.or.flags)
16362306a36Sopenharmony_ciMODFUNC(mod_or_data, *(u64 *)cf->data |= *(u64 *)mod->modframe.or.data)
16462306a36Sopenharmony_ciMODFUNC(mod_xor_id, cf->can_id ^= mod->modframe.xor.can_id)
16562306a36Sopenharmony_ciMODFUNC(mod_xor_len, cf->len ^= mod->modframe.xor.len)
16662306a36Sopenharmony_ciMODFUNC(mod_xor_flags, cf->flags ^= mod->modframe.xor.flags)
16762306a36Sopenharmony_ciMODFUNC(mod_xor_data, *(u64 *)cf->data ^= *(u64 *)mod->modframe.xor.data)
16862306a36Sopenharmony_ciMODFUNC(mod_set_id, cf->can_id = mod->modframe.set.can_id)
16962306a36Sopenharmony_ciMODFUNC(mod_set_len, cf->len = mod->modframe.set.len)
17062306a36Sopenharmony_ciMODFUNC(mod_set_flags, cf->flags = mod->modframe.set.flags)
17162306a36Sopenharmony_ciMODFUNC(mod_set_data, *(u64 *)cf->data = *(u64 *)mod->modframe.set.data)
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cistatic void mod_and_fddata(struct canfd_frame *cf, struct cf_mod *mod)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	int i;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	for (i = 0; i < CANFD_MAX_DLEN; i += 8)
17862306a36Sopenharmony_ci		*(u64 *)(cf->data + i) &= *(u64 *)(mod->modframe.and.data + i);
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistatic void mod_or_fddata(struct canfd_frame *cf, struct cf_mod *mod)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	int i;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	for (i = 0; i < CANFD_MAX_DLEN; i += 8)
18662306a36Sopenharmony_ci		*(u64 *)(cf->data + i) |= *(u64 *)(mod->modframe.or.data + i);
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_cistatic void mod_xor_fddata(struct canfd_frame *cf, struct cf_mod *mod)
19062306a36Sopenharmony_ci{
19162306a36Sopenharmony_ci	int i;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	for (i = 0; i < CANFD_MAX_DLEN; i += 8)
19462306a36Sopenharmony_ci		*(u64 *)(cf->data + i) ^= *(u64 *)(mod->modframe.xor.data + i);
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic void mod_set_fddata(struct canfd_frame *cf, struct cf_mod *mod)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	memcpy(cf->data, mod->modframe.set.data, CANFD_MAX_DLEN);
20062306a36Sopenharmony_ci}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci/* retrieve valid CC DLC value and store it into 'len' */
20362306a36Sopenharmony_cistatic void mod_retrieve_ccdlc(struct canfd_frame *cf)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	struct can_frame *ccf = (struct can_frame *)cf;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	/* len8_dlc is only valid if len == CAN_MAX_DLEN */
20862306a36Sopenharmony_ci	if (ccf->len != CAN_MAX_DLEN)
20962306a36Sopenharmony_ci		return;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	/* do we have a valid len8_dlc value from 9 .. 15 ? */
21262306a36Sopenharmony_ci	if (ccf->len8_dlc > CAN_MAX_DLEN && ccf->len8_dlc <= CAN_MAX_RAW_DLC)
21362306a36Sopenharmony_ci		ccf->len = ccf->len8_dlc;
21462306a36Sopenharmony_ci}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci/* convert valid CC DLC value in 'len' into struct can_frame elements */
21762306a36Sopenharmony_cistatic void mod_store_ccdlc(struct canfd_frame *cf)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	struct can_frame *ccf = (struct can_frame *)cf;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	/* clear potential leftovers */
22262306a36Sopenharmony_ci	ccf->len8_dlc = 0;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	/* plain data length 0 .. 8 - that was easy */
22562306a36Sopenharmony_ci	if (ccf->len <= CAN_MAX_DLEN)
22662306a36Sopenharmony_ci		return;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	/* potentially broken values are caught in can_can_gw_rcv() */
22962306a36Sopenharmony_ci	if (ccf->len > CAN_MAX_RAW_DLC)
23062306a36Sopenharmony_ci		return;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	/* we have a valid dlc value from 9 .. 15 in ccf->len */
23362306a36Sopenharmony_ci	ccf->len8_dlc = ccf->len;
23462306a36Sopenharmony_ci	ccf->len = CAN_MAX_DLEN;
23562306a36Sopenharmony_ci}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_cistatic void mod_and_ccdlc(struct canfd_frame *cf, struct cf_mod *mod)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	mod_retrieve_ccdlc(cf);
24062306a36Sopenharmony_ci	mod_and_len(cf, mod);
24162306a36Sopenharmony_ci	mod_store_ccdlc(cf);
24262306a36Sopenharmony_ci}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_cistatic void mod_or_ccdlc(struct canfd_frame *cf, struct cf_mod *mod)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	mod_retrieve_ccdlc(cf);
24762306a36Sopenharmony_ci	mod_or_len(cf, mod);
24862306a36Sopenharmony_ci	mod_store_ccdlc(cf);
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_cistatic void mod_xor_ccdlc(struct canfd_frame *cf, struct cf_mod *mod)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	mod_retrieve_ccdlc(cf);
25462306a36Sopenharmony_ci	mod_xor_len(cf, mod);
25562306a36Sopenharmony_ci	mod_store_ccdlc(cf);
25662306a36Sopenharmony_ci}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_cistatic void mod_set_ccdlc(struct canfd_frame *cf, struct cf_mod *mod)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	mod_set_len(cf, mod);
26162306a36Sopenharmony_ci	mod_store_ccdlc(cf);
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cistatic void canframecpy(struct canfd_frame *dst, struct can_frame *src)
26562306a36Sopenharmony_ci{
26662306a36Sopenharmony_ci	/* Copy the struct members separately to ensure that no uninitialized
26762306a36Sopenharmony_ci	 * data are copied in the 3 bytes hole of the struct. This is needed
26862306a36Sopenharmony_ci	 * to make easy compares of the data in the struct cf_mod.
26962306a36Sopenharmony_ci	 */
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	dst->can_id = src->can_id;
27262306a36Sopenharmony_ci	dst->len = src->len;
27362306a36Sopenharmony_ci	*(u64 *)dst->data = *(u64 *)src->data;
27462306a36Sopenharmony_ci}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_cistatic void canfdframecpy(struct canfd_frame *dst, struct canfd_frame *src)
27762306a36Sopenharmony_ci{
27862306a36Sopenharmony_ci	/* Copy the struct members separately to ensure that no uninitialized
27962306a36Sopenharmony_ci	 * data are copied in the 2 bytes hole of the struct. This is needed
28062306a36Sopenharmony_ci	 * to make easy compares of the data in the struct cf_mod.
28162306a36Sopenharmony_ci	 */
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	dst->can_id = src->can_id;
28462306a36Sopenharmony_ci	dst->flags = src->flags;
28562306a36Sopenharmony_ci	dst->len = src->len;
28662306a36Sopenharmony_ci	memcpy(dst->data, src->data, CANFD_MAX_DLEN);
28762306a36Sopenharmony_ci}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_cistatic int cgw_chk_csum_parms(s8 fr, s8 to, s8 re, struct rtcanmsg *r)
29062306a36Sopenharmony_ci{
29162306a36Sopenharmony_ci	s8 dlen = CAN_MAX_DLEN;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	if (r->flags & CGW_FLAGS_CAN_FD)
29462306a36Sopenharmony_ci		dlen = CANFD_MAX_DLEN;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	/* absolute dlc values 0 .. 7 => 0 .. 7, e.g. data [0]
29762306a36Sopenharmony_ci	 * relative to received dlc -1 .. -8 :
29862306a36Sopenharmony_ci	 * e.g. for received dlc = 8
29962306a36Sopenharmony_ci	 * -1 => index = 7 (data[7])
30062306a36Sopenharmony_ci	 * -3 => index = 5 (data[5])
30162306a36Sopenharmony_ci	 * -8 => index = 0 (data[0])
30262306a36Sopenharmony_ci	 */
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	if (fr >= -dlen && fr < dlen &&
30562306a36Sopenharmony_ci	    to >= -dlen && to < dlen &&
30662306a36Sopenharmony_ci	    re >= -dlen && re < dlen)
30762306a36Sopenharmony_ci		return 0;
30862306a36Sopenharmony_ci	else
30962306a36Sopenharmony_ci		return -EINVAL;
31062306a36Sopenharmony_ci}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_cistatic inline int calc_idx(int idx, int rx_len)
31362306a36Sopenharmony_ci{
31462306a36Sopenharmony_ci	if (idx < 0)
31562306a36Sopenharmony_ci		return rx_len + idx;
31662306a36Sopenharmony_ci	else
31762306a36Sopenharmony_ci		return idx;
31862306a36Sopenharmony_ci}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_cistatic void cgw_csum_xor_rel(struct canfd_frame *cf, struct cgw_csum_xor *xor)
32162306a36Sopenharmony_ci{
32262306a36Sopenharmony_ci	int from = calc_idx(xor->from_idx, cf->len);
32362306a36Sopenharmony_ci	int to = calc_idx(xor->to_idx, cf->len);
32462306a36Sopenharmony_ci	int res = calc_idx(xor->result_idx, cf->len);
32562306a36Sopenharmony_ci	u8 val = xor->init_xor_val;
32662306a36Sopenharmony_ci	int i;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	if (from < 0 || to < 0 || res < 0)
32962306a36Sopenharmony_ci		return;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	if (from <= to) {
33262306a36Sopenharmony_ci		for (i = from; i <= to; i++)
33362306a36Sopenharmony_ci			val ^= cf->data[i];
33462306a36Sopenharmony_ci	} else {
33562306a36Sopenharmony_ci		for (i = from; i >= to; i--)
33662306a36Sopenharmony_ci			val ^= cf->data[i];
33762306a36Sopenharmony_ci	}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	cf->data[res] = val;
34062306a36Sopenharmony_ci}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_cistatic void cgw_csum_xor_pos(struct canfd_frame *cf, struct cgw_csum_xor *xor)
34362306a36Sopenharmony_ci{
34462306a36Sopenharmony_ci	u8 val = xor->init_xor_val;
34562306a36Sopenharmony_ci	int i;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	for (i = xor->from_idx; i <= xor->to_idx; i++)
34862306a36Sopenharmony_ci		val ^= cf->data[i];
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	cf->data[xor->result_idx] = val;
35162306a36Sopenharmony_ci}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_cistatic void cgw_csum_xor_neg(struct canfd_frame *cf, struct cgw_csum_xor *xor)
35462306a36Sopenharmony_ci{
35562306a36Sopenharmony_ci	u8 val = xor->init_xor_val;
35662306a36Sopenharmony_ci	int i;
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	for (i = xor->from_idx; i >= xor->to_idx; i--)
35962306a36Sopenharmony_ci		val ^= cf->data[i];
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	cf->data[xor->result_idx] = val;
36262306a36Sopenharmony_ci}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_cistatic void cgw_csum_crc8_rel(struct canfd_frame *cf,
36562306a36Sopenharmony_ci			      struct cgw_csum_crc8 *crc8)
36662306a36Sopenharmony_ci{
36762306a36Sopenharmony_ci	int from = calc_idx(crc8->from_idx, cf->len);
36862306a36Sopenharmony_ci	int to = calc_idx(crc8->to_idx, cf->len);
36962306a36Sopenharmony_ci	int res = calc_idx(crc8->result_idx, cf->len);
37062306a36Sopenharmony_ci	u8 crc = crc8->init_crc_val;
37162306a36Sopenharmony_ci	int i;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	if (from < 0 || to < 0 || res < 0)
37462306a36Sopenharmony_ci		return;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	if (from <= to) {
37762306a36Sopenharmony_ci		for (i = crc8->from_idx; i <= crc8->to_idx; i++)
37862306a36Sopenharmony_ci			crc = crc8->crctab[crc ^ cf->data[i]];
37962306a36Sopenharmony_ci	} else {
38062306a36Sopenharmony_ci		for (i = crc8->from_idx; i >= crc8->to_idx; i--)
38162306a36Sopenharmony_ci			crc = crc8->crctab[crc ^ cf->data[i]];
38262306a36Sopenharmony_ci	}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	switch (crc8->profile) {
38562306a36Sopenharmony_ci	case CGW_CRC8PRF_1U8:
38662306a36Sopenharmony_ci		crc = crc8->crctab[crc ^ crc8->profile_data[0]];
38762306a36Sopenharmony_ci		break;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	case  CGW_CRC8PRF_16U8:
39062306a36Sopenharmony_ci		crc = crc8->crctab[crc ^ crc8->profile_data[cf->data[1] & 0xF]];
39162306a36Sopenharmony_ci		break;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	case CGW_CRC8PRF_SFFID_XOR:
39462306a36Sopenharmony_ci		crc = crc8->crctab[crc ^ (cf->can_id & 0xFF) ^
39562306a36Sopenharmony_ci				   (cf->can_id >> 8 & 0xFF)];
39662306a36Sopenharmony_ci		break;
39762306a36Sopenharmony_ci	}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	cf->data[crc8->result_idx] = crc ^ crc8->final_xor_val;
40062306a36Sopenharmony_ci}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_cistatic void cgw_csum_crc8_pos(struct canfd_frame *cf,
40362306a36Sopenharmony_ci			      struct cgw_csum_crc8 *crc8)
40462306a36Sopenharmony_ci{
40562306a36Sopenharmony_ci	u8 crc = crc8->init_crc_val;
40662306a36Sopenharmony_ci	int i;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	for (i = crc8->from_idx; i <= crc8->to_idx; i++)
40962306a36Sopenharmony_ci		crc = crc8->crctab[crc ^ cf->data[i]];
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	switch (crc8->profile) {
41262306a36Sopenharmony_ci	case CGW_CRC8PRF_1U8:
41362306a36Sopenharmony_ci		crc = crc8->crctab[crc ^ crc8->profile_data[0]];
41462306a36Sopenharmony_ci		break;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	case  CGW_CRC8PRF_16U8:
41762306a36Sopenharmony_ci		crc = crc8->crctab[crc ^ crc8->profile_data[cf->data[1] & 0xF]];
41862306a36Sopenharmony_ci		break;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	case CGW_CRC8PRF_SFFID_XOR:
42162306a36Sopenharmony_ci		crc = crc8->crctab[crc ^ (cf->can_id & 0xFF) ^
42262306a36Sopenharmony_ci				   (cf->can_id >> 8 & 0xFF)];
42362306a36Sopenharmony_ci		break;
42462306a36Sopenharmony_ci	}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	cf->data[crc8->result_idx] = crc ^ crc8->final_xor_val;
42762306a36Sopenharmony_ci}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_cistatic void cgw_csum_crc8_neg(struct canfd_frame *cf,
43062306a36Sopenharmony_ci			      struct cgw_csum_crc8 *crc8)
43162306a36Sopenharmony_ci{
43262306a36Sopenharmony_ci	u8 crc = crc8->init_crc_val;
43362306a36Sopenharmony_ci	int i;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	for (i = crc8->from_idx; i >= crc8->to_idx; i--)
43662306a36Sopenharmony_ci		crc = crc8->crctab[crc ^ cf->data[i]];
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	switch (crc8->profile) {
43962306a36Sopenharmony_ci	case CGW_CRC8PRF_1U8:
44062306a36Sopenharmony_ci		crc = crc8->crctab[crc ^ crc8->profile_data[0]];
44162306a36Sopenharmony_ci		break;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	case  CGW_CRC8PRF_16U8:
44462306a36Sopenharmony_ci		crc = crc8->crctab[crc ^ crc8->profile_data[cf->data[1] & 0xF]];
44562306a36Sopenharmony_ci		break;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	case CGW_CRC8PRF_SFFID_XOR:
44862306a36Sopenharmony_ci		crc = crc8->crctab[crc ^ (cf->can_id & 0xFF) ^
44962306a36Sopenharmony_ci				   (cf->can_id >> 8 & 0xFF)];
45062306a36Sopenharmony_ci		break;
45162306a36Sopenharmony_ci	}
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	cf->data[crc8->result_idx] = crc ^ crc8->final_xor_val;
45462306a36Sopenharmony_ci}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci/* the receive & process & send function */
45762306a36Sopenharmony_cistatic void can_can_gw_rcv(struct sk_buff *skb, void *data)
45862306a36Sopenharmony_ci{
45962306a36Sopenharmony_ci	struct cgw_job *gwj = (struct cgw_job *)data;
46062306a36Sopenharmony_ci	struct canfd_frame *cf;
46162306a36Sopenharmony_ci	struct sk_buff *nskb;
46262306a36Sopenharmony_ci	int modidx = 0;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	/* process strictly Classic CAN or CAN FD frames */
46562306a36Sopenharmony_ci	if (gwj->flags & CGW_FLAGS_CAN_FD) {
46662306a36Sopenharmony_ci		if (!can_is_canfd_skb(skb))
46762306a36Sopenharmony_ci			return;
46862306a36Sopenharmony_ci	} else {
46962306a36Sopenharmony_ci		if (!can_is_can_skb(skb))
47062306a36Sopenharmony_ci			return;
47162306a36Sopenharmony_ci	}
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	/* Do not handle CAN frames routed more than 'max_hops' times.
47462306a36Sopenharmony_ci	 * In general we should never catch this delimiter which is intended
47562306a36Sopenharmony_ci	 * to cover a misconfiguration protection (e.g. circular CAN routes).
47662306a36Sopenharmony_ci	 *
47762306a36Sopenharmony_ci	 * The Controller Area Network controllers only accept CAN frames with
47862306a36Sopenharmony_ci	 * correct CRCs - which are not visible in the controller registers.
47962306a36Sopenharmony_ci	 * According to skbuff.h documentation the csum_start element for IP
48062306a36Sopenharmony_ci	 * checksums is undefined/unused when ip_summed == CHECKSUM_UNNECESSARY.
48162306a36Sopenharmony_ci	 * Only CAN skbs can be processed here which already have this property.
48262306a36Sopenharmony_ci	 */
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci#define cgw_hops(skb) ((skb)->csum_start)
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	BUG_ON(skb->ip_summed != CHECKSUM_UNNECESSARY);
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	if (cgw_hops(skb) >= max_hops) {
48962306a36Sopenharmony_ci		/* indicate deleted frames due to misconfiguration */
49062306a36Sopenharmony_ci		gwj->deleted_frames++;
49162306a36Sopenharmony_ci		return;
49262306a36Sopenharmony_ci	}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	if (!(gwj->dst.dev->flags & IFF_UP)) {
49562306a36Sopenharmony_ci		gwj->dropped_frames++;
49662306a36Sopenharmony_ci		return;
49762306a36Sopenharmony_ci	}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	/* is sending the skb back to the incoming interface not allowed? */
50062306a36Sopenharmony_ci	if (!(gwj->flags & CGW_FLAGS_CAN_IIF_TX_OK) &&
50162306a36Sopenharmony_ci	    can_skb_prv(skb)->ifindex == gwj->dst.dev->ifindex)
50262306a36Sopenharmony_ci		return;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	/* clone the given skb, which has not been done in can_rcv()
50562306a36Sopenharmony_ci	 *
50662306a36Sopenharmony_ci	 * When there is at least one modification function activated,
50762306a36Sopenharmony_ci	 * we need to copy the skb as we want to modify skb->data.
50862306a36Sopenharmony_ci	 */
50962306a36Sopenharmony_ci	if (gwj->mod.modfunc[0])
51062306a36Sopenharmony_ci		nskb = skb_copy(skb, GFP_ATOMIC);
51162306a36Sopenharmony_ci	else
51262306a36Sopenharmony_ci		nskb = skb_clone(skb, GFP_ATOMIC);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	if (!nskb) {
51562306a36Sopenharmony_ci		gwj->dropped_frames++;
51662306a36Sopenharmony_ci		return;
51762306a36Sopenharmony_ci	}
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	/* put the incremented hop counter in the cloned skb */
52062306a36Sopenharmony_ci	cgw_hops(nskb) = cgw_hops(skb) + 1;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	/* first processing of this CAN frame -> adjust to private hop limit */
52362306a36Sopenharmony_ci	if (gwj->limit_hops && cgw_hops(nskb) == 1)
52462306a36Sopenharmony_ci		cgw_hops(nskb) = max_hops - gwj->limit_hops + 1;
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	nskb->dev = gwj->dst.dev;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	/* pointer to modifiable CAN frame */
52962306a36Sopenharmony_ci	cf = (struct canfd_frame *)nskb->data;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	/* perform preprocessed modification functions if there are any */
53262306a36Sopenharmony_ci	while (modidx < MAX_MODFUNCTIONS && gwj->mod.modfunc[modidx])
53362306a36Sopenharmony_ci		(*gwj->mod.modfunc[modidx++])(cf, &gwj->mod);
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	/* Has the CAN frame been modified? */
53662306a36Sopenharmony_ci	if (modidx) {
53762306a36Sopenharmony_ci		/* get available space for the processed CAN frame type */
53862306a36Sopenharmony_ci		int max_len = nskb->len - offsetof(struct canfd_frame, data);
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci		/* dlc may have changed, make sure it fits to the CAN frame */
54162306a36Sopenharmony_ci		if (cf->len > max_len) {
54262306a36Sopenharmony_ci			/* delete frame due to misconfiguration */
54362306a36Sopenharmony_ci			gwj->deleted_frames++;
54462306a36Sopenharmony_ci			kfree_skb(nskb);
54562306a36Sopenharmony_ci			return;
54662306a36Sopenharmony_ci		}
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci		/* check for checksum updates */
54962306a36Sopenharmony_ci		if (gwj->mod.csumfunc.crc8)
55062306a36Sopenharmony_ci			(*gwj->mod.csumfunc.crc8)(cf, &gwj->mod.csum.crc8);
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci		if (gwj->mod.csumfunc.xor)
55362306a36Sopenharmony_ci			(*gwj->mod.csumfunc.xor)(cf, &gwj->mod.csum.xor);
55462306a36Sopenharmony_ci	}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	/* clear the skb timestamp if not configured the other way */
55762306a36Sopenharmony_ci	if (!(gwj->flags & CGW_FLAGS_CAN_SRC_TSTAMP))
55862306a36Sopenharmony_ci		nskb->tstamp = 0;
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	/* send to netdevice */
56162306a36Sopenharmony_ci	if (can_send(nskb, gwj->flags & CGW_FLAGS_CAN_ECHO))
56262306a36Sopenharmony_ci		gwj->dropped_frames++;
56362306a36Sopenharmony_ci	else
56462306a36Sopenharmony_ci		gwj->handled_frames++;
56562306a36Sopenharmony_ci}
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_cistatic inline int cgw_register_filter(struct net *net, struct cgw_job *gwj)
56862306a36Sopenharmony_ci{
56962306a36Sopenharmony_ci	return can_rx_register(net, gwj->src.dev, gwj->ccgw.filter.can_id,
57062306a36Sopenharmony_ci			       gwj->ccgw.filter.can_mask, can_can_gw_rcv,
57162306a36Sopenharmony_ci			       gwj, "gw", NULL);
57262306a36Sopenharmony_ci}
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_cistatic inline void cgw_unregister_filter(struct net *net, struct cgw_job *gwj)
57562306a36Sopenharmony_ci{
57662306a36Sopenharmony_ci	can_rx_unregister(net, gwj->src.dev, gwj->ccgw.filter.can_id,
57762306a36Sopenharmony_ci			  gwj->ccgw.filter.can_mask, can_can_gw_rcv, gwj);
57862306a36Sopenharmony_ci}
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_cistatic void cgw_job_free_rcu(struct rcu_head *rcu_head)
58162306a36Sopenharmony_ci{
58262306a36Sopenharmony_ci	struct cgw_job *gwj = container_of(rcu_head, struct cgw_job, rcu);
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	kmem_cache_free(cgw_cache, gwj);
58562306a36Sopenharmony_ci}
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_cistatic int cgw_notifier(struct notifier_block *nb,
58862306a36Sopenharmony_ci			unsigned long msg, void *ptr)
58962306a36Sopenharmony_ci{
59062306a36Sopenharmony_ci	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
59162306a36Sopenharmony_ci	struct net *net = dev_net(dev);
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	if (dev->type != ARPHRD_CAN)
59462306a36Sopenharmony_ci		return NOTIFY_DONE;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	if (msg == NETDEV_UNREGISTER) {
59762306a36Sopenharmony_ci		struct cgw_job *gwj = NULL;
59862306a36Sopenharmony_ci		struct hlist_node *nx;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci		ASSERT_RTNL();
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci		hlist_for_each_entry_safe(gwj, nx, &net->can.cgw_list, list) {
60362306a36Sopenharmony_ci			if (gwj->src.dev == dev || gwj->dst.dev == dev) {
60462306a36Sopenharmony_ci				hlist_del(&gwj->list);
60562306a36Sopenharmony_ci				cgw_unregister_filter(net, gwj);
60662306a36Sopenharmony_ci				call_rcu(&gwj->rcu, cgw_job_free_rcu);
60762306a36Sopenharmony_ci			}
60862306a36Sopenharmony_ci		}
60962306a36Sopenharmony_ci	}
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	return NOTIFY_DONE;
61262306a36Sopenharmony_ci}
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_cistatic int cgw_put_job(struct sk_buff *skb, struct cgw_job *gwj, int type,
61562306a36Sopenharmony_ci		       u32 pid, u32 seq, int flags)
61662306a36Sopenharmony_ci{
61762306a36Sopenharmony_ci	struct rtcanmsg *rtcan;
61862306a36Sopenharmony_ci	struct nlmsghdr *nlh;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	nlh = nlmsg_put(skb, pid, seq, type, sizeof(*rtcan), flags);
62162306a36Sopenharmony_ci	if (!nlh)
62262306a36Sopenharmony_ci		return -EMSGSIZE;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	rtcan = nlmsg_data(nlh);
62562306a36Sopenharmony_ci	rtcan->can_family = AF_CAN;
62662306a36Sopenharmony_ci	rtcan->gwtype = gwj->gwtype;
62762306a36Sopenharmony_ci	rtcan->flags = gwj->flags;
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	/* add statistics if available */
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	if (gwj->handled_frames) {
63262306a36Sopenharmony_ci		if (nla_put_u32(skb, CGW_HANDLED, gwj->handled_frames) < 0)
63362306a36Sopenharmony_ci			goto cancel;
63462306a36Sopenharmony_ci	}
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	if (gwj->dropped_frames) {
63762306a36Sopenharmony_ci		if (nla_put_u32(skb, CGW_DROPPED, gwj->dropped_frames) < 0)
63862306a36Sopenharmony_ci			goto cancel;
63962306a36Sopenharmony_ci	}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	if (gwj->deleted_frames) {
64262306a36Sopenharmony_ci		if (nla_put_u32(skb, CGW_DELETED, gwj->deleted_frames) < 0)
64362306a36Sopenharmony_ci			goto cancel;
64462306a36Sopenharmony_ci	}
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	/* check non default settings of attributes */
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	if (gwj->limit_hops) {
64962306a36Sopenharmony_ci		if (nla_put_u8(skb, CGW_LIM_HOPS, gwj->limit_hops) < 0)
65062306a36Sopenharmony_ci			goto cancel;
65162306a36Sopenharmony_ci	}
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	if (gwj->flags & CGW_FLAGS_CAN_FD) {
65462306a36Sopenharmony_ci		struct cgw_fdframe_mod mb;
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci		if (gwj->mod.modtype.and) {
65762306a36Sopenharmony_ci			memcpy(&mb.cf, &gwj->mod.modframe.and, sizeof(mb.cf));
65862306a36Sopenharmony_ci			mb.modtype = gwj->mod.modtype.and;
65962306a36Sopenharmony_ci			if (nla_put(skb, CGW_FDMOD_AND, sizeof(mb), &mb) < 0)
66062306a36Sopenharmony_ci				goto cancel;
66162306a36Sopenharmony_ci		}
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci		if (gwj->mod.modtype.or) {
66462306a36Sopenharmony_ci			memcpy(&mb.cf, &gwj->mod.modframe.or, sizeof(mb.cf));
66562306a36Sopenharmony_ci			mb.modtype = gwj->mod.modtype.or;
66662306a36Sopenharmony_ci			if (nla_put(skb, CGW_FDMOD_OR, sizeof(mb), &mb) < 0)
66762306a36Sopenharmony_ci				goto cancel;
66862306a36Sopenharmony_ci		}
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci		if (gwj->mod.modtype.xor) {
67162306a36Sopenharmony_ci			memcpy(&mb.cf, &gwj->mod.modframe.xor, sizeof(mb.cf));
67262306a36Sopenharmony_ci			mb.modtype = gwj->mod.modtype.xor;
67362306a36Sopenharmony_ci			if (nla_put(skb, CGW_FDMOD_XOR, sizeof(mb), &mb) < 0)
67462306a36Sopenharmony_ci				goto cancel;
67562306a36Sopenharmony_ci		}
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci		if (gwj->mod.modtype.set) {
67862306a36Sopenharmony_ci			memcpy(&mb.cf, &gwj->mod.modframe.set, sizeof(mb.cf));
67962306a36Sopenharmony_ci			mb.modtype = gwj->mod.modtype.set;
68062306a36Sopenharmony_ci			if (nla_put(skb, CGW_FDMOD_SET, sizeof(mb), &mb) < 0)
68162306a36Sopenharmony_ci				goto cancel;
68262306a36Sopenharmony_ci		}
68362306a36Sopenharmony_ci	} else {
68462306a36Sopenharmony_ci		struct cgw_frame_mod mb;
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci		if (gwj->mod.modtype.and) {
68762306a36Sopenharmony_ci			memcpy(&mb.cf, &gwj->mod.modframe.and, sizeof(mb.cf));
68862306a36Sopenharmony_ci			mb.modtype = gwj->mod.modtype.and;
68962306a36Sopenharmony_ci			if (nla_put(skb, CGW_MOD_AND, sizeof(mb), &mb) < 0)
69062306a36Sopenharmony_ci				goto cancel;
69162306a36Sopenharmony_ci		}
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci		if (gwj->mod.modtype.or) {
69462306a36Sopenharmony_ci			memcpy(&mb.cf, &gwj->mod.modframe.or, sizeof(mb.cf));
69562306a36Sopenharmony_ci			mb.modtype = gwj->mod.modtype.or;
69662306a36Sopenharmony_ci			if (nla_put(skb, CGW_MOD_OR, sizeof(mb), &mb) < 0)
69762306a36Sopenharmony_ci				goto cancel;
69862306a36Sopenharmony_ci		}
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci		if (gwj->mod.modtype.xor) {
70162306a36Sopenharmony_ci			memcpy(&mb.cf, &gwj->mod.modframe.xor, sizeof(mb.cf));
70262306a36Sopenharmony_ci			mb.modtype = gwj->mod.modtype.xor;
70362306a36Sopenharmony_ci			if (nla_put(skb, CGW_MOD_XOR, sizeof(mb), &mb) < 0)
70462306a36Sopenharmony_ci				goto cancel;
70562306a36Sopenharmony_ci		}
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci		if (gwj->mod.modtype.set) {
70862306a36Sopenharmony_ci			memcpy(&mb.cf, &gwj->mod.modframe.set, sizeof(mb.cf));
70962306a36Sopenharmony_ci			mb.modtype = gwj->mod.modtype.set;
71062306a36Sopenharmony_ci			if (nla_put(skb, CGW_MOD_SET, sizeof(mb), &mb) < 0)
71162306a36Sopenharmony_ci				goto cancel;
71262306a36Sopenharmony_ci		}
71362306a36Sopenharmony_ci	}
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	if (gwj->mod.uid) {
71662306a36Sopenharmony_ci		if (nla_put_u32(skb, CGW_MOD_UID, gwj->mod.uid) < 0)
71762306a36Sopenharmony_ci			goto cancel;
71862306a36Sopenharmony_ci	}
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	if (gwj->mod.csumfunc.crc8) {
72162306a36Sopenharmony_ci		if (nla_put(skb, CGW_CS_CRC8, CGW_CS_CRC8_LEN,
72262306a36Sopenharmony_ci			    &gwj->mod.csum.crc8) < 0)
72362306a36Sopenharmony_ci			goto cancel;
72462306a36Sopenharmony_ci	}
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	if (gwj->mod.csumfunc.xor) {
72762306a36Sopenharmony_ci		if (nla_put(skb, CGW_CS_XOR, CGW_CS_XOR_LEN,
72862306a36Sopenharmony_ci			    &gwj->mod.csum.xor) < 0)
72962306a36Sopenharmony_ci			goto cancel;
73062306a36Sopenharmony_ci	}
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	if (gwj->gwtype == CGW_TYPE_CAN_CAN) {
73362306a36Sopenharmony_ci		if (gwj->ccgw.filter.can_id || gwj->ccgw.filter.can_mask) {
73462306a36Sopenharmony_ci			if (nla_put(skb, CGW_FILTER, sizeof(struct can_filter),
73562306a36Sopenharmony_ci				    &gwj->ccgw.filter) < 0)
73662306a36Sopenharmony_ci				goto cancel;
73762306a36Sopenharmony_ci		}
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci		if (nla_put_u32(skb, CGW_SRC_IF, gwj->ccgw.src_idx) < 0)
74062306a36Sopenharmony_ci			goto cancel;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci		if (nla_put_u32(skb, CGW_DST_IF, gwj->ccgw.dst_idx) < 0)
74362306a36Sopenharmony_ci			goto cancel;
74462306a36Sopenharmony_ci	}
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	nlmsg_end(skb, nlh);
74762306a36Sopenharmony_ci	return 0;
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_cicancel:
75062306a36Sopenharmony_ci	nlmsg_cancel(skb, nlh);
75162306a36Sopenharmony_ci	return -EMSGSIZE;
75262306a36Sopenharmony_ci}
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci/* Dump information about all CAN gateway jobs, in response to RTM_GETROUTE */
75562306a36Sopenharmony_cistatic int cgw_dump_jobs(struct sk_buff *skb, struct netlink_callback *cb)
75662306a36Sopenharmony_ci{
75762306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
75862306a36Sopenharmony_ci	struct cgw_job *gwj = NULL;
75962306a36Sopenharmony_ci	int idx = 0;
76062306a36Sopenharmony_ci	int s_idx = cb->args[0];
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	rcu_read_lock();
76362306a36Sopenharmony_ci	hlist_for_each_entry_rcu(gwj, &net->can.cgw_list, list) {
76462306a36Sopenharmony_ci		if (idx < s_idx)
76562306a36Sopenharmony_ci			goto cont;
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci		if (cgw_put_job(skb, gwj, RTM_NEWROUTE,
76862306a36Sopenharmony_ci				NETLINK_CB(cb->skb).portid,
76962306a36Sopenharmony_ci				cb->nlh->nlmsg_seq, NLM_F_MULTI) < 0)
77062306a36Sopenharmony_ci			break;
77162306a36Sopenharmony_cicont:
77262306a36Sopenharmony_ci		idx++;
77362306a36Sopenharmony_ci	}
77462306a36Sopenharmony_ci	rcu_read_unlock();
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	cb->args[0] = idx;
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	return skb->len;
77962306a36Sopenharmony_ci}
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_cistatic const struct nla_policy cgw_policy[CGW_MAX + 1] = {
78262306a36Sopenharmony_ci	[CGW_MOD_AND]	= { .len = sizeof(struct cgw_frame_mod) },
78362306a36Sopenharmony_ci	[CGW_MOD_OR]	= { .len = sizeof(struct cgw_frame_mod) },
78462306a36Sopenharmony_ci	[CGW_MOD_XOR]	= { .len = sizeof(struct cgw_frame_mod) },
78562306a36Sopenharmony_ci	[CGW_MOD_SET]	= { .len = sizeof(struct cgw_frame_mod) },
78662306a36Sopenharmony_ci	[CGW_CS_XOR]	= { .len = sizeof(struct cgw_csum_xor) },
78762306a36Sopenharmony_ci	[CGW_CS_CRC8]	= { .len = sizeof(struct cgw_csum_crc8) },
78862306a36Sopenharmony_ci	[CGW_SRC_IF]	= { .type = NLA_U32 },
78962306a36Sopenharmony_ci	[CGW_DST_IF]	= { .type = NLA_U32 },
79062306a36Sopenharmony_ci	[CGW_FILTER]	= { .len = sizeof(struct can_filter) },
79162306a36Sopenharmony_ci	[CGW_LIM_HOPS]	= { .type = NLA_U8 },
79262306a36Sopenharmony_ci	[CGW_MOD_UID]	= { .type = NLA_U32 },
79362306a36Sopenharmony_ci	[CGW_FDMOD_AND]	= { .len = sizeof(struct cgw_fdframe_mod) },
79462306a36Sopenharmony_ci	[CGW_FDMOD_OR]	= { .len = sizeof(struct cgw_fdframe_mod) },
79562306a36Sopenharmony_ci	[CGW_FDMOD_XOR]	= { .len = sizeof(struct cgw_fdframe_mod) },
79662306a36Sopenharmony_ci	[CGW_FDMOD_SET]	= { .len = sizeof(struct cgw_fdframe_mod) },
79762306a36Sopenharmony_ci};
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci/* check for common and gwtype specific attributes */
80062306a36Sopenharmony_cistatic int cgw_parse_attr(struct nlmsghdr *nlh, struct cf_mod *mod,
80162306a36Sopenharmony_ci			  u8 gwtype, void *gwtypeattr, u8 *limhops)
80262306a36Sopenharmony_ci{
80362306a36Sopenharmony_ci	struct nlattr *tb[CGW_MAX + 1];
80462306a36Sopenharmony_ci	struct rtcanmsg *r = nlmsg_data(nlh);
80562306a36Sopenharmony_ci	int modidx = 0;
80662306a36Sopenharmony_ci	int err = 0;
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	/* initialize modification & checksum data space */
80962306a36Sopenharmony_ci	memset(mod, 0, sizeof(*mod));
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	err = nlmsg_parse_deprecated(nlh, sizeof(struct rtcanmsg), tb,
81262306a36Sopenharmony_ci				     CGW_MAX, cgw_policy, NULL);
81362306a36Sopenharmony_ci	if (err < 0)
81462306a36Sopenharmony_ci		return err;
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	if (tb[CGW_LIM_HOPS]) {
81762306a36Sopenharmony_ci		*limhops = nla_get_u8(tb[CGW_LIM_HOPS]);
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci		if (*limhops < 1 || *limhops > max_hops)
82062306a36Sopenharmony_ci			return -EINVAL;
82162306a36Sopenharmony_ci	}
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	/* check for AND/OR/XOR/SET modifications */
82462306a36Sopenharmony_ci	if (r->flags & CGW_FLAGS_CAN_FD) {
82562306a36Sopenharmony_ci		struct cgw_fdframe_mod mb;
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci		if (tb[CGW_FDMOD_AND]) {
82862306a36Sopenharmony_ci			nla_memcpy(&mb, tb[CGW_FDMOD_AND], CGW_FDMODATTR_LEN);
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci			canfdframecpy(&mod->modframe.and, &mb.cf);
83162306a36Sopenharmony_ci			mod->modtype.and = mb.modtype;
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci			if (mb.modtype & CGW_MOD_ID)
83462306a36Sopenharmony_ci				mod->modfunc[modidx++] = mod_and_id;
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci			if (mb.modtype & CGW_MOD_LEN)
83762306a36Sopenharmony_ci				mod->modfunc[modidx++] = mod_and_len;
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci			if (mb.modtype & CGW_MOD_FLAGS)
84062306a36Sopenharmony_ci				mod->modfunc[modidx++] = mod_and_flags;
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci			if (mb.modtype & CGW_MOD_DATA)
84362306a36Sopenharmony_ci				mod->modfunc[modidx++] = mod_and_fddata;
84462306a36Sopenharmony_ci		}
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci		if (tb[CGW_FDMOD_OR]) {
84762306a36Sopenharmony_ci			nla_memcpy(&mb, tb[CGW_FDMOD_OR], CGW_FDMODATTR_LEN);
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci			canfdframecpy(&mod->modframe.or, &mb.cf);
85062306a36Sopenharmony_ci			mod->modtype.or = mb.modtype;
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci			if (mb.modtype & CGW_MOD_ID)
85362306a36Sopenharmony_ci				mod->modfunc[modidx++] = mod_or_id;
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci			if (mb.modtype & CGW_MOD_LEN)
85662306a36Sopenharmony_ci				mod->modfunc[modidx++] = mod_or_len;
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci			if (mb.modtype & CGW_MOD_FLAGS)
85962306a36Sopenharmony_ci				mod->modfunc[modidx++] = mod_or_flags;
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci			if (mb.modtype & CGW_MOD_DATA)
86262306a36Sopenharmony_ci				mod->modfunc[modidx++] = mod_or_fddata;
86362306a36Sopenharmony_ci		}
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci		if (tb[CGW_FDMOD_XOR]) {
86662306a36Sopenharmony_ci			nla_memcpy(&mb, tb[CGW_FDMOD_XOR], CGW_FDMODATTR_LEN);
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci			canfdframecpy(&mod->modframe.xor, &mb.cf);
86962306a36Sopenharmony_ci			mod->modtype.xor = mb.modtype;
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci			if (mb.modtype & CGW_MOD_ID)
87262306a36Sopenharmony_ci				mod->modfunc[modidx++] = mod_xor_id;
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci			if (mb.modtype & CGW_MOD_LEN)
87562306a36Sopenharmony_ci				mod->modfunc[modidx++] = mod_xor_len;
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci			if (mb.modtype & CGW_MOD_FLAGS)
87862306a36Sopenharmony_ci				mod->modfunc[modidx++] = mod_xor_flags;
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci			if (mb.modtype & CGW_MOD_DATA)
88162306a36Sopenharmony_ci				mod->modfunc[modidx++] = mod_xor_fddata;
88262306a36Sopenharmony_ci		}
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci		if (tb[CGW_FDMOD_SET]) {
88562306a36Sopenharmony_ci			nla_memcpy(&mb, tb[CGW_FDMOD_SET], CGW_FDMODATTR_LEN);
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci			canfdframecpy(&mod->modframe.set, &mb.cf);
88862306a36Sopenharmony_ci			mod->modtype.set = mb.modtype;
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci			if (mb.modtype & CGW_MOD_ID)
89162306a36Sopenharmony_ci				mod->modfunc[modidx++] = mod_set_id;
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci			if (mb.modtype & CGW_MOD_LEN)
89462306a36Sopenharmony_ci				mod->modfunc[modidx++] = mod_set_len;
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci			if (mb.modtype & CGW_MOD_FLAGS)
89762306a36Sopenharmony_ci				mod->modfunc[modidx++] = mod_set_flags;
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci			if (mb.modtype & CGW_MOD_DATA)
90062306a36Sopenharmony_ci				mod->modfunc[modidx++] = mod_set_fddata;
90162306a36Sopenharmony_ci		}
90262306a36Sopenharmony_ci	} else {
90362306a36Sopenharmony_ci		struct cgw_frame_mod mb;
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci		if (tb[CGW_MOD_AND]) {
90662306a36Sopenharmony_ci			nla_memcpy(&mb, tb[CGW_MOD_AND], CGW_MODATTR_LEN);
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci			canframecpy(&mod->modframe.and, &mb.cf);
90962306a36Sopenharmony_ci			mod->modtype.and = mb.modtype;
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci			if (mb.modtype & CGW_MOD_ID)
91262306a36Sopenharmony_ci				mod->modfunc[modidx++] = mod_and_id;
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci			if (mb.modtype & CGW_MOD_DLC)
91562306a36Sopenharmony_ci				mod->modfunc[modidx++] = mod_and_ccdlc;
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci			if (mb.modtype & CGW_MOD_DATA)
91862306a36Sopenharmony_ci				mod->modfunc[modidx++] = mod_and_data;
91962306a36Sopenharmony_ci		}
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci		if (tb[CGW_MOD_OR]) {
92262306a36Sopenharmony_ci			nla_memcpy(&mb, tb[CGW_MOD_OR], CGW_MODATTR_LEN);
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci			canframecpy(&mod->modframe.or, &mb.cf);
92562306a36Sopenharmony_ci			mod->modtype.or = mb.modtype;
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci			if (mb.modtype & CGW_MOD_ID)
92862306a36Sopenharmony_ci				mod->modfunc[modidx++] = mod_or_id;
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci			if (mb.modtype & CGW_MOD_DLC)
93162306a36Sopenharmony_ci				mod->modfunc[modidx++] = mod_or_ccdlc;
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci			if (mb.modtype & CGW_MOD_DATA)
93462306a36Sopenharmony_ci				mod->modfunc[modidx++] = mod_or_data;
93562306a36Sopenharmony_ci		}
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci		if (tb[CGW_MOD_XOR]) {
93862306a36Sopenharmony_ci			nla_memcpy(&mb, tb[CGW_MOD_XOR], CGW_MODATTR_LEN);
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci			canframecpy(&mod->modframe.xor, &mb.cf);
94162306a36Sopenharmony_ci			mod->modtype.xor = mb.modtype;
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci			if (mb.modtype & CGW_MOD_ID)
94462306a36Sopenharmony_ci				mod->modfunc[modidx++] = mod_xor_id;
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci			if (mb.modtype & CGW_MOD_DLC)
94762306a36Sopenharmony_ci				mod->modfunc[modidx++] = mod_xor_ccdlc;
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci			if (mb.modtype & CGW_MOD_DATA)
95062306a36Sopenharmony_ci				mod->modfunc[modidx++] = mod_xor_data;
95162306a36Sopenharmony_ci		}
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci		if (tb[CGW_MOD_SET]) {
95462306a36Sopenharmony_ci			nla_memcpy(&mb, tb[CGW_MOD_SET], CGW_MODATTR_LEN);
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci			canframecpy(&mod->modframe.set, &mb.cf);
95762306a36Sopenharmony_ci			mod->modtype.set = mb.modtype;
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci			if (mb.modtype & CGW_MOD_ID)
96062306a36Sopenharmony_ci				mod->modfunc[modidx++] = mod_set_id;
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci			if (mb.modtype & CGW_MOD_DLC)
96362306a36Sopenharmony_ci				mod->modfunc[modidx++] = mod_set_ccdlc;
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci			if (mb.modtype & CGW_MOD_DATA)
96662306a36Sopenharmony_ci				mod->modfunc[modidx++] = mod_set_data;
96762306a36Sopenharmony_ci		}
96862306a36Sopenharmony_ci	}
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	/* check for checksum operations after CAN frame modifications */
97162306a36Sopenharmony_ci	if (modidx) {
97262306a36Sopenharmony_ci		if (tb[CGW_CS_CRC8]) {
97362306a36Sopenharmony_ci			struct cgw_csum_crc8 *c = nla_data(tb[CGW_CS_CRC8]);
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci			err = cgw_chk_csum_parms(c->from_idx, c->to_idx,
97662306a36Sopenharmony_ci						 c->result_idx, r);
97762306a36Sopenharmony_ci			if (err)
97862306a36Sopenharmony_ci				return err;
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci			nla_memcpy(&mod->csum.crc8, tb[CGW_CS_CRC8],
98162306a36Sopenharmony_ci				   CGW_CS_CRC8_LEN);
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci			/* select dedicated processing function to reduce
98462306a36Sopenharmony_ci			 * runtime operations in receive hot path.
98562306a36Sopenharmony_ci			 */
98662306a36Sopenharmony_ci			if (c->from_idx < 0 || c->to_idx < 0 ||
98762306a36Sopenharmony_ci			    c->result_idx < 0)
98862306a36Sopenharmony_ci				mod->csumfunc.crc8 = cgw_csum_crc8_rel;
98962306a36Sopenharmony_ci			else if (c->from_idx <= c->to_idx)
99062306a36Sopenharmony_ci				mod->csumfunc.crc8 = cgw_csum_crc8_pos;
99162306a36Sopenharmony_ci			else
99262306a36Sopenharmony_ci				mod->csumfunc.crc8 = cgw_csum_crc8_neg;
99362306a36Sopenharmony_ci		}
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci		if (tb[CGW_CS_XOR]) {
99662306a36Sopenharmony_ci			struct cgw_csum_xor *c = nla_data(tb[CGW_CS_XOR]);
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci			err = cgw_chk_csum_parms(c->from_idx, c->to_idx,
99962306a36Sopenharmony_ci						 c->result_idx, r);
100062306a36Sopenharmony_ci			if (err)
100162306a36Sopenharmony_ci				return err;
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci			nla_memcpy(&mod->csum.xor, tb[CGW_CS_XOR],
100462306a36Sopenharmony_ci				   CGW_CS_XOR_LEN);
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci			/* select dedicated processing function to reduce
100762306a36Sopenharmony_ci			 * runtime operations in receive hot path.
100862306a36Sopenharmony_ci			 */
100962306a36Sopenharmony_ci			if (c->from_idx < 0 || c->to_idx < 0 ||
101062306a36Sopenharmony_ci			    c->result_idx < 0)
101162306a36Sopenharmony_ci				mod->csumfunc.xor = cgw_csum_xor_rel;
101262306a36Sopenharmony_ci			else if (c->from_idx <= c->to_idx)
101362306a36Sopenharmony_ci				mod->csumfunc.xor = cgw_csum_xor_pos;
101462306a36Sopenharmony_ci			else
101562306a36Sopenharmony_ci				mod->csumfunc.xor = cgw_csum_xor_neg;
101662306a36Sopenharmony_ci		}
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci		if (tb[CGW_MOD_UID])
101962306a36Sopenharmony_ci			nla_memcpy(&mod->uid, tb[CGW_MOD_UID], sizeof(u32));
102062306a36Sopenharmony_ci	}
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci	if (gwtype == CGW_TYPE_CAN_CAN) {
102362306a36Sopenharmony_ci		/* check CGW_TYPE_CAN_CAN specific attributes */
102462306a36Sopenharmony_ci		struct can_can_gw *ccgw = (struct can_can_gw *)gwtypeattr;
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci		memset(ccgw, 0, sizeof(*ccgw));
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci		/* check for can_filter in attributes */
102962306a36Sopenharmony_ci		if (tb[CGW_FILTER])
103062306a36Sopenharmony_ci			nla_memcpy(&ccgw->filter, tb[CGW_FILTER],
103162306a36Sopenharmony_ci				   sizeof(struct can_filter));
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci		err = -ENODEV;
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci		/* specifying two interfaces is mandatory */
103662306a36Sopenharmony_ci		if (!tb[CGW_SRC_IF] || !tb[CGW_DST_IF])
103762306a36Sopenharmony_ci			return err;
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci		ccgw->src_idx = nla_get_u32(tb[CGW_SRC_IF]);
104062306a36Sopenharmony_ci		ccgw->dst_idx = nla_get_u32(tb[CGW_DST_IF]);
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci		/* both indices set to 0 for flushing all routing entries */
104362306a36Sopenharmony_ci		if (!ccgw->src_idx && !ccgw->dst_idx)
104462306a36Sopenharmony_ci			return 0;
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci		/* only one index set to 0 is an error */
104762306a36Sopenharmony_ci		if (!ccgw->src_idx || !ccgw->dst_idx)
104862306a36Sopenharmony_ci			return err;
104962306a36Sopenharmony_ci	}
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	/* add the checks for other gwtypes here */
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	return 0;
105462306a36Sopenharmony_ci}
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_cistatic int cgw_create_job(struct sk_buff *skb,  struct nlmsghdr *nlh,
105762306a36Sopenharmony_ci			  struct netlink_ext_ack *extack)
105862306a36Sopenharmony_ci{
105962306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
106062306a36Sopenharmony_ci	struct rtcanmsg *r;
106162306a36Sopenharmony_ci	struct cgw_job *gwj;
106262306a36Sopenharmony_ci	struct cf_mod mod;
106362306a36Sopenharmony_ci	struct can_can_gw ccgw;
106462306a36Sopenharmony_ci	u8 limhops = 0;
106562306a36Sopenharmony_ci	int err = 0;
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci	if (!netlink_capable(skb, CAP_NET_ADMIN))
106862306a36Sopenharmony_ci		return -EPERM;
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	if (nlmsg_len(nlh) < sizeof(*r))
107162306a36Sopenharmony_ci		return -EINVAL;
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	r = nlmsg_data(nlh);
107462306a36Sopenharmony_ci	if (r->can_family != AF_CAN)
107562306a36Sopenharmony_ci		return -EPFNOSUPPORT;
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	/* so far we only support CAN -> CAN routings */
107862306a36Sopenharmony_ci	if (r->gwtype != CGW_TYPE_CAN_CAN)
107962306a36Sopenharmony_ci		return -EINVAL;
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	err = cgw_parse_attr(nlh, &mod, CGW_TYPE_CAN_CAN, &ccgw, &limhops);
108262306a36Sopenharmony_ci	if (err < 0)
108362306a36Sopenharmony_ci		return err;
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci	if (mod.uid) {
108662306a36Sopenharmony_ci		ASSERT_RTNL();
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci		/* check for updating an existing job with identical uid */
108962306a36Sopenharmony_ci		hlist_for_each_entry(gwj, &net->can.cgw_list, list) {
109062306a36Sopenharmony_ci			if (gwj->mod.uid != mod.uid)
109162306a36Sopenharmony_ci				continue;
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci			/* interfaces & filters must be identical */
109462306a36Sopenharmony_ci			if (memcmp(&gwj->ccgw, &ccgw, sizeof(ccgw)))
109562306a36Sopenharmony_ci				return -EINVAL;
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci			/* update modifications with disabled softirq & quit */
109862306a36Sopenharmony_ci			local_bh_disable();
109962306a36Sopenharmony_ci			memcpy(&gwj->mod, &mod, sizeof(mod));
110062306a36Sopenharmony_ci			local_bh_enable();
110162306a36Sopenharmony_ci			return 0;
110262306a36Sopenharmony_ci		}
110362306a36Sopenharmony_ci	}
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci	/* ifindex == 0 is not allowed for job creation */
110662306a36Sopenharmony_ci	if (!ccgw.src_idx || !ccgw.dst_idx)
110762306a36Sopenharmony_ci		return -ENODEV;
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	gwj = kmem_cache_alloc(cgw_cache, GFP_KERNEL);
111062306a36Sopenharmony_ci	if (!gwj)
111162306a36Sopenharmony_ci		return -ENOMEM;
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci	gwj->handled_frames = 0;
111462306a36Sopenharmony_ci	gwj->dropped_frames = 0;
111562306a36Sopenharmony_ci	gwj->deleted_frames = 0;
111662306a36Sopenharmony_ci	gwj->flags = r->flags;
111762306a36Sopenharmony_ci	gwj->gwtype = r->gwtype;
111862306a36Sopenharmony_ci	gwj->limit_hops = limhops;
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci	/* insert already parsed information */
112162306a36Sopenharmony_ci	memcpy(&gwj->mod, &mod, sizeof(mod));
112262306a36Sopenharmony_ci	memcpy(&gwj->ccgw, &ccgw, sizeof(ccgw));
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci	err = -ENODEV;
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	gwj->src.dev = __dev_get_by_index(net, gwj->ccgw.src_idx);
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci	if (!gwj->src.dev)
112962306a36Sopenharmony_ci		goto out;
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci	if (gwj->src.dev->type != ARPHRD_CAN)
113262306a36Sopenharmony_ci		goto out;
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci	gwj->dst.dev = __dev_get_by_index(net, gwj->ccgw.dst_idx);
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	if (!gwj->dst.dev)
113762306a36Sopenharmony_ci		goto out;
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	if (gwj->dst.dev->type != ARPHRD_CAN)
114062306a36Sopenharmony_ci		goto out;
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci	/* is sending the skb back to the incoming interface intended? */
114362306a36Sopenharmony_ci	if (gwj->src.dev == gwj->dst.dev &&
114462306a36Sopenharmony_ci	    !(gwj->flags & CGW_FLAGS_CAN_IIF_TX_OK)) {
114562306a36Sopenharmony_ci		err = -EINVAL;
114662306a36Sopenharmony_ci		goto out;
114762306a36Sopenharmony_ci	}
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci	ASSERT_RTNL();
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci	err = cgw_register_filter(net, gwj);
115262306a36Sopenharmony_ci	if (!err)
115362306a36Sopenharmony_ci		hlist_add_head_rcu(&gwj->list, &net->can.cgw_list);
115462306a36Sopenharmony_ciout:
115562306a36Sopenharmony_ci	if (err)
115662306a36Sopenharmony_ci		kmem_cache_free(cgw_cache, gwj);
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	return err;
115962306a36Sopenharmony_ci}
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_cistatic void cgw_remove_all_jobs(struct net *net)
116262306a36Sopenharmony_ci{
116362306a36Sopenharmony_ci	struct cgw_job *gwj = NULL;
116462306a36Sopenharmony_ci	struct hlist_node *nx;
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	ASSERT_RTNL();
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci	hlist_for_each_entry_safe(gwj, nx, &net->can.cgw_list, list) {
116962306a36Sopenharmony_ci		hlist_del(&gwj->list);
117062306a36Sopenharmony_ci		cgw_unregister_filter(net, gwj);
117162306a36Sopenharmony_ci		call_rcu(&gwj->rcu, cgw_job_free_rcu);
117262306a36Sopenharmony_ci	}
117362306a36Sopenharmony_ci}
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_cistatic int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh,
117662306a36Sopenharmony_ci			  struct netlink_ext_ack *extack)
117762306a36Sopenharmony_ci{
117862306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
117962306a36Sopenharmony_ci	struct cgw_job *gwj = NULL;
118062306a36Sopenharmony_ci	struct hlist_node *nx;
118162306a36Sopenharmony_ci	struct rtcanmsg *r;
118262306a36Sopenharmony_ci	struct cf_mod mod;
118362306a36Sopenharmony_ci	struct can_can_gw ccgw;
118462306a36Sopenharmony_ci	u8 limhops = 0;
118562306a36Sopenharmony_ci	int err = 0;
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci	if (!netlink_capable(skb, CAP_NET_ADMIN))
118862306a36Sopenharmony_ci		return -EPERM;
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	if (nlmsg_len(nlh) < sizeof(*r))
119162306a36Sopenharmony_ci		return -EINVAL;
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	r = nlmsg_data(nlh);
119462306a36Sopenharmony_ci	if (r->can_family != AF_CAN)
119562306a36Sopenharmony_ci		return -EPFNOSUPPORT;
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci	/* so far we only support CAN -> CAN routings */
119862306a36Sopenharmony_ci	if (r->gwtype != CGW_TYPE_CAN_CAN)
119962306a36Sopenharmony_ci		return -EINVAL;
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	err = cgw_parse_attr(nlh, &mod, CGW_TYPE_CAN_CAN, &ccgw, &limhops);
120262306a36Sopenharmony_ci	if (err < 0)
120362306a36Sopenharmony_ci		return err;
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci	/* two interface indices both set to 0 => remove all entries */
120662306a36Sopenharmony_ci	if (!ccgw.src_idx && !ccgw.dst_idx) {
120762306a36Sopenharmony_ci		cgw_remove_all_jobs(net);
120862306a36Sopenharmony_ci		return 0;
120962306a36Sopenharmony_ci	}
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	err = -EINVAL;
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci	ASSERT_RTNL();
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci	/* remove only the first matching entry */
121662306a36Sopenharmony_ci	hlist_for_each_entry_safe(gwj, nx, &net->can.cgw_list, list) {
121762306a36Sopenharmony_ci		if (gwj->flags != r->flags)
121862306a36Sopenharmony_ci			continue;
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci		if (gwj->limit_hops != limhops)
122162306a36Sopenharmony_ci			continue;
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci		/* we have a match when uid is enabled and identical */
122462306a36Sopenharmony_ci		if (gwj->mod.uid || mod.uid) {
122562306a36Sopenharmony_ci			if (gwj->mod.uid != mod.uid)
122662306a36Sopenharmony_ci				continue;
122762306a36Sopenharmony_ci		} else {
122862306a36Sopenharmony_ci			/* no uid => check for identical modifications */
122962306a36Sopenharmony_ci			if (memcmp(&gwj->mod, &mod, sizeof(mod)))
123062306a36Sopenharmony_ci				continue;
123162306a36Sopenharmony_ci		}
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ci		/* if (r->gwtype == CGW_TYPE_CAN_CAN) - is made sure here */
123462306a36Sopenharmony_ci		if (memcmp(&gwj->ccgw, &ccgw, sizeof(ccgw)))
123562306a36Sopenharmony_ci			continue;
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci		hlist_del(&gwj->list);
123862306a36Sopenharmony_ci		cgw_unregister_filter(net, gwj);
123962306a36Sopenharmony_ci		call_rcu(&gwj->rcu, cgw_job_free_rcu);
124062306a36Sopenharmony_ci		err = 0;
124162306a36Sopenharmony_ci		break;
124262306a36Sopenharmony_ci	}
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_ci	return err;
124562306a36Sopenharmony_ci}
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_cistatic int __net_init cangw_pernet_init(struct net *net)
124862306a36Sopenharmony_ci{
124962306a36Sopenharmony_ci	INIT_HLIST_HEAD(&net->can.cgw_list);
125062306a36Sopenharmony_ci	return 0;
125162306a36Sopenharmony_ci}
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_cistatic void __net_exit cangw_pernet_exit_batch(struct list_head *net_list)
125462306a36Sopenharmony_ci{
125562306a36Sopenharmony_ci	struct net *net;
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci	rtnl_lock();
125862306a36Sopenharmony_ci	list_for_each_entry(net, net_list, exit_list)
125962306a36Sopenharmony_ci		cgw_remove_all_jobs(net);
126062306a36Sopenharmony_ci	rtnl_unlock();
126162306a36Sopenharmony_ci}
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_cistatic struct pernet_operations cangw_pernet_ops = {
126462306a36Sopenharmony_ci	.init = cangw_pernet_init,
126562306a36Sopenharmony_ci	.exit_batch = cangw_pernet_exit_batch,
126662306a36Sopenharmony_ci};
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_cistatic __init int cgw_module_init(void)
126962306a36Sopenharmony_ci{
127062306a36Sopenharmony_ci	int ret;
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci	/* sanitize given module parameter */
127362306a36Sopenharmony_ci	max_hops = clamp_t(unsigned int, max_hops, CGW_MIN_HOPS, CGW_MAX_HOPS);
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_ci	pr_info("can: netlink gateway - max_hops=%d\n",	max_hops);
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_ci	ret = register_pernet_subsys(&cangw_pernet_ops);
127862306a36Sopenharmony_ci	if (ret)
127962306a36Sopenharmony_ci		return ret;
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci	ret = -ENOMEM;
128262306a36Sopenharmony_ci	cgw_cache = kmem_cache_create("can_gw", sizeof(struct cgw_job),
128362306a36Sopenharmony_ci				      0, 0, NULL);
128462306a36Sopenharmony_ci	if (!cgw_cache)
128562306a36Sopenharmony_ci		goto out_cache_create;
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci	/* set notifier */
128862306a36Sopenharmony_ci	notifier.notifier_call = cgw_notifier;
128962306a36Sopenharmony_ci	ret = register_netdevice_notifier(&notifier);
129062306a36Sopenharmony_ci	if (ret)
129162306a36Sopenharmony_ci		goto out_register_notifier;
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci	ret = rtnl_register_module(THIS_MODULE, PF_CAN, RTM_GETROUTE,
129462306a36Sopenharmony_ci				   NULL, cgw_dump_jobs, 0);
129562306a36Sopenharmony_ci	if (ret)
129662306a36Sopenharmony_ci		goto out_rtnl_register1;
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	ret = rtnl_register_module(THIS_MODULE, PF_CAN, RTM_NEWROUTE,
129962306a36Sopenharmony_ci				   cgw_create_job, NULL, 0);
130062306a36Sopenharmony_ci	if (ret)
130162306a36Sopenharmony_ci		goto out_rtnl_register2;
130262306a36Sopenharmony_ci	ret = rtnl_register_module(THIS_MODULE, PF_CAN, RTM_DELROUTE,
130362306a36Sopenharmony_ci				   cgw_remove_job, NULL, 0);
130462306a36Sopenharmony_ci	if (ret)
130562306a36Sopenharmony_ci		goto out_rtnl_register3;
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci	return 0;
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ciout_rtnl_register3:
131062306a36Sopenharmony_ci	rtnl_unregister(PF_CAN, RTM_NEWROUTE);
131162306a36Sopenharmony_ciout_rtnl_register2:
131262306a36Sopenharmony_ci	rtnl_unregister(PF_CAN, RTM_GETROUTE);
131362306a36Sopenharmony_ciout_rtnl_register1:
131462306a36Sopenharmony_ci	unregister_netdevice_notifier(&notifier);
131562306a36Sopenharmony_ciout_register_notifier:
131662306a36Sopenharmony_ci	kmem_cache_destroy(cgw_cache);
131762306a36Sopenharmony_ciout_cache_create:
131862306a36Sopenharmony_ci	unregister_pernet_subsys(&cangw_pernet_ops);
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci	return ret;
132162306a36Sopenharmony_ci}
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_cistatic __exit void cgw_module_exit(void)
132462306a36Sopenharmony_ci{
132562306a36Sopenharmony_ci	rtnl_unregister_all(PF_CAN);
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci	unregister_netdevice_notifier(&notifier);
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	unregister_pernet_subsys(&cangw_pernet_ops);
133062306a36Sopenharmony_ci	rcu_barrier(); /* Wait for completion of call_rcu()'s */
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ci	kmem_cache_destroy(cgw_cache);
133362306a36Sopenharmony_ci}
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_cimodule_init(cgw_module_init);
133662306a36Sopenharmony_cimodule_exit(cgw_module_exit);
1337