/src/openvswitch/lib/dpif-netlink-rtnl.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2017 Red Hat, Inc. |
3 | | * |
4 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | | * you may not use this file except in compliance with the License. |
6 | | * You may obtain a copy of the License at: |
7 | | * |
8 | | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | | * |
10 | | * Unless required by applicable law or agreed to in writing, software |
11 | | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | | * See the License for the specific language governing permissions and |
14 | | * limitations under the License. |
15 | | */ |
16 | | |
17 | | #include <config.h> |
18 | | |
19 | | #include "dpif-netlink-rtnl.h" |
20 | | |
21 | | #include <net/if.h> |
22 | | #include <linux/ip.h> |
23 | | #include <linux/rtnetlink.h> |
24 | | |
25 | | #include "dpif-netlink.h" |
26 | | #include "netdev-vport.h" |
27 | | #include "netlink-socket.h" |
28 | | #include "openvswitch/vlog.h" |
29 | | |
30 | | VLOG_DEFINE_THIS_MODULE(dpif_netlink_rtnl); |
31 | | |
32 | | /* On some older systems, these enums are not defined. */ |
33 | | #ifndef IFLA_VXLAN_MAX |
34 | | #define IFLA_VXLAN_MAX 0 |
35 | | #endif |
36 | | #if IFLA_VXLAN_MAX < 27 |
37 | 0 | #define IFLA_VXLAN_LEARNING 7 |
38 | 0 | #define IFLA_VXLAN_PORT 15 |
39 | 0 | #define IFLA_VXLAN_UDP_ZERO_CSUM6_RX 20 |
40 | 0 | #define IFLA_VXLAN_GBP 23 |
41 | 0 | #define IFLA_VXLAN_COLLECT_METADATA 25 |
42 | 0 | #define IFLA_VXLAN_GPE 27 |
43 | | #endif |
44 | | |
45 | | #ifndef IFLA_GRE_MAX |
46 | | #define IFLA_GRE_MAX 0 |
47 | | #endif |
48 | | #if IFLA_GRE_MAX < 18 |
49 | 0 | #define IFLA_GRE_COLLECT_METADATA 18 |
50 | | #endif |
51 | | |
52 | | #ifndef IFLA_GENEVE_MAX |
53 | | #define IFLA_GENEVE_MAX 0 |
54 | | #endif |
55 | | #if IFLA_GENEVE_MAX < 10 |
56 | 0 | #define IFLA_GENEVE_PORT 5 |
57 | 0 | #define IFLA_GENEVE_COLLECT_METADATA 6 |
58 | 0 | #define IFLA_GENEVE_UDP_ZERO_CSUM6_RX 10 |
59 | | #endif |
60 | | |
61 | | #ifndef IFLA_BAREUDP_MAX |
62 | | #define IFLA_BAREUDP_MAX 0 |
63 | | #endif |
64 | | #if IFLA_BAREUDP_MAX < 4 |
65 | 0 | #define IFLA_BAREUDP_PORT 1 |
66 | 0 | #define IFLA_BAREUDP_ETHERTYPE 2 |
67 | 0 | #define IFLA_BAREUDP_SRCPORT_MIN 3 |
68 | 0 | #define IFLA_BAREUDP_MULTIPROTO_MODE 4 |
69 | | #endif |
70 | | |
71 | 0 | #define BAREUDP_SRCPORT_MIN 49153 |
72 | | |
73 | | static const struct nl_policy rtlink_policy[] = { |
74 | | [IFLA_LINKINFO] = { .type = NL_A_NESTED }, |
75 | | }; |
76 | | static const struct nl_policy linkinfo_policy[] = { |
77 | | [IFLA_INFO_KIND] = { .type = NL_A_STRING }, |
78 | | [IFLA_INFO_DATA] = { .type = NL_A_NESTED }, |
79 | | }; |
80 | | static const struct nl_policy vxlan_policy[] = { |
81 | | [IFLA_VXLAN_COLLECT_METADATA] = { .type = NL_A_U8 }, |
82 | | [IFLA_VXLAN_LEARNING] = { .type = NL_A_U8 }, |
83 | | [IFLA_VXLAN_UDP_ZERO_CSUM6_RX] = { .type = NL_A_U8 }, |
84 | | [IFLA_VXLAN_PORT] = { .type = NL_A_U16 }, |
85 | | [IFLA_VXLAN_GBP] = { .type = NL_A_FLAG, .optional = true }, |
86 | | [IFLA_VXLAN_GPE] = { .type = NL_A_FLAG, .optional = true }, |
87 | | }; |
88 | | static const struct nl_policy gre_policy[] = { |
89 | | [IFLA_GRE_COLLECT_METADATA] = { .type = NL_A_FLAG }, |
90 | | }; |
91 | | static const struct nl_policy geneve_policy[] = { |
92 | | [IFLA_GENEVE_COLLECT_METADATA] = { .type = NL_A_FLAG }, |
93 | | [IFLA_GENEVE_UDP_ZERO_CSUM6_RX] = { .type = NL_A_U8 }, |
94 | | [IFLA_GENEVE_PORT] = { .type = NL_A_U16 }, |
95 | | }; |
96 | | static const struct nl_policy bareudp_policy[] = { |
97 | | [IFLA_BAREUDP_PORT] = { .type = NL_A_U16 }, |
98 | | [IFLA_BAREUDP_ETHERTYPE] = { .type = NL_A_U16 }, |
99 | | }; |
100 | | |
101 | | static const char * |
102 | | vport_type_to_kind(enum ovs_vport_type type, |
103 | | const struct netdev_tunnel_config *tnl_cfg) |
104 | 0 | { |
105 | 0 | switch (type) { |
106 | 0 | case OVS_VPORT_TYPE_VXLAN: |
107 | 0 | return "vxlan"; |
108 | 0 | case OVS_VPORT_TYPE_GRE: |
109 | 0 | if (tnl_cfg->pt_mode == NETDEV_PT_LEGACY_L3) { |
110 | 0 | return "gre"; |
111 | 0 | } else if (tnl_cfg->pt_mode == NETDEV_PT_LEGACY_L2) { |
112 | 0 | return "gretap"; |
113 | 0 | } else { |
114 | 0 | return NULL; |
115 | 0 | } |
116 | 0 | case OVS_VPORT_TYPE_GENEVE: |
117 | 0 | return "geneve"; |
118 | 0 | case OVS_VPORT_TYPE_ERSPAN: |
119 | 0 | return "erspan"; |
120 | 0 | case OVS_VPORT_TYPE_IP6ERSPAN: |
121 | 0 | return "ip6erspan"; |
122 | 0 | case OVS_VPORT_TYPE_IP6GRE: |
123 | 0 | if (tnl_cfg->pt_mode == NETDEV_PT_LEGACY_L2) { |
124 | 0 | return "ip6gretap"; |
125 | 0 | } else if (tnl_cfg->pt_mode == NETDEV_PT_LEGACY_L3) { |
126 | 0 | return NULL; |
127 | 0 | } else { |
128 | 0 | return NULL; |
129 | 0 | } |
130 | 0 | case OVS_VPORT_TYPE_GTPU: |
131 | 0 | return NULL; |
132 | 0 | case OVS_VPORT_TYPE_SRV6: |
133 | 0 | return "srv6"; |
134 | 0 | case OVS_VPORT_TYPE_BAREUDP: |
135 | 0 | return "bareudp"; |
136 | 0 | case OVS_VPORT_TYPE_NETDEV: |
137 | 0 | case OVS_VPORT_TYPE_INTERNAL: |
138 | 0 | case OVS_VPORT_TYPE_UNSPEC: |
139 | 0 | case __OVS_VPORT_TYPE_MAX: |
140 | 0 | default: |
141 | 0 | break; |
142 | 0 | } |
143 | | |
144 | 0 | return NULL; |
145 | 0 | } |
146 | | |
147 | | static int |
148 | | rtnl_transact(uint32_t type, uint32_t flags, const char *name, |
149 | | struct ofpbuf **reply) |
150 | 0 | { |
151 | 0 | struct ofpbuf request; |
152 | 0 | int err; |
153 | |
|
154 | 0 | ofpbuf_init(&request, 0); |
155 | 0 | nl_msg_put_nlmsghdr(&request, 0, type, flags); |
156 | 0 | ofpbuf_put_zeros(&request, sizeof(struct ifinfomsg)); |
157 | 0 | nl_msg_put_string(&request, IFLA_IFNAME, name); |
158 | |
|
159 | 0 | err = nl_transact(NETLINK_ROUTE, &request, reply); |
160 | 0 | ofpbuf_uninit(&request); |
161 | |
|
162 | 0 | return err; |
163 | 0 | } |
164 | | |
165 | | static int |
166 | | dpif_netlink_rtnl_destroy(const char *name) |
167 | 0 | { |
168 | 0 | return rtnl_transact(RTM_DELLINK, NLM_F_REQUEST | NLM_F_ACK, name, NULL); |
169 | 0 | } |
170 | | |
171 | | static int |
172 | | dpif_netlink_rtnl_getlink(const char *name, struct ofpbuf **reply) |
173 | 0 | { |
174 | 0 | return rtnl_transact(RTM_GETLINK, NLM_F_REQUEST, name, reply); |
175 | 0 | } |
176 | | |
177 | | static int |
178 | | rtnl_policy_parse(const char *kind, struct ofpbuf *reply, |
179 | | const struct nl_policy *policy, |
180 | | struct nlattr *tnl_info[], |
181 | | size_t policy_size) |
182 | 0 | { |
183 | 0 | struct nlattr *linkinfo[ARRAY_SIZE(linkinfo_policy)]; |
184 | 0 | struct nlattr *rtlink[ARRAY_SIZE(rtlink_policy)]; |
185 | 0 | int error = 0; |
186 | |
|
187 | 0 | if (!nl_policy_parse(reply, NLMSG_HDRLEN + sizeof(struct ifinfomsg), |
188 | 0 | rtlink_policy, rtlink, ARRAY_SIZE(rtlink_policy)) |
189 | 0 | || !nl_parse_nested(rtlink[IFLA_LINKINFO], linkinfo_policy, |
190 | 0 | linkinfo, ARRAY_SIZE(linkinfo_policy)) |
191 | 0 | || strcmp(nl_attr_get_string(linkinfo[IFLA_INFO_KIND]), kind) |
192 | 0 | || !nl_parse_nested(linkinfo[IFLA_INFO_DATA], policy, |
193 | 0 | tnl_info, policy_size)) { |
194 | 0 | error = EINVAL; |
195 | 0 | } |
196 | |
|
197 | 0 | return error; |
198 | 0 | } |
199 | | |
200 | | static int |
201 | | dpif_netlink_rtnl_vxlan_verify(const struct netdev_tunnel_config *tnl_cfg, |
202 | | const char *kind, struct ofpbuf *reply) |
203 | 0 | { |
204 | 0 | struct nlattr *vxlan[ARRAY_SIZE(vxlan_policy)]; |
205 | 0 | int err; |
206 | |
|
207 | 0 | err = rtnl_policy_parse(kind, reply, vxlan_policy, vxlan, |
208 | 0 | ARRAY_SIZE(vxlan_policy)); |
209 | 0 | if (!err) { |
210 | 0 | if (0 != nl_attr_get_u8(vxlan[IFLA_VXLAN_LEARNING]) |
211 | 0 | || 1 != nl_attr_get_u8(vxlan[IFLA_VXLAN_COLLECT_METADATA]) |
212 | 0 | || 1 != nl_attr_get_u8(vxlan[IFLA_VXLAN_UDP_ZERO_CSUM6_RX]) |
213 | 0 | || (tnl_cfg->dst_port |
214 | 0 | != nl_attr_get_be16(vxlan[IFLA_VXLAN_PORT])) |
215 | 0 | || (tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GBP) |
216 | 0 | && !nl_attr_get_flag(vxlan[IFLA_VXLAN_GBP])) |
217 | 0 | || (tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GPE) |
218 | 0 | && !nl_attr_get_flag(vxlan[IFLA_VXLAN_GPE]))) { |
219 | 0 | err = EINVAL; |
220 | 0 | } |
221 | 0 | } |
222 | |
|
223 | 0 | return err; |
224 | 0 | } |
225 | | |
226 | | static int |
227 | | dpif_netlink_rtnl_gre_verify(const struct netdev_tunnel_config OVS_UNUSED *tnl, |
228 | | const char *kind, struct ofpbuf *reply) |
229 | 0 | { |
230 | 0 | struct nlattr *gre[ARRAY_SIZE(gre_policy)]; |
231 | 0 | int err; |
232 | |
|
233 | 0 | err = rtnl_policy_parse(kind, reply, gre_policy, gre, |
234 | 0 | ARRAY_SIZE(gre_policy)); |
235 | 0 | if (!err) { |
236 | 0 | if (!nl_attr_get_flag(gre[IFLA_GRE_COLLECT_METADATA])) { |
237 | 0 | err = EINVAL; |
238 | 0 | } |
239 | 0 | } |
240 | |
|
241 | 0 | return err; |
242 | 0 | } |
243 | | |
244 | | static int |
245 | | dpif_netlink_rtnl_geneve_verify(const struct netdev_tunnel_config *tnl_cfg, |
246 | | const char *kind, struct ofpbuf *reply) |
247 | 0 | { |
248 | 0 | struct nlattr *geneve[ARRAY_SIZE(geneve_policy)]; |
249 | 0 | int err; |
250 | |
|
251 | 0 | err = rtnl_policy_parse(kind, reply, geneve_policy, geneve, |
252 | 0 | ARRAY_SIZE(geneve_policy)); |
253 | 0 | if (!err) { |
254 | 0 | if (!nl_attr_get_flag(geneve[IFLA_GENEVE_COLLECT_METADATA]) |
255 | 0 | || 1 != nl_attr_get_u8(geneve[IFLA_GENEVE_UDP_ZERO_CSUM6_RX]) |
256 | 0 | || (tnl_cfg->dst_port |
257 | 0 | != nl_attr_get_be16(geneve[IFLA_GENEVE_PORT]))) { |
258 | 0 | err = EINVAL; |
259 | 0 | } |
260 | 0 | } |
261 | |
|
262 | 0 | return err; |
263 | 0 | } |
264 | | static int |
265 | | dpif_netlink_rtnl_bareudp_verify(const struct netdev_tunnel_config *tnl_cfg, |
266 | | const char *kind, struct ofpbuf *reply) |
267 | 0 | { |
268 | 0 | struct nlattr *bareudp[ARRAY_SIZE(bareudp_policy)]; |
269 | 0 | int err; |
270 | |
|
271 | 0 | err = rtnl_policy_parse(kind, reply, bareudp_policy, bareudp, |
272 | 0 | ARRAY_SIZE(bareudp_policy)); |
273 | 0 | if (!err) { |
274 | 0 | if ((tnl_cfg->dst_port != nl_attr_get_be16(bareudp[IFLA_BAREUDP_PORT])) |
275 | 0 | || (tnl_cfg->payload_ethertype |
276 | 0 | != nl_attr_get_be16(bareudp[IFLA_BAREUDP_ETHERTYPE]))) { |
277 | 0 | err = EINVAL; |
278 | 0 | } |
279 | 0 | } |
280 | 0 | return err; |
281 | 0 | } |
282 | | |
283 | | static int |
284 | | dpif_netlink_rtnl_verify(const struct netdev_tunnel_config *tnl_cfg, |
285 | | enum ovs_vport_type type, const char *name) |
286 | 0 | { |
287 | 0 | struct ofpbuf *reply; |
288 | 0 | const char *kind; |
289 | 0 | int err; |
290 | |
|
291 | 0 | kind = vport_type_to_kind(type, tnl_cfg); |
292 | 0 | if (!kind) { |
293 | 0 | return EOPNOTSUPP; |
294 | 0 | } |
295 | | |
296 | 0 | err = dpif_netlink_rtnl_getlink(name, &reply); |
297 | 0 | if (err) { |
298 | 0 | return err; |
299 | 0 | } |
300 | | |
301 | 0 | switch (type) { |
302 | 0 | case OVS_VPORT_TYPE_VXLAN: |
303 | 0 | err = dpif_netlink_rtnl_vxlan_verify(tnl_cfg, kind, reply); |
304 | 0 | break; |
305 | 0 | case OVS_VPORT_TYPE_GRE: |
306 | 0 | case OVS_VPORT_TYPE_ERSPAN: |
307 | 0 | case OVS_VPORT_TYPE_IP6ERSPAN: |
308 | 0 | case OVS_VPORT_TYPE_IP6GRE: |
309 | 0 | err = dpif_netlink_rtnl_gre_verify(tnl_cfg, kind, reply); |
310 | 0 | break; |
311 | 0 | case OVS_VPORT_TYPE_GENEVE: |
312 | 0 | err = dpif_netlink_rtnl_geneve_verify(tnl_cfg, kind, reply); |
313 | 0 | break; |
314 | 0 | case OVS_VPORT_TYPE_BAREUDP: |
315 | 0 | err = dpif_netlink_rtnl_bareudp_verify(tnl_cfg, kind, reply); |
316 | 0 | break; |
317 | 0 | case OVS_VPORT_TYPE_NETDEV: |
318 | 0 | case OVS_VPORT_TYPE_INTERNAL: |
319 | 0 | case OVS_VPORT_TYPE_GTPU: |
320 | 0 | case OVS_VPORT_TYPE_SRV6: |
321 | 0 | case OVS_VPORT_TYPE_UNSPEC: |
322 | 0 | case __OVS_VPORT_TYPE_MAX: |
323 | 0 | default: |
324 | 0 | OVS_NOT_REACHED(); |
325 | 0 | } |
326 | | |
327 | 0 | ofpbuf_delete(reply); |
328 | 0 | return err; |
329 | 0 | } |
330 | | |
331 | | static int |
332 | | rtnl_set_mtu(const char *name, uint32_t mtu, struct ofpbuf *request) |
333 | 0 | { |
334 | 0 | ofpbuf_clear(request); |
335 | 0 | nl_msg_put_nlmsghdr(request, 0, RTM_SETLINK, |
336 | 0 | NLM_F_REQUEST | NLM_F_ACK); |
337 | 0 | ofpbuf_put_zeros(request, sizeof(struct ifinfomsg)); |
338 | 0 | nl_msg_put_string(request, IFLA_IFNAME, name); |
339 | 0 | nl_msg_put_u32(request, IFLA_MTU, mtu); |
340 | |
|
341 | 0 | return nl_transact(NETLINK_ROUTE, request, NULL); |
342 | 0 | } |
343 | | |
344 | | static int |
345 | | dpif_netlink_rtnl_create(const struct netdev_tunnel_config *tnl_cfg, |
346 | | const char *name, enum ovs_vport_type type, |
347 | | const char *kind, uint32_t flags) |
348 | 0 | { |
349 | 0 | enum { |
350 | | /* For performance, we want to use the largest MTU that the system |
351 | | * supports. Most existing tunnels will accept UINT16_MAX, treating it |
352 | | * as the actual max MTU, but some do not. Thus, we use a slightly |
353 | | * smaller value, that should always be safe yet does not noticeably |
354 | | * reduce performance. */ |
355 | 0 | MAX_MTU = 65000 |
356 | 0 | }; |
357 | |
|
358 | 0 | size_t linkinfo_off, infodata_off; |
359 | 0 | struct ifinfomsg *ifinfo; |
360 | 0 | struct ofpbuf request; |
361 | 0 | int err; |
362 | |
|
363 | 0 | ofpbuf_init(&request, 0); |
364 | 0 | nl_msg_put_nlmsghdr(&request, 0, RTM_NEWLINK, flags); |
365 | 0 | ifinfo = ofpbuf_put_zeros(&request, sizeof(struct ifinfomsg)); |
366 | 0 | ifinfo->ifi_change = ifinfo->ifi_flags = IFF_UP; |
367 | 0 | nl_msg_put_string(&request, IFLA_IFNAME, name); |
368 | 0 | nl_msg_put_u32(&request, IFLA_MTU, MAX_MTU); |
369 | 0 | linkinfo_off = nl_msg_start_nested(&request, IFLA_LINKINFO); |
370 | 0 | nl_msg_put_string(&request, IFLA_INFO_KIND, kind); |
371 | 0 | infodata_off = nl_msg_start_nested(&request, IFLA_INFO_DATA); |
372 | | |
373 | | /* tunnel unique info */ |
374 | 0 | switch (type) { |
375 | 0 | case OVS_VPORT_TYPE_VXLAN: |
376 | 0 | nl_msg_put_u8(&request, IFLA_VXLAN_LEARNING, 0); |
377 | 0 | nl_msg_put_u8(&request, IFLA_VXLAN_COLLECT_METADATA, 1); |
378 | 0 | nl_msg_put_u8(&request, IFLA_VXLAN_UDP_ZERO_CSUM6_RX, 1); |
379 | 0 | if (tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GBP)) { |
380 | 0 | nl_msg_put_flag(&request, IFLA_VXLAN_GBP); |
381 | 0 | } |
382 | 0 | if (tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GPE)) { |
383 | 0 | nl_msg_put_flag(&request, IFLA_VXLAN_GPE); |
384 | 0 | } |
385 | 0 | nl_msg_put_be16(&request, IFLA_VXLAN_PORT, tnl_cfg->dst_port); |
386 | 0 | break; |
387 | 0 | case OVS_VPORT_TYPE_GRE: |
388 | 0 | case OVS_VPORT_TYPE_ERSPAN: |
389 | 0 | case OVS_VPORT_TYPE_IP6ERSPAN: |
390 | 0 | case OVS_VPORT_TYPE_IP6GRE: |
391 | 0 | nl_msg_put_flag(&request, IFLA_GRE_COLLECT_METADATA); |
392 | 0 | break; |
393 | 0 | case OVS_VPORT_TYPE_GENEVE: |
394 | 0 | nl_msg_put_flag(&request, IFLA_GENEVE_COLLECT_METADATA); |
395 | 0 | nl_msg_put_u8(&request, IFLA_GENEVE_UDP_ZERO_CSUM6_RX, 1); |
396 | 0 | nl_msg_put_be16(&request, IFLA_GENEVE_PORT, tnl_cfg->dst_port); |
397 | 0 | break; |
398 | 0 | case OVS_VPORT_TYPE_BAREUDP: |
399 | 0 | nl_msg_put_be16(&request, IFLA_BAREUDP_ETHERTYPE, |
400 | 0 | tnl_cfg->payload_ethertype); |
401 | 0 | nl_msg_put_u16(&request, IFLA_BAREUDP_SRCPORT_MIN, |
402 | 0 | BAREUDP_SRCPORT_MIN); |
403 | 0 | nl_msg_put_be16(&request, IFLA_BAREUDP_PORT, tnl_cfg->dst_port); |
404 | 0 | if (tnl_cfg->exts & (1 << OVS_BAREUDP_EXT_MULTIPROTO_MODE)) { |
405 | 0 | nl_msg_put_flag(&request, IFLA_BAREUDP_MULTIPROTO_MODE); |
406 | 0 | } |
407 | 0 | break; |
408 | 0 | case OVS_VPORT_TYPE_NETDEV: |
409 | 0 | case OVS_VPORT_TYPE_INTERNAL: |
410 | 0 | case OVS_VPORT_TYPE_GTPU: |
411 | 0 | case OVS_VPORT_TYPE_SRV6: |
412 | 0 | case OVS_VPORT_TYPE_UNSPEC: |
413 | 0 | case __OVS_VPORT_TYPE_MAX: |
414 | 0 | default: |
415 | 0 | err = EOPNOTSUPP; |
416 | 0 | goto exit; |
417 | 0 | } |
418 | | |
419 | 0 | nl_msg_end_nested(&request, infodata_off); |
420 | 0 | nl_msg_end_nested(&request, linkinfo_off); |
421 | |
|
422 | 0 | err = nl_transact(NETLINK_ROUTE, &request, NULL); |
423 | 0 | if (!err && (type == OVS_VPORT_TYPE_GRE || |
424 | 0 | type == OVS_VPORT_TYPE_IP6GRE)) { |
425 | | /* Work around a bug in kernel GRE driver, which ignores IFLA_MTU in |
426 | | * RTM_NEWLINK, by setting the MTU again. See |
427 | | * https://bugzilla.redhat.com/show_bug.cgi?id=1488484. |
428 | | * |
429 | | * In case of MAX_MTU exceeds hw max MTU, retry a smaller value. */ |
430 | 0 | int err2 = rtnl_set_mtu(name, MAX_MTU, &request); |
431 | 0 | if (err2) { |
432 | 0 | err2 = rtnl_set_mtu(name, 1450, &request); |
433 | 0 | } |
434 | 0 | if (err2) { |
435 | 0 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); |
436 | |
|
437 | 0 | VLOG_WARN_RL(&rl, "setting MTU of tunnel %s failed (%s)", |
438 | 0 | name, ovs_strerror(err2)); |
439 | 0 | } |
440 | 0 | } |
441 | |
|
442 | 0 | exit: |
443 | 0 | ofpbuf_uninit(&request); |
444 | |
|
445 | 0 | return err; |
446 | 0 | } |
447 | | |
448 | | int |
449 | | dpif_netlink_rtnl_port_create(struct netdev *netdev) |
450 | 0 | { |
451 | 0 | const struct netdev_tunnel_config *tnl_cfg; |
452 | 0 | char namebuf[NETDEV_VPORT_NAME_BUFSIZE]; |
453 | 0 | enum ovs_vport_type type; |
454 | 0 | const char *name; |
455 | 0 | const char *kind; |
456 | 0 | uint32_t flags; |
457 | 0 | int err; |
458 | |
|
459 | 0 | type = netdev_to_ovs_vport_type(netdev_get_type(netdev)); |
460 | 0 | tnl_cfg = netdev_get_tunnel_config(netdev); |
461 | 0 | if (!tnl_cfg) { |
462 | 0 | return EOPNOTSUPP; |
463 | 0 | } |
464 | | |
465 | 0 | kind = vport_type_to_kind(type, tnl_cfg); |
466 | 0 | if (!kind) { |
467 | 0 | return EOPNOTSUPP; |
468 | 0 | } |
469 | | |
470 | 0 | name = netdev_vport_get_dpif_port(netdev, namebuf, sizeof namebuf); |
471 | 0 | flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL; |
472 | |
|
473 | 0 | err = dpif_netlink_rtnl_create(tnl_cfg, name, type, kind, flags); |
474 | | |
475 | | /* If the device exists, validate and/or attempt to recreate it. */ |
476 | 0 | if (err == EEXIST) { |
477 | 0 | err = dpif_netlink_rtnl_verify(tnl_cfg, type, name); |
478 | 0 | if (!err) { |
479 | 0 | return 0; |
480 | 0 | } |
481 | 0 | err = dpif_netlink_rtnl_destroy(name); |
482 | 0 | if (err) { |
483 | 0 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); |
484 | |
|
485 | 0 | VLOG_WARN_RL(&rl, "RTNL device %s exists and cannot be " |
486 | 0 | "deleted: %s", name, ovs_strerror(err)); |
487 | 0 | return err; |
488 | 0 | } |
489 | 0 | err = dpif_netlink_rtnl_create(tnl_cfg, name, type, kind, flags); |
490 | 0 | } |
491 | 0 | if (err) { |
492 | 0 | return err; |
493 | 0 | } |
494 | | |
495 | 0 | err = dpif_netlink_rtnl_verify(tnl_cfg, type, name); |
496 | 0 | if (err) { |
497 | 0 | int err2 = dpif_netlink_rtnl_destroy(name); |
498 | |
|
499 | 0 | if (err2) { |
500 | 0 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); |
501 | |
|
502 | 0 | VLOG_WARN_RL(&rl, "Failed to delete device %s during rtnl port " |
503 | 0 | "creation: %s", name, ovs_strerror(err2)); |
504 | 0 | } |
505 | 0 | } |
506 | |
|
507 | 0 | return err; |
508 | 0 | } |
509 | | |
510 | | int |
511 | | dpif_netlink_rtnl_port_destroy(const char *name, const char *type) |
512 | 0 | { |
513 | 0 | switch (netdev_to_ovs_vport_type(type)) { |
514 | 0 | case OVS_VPORT_TYPE_VXLAN: |
515 | 0 | case OVS_VPORT_TYPE_GRE: |
516 | 0 | case OVS_VPORT_TYPE_GENEVE: |
517 | 0 | case OVS_VPORT_TYPE_ERSPAN: |
518 | 0 | case OVS_VPORT_TYPE_IP6ERSPAN: |
519 | 0 | case OVS_VPORT_TYPE_IP6GRE: |
520 | 0 | case OVS_VPORT_TYPE_SRV6: |
521 | 0 | case OVS_VPORT_TYPE_BAREUDP: |
522 | 0 | return dpif_netlink_rtnl_destroy(name); |
523 | 0 | case OVS_VPORT_TYPE_NETDEV: |
524 | 0 | case OVS_VPORT_TYPE_INTERNAL: |
525 | 0 | case OVS_VPORT_TYPE_GTPU: |
526 | 0 | case OVS_VPORT_TYPE_UNSPEC: |
527 | 0 | case __OVS_VPORT_TYPE_MAX: |
528 | 0 | default: |
529 | 0 | return EOPNOTSUPP; |
530 | 0 | } |
531 | 0 | return 0; |
532 | 0 | } |
533 | | |
534 | | /** |
535 | | * Probe for whether the modules are out-of-tree (openvswitch) or in-tree |
536 | | * (upstream kernel). |
537 | | * |
538 | | * We probe for "ovs_geneve" via rtnetlink. As long as this returns something |
539 | | * other than EOPNOTSUPP we know that the module in use is the out-of-tree one. |
540 | | * This will be used to determine which netlink interface to use when creating |
541 | | * ports; rtnetlink or compat/genetlink. |
542 | | * |
543 | | * See ovs_tunnels_out_of_tree |
544 | | */ |
545 | | bool |
546 | | dpif_netlink_rtnl_probe_oot_tunnels(void) |
547 | 0 | { |
548 | 0 | char namebuf[NETDEV_VPORT_NAME_BUFSIZE]; |
549 | 0 | struct netdev *netdev = NULL; |
550 | 0 | bool out_of_tree = false; |
551 | 0 | const char *name; |
552 | 0 | int error; |
553 | |
|
554 | 0 | error = netdev_open("ovs-system-probe", "geneve", &netdev); |
555 | 0 | if (!error) { |
556 | 0 | struct ofpbuf *reply; |
557 | 0 | const struct netdev_tunnel_config *tnl_cfg; |
558 | |
|
559 | 0 | tnl_cfg = netdev_get_tunnel_config(netdev); |
560 | 0 | if (!tnl_cfg) { |
561 | 0 | netdev_close(netdev); |
562 | 0 | return true; |
563 | 0 | } |
564 | | |
565 | 0 | name = netdev_vport_get_dpif_port(netdev, namebuf, sizeof namebuf); |
566 | | |
567 | | /* The geneve module exists when ovs-vswitchd crashes |
568 | | * and restarts, handle the case here. |
569 | | */ |
570 | 0 | error = dpif_netlink_rtnl_getlink(name, &reply); |
571 | 0 | if (!error) { |
572 | |
|
573 | 0 | struct nlattr *linkinfo[ARRAY_SIZE(linkinfo_policy)]; |
574 | 0 | struct nlattr *rtlink[ARRAY_SIZE(rtlink_policy)]; |
575 | 0 | const char *kind; |
576 | |
|
577 | 0 | if (!nl_policy_parse(reply, |
578 | 0 | NLMSG_HDRLEN + sizeof(struct ifinfomsg), |
579 | 0 | rtlink_policy, rtlink, |
580 | 0 | ARRAY_SIZE(rtlink_policy)) |
581 | 0 | || !nl_parse_nested(rtlink[IFLA_LINKINFO], linkinfo_policy, |
582 | 0 | linkinfo, ARRAY_SIZE(linkinfo_policy))) { |
583 | 0 | VLOG_ABORT("Error fetching Geneve tunnel device %s " |
584 | 0 | "linkinfo", name); |
585 | 0 | } |
586 | | |
587 | 0 | kind = nl_attr_get_string(linkinfo[IFLA_INFO_KIND]); |
588 | |
|
589 | 0 | if (!strcmp(kind, "ovs_geneve")) { |
590 | 0 | out_of_tree = true; |
591 | 0 | } else if (!strcmp(kind, "geneve")) { |
592 | 0 | out_of_tree = false; |
593 | 0 | } else { |
594 | 0 | VLOG_ABORT("Geneve tunnel device %s with kind %s" |
595 | 0 | " not supported", name, kind); |
596 | 0 | } |
597 | | |
598 | 0 | ofpbuf_delete(reply); |
599 | 0 | netdev_close(netdev); |
600 | |
|
601 | 0 | return out_of_tree; |
602 | 0 | } |
603 | | |
604 | 0 | error = dpif_netlink_rtnl_create(tnl_cfg, name, OVS_VPORT_TYPE_GENEVE, |
605 | 0 | "ovs_geneve", |
606 | 0 | (NLM_F_REQUEST | NLM_F_ACK |
607 | 0 | | NLM_F_CREATE)); |
608 | 0 | if (error != EOPNOTSUPP) { |
609 | 0 | if (!error) { |
610 | 0 | dpif_netlink_rtnl_destroy(name); |
611 | 0 | } |
612 | 0 | out_of_tree = true; |
613 | 0 | } |
614 | 0 | netdev_close(netdev); |
615 | 0 | } |
616 | | |
617 | 0 | return out_of_tree; |
618 | 0 | } |