Blame view

net/bridge/br_netlink_tunnel.c 6.77 KB
2874c5fd2   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
efa5356b0   Roopa Prabhu   bridge: per vlan ...
2
3
4
5
6
  /*
   *	Bridge per vlan tunnel port dst_metadata netlink control interface
   *
   *	Authors:
   *	Roopa Prabhu		<roopa@cumulusnetworks.com>
efa5356b0   Roopa Prabhu   bridge: per vlan ...
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
   */
  
  #include <linux/kernel.h>
  #include <linux/slab.h>
  #include <linux/etherdevice.h>
  #include <net/rtnetlink.h>
  #include <net/net_namespace.h>
  #include <net/sock.h>
  #include <uapi/linux/if_bridge.h>
  #include <net/dst_metadata.h>
  
  #include "br_private.h"
  #include "br_private_tunnel.h"
  
  static size_t __get_vlan_tinfo_size(void)
  {
  	return nla_total_size(0) + /* nest IFLA_BRIDGE_VLAN_TUNNEL_INFO */
  		  nla_total_size(sizeof(u32)) + /* IFLA_BRIDGE_VLAN_TUNNEL_ID */
  		  nla_total_size(sizeof(u16)) + /* IFLA_BRIDGE_VLAN_TUNNEL_VID */
  		  nla_total_size(sizeof(u16)); /* IFLA_BRIDGE_VLAN_TUNNEL_FLAGS */
  }
8ef959476   Roopa Prabhu   bridge: vlan tunn...
28
29
  static bool vlan_tunid_inrange(struct net_bridge_vlan *v_curr,
  			       struct net_bridge_vlan *v_last)
efa5356b0   Roopa Prabhu   bridge: per vlan ...
30
  {
8ef959476   Roopa Prabhu   bridge: vlan tunn...
31
32
  	__be32 tunid_curr = tunnel_id_to_key32(v_curr->tinfo.tunnel_id);
  	__be32 tunid_last = tunnel_id_to_key32(v_last->tinfo.tunnel_id);
efa5356b0   Roopa Prabhu   bridge: per vlan ...
33

8ef959476   Roopa Prabhu   bridge: vlan tunn...
34
  	return (be32_to_cpu(tunid_curr) - be32_to_cpu(tunid_last)) == 1;
efa5356b0   Roopa Prabhu   bridge: per vlan ...
35
36
37
38
  }
  
  static int __get_num_vlan_tunnel_infos(struct net_bridge_vlan_group *vg)
  {
8ef959476   Roopa Prabhu   bridge: vlan tunn...
39
  	struct net_bridge_vlan *v, *vtbegin = NULL, *vtend = NULL;
efa5356b0   Roopa Prabhu   bridge: per vlan ...
40
41
42
43
44
45
46
  	int num_tinfos = 0;
  
  	/* Count number of vlan infos */
  	list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
  		/* only a context, bridge vlan not activated */
  		if (!br_vlan_should_use(v) || !v->tinfo.tunnel_id)
  			continue;
8ef959476   Roopa Prabhu   bridge: vlan tunn...
47
  		if (!vtbegin) {
efa5356b0   Roopa Prabhu   bridge: per vlan ...
48
  			goto initvars;
8ef959476   Roopa Prabhu   bridge: vlan tunn...
49
50
51
  		} else if ((v->vid - vtend->vid) == 1 &&
  			   vlan_tunid_inrange(v, vtend)) {
  			vtend = v;
efa5356b0   Roopa Prabhu   bridge: per vlan ...
52
53
  			continue;
  		} else {
8ef959476   Roopa Prabhu   bridge: vlan tunn...
54
  			if ((vtend->vid - vtbegin->vid) > 0)
efa5356b0   Roopa Prabhu   bridge: per vlan ...
55
56
57
58
59
  				num_tinfos += 2;
  			else
  				num_tinfos += 1;
  		}
  initvars:
8ef959476   Roopa Prabhu   bridge: vlan tunn...
60
61
  		vtbegin = v;
  		vtend = v;
efa5356b0   Roopa Prabhu   bridge: per vlan ...
62
  	}
