0001
0002
0003
0004
0005
0006
0007
0008 #include <linux/netdevice.h>
0009 #include <net/fib_rules.h>
0010 #include <net/l3mdev.h>
0011
0012 static DEFINE_SPINLOCK(l3mdev_lock);
0013
0014 struct l3mdev_handler {
0015 lookup_by_table_id_t dev_lookup;
0016 };
0017
0018 static struct l3mdev_handler l3mdev_handlers[L3MDEV_TYPE_MAX + 1];
0019
0020 static int l3mdev_check_type(enum l3mdev_type l3type)
0021 {
0022 if (l3type <= L3MDEV_TYPE_UNSPEC || l3type > L3MDEV_TYPE_MAX)
0023 return -EINVAL;
0024
0025 return 0;
0026 }
0027
0028 int l3mdev_table_lookup_register(enum l3mdev_type l3type,
0029 lookup_by_table_id_t fn)
0030 {
0031 struct l3mdev_handler *hdlr;
0032 int res;
0033
0034 res = l3mdev_check_type(l3type);
0035 if (res)
0036 return res;
0037
0038 hdlr = &l3mdev_handlers[l3type];
0039
0040 spin_lock(&l3mdev_lock);
0041
0042 if (hdlr->dev_lookup) {
0043 res = -EBUSY;
0044 goto unlock;
0045 }
0046
0047 hdlr->dev_lookup = fn;
0048 res = 0;
0049
0050 unlock:
0051 spin_unlock(&l3mdev_lock);
0052
0053 return res;
0054 }
0055 EXPORT_SYMBOL_GPL(l3mdev_table_lookup_register);
0056
0057 void l3mdev_table_lookup_unregister(enum l3mdev_type l3type,
0058 lookup_by_table_id_t fn)
0059 {
0060 struct l3mdev_handler *hdlr;
0061
0062 if (l3mdev_check_type(l3type))
0063 return;
0064
0065 hdlr = &l3mdev_handlers[l3type];
0066
0067 spin_lock(&l3mdev_lock);
0068
0069 if (hdlr->dev_lookup == fn)
0070 hdlr->dev_lookup = NULL;
0071
0072 spin_unlock(&l3mdev_lock);
0073 }
0074 EXPORT_SYMBOL_GPL(l3mdev_table_lookup_unregister);
0075
0076 int l3mdev_ifindex_lookup_by_table_id(enum l3mdev_type l3type,
0077 struct net *net, u32 table_id)
0078 {
0079 lookup_by_table_id_t lookup;
0080 struct l3mdev_handler *hdlr;
0081 int ifindex = -EINVAL;
0082 int res;
0083
0084 res = l3mdev_check_type(l3type);
0085 if (res)
0086 return res;
0087
0088 hdlr = &l3mdev_handlers[l3type];
0089
0090 spin_lock(&l3mdev_lock);
0091
0092 lookup = hdlr->dev_lookup;
0093 if (!lookup)
0094 goto unlock;
0095
0096 ifindex = lookup(net, table_id);
0097
0098 unlock:
0099 spin_unlock(&l3mdev_lock);
0100
0101 return ifindex;
0102 }
0103 EXPORT_SYMBOL_GPL(l3mdev_ifindex_lookup_by_table_id);
0104
0105
0106
0107
0108
0109
0110 int l3mdev_master_ifindex_rcu(const struct net_device *dev)
0111 {
0112 int ifindex = 0;
0113
0114 if (!dev)
0115 return 0;
0116
0117 if (netif_is_l3_master(dev)) {
0118 ifindex = dev->ifindex;
0119 } else if (netif_is_l3_slave(dev)) {
0120 struct net_device *master;
0121 struct net_device *_dev = (struct net_device *)dev;
0122
0123
0124
0125
0126
0127
0128
0129 master = netdev_master_upper_dev_get_rcu(_dev);
0130 if (master)
0131 ifindex = master->ifindex;
0132 }
0133
0134 return ifindex;
0135 }
0136 EXPORT_SYMBOL_GPL(l3mdev_master_ifindex_rcu);
0137
0138
0139
0140
0141
0142
0143
0144 int l3mdev_master_upper_ifindex_by_index_rcu(struct net *net, int ifindex)
0145 {
0146 struct net_device *dev;
0147
0148 dev = dev_get_by_index_rcu(net, ifindex);
0149 while (dev && !netif_is_l3_master(dev))
0150 dev = netdev_master_upper_dev_get_rcu(dev);
0151
0152 return dev ? dev->ifindex : 0;
0153 }
0154 EXPORT_SYMBOL_GPL(l3mdev_master_upper_ifindex_by_index_rcu);
0155
0156
0157
0158
0159
0160
0161
0162 u32 l3mdev_fib_table_rcu(const struct net_device *dev)
0163 {
0164 u32 tb_id = 0;
0165
0166 if (!dev)
0167 return 0;
0168
0169 if (netif_is_l3_master(dev)) {
0170 if (dev->l3mdev_ops->l3mdev_fib_table)
0171 tb_id = dev->l3mdev_ops->l3mdev_fib_table(dev);
0172 } else if (netif_is_l3_slave(dev)) {
0173
0174
0175
0176 struct net_device *_dev = (struct net_device *) dev;
0177 const struct net_device *master;
0178
0179 master = netdev_master_upper_dev_get_rcu(_dev);
0180 if (master &&
0181 master->l3mdev_ops->l3mdev_fib_table)
0182 tb_id = master->l3mdev_ops->l3mdev_fib_table(master);
0183 }
0184
0185 return tb_id;
0186 }
0187 EXPORT_SYMBOL_GPL(l3mdev_fib_table_rcu);
0188
0189 u32 l3mdev_fib_table_by_index(struct net *net, int ifindex)
0190 {
0191 struct net_device *dev;
0192 u32 tb_id = 0;
0193
0194 if (!ifindex)
0195 return 0;
0196
0197 rcu_read_lock();
0198
0199 dev = dev_get_by_index_rcu(net, ifindex);
0200 if (dev)
0201 tb_id = l3mdev_fib_table_rcu(dev);
0202
0203 rcu_read_unlock();
0204
0205 return tb_id;
0206 }
0207 EXPORT_SYMBOL_GPL(l3mdev_fib_table_by_index);
0208
0209
0210
0211
0212
0213
0214
0215
0216
0217
0218 struct dst_entry *l3mdev_link_scope_lookup(struct net *net,
0219 struct flowi6 *fl6)
0220 {
0221 struct dst_entry *dst = NULL;
0222 struct net_device *dev;
0223
0224 WARN_ON_ONCE(!rcu_read_lock_held());
0225 if (fl6->flowi6_oif) {
0226 dev = dev_get_by_index_rcu(net, fl6->flowi6_oif);
0227 if (dev && netif_is_l3_slave(dev))
0228 dev = netdev_master_upper_dev_get_rcu(dev);
0229
0230 if (dev && netif_is_l3_master(dev) &&
0231 dev->l3mdev_ops->l3mdev_link_scope_lookup)
0232 dst = dev->l3mdev_ops->l3mdev_link_scope_lookup(dev, fl6);
0233 }
0234
0235 return dst;
0236 }
0237 EXPORT_SYMBOL_GPL(l3mdev_link_scope_lookup);
0238
0239
0240
0241
0242
0243
0244
0245
0246
0247 int l3mdev_fib_rule_match(struct net *net, struct flowi *fl,
0248 struct fib_lookup_arg *arg)
0249 {
0250 struct net_device *dev;
0251 int rc = 0;
0252
0253
0254 if (!fl->flowi_l3mdev)
0255 return 0;
0256
0257 rcu_read_lock();
0258
0259 dev = dev_get_by_index_rcu(net, fl->flowi_l3mdev);
0260 if (dev && netif_is_l3_master(dev) &&
0261 dev->l3mdev_ops->l3mdev_fib_table) {
0262 arg->table = dev->l3mdev_ops->l3mdev_fib_table(dev);
0263 rc = 1;
0264 }
0265
0266 rcu_read_unlock();
0267
0268 return rc;
0269 }
0270
0271 void l3mdev_update_flow(struct net *net, struct flowi *fl)
0272 {
0273 struct net_device *dev;
0274
0275 rcu_read_lock();
0276
0277 if (fl->flowi_oif) {
0278 dev = dev_get_by_index_rcu(net, fl->flowi_oif);
0279 if (dev) {
0280 if (!fl->flowi_l3mdev)
0281 fl->flowi_l3mdev = l3mdev_master_ifindex_rcu(dev);
0282
0283
0284
0285
0286 if (netif_is_l3_master(dev))
0287 fl->flowi_oif = 0;
0288 goto out;
0289 }
0290 }
0291
0292 if (fl->flowi_iif > LOOPBACK_IFINDEX && !fl->flowi_l3mdev) {
0293 dev = dev_get_by_index_rcu(net, fl->flowi_iif);
0294 if (dev)
0295 fl->flowi_l3mdev = l3mdev_master_ifindex_rcu(dev);
0296 }
0297
0298 out:
0299 rcu_read_unlock();
0300 }
0301 EXPORT_SYMBOL_GPL(l3mdev_update_flow);