前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >learning:vpp hash Infrastructure (2)

learning:vpp hash Infrastructure (2)

作者头像
dpdk-vpp源码解读
发布2023-09-05 17:56:34
4020
发布2023-09-05 17:56:34
举报
文章被收录于专栏:DPDK VPP源码分析

前面讲了vpp 软件Packet Flow Hash基础架构,其中在hash算法中有一个对称hash算法(handoff-eth-sym 1 Ethernet/IPv4/IPv6/MPLS headers Symmetric),对称hash算法的作用是将同一条流的两个方向的报文hash到一个cpu核心来处理。在软件逻辑中存在基于CPU核的L34会话业务及DPVS软件(dpvs中一般是使用网卡FDIR功能实现)中都有对称hash的应用,以提升网络处理性能。

在上家单位实现过基于vpp实现tcp,sctp报文的去重,保序、分片重组功能,以提供应用层完整的业务报文。就是利用网卡对称RSS实现网络性能优化,以减少vpp handoff功能的使用(一般会存在10%左右的性能开销)。可以参考以前文章:dpdk X710 symmetric RSS Vpp使用dpdk RSS功能部分报文不生效问题

下面以代码展开说明vpp 软件中对称hash处理逻辑。

1、普通IPv4报文,分别以Sip + Dip + protocol 做异或运算。

2、普通IPv6报文,分别以Sip + Dip + protocol 做异或运算。

3、mpls报文,最多支持五层mpls 嵌套,解析内层报文如果是ipv4或ipv6 ,以Sip + Dip + protocol 做异或运算,否则以内层mpls 标签数值做hash。

4、vlan或dot1ad报文,解析内层报文如果是ipv4或ipv6 ,以Sip + Dip + protocol 做异或运算,否则以 ethernet type做hash。

代码语言:javascript
复制
    static inline u64
eth_get_sym_key (ethernet_header_t * h0)
{
    u64 hash_key;
    /*1、普通ipv4报文以sip + dip + protocl 计算hash key*/
    if (PREDICT_TRUE (h0->type) == clib_host_to_net_u16 (ETHERNET_TYPE_IP4))
    {
        ip4_header_t *ip = (ip4_header_t *) (h0 + 1);
        hash_key =(u64) (ip->src_address.as_u32 ^
                 ip->dst_address.as_u32 ^ ip->protocol);
    }
   /*2、普通ipv6报文以sip + dip + protocl 计算hash key*/
    else if (h0->type == clib_host_to_net_u16 (ETHERNET_TYPE_IP6))
    {
        ip6_header_t *ip = (ip6_header_t *) (h0 + 1);
        hash_key = (u64) (ip->src_address.as_u64[0] ^
            ip->src_address.as_u64[1] ^
            ip->dst_address.as_u64[0] ^
            ip->dst_address.as_u64[1] ^ ip->protocol);
    }
    /*3、普通mpls报文计算hash key*/
    else if (h0->type == clib_host_to_net_u16 (ETHERNET_TYPE_MPLS))
    {
        hash_key = mpls_get_key ((mpls_unicast_header_t *) (h0 + 1));
    }
    /*4、vlan或dot1ad报文计算hash key*/
    else if (PREDICT_FALSE((h0->type == clib_host_to_net_u16 (ETHERNET_TYPE_VLAN))
         || (h0->type == clib_host_to_net_u16 (ETHERNET_TYPE_DOT1AD)))) 
    {
        ethernet_vlan_header_t *outer = (ethernet_vlan_header_t *) (h0 + 1);
        outer = (outer->type == clib_host_to_net_u16 (ETHERNET_TYPE_VLAN)) ?outer + 1 : outer;
        if (PREDICT_TRUE (outer->type) ==
                clib_host_to_net_u16 (ETHERNET_TYPE_IP4))
        {
             ip4_header_t *ip = (ip4_header_t *) (outer + 1);
             hash_key = (u64) (ip->src_address.as_u32 ^
                         ip->dst_address.as_u32 ^ ip->protocol);
        }
        else if (outer->type == clib_host_to_net_u16 (ETHERNET_TYPE_IP6))
        {
            ip6_header_t *ip = (ip6_header_t *) (outer + 1);
            hash_key = (u64) (ip->src_address.as_u64[0] ^ 
                ip->src_address.as_u64[1] ^
                ip->dst_address.as_u64[0] ^
                ip->dst_address.as_u64[1] ^ ip->protocol);
        }
        else if (outer->type == clib_host_to_net_u16 (ETHERNET_TYPE_MPLS))
        {
             hash_key = mpls_get_key ((mpls_unicast_header_t *) (outer + 1));
        }
        else
        {
             hash_key = outer->type;
        }
    }
    else
    {
        hash_key = 0;
    }

    return hash_key;
}

