Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

56
net/can/Kconfig Normal file
View file

@ -0,0 +1,56 @@
#
# Controller Area Network (CAN) network layer core configuration
#
menuconfig CAN
depends on NET
tristate "CAN bus subsystem support"
---help---
Controller Area Network (CAN) is a slow (up to 1Mbit/s) serial
communications protocol which was developed by Bosch in
1991, mainly for automotive, but now widely used in marine
(NMEA2000), industrial, and medical applications.
More information on the CAN network protocol family PF_CAN
is contained in <Documentation/networking/can.txt>.
If you want CAN support you should say Y here and also to the
specific driver for your controller(s) below.
if CAN
config CAN_RAW
tristate "Raw CAN Protocol (raw access with CAN-ID filtering)"
default y
---help---
The raw CAN protocol option offers access to the CAN bus via
the BSD socket API. You probably want to use the raw socket in
most cases where no higher level protocol is being used. The raw
socket has several filter options e.g. ID masking / error frames.
To receive/send raw CAN messages, use AF_CAN with protocol CAN_RAW.
config CAN_BCM
tristate "Broadcast Manager CAN Protocol (with content filtering)"
default y
---help---
The Broadcast Manager offers content filtering, timeout monitoring,
sending of RTR frames, and cyclic CAN messages without permanent user
interaction. The BCM can be 'programmed' via the BSD socket API and
informs you on demand e.g. only on content updates / timeouts.
You probably want to use the bcm socket in most cases where cyclic
CAN messages are used on the bus (e.g. in automotive environments).
To use the Broadcast Manager, use AF_CAN with protocol CAN_BCM.
config CAN_GW
tristate "CAN Gateway/Router (with netlink configuration)"
default y
---help---
The CAN Gateway/Router is used to route (and modify) CAN frames.
It is based on the PF_CAN core infrastructure for msg filtering and
msg sending and can optionally modify routed CAN frames on the fly.
CAN frames can be routed between CAN network interfaces (one hop).
They can be modified with AND/OR/XOR/SET operations as configured
by the netlink configuration interface known e.g. from iptables.
source "drivers/net/can/Kconfig"
endif

15
net/can/Makefile Normal file
View file

@ -0,0 +1,15 @@
#
# Makefile for the Linux Controller Area Network core.
#
obj-$(CONFIG_CAN) += can.o
can-y := af_can.o proc.o
obj-$(CONFIG_CAN_RAW) += can-raw.o
can-raw-y := raw.o
obj-$(CONFIG_CAN_BCM) += can-bcm.o
can-bcm-y := bcm.o
obj-$(CONFIG_CAN_GW) += can-gw.o
can-gw-y := gw.o

964
net/can/af_can.c Normal file
View file

