1101f4de9SOz Shlomo /* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */ 2101f4de9SOz Shlomo /* Copyright (c) 2018 Mellanox Technologies. */ 3101f4de9SOz Shlomo 4101f4de9SOz Shlomo #include <net/vxlan.h> 5df2ef3bfSOz Shlomo #include <net/gre.h> 6101f4de9SOz Shlomo #include "lib/vxlan.h" 7101f4de9SOz Shlomo #include "en/tc_tun.h" 8101f4de9SOz Shlomo 9101f4de9SOz Shlomo static int mlx5e_route_lookup_ipv4(struct mlx5e_priv *priv, 10101f4de9SOz Shlomo struct net_device *mirred_dev, 11101f4de9SOz Shlomo struct net_device **out_dev, 12101f4de9SOz Shlomo struct flowi4 *fl4, 13101f4de9SOz Shlomo struct neighbour **out_n, 14101f4de9SOz Shlomo u8 *out_ttl) 15101f4de9SOz Shlomo { 16101f4de9SOz Shlomo struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; 17491c37e4SRabie Loulou struct net_device *uplink_dev, *uplink_upper; 18491c37e4SRabie Loulou bool dst_is_lag_dev; 19101f4de9SOz Shlomo struct rtable *rt; 20101f4de9SOz Shlomo struct neighbour *n = NULL; 21101f4de9SOz Shlomo 22101f4de9SOz Shlomo #if IS_ENABLED(CONFIG_INET) 23101f4de9SOz Shlomo int ret; 24101f4de9SOz Shlomo 25101f4de9SOz Shlomo rt = ip_route_output_key(dev_net(mirred_dev), fl4); 26101f4de9SOz Shlomo ret = PTR_ERR_OR_ZERO(rt); 27101f4de9SOz Shlomo if (ret) 28101f4de9SOz Shlomo return ret; 29101f4de9SOz Shlomo #else 30101f4de9SOz Shlomo return -EOPNOTSUPP; 31101f4de9SOz Shlomo #endif 32491c37e4SRabie Loulou 33491c37e4SRabie Loulou uplink_dev = mlx5_eswitch_uplink_get_proto_dev(esw, REP_ETH); 34491c37e4SRabie Loulou uplink_upper = netdev_master_upper_dev_get(uplink_dev); 35491c37e4SRabie Loulou dst_is_lag_dev = (uplink_upper && 36491c37e4SRabie Loulou netif_is_lag_master(uplink_upper) && 37491c37e4SRabie Loulou rt->dst.dev == uplink_upper && 38491c37e4SRabie Loulou mlx5_lag_is_active(priv->mdev)); 39491c37e4SRabie Loulou 40491c37e4SRabie Loulou /* if the egress device isn't on the same HW e-switch or 41491c37e4SRabie Loulou * it's a LAG device, use the uplink 42491c37e4SRabie Loulou */ 43491c37e4SRabie Loulou if (!switchdev_port_same_parent_id(priv->netdev, rt->dst.dev) || 44491c37e4SRabie Loulou dst_is_lag_dev) 45491c37e4SRabie Loulou *out_dev = uplink_dev; 46101f4de9SOz Shlomo else 47101f4de9SOz Shlomo *out_dev = rt->dst.dev; 48101f4de9SOz Shlomo 49101f4de9SOz Shlomo if (!(*out_ttl)) 50101f4de9SOz Shlomo *out_ttl = ip4_dst_hoplimit(&rt->dst); 51101f4de9SOz Shlomo n = dst_neigh_lookup(&rt->dst, &fl4->daddr); 52101f4de9SOz Shlomo ip_rt_put(rt); 53101f4de9SOz Shlomo if (!n) 54101f4de9SOz Shlomo return -ENOMEM; 55101f4de9SOz Shlomo 56101f4de9SOz Shlomo *out_n = n; 57101f4de9SOz Shlomo return 0; 58101f4de9SOz Shlomo } 59101f4de9SOz Shlomo 60101f4de9SOz Shlomo static const char *mlx5e_netdev_kind(struct net_device *dev) 61101f4de9SOz Shlomo { 62101f4de9SOz Shlomo if (dev->rtnl_link_ops) 63101f4de9SOz Shlomo return dev->rtnl_link_ops->kind; 64101f4de9SOz Shlomo else 65101f4de9SOz Shlomo return ""; 66101f4de9SOz Shlomo } 67101f4de9SOz Shlomo 68101f4de9SOz Shlomo static int mlx5e_route_lookup_ipv6(struct mlx5e_priv *priv, 69101f4de9SOz Shlomo struct net_device *mirred_dev, 70101f4de9SOz Shlomo struct net_device **out_dev, 71101f4de9SOz Shlomo struct flowi6 *fl6, 72101f4de9SOz Shlomo struct neighbour **out_n, 73101f4de9SOz Shlomo u8 *out_ttl) 74101f4de9SOz Shlomo { 75101f4de9SOz Shlomo struct neighbour *n = NULL; 76101f4de9SOz Shlomo struct dst_entry *dst; 77101f4de9SOz Shlomo 78101f4de9SOz Shlomo #if IS_ENABLED(CONFIG_INET) && IS_ENABLED(CONFIG_IPV6) 79101f4de9SOz Shlomo struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; 80491c37e4SRabie Loulou struct net_device *uplink_dev, *uplink_upper; 81491c37e4SRabie Loulou bool dst_is_lag_dev; 82101f4de9SOz Shlomo int ret; 83101f4de9SOz Shlomo 84101f4de9SOz Shlomo ret = ipv6_stub->ipv6_dst_lookup(dev_net(mirred_dev), NULL, &dst, 85101f4de9SOz Shlomo fl6); 86101f4de9SOz Shlomo if (ret < 0) 87101f4de9SOz Shlomo return ret; 88101f4de9SOz Shlomo 89101f4de9SOz Shlomo if (!(*out_ttl)) 90101f4de9SOz Shlomo *out_ttl = ip6_dst_hoplimit(dst); 91101f4de9SOz Shlomo 92491c37e4SRabie Loulou uplink_dev = mlx5_eswitch_uplink_get_proto_dev(esw, REP_ETH); 93491c37e4SRabie Loulou uplink_upper = netdev_master_upper_dev_get(uplink_dev); 94491c37e4SRabie Loulou dst_is_lag_dev = (uplink_upper && 95491c37e4SRabie Loulou netif_is_lag_master(uplink_upper) && 96491c37e4SRabie Loulou dst->dev == uplink_upper && 97491c37e4SRabie Loulou mlx5_lag_is_active(priv->mdev)); 98491c37e4SRabie Loulou 99491c37e4SRabie Loulou /* if the egress device isn't on the same HW e-switch or 100491c37e4SRabie Loulou * it's a LAG device, use the uplink 101491c37e4SRabie Loulou */ 102491c37e4SRabie Loulou if (!switchdev_port_same_parent_id(priv->netdev, dst->dev) || 103491c37e4SRabie Loulou dst_is_lag_dev) 104491c37e4SRabie Loulou *out_dev = uplink_dev; 105101f4de9SOz Shlomo else 106101f4de9SOz Shlomo *out_dev = dst->dev; 107101f4de9SOz Shlomo #else 108101f4de9SOz Shlomo return -EOPNOTSUPP; 109101f4de9SOz Shlomo #endif 110101f4de9SOz Shlomo 111101f4de9SOz Shlomo n = dst_neigh_lookup(dst, &fl6->daddr); 112101f4de9SOz Shlomo dst_release(dst); 113101f4de9SOz Shlomo if (!n) 114101f4de9SOz Shlomo return -ENOMEM; 115101f4de9SOz Shlomo 116101f4de9SOz Shlomo *out_n = n; 117101f4de9SOz Shlomo return 0; 118101f4de9SOz Shlomo } 119101f4de9SOz Shlomo 120101f4de9SOz Shlomo static int mlx5e_gen_vxlan_header(char buf[], struct ip_tunnel_key *tun_key) 121101f4de9SOz Shlomo { 122101f4de9SOz Shlomo __be32 tun_id = tunnel_id_to_key32(tun_key->tun_id); 123101f4de9SOz Shlomo struct udphdr *udp = (struct udphdr *)(buf); 124101f4de9SOz Shlomo struct vxlanhdr *vxh = (struct vxlanhdr *) 125101f4de9SOz Shlomo ((char *)udp + sizeof(struct udphdr)); 126101f4de9SOz Shlomo 127101f4de9SOz Shlomo udp->dest = tun_key->tp_dst; 128101f4de9SOz Shlomo vxh->vx_flags = VXLAN_HF_VNI; 129101f4de9SOz Shlomo vxh->vx_vni = vxlan_vni_field(tun_id); 130101f4de9SOz Shlomo 131101f4de9SOz Shlomo return 0; 132101f4de9SOz Shlomo } 133101f4de9SOz Shlomo 134df2ef3bfSOz Shlomo static int mlx5e_gen_gre_header(char buf[], struct ip_tunnel_key *tun_key) 135df2ef3bfSOz Shlomo { 136df2ef3bfSOz Shlomo __be32 tun_id = tunnel_id_to_key32(tun_key->tun_id); 137df2ef3bfSOz Shlomo int hdr_len; 138df2ef3bfSOz Shlomo struct gre_base_hdr *greh = (struct gre_base_hdr *)(buf); 139df2ef3bfSOz Shlomo 140df2ef3bfSOz Shlomo /* the HW does not calculate GRE csum or sequences */ 141df2ef3bfSOz Shlomo if (tun_key->tun_flags & (TUNNEL_CSUM | TUNNEL_SEQ)) 142df2ef3bfSOz Shlomo return -EOPNOTSUPP; 143df2ef3bfSOz Shlomo 144df2ef3bfSOz Shlomo greh->protocol = htons(ETH_P_TEB); 145df2ef3bfSOz Shlomo 146df2ef3bfSOz Shlomo /* GRE key */ 147df2ef3bfSOz Shlomo hdr_len = gre_calc_hlen(tun_key->tun_flags); 148df2ef3bfSOz Shlomo greh->flags = gre_tnl_flags_to_gre_flags(tun_key->tun_flags); 149df2ef3bfSOz Shlomo if (tun_key->tun_flags & TUNNEL_KEY) { 150df2ef3bfSOz Shlomo __be32 *ptr = (__be32 *)(((u8 *)greh) + hdr_len - 4); 151df2ef3bfSOz Shlomo 152df2ef3bfSOz Shlomo *ptr = tun_id; 153df2ef3bfSOz Shlomo } 154df2ef3bfSOz Shlomo 155df2ef3bfSOz Shlomo return 0; 156df2ef3bfSOz Shlomo } 157df2ef3bfSOz Shlomo 158101f4de9SOz Shlomo static int mlx5e_gen_ip_tunnel_header(char buf[], __u8 *ip_proto, 159101f4de9SOz Shlomo struct mlx5e_encap_entry *e) 160101f4de9SOz Shlomo { 161101f4de9SOz Shlomo int err = 0; 162101f4de9SOz Shlomo struct ip_tunnel_key *key = &e->tun_info.key; 163101f4de9SOz Shlomo 164101f4de9SOz Shlomo if (e->tunnel_type == MLX5E_TC_TUNNEL_TYPE_VXLAN) { 165101f4de9SOz Shlomo *ip_proto = IPPROTO_UDP; 166101f4de9SOz Shlomo err = mlx5e_gen_vxlan_header(buf, key); 167df2ef3bfSOz Shlomo } else if (e->tunnel_type == MLX5E_TC_TUNNEL_TYPE_GRETAP) { 168df2ef3bfSOz Shlomo *ip_proto = IPPROTO_GRE; 169df2ef3bfSOz Shlomo err = mlx5e_gen_gre_header(buf, key); 170101f4de9SOz Shlomo } else { 171101f4de9SOz Shlomo pr_warn("mlx5: Cannot generate tunnel header for tunnel type (%d)\n" 172101f4de9SOz Shlomo , e->tunnel_type); 173101f4de9SOz Shlomo err = -EOPNOTSUPP; 174101f4de9SOz Shlomo } 175101f4de9SOz Shlomo 176101f4de9SOz Shlomo return err; 177101f4de9SOz Shlomo } 178101f4de9SOz Shlomo 179101f4de9SOz Shlomo int mlx5e_tc_tun_create_header_ipv4(struct mlx5e_priv *priv, 180101f4de9SOz Shlomo struct net_device *mirred_dev, 181101f4de9SOz Shlomo struct mlx5e_encap_entry *e) 182101f4de9SOz Shlomo { 183101f4de9SOz Shlomo int max_encap_size = MLX5_CAP_ESW(priv->mdev, max_encap_header_size); 184101f4de9SOz Shlomo int ipv4_encap_size = ETH_HLEN + 185101f4de9SOz Shlomo sizeof(struct iphdr) + 186101f4de9SOz Shlomo e->tunnel_hlen; 187101f4de9SOz Shlomo struct ip_tunnel_key *tun_key = &e->tun_info.key; 188101f4de9SOz Shlomo struct net_device *out_dev; 189101f4de9SOz Shlomo struct neighbour *n = NULL; 190101f4de9SOz Shlomo struct flowi4 fl4 = {}; 191101f4de9SOz Shlomo char *encap_header; 192101f4de9SOz Shlomo struct ethhdr *eth; 193101f4de9SOz Shlomo u8 nud_state, ttl; 194101f4de9SOz Shlomo struct iphdr *ip; 195101f4de9SOz Shlomo int err; 196101f4de9SOz Shlomo 197101f4de9SOz Shlomo if (max_encap_size < ipv4_encap_size) { 198101f4de9SOz Shlomo mlx5_core_warn(priv->mdev, "encap size %d too big, max supported is %d\n", 199101f4de9SOz Shlomo ipv4_encap_size, max_encap_size); 200101f4de9SOz Shlomo return -EOPNOTSUPP; 201101f4de9SOz Shlomo } 202101f4de9SOz Shlomo 203101f4de9SOz Shlomo encap_header = kzalloc(ipv4_encap_size, GFP_KERNEL); 204101f4de9SOz Shlomo if (!encap_header) 205101f4de9SOz Shlomo return -ENOMEM; 206101f4de9SOz Shlomo 207101f4de9SOz Shlomo /* add the IP fields */ 208101f4de9SOz Shlomo fl4.flowi4_tos = tun_key->tos; 209101f4de9SOz Shlomo fl4.daddr = tun_key->u.ipv4.dst; 210101f4de9SOz Shlomo fl4.saddr = tun_key->u.ipv4.src; 211101f4de9SOz Shlomo ttl = tun_key->ttl; 212101f4de9SOz Shlomo 213101f4de9SOz Shlomo err = mlx5e_route_lookup_ipv4(priv, mirred_dev, &out_dev, 214101f4de9SOz Shlomo &fl4, &n, &ttl); 215101f4de9SOz Shlomo if (err) 216101f4de9SOz Shlomo goto free_encap; 217101f4de9SOz Shlomo 218101f4de9SOz Shlomo /* used by mlx5e_detach_encap to lookup a neigh hash table 219101f4de9SOz Shlomo * entry in the neigh hash table when a user deletes a rule 220101f4de9SOz Shlomo */ 221101f4de9SOz Shlomo e->m_neigh.dev = n->dev; 222101f4de9SOz Shlomo e->m_neigh.family = n->ops->family; 223101f4de9SOz Shlomo memcpy(&e->m_neigh.dst_ip, n->primary_key, n->tbl->key_len); 224101f4de9SOz Shlomo e->out_dev = out_dev; 225101f4de9SOz Shlomo 226101f4de9SOz Shlomo /* It's important to add the neigh to the hash table before checking 227101f4de9SOz Shlomo * the neigh validity state. So if we'll get a notification, in case the 228101f4de9SOz Shlomo * neigh changes it's validity state, we would find the relevant neigh 229101f4de9SOz Shlomo * in the hash. 230101f4de9SOz Shlomo */ 231101f4de9SOz Shlomo err = mlx5e_rep_encap_entry_attach(netdev_priv(out_dev), e); 232101f4de9SOz Shlomo if (err) 233101f4de9SOz Shlomo goto free_encap; 234101f4de9SOz Shlomo 235101f4de9SOz Shlomo read_lock_bh(&n->lock); 236101f4de9SOz Shlomo nud_state = n->nud_state; 237101f4de9SOz Shlomo ether_addr_copy(e->h_dest, n->ha); 238101f4de9SOz Shlomo read_unlock_bh(&n->lock); 239101f4de9SOz Shlomo 240101f4de9SOz Shlomo /* add ethernet header */ 241101f4de9SOz Shlomo eth = (struct ethhdr *)encap_header; 242101f4de9SOz Shlomo ether_addr_copy(eth->h_dest, e->h_dest); 243101f4de9SOz Shlomo ether_addr_copy(eth->h_source, out_dev->dev_addr); 244101f4de9SOz Shlomo eth->h_proto = htons(ETH_P_IP); 245101f4de9SOz Shlomo 246101f4de9SOz Shlomo /* add ip header */ 247101f4de9SOz Shlomo ip = (struct iphdr *)((char *)eth + sizeof(struct ethhdr)); 248101f4de9SOz Shlomo ip->tos = tun_key->tos; 249101f4de9SOz Shlomo ip->version = 0x4; 250101f4de9SOz Shlomo ip->ihl = 0x5; 251101f4de9SOz Shlomo ip->ttl = ttl; 252101f4de9SOz Shlomo ip->daddr = fl4.daddr; 253101f4de9SOz Shlomo ip->saddr = fl4.saddr; 254101f4de9SOz Shlomo 255101f4de9SOz Shlomo /* add tunneling protocol header */ 256101f4de9SOz Shlomo err = mlx5e_gen_ip_tunnel_header((char *)ip + sizeof(struct iphdr), 257101f4de9SOz Shlomo &ip->protocol, e); 258101f4de9SOz Shlomo if (err) 259101f4de9SOz Shlomo goto destroy_neigh_entry; 260101f4de9SOz Shlomo 261101f4de9SOz Shlomo e->encap_size = ipv4_encap_size; 262101f4de9SOz Shlomo e->encap_header = encap_header; 263101f4de9SOz Shlomo 264101f4de9SOz Shlomo if (!(nud_state & NUD_VALID)) { 265101f4de9SOz Shlomo neigh_event_send(n, NULL); 266101f4de9SOz Shlomo err = -EAGAIN; 267101f4de9SOz Shlomo goto out; 268101f4de9SOz Shlomo } 269101f4de9SOz Shlomo 270101f4de9SOz Shlomo err = mlx5_packet_reformat_alloc(priv->mdev, 271101f4de9SOz Shlomo e->reformat_type, 272101f4de9SOz Shlomo ipv4_encap_size, encap_header, 273101f4de9SOz Shlomo MLX5_FLOW_NAMESPACE_FDB, 274101f4de9SOz Shlomo &e->encap_id); 275101f4de9SOz Shlomo if (err) 276101f4de9SOz Shlomo goto destroy_neigh_entry; 277101f4de9SOz Shlomo 278101f4de9SOz Shlomo e->flags |= MLX5_ENCAP_ENTRY_VALID; 279101f4de9SOz Shlomo mlx5e_rep_queue_neigh_stats_work(netdev_priv(out_dev)); 280101f4de9SOz Shlomo neigh_release(n); 281101f4de9SOz Shlomo return err; 282101f4de9SOz Shlomo 283101f4de9SOz Shlomo destroy_neigh_entry: 284101f4de9SOz Shlomo mlx5e_rep_encap_entry_detach(netdev_priv(e->out_dev), e); 285101f4de9SOz Shlomo free_encap: 286101f4de9SOz Shlomo kfree(encap_header); 287101f4de9SOz Shlomo out: 288101f4de9SOz Shlomo if (n) 289101f4de9SOz Shlomo neigh_release(n); 290101f4de9SOz Shlomo return err; 291101f4de9SOz Shlomo } 292101f4de9SOz Shlomo 293101f4de9SOz Shlomo int mlx5e_tc_tun_create_header_ipv6(struct mlx5e_priv *priv, 294101f4de9SOz Shlomo struct net_device *mirred_dev, 295101f4de9SOz Shlomo struct mlx5e_encap_entry *e) 296101f4de9SOz Shlomo { 297101f4de9SOz Shlomo int max_encap_size = MLX5_CAP_ESW(priv->mdev, max_encap_header_size); 298101f4de9SOz Shlomo int ipv6_encap_size = ETH_HLEN + 299101f4de9SOz Shlomo sizeof(struct ipv6hdr) + 300101f4de9SOz Shlomo e->tunnel_hlen; 301101f4de9SOz Shlomo struct ip_tunnel_key *tun_key = &e->tun_info.key; 302101f4de9SOz Shlomo struct net_device *out_dev; 303101f4de9SOz Shlomo struct neighbour *n = NULL; 304101f4de9SOz Shlomo struct flowi6 fl6 = {}; 305101f4de9SOz Shlomo struct ipv6hdr *ip6h; 306101f4de9SOz Shlomo char *encap_header; 307101f4de9SOz Shlomo struct ethhdr *eth; 308101f4de9SOz Shlomo u8 nud_state, ttl; 309101f4de9SOz Shlomo int err; 310101f4de9SOz Shlomo 311101f4de9SOz Shlomo if (max_encap_size < ipv6_encap_size) { 312101f4de9SOz Shlomo mlx5_core_warn(priv->mdev, "encap size %d too big, max supported is %d\n", 313101f4de9SOz Shlomo ipv6_encap_size, max_encap_size); 314101f4de9SOz Shlomo return -EOPNOTSUPP; 315101f4de9SOz Shlomo } 316101f4de9SOz Shlomo 317101f4de9SOz Shlomo encap_header = kzalloc(ipv6_encap_size, GFP_KERNEL); 318101f4de9SOz Shlomo if (!encap_header) 319101f4de9SOz Shlomo return -ENOMEM; 320101f4de9SOz Shlomo 321101f4de9SOz Shlomo ttl = tun_key->ttl; 322101f4de9SOz Shlomo 323101f4de9SOz Shlomo fl6.flowlabel = ip6_make_flowinfo(RT_TOS(tun_key->tos), tun_key->label); 324101f4de9SOz Shlomo fl6.daddr = tun_key->u.ipv6.dst; 325101f4de9SOz Shlomo fl6.saddr = tun_key->u.ipv6.src; 326101f4de9SOz Shlomo 327101f4de9SOz Shlomo err = mlx5e_route_lookup_ipv6(priv, mirred_dev, &out_dev, 328101f4de9SOz Shlomo &fl6, &n, &ttl); 329101f4de9SOz Shlomo if (err) 330101f4de9SOz Shlomo goto free_encap; 331101f4de9SOz Shlomo 332101f4de9SOz Shlomo /* used by mlx5e_detach_encap to lookup a neigh hash table 333101f4de9SOz Shlomo * entry in the neigh hash table when a user deletes a rule 334101f4de9SOz Shlomo */ 335101f4de9SOz Shlomo e->m_neigh.dev = n->dev; 336101f4de9SOz Shlomo e->m_neigh.family = n->ops->family; 337101f4de9SOz Shlomo memcpy(&e->m_neigh.dst_ip, n->primary_key, n->tbl->key_len); 338101f4de9SOz Shlomo e->out_dev = out_dev; 339101f4de9SOz Shlomo 340101f4de9SOz Shlomo /* It's importent to add the neigh to the hash table before checking 341101f4de9SOz Shlomo * the neigh validity state. So if we'll get a notification, in case the 342101f4de9SOz Shlomo * neigh changes it's validity state, we would find the relevant neigh 343101f4de9SOz Shlomo * in the hash. 344101f4de9SOz Shlomo */ 345101f4de9SOz Shlomo err = mlx5e_rep_encap_entry_attach(netdev_priv(out_dev), e); 346101f4de9SOz Shlomo if (err) 347101f4de9SOz Shlomo goto free_encap; 348101f4de9SOz Shlomo 349101f4de9SOz Shlomo read_lock_bh(&n->lock); 350101f4de9SOz Shlomo nud_state = n->nud_state; 351101f4de9SOz Shlomo ether_addr_copy(e->h_dest, n->ha); 352101f4de9SOz Shlomo read_unlock_bh(&n->lock); 353101f4de9SOz Shlomo 354101f4de9SOz Shlomo /* add ethernet header */ 355101f4de9SOz Shlomo eth = (struct ethhdr *)encap_header; 356101f4de9SOz Shlomo ether_addr_copy(eth->h_dest, e->h_dest); 357101f4de9SOz Shlomo ether_addr_copy(eth->h_source, out_dev->dev_addr); 358101f4de9SOz Shlomo eth->h_proto = htons(ETH_P_IPV6); 359101f4de9SOz Shlomo 360101f4de9SOz Shlomo /* add ip header */ 361101f4de9SOz Shlomo ip6h = (struct ipv6hdr *)((char *)eth + sizeof(struct ethhdr)); 362101f4de9SOz Shlomo ip6_flow_hdr(ip6h, tun_key->tos, 0); 363101f4de9SOz Shlomo /* the HW fills up ipv6 payload len */ 364101f4de9SOz Shlomo ip6h->hop_limit = ttl; 365101f4de9SOz Shlomo ip6h->daddr = fl6.daddr; 366101f4de9SOz Shlomo ip6h->saddr = fl6.saddr; 367101f4de9SOz Shlomo 368101f4de9SOz Shlomo /* add tunneling protocol header */ 369101f4de9SOz Shlomo err = mlx5e_gen_ip_tunnel_header((char *)ip6h + sizeof(struct ipv6hdr), 370101f4de9SOz Shlomo &ip6h->nexthdr, e); 371101f4de9SOz Shlomo if (err) 372101f4de9SOz Shlomo goto destroy_neigh_entry; 373101f4de9SOz Shlomo 374101f4de9SOz Shlomo e->encap_size = ipv6_encap_size; 375101f4de9SOz Shlomo e->encap_header = encap_header; 376101f4de9SOz Shlomo 377101f4de9SOz Shlomo if (!(nud_state & NUD_VALID)) { 378101f4de9SOz Shlomo neigh_event_send(n, NULL); 379101f4de9SOz Shlomo err = -EAGAIN; 380101f4de9SOz Shlomo goto out; 381101f4de9SOz Shlomo } 382101f4de9SOz Shlomo 383101f4de9SOz Shlomo err = mlx5_packet_reformat_alloc(priv->mdev, 384101f4de9SOz Shlomo e->reformat_type, 385101f4de9SOz Shlomo ipv6_encap_size, encap_header, 386101f4de9SOz Shlomo MLX5_FLOW_NAMESPACE_FDB, 387101f4de9SOz Shlomo &e->encap_id); 388101f4de9SOz Shlomo if (err) 389101f4de9SOz Shlomo goto destroy_neigh_entry; 390101f4de9SOz Shlomo 391101f4de9SOz Shlomo e->flags |= MLX5_ENCAP_ENTRY_VALID; 392101f4de9SOz Shlomo mlx5e_rep_queue_neigh_stats_work(netdev_priv(out_dev)); 393101f4de9SOz Shlomo neigh_release(n); 394101f4de9SOz Shlomo return err; 395101f4de9SOz Shlomo 396101f4de9SOz Shlomo destroy_neigh_entry: 397101f4de9SOz Shlomo mlx5e_rep_encap_entry_detach(netdev_priv(e->out_dev), e); 398101f4de9SOz Shlomo free_encap: 399101f4de9SOz Shlomo kfree(encap_header); 400101f4de9SOz Shlomo out: 401101f4de9SOz Shlomo if (n) 402101f4de9SOz Shlomo neigh_release(n); 403101f4de9SOz Shlomo return err; 404101f4de9SOz Shlomo } 405101f4de9SOz Shlomo 406101f4de9SOz Shlomo int mlx5e_tc_tun_get_type(struct net_device *tunnel_dev) 407101f4de9SOz Shlomo { 408101f4de9SOz Shlomo if (netif_is_vxlan(tunnel_dev)) 409101f4de9SOz Shlomo return MLX5E_TC_TUNNEL_TYPE_VXLAN; 410df2ef3bfSOz Shlomo else if (netif_is_gretap(tunnel_dev) || 411df2ef3bfSOz Shlomo netif_is_ip6gretap(tunnel_dev)) 412df2ef3bfSOz Shlomo return MLX5E_TC_TUNNEL_TYPE_GRETAP; 413101f4de9SOz Shlomo else 414101f4de9SOz Shlomo return MLX5E_TC_TUNNEL_TYPE_UNKNOWN; 415101f4de9SOz Shlomo } 416101f4de9SOz Shlomo 417101f4de9SOz Shlomo bool mlx5e_tc_tun_device_to_offload(struct mlx5e_priv *priv, 418101f4de9SOz Shlomo struct net_device *netdev) 419101f4de9SOz Shlomo { 420101f4de9SOz Shlomo int tunnel_type = mlx5e_tc_tun_get_type(netdev); 421101f4de9SOz Shlomo 422101f4de9SOz Shlomo if (tunnel_type == MLX5E_TC_TUNNEL_TYPE_VXLAN && 423101f4de9SOz Shlomo MLX5_CAP_ESW(priv->mdev, vxlan_encap_decap)) 424101f4de9SOz Shlomo return true; 425df2ef3bfSOz Shlomo else if (tunnel_type == MLX5E_TC_TUNNEL_TYPE_GRETAP && 426df2ef3bfSOz Shlomo MLX5_CAP_ESW(priv->mdev, nvgre_encap_decap)) 427df2ef3bfSOz Shlomo return true; 428101f4de9SOz Shlomo else 429101f4de9SOz Shlomo return false; 430101f4de9SOz Shlomo } 431101f4de9SOz Shlomo 432101f4de9SOz Shlomo int mlx5e_tc_tun_init_encap_attr(struct net_device *tunnel_dev, 433101f4de9SOz Shlomo struct mlx5e_priv *priv, 434101f4de9SOz Shlomo struct mlx5e_encap_entry *e, 435101f4de9SOz Shlomo struct netlink_ext_ack *extack) 436101f4de9SOz Shlomo { 437101f4de9SOz Shlomo e->tunnel_type = mlx5e_tc_tun_get_type(tunnel_dev); 438101f4de9SOz Shlomo 439101f4de9SOz Shlomo if (e->tunnel_type == MLX5E_TC_TUNNEL_TYPE_VXLAN) { 440101f4de9SOz Shlomo int dst_port = be16_to_cpu(e->tun_info.key.tp_dst); 441101f4de9SOz Shlomo 442101f4de9SOz Shlomo if (!mlx5_vxlan_lookup_port(priv->mdev->vxlan, dst_port)) { 443101f4de9SOz Shlomo NL_SET_ERR_MSG_MOD(extack, 444101f4de9SOz Shlomo "vxlan udp dport was not registered with the HW"); 445101f4de9SOz Shlomo netdev_warn(priv->netdev, 446101f4de9SOz Shlomo "%d isn't an offloaded vxlan udp dport\n", 447101f4de9SOz Shlomo dst_port); 448101f4de9SOz Shlomo return -EOPNOTSUPP; 449101f4de9SOz Shlomo } 450101f4de9SOz Shlomo e->reformat_type = MLX5_REFORMAT_TYPE_L2_TO_VXLAN; 451101f4de9SOz Shlomo e->tunnel_hlen = VXLAN_HLEN; 452df2ef3bfSOz Shlomo } else if (e->tunnel_type == MLX5E_TC_TUNNEL_TYPE_GRETAP) { 453df2ef3bfSOz Shlomo e->reformat_type = MLX5_REFORMAT_TYPE_L2_TO_NVGRE; 454df2ef3bfSOz Shlomo e->tunnel_hlen = gre_calc_hlen(e->tun_info.key.tun_flags); 455101f4de9SOz Shlomo } else { 456101f4de9SOz Shlomo e->reformat_type = -1; 457101f4de9SOz Shlomo e->tunnel_hlen = -1; 458101f4de9SOz Shlomo return -EOPNOTSUPP; 459101f4de9SOz Shlomo } 460101f4de9SOz Shlomo return 0; 461101f4de9SOz Shlomo } 462101f4de9SOz Shlomo 463101f4de9SOz Shlomo static int mlx5e_tc_tun_parse_vxlan(struct mlx5e_priv *priv, 464101f4de9SOz Shlomo struct mlx5_flow_spec *spec, 465101f4de9SOz Shlomo struct tc_cls_flower_offload *f, 466101f4de9SOz Shlomo void *headers_c, 467101f4de9SOz Shlomo void *headers_v) 468101f4de9SOz Shlomo { 469101f4de9SOz Shlomo struct netlink_ext_ack *extack = f->common.extack; 470101f4de9SOz Shlomo struct flow_dissector_key_ports *key = 471101f4de9SOz Shlomo skb_flow_dissector_target(f->dissector, 472101f4de9SOz Shlomo FLOW_DISSECTOR_KEY_ENC_PORTS, 473101f4de9SOz Shlomo f->key); 474101f4de9SOz Shlomo struct flow_dissector_key_ports *mask = 475101f4de9SOz Shlomo skb_flow_dissector_target(f->dissector, 476101f4de9SOz Shlomo FLOW_DISSECTOR_KEY_ENC_PORTS, 477101f4de9SOz Shlomo f->mask); 478101f4de9SOz Shlomo void *misc_c = MLX5_ADDR_OF(fte_match_param, 479101f4de9SOz Shlomo spec->match_criteria, 480101f4de9SOz Shlomo misc_parameters); 481101f4de9SOz Shlomo void *misc_v = MLX5_ADDR_OF(fte_match_param, 482101f4de9SOz Shlomo spec->match_value, 483101f4de9SOz Shlomo misc_parameters); 484101f4de9SOz Shlomo 485101f4de9SOz Shlomo /* Full udp dst port must be given */ 486101f4de9SOz Shlomo if (!dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ENC_PORTS) || 487101f4de9SOz Shlomo memchr_inv(&mask->dst, 0xff, sizeof(mask->dst))) { 488101f4de9SOz Shlomo NL_SET_ERR_MSG_MOD(extack, 489101f4de9SOz Shlomo "VXLAN decap filter must include enc_dst_port condition"); 490101f4de9SOz Shlomo netdev_warn(priv->netdev, 491101f4de9SOz Shlomo "VXLAN decap filter must include enc_dst_port condition\n"); 492101f4de9SOz Shlomo return -EOPNOTSUPP; 493101f4de9SOz Shlomo } 494101f4de9SOz Shlomo 495101f4de9SOz Shlomo /* udp dst port must be knonwn as a VXLAN port */ 496101f4de9SOz Shlomo if (!mlx5_vxlan_lookup_port(priv->mdev->vxlan, be16_to_cpu(key->dst))) { 497101f4de9SOz Shlomo NL_SET_ERR_MSG_MOD(extack, 498101f4de9SOz Shlomo "Matched UDP port is not registered as a VXLAN port"); 499101f4de9SOz Shlomo netdev_warn(priv->netdev, 500101f4de9SOz Shlomo "UDP port %d is not registered as a VXLAN port\n", 501101f4de9SOz Shlomo be16_to_cpu(key->dst)); 502101f4de9SOz Shlomo return -EOPNOTSUPP; 503101f4de9SOz Shlomo } 504101f4de9SOz Shlomo 505101f4de9SOz Shlomo /* dst UDP port is valid here */ 506101f4de9SOz Shlomo MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, headers_c, ip_protocol); 507101f4de9SOz Shlomo MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_protocol, IPPROTO_UDP); 508101f4de9SOz Shlomo 509101f4de9SOz Shlomo MLX5_SET(fte_match_set_lyr_2_4, headers_c, udp_dport, ntohs(mask->dst)); 510101f4de9SOz Shlomo MLX5_SET(fte_match_set_lyr_2_4, headers_v, udp_dport, ntohs(key->dst)); 511101f4de9SOz Shlomo 512101f4de9SOz Shlomo MLX5_SET(fte_match_set_lyr_2_4, headers_c, udp_sport, ntohs(mask->src)); 513101f4de9SOz Shlomo MLX5_SET(fte_match_set_lyr_2_4, headers_v, udp_sport, ntohs(key->src)); 514101f4de9SOz Shlomo 515101f4de9SOz Shlomo /* match on VNI */ 516101f4de9SOz Shlomo if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ENC_KEYID)) { 517101f4de9SOz Shlomo struct flow_dissector_key_keyid *key = 518101f4de9SOz Shlomo skb_flow_dissector_target(f->dissector, 519101f4de9SOz Shlomo FLOW_DISSECTOR_KEY_ENC_KEYID, 520101f4de9SOz Shlomo f->key); 521101f4de9SOz Shlomo struct flow_dissector_key_keyid *mask = 522101f4de9SOz Shlomo skb_flow_dissector_target(f->dissector, 523101f4de9SOz Shlomo FLOW_DISSECTOR_KEY_ENC_KEYID, 524101f4de9SOz Shlomo f->mask); 525101f4de9SOz Shlomo MLX5_SET(fte_match_set_misc, misc_c, vxlan_vni, 526101f4de9SOz Shlomo be32_to_cpu(mask->keyid)); 527101f4de9SOz Shlomo MLX5_SET(fte_match_set_misc, misc_v, vxlan_vni, 528101f4de9SOz Shlomo be32_to_cpu(key->keyid)); 529101f4de9SOz Shlomo } 530101f4de9SOz Shlomo return 0; 531101f4de9SOz Shlomo } 532101f4de9SOz Shlomo 533df2ef3bfSOz Shlomo static int mlx5e_tc_tun_parse_gretap(struct mlx5e_priv *priv, 534df2ef3bfSOz Shlomo struct mlx5_flow_spec *spec, 535df2ef3bfSOz Shlomo struct tc_cls_flower_offload *f, 536df2ef3bfSOz Shlomo void *outer_headers_c, 537df2ef3bfSOz Shlomo void *outer_headers_v) 538df2ef3bfSOz Shlomo { 539df2ef3bfSOz Shlomo void *misc_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, 540df2ef3bfSOz Shlomo misc_parameters); 541df2ef3bfSOz Shlomo void *misc_v = MLX5_ADDR_OF(fte_match_param, spec->match_value, 542df2ef3bfSOz Shlomo misc_parameters); 543df2ef3bfSOz Shlomo 544df2ef3bfSOz Shlomo if (!MLX5_CAP_ESW(priv->mdev, nvgre_encap_decap)) { 545df2ef3bfSOz Shlomo NL_SET_ERR_MSG_MOD(f->common.extack, 546df2ef3bfSOz Shlomo "GRE HW offloading is not supported"); 547df2ef3bfSOz Shlomo netdev_warn(priv->netdev, "GRE HW offloading is not supported\n"); 548df2ef3bfSOz Shlomo return -EOPNOTSUPP; 549df2ef3bfSOz Shlomo } 550df2ef3bfSOz Shlomo 551df2ef3bfSOz Shlomo MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, outer_headers_c, ip_protocol); 552df2ef3bfSOz Shlomo MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v, 553df2ef3bfSOz Shlomo ip_protocol, IPPROTO_GRE); 554df2ef3bfSOz Shlomo 555df2ef3bfSOz Shlomo /* gre protocol*/ 556df2ef3bfSOz Shlomo MLX5_SET_TO_ONES(fte_match_set_misc, misc_c, gre_protocol); 557df2ef3bfSOz Shlomo MLX5_SET(fte_match_set_misc, misc_v, gre_protocol, ETH_P_TEB); 558df2ef3bfSOz Shlomo 559df2ef3bfSOz Shlomo /* gre key */ 560df2ef3bfSOz Shlomo if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ENC_KEYID)) { 561df2ef3bfSOz Shlomo struct flow_dissector_key_keyid *mask = NULL; 562df2ef3bfSOz Shlomo struct flow_dissector_key_keyid *key = NULL; 563df2ef3bfSOz Shlomo 564df2ef3bfSOz Shlomo mask = skb_flow_dissector_target(f->dissector, 565df2ef3bfSOz Shlomo FLOW_DISSECTOR_KEY_ENC_KEYID, 566df2ef3bfSOz Shlomo f->mask); 567df2ef3bfSOz Shlomo MLX5_SET(fte_match_set_misc, misc_c, 568df2ef3bfSOz Shlomo gre_key.key, be32_to_cpu(mask->keyid)); 569df2ef3bfSOz Shlomo 570df2ef3bfSOz Shlomo key = skb_flow_dissector_target(f->dissector, 571df2ef3bfSOz Shlomo FLOW_DISSECTOR_KEY_ENC_KEYID, 572df2ef3bfSOz Shlomo f->key); 573df2ef3bfSOz Shlomo MLX5_SET(fte_match_set_misc, misc_v, 574df2ef3bfSOz Shlomo gre_key.key, be32_to_cpu(key->keyid)); 575df2ef3bfSOz Shlomo } 576df2ef3bfSOz Shlomo 577df2ef3bfSOz Shlomo return 0; 578df2ef3bfSOz Shlomo } 579df2ef3bfSOz Shlomo 580101f4de9SOz Shlomo int mlx5e_tc_tun_parse(struct net_device *filter_dev, 581101f4de9SOz Shlomo struct mlx5e_priv *priv, 582101f4de9SOz Shlomo struct mlx5_flow_spec *spec, 583101f4de9SOz Shlomo struct tc_cls_flower_offload *f, 584101f4de9SOz Shlomo void *headers_c, 585101f4de9SOz Shlomo void *headers_v) 586101f4de9SOz Shlomo { 587101f4de9SOz Shlomo int tunnel_type; 588101f4de9SOz Shlomo int err = 0; 589101f4de9SOz Shlomo 590101f4de9SOz Shlomo tunnel_type = mlx5e_tc_tun_get_type(filter_dev); 591101f4de9SOz Shlomo if (tunnel_type == MLX5E_TC_TUNNEL_TYPE_VXLAN) { 592101f4de9SOz Shlomo err = mlx5e_tc_tun_parse_vxlan(priv, spec, f, 593101f4de9SOz Shlomo headers_c, headers_v); 594df2ef3bfSOz Shlomo } else if (tunnel_type == MLX5E_TC_TUNNEL_TYPE_GRETAP) { 595df2ef3bfSOz Shlomo err = mlx5e_tc_tun_parse_gretap(priv, spec, f, 596df2ef3bfSOz Shlomo headers_c, headers_v); 597101f4de9SOz Shlomo } else { 598101f4de9SOz Shlomo netdev_warn(priv->netdev, 599101f4de9SOz Shlomo "decapsulation offload is not supported for %s net device (%d)\n", 600101f4de9SOz Shlomo mlx5e_netdev_kind(filter_dev), tunnel_type); 601101f4de9SOz Shlomo return -EOPNOTSUPP; 602101f4de9SOz Shlomo } 603101f4de9SOz Shlomo return err; 604101f4de9SOz Shlomo } 605