can: canxl: update CAN infrastructure for CAN XL frames
authorOliver Hartkopp <socketcan@hartkopp.net>
Mon, 12 Sep 2022 17:07:23 +0000 (19:07 +0200)
committerMarc Kleine-Budde <mkl@pengutronix.de>
Thu, 15 Sep 2022 07:08:09 +0000 (09:08 +0200)
- add new ETH_P_CANXL ethernet protocol type
- update skb checks for CAN XL
- add alloc_canxl_skb() which now needs a data length parameter
- introduce init_can_skb_reserve() to reduce code duplication

Acked-by: Vincent Mailhol <mailhol.vincent@wanadoo.fr>
Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
Link: https://lore.kernel.org/all/20220912170725.120748-6-socketcan@hartkopp.net
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
drivers/net/can/dev/skb.c
include/linux/can/skb.h
include/uapi/linux/if_ether.h
net/can/af_can.c

index adb413b..791a51e 100644 (file)
@@ -187,6 +187,20 @@ void can_free_echo_skb(struct net_device *dev, unsigned int idx,
 }
 EXPORT_SYMBOL_GPL(can_free_echo_skb);
 
+/* fill common values for CAN sk_buffs */
+static void init_can_skb_reserve(struct sk_buff *skb)
+{
+       skb->pkt_type = PACKET_BROADCAST;
+       skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+       skb_reset_mac_header(skb);
+       skb_reset_network_header(skb);
+       skb_reset_transport_header(skb);
+
+       can_skb_reserve(skb);
+       can_skb_prv(skb)->skbcnt = 0;
+}
+
 struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf)
 {
        struct sk_buff *skb;
@@ -200,16 +214,8 @@ struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf)
        }
 
        skb->protocol = htons(ETH_P_CAN);
-       skb->pkt_type = PACKET_BROADCAST;
-       skb->ip_summed = CHECKSUM_UNNECESSARY;
-
-       skb_reset_mac_header(skb);
-       skb_reset_network_header(skb);
-       skb_reset_transport_header(skb);
-
-       can_skb_reserve(skb);
+       init_can_skb_reserve(skb);
        can_skb_prv(skb)->ifindex = dev->ifindex;
-       can_skb_prv(skb)->skbcnt = 0;
 
        *cf = skb_put_zero(skb, sizeof(struct can_frame));
 
@@ -231,16 +237,8 @@ struct sk_buff *alloc_canfd_skb(struct net_device *dev,
        }
 
        skb->protocol = htons(ETH_P_CANFD);
-       skb->pkt_type = PACKET_BROADCAST;
-       skb->ip_summed = CHECKSUM_UNNECESSARY;
-
-       skb_reset_mac_header(skb);
-       skb_reset_network_header(skb);
-       skb_reset_transport_header(skb);
-
-       can_skb_reserve(skb);
+       init_can_skb_reserve(skb);
        can_skb_prv(skb)->ifindex = dev->ifindex;
-       can_skb_prv(skb)->skbcnt = 0;
 
        *cfd = skb_put_zero(skb, sizeof(struct canfd_frame));
 
@@ -251,6 +249,39 @@ struct sk_buff *alloc_canfd_skb(struct net_device *dev,
 }
 EXPORT_SYMBOL_GPL(alloc_canfd_skb);
 
+struct sk_buff *alloc_canxl_skb(struct net_device *dev,
+                               struct canxl_frame **cxl,
+                               unsigned int data_len)
+{
+       struct sk_buff *skb;
+
+       if (data_len < CANXL_MIN_DLEN || data_len > CANXL_MAX_DLEN)
+               goto out_error;
+
+       skb = netdev_alloc_skb(dev, sizeof(struct can_skb_priv) +
+                              CANXL_HDR_SIZE + data_len);
+       if (unlikely(!skb))
+               goto out_error;
+
+       skb->protocol = htons(ETH_P_CANXL);
+       init_can_skb_reserve(skb);
+       can_skb_prv(skb)->ifindex = dev->ifindex;
+
+       *cxl = skb_put_zero(skb, CANXL_HDR_SIZE + data_len);
+
+       /* set CAN XL flag and length information by default */
+       (*cxl)->flags = CANXL_XLF;
+       (*cxl)->len = data_len;
+
+       return skb;
+
+out_error:
+       *cxl = NULL;
+
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(alloc_canxl_skb);
+
 struct sk_buff *alloc_can_err_skb(struct net_device *dev, struct can_frame **cf)
 {
        struct sk_buff *skb;
@@ -319,6 +350,11 @@ bool can_dropped_invalid_skb(struct net_device *dev, struct sk_buff *skb)
                        goto inval_skb;
                break;
 
+       case ETH_P_CANXL:
+               if (!can_is_canxl_skb(skb))
+                       goto inval_skb;
+               break;
+
        default:
                goto inval_skb;
        }
index ddffc2f..1abc25a 100644 (file)
@@ -30,6 +30,9 @@ void can_free_echo_skb(struct net_device *dev, unsigned int idx,
 struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf);
 struct sk_buff *alloc_canfd_skb(struct net_device *dev,
                                struct canfd_frame **cfd);
+struct sk_buff *alloc_canxl_skb(struct net_device *dev,
+                               struct canxl_frame **cxl,
+                               unsigned int data_len);
 struct sk_buff *alloc_can_err_skb(struct net_device *dev,
                                  struct can_frame **cf);
 bool can_dropped_invalid_skb(struct net_device *dev, struct sk_buff *skb);
@@ -114,11 +117,29 @@ static inline bool can_is_canfd_skb(const struct sk_buff *skb)
        return (skb->len == CANFD_MTU && cfd->len <= CANFD_MAX_DLEN);
 }
 
