xref: /openbmc/linux/net/l3mdev/l3mdev.c (revision 3dc4b6fb)
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  *	This function does not hold refcnt on the returned dst.
122  *	Caller must hold rcu_read_lock().
123  */
124 
125 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 	if (fl6->flowi6_oif) {
133 		dev = dev_get_by_index_rcu(net, fl6->flowi6_oif);
134 		if (dev && netif_is_l3_slave(dev))
135 			dev = netdev_master_upper_dev_get_rcu(dev);
136 
137 		if (dev && netif_is_l3_master(dev) &&
138 		    dev->l3mdev_ops->l3mdev_link_scope_lookup)
139 			dst = dev->l3mdev_ops->l3mdev_link_scope_lookup(dev, fl6);
140 	}
141 
142 	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 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 	dev = dev_get_by_index_rcu(net, fl->flowi_oif);
162 	if (dev && netif_is_l3_master(dev) &&
163 	    dev->l3mdev_ops->l3mdev_fib_table) {
164 		arg->table = dev->l3mdev_ops->l3mdev_fib_table(dev);
165 		rc = 1;
166 		goto out;
167 	}
168 
169 	dev = dev_get_by_index_rcu(net, fl->flowi_iif);
170 	if (dev && netif_is_l3_master(dev) &&
171 	    dev->l3mdev_ops->l3mdev_fib_table) {
172 		arg->table = dev->l3mdev_ops->l3mdev_fib_table(dev);
173 		rc = 1;
174 		goto out;
175 	}
176 
177 out:
178 	rcu_read_unlock();
179 
180 	return rc;
181 }
182 
183 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 	if (fl->flowi_oif) {
191 		dev = dev_get_by_index_rcu(net, fl->flowi_oif);
192 		if (dev) {
193 			ifindex = l3mdev_master_ifindex_rcu(dev);
194 			if (ifindex) {
195 				fl->flowi_oif = ifindex;
196 				fl->flowi_flags |= FLOWI_FLAG_SKIP_NH_OIF;
197 				goto out;
198 			}
199 		}
200 	}
201 
202 	if (fl->flowi_iif) {
203 		dev = dev_get_by_index_rcu(net, fl->flowi_iif);
204 		if (dev) {
205 			ifindex = l3mdev_master_ifindex_rcu(dev);
206 			if (ifindex) {
207 				fl->flowi_iif = ifindex;
208 				fl->flowi_flags |= FLOWI_FLAG_SKIP_NH_OIF;
209 			}
210 		}
211 	}
212 
213 out:
214 	rcu_read_unlock();
215 }
216 EXPORT_SYMBOL_GPL(l3mdev_update_flow);
217