@ -0,0 +1,964 @@
/*
* af_can.c - Protocol family CAN core module
* (used by different CAN protocol modules)
*
* Copyright (c) 2002-2007 Volkswagen Group Electronic Research
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Volkswagen nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* Alternatively, provided that this notice is retained in full, this
* software may be distributed under the terms of the GNU General
* Public License ("GPL") version 2, in which case the provisions of the
* GPL apply INSTEAD OF those given above.
*
* The provided data structures and external interfaces from this code
* are not restricted to be used by modules with a GPL compatible license.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
*/
#include <linux/module.h>
#include <linux/stddef.h>
#include <linux/init.h>
#include <linux/kmod.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/rcupdate.h>
#include <linux/uaccess.h>
#include <linux/net.h>
#include <linux/netdevice.h>
#include <linux/socket.h>
#include <linux/if_ether.h>
#include <linux/if_arp.h>
#include <linux/skbuff.h>
#include <linux/can.h>
#include <linux/can/core.h>
#include <linux/can/skb.h>
#include <linux/ratelimit.h>
#include <net/net_namespace.h>
#include <net/sock.h>
#include "af_can.h"
static __initconst const char banner[] = KERN_INFO
"can: controller area network core (" CAN_VERSION_STRING ")\n";
MODULE_DESCRIPTION("Controller Area Network PF_CAN core");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Urs Thuermann <urs.thuermann@volkswagen.de>, "
"Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
MODULE_ALIAS_NETPROTO(PF_CAN);
static int stats_timer __read_mostly = 1;
module_param(stats_timer, int, S_IRUGO);
MODULE_PARM_DESC(stats_timer, "enable timer for statistics (default:on)");
/* receive filters subscribed for 'all' CAN devices */
struct dev_rcv_lists can_rx_alldev_list;
static DEFINE_SPINLOCK(can_rcvlists_lock);
static struct kmem_cache *rcv_cache __read_mostly;
/* table of registered CAN protocols */
static const struct can_proto *proto_tab[CAN_NPROTO] __read_mostly;
static DEFINE_MUTEX(proto_tab_lock);
struct timer_list can_stattimer; /* timer for statistics update */
struct s_stats can_stats; /* packet statistics */
struct s_pstats can_pstats; /* receive list statistics */
/*
* af_can socket functions
*/
int can_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
struct sock *sk = sock->sk;
switch (cmd) {
case SIOCGSTAMP:
return sock_get_timestamp(sk, (struct timeval __user *)arg);
default:
return -ENOIOCTLCMD;
}
}
EXPORT_SYMBOL(can_ioctl);
static void can_sock_destruct(struct sock *sk)
{
skb_queue_purge(&sk->sk_receive_queue);
}
static const struct can_proto *can_get_proto(int protocol)
{
const struct can_proto *cp;
rcu_read_lock();
cp = rcu_dereference(proto_tab[protocol]);
if (cp && !try_module_get(cp->prot->owner))
cp = NULL;
rcu_read_unlock();
return cp;
}
static inline void can_put_proto(const struct can_proto *cp)
{
module_put(cp->prot->owner);
}
static int can_create(struct net *net, struct socket *sock, int protocol,
int kern)
{
struct sock *sk;
const struct can_proto *cp;
int err = 0;
sock->state = SS_UNCONNECTED;
if (protocol < 0 || protocol >= CAN_NPROTO)
return -EINVAL;
if (!net_eq(net, &init_net))
return -EAFNOSUPPORT;
cp = can_get_proto(protocol);
#ifdef CONFIG_MODULES
if (!cp) {
/* try to load protocol module if kernel is modular */
err = request_module("can-proto-%d", protocol);
/*
* In case of error we only print a message but don't
* return the error code immediately. Below we will
* return -EPROTONOSUPPORT
*/
if (err)
printk_ratelimited(KERN_ERR "can: request_module "
"(can-proto-%d) failed.\n", protocol);
cp = can_get_proto(protocol);
}
#endif
/* check for available protocol and correct usage */
if (!cp)
return -EPROTONOSUPPORT;
if (cp->type != sock->type) {
err = -EPROTOTYPE;
goto errout;
}
sock->ops = cp->ops;
sk = sk_alloc(net, PF_CAN, GFP_KERNEL, cp->prot);
if (!sk) {
err = -ENOMEM;
goto errout;
}
sock_init_data(sock, sk);
sk->sk_destruct = can_sock_destruct;
if (sk->sk_prot->init)
err = sk->sk_prot->init(sk);
if (err) {
/* release sk on errors */
sock_orphan(sk);
sock_put(sk);
}
errout:
can_put_proto(cp);
return err;
}
/*
* af_can tx path
*/
/**
* can_send - transmit a CAN frame (optional with local loopback)
* @skb: pointer to socket buffer with CAN frame in data section
* @loop: loopback for listeners on local CAN sockets (recommended default!)
*
* Due to the loopback this routine must not be called from hardirq context.
*
* Return:
* 0 on success
* -ENETDOWN when the selected interface is down
* -ENOBUFS on full driver queue (see net_xmit_errno())
* -ENOMEM when local loopback failed at calling skb_clone()
* -EPERM when trying to send on a non-CAN interface
* -EMSGSIZE CAN frame size is bigger than CAN interface MTU
* -EINVAL when the skb->data does not contain a valid CAN frame
*/
int can_send(struct sk_buff *skb, int loop)
{
struct sk_buff *newskb = NULL;
struct canfd_frame *cfd = (struct canfd_frame *)skb->data;
int err = -EINVAL;
if (skb->len == CAN_MTU) {
skb->protocol = htons(ETH_P_CAN);
if (unlikely(cfd->len > CAN_MAX_DLEN))
goto inval_skb;
} else if (skb->len == CANFD_MTU) {
skb->protocol = htons(ETH_P_CANFD);
if (unlikely(cfd->len > CANFD_MAX_DLEN))
goto inval_skb;
} else
goto inval_skb;
/*
* Make sure the CAN frame can pass the selected CAN netdevice.
* As structs can_frame and canfd_frame are similar, we can provide
* CAN FD frames to legacy CAN drivers as long as the length is <= 8
*/
if (unlikely(skb->len > skb->dev->mtu && cfd->len > CAN_MAX_DLEN)) {
err = -EMSGSIZE;
goto inval_skb;
}
if (unlikely(skb->dev->type != ARPHRD_CAN)) {
err = -EPERM;
goto inval_skb;
}
if (unlikely(!(skb->dev->flags & IFF_UP))) {
err = -ENETDOWN;
goto inval_skb;
}
skb->ip_summed = CHECKSUM_UNNECESSARY;
skb_reset_mac_header(skb);
skb_reset_network_header(skb);
skb_reset_transport_header(skb);
if (loop) {
/* local loopback of sent CAN frames */
/* indication for the CAN driver: do loopback */
skb->pkt_type = PACKET_LOOPBACK;
/*
* The reference to the originating sock may be required
* by the receiving socket to check whether the frame is
* its own. Example: can_raw sockopt CAN_RAW_RECV_OWN_MSGS
* Therefore we have to ensure that skb->sk remains the
* reference to the originating sock by restoring skb->sk
* after each skb_clone() or skb_orphan() usage.
*/
if (!(skb->dev->flags & IFF_ECHO)) {
/*
* If the interface is not capable to do loopback
* itself, we do it here.
*/
newskb = skb_clone(skb, GFP_ATOMIC);
if (!newskb) {
kfree_skb(skb);
return -ENOMEM;
}
can_skb_set_owner(newskb, skb->sk);
newskb->ip_summed = CHECKSUM_UNNECESSARY;
newskb->pkt_type = PACKET_BROADCAST;
}
} else {
/* indication for the CAN driver: no loopback required */
skb->pkt_type = PACKET_HOST;
}
/* send to netdevice */
err = dev_queue_xmit(skb);
if (err > 0)
err = net_xmit_errno(err);
if (err) {
kfree_skb(newskb);
return err;
}
if (newskb)
netif_rx_ni(newskb);
/* update statistics */
can_stats.tx_frames++;
can_stats.tx_frames_delta++;
return 0;
inval_skb:
kfree_skb(skb);
return err;
}
EXPORT_SYMBOL(can_send);
/*
* af_can rx path
*/
static struct dev_rcv_lists *find_dev_rcv_lists(struct net_device *dev)
{
if (!dev)
return &can_rx_alldev_list;
else
return (struct dev_rcv_lists *)dev->ml_priv;
}
/**
* effhash - hash function for 29 bit CAN identifier reduction
* @can_id: 29 bit CAN identifier
*
* Description:
* To reduce the linear traversal in one linked list of _single_ EFF CAN
* frame subscriptions the 29 bit identifier is mapped to 10 bits.
* (see CAN_EFF_RCV_HASH_BITS definition)
*
* Return:
* Hash value from 0x000 - 0x3FF ( enforced by CAN_EFF_RCV_HASH_BITS mask )
*/
static unsigned int effhash(canid_t can_id)
{
unsigned int hash;
hash = can_id;
hash ^= can_id >> CAN_EFF_RCV_HASH_BITS;
hash ^= can_id >> (2 * CAN_EFF_RCV_HASH_BITS);
return hash & ((1 << CAN_EFF_RCV_HASH_BITS) - 1);
}
/**
* find_rcv_list - determine optimal filterlist inside device filter struct
* @can_id: pointer to CAN identifier of a given can_filter
* @mask: pointer to CAN mask of a given can_filter
* @d: pointer to the device filter struct
*
* Description:
* Returns the optimal filterlist to reduce the filter handling in the
* receive path. This function is called by service functions that need
* to register or unregister a can_filter in the filter lists.
*
* A filter matches in general, when
*
* <received_can_id> & mask == can_id & mask
*
* so every bit set in the mask (even CAN_EFF_FLAG, CAN_RTR_FLAG) describe
* relevant bits for the filter.
*
* The filter can be inverted (CAN_INV_FILTER bit set in can_id) or it can
* filter for error messages (CAN_ERR_FLAG bit set in mask). For error msg
* frames there is a special filterlist and a special rx path filter handling.
*
* Return:
* Pointer to optimal filterlist for the given can_id/mask pair.
* Constistency checked mask.
* Reduced can_id to have a preprocessed filter compare value.
*/
static struct hlist_head *find_rcv_list(canid_t *can_id, canid_t *mask,
struct dev_rcv_lists *d)
{
canid_t inv = *can_id & CAN_INV_FILTER; /* save flag before masking */
/* filter for error message frames in extra filterlist */
if (*mask & CAN_ERR_FLAG) {
/* clear CAN_ERR_FLAG in filter entry */
*mask &= CAN_ERR_MASK;
return &d->rx[RX_ERR];
}
/* with cleared CAN_ERR_FLAG we have a simple mask/value filterpair */
#define CAN_EFF_RTR_FLAGS (CAN_EFF_FLAG | CAN_RTR_FLAG)
/* ensure valid values in can_mask for 'SFF only' frame filtering */
if ((*mask & CAN_EFF_FLAG) && !(*can_id & CAN_EFF_FLAG))
*mask &= (CAN_SFF_MASK | CAN_EFF_RTR_FLAGS);
/* reduce condition testing at receive time */
*can_id &= *mask;
/* inverse can_id/can_mask filter */
if (inv)
return &d->rx[RX_INV];
/* mask == 0 => no condition testing at receive time */
if (!(*mask))
return &d->rx[RX_ALL];
/* extra filterlists for the subscription of a single non-RTR can_id */
if (((*mask & CAN_EFF_RTR_FLAGS) == CAN_EFF_RTR_FLAGS) &&
!(*can_id & CAN_RTR_FLAG)) {
if (*can_id & CAN_EFF_FLAG) {
if (*mask == (CAN_EFF_MASK | CAN_EFF_RTR_FLAGS))
return &d->rx_eff[effhash(*can_id)];
} else {
if (*mask == (CAN_SFF_MASK | CAN_EFF_RTR_FLAGS))
return &d->rx_sff[*can_id];
}
}
/* default: filter via can_id/can_mask */
return &d->rx[RX_FIL];
}
/**
* can_rx_register - subscribe CAN frames from a specific interface
* @dev: pointer to netdevice (NULL => subcribe from 'all' CAN devices list)
* @can_id: CAN identifier (see description)
* @mask: CAN mask (see description)
* @func: callback function on filter match
* @data: returned parameter for callback function
* @ident: string for calling module identification
*
* Description:
* Invokes the callback function with the received sk_buff and the given
* parameter 'data' on a matching receive filter. A filter matches, when
*
* <received_can_id> & mask == can_id & mask
*
* The filter can be inverted (CAN_INV_FILTER bit set in can_id) or it can
* filter for error message frames (CAN_ERR_FLAG bit set in mask).
*
* The provided pointer to the sk_buff is guaranteed to be valid as long as
* the callback function is running. The callback function must *not* free
* the given sk_buff while processing it's task. When the given sk_buff is
* needed after the end of the callback function it must be cloned inside
* the callback function with skb_clone().
*
* Return:
* 0 on success
* -ENOMEM on missing cache mem to create subscription entry
* -ENODEV unknown device
*/
int can_rx_register(struct net_device *dev, canid_t can_id, canid_t mask,
void (*func)(struct sk_buff *, void *), void *data,
char *ident)
{
struct receiver *r;
struct hlist_head *rl;
struct dev_rcv_lists *d;
int err = 0;
/* insert new receiver (dev,canid,mask) -> (func,data) */
if (dev && dev->type != ARPHRD_CAN)
return -ENODEV;
r = kmem_cache_alloc(rcv_cache, GFP_KERNEL);
if (!r)
return -ENOMEM;
spin_lock(&can_rcvlists_lock);
d = find_dev_rcv_lists(dev);
if (d) {
rl = find_rcv_list(&can_id, &mask, d);
r->can_id = can_id;
r->mask = mask;
r->matches = 0;
r->func = func;
r->data = data;
r->ident = ident;
hlist_add_head_rcu(&r->list, rl);
d->entries++;
can_pstats.rcv_entries++;
if (can_pstats.rcv_entries_max < can_pstats.rcv_entries)
can_pstats.rcv_entries_max = can_pstats.rcv_entries;
} else {
kmem_cache_free(rcv_cache, r);
err = -ENODEV;
}
spin_unlock(&can_rcvlists_lock);
return err;
}
EXPORT_SYMBOL(can_rx_register);
/*
* can_rx_delete_receiver - rcu callback for single receiver entry removal
*/
static void can_rx_delete_receiver(struct rcu_head *rp)
{
struct receiver *r = container_of(rp, struct receiver, rcu);
kmem_cache_free(rcv_cache, r);
}
/**
* can_rx_unregister - unsubscribe CAN frames from a specific interface
* @dev: pointer to netdevice (NULL => unsubcribe from 'all' CAN devices list)
* @can_id: CAN identifier
* @mask: CAN mask
* @func: callback function on filter match
* @data: returned parameter for callback function
*
* Description:
* Removes subscription entry depending on given (subscription) values.
*/
void can_rx_unregister(struct net_device *dev, canid_t can_id, canid_t mask,
void (*func)(struct sk_buff *, void *), void *data)
{
struct receiver *r = NULL;
struct hlist_head *rl;
struct dev_rcv_lists *d;
if (dev && dev->type != ARPHRD_CAN)
return;
spin_lock(&can_rcvlists_lock);
d = find_dev_rcv_lists(dev);
if (!d) {
pr_err("BUG: receive list not found for "
"dev %s, id %03X, mask %03X\n",
DNAME(dev), can_id, mask);
goto out;
}
rl = find_rcv_list(&can_id, &mask, d);
/*
* Search the receiver list for the item to delete. This should
* exist, since no receiver may be unregistered that hasn't
* been registered before.
*/
hlist_for_each_entry_rcu(r, rl, list) {
if (r->can_id == can_id && r->mask == mask &&
r->func == func && r->data == data)
break;
}
/*
* Check for bugs in CAN protocol implementations using af_can.c:
* 'r' will be NULL if no matching list item was found for removal.
*/
if (!r) {
WARN(1, "BUG: receive list entry not found for dev %s, "
"id %03X, mask %03X\n", DNAME(dev), can_id, mask);
goto out;
}
hlist_del_rcu(&r->list);
d->entries--;
if (can_pstats.rcv_entries > 0)
can_pstats.rcv_entries--;
/* remove device structure requested by NETDEV_UNREGISTER */
if (d->remove_on_zero_entries && !d->entries) {
kfree(d);
dev->ml_priv = NULL;
}
out:
spin_unlock(&can_rcvlists_lock);
/* schedule the receiver item for deletion */
if (r)
call_rcu(&r->rcu, can_rx_delete_receiver);
}
EXPORT_SYMBOL(can_rx_unregister);
static inline void deliver(struct sk_buff *skb, struct receiver *r)
{
r->func(skb, r->data);
r->matches++;
}
static int can_rcv_filter(struct dev_rcv_lists *d, struct sk_buff *skb)
{
struct receiver *r;
int matches = 0;
struct can_frame *cf = (struct can_frame *)skb->data;
canid_t can_id = cf->can_id;
if (d->entries == 0)
return 0;
if (can_id & CAN_ERR_FLAG) {
/* check for error message frame entries only */
hlist_for_each_entry_rcu(r, &d->rx[RX_ERR], list) {
if (can_id & r->mask) {
deliver(skb, r);
matches++;
}
}
return matches;
}
/* check for unfiltered entries */
hlist_for_each_entry_rcu(r, &d->rx[RX_ALL], list) {
deliver(skb, r);
matches++;
}
/* check for can_id/mask entries */
hlist_for_each_entry_rcu(r, &d->rx[RX_FIL], list) {
if ((can_id & r->mask) == r->can_id) {
deliver(skb, r);
matches++;
}
}
/* check for inverted can_id/mask entries */
hlist_for_each_entry_rcu(r, &d->rx[RX_INV], list) {
if ((can_id & r->mask) != r->can_id) {
deliver(skb, r);
matches++;
}
}
/* check filterlists for single non-RTR can_ids */
if (can_id & CAN_RTR_FLAG)
return matches;
if (can_id & CAN_EFF_FLAG) {
hlist_for_each_entry_rcu(r, &d->rx_eff[effhash(can_id)], list) {
if (r->can_id == can_id) {
deliver(skb, r);
matches++;
}
}
} else {
can_id &= CAN_SFF_MASK;
hlist_for_each_entry_rcu(r, &d->rx_sff[can_id], list) {
deliver(skb, r);
matches++;
}
}
return matches;
}
static void can_receive(struct sk_buff *skb, struct net_device *dev)
{
struct dev_rcv_lists *d;
int matches;
/* update statistics */
can_stats.rx_frames++;
can_stats.rx_frames_delta++;
rcu_read_lock();
/* deliver the packet to sockets listening on all devices */
matches = can_rcv_filter(&can_rx_alldev_list, skb);
/* find receive list for this device */
d = find_dev_rcv_lists(dev);
if (d)
matches += can_rcv_filter(d, skb);
rcu_read_unlock();
/* consume the skbuff allocated by the netdevice driver */
consume_skb(skb);
if (matches > 0) {
can_stats.matches++;
can_stats.matches_delta++;
}
}
static int can_rcv(struct sk_buff *skb, struct net_device *dev,
struct packet_type *pt, struct net_device *orig_dev)
{
struct canfd_frame *cfd = (struct canfd_frame *)skb->data;
if (unlikely(!net_eq(dev_net(dev), &init_net)))
goto drop;
if (WARN_ONCE(dev->type != ARPHRD_CAN ||
skb->len != CAN_MTU ||
cfd->len > CAN_MAX_DLEN,
"PF_CAN: dropped non conform CAN skbuf: "
"dev type %d, len %d, datalen %d\n",
dev->type, skb->len, cfd->len))
goto drop;
can_receive(skb, dev);
return NET_RX_SUCCESS;
drop:
kfree_skb(skb);
return NET_RX_DROP;
}
static int canfd_rcv(struct sk_buff *skb, struct net_device *dev,
struct packet_type *pt, struct net_device *orig_dev)
{
struct canfd_frame *cfd = (struct canfd_frame *)skb->data;
if (unlikely(!net_eq(dev_net(dev), &init_net)))
goto drop;
if (WARN_ONCE(dev->type != ARPHRD_CAN ||
skb->len != CANFD_MTU ||
cfd->len > CANFD_MAX_DLEN,
"PF_CAN: dropped non conform CAN FD skbuf: "
"dev type %d, len %d, datalen %d\n",
dev->type, skb->len, cfd->len))
goto drop;
can_receive(skb, dev);
return NET_RX_SUCCESS;
drop:
kfree_skb(skb);
return NET_RX_DROP;
}
/*
* af_can protocol functions
*/
/**
* can_proto_register - register CAN transport protocol
* @cp: pointer to CAN protocol structure
*
* Return:
* 0 on success
* -EINVAL invalid (out of range) protocol number
* -EBUSY protocol already in use
* -ENOBUF if proto_register() fails
*/
int can_proto_register(const struct can_proto *cp)
{
int proto = cp->protocol;
int err = 0;
if (proto < 0 || proto >= CAN_NPROTO) {
pr_err("can: protocol number %d out of range\n", proto);
return -EINVAL;
}
err = proto_register(cp->prot, 0);
if (err < 0)
return err;
mutex_lock(&proto_tab_lock);
if (proto_tab[proto]) {
pr_err("can: protocol %d already registered\n", proto);
err = -EBUSY;
} else
RCU_INIT_POINTER(proto_tab[proto], cp);
mutex_unlock(&proto_tab_lock);
if (err < 0)
proto_unregister(cp->prot);
return err;
}
EXPORT_SYMBOL(can_proto_register);
/**
* can_proto_unregister - unregister CAN transport protocol
* @cp: pointer to CAN protocol structure
*/
void can_proto_unregister(const struct can_proto *cp)
{
int proto = cp->protocol;
mutex_lock(&proto_tab_lock);
BUG_ON(proto_tab[proto] != cp);
RCU_INIT_POINTER(proto_tab[proto], NULL);
mutex_unlock(&proto_tab_lock);
synchronize_rcu();
proto_unregister(cp->prot);
}
EXPORT_SYMBOL(can_proto_unregister);
/*
* af_can notifier to create/remove CAN netdevice specific structs
*/
static int can_notifier(struct notifier_block *nb, unsigned long msg,
void *ptr)
{
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
struct dev_rcv_lists *d;
if (!net_eq(dev_net(dev), &init_net))
return NOTIFY_DONE;
if (dev->type != ARPHRD_CAN)
return NOTIFY_DONE;
switch (msg) {
case NETDEV_REGISTER:
/* create new dev_rcv_lists for this device */
d = kzalloc(sizeof(*d), GFP_KERNEL);
if (!d)
return NOTIFY_DONE;
BUG_ON(dev->ml_priv);
dev->ml_priv = d;
break;
case NETDEV_UNREGISTER:
spin_lock(&can_rcvlists_lock);
d = dev->ml_priv;
if (d) {
if (d->entries)
d->remove_on_zero_entries = 1;
else {
kfree(d);
dev->ml_priv = NULL;
}
} else
pr_err("can: notifier: receive list not found for dev "
"%s\n", dev->name);
spin_unlock(&can_rcvlists_lock);
break;
}
return NOTIFY_DONE;
}
/*
* af_can module init/exit functions
*/
static struct packet_type can_packet __read_mostly = {
.type = cpu_to_be16(ETH_P_CAN),
.func = can_rcv,
};
static struct packet_type canfd_packet __read_mostly = {
.type = cpu_to_be16(ETH_P_CANFD),
.func = canfd_rcv,
};
static const struct net_proto_family can_family_ops = {
.family = PF_CAN,
.create = can_create,
.owner = THIS_MODULE,
};
/* notifier block for netdevice event */
static struct notifier_block can_netdev_notifier __read_mostly = {
.notifier_call = can_notifier,
};
static __init int can_init(void)
{
/* check for correct padding to be able to use the structs similarly */
BUILD_BUG_ON(offsetof(struct can_frame, can_dlc) !=
offsetof(struct canfd_frame, len) ||
offsetof(struct can_frame, data) !=
offsetof(struct canfd_frame, data));
printk(banner);
memset(&can_rx_alldev_list, 0, sizeof(can_rx_alldev_list));
rcv_cache = kmem_cache_create("can_receiver", sizeof(struct receiver),
0, 0, NULL);
if (!rcv_cache)
return -ENOMEM;
if (stats_timer) {
/* the statistics are updated every second (timer triggered) */
setup_timer(&can_stattimer, can_stat_update, 0);
mod_timer(&can_stattimer, round_jiffies(jiffies + HZ));
} else
can_stattimer.function = NULL;
can_init_proc();
/* protocol register */
sock_register(&can_family_ops);
register_netdevice_notifier(&can_netdev_notifier);
dev_add_pack(&can_packet);
dev_add_pack(&canfd_packet);
return 0;
}
static __exit void can_exit(void)
{
struct net_device *dev;
if (stats_timer)
del_timer_sync(&can_stattimer);
can_remove_proc();
/* protocol unregister */
dev_remove_pack(&canfd_packet);
dev_remove_pack(&can_packet);
unregister_netdevice_notifier(&can_netdev_notifier);
sock_unregister(PF_CAN);
/* remove created dev_rcv_lists from still registered CAN devices */
rcu_read_lock();
for_each_netdev_rcu(&init_net, dev) {
if (dev->type == ARPHRD_CAN && dev->ml_priv) {
struct dev_rcv_lists *d = dev->ml_priv;
BUG_ON(d->entries);
kfree(d);
dev->ml_priv = NULL;
}
}
rcu_read_unlock();
rcu_barrier(); /* Wait for completion of call_rcu()'s */
kmem_cache_destroy(rcv_cache);
}
module_init(can_init);
module_exit(can_exit);

