| From 4f7a91ca56457bd3b42563447766a8311033147f Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Mon, 28 Sep 2020 05:56:43 -0700 |
| Subject: drivers/net/wan/hdlc_fr: Correctly handle special skb->protocol |
| values |
| |
| From: Xie He <xie.he.0141@gmail.com> |
| |
| [ 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 | 98 ++++++++++++++++++++------------------- |
| 1 file changed, 51 insertions(+), 47 deletions(-) |
| |
| diff --git a/drivers/net/wan/hdlc_fr.c b/drivers/net/wan/hdlc_fr.c |
| index d6cfd51613ed8..3a44dad87602d 100644 |
| --- a/drivers/net/wan/hdlc_fr.c |
| +++ b/drivers/net/wan/hdlc_fr.c |
| @@ -273,63 +273,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); |
| @@ -425,8 +431,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++; |
| @@ -494,10 +500,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); |
| -- |
| 2.27.0 |
| |