Merge branch 'next' into for-linus
[linux-block.git] / net / l3mdev / l3mdev.c
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);