126
net/can/af_can.h Normal file
View file

@ -0,0 +1,126 @@
/*
* Copyright (c) 2002-2007 Volkswagen Group Electronic Research
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Volkswagen nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* Alternatively, provided that this notice is retained in full, this
* software may be distributed under the terms of the GNU General
* Public License ("GPL") version 2, in which case the provisions of the
* GPL apply INSTEAD OF those given above.
*
* The provided data structures and external interfaces from this code
* are not restricted to be used by modules with a GPL compatible license.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
*/
#ifndef AF_CAN_H
#define AF_CAN_H
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/list.h>
#include <linux/rcupdate.h>
#include <linux/can.h>
/* af_can rx dispatcher structures */
struct receiver {
struct hlist_node list;
struct rcu_head rcu;
canid_t can_id;
canid_t mask;
unsigned long matches;
void (*func)(struct sk_buff *, void *);
void *data;
char *ident;
};
#define CAN_SFF_RCV_ARRAY_SZ (1 << CAN_SFF_ID_BITS)
#define CAN_EFF_RCV_HASH_BITS 10
#define CAN_EFF_RCV_ARRAY_SZ (1 << CAN_EFF_RCV_HASH_BITS)
enum { RX_ERR, RX_ALL, RX_FIL, RX_INV, RX_MAX };
/* per device receive filters linked at dev->ml_priv */
struct dev_rcv_lists {
struct hlist_head rx[RX_MAX];
struct hlist_head rx_sff[CAN_SFF_RCV_ARRAY_SZ];
struct hlist_head rx_eff[CAN_EFF_RCV_ARRAY_SZ];
int remove_on_zero_entries;
int entries;
};
/* statistic structures */
/* can be reset e.g. by can_init_stats() */
struct s_stats {
unsigned long jiffies_init;
unsigned long rx_frames;
unsigned long tx_frames;
unsigned long matches;
unsigned long total_rx_rate;
unsigned long total_tx_rate;
unsigned long total_rx_match_ratio;
unsigned long current_rx_rate;
unsigned long current_tx_rate;
unsigned long current_rx_match_ratio;
unsigned long max_rx_rate;
unsigned long max_tx_rate;
unsigned long max_rx_match_ratio;
unsigned long rx_frames_delta;
unsigned long tx_frames_delta;
unsigned long matches_delta;
};
/* persistent statistics */
struct s_pstats {
unsigned long stats_reset;
unsigned long user_reset;
unsigned long rcv_entries;
unsigned long rcv_entries_max;
};
/* receive filters subscribed for 'all' CAN devices */
extern struct dev_rcv_lists can_rx_alldev_list;
/* function prototypes for the CAN networklayer procfs (proc.c) */
void can_init_proc(void);
void can_remove_proc(void);
void can_stat_update(unsigned long data);
/* structures and variables from af_can.c needed in proc.c for reading */
extern struct timer_list can_stattimer; /* timer for statistics update */
extern struct s_stats can_stats; /* packet statistics */
extern struct s_pstats can_pstats; /* receive list statistics */
extern struct hlist_head can_rx_dev_list; /* rx dispatcher structures */
#endif /* AF_CAN_H */

1640
net/can/bcm.c Normal file

File diff suppressed because it is too large Load diff

997
net/can/gw.c Normal file
View file

