Commit c1aabdf379bc2feeb0df7057ed5bad96f492133e

Authored by Oliver Hartkopp
Committed by David S. Miller
1 parent 13225977f5

can-gw: add netlink based CAN routing

This patch adds a CAN Gateway/Router to route (and modify) CAN frames.

It is based on the PF_CAN core infrastructure for msg filtering and msg
sending and can optionally modify routed CAN frames on the fly.
CAN frames can *only* be routed between CAN network interfaces (one hop).
They can be modified with AND/OR/XOR/SET operations as configured by the
netlink configuration interface known e.g. from iptables. From the netlink
view this can-gw implements RTM_{NEW|DEL|GET}ROUTE for PF_CAN.

The CAN specific userspace tool to manage CAN routing entries can be found in
the CAN utils http://svn.berlios.de/wsvn/socketcan/trunk/can-utils/cangw.c
at the SocketCAN SVN on BerliOS.

Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
Signed-off-by: David S. Miller <davem@davemloft.net>

Showing 5 changed files with 1138 additions and 0 deletions Side-by-side Diff

include/linux/can/Kbuild
1 1 header-y += raw.h
2 2 header-y += bcm.h
  3 +header-y += gw.h
3 4 header-y += error.h
4 5 header-y += netlink.h
include/linux/can/gw.h
  1 +/*
  2 + * linux/can/gw.h
  3 + *
  4 + * Definitions for CAN frame Gateway/Router/Bridge
  5 + *
  6 + * Author: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
  7 + * Copyright (c) 2011 Volkswagen Group Electronic Research
  8 + * All rights reserved.
  9 + *
  10 + * Send feedback to <socketcan-users@lists.berlios.de>
  11 + *
  12 + */
  13 +
  14 +#ifndef CAN_GW_H
  15 +#define CAN_GW_H
  16 +
  17 +#include <linux/types.h>
  18 +#include <linux/can.h>
  19 +
  20 +struct rtcanmsg {
  21 + __u8 can_family;
  22 + __u8 gwtype;
  23 + __u16 flags;
  24 +};
  25 +
  26 +/* CAN gateway types */
  27 +enum {
  28 + CGW_TYPE_UNSPEC,
  29 + CGW_TYPE_CAN_CAN, /* CAN->CAN routing */
  30 + __CGW_TYPE_MAX
  31 +};
  32 +
  33 +#define CGW_TYPE_MAX (__CGW_TYPE_MAX - 1)
  34 +
  35 +/* CAN rtnetlink attribute definitions */
  36 +enum {
  37 + CGW_UNSPEC,
  38 + CGW_MOD_AND, /* CAN frame modification binary AND */
  39 + CGW_MOD_OR, /* CAN frame modification binary OR */
  40 + CGW_MOD_XOR, /* CAN frame modification binary XOR */
  41 + CGW_MOD_SET, /* CAN frame modification set alternate values */
  42 + CGW_CS_XOR, /* set data[] XOR checksum into data[index] */
  43 + CGW_CS_CRC8, /* set data[] CRC8 checksum into data[index] */
  44 + CGW_HANDLED, /* number of handled CAN frames */
  45 + CGW_DROPPED, /* number of dropped CAN frames */
  46 + CGW_SRC_IF, /* ifindex of source network interface */
  47 + CGW_DST_IF, /* ifindex of destination network interface */
  48 + CGW_FILTER, /* specify struct can_filter on source CAN device */
  49 + __CGW_MAX
  50 +};
  51 +
  52 +#define CGW_MAX (__CGW_MAX - 1)
  53 +
  54 +#define CGW_FLAGS_CAN_ECHO 0x01
  55 +#define CGW_FLAGS_CAN_SRC_TSTAMP 0x02
  56 +
  57 +#define CGW_MOD_FUNCS 4 /* AND OR XOR SET */
  58 +
  59 +/* CAN frame elements that are affected by curr. 3 CAN frame modifications */
  60 +#define CGW_MOD_ID 0x01
  61 +#define CGW_MOD_DLC 0x02
  62 +#define CGW_MOD_DATA 0x04
  63 +
  64 +#define CGW_FRAME_MODS 3 /* ID DLC DATA */
  65 +
  66 +#define MAX_MODFUNCTIONS (CGW_MOD_FUNCS * CGW_FRAME_MODS)
  67 +
  68 +struct cgw_frame_mod {
  69 + struct can_frame cf;
  70 + __u8 modtype;
  71 +} __attribute__((packed));
  72 +
  73 +#define CGW_MODATTR_LEN sizeof(struct cgw_frame_mod)
  74 +
  75 +struct cgw_csum_xor {
  76 + __s8 from_idx;
  77 + __s8 to_idx;
  78 + __s8 result_idx;
  79 + __u8 init_xor_val;
  80 +} __attribute__((packed));
  81 +
  82 +struct cgw_csum_crc8 {
  83 + __s8 from_idx;
  84 + __s8 to_idx;
  85 + __s8 result_idx;
  86 + __u8 init_crc_val;
  87 + __u8 final_xor_val;
  88 + __u8 crctab[256];
  89 + __u8 profile;
  90 + __u8 profile_data[20];
  91 +} __attribute__((packed));
  92 +
  93 +/* length of checksum operation parameters. idx = index in CAN frame data[] */
  94 +#define CGW_CS_XOR_LEN sizeof(struct cgw_csum_xor)
  95 +#define CGW_CS_CRC8_LEN sizeof(struct cgw_csum_crc8)
  96 +
  97 +/* CRC8 profiles (compute CRC for additional data elements - see below) */
  98 +enum {
  99 + CGW_CRC8PRF_UNSPEC,
  100 + CGW_CRC8PRF_1U8, /* compute one additional u8 value */
  101 + CGW_CRC8PRF_16U8, /* u8 value table indexed by data[1] & 0xF */
  102 + CGW_CRC8PRF_SFFID_XOR, /* (can_id & 0xFF) ^ (can_id >> 8 & 0xFF) */
  103 + __CGW_CRC8PRF_MAX
  104 +};
  105 +
  106 +#define CGW_CRC8PRF_MAX (__CGW_CRC8PRF_MAX - 1)
  107 +
  108 +/*
  109 + * CAN rtnetlink attribute contents in detail
  110 + *
  111 + * CGW_XXX_IF (length 4 bytes):
  112 + * Sets an interface index for source/destination network interfaces.
  113 + * For the CAN->CAN gwtype the indices of _two_ CAN interfaces are mandatory.
  114 + *
  115 + * CGW_FILTER (length 8 bytes):
  116 + * Sets a CAN receive filter for the gateway job specified by the
  117 + * struct can_filter described in include/linux/can.h
  118 + *
  119 + * CGW_MOD_XXX (length 17 bytes):
  120 + * Specifies a modification that's done to a received CAN frame before it is
  121 + * send out to the destination interface.
  122 + *
  123 + * <struct can_frame> data used as operator
  124 + * <u8> affected CAN frame elements
  125 + *
  126 + * CGW_CS_XOR (length 4 bytes):
  127 + * Set a simple XOR checksum starting with an initial value into
  128 + * data[result-idx] using data[start-idx] .. data[end-idx]
  129 + *
  130 + * The XOR checksum is calculated like this:
  131 + *
  132 + * xor = init_xor_val
  133 + *
  134 + * for (i = from_idx .. to_idx)
  135 + * xor ^= can_frame.data[i]
  136 + *
  137 + * can_frame.data[ result_idx ] = xor
  138 + *
  139 + * CGW_CS_CRC8 (length 282 bytes):
  140 + * Set a CRC8 value into data[result-idx] using a given 256 byte CRC8 table,
  141 + * a given initial value and a defined input data[start-idx] .. data[end-idx].
  142 + * Finally the result value is XOR'ed with the final_xor_val.
  143 + *
  144 + * The CRC8 checksum is calculated like this:
  145 + *
  146 + * crc = init_crc_val
  147 + *
  148 + * for (i = from_idx .. to_idx)
  149 + * crc = crctab[ crc ^ can_frame.data[i] ]
  150 + *
  151 + * can_frame.data[ result_idx ] = crc ^ final_xor_val
  152 + *
  153 + * The calculated CRC may contain additional source data elements that can be
  154 + * defined in the handling of 'checksum profiles' e.g. shown in AUTOSAR specs
  155 + * like http://www.autosar.org/download/R4.0/AUTOSAR_SWS_E2ELibrary.pdf
  156 + * E.g. the profile_data[] may contain additional u8 values (called DATA_IDs)
  157 + * that are used depending on counter values inside the CAN frame data[].
  158 + * So far only three profiles have been implemented for illustration.
  159 + *
  160 + * Remark: In general the attribute data is a linear buffer.
  161 + * Beware of sending unpacked or aligned structs!
  162 + */
  163 +
  164 +#endif
