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 __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);
 958