Blame view

net/l3mdev/l3mdev.c 4.61 KB
1b69c6d0a   David Ahern   net: Introduce L3...
1
2
3
4
5
6
7
8
9
10
11
12
  /*
   * net/l3mdev/l3mdev.c - L3 master device implementation
   * Copyright (c) 2015 Cumulus Networks
   * Copyright (c) 2015 David Ahern <dsa@cumulusnetworks.com>
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2 of the License, or
   * (at your option) any later version.
   */
  
  #include <linux/netdevice.h>
96c63fa73   David Ahern   net: Add l3mdev rule
13
  #include <net/fib_rules.h>
1b69c6d0a   David Ahern   net: Introduce L3...
14
15
16
17
18
19
  #include <net/l3mdev.h>
  
  /**
   *	l3mdev_master_ifindex - get index of L3 master device
   *	@dev: targeted interface
   */
3f2fb9a83   David Ahern   net: l3mdev: addr...
20
  int l3mdev_master_ifindex_rcu(const struct net_device *dev)
1b69c6d0a   David Ahern   net: Introduce L3...
21
22
23
24
25
26
27
28
  {
  	int ifindex = 0;
  
  	if (!dev)
  		return 0;
  
  	if (netif_is_l3_master(dev)) {
  		ifindex = dev->ifindex;
fee6d4c77   David Ahern   net: Add netif_is...
29
  	} else if (netif_is_l3_slave(dev)) {
1b69c6d0a   David Ahern   net: Introduce L3...
30
  		struct net_device *master;
3f2fb9a83   David Ahern   net: l3mdev: addr...
31
32
33
34
35
36
37
38
39
  		struct net_device *_dev = (struct net_device *)dev;
  
  		/* netdev_master_upper_dev_get_rcu calls
  		 * list_first_or_null_rcu to walk the upper dev list.
  		 * list_first_or_null_rcu does not handle a const arg. We aren't
  		 * making changes, just want the master device from that list so
  		 * typecast to remove the const
  		 */
  		master = netdev_master_upper_dev_get_rcu(_dev);
fee6d4c77   David Ahern   net: Add netif_is...
40
  		if (master)
1b69c6d0a   David Ahern   net: Introduce L3...
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
  			ifindex = master->ifindex;
  	}
  
  	return ifindex;
  }
  EXPORT_SYMBOL_GPL(l3mdev_master_ifindex_rcu);
  
  /**
   *	l3mdev_fib_table - get FIB table id associated with an L3
   *                             master interface
   *	@dev: targeted interface
   */
  
  u32 l3mdev_fib_table_rcu(const struct net_device *dev)
  {
  	u32 tb_id = 0;
  
  	if (!dev)
  		return 0;
  
  	if (netif_is_l3_master(dev)) {
  		if (dev->l3mdev_ops->l3mdev_fib_table)
  			tb_id = dev->l3mdev_ops->l3mdev_fib_table(dev);
fee6d4c77   David Ahern   net: Add netif_is...
64
  	} else if (netif_is_l3_slave(dev)) {
1b69c6d0a   David Ahern   net: Introduce L3...
65
66
67
68
69
70
71
  		/* Users of netdev_master_upper_dev_get_rcu need non-const,
  		 * but current inet_*type functions take a const
  		 */
  		struct net_device *_dev = (struct net_device *) dev;
  		const struct net_device *master;
  
  		master = netdev_master_upper_dev_get_rcu(_dev);
fee6d4c77   David Ahern   net: Add netif_is...
72
  		if (master &&
1b69c6d0a   David Ahern   net: Introduce L3...
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
  		    master->l3mdev_ops->l3mdev_fib_table)
  			tb_id = master->l3mdev_ops->l3mdev_fib_table(master);
  	}
  
  	return tb_id;
  }
  EXPORT_SYMBOL_GPL(l3mdev_fib_table_rcu);
  
  u32 l3mdev_fib_table_by_index(struct net *net, int ifindex)
  {
  	struct net_device *dev;
  	u32 tb_id = 0;
  
  	if (!ifindex)
  		return 0;
  
  	rcu_read_lock();
  
  	dev = dev_get_by_index_rcu(net, ifindex);
  	if (dev)
  		tb_id = l3mdev_fib_table_rcu(dev);
  
  	rcu_read_unlock();
  
  	return tb_id;
  }
  EXPORT_SYMBOL_GPL(l3mdev_fib_table_by_index);