-/* get length element value from can[fd]_frame structure */
+static inline bool can_is_canxl_skb(const struct sk_buff *skb)
+{
+       const struct canxl_frame *cxl = (struct canxl_frame *)skb->data;
+
+       if (skb->len < CANXL_HDR_SIZE + CANXL_MIN_DLEN || skb->len > CANXL_MTU)
+               return false;
+
+       /* this also checks valid CAN XL data length boundaries */
+       if (skb->len != CANXL_HDR_SIZE + cxl->len)
+               return false;
+
+       return cxl->flags & CANXL_XLF;
+}
+
+/* get length element value from can[|fd|xl]_frame structure */
 static inline unsigned int can_skb_get_len_val(struct sk_buff *skb)
 {
+       const struct canxl_frame *cxl = (struct canxl_frame *)skb->data;
        const struct canfd_frame *cfd = (struct canfd_frame *)skb->data;
 
+       if (can_is_canxl_skb(skb))
+               return cxl->len;
+
        return cfd->len;
 }
 
index d370165..69e0457 100644 (file)
 #define ETH_P_LOCALTALK 0x0009         /* Localtalk pseudo type        */
 #define ETH_P_CAN      0x000C          /* CAN: Controller Area Network */
 #define ETH_P_CANFD    0x000D          /* CANFD: CAN flexible data rate*/
+#define ETH_P_CANXL    0x000E          /* CANXL: eXtended frame Length */
 #define ETH_P_PPPTALK  0x0010          /* Dummy type for Atalk over PPP*/
 #define ETH_P_TR_802_2 0x0011          /* 802.2 frames                 */
 #define ETH_P_MOBITEX  0x0015          /* Mobitex (kaz@cafe.net)       */
index 072a6a5..9503ab1 100644 (file)
@@ -202,7 +202,9 @@ int can_send(struct sk_buff *skb, int loop)
        struct can_pkg_stats *pkg_stats = dev_net(skb->dev)->can.pkg_stats;
        int err = -EINVAL;
 
-       if (can_is_can_skb(skb)) {
+       if (can_is_canxl_skb(skb)) {
+               skb->protocol = htons(ETH_P_CANXL);
+       } else if (can_is_can_skb(skb)) {
                skb->protocol = htons(ETH_P_CAN);
        } else if (can_is_canfd_skb(skb)) {
                struct canfd_frame *cfd = (struct canfd_frame *)skb->data;
@@ -702,6 +704,21 @@ static int canfd_rcv(struct sk_buff *skb, struct net_device *dev,
        return NET_RX_SUCCESS;
 }
 
+static int canxl_rcv(struct sk_buff *skb, struct net_device *dev,
+                    struct packet_type *pt, struct net_device *orig_dev)
+{
+       if (unlikely(dev->type != ARPHRD_CAN || (!can_is_canxl_skb(skb)))) {
+               pr_warn_once("PF_CAN: dropped non conform CAN XL skbuff: dev type %d, len %d\n",
+                            dev->type, skb->len);
+
+               kfree_skb(skb);
+               return NET_RX_DROP;
+       }
+
+       can_receive(skb, dev);
+       return NET_RX_SUCCESS;
+}
+
 /* af_can protocol functions */
 
 /**
@@ -826,6 +843,11 @@ static struct packet_type canfd_packet __read_mostly = {
        .func = canfd_rcv,
 };
 
+static struct packet_type canxl_packet __read_mostly = {
+       .type = cpu_to_be16(ETH_P_CANXL),
+       .func = canxl_rcv,
+};
+
 static const struct net_proto_family can_family_ops = {
        .family = PF_CAN,
        .create = can_create,
@@ -865,6 +887,7 @@ static __init int can_init(void)
 
        dev_add_pack(&can_packet);
        dev_add_pack(&canfd_packet);
+       dev_add_pack(&canxl_packet);
 
        return 0;