hc
2024-12-19 9370bb92b2d16684ee45cf24e879c93c509162da
kernel/net/l3mdev/l3mdev.c
....@@ -1,17 +1,106 @@
1
+// SPDX-License-Identifier: GPL-2.0-or-later
12 /*
23 * net/l3mdev/l3mdev.c - L3 master device implementation
34 * Copyright (c) 2015 Cumulus Networks
45 * 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.
106 */
117
128 #include <linux/netdevice.h>
139 #include <net/fib_rules.h>
1410 #include <net/l3mdev.h>
11
+
12
+static DEFINE_SPINLOCK(l3mdev_lock);
13
+
14
+struct l3mdev_handler {
15
+ lookup_by_table_id_t dev_lookup;
16
+};
17
+
18
+static struct l3mdev_handler l3mdev_handlers[L3MDEV_TYPE_MAX + 1];
19
+
20
+static int l3mdev_check_type(enum l3mdev_type l3type)
21
+{
22
+ if (l3type <= L3MDEV_TYPE_UNSPEC || l3type > L3MDEV_TYPE_MAX)
23
+ return -EINVAL;
24
+
25
+ return 0;
26
+}
27
+
28
+int l3mdev_table_lookup_register(enum l3mdev_type l3type,
29
+ lookup_by_table_id_t fn)
30
+{
31
+ struct l3mdev_handler *hdlr;
32
+ int res;
33
+
34
+ res = l3mdev_check_type(l3type);
35
+ if (res)
36
+ return res;
37
+
38
+ hdlr = &l3mdev_handlers[l3type];
39
+
40
+ spin_lock(&l3mdev_lock);
41
+
42
+ if (hdlr->dev_lookup) {
43
+ res = -EBUSY;
44
+ goto unlock;
45
+ }
46
+
47
+ hdlr->dev_lookup = fn;
48
+ res = 0;
49
+
50
+unlock:
51
+ spin_unlock(&l3mdev_lock);
52
+
53
+ return res;
54
+}
55
+EXPORT_SYMBOL_GPL(l3mdev_table_lookup_register);
56
+
57
+void l3mdev_table_lookup_unregister(enum l3mdev_type l3type,
58
+ lookup_by_table_id_t fn)
59
+{
60
+ struct l3mdev_handler *hdlr;
61
+
62
+ if (l3mdev_check_type(l3type))
63
+ return;
64
+
65
+ hdlr = &l3mdev_handlers[l3type];
66
+
67
+ spin_lock(&l3mdev_lock);
68
+
69
+ if (hdlr->dev_lookup == fn)
70
+ hdlr->dev_lookup = NULL;
71
+
72
+ spin_unlock(&l3mdev_lock);
73
+}
74
+EXPORT_SYMBOL_GPL(l3mdev_table_lookup_unregister);
75
+
76
+int l3mdev_ifindex_lookup_by_table_id(enum l3mdev_type l3type,
77
+ struct net *net, u32 table_id)
78
+{
79
+ lookup_by_table_id_t lookup;
80
+ struct l3mdev_handler *hdlr;
81
+ int ifindex = -EINVAL;
82
+ int res;
83
+
84
+ res = l3mdev_check_type(l3type);
85
+ if (res)
86
+ return res;
87
+
88
+ hdlr = &l3mdev_handlers[l3type];
89
+
90
+ spin_lock(&l3mdev_lock);
91
+
92
+ lookup = hdlr->dev_lookup;
93
+ if (!lookup)
94
+ goto unlock;
95
+
96
+ ifindex = lookup(net, table_id);
97
+
98
+unlock:
99
+ spin_unlock(&l3mdev_lock);
100
+
101
+ return ifindex;
102
+}
103
+EXPORT_SYMBOL_GPL(l3mdev_ifindex_lookup_by_table_id);
15104
16105 /**
17106 * l3mdev_master_ifindex - get index of L3 master device
....@@ -47,7 +136,25 @@
47136 EXPORT_SYMBOL_GPL(l3mdev_master_ifindex_rcu);
48137
49138 /**
50
- * l3mdev_fib_table - get FIB table id associated with an L3
139
+ * l3mdev_master_upper_ifindex_by_index - get index of upper l3 master
140
+ * device
141
+ * @net: network namespace for device index lookup
142
+ * @ifindex: targeted interface
143
+ */
144
+int l3mdev_master_upper_ifindex_by_index_rcu(struct net *net, int ifindex)
145
+{
146
+ struct net_device *dev;
147
+
148
+ dev = dev_get_by_index_rcu(net, ifindex);
149
+ while (dev && !netif_is_l3_master(dev))
150
+ dev = netdev_master_upper_dev_get_rcu(dev);
151
+
152
+ return dev ? dev->ifindex : 0;
153
+}
154
+EXPORT_SYMBOL_GPL(l3mdev_master_upper_ifindex_by_index_rcu);
155
+
156
+/**
157
+ * l3mdev_fib_table_rcu - get FIB table id associated with an L3
51158 * master interface
52159 * @dev: targeted interface
53160 */
....@@ -104,6 +211,8 @@
104211 * local and multicast addresses
105212 * @net: network namespace for device index lookup
106213 * @fl6: IPv6 flow struct for lookup
214
+ * This function does not hold refcnt on the returned dst.
215
+ * Caller must hold rcu_read_lock().
107216 */
108217
109218 struct dst_entry *l3mdev_link_scope_lookup(struct net *net,
....@@ -112,9 +221,8 @@
112221 struct dst_entry *dst = NULL;
113222 struct net_device *dev;
114223
224
+ WARN_ON_ONCE(!rcu_read_lock_held());
115225 if (fl6->flowi6_oif) {
116
- rcu_read_lock();
117
-
118226 dev = dev_get_by_index_rcu(net, fl6->flowi6_oif);
119227 if (dev && netif_is_l3_slave(dev))
120228 dev = netdev_master_upper_dev_get_rcu(dev);
....@@ -122,8 +230,6 @@
122230 if (dev && netif_is_l3_master(dev) &&
123231 dev->l3mdev_ops->l3mdev_link_scope_lookup)
124232 dst = dev->l3mdev_ops->l3mdev_link_scope_lookup(dev, fl6);
125
-
126
- rcu_read_unlock();
127233 }
128234
129235 return dst;