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