summaryrefslogtreecommitdiff
path: root/drivers/net
diff options
context:
space:
mode:
authorJakub Kicinski <kuba@kernel.org>2020-11-03 15:19:26 -0800
committerJakub Kicinski <kuba@kernel.org>2020-11-03 15:19:27 -0800
commit12c9ee3cb4dc554abced62e9e54ff86bb74fc08c (patch)
treeb03ee3ca633fd7d35cc9f77d2721ed9a0e80e4b3 /drivers/net
parent16b5f5ce351f8709a6b518cc3cbf240c378305bf (diff)
parent54b77a77e30a5170a25cfd2f211e08ea5a6c0c4c (diff)
Merge branch 'net-hdlc_fr-improve-fr_rx-and-add-support-for-any-ethertype'
Xie He says: ==================== net: hdlc_fr: Improve fr_rx and add support for any Ethertype The main purpose of this series is the last patch. The previous 4 patches are just code clean-ups so that the last patch will not make the code too messy. The patches must be applied in sequence. The receiving code of this driver doesn't support arbitrary Ethertype values. It only recognizes a few known Ethertypes when receiving and drops skbs with other Ethertypes. However, the standard document RFC 2427 allows Frame Relay to support any Ethertype values. This series adds support for this. Change from v6: Remove the explanation about why only a 2-byte address field is accepted because I think it is inadequate and unnecessary. Change from v5: Small fix to the commit messages. Change from v4: Drop the change related to the stats.rx_dropped count. Improve the commit message by stating why only a 2-byte address field is accepted. Change from v3: Split the last patch into 2 patches. Improve the commit message about the stats.rx_dropped count. Change from v2: Small fix to the commit messages. Change from v1: Small fix to the commit messages. ==================== Link: https://lore.kernel.org/r/20201031181043.805329-1-xie.he.0141@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/wan/hdlc_fr.c118
1 files changed, 72 insertions, 46 deletions
diff --git a/drivers/net/wan/hdlc_fr.c b/drivers/net/wan/hdlc_fr.c
index 409e5a7ad8e2..0720f5f92caa 100644
--- a/drivers/net/wan/hdlc_fr.c
+++ b/drivers/net/wan/hdlc_fr.c
@@ -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)
{
@@ -880,9 +919,9 @@ static int fr_rx(struct sk_buff *skb)
u8 *data = skb->data;
u16 dlci;
struct pvc_device *pvc;
- struct net_device *dev = NULL;
+ struct net_device *dev;
- if (skb->len <= 4 || fh->ea1 || data[2] != FR_UI)
+ if (skb->len < 4 || fh->ea1 || !fh->ea2 || data[2] != FR_UI)
goto rx_error;
dlci = q922_to_dlci(skb->data);
@@ -904,8 +943,7 @@ static int fr_rx(struct sk_buff *skb)
netdev_info(frad, "No PVC for received frame's DLCI %d\n",
dlci);
#endif
- dev_kfree_skb_any(skb);
- return NET_RX_DROP;
+ goto rx_drop;
}
if (pvc->state.fecn != fh->fecn) {
@@ -931,63 +969,51 @@ static int fr_rx(struct sk_buff *skb)
}
if (data[3] == NLPID_IP) {
+ if (!pvc->main)
+ goto rx_drop;
skb_pull(skb, 4); /* Remove 4-byte header (hdr, UI, NLPID) */
- dev = pvc->main;
+ skb->dev = pvc->main;
skb->protocol = htons(ETH_P_IP);
+ skb_reset_mac_header(skb);
} else if (data[3] == NLPID_IPV6) {
+ if (!pvc->main)
+ goto rx_drop;
skb_pull(skb, 4); /* Remove 4-byte header (hdr, UI, NLPID) */
- dev = pvc->main;
+ skb->dev = pvc->main;
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:
- dev = pvc->main;
- skb->protocol = htons(pid);
- break;
-
- case 0x80C20007: /* bridged Ethernet frame */
- if ((dev = pvc->ether) != NULL)
- skb->protocol = eth_type_trans(skb, dev);
- break;
-
- default:
- netdev_info(frad, "Unsupported protocol, OUI=%x PID=%x\n",
- oui, pid);
- dev_kfree_skb_any(skb);
- return NET_RX_DROP;
+ } 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;
+ } else {
+ goto rx_drop;
}
+
} else {
netdev_info(frad, "Unsupported protocol, NLPID=%x length=%i\n",
data[3], skb->len);
- dev_kfree_skb_any(skb);
- return NET_RX_DROP;
+ goto rx_drop;
}
- if (dev) {
- dev->stats.rx_packets++; /* PVC traffic */
- dev->stats.rx_bytes += skb->len;
- if (pvc->state.becn)
- dev->stats.rx_compressed++;
- skb->dev = dev;
- netif_rx(skb);
- return NET_RX_SUCCESS;
- } else {
- dev_kfree_skb_any(skb);
- return NET_RX_DROP;
- }
+ dev = skb->dev;
+ dev->stats.rx_packets++; /* PVC traffic */
+ dev->stats.rx_bytes += skb->len;
+ if (pvc->state.becn)
+ dev->stats.rx_compressed++;
+ netif_rx(skb);
+ return NET_RX_SUCCESS;
- rx_error:
+rx_error:
frad->stats.rx_errors++; /* Mark error */
+rx_drop:
dev_kfree_skb_any(skb);
return NET_RX_DROP;
}