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