通过分析代码,软件对称hash因子实现还是比较简单,不能做到hash均匀,vpp只是简单实现了此算法。vpp中flow hash使用的地方还是相当多的,也曾经因hash遇到过很多问题。

在vpp 19.01版本中ipsec使用vpp自带的ikev2协商,遇到过因硬件RSS算法(默认是L34层做hash key)导致协商失败的问题。大致原因是因为ikev2在协商过程中创建了以cpu核资源,通过spi查询到ipsec sa资源。在网卡开启多队列之后,ikev2第一阶段,端口号500,rss hash到worker1上,ikev2 第二阶段,因存在nat 端口号4500,rss hash 到worker2上面,导致查询不到sa资源,协商失败。解决方案也比较简单,就是修改网卡硬件rss key因子,以L3层为key做hash。

在vxlan报文封装过程中,计算外层UDP报文src port端口号,就是以内层报文来做hash key计算得到端口号。因资源池存在防火墙会话,内层报文数据量太大,导致防火墙会话满而丢包的问题。通过优化hash算法解决此问题。

代码语言:javascript
复制
static inline u32
vnet_l2_compute_flow_hash (vlib_buffer_t * b)
{
  ethernet_header_t *eh = vlib_buffer_get_current (b);
  u8 *l3h = (u8 *) eh + vnet_buffer (b)->l2.l2_len;
  u16 ethertype = clib_net_to_host_u16 (*(u16 *) (l3h - 2));
  /*1、ipv4 报文 以报文五元组做hash key*/
  if (ethertype == ETHERNET_TYPE_IP4)
    return ip4_compute_flow_hash ((ip4_header_t *) l3h, IP_FLOW_HASH_DEFAULT);
/*2、ipv6 报文 以报文五元组 + flowtable 做hash key*/
  else if (ethertype == ETHERNET_TYPE_IP6)
    return ip6_compute_flow_hash ((ip6_header_t *) l3h, IP_FLOW_HASH_DEFAULT);
  else
    {/*2、二层报文 以报文smac + dmac + ethernet type 做hash key*/
      u32 a, b, c;
      u32 *ap = (u32 *) & eh->dst_address[2];
      u32 *bp = (u32 *) & eh->src_address[2];
      a = *ap;
      b = *bp;
      c = ethertype;
      hash_v3_mix32 (a, b, c);
      hash_v3_finalize32 (a, b, c);
      return c;
    }
}

/** Default: 5-tuple + flowlabel without the "reverse" bit 
* 以Sip+Dip+Sport + Dport + proctcol + flowlabel (ipv6报文 flowlable)*/
#define IP_FLOW_HASH_DEFAULT (0x9F)

另外在ip转发模块中,存在ECMP(Equal-Cost Multi-Path Routing等价多路径路由)中,也存在软件hash 选择的问题。

代码语言:javascript
复制
/** Default: 5-tuple + flowlabel without the "reverse" bit */
#define IP_FLOW_HASH_DEFAULT (0x9F)

#define foreach_flow_hash_bit_v1                                              \
  _ (src, IP_FLOW_HASH_SRC_ADDR)                                              \
  _ (dst, IP_FLOW_HASH_DST_ADDR)                                              \
  _ (sport, IP_FLOW_HASH_SRC_PORT)                                            \
  _ (dport, IP_FLOW_HASH_DST_PORT)                                            \
  _ (proto, IP_FLOW_HASH_PROTO)                                               \
  _ (reverse, IP_FLOW_HASH_REVERSE_SRC_DST)                                   \
  _ (symmetric, IP_FLOW_HASH_SYMMETRIC)

#define foreach_flow_hash_bit                                                 \
  _ (src, 0, IP_FLOW_HASH_SRC_ADDR)                                           \
  _ (dst, 1, IP_FLOW_HASH_DST_ADDR)                                           \
  _ (sport, 2, IP_FLOW_HASH_SRC_PORT)                                         \
  _ (dport, 3, IP_FLOW_HASH_DST_PORT)                                         \
  _ (proto, 4, IP_FLOW_HASH_PROTO)                                            \
  _ (reverse, 5, IP_FLOW_HASH_REVERSE_SRC_DST)                                \
  _ (symmetric, 6, IP_FLOW_HASH_SYMMETRIC)                                    \
  _ (flowlabel, 7, IP_FLOW_HASH_FL)                                           \
  _ (gtpv1teid, 8, IP_FLOW_HASH_GTPV1_TEID)

可以通过set ip flow-hash 来设置hash key选择因子,此设置是基于vrf的。

代码语言:javascript
复制
set ip flow-hash table <table-id> [src] [dst] [sport] [dport] [proto] [reverse] [gtpv1teid]

至此我们了解了vpp中软件Packet Flow Hash的使用,以及遇到问题的一些解决思路。如果遇到类似的问题,欢迎留言讨论学习。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-08-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 DPDK VPP源码分析 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档