8ef959476   Roopa Prabhu   bridge: vlan tunn...
63
64
  	if (vtbegin && vtend) {
  		if ((vtend->vid - vtbegin->vid) > 0)
efa5356b0   Roopa Prabhu   bridge: per vlan ...
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
  			num_tinfos += 2;
  		else
  			num_tinfos += 1;
  	}
  
  	return num_tinfos;
  }
  
  int br_get_vlan_tunnel_info_size(struct net_bridge_vlan_group *vg)
  {
  	int num_tinfos;
  
  	if (!vg)
  		return 0;
  
  	rcu_read_lock();
  	num_tinfos = __get_num_vlan_tunnel_infos(vg);
  	rcu_read_unlock();
  
  	return num_tinfos * __get_vlan_tinfo_size();
  }
  
  static int br_fill_vlan_tinfo(struct sk_buff *skb, u16 vid,
  			      __be64 tunnel_id, u16 flags)
  {
  	__be32 tid = tunnel_id_to_key32(tunnel_id);
  	struct nlattr *tmap;
ae0be8de9   Michal Kubecek   netlink: make nla...
92
  	tmap = nla_nest_start_noflag(skb, IFLA_BRIDGE_VLAN_TUNNEL_INFO);
efa5356b0   Roopa Prabhu   bridge: per vlan ...
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
  	if (!tmap)
  		return -EMSGSIZE;
  	if (nla_put_u32(skb, IFLA_BRIDGE_VLAN_TUNNEL_ID,
  			be32_to_cpu(tid)))
  		goto nla_put_failure;
  	if (nla_put_u16(skb, IFLA_BRIDGE_VLAN_TUNNEL_VID,
  			vid))
  		goto nla_put_failure;
  	if (nla_put_u16(skb, IFLA_BRIDGE_VLAN_TUNNEL_FLAGS,
  			flags))
  		goto nla_put_failure;
  	nla_nest_end(skb, tmap);
  
  	return 0;
  
  nla_put_failure:
  	nla_nest_cancel(skb, tmap);
  
  	return -EMSGSIZE;
  }
  
  static int br_fill_vlan_tinfo_range(struct sk_buff *skb,
  				    struct net_bridge_vlan *vtbegin,
  				    struct net_bridge_vlan *vtend)
  {
  	int err;
a8cab863a   Roopa Prabhu   bridge: remove un...
119
  	if (vtend && (vtend->vid - vtbegin->vid) > 0) {
efa5356b0   Roopa Prabhu   bridge: per vlan ...
120
121
122
123
124
125
126
127
128
129
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
  		/* add range to skb */
  		err = br_fill_vlan_tinfo(skb, vtbegin->vid,
  					 vtbegin->tinfo.tunnel_id,
  					 BRIDGE_VLAN_INFO_RANGE_BEGIN);
  		if (err)
  			return err;
  
  		err = br_fill_vlan_tinfo(skb, vtend->vid,
  					 vtend->tinfo.tunnel_id,
  					 BRIDGE_VLAN_INFO_RANGE_END);
  		if (err)
  			return err;
  	} else {
  		err = br_fill_vlan_tinfo(skb, vtbegin->vid,
  					 vtbegin->tinfo.tunnel_id,
  					 0);
  		if (err)
  			return err;
  	}
  
  	return 0;
  }
  
  int br_fill_vlan_tunnel_info(struct sk_buff *skb,
  			     struct net_bridge_vlan_group *vg)
  {
  	struct net_bridge_vlan *vtbegin = NULL;
  	struct net_bridge_vlan *vtend = NULL;
  	struct net_bridge_vlan *v;
  	int err;
  
  	/* Count number of vlan infos */
  	list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
  		/* only a context, bridge vlan not activated */
  		if (!br_vlan_should_use(v))
  			continue;
  
  		if (!v->tinfo.tunnel_dst)
  			continue;
  
  		if (!vtbegin) {
  			goto initvars;
  		} else if ((v->vid - vtend->vid) == 1 &&
8ef959476   Roopa Prabhu   bridge: vlan tunn...
163
  			    vlan_tunid_inrange(v, vtend)) {
efa5356b0   Roopa Prabhu   bridge: per vlan ...
164
165
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
  			vtend = v;
  			continue;
  		} else {
  			err = br_fill_vlan_tinfo_range(skb, vtbegin, vtend);
  			if (err)
  				return err;
  		}
  initvars:
  		vtbegin = v;
  		vtend = v;
  	}
  
  	if (vtbegin) {
  		err = br_fill_vlan_tinfo_range(skb, vtbegin, vtend);
  		if (err)
  			return err;
  	}
  
  	return 0;
  }
  
  static const struct nla_policy vlan_tunnel_policy[IFLA_BRIDGE_VLAN_TUNNEL_MAX + 1] = {
  	[IFLA_BRIDGE_VLAN_TUNNEL_ID] = { .type = NLA_U32 },
  	[IFLA_BRIDGE_VLAN_TUNNEL_VID] = { .type = NLA_U16 },
  	[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS] = { .type = NLA_U16 },
  };
  
  static int br_vlan_tunnel_info(struct net_bridge_port *p, int cmd,
e19b42a1a   Nikolay Aleksandrov   bridge: netlink: ...
192
  			       u16 vid, u32 tun_id, bool *changed)
