]>
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" |
aca40d4f | 44 | #include "smap.h" |
777ece09 | 45 | #include "socket-util.h" |
a36de779 PS |
46 | #include "unaligned.h" |
47 | #include "unixctl.h" | |
aca40d4f | 48 | #include "openvswitch/vlog.h" |
01b25786 PB |
49 | #include "netdev-tc-offloads.h" |
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 *); |
2b9d6589 | 69 | |
6b241d64 PS |
70 | struct vport_class { |
71 | const char *dpif_port; | |
72 | struct netdev_class netdev_class; | |
73 | }; | |
777ece09 | 74 | |
41ca1e0a AW |
75 | bool |
76 | netdev_vport_is_vport_class(const struct netdev_class *class) | |
77 | { | |
78 | return is_vport_class(class); | |
79 | } | |
80 | ||
2b9d6589 BP |
81 | static const struct vport_class * |
82 | vport_class_cast(const struct netdev_class *class) | |
83 | { | |
cb22974d | 84 | ovs_assert(is_vport_class(class)); |
2b9d6589 BP |
85 | return CONTAINER_OF(class, struct vport_class, netdev_class); |
86 | } | |
87 | ||
f431bf7d | 88 | static const struct netdev_tunnel_config * |
b5d57fc8 | 89 | get_netdev_tunnel_config(const struct netdev *netdev) |
f431bf7d | 90 | { |
b5d57fc8 | 91 | return &netdev_vport_cast(netdev)->tnl_cfg; |
f431bf7d EJ |
92 | } |
93 | ||
0a740f48 EJ |
94 | bool |
95 | netdev_vport_is_patch(const struct netdev *netdev) | |
96 | { | |
b5d57fc8 | 97 | const struct netdev_class *class = netdev_get_class(netdev); |
f18a39b7 | 98 | |
c060c4cf | 99 | return class->get_config == get_patch_config; |
0a740f48 EJ |
100 | } |
101 | ||
56b11f0b | 102 | static bool |
b5d57fc8 | 103 | netdev_vport_needs_dst_port(const struct netdev *dev) |
56b11f0b | 104 | { |
b5d57fc8 BP |
105 | const struct netdev_class *class = netdev_get_class(dev); |
106 | const char *type = netdev_get_type(dev); | |
56b11f0b | 107 | |
a6ae068b | 108 | return (class->get_config == get_tunnel_config && |
c1fc1411 | 109 | (!strcmp("geneve", type) || !strcmp("vxlan", type) || |
4237026e | 110 | !strcmp("lisp", type) || !strcmp("stt", type)) ); |
56b11f0b KM |
111 | } |
112 | ||
94a53842 AW |
113 | const char * |
114 | netdev_vport_class_get_dpif_port(const struct netdev_class *class) | |
115 | { | |
116 | return is_vport_class(class) ? vport_class_cast(class)->dpif_port : NULL; | |
117 | } | |
118 | ||
de281153 | 119 | const char * |
3aa30359 BP |
120 | netdev_vport_get_dpif_port(const struct netdev *netdev, |
121 | char namebuf[], size_t bufsize) | |
de281153 | 122 | { |
a5d4fadd JG |
123 | const struct netdev_class *class = netdev_get_class(netdev); |
124 | const char *dpif_port = netdev_vport_class_get_dpif_port(class); | |
125 | ||
126 | if (!dpif_port) { | |
127 | return netdev_get_name(netdev); | |
128 | } | |
129 | ||
b5d57fc8 BP |
130 | if (netdev_vport_needs_dst_port(netdev)) { |
131 | const struct netdev_vport *vport = netdev_vport_cast(netdev); | |
56b11f0b KM |
132 | |
133 | /* | |
a5d4fadd JG |
134 | * Note: IFNAMSIZ is 16 bytes long. Implementations should choose |
135 | * a dpif port name that is short enough to fit including any | |
136 | * port numbers but assert just in case. | |
56b11f0b | 137 | */ |
3aa30359 | 138 | BUILD_ASSERT(NETDEV_VPORT_NAME_BUFSIZE >= IFNAMSIZ); |
a5d4fadd JG |
139 | ovs_assert(strlen(dpif_port) + 6 < IFNAMSIZ); |
140 | snprintf(namebuf, bufsize, "%s_%d", dpif_port, | |
56b11f0b | 141 | ntohs(vport->tnl_cfg.dst_port)); |
3aa30359 | 142 | return namebuf; |
56b11f0b | 143 | } else { |
a5d4fadd | 144 | return dpif_port; |
56b11f0b | 145 | } |
2b9d6589 | 146 | } |
777ece09 | 147 | |
41ca1e0a AW |
148 | /* Whenever the route-table change number is incremented, |
149 | * netdev_vport_route_changed() should be called to update | |
150 | * the corresponding tunnel interface status. */ | |
151 | static void | |
152 | netdev_vport_route_changed(void) | |
153 | { | |
154 | struct netdev **vports; | |
155 | size_t i, n_vports; | |
156 | ||
157 | vports = netdev_get_vports(&n_vports); | |
158 | for (i = 0; i < n_vports; i++) { | |
159 | struct netdev *netdev_ = vports[i]; | |
160 | struct netdev_vport *netdev = netdev_vport_cast(netdev_); | |
161 | ||
162 | ovs_mutex_lock(&netdev->mutex); | |
163 | /* Finds all tunnel vports. */ | |
3ae91c01 | 164 | if (ipv6_addr_is_set(&netdev->tnl_cfg.ipv6_dst)) { |
41ca1e0a AW |
165 | if (tunnel_check_status_change__(netdev)) { |
166 | netdev_change_seq_changed(netdev_); | |
167 | } | |
168 | } | |
41ca1e0a | 169 | ovs_mutex_unlock(&netdev->mutex); |
b2f771ef BP |
170 | |
171 | netdev_close(netdev_); | |
41ca1e0a AW |
172 | } |
173 | ||
174 | free(vports); | |
175 | } | |
176 | ||
9dc63482 BP |
177 | static struct netdev * |
178 | netdev_vport_alloc(void) | |
179 | { | |
180 | struct netdev_vport *netdev = xzalloc(sizeof *netdev); | |
181 | return &netdev->up; | |
182 | } | |
183 | ||
6b241d64 | 184 | int |
9dc63482 | 185 | netdev_vport_construct(struct netdev *netdev_) |
2b9d6589 | 186 | { |
a36de779 PS |
187 | struct netdev_vport *dev = netdev_vport_cast(netdev_); |
188 | const char *type = netdev_get_type(netdev_); | |
6d9e6eb4 | 189 | |
a36de779 | 190 | ovs_mutex_init(&dev->mutex); |
74ff3298 | 191 | eth_addr_random(&dev->etheraddr); |
a36de779 PS |
192 | |
193 | /* Add a default destination port for tunnel ports if none specified. */ | |
194 | if (!strcmp(type, "geneve")) { | |
195 | dev->tnl_cfg.dst_port = htons(GENEVE_DST_PORT); | |
196 | } else if (!strcmp(type, "vxlan")) { | |
197 | dev->tnl_cfg.dst_port = htons(VXLAN_DST_PORT); | |
198 | } else if (!strcmp(type, "lisp")) { | |
199 | dev->tnl_cfg.dst_port = htons(LISP_DST_PORT); | |
4237026e PS |
200 | } else if (!strcmp(type, "stt")) { |
201 | dev->tnl_cfg.dst_port = htons(STT_DST_PORT); | |
a36de779 | 202 | } |
6d9e6eb4 | 203 | |
0890056e PS |
204 | dev->tnl_cfg.dont_fragment = true; |
205 | dev->tnl_cfg.ttl = DEFAULT_TTL; | |
de5cdb90 | 206 | return 0; |
777ece09 JG |
207 | } |
208 | ||
2b9d6589 | 209 | static void |
9dc63482 | 210 | netdev_vport_destruct(struct netdev *netdev_) |
2b9d6589 | 211 | { |
b5d57fc8 | 212 | struct netdev_vport *netdev = netdev_vport_cast(netdev_); |
2b9d6589 | 213 | |
b5d57fc8 | 214 | free(netdev->peer); |
86383816 | 215 | ovs_mutex_destroy(&netdev->mutex); |
9dc63482 BP |
216 | } |
217 | ||
218 | static void | |
219 | netdev_vport_dealloc(struct netdev *netdev_) | |
220 | { | |
221 | struct netdev_vport *netdev = netdev_vport_cast(netdev_); | |
2b9d6589 BP |
222 | free(netdev); |
223 | } | |
224 | ||
2b9d6589 | 225 | static int |
74ff3298 | 226 | netdev_vport_set_etheraddr(struct netdev *netdev_, const struct eth_addr mac) |
777ece09 | 227 | { |
b5d57fc8 | 228 | struct netdev_vport *netdev = netdev_vport_cast(netdev_); |
86383816 BP |
229 | |
230 | ovs_mutex_lock(&netdev->mutex); | |
74ff3298 | 231 | netdev->etheraddr = mac; |
86383816 | 232 | ovs_mutex_unlock(&netdev->mutex); |
3e912ffc | 233 | netdev_change_seq_changed(netdev_); |
86383816 | 234 | |
35b769cb | 235 | return 0; |
777ece09 JG |
236 | } |
237 | ||
2b9d6589 | 238 | static int |
74ff3298 | 239 | netdev_vport_get_etheraddr(const struct netdev *netdev_, struct eth_addr *mac) |
777ece09 | 240 | { |
86383816 BP |
241 | struct netdev_vport *netdev = netdev_vport_cast(netdev_); |
242 | ||
243 | ovs_mutex_lock(&netdev->mutex); | |
74ff3298 | 244 | *mac = netdev->etheraddr; |
86383816 BP |
245 | ovs_mutex_unlock(&netdev->mutex); |
246 | ||
35b769cb | 247 | return 0; |
777ece09 JG |
248 | } |
249 | ||
41ca1e0a AW |
250 | /* Checks if the tunnel status has changed and returns a boolean. |
251 | * Updates the tunnel status if it has changed. */ | |
252 | static bool | |
253 | tunnel_check_status_change__(struct netdev_vport *netdev) | |
254 | OVS_REQUIRES(netdev->mutex) | |
ea763e0e | 255 | { |
3dea0874 | 256 | char iface[IFNAMSIZ]; |
41ca1e0a | 257 | bool status = false; |
3ae91c01 JB |
258 | struct in6_addr *route; |
259 | struct in6_addr gw; | |
ed52ca57 | 260 | uint32_t mark; |
ea763e0e | 261 | |
41ca1e0a | 262 | iface[0] = '\0'; |
3ae91c01 | 263 | route = &netdev->tnl_cfg.ipv6_dst; |
ed52ca57 PS |
264 | mark = netdev->tnl_cfg.egress_pkt_mark; |
265 | if (ovs_router_lookup(mark, route, iface, NULL, &gw)) { | |
a404826e AE |
266 | struct netdev *egress_netdev; |
267 | ||
6c607a64 | 268 | if (!netdev_open(iface, NULL, &egress_netdev)) { |
41ca1e0a | 269 | status = netdev_get_carrier(egress_netdev); |
a404826e AE |
270 | netdev_close(egress_netdev); |
271 | } | |
ea763e0e EJ |
272 | } |
273 | ||
41ca1e0a AW |
274 | if (strcmp(netdev->egress_iface, iface) |
275 | || netdev->carrier_status != status) { | |
f9ac0f03 | 276 | ovs_strlcpy_arrays(netdev->egress_iface, iface); |
41ca1e0a AW |
277 | netdev->carrier_status = status; |
278 | ||
279 | return true; | |
280 | } | |
281 | ||
282 | return false; | |
283 | } | |
284 | ||
285 | static int | |
286 | tunnel_get_status(const struct netdev *netdev_, struct smap *smap) | |
287 | { | |
288 | struct netdev_vport *netdev = netdev_vport_cast(netdev_); | |
289 | ||
290 | if (netdev->egress_iface[0]) { | |
291 | smap_add(smap, "tunnel_egress_iface", netdev->egress_iface); | |
292 | ||
293 | smap_add(smap, "tunnel_egress_iface_carrier", | |
294 | netdev->carrier_status ? "up" : "down"); | |
295 | } | |
296 | ||
ea763e0e EJ |
297 | return 0; |
298 | } | |
299 | ||
2b9d6589 | 300 | static int |
b5d57fc8 BP |
301 | netdev_vport_update_flags(struct netdev *netdev OVS_UNUSED, |
302 | enum netdev_flags off, | |
303 | enum netdev_flags on OVS_UNUSED, | |
304 | enum netdev_flags *old_flagsp) | |
777ece09 JG |
305 | { |
306 | if (off & (NETDEV_UP | NETDEV_PROMISC)) { | |
307 | return EOPNOTSUPP; | |
308 | } | |
309 | ||
310 | *old_flagsp = NETDEV_UP | NETDEV_PROMISC; | |
311 | return 0; | |
312 | } | |
313 | ||
ea83a2fc | 314 | static void |
1c33f0c3 | 315 | netdev_vport_run(const struct netdev_class *netdev_class OVS_UNUSED) |
ea83a2fc | 316 | { |
41ca1e0a AW |
317 | uint64_t seq; |
318 | ||
a132aa96 | 319 | route_table_run(); |
41ca1e0a AW |
320 | seq = route_table_get_change_seq(); |
321 | if (rt_change_seqno != seq) { | |
322 | rt_change_seqno = seq; | |
323 | netdev_vport_route_changed(); | |
324 | } | |
ea83a2fc EJ |
325 | } |
326 | ||
327 | static void | |
1c33f0c3 | 328 | netdev_vport_wait(const struct netdev_class *netdev_class OVS_UNUSED) |
ea83a2fc | 329 | { |
41ca1e0a AW |
330 | uint64_t seq; |
331 | ||
a132aa96 | 332 | route_table_wait(); |
41ca1e0a AW |
333 | seq = route_table_get_change_seq(); |
334 | if (rt_change_seqno != seq) { | |
335 | poll_immediate_wake(); | |
336 | } | |
ea83a2fc EJ |
337 | } |
338 | \f | |
0a740f48 | 339 | /* Code specific to tunnel types. */ |
2b9d6589 | 340 | |
f431bf7d EJ |
341 | static ovs_be64 |
342 | parse_key(const struct smap *args, const char *name, | |
343 | bool *present, bool *flow) | |
c19e6535 BP |
344 | { |
345 | const char *s; | |
346 | ||
f431bf7d EJ |
347 | *present = false; |
348 | *flow = false; | |
349 | ||
79f1cbe9 | 350 | s = smap_get(args, name); |
c19e6535 | 351 | if (!s) { |
79f1cbe9 | 352 | s = smap_get(args, "key"); |
c19e6535 | 353 | if (!s) { |
f431bf7d | 354 | return 0; |
c19e6535 BP |
355 | } |
356 | } | |
357 | ||
f431bf7d EJ |
358 | *present = true; |
359 | ||
c19e6535 | 360 | if (!strcmp(s, "flow")) { |
f431bf7d EJ |
361 | *flow = true; |
362 | return 0; | |
c19e6535 | 363 | } else { |
f431bf7d | 364 | return htonll(strtoull(s, NULL, 0)); |
c19e6535 BP |
365 | } |
366 | } | |
367 | ||
3ae91c01 JB |
368 | static int |
369 | parse_tunnel_ip(const char *value, bool accept_mcast, bool *flow, | |
370 | struct in6_addr *ipv6, uint16_t *protocol) | |
371 | { | |
372 | if (!strcmp(value, "flow")) { | |
373 | *flow = true; | |
374 | *protocol = 0; | |
375 | return 0; | |
376 | } | |
377 | if (addr_is_ipv6(value)) { | |
378 | if (lookup_ipv6(value, ipv6)) { | |
379 | return ENOENT; | |
380 | } | |
381 | if (!accept_mcast && ipv6_addr_is_multicast(ipv6)) { | |
382 | return EINVAL; | |
383 | } | |
384 | *protocol = ETH_TYPE_IPV6; | |
385 | } else { | |
386 | struct in_addr ip; | |
387 | if (lookup_ip(value, &ip)) { | |
388 | return ENOENT; | |
389 | } | |
390 | if (!accept_mcast && ip_is_multicast(ip.s_addr)) { | |
391 | return EINVAL; | |
392 | } | |
393 | in6_addr_set_mapped_ipv4(ipv6, ip.s_addr); | |
394 | *protocol = ETH_TYPE_IP; | |
395 | } | |
396 | return 0; | |
397 | } | |
398 | ||
875ab130 BP |
399 | enum tunnel_layers { |
400 | TNL_L2 = 1 << 0, /* 1 if a tunnel type can carry Ethernet traffic. */ | |
401 | TNL_L3 = 1 << 1 /* 1 if a tunnel type can carry L3 traffic. */ | |
402 | }; | |
403 | static enum tunnel_layers | |
404 | tunnel_supported_layers(const char *type, | |
405 | const struct netdev_tunnel_config *tnl_cfg) | |
406 | { | |
407 | if (!strcmp(type, "lisp")) { | |
408 | return TNL_L3; | |
409 | } else if (!strcmp(type, "gre")) { | |
410 | return TNL_L2 | TNL_L3; | |
ea4cb65c EG |
411 | } else if (!strcmp(type, "vxlan") |
412 | && tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GPE)) { | |
875ab130 BP |
413 | return TNL_L2 | TNL_L3; |
414 | } else { | |
415 | return TNL_L2; | |
416 | } | |
417 | } | |
418 | static enum netdev_pt_mode | |
419 | default_pt_mode(enum tunnel_layers layers) | |
420 | { | |
421 | return layers == TNL_L3 ? NETDEV_PT_LEGACY_L3 : NETDEV_PT_LEGACY_L2; | |
422 | } | |
423 | ||
2b9d6589 | 424 | static int |
9fff138e | 425 | set_tunnel_config(struct netdev *dev_, const struct smap *args, char **errp) |
2b9d6589 | 426 | { |
b5d57fc8 BP |
427 | struct netdev_vport *dev = netdev_vport_cast(dev_); |
428 | const char *name = netdev_get_name(dev_); | |
429 | const char *type = netdev_get_type(dev_); | |
9fff138e | 430 | struct ds errors = DS_EMPTY_INITIALIZER; |
875ab130 | 431 | bool needs_dst_port, has_csum; |
3ae91c01 | 432 | uint16_t dst_proto = 0, src_proto = 0; |
f431bf7d | 433 | struct netdev_tunnel_config tnl_cfg; |
79f1cbe9 | 434 | struct smap_node *node; |
9fff138e | 435 | int err; |
f431bf7d | 436 | |
4752cc0c | 437 | has_csum = strstr(type, "gre") || strstr(type, "geneve") || |
4237026e | 438 | strstr(type, "stt") || strstr(type, "vxlan"); |
f431bf7d | 439 | memset(&tnl_cfg, 0, sizeof tnl_cfg); |
2b9d6589 | 440 | |
a36de779 PS |
441 | /* Add a default destination port for tunnel ports if none specified. */ |
442 | if (!strcmp(type, "geneve")) { | |
443 | tnl_cfg.dst_port = htons(GENEVE_DST_PORT); | |
444 | } | |
445 | ||
446 | if (!strcmp(type, "vxlan")) { | |
447 | tnl_cfg.dst_port = htons(VXLAN_DST_PORT); | |
448 | } | |
449 | ||
450 | if (!strcmp(type, "lisp")) { | |
451 | tnl_cfg.dst_port = htons(LISP_DST_PORT); | |
452 | } | |
453 | ||
4237026e PS |
454 | if (!strcmp(type, "stt")) { |
455 | tnl_cfg.dst_port = htons(STT_DST_PORT); | |
456 | } | |
457 | ||
a6ae068b | 458 | needs_dst_port = netdev_vport_needs_dst_port(dev_); |
f431bf7d | 459 | tnl_cfg.dont_fragment = true; |
e16a28b5 | 460 | |
79f1cbe9 EJ |
461 | SMAP_FOR_EACH (node, args) { |
462 | if (!strcmp(node->key, "remote_ip")) { | |
3ae91c01 JB |
463 | err = parse_tunnel_ip(node->value, false, &tnl_cfg.ip_dst_flow, |
464 | &tnl_cfg.ipv6_dst, &dst_proto); | |
465 | switch (err) { | |
466 | case ENOENT: | |
9fff138e | 467 | ds_put_format(&errors, "%s: bad %s 'remote_ip'\n", name, type); |
3ae91c01 JB |
468 | break; |
469 | case EINVAL: | |
9fff138e DDP |
470 | ds_put_format(&errors, |
471 | "%s: multicast remote_ip=%s not allowed\n", | |
472 | name, node->value); | |
473 | goto out; | |
2b9d6589 | 474 | } |
79f1cbe9 | 475 | } else if (!strcmp(node->key, "local_ip")) { |
3ae91c01 JB |
476 | err = parse_tunnel_ip(node->value, true, &tnl_cfg.ip_src_flow, |
477 | &tnl_cfg.ipv6_src, &src_proto); | |
478 | switch (err) { | |
479 | case ENOENT: | |
9fff138e | 480 | ds_put_format(&errors, "%s: bad %s 'local_ip'\n", name, type); |
3ae91c01 | 481 | break; |
2b9d6589 | 482 | } |
79f1cbe9 EJ |
483 | } else if (!strcmp(node->key, "tos")) { |
484 | if (!strcmp(node->value, "inherit")) { | |
f431bf7d | 485 | tnl_cfg.tos_inherit = true; |
2b9d6589 | 486 | } else { |
3fca7064 PS |
487 | char *endptr; |
488 | int tos; | |
79f1cbe9 | 489 | tos = strtol(node->value, &endptr, 0); |
91aff446 | 490 | if (*endptr == '\0' && tos == (tos & IP_DSCP_MASK)) { |
f431bf7d | 491 | tnl_cfg.tos = tos; |
91aff446 | 492 | } else { |
9fff138e DDP |
493 | ds_put_format(&errors, "%s: invalid TOS %s\n", name, |
494 | node->value); | |
3fca7064 | 495 | } |
2b9d6589 | 496 | } |
79f1cbe9 EJ |
497 | } else if (!strcmp(node->key, "ttl")) { |
498 | if (!strcmp(node->value, "inherit")) { | |
f431bf7d | 499 | tnl_cfg.ttl_inherit = true; |
2b9d6589 | 500 | } else { |
f431bf7d | 501 | tnl_cfg.ttl = atoi(node->value); |
2b9d6589 | 502 | } |
79f827fa | 503 | } else if (!strcmp(node->key, "dst_port") && needs_dst_port) { |
f431bf7d | 504 | tnl_cfg.dst_port = htons(atoi(node->value)); |
f431bf7d | 505 | } else if (!strcmp(node->key, "csum") && has_csum) { |
79f1cbe9 | 506 | if (!strcmp(node->value, "true")) { |
f431bf7d | 507 | tnl_cfg.csum = true; |
2b9d6589 | 508 | } |
79f1cbe9 EJ |
509 | } else if (!strcmp(node->key, "df_default")) { |
510 | if (!strcmp(node->value, "false")) { | |
f431bf7d | 511 | tnl_cfg.dont_fragment = false; |
66409d1b | 512 | } |
79f1cbe9 EJ |
513 | } else if (!strcmp(node->key, "key") || |
514 | !strcmp(node->key, "in_key") || | |
875ab130 BP |
515 | !strcmp(node->key, "out_key") || |
516 | !strcmp(node->key, "packet_type")) { | |
c19e6535 | 517 | /* Handled separately below. */ |
875ab130 | 518 | } else if (!strcmp(node->key, "exts") && !strcmp(type, "vxlan")) { |
526df7d8 TG |
519 | char *str = xstrdup(node->value); |
520 | char *ext, *save_ptr = NULL; | |
521 | ||
522 | tnl_cfg.exts = 0; | |
523 | ||
524 | ext = strtok_r(str, ",", &save_ptr); | |
525 | while (ext) { | |
526 | if (!strcmp(type, "vxlan") && !strcmp(ext, "gbp")) { | |
527 | tnl_cfg.exts |= (1 << OVS_VXLAN_EXT_GBP); | |
439f39cb GS |
528 | } else if (!strcmp(type, "vxlan") && !strcmp(ext, "gpe")) { |
529 | tnl_cfg.exts |= (1 << OVS_VXLAN_EXT_GPE); | |
526df7d8 | 530 | } else { |
9fff138e DDP |
531 | ds_put_format(&errors, "%s: unknown extension '%s'\n", |
532 | name, ext); | |
526df7d8 TG |
533 | } |
534 | ||
535 | ext = strtok_r(NULL, ",", &save_ptr); | |
536 | } | |
537 | ||
538 | free(str); | |
bf4bbd0d PS |
539 | } else if (!strcmp(node->key, "egress_pkt_mark")) { |
540 | tnl_cfg.egress_pkt_mark = strtoul(node->value, NULL, 10); | |
541 | tnl_cfg.set_egress_pkt_mark = true; | |
2b9d6589 | 542 | } else { |
439f39cb GS |
543 | ds_put_format(&errors, "%s: unknown %s argument '%s'\n", name, |
544 | type, node->key); | |
2b9d6589 BP |
545 | } |
546 | } | |
547 | ||
875ab130 BP |
548 | enum tunnel_layers layers = tunnel_supported_layers(type, &tnl_cfg); |
549 | const char *full_type = (strcmp(type, "vxlan") ? type | |
ea4cb65c EG |
550 | : (tnl_cfg.exts & (1 << OVS_VXLAN_EXT_GPE) |
551 | ? "VXLAN-GPE" : "VXLAN (without GPE")); | |
875ab130 BP |
552 | const char *packet_type = smap_get(args, "packet_type"); |
553 | if (!packet_type) { | |
554 | tnl_cfg.pt_mode = default_pt_mode(layers); | |
555 | } else if (!strcmp(packet_type, "legacy_l2")) { | |
556 | tnl_cfg.pt_mode = NETDEV_PT_LEGACY_L2; | |
557 | if (!(layers & TNL_L2)) { | |
558 | ds_put_format(&errors, "%s: legacy_l2 configured on %s tunnel " | |
559 | "that cannot carry L2 traffic\n", | |
560 | name, full_type); | |
561 | err = EINVAL; | |
562 | goto out; | |
563 | } | |
564 | } else if (!strcmp(packet_type, "legacy_l3")) { | |
565 | tnl_cfg.pt_mode = NETDEV_PT_LEGACY_L3; | |
566 | if (!(layers & TNL_L3)) { | |
567 | ds_put_format(&errors, "%s: legacy_l3 configured on %s tunnel " | |
568 | "that cannot carry L3 traffic\n", | |
569 | name, full_type); | |
570 | err = EINVAL; | |
571 | goto out; | |
572 | } | |
573 | } else if (!strcmp(packet_type, "ptap")) { | |
574 | tnl_cfg.pt_mode = NETDEV_PT_AWARE; | |
575 | } else { | |
576 | ds_put_format(&errors, "%s: unknown packet_type '%s'\n", | |
577 | name, packet_type); | |
578 | err = EINVAL; | |
579 | goto out; | |
439f39cb GS |
580 | } |
581 | ||
3ae91c01 | 582 | if (!ipv6_addr_is_set(&tnl_cfg.ipv6_dst) && !tnl_cfg.ip_dst_flow) { |
9fff138e DDP |
583 | ds_put_format(&errors, |
584 | "%s: %s type requires valid 'remote_ip' argument\n", | |
585 | name, type); | |
586 | err = EINVAL; | |
587 | goto out; | |
2b9d6589 | 588 | } |
0ad90c84 | 589 | if (tnl_cfg.ip_src_flow && !tnl_cfg.ip_dst_flow) { |
9fff138e DDP |
590 | ds_put_format(&errors, |
591 | "%s: %s type requires 'remote_ip=flow' " | |
592 | "with 'local_ip=flow'\n", | |
593 | name, type); | |
594 | err = EINVAL; | |
595 | goto out; | |
0ad90c84 | 596 | } |
3ae91c01 | 597 | if (src_proto && dst_proto && src_proto != dst_proto) { |
9fff138e DDP |
598 | ds_put_format(&errors, |
599 | "%s: 'remote_ip' and 'local_ip' " | |
600 | "has to be of the same address family\n", | |
601 | name); | |
602 | err = EINVAL; | |
603 | goto out; | |
3ae91c01 | 604 | } |
f431bf7d EJ |
605 | if (!tnl_cfg.ttl) { |
606 | tnl_cfg.ttl = DEFAULT_TTL; | |
607 | } | |
608 | ||
609 | tnl_cfg.in_key = parse_key(args, "in_key", | |
610 | &tnl_cfg.in_key_present, | |
611 | &tnl_cfg.in_key_flow); | |
f431bf7d EJ |
612 | |
613 | tnl_cfg.out_key = parse_key(args, "out_key", | |
614 | &tnl_cfg.out_key_present, | |
615 | &tnl_cfg.out_key_flow); | |
2b9d6589 | 616 | |
86383816 | 617 | ovs_mutex_lock(&dev->mutex); |
a1908399 AW |
618 | if (memcmp(&dev->tnl_cfg, &tnl_cfg, sizeof tnl_cfg)) { |
619 | dev->tnl_cfg = tnl_cfg; | |
620 | tunnel_check_status_change__(dev); | |
621 | netdev_change_seq_changed(dev_); | |
622 | } | |
86383816 | 623 | ovs_mutex_unlock(&dev->mutex); |
f431bf7d | 624 | |
9fff138e DDP |
625 | err = 0; |
626 | ||
627 | out: | |
c296d3f8 DDP |
628 | if (errors.length) { |
629 | ds_chomp(&errors, '\n'); | |
630 | VLOG_WARN("%s", ds_cstr(&errors)); | |
631 | if (err) { | |
632 | *errp = ds_steal_cstr(&errors); | |
633 | } | |
9fff138e DDP |
634 | } |
635 | ||
636 | ds_destroy(&errors); | |
637 | ||
638 | return err; | |
c19e6535 BP |
639 | } |
640 | ||
2b9d6589 | 641 | static int |
b5d57fc8 | 642 | get_tunnel_config(const struct netdev *dev, struct smap *args) |
6d9e6eb4 | 643 | { |
86383816 | 644 | struct netdev_vport *netdev = netdev_vport_cast(dev); |
63171f04 | 645 | const char *type = netdev_get_type(dev); |
86383816 BP |
646 | struct netdev_tunnel_config tnl_cfg; |
647 | ||
648 | ovs_mutex_lock(&netdev->mutex); | |
649 | tnl_cfg = netdev->tnl_cfg; | |
650 | ovs_mutex_unlock(&netdev->mutex); | |
6d9e6eb4 | 651 | |
3ae91c01 JB |
652 | if (ipv6_addr_is_set(&tnl_cfg.ipv6_dst)) { |
653 | smap_add_ipv6(args, "remote_ip", &tnl_cfg.ipv6_dst); | |
86383816 | 654 | } else if (tnl_cfg.ip_dst_flow) { |
0ad90c84 | 655 | smap_add(args, "remote_ip", "flow"); |
0a740f48 EJ |
656 | } |
657 | ||
3ae91c01 JB |
658 | if (ipv6_addr_is_set(&tnl_cfg.ipv6_src)) { |
659 | smap_add_ipv6(args, "local_ip", &tnl_cfg.ipv6_src); | |
86383816 | 660 | } else if (tnl_cfg.ip_src_flow) { |
0ad90c84 | 661 | smap_add(args, "local_ip", "flow"); |
7f804ea5 | 662 | } |
c19e6535 | 663 | |
86383816 | 664 | if (tnl_cfg.in_key_flow && tnl_cfg.out_key_flow) { |
6d9e6eb4 | 665 | smap_add(args, "key", "flow"); |
86383816 BP |
666 | } else if (tnl_cfg.in_key_present && tnl_cfg.out_key_present |
667 | && tnl_cfg.in_key == tnl_cfg.out_key) { | |
668 | smap_add_format(args, "key", "%"PRIu64, ntohll(tnl_cfg.in_key)); | |
6d9e6eb4 | 669 | } else { |
86383816 | 670 | if (tnl_cfg.in_key_flow) { |
b9ad7294 | 671 | smap_add(args, "in_key", "flow"); |
86383816 | 672 | } else if (tnl_cfg.in_key_present) { |
b9ad7294 | 673 | smap_add_format(args, "in_key", "%"PRIu64, |
86383816 | 674 | ntohll(tnl_cfg.in_key)); |
b9ad7294 | 675 | } |
6d9e6eb4 | 676 | |
86383816 | 677 | if (tnl_cfg.out_key_flow) { |
b9ad7294 | 678 | smap_add(args, "out_key", "flow"); |
86383816 | 679 | } else if (tnl_cfg.out_key_present) { |
b9ad7294 | 680 | smap_add_format(args, "out_key", "%"PRIu64, |
86383816 | 681 | ntohll(tnl_cfg.out_key)); |
6d9e6eb4 BP |
682 | } |
683 | } | |
684 | ||
86383816 | 685 | if (tnl_cfg.ttl_inherit) { |
62827e6a | 686 | smap_add(args, "ttl", "inherit"); |
86383816 BP |
687 | } else if (tnl_cfg.ttl != DEFAULT_TTL) { |
688 | smap_add_format(args, "ttl", "%"PRIu8, tnl_cfg.ttl); | |
c19e6535 BP |
689 | } |
690 | ||
86383816 | 691 | if (tnl_cfg.tos_inherit) { |
6d9e6eb4 | 692 | smap_add(args, "tos", "inherit"); |
86383816 BP |
693 | } else if (tnl_cfg.tos) { |
694 | smap_add_format(args, "tos", "0x%x", tnl_cfg.tos); | |
6d9e6eb4 BP |
695 | } |
696 | ||
86383816 BP |
697 | if (tnl_cfg.dst_port) { |
698 | uint16_t dst_port = ntohs(tnl_cfg.dst_port); | |
9eeb949b | 699 | |
c1fc1411 JG |
700 | if ((!strcmp("geneve", type) && dst_port != GENEVE_DST_PORT) || |
701 | (!strcmp("vxlan", type) && dst_port != VXLAN_DST_PORT) || | |
4237026e PS |
702 | (!strcmp("lisp", type) && dst_port != LISP_DST_PORT) || |
703 | (!strcmp("stt", type) && dst_port != STT_DST_PORT)) { | |
79f827fa KM |
704 | smap_add_format(args, "dst_port", "%d", dst_port); |
705 | } | |
706 | } | |
707 | ||
86383816 | 708 | if (tnl_cfg.csum) { |
6d9e6eb4 BP |
709 | smap_add(args, "csum", "true"); |
710 | } | |
8a9ff93a | 711 | |
875ab130 BP |
712 | enum tunnel_layers layers = tunnel_supported_layers(type, &tnl_cfg); |
713 | if (tnl_cfg.pt_mode != default_pt_mode(layers)) { | |
714 | smap_add(args, "packet_type", | |
715 | tnl_cfg.pt_mode == NETDEV_PT_LEGACY_L2 ? "legacy_l2" | |
716 | : tnl_cfg.pt_mode == NETDEV_PT_LEGACY_L3 ? "legacy_l3" | |
717 | : "ptap"); | |
63171f04 JS |
718 | } |
719 | ||
86383816 | 720 | if (!tnl_cfg.dont_fragment) { |
66409d1b AE |
721 | smap_add(args, "df_default", "false"); |
722 | } | |
6d9e6eb4 | 723 | |
bf4bbd0d PS |
724 | if (tnl_cfg.set_egress_pkt_mark) { |
725 | smap_add_format(args, "egress_pkt_mark", | |
726 | "%"PRIu32, tnl_cfg.egress_pkt_mark); | |
727 | } | |
6d9e6eb4 BP |
728 | return 0; |
729 | } | |
0a740f48 EJ |
730 | \f |
731 | /* Code specific to patch ports. */ | |
732 | ||
161b6042 BP |
733 | /* If 'netdev' is a patch port, returns the name of its peer as a malloc()'d |
734 | * string that the caller must free. | |
735 | * | |
736 | * If 'netdev' is not a patch port, returns NULL. */ | |
737 | char * | |
738 | netdev_vport_patch_peer(const struct netdev *netdev_) | |
0a740f48 | 739 | { |
161b6042 BP |
740 | char *peer = NULL; |
741 | ||
742 | if (netdev_vport_is_patch(netdev_)) { | |
743 | struct netdev_vport *netdev = netdev_vport_cast(netdev_); | |
86383816 BP |
744 | |
745 | ovs_mutex_lock(&netdev->mutex); | |
161b6042 BP |
746 | if (netdev->peer) { |
747 | peer = xstrdup(netdev->peer); | |
748 | } | |
86383816 | 749 | ovs_mutex_unlock(&netdev->mutex); |
161b6042 BP |
750 | } |
751 | ||
752 | return peer; | |
0a740f48 EJ |
753 | } |
754 | ||
755 | void | |
b9ad7294 | 756 | netdev_vport_inc_rx(const struct netdev *netdev, |
9e04d6f6 | 757 | const struct dpif_flow_stats *stats) |
0a740f48 | 758 | { |
b5d57fc8 BP |
759 | if (is_vport_class(netdev_get_class(netdev))) { |
760 | struct netdev_vport *dev = netdev_vport_cast(netdev); | |
86383816 BP |
761 | |
762 | ovs_mutex_lock(&dev->mutex); | |
0a740f48 EJ |
763 | dev->stats.rx_packets += stats->n_packets; |
764 | dev->stats.rx_bytes += stats->n_bytes; | |
86383816 | 765 | ovs_mutex_unlock(&dev->mutex); |
0a740f48 EJ |
766 | } |
767 | } | |
768 | ||
769 | void | |
b9ad7294 EJ |
770 | netdev_vport_inc_tx(const struct netdev *netdev, |
771 | const struct dpif_flow_stats *stats) | |
0a740f48 | 772 | { |
b5d57fc8 BP |
773 | if (is_vport_class(netdev_get_class(netdev))) { |
774 | struct netdev_vport *dev = netdev_vport_cast(netdev); | |
86383816 BP |
775 | |
776 | ovs_mutex_lock(&dev->mutex); | |
0a740f48 EJ |
777 | dev->stats.tx_packets += stats->n_packets; |
778 | dev->stats.tx_bytes += stats->n_bytes; | |
86383816 | 779 | ovs_mutex_unlock(&dev->mutex); |
0a740f48 EJ |
780 | } |
781 | } | |
782 | ||
783 | static int | |
b5d57fc8 | 784 | get_patch_config(const struct netdev *dev_, struct smap *args) |
0a740f48 | 785 | { |
b5d57fc8 | 786 | struct netdev_vport *dev = netdev_vport_cast(dev_); |
0a740f48 | 787 | |
86383816 | 788 | ovs_mutex_lock(&dev->mutex); |
0a740f48 EJ |
789 | if (dev->peer) { |
790 | smap_add(args, "peer", dev->peer); | |
791 | } | |
86383816 BP |
792 | ovs_mutex_unlock(&dev->mutex); |
793 | ||
0a740f48 EJ |
794 | return 0; |
795 | } | |
6d9e6eb4 BP |
796 | |
797 | static int | |
9fff138e | 798 | set_patch_config(struct netdev *dev_, const struct smap *args, char **errp) |
2b9d6589 | 799 | { |
b5d57fc8 BP |
800 | struct netdev_vport *dev = netdev_vport_cast(dev_); |
801 | const char *name = netdev_get_name(dev_); | |
2b9d6589 BP |
802 | const char *peer; |
803 | ||
79f1cbe9 | 804 | peer = smap_get(args, "peer"); |
2b9d6589 | 805 | if (!peer) { |
9fff138e DDP |
806 | VLOG_ERR_BUF(errp, "%s: patch type requires valid 'peer' argument", |
807 | name); | |
2b9d6589 BP |
808 | return EINVAL; |
809 | } | |
810 | ||
79f1cbe9 | 811 | if (smap_count(args) > 1) { |
9fff138e DDP |
812 | VLOG_ERR_BUF(errp, "%s: patch type takes only a 'peer' argument", |
813 | name); | |
2b9d6589 BP |
814 | return EINVAL; |
815 | } | |
816 | ||
2b9d6589 | 817 | if (!strcmp(name, peer)) { |
9fff138e | 818 | VLOG_ERR_BUF(errp, "%s: patch peer must not be self", name); |
2b9d6589 BP |
819 | return EINVAL; |
820 | } | |
821 | ||
86383816 | 822 | ovs_mutex_lock(&dev->mutex); |
a1908399 AW |
823 | if (!dev->peer || strcmp(dev->peer, peer)) { |
824 | free(dev->peer); | |
825 | dev->peer = xstrdup(peer); | |
826 | netdev_change_seq_changed(dev_); | |
827 | } | |
86383816 BP |
828 | ovs_mutex_unlock(&dev->mutex); |
829 | ||
2b9d6589 BP |
830 | return 0; |
831 | } | |
6d9e6eb4 BP |
832 | |
833 | static int | |
b9ad7294 | 834 | get_stats(const struct netdev *netdev, struct netdev_stats *stats) |
0a740f48 | 835 | { |
b5d57fc8 | 836 | struct netdev_vport *dev = netdev_vport_cast(netdev); |
86383816 BP |
837 | |
838 | ovs_mutex_lock(&dev->mutex); | |
d6e3feb5 | 839 | /* Passing only collected counters */ |
840 | stats->tx_packets = dev->stats.tx_packets; | |
841 | stats->tx_bytes = dev->stats.tx_bytes; | |
842 | stats->rx_packets = dev->stats.rx_packets; | |
843 | stats->rx_bytes = dev->stats.rx_bytes; | |
86383816 BP |
844 | ovs_mutex_unlock(&dev->mutex); |
845 | ||
6d9e6eb4 BP |
846 | return 0; |
847 | } | |
a36de779 | 848 | |
875ab130 BP |
849 | static enum netdev_pt_mode |
850 | get_pt_mode(const struct netdev *netdev) | |
851 | { | |
852 | struct netdev_vport *dev = netdev_vport_cast(netdev); | |
853 | ||
854 | return dev->tnl_cfg.pt_mode; | |
855 | } | |
856 | ||
857 | ||
2b9d6589 | 858 | \f |
01b25786 PB |
859 | #ifdef __linux__ |
860 | static int | |
5e530e26 | 861 | netdev_vport_get_ifindex(const struct netdev *netdev_) |
01b25786 PB |
862 | { |
863 | char buf[NETDEV_VPORT_NAME_BUFSIZE]; | |
864 | const char *name = netdev_vport_get_dpif_port(netdev_, buf, sizeof(buf)); | |
865 | ||
866 | return linux_get_ifindex(name); | |
867 | } | |
868 | ||
01b25786 PB |
869 | #define NETDEV_VPORT_GET_IFINDEX netdev_vport_get_ifindex |
870 | #define NETDEV_FLOW_OFFLOAD_API LINUX_FLOW_OFFLOAD_API | |
871 | #else /* !__linux__ */ | |
872 | #define NETDEV_VPORT_GET_IFINDEX NULL | |
873 | #define NETDEV_FLOW_OFFLOAD_API NO_OFFLOAD_API | |
874 | #endif /* __linux__ */ | |
875 | ||
0a740f48 | 876 | #define VPORT_FUNCTIONS(GET_CONFIG, SET_CONFIG, \ |
a36de779 PS |
877 | GET_TUNNEL_CONFIG, GET_STATUS, \ |
878 | BUILD_HEADER, \ | |
01b25786 PB |
879 | PUSH_HEADER, POP_HEADER, \ |
880 | GET_IFINDEX) \ | |
b46ccdf5 | 881 | NULL, \ |
ea83a2fc EJ |
882 | netdev_vport_run, \ |
883 | netdev_vport_wait, \ | |
2b9d6589 | 884 | \ |
9dc63482 BP |
885 | netdev_vport_alloc, \ |
886 | netdev_vport_construct, \ | |
887 | netdev_vport_destruct, \ | |
888 | netdev_vport_dealloc, \ | |
0a740f48 EJ |
889 | GET_CONFIG, \ |
890 | SET_CONFIG, \ | |
f431bf7d | 891 | GET_TUNNEL_CONFIG, \ |
a36de779 PS |
892 | BUILD_HEADER, \ |
893 | PUSH_HEADER, \ | |
894 | POP_HEADER, \ | |
7dec44fe | 895 | NULL, /* get_numa_id */ \ |
050c60bf | 896 | NULL, /* set_tx_multiq */ \ |
2b9d6589 | 897 | \ |
552e20d0 | 898 | NULL, /* send */ \ |
2b9d6589 BP |
899 | NULL, /* send_wait */ \ |
900 | \ | |
901 | netdev_vport_set_etheraddr, \ | |
902 | netdev_vport_get_etheraddr, \ | |
14622f22 BP |
903 | NULL, /* get_mtu */ \ |
904 | NULL, /* set_mtu */ \ | |
01b25786 | 905 | GET_IFINDEX, \ |
85da620e | 906 | NULL, /* get_carrier */ \ |
65c3058c | 907 | NULL, /* get_carrier_resets */ \ |
63331829 | 908 | NULL, /* get_miimon */ \ |
b9ad7294 | 909 | get_stats, \ |
2b9d6589 BP |
910 | \ |
911 | NULL, /* get_features */ \ | |
912 | NULL, /* set_advertisements */ \ | |
875ab130 | 913 | get_pt_mode, \ |
2b9d6589 BP |
914 | \ |
915 | NULL, /* set_policing */ \ | |
916 | NULL, /* get_qos_types */ \ | |
917 | NULL, /* get_qos_capabilities */ \ | |
918 | NULL, /* get_qos */ \ | |
919 | NULL, /* set_qos */ \ | |
920 | NULL, /* get_queue */ \ | |
921 | NULL, /* set_queue */ \ | |
922 | NULL, /* delete_queue */ \ | |
923 | NULL, /* get_queue_stats */ \ | |
89454bf4 BP |
924 | NULL, /* queue_dump_start */ \ |
925 | NULL, /* queue_dump_next */ \ | |
926 | NULL, /* queue_dump_done */ \ | |
2b9d6589 BP |
927 | NULL, /* dump_queue_stats */ \ |
928 | \ | |
2b9d6589 | 929 | NULL, /* set_in4 */ \ |
a8704b50 | 930 | NULL, /* get_addr_list */ \ |
2b9d6589 BP |
931 | NULL, /* add_router */ \ |
932 | NULL, /* get_next_hop */ \ | |
ea763e0e | 933 | GET_STATUS, \ |
2b9d6589 BP |
934 | NULL, /* arp_lookup */ \ |
935 | \ | |
936 | netdev_vport_update_flags, \ | |
790fb3b7 | 937 | NULL, /* reconfigure */ \ |
2b9d6589 | 938 | \ |
9dc63482 BP |
939 | NULL, /* rx_alloc */ \ |
940 | NULL, /* rx_construct */ \ | |
941 | NULL, /* rx_destruct */ \ | |
942 | NULL, /* rx_dealloc */ \ | |
943 | NULL, /* rx_recv */ \ | |
944 | NULL, /* rx_wait */ \ | |
18ebd48c PB |
945 | NULL, /* rx_drain */ \ |
946 | \ | |
01b25786 | 947 | NETDEV_FLOW_OFFLOAD_API |
2b9d6589 | 948 | |
a36de779 | 949 | |
01b25786 PB |
950 | #define TUNNEL_CLASS(NAME, DPIF_PORT, BUILD_HEADER, PUSH_HEADER, POP_HEADER, \ |
951 | GET_IFINDEX) \ | |
a36de779 | 952 | { DPIF_PORT, \ |
118c77b1 IM |
953 | { NAME, false, \ |
954 | VPORT_FUNCTIONS(get_tunnel_config, \ | |
955 | set_tunnel_config, \ | |
956 | get_netdev_tunnel_config, \ | |
957 | tunnel_get_status, \ | |
01b25786 PB |
958 | BUILD_HEADER, PUSH_HEADER, POP_HEADER, \ |
959 | GET_IFINDEX) }} | |
db078f85 | 960 | |
2b9d6589 | 961 | void |
c060c4cf | 962 | netdev_vport_tunnel_register(void) |
2b9d6589 | 963 | { |
a5d4fadd JG |
964 | /* The name of the dpif_port should be short enough to accomodate adding |
965 | * a port number to the end if one is necessary. */ | |
c3827f61 | 966 | static const struct vport_class vport_classes[] = { |
e5a1caee | 967 | TUNNEL_CLASS("geneve", "genev_sys", netdev_geneve_build_header, |
6b241d64 | 968 | netdev_tnl_push_udp_header, |
01b25786 PB |
969 | netdev_geneve_pop_header, |
970 | NULL), | |
a36de779 PS |
971 | TUNNEL_CLASS("gre", "gre_sys", netdev_gre_build_header, |
972 | netdev_gre_push_header, | |
01b25786 PB |
973 | netdev_gre_pop_header, |
974 | NULL), | |
a36de779 | 975 | TUNNEL_CLASS("vxlan", "vxlan_sys", netdev_vxlan_build_header, |
6b241d64 | 976 | netdev_tnl_push_udp_header, |
01b25786 PB |
977 | netdev_vxlan_pop_header, |
978 | NETDEV_VPORT_GET_IFINDEX), | |
979 | TUNNEL_CLASS("lisp", "lisp_sys", NULL, NULL, NULL, NULL), | |
980 | TUNNEL_CLASS("stt", "stt_sys", NULL, NULL, NULL, NULL), | |
c3827f61 | 981 | }; |
86383816 | 982 | static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; |
c3827f61 | 983 | |
86383816 BP |
984 | if (ovsthread_once_start(&once)) { |
985 | int i; | |
c3827f61 | 986 | |
7c54c27f BP |
987 | for (i = 0; i < ARRAY_SIZE(vport_classes); i++) { |
988 | netdev_register_provider(&vport_classes[i].netdev_class); | |
989 | } | |
a36de779 PS |
990 | |
991 | unixctl_command_register("tnl/egress_port_range", "min max", 0, 2, | |
6b241d64 | 992 | netdev_tnl_egress_port_range, NULL); |
a36de779 | 993 | |
86383816 | 994 | ovsthread_once_done(&once); |
c3827f61 | 995 | } |
2b9d6589 | 996 | } |
c060c4cf EJ |
997 | |
998 | void | |
999 | netdev_vport_patch_register(void) | |
1000 | { | |
1001 | static const struct vport_class patch_class = | |
1002 | { NULL, | |
118c77b1 IM |
1003 | { "patch", false, |
1004 | VPORT_FUNCTIONS(get_patch_config, | |
1005 | set_patch_config, | |
1006 | NULL, | |
01b25786 | 1007 | NULL, NULL, NULL, NULL, NULL) }}; |
c060c4cf EJ |
1008 | netdev_register_provider(&patch_class.netdev_class); |
1009 | } |