forked from Minki/linux
can: skb: unify skb CAN frame identification helpers
Replace open coded checks for sk_buffs containing Classical CAN and CAN FD frame structures as a preparation for CAN XL support. With the added length check the unintended processing of CAN XL frames having the CANXL_XLF bit set can be suppressed even when the skb->len fits to non CAN XL frames. The CAN_RAW socket needs a rework to use these helpers. Therefore the use of these helpers is postponed to the CAN_RAW CAN XL integration. The J1939 protocol gets a check for Classical CAN frames too. Acked-by: Vincent Mailhol <mailhol.vincent@wanadoo.fr> Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net> Link: https://lore.kernel.org/all/20220912170725.120748-2-socketcan@hartkopp.net Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
This commit is contained in:
parent
1c679f9173
commit
96a7457a14
@ -299,18 +299,20 @@ static bool can_skb_headroom_valid(struct net_device *dev, struct sk_buff *skb)
|
|||||||
/* Drop a given socketbuffer if it does not contain a valid CAN frame. */
|
/* Drop a given socketbuffer if it does not contain a valid CAN frame. */
|
||||||
bool can_dropped_invalid_skb(struct net_device *dev, struct sk_buff *skb)
|
bool can_dropped_invalid_skb(struct net_device *dev, struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
const struct canfd_frame *cfd = (struct canfd_frame *)skb->data;
|
|
||||||
struct can_priv *priv = netdev_priv(dev);
|
struct can_priv *priv = netdev_priv(dev);
|
||||||
|
|
||||||
if (skb->protocol == htons(ETH_P_CAN)) {
|
switch (ntohs(skb->protocol)) {
|
||||||
if (unlikely(skb->len != CAN_MTU ||
|
case ETH_P_CAN:
|
||||||
cfd->len > CAN_MAX_DLEN))
|
if (!can_is_can_skb(skb))
|
||||||
goto inval_skb;
|
goto inval_skb;
|
||||||
} else if (skb->protocol == htons(ETH_P_CANFD)) {
|
break;
|
||||||
if (unlikely(skb->len != CANFD_MTU ||
|
|
||||||
cfd->len > CANFD_MAX_DLEN))
|
case ETH_P_CANFD:
|
||||||
|
if (!can_is_canfd_skb(skb))
|
||||||
goto inval_skb;
|
goto inval_skb;
|
||||||
} else {
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
goto inval_skb;
|
goto inval_skb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,10 +97,20 @@ static inline struct sk_buff *can_create_echo_skb(struct sk_buff *skb)
|
|||||||
return nskb;
|
return nskb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool can_is_can_skb(const struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
struct can_frame *cf = (struct can_frame *)skb->data;
|
||||||
|
|
||||||
|
/* the CAN specific type of skb is identified by its data length */
|
||||||
|
return (skb->len == CAN_MTU && cf->len <= CAN_MAX_DLEN);
|
||||||
|
}
|
||||||
|
|
||||||
static inline bool can_is_canfd_skb(const struct sk_buff *skb)
|
static inline bool can_is_canfd_skb(const struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
|
struct canfd_frame *cfd = (struct canfd_frame *)skb->data;
|
||||||
|
|
||||||
/* the CAN specific type of skb is identified by its data length */
|
/* the CAN specific type of skb is identified by its data length */
|
||||||
return skb->len == CANFD_MTU;
|
return (skb->len == CANFD_MTU && cfd->len <= CANFD_MAX_DLEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* !_CAN_SKB_H */
|
#endif /* !_CAN_SKB_H */
|
||||||
|
@ -199,27 +199,19 @@ static int can_create(struct net *net, struct socket *sock, int protocol,
|
|||||||
int can_send(struct sk_buff *skb, int loop)
|
int can_send(struct sk_buff *skb, int loop)
|
||||||
{
|
{
|
||||||
struct sk_buff *newskb = NULL;
|
struct sk_buff *newskb = NULL;
|
||||||
struct canfd_frame *cfd = (struct canfd_frame *)skb->data;
|
|
||||||
struct can_pkg_stats *pkg_stats = dev_net(skb->dev)->can.pkg_stats;
|
struct can_pkg_stats *pkg_stats = dev_net(skb->dev)->can.pkg_stats;
|
||||||
int err = -EINVAL;
|
int err = -EINVAL;
|
||||||
|
|
||||||
if (skb->len == CAN_MTU) {
|
if (can_is_can_skb(skb)) {
|
||||||
skb->protocol = htons(ETH_P_CAN);
|
skb->protocol = htons(ETH_P_CAN);
|
||||||
if (unlikely(cfd->len > CAN_MAX_DLEN))
|
} else if (can_is_canfd_skb(skb)) {
|
||||||
goto inval_skb;
|
|
||||||
} else if (skb->len == CANFD_MTU) {
|
|
||||||
skb->protocol = htons(ETH_P_CANFD);
|
skb->protocol = htons(ETH_P_CANFD);
|
||||||
if (unlikely(cfd->len > CANFD_MAX_DLEN))
|
|
||||||
goto inval_skb;
|
|
||||||
} else {
|
} else {
|
||||||
goto inval_skb;
|
goto inval_skb;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Make sure the CAN frame can pass the selected CAN netdevice.
|
/* Make sure the CAN frame can pass the selected CAN netdevice. */
|
||||||
* As structs can_frame and canfd_frame are similar, we can provide
|
if (unlikely(skb->len > skb->dev->mtu)) {
|
||||||
* 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;
|
err = -EMSGSIZE;
|
||||||
goto inval_skb;
|
goto inval_skb;
|
||||||
}
|
}
|
||||||
@ -678,53 +670,31 @@ static void can_receive(struct sk_buff *skb, struct net_device *dev)
|
|||||||
static int can_rcv(struct sk_buff *skb, struct net_device *dev,
|
static int can_rcv(struct sk_buff *skb, struct net_device *dev,
|
||||||
struct packet_type *pt, struct net_device *orig_dev)
|
struct packet_type *pt, struct net_device *orig_dev)
|
||||||
{
|
{
|
||||||
struct canfd_frame *cfd = (struct canfd_frame *)skb->data;
|
if (unlikely(dev->type != ARPHRD_CAN || (!can_is_can_skb(skb)))) {
|
||||||
|
|
||||||
if (unlikely(dev->type != ARPHRD_CAN || skb->len != CAN_MTU)) {
|
|
||||||
pr_warn_once("PF_CAN: dropped non conform CAN skbuff: dev type %d, len %d\n",
|
pr_warn_once("PF_CAN: dropped non conform CAN skbuff: dev type %d, len %d\n",
|
||||||
dev->type, skb->len);
|
dev->type, skb->len);
|
||||||
goto free_skb;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This check is made separately since cfd->len would be uninitialized if skb->len = 0. */
|
kfree_skb(skb);
|
||||||
if (unlikely(cfd->len > CAN_MAX_DLEN)) {
|
return NET_RX_DROP;
|
||||||
pr_warn_once("PF_CAN: dropped non conform CAN skbuff: dev type %d, len %d, datalen %d\n",
|
|
||||||
dev->type, skb->len, cfd->len);
|
|
||||||
goto free_skb;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
can_receive(skb, dev);
|
can_receive(skb, dev);
|
||||||
return NET_RX_SUCCESS;
|
return NET_RX_SUCCESS;
|
||||||
|
|
||||||
free_skb:
|
|
||||||
kfree_skb(skb);
|
|
||||||
return NET_RX_DROP;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int canfd_rcv(struct sk_buff *skb, struct net_device *dev,
|
static int canfd_rcv(struct sk_buff *skb, struct net_device *dev,
|
||||||
struct packet_type *pt, struct net_device *orig_dev)
|
struct packet_type *pt, struct net_device *orig_dev)
|
||||||
{
|
{
|
||||||
struct canfd_frame *cfd = (struct canfd_frame *)skb->data;
|
if (unlikely(dev->type != ARPHRD_CAN || (!can_is_canfd_skb(skb)))) {
|
||||||
|
|
||||||
if (unlikely(dev->type != ARPHRD_CAN || skb->len != CANFD_MTU)) {
|
|
||||||
pr_warn_once("PF_CAN: dropped non conform CAN FD skbuff: dev type %d, len %d\n",
|
pr_warn_once("PF_CAN: dropped non conform CAN FD skbuff: dev type %d, len %d\n",
|
||||||
dev->type, skb->len);
|
dev->type, skb->len);
|
||||||
goto free_skb;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This check is made separately since cfd->len would be uninitialized if skb->len = 0. */
|
kfree_skb(skb);
|
||||||
if (unlikely(cfd->len > CANFD_MAX_DLEN)) {
|
return NET_RX_DROP;
|
||||||
pr_warn_once("PF_CAN: dropped non conform CAN FD skbuff: dev type %d, len %d, datalen %d\n",
|
|
||||||
dev->type, skb->len, cfd->len);
|
|
||||||
goto free_skb;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
can_receive(skb, dev);
|
can_receive(skb, dev);
|
||||||
return NET_RX_SUCCESS;
|
return NET_RX_SUCCESS;
|
||||||
|
|
||||||
free_skb:
|
|
||||||
kfree_skb(skb);
|
|
||||||
return NET_RX_DROP;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* af_can protocol functions */
|
/* af_can protocol functions */
|
||||||
|
@ -648,8 +648,13 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
/* make sure to handle the correct frame type (CAN / CAN FD) */
|
/* make sure to handle the correct frame type (CAN / CAN FD) */
|
||||||
if (skb->len != op->cfsiz)
|
if (op->flags & CAN_FD_FRAME) {
|
||||||
return;
|
if (!can_is_canfd_skb(skb))
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
if (!can_is_can_skb(skb))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* disable timeout */
|
/* disable timeout */
|
||||||
hrtimer_cancel(&op->timer);
|
hrtimer_cancel(&op->timer);
|
||||||
|
@ -463,10 +463,10 @@ static void can_can_gw_rcv(struct sk_buff *skb, void *data)
|
|||||||
|
|
||||||
/* process strictly Classic CAN or CAN FD frames */
|
/* process strictly Classic CAN or CAN FD frames */
|
||||||
if (gwj->flags & CGW_FLAGS_CAN_FD) {
|
if (gwj->flags & CGW_FLAGS_CAN_FD) {
|
||||||
if (skb->len != CANFD_MTU)
|
if (!can_is_canfd_skb(skb))
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
if (skb->len != CAN_MTU)
|
if (!can_is_can_skb(skb))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -669,7 +669,7 @@ static void isotp_rcv(struct sk_buff *skb, void *data)
|
|||||||
if (cf->len <= CAN_MAX_DLEN) {
|
if (cf->len <= CAN_MAX_DLEN) {
|
||||||
isotp_rcv_sf(sk, cf, SF_PCI_SZ4 + ae, skb, sf_dl);
|
isotp_rcv_sf(sk, cf, SF_PCI_SZ4 + ae, skb, sf_dl);
|
||||||
} else {
|
} else {
|
||||||
if (skb->len == CANFD_MTU) {
|
if (can_is_canfd_skb(skb)) {
|
||||||
/* We have a CAN FD frame and CAN_DL is greater than 8:
|
/* We have a CAN FD frame and CAN_DL is greater than 8:
|
||||||
* Only frames with the SF_DL == 0 ESC value are valid.
|
* Only frames with the SF_DL == 0 ESC value are valid.
|
||||||
*
|
*
|
||||||
|
@ -42,6 +42,10 @@ static void j1939_can_recv(struct sk_buff *iskb, void *data)
|
|||||||
struct j1939_sk_buff_cb *skcb, *iskcb;
|
struct j1939_sk_buff_cb *skcb, *iskcb;
|
||||||
struct can_frame *cf;
|
struct can_frame *cf;
|
||||||
|
|
||||||
|
/* make sure we only get Classical CAN frames */
|
||||||
|
if (!can_is_can_skb(iskb))
|
||||||
|
return;
|
||||||
|
|
||||||
/* create a copy of the skb
|
/* create a copy of the skb
|
||||||
* j1939 only delivers the real data bytes,
|
* j1939 only delivers the real data bytes,
|
||||||
* the header goes into sockaddr.
|
* the header goes into sockaddr.
|
||||||
|
Loading…
Reference in New Issue
Block a user