aboutsummaryrefslogtreecommitdiffstats
path: root/net/can
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2011-10-25 07:25:22 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2011-10-25 07:25:22 -0400
commit8a9ea3237e7eb5c25f09e429ad242ae5a3d5ea22 (patch)
treea0a63398a9983667d52cbbbf4e2405b4f22b1d83 /net/can
parent1be025d3cb40cd295123af2c394f7229ef9b30ca (diff)
parent8b3408f8ee994973869d8ba32c5bf482bc4ddca4 (diff)
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (1745 commits) dp83640: free packet queues on remove dp83640: use proper function to free transmit time stamping packets ipv6: Do not use routes from locally generated RAs |PATCH net-next] tg3: add tx_dropped counter be2net: don't create multiple RX/TX rings in multi channel mode be2net: don't create multiple TXQs in BE2 be2net: refactor VF setup/teardown code into be_vf_setup/clear() be2net: add vlan/rx-mode/flow-control config to be_setup() net_sched: cls_flow: use skb_header_pointer() ipv4: avoid useless call of the function check_peer_pmtu TCP: remove TCP_DEBUG net: Fix driver name for mdio-gpio.c ipv4: tcp: fix TOS value in ACK messages sent from TIME_WAIT rtnetlink: Add missing manual netlink notification in dev_change_net_namespaces ipv4: fix ipsec forward performance regression jme: fix irq storm after suspend/resume route: fix ICMP redirect validation net: hold sock reference while processing tx timestamps tcp: md5: add more const attributes Add ethtool -g support to virtio_net ... Fix up conflicts in: - drivers/net/Kconfig: The split-up generated a trivial conflict with removal of a stale reference to Documentation/networking/net-modules.txt. Remove it from the new location instead. - fs/sysfs/dir.c: Fairly nasty conflicts with the sysfs rb-tree usage, conflicting with Eric Biederman's changes for tagged directories.
Diffstat (limited to 'net/can')
-rw-r--r--net/can/Kconfig11
-rw-r--r--net/can/Makefile3
-rw-r--r--net/can/af_can.c6
-rw-r--r--net/can/af_can.h2
-rw-r--r--net/can/bcm.c2
-rw-r--r--net/can/gw.c957
-rw-r--r--net/can/proc.c2
-rw-r--r--net/can/raw.c2
8 files changed, 973 insertions, 12 deletions
diff --git a/net/can/Kconfig b/net/can/Kconfig
index 89395b2c8bca..03200699d274 100644
--- a/net/can/Kconfig
+++ b/net/can/Kconfig
@@ -40,5 +40,16 @@ config CAN_BCM
40 CAN messages are used on the bus (e.g. in automotive environments). 40 CAN messages are used on the bus (e.g. in automotive environments).
41 To use the Broadcast Manager, use AF_CAN with protocol CAN_BCM. 41 To use the Broadcast Manager, use AF_CAN with protocol CAN_BCM.
42 42
43config 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
44source "drivers/net/can/Kconfig" 55source "drivers/net/can/Kconfig"
diff --git a/net/can/Makefile b/net/can/Makefile
index 2d3894b32742..cef49eb1f5c7 100644
--- a/net/can/Makefile
+++ b/net/can/Makefile
@@ -10,3 +10,6 @@ can-raw-y := raw.o
10 10
11obj-$(CONFIG_CAN_BCM) += can-bcm.o 11obj-$(CONFIG_CAN_BCM) += can-bcm.o
12can-bcm-y := bcm.o 12can-bcm-y := bcm.o
13
14obj-$(CONFIG_CAN_GW) += can-gw.o
15can-gw-y := gw.o
diff --git a/net/can/af_can.c b/net/can/af_can.c
index 9b0c32a2690c..0ce2ad0696da 100644
--- a/net/can/af_can.c
+++ b/net/can/af_can.c
@@ -38,8 +38,6 @@
38 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 38 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
39 * DAMAGE. 39 * DAMAGE.
40 * 40 *
41 * Send feedback to <socketcan-users@lists.berlios.de>
42 *
43 */ 41 */
44 42
45#include <linux/module.h> 43#include <linux/module.h>
@@ -719,7 +717,7 @@ int can_proto_register(const struct can_proto *cp)
719 proto); 717 proto);
720 err = -EBUSY; 718 err = -EBUSY;
721 } else 719 } else
722 rcu_assign_pointer(proto_tab[proto], cp); 720 RCU_INIT_POINTER(proto_tab[proto], cp);
723 721
724 mutex_unlock(&proto_tab_lock); 722 mutex_unlock(&proto_tab_lock);
725 723
@@ -740,7 +738,7 @@ void can_proto_unregister(const struct can_proto *cp)
740 738
741 mutex_lock(&proto_tab_lock); 739 mutex_lock(&proto_tab_lock);
742 BUG_ON(proto_tab[proto] != cp); 740 BUG_ON(proto_tab[proto] != cp);
743 rcu_assign_pointer(proto_tab[proto], NULL); 741 RCU_INIT_POINTER(proto_tab[proto], NULL);
744 mutex_unlock(&proto_tab_lock); 742 mutex_unlock(&proto_tab_lock);
745 743
746 synchronize_rcu(); 744 synchronize_rcu();
diff --git a/net/can/af_can.h b/net/can/af_can.h
index 34253b84e30f..fd882dbadad3 100644
--- a/net/can/af_can.h
+++ b/net/can/af_can.h
@@ -35,8 +35,6 @@
35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
36 * DAMAGE. 36 * DAMAGE.
37 * 37 *
38 * Send feedback to <socketcan-users@lists.berlios.de>
39 *
40 */ 38 */
41 39
42#ifndef AF_CAN_H 40#ifndef AF_CAN_H
diff --git a/net/can/bcm.c b/net/can/bcm.c
index c84963d2dee6..151b7730c12c 100644
--- a/net/can/bcm.c
+++ b/net/can/bcm.c
@@ -37,8 +37,6 @@
37 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 37 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
38 * DAMAGE. 38 * DAMAGE.
39 * 39 *
40 * Send feedback to <socketcan-users@lists.berlios.de>
41 *
42 */ 40 */
43 41
44#include <linux/module.h> 42#include <linux/module.h>
diff --git a/net/can/gw.c b/net/can/gw.c
new file mode 100644
index 000000000000..3d79b127881e
--- /dev/null
+++ b/net/can/gw.c
@@ -0,0 +1,957 @@
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 */
41
42#include <linux/module.h>
43#include <linux/init.h>
44#include <linux/types.h>
45#include <linux/list.h>
46#include <linux/spinlock.h>
47#include <linux/rcupdate.h>
48#include <linux/rculist.h>
49#include <linux/net.h>
50#include <linux/netdevice.h>
51#include <linux/if_arp.h>
52#include <linux/skbuff.h>
53#include <linux/can.h>
54#include <linux/can/core.h>
55#include <linux/can/gw.h>
56#include <net/rtnetlink.h>
57#include <net/net_namespace.h>
58#include <net/sock.h>
59
60#define CAN_GW_VERSION "20101209"
61static __initdata const char banner[] =
62 KERN_INFO "can: netlink gateway (rev " CAN_GW_VERSION ")\n";
63
64MODULE_DESCRIPTION("PF_CAN netlink gateway");
65MODULE_LICENSE("Dual BSD/GPL");
66MODULE_AUTHOR("Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
67MODULE_ALIAS("can-gw");
68
69HLIST_HEAD(cgw_list);
70static struct notifier_block notifier;
71
72static struct kmem_cache *cgw_cache __read_mostly;
73
74/* structure that contains the (on-the-fly) CAN frame modifications */
75struct cf_mod {
76 struct {
77 struct can_frame and;
78 struct can_frame or;
79 struct can_frame xor;
80 struct can_frame set;
81 } modframe;
82 struct {
83 u8 and;
84 u8 or;
85 u8 xor;
86 u8 set;
87 } modtype;
88 void (*modfunc[MAX_MODFUNCTIONS])(struct can_frame *cf,
89 struct cf_mod *mod);
90
91 /* CAN frame checksum calculation after CAN frame modifications */
92 struct {
93 struct cgw_csum_xor xor;
94 struct cgw_csum_crc8 crc8;
95 } csum;
96 struct {
97 void (*xor)(struct can_frame *cf, struct cgw_csum_xor *xor);
98 void (*crc8)(struct can_frame *cf, struct cgw_csum_crc8 *crc8);
99 } csumfunc;
100};
101
102
103/*
104 * So far we just support CAN -> CAN routing and frame modifications.
105 *
106 * The internal can_can_gw structure contains data and attributes for
107 * a CAN -> CAN gateway job.
108 */
109struct can_can_gw {
110 struct can_filter filter;
111 int src_idx;
112 int dst_idx;
113};
114
115/* list entry for CAN gateways jobs */
116struct cgw_job {
117 struct hlist_node list;
118 struct rcu_head rcu;
119 u32 handled_frames;
120 u32 dropped_frames;
121 struct cf_mod mod;
122 union {
123 /* CAN frame data source */
124 struct net_device *dev;
125 } src;
126 union {
127 /* CAN frame data destination */
128 struct net_device *dev;
129 } dst;
130 union {
131 struct can_can_gw ccgw;
132 /* tbc */
133 };
134 u8 gwtype;
135 u16 flags;
136};
137
138/* modification functions that are invoked in the hot path in can_can_gw_rcv */
139
140#define MODFUNC(func, op) static void func(struct can_frame *cf, \
141 struct cf_mod *mod) { op ; }
142
143MODFUNC(mod_and_id, cf->can_id &= mod->modframe.and.can_id)
144MODFUNC(mod_and_dlc, cf->can_dlc &= mod->modframe.and.can_dlc)
145MODFUNC(mod_and_data, *(u64 *)cf->data &= *(u64 *)mod->modframe.and.data)
146MODFUNC(mod_or_id, cf->can_id |= mod->modframe.or.can_id)
147MODFUNC(mod_or_dlc, cf->can_dlc |= mod->modframe.or.can_dlc)
148MODFUNC(mod_or_data, *(u64 *)cf->data |= *(u64 *)mod->modframe.or.data)
149MODFUNC(mod_xor_id, cf->can_id ^= mod->modframe.xor.can_id)
150MODFUNC(mod_xor_dlc, cf->can_dlc ^= mod->modframe.xor.can_dlc)
151MODFUNC(mod_xor_data, *(u64 *)cf->data ^= *(u64 *)mod->modframe.xor.data)
152MODFUNC(mod_set_id, cf->can_id = mod->modframe.set.can_id)
153MODFUNC(mod_set_dlc, cf->can_dlc = mod->modframe.set.can_dlc)
154MODFUNC(mod_set_data, *(u64 *)cf->data = *(u64 *)mod->modframe.set.data)
155
156static inline void canframecpy(struct can_frame *dst, struct can_frame *src)
157{
158 /*
159 * Copy the struct members separately to ensure that no uninitialized
160 * data are copied in the 3 bytes hole of the struct. This is needed
161 * to make easy compares of the data in the struct cf_mod.
162 */
163
164 dst->can_id = src->can_id;
165 dst->can_dlc = src->can_dlc;
166 *(u64 *)dst->data = *(u64 *)src->data;
167}
168
169static int cgw_chk_csum_parms(s8 fr, s8 to, s8 re)
170{
171 /*
172 * absolute dlc values 0 .. 7 => 0 .. 7, e.g. data [0]
173 * relative to received dlc -1 .. -8 :
174 * e.g. for received dlc = 8
175 * -1 => index = 7 (data[7])
176 * -3 => index = 5 (data[5])
177 * -8 => index = 0 (data[0])
178 */
179
180 if (fr > -9 && fr < 8 &&
181 to > -9 && to < 8 &&
182 re > -9 && re < 8)
183 return 0;
184 else
185 return -EINVAL;
186}
187
188static inline int calc_idx(int idx, int rx_dlc)
189{
190 if (idx < 0)
191 return rx_dlc + idx;
192 else
193 return idx;
194}
195
196static void cgw_csum_xor_rel(struct can_frame *cf, struct cgw_csum_xor *xor)
197{
198 int from = calc_idx(xor->from_idx, cf->can_dlc);
199 int to = calc_idx(xor->to_idx, cf->can_dlc);
200 int res = calc_idx(xor->result_idx, cf->can_dlc);
201 u8 val = xor->init_xor_val;
202 int i;
203
204 if (from < 0 || to < 0 || res < 0)
205 return;
206
207 if (from <= to) {
208 for (i = from; i <= to; i++)
209 val ^= cf->data[i];
210 } else {
211 for (i = from; i >= to; i--)
212 val ^= cf->data[i];
213 }
214
215 cf->data[res] = val;
216}
217
218static void cgw_csum_xor_pos(struct can_frame *cf, struct cgw_csum_xor *xor)
219{
220 u8 val = xor->init_xor_val;
221 int i;
222
223 for (i = xor->from_idx; i <= xor->to_idx; i++)
224 val ^= cf->data[i];
225
226 cf->data[xor->result_idx] = val;
227}
228
229static void cgw_csum_xor_neg(struct can_frame *cf, struct cgw_csum_xor *xor)
230{
231 u8 val = xor->init_xor_val;
232 int i;
233
234 for (i = xor->from_idx; i >= xor->to_idx; i--)
235 val ^= cf->data[i];
236
237 cf->data[xor->result_idx] = val;
238}
239
240static void cgw_csum_crc8_rel(struct can_frame *cf, struct cgw_csum_crc8 *crc8)
241{
242 int from = calc_idx(crc8->from_idx, cf->can_dlc);
243 int to = calc_idx(crc8->to_idx, cf->can_dlc);
244 int res = calc_idx(crc8->result_idx, cf->can_dlc);
245 u8 crc = crc8->init_crc_val;
246 int i;
247
248 if (from < 0 || to < 0 || res < 0)
249 return;
250
251 if (from <= to) {
252 for (i = crc8->from_idx; i <= crc8->to_idx; i++)
253 crc = crc8->crctab[crc^cf->data[i]];
254 } else {
255 for (i = crc8->from_idx; i >= crc8->to_idx; i--)
256 crc = crc8->crctab[crc^cf->data[i]];
257 }
258
259 switch (crc8->profile) {
260
261 case CGW_CRC8PRF_1U8:
262 crc = crc8->crctab[crc^crc8->profile_data[0]];
263 break;
264
265 case CGW_CRC8PRF_16U8:
266 crc = crc8->crctab[crc^crc8->profile_data[cf->data[1] & 0xF]];
267 break;
268
269 case CGW_CRC8PRF_SFFID_XOR:
270 crc = crc8->crctab[crc^(cf->can_id & 0xFF)^
271 (cf->can_id >> 8 & 0xFF)];
272 break;
273
274 }
275
276 cf->data[crc8->result_idx] = crc^crc8->final_xor_val;
277}
278
279static void cgw_csum_crc8_pos(struct can_frame *cf, struct cgw_csum_crc8 *crc8)
280{
281 u8 crc = crc8->init_crc_val;
282 int i;
283
284 for (i = crc8->from_idx; i <= crc8->to_idx; i++)
285 crc = crc8->crctab[crc^cf->data[i]];
286
287 switch (crc8->profile) {
288
289 case CGW_CRC8PRF_1U8:
290 crc = crc8->crctab[crc^crc8->profile_data[0]];
291 break;
292
293 case CGW_CRC8PRF_16U8:
294 crc = crc8->crctab[crc^crc8->profile_data[cf->data[1] & 0xF]];
295 break;
296
297 case CGW_CRC8PRF_SFFID_XOR:
298 crc = crc8->crctab[crc^(cf->can_id & 0xFF)^
299 (cf->can_id >> 8 & 0xFF)];
300 break;
301 }
302
303 cf->data[crc8->result_idx] = crc^crc8->final_xor_val;
304}
305
306static void cgw_csum_crc8_neg(struct can_frame *cf, struct cgw_csum_crc8 *crc8)
307{
308 u8 crc = crc8->init_crc_val;
309 int i;
310
311 for (i = crc8->from_idx; i >= crc8->to_idx; i--)
312 crc = crc8->crctab[crc^cf->data[i]];
313
314 switch (crc8->profile) {
315
316 case CGW_CRC8PRF_1U8:
317 crc = crc8->crctab[crc^crc8->profile_data[0]];
318 break;
319
320 case CGW_CRC8PRF_16U8:
321 crc = crc8->crctab[crc^crc8->profile_data[cf->data[1] & 0xF]];
322 break;
323
324 case CGW_CRC8PRF_SFFID_XOR:
325 crc = crc8->crctab[crc^(cf->can_id & 0xFF)^
326 (cf->can_id >> 8 & 0xFF)];
327 break;
328 }
329
330 cf->data[crc8->result_idx] = crc^crc8->final_xor_val;
331}
332
333/* the receive & process & send function */
334static void can_can_gw_rcv(struct sk_buff *skb, void *data)
335{
336 struct cgw_job *gwj = (struct cgw_job *)data;
337 struct can_frame *cf;
338 struct sk_buff *nskb;
339 int modidx = 0;
340
341 /* do not handle already routed frames - see comment below */
342 if (skb_mac_header_was_set(skb))
343 return;
344
345 if (!(gwj->dst.dev->flags & IFF_UP)) {
346 gwj->dropped_frames++;
347 return;
348 }
349
350 /*
351 * clone the given skb, which has not been done in can_rcv()
352 *
353 * When there is at least one modification function activated,
354 * we need to copy the skb as we want to modify skb->data.
355 */
356 if (gwj->mod.modfunc[0])
357 nskb = skb_copy(skb, GFP_ATOMIC);
358 else
359 nskb = skb_clone(skb, GFP_ATOMIC);
360
361 if (!nskb) {
362 gwj->dropped_frames++;
363 return;
364 }
365
366 /*
367 * Mark routed frames by setting some mac header length which is
368 * not relevant for the CAN frames located in the skb->data section.
369 *
370 * As dev->header_ops is not set in CAN netdevices no one is ever
371 * accessing the various header offsets in the CAN skbuffs anyway.
372 * E.g. using the packet socket to read CAN frames is still working.
373 */
374 skb_set_mac_header(nskb, 8);
375 nskb->dev = gwj->dst.dev;
376
377 /* pointer to modifiable CAN frame */
378 cf = (struct can_frame *)nskb->data;
379
380 /* perform preprocessed modification functions if there are any */
381 while (modidx < MAX_MODFUNCTIONS && gwj->mod.modfunc[modidx])
382 (*gwj->mod.modfunc[modidx++])(cf, &gwj->mod);
383
384 /* check for checksum updates when the CAN frame has been modified */
385 if (modidx) {
386 if (gwj->mod.csumfunc.crc8)
387 (*gwj->mod.csumfunc.crc8)(cf, &gwj->mod.csum.crc8);
388
389 if (gwj->mod.csumfunc.xor)
390 (*gwj->mod.csumfunc.xor)(cf, &gwj->mod.csum.xor);
391 }
392
393 /* clear the skb timestamp if not configured the other way */
394 if (!(gwj->flags & CGW_FLAGS_CAN_SRC_TSTAMP))
395 nskb->tstamp.tv64 = 0;
396
397 /* send to netdevice */
398 if (can_send(nskb, gwj->flags & CGW_FLAGS_CAN_ECHO))
399 gwj->dropped_frames++;
400 else
401 gwj->handled_frames++;
402}
403
404static inline int cgw_register_filter(struct cgw_job *gwj)
405{
406 return can_rx_register(gwj->src.dev, gwj->ccgw.filter.can_id,
407 gwj->ccgw.filter.can_mask, can_can_gw_rcv,
408 gwj, "gw");
409}
410
411static inline void cgw_unregister_filter(struct cgw_job *gwj)
412{
413 can_rx_unregister(gwj->src.dev, gwj->ccgw.filter.can_id,
414 gwj->ccgw.filter.can_mask, can_can_gw_rcv, gwj);
415}
416
417static int cgw_notifier(struct notifier_block *nb,
418 unsigned long msg, void *data)
419{
420 struct net_device *dev = (struct net_device *)data;
421
422 if (!net_eq(dev_net(dev), &init_net))
423 return NOTIFY_DONE;
424 if (dev->type != ARPHRD_CAN)
425 return NOTIFY_DONE;
426
427 if (msg == NETDEV_UNREGISTER) {
428
429 struct cgw_job *gwj = NULL;
430 struct hlist_node *n, *nx;
431
432 ASSERT_RTNL();
433
434 hlist_for_each_entry_safe(gwj, n, nx, &cgw_list, list) {
435
436 if (gwj->src.dev == dev || gwj->dst.dev == dev) {
437 hlist_del(&gwj->list);
438 cgw_unregister_filter(gwj);
439 kfree(gwj);
440 }
441 }
442 }
443
444 return NOTIFY_DONE;
445}
446
447static int cgw_put_job(struct sk_buff *skb, struct cgw_job *gwj)
448{
449 struct cgw_frame_mod mb;
450 struct rtcanmsg *rtcan;
451 struct nlmsghdr *nlh = nlmsg_put(skb, 0, 0, 0, sizeof(*rtcan), 0);
452 if (!nlh)
453 return -EMSGSIZE;
454
455 rtcan = nlmsg_data(nlh);
456 rtcan->can_family = AF_CAN;
457 rtcan->gwtype = gwj->gwtype;
458 rtcan->flags = gwj->flags;
459
460 /* add statistics if available */
461
462 if (gwj->handled_frames) {
463 if (nla_put_u32(skb, CGW_HANDLED, gwj->handled_frames) < 0)
464 goto cancel;
465 else
466 nlh->nlmsg_len += NLA_HDRLEN + NLA_ALIGN(sizeof(u32));
467 }
468
469 if (gwj->dropped_frames) {
470 if (nla_put_u32(skb, CGW_DROPPED, gwj->dropped_frames) < 0)
471 goto cancel;
472 else
473 nlh->nlmsg_len += NLA_HDRLEN + NLA_ALIGN(sizeof(u32));
474 }
475
476 /* check non default settings of attributes */
477
478 if (gwj->mod.modtype.and) {
479 memcpy(&mb.cf, &gwj->mod.modframe.and, sizeof(mb.cf));
480 mb.modtype = gwj->mod.modtype.and;
481 if (nla_put(skb, CGW_MOD_AND, sizeof(mb), &mb) < 0)
482 goto cancel;
483 else
484 nlh->nlmsg_len += NLA_HDRLEN + NLA_ALIGN(sizeof(mb));
485 }
486
487 if (gwj->mod.modtype.or) {
488 memcpy(&mb.cf, &gwj->mod.modframe.or, sizeof(mb.cf));
489 mb.modtype = gwj->mod.modtype.or;
490 if (nla_put(skb, CGW_MOD_OR, sizeof(mb), &mb) < 0)
491 goto cancel;
492 else
493 nlh->nlmsg_len += NLA_HDRLEN + NLA_ALIGN(sizeof(mb));
494 }
495
496 if (gwj->mod.modtype.xor) {
497 memcpy(&mb.cf, &gwj->mod.modframe.xor, sizeof(mb.cf));
498 mb.modtype = gwj->mod.modtype.xor;
499 if (nla_put(skb, CGW_MOD_XOR, sizeof(mb), &mb) < 0)
500 goto cancel;
501 else
502 nlh->nlmsg_len += NLA_HDRLEN + NLA_ALIGN(sizeof(mb));
503 }
504
505 if (gwj->mod.modtype.set) {
506 memcpy(&mb.cf, &gwj->mod.modframe.set, sizeof(mb.cf));
507 mb.modtype = gwj->mod.modtype.set;
508 if (nla_put(skb, CGW_MOD_SET, sizeof(mb), &mb) < 0)
509 goto cancel;
510 else
511 nlh->nlmsg_len += NLA_HDRLEN + NLA_ALIGN(sizeof(mb));
512 }
513
514 if (gwj->mod.csumfunc.crc8) {
515 if (nla_put(skb, CGW_CS_CRC8, CGW_CS_CRC8_LEN,
516 &gwj->mod.csum.crc8) < 0)
517 goto cancel;
518 else
519 nlh->nlmsg_len += NLA_HDRLEN + \
520 NLA_ALIGN(CGW_CS_CRC8_LEN);
521 }
522
523 if (gwj->mod.csumfunc.xor) {
524 if (nla_put(skb, CGW_CS_XOR, CGW_CS_XOR_LEN,
525 &gwj->mod.csum.xor) < 0)
526 goto cancel;
527 else
528 nlh->nlmsg_len += NLA_HDRLEN + \
529 NLA_ALIGN(CGW_CS_XOR_LEN);
530 }
531
532 if (gwj->gwtype == CGW_TYPE_CAN_CAN) {
533
534 if (gwj->ccgw.filter.can_id || gwj->ccgw.filter.can_mask) {
535 if (nla_put(skb, CGW_FILTER, sizeof(struct can_filter),
536 &gwj->ccgw.filter) < 0)
537 goto cancel;
538 else
539 nlh->nlmsg_len += NLA_HDRLEN +
540 NLA_ALIGN(sizeof(struct can_filter));
541 }
542
543 if (nla_put_u32(skb, CGW_SRC_IF, gwj->ccgw.src_idx) < 0)
544 goto cancel;
545 else
546 nlh->nlmsg_len += NLA_HDRLEN + NLA_ALIGN(sizeof(u32));
547
548 if (nla_put_u32(skb, CGW_DST_IF, gwj->ccgw.dst_idx) < 0)
549 goto cancel;
550 else
551 nlh->nlmsg_len += NLA_HDRLEN + NLA_ALIGN(sizeof(u32));
552 }
553
554 return skb->len;
555
556cancel:
557 nlmsg_cancel(skb, nlh);
558 return -EMSGSIZE;
559}
560
561/* Dump information about all CAN gateway jobs, in response to RTM_GETROUTE */
562static int cgw_dump_jobs(struct sk_buff *skb, struct netlink_callback *cb)
563{
564 struct cgw_job *gwj = NULL;
565 struct hlist_node *n;
566 int idx = 0;
567 int s_idx = cb->args[0];
568
569 rcu_read_lock();
570 hlist_for_each_entry_rcu(gwj, n, &cgw_list, list) {
571 if (idx < s_idx)
572 goto cont;
573
574 if (cgw_put_job(skb, gwj) < 0)
575 break;
576cont:
577 idx++;
578 }
579 rcu_read_unlock();
580
581 cb->args[0] = idx;
582
583 return skb->len;
584}
585
586/* check for common and gwtype specific attributes */
587static int cgw_parse_attr(struct nlmsghdr *nlh, struct cf_mod *mod,
588 u8 gwtype, void *gwtypeattr)
589{
590 struct nlattr *tb[CGW_MAX+1];
591 struct cgw_frame_mod mb;
592 int modidx = 0;
593 int err = 0;
594
595 /* initialize modification & checksum data space */
596 memset(mod, 0, sizeof(*mod));
597
598 err = nlmsg_parse(nlh, sizeof(struct rtcanmsg), tb, CGW_MAX, NULL);
599 if (err < 0)
600 return err;
601
602 /* check for AND/OR/XOR/SET modifications */
603
604 if (tb[CGW_MOD_AND] &&
605 nla_len(tb[CGW_MOD_AND]) == CGW_MODATTR_LEN) {
606 nla_memcpy(&mb, tb[CGW_MOD_AND], CGW_MODATTR_LEN);
607
608 canframecpy(&mod->modframe.and, &mb.cf);
609 mod->modtype.and = mb.modtype;
610
611 if (mb.modtype & CGW_MOD_ID)
612 mod->modfunc[modidx++] = mod_and_id;
613
614 if (mb.modtype & CGW_MOD_DLC)
615 mod->modfunc[modidx++] = mod_and_dlc;
616
617 if (mb.modtype & CGW_MOD_DATA)
618 mod->modfunc[modidx++] = mod_and_data;
619 }
620
621 if (tb[CGW_MOD_OR] &&
622 nla_len(tb[CGW_MOD_OR]) == CGW_MODATTR_LEN) {
623 nla_memcpy(&mb, tb[CGW_MOD_OR], CGW_MODATTR_LEN);
624
625 canframecpy(&mod->modframe.or, &mb.cf);
626 mod->modtype.or = mb.modtype;
627
628 if (mb.modtype & CGW_MOD_ID)
629 mod->modfunc[modidx++] = mod_or_id;
630
631 if (mb.modtype & CGW_MOD_DLC)
632 mod->modfunc[modidx++] = mod_or_dlc;
633
634 if (mb.modtype & CGW_MOD_DATA)
635 mod->modfunc[modidx++] = mod_or_data;
636 }
637
638 if (tb[CGW_MOD_XOR] &&
639 nla_len(tb[CGW_MOD_XOR]) == CGW_MODATTR_LEN) {
640 nla_memcpy(&mb, tb[CGW_MOD_XOR], CGW_MODATTR_LEN);
641
642 canframecpy(&mod->modframe.xor, &mb.cf);
643 mod->modtype.xor = mb.modtype;
644
645 if (mb.modtype & CGW_MOD_ID)
646 mod->modfunc[modidx++] = mod_xor_id;
647
648 if (mb.modtype & CGW_MOD_DLC)
649 mod->modfunc[modidx++] = mod_xor_dlc;
650
651 if (mb.modtype & CGW_MOD_DATA)
652 mod->modfunc[modidx++] = mod_xor_data;
653 }
654
655 if (tb[CGW_MOD_SET] &&
656 nla_len(tb[CGW_MOD_SET]) == CGW_MODATTR_LEN) {
657 nla_memcpy(&mb, tb[CGW_MOD_SET], CGW_MODATTR_LEN);
658
659 canframecpy(&mod->modframe.set, &mb.cf);
660 mod->modtype.set = mb.modtype;
661
662 if (mb.modtype & CGW_MOD_ID)
663 mod->modfunc[modidx++] = mod_set_id;
664
665 if (mb.modtype & CGW_MOD_DLC)
666 mod->modfunc[modidx++] = mod_set_dlc;
667
668 if (mb.modtype & CGW_MOD_DATA)
669 mod->modfunc[modidx++] = mod_set_data;
670 }
671
672 /* check for checksum operations after CAN frame modifications */
673 if (modidx) {
674
675 if (tb[CGW_CS_CRC8] &&
676 nla_len(tb[CGW_CS_CRC8]) == CGW_CS_CRC8_LEN) {
677
678 struct cgw_csum_crc8 *c = (struct cgw_csum_crc8 *)\
679 nla_data(tb[CGW_CS_CRC8]);
680
681 err = cgw_chk_csum_parms(c->from_idx, c->to_idx,
682 c->result_idx);
683 if (err)
684 return err;
685
686 nla_memcpy(&mod->csum.crc8, tb[CGW_CS_CRC8],
687 CGW_CS_CRC8_LEN);
688
689 /*
690 * select dedicated processing function to reduce
691 * runtime operations in receive hot path.
692 */
693 if (c->from_idx < 0 || c->to_idx < 0 ||
694 c->result_idx < 0)
695 mod->csumfunc.crc8 = cgw_csum_crc8_rel;
696 else if (c->from_idx <= c->to_idx)
697 mod->csumfunc.crc8 = cgw_csum_crc8_pos;
698 else
699 mod->csumfunc.crc8 = cgw_csum_crc8_neg;
700 }
701
702 if (tb[CGW_CS_XOR] &&
703 nla_len(tb[CGW_CS_XOR]) == CGW_CS_XOR_LEN) {
704
705 struct cgw_csum_xor *c = (struct cgw_csum_xor *)\
706 nla_data(tb[CGW_CS_XOR]);
707
708 err = cgw_chk_csum_parms(c->from_idx, c->to_idx,
709 c->result_idx);
710 if (err)
711 return err;
712
713 nla_memcpy(&mod->csum.xor, tb[CGW_CS_XOR],
714 CGW_CS_XOR_LEN);
715
716 /*
717 * select dedicated processing function to reduce
718 * runtime operations in receive hot path.
719 */
720 if (c->from_idx < 0 || c->to_idx < 0 ||
721 c->result_idx < 0)
722 mod->csumfunc.xor = cgw_csum_xor_rel;
723 else if (c->from_idx <= c->to_idx)
724 mod->csumfunc.xor = cgw_csum_xor_pos;
725 else
726 mod->csumfunc.xor = cgw_csum_xor_neg;
727 }
728 }
729
730 if (gwtype == CGW_TYPE_CAN_CAN) {
731
732 /* check CGW_TYPE_CAN_CAN specific attributes */
733
734 struct can_can_gw *ccgw = (struct can_can_gw *)gwtypeattr;
735 memset(ccgw, 0, sizeof(*ccgw));
736
737 /* check for can_filter in attributes */
738 if (tb[CGW_FILTER] &&
739 nla_len(tb[CGW_FILTER]) == sizeof(struct can_filter))
740 nla_memcpy(&ccgw->filter, tb[CGW_FILTER],
741 sizeof(struct can_filter));
742
743 err = -ENODEV;
744
745 /* specifying two interfaces is mandatory */
746 if (!tb[CGW_SRC_IF] || !tb[CGW_DST_IF])
747 return err;
748
749 if (nla_len(tb[CGW_SRC_IF]) == sizeof(u32))
750 nla_memcpy(&ccgw->src_idx, tb[CGW_SRC_IF],
751 sizeof(u32));
752
753 if (nla_len(tb[CGW_DST_IF]) == sizeof(u32))
754 nla_memcpy(&ccgw->dst_idx, tb[CGW_DST_IF],
755 sizeof(u32));
756
757 /* both indices set to 0 for flushing all routing entries */
758 if (!ccgw->src_idx && !ccgw->dst_idx)
759 return 0;
760
761 /* only one index set to 0 is an error */
762 if (!ccgw->src_idx || !ccgw->dst_idx)
763 return err;
764 }
765
766 /* add the checks for other gwtypes here */
767
768 return 0;
769}
770
771static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh,
772 void *arg)
773{
774 struct rtcanmsg *r;
775 struct cgw_job *gwj;
776 int err = 0;
777
778 if (nlmsg_len(nlh) < sizeof(*r))
779 return -EINVAL;
780
781 r = nlmsg_data(nlh);
782 if (r->can_family != AF_CAN)
783 return -EPFNOSUPPORT;
784
785 /* so far we only support CAN -> CAN routings */
786 if (r->gwtype != CGW_TYPE_CAN_CAN)
787 return -EINVAL;
788
789 gwj = kmem_cache_alloc(cgw_cache, GFP_KERNEL);
790 if (!gwj)
791 return -ENOMEM;
792
793 gwj->handled_frames = 0;
794 gwj->dropped_frames = 0;
795 gwj->flags = r->flags;
796 gwj->gwtype = r->gwtype;
797
798 err = cgw_parse_attr(nlh, &gwj->mod, CGW_TYPE_CAN_CAN, &gwj->ccgw);
799 if (err < 0)
800 goto out;
801
802 err = -ENODEV;
803
804 /* ifindex == 0 is not allowed for job creation */
805 if (!gwj->ccgw.src_idx || !gwj->ccgw.dst_idx)
806 goto out;
807
808 gwj->src.dev = dev_get_by_index(&init_net, gwj->ccgw.src_idx);
809
810 if (!gwj->src.dev)
811 goto out;
812
813 /* check for CAN netdev not using header_ops - see gw_rcv() */
814 if (gwj->src.dev->type != ARPHRD_CAN || gwj->src.dev->header_ops)
815 goto put_src_out;
816
817 gwj->dst.dev = dev_get_by_index(&init_net, gwj->ccgw.dst_idx);
818
819 if (!gwj->dst.dev)
820 goto put_src_out;
821
822 /* check for CAN netdev not using header_ops - see gw_rcv() */
823 if (gwj->dst.dev->type != ARPHRD_CAN || gwj->dst.dev->header_ops)
824 goto put_src_dst_out;
825
826 ASSERT_RTNL();
827
828 err = cgw_register_filter(gwj);
829 if (!err)
830 hlist_add_head_rcu(&gwj->list, &cgw_list);
831
832put_src_dst_out:
833 dev_put(gwj->dst.dev);
834put_src_out:
835 dev_put(gwj->src.dev);
836out:
837 if (err)
838 kmem_cache_free(cgw_cache, gwj);
839
840 return err;
841}
842
843static void cgw_remove_all_jobs(void)
844{
845 struct cgw_job *gwj = NULL;
846 struct hlist_node *n, *nx;
847
848 ASSERT_RTNL();
849
850 hlist_for_each_entry_safe(gwj, n, nx, &cgw_list, list) {
851 hlist_del(&gwj->list);
852 cgw_unregister_filter(gwj);
853 kfree(gwj);
854 }
855}
856
857static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
858{
859 struct cgw_job *gwj = NULL;
860 struct hlist_node *n, *nx;
861 struct rtcanmsg *r;
862 struct cf_mod mod;
863 struct can_can_gw ccgw;
864 int err = 0;
865
866 if (nlmsg_len(nlh) < sizeof(*r))
867 return -EINVAL;
868
869 r = nlmsg_data(nlh);
870 if (r->can_family != AF_CAN)
871 return -EPFNOSUPPORT;
872
873 /* so far we only support CAN -> CAN routings */
874 if (r->gwtype != CGW_TYPE_CAN_CAN)
875 return -EINVAL;
876
877 err = cgw_parse_attr(nlh, &mod, CGW_TYPE_CAN_CAN, &ccgw);
878 if (err < 0)
879 return err;
880
881 /* two interface indices both set to 0 => remove all entries */
882 if (!ccgw.src_idx && !ccgw.dst_idx) {
883 cgw_remove_all_jobs();
884 return 0;
885 }
886
887 err = -EINVAL;
888
889 ASSERT_RTNL();
890
891 /* remove only the first matching entry */
892 hlist_for_each_entry_safe(gwj, n, nx, &cgw_list, list) {
893
894 if (gwj->flags != r->flags)
895 continue;
896
897 if (memcmp(&gwj->mod, &mod, sizeof(mod)))
898 continue;
899
900 /* if (r->gwtype == CGW_TYPE_CAN_CAN) - is made sure here */
901 if (memcmp(&gwj->ccgw, &ccgw, sizeof(ccgw)))
902 continue;
903
904 hlist_del(&gwj->list);
905 cgw_unregister_filter(gwj);
906 kfree(gwj);
907 err = 0;
908 break;
909 }
910
911 return err;
912}
913
914static __init int cgw_module_init(void)
915{
916 printk(banner);
917
918 cgw_cache = kmem_cache_create("can_gw", sizeof(struct cgw_job),
919 0, 0, NULL);
920
921 if (!cgw_cache)
922 return -ENOMEM;
923
924 /* set notifier */
925 notifier.notifier_call = cgw_notifier;
926 register_netdevice_notifier(&notifier);
927
928 if (__rtnl_register(PF_CAN, RTM_GETROUTE, NULL, cgw_dump_jobs, NULL)) {
929 unregister_netdevice_notifier(&notifier);
930 kmem_cache_destroy(cgw_cache);
931 return -ENOBUFS;
932 }
933
934 /* Only the first call to __rtnl_register can fail */
935 __rtnl_register(PF_CAN, RTM_NEWROUTE, cgw_create_job, NULL, NULL);
936 __rtnl_register(PF_CAN, RTM_DELROUTE, cgw_remove_job, NULL, NULL);
937
938 return 0;
939}
940
941static __exit void cgw_module_exit(void)
942{
943 rtnl_unregister_all(PF_CAN);
944
945 unregister_netdevice_notifier(&notifier);
946
947 rtnl_lock();
948 cgw_remove_all_jobs();
949 rtnl_unlock();
950
951 rcu_barrier(); /* Wait for completion of call_rcu()'s */
952
953 kmem_cache_destroy(cgw_cache);
954}
955
956module_init(cgw_module_init);
957module_exit(cgw_module_exit);
diff --git a/net/can/proc.c b/net/can/proc.c
index 0016f7339699..ba873c36d2fd 100644
--- a/net/can/proc.c
+++ b/net/can/proc.c
@@ -37,8 +37,6 @@
37 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 37 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
38 * DAMAGE. 38 * DAMAGE.
39 * 39 *
40 * Send feedback to <socketcan-users@lists.berlios.de>
41 *
42 */ 40 */
43 41
44#include <linux/module.h> 42#include <linux/module.h>
diff --git a/net/can/raw.c b/net/can/raw.c
index dea99a6e596c..cde1b4a20f75 100644
--- a/net/can/raw.c
+++ b/net/can/raw.c
@@ -37,8 +37,6 @@
37 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 37 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
38 * DAMAGE. 38 * DAMAGE.
39 * 39 *
40 * Send feedback to <socketcan-users@lists.berlios.de>
41 *
42 */ 40 */
43 41
44#include <linux/module.h> 42#include <linux/module.h>