source: src/linux/universal/linux-3.18/net/can/gw.c @ 31869

Last change on this file since 31869 was 31869, checked in by brainslayer, 6 weeks ago

update

File size: 24.7 KB
Line 
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/kernel.h>
46#include <linux/list.h>
47#include <linux/spinlock.h>
48#include <linux/rcupdate.h>
49#include <linux/rculist.h>
50#include <linux/net.h>
51#include <linux/netdevice.h>
52#include <linux/if_arp.h>
53#include <linux/skbuff.h>
54#include <linux/can.h>
55#include <linux/can/core.h>
56#include <linux/can/skb.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 "20130117"
63#define CAN_GW_NAME "can-gw"
64
65MODULE_DESCRIPTION("PF_CAN netlink gateway");
66MODULE_LICENSE("Dual BSD/GPL");
67MODULE_AUTHOR("Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
68MODULE_ALIAS(CAN_GW_NAME);
69
70#define CGW_MIN_HOPS 1
71#define CGW_MAX_HOPS 6
72#define CGW_DEFAULT_HOPS 1
73
74static unsigned int max_hops __read_mostly = CGW_DEFAULT_HOPS;
75module_param(max_hops, uint, S_IRUGO);
76MODULE_PARM_DESC(max_hops,
77                 "maximum " CAN_GW_NAME " routing hops for CAN frames "
78                 "(valid values: " __stringify(CGW_MIN_HOPS) "-"
79                 __stringify(CGW_MAX_HOPS) " hops, "
80                 "default: " __stringify(CGW_DEFAULT_HOPS) ")");
81
82static HLIST_HEAD(cgw_list);
83static struct notifier_block notifier;
84
85static struct kmem_cache *cgw_cache __read_mostly;
86
87/* structure that contains the (on-the-fly) CAN frame modifications */
88struct cf_mod {
89        struct {
90                struct can_frame and;
91                struct can_frame or;
92                struct can_frame xor;
93                struct can_frame set;
94        } modframe;
95        struct {
96                u8 and;
97                u8 or;
98                u8 xor;
99                u8 set;
100        } modtype;
101        void (*modfunc[MAX_MODFUNCTIONS])(struct can_frame *cf,
102                                          struct cf_mod *mod);
103
104        /* CAN frame checksum calculation after CAN frame modifications */
105        struct {
106                struct cgw_csum_xor xor;
107                struct cgw_csum_crc8 crc8;
108        } csum;
109        struct {
110                void (*xor)(struct can_frame *cf, struct cgw_csum_xor *xor);
111                void (*crc8)(struct can_frame *cf, struct cgw_csum_crc8 *crc8);
112        } csumfunc;
113};
114
115
116/*
117 * So far we just support CAN -> CAN routing and frame modifications.
118 *
119 * The internal can_can_gw structure contains data and attributes for
120 * a CAN -> CAN gateway job.
121 */
122struct can_can_gw {
123        struct can_filter filter;
124        int src_idx;
125        int dst_idx;
126};
127
128/* list entry for CAN gateways jobs */
129struct cgw_job {
130        struct hlist_node list;
131        struct rcu_head rcu;
132        u32 handled_frames;
133        u32 dropped_frames;
134        u32 deleted_frames;
135        struct cf_mod mod;
136        union {
137                /* CAN frame data source */
138                struct net_device *dev;
139        } src;
140        union {
141                /* CAN frame data destination */
142                struct net_device *dev;
143        } dst;
144        union {
145                struct can_can_gw ccgw;
146                /* tbc */
147        };
148        u8 gwtype;
149        u8 limit_hops;
150        u16 flags;
151};
152
153/* modification functions that are invoked in the hot path in can_can_gw_rcv */
154
155#define MODFUNC(func, op) static void func(struct can_frame *cf, \
156                                           struct cf_mod *mod) { op ; }
157
158MODFUNC(mod_and_id, cf->can_id &= mod->modframe.and.can_id)
159MODFUNC(mod_and_dlc, cf->can_dlc &= mod->modframe.and.can_dlc)
160MODFUNC(mod_and_data, *(u64 *)cf->data &= *(u64 *)mod->modframe.and.data)
161MODFUNC(mod_or_id, cf->can_id |= mod->modframe.or.can_id)
162MODFUNC(mod_or_dlc, cf->can_dlc |= mod->modframe.or.can_dlc)
163MODFUNC(mod_or_data, *(u64 *)cf->data |= *(u64 *)mod->modframe.or.data)
164MODFUNC(mod_xor_id, cf->can_id ^= mod->modframe.xor.can_id)
165MODFUNC(mod_xor_dlc, cf->can_dlc ^= mod->modframe.xor.can_dlc)
166MODFUNC(mod_xor_data, *(u64 *)cf->data ^= *(u64 *)mod->modframe.xor.data)
167MODFUNC(mod_set_id, cf->can_id = mod->modframe.set.can_id)
168MODFUNC(mod_set_dlc, cf->can_dlc = mod->modframe.set.can_dlc)
169MODFUNC(mod_set_data, *(u64 *)cf->data = *(u64 *)mod->modframe.set.data)
170
171static inline void canframecpy(struct can_frame *dst, struct can_frame *src)
172{
173        /*
174         * Copy the struct members separately to ensure that no uninitialized
175         * data are copied in the 3 bytes hole of the struct. This is needed
176         * to make easy compares of the data in the struct cf_mod.
177         */
178
179        dst->can_id = src->can_id;
180        dst->can_dlc = src->can_dlc;
181        *(u64 *)dst->data = *(u64 *)src->data;
182}
183
184static int cgw_chk_csum_parms(s8 fr, s8 to, s8 re)
185{
186        /*
187         * absolute dlc values 0 .. 7 => 0 .. 7, e.g. data [0]
188         * relative to received dlc -1 .. -8 :
189         * e.g. for received dlc = 8
190         * -1 => index = 7 (data[7])
191         * -3 => index = 5 (data[5])
192         * -8 => index = 0 (data[0])
193         */
194
195        if (fr > -9 && fr < 8 &&
196            to > -9 && to < 8 &&
197            re > -9 && re < 8)
198                return 0;
199        else
200                return -EINVAL;
201}
202
203static inline int calc_idx(int idx, int rx_dlc)
204{
205        if (idx < 0)
206                return rx_dlc + idx;
207        else
208                return idx;
209}
210
211static void cgw_csum_xor_rel(struct can_frame *cf, struct cgw_csum_xor *xor)
212{
213        int from = calc_idx(xor->from_idx, cf->can_dlc);
214        int to = calc_idx(xor->to_idx, cf->can_dlc);
215        int res = calc_idx(xor->result_idx, cf->can_dlc);
216        u8 val = xor->init_xor_val;
217        int i;
218
219        if (from < 0 || to < 0 || res < 0)
220                return;
221
222        if (from <= to) {
223                for (i = from; i <= to; i++)
224                        val ^= cf->data[i];
225        } else {
226                for (i = from; i >= to; i--)
227                        val ^= cf->data[i];
228        }
229
230        cf->data[res] = val;
231}
232
233static void cgw_csum_xor_pos(struct can_frame *cf, struct cgw_csum_xor *xor)
234{
235        u8 val = xor->init_xor_val;
236        int i;
237
238        for (i = xor->from_idx; i <= xor->to_idx; i++)
239                val ^= cf->data[i];
240
241        cf->data[xor->result_idx] = val;
242}
243
244static void cgw_csum_xor_neg(struct can_frame *cf, struct cgw_csum_xor *xor)
245{
246        u8 val = xor->init_xor_val;
247        int i;
248
249        for (i = xor->from_idx; i >= xor->to_idx; i--)
250                val ^= cf->data[i];
251
252        cf->data[xor->result_idx] = val;
253}
254
255static void cgw_csum_crc8_rel(struct can_frame *cf, struct cgw_csum_crc8 *crc8)
256{
257        int from = calc_idx(crc8->from_idx, cf->can_dlc);
258        int to = calc_idx(crc8->to_idx, cf->can_dlc);
259        int res = calc_idx(crc8->result_idx, cf->can_dlc);
260        u8 crc = crc8->init_crc_val;
261        int i;
262
263        if (from < 0 || to < 0 || res < 0)
264                return;
265
266        if (from <= to) {
267                for (i = crc8->from_idx; i <= crc8->to_idx; i++)
268                        crc = crc8->crctab[crc^cf->data[i]];
269        } else {
270                for (i = crc8->from_idx; i >= crc8->to_idx; i--)
271                        crc = crc8->crctab[crc^cf->data[i]];
272        }
273
274        switch (crc8->profile) {
275
276        case CGW_CRC8PRF_1U8:
277                crc = crc8->crctab[crc^crc8->profile_data[0]];
278                break;
279
280        case  CGW_CRC8PRF_16U8:
281                crc = crc8->crctab[crc^crc8->profile_data[cf->data[1] & 0xF]];
282                break;
283
284        case CGW_CRC8PRF_SFFID_XOR:
285                crc = crc8->crctab[crc^(cf->can_id & 0xFF)^
286                                   (cf->can_id >> 8 & 0xFF)];
287                break;
288
289        }
290
291        cf->data[crc8->result_idx] = crc^crc8->final_xor_val;
292}
293
294static void cgw_csum_crc8_pos(struct can_frame *cf, struct cgw_csum_crc8 *crc8)
295{
296        u8 crc = crc8->init_crc_val;
297        int i;
298
299        for (i = crc8->from_idx; i <= crc8->to_idx; i++)
300                crc = crc8->crctab[crc^cf->data[i]];
301
302        switch (crc8->profile) {
303
304        case CGW_CRC8PRF_1U8:
305                crc = crc8->crctab[crc^crc8->profile_data[0]];
306                break;
307
308        case  CGW_CRC8PRF_16U8:
309                crc = crc8->crctab[crc^crc8->profile_data[cf->data[1] & 0xF]];
310                break;
311
312        case CGW_CRC8PRF_SFFID_XOR:
313                crc = crc8->crctab[crc^(cf->can_id & 0xFF)^
314                                   (cf->can_id >> 8 & 0xFF)];
315                break;
316        }
317
318        cf->data[crc8->result_idx] = crc^crc8->final_xor_val;
319}
320
321static void cgw_csum_crc8_neg(struct can_frame *cf, struct cgw_csum_crc8 *crc8)
322{
323        u8 crc = crc8->init_crc_val;
324        int i;
325
326        for (i = crc8->from_idx; i >= crc8->to_idx; i--)
327                crc = crc8->crctab[crc^cf->data[i]];
328
329        switch (crc8->profile) {
330
331        case CGW_CRC8PRF_1U8:
332                crc = crc8->crctab[crc^crc8->profile_data[0]];
333                break;
334
335        case  CGW_CRC8PRF_16U8:
336                crc = crc8->crctab[crc^crc8->profile_data[cf->data[1] & 0xF]];
337                break;
338
339        case CGW_CRC8PRF_SFFID_XOR:
340                crc = crc8->crctab[crc^(cf->can_id & 0xFF)^
341                                   (cf->can_id >> 8 & 0xFF)];
342                break;
343        }
344
345        cf->data[crc8->result_idx] = crc^crc8->final_xor_val;
346}
347
348/* the receive & process & send function */
349static void can_can_gw_rcv(struct sk_buff *skb, void *data)
350{
351        struct cgw_job *gwj = (struct cgw_job *)data;
352        struct can_frame *cf;
353        struct sk_buff *nskb;
354        int modidx = 0;
355
356        /*
357         * Do not handle CAN frames routed more than 'max_hops' times.
358         * In general we should never catch this delimiter which is intended
359         * to cover a misconfiguration protection (e.g. circular CAN routes).
360         *
361         * The Controller Area Network controllers only accept CAN frames with
362         * correct CRCs - which are not visible in the controller registers.
363         * According to skbuff.h documentation the csum_start element for IP
364         * checksums is undefined/unsued when ip_summed == CHECKSUM_UNNECESSARY.
365         * Only CAN skbs can be processed here which already have this property.
366         */
367
368#define cgw_hops(skb) ((skb)->csum_start)
369
370        BUG_ON(skb->ip_summed != CHECKSUM_UNNECESSARY);
371
372        if (cgw_hops(skb) >= max_hops) {
373                /* indicate deleted frames due to misconfiguration */
374                gwj->deleted_frames++;
375                return;
376        }
377
378        if (!(gwj->dst.dev->flags & IFF_UP)) {
379                gwj->dropped_frames++;
380                return;
381        }
382
383        /* is sending the skb back to the incoming interface not allowed? */
384        if (!(gwj->flags & CGW_FLAGS_CAN_IIF_TX_OK) &&
385            can_skb_prv(skb)->ifindex == gwj->dst.dev->ifindex)
386                return;
387
388        /*
389         * clone the given skb, which has not been done in can_rcv()
390         *
391         * When there is at least one modification function activated,
392         * we need to copy the skb as we want to modify skb->data.
393         */
394        if (gwj->mod.modfunc[0])
395                nskb = skb_copy(skb, GFP_ATOMIC);
396        else
397                nskb = skb_clone(skb, GFP_ATOMIC);
398
399        if (!nskb) {
400                gwj->dropped_frames++;
401                return;
402        }
403
404        /* put the incremented hop counter in the cloned skb */
405        cgw_hops(nskb) = cgw_hops(skb) + 1;
406
407        /* first processing of this CAN frame -> adjust to private hop limit */
408        if (gwj->limit_hops && cgw_hops(nskb) == 1)
409                cgw_hops(nskb) = max_hops - gwj->limit_hops + 1;
410
411        nskb->dev = gwj->dst.dev;
412
413        /* pointer to modifiable CAN frame */
414        cf = (struct can_frame *)nskb->data;
415
416        /* perform preprocessed modification functions if there are any */
417        while (modidx < MAX_MODFUNCTIONS && gwj->mod.modfunc[modidx])
418                (*gwj->mod.modfunc[modidx++])(cf, &gwj->mod);
419
420        /* check for checksum updates when the CAN frame has been modified */
421        if (modidx) {
422                if (gwj->mod.csumfunc.crc8)
423                        (*gwj->mod.csumfunc.crc8)(cf, &gwj->mod.csum.crc8);
424
425                if (gwj->mod.csumfunc.xor)
426                        (*gwj->mod.csumfunc.xor)(cf, &gwj->mod.csum.xor);
427        }
428
429        /* clear the skb timestamp if not configured the other way */
430        if (!(gwj->flags & CGW_FLAGS_CAN_SRC_TSTAMP))
431                nskb->tstamp.tv64 = 0;
432
433        /* send to netdevice */
434        if (can_send(nskb, gwj->flags & CGW_FLAGS_CAN_ECHO))
435                gwj->dropped_frames++;
436        else
437                gwj->handled_frames++;
438}
439
440static inline int cgw_register_filter(struct cgw_job *gwj)
441{
442        return can_rx_register(gwj->src.dev, gwj->ccgw.filter.can_id,
443                               gwj->ccgw.filter.can_mask, can_can_gw_rcv,
444                               gwj, "gw", NULL);
445}
446
447static inline void cgw_unregister_filter(struct cgw_job *gwj)
448{
449        can_rx_unregister(gwj->src.dev, gwj->ccgw.filter.can_id,
450                          gwj->ccgw.filter.can_mask, can_can_gw_rcv, gwj);
451}
452
453static int cgw_notifier(struct notifier_block *nb,
454                        unsigned long msg, void *ptr)
455{
456        struct net_device *dev = netdev_notifier_info_to_dev(ptr);
457
458        if (!net_eq(dev_net(dev), &init_net))
459                return NOTIFY_DONE;
460        if (dev->type != ARPHRD_CAN)
461                return NOTIFY_DONE;
462
463        if (msg == NETDEV_UNREGISTER) {
464
465                struct cgw_job *gwj = NULL;
466                struct hlist_node *nx;
467
468                ASSERT_RTNL();
469
470                hlist_for_each_entry_safe(gwj, nx, &cgw_list, list) {
471
472                        if (gwj->src.dev == dev || gwj->dst.dev == dev) {
473                                hlist_del(&gwj->list);
474                                cgw_unregister_filter(gwj);
475                                kmem_cache_free(cgw_cache, gwj);
476                        }
477                }
478        }
479
480        return NOTIFY_DONE;
481}
482
483static int cgw_put_job(struct sk_buff *skb, struct cgw_job *gwj, int type,
484                       u32 pid, u32 seq, int flags)
485{
486        struct cgw_frame_mod mb;
487        struct rtcanmsg *rtcan;
488        struct nlmsghdr *nlh;
489
490        nlh = nlmsg_put(skb, pid, seq, type, sizeof(*rtcan), flags);
491        if (!nlh)
492                return -EMSGSIZE;
493
494        rtcan = nlmsg_data(nlh);
495        rtcan->can_family = AF_CAN;
496        rtcan->gwtype = gwj->gwtype;
497        rtcan->flags = gwj->flags;
498
499        /* add statistics if available */
500
501        if (gwj->handled_frames) {
502                if (nla_put_u32(skb, CGW_HANDLED, gwj->handled_frames) < 0)
503                        goto cancel;
504        }
505
506        if (gwj->dropped_frames) {
507                if (nla_put_u32(skb, CGW_DROPPED, gwj->dropped_frames) < 0)
508                        goto cancel;
509        }
510
511        if (gwj->deleted_frames) {
512                if (nla_put_u32(skb, CGW_DELETED, gwj->deleted_frames) < 0)
513                        goto cancel;
514        }
515
516        /* check non default settings of attributes */
517
518        if (gwj->limit_hops) {
519                if (nla_put_u8(skb, CGW_LIM_HOPS, gwj->limit_hops) < 0)
520                        goto cancel;
521        }
522
523        if (gwj->mod.modtype.and) {
524                memcpy(&mb.cf, &gwj->mod.modframe.and, sizeof(mb.cf));
525                mb.modtype = gwj->mod.modtype.and;
526                if (nla_put(skb, CGW_MOD_AND, sizeof(mb), &mb) < 0)
527                        goto cancel;
528        }
529
530        if (gwj->mod.modtype.or) {
531                memcpy(&mb.cf, &gwj->mod.modframe.or, sizeof(mb.cf));
532                mb.modtype = gwj->mod.modtype.or;
533                if (nla_put(skb, CGW_MOD_OR, sizeof(mb), &mb) < 0)
534                        goto cancel;
535        }
536
537        if (gwj->mod.modtype.xor) {
538                memcpy(&mb.cf, &gwj->mod.modframe.xor, sizeof(mb.cf));
539                mb.modtype = gwj->mod.modtype.xor;
540                if (nla_put(skb, CGW_MOD_XOR, sizeof(mb), &mb) < 0)
541                        goto cancel;
542        }
543
544        if (gwj->mod.modtype.set) {
545                memcpy(&mb.cf, &gwj->mod.modframe.set, sizeof(mb.cf));
546                mb.modtype = gwj->mod.modtype.set;
547                if (nla_put(skb, CGW_MOD_SET, sizeof(mb), &mb) < 0)
548                        goto cancel;
549        }
550
551        if (gwj->mod.csumfunc.crc8) {
552                if (nla_put(skb, CGW_CS_CRC8, CGW_CS_CRC8_LEN,
553                            &gwj->mod.csum.crc8) < 0)
554                        goto cancel;
555        }
556
557        if (gwj->mod.csumfunc.xor) {
558                if (nla_put(skb, CGW_CS_XOR, CGW_CS_XOR_LEN,
559                            &gwj->mod.csum.xor) < 0)
560                        goto cancel;
561        }
562
563        if (gwj->gwtype == CGW_TYPE_CAN_CAN) {
564
565                if (gwj->ccgw.filter.can_id || gwj->ccgw.filter.can_mask) {
566                        if (nla_put(skb, CGW_FILTER, sizeof(struct can_filter),
567                                    &gwj->ccgw.filter) < 0)
568                                goto cancel;
569                }
570
571                if (nla_put_u32(skb, CGW_SRC_IF, gwj->ccgw.src_idx) < 0)
572                        goto cancel;
573
574                if (nla_put_u32(skb, CGW_DST_IF, gwj->ccgw.dst_idx) < 0)
575                        goto cancel;
576        }
577
578        return nlmsg_end(skb, nlh);
579
580cancel:
581        nlmsg_cancel(skb, nlh);
582        return -EMSGSIZE;
583}
584
585/* Dump information about all CAN gateway jobs, in response to RTM_GETROUTE */
586static int cgw_dump_jobs(struct sk_buff *skb, struct netlink_callback *cb)
587{
588        struct cgw_job *gwj = NULL;
589        int idx = 0;
590        int s_idx = cb->args[0];
591
592        rcu_read_lock();
593        hlist_for_each_entry_rcu(gwj, &cgw_list, list) {
594                if (idx < s_idx)
595                        goto cont;
596
597                if (cgw_put_job(skb, gwj, RTM_NEWROUTE, NETLINK_CB(cb->skb).portid,
598                    cb->nlh->nlmsg_seq, NLM_F_MULTI) < 0)
599                        break;
600cont:
601                idx++;
602        }
603        rcu_read_unlock();
604
605        cb->args[0] = idx;
606
607        return skb->len;
608}
609
610static const struct nla_policy cgw_policy[CGW_MAX+1] = {
611        [CGW_MOD_AND]   = { .len = sizeof(struct cgw_frame_mod) },
612        [CGW_MOD_OR]    = { .len = sizeof(struct cgw_frame_mod) },
613        [CGW_MOD_XOR]   = { .len = sizeof(struct cgw_frame_mod) },
614        [CGW_MOD_SET]   = { .len = sizeof(struct cgw_frame_mod) },
615        [CGW_CS_XOR]    = { .len = sizeof(struct cgw_csum_xor) },
616        [CGW_CS_CRC8]   = { .len = sizeof(struct cgw_csum_crc8) },
617        [CGW_SRC_IF]    = { .type = NLA_U32 },
618        [CGW_DST_IF]    = { .type = NLA_U32 },
619        [CGW_FILTER]    = { .len = sizeof(struct can_filter) },
620        [CGW_LIM_HOPS]  = { .type = NLA_U8 },
621};
622
623/* check for common and gwtype specific attributes */
624static int cgw_parse_attr(struct nlmsghdr *nlh, struct cf_mod *mod,
625                          u8 gwtype, void *gwtypeattr, u8 *limhops)
626{
627        struct nlattr *tb[CGW_MAX+1];
628        struct cgw_frame_mod mb;
629        int modidx = 0;
630        int err = 0;
631
632        /* initialize modification & checksum data space */
633        memset(mod, 0, sizeof(*mod));
634
635        err = nlmsg_parse(nlh, sizeof(struct rtcanmsg), tb, CGW_MAX,
636                          cgw_policy);
637        if (err < 0)
638                return err;
639
640        if (tb[CGW_LIM_HOPS]) {
641                *limhops = nla_get_u8(tb[CGW_LIM_HOPS]);
642
643                if (*limhops < 1 || *limhops > max_hops)
644                        return -EINVAL;
645        }
646
647        /* check for AND/OR/XOR/SET modifications */
648
649        if (tb[CGW_MOD_AND]) {
650                nla_memcpy(&mb, tb[CGW_MOD_AND], CGW_MODATTR_LEN);
651
652                canframecpy(&mod->modframe.and, &mb.cf);
653                mod->modtype.and = mb.modtype;
654
655                if (mb.modtype & CGW_MOD_ID)
656                        mod->modfunc[modidx++] = mod_and_id;
657
658                if (mb.modtype & CGW_MOD_DLC)
659                        mod->modfunc[modidx++] = mod_and_dlc;
660
661                if (mb.modtype & CGW_MOD_DATA)
662                        mod->modfunc[modidx++] = mod_and_data;
663        }
664
665        if (tb[CGW_MOD_OR]) {
666                nla_memcpy(&mb, tb[CGW_MOD_OR], CGW_MODATTR_LEN);
667
668                canframecpy(&mod->modframe.or, &mb.cf);
669                mod->modtype.or = mb.modtype;
670
671                if (mb.modtype & CGW_MOD_ID)
672                        mod->modfunc[modidx++] = mod_or_id;
673
674                if (mb.modtype & CGW_MOD_DLC)
675                        mod->modfunc[modidx++] = mod_or_dlc;
676
677                if (mb.modtype & CGW_MOD_DATA)
678                        mod->modfunc[modidx++] = mod_or_data;
679        }
680
681        if (tb[CGW_MOD_XOR]) {
682                nla_memcpy(&mb, tb[CGW_MOD_XOR], CGW_MODATTR_LEN);
683
684                canframecpy(&mod->modframe.xor, &mb.cf);
685                mod->modtype.xor = mb.modtype;
686
687                if (mb.modtype & CGW_MOD_ID)
688                        mod->modfunc[modidx++] = mod_xor_id;
689
690                if (mb.modtype & CGW_MOD_DLC)
691                        mod->modfunc[modidx++] = mod_xor_dlc;
692
693                if (mb.modtype & CGW_MOD_DATA)
694                        mod->modfunc[modidx++] = mod_xor_data;
695        }
696
697        if (tb[CGW_MOD_SET]) {
698                nla_memcpy(&mb, tb[CGW_MOD_SET], CGW_MODATTR_LEN);
699
700                canframecpy(&mod->modframe.set, &mb.cf);
701                mod->modtype.set = mb.modtype;
702
703                if (mb.modtype & CGW_MOD_ID)
704                        mod->modfunc[modidx++] = mod_set_id;
705
706                if (mb.modtype & CGW_MOD_DLC)
707                        mod->modfunc[modidx++] = mod_set_dlc;
708
709                if (mb.modtype & CGW_MOD_DATA)
710                        mod->modfunc[modidx++] = mod_set_data;
711        }
712
713        /* check for checksum operations after CAN frame modifications */
714        if (modidx) {
715
716                if (tb[CGW_CS_CRC8]) {
717                        struct cgw_csum_crc8 *c = nla_data(tb[CGW_CS_CRC8]);
718
719                        err = cgw_chk_csum_parms(c->from_idx, c->to_idx,
720                                                 c->result_idx);
721                        if (err)
722                                return err;
723
724                        nla_memcpy(&mod->csum.crc8, tb[CGW_CS_CRC8],
725                                   CGW_CS_CRC8_LEN);
726
727                        /*
728                         * select dedicated processing function to reduce
729                         * runtime operations in receive hot path.
730                         */
731                        if (c->from_idx < 0 || c->to_idx < 0 ||
732                            c->result_idx < 0)
733                                mod->csumfunc.crc8 = cgw_csum_crc8_rel;
734                        else if (c->from_idx <= c->to_idx)
735                                mod->csumfunc.crc8 = cgw_csum_crc8_pos;
736                        else
737                                mod->csumfunc.crc8 = cgw_csum_crc8_neg;
738                }
739
740                if (tb[CGW_CS_XOR]) {
741                        struct cgw_csum_xor *c = nla_data(tb[CGW_CS_XOR]);
742
743                        err = cgw_chk_csum_parms(c->from_idx, c->to_idx,
744                                                 c->result_idx);
745                        if (err)
746                                return err;
747
748                        nla_memcpy(&mod->csum.xor, tb[CGW_CS_XOR],
749                                   CGW_CS_XOR_LEN);
750
751                        /*
752                         * select dedicated processing function to reduce
753                         * runtime operations in receive hot path.
754                         */
755                        if (c->from_idx < 0 || c->to_idx < 0 ||
756                            c->result_idx < 0)
757                                mod->csumfunc.xor = cgw_csum_xor_rel;
758                        else if (c->from_idx <= c->to_idx)
759                                mod->csumfunc.xor = cgw_csum_xor_pos;
760                        else
761                                mod->csumfunc.xor = cgw_csum_xor_neg;
762                }
763        }
764
765        if (gwtype == CGW_TYPE_CAN_CAN) {
766
767                /* check CGW_TYPE_CAN_CAN specific attributes */
768
769                struct can_can_gw *ccgw = (struct can_can_gw *)gwtypeattr;
770                memset(ccgw, 0, sizeof(*ccgw));
771
772                /* check for can_filter in attributes */
773                if (tb[CGW_FILTER])
774                        nla_memcpy(&ccgw->filter, tb[CGW_FILTER],
775                                   sizeof(struct can_filter));
776
777                err = -ENODEV;
778
779                /* specifying two interfaces is mandatory */
780                if (!tb[CGW_SRC_IF] || !tb[CGW_DST_IF])
781                        return err;
782
783                ccgw->src_idx = nla_get_u32(tb[CGW_SRC_IF]);
784                ccgw->dst_idx = nla_get_u32(tb[CGW_DST_IF]);
785
786                /* both indices set to 0 for flushing all routing entries */
787                if (!ccgw->src_idx && !ccgw->dst_idx)
788                        return 0;
789
790                /* only one index set to 0 is an error */
791                if (!ccgw->src_idx || !ccgw->dst_idx)
792                        return err;
793        }
794
795        /* add the checks for other gwtypes here */
796
797        return 0;
798}
799
800static int cgw_create_job(struct sk_buff *skb,  struct nlmsghdr *nlh)
801{
802        struct rtcanmsg *r;
803        struct cgw_job *gwj;
804        u8 limhops = 0;
805        int err = 0;
806
807        if (!netlink_capable(skb, CAP_NET_ADMIN))
808                return -EPERM;
809
810        if (nlmsg_len(nlh) < sizeof(*r))
811                return -EINVAL;
812
813        r = nlmsg_data(nlh);
814        if (r->can_family != AF_CAN)
815                return -EPFNOSUPPORT;
816
817        /* so far we only support CAN -> CAN routings */
818        if (r->gwtype != CGW_TYPE_CAN_CAN)
819                return -EINVAL;
820
821        gwj = kmem_cache_alloc(cgw_cache, GFP_KERNEL);
822        if (!gwj)
823                return -ENOMEM;
824
825        gwj->handled_frames = 0;
826        gwj->dropped_frames = 0;
827        gwj->deleted_frames = 0;
828        gwj->flags = r->flags;
829        gwj->gwtype = r->gwtype;
830
831        err = cgw_parse_attr(nlh, &gwj->mod, CGW_TYPE_CAN_CAN, &gwj->ccgw,
832                             &limhops);
833        if (err < 0)
834                goto out;
835
836        err = -ENODEV;
837
838        /* ifindex == 0 is not allowed for job creation */
839        if (!gwj->ccgw.src_idx || !gwj->ccgw.dst_idx)
840                goto out;
841
842        gwj->src.dev = __dev_get_by_index(&init_net, gwj->ccgw.src_idx);
843
844        if (!gwj->src.dev)
845                goto out;
846
847        if (gwj->src.dev->type != ARPHRD_CAN)
848                goto out;
849
850        gwj->dst.dev = __dev_get_by_index(&init_net, gwj->ccgw.dst_idx);
851
852        if (!gwj->dst.dev)
853                goto out;
854
855        if (gwj->dst.dev->type != ARPHRD_CAN)
856                goto out;
857
858        gwj->limit_hops = limhops;
859
860        ASSERT_RTNL();
861
862        err = cgw_register_filter(gwj);
863        if (!err)
864                hlist_add_head_rcu(&gwj->list, &cgw_list);
865out:
866        if (err)
867                kmem_cache_free(cgw_cache, gwj);
868
869        return err;
870}
871
872static void cgw_remove_all_jobs(void)
873{
874        struct cgw_job *gwj = NULL;
875        struct hlist_node *nx;
876
877        ASSERT_RTNL();
878
879        hlist_for_each_entry_safe(gwj, nx, &cgw_list, list) {
880                hlist_del(&gwj->list);
881                cgw_unregister_filter(gwj);
882                kmem_cache_free(cgw_cache, gwj);
883        }
884}
885
886static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh)
887{
888        struct cgw_job *gwj = NULL;
889        struct hlist_node *nx;
890        struct rtcanmsg *r;
891        struct cf_mod mod;
892        struct can_can_gw ccgw;
893        u8 limhops = 0;
894        int err = 0;
895
896        if (!netlink_capable(skb, CAP_NET_ADMIN))
897                return -EPERM;
898
899        if (nlmsg_len(nlh) < sizeof(*r))
900                return -EINVAL;
901
902        r = nlmsg_data(nlh);
903        if (r->can_family != AF_CAN)
904                return -EPFNOSUPPORT;
905
906        /* so far we only support CAN -> CAN routings */
907        if (r->gwtype != CGW_TYPE_CAN_CAN)
908                return -EINVAL;
909
910        err = cgw_parse_attr(nlh, &mod, CGW_TYPE_CAN_CAN, &ccgw, &limhops);
911        if (err < 0)
912                return err;
913
914        /* two interface indices both set to 0 => remove all entries */
915        if (!ccgw.src_idx && !ccgw.dst_idx) {
916                cgw_remove_all_jobs();
917                return 0;
918        }
919
920        err = -EINVAL;
921
922        ASSERT_RTNL();
923
924        /* remove only the first matching entry */
925        hlist_for_each_entry_safe(gwj, nx, &cgw_list, list) {
926
927                if (gwj->flags != r->flags)
928                        continue;
929
930                if (gwj->limit_hops != limhops)
931                        continue;
932
933                if (memcmp(&gwj->mod, &mod, sizeof(mod)))
934                        continue;
935
936                /* if (r->gwtype == CGW_TYPE_CAN_CAN) - is made sure here */
937                if (memcmp(&gwj->ccgw, &ccgw, sizeof(ccgw)))
938                        continue;
939
940                hlist_del(&gwj->list);
941                cgw_unregister_filter(gwj);
942                kmem_cache_free(cgw_cache, gwj);
943                err = 0;
944                break;
945        }
946
947        return err;
948}
949
950static __init int cgw_module_init(void)
951{
952        /* sanitize given module parameter */
953        max_hops = clamp_t(unsigned int, max_hops, CGW_MIN_HOPS, CGW_MAX_HOPS);
954
955        pr_info("can: netlink gateway (rev " CAN_GW_VERSION ") max_hops=%d\n",
956                max_hops);
957
958        cgw_cache = kmem_cache_create("can_gw", sizeof(struct cgw_job),
959                                      0, 0, NULL);
960
961        if (!cgw_cache)
962                return -ENOMEM;
963
964        /* set notifier */
965        notifier.notifier_call = cgw_notifier;
966        register_netdevice_notifier(&notifier);
967
968        if (__rtnl_register(PF_CAN, RTM_GETROUTE, NULL, cgw_dump_jobs, NULL)) {
969                unregister_netdevice_notifier(&notifier);
970                kmem_cache_destroy(cgw_cache);
971                return -ENOBUFS;
972        }
973
974        /* Only the first call to __rtnl_register can fail */
975        __rtnl_register(PF_CAN, RTM_NEWROUTE, cgw_create_job, NULL, NULL);
976        __rtnl_register(PF_CAN, RTM_DELROUTE, cgw_remove_job, NULL, NULL);
977
978        return 0;
979}
980
981static __exit void cgw_module_exit(void)
982{
983        rtnl_unregister_all(PF_CAN);
984
985        unregister_netdevice_notifier(&notifier);
986
987        rtnl_lock();
988        cgw_remove_all_jobs();
989        rtnl_unlock();
990
991        rcu_barrier(); /* Wait for completion of call_rcu()'s */
992
993        kmem_cache_destroy(cgw_cache);
994}
995
996module_init(cgw_module_init);
997module_exit(cgw_module_exit);
Note: See TracBrowser for help on using the repository browser.