18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Bridge per vlan tunnel port dst_metadata handling code 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Authors: 68c2ecf20Sopenharmony_ci * Roopa Prabhu <roopa@cumulusnetworks.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 118c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <net/switchdev.h> 148c2ecf20Sopenharmony_ci#include <net/dst_metadata.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include "br_private.h" 178c2ecf20Sopenharmony_ci#include "br_private_tunnel.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistatic inline int br_vlan_tunid_cmp(struct rhashtable_compare_arg *arg, 208c2ecf20Sopenharmony_ci const void *ptr) 218c2ecf20Sopenharmony_ci{ 228c2ecf20Sopenharmony_ci const struct net_bridge_vlan *vle = ptr; 238c2ecf20Sopenharmony_ci __be64 tunid = *(__be64 *)arg->key; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci return vle->tinfo.tunnel_id != tunid; 268c2ecf20Sopenharmony_ci} 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic const struct rhashtable_params br_vlan_tunnel_rht_params = { 298c2ecf20Sopenharmony_ci .head_offset = offsetof(struct net_bridge_vlan, tnode), 308c2ecf20Sopenharmony_ci .key_offset = offsetof(struct net_bridge_vlan, tinfo.tunnel_id), 318c2ecf20Sopenharmony_ci .key_len = sizeof(__be64), 328c2ecf20Sopenharmony_ci .nelem_hint = 3, 338c2ecf20Sopenharmony_ci .obj_cmpfn = br_vlan_tunid_cmp, 348c2ecf20Sopenharmony_ci .automatic_shrinking = true, 358c2ecf20Sopenharmony_ci}; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic struct net_bridge_vlan *br_vlan_tunnel_lookup(struct rhashtable *tbl, 388c2ecf20Sopenharmony_ci u64 tunnel_id) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci return rhashtable_lookup_fast(tbl, &tunnel_id, 418c2ecf20Sopenharmony_ci br_vlan_tunnel_rht_params); 428c2ecf20Sopenharmony_ci} 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic void vlan_tunnel_info_release(struct net_bridge_vlan *vlan) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci struct metadata_dst *tdst = rtnl_dereference(vlan->tinfo.tunnel_dst); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci WRITE_ONCE(vlan->tinfo.tunnel_id, 0); 498c2ecf20Sopenharmony_ci RCU_INIT_POINTER(vlan->tinfo.tunnel_dst, NULL); 508c2ecf20Sopenharmony_ci dst_release(&tdst->dst); 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_civoid vlan_tunnel_info_del(struct net_bridge_vlan_group *vg, 548c2ecf20Sopenharmony_ci struct net_bridge_vlan *vlan) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci if (!rcu_access_pointer(vlan->tinfo.tunnel_dst)) 578c2ecf20Sopenharmony_ci return; 588c2ecf20Sopenharmony_ci rhashtable_remove_fast(&vg->tunnel_hash, &vlan->tnode, 598c2ecf20Sopenharmony_ci br_vlan_tunnel_rht_params); 608c2ecf20Sopenharmony_ci vlan_tunnel_info_release(vlan); 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic int __vlan_tunnel_info_add(struct net_bridge_vlan_group *vg, 648c2ecf20Sopenharmony_ci struct net_bridge_vlan *vlan, u32 tun_id) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci struct metadata_dst *metadata = rtnl_dereference(vlan->tinfo.tunnel_dst); 678c2ecf20Sopenharmony_ci __be64 key = key32_to_tunnel_id(cpu_to_be32(tun_id)); 688c2ecf20Sopenharmony_ci int err; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci if (metadata) 718c2ecf20Sopenharmony_ci return -EEXIST; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci metadata = __ip_tun_set_dst(0, 0, 0, 0, 0, TUNNEL_KEY, 748c2ecf20Sopenharmony_ci key, 0); 758c2ecf20Sopenharmony_ci if (!metadata) 768c2ecf20Sopenharmony_ci return -EINVAL; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci metadata->u.tun_info.mode |= IP_TUNNEL_INFO_TX | IP_TUNNEL_INFO_BRIDGE; 798c2ecf20Sopenharmony_ci rcu_assign_pointer(vlan->tinfo.tunnel_dst, metadata); 808c2ecf20Sopenharmony_ci WRITE_ONCE(vlan->tinfo.tunnel_id, key); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci err = rhashtable_lookup_insert_fast(&vg->tunnel_hash, &vlan->tnode, 838c2ecf20Sopenharmony_ci br_vlan_tunnel_rht_params); 848c2ecf20Sopenharmony_ci if (err) 858c2ecf20Sopenharmony_ci goto out; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci return 0; 888c2ecf20Sopenharmony_ciout: 898c2ecf20Sopenharmony_ci vlan_tunnel_info_release(vlan); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci return err; 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci/* Must be protected by RTNL. 958c2ecf20Sopenharmony_ci * Must be called with vid in range from 1 to 4094 inclusive. 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_ciint nbp_vlan_tunnel_info_add(const struct net_bridge_port *port, u16 vid, 988c2ecf20Sopenharmony_ci u32 tun_id) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci struct net_bridge_vlan_group *vg; 1018c2ecf20Sopenharmony_ci struct net_bridge_vlan *vlan; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci ASSERT_RTNL(); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci vg = nbp_vlan_group(port); 1068c2ecf20Sopenharmony_ci vlan = br_vlan_find(vg, vid); 1078c2ecf20Sopenharmony_ci if (!vlan) 1088c2ecf20Sopenharmony_ci return -EINVAL; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci return __vlan_tunnel_info_add(vg, vlan, tun_id); 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci/* Must be protected by RTNL. 1148c2ecf20Sopenharmony_ci * Must be called with vid in range from 1 to 4094 inclusive. 1158c2ecf20Sopenharmony_ci */ 1168c2ecf20Sopenharmony_ciint nbp_vlan_tunnel_info_delete(const struct net_bridge_port *port, u16 vid) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci struct net_bridge_vlan_group *vg; 1198c2ecf20Sopenharmony_ci struct net_bridge_vlan *v; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci ASSERT_RTNL(); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci vg = nbp_vlan_group(port); 1248c2ecf20Sopenharmony_ci v = br_vlan_find(vg, vid); 1258c2ecf20Sopenharmony_ci if (!v) 1268c2ecf20Sopenharmony_ci return -ENOENT; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci vlan_tunnel_info_del(vg, v); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci return 0; 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic void __vlan_tunnel_info_flush(struct net_bridge_vlan_group *vg) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci struct net_bridge_vlan *vlan, *tmp; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci list_for_each_entry_safe(vlan, tmp, &vg->vlan_list, vlist) 1388c2ecf20Sopenharmony_ci vlan_tunnel_info_del(vg, vlan); 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_civoid nbp_vlan_tunnel_info_flush(struct net_bridge_port *port) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci struct net_bridge_vlan_group *vg; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci ASSERT_RTNL(); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci vg = nbp_vlan_group(port); 1488c2ecf20Sopenharmony_ci __vlan_tunnel_info_flush(vg); 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ciint vlan_tunnel_init(struct net_bridge_vlan_group *vg) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci return rhashtable_init(&vg->tunnel_hash, &br_vlan_tunnel_rht_params); 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_civoid vlan_tunnel_deinit(struct net_bridge_vlan_group *vg) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci rhashtable_destroy(&vg->tunnel_hash); 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ciint br_handle_ingress_vlan_tunnel(struct sk_buff *skb, 1628c2ecf20Sopenharmony_ci struct net_bridge_port *p, 1638c2ecf20Sopenharmony_ci struct net_bridge_vlan_group *vg) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci struct ip_tunnel_info *tinfo = skb_tunnel_info(skb); 1668c2ecf20Sopenharmony_ci struct net_bridge_vlan *vlan; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci if (!vg || !tinfo) 1698c2ecf20Sopenharmony_ci return 0; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* if already tagged, ignore */ 1728c2ecf20Sopenharmony_ci if (skb_vlan_tagged(skb)) 1738c2ecf20Sopenharmony_ci return 0; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci /* lookup vid, given tunnel id */ 1768c2ecf20Sopenharmony_ci vlan = br_vlan_tunnel_lookup(&vg->tunnel_hash, tinfo->key.tun_id); 1778c2ecf20Sopenharmony_ci if (!vlan) 1788c2ecf20Sopenharmony_ci return 0; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci skb_dst_drop(skb); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci __vlan_hwaccel_put_tag(skb, p->br->vlan_proto, vlan->vid); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci return 0; 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ciint br_handle_egress_vlan_tunnel(struct sk_buff *skb, 1888c2ecf20Sopenharmony_ci struct net_bridge_vlan *vlan) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci struct metadata_dst *tunnel_dst; 1918c2ecf20Sopenharmony_ci __be64 tunnel_id; 1928c2ecf20Sopenharmony_ci int err; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci if (!vlan) 1958c2ecf20Sopenharmony_ci return 0; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci tunnel_id = READ_ONCE(vlan->tinfo.tunnel_id); 1988c2ecf20Sopenharmony_ci if (!tunnel_id || unlikely(!skb_vlan_tag_present(skb))) 1998c2ecf20Sopenharmony_ci return 0; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci skb_dst_drop(skb); 2028c2ecf20Sopenharmony_ci err = skb_vlan_pop(skb); 2038c2ecf20Sopenharmony_ci if (err) 2048c2ecf20Sopenharmony_ci return err; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci tunnel_dst = rcu_dereference(vlan->tinfo.tunnel_dst); 2078c2ecf20Sopenharmony_ci if (tunnel_dst && dst_hold_safe(&tunnel_dst->dst)) 2088c2ecf20Sopenharmony_ci skb_dst_set(skb, &tunnel_dst->dst); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci return 0; 2118c2ecf20Sopenharmony_ci} 212