linux/net/can/gw.c
<<
>>
Prefs
   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 __initconst 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
  69static HLIST_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, int type,
 448                       u32 pid, u32 seq, int flags)
 449{
 450        struct cgw_frame_mod mb;
 451        struct rtcanmsg *rtcan;
 452        struct nlmsghdr *nlh;
 453
 454        nlh = nlmsg_put(skb, pid, seq, type, sizeof(*rtcan), flags);
 455        if (!nlh)
 456                return -EMSGSIZE;
 457
 458        rtcan = nlmsg_data(nlh);
 459        rtcan->can_family = AF_CAN;
 460        rtcan->gwtype = gwj->gwtype;
 461        rtcan->flags = gwj->flags;
 462
 463        /* add statistics if available */
 464
 465        if (gwj->handled_frames) {
 466                if (nla_put_u32(skb, CGW_HANDLED, gwj->handled_frames) < 0)
 467                        goto cancel;
 468        }
 469
 470        if (gwj->dropped_frames) {
 471                if (nla_put_u32(skb, CGW_DROPPED, gwj->dropped_frames) < 0)
 472                        goto cancel;
 473        }
 474
 475        /* check non default settings of attributes */
 476
 477        if (gwj->mod.modtype.and) {
 478                memcpy(&mb.cf, &gwj->mod.modframe.and, sizeof(mb.cf));
 479                mb.modtype = gwj->mod.modtype.and;
 480                if (nla_put(skb, CGW_MOD_AND, sizeof(mb), &mb) < 0)
 481                        goto cancel;
 482        }
 483
 484        if (gwj->mod.modtype.or) {
 485                memcpy(&mb.cf, &gwj->mod.modframe.or, sizeof(mb.cf));
 486                mb.modtype = gwj->mod.modtype.or;
 487                if (nla_put(skb, CGW_MOD_OR, sizeof(mb), &mb) < 0)
 488                        goto cancel;
 489        }
 490
 491        if (gwj->mod.modtype.xor) {
 492                memcpy(&mb.cf, &gwj->mod.modframe.xor, sizeof(mb.cf));
 493                mb.modtype = gwj->mod.modtype.xor;
 494                if (nla_put(skb, CGW_MOD_XOR, sizeof(mb), &mb) < 0)
 495                        goto cancel;
 496        }
 497
 498        if (gwj->mod.modtype.set) {
 499                memcpy(&mb.cf, &gwj->mod.modframe.set, sizeof(mb.cf));
 500                mb.modtype = gwj->mod.modtype.set;
 501                if (nla_put(skb, CGW_MOD_SET, sizeof(mb), &mb) < 0)
 502                        goto cancel;
 503        }
 504
 505        if (gwj->mod.csumfunc.crc8) {
 506                if (nla_put(skb, CGW_CS_CRC8, CGW_CS_CRC8_LEN,
 507                            &gwj->mod.csum.crc8) < 0)
 508                        goto cancel;
 509        }
 510
 511        if (gwj->mod.csumfunc.xor) {
 512                if (nla_put(skb, CGW_CS_XOR, CGW_CS_XOR_LEN,
 513                            &gwj->mod.csum.xor) < 0)
 514                        goto cancel;
 515        }
 516
 517        if (gwj->gwtype == CGW_TYPE_CAN_CAN) {
 518
 519                if (gwj->ccgw.filter.can_id || gwj->ccgw.filter.can_mask) {
 520                        if (nla_put(skb, CGW_FILTER, sizeof(struct can_filter),
 521                                    &gwj->ccgw.filter) < 0)
 522                                goto cancel;
 523                }
 524
 525                if (nla_put_u32(skb, CGW_SRC_IF, gwj->ccgw.src_idx) < 0)
 526                        goto cancel;
 527
 528                if (nla_put_u32(skb, CGW_DST_IF, gwj->ccgw.dst_idx) < 0)
 529                        goto cancel;
 530        }
 531
 532        return nlmsg_end(skb, nlh);
 533
 534cancel:
 535        nlmsg_cancel(skb, nlh);
 536        return -EMSGSIZE;
 537}
 538
 539/* Dump information about all CAN gateway jobs, in response to RTM_GETROUTE */
 540static int cgw_dump_jobs(struct sk_buff *skb, struct netlink_callback *cb)
 541{
 542        struct cgw_job *gwj = NULL;
 543        struct hlist_node *n;
 544        int idx = 0;
 545        int s_idx = cb->args[0];
 546
 547        rcu_read_lock();
 548        hlist_for_each_entry_rcu(gwj, n, &cgw_list, list) {
 549                if (idx < s_idx)
 550                        goto cont;
 551
 552                if (cgw_put_job(skb, gwj, RTM_NEWROUTE, NETLINK_CB(cb->skb).portid,
 553                    cb->nlh->nlmsg_seq, NLM_F_MULTI) < 0)
 554                        break;
 555cont:
 556                idx++;
 557        }
 558        rcu_read_unlock();
 559
 560        cb->args[0] = idx;
 561
 562        return skb->len;
 563}
 564
 565static const struct nla_policy cgw_policy[CGW_MAX+1] = {
 566        [CGW_MOD_AND]   = { .len = sizeof(struct cgw_frame_mod) },
 567        [CGW_MOD_OR]    = { .len = sizeof(struct cgw_frame_mod) },
 568        [CGW_MOD_XOR]   = { .len = sizeof(struct cgw_frame_mod) },
 569        [CGW_MOD_SET]   = { .len = sizeof(struct cgw_frame_mod) },
 570        [CGW_CS_XOR]    = { .len = sizeof(struct cgw_csum_xor) },
 571        [CGW_CS_CRC8]   = { .len = sizeof(struct cgw_csum_crc8) },
 572        [CGW_SRC_IF]    = { .type = NLA_U32 },
 573        [CGW_DST_IF]    = { .type = NLA_U32 },
 574        [CGW_FILTER]    = { .len = sizeof(struct can_filter) },
 575};
 576
 577/* check for common and gwtype specific attributes */
 578static int cgw_parse_attr(struct nlmsghdr *nlh, struct cf_mod *mod,
 579                          u8 gwtype, void *gwtypeattr)
 580{
 581        struct nlattr *tb[CGW_MAX+1];
 582        struct cgw_frame_mod mb;
 583        int modidx = 0;
 584        int err = 0;
 585
 586        /* initialize modification & checksum data space */
 587        memset(mod, 0, sizeof(*mod));
 588
 589        err = nlmsg_parse(nlh, sizeof(struct rtcanmsg), tb, CGW_MAX,
 590                          cgw_policy);
 591        if (err < 0)
 592                return err;
 593
 594        /* check for AND/OR/XOR/SET modifications */
 595
 596        if (tb[CGW_MOD_AND]) {
 597                nla_memcpy(&mb, tb[CGW_MOD_AND], CGW_MODATTR_LEN);
 598
 599                canframecpy(&mod->modframe.and, &mb.cf);
 600                mod->modtype.and = mb.modtype;
 601
 602                if (mb.modtype & CGW_MOD_ID)
 603                        mod->modfunc[modidx++] = mod_and_id;
 604
 605                if (mb.modtype & CGW_MOD_DLC)
 606                        mod->modfunc[modidx++] = mod_and_dlc;
 607
 608                if (mb.modtype & CGW_MOD_DATA)
 609                        mod->modfunc[modidx++] = mod_and_data;
 610        }
 611
 612        if (tb[CGW_MOD_OR]) {
 613                nla_memcpy(&mb, tb[CGW_MOD_OR], CGW_MODATTR_LEN);
 614
 615                canframecpy(&mod->modframe.or, &mb.cf);
 616                mod->modtype.or = mb.modtype;
 617
 618                if (mb.modtype & CGW_MOD_ID)
 619                        mod->modfunc[modidx++] = mod_or_id;
 620
 621                if (mb.modtype & CGW_MOD_DLC)
 622                        mod->modfunc[modidx++] = mod_or_dlc;
 623
 624                if (mb.modtype & CGW_MOD_DATA)
 625                        mod->modfunc[modidx++] = mod_or_data;
 626        }
 627
 628        if (tb[CGW_MOD_XOR]) {
 629                nla_memcpy(&mb, tb[CGW_MOD_XOR], CGW_MODATTR_LEN);
 630
 631                canframecpy(&mod->modframe.xor, &mb.cf);
 632                mod->modtype.xor = mb.modtype;
 633
 634                if (mb.modtype & CGW_MOD_ID)
 635                        mod->modfunc[modidx++] = mod_xor_id;
 636
 637                if (mb.modtype & CGW_MOD_DLC)
 638                        mod->modfunc[modidx++] = mod_xor_dlc;
 639
 640                if (mb.modtype & CGW_MOD_DATA)
 641                        mod->modfunc[modidx++] = mod_xor_data;
 642        }
 643
 644        if (tb[CGW_MOD_SET]) {
 645                nla_memcpy(&mb, tb[CGW_MOD_SET], CGW_MODATTR_LEN);
 646
 647                canframecpy(&mod->modframe.set, &mb.cf);
 648                mod->modtype.set = mb.modtype;
 649
 650                if (mb.modtype & CGW_MOD_ID)
 651                        mod->modfunc[modidx++] = mod_set_id;
 652
 653                if (mb.modtype & CGW_MOD_DLC)
 654                        mod->modfunc[modidx++] = mod_set_dlc;
 655
 656                if (mb.modtype & CGW_MOD_DATA)
 657                        mod->modfunc[modidx++] = mod_set_data;
 658        }
 659
 660        /* check for checksum operations after CAN frame modifications */
 661        if (modidx) {
 662
 663                if (tb[CGW_CS_CRC8]) {
 664                        struct cgw_csum_crc8 *c = nla_data(tb[CGW_CS_CRC8]);
 665
 666                        err = cgw_chk_csum_parms(c->from_idx, c->to_idx,
 667                                                 c->result_idx);
 668                        if (err)
 669                                return err;
 670
 671                        nla_memcpy(&mod->csum.crc8, tb[CGW_CS_CRC8],
 672                                   CGW_CS_CRC8_LEN);
 673
 674                        /*
 675                         * select dedicated processing function to reduce
 676                         * runtime operations in receive hot path.
 677                         */
 678                        if (c->from_idx < 0 || c->to_idx < 0 ||
 679                            c->result_idx < 0)
 680                                mod->csumfunc.crc8 = cgw_csum_crc8_rel;
 681                        else if (c->from_idx <= c->to_idx)
 682                                mod->csumfunc.crc8 = cgw_csum_crc8_pos;
 683                        else
 684                                mod->csumfunc.crc8 = cgw_csum_crc8_neg;
 685                }
 686
 687                if (tb[CGW_CS_XOR]) {
 688                        struct cgw_csum_xor *c = nla_data(tb[CGW_CS_XOR]);
 689
 690                        err = cgw_chk_csum_parms(c->from_idx, c->to_idx,
 691                                                 c->result_idx);
 692                        if (err)
 693                                return err;
 694
 695                        nla_memcpy(&mod->csum.xor, tb[CGW_CS_XOR],
 696                                   CGW_CS_XOR_LEN);
 697
 698                        /*
 699                         * select dedicated processing function to reduce
 700                         * runtime operations in receive hot path.
 701                         */
 702                        if (c->from_idx < 0 || c->to_idx < 0 ||
 703                            c->result_idx < 0)
 704                                mod->csumfunc.xor = cgw_csum_xor_rel;
 705                        else if (c->from_idx <= c->to_idx)
 706                                mod->csumfunc.xor = cgw_csum_xor_pos;
 707                        else
 708                                mod->csumfunc.xor = cgw_csum_xor_neg;
 709                }
 710        }
 711
 712        if (gwtype == CGW_TYPE_CAN_CAN) {
 713
 714                /* check CGW_TYPE_CAN_CAN specific attributes */
 715
 716                struct can_can_gw *ccgw = (struct can_can_gw *)gwtypeattr;
 717                memset(ccgw, 0, sizeof(*ccgw));
 718
 719                /* check for can_filter in attributes */
 720                if (tb[CGW_FILTER])
 721                        nla_memcpy(&ccgw->filter, tb[CGW_FILTER],
 722                                   sizeof(struct can_filter));
 723
 724                err = -ENODEV;
 725
 726                /* specifying two interfaces is mandatory */
 727                if (!tb[CGW_SRC_IF] || !tb[CGW_DST_IF])
 728                        return err;
 729
 730                ccgw->src_idx = nla_get_u32(tb[CGW_SRC_IF]);
 731                ccgw->dst_idx = nla_get_u32(tb[CGW_DST_IF]);
 732
 733                /* both indices set to 0 for flushing all routing entries */
 734                if (!ccgw->src_idx && !ccgw->dst_idx)
 735                        return 0;
 736
 737                /* only one index set to 0 is an error */
 738                if (!ccgw->src_idx || !ccgw->dst_idx)
 739                        return err;
 740        }
 741
 742        /* add the checks for other gwtypes here */
 743
 744        return 0;
 745}
 746
 747static int cgw_create_job(struct sk_buff *skb,  struct nlmsghdr *nlh,
 748                          void *arg)
 749{
 750        struct rtcanmsg *r;
 751        struct cgw_job *gwj;
 752        int err = 0;
 753
 754        if (nlmsg_len(nlh) < sizeof(*r))
 755                return -EINVAL;
 756
 757        r = nlmsg_data(nlh);
 758        if (r->can_family != AF_CAN)
 759                return -EPFNOSUPPORT;
 760
 761        /* so far we only support CAN -> CAN routings */
 762        if (r->gwtype != CGW_TYPE_CAN_CAN)
 763                return -EINVAL;
 764
 765        gwj = kmem_cache_alloc(cgw_cache, GFP_KERNEL);
 766        if (!gwj)
 767                return -ENOMEM;
 768
 769        gwj->handled_frames = 0;
 770        gwj->dropped_frames = 0;
 771        gwj->flags = r->flags;
 772        gwj->gwtype = r->gwtype;
 773
 774        err = cgw_parse_attr(nlh, &gwj->mod, CGW_TYPE_CAN_CAN, &gwj->ccgw);
 775        if (err < 0)
 776                goto out;
 777
 778        err = -ENODEV;
 779
 780        /* ifindex == 0 is not allowed for job creation */
 781        if (!gwj->ccgw.src_idx || !gwj->ccgw.dst_idx)
 782                goto out;
 783
 784        gwj->src.dev = dev_get_by_index(&init_net, gwj->ccgw.src_idx);
 785
 786        if (!gwj->src.dev)
 787                goto out;
 788
 789        /* check for CAN netdev not using header_ops - see gw_rcv() */
 790        if (gwj->src.dev->type != ARPHRD_CAN || gwj->src.dev->header_ops)
 791                goto put_src_out;
 792
 793        gwj->dst.dev = dev_get_by_index(&init_net, gwj->ccgw.dst_idx);
 794
 795        if (!gwj->dst.dev)
 796                goto put_src_out;
 797
 798        /* check for CAN netdev not using header_ops - see gw_rcv() */
 799        if (gwj->dst.dev->type != ARPHRD_CAN || gwj->dst.dev->header_ops)
 800                goto put_src_dst_out;
 801
 802        ASSERT_RTNL();
 803
 804        err = cgw_register_filter(gwj);
 805        if (!err)
 806                hlist_add_head_rcu(&gwj->list, &cgw_list);
 807
 808put_src_dst_out:
 809        dev_put(gwj->dst.dev);
 810put_src_out:
 811        dev_put(gwj->src.dev);
 812out:
 813        if (err)
 814                kmem_cache_free(cgw_cache, gwj);
 815
 816        return err;
 817}
 818
 819static void cgw_remove_all_jobs(void)
 820{
 821        struct cgw_job *gwj = NULL;
 822        struct hlist_node *n, *nx;
 823
 824        ASSERT_RTNL();
 825
 826        hlist_for_each_entry_safe(gwj, n, nx, &cgw_list, list) {
 827                hlist_del(&gwj->list);
 828                cgw_unregister_filter(gwj);
 829                kfree(gwj);
 830        }
 831}
 832
 833static int cgw_remove_job(struct sk_buff *skb,  struct nlmsghdr *nlh, void *arg)
 834{
 835        struct cgw_job *gwj = NULL;
 836        struct hlist_node *n, *nx;
 837        struct rtcanmsg *r;
 838        struct cf_mod mod;
 839        struct can_can_gw ccgw;
 840        int err = 0;
 841
 842        if (nlmsg_len(nlh) < sizeof(*r))
 843                return -EINVAL;
 844
 845        r = nlmsg_data(nlh);
 846        if (r->can_family != AF_CAN)
 847                return -EPFNOSUPPORT;
 848
 849        /* so far we only support CAN -> CAN routings */
 850        if (r->gwtype != CGW_TYPE_CAN_CAN)
 851                return -EINVAL;
 852
 853        err = cgw_parse_attr(nlh, &mod, CGW_TYPE_CAN_CAN, &ccgw);
 854        if (err < 0)
 855                return err;
 856
 857        /* two interface indices both set to 0 => remove all entries */
 858        if (!ccgw.src_idx && !ccgw.dst_idx) {
 859                cgw_remove_all_jobs();
 860                return 0;
 861        }
 862
 863        err = -EINVAL;
 864
 865        ASSERT_RTNL();
 866
 867        /* remove only the first matching entry */
 868        hlist_for_each_entry_safe(gwj, n, nx, &cgw_list, list) {
 869
 870                if (gwj->flags != r->flags)
 871                        continue;
 872
 873                if (memcmp(&gwj->mod, &mod, sizeof(mod)))
 874                        continue;
 875
 876                /* if (r->gwtype == CGW_TYPE_CAN_CAN) - is made sure here */
 877                if (memcmp(&gwj->ccgw, &ccgw, sizeof(ccgw)))
 878                        continue;
 879
 880                hlist_del(&gwj->list);
 881                cgw_unregister_filter(gwj);
 882                kfree(gwj);
 883                err = 0;
 884                break;
 885        }
 886
 887        return err;
 888}
 889
 890static __init int cgw_module_init(void)
 891{
 892        printk(banner);
 893
 894        cgw_cache = kmem_cache_create("can_gw", sizeof(struct cgw_job),
 895                                      0, 0, NULL);
 896
 897        if (!cgw_cache)
 898                return -ENOMEM;
 899
 900        /* set notifier */
 901        notifier.notifier_call = cgw_notifier;
 902        register_netdevice_notifier(&notifier);
 903
 904        if (__rtnl_register(PF_CAN, RTM_GETROUTE, NULL, cgw_dump_jobs, NULL)) {
 905                unregister_netdevice_notifier(&notifier);
 906                kmem_cache_destroy(cgw_cache);
 907                return -ENOBUFS;
 908        }
 909
 910        /* Only the first call to __rtnl_register can fail */
 911        __rtnl_register(PF_CAN, RTM_NEWROUTE, cgw_create_job, NULL, NULL);
 912        __rtnl_register(PF_CAN, RTM_DELROUTE, cgw_remove_job, NULL, NULL);
 913
 914        return 0;
 915}
 916
 917static __exit void cgw_module_exit(void)
 918{
 919        rtnl_unregister_all(PF_CAN);
 920
 921        unregister_netdevice_notifier(&notifier);
 922
 923        rtnl_lock();
 924        cgw_remove_all_jobs();
 925        rtnl_unlock();
 926
 927        rcu_barrier(); /* Wait for completion of call_rcu()'s */
 928
 929        kmem_cache_destroy(cgw_cache);
 930}
 931
 932module_init(cgw_module_init);
 933module_exit(cgw_module_exit);
 934
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.