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> 69272e3dfSYevgeny Kliteynik #include <net/geneve.h> 7f828ca6aSEli Cohen #include <net/bareudp.h> 8101f4de9SOz Shlomo #include "en/tc_tun.h" 90d9f9647SVlad Buslov #include "en/tc_priv.h" 10f6dc1264SPaul Blakey #include "en_tc.h" 11768c3667SVlad Buslov #include "rep/tc.h" 12549c243eSVlad Buslov #include "rep/neigh.h" 13016c8946SJakub Kicinski #include "lag/lag.h" 14016c8946SJakub Kicinski #include "lag/mp.h" 15101f4de9SOz Shlomo 166717986eSVlad Buslov struct mlx5e_tc_tun_route_attr { 176717986eSVlad Buslov struct net_device *out_dev; 186717986eSVlad Buslov struct net_device *route_dev; 196717986eSVlad Buslov union { 206717986eSVlad Buslov struct flowi4 fl4; 216717986eSVlad Buslov struct flowi6 fl6; 226717986eSVlad Buslov } fl; 236717986eSVlad Buslov struct neighbour *n; 246717986eSVlad Buslov u8 ttl; 256717986eSVlad Buslov }; 266717986eSVlad Buslov 276717986eSVlad Buslov #define TC_TUN_ROUTE_ATTR_INIT(name) struct mlx5e_tc_tun_route_attr name = {} 286717986eSVlad Buslov 296717986eSVlad Buslov static void mlx5e_tc_tun_route_attr_cleanup(struct mlx5e_tc_tun_route_attr *attr) 306717986eSVlad Buslov { 316717986eSVlad Buslov if (attr->n) 326717986eSVlad Buslov neigh_release(attr->n); 336717986eSVlad Buslov if (attr->route_dev) 346717986eSVlad Buslov dev_put(attr->route_dev); 356717986eSVlad Buslov } 366717986eSVlad Buslov 37d386939aSYevgeny Kliteynik struct mlx5e_tc_tunnel *mlx5e_get_tc_tun(struct net_device *tunnel_dev) 38d386939aSYevgeny Kliteynik { 39d386939aSYevgeny Kliteynik if (netif_is_vxlan(tunnel_dev)) 40d386939aSYevgeny Kliteynik return &vxlan_tunnel; 419272e3dfSYevgeny Kliteynik else if (netif_is_geneve(tunnel_dev)) 429272e3dfSYevgeny Kliteynik return &geneve_tunnel; 43d386939aSYevgeny Kliteynik else if (netif_is_gretap(tunnel_dev) || 44d386939aSYevgeny Kliteynik netif_is_ip6gretap(tunnel_dev)) 45d386939aSYevgeny Kliteynik return &gre_tunnel; 46f828ca6aSEli Cohen else if (netif_is_bareudp(tunnel_dev)) 47f828ca6aSEli Cohen return &mplsoudp_tunnel; 48d386939aSYevgeny Kliteynik else 49d386939aSYevgeny Kliteynik return NULL; 50d386939aSYevgeny Kliteynik } 51d386939aSYevgeny Kliteynik 52442e1228SEli Britstein static int get_route_and_out_devs(struct mlx5e_priv *priv, 53442e1228SEli Britstein struct net_device *dev, 54442e1228SEli Britstein struct net_device **route_dev, 55442e1228SEli Britstein struct net_device **out_dev) 56442e1228SEli Britstein { 5745e7d4c0SEli Britstein struct net_device *uplink_dev, *uplink_upper, *real_dev; 58442e1228SEli Britstein struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; 59442e1228SEli Britstein bool dst_is_lag_dev; 60442e1228SEli Britstein 6145e7d4c0SEli Britstein real_dev = is_vlan_dev(dev) ? vlan_dev_real_dev(dev) : dev; 62442e1228SEli Britstein uplink_dev = mlx5_eswitch_uplink_get_proto_dev(esw, REP_ETH); 63fa833bd5SVlad Buslov 64fa833bd5SVlad Buslov rcu_read_lock(); 65fa833bd5SVlad Buslov uplink_upper = netdev_master_upper_dev_get_rcu(uplink_dev); 66fa833bd5SVlad Buslov /* mlx5_lag_is_sriov() is a blocking function which can't be called 67fa833bd5SVlad Buslov * while holding rcu read lock. Take the net_device for correctness 68fa833bd5SVlad Buslov * sake. 69fa833bd5SVlad Buslov */ 70fa833bd5SVlad Buslov if (uplink_upper) 71fa833bd5SVlad Buslov dev_hold(uplink_upper); 72fa833bd5SVlad Buslov rcu_read_unlock(); 73fa833bd5SVlad Buslov 74442e1228SEli Britstein dst_is_lag_dev = (uplink_upper && 75442e1228SEli Britstein netif_is_lag_master(uplink_upper) && 7645e7d4c0SEli Britstein real_dev == uplink_upper && 77442e1228SEli Britstein mlx5_lag_is_sriov(priv->mdev)); 78fa833bd5SVlad Buslov if (uplink_upper) 79fa833bd5SVlad Buslov dev_put(uplink_upper); 80442e1228SEli Britstein 81442e1228SEli Britstein /* if the egress device isn't on the same HW e-switch or 82442e1228SEli Britstein * it's a LAG device, use the uplink 83442e1228SEli Britstein */ 84233fd212SEli Britstein *route_dev = dev; 8545e7d4c0SEli Britstein if (!netdev_port_same_parent_id(priv->netdev, real_dev) || 86*100ad4e2SAriel Levkovich dst_is_lag_dev || is_vlan_dev(*route_dev) || 87*100ad4e2SAriel Levkovich netif_is_ovs_master(*route_dev)) 88442e1228SEli Britstein *out_dev = uplink_dev; 89f6dc1264SPaul Blakey else if (mlx5e_eswitch_rep(dev) && 90f6dc1264SPaul Blakey mlx5e_is_valid_eswitch_fwd_dev(priv, dev)) 91e32ee6c7SEli Britstein *out_dev = *route_dev; 92442e1228SEli Britstein else 93e32ee6c7SEli Britstein return -EOPNOTSUPP; 94442e1228SEli Britstein 955e0060b1SDmytro Linkin if (!(mlx5e_eswitch_rep(*out_dev) && 965e0060b1SDmytro Linkin mlx5e_is_uplink_rep(netdev_priv(*out_dev)))) 975e0060b1SDmytro Linkin return -EOPNOTSUPP; 985e0060b1SDmytro Linkin 9987b51810SEli Cohen if (mlx5e_eswitch_uplink_rep(priv->netdev) && *out_dev != priv->netdev) 10087b51810SEli Cohen return -EOPNOTSUPP; 10187b51810SEli Cohen 102442e1228SEli Britstein return 0; 103442e1228SEli Britstein } 104442e1228SEli Britstein 10578c906e4SVlad Buslov static int mlx5e_route_lookup_ipv4_get(struct mlx5e_priv *priv, 106101f4de9SOz Shlomo struct net_device *mirred_dev, 1076717986eSVlad Buslov struct mlx5e_tc_tun_route_attr *attr) 108101f4de9SOz Shlomo { 1096717986eSVlad Buslov struct net_device *route_dev; 1106717986eSVlad Buslov struct net_device *out_dev; 111e6014afdSEli Cohen struct neighbour *n; 112101f4de9SOz Shlomo struct rtable *rt; 113101f4de9SOz Shlomo 114101f4de9SOz Shlomo #if IS_ENABLED(CONFIG_INET) 1155fb091e8SRoi Dayan struct mlx5_core_dev *mdev = priv->mdev; 1165fb091e8SRoi Dayan struct net_device *uplink_dev; 117101f4de9SOz Shlomo int ret; 118101f4de9SOz Shlomo 1195fb091e8SRoi Dayan if (mlx5_lag_is_multipath(mdev)) { 1205fb091e8SRoi Dayan struct mlx5_eswitch *esw = mdev->priv.eswitch; 1215fb091e8SRoi Dayan 1225fb091e8SRoi Dayan uplink_dev = mlx5_eswitch_uplink_get_proto_dev(esw, REP_ETH); 1236717986eSVlad Buslov attr->fl.fl4.flowi4_oif = uplink_dev->ifindex; 1242f8ec867SChris Mi } else { 1252f8ec867SChris Mi struct mlx5e_tc_tunnel *tunnel = mlx5e_get_tc_tun(mirred_dev); 1262f8ec867SChris Mi 1272f8ec867SChris Mi if (tunnel && tunnel->get_remote_ifindex) 1282f8ec867SChris Mi attr->fl.fl4.flowi4_oif = tunnel->get_remote_ifindex(mirred_dev); 1295fb091e8SRoi Dayan } 1305fb091e8SRoi Dayan 1316717986eSVlad Buslov rt = ip_route_output_key(dev_net(mirred_dev), &attr->fl.fl4); 1322639324aSTang Bin if (IS_ERR(rt)) 1332639324aSTang Bin return PTR_ERR(rt); 1345fb091e8SRoi Dayan 135c623c95aSRoi Dayan if (rt->rt_type != RTN_UNICAST) { 136c623c95aSRoi Dayan ret = -ENETUNREACH; 137c623c95aSRoi Dayan goto err_rt_release; 138c623c95aSRoi Dayan } 139c623c95aSRoi Dayan 1402347cee8SParav Pandit if (mlx5_lag_is_multipath(mdev) && rt->rt_gw_family != AF_INET) { 1416717986eSVlad Buslov ret = -ENETUNREACH; 1426717986eSVlad Buslov goto err_rt_release; 1432347cee8SParav Pandit } 144101f4de9SOz Shlomo #else 145101f4de9SOz Shlomo return -EOPNOTSUPP; 146101f4de9SOz Shlomo #endif 147491c37e4SRabie Loulou 1486717986eSVlad Buslov ret = get_route_and_out_devs(priv, rt->dst.dev, &route_dev, &out_dev); 1496717986eSVlad Buslov if (ret < 0) 1506717986eSVlad Buslov goto err_rt_release; 1516717986eSVlad Buslov dev_hold(route_dev); 1526717986eSVlad Buslov 1536717986eSVlad Buslov if (!attr->ttl) 1546717986eSVlad Buslov attr->ttl = ip4_dst_hoplimit(&rt->dst); 1556717986eSVlad Buslov n = dst_neigh_lookup(&rt->dst, &attr->fl.fl4.daddr); 1566717986eSVlad Buslov if (!n) { 1576717986eSVlad Buslov ret = -ENOMEM; 1586717986eSVlad Buslov goto err_dev_release; 1596717986eSVlad Buslov } 1606717986eSVlad Buslov 1616717986eSVlad Buslov ip_rt_put(rt); 1626717986eSVlad Buslov attr->route_dev = route_dev; 1636717986eSVlad Buslov attr->out_dev = out_dev; 1646717986eSVlad Buslov attr->n = n; 1656717986eSVlad Buslov return 0; 1666717986eSVlad Buslov 1676717986eSVlad Buslov err_dev_release: 1686717986eSVlad Buslov dev_put(route_dev); 1696717986eSVlad Buslov err_rt_release: 1702347cee8SParav Pandit ip_rt_put(rt); 171442e1228SEli Britstein return ret; 1722347cee8SParav Pandit } 173101f4de9SOz Shlomo 1746717986eSVlad Buslov static void mlx5e_route_lookup_ipv4_put(struct mlx5e_tc_tun_route_attr *attr) 17578c906e4SVlad Buslov { 1766717986eSVlad Buslov mlx5e_tc_tun_route_attr_cleanup(attr); 17778c906e4SVlad Buslov } 17878c906e4SVlad Buslov 179101f4de9SOz Shlomo static const char *mlx5e_netdev_kind(struct net_device *dev) 180101f4de9SOz Shlomo { 181101f4de9SOz Shlomo if (dev->rtnl_link_ops) 182101f4de9SOz Shlomo return dev->rtnl_link_ops->kind; 183101f4de9SOz Shlomo else 1841b18b781STonghao Zhang return "unknown"; 185101f4de9SOz Shlomo } 186101f4de9SOz Shlomo 187101f4de9SOz Shlomo static int mlx5e_gen_ip_tunnel_header(char buf[], __u8 *ip_proto, 188101f4de9SOz Shlomo struct mlx5e_encap_entry *e) 189101f4de9SOz Shlomo { 190d386939aSYevgeny Kliteynik if (!e->tunnel) { 191d386939aSYevgeny Kliteynik pr_warn("mlx5: Cannot generate tunnel header for this tunnel\n"); 192d386939aSYevgeny Kliteynik return -EOPNOTSUPP; 193101f4de9SOz Shlomo } 194101f4de9SOz Shlomo 195d386939aSYevgeny Kliteynik return e->tunnel->generate_ip_tun_hdr(buf, ip_proto, e); 196101f4de9SOz Shlomo } 197101f4de9SOz Shlomo 19805ada1adSEli Britstein static char *gen_eth_tnl_hdr(char *buf, struct net_device *dev, 19905ada1adSEli Britstein struct mlx5e_encap_entry *e, 20005ada1adSEli Britstein u16 proto) 20105ada1adSEli Britstein { 20205ada1adSEli Britstein struct ethhdr *eth = (struct ethhdr *)buf; 203aa331450SEli Britstein char *ip; 20405ada1adSEli Britstein 20505ada1adSEli Britstein ether_addr_copy(eth->h_dest, e->h_dest); 20605ada1adSEli Britstein ether_addr_copy(eth->h_source, dev->dev_addr); 207aa331450SEli Britstein if (is_vlan_dev(dev)) { 208aa331450SEli Britstein struct vlan_hdr *vlan = (struct vlan_hdr *) 209aa331450SEli Britstein ((char *)eth + ETH_HLEN); 210aa331450SEli Britstein ip = (char *)vlan + VLAN_HLEN; 211aa331450SEli Britstein eth->h_proto = vlan_dev_vlan_proto(dev); 212aa331450SEli Britstein vlan->h_vlan_TCI = htons(vlan_dev_vlan_id(dev)); 213aa331450SEli Britstein vlan->h_vlan_encapsulated_proto = htons(proto); 214aa331450SEli Britstein } else { 21505ada1adSEli Britstein eth->h_proto = htons(proto); 216aa331450SEli Britstein ip = (char *)eth + ETH_HLEN; 217aa331450SEli Britstein } 21805ada1adSEli Britstein 219aa331450SEli Britstein return ip; 22005ada1adSEli Britstein } 22105ada1adSEli Britstein 222101f4de9SOz Shlomo int mlx5e_tc_tun_create_header_ipv4(struct mlx5e_priv *priv, 223101f4de9SOz Shlomo struct net_device *mirred_dev, 224101f4de9SOz Shlomo struct mlx5e_encap_entry *e) 225101f4de9SOz Shlomo { 226101f4de9SOz Shlomo int max_encap_size = MLX5_CAP_ESW(priv->mdev, max_encap_header_size); 2271f6da306SYevgeny Kliteynik const struct ip_tunnel_key *tun_key = &e->tun_info->key; 2283f3f05abSYevgeny Kliteynik struct mlx5_pkt_reformat_params reformat_params; 2292221d954SVlad Buslov struct mlx5e_neigh m_neigh = {}; 2306717986eSVlad Buslov TC_TUN_ROUTE_ATTR_INIT(attr); 231c7bcb277SEli Britstein int ipv4_encap_size; 232101f4de9SOz Shlomo char *encap_header; 233101f4de9SOz Shlomo struct iphdr *ip; 2346717986eSVlad Buslov u8 nud_state; 235101f4de9SOz Shlomo int err; 236101f4de9SOz Shlomo 237101f4de9SOz Shlomo /* add the IP fields */ 2386717986eSVlad Buslov attr.fl.fl4.flowi4_tos = tun_key->tos; 2396717986eSVlad Buslov attr.fl.fl4.daddr = tun_key->u.ipv4.dst; 2406717986eSVlad Buslov attr.fl.fl4.saddr = tun_key->u.ipv4.src; 2416717986eSVlad Buslov attr.ttl = tun_key->ttl; 242101f4de9SOz Shlomo 2436717986eSVlad Buslov err = mlx5e_route_lookup_ipv4_get(priv, mirred_dev, &attr); 244101f4de9SOz Shlomo if (err) 245c7bcb277SEli Britstein return err; 246c7bcb277SEli Britstein 247c7bcb277SEli Britstein ipv4_encap_size = 2486717986eSVlad Buslov (is_vlan_dev(attr.route_dev) ? VLAN_ETH_HLEN : ETH_HLEN) + 249c7bcb277SEli Britstein sizeof(struct iphdr) + 250d386939aSYevgeny Kliteynik e->tunnel->calc_hlen(e); 251c7bcb277SEli Britstein 252c7bcb277SEli Britstein if (max_encap_size < ipv4_encap_size) { 253c7bcb277SEli Britstein mlx5_core_warn(priv->mdev, "encap size %d too big, max supported is %d\n", 254c7bcb277SEli Britstein ipv4_encap_size, max_encap_size); 25585bf490aSEli Cohen err = -EOPNOTSUPP; 25690ac2458SEli Cohen goto release_neigh; 257c7bcb277SEli Britstein } 258c7bcb277SEli Britstein 259c7bcb277SEli Britstein encap_header = kzalloc(ipv4_encap_size, GFP_KERNEL); 26085bf490aSEli Cohen if (!encap_header) { 26185bf490aSEli Cohen err = -ENOMEM; 26290ac2458SEli Cohen goto release_neigh; 26385bf490aSEli Cohen } 264101f4de9SOz Shlomo 2652221d954SVlad Buslov m_neigh.family = attr.n->ops->family; 2662221d954SVlad Buslov memcpy(&m_neigh.dst_ip, attr.n->primary_key, attr.n->tbl->key_len); 2676717986eSVlad Buslov e->out_dev = attr.out_dev; 2686717986eSVlad Buslov e->route_dev_ifindex = attr.route_dev->ifindex; 269101f4de9SOz Shlomo 270101f4de9SOz Shlomo /* It's important to add the neigh to the hash table before checking 271101f4de9SOz Shlomo * the neigh validity state. So if we'll get a notification, in case the 272101f4de9SOz Shlomo * neigh changes it's validity state, we would find the relevant neigh 273101f4de9SOz Shlomo * in the hash. 274101f4de9SOz Shlomo */ 2752221d954SVlad Buslov err = mlx5e_rep_encap_entry_attach(netdev_priv(attr.out_dev), e, &m_neigh, attr.n->dev); 276101f4de9SOz Shlomo if (err) 277101f4de9SOz Shlomo goto free_encap; 278101f4de9SOz Shlomo 2796717986eSVlad Buslov read_lock_bh(&attr.n->lock); 2806717986eSVlad Buslov nud_state = attr.n->nud_state; 2816717986eSVlad Buslov ether_addr_copy(e->h_dest, attr.n->ha); 2826717986eSVlad Buslov read_unlock_bh(&attr.n->lock); 283101f4de9SOz Shlomo 284101f4de9SOz Shlomo /* add ethernet header */ 2856717986eSVlad Buslov ip = (struct iphdr *)gen_eth_tnl_hdr(encap_header, attr.route_dev, e, 28605ada1adSEli Britstein ETH_P_IP); 287101f4de9SOz Shlomo 288101f4de9SOz Shlomo /* add ip header */ 289101f4de9SOz Shlomo ip->tos = tun_key->tos; 290101f4de9SOz Shlomo ip->version = 0x4; 291101f4de9SOz Shlomo ip->ihl = 0x5; 2926717986eSVlad Buslov ip->ttl = attr.ttl; 2936717986eSVlad Buslov ip->daddr = attr.fl.fl4.daddr; 2946717986eSVlad Buslov ip->saddr = attr.fl.fl4.saddr; 295101f4de9SOz Shlomo 296101f4de9SOz Shlomo /* add tunneling protocol header */ 297101f4de9SOz Shlomo err = mlx5e_gen_ip_tunnel_header((char *)ip + sizeof(struct iphdr), 298101f4de9SOz Shlomo &ip->protocol, e); 299101f4de9SOz Shlomo if (err) 300101f4de9SOz Shlomo goto destroy_neigh_entry; 301101f4de9SOz Shlomo 302101f4de9SOz Shlomo e->encap_size = ipv4_encap_size; 303101f4de9SOz Shlomo e->encap_header = encap_header; 304101f4de9SOz Shlomo 305101f4de9SOz Shlomo if (!(nud_state & NUD_VALID)) { 3066717986eSVlad Buslov neigh_event_send(attr.n, NULL); 3070ad060eeSRoi Dayan /* the encap entry will be made valid on neigh update event 3080ad060eeSRoi Dayan * and not used before that. 3090ad060eeSRoi Dayan */ 31090ac2458SEli Cohen goto release_neigh; 311101f4de9SOz Shlomo } 3123f3f05abSYevgeny Kliteynik 3133f3f05abSYevgeny Kliteynik memset(&reformat_params, 0, sizeof(reformat_params)); 3143f3f05abSYevgeny Kliteynik reformat_params.type = e->reformat_type; 3153f3f05abSYevgeny Kliteynik reformat_params.size = ipv4_encap_size; 3163f3f05abSYevgeny Kliteynik reformat_params.data = encap_header; 3173f3f05abSYevgeny Kliteynik e->pkt_reformat = mlx5_packet_reformat_alloc(priv->mdev, &reformat_params, 3182b688ea5SMaor Gottlieb MLX5_FLOW_NAMESPACE_FDB); 3192b688ea5SMaor Gottlieb if (IS_ERR(e->pkt_reformat)) { 3202b688ea5SMaor Gottlieb err = PTR_ERR(e->pkt_reformat); 321101f4de9SOz Shlomo goto destroy_neigh_entry; 3222b688ea5SMaor Gottlieb } 323101f4de9SOz Shlomo 324101f4de9SOz Shlomo e->flags |= MLX5_ENCAP_ENTRY_VALID; 3256717986eSVlad Buslov mlx5e_rep_queue_neigh_stats_work(netdev_priv(attr.out_dev)); 3266717986eSVlad Buslov mlx5e_route_lookup_ipv4_put(&attr); 327101f4de9SOz Shlomo return err; 328101f4de9SOz Shlomo 329101f4de9SOz Shlomo destroy_neigh_entry: 330101f4de9SOz Shlomo mlx5e_rep_encap_entry_detach(netdev_priv(e->out_dev), e); 331101f4de9SOz Shlomo free_encap: 332101f4de9SOz Shlomo kfree(encap_header); 33390ac2458SEli Cohen release_neigh: 3346717986eSVlad Buslov mlx5e_route_lookup_ipv4_put(&attr); 335101f4de9SOz Shlomo return err; 336101f4de9SOz Shlomo } 337101f4de9SOz Shlomo 338c7b9038dSVlad Buslov int mlx5e_tc_tun_update_header_ipv4(struct mlx5e_priv *priv, 339c7b9038dSVlad Buslov struct net_device *mirred_dev, 340c7b9038dSVlad Buslov struct mlx5e_encap_entry *e) 341c7b9038dSVlad Buslov { 342c7b9038dSVlad Buslov int max_encap_size = MLX5_CAP_ESW(priv->mdev, max_encap_header_size); 343c7b9038dSVlad Buslov const struct ip_tunnel_key *tun_key = &e->tun_info->key; 3443f3f05abSYevgeny Kliteynik struct mlx5_pkt_reformat_params reformat_params; 345c7b9038dSVlad Buslov TC_TUN_ROUTE_ATTR_INIT(attr); 346c7b9038dSVlad Buslov int ipv4_encap_size; 347c7b9038dSVlad Buslov char *encap_header; 348c7b9038dSVlad Buslov struct iphdr *ip; 349c7b9038dSVlad Buslov u8 nud_state; 350c7b9038dSVlad Buslov int err; 351c7b9038dSVlad Buslov 352c7b9038dSVlad Buslov /* add the IP fields */ 353c7b9038dSVlad Buslov attr.fl.fl4.flowi4_tos = tun_key->tos; 354c7b9038dSVlad Buslov attr.fl.fl4.daddr = tun_key->u.ipv4.dst; 355c7b9038dSVlad Buslov attr.fl.fl4.saddr = tun_key->u.ipv4.src; 356c7b9038dSVlad Buslov attr.ttl = tun_key->ttl; 357c7b9038dSVlad Buslov 358c7b9038dSVlad Buslov err = mlx5e_route_lookup_ipv4_get(priv, mirred_dev, &attr); 359c7b9038dSVlad Buslov if (err) 360c7b9038dSVlad Buslov return err; 361c7b9038dSVlad Buslov 362c7b9038dSVlad Buslov ipv4_encap_size = 363c7b9038dSVlad Buslov (is_vlan_dev(attr.route_dev) ? VLAN_ETH_HLEN : ETH_HLEN) + 364c7b9038dSVlad Buslov sizeof(struct iphdr) + 365c7b9038dSVlad Buslov e->tunnel->calc_hlen(e); 366c7b9038dSVlad Buslov 367c7b9038dSVlad Buslov if (max_encap_size < ipv4_encap_size) { 368c7b9038dSVlad Buslov mlx5_core_warn(priv->mdev, "encap size %d too big, max supported is %d\n", 369c7b9038dSVlad Buslov ipv4_encap_size, max_encap_size); 370c7b9038dSVlad Buslov err = -EOPNOTSUPP; 371c7b9038dSVlad Buslov goto release_neigh; 372c7b9038dSVlad Buslov } 373c7b9038dSVlad Buslov 374c7b9038dSVlad Buslov encap_header = kzalloc(ipv4_encap_size, GFP_KERNEL); 375c7b9038dSVlad Buslov if (!encap_header) { 376c7b9038dSVlad Buslov err = -ENOMEM; 377c7b9038dSVlad Buslov goto release_neigh; 378c7b9038dSVlad Buslov } 379c7b9038dSVlad Buslov 380c7b9038dSVlad Buslov e->route_dev_ifindex = attr.route_dev->ifindex; 381c7b9038dSVlad Buslov 382c7b9038dSVlad Buslov read_lock_bh(&attr.n->lock); 383c7b9038dSVlad Buslov nud_state = attr.n->nud_state; 384c7b9038dSVlad Buslov ether_addr_copy(e->h_dest, attr.n->ha); 385c7b9038dSVlad Buslov WRITE_ONCE(e->nhe->neigh_dev, attr.n->dev); 386c7b9038dSVlad Buslov read_unlock_bh(&attr.n->lock); 387c7b9038dSVlad Buslov 388c7b9038dSVlad Buslov /* add ethernet header */ 389c7b9038dSVlad Buslov ip = (struct iphdr *)gen_eth_tnl_hdr(encap_header, attr.route_dev, e, 390c7b9038dSVlad Buslov ETH_P_IP); 391c7b9038dSVlad Buslov 392c7b9038dSVlad Buslov /* add ip header */ 393c7b9038dSVlad Buslov ip->tos = tun_key->tos; 394c7b9038dSVlad Buslov ip->version = 0x4; 395c7b9038dSVlad Buslov ip->ihl = 0x5; 396c7b9038dSVlad Buslov ip->ttl = attr.ttl; 397c7b9038dSVlad Buslov ip->daddr = attr.fl.fl4.daddr; 398c7b9038dSVlad Buslov ip->saddr = attr.fl.fl4.saddr; 399c7b9038dSVlad Buslov 400c7b9038dSVlad Buslov /* add tunneling protocol header */ 401c7b9038dSVlad Buslov err = mlx5e_gen_ip_tunnel_header((char *)ip + sizeof(struct iphdr), 402c7b9038dSVlad Buslov &ip->protocol, e); 403c7b9038dSVlad Buslov if (err) 404c7b9038dSVlad Buslov goto free_encap; 405c7b9038dSVlad Buslov 406c7b9038dSVlad Buslov e->encap_size = ipv4_encap_size; 407c7b9038dSVlad Buslov kfree(e->encap_header); 408c7b9038dSVlad Buslov e->encap_header = encap_header; 409c7b9038dSVlad Buslov 410c7b9038dSVlad Buslov if (!(nud_state & NUD_VALID)) { 411c7b9038dSVlad Buslov neigh_event_send(attr.n, NULL); 412c7b9038dSVlad Buslov /* the encap entry will be made valid on neigh update event 413c7b9038dSVlad Buslov * and not used before that. 414c7b9038dSVlad Buslov */ 415c7b9038dSVlad Buslov goto release_neigh; 416c7b9038dSVlad Buslov } 4173f3f05abSYevgeny Kliteynik 4183f3f05abSYevgeny Kliteynik memset(&reformat_params, 0, sizeof(reformat_params)); 4193f3f05abSYevgeny Kliteynik reformat_params.type = e->reformat_type; 4203f3f05abSYevgeny Kliteynik reformat_params.size = ipv4_encap_size; 4213f3f05abSYevgeny Kliteynik reformat_params.data = encap_header; 4223f3f05abSYevgeny Kliteynik e->pkt_reformat = mlx5_packet_reformat_alloc(priv->mdev, &reformat_params, 423c7b9038dSVlad Buslov MLX5_FLOW_NAMESPACE_FDB); 424c7b9038dSVlad Buslov if (IS_ERR(e->pkt_reformat)) { 425c7b9038dSVlad Buslov err = PTR_ERR(e->pkt_reformat); 426c7b9038dSVlad Buslov goto free_encap; 427c7b9038dSVlad Buslov } 428c7b9038dSVlad Buslov 429c7b9038dSVlad Buslov e->flags |= MLX5_ENCAP_ENTRY_VALID; 430c7b9038dSVlad Buslov mlx5e_rep_queue_neigh_stats_work(netdev_priv(attr.out_dev)); 431c7b9038dSVlad Buslov mlx5e_route_lookup_ipv4_put(&attr); 432c7b9038dSVlad Buslov return err; 433c7b9038dSVlad Buslov 434c7b9038dSVlad Buslov free_encap: 435c7b9038dSVlad Buslov kfree(encap_header); 436c7b9038dSVlad Buslov release_neigh: 437c7b9038dSVlad Buslov mlx5e_route_lookup_ipv4_put(&attr); 438c7b9038dSVlad Buslov return err; 439c7b9038dSVlad Buslov } 440c7b9038dSVlad Buslov 4415f9fc332SYueHaibing #if IS_ENABLED(CONFIG_INET) && IS_ENABLED(CONFIG_IPV6) 44278c906e4SVlad Buslov static int mlx5e_route_lookup_ipv6_get(struct mlx5e_priv *priv, 4435f9fc332SYueHaibing struct net_device *mirred_dev, 4446717986eSVlad Buslov struct mlx5e_tc_tun_route_attr *attr) 4455f9fc332SYueHaibing { 4462f8ec867SChris Mi struct mlx5e_tc_tunnel *tunnel = mlx5e_get_tc_tun(mirred_dev); 4476717986eSVlad Buslov struct net_device *route_dev; 4486717986eSVlad Buslov struct net_device *out_dev; 4495f9fc332SYueHaibing struct dst_entry *dst; 4505f9fc332SYueHaibing struct neighbour *n; 4515f9fc332SYueHaibing int ret; 4525f9fc332SYueHaibing 4532f8ec867SChris Mi if (tunnel && tunnel->get_remote_ifindex) 4542f8ec867SChris Mi attr->fl.fl6.flowi6_oif = tunnel->get_remote_ifindex(mirred_dev); 4556717986eSVlad Buslov dst = ipv6_stub->ipv6_dst_lookup_flow(dev_net(mirred_dev), NULL, &attr->fl.fl6, 4566c8991f4SSabrina Dubroca NULL); 4576c8991f4SSabrina Dubroca if (IS_ERR(dst)) 4586c8991f4SSabrina Dubroca return PTR_ERR(dst); 4595f9fc332SYueHaibing 4606717986eSVlad Buslov if (!attr->ttl) 4616717986eSVlad Buslov attr->ttl = ip6_dst_hoplimit(dst); 4625f9fc332SYueHaibing 4636717986eSVlad Buslov ret = get_route_and_out_devs(priv, dst->dev, &route_dev, &out_dev); 4646717986eSVlad Buslov if (ret < 0) 4656717986eSVlad Buslov goto err_dst_release; 4666717986eSVlad Buslov 4676717986eSVlad Buslov dev_hold(route_dev); 4686717986eSVlad Buslov n = dst_neigh_lookup(dst, &attr->fl.fl6.daddr); 4696717986eSVlad Buslov if (!n) { 4706717986eSVlad Buslov ret = -ENOMEM; 4716717986eSVlad Buslov goto err_dev_release; 4726717986eSVlad Buslov } 4736717986eSVlad Buslov 4746717986eSVlad Buslov dst_release(dst); 4756717986eSVlad Buslov attr->out_dev = out_dev; 4766717986eSVlad Buslov attr->route_dev = route_dev; 4776717986eSVlad Buslov attr->n = n; 4786717986eSVlad Buslov return 0; 4796717986eSVlad Buslov 4806717986eSVlad Buslov err_dev_release: 4816717986eSVlad Buslov dev_put(route_dev); 4826717986eSVlad Buslov err_dst_release: 4835f9fc332SYueHaibing dst_release(dst); 4845f9fc332SYueHaibing return ret; 4855f9fc332SYueHaibing } 4865f9fc332SYueHaibing 4876717986eSVlad Buslov static void mlx5e_route_lookup_ipv6_put(struct mlx5e_tc_tun_route_attr *attr) 48878c906e4SVlad Buslov { 4896717986eSVlad Buslov mlx5e_tc_tun_route_attr_cleanup(attr); 49078c906e4SVlad Buslov } 49178c906e4SVlad Buslov 492101f4de9SOz Shlomo int mlx5e_tc_tun_create_header_ipv6(struct mlx5e_priv *priv, 493101f4de9SOz Shlomo struct net_device *mirred_dev, 494101f4de9SOz Shlomo struct mlx5e_encap_entry *e) 495101f4de9SOz Shlomo { 496101f4de9SOz Shlomo int max_encap_size = MLX5_CAP_ESW(priv->mdev, max_encap_header_size); 4971f6da306SYevgeny Kliteynik const struct ip_tunnel_key *tun_key = &e->tun_info->key; 4983f3f05abSYevgeny Kliteynik struct mlx5_pkt_reformat_params reformat_params; 4992221d954SVlad Buslov struct mlx5e_neigh m_neigh = {}; 5006717986eSVlad Buslov TC_TUN_ROUTE_ATTR_INIT(attr); 501101f4de9SOz Shlomo struct ipv6hdr *ip6h; 502c7bcb277SEli Britstein int ipv6_encap_size; 503101f4de9SOz Shlomo char *encap_header; 5046717986eSVlad Buslov u8 nud_state; 505101f4de9SOz Shlomo int err; 506101f4de9SOz Shlomo 5076717986eSVlad Buslov attr.ttl = tun_key->ttl; 5086717986eSVlad Buslov attr.fl.fl6.flowlabel = ip6_make_flowinfo(RT_TOS(tun_key->tos), tun_key->label); 5096717986eSVlad Buslov attr.fl.fl6.daddr = tun_key->u.ipv6.dst; 5106717986eSVlad Buslov attr.fl.fl6.saddr = tun_key->u.ipv6.src; 511101f4de9SOz Shlomo 5126717986eSVlad Buslov err = mlx5e_route_lookup_ipv6_get(priv, mirred_dev, &attr); 513101f4de9SOz Shlomo if (err) 514c7bcb277SEli Britstein return err; 515c7bcb277SEli Britstein 516c7bcb277SEli Britstein ipv6_encap_size = 5176717986eSVlad Buslov (is_vlan_dev(attr.route_dev) ? VLAN_ETH_HLEN : ETH_HLEN) + 518c7bcb277SEli Britstein sizeof(struct ipv6hdr) + 519d386939aSYevgeny Kliteynik e->tunnel->calc_hlen(e); 520c7bcb277SEli Britstein 521c7bcb277SEli Britstein if (max_encap_size < ipv6_encap_size) { 522c7bcb277SEli Britstein mlx5_core_warn(priv->mdev, "encap size %d too big, max supported is %d\n", 523c7bcb277SEli Britstein ipv6_encap_size, max_encap_size); 52485bf490aSEli Cohen err = -EOPNOTSUPP; 52590ac2458SEli Cohen goto release_neigh; 526c7bcb277SEli Britstein } 527c7bcb277SEli Britstein 528c7bcb277SEli Britstein encap_header = kzalloc(ipv6_encap_size, GFP_KERNEL); 52985bf490aSEli Cohen if (!encap_header) { 53085bf490aSEli Cohen err = -ENOMEM; 53190ac2458SEli Cohen goto release_neigh; 53285bf490aSEli Cohen } 533101f4de9SOz Shlomo 5342221d954SVlad Buslov m_neigh.family = attr.n->ops->family; 5352221d954SVlad Buslov memcpy(&m_neigh.dst_ip, attr.n->primary_key, attr.n->tbl->key_len); 5366717986eSVlad Buslov e->out_dev = attr.out_dev; 5376717986eSVlad Buslov e->route_dev_ifindex = attr.route_dev->ifindex; 538101f4de9SOz Shlomo 53939c538d6SCai Huoqing /* It's important to add the neigh to the hash table before checking 540101f4de9SOz Shlomo * the neigh validity state. So if we'll get a notification, in case the 541101f4de9SOz Shlomo * neigh changes it's validity state, we would find the relevant neigh 542101f4de9SOz Shlomo * in the hash. 543101f4de9SOz Shlomo */ 5442221d954SVlad Buslov err = mlx5e_rep_encap_entry_attach(netdev_priv(attr.out_dev), e, &m_neigh, attr.n->dev); 545101f4de9SOz Shlomo if (err) 546101f4de9SOz Shlomo goto free_encap; 547101f4de9SOz Shlomo 5486717986eSVlad Buslov read_lock_bh(&attr.n->lock); 5496717986eSVlad Buslov nud_state = attr.n->nud_state; 5506717986eSVlad Buslov ether_addr_copy(e->h_dest, attr.n->ha); 5516717986eSVlad Buslov read_unlock_bh(&attr.n->lock); 552101f4de9SOz Shlomo 553101f4de9SOz Shlomo /* add ethernet header */ 5546717986eSVlad Buslov ip6h = (struct ipv6hdr *)gen_eth_tnl_hdr(encap_header, attr.route_dev, e, 55505ada1adSEli Britstein ETH_P_IPV6); 556101f4de9SOz Shlomo 557101f4de9SOz Shlomo /* add ip header */ 558101f4de9SOz Shlomo ip6_flow_hdr(ip6h, tun_key->tos, 0); 559101f4de9SOz Shlomo /* the HW fills up ipv6 payload len */ 5606717986eSVlad Buslov ip6h->hop_limit = attr.ttl; 5616717986eSVlad Buslov ip6h->daddr = attr.fl.fl6.daddr; 5626717986eSVlad Buslov ip6h->saddr = attr.fl.fl6.saddr; 563101f4de9SOz Shlomo 564101f4de9SOz Shlomo /* add tunneling protocol header */ 565101f4de9SOz Shlomo err = mlx5e_gen_ip_tunnel_header((char *)ip6h + sizeof(struct ipv6hdr), 566101f4de9SOz Shlomo &ip6h->nexthdr, e); 567101f4de9SOz Shlomo if (err) 568101f4de9SOz Shlomo goto destroy_neigh_entry; 569101f4de9SOz Shlomo 570101f4de9SOz Shlomo e->encap_size = ipv6_encap_size; 571101f4de9SOz Shlomo e->encap_header = encap_header; 572101f4de9SOz Shlomo 573101f4de9SOz Shlomo if (!(nud_state & NUD_VALID)) { 5746717986eSVlad Buslov neigh_event_send(attr.n, NULL); 5750ad060eeSRoi Dayan /* the encap entry will be made valid on neigh update event 5760ad060eeSRoi Dayan * and not used before that. 5770ad060eeSRoi Dayan */ 57890ac2458SEli Cohen goto release_neigh; 579101f4de9SOz Shlomo } 580101f4de9SOz Shlomo 5813f3f05abSYevgeny Kliteynik memset(&reformat_params, 0, sizeof(reformat_params)); 5823f3f05abSYevgeny Kliteynik reformat_params.type = e->reformat_type; 5833f3f05abSYevgeny Kliteynik reformat_params.size = ipv6_encap_size; 5843f3f05abSYevgeny Kliteynik reformat_params.data = encap_header; 5853f3f05abSYevgeny Kliteynik e->pkt_reformat = mlx5_packet_reformat_alloc(priv->mdev, &reformat_params, 5862b688ea5SMaor Gottlieb MLX5_FLOW_NAMESPACE_FDB); 5872b688ea5SMaor Gottlieb if (IS_ERR(e->pkt_reformat)) { 5882b688ea5SMaor Gottlieb err = PTR_ERR(e->pkt_reformat); 589101f4de9SOz Shlomo goto destroy_neigh_entry; 5902b688ea5SMaor Gottlieb } 591101f4de9SOz Shlomo 592101f4de9SOz Shlomo e->flags |= MLX5_ENCAP_ENTRY_VALID; 5936717986eSVlad Buslov mlx5e_rep_queue_neigh_stats_work(netdev_priv(attr.out_dev)); 5946717986eSVlad Buslov mlx5e_route_lookup_ipv6_put(&attr); 595101f4de9SOz Shlomo return err; 596101f4de9SOz Shlomo 597101f4de9SOz Shlomo destroy_neigh_entry: 598101f4de9SOz Shlomo mlx5e_rep_encap_entry_detach(netdev_priv(e->out_dev), e); 599101f4de9SOz Shlomo free_encap: 600101f4de9SOz Shlomo kfree(encap_header); 60190ac2458SEli Cohen release_neigh: 6026717986eSVlad Buslov mlx5e_route_lookup_ipv6_put(&attr); 603101f4de9SOz Shlomo return err; 604101f4de9SOz Shlomo } 605c7b9038dSVlad Buslov 606c7b9038dSVlad Buslov int mlx5e_tc_tun_update_header_ipv6(struct mlx5e_priv *priv, 607c7b9038dSVlad Buslov struct net_device *mirred_dev, 608c7b9038dSVlad Buslov struct mlx5e_encap_entry *e) 609c7b9038dSVlad Buslov { 610c7b9038dSVlad Buslov int max_encap_size = MLX5_CAP_ESW(priv->mdev, max_encap_header_size); 611c7b9038dSVlad Buslov const struct ip_tunnel_key *tun_key = &e->tun_info->key; 6123f3f05abSYevgeny Kliteynik struct mlx5_pkt_reformat_params reformat_params; 613c7b9038dSVlad Buslov TC_TUN_ROUTE_ATTR_INIT(attr); 614c7b9038dSVlad Buslov struct ipv6hdr *ip6h; 615c7b9038dSVlad Buslov int ipv6_encap_size; 616c7b9038dSVlad Buslov char *encap_header; 617c7b9038dSVlad Buslov u8 nud_state; 618c7b9038dSVlad Buslov int err; 619c7b9038dSVlad Buslov 620c7b9038dSVlad Buslov attr.ttl = tun_key->ttl; 621c7b9038dSVlad Buslov 622c7b9038dSVlad Buslov attr.fl.fl6.flowlabel = ip6_make_flowinfo(RT_TOS(tun_key->tos), tun_key->label); 623c7b9038dSVlad Buslov attr.fl.fl6.daddr = tun_key->u.ipv6.dst; 624c7b9038dSVlad Buslov attr.fl.fl6.saddr = tun_key->u.ipv6.src; 625c7b9038dSVlad Buslov 626c7b9038dSVlad Buslov err = mlx5e_route_lookup_ipv6_get(priv, mirred_dev, &attr); 627c7b9038dSVlad Buslov if (err) 628c7b9038dSVlad Buslov return err; 629c7b9038dSVlad Buslov 630c7b9038dSVlad Buslov ipv6_encap_size = 631c7b9038dSVlad Buslov (is_vlan_dev(attr.route_dev) ? VLAN_ETH_HLEN : ETH_HLEN) + 632c7b9038dSVlad Buslov sizeof(struct ipv6hdr) + 633c7b9038dSVlad Buslov e->tunnel->calc_hlen(e); 634c7b9038dSVlad Buslov 635c7b9038dSVlad Buslov if (max_encap_size < ipv6_encap_size) { 636c7b9038dSVlad Buslov mlx5_core_warn(priv->mdev, "encap size %d too big, max supported is %d\n", 637c7b9038dSVlad Buslov ipv6_encap_size, max_encap_size); 638c7b9038dSVlad Buslov err = -EOPNOTSUPP; 639c7b9038dSVlad Buslov goto release_neigh; 640c7b9038dSVlad Buslov } 641c7b9038dSVlad Buslov 642c7b9038dSVlad Buslov encap_header = kzalloc(ipv6_encap_size, GFP_KERNEL); 643c7b9038dSVlad Buslov if (!encap_header) { 644c7b9038dSVlad Buslov err = -ENOMEM; 645c7b9038dSVlad Buslov goto release_neigh; 646c7b9038dSVlad Buslov } 647c7b9038dSVlad Buslov 648c7b9038dSVlad Buslov e->route_dev_ifindex = attr.route_dev->ifindex; 649c7b9038dSVlad Buslov 650c7b9038dSVlad Buslov read_lock_bh(&attr.n->lock); 651c7b9038dSVlad Buslov nud_state = attr.n->nud_state; 652c7b9038dSVlad Buslov ether_addr_copy(e->h_dest, attr.n->ha); 653c7b9038dSVlad Buslov WRITE_ONCE(e->nhe->neigh_dev, attr.n->dev); 654c7b9038dSVlad Buslov read_unlock_bh(&attr.n->lock); 655c7b9038dSVlad Buslov 656c7b9038dSVlad Buslov /* add ethernet header */ 657c7b9038dSVlad Buslov ip6h = (struct ipv6hdr *)gen_eth_tnl_hdr(encap_header, attr.route_dev, e, 658c7b9038dSVlad Buslov ETH_P_IPV6); 659c7b9038dSVlad Buslov 660c7b9038dSVlad Buslov /* add ip header */ 661c7b9038dSVlad Buslov ip6_flow_hdr(ip6h, tun_key->tos, 0); 662c7b9038dSVlad Buslov /* the HW fills up ipv6 payload len */ 663c7b9038dSVlad Buslov ip6h->hop_limit = attr.ttl; 664c7b9038dSVlad Buslov ip6h->daddr = attr.fl.fl6.daddr; 665c7b9038dSVlad Buslov ip6h->saddr = attr.fl.fl6.saddr; 666c7b9038dSVlad Buslov 667c7b9038dSVlad Buslov /* add tunneling protocol header */ 668c7b9038dSVlad Buslov err = mlx5e_gen_ip_tunnel_header((char *)ip6h + sizeof(struct ipv6hdr), 669c7b9038dSVlad Buslov &ip6h->nexthdr, e); 670c7b9038dSVlad Buslov if (err) 671c7b9038dSVlad Buslov goto free_encap; 672c7b9038dSVlad Buslov 673c7b9038dSVlad Buslov e->encap_size = ipv6_encap_size; 674c7b9038dSVlad Buslov kfree(e->encap_header); 675c7b9038dSVlad Buslov e->encap_header = encap_header; 676c7b9038dSVlad Buslov 677c7b9038dSVlad Buslov if (!(nud_state & NUD_VALID)) { 678c7b9038dSVlad Buslov neigh_event_send(attr.n, NULL); 679c7b9038dSVlad Buslov /* the encap entry will be made valid on neigh update event 680c7b9038dSVlad Buslov * and not used before that. 681c7b9038dSVlad Buslov */ 682c7b9038dSVlad Buslov goto release_neigh; 683c7b9038dSVlad Buslov } 684c7b9038dSVlad Buslov 6853f3f05abSYevgeny Kliteynik memset(&reformat_params, 0, sizeof(reformat_params)); 6863f3f05abSYevgeny Kliteynik reformat_params.type = e->reformat_type; 6873f3f05abSYevgeny Kliteynik reformat_params.size = ipv6_encap_size; 6883f3f05abSYevgeny Kliteynik reformat_params.data = encap_header; 6893f3f05abSYevgeny Kliteynik e->pkt_reformat = mlx5_packet_reformat_alloc(priv->mdev, &reformat_params, 690c7b9038dSVlad Buslov MLX5_FLOW_NAMESPACE_FDB); 691c7b9038dSVlad Buslov if (IS_ERR(e->pkt_reformat)) { 692c7b9038dSVlad Buslov err = PTR_ERR(e->pkt_reformat); 693c7b9038dSVlad Buslov goto free_encap; 694c7b9038dSVlad Buslov } 695c7b9038dSVlad Buslov 696c7b9038dSVlad Buslov e->flags |= MLX5_ENCAP_ENTRY_VALID; 697c7b9038dSVlad Buslov mlx5e_rep_queue_neigh_stats_work(netdev_priv(attr.out_dev)); 698c7b9038dSVlad Buslov mlx5e_route_lookup_ipv6_put(&attr); 699c7b9038dSVlad Buslov return err; 700c7b9038dSVlad Buslov 701c7b9038dSVlad Buslov free_encap: 702c7b9038dSVlad Buslov kfree(encap_header); 703c7b9038dSVlad Buslov release_neigh: 704c7b9038dSVlad Buslov mlx5e_route_lookup_ipv6_put(&attr); 705c7b9038dSVlad Buslov return err; 706c7b9038dSVlad Buslov } 7075f9fc332SYueHaibing #endif 708101f4de9SOz Shlomo 709a508728aSVlad Buslov int mlx5e_tc_tun_route_lookup(struct mlx5e_priv *priv, 710a508728aSVlad Buslov struct mlx5_flow_spec *spec, 711a508728aSVlad Buslov struct mlx5_flow_attr *flow_attr) 712a508728aSVlad Buslov { 713a508728aSVlad Buslov struct mlx5_esw_flow_attr *esw_attr = flow_attr->esw_attr; 714a508728aSVlad Buslov TC_TUN_ROUTE_ATTR_INIT(attr); 715a508728aSVlad Buslov u16 vport_num; 716a508728aSVlad Buslov int err = 0; 717a508728aSVlad Buslov 7181e74152eSRoi Dayan if (flow_attr->tun_ip_version == 4) { 719a508728aSVlad Buslov /* Addresses are swapped for decap */ 720a508728aSVlad Buslov attr.fl.fl4.saddr = esw_attr->rx_tun_attr->dst_ip.v4; 721a508728aSVlad Buslov attr.fl.fl4.daddr = esw_attr->rx_tun_attr->src_ip.v4; 722a508728aSVlad Buslov err = mlx5e_route_lookup_ipv4_get(priv, priv->netdev, &attr); 723a508728aSVlad Buslov } 724a508728aSVlad Buslov #if IS_ENABLED(CONFIG_INET) && IS_ENABLED(CONFIG_IPV6) 7251e74152eSRoi Dayan else if (flow_attr->tun_ip_version == 6) { 726a508728aSVlad Buslov /* Addresses are swapped for decap */ 727a508728aSVlad Buslov attr.fl.fl6.saddr = esw_attr->rx_tun_attr->dst_ip.v6; 728a508728aSVlad Buslov attr.fl.fl6.daddr = esw_attr->rx_tun_attr->src_ip.v6; 729a508728aSVlad Buslov err = mlx5e_route_lookup_ipv6_get(priv, priv->netdev, &attr); 730a508728aSVlad Buslov } 731a508728aSVlad Buslov #endif 732a508728aSVlad Buslov else 733a508728aSVlad Buslov return 0; 734a508728aSVlad Buslov 735a508728aSVlad Buslov if (err) 736a508728aSVlad Buslov return err; 737a508728aSVlad Buslov 738a508728aSVlad Buslov if (attr.route_dev->netdev_ops != &mlx5e_netdev_ops || 739a508728aSVlad Buslov !mlx5e_tc_is_vf_tunnel(attr.out_dev, attr.route_dev)) 740a508728aSVlad Buslov goto out; 741a508728aSVlad Buslov 742a508728aSVlad Buslov err = mlx5e_tc_query_route_vport(attr.out_dev, attr.route_dev, &vport_num); 743a508728aSVlad Buslov if (err) 744a508728aSVlad Buslov goto out; 745a508728aSVlad Buslov 746a508728aSVlad Buslov esw_attr->rx_tun_attr->vni = MLX5_GET(fte_match_param, spec->match_value, 747a508728aSVlad Buslov misc_parameters.vxlan_vni); 748a508728aSVlad Buslov esw_attr->rx_tun_attr->decap_vport = vport_num; 749a508728aSVlad Buslov 750a508728aSVlad Buslov out: 7511e74152eSRoi Dayan if (flow_attr->tun_ip_version == 4) 752a508728aSVlad Buslov mlx5e_route_lookup_ipv4_put(&attr); 753a508728aSVlad Buslov #if IS_ENABLED(CONFIG_INET) && IS_ENABLED(CONFIG_IPV6) 7541e74152eSRoi Dayan else if (flow_attr->tun_ip_version == 6) 755a508728aSVlad Buslov mlx5e_route_lookup_ipv6_put(&attr); 756a508728aSVlad Buslov #endif 757a508728aSVlad Buslov return err; 758a508728aSVlad Buslov } 759a508728aSVlad Buslov 760101f4de9SOz Shlomo bool mlx5e_tc_tun_device_to_offload(struct mlx5e_priv *priv, 761101f4de9SOz Shlomo struct net_device *netdev) 762101f4de9SOz Shlomo { 763d386939aSYevgeny Kliteynik struct mlx5e_tc_tunnel *tunnel = mlx5e_get_tc_tun(netdev); 764101f4de9SOz Shlomo 765d386939aSYevgeny Kliteynik if (tunnel && tunnel->can_offload(priv)) 766df2ef3bfSOz Shlomo return true; 767101f4de9SOz Shlomo else 768101f4de9SOz Shlomo return false; 769101f4de9SOz Shlomo } 770101f4de9SOz Shlomo 771101f4de9SOz Shlomo int mlx5e_tc_tun_init_encap_attr(struct net_device *tunnel_dev, 772101f4de9SOz Shlomo struct mlx5e_priv *priv, 773101f4de9SOz Shlomo struct mlx5e_encap_entry *e, 774101f4de9SOz Shlomo struct netlink_ext_ack *extack) 775101f4de9SOz Shlomo { 776d386939aSYevgeny Kliteynik struct mlx5e_tc_tunnel *tunnel = mlx5e_get_tc_tun(tunnel_dev); 777101f4de9SOz Shlomo 778d386939aSYevgeny Kliteynik if (!tunnel) { 779101f4de9SOz Shlomo e->reformat_type = -1; 780101f4de9SOz Shlomo return -EOPNOTSUPP; 781101f4de9SOz Shlomo } 782101f4de9SOz Shlomo 783d386939aSYevgeny Kliteynik return tunnel->init_encap_attr(tunnel_dev, priv, e, extack); 784df2ef3bfSOz Shlomo } 785df2ef3bfSOz Shlomo 786101f4de9SOz Shlomo int mlx5e_tc_tun_parse(struct net_device *filter_dev, 787101f4de9SOz Shlomo struct mlx5e_priv *priv, 788101f4de9SOz Shlomo struct mlx5_flow_spec *spec, 789f9e30088SPablo Neira Ayuso struct flow_cls_offload *f, 790ea4cd837SPaul Blakey u8 *match_level) 791101f4de9SOz Shlomo { 792d386939aSYevgeny Kliteynik struct mlx5e_tc_tunnel *tunnel = mlx5e_get_tc_tun(filter_dev); 793ea4cd837SPaul Blakey struct flow_rule *rule = flow_cls_offload_flow_rule(f); 794ea4cd837SPaul Blakey void *headers_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, 795ea4cd837SPaul Blakey outer_headers); 796ea4cd837SPaul Blakey void *headers_v = MLX5_ADDR_OF(fte_match_param, spec->match_value, 797ea4cd837SPaul Blakey outer_headers); 798ea4cd837SPaul Blakey struct netlink_ext_ack *extack = f->common.extack; 799101f4de9SOz Shlomo int err = 0; 800101f4de9SOz Shlomo 801d386939aSYevgeny Kliteynik if (!tunnel) { 802101f4de9SOz Shlomo netdev_warn(priv->netdev, 803d386939aSYevgeny Kliteynik "decapsulation offload is not supported for %s net device\n", 8041b18b781STonghao Zhang mlx5e_netdev_kind(filter_dev)); 805d386939aSYevgeny Kliteynik err = -EOPNOTSUPP; 806d386939aSYevgeny Kliteynik goto out; 807d386939aSYevgeny Kliteynik } 8081b18b781STonghao Zhang 809d386939aSYevgeny Kliteynik *match_level = tunnel->match_level; 810d386939aSYevgeny Kliteynik 811d386939aSYevgeny Kliteynik if (tunnel->parse_udp_ports) { 812d386939aSYevgeny Kliteynik err = tunnel->parse_udp_ports(priv, spec, f, 813d386939aSYevgeny Kliteynik headers_c, headers_v); 814d386939aSYevgeny Kliteynik if (err) 815d386939aSYevgeny Kliteynik goto out; 816d386939aSYevgeny Kliteynik } 817d386939aSYevgeny Kliteynik 818d386939aSYevgeny Kliteynik if (tunnel->parse_tunnel) { 819d386939aSYevgeny Kliteynik err = tunnel->parse_tunnel(priv, spec, f, 820d386939aSYevgeny Kliteynik headers_c, headers_v); 821d386939aSYevgeny Kliteynik if (err) 822d386939aSYevgeny Kliteynik goto out; 823d386939aSYevgeny Kliteynik } 824d386939aSYevgeny Kliteynik 825ea4cd837SPaul Blakey if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_CONTROL)) { 8264a5d5d73SEli Britstein struct flow_dissector_key_basic key_basic = {}; 8274a5d5d73SEli Britstein struct flow_dissector_key_basic mask_basic = { 8284a5d5d73SEli Britstein .n_proto = htons(0xFFFF), 8294a5d5d73SEli Britstein }; 8304a5d5d73SEli Britstein struct flow_match_basic match_basic = { 8314a5d5d73SEli Britstein .key = &key_basic, .mask = &mask_basic, 8324a5d5d73SEli Britstein }; 833ea4cd837SPaul Blakey struct flow_match_control match; 834ea4cd837SPaul Blakey u16 addr_type; 835ea4cd837SPaul Blakey 836ea4cd837SPaul Blakey flow_rule_match_enc_control(rule, &match); 837ea4cd837SPaul Blakey addr_type = match.key->addr_type; 838ea4cd837SPaul Blakey 839ea4cd837SPaul Blakey /* For tunnel addr_type used same key id`s as for non-tunnel */ 840ea4cd837SPaul Blakey if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) { 841ea4cd837SPaul Blakey struct flow_match_ipv4_addrs match; 842ea4cd837SPaul Blakey 843ea4cd837SPaul Blakey flow_rule_match_enc_ipv4_addrs(rule, &match); 844ea4cd837SPaul Blakey MLX5_SET(fte_match_set_lyr_2_4, headers_c, 845ea4cd837SPaul Blakey src_ipv4_src_ipv6.ipv4_layout.ipv4, 846ea4cd837SPaul Blakey ntohl(match.mask->src)); 847ea4cd837SPaul Blakey MLX5_SET(fte_match_set_lyr_2_4, headers_v, 848ea4cd837SPaul Blakey src_ipv4_src_ipv6.ipv4_layout.ipv4, 849ea4cd837SPaul Blakey ntohl(match.key->src)); 850ea4cd837SPaul Blakey 851ea4cd837SPaul Blakey MLX5_SET(fte_match_set_lyr_2_4, headers_c, 852ea4cd837SPaul Blakey dst_ipv4_dst_ipv6.ipv4_layout.ipv4, 853ea4cd837SPaul Blakey ntohl(match.mask->dst)); 854ea4cd837SPaul Blakey MLX5_SET(fte_match_set_lyr_2_4, headers_v, 855ea4cd837SPaul Blakey dst_ipv4_dst_ipv6.ipv4_layout.ipv4, 856ea4cd837SPaul Blakey ntohl(match.key->dst)); 857ea4cd837SPaul Blakey 8584a5d5d73SEli Britstein key_basic.n_proto = htons(ETH_P_IP); 859fca53304SEli Britstein mlx5e_tc_set_ethertype(priv->mdev, &match_basic, true, 860fca53304SEli Britstein headers_c, headers_v); 861ea4cd837SPaul Blakey } else if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) { 862ea4cd837SPaul Blakey struct flow_match_ipv6_addrs match; 863ea4cd837SPaul Blakey 864ea4cd837SPaul Blakey flow_rule_match_enc_ipv6_addrs(rule, &match); 865ea4cd837SPaul Blakey memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c, 866ea4cd837SPaul Blakey src_ipv4_src_ipv6.ipv6_layout.ipv6), 867ea4cd837SPaul Blakey &match.mask->src, MLX5_FLD_SZ_BYTES(ipv6_layout, 868ea4cd837SPaul Blakey ipv6)); 869ea4cd837SPaul Blakey memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v, 870ea4cd837SPaul Blakey src_ipv4_src_ipv6.ipv6_layout.ipv6), 871ea4cd837SPaul Blakey &match.key->src, MLX5_FLD_SZ_BYTES(ipv6_layout, 872ea4cd837SPaul Blakey ipv6)); 873ea4cd837SPaul Blakey 874ea4cd837SPaul Blakey memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c, 875ea4cd837SPaul Blakey dst_ipv4_dst_ipv6.ipv6_layout.ipv6), 876ea4cd837SPaul Blakey &match.mask->dst, MLX5_FLD_SZ_BYTES(ipv6_layout, 877ea4cd837SPaul Blakey ipv6)); 878ea4cd837SPaul Blakey memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v, 879ea4cd837SPaul Blakey dst_ipv4_dst_ipv6.ipv6_layout.ipv6), 880ea4cd837SPaul Blakey &match.key->dst, MLX5_FLD_SZ_BYTES(ipv6_layout, 881ea4cd837SPaul Blakey ipv6)); 882ea4cd837SPaul Blakey 8834a5d5d73SEli Britstein key_basic.n_proto = htons(ETH_P_IPV6); 884fca53304SEli Britstein mlx5e_tc_set_ethertype(priv->mdev, &match_basic, true, 885fca53304SEli Britstein headers_c, headers_v); 886ea4cd837SPaul Blakey } 887ea4cd837SPaul Blakey } 888ea4cd837SPaul Blakey 889ea4cd837SPaul Blakey if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_IP)) { 890ea4cd837SPaul Blakey struct flow_match_ip match; 891ea4cd837SPaul Blakey 892ea4cd837SPaul Blakey flow_rule_match_enc_ip(rule, &match); 893ea4cd837SPaul Blakey MLX5_SET(fte_match_set_lyr_2_4, headers_c, ip_ecn, 894ea4cd837SPaul Blakey match.mask->tos & 0x3); 895ea4cd837SPaul Blakey MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_ecn, 896ea4cd837SPaul Blakey match.key->tos & 0x3); 897ea4cd837SPaul Blakey 898ea4cd837SPaul Blakey MLX5_SET(fte_match_set_lyr_2_4, headers_c, ip_dscp, 899ea4cd837SPaul Blakey match.mask->tos >> 2); 900ea4cd837SPaul Blakey MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_dscp, 901ea4cd837SPaul Blakey match.key->tos >> 2); 902ea4cd837SPaul Blakey 903ea4cd837SPaul Blakey MLX5_SET(fte_match_set_lyr_2_4, headers_c, ttl_hoplimit, 904ea4cd837SPaul Blakey match.mask->ttl); 905ea4cd837SPaul Blakey MLX5_SET(fte_match_set_lyr_2_4, headers_v, ttl_hoplimit, 906ea4cd837SPaul Blakey match.key->ttl); 907ea4cd837SPaul Blakey 908ea4cd837SPaul Blakey if (match.mask->ttl && 909ea4cd837SPaul Blakey !MLX5_CAP_ESW_FLOWTABLE_FDB 910ea4cd837SPaul Blakey (priv->mdev, 911ea4cd837SPaul Blakey ft_field_support.outer_ipv4_ttl)) { 912ea4cd837SPaul Blakey NL_SET_ERR_MSG_MOD(extack, 913ea4cd837SPaul Blakey "Matching on TTL is not supported"); 914ea4cd837SPaul Blakey err = -EOPNOTSUPP; 915ea4cd837SPaul Blakey goto out; 916ea4cd837SPaul Blakey } 917ea4cd837SPaul Blakey } 918ea4cd837SPaul Blakey 919ea4cd837SPaul Blakey /* let software handle IP fragments */ 920ea4cd837SPaul Blakey MLX5_SET(fte_match_set_lyr_2_4, headers_c, frag, 1); 921ea4cd837SPaul Blakey MLX5_SET(fte_match_set_lyr_2_4, headers_v, frag, 0); 922ea4cd837SPaul Blakey 923ea4cd837SPaul Blakey return 0; 924ea4cd837SPaul Blakey 925d386939aSYevgeny Kliteynik out: 926d386939aSYevgeny Kliteynik return err; 927d386939aSYevgeny Kliteynik } 928d386939aSYevgeny Kliteynik 929d386939aSYevgeny Kliteynik int mlx5e_tc_tun_parse_udp_ports(struct mlx5e_priv *priv, 930d386939aSYevgeny Kliteynik struct mlx5_flow_spec *spec, 931f9e30088SPablo Neira Ayuso struct flow_cls_offload *f, 932d386939aSYevgeny Kliteynik void *headers_c, 933d386939aSYevgeny Kliteynik void *headers_v) 934d386939aSYevgeny Kliteynik { 935f9e30088SPablo Neira Ayuso struct flow_rule *rule = flow_cls_offload_flow_rule(f); 936d386939aSYevgeny Kliteynik struct netlink_ext_ack *extack = f->common.extack; 937d386939aSYevgeny Kliteynik struct flow_match_ports enc_ports; 938d386939aSYevgeny Kliteynik 939d386939aSYevgeny Kliteynik /* Full udp dst port must be given */ 940d386939aSYevgeny Kliteynik 941d386939aSYevgeny Kliteynik if (!flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_PORTS)) { 942d386939aSYevgeny Kliteynik NL_SET_ERR_MSG_MOD(extack, 943d386939aSYevgeny Kliteynik "UDP tunnel decap filter must include enc_dst_port condition"); 944d386939aSYevgeny Kliteynik netdev_warn(priv->netdev, 945d386939aSYevgeny Kliteynik "UDP tunnel decap filter must include enc_dst_port condition\n"); 946101f4de9SOz Shlomo return -EOPNOTSUPP; 947101f4de9SOz Shlomo } 948d386939aSYevgeny Kliteynik 949d386939aSYevgeny Kliteynik flow_rule_match_enc_ports(rule, &enc_ports); 950d386939aSYevgeny Kliteynik 951d386939aSYevgeny Kliteynik if (memchr_inv(&enc_ports.mask->dst, 0xff, 952d386939aSYevgeny Kliteynik sizeof(enc_ports.mask->dst))) { 953d386939aSYevgeny Kliteynik NL_SET_ERR_MSG_MOD(extack, 954d386939aSYevgeny Kliteynik "UDP tunnel decap filter must match enc_dst_port fully"); 955d386939aSYevgeny Kliteynik netdev_warn(priv->netdev, 956d386939aSYevgeny Kliteynik "UDP tunnel decap filter must match enc_dst_port fully\n"); 957d386939aSYevgeny Kliteynik return -EOPNOTSUPP; 958d386939aSYevgeny Kliteynik } 959d386939aSYevgeny Kliteynik 960d386939aSYevgeny Kliteynik /* match on UDP protocol and dst port number */ 961d386939aSYevgeny Kliteynik 962d386939aSYevgeny Kliteynik MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, headers_c, ip_protocol); 963d386939aSYevgeny Kliteynik MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_protocol, IPPROTO_UDP); 964d386939aSYevgeny Kliteynik 965d386939aSYevgeny Kliteynik MLX5_SET(fte_match_set_lyr_2_4, headers_c, udp_dport, 966d386939aSYevgeny Kliteynik ntohs(enc_ports.mask->dst)); 967d386939aSYevgeny Kliteynik MLX5_SET(fte_match_set_lyr_2_4, headers_v, udp_dport, 968d386939aSYevgeny Kliteynik ntohs(enc_ports.key->dst)); 969d386939aSYevgeny Kliteynik 970d386939aSYevgeny Kliteynik /* UDP src port on outer header is generated by HW, 971d386939aSYevgeny Kliteynik * so it is probably a bad idea to request matching it. 972d386939aSYevgeny Kliteynik * Nonetheless, it is allowed. 973d386939aSYevgeny Kliteynik */ 974d386939aSYevgeny Kliteynik 975d386939aSYevgeny Kliteynik MLX5_SET(fte_match_set_lyr_2_4, headers_c, udp_sport, 976d386939aSYevgeny Kliteynik ntohs(enc_ports.mask->src)); 977d386939aSYevgeny Kliteynik MLX5_SET(fte_match_set_lyr_2_4, headers_v, udp_sport, 978d386939aSYevgeny Kliteynik ntohs(enc_ports.key->src)); 979d386939aSYevgeny Kliteynik 980d386939aSYevgeny Kliteynik return 0; 981101f4de9SOz Shlomo } 982