From: Peiyang Wang wangpeiyang1@huawei.com
driver inclusion category:feature bugzilla: https://gitee.com/openeuler/kernel/issues/I9A3QT CVE: NA
----------------------------------------------------------------------
In RoH mode, the IP and MAC of network devices have a special mapping relationship. The mapping is show as followed: IP: a.b.c.d <------> MAC: 0.0.0.b.c.d Thus, the arp request for find the mac of a ip is not necessary to send to network. The driver can generate a response arp to the protocol stack based on above mapping relationship.
Arp proxy is designed to acheive the above function. The scope of ARP proxy include all ARP request message including vlan, except free ARP request and tunnel message.
Signed-off-by: Peiyang Wang wangpeiyang1@huawei.com Signed-off-by: Jiantao Xiao xiaojiantao1@h-partners.com --- drivers/net/ethernet/hisilicon/hns3/Makefile | 2 +- .../net/ethernet/hisilicon/hns3/hns3_enet.c | 9 + .../net/ethernet/hisilicon/hns3/hns3_enet.h | 14 ++ .../net/ethernet/hisilicon/hns3/hns3_roh.c | 174 ++++++++++++++++++ .../net/ethernet/hisilicon/hns3/hns3_roh.h | 26 +++ 5 files changed, 224 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ethernet/hisilicon/hns3/hns3_roh.c create mode 100644 drivers/net/ethernet/hisilicon/hns3/hns3_roh.h
diff --git a/drivers/net/ethernet/hisilicon/hns3/Makefile b/drivers/net/ethernet/hisilicon/hns3/Makefile index cc9158097e93..1cc61b2062c0 100644 --- a/drivers/net/ethernet/hisilicon/hns3/Makefile +++ b/drivers/net/ethernet/hisilicon/hns3/Makefile @@ -12,7 +12,7 @@ obj-$(CONFIG_HNS3) += hnae3.o
obj-$(CONFIG_HNS3_ENET) += hns3.o hns3-objs = hns3_enet.o hns3_ethtool.o hns3_debugfs.o -hns3-objs += hns3_ext.o +hns3-objs += hns3_ext.o hns3_roh.o
hns3-$(CONFIG_HNS3_DCB) += hns3_dcbnl.o
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c index a8f320eb3637..c8a32d57c0d1 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c @@ -28,6 +28,7 @@ #include "hnae3.h" #include "hnae3_ext.h" #include "hns3_enet.h" +#include "hns3_roh.h" /* All hns3 tracepoints are defined by the include below, which * must be included exactly once across the whole kernel with * CREATE_TRACE_POINTS defined @@ -2400,6 +2401,11 @@ netdev_tx_t hns3_nic_net_xmit(struct sk_buff *skb, struct net_device *netdev) goto out_err_tx_ok; }
+ if (test_bit(HNAE3_PFLAG_ROH_ARP_PROXY_ENABLE, + &priv->ae_handle->priv_flags) && + hns3_need_to_handle_roh_arp_req(skb)) + return hns3_handle_roh_arp_req(skb, priv); + ret = hns3_handle_skb_desc(ring, skb, desc_cb, ring->next_to_use); if (unlikely(ret <= 0)) goto out_err_tx_ok; @@ -4564,6 +4570,9 @@ static int hns3_nic_common_poll(struct napi_struct *napi, int budget) if (tqp_vector->num_tqps > 1) rx_budget = max(budget / tqp_vector->num_tqps, 1);
+ if (hnae3_check_roh_mac_type(priv->ae_handle)) + hns3_handle_roh_arp_reply(tqp_vector, priv); + hns3_for_each_ring(ring, tqp_vector->rx_group) { int rx_cleaned = hns3_clean_rx_ring(ring, rx_budget, hns3_rx_skb); diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h index 3afab7db17eb..1456de40f4b6 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h @@ -471,6 +471,15 @@ struct hns3_tx_spare { u32 len; };
+struct hns3_arp_reply { + __be32 dest_ip; + __be32 src_ip; + u8 dest_hw[ETH_ALEN]; + u8 src_hw[ETH_ALEN]; + u8 has_vlan; + __be16 vlan_tci; +}; + struct hns3_enet_ring { struct hns3_desc *desc; /* dma map address space */ struct hns3_desc_cb *desc_cb; @@ -496,6 +505,11 @@ struct hns3_enet_ring { int next_to_clean; u32 flag; /* ring attribute */
+#define HNS3_APR_REPLY_LTH 32 + struct hns3_arp_reply arp_reply[HNS3_APR_REPLY_LTH]; + int arp_reply_head; + int arp_reply_tail; + int pending_buf; union { /* for Tx ring */ diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_roh.c b/drivers/net/ethernet/hisilicon/hns3/hns3_roh.c new file mode 100644 index 000000000000..1c0341b34f6d --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_roh.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-2.0+ +// Copyright (c) 2016-2017 Hisilicon Limited. + +#include <linux/skbuff.h> +#include <linux/if_arp.h> +#include <linux/if_vlan.h> +#include <net/arp.h> + +#include "hns3_roh.h" +#include "hns3_enet.h" + +static void hns3_extract_arp_ip_field(struct arphdr *arphdr, __be32 **sip, + __be32 **tip, unsigned char addr_len) +{ + unsigned char *arp_ptr = (unsigned char *)(arphdr + 1); + + arp_ptr += addr_len; + *sip = (__be32 *)arp_ptr; + arp_ptr += ARP_IP_LEN; + arp_ptr += addr_len; + *tip = (__be32 *)arp_ptr; +} + +bool hns3_need_to_handle_roh_arp_req(struct sk_buff *skb) +{ + struct arphdr *arphdr = arp_hdr(skb); + __be32 *sip, *tip; + + /* Intercept most non-ARP packets based on packet length. */ + if (skb->len > hns3_roh_arp_hlen_max(skb)) + return false; + + /* if txvlan offload is off, check encapsulated protocol in vlan. */ + if (eth_type_vlan(skb->protocol)) { + struct vlan_hdr *vh = (struct vlan_hdr *)(skb->data + ETH_HLEN); + + if (vh->h_vlan_encapsulated_proto == htons(ETH_P_ARP) && + arphdr->ar_op == htons(ARPOP_REQUEST)) + goto check_gratuitous_arp; + return false; + } + + /* if txvlan offload is on or it's a normal packet, check protocol. */ + if (skb->protocol != htons(ETH_P_ARP) || + arphdr->ar_op != htons(ARPOP_REQUEST)) + return false; + + /* don't support Gratuitous ARP, which request packet where the source + * and destination IP are both set to the IP of the machine issuing the + * packet. + */ +check_gratuitous_arp: + hns3_extract_arp_ip_field(arphdr, &sip, &tip, skb->dev->addr_len); + return *tip != *sip; +} + +int hns3_handle_roh_arp_req(struct sk_buff *skb, struct hns3_nic_priv *priv) +{ + struct hnae3_handle *h = priv->ae_handle; + struct hns3_enet_ring *ring; + struct arphdr *arphdr; + struct ethhdr *ethhdr; + int reply_idx, len; + __be32 *sip, *tip; + + /* use same queue num in rx */ + ring = &priv->ring[skb->queue_mapping + h->kinfo.num_tqps]; + reply_idx = ring->arp_reply_tail; + reply_idx = hns3_roh_arp_reply_idx_move_fd(reply_idx); + /* This smp_load_acquire() pairs with smp_store_release() in + * hns3_handle_roh_arp_reply(). + */ + if (reply_idx == smp_load_acquire(&ring->arp_reply_head)) + return NETDEV_TX_BUSY; + len = skb->len; + + if (skb_vlan_tagged(skb)) { + ring->arp_reply[reply_idx].has_vlan = true; + ring->arp_reply[reply_idx].vlan_tci = skb_vlan_tag_get(skb); + skb_vlan_pop(skb); + len += VLAN_HLEN; + } else { + ring->arp_reply[reply_idx].has_vlan = false; + } + + ethhdr = eth_hdr(skb); + arphdr = arp_hdr(skb); + + hns3_extract_arp_ip_field(arphdr, &sip, &tip, skb->dev->addr_len); + ether_addr_copy(ring->arp_reply[reply_idx].dest_hw, ethhdr->h_source); + ether_addr_copy(ring->arp_reply[reply_idx].src_hw, ethhdr->h_dest); + ring->arp_reply[reply_idx].dest_ip = *sip; + ring->arp_reply[reply_idx].src_ip = *tip; + hns3_roh_update_mac_by_ip(be32_to_cpu(*tip), + ring->arp_reply[reply_idx].src_hw); + /* This smp_store_release() pairs with smp_load_acquire() in + * hns3_handle_roh_arp_reply(). Ensure that the arp_reply_tail is + * update validly. + */ + smp_store_release(&ring->arp_reply_tail, reply_idx); + + ring = &priv->ring[skb->queue_mapping]; + u64_stats_update_begin(&ring->syncp); + ring->stats.tx_pkts++; + ring->stats.tx_bytes += len; + u64_stats_update_end(&ring->syncp); + + dev_kfree_skb_any(skb); + napi_schedule(&ring->tqp_vector->napi); + return NETDEV_TX_OK; +} + +static struct sk_buff *setup_arp_reply_skb(struct hns3_arp_reply *arp_reply, + struct hns3_nic_priv *priv) +{ + struct sk_buff *skb = arp_create(ARPOP_REPLY, ETH_P_ARP, + arp_reply->dest_ip, priv->netdev, + arp_reply->src_ip, arp_reply->dest_hw, + arp_reply->src_hw, arp_reply->dest_hw); + if (!skb) + return NULL; + + skb_reset_mac_header(skb); + skb_reset_mac_len(skb); + + if (arp_reply->has_vlan) { + skb = vlan_insert_tag_set_proto(skb, htons(ETH_P_8021Q), + arp_reply->vlan_tci); + if (!skb) + return NULL; + skb_reset_network_header(skb); + } + + skb_reserve(skb, skb->mac_len); + return skb; +} + +void hns3_handle_roh_arp_reply(struct hns3_enet_tqp_vector *tqp_vector, + struct hns3_nic_priv *priv) +{ + struct hns3_arp_reply *arp_reply; + struct hns3_enet_ring *ring; + struct sk_buff *skb; + int reply_idx; + + hns3_for_each_ring(ring, tqp_vector->rx_group) { + /* This smp_load_acquire() pairs with smp_store_release() in + * hns3_handle_roh_arp_reply(). + */ + while (smp_load_acquire(&ring->arp_reply_tail) != + ring->arp_reply_head) { + reply_idx = ring->arp_reply_head; + reply_idx = hns3_roh_arp_reply_idx_move_fd(reply_idx); + arp_reply = &ring->arp_reply[reply_idx]; + skb = setup_arp_reply_skb(arp_reply, priv); + /* This smp_store_release() pairs with + * smp_load_acquire() in hns3_handle_roh_arp_req(). + * Ensure that the arp_reply_head is update validly. + */ + smp_store_release(&ring->arp_reply_head, reply_idx); + + if (!skb) { + hns3_ring_stats_update(ring, rx_err_cnt); + continue; + } + napi_gro_receive(&tqp_vector->napi, skb); + + u64_stats_update_begin(&ring->syncp); + ring->stats.rx_pkts++; + ring->stats.rx_bytes += skb->len; + u64_stats_update_end(&ring->syncp); + } + } +} diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_roh.h b/drivers/net/ethernet/hisilicon/hns3/hns3_roh.h new file mode 100644 index 000000000000..222ed20d8772 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_roh.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Copyright (c) 2023 Hisilicon Limited. */ + +#ifndef __HNS3_ROH_H +#define __HNS3_ROH_H + +#include "hns3_enet.h" + +#define ARP_IP_LEN 4 +#define HNS3_ROH_MAC_ADDR_MASK 0x00ffffff + +#define hns3_roh_update_mac_by_ip(ip_addr, mac) \ + u64_to_ether_addr((ip_addr) & HNS3_ROH_MAC_ADDR_MASK, mac) +#define hns3_roh_arp_hlen_max(skb) \ + (ETH_HLEN + arp_hdr_len((skb)->dev) + VLAN_HLEN) + +static inline int hns3_roh_arp_reply_idx_move_fd(int idx) +{ + return (idx + 1) % HNS3_APR_REPLY_LTH; +} + +void hns3_handle_roh_arp_reply(struct hns3_enet_tqp_vector *tqp_vector, + struct hns3_nic_priv *priv); +int hns3_handle_roh_arp_req(struct sk_buff *skb, struct hns3_nic_priv *priv); +bool hns3_need_to_handle_roh_arp_req(struct sk_buff *skb); +#endif