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