... ... @@ -40,6 +40,17 @@
40 40 CAN messages are used on the bus (e.g. in automotive environments).
41 41 To use the Broadcast Manager, use AF_CAN with protocol CAN_BCM.
42 42  
  43 +config CAN_GW
  44 + tristate "CAN Gateway/Router (with netlink configuration)"
  45 + depends on CAN
  46 + default N
  47 + ---help---
  48 + The CAN Gateway/Router is used to route (and modify) CAN frames.
  49 + It is based on the PF_CAN core infrastructure for msg filtering and
  50 + msg sending and can optionally modify routed CAN frames on the fly.
  51 + CAN frames can be routed between CAN network interfaces (one hop).
  52 + They can be modified with AND/OR/XOR/SET operations as configured
  53 + by the netlink configuration interface known e.g. from iptables.
43 54  
44 55 source "drivers/net/can/Kconfig"
... ... @@ -10,4 +10,7 @@
10 10  
11 11 obj-$(CONFIG_CAN_BCM) += can-bcm.o
12 12 can-bcm-y := bcm.o
  13 +
  14 +obj-$(CONFIG_CAN_GW) += can-gw.o
  15 +can-gw-y := gw.o
  1 +/*
  2 + * gw.c - CAN frame Gateway/Router/Bridge with netlink interface
  3 + *
  4 + * Copyright (c) 2011 Volkswagen Group Electronic Research
  5 + * All rights reserved.
  6 + *
  7 + * Redistribution and use in source and binary forms, with or without
  8 + * modification, are permitted provided that the following conditions
  9 + * are met:
  10 + * 1. Redistributions of source code must retain the above copyright
  11 + * notice, this list of conditions and the following disclaimer.
  12 + * 2. Redistributions in binary form must reproduce the above copyright
  13 + * notice, this list of conditions and the following disclaimer in the
  14 + * documentation and/or other materials provided with the distribution.
  15 + * 3. Neither the name of Volkswagen nor the names of its contributors
  16 + * may be used to endorse or promote products derived from this software
  17 + * without specific prior written permission.
  18 + *
  19 + * Alternatively, provided that this notice is retained in full, this
  20 + * software may be distributed under the terms of the GNU General
  21 + * Public License ("GPL") version 2, in which case the provisions of the
  22 + * GPL apply INSTEAD OF those given above.
  23 + *
  24 + * The provided data structures and external interfaces from this code
  25 + * are not restricted to be used by modules with a GPL compatible license.
  26 + *
  27 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  28 + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  29 + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  30 + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  31 + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  32 + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  33 + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  34 + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  35 + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  36 + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  37 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
  38 + * DAMAGE.
  39 + *
  40 + * Send feedback to <socketcan-users@lists.berlios.de>
  41 + *
  42 + */
  43 +
  44 +#include <linux/module.h>
  45 +#include <linux/init.h>
  46 +#include <linux/types.h>
  47 +#include <linux/list.h>
  48 +#include <linux/spinlock.h>
  49 +#include <linux/rcupdate.h>
  50 +#include <linux/rculist.h>
  51 +#include <linux/net.h>
  52 +#include <linux/netdevice.h>
  53 +#include <linux/if_arp.h>
  54 +#include <linux/skbuff.h>
  55 +#include <linux/can.h>
  56 +#include <linux/can/core.h>
  57 +#include <linux/can/gw.h>
  58 +#include <net/rtnetlink.h>
  59 +#include <net/net_namespace.h>
  60 +#include <net/sock.h>
  61 +
  62 +#define CAN_GW_VERSION "20101209"
  63 +static __initdata const char banner[] =
  64 + KERN_INFO "can: netlink gateway (rev " CAN_GW_VERSION ")\n";
  65 +
  66 +MODULE_DESCRIPTION("PF_CAN netlink gateway");
  67 +MODULE_LICENSE("Dual BSD/GPL");
  68 +MODULE_AUTHOR("Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
  69 +MODULE_ALIAS("can-gw");
  70 +
  71 +HLIST_HEAD(cgw_list);
  72 +static struct notifier_block notifier;
  73 +
  74 +static struct kmem_cache *cgw_cache __read_mostly;
  75 +
  76 +/* structure that contains the (on-the-fly) CAN frame modifications */
  77 +struct cf_mod {
  78 + struct {
  79 + struct can_frame and;
  80 + struct can_frame or;
  81 + struct can_frame xor;
  82 + struct can_frame set;
  83 + } modframe;
  84 + struct {
  85 + u8 and;
  86 + u8 or;
  87 + u8 xor;
  88 + u8 set;
  89 + } modtype;
  90 + void (*modfunc[MAX_MODFUNCTIONS])(struct can_frame *cf,
  91 + struct cf_mod *mod);
  92 +
  93 + /* CAN frame checksum calculation after CAN frame modifications */
  94 + struct {
  95 + struct cgw_csum_xor xor;
  96 + struct cgw_csum_crc8 crc8;
  97 + } csum;
  98 + struct {
  99 + void (*xor)(struct can_frame *cf, struct cgw_csum_xor *xor);
  100 + void (*crc8)(struct can_frame *cf, struct cgw_csum_crc8 *crc8);
  101 + } csumfunc;
  102 +};
  103 +
  104 +
  105 +/*
  106 + * So far we just support CAN -> CAN routing and frame modifications.
  107 + *
  108 + * The internal can_can_gw structure contains data and attributes for
  109 + * a CAN -> CAN gateway job.
  110 + */
  111 +struct can_can_gw {
  112 + struct can_filter filter;
  113 + int src_idx;
  114 + int dst_idx;
  115 +};
  116 +
  117 +/* list entry for CAN gateways jobs */
  118 +struct cgw_job {
  119 + struct hlist_node list;
  120 + struct rcu_head rcu;
  121 + u32 handled_frames;
  122 + u32 dropped_frames;
  123 + struct cf_mod mod;
  124 + union {
  125 + /* CAN frame data source */
  126 + struct net_device *dev;
  127 + } src;
  128 + union {
  129 + /* CAN frame data destination */
  130 + struct net_device *dev;
  131 + } dst;
  132 + union {
  133 + struct can_can_gw ccgw;
  134 + /* tbc */
  135 + };
  136 + u8 gwtype;
  137 + u16 flags;
  138 +};
  139 +
  140 +/* modification functions that are invoked in the hot path in can_can_gw_rcv */
  141 +
  142 +#define MODFUNC(func, op) static void func(struct can_frame *cf, \
  143 + struct cf_mod *mod) { op ; }
  144 +
  145 +MODFUNC(mod_and_id, cf->can_id &= mod->modframe.and.can_id)
  146 +MODFUNC(mod_and_dlc, cf->can_dlc &= mod->modframe.and.can_dlc)
  147 +MODFUNC(mod_and_data, *(u64 *)cf->data &= *(u64 *)mod->modframe.and.data)
  148 +MODFUNC(mod_or_id, cf->can_id |= mod->modframe.or.can_id)
  149 +MODFUNC(mod_or_dlc, cf->can_dlc |= mod->modframe.or.can_dlc)
  150 +MODFUNC(mod_or_data, *(u64 *)cf->data |= *(u64 *)mod->modframe.or.data)
  151 +MODFUNC(mod_xor_id, cf->can_id ^= mod->modframe.xor.can_id)
  152 +MODFUNC(mod_xor_dlc, cf->can_dlc ^= mod->modframe.xor.can_dlc)
  153 +MODFUNC(mod_xor_data, *(u64 *)cf->data ^= *(u64 *)mod->modframe.xor.data)
  154 +MODFUNC(mod_set_id, cf->can_id = mod->modframe.set.can_id)
  155 +MODFUNC(mod_set_dlc, cf->can_dlc = mod->modframe.set.can_dlc)
  156 +MODFUNC(mod_set_data, *(u64 *)cf->data = *(u64 *)mod->modframe.set.data)
  157 +
  158 +static inline void canframecpy(struct can_frame *dst, struct can_frame *src)
  159 +{
  160 + /*
  161 + * Copy the struct members separately to ensure that no uninitialized
  162 + * data are copied in the 3 bytes hole of the struct. This is needed
  163 + * to make easy compares of the data in the struct cf_mod.
  164 + */
  165 +
  166 + dst->can_id = src->can_id;
  167 + dst->can_dlc = src->can_dlc;
  168 + *(u64 *)dst->data = *(u64 *)src->data;
  169 +}
  170 +
  171 +static int cgw_chk_csum_parms(s8 fr, s8 to, s8 re)
  172 +{
  173 + /*
  174 + * absolute dlc values 0 .. 7 => 0 .. 7, e.g. data [0]
  175 + * relative to received dlc -1 .. -8 :
  176 + * e.g. for received dlc = 8
  177 + * -1 => index = 7 (data[7])
  178 + * -3 => index = 5 (data[5])
  179 + * -8 => index = 0 (data[0])
  180 + */
  181 +
  182 + if (fr > -9 && fr < 8 &&
  183 + to > -9 && to < 8 &&
  184 + re > -9 && re < 8)
  185 + return 0;
  186 + else
  187 + return -EINVAL;
  188 +}
  189 +
  190 +static inline int calc_idx(int idx, int rx_dlc)
  191 +{
  192 + if (idx < 0)
  193 + return rx_dlc + idx;
  194 + else
  195 + return idx;
  196 +}
  197 +
  198 +static void cgw_csum_xor_rel(struct can_frame *cf, struct cgw_csum_xor *xor)
  199 +{
  200 + int from = calc_idx(xor->from_idx, cf->can_dlc);
  201 + int to = calc_idx(xor->to_idx, cf->can_dlc);
  202 + int res = calc_idx(xor->result_idx, cf->can_dlc);
  203 + u8 val = xor->init_xor_val;
  204 + int i;
  205 +
  206 + if (from < 0 || to < 0 || res < 0)
  207 + return;
  208 +
  209 + if (from <= to) {
  210 + for (i = from; i <= to; i++)
  211 + val ^= cf->data[i];
  212 + } else {
  213 + for (i = from; i >= to; i--)
  214 + val ^= cf->data[i];
  215 + }
  216 +
  217 + cf->data[res] = val;
  218 +}
  219 +
  220 +static void cgw_csum_xor_pos(struct can_frame *cf, struct cgw_csum_xor *xor)
  221 +{
  222 + u8 val = xor->init_xor_val;
  223 + int i;
  224 +
  225 + for (i = xor->from_idx; i <= xor->to_idx; i++)
  226 + val ^= cf->data[i];
  227 +
  228 + cf->data[xor->result_idx] = val;
  229 +}
  230 +
  231 +static void cgw_csum_xor_neg(struct can_frame *cf, struct cgw_csum_xor *xor)
  232 +{
  233 + u8 val = xor->init_xor_val;
  234 + int i;
  235 +
  236 + for (i = xor->from_idx; i >= xor->to_idx; i--)
  237 + val ^= cf->data[i];
  238 +
  239 + cf->data[xor->result_idx] = val;
  240 +}
  241 +
  242 +static void cgw_csum_crc8_rel(struct can_frame *cf, struct cgw_csum_crc8 *crc8)
  243 +{
  244 + int from = calc_idx(crc8->from_idx, cf->can_dlc);
  245 + int to = calc_idx(crc8->to_idx, cf->can_dlc);
  246 + int res = calc_idx(crc8->result_idx, cf->can_dlc);
  247 + u8 crc = crc8->init_crc_val;
  248 + int i;
  249 +
  250 + if (from < 0 || to < 0 || res < 0)
  251 + return;
  252 +
  253 + if (from <= to) {
  254 + for (i = crc8->from_idx; i <= crc8->to_idx; i++)
  255 + crc = crc8->crctab[crc^cf->data[i]];
  256 + } else {
  257 + for (i = crc8->from_idx; i >= crc8->to_idx; i--)
  258 + crc = crc8->crctab[crc^cf->data[i]];
  259 + }
  260 +
  261 + switch (crc8->profile) {
  262 +
  263 + case CGW_CRC8PRF_1U8:
  264 + crc = crc8->crctab[crc^crc8->profile_data[0]];
  265 + break;
  266 +
  267 + case CGW_CRC8PRF_16U8:
  268 + crc = crc8->crctab[crc^crc8->profile_data[cf->data[1] & 0xF]];
  269 + break;
  270 +
  271 + case CGW_CRC8PRF_SFFID_XOR:
  272 + crc = crc8->crctab[crc^(cf->can_id & 0xFF)^
  273 + (cf->can_id >> 8 & 0xFF)];
  274 + break;
  275 +
  276 + }
  277 +
  278 + cf->data[crc8->result_idx] = crc^crc8->final_xor_val;
  279 +}
  280 +
  281 +static void cgw_csum_crc8_pos(struct can_frame *cf, struct cgw_csum_crc8 *crc8)
  282 +{
  283 + u8 crc = crc8->init_crc_val;
  284 + int i;
  285 +
  286 + for (i = crc8->from_idx; i <= crc8->to_idx; i++)
  287 + crc = crc8->crctab[crc^cf->data[i]];
  288 +
  289 + switch (crc8->profile) {
  290 +
  291 + case CGW_CRC8PRF_1U8:
  292 + crc = crc8->crctab[crc^crc8->profile_data[0]];
  293 + break;
  294 +
  295 + case CGW_CRC8PRF_16U8:
  296 + crc = crc8->crctab[crc^crc8->profile_data[cf->data[1] & 0xF]];
  297 + break;
  298 +
  299 + case CGW_CRC8PRF_SFFID_XOR:
  300 + crc = crc8->crctab[crc^(cf->can_id & 0xFF)^
  301 + (cf->can_id >> 8 & 0xFF)];
  302 + break;
  303 + }
  304 +
  305 + cf->data[crc8->result_idx] = crc^crc8->final_xor_val;
  306 +}
  307 +
  308 +static void cgw_csum_crc8_neg(struct can_frame *cf, struct cgw_csum_crc8 *crc8)
  309 +{
  310 + u8 crc = crc8->init_crc_val;
  311 + int i;
  312 +
  313 + for (i = crc8->from_idx; i >= crc8->to_idx; i--)
  314 + crc = crc8->crctab[crc^cf->data[i]];
  315 +
  316 + switch (crc8->profile) {
  317 +
  318 + case CGW_CRC8PRF_1U8:
  319 + crc = crc8->crctab[crc^crc8->profile_data[0]];
  320 + break;
  321 +
  322 + case CGW_CRC8PRF_16U8:
  323 + crc = crc8->crctab[crc^crc8->profile_data[cf->data[1] & 0xF]];
  324 + break;
  325 +
  326 + case CGW_CRC8PRF_SFFID_XOR:
  327 + crc = crc8->crctab[crc^(cf->can_id & 0xFF)^
  328 + (cf->can_id >> 8 & 0xFF)];
  329 + break;
  330 + }
  331 +
  332 + cf->data[crc8->result_idx] = crc^crc8->final_xor_val;
  333 +}
  334 +
  335 +/* the receive & process & send function */
  336 +static void can_can_gw_rcv(struct sk_buff *skb, void *data)
  337 +{
  338 + struct cgw_job *gwj = (struct cgw_job *)data;
  339 + struct can_frame *cf;
  340 + struct sk_buff *nskb;
  341 + int modidx = 0;
  342 +
  343 + /* do not handle already routed frames - see comment below */
  344 + if (skb_mac_header_was_set(skb))
  345 + return;
  346 +
  347 + if (!(gwj->dst.dev->flags & IFF_UP)) {
  348 + gwj->dropped_frames++;
  349 + return;
  350 + }
  351 +
  352 + /*
  353 + * clone the given skb, which has not been done in can_rcv()
  354 + *
  355 + * When there is at least one modification function activated,
  356 + * we need to copy the skb as we want to modify skb->data.
  357 + */
  358 + if (gwj->mod.modfunc[0])
  359 + nskb = skb_copy(skb, GFP_ATOMIC);
  360 + else
  361 + nskb = skb_clone(skb, GFP_ATOMIC);
  362 +
  363 + if (!nskb) {
  364 + gwj->dropped_frames++;
  365 + return;
  366 + }
  367 +
  368 + /*
  369 + * Mark routed frames by setting some mac header length which is
  370 + * not relevant for the CAN frames located in the skb->data section.
  371 + *
  372 + * As dev->header_ops is not set in CAN netdevices no one is ever
  373 + * accessing the various header offsets in the CAN skbuffs anyway.
  374 + * E.g. using the packet socket to read CAN frames is still working.
  375 + */
  376 + skb_set_mac_header(nskb, 8);
  377 + nskb->dev = gwj->dst.dev;
  378 +
  379 + /* pointer to modifiable CAN frame */
  380 + cf = (struct can_frame *)nskb->data;
  381 +
  382 + /* perform preprocessed modification functions if there are any */
  383 + while (modidx < MAX_MODFUNCTIONS && gwj->mod.modfunc[modidx])
  384 + (*gwj->mod.modfunc[modidx++])(cf, &gwj->mod);
  385 +
  386 + /* check for checksum updates when the CAN frame has been modified */
  387 + if (modidx) {
  388 + if (gwj->mod.csumfunc.crc8)
  389 + (*gwj->mod.csumfunc.crc8)(cf, &gwj->mod.csum.crc8);
  390 +
  391 + if (gwj->mod.csumfunc.xor)
  392 + (*gwj->mod.csumfunc.xor)(cf, &gwj->mod.csum.xor);
  393 + }
  394 +
  395 + /* clear the skb timestamp if not configured the other way */
  396 + if (!(gwj->flags & CGW_FLAGS_CAN_SRC_TSTAMP))
  397 + nskb->tstamp.tv64 = 0;
  398 +
  399 + /* send to netdevice */
  400 + if (can_send(nskb, gwj->flags & CGW_FLAGS_CAN_ECHO))
  401 + gwj->dropped_frames++;
  402 + else
  403 + gwj->handled_frames++;
  404 +}
  405 +
  406 +static inline int cgw_register_filter(struct cgw_job *gwj)
  407 +{
  408 + return can_rx_register(gwj->src.dev, gwj->ccgw.filter.can_id,
  409 + gwj->ccgw.filter.can_mask, can_can_gw_rcv,
  410 + gwj, "gw");
  411 +}
  412 +
  413 +static inline void cgw_unregister_filter(struct cgw_job *gwj)
  414 +{
  415 + can_rx_unregister(gwj->src.dev, gwj->ccgw.filter.can_id,
  416 + gwj->ccgw.filter.can_mask, can_can_gw_rcv, gwj);
  417 +}
  418 +
  419 +static int cgw_notifier(struct notifier_block *nb,
  420 + unsigned long msg, void *data)
  421 +{
  422 + struct net_device *dev = (struct net_device *)data;
  423 +
  424 + if (!net_eq(dev_net(dev), &init_net))
  425 + return NOTIFY_DONE;
  426 + if (dev->type != ARPHRD_CAN)
  427 + return NOTIFY_DONE;
  428 +
  429 + if (msg == NETDEV_UNREGISTER) {
  430 +
  431 + struct cgw_job *gwj = NULL;
  432 + struct hlist_node *n, *nx;
  433 +
  434 + ASSERT_RTNL();
  435 +
  436 + hlist_for_each_entry_safe(gwj, n, nx, &cgw_list, list) {
  437 +
  438 + if (gwj->src.dev == dev || gwj->dst.dev == dev) {
  439 + hlist_del(&gwj->list);
  440 + cgw_unregister_filter(gwj);
  441 + kfree(gwj);
  442 + }
  443 + }
  444 + }
  445 +
  446 + return NOTIFY_DONE;
  447 +}
  448 +
  449 +static int cgw_put_job(struct sk_buff *skb, struct cgw_job *gwj)
  450 +{
  451 + struct cgw_frame_mod mb;
  452 + struct rtcanmsg *rtcan;
  453 + struct nlmsghdr *nlh = nlmsg_put(skb, 0, 0, 0, sizeof(*rtcan), 0);
  454 + if (!nlh)
  455 + return -EMSGSIZE;
  456 +
  457 + rtcan = nlmsg_data(nlh);
  458 + rtcan->can_family = AF_CAN;
  459 + rtcan->gwtype = gwj->gwtype;
  460 + rtcan->flags = gwj->flags;
  461 +
  462 + /* add statistics if available */
  463 +
  464 + if (gwj->handled_frames) {
  465 + if (nla_put_u32(skb, CGW_HANDLED, gwj->handled_frames) < 0)
  466 + goto cancel;
  467 + else
  468 + nlh->nlmsg_len += NLA_HDRLEN + NLA_ALIGN(sizeof(u32));
  469 + }
  470 +
  471 + if (gwj->dropped_frames) {
  472 + if (nla_put_u32(skb, CGW_DROPPED, gwj->dropped_frames) < 0)
  473 + goto cancel;
  474 + else
  475 + nlh->nlmsg_len += NLA_HDRLEN + NLA_ALIGN(sizeof(u32));
  476 + }
  477 +
  478 + /* check non default settings of attributes */
  479 +
  480 + if (gwj->mod.modtype.and) {
  481 + memcpy(&mb.cf, &gwj->mod.modframe.and, sizeof(mb.cf));
  482 + mb.modtype = gwj->mod.modtype.and;
  483 + if (nla_put(skb, CGW_MOD_AND, sizeof(mb), &mb) < 0)
  484 + goto cancel;
  485 + else
  486 + nlh->nlmsg_len += NLA_HDRLEN + NLA_ALIGN(sizeof(mb));
  487 + }
  488 +
  489 + if (gwj->mod.modtype.or) {
  490 + memcpy(&mb.cf, &gwj->mod.modframe.or, sizeof(mb.cf));
  491 + mb.modtype = gwj->mod.modtype.or;
  492 + if (nla_put(skb, CGW_MOD_OR, sizeof(mb), &mb) < 0)
  493 + goto cancel;
  494 + else
  495 + nlh->nlmsg_len += NLA_HDRLEN + NLA_ALIGN(sizeof(mb));
  496 + }
  497 +
  498 + if (gwj->mod.modtype.xor) {
  499 + memcpy(&mb.cf, &gwj->mod.modframe.xor, sizeof(mb.cf));
  500 + mb.modtype = gwj->mod.modtype.xor;
  501 + if (nla_put(skb, CGW_MOD_XOR, sizeof(mb), &mb) < 0)
  502 + goto cancel;
  503 + else
  504 + nlh->nlmsg_len += NLA_HDRLEN + NLA_ALIGN(sizeof(mb));
  505 + }
  506 +
  507 + if (gwj->mod.modtype.set) {
  508 + memcpy(&mb.cf, &gwj->mod.modframe.set, sizeof(mb.cf));
  509 + mb.modtype = gwj->mod.modtype.set;
  510 + if (nla_put(skb, CGW_MOD_SET, sizeof(mb), &mb) < 0)
  511 + goto cancel;
  512 + else
  513 + nlh->nlmsg_len += NLA_HDRLEN + NLA_ALIGN(sizeof(mb));
  514 + }
  515 +
  516 + if (gwj->mod.csumfunc.crc8) {
  517 + if (nla_put(skb, CGW_CS_CRC8, CGW_CS_CRC8_LEN,
  518 + &gwj->mod.csum.crc8) < 0)
  519 + goto cancel;
  520 + else
  521 + nlh->nlmsg_len += NLA_HDRLEN + \
  522 + NLA_ALIGN(CGW_CS_CRC8_LEN);
  523 + }
  524 +
  525 + if (gwj->mod.csumfunc.xor) {
  526 + if (nla_put(skb, CGW_CS_XOR, CGW_CS_XOR_LEN,
  527 + &gwj->mod.csum.xor) < 0)
  528 + goto cancel;
  529 + else
  530 + nlh->nlmsg_len += NLA_HDRLEN + \
  531 + NLA_ALIGN(CGW_CS_XOR_LEN);
  532 + }
  533 +
  534 + if (gwj->gwtype == CGW_TYPE_CAN_CAN) {
  535 +
  536 + if (gwj->ccgw.filter.can_id || gwj->ccgw.filter.can_mask) {
  537 + if (nla_put(skb, CGW_FILTER, sizeof(struct can_filter),
  538 + &gwj->ccgw.filter) < 0)
  539 + goto cancel;
  540 + else
  541 + nlh->nlmsg_len += NLA_HDRLEN +
  542 + NLA_ALIGN(sizeof(struct can_filter));
  543 + }
  544 +
  545 + if (nla_put_u32(skb, CGW_SRC_IF, gwj->ccgw.src_idx) < 0)
  546 + goto cancel;
  547 + else
  548 + nlh->nlmsg_len += NLA_HDRLEN + NLA_ALIGN(sizeof(u32));
  549 +
  550 + if (nla_put_u32(skb, CGW_DST_IF, gwj->ccgw.dst_idx) < 0)
  551 + goto cancel;
  552 + else
  553 + nlh->nlmsg_len += NLA_HDRLEN + NLA_ALIGN(sizeof(u32));
  554 + }
  555 +
  556 + return skb->len;
  557 +
  558 +cancel:
  559 + nlmsg_cancel(skb, nlh);
  560 + return -EMSGSIZE;
  561 +}
  562 +
  563 +/* Dump information about all CAN gateway jobs, in response to RTM_GETROUTE */
  564 +static int cgw_dump_jobs(struct sk_buff *skb, struct netlink_callback *cb)
  565 +{
  566 + struct cgw_job *gwj = NULL;
  567 + struct hlist_node *n;
  568 + int idx = 0;
  569 + int s_idx = cb->args[0];
  570 +
  571 + rcu_read_lock();
  572 + hlist_for_each_entry_rcu(gwj, n, &cgw_list, list) {
  573 + if (idx < s_idx)
  574 + goto cont;
  575 +
  576 + if (cgw_put_job(skb, gwj) < 0)
  577 + break;
  578 +cont:
  579 + idx++;
  580 + }
  581 + rcu_read_unlock();
  582 +
  583 + cb->args[0] = idx;
  584 +
  585 + return skb->len;
  586 +}
  587 +
  588 +/* check for common and gwtype specific attributes */
  589 +static int cgw_parse_attr(struct nlmsghdr *nlh, struct cf_mod *mod,
  590 + u8 gwtype, void *gwtypeattr)
  591 +{
  592 + struct nlattr *tb[CGW_MAX+1];
  593 + struct cgw_frame_mod mb;
  594 + int modidx = 0;
  595 + int err = 0;
  596 +
  597 + /* initialize modification & checksum data space */
  598 + memset(mod, 0, sizeof(*mod));
  599 +
  600 + err = nlmsg_parse(nlh, sizeof(struct rtcanmsg), tb, CGW_MAX, NULL);
  601 + if (err < 0)
  602 + return err;
  603 +
  604 + /* check for AND/OR/XOR/SET modifications */
  605 +
  606 + if (tb[CGW_MOD_AND] &&
  607 + nla_len(tb[CGW_MOD_AND]) == CGW_MODATTR_LEN) {
  608 + nla_memcpy(&mb, tb[CGW_MOD_AND], CGW_MODATTR_LEN);
  609 +
  610 + canframecpy(&mod->modframe.and, &mb.cf);
  611 + mod->modtype.and = mb.modtype;
  612 +
  613 + if (mb.modtype & CGW_MOD_ID)
  614 + mod->modfunc[modidx++] = mod_and_id;
  615 +
  616 + if (mb.modtype & CGW_MOD_DLC)
  617 + mod->modfunc[modidx++] = mod_and_dlc;
  618 +
  619 + if (mb.modtype & CGW_MOD_DATA)
  620 + mod->modfunc[modidx++] = mod_and_data;
  621 + }
  622 +
  623 + if (tb[CGW_MOD_OR] &&
  624 + nla_len(tb[CGW_MOD_OR]) == CGW_MODATTR_LEN) {
  625 + nla_memcpy(&mb, tb[CGW_MOD_OR], CGW_MODATTR_LEN);
  626 +
  627 + canframecpy(&mod->modframe.or, &mb.cf);
  628 + mod->modtype.or = mb.modtype;
  629 +
  630 + if (mb.modtype & CGW_MOD_ID)
  631 + mod->modfunc[modidx++] = mod_or_id;
  632 +
  633 + if (mb.modtype & CGW_MOD_DLC)
  634 + mod->modfunc[modidx++] = mod_or_dlc;
  635 +
  636 + if (mb.modtype & CGW_MOD_DATA)
  637 + mod->modfunc[modidx++] = mod_or_data;
  638 + }
  639 +
  640 + if (tb[CGW_MOD_XOR] &&
  641 + nla_len(tb[CGW_MOD_XOR]) == CGW_MODATTR_LEN) {
  642 + nla_memcpy(&mb, tb[CGW_MOD_XOR], CGW_MODATTR_LEN);
  643 +
  644 + canframecpy(&mod->modframe.xor, &mb.cf);
  645 + mod->modtype.xor = mb.modtype;
  646 +
  647 + if (mb.modtype & CGW_MOD_ID)
  648 + mod->modfunc[modidx++] = mod_xor_id;
  649 +
  650 + if (mb.modtype & CGW_MOD_DLC)
  651 + mod->modfunc[modidx++] = mod_xor_dlc;
  652 +
  653 + if (mb.modtype & CGW_MOD_DATA)
  654 + mod->modfunc[modidx++] = mod_xor_data;
  655 + }
  656 +
  657 + if (tb[CGW_MOD_SET] &&
  658 + nla_len(tb[CGW_MOD_SET]) == CGW_MODATTR_LEN) {
  659 + nla_memcpy(&mb, tb[CGW_MOD_SET], CGW_MODATTR_LEN);
  660 +
  661 + canframecpy(&mod->modframe.set, &mb.cf);
  662 + mod->modtype.set = mb.modtype;
  663 +
  664 + if (mb.modtype & CGW_MOD_ID)
  665 + mod->modfunc[modidx++] = mod_set_id;
  666 +
  667 + if (mb.modtype & CGW_MOD_DLC)
  668 + mod->modfunc[modidx++] = mod_set_dlc;
  669 +
  670 + if (mb.modtype & CGW_MOD_DATA)
  671 + mod->modfunc[modidx++] = mod_set_data;
  672 + }
  673 +
  674 + /* check for checksum operations after CAN frame modifications */
  675 + if (modidx) {
  676 +
  677 + if (tb[CGW_CS_CRC8] &&
  678 + nla_len(tb[CGW_CS_CRC8]) == CGW_CS_CRC8_LEN) {
  679 +
  680 + struct cgw_csum_crc8 *c = (struct cgw_csum_crc8 *)\
  681 + nla_data(tb[CGW_CS_CRC8]);
  682 +
  683 + err = cgw_chk_csum_parms(c->from_idx, c->to_idx,
  684 + c->result_idx);
  685 + if (err)
  686 + return err;
  687 +
  688 + nla_memcpy(&mod->csum.crc8, tb[CGW_CS_CRC8],
  689 + CGW_CS_CRC8_LEN);
  690 +
  691 + /*
  692 + * select dedicated processing function to reduce
  693 + * runtime operations in receive hot path.
  694 + */
  695 + if (c->from_idx < 0 || c->to_idx < 0 ||
  696 + c->result_idx < 0)
  697 + mod->csumfunc.crc8 = cgw_csum_crc8_rel;
  698 + else if (c->from_idx <= c->to_idx)
  699 + mod->csumfunc.crc8 = cgw_csum_crc8_pos;
  700 + else
  701 + mod->csumfunc.crc8 = cgw_csum_crc8_neg;
  702 + }
  703 +
  704 + if (tb[CGW_CS_XOR] &&
  705 + nla_len(tb[CGW_CS_XOR]) == CGW_CS_XOR_LEN) {
  706 +
  707 + struct cgw_csum_xor *c = (struct cgw_csum_xor *)\
  708 + nla_data(tb[CGW_CS_XOR]);
  709 +
  710 + err = cgw_chk_csum_parms(c->from_idx, c->to_idx,
  711 + c->result_idx);
  712 + if (err)
  713 + return err;
  714 +
  715 + nla_memcpy(&mod->csum.xor, tb[CGW_CS_XOR],
  716 + CGW_CS_XOR_LEN);
  717 +
  718 + /*
  719 + * select dedicated processing function to reduce
  720 + * runtime operations in receive hot path.
  721 + */
  722 + if (c->from_idx < 0 || c->to_idx < 0 ||
  723 + c->result_idx < 0)
  724 + mod->csumfunc.xor = cgw_csum_xor_rel;
  725 + else if (c->from_idx <= c->to_idx)
  726 + mod->csumfunc.xor = cgw_csum_xor_pos;
  727 + else
  728 + mod->csumfunc.xor = cgw_csum_xor_neg;
  729 + }
  730 + }
  731 +
  732 + if (gwtype == CGW_TYPE_CAN_CAN) {
  733 +
  734 + /* check CGW_TYPE_CAN_CAN specific attributes */
  735 +
  736 + struct can_can_gw *ccgw = (struct can_can_gw *)gwtypeattr;
  737 + memset(ccgw, 0, sizeof(*ccgw));
  738 +
  739 + /* check for can_filter in attributes */
  740 + if (tb[CGW_FILTER] &&
  741 + nla_len(tb[CGW_FILTER]) == sizeof(struct can_filter))
  742 + nla_memcpy(&ccgw->filter, tb[CGW_FILTER],
  743 + sizeof(struct can_filter));
  744 +
  745 + err = -ENODEV;
  746 +
  747 + /* specifying two interfaces is mandatory */
  748 + if (!tb[CGW_SRC_IF] || !tb[CGW_DST_IF])
  749 + return err;
  750 +
  751 + if (nla_len(tb[CGW_SRC_IF]) == sizeof(u32))
  752 + nla_memcpy(&ccgw->src_idx, tb[CGW_SRC_IF],
  753 + sizeof(u32));
  754 +
  755 + if (nla_len(tb[CGW_DST_IF]) == sizeof(u32))
  756 + nla_memcpy(&ccgw->dst_idx, tb[CGW_DST_IF],
  757 + sizeof(u32));
  758 +
  759 + /* both indices set to 0 for flushing all routing entries */
  760 + if (!ccgw->src_idx && !ccgw->dst_idx)
  761 + return 0;
  762 +
  763 + /* only one index set to 0 is an error */
  764 + if (!ccgw->src_idx || !ccgw->dst_idx)
  765 + return err;
  766 + }
  767 +
  768 + /* add the checks for other gwtypes here */
  769 +
  770 + return 0;
  771 +}
  772 +
  773 +static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh,
  774 + void *arg)
  775 +{
  776 + struct rtcanmsg *r;
  777 + struct cgw_job *gwj;
  778 + int err = 0;
  779 +
  780 + if (nlmsg_len(nlh) < sizeof(*r))
  781 + return -EINVAL;
  782 +
  783 + r = nlmsg_data(nlh);
  784 + if (r->can_family != AF_CAN)
  785 + return -EPFNOSUPPORT;
  786 +
  787 + /* so far we only support CAN -> CAN routings */
  788 + if (r->gwtype != CGW_TYPE_CAN_CAN)
  789 + return -EINVAL;
  790 +
  791 + gwj = kmem_cache_alloc(cgw_cache, GFP_KERNEL);
  792 + if (!gwj)
  793 + return -ENOMEM;
  794 +
  795 + gwj->handled_frames = 0;
  796 + gwj->dropped_frames = 0;
  797 + gwj->flags = r->flags;
  798 + gwj->gwtype = r->gwtype;
  799 +
  800 + err = cgw_parse_attr(nlh, &gwj->mod, CGW_TYPE_CAN_CAN, &gwj->ccgw);
  801 + if (err < 0)
  802 + goto out;
  803 +
  804 + err = -ENODEV;
  805 +
  806 + /* ifindex == 0 is not allowed for job creation */
  807 + if (!gwj->ccgw.src_idx || !gwj->ccgw.dst_idx)
  808 + goto out;
  809 +
  810 + gwj->src.dev = dev_get_by_index(&init_net, gwj->ccgw.src_idx);
  811 +
  812 + if (!gwj->src.dev)
  813 + goto out;
  814 +
  815 + /* check for CAN netdev not using header_ops - see gw_rcv() */
  816 + if (gwj->src.dev->type != ARPHRD_CAN || gwj->src.dev->header_ops)
  817 + goto put_src_out;
  818 +
  819 + gwj->dst.dev = dev_get_by_index(&init_net, gwj->ccgw.dst_idx);
  820 +
  821 + if (!gwj->dst.dev)
  822 + goto put_src_out;
  823 +
  824 + /* check for CAN netdev not using header_ops - see gw_rcv() */
  825 + if (gwj->dst.dev->type != ARPHRD_CAN || gwj->dst.dev->header_ops)
  826 + goto put_src_dst_out;
  827 +
  828 + ASSERT_RTNL();
  829 +
  830 + err = cgw_register_filter(gwj);
  831 + if (!err)
  832 + hlist_add_head_rcu(&gwj->list, &cgw_list);
  833 +
  834 +put_src_dst_out:
  835 + dev_put(gwj->dst.dev);
  836 +put_src_out:
  837 + dev_put(gwj->src.dev);
  838 +out:
  839 + if (err)
  840 + kmem_cache_free(cgw_cache, gwj);
  841 +
  842 + return err;
  843 +}
  844 +
  845 +static void cgw_remove_all_jobs(void)
  846 +{
  847 + struct cgw_job *gwj = NULL;
  848 + struct hlist_node *n, *nx;
  849 +
  850 + ASSERT_RTNL();
  851 +
  852 + hlist_for_each_entry_safe(gwj, n, nx, &cgw_list, list) {
  853 + hlist_del(&gwj->list);
  854 + cgw_unregister_filter(gwj);
  855 + kfree(gwj);
  856 + }
  857 +}
  858 +
  859 +static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
  860 +{
  861 + struct cgw_job *gwj = NULL;
  862 + struct hlist_node *n, *nx;
  863 + struct rtcanmsg *r;
  864 + struct cf_mod mod;
  865 + struct can_can_gw ccgw;
  866 + int err = 0;
  867 +
  868 + if (nlmsg_len(nlh) < sizeof(*r))
  869 + return -EINVAL;
  870 +
  871 + r = nlmsg_data(nlh);
  872 + if (r->can_family != AF_CAN)
  873 + return -EPFNOSUPPORT;
  874 +
  875 + /* so far we only support CAN -> CAN routings */
  876 + if (r->gwtype != CGW_TYPE_CAN_CAN)
  877 + return -EINVAL;
  878 +
  879 + err = cgw_parse_attr(nlh, &mod, CGW_TYPE_CAN_CAN, &ccgw);
  880 + if (err < 0)
  881 + return err;
  882 +
  883 + /* two interface indices both set to 0 => remove all entries */
  884 + if (!ccgw.src_idx && !ccgw.dst_idx) {
  885 + cgw_remove_all_jobs();
  886 + return 0;
  887 + }
  888 +
  889 + err = -EINVAL;
  890 +
  891 + ASSERT_RTNL();
  892 +
  893 + /* remove only the first matching entry */
  894 + hlist_for_each_entry_safe(gwj, n, nx, &cgw_list, list) {
  895 +
  896 + if (gwj->flags != r->flags)
  897 + continue;
  898 +
  899 + if (memcmp(&gwj->mod, &mod, sizeof(mod)))
  900 + continue;
  901 +
  902 + /* if (r->gwtype == CGW_TYPE_CAN_CAN) - is made sure here */
  903 + if (memcmp(&gwj->ccgw, &ccgw, sizeof(ccgw)))
  904 + continue;
  905 +
  906 + hlist_del(&gwj->list);
  907 + cgw_unregister_filter(gwj);
  908 + kfree(gwj);
  909 + err = 0;
  910 + break;
  911 + }
  912 +
  913 + return err;
  914 +}
  915 +
  916 +static __init int cgw_module_init(void)
  917 +{
  918 + printk(banner);
  919 +
  920 + cgw_cache = kmem_cache_create("can_gw", sizeof(struct cgw_job),
  921 + 0, 0, NULL);
  922 +
  923 + if (!cgw_cache)
  924 + return -ENOMEM;
  925 +
  926 + /* set notifier */
  927 + notifier.notifier_call = cgw_notifier;
  928 + register_netdevice_notifier(&notifier);
  929 +
  930 + if (__rtnl_register(PF_CAN, RTM_GETROUTE, NULL, cgw_dump_jobs, NULL)) {
  931 + unregister_netdevice_notifier(&notifier);
  932 + kmem_cache_destroy(cgw_cache);
  933 + return -ENOBUFS;
  934 + }
  935 +
  936 + /* Only the first call to __rtnl_register can fail */
  937 + __rtnl_register(PF_CAN, RTM_NEWROUTE, cgw_create_job, NULL, NULL);
  938 + __rtnl_register(PF_CAN, RTM_DELROUTE, cgw_remove_job, NULL, NULL);
  939 +
  940 + return 0;
  941 +}
  942 +
  943 +static __exit void cgw_module_exit(void)
  944 +{
  945 + rtnl_unregister_all(PF_CAN);
  946 +
  947 + unregister_netdevice_notifier(&notifier);
  948 +
  949 + rtnl_lock();
  950 + cgw_remove_all_jobs();
  951 + rtnl_unlock();
  952 +
  953 + rcu_barrier(); /* Wait for completion of call_rcu()'s */
  954 +
  955 + kmem_cache_destroy(cgw_cache);
  956 +}
  957 +
  958 +module_init(cgw_module_init);
  959 +module_exit(cgw_module_exit);