at v5.2-rc7 5.0 kB view raw
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 17int 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} 43EXPORT_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 */ 51int 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} 61EXPORT_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 69u32 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} 94EXPORT_SYMBOL_GPL(l3mdev_fib_table_rcu); 95 96u32 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} 114EXPORT_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 123struct 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} 145EXPORT_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 154int 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 178out: 179 rcu_read_unlock(); 180 181 return rc; 182} 183 184void 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 214out: 215 rcu_read_unlock(); 216} 217EXPORT_SYMBOL_GPL(l3mdev_update_flow);