Commit 0b68f477 authored by Peiyang Wang's avatar Peiyang Wang Committed by Jiantao Xiao
Browse files

net: hns3: support arp proxy

driver inclusion
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/I8GQDK


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: default avatarPeiyang Wang <wangpeiyang1@huawei.com>
parent 7906c655
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -13,7 +13,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
hns3-$(CONFIG_HNS3_UBL) += hns3_unic.o hns3_unic_debugfs.o
+6 −0
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@
#include "hnae3_ext.h"
#include "hns3_enet.h"
#include "hns3_unic.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
@@ -2588,6 +2589,9 @@ netdev_tx_t hns3_nic_net_xmit(struct sk_buff *skb, struct net_device *netdev)
		hns3_unic_set_default_cc(skb);
	}
#endif
	if (hns3_need_to_handle_roh_arp_req(skb))
		return hns3_handle_roh_arp_req(skb, priv);

	ret = hns3_handle_skb_desc(priv, ring, skb, desc_cb, ring->next_to_use);
	if (unlikely(ret <= 0))
		goto out_err_tx_ok;
@@ -4821,6 +4825,8 @@ 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);

	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);
+14 −0
Original line number Diff line number Diff line
@@ -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 */
+174 −0
Original line number Diff line number Diff line
// 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;
	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;
			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);
		}
	}
}
+24 −0
Original line number Diff line number Diff line
/* 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)
#define hns3_roh_arp_reply_idx_move_fd(idx) \
	({ typeof(idx) x_ = (idx); \
	   x_ = (x_ + 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