@ -0,0 +1,997 @@
/*
* gw.c - CAN frame Gateway/Router/Bridge with netlink interface
*
* Copyright (c) 2011 Volkswagen Group Electronic Research
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Volkswagen nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* Alternatively, provided that this notice is retained in full, this
* software may be distributed under the terms of the GNU General
* Public License ("GPL") version 2, in which case the provisions of the
* GPL apply INSTEAD OF those given above.
*
* The provided data structures and external interfaces from this code
* are not restricted to be used by modules with a GPL compatible license.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/rcupdate.h>
#include <linux/rculist.h>
#include <linux/net.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/skbuff.h>
#include <linux/can.h>
#include <linux/can/core.h>
#include <linux/can/skb.h>
#include <linux/can/gw.h>
#include <net/rtnetlink.h>
#include <net/net_namespace.h>
#include <net/sock.h>
#define CAN_GW_VERSION "20130117"
#define CAN_GW_NAME "can-gw"
MODULE_DESCRIPTION("PF_CAN netlink gateway");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
MODULE_ALIAS(CAN_GW_NAME);
#define CGW_MIN_HOPS 1
#define CGW_MAX_HOPS 6
#define CGW_DEFAULT_HOPS 1
static unsigned int max_hops __read_mostly = CGW_DEFAULT_HOPS;
module_param(max_hops, uint, S_IRUGO);
MODULE_PARM_DESC(max_hops,
"maximum " CAN_GW_NAME " routing hops for CAN frames "
"(valid values: " __stringify(CGW_MIN_HOPS) "-"
__stringify(CGW_MAX_HOPS) " hops, "
"default: " __stringify(CGW_DEFAULT_HOPS) ")");
static HLIST_HEAD(cgw_list);
static struct notifier_block notifier;
static struct kmem_cache *cgw_cache __read_mostly;
/* structure that contains the (on-the-fly) CAN frame modifications */
struct cf_mod {
struct {
struct can_frame and;
struct can_frame or;
struct can_frame xor;
struct can_frame set;
} modframe;
struct {
u8 and;
u8 or;
u8 xor;
u8 set;
} modtype;
void (*modfunc[MAX_MODFUNCTIONS])(struct can_frame *cf,
struct cf_mod *mod);
/* CAN frame checksum calculation after CAN frame modifications */
struct {
struct cgw_csum_xor xor;
struct cgw_csum_crc8 crc8;
} csum;
struct {
void (*xor)(struct can_frame *cf, struct cgw_csum_xor *xor);
void (*crc8)(struct can_frame *cf, struct cgw_csum_crc8 *crc8);
} csumfunc;
};
/*
* So far we just support CAN -> CAN routing and frame modifications.
*
* The internal can_can_gw structure contains data and attributes for
* a CAN -> CAN gateway job.
*/
struct can_can_gw {
struct can_filter filter;
int src_idx;
int dst_idx;
};
/* list entry for CAN gateways jobs */
struct cgw_job {
struct hlist_node list;
struct rcu_head rcu;
u32 handled_frames;
u32 dropped_frames;
u32 deleted_frames;
struct cf_mod mod;
union {
/* CAN frame data source */
struct net_device *dev;
} src;
union {
/* CAN frame data destination */
struct net_device *dev;
} dst;
union {
struct can_can_gw ccgw;
/* tbc */
};
u8 gwtype;
u8 limit_hops;
u16 flags;
};
/* modification functions that are invoked in the hot path in can_can_gw_rcv */
#define MODFUNC(func, op) static void func(struct can_frame *cf, \
struct cf_mod *mod) { op ; }
MODFUNC(mod_and_id, cf->can_id &= mod->modframe.and.can_id)
MODFUNC(mod_and_dlc, cf->can_dlc &= mod->modframe.and.can_dlc)
MODFUNC(mod_and_data, *(u64 *)cf->data &= *(u64 *)mod->modframe.and.data)
MODFUNC(mod_or_id, cf->can_id |= mod->modframe.or.can_id)
MODFUNC(mod_or_dlc, cf->can_dlc |= mod->modframe.or.can_dlc)
MODFUNC(mod_or_data, *(u64 *)cf->data |= *(u64 *)mod->modframe.or.data)
MODFUNC(mod_xor_id, cf->can_id ^= mod->modframe.xor.can_id)
MODFUNC(mod_xor_dlc, cf->can_dlc ^= mod->modframe.xor.can_dlc)
MODFUNC(mod_xor_data, *(u64 *)cf->data ^= *(u64 *)mod->modframe.xor.data)
MODFUNC(mod_set_id, cf->can_id = mod->modframe.set.can_id)
MODFUNC(mod_set_dlc, cf->can_dlc = mod->modframe.set.can_dlc)
MODFUNC(mod_set_data, *(u64 *)cf->data = *(u64 *)mod->modframe.set.data)
static inline void canframecpy(struct can_frame *dst, struct can_frame *src)
{
/*
* Copy the struct members separately to ensure that no uninitialized
* data are copied in the 3 bytes hole of the struct. This is needed
* to make easy compares of the data in the struct cf_mod.
*/
dst->can_id = src->can_id;
dst->can_dlc = src->can_dlc;
*(u64 *)dst->data = *(u64 *)src->data;
}
static int cgw_chk_csum_parms(s8 fr, s8 to, s8 re)
{
/*
* absolute dlc values 0 .. 7 => 0 .. 7, e.g. data [0]
* relative to received dlc -1 .. -8 :
* e.g. for received dlc = 8
* -1 => index = 7 (data[7])
* -3 => index = 5 (data[5])
* -8 => index = 0 (data[0])
*/
if (fr > -9 && fr < 8 &&
to > -9 && to < 8 &&
re > -9 && re < 8)
return 0;
else
return -EINVAL;
}
static inline int calc_idx(int idx, int rx_dlc)
{
if (idx < 0)
return rx_dlc + idx;
else
return idx;
}
static void cgw_csum_xor_rel(struct can_frame *cf, struct cgw_csum_xor *xor)
{
int from = calc_idx(xor->from_idx, cf->can_dlc);
int to = calc_idx(xor->to_idx, cf->can_dlc);
int res = calc_idx(xor->result_idx, cf->can_dlc);
u8 val = xor->init_xor_val;
int i;
if (from < 0 || to < 0 || res < 0)
return;
if (from <= to) {
for (i = from; i <= to; i++)
val ^= cf->data[i];
} else {
for (i = from; i >= to; i--)
val ^= cf->data[i];
}
cf->data[res] = val;
}
static void cgw_csum_xor_pos(struct can_frame *cf, struct cgw_csum_xor *xor)
{
u8 val = xor->init_xor_val;
int i;
for (i = xor->from_idx; i <= xor->to_idx; i++)
val ^= cf->data[i];
cf->data[xor->result_idx] = val;
}
static void cgw_csum_xor_neg(struct can_frame *cf, struct cgw_csum_xor *xor)
{
u8 val = xor->init_xor_val;
int i;
for (i = xor->from_idx; i >= xor->to_idx; i--)
val ^= cf->data[i];
cf->data[xor->result_idx] = val;
}
static void cgw_csum_crc8_rel(struct can_frame *cf, struct cgw_csum_crc8 *crc8)
{
int from = calc_idx(crc8->from_idx, cf->can_dlc);
int to = calc_idx(crc8->to_idx, cf->can_dlc);
int res = calc_idx(crc8->result_idx, cf->can_dlc);
u8 crc = crc8->init_crc_val;
int i;
if (from < 0 || to < 0 || res < 0)
return;
if (from <= to) {
for (i = crc8->from_idx; i <= crc8->to_idx; i++)
crc = crc8->crctab[crc^cf->data[i]];
} else {
for (i = crc8->from_idx; i >= crc8->to_idx; i--)
crc = crc8->crctab[crc^cf->data[i]];
}
switch (crc8->profile) {
case CGW_CRC8PRF_1U8:
crc = crc8->crctab[crc^crc8->profile_data[0]];
break;
case CGW_CRC8PRF_16U8:
crc = crc8->crctab[crc^crc8->profile_data[cf->data[1] & 0xF]];
break;
case CGW_CRC8PRF_SFFID_XOR:
crc = crc8->crctab[crc^(cf->can_id & 0xFF)^
(cf->can_id >> 8 & 0xFF)];
break;
}
cf->data[crc8->result_idx] = crc^crc8->final_xor_val;
}
static void cgw_csum_crc8_pos(struct can_frame *cf, struct cgw_csum_crc8 *crc8)
{
u8 crc = crc8->init_crc_val;
int i;
for (i = crc8->from_idx; i <= crc8->to_idx; i++)
crc = crc8->crctab[crc^cf->data[i]];
switch (crc8->profile) {
case CGW_CRC8PRF_1U8:
crc = crc8->crctab[crc^crc8->profile_data[0]];
break;
case CGW_CRC8PRF_16U8:
crc = crc8->crctab[crc^crc8->profile_data[cf->data[1] & 0xF]];
break;
case CGW_CRC8PRF_SFFID_XOR:
crc = crc8->crctab[crc^(cf->can_id & 0xFF)^
(cf->can_id >> 8 & 0xFF)];
break;
}
cf->data[crc8->result_idx] = crc^crc8->final_xor_val;
}
static void cgw_csum_crc8_neg(struct can_frame *cf, struct cgw_csum_crc8 *crc8)
{
u8 crc = crc8->init_crc_val;
int i;
for (i = crc8->from_idx; i >= crc8->to_idx; i--)
crc = crc8->crctab[crc^cf->data[i]];
switch (crc8->profile) {
case CGW_CRC8PRF_1U8:
crc = crc8->crctab[crc^crc8->profile_data[0]];
break;
case CGW_CRC8PRF_16U8:
crc = crc8->crctab[crc^crc8->profile_data[cf->data[1] & 0xF]];
break;
case CGW_CRC8PRF_SFFID_XOR:
crc = crc8->crctab[crc^(cf->can_id & 0xFF)^
(cf->can_id >> 8 & 0xFF)];
break;
}
cf->data[crc8->result_idx] = crc^crc8->final_xor_val;
}
/* the receive & process & send function */
static void can_can_gw_rcv(struct sk_buff *skb, void *data)
{
struct cgw_job *gwj = (struct cgw_job *)data;
struct can_frame *cf;
struct sk_buff *nskb;
int modidx = 0;
/*
* Do not handle CAN frames routed more than 'max_hops' times.
* In general we should never catch this delimiter which is intended
* to cover a misconfiguration protection (e.g. circular CAN routes).
*
* The Controller Area Network controllers only accept CAN frames with
* correct CRCs - which are not visible in the controller registers.
* According to skbuff.h documentation the csum_start element for IP
* checksums is undefined/unsued when ip_summed == CHECKSUM_UNNECESSARY.
* Only CAN skbs can be processed here which already have this property.
*/
#define cgw_hops(skb) ((skb)->csum_start)
BUG_ON(skb->ip_summed != CHECKSUM_UNNECESSARY);
if (cgw_hops(skb) >= max_hops) {
/* indicate deleted frames due to misconfiguration */
gwj->deleted_frames++;
return;
}
if (!(gwj->dst.dev->flags & IFF_UP)) {
gwj->dropped_frames++;
return;
}
/* is sending the skb back to the incoming interface not allowed? */
if (!(gwj->flags & CGW_FLAGS_CAN_IIF_TX_OK) &&
can_skb_prv(skb)->ifindex == gwj->dst.dev->ifindex)
return;
/*
* clone the given skb, which has not been done in can_rcv()
*
* When there is at least one modification function activated,
* we need to copy the skb as we want to modify skb->data.
*/
if (gwj->mod.modfunc[0])
nskb = skb_copy(skb, GFP_ATOMIC);
else
nskb = skb_clone(skb, GFP_ATOMIC);
if (!nskb) {
gwj->dropped_frames++;
return;
}
/* put the incremented hop counter in the cloned skb */
cgw_hops(nskb) = cgw_hops(skb) + 1;
/* first processing of this CAN frame -> adjust to private hop limit */
if (gwj->limit_hops && cgw_hops(nskb) == 1)
cgw_hops(nskb) = max_hops - gwj->limit_hops + 1;
nskb->dev = gwj->dst.dev;
/* pointer to modifiable CAN frame */
cf = (struct can_frame *)nskb->data;
/* perform preprocessed modification functions if there are any */
while (modidx < MAX_MODFUNCTIONS && gwj->mod.modfunc[modidx])
(*gwj->mod.modfunc[modidx++])(cf, &gwj->mod);
/* check for checksum updates when the CAN frame has been modified */
if (modidx) {
if (gwj->mod.csumfunc.crc8)
(*gwj->mod.csumfunc.crc8)(cf, &gwj->mod.csum.crc8);
if (gwj->mod.csumfunc.xor)
(*gwj->mod.csumfunc.xor)(cf, &gwj->mod.csum.xor);
}
/* clear the skb timestamp if not configured the other way */
if (!(gwj->flags & CGW_FLAGS_CAN_SRC_TSTAMP))
nskb->tstamp.tv64 = 0;
/* send to netdevice */
if (can_send(nskb, gwj->flags & CGW_FLAGS_CAN_ECHO))
gwj->dropped_frames++;
else
gwj->handled_frames++;
}
static inline int cgw_register_filter(struct cgw_job *gwj)
{
return can_rx_register(gwj->src.dev, gwj->ccgw.filter.can_id,
gwj->ccgw.filter.can_mask, can_can_gw_rcv,
gwj, "gw");
}
static inline void cgw_unregister_filter(struct cgw_job *gwj)
{
can_rx_unregister(gwj->src.dev, gwj->ccgw.filter.can_id,
gwj->ccgw.filter.can_mask, can_can_gw_rcv, gwj);
}
static int cgw_notifier(struct notifier_block *nb,
unsigned long msg, void *ptr)
{
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
if (!net_eq(dev_net(dev), &init_net))
return NOTIFY_DONE;
if (dev->type != ARPHRD_CAN)
return NOTIFY_DONE;
if (msg == NETDEV_UNREGISTER) {
struct cgw_job *gwj = NULL;
struct hlist_node *nx;
ASSERT_RTNL();
hlist_for_each_entry_safe(gwj, nx, &cgw_list, list) {
if (gwj->src.dev == dev || gwj->dst.dev == dev) {
hlist_del(&gwj->list);
cgw_unregister_filter(gwj);
kmem_cache_free(cgw_cache, gwj);
}
}
}
return NOTIFY_DONE;
}
static int cgw_put_job(struct sk_buff *skb, struct cgw_job *gwj, int type,
u32 pid, u32 seq, int flags)
{
struct cgw_frame_mod mb;
struct rtcanmsg *rtcan;
struct nlmsghdr *nlh;
nlh = nlmsg_put(skb, pid, seq, type, sizeof(*rtcan), flags);
if (!nlh)
return -EMSGSIZE;
rtcan = nlmsg_data(nlh);
rtcan->can_family = AF_CAN;
rtcan->gwtype = gwj->gwtype;
rtcan->flags = gwj->flags;
/* add statistics if available */
if (gwj->handled_frames) {
if (nla_put_u32(skb, CGW_HANDLED, gwj->handled_frames) < 0)
goto cancel;
}
if (gwj->dropped_frames) {
if (nla_put_u32(skb, CGW_DROPPED, gwj->dropped_frames) < 0)
goto cancel;
}
if (gwj->deleted_frames) {
if (nla_put_u32(skb, CGW_DELETED, gwj->deleted_frames) < 0)
goto cancel;
}
/* check non default settings of attributes */
if (gwj->limit_hops) {
if (nla_put_u8(skb, CGW_LIM_HOPS, gwj->limit_hops) < 0)
goto cancel;
}
if (gwj->mod.modtype.and) {
memcpy(&mb.cf, &gwj->mod.modframe.and, sizeof(mb.cf));
mb.modtype = gwj->mod.modtype.and;
if (nla_put(skb, CGW_MOD_AND, sizeof(mb), &mb) < 0)
goto cancel;
}
if (gwj->mod.modtype.or) {
memcpy(&mb.cf, &gwj->mod.modframe.or, sizeof(mb.cf));
mb.modtype = gwj->mod.modtype.or;
if (nla_put(skb, CGW_MOD_OR, sizeof(mb), &mb) < 0)
goto cancel;
}
if (gwj->mod.modtype.xor) {
memcpy(&mb.cf, &gwj->mod.modframe.xor, sizeof(mb.cf));
mb.modtype = gwj->mod.modtype.xor;
if (nla_put(skb, CGW_MOD_XOR, sizeof(mb), &mb) < 0)
goto cancel;
}
if (gwj->mod.modtype.set) {
memcpy(&mb.cf, &gwj->mod.modframe.set, sizeof(mb.cf));
mb.modtype = gwj->mod.modtype.set;
if (nla_put(skb, CGW_MOD_SET, sizeof(mb), &mb) < 0)
goto cancel;
}
if (gwj->mod.csumfunc.crc8) {
if (nla_put(skb, CGW_CS_CRC8, CGW_CS_CRC8_LEN,
&gwj->mod.csum.crc8) < 0)
goto cancel;
}
if (gwj->mod.csumfunc.xor) {
if (nla_put(skb, CGW_CS_XOR, CGW_CS_XOR_LEN,
&gwj->mod.csum.xor) < 0)
goto cancel;
}
if (gwj->gwtype == CGW_TYPE_CAN_CAN) {
if (gwj->ccgw.filter.can_id || gwj->ccgw.filter.can_mask) {
if (nla_put(skb, CGW_FILTER, sizeof(struct can_filter),
&gwj->ccgw.filter) < 0)
goto cancel;
}
if (nla_put_u32(skb, CGW_SRC_IF, gwj->ccgw.src_idx) < 0)
goto cancel;
if (nla_put_u32(skb, CGW_DST_IF, gwj->ccgw.dst_idx) < 0)
goto cancel;
}
return nlmsg_end(skb, nlh);
cancel:
nlmsg_cancel(skb, nlh);
return -EMSGSIZE;
}
/* Dump information about all CAN gateway jobs, in response to RTM_GETROUTE */
static int cgw_dump_jobs(struct sk_buff *skb, struct netlink_callback *cb)
{
struct cgw_job *gwj = NULL;
int idx = 0;
int s_idx = cb->args[0];
rcu_read_lock();
hlist_for_each_entry_rcu(gwj, &cgw_list, list) {
if (idx < s_idx)
goto cont;
if (cgw_put_job(skb, gwj, RTM_NEWROUTE, NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, NLM_F_MULTI) < 0)
break;
cont:
idx++;
}
rcu_read_unlock();
cb->args[0] = idx;
return skb->len;
}
static const struct nla_policy cgw_policy[CGW_MAX+1] = {
[CGW_MOD_AND] = { .len = sizeof(struct cgw_frame_mod) },
[CGW_MOD_OR] = { .len = sizeof(struct cgw_frame_mod) },
[CGW_MOD_XOR] = { .len = sizeof(struct cgw_frame_mod) },
[CGW_MOD_SET] = { .len = sizeof(struct cgw_frame_mod) },
[CGW_CS_XOR] = { .len = sizeof(struct cgw_csum_xor) },
[CGW_CS_CRC8] = { .len = sizeof(struct cgw_csum_crc8) },
[CGW_SRC_IF] = { .type = NLA_U32 },
[CGW_DST_IF] = { .type = NLA_U32 },
[CGW_FILTER] = { .len = sizeof(struct can_filter) },
[CGW_LIM_HOPS] = { .type = NLA_U8 },
};
/* check for common and gwtype specific attributes */
static int cgw_parse_attr(struct nlmsghdr *nlh, struct cf_mod *mod,
u8 gwtype, void *gwtypeattr, u8 *limhops)
{
struct nlattr *tb[CGW_MAX+1];
struct cgw_frame_mod mb;
int modidx = 0;
int err = 0;
/* initialize modification & checksum data space */
memset(mod, 0, sizeof(*mod));
err = nlmsg_parse(nlh, sizeof(struct rtcanmsg), tb, CGW_MAX,
cgw_policy);
if (err < 0)
return err;
if (tb[CGW_LIM_HOPS]) {
*limhops = nla_get_u8(tb[CGW_LIM_HOPS]);
if (*limhops < 1 || *limhops > max_hops)
return -EINVAL;
}
/* check for AND/OR/XOR/SET modifications */
if (tb[CGW_MOD_AND]) {
nla_memcpy(&mb, tb[CGW_MOD_AND], CGW_MODATTR_LEN);
canframecpy(&mod->modframe.and, &mb.cf);
mod->modtype.and = mb.modtype;
if (mb.modtype & CGW_MOD_ID)
mod->modfunc[modidx++] = mod_and_id;
if (mb.modtype & CGW_MOD_DLC)
mod->modfunc[modidx++] = mod_and_dlc;
if (mb.modtype & CGW_MOD_DATA)
mod->modfunc[modidx++] = mod_and_data;
}
if (tb[CGW_MOD_OR]) {
nla_memcpy(&mb, tb[CGW_MOD_OR], CGW_MODATTR_LEN);
canframecpy(&mod->modframe.or, &mb.cf);
mod->modtype.or = mb.modtype;
if (mb.modtype & CGW_MOD_ID)
mod->modfunc[modidx++] = mod_or_id;
if (mb.modtype & CGW_MOD_DLC)
mod->modfunc[modidx++] = mod_or_dlc;
if (mb.modtype & CGW_MOD_DATA)
mod->modfunc[modidx++] = mod_or_data;
}
if (tb[CGW_MOD_XOR]) {
nla_memcpy(&mb, tb[CGW_MOD_XOR], CGW_MODATTR_LEN);
canframecpy(&mod->modframe.xor, &mb.cf);
mod->modtype.xor = mb.modtype;
if (mb.modtype & CGW_MOD_ID)
mod->modfunc[modidx++] = mod_xor_id;
if (mb.modtype & CGW_MOD_DLC)
mod->modfunc[modidx++] = mod_xor_dlc;
if (mb.modtype & CGW_MOD_DATA)
mod->modfunc[modidx++] = mod_xor_data;
}
if (tb[CGW_MOD_SET]) {
nla_memcpy(&mb, tb[CGW_MOD_SET], CGW_MODATTR_LEN);
canframecpy(&mod->modframe.set, &mb.cf);
mod->modtype.set = mb.modtype;
if (mb.modtype & CGW_MOD_ID)
mod->modfunc[modidx++] = mod_set_id;
if (mb.modtype & CGW_MOD_DLC)
mod->modfunc[modidx++] = mod_set_dlc;
if (mb.modtype & CGW_MOD_DATA)
mod->modfunc[modidx++] = mod_set_data;
}
/* check for checksum operations after CAN frame modifications */
if (modidx) {
if (tb[CGW_CS_CRC8]) {
struct cgw_csum_crc8 *c = nla_data(tb[CGW_CS_CRC8]);
err = cgw_chk_csum_parms(c->from_idx, c->to_idx,
c->result_idx);
if (err)
return err;
nla_memcpy(&mod->csum.crc8, tb[CGW_CS_CRC8],
CGW_CS_CRC8_LEN);
/*
* select dedicated processing function to reduce
* runtime operations in receive hot path.
*/
if (c->from_idx < 0 || c->to_idx < 0 ||
c->result_idx < 0)
mod->csumfunc.crc8 = cgw_csum_crc8_rel;
else if (c->from_idx <= c->to_idx)
mod->csumfunc.crc8 = cgw_csum_crc8_pos;
else
mod->csumfunc.crc8 = cgw_csum_crc8_neg;
}
if (tb[CGW_CS_XOR]) {
struct cgw_csum_xor *c = nla_data(tb[CGW_CS_XOR]);
err = cgw_chk_csum_parms(c->from_idx, c->to_idx,
c->result_idx);
if (err)
return err;
nla_memcpy(&mod->csum.xor, tb[CGW_CS_XOR],
CGW_CS_XOR_LEN);
/*
* select dedicated processing function to reduce
* runtime operations in receive hot path.
*/
if (c->from_idx < 0 || c->to_idx < 0 ||
c->result_idx < 0)
mod->csumfunc.xor = cgw_csum_xor_rel;
else if (c->from_idx <= c->to_idx)
mod->csumfunc.xor = cgw_csum_xor_pos;
else
mod->csumfunc.xor = cgw_csum_xor_neg;
}
}
if (gwtype == CGW_TYPE_CAN_CAN) {
/* check CGW_TYPE_CAN_CAN specific attributes */
struct can_can_gw *ccgw = (struct can_can_gw *)gwtypeattr;
memset(ccgw, 0, sizeof(*ccgw));
/* check for can_filter in attributes */
if (tb[CGW_FILTER])
nla_memcpy(&ccgw->filter, tb[CGW_FILTER],
sizeof(struct can_filter));
err = -ENODEV;
/* specifying two interfaces is mandatory */
if (!tb[CGW_SRC_IF] || !tb[CGW_DST_IF])
return err;
ccgw->src_idx = nla_get_u32(tb[CGW_SRC_IF]);
ccgw->dst_idx = nla_get_u32(tb[CGW_DST_IF]);
/* both indices set to 0 for flushing all routing entries */
if (!ccgw->src_idx && !ccgw->dst_idx)
return 0;
/* only one index set to 0 is an error */
if (!ccgw->src_idx || !ccgw->dst_idx)
return err;
}
/* add the checks for other gwtypes here */
return 0;
}
static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh)
{
struct rtcanmsg *r;
struct cgw_job *gwj;
u8 limhops = 0;
int err = 0;
if (!netlink_capable(skb, CAP_NET_ADMIN))
return -EPERM;
if (nlmsg_len(nlh) < sizeof(*r))
return -EINVAL;
r = nlmsg_data(nlh);
if (r->can_family != AF_CAN)
return -EPFNOSUPPORT;
/* so far we only support CAN -> CAN routings */
if (r->gwtype != CGW_TYPE_CAN_CAN)
return -EINVAL;
gwj = kmem_cache_alloc(cgw_cache, GFP_KERNEL);
if (!gwj)
return -ENOMEM;
gwj->handled_frames = 0;
gwj->dropped_frames = 0;
gwj->deleted_frames = 0;
gwj->flags = r->flags;
gwj->gwtype = r->gwtype;
err = cgw_parse_attr(nlh, &gwj->mod, CGW_TYPE_CAN_CAN, &gwj->ccgw,
&limhops);
if (err < 0)
goto out;
err = -ENODEV;
/* ifindex == 0 is not allowed for job creation */
if (!gwj->ccgw.src_idx || !gwj->ccgw.dst_idx)
goto out;
gwj->src.dev = __dev_get_by_index(&init_net, gwj->ccgw.src_idx);
if (!gwj->src.dev)
goto out;
if (gwj->src.dev->type != ARPHRD_CAN)
goto out;
gwj->dst.dev = __dev_get_by_index(&init_net, gwj->ccgw.dst_idx);
if (!gwj->dst.dev)
goto out;
if (gwj->dst.dev->type != ARPHRD_CAN)
goto out;
gwj->limit_hops = limhops;
ASSERT_RTNL();
err = cgw_register_filter(gwj);
if (!err)
hlist_add_head_rcu(&gwj->list, &cgw_list);
out:
if (err)
kmem_cache_free(cgw_cache, gwj);
return err;
}
static void cgw_remove_all_jobs(void)
{
struct cgw_job *gwj = NULL;
struct hlist_node *nx;
ASSERT_RTNL();
hlist_for_each_entry_safe(gwj, nx, &cgw_list, list) {
hlist_del(&gwj->list);
cgw_unregister_filter(gwj);
kmem_cache_free(cgw_cache, gwj);
}
}
static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh)
{
struct cgw_job *gwj = NULL;
struct hlist_node *nx;
struct rtcanmsg *r;
struct cf_mod mod;
struct can_can_gw ccgw;
u8 limhops = 0;
int err = 0;
if (!netlink_capable(skb, CAP_NET_ADMIN))
return -EPERM;
if (nlmsg_len(nlh) < sizeof(*r))
return -EINVAL;
r = nlmsg_data(nlh);
if (r->can_family != AF_CAN)
return -EPFNOSUPPORT;
/* so far we only support CAN -> CAN routings */
if (r->gwtype != CGW_TYPE_CAN_CAN)
return -EINVAL;
err = cgw_parse_attr(nlh, &mod, CGW_TYPE_CAN_CAN, &ccgw, &limhops);
if (err < 0)
return err;
/* two interface indices both set to 0 => remove all entries */
if (!ccgw.src_idx && !ccgw.dst_idx) {
cgw_remove_all_jobs();
return 0;
}
err = -EINVAL;
ASSERT_RTNL();
/* remove only the first matching entry */
hlist_for_each_entry_safe(gwj, nx, &cgw_list, list) {
if (gwj->flags != r->flags)
continue;
if (gwj->limit_hops != limhops)
continue;
if (memcmp(&gwj->mod, &mod, sizeof(mod)))
continue;
/* if (r->gwtype == CGW_TYPE_CAN_CAN) - is made sure here */
if (memcmp(&gwj->ccgw, &ccgw, sizeof(ccgw)))
continue;
hlist_del(&gwj->list);
cgw_unregister_filter(gwj);
kmem_cache_free(cgw_cache, gwj);
err = 0;
break;
}
return err;
}
static __init int cgw_module_init(void)
{
/* sanitize given module parameter */
max_hops = clamp_t(unsigned int, max_hops, CGW_MIN_HOPS, CGW_MAX_HOPS);
pr_info("can: netlink gateway (rev " CAN_GW_VERSION ") max_hops=%d\n",
max_hops);
cgw_cache = kmem_cache_create("can_gw", sizeof(struct cgw_job),
0, 0, NULL);
if (!cgw_cache)
return -ENOMEM;
/* set notifier */
notifier.notifier_call = cgw_notifier;
register_netdevice_notifier(&notifier);
if (__rtnl_register(PF_CAN, RTM_GETROUTE, NULL, cgw_dump_jobs, NULL)) {
unregister_netdevice_notifier(&notifier);
kmem_cache_destroy(cgw_cache);
return -ENOBUFS;
}
/* Only the first call to __rtnl_register can fail */
__rtnl_register(PF_CAN, RTM_NEWROUTE, cgw_create_job, NULL, NULL);
__rtnl_register(PF_CAN, RTM_DELROUTE, cgw_remove_job, NULL, NULL);
return 0;
}
static __exit void cgw_module_exit(void)
{
rtnl_unregister_all(PF_CAN);
unregister_netdevice_notifier(&notifier);
rtnl_lock();
cgw_remove_all_jobs();
rtnl_unlock();
rcu_barrier(); /* Wait for completion of call_rcu()'s */
kmem_cache_destroy(cgw_cache);
}
module_init(cgw_module_init);
module_exit(cgw_module_exit);