4a65896f9   David Ahern   net: l3mdev: Move...
100
101
  
  /**
4c1feac58   David Ahern   net: vrf: Flip IP...
102
103
   *	l3mdev_link_scope_lookup - IPv6 route lookup based on flow for link
   *			     local and multicast addresses
4a65896f9   David Ahern   net: l3mdev: Move...
104
105
106
   *	@net: network namespace for device index lookup
   *	@fl6: IPv6 flow struct for lookup
   */
4c1feac58   David Ahern   net: vrf: Flip IP...
107
108
  struct dst_entry *l3mdev_link_scope_lookup(struct net *net,
  					   struct flowi6 *fl6)
4a65896f9   David Ahern   net: l3mdev: Move...
109
110
111
  {
  	struct dst_entry *dst = NULL;
  	struct net_device *dev;
1ff23beeb   David Ahern   net: l3mdev: Allo...
112
113
114
115
116
117
118
119
  	if (fl6->flowi6_oif) {
  		rcu_read_lock();
  
  		dev = dev_get_by_index_rcu(net, fl6->flowi6_oif);
  		if (dev && netif_is_l3_slave(dev))
  			dev = netdev_master_upper_dev_get_rcu(dev);
  
  		if (dev && netif_is_l3_master(dev) &&
4c1feac58   David Ahern   net: vrf: Flip IP...
120
121
  		    dev->l3mdev_ops->l3mdev_link_scope_lookup)
  			dst = dev->l3mdev_ops->l3mdev_link_scope_lookup(dev, fl6);
1ff23beeb   David Ahern   net: l3mdev: Allo...
122
123
  
  		rcu_read_unlock();
4a65896f9   David Ahern   net: l3mdev: Move...
124
125
126
127
  	}
  
  	return dst;
  }
4c1feac58   David Ahern   net: vrf: Flip IP...
128
  EXPORT_SYMBOL_GPL(l3mdev_link_scope_lookup);
4a65896f9   David Ahern   net: l3mdev: Move...
129

96c63fa73   David Ahern   net: Add l3mdev rule
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
  /**
   *	l3mdev_fib_rule_match - Determine if flowi references an
   *				L3 master device
   *	@net: network namespace for device index lookup
   *	@fl:  flow struct
   */
  
  int l3mdev_fib_rule_match(struct net *net, struct flowi *fl,
  			  struct fib_lookup_arg *arg)
  {
  	struct net_device *dev;
  	int rc = 0;
  
  	rcu_read_lock();
  
  	dev = dev_get_by_index_rcu(net, fl->flowi_oif);
  	if (dev && netif_is_l3_master(dev) &&
  	    dev->l3mdev_ops->l3mdev_fib_table) {
  		arg->table = dev->l3mdev_ops->l3mdev_fib_table(dev);
  		rc = 1;
  		goto out;
  	}
  
  	dev = dev_get_by_index_rcu(net, fl->flowi_iif);
  	if (dev && netif_is_l3_master(dev) &&
  	    dev->l3mdev_ops->l3mdev_fib_table) {
  		arg->table = dev->l3mdev_ops->l3mdev_fib_table(dev);
  		rc = 1;
  		goto out;
  	}
  
  out:
  	rcu_read_unlock();
  
  	return rc;
  }
9ee0034b8   David Ahern   net: flow: Add l3...
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
  
  void l3mdev_update_flow(struct net *net, struct flowi *fl)
  {
  	struct net_device *dev;
  	int ifindex;
  
  	rcu_read_lock();
  
  	if (fl->flowi_oif) {
  		dev = dev_get_by_index_rcu(net, fl->flowi_oif);
  		if (dev) {
  			ifindex = l3mdev_master_ifindex_rcu(dev);
  			if (ifindex) {
  				fl->flowi_oif = ifindex;
  				fl->flowi_flags |= FLOWI_FLAG_SKIP_NH_OIF;
  				goto out;
  			}
  		}
  	}
  
  	if (fl->flowi_iif) {
  		dev = dev_get_by_index_rcu(net, fl->flowi_iif);
  		if (dev) {
  			ifindex = l3mdev_master_ifindex_rcu(dev);
  			if (ifindex) {
  				fl->flowi_iif = ifindex;
  				fl->flowi_flags |= FLOWI_FLAG_SKIP_NH_OIF;
  			}
  		}
  	}
  
  out:
  	rcu_read_unlock();
  }
  EXPORT_SYMBOL_GPL(l3mdev_update_flow);