/src/openvswitch/lib/dpif-netlink-rtnl.c
Line | Count | Source |
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_BAREUDP: |
131 | 0 | return "bareudp"; |
132 | 0 | case OVS_VPORT_TYPE_NETDEV: |
133 | 0 | case OVS_VPORT_TYPE_INTERNAL: |
134 | 0 | case OVS_VPORT_TYPE_GTPU: |
135 | 0 | case OVS_VPORT_TYPE_SRV6: |
136 | 0 | case OVS_VPORT_TYPE_UNSPEC: |
137 | 0 | case __OVS_VPORT_TYPE_MAX: |
138 | 0 | default: |
139 | 0 | break; |
140 | 0 | } |
141 | | |
142 | 0 | return NULL; |
143 | 0 | } |
144 | | |
145 | | static int |
146 | | rtnl_transact(uint32_t type, uint32_t flags, const char *name, |
147 | | struct ofpbuf **reply) |
148 | 0 | { |
149 | 0 | struct ofpbuf request; |
150 | 0 | int err; |
151 | |
|
152 | 0 | ofpbuf_init(&request, 0); |
153 | 0 | nl_msg_put_nlmsghdr(&request, 0, type, flags); |
154 | 0 | ofpbuf_put_zeros(&request, sizeof(struct ifinfomsg)); |
155 | 0 | nl_msg_put_string(&request, IFLA_IFNAME, name); |
156 | |
|
157 | 0 | err = nl_transact(NETLINK_ROUTE, &request, reply); |
158 | 0 | ofpbuf_uninit(&request); |
159 | |
|
160 | 0 | return err; |
161 | 0 | } |
162 | | |
163 | | static int |
164 | | dpif_netlink_rtnl_destroy(const char *name) |
165 | 0 | { |
166 | 0 | return rtnl_transact(RTM_DELLINK, NLM_F_REQUEST | NLM_F_ACK, name, NULL); |
167 | 0 | } |
168 | | |
169 | | static int |
170 | | dpif_netlink_rtnl_getlink(const char *name, struct ofpbuf **reply) |
171 | 0 | { |
172 | 0 | return rtnl_transact(RTM_GETLINK, NLM_F_REQUEST, name, reply); |
173 | 0 | } |
174 | | |
175 | | static int |
176 | | rtnl_policy_parse(const char *kind, struct ofpbuf *reply, |
177 | | const struct nl_policy *policy, |
178 | | struct nlattr *tnl_info[], |
179 | | size_t policy_size) |
180 | 0 | { |
181 | 0 | struct nlattr *linkinfo[ARRAY_SIZE(linkinfo_policy)]; |
182 | 0 | struct nlattr *rtlink[ARRAY_SIZE(rtlink_policy)]; |
183 | 0 | int error = 0; |
184 | |
|
185 | 0 | if (!nl_policy_parse(reply, NLMSG_HDRLEN + sizeof(struct ifinfomsg), |
186 | 0 | rtlink_policy, rtlink, ARRAY_SIZE(rtlink_policy)) |
187 | 0 | || !nl_parse_nested(rtlink[IFLA_LINKINFO], linkinfo_policy, |
188 | 0 | linkinfo, ARRAY_SIZE(linkinfo_policy)) |
189 | 0 | || strcmp(nl_attr_get_string(linkinfo[IFLA_INFO_KIND]), kind) |
190 | 0 | || !nl_parse_nested(linkinfo[IFLA_INFO_DATA], policy, |
191 | 0 | tnl_info, policy_size)) { |
192 | 0 | error = EINVAL; |
193 | 0 | } |
194 | |
|
195 | 0 | return error; |
196 | 0 | } |
197 | | |
198 | | static int |
199 | | dpif_netlink_rtnl_vxlan_verify(const struct netdev_tunnel_config *tnl_cfg, |
200 | | const char *kind, struct ofpbuf *reply) |
201 | 0 | { |
202 | 0 | struct nlattr *vxlan[ARRAY_SIZE(vxlan_policy)]; |
203 | 0 | int err; |
204 | |
|
205 | 0 | err = rtnl_policy_parse(kind, reply, vxlan_policy, vxlan, |
206 | 0 | ARRAY_SIZE(vxlan_policy)); |
207 | 0 | if (!err) { |
208 | 0 | if (0 != nl_attr_get_u8(vxlan[IFLA_VXLAN_LEARNING]) |
209 | 0 | || 1 != nl_attr_get_u8(vxlan[IFLA_VXLAN_COLLECT_METADATA]) |
210 | 0 | || 1 != nl_attr_get_u8(vxlan[IFLA_VXLAN_UDP_ZERO_CSUM6_RX]) |
211 | 0 | || (tnl_cfg->dst_port |
212 | 0 | != nl_attr_get_be16(vxlan[IFLA_VXLAN_PORT])) |
213 | 0 | || (tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GBP) |
214 | 0 | && !nl_attr_get_flag(vxlan[IFLA_VXLAN_GBP])) |
215 | 0 | || (tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GPE) |
216 | 0 | && !nl_attr_get_flag(vxlan[IFLA_VXLAN_GPE]))) { |
217 | 0 | err = EINVAL; |
218 | 0 | } |
219 | 0 | } |
220 | |
|
221 | 0 | return err; |
222 | 0 | } |
223 | | |
224 | | static int |
225 | | dpif_netlink_rtnl_gre_verify(const struct netdev_tunnel_config OVS_UNUSED *tnl, |
226 | | const char *kind, struct ofpbuf *reply) |
227 | 0 | { |
228 | 0 | struct nlattr *gre[ARRAY_SIZE(gre_policy)]; |
229 | 0 | int err; |
230 | |
|
231 | 0 | err = rtnl_policy_parse(kind, reply, gre_policy, gre, |
232 | 0 | ARRAY_SIZE(gre_policy)); |
233 | 0 | if (!err) { |
234 | 0 | if (!nl_attr_get_flag(gre[IFLA_GRE_COLLECT_METADATA])) { |
235 | 0 | err = EINVAL; |
236 | 0 | } |
237 | 0 | } |
238 | |
|
239 | 0 | return err; |
240 | 0 | } |
241 | | |
242 | | static int |
243 | | dpif_netlink_rtnl_geneve_verify(const struct netdev_tunnel_config *tnl_cfg, |
244 | | const char *kind, struct ofpbuf *reply) |
245 | 0 | { |
246 | 0 | struct nlattr *geneve[ARRAY_SIZE(geneve_policy)]; |
247 | 0 | int err; |
248 | |
|
249 | 0 | err = rtnl_policy_parse(kind, reply, geneve_policy, geneve, |
250 | 0 | ARRAY_SIZE(geneve_policy)); |
251 | 0 | if (!err) { |
252 | 0 | if (!nl_attr_get_flag(geneve[IFLA_GENEVE_COLLECT_METADATA]) |
253 | 0 | || 1 != nl_attr_get_u8(geneve[IFLA_GENEVE_UDP_ZERO_CSUM6_RX]) |
254 | 0 | || (tnl_cfg->dst_port |
255 | 0 | != nl_attr_get_be16(geneve[IFLA_GENEVE_PORT]))) { |
256 | 0 | err = EINVAL; |
257 | 0 | } |
258 | 0 | } |
259 | |
|
260 | 0 | return err; |
261 | 0 | } |
262 | | static int |
263 | | dpif_netlink_rtnl_bareudp_verify(const struct netdev_tunnel_config *tnl_cfg, |
264 | | const char *kind, struct ofpbuf *reply) |
265 | 0 | { |
266 | 0 | struct nlattr *bareudp[ARRAY_SIZE(bareudp_policy)]; |
267 | 0 | int err; |
268 | |
|
269 | 0 | err = rtnl_policy_parse(kind, reply, bareudp_policy, bareudp, |
270 | 0 | ARRAY_SIZE(bareudp_policy)); |
271 | 0 | if (!err) { |
272 | 0 | if ((tnl_cfg->dst_port != nl_attr_get_be16(bareudp[IFLA_BAREUDP_PORT])) |
273 | 0 | || (tnl_cfg->payload_ethertype |
274 | 0 | != nl_attr_get_be16(bareudp[IFLA_BAREUDP_ETHERTYPE]))) { |
275 | 0 | err = EINVAL; |
276 | 0 | } |
277 | 0 | } |
278 | 0 | return err; |
279 | 0 | } |
280 | | |
281 | | static int |
282 | | dpif_netlink_rtnl_verify(const struct netdev_tunnel_config *tnl_cfg, |
283 | | enum ovs_vport_type type, const char *name) |
284 | 0 | { |
285 | 0 | struct ofpbuf *reply; |
286 | 0 | const char *kind; |
287 | 0 | int err; |
288 | |
|
289 | 0 | kind = vport_type_to_kind(type, tnl_cfg); |
290 | 0 | if (!kind) { |
291 | 0 | return EOPNOTSUPP; |
292 | 0 | } |
293 | | |
294 | 0 | err = dpif_netlink_rtnl_getlink(name, &reply); |
295 | 0 | if (err) { |
296 | 0 | return err; |
297 | 0 | } |
298 | | |
299 | 0 | switch (type) { |
300 | 0 | case OVS_VPORT_TYPE_VXLAN: |
301 | 0 | err = dpif_netlink_rtnl_vxlan_verify(tnl_cfg, kind, reply); |
302 | 0 | break; |
303 | 0 | case OVS_VPORT_TYPE_GRE: |
304 | 0 | case OVS_VPORT_TYPE_ERSPAN: |
305 | 0 | case OVS_VPORT_TYPE_IP6ERSPAN: |
306 | 0 | case OVS_VPORT_TYPE_IP6GRE: |
307 | 0 | err = dpif_netlink_rtnl_gre_verify(tnl_cfg, kind, reply); |
308 | 0 | break; |
309 | 0 | case OVS_VPORT_TYPE_GENEVE: |
310 | 0 | err = dpif_netlink_rtnl_geneve_verify(tnl_cfg, kind, reply); |
311 | 0 | break; |
312 | 0 | case OVS_VPORT_TYPE_BAREUDP: |
313 | 0 | err = dpif_netlink_rtnl_bareudp_verify(tnl_cfg, kind, reply); |
314 | 0 | break; |
315 | 0 | case OVS_VPORT_TYPE_NETDEV: |
316 | 0 | case OVS_VPORT_TYPE_INTERNAL: |
317 | 0 | case OVS_VPORT_TYPE_GTPU: |
318 | 0 | case OVS_VPORT_TYPE_SRV6: |
319 | 0 | case OVS_VPORT_TYPE_UNSPEC: |
320 | 0 | case __OVS_VPORT_TYPE_MAX: |
321 | 0 | default: |
322 | 0 | OVS_NOT_REACHED(); |
323 | 0 | } |
324 | | |
325 | 0 | ofpbuf_delete(reply); |
326 | 0 | return err; |
327 | 0 | } |
328 | | |
329 | | static int |
330 | | rtnl_set_mtu(const char *name, uint32_t mtu, struct ofpbuf *request) |
331 | 0 | { |
332 | 0 | ofpbuf_clear(request); |
333 | 0 | nl_msg_put_nlmsghdr(request, 0, RTM_SETLINK, |
334 | 0 | NLM_F_REQUEST | NLM_F_ACK); |
335 | 0 | ofpbuf_put_zeros(request, sizeof(struct ifinfomsg)); |
336 | 0 | nl_msg_put_string(request, IFLA_IFNAME, name); |
337 | 0 | nl_msg_put_u32(request, IFLA_MTU, mtu); |
338 | |
|
339 | 0 | return nl_transact(NETLINK_ROUTE, request, NULL); |
340 | 0 | } |
341 | | |
342 | | static int |
343 | | dpif_netlink_rtnl_create(const struct netdev_tunnel_config *tnl_cfg, |
344 | | const char *name, enum ovs_vport_type type, |
345 | | const char *kind, uint32_t flags) |
346 | 0 | { |
347 | 0 | enum { |
348 | | /* For performance, we want to use the largest MTU that the system |
349 | | * supports. Most existing tunnels will accept UINT16_MAX, treating it |
350 | | * as the actual max MTU, but some do not. Thus, we use a slightly |
351 | | * smaller value, that should always be safe yet does not noticeably |
352 | | * reduce performance. */ |
353 | 0 | MAX_MTU = 65000 |
354 | 0 | }; |
355 | |
|
356 | 0 | size_t linkinfo_off, infodata_off; |
357 | 0 | struct ifinfomsg *ifinfo; |
358 | 0 | struct ofpbuf request; |
359 | 0 | int err; |
360 | |
|
361 | 0 | ofpbuf_init(&request, 0); |
362 | 0 | nl_msg_put_nlmsghdr(&request, 0, RTM_NEWLINK, flags); |
363 | 0 | ifinfo = ofpbuf_put_zeros(&request, sizeof(struct ifinfomsg)); |
364 | 0 | ifinfo->ifi_change = ifinfo->ifi_flags = IFF_UP; |
365 | 0 | nl_msg_put_string(&request, IFLA_IFNAME, name); |
366 | 0 | nl_msg_put_u32(&request, IFLA_MTU, MAX_MTU); |
367 | 0 | linkinfo_off = nl_msg_start_nested(&request, IFLA_LINKINFO); |
368 | 0 | nl_msg_put_string(&request, IFLA_INFO_KIND, kind); |
369 | 0 | infodata_off = nl_msg_start_nested(&request, IFLA_INFO_DATA); |
370 | | |
371 | | /* tunnel unique info */ |
372 | 0 | switch (type) { |
373 | 0 | case OVS_VPORT_TYPE_VXLAN: |
374 | 0 | nl_msg_put_u8(&request, IFLA_VXLAN_LEARNING, 0); |
375 | 0 | nl_msg_put_u8(&request, IFLA_VXLAN_COLLECT_METADATA, 1); |
376 | 0 | nl_msg_put_u8(&request, IFLA_VXLAN_UDP_ZERO_CSUM6_RX, 1); |
377 | 0 | if (tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GBP)) { |
378 | 0 | nl_msg_put_flag(&request, IFLA_VXLAN_GBP); |
379 | 0 | } |
380 | 0 | if (tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GPE)) { |
381 | 0 | nl_msg_put_flag(&request, IFLA_VXLAN_GPE); |
382 | 0 | } |
383 | 0 | nl_msg_put_be16(&request, IFLA_VXLAN_PORT, tnl_cfg->dst_port); |
384 | 0 | break; |
385 | 0 | case OVS_VPORT_TYPE_GRE: |
386 | 0 | case OVS_VPORT_TYPE_ERSPAN: |
387 | 0 | case OVS_VPORT_TYPE_IP6ERSPAN: |
388 | 0 | case OVS_VPORT_TYPE_IP6GRE: |
389 | 0 | nl_msg_put_flag(&request, IFLA_GRE_COLLECT_METADATA); |
390 | 0 | break; |
391 | 0 | case OVS_VPORT_TYPE_GENEVE: |
392 | 0 | nl_msg_put_flag(&request, IFLA_GENEVE_COLLECT_METADATA); |
393 | 0 | nl_msg_put_u8(&request, IFLA_GENEVE_UDP_ZERO_CSUM6_RX, 1); |
394 | 0 | nl_msg_put_be16(&request, IFLA_GENEVE_PORT, tnl_cfg->dst_port); |
395 | 0 | break; |
396 | 0 | case OVS_VPORT_TYPE_BAREUDP: |
397 | 0 | nl_msg_put_be16(&request, IFLA_BAREUDP_ETHERTYPE, |
398 | 0 | tnl_cfg->payload_ethertype); |
399 | 0 | nl_msg_put_u16(&request, IFLA_BAREUDP_SRCPORT_MIN, |
400 | 0 | BAREUDP_SRCPORT_MIN); |
401 | 0 | nl_msg_put_be16(&request, IFLA_BAREUDP_PORT, tnl_cfg->dst_port); |
402 | 0 | if (tnl_cfg->exts & (1 << OVS_BAREUDP_EXT_MULTIPROTO_MODE)) { |
403 | 0 | nl_msg_put_flag(&request, IFLA_BAREUDP_MULTIPROTO_MODE); |
404 | 0 | } |
405 | 0 | break; |
406 | 0 | case OVS_VPORT_TYPE_NETDEV: |
407 | 0 | case OVS_VPORT_TYPE_INTERNAL: |
408 | 0 | case OVS_VPORT_TYPE_GTPU: |
409 | 0 | case OVS_VPORT_TYPE_SRV6: |
410 | 0 | case OVS_VPORT_TYPE_UNSPEC: |
411 | 0 | case __OVS_VPORT_TYPE_MAX: |
412 | 0 | default: |
413 | 0 | err = EOPNOTSUPP; |
414 | 0 | goto exit; |
415 | 0 | } |
416 | | |
417 | 0 | nl_msg_end_nested(&request, infodata_off); |
418 | 0 | nl_msg_end_nested(&request, linkinfo_off); |
419 | |
|
420 | 0 | err = nl_transact(NETLINK_ROUTE, &request, NULL); |
421 | 0 | if (!err && (type == OVS_VPORT_TYPE_GRE || |
422 | 0 | type == OVS_VPORT_TYPE_IP6GRE)) { |
423 | | /* Work around a bug in kernel GRE driver, which ignores IFLA_MTU in |
424 | | * RTM_NEWLINK, by setting the MTU again. See |
425 | | * https://bugzilla.redhat.com/show_bug.cgi?id=1488484. |
426 | | * |
427 | | * In case of MAX_MTU exceeds hw max MTU, retry a smaller value. */ |
428 | 0 | int err2 = rtnl_set_mtu(name, MAX_MTU, &request); |
429 | 0 | if (err2) { |
430 | 0 | err2 = rtnl_set_mtu(name, 1450, &request); |
431 | 0 | } |
432 | 0 | if (err2) { |
433 | 0 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); |
434 | |
|
435 | 0 | VLOG_WARN_RL(&rl, "setting MTU of tunnel %s failed (%s)", |
436 | 0 | name, ovs_strerror(err2)); |
437 | 0 | } |
438 | 0 | } |
439 | |
|
440 | 0 | exit: |
441 | 0 | ofpbuf_uninit(&request); |
442 | |
|
443 | 0 | return err; |
444 | 0 | } |
445 | | |
446 | | int |
447 | | dpif_netlink_rtnl_tunnel_create(struct netdev *netdev) |
448 | 0 | { |
449 | 0 | const struct netdev_tunnel_config *tnl_cfg; |
450 | 0 | char namebuf[NETDEV_VPORT_NAME_BUFSIZE]; |
451 | 0 | enum ovs_vport_type type; |
452 | 0 | const char *name; |
453 | 0 | const char *kind; |
454 | 0 | uint32_t flags; |
455 | 0 | int err; |
456 | |
|
457 | 0 | type = netdev_to_ovs_vport_type(netdev_get_type(netdev)); |
458 | 0 | tnl_cfg = netdev_get_tunnel_config(netdev); |
459 | 0 | if (!tnl_cfg) { |
460 | 0 | return EOPNOTSUPP; |
461 | 0 | } |
462 | | |
463 | 0 | kind = vport_type_to_kind(type, tnl_cfg); |
464 | 0 | if (!kind) { |
465 | 0 | return EOPNOTSUPP; |
466 | 0 | } |
467 | | |
468 | 0 | name = netdev_vport_get_dpif_port(netdev, namebuf, sizeof namebuf); |
469 | 0 | flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL; |
470 | |
|
471 | 0 | err = dpif_netlink_rtnl_create(tnl_cfg, name, type, kind, flags); |
472 | | |
473 | | /* If the device exists, validate and/or attempt to recreate it. */ |
474 | 0 | if (err == EEXIST) { |
475 | 0 | err = dpif_netlink_rtnl_verify(tnl_cfg, type, name); |
476 | 0 | if (!err) { |
477 | 0 | return 0; |
478 | 0 | } |
479 | 0 | err = dpif_netlink_rtnl_destroy(name); |
480 | 0 | if (err) { |
481 | 0 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); |
482 | |
|
483 | 0 | VLOG_WARN_RL(&rl, "RTNL device %s exists and cannot be " |
484 | 0 | "deleted: %s", name, ovs_strerror(err)); |
485 | 0 | return err; |
486 | 0 | } |
487 | 0 | err = dpif_netlink_rtnl_create(tnl_cfg, name, type, kind, flags); |
488 | 0 | } |
489 | 0 | if (err) { |
490 | 0 | return err; |
491 | 0 | } |
492 | | |
493 | 0 | err = dpif_netlink_rtnl_verify(tnl_cfg, type, name); |
494 | 0 | if (err) { |
495 | 0 | int err2 = dpif_netlink_rtnl_destroy(name); |
496 | |
|
497 | 0 | if (err2) { |
498 | 0 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); |
499 | |
|
500 | 0 | VLOG_WARN_RL(&rl, "Failed to delete device %s during rtnl port " |
501 | 0 | "creation: %s", name, ovs_strerror(err2)); |
502 | 0 | } |
503 | 0 | } |
504 | |
|
505 | 0 | return err; |
506 | 0 | } |
507 | | |
508 | | int |
509 | | dpif_netlink_rtnl_tunnel_destroy(const char *name, const char *type) |
510 | 0 | { |
511 | 0 | switch (netdev_to_ovs_vport_type(type)) { |
512 | 0 | case OVS_VPORT_TYPE_VXLAN: |
513 | 0 | case OVS_VPORT_TYPE_GRE: |
514 | 0 | case OVS_VPORT_TYPE_GENEVE: |
515 | 0 | case OVS_VPORT_TYPE_ERSPAN: |
516 | 0 | case OVS_VPORT_TYPE_IP6ERSPAN: |
517 | 0 | case OVS_VPORT_TYPE_IP6GRE: |
518 | 0 | case OVS_VPORT_TYPE_BAREUDP: |
519 | 0 | return dpif_netlink_rtnl_destroy(name); |
520 | 0 | case OVS_VPORT_TYPE_NETDEV: |
521 | 0 | case OVS_VPORT_TYPE_INTERNAL: |
522 | 0 | case OVS_VPORT_TYPE_GTPU: |
523 | 0 | case OVS_VPORT_TYPE_SRV6: |
524 | 0 | case OVS_VPORT_TYPE_UNSPEC: |
525 | 0 | case __OVS_VPORT_TYPE_MAX: |
526 | 0 | default: |
527 | 0 | return EOPNOTSUPP; |
528 | 0 | } |
529 | 0 | return 0; |
530 | 0 | } |