at v5.3-rc2 5.1 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 * This function does not hold refcnt on the returned dst. 122 * Caller must hold rcu_read_lock(). 123 */ 124 125struct 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} 144EXPORT_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 153int 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 177out: 178 rcu_read_unlock(); 179 180 return rc; 181} 182 183void 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 213out: 214 rcu_read_unlock(); 215} 216EXPORT_SYMBOL_GPL(l3mdev_update_flow);