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