From: Ke Chen chenke54@huawei.com
driver inclusion category:feature bugzilla: https://gitee.com/openeuler/kernel/issues/I9A3QT CVE: NA
----------------------------------------------------------------------
For ROH distributed scenario, EID is allocated by DHCP mode. Driver needs to convert the origin MAC address to EID format, and updates the destination MAC, chaddr and client id(if exists) when transmit DHCP packets. Meantime, the chaddr field should follow the source mac address, in order to make the dhcp server reply to the right client. For the payload of dhcp packet changed, so the checksum of L4 should be calculated too.
Signed-off-by: Jian Shen shenjian15@huawei.com Signed-off-by: Ke Chen chenke54@huawei.com Signed-off-by: Jiantao Xiao xiaojiantao1@h-partners.com --- .../net/ethernet/hisilicon/hns3/hns3_enet.c | 174 +++++++++++++++++- .../net/ethernet/hisilicon/hns3/hns3_enet.h | 50 +++++ .../hisilicon/hns3/hns3pf/hclge_main.c | 14 ++ 3 files changed, 233 insertions(+), 5 deletions(-)
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c index c8a32d57c0d1..f17ab8c4788f 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c @@ -1163,6 +1163,142 @@ static void hns3_tx_spare_reclaim_cb(struct hns3_enet_ring *ring, } }
+static struct hns3_dhcp_packet *hns3_get_dhcp_packet(struct sk_buff *skb, + int *dhcp_len) +{ + struct hns3_dhcp_packet *dhcp; + union l4_hdr_info l4; + int l4_payload_len; + + l4.hdr = skb_transport_header(skb); + if (l4.udp->dest != htons(HNS3_DHCP_CLIENT_PORT) || + l4.udp->source != htons(HNS3_DHCP_SERVER_PORT)) + return NULL; + + dhcp = (struct hns3_dhcp_packet *)(l4.hdr + sizeof(struct udphdr)); + l4_payload_len = ntohs(l4.udp->len) - sizeof(struct udphdr); + if (l4_payload_len < offsetof(struct hns3_dhcp_packet, options) || + dhcp->hlen != ETH_ALEN || + dhcp->cookie != htonl(HNS3_DHCP_MAGIC)) + return NULL; + + *dhcp_len = l4_payload_len; + return dhcp; +} + +static u8 *hns3_dhcp_option_scan(struct hns3_dhcp_packet *packet, + struct hns3_dhcp_opt_state *opt_state) +{ + int opt_len; + u8 *cur_opt; + + /* option bytes: [code][len][data0~data[len-1]] */ + while (opt_state->rem > 0) { + switch (opt_state->opt_ptr[DHCP_OPT_CODE]) { + /* option padding and end have no len and data byte. */ + case DHCP_OPT_PADDING: + opt_state->rem--; + opt_state->opt_ptr++; + break; + case DHCP_OPT_END: + if (DHCP_OVERLOAD_USE_FILE(opt_state->overload_flag)) { + opt_state->overload_flag |= + DHCP_OVERLOAD_FILE_USED; + opt_state->opt_ptr = packet->file; + opt_state->rem = sizeof(packet->file); + break; + } + if (DHCP_OVERLOAD_USE_SNAME(opt_state->overload_flag)) { + opt_state->overload_flag |= + DHCP_OVERLOAD_SNAME_USED; + opt_state->opt_ptr = packet->sname; + opt_state->rem = sizeof(packet->sname); + break; + } + return NULL; + default: + if (opt_state->rem <= DHCP_OPT_LEN) + return NULL; + /* opt_len includes code, len and data bytes */ + opt_len = opt_state->opt_ptr[DHCP_OPT_LEN] + + DHCP_OPT_DATA; + cur_opt = opt_state->opt_ptr; + if (opt_state->rem < opt_len) + return NULL; + + opt_state->opt_ptr += opt_len; + opt_state->rem -= opt_len; + if (cur_opt[DHCP_OPT_CODE] == DHCP_OPT_OVERLOAD) { + opt_state->overload_flag |= + cur_opt[DHCP_OPT_DATA]; + break; + } + return cur_opt; + } + } + + return NULL; +} + +static void hns3_dhcp_update_option61(struct hns3_nic_priv *priv, + struct hns3_dhcp_packet *packet, + int dhcp_len) +{ + struct hns3_dhcp_opt_state opt_state; + u8 *cur_opt; + + opt_state.opt_ptr = packet->options; + opt_state.rem = dhcp_len - offsetof(struct hns3_dhcp_packet, options); + opt_state.overload_flag = 0; + + cur_opt = hns3_dhcp_option_scan(packet, &opt_state); + while (cur_opt) { + if (cur_opt[DHCP_OPT_CODE] != DHCP_OPT_CLIENT_ID) { + cur_opt = hns3_dhcp_option_scan(packet, &opt_state); + continue; + } + if (cur_opt[DHCP_OPT_LEN] > ETH_ALEN) + ether_addr_copy(&cur_opt[DHCP_CLIENT_ID_MAC_OFT], + priv->roh_perm_mac); + break; + } +} + +static void hns3_dhcp_cal_l4_csum(struct sk_buff *skb) +{ + union l3_hdr_info l3; + union l4_hdr_info l4; + __wsum csum; + int offset; + + if (skb->ip_summed == CHECKSUM_PARTIAL) + return; + + l3.hdr = skb_network_header(skb); + l4.hdr = skb_transport_header(skb); + offset = skb_transport_offset(skb); + l4.udp->check = 0; + csum = csum_partial(l4.udp, ntohs(l4.udp->len), 0); + l4.udp->check = csum_tcpudp_magic(l3.v4->saddr, l3.v4->daddr, + skb->len - offset, IPPROTO_UDP, csum); +} + +static void hns3_dhcp_packet_convert(struct hns3_nic_priv *priv, + struct sk_buff *skb, + struct hns3_dhcp_packet *dhcp, + int dhcp_len) +{ + struct ethhdr *l2hdr = eth_hdr(skb); + + if (!dhcp) + return; + + ether_addr_copy(dhcp->chaddr, l2hdr->h_source); + hns3_dhcp_update_option61(priv, dhcp, dhcp_len); + /* for l4 payload changed, need to re-calculate the csum */ + hns3_dhcp_cal_l4_csum(skb); +} + static int hns3_set_tso(struct sk_buff *skb, u32 *paylen_fdop_ol4cs, u16 *mss, u32 *type_cs_vlan_tso, u32 *send_bytes) { @@ -1714,7 +1850,20 @@ static int hns3_handle_csum_partial(struct hns3_enet_ring *ring, return 0; }
-static int hns3_fill_skb_desc(struct hns3_enet_ring *ring, +static bool hns3_roh_check_udpv4(struct sk_buff *skb) +{ + union l3_hdr_info l3; + + l3.hdr = skb_network_header(skb); + if (skb->protocol != htons(ETH_P_IP) || + l3.v4->version != IP_VERSION_IPV4) + return false; + + return l3.v4->protocol == IPPROTO_UDP; +} + +static int hns3_fill_skb_desc(struct hns3_nic_priv *priv, + struct hns3_enet_ring *ring, struct sk_buff *skb, struct hns3_desc *desc, struct hns3_desc_cb *desc_cb) { @@ -1739,6 +1888,15 @@ static int hns3_fill_skb_desc(struct hns3_enet_ring *ring, hnae3_set_field(param.paylen_fdop_ol4cs, HNS3_TXD_FD_OP_M, HNS3_TXD_FD_OP_S, fd_op);
+ if (hnae3_check_roh_mac_type(priv->ae_handle) && + hns3_roh_check_udpv4(skb)) { + struct hns3_dhcp_packet *dhcp; + int dhcp_len; + + dhcp = hns3_get_dhcp_packet(skb, &dhcp_len); + hns3_dhcp_packet_convert(priv, skb, dhcp, dhcp_len); + } + /* Set txbd */ desc->tx.ol_type_vlan_len_msec = cpu_to_le32(param.ol_type_vlan_len_msec); @@ -2344,15 +2502,16 @@ static int hns3_handle_desc_filling(struct hns3_enet_ring *ring, return hns3_fill_skb_to_desc(ring, skb, DESC_TYPE_SKB); }
-static int hns3_handle_skb_desc(struct hns3_enet_ring *ring, +static int hns3_handle_skb_desc(struct hns3_nic_priv *priv, + struct hns3_enet_ring *ring, struct sk_buff *skb, struct hns3_desc_cb *desc_cb, int next_to_use_head) { int ret;
- ret = hns3_fill_skb_desc(ring, skb, &ring->desc[ring->next_to_use], - desc_cb); + ret = hns3_fill_skb_desc(priv, ring, skb, + &ring->desc[ring->next_to_use], desc_cb); if (unlikely(ret < 0)) goto fill_err;
@@ -2406,7 +2565,7 @@ netdev_tx_t hns3_nic_net_xmit(struct sk_buff *skb, struct net_device *netdev) 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); + ret = hns3_handle_skb_desc(priv, ring, skb, desc_cb, ring->next_to_use); if (unlikely(ret <= 0)) goto out_err_tx_ok;
@@ -5246,6 +5405,10 @@ static int hns3_init_mac_addr(struct net_device *netdev) return 0; }
+ if (hnae3_check_roh_mac_type(h) && + is_zero_ether_addr(priv->roh_perm_mac)) + ether_addr_copy(priv->roh_perm_mac, netdev->dev_addr); + if (h->ae_algo->ops->set_mac_addr) ret = h->ae_algo->ops->set_mac_addr(h, netdev->dev_addr, true);
@@ -5400,6 +5563,7 @@ static int hns3_client_init(struct hnae3_handle *handle) priv->tx_timeout_count = 0; priv->max_non_tso_bd_num = ae_dev->dev_specs.max_non_tso_bd_num; set_bit(HNS3_NIC_STATE_DOWN, &priv->state); + eth_zero_addr(priv->roh_perm_mac);
handle->msg_enable = netif_msg_init(debug, DEFAULT_MSG_LEVEL);
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h index 1456de40f4b6..60a4ae384233 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h @@ -618,6 +618,56 @@ struct hns3_nic_priv { struct hns3_enet_coalesce rx_coal; u32 tx_copybreak; u32 rx_copybreak; + u8 roh_perm_mac[ETH_ALEN]; +}; + +#define HNS3_DHCP_SERVER_PORT 68 +#define HNS3_DHCP_CLIENT_PORT 67 +#define HNS3_DHCP_MAGIC 0x63825363 +#define DHCP_OPT_CODE 0 +#define DHCP_OPT_LEN 1 +#define DHCP_OPT_DATA 2 +#define DHCP_CLIENT_ID_LEN 7 +#define DHCP_CLIENT_ID_MAC_OFT 3 +#define DHCP_OVERLOAD_FILE 0x1 +#define DHCP_OVERLOAD_SNAME 0x2 +#define DHCP_OVERLOAD_FILE_USED 0x101 +#define DHCP_OVERLOAD_SNAME_USED 0x202 +#define DHCP_OVERLOAD_USE_FILE(x) \ + (((x) & DHCP_OVERLOAD_FILE_USED) == DHCP_OVERLOAD_FILE) +#define DHCP_OVERLOAD_USE_SNAME(x) \ + (((x) & DHCP_OVERLOAD_SNAME_USED) == DHCP_OVERLOAD_SNAME) + +enum DHCP_OPTION_CODES { + DHCP_OPT_PADDING = 0, + DHCP_OPT_OVERLOAD = 52, + DHCP_OPT_CLIENT_ID = 61, + DHCP_OPT_END = 255 +}; + +struct hns3_dhcp_packet { + u8 op; + u8 htype; + u8 hlen; + u8 hops; + u32 xid; + u16 secs; + u16 flags; + u32 ciaddr; + u32 yiaddr; + u32 siaddr_nip; + u32 gateway_nip; + u8 chaddr[16]; /* link-layer client hardware address (MAC) */ + u8 sname[64]; + u8 file[128]; + u32 cookie; /* DHCP magic bytes: 0x63825363 */ + u8 options[312]; +}; + +struct hns3_dhcp_opt_state { + u8 *opt_ptr; /* refer to current option item */ + int rem; /* remain bytes in options */ + u32 overload_flag; /* whether use file and sname field as options */ };
union l3_hdr_info { diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index 739dbc68404a..59ec55a90881 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -2955,6 +2955,20 @@ static void hclge_get_fec(struct hnae3_handle *handle, u8 *fec_ability, *fec_mode = mac->fec_mode; }
+static void hclge_roh_convert_mac_addr(struct hclge_dev *hdev) +{ +#define HCLGE_ROH_EID_MASK_BYTE 3 + + struct hclge_vport *vport = &hdev->vport[0]; + struct hnae3_handle *handle = &vport->nic; + + if (hnae3_check_roh_mac_type(handle)) { + if (!is_valid_ether_addr(hdev->hw.mac.mac_addr)) + eth_random_addr(hdev->hw.mac.mac_addr); + memset(hdev->hw.mac.mac_addr, 0, HCLGE_ROH_EID_MASK_BYTE); + } +} + static int hclge_mac_init(struct hclge_dev *hdev) { struct hclge_mac *mac = &hdev->hw.mac;