580
net/can/proc.c Normal file
View file

@ -0,0 +1,580 @@
/*
* proc.c - procfs support for Protocol family CAN core module
*
* Copyright (c) 2002-2007 Volkswagen Group Electronic Research
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Volkswagen nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* Alternatively, provided that this notice is retained in full, this
* software may be distributed under the terms of the GNU General
* Public License ("GPL") version 2, in which case the provisions of the
* GPL apply INSTEAD OF those given above.
*
* The provided data structures and external interfaces from this code
* are not restricted to be used by modules with a GPL compatible license.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
*/
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/list.h>
#include <linux/rcupdate.h>
#include <linux/if_arp.h>
#include <linux/can/core.h>
#include "af_can.h"
/*
* proc filenames for the PF_CAN core
*/
#define CAN_PROC_VERSION "version"
#define CAN_PROC_STATS "stats"
#define CAN_PROC_RESET_STATS "reset_stats"
#define CAN_PROC_RCVLIST_ALL "rcvlist_all"
#define CAN_PROC_RCVLIST_FIL "rcvlist_fil"
#define CAN_PROC_RCVLIST_INV "rcvlist_inv"
#define CAN_PROC_RCVLIST_SFF "rcvlist_sff"
#define CAN_PROC_RCVLIST_EFF "rcvlist_eff"
#define CAN_PROC_RCVLIST_ERR "rcvlist_err"
static struct proc_dir_entry *can_dir;
static struct proc_dir_entry *pde_version;
static struct proc_dir_entry *pde_stats;
static struct proc_dir_entry *pde_reset_stats;
static struct proc_dir_entry *pde_rcvlist_all;
static struct proc_dir_entry *pde_rcvlist_fil;
static struct proc_dir_entry *pde_rcvlist_inv;
static struct proc_dir_entry *pde_rcvlist_sff;
static struct proc_dir_entry *pde_rcvlist_eff;
static struct proc_dir_entry *pde_rcvlist_err;
static int user_reset;
static const char rx_list_name[][8] = {
[RX_ERR] = "rx_err",
[RX_ALL] = "rx_all",
[RX_FIL] = "rx_fil",
[RX_INV] = "rx_inv",
};
/*
* af_can statistics stuff
*/
static void can_init_stats(void)
{
/*
* This memset function is called from a timer context (when
* can_stattimer is active which is the default) OR in a process
* context (reading the proc_fs when can_stattimer is disabled).
*/
memset(&can_stats, 0, sizeof(can_stats));
can_stats.jiffies_init = jiffies;
can_pstats.stats_reset++;
if (user_reset) {
user_reset = 0;
can_pstats.user_reset++;
}
}
static unsigned long calc_rate(unsigned long oldjif, unsigned long newjif,
unsigned long count)
{
unsigned long rate;
if (oldjif == newjif)
return 0;
/* see can_stat_update() - this should NEVER happen! */
if (count > (ULONG_MAX / HZ)) {
printk(KERN_ERR "can: calc_rate: count exceeded! %ld\n",
count);
return 99999999;
}
rate = (count * HZ) / (newjif - oldjif);
return rate;
}
void can_stat_update(unsigned long data)
{
unsigned long j = jiffies; /* snapshot */
/* restart counting in timer context on user request */
if (user_reset)
can_init_stats();
/* restart counting on jiffies overflow */
if (j < can_stats.jiffies_init)
can_init_stats();
/* prevent overflow in calc_rate() */
if (can_stats.rx_frames > (ULONG_MAX / HZ))
can_init_stats();
/* prevent overflow in calc_rate() */
if (can_stats.tx_frames > (ULONG_MAX / HZ))
can_init_stats();
/* matches overflow - very improbable */
if (can_stats.matches > (ULONG_MAX / 100))
can_init_stats();
/* calc total values */
if (can_stats.rx_frames)
can_stats.total_rx_match_ratio = (can_stats.matches * 100) /
can_stats.rx_frames;
can_stats.total_tx_rate = calc_rate(can_stats.jiffies_init, j,
can_stats.tx_frames);
can_stats.total_rx_rate = calc_rate(can_stats.jiffies_init, j,
can_stats.rx_frames);
/* calc current values */
if (can_stats.rx_frames_delta)
can_stats.current_rx_match_ratio =
(can_stats.matches_delta * 100) /
can_stats.rx_frames_delta;
can_stats.current_tx_rate = calc_rate(0, HZ, can_stats.tx_frames_delta);
can_stats.current_rx_rate = calc_rate(0, HZ, can_stats.rx_frames_delta);
/* check / update maximum values */
if (can_stats.max_tx_rate < can_stats.current_tx_rate)
can_stats.max_tx_rate = can_stats.current_tx_rate;
if (can_stats.max_rx_rate < can_stats.current_rx_rate)
can_stats.max_rx_rate = can_stats.current_rx_rate;
if (can_stats.max_rx_match_ratio < can_stats.current_rx_match_ratio)
can_stats.max_rx_match_ratio = can_stats.current_rx_match_ratio;
/* clear values for 'current rate' calculation */
can_stats.tx_frames_delta = 0;
can_stats.rx_frames_delta = 0;
can_stats.matches_delta = 0;
/* restart timer (one second) */
mod_timer(&can_stattimer, round_jiffies(jiffies + HZ));
}
/*
* proc read functions
*/
static void can_print_rcvlist(struct seq_file *m, struct hlist_head *rx_list,
struct net_device *dev)
{
struct receiver *r;
hlist_for_each_entry_rcu(r, rx_list, list) {
char *fmt = (r->can_id & CAN_EFF_FLAG)?
" %-5s %08x %08x %pK %pK %8ld %s\n" :
" %-5s %03x %08x %pK %pK %8ld %s\n";
seq_printf(m, fmt, DNAME(dev), r->can_id, r->mask,
r->func, r->data, r->matches, r->ident);
}
}
static void can_print_recv_banner(struct seq_file *m)
{
/*
* can1. 00000000 00000000 00000000
* ....... 0 tp20
*/
seq_puts(m, " device can_id can_mask function"
" userdata matches ident\n");
}
static int can_stats_proc_show(struct seq_file *m, void *v)
{
seq_putc(m, '\n');
seq_printf(m, " %8ld transmitted frames (TXF)\n", can_stats.tx_frames);
seq_printf(m, " %8ld received frames (RXF)\n", can_stats.rx_frames);
seq_printf(m, " %8ld matched frames (RXMF)\n", can_stats.matches);
seq_putc(m, '\n');
if (can_stattimer.function == can_stat_update) {
seq_printf(m, " %8ld %% total match ratio (RXMR)\n",
can_stats.total_rx_match_ratio);
seq_printf(m, " %8ld frames/s total tx rate (TXR)\n",
can_stats.total_tx_rate);
seq_printf(m, " %8ld frames/s total rx rate (RXR)\n",
can_stats.total_rx_rate);
seq_putc(m, '\n');
seq_printf(m, " %8ld %% current match ratio (CRXMR)\n",
can_stats.current_rx_match_ratio);
seq_printf(m, " %8ld frames/s current tx rate (CTXR)\n",
can_stats.current_tx_rate);
seq_printf(m, " %8ld frames/s current rx rate (CRXR)\n",
can_stats.current_rx_rate);
seq_putc(m, '\n');
seq_printf(m, " %8ld %% max match ratio (MRXMR)\n",
can_stats.max_rx_match_ratio);
seq_printf(m, " %8ld frames/s max tx rate (MTXR)\n",
can_stats.max_tx_rate);
seq_printf(m, " %8ld frames/s max rx rate (MRXR)\n",
can_stats.max_rx_rate);
seq_putc(m, '\n');
}
seq_printf(m, " %8ld current receive list entries (CRCV)\n",
can_pstats.rcv_entries);
seq_printf(m, " %8ld maximum receive list entries (MRCV)\n",
can_pstats.rcv_entries_max);
if (can_pstats.stats_reset)
seq_printf(m, "\n %8ld statistic resets (STR)\n",
can_pstats.stats_reset);
if (can_pstats.user_reset)
seq_printf(m, " %8ld user statistic resets (USTR)\n",
can_pstats.user_reset);
seq_putc(m, '\n');
return 0;
}
static int can_stats_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, can_stats_proc_show, NULL);
}
static const struct file_operations can_stats_proc_fops = {
.owner = THIS_MODULE,
.open = can_stats_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int can_reset_stats_proc_show(struct seq_file *m, void *v)
{
user_reset = 1;
if (can_stattimer.function == can_stat_update) {
seq_printf(m, "Scheduled statistic reset #%ld.\n",
can_pstats.stats_reset + 1);
} else {
if (can_stats.jiffies_init != jiffies)
can_init_stats();
seq_printf(m, "Performed statistic reset #%ld.\n",
can_pstats.stats_reset);
}
return 0;
}
static int can_reset_stats_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, can_reset_stats_proc_show, NULL);
}
static const struct file_operations can_reset_stats_proc_fops = {
.owner = THIS_MODULE,
.open = can_reset_stats_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int can_version_proc_show(struct seq_file *m, void *v)
{
seq_printf(m, "%s\n", CAN_VERSION_STRING);
return 0;
}
static int can_version_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, can_version_proc_show, NULL);
}
static const struct file_operations can_version_proc_fops = {
.owner = THIS_MODULE,
.open = can_version_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static inline void can_rcvlist_proc_show_one(struct seq_file *m, int idx,
struct net_device *dev,
struct dev_rcv_lists *d)
{
if (!hlist_empty(&d->rx[idx])) {
can_print_recv_banner(m);
can_print_rcvlist(m, &d->rx[idx], dev);
} else
seq_printf(m, " (%s: no entry)\n", DNAME(dev));
}
static int can_rcvlist_proc_show(struct seq_file *m, void *v)
{
/* double cast to prevent GCC warning */
int idx = (int)(long)m->private;
struct net_device *dev;
struct dev_rcv_lists *d;
seq_printf(m, "\nreceive list '%s':\n", rx_list_name[idx]);
rcu_read_lock();
/* receive list for 'all' CAN devices (dev == NULL) */
d = &can_rx_alldev_list;
can_rcvlist_proc_show_one(m, idx, NULL, d);
/* receive list for registered CAN devices */
for_each_netdev_rcu(&init_net, dev) {
if (dev->type == ARPHRD_CAN && dev->ml_priv)
can_rcvlist_proc_show_one(m, idx, dev, dev->ml_priv);
}
rcu_read_unlock();
seq_putc(m, '\n');
return 0;
}
static int can_rcvlist_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, can_rcvlist_proc_show, PDE_DATA(inode));
}
static const struct file_operations can_rcvlist_proc_fops = {
.owner = THIS_MODULE,
.open = can_rcvlist_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static inline void can_rcvlist_proc_show_array(struct seq_file *m,
struct net_device *dev,
struct hlist_head *rcv_array,
unsigned int rcv_array_sz)
{
unsigned int i;
int all_empty = 1;
/* check whether at least one list is non-empty */
for (i = 0; i < rcv_array_sz; i++)
if (!hlist_empty(&rcv_array[i])) {
all_empty = 0;
break;
}
if (!all_empty) {
can_print_recv_banner(m);
for (i = 0; i < rcv_array_sz; i++) {
if (!hlist_empty(&rcv_array[i]))
can_print_rcvlist(m, &rcv_array[i], dev);
}
} else
seq_printf(m, " (%s: no entry)\n", DNAME(dev));
}
static int can_rcvlist_sff_proc_show(struct seq_file *m, void *v)
{
struct net_device *dev;
struct dev_rcv_lists *d;
/* RX_SFF */
seq_puts(m, "\nreceive list 'rx_sff':\n");
rcu_read_lock();
/* sff receive list for 'all' CAN devices (dev == NULL) */
d = &can_rx_alldev_list;
can_rcvlist_proc_show_array(m, NULL, d->rx_sff, ARRAY_SIZE(d->rx_sff));
/* sff receive list for registered CAN devices */
for_each_netdev_rcu(&init_net, dev) {
if (dev->type == ARPHRD_CAN && dev->ml_priv) {
d = dev->ml_priv;
can_rcvlist_proc_show_array(m, dev, d->rx_sff,
ARRAY_SIZE(d->rx_sff));
}
}
rcu_read_unlock();
seq_putc(m, '\n');
return 0;
}
static int can_rcvlist_sff_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, can_rcvlist_sff_proc_show, NULL);
}
static const struct file_operations can_rcvlist_sff_proc_fops = {
.owner = THIS_MODULE,
.open = can_rcvlist_sff_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int can_rcvlist_eff_proc_show(struct seq_file *m, void *v)
{
struct net_device *dev;
struct dev_rcv_lists *d;
/* RX_EFF */
seq_puts(m, "\nreceive list 'rx_eff':\n");
rcu_read_lock();
/* eff receive list for 'all' CAN devices (dev == NULL) */
d = &can_rx_alldev_list;
can_rcvlist_proc_show_array(m, NULL, d->rx_eff, ARRAY_SIZE(d->rx_eff));
/* eff receive list for registered CAN devices */
for_each_netdev_rcu(&init_net, dev) {
if (dev->type == ARPHRD_CAN && dev->ml_priv) {
d = dev->ml_priv;
can_rcvlist_proc_show_array(m, dev, d->rx_eff,
ARRAY_SIZE(d->rx_eff));
}
}
rcu_read_unlock();
seq_putc(m, '\n');
return 0;
}
static int can_rcvlist_eff_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, can_rcvlist_eff_proc_show, NULL);
}
static const struct file_operations can_rcvlist_eff_proc_fops = {
.owner = THIS_MODULE,
.open = can_rcvlist_eff_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
/*
* proc utility functions
*/
static void can_remove_proc_readentry(const char *name)
{
if (can_dir)
remove_proc_entry(name, can_dir);
}
/*
* can_init_proc - create main CAN proc directory and procfs entries
*/
void can_init_proc(void)
{
/* create /proc/net/can directory */
can_dir = proc_mkdir("can", init_net.proc_net);
if (!can_dir) {
printk(KERN_INFO "can: failed to create /proc/net/can . "
"CONFIG_PROC_FS missing?\n");
return;
}
/* own procfs entries from the AF_CAN core */
pde_version = proc_create(CAN_PROC_VERSION, 0644, can_dir,
&can_version_proc_fops);
pde_stats = proc_create(CAN_PROC_STATS, 0644, can_dir,
&can_stats_proc_fops);
pde_reset_stats = proc_create(CAN_PROC_RESET_STATS, 0644, can_dir,
&can_reset_stats_proc_fops);
pde_rcvlist_err = proc_create_data(CAN_PROC_RCVLIST_ERR, 0644, can_dir,
&can_rcvlist_proc_fops, (void *)RX_ERR);
pde_rcvlist_all = proc_create_data(CAN_PROC_RCVLIST_ALL, 0644, can_dir,
&can_rcvlist_proc_fops, (void *)RX_ALL);
pde_rcvlist_fil = proc_create_data(CAN_PROC_RCVLIST_FIL, 0644, can_dir,
&can_rcvlist_proc_fops, (void *)RX_FIL);
pde_rcvlist_inv = proc_create_data(CAN_PROC_RCVLIST_INV, 0644, can_dir,
&can_rcvlist_proc_fops, (void *)RX_INV);
pde_rcvlist_eff = proc_create(CAN_PROC_RCVLIST_EFF, 0644, can_dir,
&can_rcvlist_eff_proc_fops);
pde_rcvlist_sff = proc_create(CAN_PROC_RCVLIST_SFF, 0644, can_dir,
&can_rcvlist_sff_proc_fops);
}
/*
* can_remove_proc - remove procfs entries and main CAN proc directory
*/
void can_remove_proc(void)
{
if (pde_version)
can_remove_proc_readentry(CAN_PROC_VERSION);
if (pde_stats)
can_remove_proc_readentry(CAN_PROC_STATS);
if (pde_reset_stats)
can_remove_proc_readentry(CAN_PROC_RESET_STATS);
if (pde_rcvlist_err)
can_remove_proc_readentry(CAN_PROC_RCVLIST_ERR);
if (pde_rcvlist_all)
can_remove_proc_readentry(CAN_PROC_RCVLIST_ALL);
if (pde_rcvlist_fil)
can_remove_proc_readentry(CAN_PROC_RCVLIST_FIL);
if (pde_rcvlist_inv)
can_remove_proc_readentry(CAN_PROC_RCVLIST_INV);
if (pde_rcvlist_eff)
can_remove_proc_readentry(CAN_PROC_RCVLIST_EFF);
if (pde_rcvlist_sff)
can_remove_proc_readentry(CAN_PROC_RCVLIST_SFF);
if (can_dir)
remove_proc_entry("can", init_net.proc_net);
}

828
net/can/raw.c Normal file
View file

@ -0,0 +1,828 @@
/*
* raw.c - Raw sockets for protocol family CAN
*
* Copyright (c) 2002-2007 Volkswagen Group Electronic Research
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Volkswagen nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* Alternatively, provided that this notice is retained in full, this
* software may be distributed under the terms of the GNU General
* Public License ("GPL") version 2, in which case the provisions of the
* GPL apply INSTEAD OF those given above.
*
* The provided data structures and external interfaces from this code
* are not restricted to be used by modules with a GPL compatible license.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/uio.h>
#include <linux/net.h>
#include <linux/slab.h>
#include <linux/netdevice.h>
#include <linux/socket.h>
#include <linux/if_arp.h>
#include <linux/skbuff.h>
#include <linux/can.h>
#include <linux/can/core.h>
#include <linux/can/skb.h>
#include <linux/can/raw.h>
#include <net/sock.h>
#include <net/net_namespace.h>
#define CAN_RAW_VERSION CAN_VERSION
static __initconst const char banner[] =
KERN_INFO "can: raw protocol (rev " CAN_RAW_VERSION ")\n";
MODULE_DESCRIPTION("PF_CAN raw protocol");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Urs Thuermann <urs.thuermann@volkswagen.de>");
MODULE_ALIAS("can-proto-1");
#define MASK_ALL 0
/*
* A raw socket has a list of can_filters attached to it, each receiving
* the CAN frames matching that filter. If the filter list is empty,
* no CAN frames will be received by the socket. The default after
* opening the socket, is to have one filter which receives all frames.
* The filter list is allocated dynamically with the exception of the
* list containing only one item. This common case is optimized by
* storing the single filter in dfilter, to avoid using dynamic memory.
*/
struct raw_sock {
struct sock sk;
int bound;
int ifindex;
struct notifier_block notifier;
int loopback;
int recv_own_msgs;
int fd_frames;
int count; /* number of active filters */
struct can_filter dfilter; /* default/single filter */
struct can_filter *filter; /* pointer to filter(s) */
can_err_mask_t err_mask;
};
/*
* Return pointer to store the extra msg flags for raw_recvmsg().
* We use the space of one unsigned int beyond the 'struct sockaddr_can'
* in skb->cb.
*/
static inline unsigned int *raw_flags(struct sk_buff *skb)
{
BUILD_BUG_ON(sizeof(skb->cb) <= (sizeof(struct sockaddr_can) +
sizeof(unsigned int)));
/* return pointer after struct sockaddr_can */
return (unsigned int *)(&((struct sockaddr_can *)skb->cb)[1]);
}
static inline struct raw_sock *raw_sk(const struct sock *sk)
{
return (struct raw_sock *)sk;
}
static void raw_rcv(struct sk_buff *oskb, void *data)
{
struct sock *sk = (struct sock *)data;
struct raw_sock *ro = raw_sk(sk);
struct sockaddr_can *addr;
struct sk_buff *skb;
unsigned int *pflags;
/* check the received tx sock reference */
if (!ro->recv_own_msgs && oskb->sk == sk)
return;
/* do not pass non-CAN2.0 frames to a legacy socket */
if (!ro->fd_frames && oskb->len != CAN_MTU)
return;
/* clone the given skb to be able to enqueue it into the rcv queue */
skb = skb_clone(oskb, GFP_ATOMIC);
if (!skb)
return;
/*
* Put the datagram to the queue so that raw_recvmsg() can
* get it from there. We need to pass the interface index to
* raw_recvmsg(). We pass a whole struct sockaddr_can in skb->cb
* containing the interface index.
*/
BUILD_BUG_ON(sizeof(skb->cb) < sizeof(struct sockaddr_can));
addr = (struct sockaddr_can *)skb->cb;
memset(addr, 0, sizeof(*addr));
addr->can_family = AF_CAN;
addr->can_ifindex = skb->dev->ifindex;
/* add CAN specific message flags for raw_recvmsg() */
pflags = raw_flags(skb);
*pflags = 0;
if (oskb->sk)
*pflags |= MSG_DONTROUTE;
if (oskb->sk == sk)
*pflags |= MSG_CONFIRM;
if (sock_queue_rcv_skb(sk, skb) < 0)
kfree_skb(skb);
}
static int raw_enable_filters(struct net_device *dev, struct sock *sk,
struct can_filter *filter, int count)
{
int err = 0;
int i;
for (i = 0; i < count; i++) {
err = can_rx_register(dev, filter[i].can_id,
filter[i].can_mask,
raw_rcv, sk, "raw");
if (err) {
/* clean up successfully registered filters */
while (--i >= 0)
can_rx_unregister(dev, filter[i].can_id,
filter[i].can_mask,
raw_rcv, sk);
break;
}
}
return err;
}
static int raw_enable_errfilter(struct net_device *dev, struct sock *sk,
can_err_mask_t err_mask)
{
int err = 0;
if (err_mask)
err = can_rx_register(dev, 0, err_mask | CAN_ERR_FLAG,
raw_rcv, sk, "raw");
return err;
}
static void raw_disable_filters(struct net_device *dev, struct sock *sk,
struct can_filter *filter, int count)
{
int i;
for (i = 0; i < count; i++)
can_rx_unregister(dev, filter[i].can_id, filter[i].can_mask,
raw_rcv, sk);
}
static inline void raw_disable_errfilter(struct net_device *dev,
struct sock *sk,
can_err_mask_t err_mask)
{
if (err_mask)
can_rx_unregister(dev, 0, err_mask | CAN_ERR_FLAG,
raw_rcv, sk);
}
static inline void raw_disable_allfilters(struct net_device *dev,
struct sock *sk)
{
struct raw_sock *ro = raw_sk(sk);
raw_disable_filters(dev, sk, ro->filter, ro->count);
raw_disable_errfilter(dev, sk, ro->err_mask);
}
static int raw_enable_allfilters(struct net_device *dev, struct sock *sk)
{
struct raw_sock *ro = raw_sk(sk);
int err;
err = raw_enable_filters(dev, sk, ro->filter, ro->count);
if (!err) {
err = raw_enable_errfilter(dev, sk, ro->err_mask);
if (err)
raw_disable_filters(dev, sk, ro->filter, ro->count);
}
return err;
}
static int raw_notifier(struct notifier_block *nb,
unsigned long msg, void *ptr)
{
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
struct raw_sock *ro = container_of(nb, struct raw_sock, notifier);
struct sock *sk = &ro->sk;
if (!net_eq(dev_net(dev), &init_net))
return NOTIFY_DONE;
if (dev->type != ARPHRD_CAN)
return NOTIFY_DONE;
if (ro->ifindex != dev->ifindex)
return NOTIFY_DONE;
switch (msg) {
case NETDEV_UNREGISTER:
lock_sock(sk);
/* remove current filters & unregister */
if (ro->bound)
raw_disable_allfilters(dev, sk);
if (ro->count > 1)
kfree(ro->filter);
ro->ifindex = 0;
ro->bound = 0;
ro->count = 0;
release_sock(sk);
sk->sk_err = ENODEV;
if (!sock_flag(sk, SOCK_DEAD))
sk->sk_error_report(sk);
break;
case NETDEV_DOWN:
sk->sk_err = ENETDOWN;
if (!sock_flag(sk, SOCK_DEAD))
sk->sk_error_report(sk);
break;
}
return NOTIFY_DONE;
}
static int raw_init(struct sock *sk)
{
struct raw_sock *ro = raw_sk(sk);
ro->bound = 0;
ro->ifindex = 0;
/* set default filter to single entry dfilter */
ro->dfilter.can_id = 0;
ro->dfilter.can_mask = MASK_ALL;
ro->filter = &ro->dfilter;
ro->count = 1;
/* set default loopback behaviour */
ro->loopback = 1;
ro->recv_own_msgs = 0;
ro->fd_frames = 0;
/* set notifier */
ro->notifier.notifier_call = raw_notifier;
register_netdevice_notifier(&ro->notifier);
return 0;
}
static int raw_release(struct socket *sock)
{
struct sock *sk = sock->sk;
struct raw_sock *ro;
if (!sk)
return 0;
ro = raw_sk(sk);
unregister_netdevice_notifier(&ro->notifier);
lock_sock(sk);
/* remove current filters & unregister */
if (ro->bound) {
if (ro->ifindex) {
struct net_device *dev;
dev = dev_get_by_index(&init_net, ro->ifindex);
if (dev) {
raw_disable_allfilters(dev, sk);
dev_put(dev);
}
} else
raw_disable_allfilters(NULL, sk);
}
if (ro->count > 1)
kfree(ro->filter);
ro->ifindex = 0;
ro->bound = 0;
ro->count = 0;
sock_orphan(sk);
sock->sk = NULL;
release_sock(sk);
sock_put(sk);
return 0;
}
static int raw_bind(struct socket *sock, struct sockaddr *uaddr, int len)
{
struct sockaddr_can *addr = (struct sockaddr_can *)uaddr;
struct sock *sk = sock->sk;
struct raw_sock *ro = raw_sk(sk);
int ifindex;
int err = 0;
int notify_enetdown = 0;
if (len < sizeof(*addr))
return -EINVAL;
lock_sock(sk);
if (ro->bound && addr->can_ifindex == ro->ifindex)
goto out;
if (addr->can_ifindex) {
struct net_device *dev;
dev = dev_get_by_index(&init_net, addr->can_ifindex);
if (!dev) {
err = -ENODEV;
goto out;
}
if (dev->type != ARPHRD_CAN) {
dev_put(dev);
err = -ENODEV;
goto out;
}
if (!(dev->flags & IFF_UP))
notify_enetdown = 1;
ifindex = dev->ifindex;
/* filters set by default/setsockopt */
err = raw_enable_allfilters(dev, sk);
dev_put(dev);
} else {
ifindex = 0;
/* filters set by default/setsockopt */
err = raw_enable_allfilters(NULL, sk);
}
if (!err) {
if (ro->bound) {
/* unregister old filters */
if (ro->ifindex) {
struct net_device *dev;
dev = dev_get_by_index(&init_net, ro->ifindex);
if (dev) {
raw_disable_allfilters(dev, sk);
dev_put(dev);
}
} else
raw_disable_allfilters(NULL, sk);
}
ro->ifindex = ifindex;
ro->bound = 1;
}
out:
release_sock(sk);
if (notify_enetdown) {
sk->sk_err = ENETDOWN;
if (!sock_flag(sk, SOCK_DEAD))
sk->sk_error_report(sk);
}
return err;
}
static int raw_getname(struct socket *sock, struct sockaddr *uaddr,
int *len, int peer)
{
struct sockaddr_can *addr = (struct sockaddr_can *)uaddr;
struct sock *sk = sock->sk;
struct raw_sock *ro = raw_sk(sk);
if (peer)
return -EOPNOTSUPP;
memset(addr, 0, sizeof(*addr));
addr->can_family = AF_CAN;
addr->can_ifindex = ro->ifindex;
*len = sizeof(*addr);
return 0;
}
static int raw_setsockopt(struct socket *sock, int level, int optname,
char __user *optval, unsigned int optlen)
{
struct sock *sk = sock->sk;
struct raw_sock *ro = raw_sk(sk);
struct can_filter *filter = NULL; /* dyn. alloc'ed filters */
struct can_filter sfilter; /* single filter */
struct net_device *dev = NULL;
can_err_mask_t err_mask = 0;
int count = 0;
int err = 0;
if (level != SOL_CAN_RAW)
return -EINVAL;
switch (optname) {
case CAN_RAW_FILTER:
if (optlen % sizeof(struct can_filter) != 0)
return -EINVAL;
count = optlen / sizeof(struct can_filter);
if (count > 1) {
/* filter does not fit into dfilter => alloc space */
filter = memdup_user(optval, optlen);
if (IS_ERR(filter))
return PTR_ERR(filter);
} else if (count == 1) {
if (copy_from_user(&sfilter, optval, sizeof(sfilter)))
return -EFAULT;
}
lock_sock(sk);
if (ro->bound && ro->ifindex)
dev = dev_get_by_index(&init_net, ro->ifindex);
if (ro->bound) {
/* (try to) register the new filters */
if (count == 1)
err = raw_enable_filters(dev, sk, &sfilter, 1);
else
err = raw_enable_filters(dev, sk, filter,
count);
if (err) {
if (count > 1)
kfree(filter);
goto out_fil;
}
/* remove old filter registrations */
raw_disable_filters(dev, sk, ro->filter, ro->count);
}
/* remove old filter space */
if (ro->count > 1)
kfree(ro->filter);
/* link new filters to the socket */
if (count == 1) {
/* copy filter data for single filter */
ro->dfilter = sfilter;
filter = &ro->dfilter;
}
ro->filter = filter;
ro->count = count;
out_fil:
if (dev)
dev_put(dev);
release_sock(sk);
break;
case CAN_RAW_ERR_FILTER:
if (optlen != sizeof(err_mask))
return -EINVAL;
if (copy_from_user(&err_mask, optval, optlen))
return -EFAULT;
err_mask &= CAN_ERR_MASK;
lock_sock(sk);
if (ro->bound && ro->ifindex)
dev = dev_get_by_index(&init_net, ro->ifindex);
/* remove current error mask */
if (ro->bound) {
/* (try to) register the new err_mask */
err = raw_enable_errfilter(dev, sk, err_mask);
if (err)
goto out_err;
/* remove old err_mask registration */
raw_disable_errfilter(dev, sk, ro->err_mask);
}
/* link new err_mask to the socket */
ro->err_mask = err_mask;
out_err:
if (dev)
dev_put(dev);
release_sock(sk);
break;
case CAN_RAW_LOOPBACK:
if (optlen != sizeof(ro->loopback))
return -EINVAL;
if (copy_from_user(&ro->loopback, optval, optlen))
return -EFAULT;
break;
case CAN_RAW_RECV_OWN_MSGS:
if (optlen != sizeof(ro->recv_own_msgs))
return -EINVAL;
if (copy_from_user(&ro->recv_own_msgs, optval, optlen))
return -EFAULT;
break;
case CAN_RAW_FD_FRAMES:
if (optlen != sizeof(ro->fd_frames))
return -EINVAL;
if (copy_from_user(&ro->fd_frames, optval, optlen))
return -EFAULT;
break;
default:
return -ENOPROTOOPT;
}
return err;
}
static int raw_getsockopt(struct socket *sock, int level, int optname,
char __user *optval, int __user *optlen)
{
struct sock *sk = sock->sk;
struct raw_sock *ro = raw_sk(sk);
int len;
void *val;
int err = 0;
if (level != SOL_CAN_RAW)
return -EINVAL;
if (get_user(len, optlen))
return -EFAULT;
if (len < 0)
return -EINVAL;
switch (optname) {
case CAN_RAW_FILTER:
lock_sock(sk);
if (ro->count > 0) {
int fsize = ro->count * sizeof(struct can_filter);
if (len > fsize)
len = fsize;
if (copy_to_user(optval, ro->filter, len))
err = -EFAULT;
} else
len = 0;
release_sock(sk);
if (!err)
err = put_user(len, optlen);
return err;
case CAN_RAW_ERR_FILTER:
if (len > sizeof(can_err_mask_t))
len = sizeof(can_err_mask_t);
val = &ro->err_mask;
break;
case CAN_RAW_LOOPBACK:
if (len > sizeof(int))
len = sizeof(int);
val = &ro->loopback;
break;
case CAN_RAW_RECV_OWN_MSGS:
if (len > sizeof(int))
len = sizeof(int);
val = &ro->recv_own_msgs;
break;
case CAN_RAW_FD_FRAMES:
if (len > sizeof(int))
len = sizeof(int);
val = &ro->fd_frames;
break;
default:
return -ENOPROTOOPT;
}
if (put_user(len, optlen))
return -EFAULT;
if (copy_to_user(optval, val, len))
return -EFAULT;
return 0;
}
static int raw_sendmsg(struct kiocb *iocb, struct socket *sock,
struct msghdr *msg, size_t size)
{
struct sock *sk = sock->sk;
struct raw_sock *ro = raw_sk(sk);
struct sk_buff *skb;
struct net_device *dev;
int ifindex;
int err;
if (msg->msg_name) {
DECLARE_SOCKADDR(struct sockaddr_can *, addr, msg->msg_name);
if (msg->msg_namelen < sizeof(*addr))
return -EINVAL;
if (addr->can_family != AF_CAN)
return -EINVAL;
ifindex = addr->can_ifindex;
} else
ifindex = ro->ifindex;
if (ro->fd_frames) {
if (unlikely(size != CANFD_MTU && size != CAN_MTU))
return -EINVAL;
} else {
if (unlikely(size != CAN_MTU))
return -EINVAL;
}
dev = dev_get_by_index(&init_net, ifindex);
if (!dev)
return -ENXIO;
skb = sock_alloc_send_skb(sk, size + sizeof(struct can_skb_priv),
msg->msg_flags & MSG_DONTWAIT, &err);
if (!skb)
goto put_dev;
can_skb_reserve(skb);
can_skb_prv(skb)->ifindex = dev->ifindex;
err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
if (err < 0)
goto free_skb;
sock_tx_timestamp(sk, &skb_shinfo(skb)->tx_flags);
skb->dev = dev;
skb->sk = sk;
skb->priority = sk->sk_priority;
err = can_send(skb, ro->loopback);
dev_put(dev);
if (err)
goto send_failed;
return size;
free_skb:
kfree_skb(skb);
put_dev:
dev_put(dev);
send_failed:
return err;
}
static int raw_recvmsg(struct kiocb *iocb, struct socket *sock,
struct msghdr *msg, size_t size, int flags)
{
struct sock *sk = sock->sk;
struct sk_buff *skb;
int err = 0;
int noblock;
noblock = flags & MSG_DONTWAIT;
flags &= ~MSG_DONTWAIT;
skb = skb_recv_datagram(sk, flags, noblock, &err);
if (!skb)
return err;
if (size < skb->len)
msg->msg_flags |= MSG_TRUNC;
else
size = skb->len;
err = memcpy_toiovec(msg->msg_iov, skb->data, size);
if (err < 0) {
skb_free_datagram(sk, skb);
return err;
}
sock_recv_ts_and_drops(msg, sk, skb);
if (msg->msg_name) {
__sockaddr_check_size(sizeof(struct sockaddr_can));
msg->msg_namelen = sizeof(struct sockaddr_can);
memcpy(msg->msg_name, skb->cb, msg->msg_namelen);
}
/* assign the flags that have been recorded in raw_rcv() */
msg->msg_flags |= *(raw_flags(skb));
skb_free_datagram(sk, skb);
return size;
}
static const struct proto_ops raw_ops = {
.family = PF_CAN,
.release = raw_release,
.bind = raw_bind,
.connect = sock_no_connect,
.socketpair = sock_no_socketpair,
.accept = sock_no_accept,
.getname = raw_getname,
.poll = datagram_poll,
.ioctl = can_ioctl, /* use can_ioctl() from af_can.c */
.listen = sock_no_listen,
.shutdown = sock_no_shutdown,
.setsockopt = raw_setsockopt,
.getsockopt = raw_getsockopt,
.sendmsg = raw_sendmsg,
.recvmsg = raw_recvmsg,
.mmap = sock_no_mmap,
.sendpage = sock_no_sendpage,
};
static struct proto raw_proto __read_mostly = {
.name = "CAN_RAW",
.owner = THIS_MODULE,
.obj_size = sizeof(struct raw_sock),
.init = raw_init,
};
static const struct can_proto raw_can_proto = {
.type = SOCK_RAW,
.protocol = CAN_RAW,
.ops = &raw_ops,
.prot = &raw_proto,
};
static __init int raw_module_init(void)
{
int err;
printk(banner);
err = can_proto_register(&raw_can_proto);
if (err < 0)
printk(KERN_ERR "can: registration of raw protocol failed\n");
return err;
}
static __exit void raw_module_exit(void)
{
can_proto_unregister(&raw_can_proto);
}
module_init(raw_module_init);
module_exit(raw_module_exit);