xref: /linux/net/l3mdev/l3mdev.c (revision ebf68996de0ab250c5d520eb2291ab65643e9a1e)
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 int 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 }
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 int 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 }
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 u32 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 }
94 EXPORT_SYMBOL_GPL(l3mdev_fib_table_rcu);
95 
96 u32 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 }
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  */
122 
123 struct dst_entry *l3mdev_link_scope_lookup(struct net *net,
124 					   struct flowi6 *fl6)
125 {
126 	struct dst_entry *dst = NULL;
127 	struct net_device *dev;
128 
129 	if (fl6->flowi6_oif) {
130 		rcu_read_lock();
131 
132 		dev = dev_get_by_index_rcu(net, fl6->flowi6_oif);
133 		if (dev && netif_is_l3_slave(dev))
134 			dev = netdev_master_upper_dev_get_rcu(dev);
135 
136 		if (dev && netif_is_l3_master(dev) &&
137 		    dev->l3mdev_ops->l3mdev_link_scope_lookup)
138 			dst = dev->l3mdev_ops->l3mdev_link_scope_lookup(dev, fl6);
139 
140 		rcu_read_unlock();
141 	}
142 
143 	return dst;
144 }
145 EXPORT_SYMBOL_GPL(l3mdev_link_scope_lookup);
146 
147 /**
148  *	l3mdev_fib_rule_match - Determine if flowi references an
149  *				L3 master device
150  *	@net: network namespace for device index lookup
151  *	@fl:  flow struct
152  */
153 
154 int l3mdev_fib_rule_match(struct net *net, struct flowi *fl,
155 			  struct fib_lookup_arg *arg)
156 {
157 	struct net_device *dev;
158 	int rc = 0;
159 
160 	rcu_read_lock();
161 
162 	dev = dev_get_by_index_rcu(net, fl->flowi_oif);
163 	if (dev && netif_is_l3_master(dev) &&
164 	    dev->l3mdev_ops->l3mdev_fib_table) {
165 		arg->table = dev->l3mdev_ops->l3mdev_fib_table(dev);
166 		rc = 1;
167 		goto out;
168 	}
169 
170 	dev = dev_get_by_index_rcu(net, fl->flowi_iif);
171 	if (dev && netif_is_l3_master(dev) &&
172 	    dev->l3mdev_ops->l3mdev_fib_table) {
173 		arg->table = dev->l3mdev_ops->l3mdev_fib_table(dev);
174 		rc = 1;
175 		goto out;
176 	}
177 
178 out:
179 	rcu_read_unlock();
180 
181 	return rc;
182 }
183 
184 void l3mdev_update_flow(struct net *net, struct flowi *fl)
185 {
186 	struct net_device *dev;
187 	int ifindex;
188 
189 	rcu_read_lock();
190 
191 	if (fl->flowi_oif) {
192 		dev = dev_get_by_index_rcu(net, fl->flowi_oif);
193 		if (dev) {
194 			ifindex = l3mdev_master_ifindex_rcu(dev);
195 			if (ifindex) {
196 				fl->flowi_oif = ifindex;
197 				fl->flowi_flags |= FLOWI_FLAG_SKIP_NH_OIF;
198 				goto out;
199 			}
200 		}
201 	}
202 
203 	if (fl->flowi_iif) {
204 		dev = dev_get_by_index_rcu(net, fl->flowi_iif);
205 		if (dev) {
206 			ifindex = l3mdev_master_ifindex_rcu(dev);
207 			if (ifindex) {
208 				fl->flowi_iif = ifindex;
209 				fl->flowi_flags |= FLOWI_FLAG_SKIP_NH_OIF;
210 			}
211 		}
212 	}
213 
214 out:
215 	rcu_read_unlock();
216 }
217 EXPORT_SYMBOL_GPL(l3mdev_update_flow);
218