1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * net/l3mdev/l3mdev.c - L3 master device implementation 4 * Copyright (c) 2015 Cumulus Networks 5 * Copyright (c) 2015 David Ahern <dsa@cumulusnetworks.com> 6 */ 7 8 #include <linux/netdevice.h> 9 #include <net/fib_rules.h> 10 #include <net/l3mdev.h> 11 12 /** 13 * l3mdev_master_ifindex - get index of L3 master device 14 * @dev: targeted interface 15 */ 16 17 int l3mdev_master_ifindex_rcu(const struct net_device *dev) 18 { 19 int ifindex = 0; 20 21 if (!dev) 22 return 0; 23 24 if (netif_is_l3_master(dev)) { 25 ifindex = dev->ifindex; 26 } else if (netif_is_l3_slave(dev)) { 27 struct net_device *master; 28 struct net_device *_dev = (struct net_device *)dev; 29 30 /* netdev_master_upper_dev_get_rcu calls 31 * list_first_or_null_rcu to walk the upper dev list. 32 * list_first_or_null_rcu does not handle a const arg. We aren't 33 * making changes, just want the master device from that list so 34 * typecast to remove the const 35 */ 36 master = netdev_master_upper_dev_get_rcu(_dev); 37 if (master) 38 ifindex = master->ifindex; 39 } 40 41 return ifindex; 42 } 43 EXPORT_SYMBOL_GPL(l3mdev_master_ifindex_rcu); 44 45 /** 46 * l3mdev_master_upper_ifindex_by_index - get index of upper l3 master 47 * device 48 * @net: network namespace for device index lookup 49 * @ifindex: targeted interface 50 */ 51 int l3mdev_master_upper_ifindex_by_index_rcu(struct net *net, int ifindex) 52 { 53 struct net_device *dev; 54 55 dev = dev_get_by_index_rcu(net, ifindex); 56 while (dev && !netif_is_l3_master(dev)) 57 dev = netdev_master_upper_dev_get(dev); 58 59 return dev ? dev->ifindex : 0; 60 } 61 EXPORT_SYMBOL_GPL(l3mdev_master_upper_ifindex_by_index_rcu); 62 63 /** 64 * l3mdev_fib_table - get FIB table id associated with an L3 65 * master interface 66 * @dev: targeted interface 67 */ 68 69 u32 l3mdev_fib_table_rcu(const struct net_device *dev) 70 { 71 u32 tb_id = 0; 72 73 if (!dev) 74 return 0; 75 76 if (netif_is_l3_master(dev)) { 77 if (dev->l3mdev_ops->l3mdev_fib_table) 78 tb_id = dev->l3mdev_ops->l3mdev_fib_table(dev); 79 } else if (netif_is_l3_slave(dev)) { 80 /* Users of netdev_master_upper_dev_get_rcu need non-const, 81 * but current inet_*type functions take a const 82 */ 83 struct net_device *_dev = (struct net_device *) dev; 84 const struct net_device *master; 85 86 master = netdev_master_upper_dev_get_rcu(_dev); 87 if (master && 88 master->l3mdev_ops->l3mdev_fib_table) 89 tb_id = master->l3mdev_ops->l3mdev_fib_table(master); 90 } 91 92 return tb_id; 93 } 94 EXPORT_SYMBOL_GPL(l3mdev_fib_table_rcu); 95 96 u32 l3mdev_fib_table_by_index(struct net *net, int ifindex) 97 { 98 struct net_device *dev; 99 u32 tb_id = 0; 100 101 if (!ifindex) 102 return 0; 103 104 rcu_read_lock(); 105 106 dev = dev_get_by_index_rcu(net, ifindex); 107 if (dev) 108 tb_id = l3mdev_fib_table_rcu(dev); 109 110 rcu_read_unlock(); 111 112 return tb_id; 113 } 114 EXPORT_SYMBOL_GPL(l3mdev_fib_table_by_index); 115 116 /** 117 * l3mdev_link_scope_lookup - IPv6 route lookup based on flow for link 118 * local and multicast addresses 119 * @net: network namespace for device index lookup 120 * @fl6: IPv6 flow struct for lookup 121 * This function does not hold refcnt on the returned dst. 122 * Caller must hold rcu_read_lock(). 123 */ 124 125 struct dst_entry *l3mdev_link_scope_lookup(struct net *net, 126 struct flowi6 *fl6) 127 { 128 struct dst_entry *dst = NULL; 129 struct net_device *dev; 130 131 WARN_ON_ONCE(!rcu_read_lock_held()); 132 if (fl6->flowi6_oif) { 133 dev = dev_get_by_index_rcu(net, fl6->flowi6_oif); 134 if (dev && netif_is_l3_slave(dev)) 135 dev = netdev_master_upper_dev_get_rcu(dev); 136 137 if (dev && netif_is_l3_master(dev) && 138 dev->l3mdev_ops->l3mdev_link_scope_lookup) 139 dst = dev->l3mdev_ops->l3mdev_link_scope_lookup(dev, fl6); 140 } 141 142 return dst; 143 } 144 EXPORT_SYMBOL_GPL(l3mdev_link_scope_lookup); 145 146 /** 147 * l3mdev_fib_rule_match - Determine if flowi references an 148 * L3 master device 149 * @net: network namespace for device index lookup 150 * @fl: flow struct 151 */ 152 153 int l3mdev_fib_rule_match(struct net *net, struct flowi *fl, 154 struct fib_lookup_arg *arg) 155 { 156 struct net_device *dev; 157 int rc = 0; 158 159 rcu_read_lock(); 160 161 dev = dev_get_by_index_rcu(net, fl->flowi_oif); 162 if (dev && netif_is_l3_master(dev) && 163 dev->l3mdev_ops->l3mdev_fib_table) { 164 arg->table = dev->l3mdev_ops->l3mdev_fib_table(dev); 165 rc = 1; 166 goto out; 167 } 168 169 dev = dev_get_by_index_rcu(net, fl->flowi_iif); 170 if (dev && netif_is_l3_master(dev) && 171 dev->l3mdev_ops->l3mdev_fib_table) { 172 arg->table = dev->l3mdev_ops->l3mdev_fib_table(dev); 173 rc = 1; 174 goto out; 175 } 176 177 out: 178 rcu_read_unlock(); 179 180 return rc; 181 } 182 183 void l3mdev_update_flow(struct net *net, struct flowi *fl) 184 { 185 struct net_device *dev; 186 int ifindex; 187 188 rcu_read_lock(); 189 190 if (fl->flowi_oif) { 191 dev = dev_get_by_index_rcu(net, fl->flowi_oif); 192 if (dev) { 193 ifindex = l3mdev_master_ifindex_rcu(dev); 194 if (ifindex) { 195 fl->flowi_oif = ifindex; 196 fl->flowi_flags |= FLOWI_FLAG_SKIP_NH_OIF; 197 goto out; 198 } 199 } 200 } 201 202 if (fl->flowi_iif) { 203 dev = dev_get_by_index_rcu(net, fl->flowi_iif); 204 if (dev) { 205 ifindex = l3mdev_master_ifindex_rcu(dev); 206 if (ifindex) { 207 fl->flowi_iif = ifindex; 208 fl->flowi_flags |= FLOWI_FLAG_SKIP_NH_OIF; 209 } 210 } 211 } 212 213 out: 214 rcu_read_unlock(); 215 } 216 EXPORT_SYMBOL_GPL(l3mdev_update_flow); 217