// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) /* 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. *
*/
/* 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.
*/
/* 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.
*/ staticinlineunsignedint *raw_flags(struct sk_buff *skb)
{
sock_skb_cb_check_size(sizeof(struct sockaddr_can) + sizeof(unsignedint));
/* check the received tx sock reference */ if (!ro->recv_own_msgs && oskb->sk == sk) return;
/* make sure to not pass oversized frames to the socket */ if (!ro->fd_frames && can_is_canfd_skb(oskb)) return;
if (can_is_canxl_skb(oskb)) { struct canxl_frame *cxl = (struct canxl_frame *)oskb->data;
/* make sure to not pass oversized frames to the socket */ if (!ro->xl_frames) return;
/* filter CAN XL VCID content */ if (ro->raw_vcid_opts.flags & CAN_RAW_XL_VCID_RX_FILTER) { /* apply VCID filter if user enabled the filter */ if ((cxl->prio & ro->rx_vcid_mask_shifted) !=
(ro->rx_vcid_shifted & ro->rx_vcid_mask_shifted)) return;
} else { /* no filter => do not forward VCID tagged frames */ if (cxl->prio & CANXL_VCID_MASK) return;
}
}
/* eliminate multiple filter matches for the same skb */ if (this_cpu_ptr(ro->uniq)->skb == oskb &&
this_cpu_ptr(ro->uniq)->skbcnt == can_skb_prv(oskb)->skbcnt) { if (!ro->join_filters) return;
this_cpu_inc(ro->uniq->join_rx_count); /* drop frame until all enabled filters matched */ if (this_cpu_ptr(ro->uniq)->join_rx_count < ro->count) return;
} else {
this_cpu_ptr(ro->uniq)->skb = oskb;
this_cpu_ptr(ro->uniq)->skbcnt = can_skb_prv(oskb)->skbcnt;
this_cpu_ptr(ro->uniq)->join_rx_count = 1; /* drop first frame to check all enabled filters? */ if (ro->join_filters && ro->count > 1) 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.
*/
if (len < RAW_MIN_NAMELEN) return -EINVAL; if (addr->can_family != AF_CAN) return -EINVAL;
rtnl_lock();
lock_sock(sk);
if (ro->bound && addr->can_ifindex == ro->ifindex) goto out;
if (addr->can_ifindex) {
dev = dev_get_by_index(sock_net(sk), addr->can_ifindex); if (!dev) {
err = -ENODEV; goto out;
} if (dev->type != ARPHRD_CAN) {
err = -ENODEV; goto out_put_dev;
}
if (!(dev->flags & IFF_UP))
notify_enetdown = 1;
ifindex = dev->ifindex;
/* filters set by default/setsockopt */
err = raw_enable_allfilters(sock_net(sk), dev, sk); if (err) goto out_put_dev;
} else {
ifindex = 0;
/* filters set by default/setsockopt */
err = raw_enable_allfilters(sock_net(sk), NULL, sk);
}
if (!err) { if (ro->bound) { /* unregister old filters */ if (ro->dev) {
raw_disable_allfilters(dev_net(ro->dev),
ro->dev, sk); /* drop reference to old ro->dev */
netdev_put(ro->dev, &ro->dev_tracker);
} else {
raw_disable_allfilters(sock_net(sk), NULL, sk);
}
}
ro->ifindex = ifindex;
ro->bound = 1; /* bind() ok -> hold a reference for new ro->dev */
ro->dev = dev; if (ro->dev)
netdev_hold(ro->dev, &ro->dev_tracker, GFP_KERNEL);
}
staticint raw_setsockopt(struct socket *sock, int level, int optname,
sockptr_t optval, unsignedint 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 fd_frames; 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;
if (optlen > CAN_RAW_FILTER_MAX * sizeof(struct can_filter)) return -EINVAL;
count = optlen / sizeof(struct can_filter);
if (count > 1) { /* filter does not fit into dfilter => alloc space */
filter = memdup_sockptr(optval, optlen); if (IS_ERR(filter)) return PTR_ERR(filter);
} elseif (count == 1) { if (copy_from_sockptr(&sfilter, optval, sizeof(sfilter))) return -EFAULT;
}
rtnl_lock();
lock_sock(sk);
dev = ro->dev; if (ro->bound && dev) { if (dev->reg_state != NETREG_REGISTERED) { if (count > 1)
kfree(filter);
err = -ENODEV; goto out_fil;
}
}
if (ro->bound) { /* (try to) register the new filters */ if (count == 1)
err = raw_enable_filters(sock_net(sk), dev, sk,
&sfilter, 1); else
err = raw_enable_filters(sock_net(sk), dev, sk,
filter, count); if (err) { if (count > 1)
kfree(filter); goto out_fil;
}
/* 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:
release_sock(sk);
rtnl_unlock();
break;
case CAN_RAW_ERR_FILTER: if (optlen != sizeof(err_mask)) return -EINVAL;
if (copy_from_sockptr(&err_mask, optval, optlen)) return -EFAULT;
err_mask &= CAN_ERR_MASK;
rtnl_lock();
lock_sock(sk);
dev = ro->dev; if (ro->bound && dev) { if (dev->reg_state != NETREG_REGISTERED) {
err = -ENODEV; goto out_err;
}
}
/* remove current error mask */ if (ro->bound) { /* (try to) register the new err_mask */
err = raw_enable_errfilter(sock_net(sk), dev, sk,
err_mask);
/* link new err_mask to the socket */
ro->err_mask = err_mask;
out_err:
release_sock(sk);
rtnl_unlock();
break;
case CAN_RAW_LOOPBACK: if (optlen != sizeof(ro->loopback)) return -EINVAL;
if (copy_from_sockptr(&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_sockptr(&ro->recv_own_msgs, optval, optlen)) return -EFAULT;
break;
case CAN_RAW_FD_FRAMES: if (optlen != sizeof(fd_frames)) return -EINVAL;
if (copy_from_sockptr(&fd_frames, optval, optlen)) return -EFAULT;
/* Enabling CAN XL includes CAN FD */ if (ro->xl_frames && !fd_frames) return -EINVAL;
ro->fd_frames = fd_frames; break;
case CAN_RAW_XL_FRAMES: if (optlen != sizeof(ro->xl_frames)) return -EINVAL;
if (copy_from_sockptr(&ro->xl_frames, optval, optlen)) return -EFAULT;
/* Enabling CAN XL includes CAN FD */ if (ro->xl_frames)
ro->fd_frames = ro->xl_frames; break;
case CAN_RAW_XL_VCID_OPTS: if (optlen != sizeof(ro->raw_vcid_opts)) return -EINVAL;
if (copy_from_sockptr(&ro->raw_vcid_opts, optval, optlen)) return -EFAULT;
/* prepare 32 bit values for handling in hot path */
ro->tx_vcid_shifted = ro->raw_vcid_opts.tx_vcid << CANXL_VCID_OFFSET;
ro->rx_vcid_shifted = ro->raw_vcid_opts.rx_vcid << CANXL_VCID_OFFSET;
ro->rx_vcid_mask_shifted = ro->raw_vcid_opts.rx_vcid_mask << CANXL_VCID_OFFSET; break;
case CAN_RAW_JOIN_FILTERS: if (optlen != sizeof(ro->join_filters)) return -EINVAL;
if (copy_from_sockptr(&ro->join_filters, optval, optlen)) return -EFAULT;
break;
default: return -ENOPROTOOPT;
} return err;
}
staticint 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;
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: { int err = 0;
lock_sock(sk); if (ro->count > 0) { int fsize = ro->count * sizeof(struct can_filter);
/* user space buffer to small for filter list? */ if (len < fsize) { /* return -ERANGE and needed space in optlen */
err = -ERANGE; if (put_user(fsize, optlen))
err = -EFAULT;
} else { 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;
case CAN_RAW_XL_FRAMES: if (len > sizeof(int))
len = sizeof(int);
val = &ro->xl_frames; break;
case CAN_RAW_XL_VCID_OPTS: { int err = 0;
/* user space buffer to small for VCID opts? */ if (len < sizeof(ro->raw_vcid_opts)) { /* return -ERANGE and needed space in optlen */
err = -ERANGE; if (put_user(sizeof(ro->raw_vcid_opts), optlen))
err = -EFAULT;
} else { if (len > sizeof(ro->raw_vcid_opts))
len = sizeof(ro->raw_vcid_opts); if (copy_to_user(optval, &ro->raw_vcid_opts, len))
err = -EFAULT;
} if (!err)
err = put_user(len, optlen); return err;
} case CAN_RAW_JOIN_FILTERS: if (len > sizeof(int))
len = sizeof(int);
val = &ro->join_filters; break;
default: return -ENOPROTOOPT;
}
if (put_user(len, optlen)) return -EFAULT; if (copy_to_user(optval, val, len)) return -EFAULT; return 0;
}
/* sanitize non CAN XL bits */
cxl->prio &= (CANXL_PRIO_MASK | CANXL_VCID_MASK);
/* clear VCID in CAN XL frame if pass through is disabled */ if (!(ro->raw_vcid_opts.flags & CAN_RAW_XL_VCID_TX_PASS))
cxl->prio &= CANXL_PRIO_MASK;
/* set VCID in CAN XL frame if enabled */ if (ro->raw_vcid_opts.flags & CAN_RAW_XL_VCID_TX_SET) {
cxl->prio &= CANXL_PRIO_MASK;
cxl->prio |= ro->tx_vcid_shifted;
}
}
staticunsignedint raw_check_txframe(struct raw_sock *ro, struct sk_buff *skb, int mtu)
{ /* Classical CAN -> no checks for flags and device capabilities */ if (can_is_can_skb(skb)) return CAN_MTU;
/* CAN FD -> needs to be enabled and a CAN FD or CAN XL device */ if (ro->fd_frames && can_is_canfd_skb(skb) &&
(mtu == CANFD_MTU || can_is_canxl_dev_mtu(mtu))) return CANFD_MTU;
/* CAN XL -> needs to be enabled and a CAN XL device */ if (ro->xl_frames && can_is_canxl_skb(skb) &&
can_is_canxl_dev_mtu(mtu)) return CANXL_MTU;
/* assign the flags that have been recorded in raw_rcv() */
msg->msg_flags |= *(raw_flags(skb));
skb_free_datagram(sk, skb);
return size;
}
staticint raw_sock_no_ioctlcmd(struct socket *sock, unsignedint cmd, unsignedlong arg)
{ /* no ioctls for socket layer -> hand it down to NIC layer */ return -ENOIOCTLCMD;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.