net: hdlc_fr: Add support for any Ethertype
authorXie He <xie.he.0141@gmail.com>
Sat, 31 Oct 2020 18:10:43 +0000 (11:10 -0700)
committerJakub Kicinski <kuba@kernel.org>
Tue, 3 Nov 2020 23:19:21 +0000 (15:19 -0800)
Change the fr_rx function to make this driver support any Ethertype
when receiving skbs on normal (non-Ethernet-emulating) PVC devices.
(This driver is already able to handle any Ethertype when sending.)

Originally in the fr_rx function, the code that parses the long (10-byte)
header only recognizes a few Ethertype values and drops frames with other
Ethertype values. This patch replaces this code to make fr_rx support
any Ethertype. This patch also creates a new function fr_snap_parse as
part of the new code.

Cc: Krzysztof Halasa <khc@pm.waw.pl>
Signed-off-by: Xie He <xie.he.0141@gmail.com>
Acked-by: Willem de Bruijn <willemb@google.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/wan/hdlc_fr.c

index 98444f1..0720f5f 100644 (file)
@@ -871,6 +871,45 @@ static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb)
        return 0;
 }
 
+static int fr_snap_parse(struct sk_buff *skb, struct pvc_device *pvc)
+{
+       /* OUI 00-00-00 indicates an Ethertype follows */
+       if (skb->data[0] == 0x00 &&
+           skb->data[1] == 0x00 &&
+           skb->data[2] == 0x00) {
+               if (!pvc->main)
+                       return -1;
+               skb->dev = pvc->main;
+               skb->protocol = *(__be16 *)(skb->data + 3); /* Ethertype */
+               skb_pull(skb, 5);
+               skb_reset_mac_header(skb);
+               return 0;
+
+       /* OUI 00-80-C2 stands for the 802.1 organization */
+       } else if (skb->data[0] == 0x00 &&
+                  skb->data[1] == 0x80 &&
+                  skb->data[2] == 0xC2) {
+               /* PID 00-07 stands for Ethernet frames without FCS */
+               if (skb->data[3] == 0x00 &&
+                   skb->data[4] == 0x07) {
+                       if (!pvc->ether)
+                               return -1;
+                       skb_pull(skb, 5);
+                       if (skb->len < ETH_HLEN)
+                               return -1;
+                       skb->protocol = eth_type_trans(skb, pvc->ether);
+                       return 0;
+
+               /* PID unsupported */
+               } else {
+                       return -1;
+               }
+
+       /* OUI unsupported */
+       } else {
+               return -1;
+       }
+}
 
 static int fr_rx(struct sk_buff *skb)
 {
@@ -945,35 +984,19 @@ static int fr_rx(struct sk_buff *skb)
                skb->protocol = htons(ETH_P_IPV6);
                skb_reset_mac_header(skb);
 
-       } else if (skb->len > 10 && data[3] == FR_PAD &&
-                  data[4] == NLPID_SNAP && data[5] == FR_PAD) {
-               u16 oui = ntohs(*(__be16*)(data + 6));
-               u16 pid = ntohs(*(__be16*)(data + 8));
-               skb_pull(skb, 10);
-
-               switch ((((u32)oui) << 16) | pid) {
-               case ETH_P_ARP: /* routed frame with SNAP */
-               case ETH_P_IPX:
-               case ETH_P_IP:  /* a long variant */
-               case ETH_P_IPV6:
-                       if (!pvc->main)
-                               goto rx_drop;
-                       skb->dev = pvc->main;
-                       skb->protocol = htons(pid);
-                       skb_reset_mac_header(skb);
-                       break;
-
-               case 0x80C20007: /* bridged Ethernet frame */
-                       if (!pvc->ether)
+       } else if (data[3] == FR_PAD) {
+               if (skb->len < 5)
+                       goto rx_error;
+               if (data[4] == NLPID_SNAP) { /* A SNAP header follows */
+                       skb_pull(skb, 5);
+                       if (skb->len < 5) /* Incomplete SNAP header */
+                               goto rx_error;
+                       if (fr_snap_parse(skb, pvc))
                                goto rx_drop;
-                       skb->protocol = eth_type_trans(skb, pvc->ether);
-                       break;
-
-               default:
-                       netdev_info(frad, "Unsupported protocol, OUI=%x PID=%x\n",
-                                   oui, pid);
+               } else {
                        goto rx_drop;
                }
+
        } else {
                netdev_info(frad, "Unsupported protocol, NLPID=%x length=%i\n",
                            data[3], skb->len);