efa5356b0   Roopa Prabhu   bridge: per vlan ...
193
194
195
196
197
198
199
200
201
  {
  	int err = 0;
  
  	if (!p)
  		return -EINVAL;
  
  	switch (cmd) {
  	case RTM_SETLINK:
  		err = nbp_vlan_tunnel_info_add(p, vid, tun_id);
e19b42a1a   Nikolay Aleksandrov   bridge: netlink: ...
202
203
  		if (!err)
  			*changed = true;
efa5356b0   Roopa Prabhu   bridge: per vlan ...
204
205
  		break;
  	case RTM_DELLINK:
e19b42a1a   Nikolay Aleksandrov   bridge: netlink: ...
206
207
  		if (!nbp_vlan_tunnel_info_delete(p, vid))
  			*changed = true;
efa5356b0   Roopa Prabhu   bridge: per vlan ...
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
  		break;
  	}
  
  	return err;
  }
  
  int br_parse_vlan_tunnel_info(struct nlattr *attr,
  			      struct vtunnel_info *tinfo)
  {
  	struct nlattr *tb[IFLA_BRIDGE_VLAN_TUNNEL_MAX + 1];
  	u32 tun_id;
  	u16 vid, flags = 0;
  	int err;
  
  	memset(tinfo, 0, sizeof(*tinfo));
8cb081746   Johannes Berg   netlink: make val...
223
224
  	err = nla_parse_nested_deprecated(tb, IFLA_BRIDGE_VLAN_TUNNEL_MAX,
  					  attr, vlan_tunnel_policy, NULL);
efa5356b0   Roopa Prabhu   bridge: per vlan ...
225
226
  	if (err < 0)
  		return err;
bb580ad69   Nikolay Aleksandrov   bridge: tunnel: f...
227
228
229
  	if (!tb[IFLA_BRIDGE_VLAN_TUNNEL_ID] ||
  	    !tb[IFLA_BRIDGE_VLAN_TUNNEL_VID])
  		return -EINVAL;
efa5356b0   Roopa Prabhu   bridge: per vlan ...
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
  	tun_id = nla_get_u32(tb[IFLA_BRIDGE_VLAN_TUNNEL_ID]);
  	vid = nla_get_u16(tb[IFLA_BRIDGE_VLAN_TUNNEL_VID]);
  	if (vid >= VLAN_VID_MASK)
  		return -ERANGE;
  
  	if (tb[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS])
  		flags = nla_get_u16(tb[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS]);
  
  	tinfo->tunid = tun_id;
  	tinfo->vid = vid;
  	tinfo->flags = flags;
  
  	return 0;
  }
  
  int br_process_vlan_tunnel_info(struct net_bridge *br,
  				struct net_bridge_port *p, int cmd,
  				struct vtunnel_info *tinfo_curr,
e19b42a1a   Nikolay Aleksandrov   bridge: netlink: ...
248
249
  				struct vtunnel_info *tinfo_last,
  				bool *changed)
efa5356b0   Roopa Prabhu   bridge: per vlan ...
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
  {
  	int err;
  
  	if (tinfo_curr->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) {
  		if (tinfo_last->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN)
  			return -EINVAL;
  		memcpy(tinfo_last, tinfo_curr, sizeof(struct vtunnel_info));
  	} else if (tinfo_curr->flags & BRIDGE_VLAN_INFO_RANGE_END) {
  		int t, v;
  
  		if (!(tinfo_last->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN))
  			return -EINVAL;
  		if ((tinfo_curr->vid - tinfo_last->vid) !=
  		    (tinfo_curr->tunid - tinfo_last->tunid))
  			return -EINVAL;
  		t = tinfo_last->tunid;
  		for (v = tinfo_last->vid; v <= tinfo_curr->vid; v++) {
e19b42a1a   Nikolay Aleksandrov   bridge: netlink: ...
267
  			err = br_vlan_tunnel_info(p, cmd, v, t, changed);
efa5356b0   Roopa Prabhu   bridge: per vlan ...
268
269
270
271
272
273
274
275
276
277
  			if (err)
  				return err;
  			t++;
  		}
  		memset(tinfo_last, 0, sizeof(struct vtunnel_info));
  		memset(tinfo_curr, 0, sizeof(struct vtunnel_info));
  	} else {
  		if (tinfo_last->flags)
  			return -EINVAL;
  		err = br_vlan_tunnel_info(p, cmd, tinfo_curr->vid,
e19b42a1a   Nikolay Aleksandrov   bridge: netlink: ...
278
  					  tinfo_curr->tunid, changed);
efa5356b0   Roopa Prabhu   bridge: per vlan ...
279
280
281
282
283
284
285
286
  		if (err)
  			return err;
  		memset(tinfo_last, 0, sizeof(struct vtunnel_info));
  		memset(tinfo_curr, 0, sizeof(struct vtunnel_info));
  	}
  
  	return 0;
  }