/src/openvswitch/lib/netdev-vport.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2010, 2011, 2012, 2013, 2014, 2017 Nicira, Inc. |
3 | | * Copyright (c) 2016 Red Hat, Inc. |
4 | | * |
5 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
6 | | * you may not use this file except in compliance with the License. |
7 | | * You may obtain a copy of the License at: |
8 | | * |
9 | | * http://www.apache.org/licenses/LICENSE-2.0 |
10 | | * |
11 | | * Unless required by applicable law or agreed to in writing, software |
12 | | * distributed under the License is distributed on an "AS IS" BASIS, |
13 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
14 | | * See the License for the specific language governing permissions and |
15 | | * limitations under the License. |
16 | | */ |
17 | | |
18 | | #include <config.h> |
19 | | |
20 | | #include "netdev-vport.h" |
21 | | |
22 | | #include <errno.h> |
23 | | #include <fcntl.h> |
24 | | #include <sys/socket.h> |
25 | | #include <net/if.h> |
26 | | #include <sys/types.h> |
27 | | #include <netinet/in.h> |
28 | | #include <netinet/ip6.h> |
29 | | #include <sys/ioctl.h> |
30 | | |
31 | | #include "byte-order.h" |
32 | | #include "daemon.h" |
33 | | #include "dirs.h" |
34 | | #include "dpif.h" |
35 | | #include "netdev.h" |
36 | | #include "netdev-native-tnl.h" |
37 | | #include "netdev-provider.h" |
38 | | #include "netdev-vport-private.h" |
39 | | #include "openvswitch/dynamic-string.h" |
40 | | #include "ovs-atomic.h" |
41 | | #include "ovs-router.h" |
42 | | #include "packets.h" |
43 | | #include "openvswitch/poll-loop.h" |
44 | | #include "route-table.h" |
45 | | #include "simap.h" |
46 | | #include "smap.h" |
47 | | #include "socket-util.h" |
48 | | #include "unaligned.h" |
49 | | #include "unixctl.h" |
50 | | #include "openvswitch/vlog.h" |
51 | | #include "openvswitch/ofp-parse.h" |
52 | | #ifdef __linux__ |
53 | | #include "netdev-linux.h" |
54 | | #endif |
55 | | |
56 | | VLOG_DEFINE_THIS_MODULE(netdev_vport); |
57 | | |
58 | 0 | #define GENEVE_DST_PORT 6081 |
59 | 0 | #define VXLAN_DST_PORT 4789 |
60 | | |
61 | 0 | #define DEFAULT_TTL 64 |
62 | | |
63 | | /* Last read of the route-table's change number. */ |
64 | | static uint64_t rt_change_seqno; |
65 | | |
66 | | static int get_patch_config(const struct netdev *netdev, struct smap *args); |
67 | | static int get_tunnel_config(const struct netdev *, struct smap *args); |
68 | | static bool tunnel_check_status_change__(struct netdev_vport *); |
69 | | static void update_vxlan_global_cfg(struct netdev *, |
70 | | const struct netdev_tunnel_config *, |
71 | | const struct netdev_tunnel_config *); |
72 | | |
73 | | struct vport_class { |
74 | | const char *dpif_port; |
75 | | struct netdev_class netdev_class; |
76 | | struct simap global_cfg_tracker; |
77 | | }; |
78 | | |
79 | | bool |
80 | | netdev_vport_is_vport_class(const struct netdev_class *class) |
81 | 0 | { |
82 | 0 | return is_vport_class(class); |
83 | 0 | } |
84 | | |
85 | | static struct vport_class * |
86 | | vport_class_cast(const struct netdev_class *class) |
87 | 0 | { |
88 | 0 | ovs_assert(is_vport_class(class)); |
89 | 0 | return CONTAINER_OF(class, struct vport_class, netdev_class); |
90 | 0 | } |
91 | | |
92 | | static const struct netdev_tunnel_config * |
93 | | vport_tunnel_config(struct netdev_vport *netdev) |
94 | 0 | { |
95 | 0 | return ovsrcu_get(const struct netdev_tunnel_config *, &netdev->tnl_cfg); |
96 | 0 | } |
97 | | |
98 | | static const struct netdev_tunnel_config * |
99 | | get_netdev_tunnel_config(const struct netdev *netdev) |
100 | 0 | { |
101 | 0 | return vport_tunnel_config(netdev_vport_cast(netdev)); |
102 | 0 | } |
103 | | |
104 | | bool |
105 | | netdev_vport_is_patch(const struct netdev *netdev) |
106 | 0 | { |
107 | 0 | const struct netdev_class *class = netdev_get_class(netdev); |
108 | |
|
109 | 0 | return class->get_config == get_patch_config; |
110 | 0 | } |
111 | | |
112 | | static bool |
113 | | netdev_vport_needs_dst_port(const struct netdev *dev) |
114 | 0 | { |
115 | 0 | const struct netdev_class *class = netdev_get_class(dev); |
116 | 0 | const char *type = netdev_get_type(dev); |
117 | |
|
118 | 0 | return (class->get_config == get_tunnel_config && |
119 | 0 | (!strcmp("geneve", type) || !strcmp("vxlan", type) || |
120 | 0 | !strcmp("gtpu", type) || !strcmp("bareudp",type))); |
121 | 0 | } |
122 | | |
123 | | const char * |
124 | | netdev_vport_class_get_dpif_port(const struct netdev_class *class) |
125 | 0 | { |
126 | 0 | return is_vport_class(class) ? vport_class_cast(class)->dpif_port : NULL; |
127 | 0 | } |
128 | | |
129 | | const char * |
130 | | netdev_vport_get_dpif_port(const struct netdev *netdev, |
131 | | char namebuf[], size_t bufsize) |
132 | 0 | { |
133 | 0 | const struct netdev_class *class = netdev_get_class(netdev); |
134 | 0 | const char *dpif_port = netdev_vport_class_get_dpif_port(class); |
135 | |
|
136 | 0 | if (!dpif_port) { |
137 | 0 | return netdev_get_name(netdev); |
138 | 0 | } |
139 | | |
140 | 0 | if (netdev_vport_needs_dst_port(netdev)) { |
141 | | /* |
142 | | * Note: IFNAMSIZ is 16 bytes long. Implementations should choose |
143 | | * a dpif port name that is short enough to fit including any |
144 | | * port numbers but assert just in case. |
145 | | */ |
146 | 0 | BUILD_ASSERT(NETDEV_VPORT_NAME_BUFSIZE >= IFNAMSIZ); |
147 | 0 | ovs_assert(strlen(dpif_port) + 6 < IFNAMSIZ); |
148 | 0 | snprintf(namebuf, bufsize, "%s_%d", dpif_port, |
149 | 0 | ntohs(netdev_get_tunnel_config(netdev)->dst_port)); |
150 | 0 | return namebuf; |
151 | 0 | } else { |
152 | 0 | return dpif_port; |
153 | 0 | } |
154 | 0 | } |
155 | | |
156 | | /* Whenever the route-table change number is incremented, |
157 | | * netdev_vport_route_changed() should be called to update |
158 | | * the corresponding tunnel interface status. */ |
159 | | static void |
160 | | netdev_vport_route_changed(void) |
161 | 0 | { |
162 | 0 | struct netdev **vports; |
163 | 0 | size_t i, n_vports; |
164 | |
|
165 | 0 | vports = netdev_get_vports(&n_vports); |
166 | 0 | for (i = 0; i < n_vports; i++) { |
167 | 0 | const struct netdev_tunnel_config *tnl_cfg; |
168 | 0 | struct netdev *netdev_ = vports[i]; |
169 | 0 | struct netdev_vport *netdev = netdev_vport_cast(netdev_); |
170 | |
|
171 | 0 | ovs_mutex_lock(&netdev->mutex); |
172 | | /* Finds all tunnel vports. */ |
173 | 0 | tnl_cfg = netdev_get_tunnel_config(netdev_); |
174 | 0 | if (tnl_cfg && ipv6_addr_is_set(&tnl_cfg->ipv6_dst)) { |
175 | 0 | if (tunnel_check_status_change__(netdev)) { |
176 | 0 | netdev_change_seq_changed(netdev_); |
177 | 0 | } |
178 | 0 | } |
179 | 0 | ovs_mutex_unlock(&netdev->mutex); |
180 | |
|
181 | 0 | netdev_close(netdev_); |
182 | 0 | } |
183 | |
|
184 | 0 | free(vports); |
185 | 0 | } |
186 | | |
187 | | static struct netdev * |
188 | | netdev_vport_alloc(void) |
189 | 0 | { |
190 | 0 | struct netdev_vport *netdev = xzalloc(sizeof *netdev); |
191 | 0 | return &netdev->up; |
192 | 0 | } |
193 | | |
194 | | int |
195 | | netdev_vport_construct(struct netdev *netdev_) |
196 | 0 | { |
197 | 0 | const struct netdev_class *class = netdev_get_class(netdev_); |
198 | 0 | const char *dpif_port = netdev_vport_class_get_dpif_port(class); |
199 | 0 | struct netdev_vport *dev = netdev_vport_cast(netdev_); |
200 | 0 | const char *p, *name = netdev_get_name(netdev_); |
201 | 0 | const char *type = netdev_get_type(netdev_); |
202 | 0 | uint16_t port = 0; |
203 | |
|
204 | 0 | ovs_mutex_init(&dev->mutex); |
205 | 0 | atomic_count_init(&dev->gre_seqno, 0); |
206 | 0 | eth_addr_random(&dev->etheraddr); |
207 | |
|
208 | 0 | if (name && dpif_port && (strlen(name) > strlen(dpif_port) + 1) && |
209 | 0 | (!strncmp(name, dpif_port, strlen(dpif_port)))) { |
210 | 0 | p = name + strlen(dpif_port) + 1; |
211 | 0 | port = atoi(p); |
212 | 0 | } |
213 | |
|
214 | 0 | struct netdev_tunnel_config *tnl_cfg = xzalloc(sizeof *tnl_cfg); |
215 | | |
216 | | /* If a destination port for tunnel ports is specified in the netdev |
217 | | * name, use it instead of the default one. Otherwise, use the default |
218 | | * destination port */ |
219 | 0 | if (!strcmp(type, "geneve")) { |
220 | 0 | tnl_cfg->dst_port = port ? htons(port) : htons(GENEVE_DST_PORT); |
221 | 0 | } else if (!strcmp(type, "vxlan")) { |
222 | 0 | tnl_cfg->dst_port = port ? htons(port) : htons(VXLAN_DST_PORT); |
223 | 0 | update_vxlan_global_cfg(netdev_, NULL, tnl_cfg); |
224 | 0 | } else if (!strcmp(type, "gtpu")) { |
225 | 0 | tnl_cfg->dst_port = port ? htons(port) : htons(GTPU_DST_PORT); |
226 | 0 | } else if (!strcmp(type, "bareudp")) { |
227 | 0 | tnl_cfg->dst_port = htons(port); |
228 | 0 | } |
229 | |
|
230 | 0 | tnl_cfg->dont_fragment = true; |
231 | 0 | tnl_cfg->ttl = DEFAULT_TTL; |
232 | |
|
233 | 0 | ovsrcu_set(&dev->tnl_cfg, tnl_cfg); |
234 | |
|
235 | 0 | return 0; |
236 | 0 | } |
237 | | |
238 | | static void |
239 | | netdev_vport_destruct(struct netdev *netdev_) |
240 | 0 | { |
241 | 0 | struct netdev_vport *netdev = netdev_vport_cast(netdev_); |
242 | 0 | const struct netdev_tunnel_config *tnl_cfg = vport_tunnel_config(netdev); |
243 | 0 | const char *type = netdev_get_type(netdev_); |
244 | |
|
245 | 0 | if (!strcmp(type, "vxlan")) { |
246 | 0 | update_vxlan_global_cfg(netdev_, tnl_cfg, NULL); |
247 | 0 | } |
248 | |
|
249 | 0 | ovsrcu_set(&netdev->tnl_cfg, NULL); |
250 | 0 | ovsrcu_postpone(free, CONST_CAST(struct netdev_tunnel_config *, tnl_cfg)); |
251 | 0 | free(netdev->peer); |
252 | 0 | ovs_mutex_destroy(&netdev->mutex); |
253 | 0 | } |
254 | | |
255 | | static void |
256 | | netdev_vport_dealloc(struct netdev *netdev_) |
257 | 0 | { |
258 | 0 | struct netdev_vport *netdev = netdev_vport_cast(netdev_); |
259 | 0 | free(netdev); |
260 | 0 | } |
261 | | |
262 | | static int |
263 | | netdev_vport_set_etheraddr(struct netdev *netdev_, const struct eth_addr mac) |
264 | 0 | { |
265 | 0 | struct netdev_vport *netdev = netdev_vport_cast(netdev_); |
266 | |
|
267 | 0 | ovs_mutex_lock(&netdev->mutex); |
268 | 0 | netdev->etheraddr = mac; |
269 | 0 | ovs_mutex_unlock(&netdev->mutex); |
270 | 0 | netdev_change_seq_changed(netdev_); |
271 | |
|
272 | 0 | return 0; |
273 | 0 | } |
274 | | |
275 | | static int |
276 | | netdev_vport_get_etheraddr(const struct netdev *netdev_, struct eth_addr *mac) |
277 | 0 | { |
278 | 0 | struct netdev_vport *netdev = netdev_vport_cast(netdev_); |
279 | |
|
280 | 0 | ovs_mutex_lock(&netdev->mutex); |
281 | 0 | *mac = netdev->etheraddr; |
282 | 0 | ovs_mutex_unlock(&netdev->mutex); |
283 | |
|
284 | 0 | return 0; |
285 | 0 | } |
286 | | |
287 | | /* Checks if the tunnel status has changed and returns a boolean. |
288 | | * Updates the tunnel status if it has changed. */ |
289 | | static bool |
290 | | tunnel_check_status_change__(struct netdev_vport *netdev) |
291 | | OVS_REQUIRES(netdev->mutex) |
292 | 0 | { |
293 | 0 | const struct netdev_tunnel_config *tnl_cfg = vport_tunnel_config(netdev); |
294 | 0 | const struct in6_addr *route; |
295 | 0 | char iface[IFNAMSIZ]; |
296 | 0 | bool status = false; |
297 | 0 | struct in6_addr gw; |
298 | 0 | uint32_t mark; |
299 | |
|
300 | 0 | iface[0] = '\0'; |
301 | 0 | route = &tnl_cfg->ipv6_dst; |
302 | 0 | mark = tnl_cfg->egress_pkt_mark; |
303 | 0 | if (ovs_router_lookup(mark, route, iface, NULL, &gw)) { |
304 | 0 | struct netdev *egress_netdev; |
305 | |
|
306 | 0 | if (!netdev_open(iface, NULL, &egress_netdev)) { |
307 | 0 | status = netdev_get_carrier(egress_netdev); |
308 | 0 | netdev_close(egress_netdev); |
309 | 0 | } |
310 | 0 | } |
311 | |
|
312 | 0 | if (strcmp(netdev->egress_iface, iface) |
313 | 0 | || netdev->carrier_status != status) { |
314 | 0 | ovs_strlcpy_arrays(netdev->egress_iface, iface); |
315 | 0 | netdev->carrier_status = status; |
316 | |
|
317 | 0 | return true; |
318 | 0 | } |
319 | | |
320 | 0 | return false; |
321 | 0 | } |
322 | | |
323 | | static int |
324 | | tunnel_get_status(const struct netdev *netdev_, struct smap *smap) |
325 | 0 | { |
326 | 0 | struct netdev_vport *netdev = netdev_vport_cast(netdev_); |
327 | |
|
328 | 0 | if (netdev->egress_iface[0]) { |
329 | 0 | smap_add(smap, "tunnel_egress_iface", netdev->egress_iface); |
330 | |
|
331 | 0 | smap_add(smap, "tunnel_egress_iface_carrier", |
332 | 0 | netdev->carrier_status ? "up" : "down"); |
333 | 0 | } |
334 | |
|
335 | 0 | return 0; |
336 | 0 | } |
337 | | |
338 | | static int |
339 | | netdev_vport_update_flags(struct netdev *netdev OVS_UNUSED, |
340 | | enum netdev_flags off, |
341 | | enum netdev_flags on OVS_UNUSED, |
342 | | enum netdev_flags *old_flagsp) |
343 | 0 | { |
344 | 0 | if (off & (NETDEV_UP | NETDEV_PROMISC)) { |
345 | 0 | return EOPNOTSUPP; |
346 | 0 | } |
347 | | |
348 | 0 | *old_flagsp = NETDEV_UP | NETDEV_PROMISC; |
349 | 0 | return 0; |
350 | 0 | } |
351 | | |
352 | | static void |
353 | | netdev_vport_run(const struct netdev_class *netdev_class OVS_UNUSED) |
354 | 0 | { |
355 | 0 | uint64_t seq; |
356 | |
|
357 | 0 | route_table_run(); |
358 | 0 | seq = route_table_get_change_seq(); |
359 | 0 | if (rt_change_seqno != seq) { |
360 | 0 | rt_change_seqno = seq; |
361 | 0 | netdev_vport_route_changed(); |
362 | 0 | } |
363 | 0 | } |
364 | | |
365 | | static void |
366 | | netdev_vport_wait(const struct netdev_class *netdev_class OVS_UNUSED) |
367 | 0 | { |
368 | 0 | uint64_t seq; |
369 | |
|
370 | 0 | route_table_wait(); |
371 | 0 | seq = route_table_get_change_seq(); |
372 | 0 | if (rt_change_seqno != seq) { |
373 | 0 | poll_immediate_wake(); |
374 | 0 | } |
375 | 0 | } |
376 | | |
377 | | /* Code specific to tunnel types. */ |
378 | | |
379 | | static ovs_be64 |
380 | | parse_key(const struct smap *args, const char *name, |
381 | | bool *present, bool *flow) |
382 | 0 | { |
383 | 0 | const char *s; |
384 | |
|
385 | 0 | *present = false; |
386 | 0 | *flow = false; |
387 | |
|
388 | 0 | s = smap_get(args, name); |
389 | 0 | if (!s) { |
390 | 0 | s = smap_get(args, "key"); |
391 | 0 | if (!s) { |
392 | 0 | return 0; |
393 | 0 | } |
394 | 0 | } |
395 | | |
396 | 0 | *present = true; |
397 | |
|
398 | 0 | if (!strcmp(s, "flow")) { |
399 | 0 | *flow = true; |
400 | 0 | return 0; |
401 | 0 | } else { |
402 | 0 | return htonll(strtoull(s, NULL, 0)); |
403 | 0 | } |
404 | 0 | } |
405 | | |
406 | | static int |
407 | | parse_tunnel_ip(const char *value, bool accept_mcast, bool *flow, |
408 | | struct in6_addr *ipv6, uint16_t *protocol) |
409 | 0 | { |
410 | 0 | if (!strcmp(value, "flow")) { |
411 | 0 | *flow = true; |
412 | 0 | *protocol = 0; |
413 | 0 | return 0; |
414 | 0 | } |
415 | 0 | if (addr_is_ipv6(value)) { |
416 | 0 | if (lookup_ipv6(value, ipv6)) { |
417 | 0 | return ENOENT; |
418 | 0 | } |
419 | 0 | if (!accept_mcast && ipv6_addr_is_multicast(ipv6)) { |
420 | 0 | return EINVAL; |
421 | 0 | } |
422 | 0 | *protocol = ETH_TYPE_IPV6; |
423 | 0 | } else { |
424 | 0 | struct in_addr ip; |
425 | 0 | if (lookup_ip(value, &ip)) { |
426 | 0 | return ENOENT; |
427 | 0 | } |
428 | 0 | if (!accept_mcast && ip_is_multicast(ip.s_addr)) { |
429 | 0 | return EINVAL; |
430 | 0 | } |
431 | 0 | in6_addr_set_mapped_ipv4(ipv6, ip.s_addr); |
432 | 0 | *protocol = ETH_TYPE_IP; |
433 | 0 | } |
434 | 0 | return 0; |
435 | 0 | } |
436 | | |
437 | | static int |
438 | | parse_srv6_segs(char *s, struct in6_addr *segs, uint8_t *num_segs) |
439 | 0 | { |
440 | 0 | char *save_ptr = NULL; |
441 | 0 | char *token; |
442 | |
|
443 | 0 | if (!s) { |
444 | 0 | return EINVAL; |
445 | 0 | } |
446 | | |
447 | 0 | *num_segs = 0; |
448 | |
|
449 | 0 | while ((token = strtok_r(s, ",", &save_ptr)) != NULL) { |
450 | 0 | if (*num_segs == SRV6_MAX_SEGS) { |
451 | 0 | return EINVAL; |
452 | 0 | } |
453 | | |
454 | 0 | if (inet_pton(AF_INET6, token, segs) != 1) { |
455 | 0 | return EINVAL; |
456 | 0 | } |
457 | | |
458 | 0 | segs++; |
459 | 0 | (*num_segs)++; |
460 | 0 | s = NULL; |
461 | 0 | } |
462 | | |
463 | 0 | return 0; |
464 | 0 | } |
465 | | |
466 | | enum tunnel_layers { |
467 | | TNL_L2 = 1 << 0, /* 1 if a tunnel type can carry Ethernet traffic. */ |
468 | | TNL_L3 = 1 << 1 /* 1 if a tunnel type can carry L3 traffic. */ |
469 | | }; |
470 | | static enum tunnel_layers |
471 | | tunnel_supported_layers(const char *type, |
472 | | const struct netdev_tunnel_config *tnl_cfg) |
473 | 0 | { |
474 | 0 | if (!strcmp(type, "gre")) { |
475 | 0 | return TNL_L2 | TNL_L3; |
476 | 0 | } else if (!strcmp(type, "vxlan") |
477 | 0 | && tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GPE)) { |
478 | 0 | return TNL_L2 | TNL_L3; |
479 | 0 | } else if (!strcmp(type, "gtpu")) { |
480 | 0 | return TNL_L3; |
481 | 0 | } else if (!strcmp(type, "bareudp")) { |
482 | 0 | return TNL_L3; |
483 | 0 | } else if (!strcmp(type, "srv6")) { |
484 | 0 | return TNL_L3; |
485 | 0 | } else { |
486 | 0 | return TNL_L2; |
487 | 0 | } |
488 | 0 | } |
489 | | static enum netdev_pt_mode |
490 | | default_pt_mode(enum tunnel_layers layers) |
491 | 0 | { |
492 | 0 | return layers == TNL_L3 ? NETDEV_PT_LEGACY_L3 : NETDEV_PT_LEGACY_L2; |
493 | 0 | } |
494 | | |
495 | | static char * |
496 | | vxlan_get_port_ext_gbp_str(uint16_t port, bool gbp, |
497 | | char namebuf[], size_t bufsize) |
498 | 0 | { |
499 | 0 | snprintf(namebuf, bufsize, "dst_port_%d%s", |
500 | 0 | port, gbp ? "_gbp" : ""); |
501 | |
|
502 | 0 | return namebuf; |
503 | 0 | } |
504 | | |
505 | | static void |
506 | | update_vxlan_global_cfg(struct netdev *netdev, |
507 | | const struct netdev_tunnel_config *old_cfg, |
508 | | const struct netdev_tunnel_config *new_cfg) |
509 | 0 | { |
510 | 0 | unsigned int count; |
511 | 0 | char namebuf[20]; |
512 | 0 | const char *type = netdev_get_type(netdev); |
513 | 0 | struct vport_class *vclass = vport_class_cast(netdev_get_class(netdev)); |
514 | |
|
515 | 0 | if (strcmp(type, "vxlan") || |
516 | 0 | (old_cfg != NULL && new_cfg != NULL && |
517 | 0 | old_cfg->dst_port == new_cfg->dst_port && |
518 | 0 | old_cfg->exts == new_cfg->exts)) { |
519 | 0 | return; |
520 | 0 | } |
521 | | |
522 | 0 | if (old_cfg != NULL) { |
523 | 0 | vxlan_get_port_ext_gbp_str(ntohs(old_cfg->dst_port), |
524 | 0 | old_cfg->exts & |
525 | 0 | (1 << OVS_VXLAN_EXT_GBP), |
526 | 0 | namebuf, sizeof(namebuf)); |
527 | |
|
528 | 0 | count = simap_get(&vclass->global_cfg_tracker, namebuf); |
529 | 0 | if (count != 0) { |
530 | 0 | if (--count) { |
531 | 0 | simap_put(&vclass->global_cfg_tracker, namebuf, count); |
532 | 0 | } else { |
533 | 0 | simap_find_and_delete(&vclass->global_cfg_tracker, namebuf); |
534 | 0 | } |
535 | 0 | } |
536 | 0 | } |
537 | |
|
538 | 0 | if (new_cfg != NULL) { |
539 | 0 | vxlan_get_port_ext_gbp_str(ntohs(new_cfg->dst_port), |
540 | 0 | new_cfg->exts & |
541 | 0 | (1 << OVS_VXLAN_EXT_GBP), |
542 | 0 | namebuf, sizeof(namebuf)); |
543 | |
|
544 | 0 | simap_increase(&vclass->global_cfg_tracker, namebuf, 1); |
545 | 0 | } |
546 | 0 | } |
547 | | |
548 | | static bool |
549 | | is_concomitant_vxlan_tunnel_present(struct netdev_vport *dev, |
550 | | const struct netdev_tunnel_config *tnl_cfg) |
551 | 0 | { |
552 | 0 | const struct netdev_tunnel_config *dev_tnl_cfg = vport_tunnel_config(dev); |
553 | 0 | struct vport_class *vclass = vport_class_cast(netdev_get_class(&dev->up)); |
554 | 0 | const char *type = netdev_get_type(&dev->up); |
555 | 0 | char namebuf[20]; |
556 | |
|
557 | 0 | if (strcmp(type, "vxlan")) { |
558 | 0 | return false; |
559 | 0 | } |
560 | | |
561 | 0 | if (dev_tnl_cfg->dst_port == tnl_cfg->dst_port && |
562 | 0 | (dev_tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GBP)) == |
563 | 0 | (tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GBP))) { |
564 | |
|
565 | 0 | if (ntohs(dev_tnl_cfg->dst_port) == VXLAN_DST_PORT) { |
566 | | /* Special case where we kept the default port/gbp, only ok if |
567 | | the opposite of the default does not exits */ |
568 | 0 | vxlan_get_port_ext_gbp_str(ntohs(tnl_cfg->dst_port), |
569 | 0 | !(tnl_cfg->exts & |
570 | 0 | (1 << OVS_VXLAN_EXT_GBP)), |
571 | 0 | namebuf, sizeof(namebuf)); |
572 | |
|
573 | 0 | if (simap_get(&vclass->global_cfg_tracker, namebuf) > 0) { |
574 | 0 | return true; |
575 | 0 | } |
576 | 0 | } |
577 | 0 | return false; |
578 | 0 | } |
579 | | |
580 | | /* Same port: ok if no one is left with the previous configuration */ |
581 | 0 | if (dev_tnl_cfg->dst_port == tnl_cfg->dst_port) { |
582 | 0 | vxlan_get_port_ext_gbp_str(ntohs(dev_tnl_cfg->dst_port), |
583 | 0 | dev_tnl_cfg->exts & |
584 | 0 | (1 << OVS_VXLAN_EXT_GBP), |
585 | 0 | namebuf, sizeof(namebuf)); |
586 | |
|
587 | 0 | if (simap_get(&vclass->global_cfg_tracker, namebuf) > 1) { |
588 | 0 | return true; |
589 | 0 | } |
590 | | |
591 | 0 | return false; |
592 | 0 | } |
593 | | |
594 | | /* Different port: ok if the opposite gbp option does not yet exists */ |
595 | 0 | vxlan_get_port_ext_gbp_str(ntohs(tnl_cfg->dst_port), |
596 | 0 | !(tnl_cfg->exts & |
597 | 0 | (1 << OVS_VXLAN_EXT_GBP)), |
598 | 0 | namebuf, sizeof(namebuf)); |
599 | |
|
600 | 0 | if (simap_get(&vclass->global_cfg_tracker, namebuf) > 0) { |
601 | 0 | return true; |
602 | 0 | } |
603 | | |
604 | 0 | return false; |
605 | 0 | } |
606 | | |
607 | | static int |
608 | | set_tunnel_config(struct netdev *dev_, const struct smap *args, char **errp) |
609 | 0 | { |
610 | 0 | struct netdev_vport *dev = netdev_vport_cast(dev_); |
611 | 0 | const struct netdev_tunnel_config *curr_tnl_cfg; |
612 | 0 | const char *name = netdev_get_name(dev_); |
613 | 0 | const char *type = netdev_get_type(dev_); |
614 | 0 | struct ds errors = DS_EMPTY_INITIALIZER; |
615 | 0 | bool needs_dst_port, has_csum, has_seq; |
616 | 0 | uint16_t dst_proto = 0, src_proto = 0; |
617 | 0 | struct netdev_tunnel_config tnl_cfg; |
618 | 0 | struct smap_node *node; |
619 | 0 | int err; |
620 | |
|
621 | 0 | has_csum = strstr(type, "gre") || strstr(type, "geneve") || |
622 | 0 | strstr(type, "vxlan"); |
623 | 0 | has_seq = strstr(type, "gre"); |
624 | 0 | memset(&tnl_cfg, 0, sizeof tnl_cfg); |
625 | | |
626 | | /* Add a default destination port for tunnel ports if none specified. */ |
627 | 0 | if (!strcmp(type, "geneve")) { |
628 | 0 | tnl_cfg.dst_port = htons(GENEVE_DST_PORT); |
629 | 0 | } |
630 | |
|
631 | 0 | if (!strcmp(type, "vxlan")) { |
632 | 0 | tnl_cfg.dst_port = htons(VXLAN_DST_PORT); |
633 | 0 | } |
634 | |
|
635 | 0 | if (!strcmp(type, "gtpu")) { |
636 | 0 | tnl_cfg.dst_port = htons(GTPU_DST_PORT); |
637 | 0 | } |
638 | |
|
639 | 0 | needs_dst_port = netdev_vport_needs_dst_port(dev_); |
640 | 0 | tnl_cfg.dont_fragment = true; |
641 | |
|
642 | 0 | SMAP_FOR_EACH (node, args) { |
643 | 0 | if (!strcmp(node->key, "remote_ip")) { |
644 | 0 | err = parse_tunnel_ip(node->value, false, &tnl_cfg.ip_dst_flow, |
645 | 0 | &tnl_cfg.ipv6_dst, &dst_proto); |
646 | 0 | switch (err) { |
647 | 0 | case ENOENT: |
648 | 0 | ds_put_format(&errors, "%s: bad %s 'remote_ip'\n", name, type); |
649 | 0 | break; |
650 | 0 | case EINVAL: |
651 | 0 | ds_put_format(&errors, |
652 | 0 | "%s: multicast remote_ip=%s not allowed\n", |
653 | 0 | name, node->value); |
654 | 0 | goto out; |
655 | 0 | } |
656 | 0 | } else if (!strcmp(node->key, "local_ip")) { |
657 | 0 | err = parse_tunnel_ip(node->value, true, &tnl_cfg.ip_src_flow, |
658 | 0 | &tnl_cfg.ipv6_src, &src_proto); |
659 | 0 | switch (err) { |
660 | 0 | case ENOENT: |
661 | 0 | ds_put_format(&errors, "%s: bad %s 'local_ip'\n", name, type); |
662 | 0 | break; |
663 | 0 | } |
664 | 0 | } else if (!strcmp(node->key, "tos")) { |
665 | 0 | if (!strcmp(node->value, "inherit")) { |
666 | 0 | tnl_cfg.tos_inherit = true; |
667 | 0 | } else { |
668 | 0 | char *endptr; |
669 | 0 | int tos; |
670 | 0 | tos = strtol(node->value, &endptr, 0); |
671 | 0 | if (*endptr == '\0' && tos == (tos & IP_DSCP_MASK)) { |
672 | 0 | tnl_cfg.tos = tos; |
673 | 0 | } else { |
674 | 0 | ds_put_format(&errors, "%s: invalid TOS %s\n", name, |
675 | 0 | node->value); |
676 | 0 | } |
677 | 0 | } |
678 | 0 | } else if (!strcmp(node->key, "ttl")) { |
679 | 0 | if (!strcmp(node->value, "inherit")) { |
680 | 0 | tnl_cfg.ttl_inherit = true; |
681 | 0 | } else { |
682 | 0 | tnl_cfg.ttl = atoi(node->value); |
683 | 0 | } |
684 | 0 | } else if (!strcmp(node->key, "dst_port") && needs_dst_port) { |
685 | 0 | tnl_cfg.dst_port = htons(atoi(node->value)); |
686 | 0 | } else if (!strcmp(node->key, "csum") && has_csum) { |
687 | 0 | if (!strcmp(node->value, "true")) { |
688 | 0 | tnl_cfg.csum = NETDEV_TNL_CSUM_ENABLED; |
689 | 0 | } else if (!strcmp(node->value, "false")) { |
690 | 0 | tnl_cfg.csum = NETDEV_TNL_CSUM_DISABLED; |
691 | 0 | } |
692 | 0 | } else if (!strcmp(node->key, "seq") && has_seq) { |
693 | 0 | if (!strcmp(node->value, "true")) { |
694 | 0 | tnl_cfg.set_seq = true; |
695 | 0 | } |
696 | 0 | } else if (!strcmp(node->key, "df_default")) { |
697 | 0 | if (!strcmp(node->value, "false")) { |
698 | 0 | tnl_cfg.dont_fragment = false; |
699 | 0 | } |
700 | 0 | } else if (!strcmp(node->key, "key") || |
701 | 0 | !strcmp(node->key, "in_key") || |
702 | 0 | !strcmp(node->key, "out_key") || |
703 | 0 | !strcmp(node->key, "packet_type")) { |
704 | | /* Handled separately below. */ |
705 | 0 | } else if (!strcmp(node->key, "exts") && !strcmp(type, "vxlan")) { |
706 | 0 | char *str = xstrdup(node->value); |
707 | 0 | char *ext, *save_ptr = NULL; |
708 | |
|
709 | 0 | tnl_cfg.exts = 0; |
710 | |
|
711 | 0 | ext = strtok_r(str, ",", &save_ptr); |
712 | 0 | while (ext) { |
713 | 0 | if (!strcmp(type, "vxlan") && !strcmp(ext, "gbp")) { |
714 | 0 | tnl_cfg.exts |= (1 << OVS_VXLAN_EXT_GBP); |
715 | 0 | } else if (!strcmp(type, "vxlan") && !strcmp(ext, "gpe")) { |
716 | 0 | tnl_cfg.exts |= (1 << OVS_VXLAN_EXT_GPE); |
717 | 0 | } else { |
718 | 0 | ds_put_format(&errors, "%s: unknown extension '%s'\n", |
719 | 0 | name, ext); |
720 | 0 | } |
721 | |
|
722 | 0 | ext = strtok_r(NULL, ",", &save_ptr); |
723 | 0 | } |
724 | |
|
725 | 0 | free(str); |
726 | 0 | } else if (!strcmp(node->key, "egress_pkt_mark")) { |
727 | 0 | tnl_cfg.egress_pkt_mark = strtoul(node->value, NULL, 10); |
728 | 0 | tnl_cfg.set_egress_pkt_mark = true; |
729 | 0 | } else if (!strcmp(node->key, "erspan_idx")) { |
730 | 0 | if (!strcmp(node->value, "flow")) { |
731 | 0 | tnl_cfg.erspan_idx_flow = true; |
732 | 0 | } else { |
733 | 0 | tnl_cfg.erspan_idx_flow = false; |
734 | 0 | tnl_cfg.erspan_idx = strtol(node->value, NULL, 16); |
735 | |
|
736 | 0 | if (tnl_cfg.erspan_idx & ~ERSPAN_IDX_MASK) { |
737 | 0 | ds_put_format(&errors, "%s: invalid erspan index: %s\n", |
738 | 0 | name, node->value); |
739 | 0 | err = EINVAL; |
740 | 0 | goto out; |
741 | 0 | } |
742 | 0 | } |
743 | 0 | } else if (!strcmp(node->key, "erspan_ver")) { |
744 | 0 | if (!strcmp(node->value, "flow")) { |
745 | 0 | tnl_cfg.erspan_ver_flow = true; |
746 | 0 | tnl_cfg.erspan_idx_flow = true; |
747 | 0 | tnl_cfg.erspan_dir_flow = true; |
748 | 0 | tnl_cfg.erspan_hwid_flow = true; |
749 | 0 | } else { |
750 | 0 | tnl_cfg.erspan_ver_flow = false; |
751 | 0 | tnl_cfg.erspan_ver = atoi(node->value); |
752 | |
|
753 | 0 | if (tnl_cfg.erspan_ver != 1 && tnl_cfg.erspan_ver != 2) { |
754 | 0 | ds_put_format(&errors, "%s: invalid erspan version: %s\n", |
755 | 0 | name, node->value); |
756 | 0 | err = EINVAL; |
757 | 0 | goto out; |
758 | 0 | } |
759 | 0 | } |
760 | 0 | } else if (!strcmp(node->key, "erspan_dir")) { |
761 | 0 | if (!strcmp(node->value, "flow")) { |
762 | 0 | tnl_cfg.erspan_dir_flow = true; |
763 | 0 | } else { |
764 | 0 | tnl_cfg.erspan_dir_flow = false; |
765 | 0 | tnl_cfg.erspan_dir = atoi(node->value); |
766 | |
|
767 | 0 | if (tnl_cfg.erspan_dir != 0 && tnl_cfg.erspan_dir != 1) { |
768 | 0 | ds_put_format(&errors, "%s: invalid erspan direction: %s\n", |
769 | 0 | name, node->value); |
770 | 0 | err = EINVAL; |
771 | 0 | goto out; |
772 | 0 | } |
773 | 0 | } |
774 | 0 | } else if (!strcmp(node->key, "erspan_hwid")) { |
775 | 0 | if (!strcmp(node->value, "flow")) { |
776 | 0 | tnl_cfg.erspan_hwid_flow = true; |
777 | 0 | } else { |
778 | 0 | tnl_cfg.erspan_hwid_flow = false; |
779 | 0 | tnl_cfg.erspan_hwid = strtol(node->value, NULL, 16); |
780 | |
|
781 | 0 | if (tnl_cfg.erspan_hwid & ~(ERSPAN_HWID_MASK >> 4)) { |
782 | 0 | ds_put_format(&errors, "%s: invalid erspan hardware ID: %s\n", |
783 | 0 | name, node->value); |
784 | 0 | err = EINVAL; |
785 | 0 | goto out; |
786 | 0 | } |
787 | 0 | } |
788 | 0 | } else if (!strcmp(node->key, "srv6_segs")) { |
789 | 0 | err = parse_srv6_segs(node->value, |
790 | 0 | tnl_cfg.srv6_segs, |
791 | 0 | &tnl_cfg.srv6_num_segs); |
792 | |
|
793 | 0 | switch (err) { |
794 | 0 | case EINVAL: |
795 | 0 | ds_put_format(&errors, "%s: bad %s 'srv6_segs'\n", |
796 | 0 | name, node->value); |
797 | 0 | break; |
798 | 0 | } |
799 | 0 | } else if (!strcmp(node->key, "srv6_flowlabel")) { |
800 | 0 | if (!strcmp(node->value, "zero")) { |
801 | 0 | tnl_cfg.srv6_flowlabel = SRV6_FLOWLABEL_ZERO; |
802 | 0 | } else if (!strcmp(node->value, "compute")) { |
803 | 0 | tnl_cfg.srv6_flowlabel = SRV6_FLOWLABEL_COMPUTE; |
804 | 0 | } else { |
805 | 0 | tnl_cfg.srv6_flowlabel = SRV6_FLOWLABEL_COPY; |
806 | 0 | } |
807 | 0 | } else if (!strcmp(node->key, "payload_type")) { |
808 | 0 | if (!strcmp(node->value, "mpls")) { |
809 | 0 | tnl_cfg.payload_ethertype = htons(ETH_TYPE_MPLS); |
810 | 0 | tnl_cfg.exts |= (1 << OVS_BAREUDP_EXT_MULTIPROTO_MODE); |
811 | 0 | } else if (!strcmp(node->value, "ip")) { |
812 | 0 | tnl_cfg.payload_ethertype = htons(ETH_TYPE_IP); |
813 | 0 | tnl_cfg.exts |= (1 << OVS_BAREUDP_EXT_MULTIPROTO_MODE); |
814 | 0 | } else { |
815 | 0 | uint16_t payload_ethertype; |
816 | |
|
817 | 0 | if (str_to_u16(node->value, "payload_type", |
818 | 0 | &payload_ethertype)) { |
819 | 0 | err = EINVAL; |
820 | 0 | goto out; |
821 | 0 | } |
822 | 0 | tnl_cfg.payload_ethertype = htons(payload_ethertype); |
823 | 0 | } |
824 | 0 | } else if (!strcmp(node->key, "remote_cert") || |
825 | 0 | !strcmp(node->key, "remote_name") || |
826 | 0 | !strcmp(node->key, "psk") || |
827 | 0 | !strncmp(node->key, "ipsec_", strlen("ipsec_"))) { |
828 | | /* When configuring OVS for IPsec, these keys may be set in the |
829 | | tunnel port's 'options' column. 'ovs-vswitchd' does not directly |
830 | | use them, but they are read by 'ovs-monitor-ipsec'. In order to |
831 | | suppress the "unknown %s argument" warning message below, we |
832 | | handle them here by ignoring them. */ |
833 | 0 | } else { |
834 | 0 | ds_put_format(&errors, "%s: unknown %s argument '%s'\n", name, |
835 | 0 | type, node->key); |
836 | 0 | } |
837 | 0 | } |
838 | | |
839 | | /* The default csum state for GRE is special as it does have an optional |
840 | | * checksum but the default configuration isn't correlated with IP version |
841 | | * like UDP tunnels are. Likewise, tunnels with no checksum at all must be |
842 | | * in this state. */ |
843 | 0 | if (tnl_cfg.csum == NETDEV_TNL_CSUM_DEFAULT && |
844 | 0 | (!has_csum || strstr(type, "gre"))) { |
845 | 0 | tnl_cfg.csum = NETDEV_TNL_DEFAULT_NO_CSUM; |
846 | 0 | } |
847 | |
|
848 | 0 | enum tunnel_layers layers = tunnel_supported_layers(type, &tnl_cfg); |
849 | 0 | const char *full_type = (strcmp(type, "vxlan") ? type |
850 | 0 | : (tnl_cfg.exts & (1 << OVS_VXLAN_EXT_GPE) |
851 | 0 | ? "VXLAN-GPE" : "VXLAN (without GPE)")); |
852 | 0 | const char *packet_type = smap_get(args, "packet_type"); |
853 | 0 | if (!packet_type) { |
854 | 0 | tnl_cfg.pt_mode = default_pt_mode(layers); |
855 | 0 | } else if (!strcmp(packet_type, "legacy_l2")) { |
856 | 0 | tnl_cfg.pt_mode = NETDEV_PT_LEGACY_L2; |
857 | 0 | if (!(layers & TNL_L2)) { |
858 | 0 | ds_put_format(&errors, "%s: legacy_l2 configured on %s tunnel " |
859 | 0 | "that cannot carry L2 traffic\n", |
860 | 0 | name, full_type); |
861 | 0 | err = EINVAL; |
862 | 0 | goto out; |
863 | 0 | } |
864 | 0 | } else if (!strcmp(packet_type, "legacy_l3")) { |
865 | 0 | tnl_cfg.pt_mode = NETDEV_PT_LEGACY_L3; |
866 | 0 | if (!(layers & TNL_L3)) { |
867 | 0 | ds_put_format(&errors, "%s: legacy_l3 configured on %s tunnel " |
868 | 0 | "that cannot carry L3 traffic\n", |
869 | 0 | name, full_type); |
870 | 0 | err = EINVAL; |
871 | 0 | goto out; |
872 | 0 | } |
873 | 0 | } else if (!strcmp(packet_type, "ptap")) { |
874 | 0 | tnl_cfg.pt_mode = NETDEV_PT_AWARE; |
875 | 0 | } else { |
876 | 0 | ds_put_format(&errors, "%s: unknown packet_type '%s'\n", |
877 | 0 | name, packet_type); |
878 | 0 | err = EINVAL; |
879 | 0 | goto out; |
880 | 0 | } |
881 | | |
882 | 0 | if (!ipv6_addr_is_set(&tnl_cfg.ipv6_dst) && !tnl_cfg.ip_dst_flow) { |
883 | 0 | ds_put_format(&errors, |
884 | 0 | "%s: %s type requires valid 'remote_ip' argument\n", |
885 | 0 | name, type); |
886 | 0 | err = EINVAL; |
887 | 0 | goto out; |
888 | 0 | } |
889 | 0 | if (tnl_cfg.ip_src_flow && !tnl_cfg.ip_dst_flow) { |
890 | 0 | ds_put_format(&errors, |
891 | 0 | "%s: %s type requires 'remote_ip=flow' " |
892 | 0 | "with 'local_ip=flow'\n", |
893 | 0 | name, type); |
894 | 0 | err = EINVAL; |
895 | 0 | goto out; |
896 | 0 | } |
897 | 0 | if (src_proto && dst_proto && src_proto != dst_proto) { |
898 | 0 | ds_put_format(&errors, |
899 | 0 | "%s: 'remote_ip' and 'local_ip' " |
900 | 0 | "has to be of the same address family\n", |
901 | 0 | name); |
902 | 0 | err = EINVAL; |
903 | 0 | goto out; |
904 | 0 | } |
905 | 0 | if (!tnl_cfg.ttl) { |
906 | 0 | tnl_cfg.ttl = DEFAULT_TTL; |
907 | 0 | } |
908 | |
|
909 | 0 | tnl_cfg.in_key = parse_key(args, "in_key", |
910 | 0 | &tnl_cfg.in_key_present, |
911 | 0 | &tnl_cfg.in_key_flow); |
912 | |
|
913 | 0 | tnl_cfg.out_key = parse_key(args, "out_key", |
914 | 0 | &tnl_cfg.out_key_present, |
915 | 0 | &tnl_cfg.out_key_flow); |
916 | |
|
917 | 0 | if (is_concomitant_vxlan_tunnel_present(dev, &tnl_cfg)) { |
918 | 0 | ds_put_format(&errors, "%s: VXLAN-GBP, and non-VXLAN-GBP " |
919 | 0 | "tunnels can't be configured on the same " |
920 | 0 | "dst_port\n", |
921 | 0 | name); |
922 | 0 | err = EEXIST; |
923 | 0 | goto out; |
924 | 0 | } |
925 | | |
926 | 0 | ovs_mutex_lock(&dev->mutex); |
927 | |
|
928 | 0 | curr_tnl_cfg = vport_tunnel_config(dev); |
929 | 0 | update_vxlan_global_cfg(dev_, curr_tnl_cfg, &tnl_cfg); |
930 | |
|
931 | 0 | if (memcmp(curr_tnl_cfg, &tnl_cfg, sizeof tnl_cfg)) { |
932 | 0 | ovsrcu_set(&dev->tnl_cfg, xmemdup(&tnl_cfg, sizeof tnl_cfg)); |
933 | 0 | ovsrcu_postpone(free, CONST_CAST(struct netdev_tunnel_config *, |
934 | 0 | curr_tnl_cfg)); |
935 | 0 | tunnel_check_status_change__(dev); |
936 | 0 | netdev_change_seq_changed(dev_); |
937 | 0 | } |
938 | 0 | ovs_mutex_unlock(&dev->mutex); |
939 | |
|
940 | 0 | err = 0; |
941 | |
|
942 | 0 | out: |
943 | 0 | if (errors.length) { |
944 | 0 | ds_chomp(&errors, '\n'); |
945 | 0 | VLOG_WARN("%s", ds_cstr(&errors)); |
946 | 0 | if (err) { |
947 | 0 | *errp = ds_steal_cstr(&errors); |
948 | 0 | } |
949 | 0 | } |
950 | |
|
951 | 0 | ds_destroy(&errors); |
952 | |
|
953 | 0 | return err; |
954 | 0 | } |
955 | | |
956 | | static int |
957 | | get_tunnel_config(const struct netdev *dev, struct smap *args) |
958 | 0 | { |
959 | 0 | const struct netdev_tunnel_config *tnl_cfg = netdev_get_tunnel_config(dev); |
960 | 0 | const char *type = netdev_get_type(dev); |
961 | |
|
962 | 0 | if (!tnl_cfg) { |
963 | 0 | return 0; |
964 | 0 | } |
965 | | |
966 | 0 | if (ipv6_addr_is_set(&tnl_cfg->ipv6_dst)) { |
967 | 0 | smap_add_ipv6(args, "remote_ip", &tnl_cfg->ipv6_dst); |
968 | 0 | } else if (tnl_cfg->ip_dst_flow) { |
969 | 0 | smap_add(args, "remote_ip", "flow"); |
970 | 0 | } |
971 | |
|
972 | 0 | if (ipv6_addr_is_set(&tnl_cfg->ipv6_src)) { |
973 | 0 | smap_add_ipv6(args, "local_ip", &tnl_cfg->ipv6_src); |
974 | 0 | } else if (tnl_cfg->ip_src_flow) { |
975 | 0 | smap_add(args, "local_ip", "flow"); |
976 | 0 | } |
977 | |
|
978 | 0 | if (tnl_cfg->in_key_flow && tnl_cfg->out_key_flow) { |
979 | 0 | smap_add(args, "key", "flow"); |
980 | 0 | } else if (tnl_cfg->in_key_present && tnl_cfg->out_key_present |
981 | 0 | && tnl_cfg->in_key == tnl_cfg->out_key) { |
982 | 0 | smap_add_format(args, "key", "%"PRIu64, ntohll(tnl_cfg->in_key)); |
983 | 0 | } else { |
984 | 0 | if (tnl_cfg->in_key_flow) { |
985 | 0 | smap_add(args, "in_key", "flow"); |
986 | 0 | } else if (tnl_cfg->in_key_present) { |
987 | 0 | smap_add_format(args, "in_key", "%"PRIu64, |
988 | 0 | ntohll(tnl_cfg->in_key)); |
989 | 0 | } |
990 | |
|
991 | 0 | if (tnl_cfg->out_key_flow) { |
992 | 0 | smap_add(args, "out_key", "flow"); |
993 | 0 | } else if (tnl_cfg->out_key_present) { |
994 | 0 | smap_add_format(args, "out_key", "%"PRIu64, |
995 | 0 | ntohll(tnl_cfg->out_key)); |
996 | 0 | } |
997 | 0 | } |
998 | |
|
999 | 0 | if (tnl_cfg->ttl_inherit) { |
1000 | 0 | smap_add(args, "ttl", "inherit"); |
1001 | 0 | } else if (tnl_cfg->ttl != DEFAULT_TTL) { |
1002 | 0 | smap_add_format(args, "ttl", "%"PRIu8, tnl_cfg->ttl); |
1003 | 0 | } |
1004 | |
|
1005 | 0 | if (tnl_cfg->tos_inherit) { |
1006 | 0 | smap_add(args, "tos", "inherit"); |
1007 | 0 | } else if (tnl_cfg->tos) { |
1008 | 0 | smap_add_format(args, "tos", "0x%x", tnl_cfg->tos); |
1009 | 0 | } |
1010 | |
|
1011 | 0 | if (tnl_cfg->dst_port) { |
1012 | 0 | uint16_t dst_port = ntohs(tnl_cfg->dst_port); |
1013 | |
|
1014 | 0 | if ((!strcmp("geneve", type) && dst_port != GENEVE_DST_PORT) || |
1015 | 0 | (!strcmp("vxlan", type) && dst_port != VXLAN_DST_PORT) || |
1016 | 0 | (!strcmp("gtpu", type) && dst_port != GTPU_DST_PORT) || |
1017 | 0 | !strcmp("bareudp", type)) { |
1018 | 0 | smap_add_format(args, "dst_port", "%d", dst_port); |
1019 | 0 | } |
1020 | 0 | } |
1021 | |
|
1022 | 0 | if (tnl_cfg->csum == NETDEV_TNL_CSUM_ENABLED) { |
1023 | 0 | smap_add(args, "csum", "true"); |
1024 | 0 | } else if (tnl_cfg->csum == NETDEV_TNL_CSUM_DISABLED) { |
1025 | 0 | smap_add(args, "csum", "false"); |
1026 | 0 | } |
1027 | |
|
1028 | 0 | if (tnl_cfg->set_seq) { |
1029 | 0 | smap_add(args, "seq", "true"); |
1030 | 0 | } |
1031 | |
|
1032 | 0 | enum tunnel_layers layers = tunnel_supported_layers(type, tnl_cfg); |
1033 | 0 | if (tnl_cfg->pt_mode != default_pt_mode(layers)) { |
1034 | 0 | smap_add(args, "packet_type", |
1035 | 0 | tnl_cfg->pt_mode == NETDEV_PT_LEGACY_L2 ? "legacy_l2" |
1036 | 0 | : tnl_cfg->pt_mode == NETDEV_PT_LEGACY_L3 ? "legacy_l3" |
1037 | 0 | : "ptap"); |
1038 | 0 | } |
1039 | |
|
1040 | 0 | if (!tnl_cfg->dont_fragment) { |
1041 | 0 | smap_add(args, "df_default", "false"); |
1042 | 0 | } |
1043 | |
|
1044 | 0 | if (tnl_cfg->set_egress_pkt_mark) { |
1045 | 0 | smap_add_format(args, "egress_pkt_mark", |
1046 | 0 | "%"PRIu32, tnl_cfg->egress_pkt_mark); |
1047 | 0 | } |
1048 | |
|
1049 | 0 | if (!strcmp("erspan", type) || !strcmp("ip6erspan", type)) { |
1050 | 0 | if (tnl_cfg->erspan_ver_flow) { |
1051 | | /* since version number is not determined, |
1052 | | * assume print all other as flow |
1053 | | */ |
1054 | 0 | smap_add(args, "erspan_ver", "flow"); |
1055 | 0 | smap_add(args, "erspan_idx", "flow"); |
1056 | 0 | smap_add(args, "erspan_dir", "flow"); |
1057 | 0 | smap_add(args, "erspan_hwid", "flow"); |
1058 | 0 | } else { |
1059 | 0 | smap_add_format(args, "erspan_ver", "%d", tnl_cfg->erspan_ver); |
1060 | |
|
1061 | 0 | if (tnl_cfg->erspan_ver == 1) { |
1062 | 0 | if (tnl_cfg->erspan_idx_flow) { |
1063 | 0 | smap_add(args, "erspan_idx", "flow"); |
1064 | 0 | } else { |
1065 | 0 | smap_add_format(args, "erspan_idx", "0x%x", |
1066 | 0 | tnl_cfg->erspan_idx); |
1067 | 0 | } |
1068 | 0 | } else if (tnl_cfg->erspan_ver == 2) { |
1069 | 0 | if (tnl_cfg->erspan_dir_flow) { |
1070 | 0 | smap_add(args, "erspan_dir", "flow"); |
1071 | 0 | } else { |
1072 | 0 | smap_add_format(args, "erspan_dir", "%d", |
1073 | 0 | tnl_cfg->erspan_dir); |
1074 | 0 | } |
1075 | 0 | if (tnl_cfg->erspan_hwid_flow) { |
1076 | 0 | smap_add(args, "erspan_hwid", "flow"); |
1077 | 0 | } else { |
1078 | 0 | smap_add_format(args, "erspan_hwid", "0x%x", |
1079 | 0 | tnl_cfg->erspan_hwid); |
1080 | 0 | } |
1081 | 0 | } |
1082 | 0 | } |
1083 | 0 | } |
1084 | |
|
1085 | 0 | return 0; |
1086 | 0 | } |
1087 | | |
1088 | | /* Code specific to patch ports. */ |
1089 | | |
1090 | | /* If 'netdev' is a patch port, returns the name of its peer as a malloc()'d |
1091 | | * string that the caller must free. |
1092 | | * |
1093 | | * If 'netdev' is not a patch port, returns NULL. */ |
1094 | | char * |
1095 | | netdev_vport_patch_peer(const struct netdev *netdev_) |
1096 | 0 | { |
1097 | 0 | char *peer = NULL; |
1098 | |
|
1099 | 0 | if (netdev_vport_is_patch(netdev_)) { |
1100 | 0 | struct netdev_vport *netdev = netdev_vport_cast(netdev_); |
1101 | |
|
1102 | 0 | ovs_mutex_lock(&netdev->mutex); |
1103 | 0 | if (netdev->peer) { |
1104 | 0 | peer = xstrdup(netdev->peer); |
1105 | 0 | } |
1106 | 0 | ovs_mutex_unlock(&netdev->mutex); |
1107 | 0 | } |
1108 | |
|
1109 | 0 | return peer; |
1110 | 0 | } |
1111 | | |
1112 | | void |
1113 | | netdev_vport_inc_rx(const struct netdev *netdev, |
1114 | | const struct dpif_flow_stats *stats) |
1115 | 0 | { |
1116 | 0 | if (is_vport_class(netdev_get_class(netdev))) { |
1117 | 0 | struct netdev_vport *dev = netdev_vport_cast(netdev); |
1118 | |
|
1119 | 0 | ovs_mutex_lock(&dev->mutex); |
1120 | 0 | dev->stats.rx_packets += stats->n_packets; |
1121 | 0 | dev->stats.rx_bytes += stats->n_bytes; |
1122 | 0 | ovs_mutex_unlock(&dev->mutex); |
1123 | 0 | } |
1124 | 0 | } |
1125 | | |
1126 | | void |
1127 | | netdev_vport_inc_tx(const struct netdev *netdev, |
1128 | | const struct dpif_flow_stats *stats) |
1129 | 0 | { |
1130 | 0 | if (is_vport_class(netdev_get_class(netdev))) { |
1131 | 0 | struct netdev_vport *dev = netdev_vport_cast(netdev); |
1132 | |
|
1133 | 0 | ovs_mutex_lock(&dev->mutex); |
1134 | 0 | dev->stats.tx_packets += stats->n_packets; |
1135 | 0 | dev->stats.tx_bytes += stats->n_bytes; |
1136 | 0 | ovs_mutex_unlock(&dev->mutex); |
1137 | 0 | } |
1138 | 0 | } |
1139 | | |
1140 | | static int |
1141 | | get_patch_config(const struct netdev *dev_, struct smap *args) |
1142 | 0 | { |
1143 | 0 | struct netdev_vport *dev = netdev_vport_cast(dev_); |
1144 | |
|
1145 | 0 | ovs_mutex_lock(&dev->mutex); |
1146 | 0 | if (dev->peer) { |
1147 | 0 | smap_add(args, "peer", dev->peer); |
1148 | 0 | } |
1149 | 0 | ovs_mutex_unlock(&dev->mutex); |
1150 | |
|
1151 | 0 | return 0; |
1152 | 0 | } |
1153 | | |
1154 | | static int |
1155 | | set_patch_config(struct netdev *dev_, const struct smap *args, char **errp) |
1156 | 0 | { |
1157 | 0 | struct netdev_vport *dev = netdev_vport_cast(dev_); |
1158 | 0 | const char *name = netdev_get_name(dev_); |
1159 | 0 | const char *peer; |
1160 | |
|
1161 | 0 | peer = smap_get(args, "peer"); |
1162 | 0 | if (!peer) { |
1163 | 0 | VLOG_ERR_BUF(errp, "%s: patch type requires valid 'peer' argument", |
1164 | 0 | name); |
1165 | 0 | return EINVAL; |
1166 | 0 | } |
1167 | | |
1168 | 0 | if (smap_count(args) > 1) { |
1169 | 0 | VLOG_ERR_BUF(errp, "%s: patch type takes only a 'peer' argument", |
1170 | 0 | name); |
1171 | 0 | return EINVAL; |
1172 | 0 | } |
1173 | | |
1174 | 0 | if (!strcmp(name, peer)) { |
1175 | 0 | VLOG_ERR_BUF(errp, "%s: patch peer must not be self", name); |
1176 | 0 | return EINVAL; |
1177 | 0 | } |
1178 | | |
1179 | 0 | ovs_mutex_lock(&dev->mutex); |
1180 | 0 | if (!dev->peer || strcmp(dev->peer, peer)) { |
1181 | 0 | free(dev->peer); |
1182 | 0 | dev->peer = xstrdup(peer); |
1183 | 0 | netdev_change_seq_changed(dev_); |
1184 | 0 | } |
1185 | 0 | ovs_mutex_unlock(&dev->mutex); |
1186 | |
|
1187 | 0 | return 0; |
1188 | 0 | } |
1189 | | |
1190 | | static int |
1191 | | netdev_vport_get_stats(const struct netdev *netdev, struct netdev_stats *stats) |
1192 | 0 | { |
1193 | 0 | struct netdev_vport *dev = netdev_vport_cast(netdev); |
1194 | |
|
1195 | 0 | ovs_mutex_lock(&dev->mutex); |
1196 | | /* Passing only collected counters */ |
1197 | 0 | stats->tx_packets = dev->stats.tx_packets; |
1198 | 0 | stats->tx_bytes = dev->stats.tx_bytes; |
1199 | 0 | stats->rx_packets = dev->stats.rx_packets; |
1200 | 0 | stats->rx_bytes = dev->stats.rx_bytes; |
1201 | 0 | ovs_mutex_unlock(&dev->mutex); |
1202 | |
|
1203 | 0 | return 0; |
1204 | 0 | } |
1205 | | |
1206 | | static enum netdev_pt_mode |
1207 | | netdev_vport_get_pt_mode(const struct netdev *netdev) |
1208 | 0 | { |
1209 | 0 | const struct netdev_tunnel_config *tnl_cfg; |
1210 | |
|
1211 | 0 | tnl_cfg = netdev_get_tunnel_config(netdev); |
1212 | |
|
1213 | 0 | return tnl_cfg ? tnl_cfg->pt_mode : NETDEV_PT_UNKNOWN; |
1214 | 0 | } |
1215 | | |
1216 | | |
1217 | | |
1218 | | #ifdef __linux__ |
1219 | | static int |
1220 | | netdev_vport_get_ifindex(const struct netdev *netdev_) |
1221 | 0 | { |
1222 | 0 | char buf[NETDEV_VPORT_NAME_BUFSIZE]; |
1223 | 0 | const char *name = netdev_vport_get_dpif_port(netdev_, buf, sizeof(buf)); |
1224 | 0 | const char *dpif_type = netdev_get_dpif_type(netdev_); |
1225 | |
|
1226 | 0 | if (dpif_type && strcmp(dpif_type, "system")) { |
1227 | | /* Not a system device. */ |
1228 | 0 | return -ENODEV; |
1229 | 0 | } |
1230 | | |
1231 | 0 | return linux_get_ifindex(name); |
1232 | 0 | } |
1233 | | |
1234 | 0 | #define NETDEV_VPORT_GET_IFINDEX netdev_vport_get_ifindex |
1235 | | #else /* !__linux__ */ |
1236 | | #define NETDEV_VPORT_GET_IFINDEX NULL |
1237 | | #endif /* __linux__ */ |
1238 | | |
1239 | | #define VPORT_FUNCTIONS_COMMON \ |
1240 | 0 | .run = netdev_vport_run, \ |
1241 | 0 | .wait = netdev_vport_wait, \ |
1242 | 0 | .alloc = netdev_vport_alloc, \ |
1243 | 0 | .construct = netdev_vport_construct, \ |
1244 | 0 | .destruct = netdev_vport_destruct, \ |
1245 | 0 | .dealloc = netdev_vport_dealloc, \ |
1246 | 0 | .set_etheraddr = netdev_vport_set_etheraddr, \ |
1247 | 0 | .get_etheraddr = netdev_vport_get_etheraddr, \ |
1248 | 0 | .get_stats = netdev_vport_get_stats, \ |
1249 | 0 | .get_pt_mode = netdev_vport_get_pt_mode, \ |
1250 | 0 | .update_flags = netdev_vport_update_flags |
1251 | | |
1252 | | #define TUNNEL_FUNCTIONS_COMMON \ |
1253 | 0 | VPORT_FUNCTIONS_COMMON, \ |
1254 | 0 | .get_config = get_tunnel_config, \ |
1255 | 0 | .set_config = set_tunnel_config, \ |
1256 | 0 | .get_tunnel_config = get_netdev_tunnel_config, \ |
1257 | 0 | .get_status = tunnel_get_status |
1258 | | |
1259 | | void |
1260 | | netdev_vport_tunnel_register(void) |
1261 | 0 | { |
1262 | | /* The name of the dpif_port should be short enough to accomodate adding |
1263 | | * a port number to the end if one is necessary. */ |
1264 | 0 | static struct vport_class vport_classes[] = { |
1265 | 0 | { "genev_sys", |
1266 | 0 | { |
1267 | 0 | TUNNEL_FUNCTIONS_COMMON, |
1268 | 0 | .type = "geneve", |
1269 | 0 | .build_header = netdev_geneve_build_header, |
1270 | 0 | .push_header = netdev_tnl_push_udp_header, |
1271 | 0 | .pop_header = netdev_geneve_pop_header, |
1272 | 0 | .get_ifindex = NETDEV_VPORT_GET_IFINDEX, |
1273 | 0 | }, |
1274 | 0 | {{NULL, NULL, 0, 0}} |
1275 | 0 | }, |
1276 | 0 | { "gre_sys", |
1277 | 0 | { |
1278 | 0 | TUNNEL_FUNCTIONS_COMMON, |
1279 | 0 | .type = "gre", |
1280 | 0 | .build_header = netdev_gre_build_header, |
1281 | 0 | .push_header = netdev_gre_push_header, |
1282 | 0 | .pop_header = netdev_gre_pop_header, |
1283 | 0 | .get_ifindex = NETDEV_VPORT_GET_IFINDEX, |
1284 | 0 | }, |
1285 | 0 | {{NULL, NULL, 0, 0}} |
1286 | 0 | }, |
1287 | 0 | { "vxlan_sys", |
1288 | 0 | { |
1289 | 0 | TUNNEL_FUNCTIONS_COMMON, |
1290 | 0 | .type = "vxlan", |
1291 | 0 | .build_header = netdev_vxlan_build_header, |
1292 | 0 | .push_header = netdev_tnl_push_udp_header, |
1293 | 0 | .pop_header = netdev_vxlan_pop_header, |
1294 | 0 | .get_ifindex = NETDEV_VPORT_GET_IFINDEX |
1295 | 0 | }, |
1296 | 0 | {{NULL, NULL, 0, 0}} |
1297 | 0 | }, |
1298 | 0 | { "erspan_sys", |
1299 | 0 | { |
1300 | 0 | TUNNEL_FUNCTIONS_COMMON, |
1301 | 0 | .type = "erspan", |
1302 | 0 | .build_header = netdev_erspan_build_header, |
1303 | 0 | .push_header = netdev_erspan_push_header, |
1304 | 0 | .pop_header = netdev_erspan_pop_header, |
1305 | 0 | .get_ifindex = NETDEV_VPORT_GET_IFINDEX |
1306 | 0 | }, |
1307 | 0 | {{NULL, NULL, 0, 0}} |
1308 | 0 | }, |
1309 | 0 | { "ip6erspan_sys", |
1310 | 0 | { |
1311 | 0 | TUNNEL_FUNCTIONS_COMMON, |
1312 | 0 | .type = "ip6erspan", |
1313 | 0 | .build_header = netdev_erspan_build_header, |
1314 | 0 | .push_header = netdev_erspan_push_header, |
1315 | 0 | .pop_header = netdev_erspan_pop_header, |
1316 | 0 | .get_ifindex = NETDEV_VPORT_GET_IFINDEX |
1317 | 0 | }, |
1318 | 0 | {{NULL, NULL, 0, 0}} |
1319 | 0 | }, |
1320 | 0 | { "ip6gre_sys", |
1321 | 0 | { |
1322 | 0 | TUNNEL_FUNCTIONS_COMMON, |
1323 | 0 | .type = "ip6gre", |
1324 | 0 | .build_header = netdev_gre_build_header, |
1325 | 0 | .push_header = netdev_gre_push_header, |
1326 | 0 | .pop_header = netdev_gre_pop_header, |
1327 | 0 | .get_ifindex = NETDEV_VPORT_GET_IFINDEX, |
1328 | 0 | }, |
1329 | 0 | {{NULL, NULL, 0, 0}} |
1330 | 0 | }, |
1331 | 0 | { "gtpu_sys", |
1332 | 0 | { |
1333 | 0 | TUNNEL_FUNCTIONS_COMMON, |
1334 | 0 | .type = "gtpu", |
1335 | 0 | .build_header = netdev_gtpu_build_header, |
1336 | 0 | .push_header = netdev_gtpu_push_header, |
1337 | 0 | .pop_header = netdev_gtpu_pop_header, |
1338 | 0 | }, |
1339 | 0 | {{NULL, NULL, 0, 0}} |
1340 | 0 | }, |
1341 | 0 | { "udp_sys", |
1342 | 0 | { |
1343 | 0 | TUNNEL_FUNCTIONS_COMMON, |
1344 | 0 | .type = "bareudp", |
1345 | 0 | .get_ifindex = NETDEV_VPORT_GET_IFINDEX, |
1346 | 0 | }, |
1347 | 0 | {{NULL, NULL, 0, 0}} |
1348 | 0 | }, |
1349 | 0 | { "srv6_sys", |
1350 | 0 | { |
1351 | 0 | TUNNEL_FUNCTIONS_COMMON, |
1352 | 0 | .type = "srv6", |
1353 | 0 | .build_header = netdev_srv6_build_header, |
1354 | 0 | .push_header = netdev_srv6_push_header, |
1355 | 0 | .pop_header = netdev_srv6_pop_header, |
1356 | 0 | .get_ifindex = NETDEV_VPORT_GET_IFINDEX, |
1357 | 0 | }, |
1358 | 0 | {{NULL, NULL, 0, 0}} |
1359 | 0 | }, |
1360 | |
|
1361 | 0 | }; |
1362 | 0 | static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; |
1363 | |
|
1364 | 0 | if (ovsthread_once_start(&once)) { |
1365 | 0 | int i; |
1366 | |
|
1367 | 0 | for (i = 0; i < ARRAY_SIZE(vport_classes); i++) { |
1368 | 0 | simap_init(&vport_classes[i].global_cfg_tracker); |
1369 | 0 | netdev_register_provider(&vport_classes[i].netdev_class); |
1370 | 0 | } |
1371 | |
|
1372 | 0 | unixctl_command_register("tnl/egress_port_range", "min max", 0, 2, |
1373 | 0 | netdev_tnl_egress_port_range, NULL); |
1374 | |
|
1375 | 0 | ovsthread_once_done(&once); |
1376 | 0 | } |
1377 | 0 | } |
1378 | | |
1379 | | void |
1380 | | netdev_vport_patch_register(void) |
1381 | 0 | { |
1382 | 0 | static struct vport_class patch_class = { |
1383 | 0 | NULL, |
1384 | 0 | { VPORT_FUNCTIONS_COMMON, |
1385 | 0 | .type = "patch", |
1386 | 0 | .get_config = get_patch_config, |
1387 | 0 | .set_config = set_patch_config, |
1388 | 0 | }, |
1389 | 0 | {{NULL, NULL, 0, 0}} |
1390 | 0 | }; |
1391 | 0 | simap_init(&patch_class.global_cfg_tracker); |
1392 | 0 | netdev_register_provider(&patch_class.netdev_class); |
1393 | 0 | } |