]> git.ipfire.org Git - people/arne_f/kernel.git/commitdiff
drivers/net/wan/hdlc_fr: Correctly handle special skb->protocol values
authorXie He <xie.he.0141@gmail.com>
Mon, 28 Sep 2020 12:56:43 +0000 (05:56 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 10 Nov 2020 09:22:14 +0000 (10:22 +0100)
[ Upstream commit 8306266c1d51aac9aa7aa907fe99032a58c6382c ]

The fr_hard_header function is used to prepend the header to skbs before
transmission. It is used in 3 situations:
1) When a control packet is generated internally in this driver;
2) When a user sends an skb on an Ethernet-emulating PVC device;
3) When a user sends an skb on a normal PVC device.

These 3 situations need to be handled differently by fr_hard_header.
Different headers should be prepended to the skb in different situations.

Currently fr_hard_header distinguishes these 3 situations using
skb->protocol. For situation 1 and 2, a special skb->protocol value
will be assigned before calling fr_hard_header, so that it can recognize
these 2 situations. All skb->protocol values other than these special ones
are treated by fr_hard_header as situation 3.

However, it is possible that in situation 3, the user sends an skb with
one of the special skb->protocol values. In this case, fr_hard_header
would incorrectly treat it as situation 1 or 2.

This patch tries to solve this issue by using skb->dev instead of
skb->protocol to distinguish between these 3 situations. For situation
1, skb->dev would be NULL; for situation 2, skb->dev->type would be
ARPHRD_ETHER; and for situation 3, skb->dev->type would be ARPHRD_DLCI.

This way fr_hard_header would be able to distinguish these 3 situations
correctly regardless what skb->protocol value the user tries to use in
situation 3.

Cc: Krzysztof Halasa <khc@pm.waw.pl>
Signed-off-by: Xie He <xie.he.0141@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/net/wan/hdlc_fr.c

index 74d46f7e77eaa67c51d3cfdbd56808ff88fa62b9..045158fab8fe6498a6df9a30170a36cb29e1bd9f 100644 (file)
@@ -275,63 +275,69 @@ static inline struct net_device **get_dev_p(struct pvc_device *pvc,
 
 static int fr_hard_header(struct sk_buff **skb_p, u16 dlci)
 {
-       u16 head_len;
        struct sk_buff *skb = *skb_p;
 
-       switch (skb->protocol) {
-       case cpu_to_be16(NLPID_CCITT_ANSI_LMI):
-               head_len = 4;
-               skb_push(skb, head_len);
-               skb->data[3] = NLPID_CCITT_ANSI_LMI;
-               break;
-
-       case cpu_to_be16(NLPID_CISCO_LMI):
-               head_len = 4;
-               skb_push(skb, head_len);
-               skb->data[3] = NLPID_CISCO_LMI;
-               break;
-
-       case cpu_to_be16(ETH_P_IP):
-               head_len = 4;
-               skb_push(skb, head_len);
-               skb->data[3] = NLPID_IP;
-               break;
-
-       case cpu_to_be16(ETH_P_IPV6):
-               head_len = 4;
-               skb_push(skb, head_len);
-               skb->data[3] = NLPID_IPV6;
-               break;
-
-       case cpu_to_be16(ETH_P_802_3):
-               head_len = 10;
-               if (skb_headroom(skb) < head_len) {
-                       struct sk_buff *skb2 = skb_realloc_headroom(skb,
-                                                                   head_len);
+       if (!skb->dev) { /* Control packets */
+               switch (dlci) {
+               case LMI_CCITT_ANSI_DLCI:
+                       skb_push(skb, 4);
+                       skb->data[3] = NLPID_CCITT_ANSI_LMI;
+                       break;
+
+               case LMI_CISCO_DLCI:
+                       skb_push(skb, 4);
+                       skb->data[3] = NLPID_CISCO_LMI;
+                       break;
+
+               default:
+                       return -EINVAL;
+               }
+
+       } else if (skb->dev->type == ARPHRD_DLCI) {
+               switch (skb->protocol) {
+               case htons(ETH_P_IP):
+                       skb_push(skb, 4);
+                       skb->data[3] = NLPID_IP;
+                       break;
+
+               case htons(ETH_P_IPV6):
+                       skb_push(skb, 4);
+                       skb->data[3] = NLPID_IPV6;
+                       break;
+
+               default:
+                       skb_push(skb, 10);
+                       skb->data[3] = FR_PAD;
+                       skb->data[4] = NLPID_SNAP;
+                       /* OUI 00-00-00 indicates an Ethertype follows */
+                       skb->data[5] = 0x00;
+                       skb->data[6] = 0x00;
+                       skb->data[7] = 0x00;
+                       /* This should be an Ethertype: */
+                       *(__be16 *)(skb->data + 8) = skb->protocol;
+               }
+
+       } else if (skb->dev->type == ARPHRD_ETHER) {
+               if (skb_headroom(skb) < 10) {
+                       struct sk_buff *skb2 = skb_realloc_headroom(skb, 10);
                        if (!skb2)
                                return -ENOBUFS;
                        dev_kfree_skb(skb);
                        skb = *skb_p = skb2;
                }
-               skb_push(skb, head_len);
+               skb_push(skb, 10);
                skb->data[3] = FR_PAD;
                skb->data[4] = NLPID_SNAP;
-               skb->data[5] = FR_PAD;
+               /* OUI 00-80-C2 stands for the 802.1 organization */
+               skb->data[5] = 0x00;
                skb->data[6] = 0x80;
                skb->data[7] = 0xC2;
+               /* PID 00-07 stands for Ethernet frames without FCS */
                skb->data[8] = 0x00;
-               skb->data[9] = 0x07; /* bridged Ethernet frame w/out FCS */
-               break;
+               skb->data[9] = 0x07;
 
-       default:
-               head_len = 10;
-               skb_push(skb, head_len);
-               skb->data[3] = FR_PAD;
-               skb->data[4] = NLPID_SNAP;
-               skb->data[5] = FR_PAD;
-               skb->data[6] = FR_PAD;
-               skb->data[7] = FR_PAD;
-               *(__be16*)(skb->data + 8) = skb->protocol;
+       } else {
+               return -EINVAL;
        }
 
        dlci_to_q922(skb->data, dlci);
@@ -427,8 +433,8 @@ static netdev_tx_t pvc_xmit(struct sk_buff *skb, struct net_device *dev)
                                skb_put(skb, pad);
                                memset(skb->data + len, 0, pad);
                        }
-                       skb->protocol = cpu_to_be16(ETH_P_802_3);
                }
+               skb->dev = dev;
                if (!fr_hard_header(&skb, pvc->dlci)) {
                        dev->stats.tx_bytes += skb->len;
                        dev->stats.tx_packets++;
@@ -496,10 +502,8 @@ static void fr_lmi_send(struct net_device *dev, int fullrep)
        memset(skb->data, 0, len);
        skb_reserve(skb, 4);
        if (lmi == LMI_CISCO) {
-               skb->protocol = cpu_to_be16(NLPID_CISCO_LMI);
                fr_hard_header(&skb, LMI_CISCO_DLCI);
        } else {
-               skb->protocol = cpu_to_be16(NLPID_CCITT_ANSI_LMI);
                fr_hard_header(&skb, LMI_CCITT_ANSI_DLCI);
        }
        data = skb_tail_pointer(skb);