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