Branch data Line data Source code
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 : 3 : int l3mdev_master_ifindex_rcu(const struct net_device *dev) 18 : : { 19 : : int ifindex = 0; 20 : : 21 : 3 : if (!dev) 22 : : return 0; 23 : : 24 : 3 : if (netif_is_l3_master(dev)) { 25 : 0 : ifindex = dev->ifindex; 26 : 3 : } 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 : 0 : master = netdev_master_upper_dev_get_rcu(_dev); 37 : 0 : if (master) 38 : 0 : ifindex = master->ifindex; 39 : : } 40 : : 41 : 3 : 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 : 0 : int l3mdev_master_upper_ifindex_by_index_rcu(struct net *net, int ifindex) 52 : : { 53 : : struct net_device *dev; 54 : : 55 : 0 : dev = dev_get_by_index_rcu(net, ifindex); 56 : 0 : while (dev && !netif_is_l3_master(dev)) 57 : 0 : dev = netdev_master_upper_dev_get(dev); 58 : : 59 : 0 : 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 : 3 : u32 l3mdev_fib_table_rcu(const struct net_device *dev) 70 : : { 71 : : u32 tb_id = 0; 72 : : 73 : 3 : if (!dev) 74 : : return 0; 75 : : 76 : 3 : if (netif_is_l3_master(dev)) { 77 : 0 : if (dev->l3mdev_ops->l3mdev_fib_table) 78 : 0 : tb_id = dev->l3mdev_ops->l3mdev_fib_table(dev); 79 : 3 : } 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 : 0 : master = netdev_master_upper_dev_get_rcu(_dev); 87 : 0 : if (master && 88 : 0 : master->l3mdev_ops->l3mdev_fib_table) 89 : 0 : tb_id = master->l3mdev_ops->l3mdev_fib_table(master); 90 : : } 91 : : 92 : 3 : return tb_id; 93 : : } 94 : : EXPORT_SYMBOL_GPL(l3mdev_fib_table_rcu); 95 : : 96 : 3 : u32 l3mdev_fib_table_by_index(struct net *net, int ifindex) 97 : : { 98 : : struct net_device *dev; 99 : : u32 tb_id = 0; 100 : : 101 : 3 : if (!ifindex) 102 : : return 0; 103 : : 104 : : rcu_read_lock(); 105 : : 106 : 0 : dev = dev_get_by_index_rcu(net, ifindex); 107 : 0 : if (dev) 108 : 0 : tb_id = l3mdev_fib_table_rcu(dev); 109 : : 110 : : rcu_read_unlock(); 111 : : 112 : 0 : 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 : 3 : 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 : 3 : if (fl6->flowi6_oif) { 133 : 3 : dev = dev_get_by_index_rcu(net, fl6->flowi6_oif); 134 : 3 : if (dev && netif_is_l3_slave(dev)) 135 : 0 : dev = netdev_master_upper_dev_get_rcu(dev); 136 : : 137 : 3 : if (dev && netif_is_l3_master(dev) && 138 : 0 : dev->l3mdev_ops->l3mdev_link_scope_lookup) 139 : 0 : dst = dev->l3mdev_ops->l3mdev_link_scope_lookup(dev, fl6); 140 : : } 141 : : 142 : 3 : 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 : 0 : 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 : 0 : dev = dev_get_by_index_rcu(net, fl->flowi_oif); 162 : 0 : if (dev && netif_is_l3_master(dev) && 163 : 0 : dev->l3mdev_ops->l3mdev_fib_table) { 164 : 0 : arg->table = dev->l3mdev_ops->l3mdev_fib_table(dev); 165 : : rc = 1; 166 : 0 : goto out; 167 : : } 168 : : 169 : 0 : dev = dev_get_by_index_rcu(net, fl->flowi_iif); 170 : 0 : if (dev && netif_is_l3_master(dev) && 171 : 0 : dev->l3mdev_ops->l3mdev_fib_table) { 172 : 0 : arg->table = dev->l3mdev_ops->l3mdev_fib_table(dev); 173 : : rc = 1; 174 : 0 : goto out; 175 : : } 176 : : 177 : : out: 178 : : rcu_read_unlock(); 179 : : 180 : 0 : return rc; 181 : : } 182 : : 183 : 3 : 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 : 3 : if (fl->flowi_oif) { 191 : 3 : dev = dev_get_by_index_rcu(net, fl->flowi_oif); 192 : 3 : if (dev) { 193 : 3 : ifindex = l3mdev_master_ifindex_rcu(dev); 194 : 3 : if (ifindex) { 195 : 0 : fl->flowi_oif = ifindex; 196 : 0 : fl->flowi_flags |= FLOWI_FLAG_SKIP_NH_OIF; 197 : 0 : goto out; 198 : : } 199 : : } 200 : : } 201 : : 202 : 3 : if (fl->flowi_iif) { 203 : 3 : dev = dev_get_by_index_rcu(net, fl->flowi_iif); 204 : 3 : if (dev) { 205 : 3 : ifindex = l3mdev_master_ifindex_rcu(dev); 206 : 3 : if (ifindex) { 207 : 0 : fl->flowi_iif = ifindex; 208 : 0 : fl->flowi_flags |= FLOWI_FLAG_SKIP_NH_OIF; 209 : : } 210 : : } 211 : : } 212 : : 213 : : out: 214 : : rcu_read_unlock(); 215 : 3 : } 216 : : EXPORT_SYMBOL_GPL(l3mdev_update_flow);