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 */ 122 123 struct dst_entry *l3mdev_link_scope_lookup(struct net *net, 124 struct flowi6 *fl6) 125 { 126 struct dst_entry *dst = NULL; 127 struct net_device *dev; 128 129 if (fl6->flowi6_oif) { 130 rcu_read_lock(); 131 132 dev = dev_get_by_index_rcu(net, fl6->flowi6_oif); 133 if (dev && netif_is_l3_slave(dev)) 134 dev = netdev_master_upper_dev_get_rcu(dev); 135 136 if (dev && netif_is_l3_master(dev) && 137 dev->l3mdev_ops->l3mdev_link_scope_lookup) 138 dst = dev->l3mdev_ops->l3mdev_link_scope_lookup(dev, fl6); 139 140 rcu_read_unlock(); 141 } 142 143 return dst; 144 } 145 EXPORT_SYMBOL_GPL(l3mdev_link_scope_lookup); 146 147 /** 148 * l3mdev_fib_rule_match - Determine if flowi references an 149 * L3 master device 150 * @net: network namespace for device index lookup 151 * @fl: flow struct 152 */ 153 154 int l3mdev_fib_rule_match(struct net *net, struct flowi *fl, 155 struct fib_lookup_arg *arg) 156 { 157 struct net_device *dev; 158 int rc = 0; 159 160 rcu_read_lock(); 161 162 dev = dev_get_by_index_rcu(net, fl->flowi_oif); 163 if (dev && netif_is_l3_master(dev) && 164 dev->l3mdev_ops->l3mdev_fib_table) { 165 arg->table = dev->l3mdev_ops->l3mdev_fib_table(dev); 166 rc = 1; 167 goto out; 168 } 169 170 dev = dev_get_by_index_rcu(net, fl->flowi_iif); 171 if (dev && netif_is_l3_master(dev) && 172 dev->l3mdev_ops->l3mdev_fib_table) { 173 arg->table = dev->l3mdev_ops->l3mdev_fib_table(dev); 174 rc = 1; 175 goto out; 176 } 177 178 out: 179 rcu_read_unlock(); 180 181 return rc; 182 } 183 184 void l3mdev_update_flow(struct net *net, struct flowi *fl) 185 { 186 struct net_device *dev; 187 int ifindex; 188 189 rcu_read_lock(); 190 191 if (fl->flowi_oif) { 192 dev = dev_get_by_index_rcu(net, fl->flowi_oif); 193 if (dev) { 194 ifindex = l3mdev_master_ifindex_rcu(dev); 195 if (ifindex) { 196 fl->flowi_oif = ifindex; 197 fl->flowi_flags |= FLOWI_FLAG_SKIP_NH_OIF; 198 goto out; 199 } 200 } 201 } 202 203 if (fl->flowi_iif) { 204 dev = dev_get_by_index_rcu(net, fl->flowi_iif); 205 if (dev) { 206 ifindex = l3mdev_master_ifindex_rcu(dev); 207 if (ifindex) { 208 fl->flowi_iif = ifindex; 209 fl->flowi_flags |= FLOWI_FLAG_SKIP_NH_OIF; 210 } 211 } 212 } 213 214 out: 215 rcu_read_unlock(); 216 } 217 EXPORT_SYMBOL_GPL(l3mdev_update_flow); 218