]>
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; | |
ea4cb65c EG |
410 | } else if (!strcmp(type, "vxlan") |
411 | && tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GPE)) { | |
875ab130 BP |
412 | return TNL_L2 | TNL_L3; |
413 | } else { | |
414 | return TNL_L2; | |
415 | } | |
416 | } | |
417 | static enum netdev_pt_mode | |
418 | default_pt_mode(enum tunnel_layers layers) | |
419 | { | |
420 | return layers == TNL_L3 ? NETDEV_PT_LEGACY_L3 : NETDEV_PT_LEGACY_L2; | |
421 | } | |
422 | ||
2b9d6589 | 423 | static int |
9fff138e | 424 | set_tunnel_config(struct netdev *dev_, const struct smap *args, char **errp) |
2b9d6589 | 425 | { |
b5d57fc8 BP |
426 | struct netdev_vport *dev = netdev_vport_cast(dev_); |
427 | const char *name = netdev_get_name(dev_); | |
428 | const char *type = netdev_get_type(dev_); | |
9fff138e | 429 | struct ds errors = DS_EMPTY_INITIALIZER; |
875ab130 | 430 | bool needs_dst_port, has_csum; |
3ae91c01 | 431 | uint16_t dst_proto = 0, src_proto = 0; |
f431bf7d | 432 | struct netdev_tunnel_config tnl_cfg; |
79f1cbe9 | 433 | struct smap_node *node; |
9fff138e | 434 | int err; |
f431bf7d | 435 | |
4752cc0c | 436 | has_csum = strstr(type, "gre") || strstr(type, "geneve") || |
4237026e | 437 | strstr(type, "stt") || strstr(type, "vxlan"); |
f431bf7d | 438 | memset(&tnl_cfg, 0, sizeof tnl_cfg); |
2b9d6589 | 439 | |
a36de779 PS |
440 | /* Add a default destination port for tunnel ports if none specified. */ |
441 | if (!strcmp(type, "geneve")) { | |
442 | tnl_cfg.dst_port = htons(GENEVE_DST_PORT); | |
443 | } | |
444 | ||
445 | if (!strcmp(type, "vxlan")) { | |
446 | tnl_cfg.dst_port = htons(VXLAN_DST_PORT); | |
447 | } | |
448 | ||
449 | if (!strcmp(type, "lisp")) { | |
450 | tnl_cfg.dst_port = htons(LISP_DST_PORT); | |
451 | } | |
452 | ||
4237026e PS |
453 | if (!strcmp(type, "stt")) { |
454 | tnl_cfg.dst_port = htons(STT_DST_PORT); | |
455 | } | |
456 | ||
a6ae068b | 457 | needs_dst_port = netdev_vport_needs_dst_port(dev_); |
f431bf7d | 458 | tnl_cfg.dont_fragment = true; |
e16a28b5 | 459 | |
79f1cbe9 EJ |
460 | SMAP_FOR_EACH (node, args) { |
461 | if (!strcmp(node->key, "remote_ip")) { | |
3ae91c01 JB |
462 | err = parse_tunnel_ip(node->value, false, &tnl_cfg.ip_dst_flow, |
463 | &tnl_cfg.ipv6_dst, &dst_proto); | |
464 | switch (err) { | |
465 | case ENOENT: | |
9fff138e | 466 | ds_put_format(&errors, "%s: bad %s 'remote_ip'\n", name, type); |
3ae91c01 JB |
467 | break; |
468 | case EINVAL: | |
9fff138e DDP |
469 | ds_put_format(&errors, |
470 | "%s: multicast remote_ip=%s not allowed\n", | |
471 | name, node->value); | |
472 | goto out; | |
2b9d6589 | 473 | } |
79f1cbe9 | 474 | } else if (!strcmp(node->key, "local_ip")) { |
3ae91c01 JB |
475 | err = parse_tunnel_ip(node->value, true, &tnl_cfg.ip_src_flow, |
476 | &tnl_cfg.ipv6_src, &src_proto); | |
477 | switch (err) { | |
478 | case ENOENT: | |
9fff138e | 479 | ds_put_format(&errors, "%s: bad %s 'local_ip'\n", name, type); |
3ae91c01 | 480 | break; |
2b9d6589 | 481 | } |
79f1cbe9 EJ |
482 | } else if (!strcmp(node->key, "tos")) { |
483 | if (!strcmp(node->value, "inherit")) { | |
f431bf7d | 484 | tnl_cfg.tos_inherit = true; |
2b9d6589 | 485 | } else { |
3fca7064 PS |
486 | char *endptr; |
487 | int tos; | |
79f1cbe9 | 488 | tos = strtol(node->value, &endptr, 0); |
91aff446 | 489 | if (*endptr == '\0' && tos == (tos & IP_DSCP_MASK)) { |
f431bf7d | 490 | tnl_cfg.tos = tos; |
91aff446 | 491 | } else { |
9fff138e DDP |
492 | ds_put_format(&errors, "%s: invalid TOS %s\n", name, |
493 | node->value); | |
3fca7064 | 494 | } |
2b9d6589 | 495 | } |
79f1cbe9 EJ |
496 | } else if (!strcmp(node->key, "ttl")) { |
497 | if (!strcmp(node->value, "inherit")) { | |
f431bf7d | 498 | tnl_cfg.ttl_inherit = true; |
2b9d6589 | 499 | } else { |
f431bf7d | 500 | tnl_cfg.ttl = atoi(node->value); |
2b9d6589 | 501 | } |
79f827fa | 502 | } else if (!strcmp(node->key, "dst_port") && needs_dst_port) { |
f431bf7d | 503 | tnl_cfg.dst_port = htons(atoi(node->value)); |
f431bf7d | 504 | } else if (!strcmp(node->key, "csum") && has_csum) { |
79f1cbe9 | 505 | if (!strcmp(node->value, "true")) { |
f431bf7d | 506 | tnl_cfg.csum = true; |
2b9d6589 | 507 | } |
79f1cbe9 EJ |
508 | } else if (!strcmp(node->key, "df_default")) { |
509 | if (!strcmp(node->value, "false")) { | |
f431bf7d | 510 | tnl_cfg.dont_fragment = false; |
66409d1b | 511 | } |
79f1cbe9 EJ |
512 | } else if (!strcmp(node->key, "key") || |
513 | !strcmp(node->key, "in_key") || | |
875ab130 BP |
514 | !strcmp(node->key, "out_key") || |
515 | !strcmp(node->key, "packet_type")) { | |
c19e6535 | 516 | /* Handled separately below. */ |
875ab130 | 517 | } else if (!strcmp(node->key, "exts") && !strcmp(type, "vxlan")) { |
526df7d8 TG |
518 | char *str = xstrdup(node->value); |
519 | char *ext, *save_ptr = NULL; | |
520 | ||
521 | tnl_cfg.exts = 0; | |
522 | ||
523 | ext = strtok_r(str, ",", &save_ptr); | |
524 | while (ext) { | |
525 | if (!strcmp(type, "vxlan") && !strcmp(ext, "gbp")) { | |
526 | tnl_cfg.exts |= (1 << OVS_VXLAN_EXT_GBP); | |
439f39cb GS |
527 | } else if (!strcmp(type, "vxlan") && !strcmp(ext, "gpe")) { |
528 | tnl_cfg.exts |= (1 << OVS_VXLAN_EXT_GPE); | |
526df7d8 | 529 | } else { |
9fff138e DDP |
530 | ds_put_format(&errors, "%s: unknown extension '%s'\n", |
531 | name, ext); | |
526df7d8 TG |
532 | } |
533 | ||
534 | ext = strtok_r(NULL, ",", &save_ptr); | |
535 | } | |
536 | ||
537 | free(str); | |
bf4bbd0d PS |
538 | } else if (!strcmp(node->key, "egress_pkt_mark")) { |
539 | tnl_cfg.egress_pkt_mark = strtoul(node->value, NULL, 10); | |
540 | tnl_cfg.set_egress_pkt_mark = true; | |
2b9d6589 | 541 | } else { |
439f39cb GS |
542 | ds_put_format(&errors, "%s: unknown %s argument '%s'\n", name, |
543 | type, node->key); | |
2b9d6589 BP |
544 | } |
545 | } | |
546 | ||
875ab130 BP |
547 | enum tunnel_layers layers = tunnel_supported_layers(type, &tnl_cfg); |
548 | const char *full_type = (strcmp(type, "vxlan") ? type | |
ea4cb65c EG |
549 | : (tnl_cfg.exts & (1 << OVS_VXLAN_EXT_GPE) |
550 | ? "VXLAN-GPE" : "VXLAN (without GPE")); | |
875ab130 BP |
551 | const char *packet_type = smap_get(args, "packet_type"); |
552 | if (!packet_type) { | |
553 | tnl_cfg.pt_mode = default_pt_mode(layers); | |
554 | } else if (!strcmp(packet_type, "legacy_l2")) { | |
555 | tnl_cfg.pt_mode = NETDEV_PT_LEGACY_L2; | |
556 | if (!(layers & TNL_L2)) { | |
557 | ds_put_format(&errors, "%s: legacy_l2 configured on %s tunnel " | |
558 | "that cannot carry L2 traffic\n", | |
559 | name, full_type); | |
560 | err = EINVAL; | |
561 | goto out; | |
562 | } | |
563 | } else if (!strcmp(packet_type, "legacy_l3")) { | |
564 | tnl_cfg.pt_mode = NETDEV_PT_LEGACY_L3; | |
565 | if (!(layers & TNL_L3)) { | |
566 | ds_put_format(&errors, "%s: legacy_l3 configured on %s tunnel " | |
567 | "that cannot carry L3 traffic\n", | |
568 | name, full_type); | |
569 | err = EINVAL; | |
570 | goto out; | |
571 | } | |
572 | } else if (!strcmp(packet_type, "ptap")) { | |
573 | tnl_cfg.pt_mode = NETDEV_PT_AWARE; | |
574 | } else { | |
575 | ds_put_format(&errors, "%s: unknown packet_type '%s'\n", | |
576 | name, packet_type); | |
577 | err = EINVAL; | |
578 | goto out; | |
439f39cb GS |
579 | } |
580 | ||
3ae91c01 | 581 | if (!ipv6_addr_is_set(&tnl_cfg.ipv6_dst) && !tnl_cfg.ip_dst_flow) { |
9fff138e DDP |
582 | ds_put_format(&errors, |
583 | "%s: %s type requires valid 'remote_ip' argument\n", | |
584 | name, type); | |
585 | err = EINVAL; | |
586 | goto out; | |
2b9d6589 | 587 | } |
0ad90c84 | 588 | if (tnl_cfg.ip_src_flow && !tnl_cfg.ip_dst_flow) { |
9fff138e DDP |
589 | ds_put_format(&errors, |
590 | "%s: %s type requires 'remote_ip=flow' " | |
591 | "with 'local_ip=flow'\n", | |
592 | name, type); | |
593 | err = EINVAL; | |
594 | goto out; | |
0ad90c84 | 595 | } |
3ae91c01 | 596 | if (src_proto && dst_proto && src_proto != dst_proto) { |
9fff138e DDP |
597 | ds_put_format(&errors, |
598 | "%s: 'remote_ip' and 'local_ip' " | |
599 | "has to be of the same address family\n", | |
600 | name); | |
601 | err = EINVAL; | |
602 | goto out; | |
3ae91c01 | 603 | } |
f431bf7d EJ |
604 | if (!tnl_cfg.ttl) { |
605 | tnl_cfg.ttl = DEFAULT_TTL; | |
606 | } | |
607 | ||
608 | tnl_cfg.in_key = parse_key(args, "in_key", | |
609 | &tnl_cfg.in_key_present, | |
610 | &tnl_cfg.in_key_flow); | |
f431bf7d EJ |
611 | |
612 | tnl_cfg.out_key = parse_key(args, "out_key", | |
613 | &tnl_cfg.out_key_present, | |
614 | &tnl_cfg.out_key_flow); | |
2b9d6589 | 615 | |
86383816 | 616 | ovs_mutex_lock(&dev->mutex); |
a1908399 AW |
617 | if (memcmp(&dev->tnl_cfg, &tnl_cfg, sizeof tnl_cfg)) { |
618 | dev->tnl_cfg = tnl_cfg; | |
619 | tunnel_check_status_change__(dev); | |
620 | netdev_change_seq_changed(dev_); | |
621 | } | |
86383816 | 622 | ovs_mutex_unlock(&dev->mutex); |
f431bf7d | 623 | |
9fff138e DDP |
624 | err = 0; |
625 | ||
626 | out: | |
c296d3f8 DDP |
627 | if (errors.length) { |
628 | ds_chomp(&errors, '\n'); | |
629 | VLOG_WARN("%s", ds_cstr(&errors)); | |
630 | if (err) { | |
631 | *errp = ds_steal_cstr(&errors); | |
632 | } | |
9fff138e DDP |
633 | } |
634 | ||
635 | ds_destroy(&errors); | |
636 | ||
637 | return err; | |
c19e6535 BP |
638 | } |
639 | ||
2b9d6589 | 640 | static int |
b5d57fc8 | 641 | get_tunnel_config(const struct netdev *dev, struct smap *args) |
6d9e6eb4 | 642 | { |
86383816 | 643 | struct netdev_vport *netdev = netdev_vport_cast(dev); |
63171f04 | 644 | const char *type = netdev_get_type(dev); |
86383816 BP |
645 | struct netdev_tunnel_config tnl_cfg; |
646 | ||
647 | ovs_mutex_lock(&netdev->mutex); | |
648 | tnl_cfg = netdev->tnl_cfg; | |
649 | ovs_mutex_unlock(&netdev->mutex); | |
6d9e6eb4 | 650 | |
3ae91c01 JB |
651 | if (ipv6_addr_is_set(&tnl_cfg.ipv6_dst)) { |
652 | smap_add_ipv6(args, "remote_ip", &tnl_cfg.ipv6_dst); | |
86383816 | 653 | } else if (tnl_cfg.ip_dst_flow) { |
0ad90c84 | 654 | smap_add(args, "remote_ip", "flow"); |
0a740f48 EJ |
655 | } |
656 | ||
3ae91c01 JB |
657 | if (ipv6_addr_is_set(&tnl_cfg.ipv6_src)) { |
658 | smap_add_ipv6(args, "local_ip", &tnl_cfg.ipv6_src); | |
86383816 | 659 | } else if (tnl_cfg.ip_src_flow) { |
0ad90c84 | 660 | smap_add(args, "local_ip", "flow"); |
7f804ea5 | 661 | } |
c19e6535 | 662 | |
86383816 | 663 | if (tnl_cfg.in_key_flow && tnl_cfg.out_key_flow) { |
6d9e6eb4 | 664 | smap_add(args, "key", "flow"); |
86383816 BP |
665 | } else if (tnl_cfg.in_key_present && tnl_cfg.out_key_present |
666 | && tnl_cfg.in_key == tnl_cfg.out_key) { | |
667 | smap_add_format(args, "key", "%"PRIu64, ntohll(tnl_cfg.in_key)); | |
6d9e6eb4 | 668 | } else { |
86383816 | 669 | if (tnl_cfg.in_key_flow) { |
b9ad7294 | 670 | smap_add(args, "in_key", "flow"); |
86383816 | 671 | } else if (tnl_cfg.in_key_present) { |
b9ad7294 | 672 | smap_add_format(args, "in_key", "%"PRIu64, |
86383816 | 673 | ntohll(tnl_cfg.in_key)); |
b9ad7294 | 674 | } |
6d9e6eb4 | 675 | |
86383816 | 676 | if (tnl_cfg.out_key_flow) { |
b9ad7294 | 677 | smap_add(args, "out_key", "flow"); |
86383816 | 678 | } else if (tnl_cfg.out_key_present) { |
b9ad7294 | 679 | smap_add_format(args, "out_key", "%"PRIu64, |
86383816 | 680 | ntohll(tnl_cfg.out_key)); |
6d9e6eb4 BP |
681 | } |
682 | } | |
683 | ||
86383816 | 684 | if (tnl_cfg.ttl_inherit) { |
62827e6a | 685 | smap_add(args, "ttl", "inherit"); |
86383816 BP |
686 | } else if (tnl_cfg.ttl != DEFAULT_TTL) { |
687 | smap_add_format(args, "ttl", "%"PRIu8, tnl_cfg.ttl); | |
c19e6535 BP |
688 | } |
689 | ||
86383816 | 690 | if (tnl_cfg.tos_inherit) { |
6d9e6eb4 | 691 | smap_add(args, "tos", "inherit"); |
86383816 BP |
692 | } else if (tnl_cfg.tos) { |
693 | smap_add_format(args, "tos", "0x%x", tnl_cfg.tos); | |
6d9e6eb4 BP |
694 | } |
695 | ||
86383816 BP |
696 | if (tnl_cfg.dst_port) { |
697 | uint16_t dst_port = ntohs(tnl_cfg.dst_port); | |
9eeb949b | 698 | |
c1fc1411 JG |
699 | if ((!strcmp("geneve", type) && dst_port != GENEVE_DST_PORT) || |
700 | (!strcmp("vxlan", type) && dst_port != VXLAN_DST_PORT) || | |
4237026e PS |
701 | (!strcmp("lisp", type) && dst_port != LISP_DST_PORT) || |
702 | (!strcmp("stt", type) && dst_port != STT_DST_PORT)) { | |
79f827fa KM |
703 | smap_add_format(args, "dst_port", "%d", dst_port); |
704 | } | |
705 | } | |
706 | ||
86383816 | 707 | if (tnl_cfg.csum) { |
6d9e6eb4 BP |
708 | smap_add(args, "csum", "true"); |
709 | } | |
8a9ff93a | 710 | |
875ab130 BP |
711 | enum tunnel_layers layers = tunnel_supported_layers(type, &tnl_cfg); |
712 | if (tnl_cfg.pt_mode != default_pt_mode(layers)) { | |
713 | smap_add(args, "packet_type", | |
714 | tnl_cfg.pt_mode == NETDEV_PT_LEGACY_L2 ? "legacy_l2" | |
715 | : tnl_cfg.pt_mode == NETDEV_PT_LEGACY_L3 ? "legacy_l3" | |
716 | : "ptap"); | |
63171f04 JS |
717 | } |
718 | ||
86383816 | 719 | if (!tnl_cfg.dont_fragment) { |
66409d1b AE |
720 | smap_add(args, "df_default", "false"); |
721 | } | |
6d9e6eb4 | 722 | |
bf4bbd0d PS |
723 | if (tnl_cfg.set_egress_pkt_mark) { |
724 | smap_add_format(args, "egress_pkt_mark", | |
725 | "%"PRIu32, tnl_cfg.egress_pkt_mark); | |
726 | } | |
6d9e6eb4 BP |
727 | return 0; |
728 | } | |
0a740f48 EJ |
729 | \f |
730 | /* Code specific to patch ports. */ | |
731 | ||
161b6042 BP |
732 | /* If 'netdev' is a patch port, returns the name of its peer as a malloc()'d |
733 | * string that the caller must free. | |
734 | * | |
735 | * If 'netdev' is not a patch port, returns NULL. */ | |
736 | char * | |
737 | netdev_vport_patch_peer(const struct netdev *netdev_) | |
0a740f48 | 738 | { |
161b6042 BP |
739 | char *peer = NULL; |
740 | ||
741 | if (netdev_vport_is_patch(netdev_)) { | |
742 | struct netdev_vport *netdev = netdev_vport_cast(netdev_); | |
86383816 BP |
743 | |
744 | ovs_mutex_lock(&netdev->mutex); | |
161b6042 BP |
745 | if (netdev->peer) { |
746 | peer = xstrdup(netdev->peer); | |
747 | } | |
86383816 | 748 | ovs_mutex_unlock(&netdev->mutex); |
161b6042 BP |
749 | } |
750 | ||
751 | return peer; | |
0a740f48 EJ |
752 | } |
753 | ||
754 | void | |
b9ad7294 | 755 | netdev_vport_inc_rx(const struct netdev *netdev, |
9e04d6f6 | 756 | const struct dpif_flow_stats *stats) |
0a740f48 | 757 | { |
b5d57fc8 BP |
758 | if (is_vport_class(netdev_get_class(netdev))) { |
759 | struct netdev_vport *dev = netdev_vport_cast(netdev); | |
86383816 BP |
760 | |
761 | ovs_mutex_lock(&dev->mutex); | |
0a740f48 EJ |
762 | dev->stats.rx_packets += stats->n_packets; |
763 | dev->stats.rx_bytes += stats->n_bytes; | |
86383816 | 764 | ovs_mutex_unlock(&dev->mutex); |
0a740f48 EJ |
765 | } |
766 | } | |
767 | ||
768 | void | |
b9ad7294 EJ |
769 | netdev_vport_inc_tx(const struct netdev *netdev, |
770 | const struct dpif_flow_stats *stats) | |
0a740f48 | 771 | { |
b5d57fc8 BP |
772 | if (is_vport_class(netdev_get_class(netdev))) { |
773 | struct netdev_vport *dev = netdev_vport_cast(netdev); | |
86383816 BP |
774 | |
775 | ovs_mutex_lock(&dev->mutex); | |
0a740f48 EJ |
776 | dev->stats.tx_packets += stats->n_packets; |
777 | dev->stats.tx_bytes += stats->n_bytes; | |
86383816 | 778 | ovs_mutex_unlock(&dev->mutex); |
0a740f48 EJ |
779 | } |
780 | } | |
781 | ||
782 | static int | |
b5d57fc8 | 783 | get_patch_config(const struct netdev *dev_, struct smap *args) |
0a740f48 | 784 | { |
b5d57fc8 | 785 | struct netdev_vport *dev = netdev_vport_cast(dev_); |
0a740f48 | 786 | |
86383816 | 787 | ovs_mutex_lock(&dev->mutex); |
0a740f48 EJ |
788 | if (dev->peer) { |
789 | smap_add(args, "peer", dev->peer); | |
790 | } | |
86383816 BP |
791 | ovs_mutex_unlock(&dev->mutex); |
792 | ||
0a740f48 EJ |
793 | return 0; |
794 | } | |
6d9e6eb4 BP |
795 | |
796 | static int | |
9fff138e | 797 | set_patch_config(struct netdev *dev_, const struct smap *args, char **errp) |
2b9d6589 | 798 | { |
b5d57fc8 BP |
799 | struct netdev_vport *dev = netdev_vport_cast(dev_); |
800 | const char *name = netdev_get_name(dev_); | |
2b9d6589 BP |
801 | const char *peer; |
802 | ||
79f1cbe9 | 803 | peer = smap_get(args, "peer"); |
2b9d6589 | 804 | if (!peer) { |
9fff138e DDP |
805 | VLOG_ERR_BUF(errp, "%s: patch type requires valid 'peer' argument", |
806 | name); | |
2b9d6589 BP |
807 | return EINVAL; |
808 | } | |
809 | ||
79f1cbe9 | 810 | if (smap_count(args) > 1) { |
9fff138e DDP |
811 | VLOG_ERR_BUF(errp, "%s: patch type takes only a 'peer' argument", |
812 | name); | |
2b9d6589 BP |
813 | return EINVAL; |
814 | } | |
815 | ||
2b9d6589 | 816 | if (!strcmp(name, peer)) { |
9fff138e | 817 | VLOG_ERR_BUF(errp, "%s: patch peer must not be self", name); |
2b9d6589 BP |
818 | return EINVAL; |
819 | } | |
820 | ||
86383816 | 821 | ovs_mutex_lock(&dev->mutex); |
a1908399 AW |
822 | if (!dev->peer || strcmp(dev->peer, peer)) { |
823 | free(dev->peer); | |
824 | dev->peer = xstrdup(peer); | |
825 | netdev_change_seq_changed(dev_); | |
826 | } | |
86383816 BP |
827 | ovs_mutex_unlock(&dev->mutex); |
828 | ||
2b9d6589 BP |
829 | return 0; |
830 | } | |
6d9e6eb4 BP |
831 | |
832 | static int | |
b9ad7294 | 833 | get_stats(const struct netdev *netdev, struct netdev_stats *stats) |
0a740f48 | 834 | { |
b5d57fc8 | 835 | struct netdev_vport *dev = netdev_vport_cast(netdev); |
86383816 BP |
836 | |
837 | ovs_mutex_lock(&dev->mutex); | |
d6e3feb5 | 838 | /* Passing only collected counters */ |
839 | stats->tx_packets = dev->stats.tx_packets; | |
840 | stats->tx_bytes = dev->stats.tx_bytes; | |
841 | stats->rx_packets = dev->stats.rx_packets; | |
842 | stats->rx_bytes = dev->stats.rx_bytes; | |
86383816 BP |
843 | ovs_mutex_unlock(&dev->mutex); |
844 | ||
6d9e6eb4 BP |
845 | return 0; |
846 | } | |
a36de779 | 847 | |
875ab130 BP |
848 | static enum netdev_pt_mode |
849 | get_pt_mode(const struct netdev *netdev) | |
850 | { | |
851 | struct netdev_vport *dev = netdev_vport_cast(netdev); | |
852 | ||
853 | return dev->tnl_cfg.pt_mode; | |
854 | } | |
855 | ||
856 | ||
2b9d6589 | 857 | \f |
01b25786 PB |
858 | #ifdef __linux__ |
859 | static int | |
5e530e26 | 860 | netdev_vport_get_ifindex(const struct netdev *netdev_) |
01b25786 PB |
861 | { |
862 | char buf[NETDEV_VPORT_NAME_BUFSIZE]; | |
863 | const char *name = netdev_vport_get_dpif_port(netdev_, buf, sizeof(buf)); | |
864 | ||
865 | return linux_get_ifindex(name); | |
866 | } | |
867 | ||
01b25786 PB |
868 | #define NETDEV_VPORT_GET_IFINDEX netdev_vport_get_ifindex |
869 | #define NETDEV_FLOW_OFFLOAD_API LINUX_FLOW_OFFLOAD_API | |
870 | #else /* !__linux__ */ | |
871 | #define NETDEV_VPORT_GET_IFINDEX NULL | |
872 | #define NETDEV_FLOW_OFFLOAD_API NO_OFFLOAD_API | |
873 | #endif /* __linux__ */ | |
874 | ||
0a740f48 | 875 | #define VPORT_FUNCTIONS(GET_CONFIG, SET_CONFIG, \ |
a36de779 PS |
876 | GET_TUNNEL_CONFIG, GET_STATUS, \ |
877 | BUILD_HEADER, \ | |
01b25786 PB |
878 | PUSH_HEADER, POP_HEADER, \ |
879 | GET_IFINDEX) \ | |
b46ccdf5 | 880 | NULL, \ |
ea83a2fc EJ |
881 | netdev_vport_run, \ |
882 | netdev_vport_wait, \ | |
2b9d6589 | 883 | \ |
9dc63482 BP |
884 | netdev_vport_alloc, \ |
885 | netdev_vport_construct, \ | |
886 | netdev_vport_destruct, \ | |
887 | netdev_vport_dealloc, \ | |
0a740f48 EJ |
888 | GET_CONFIG, \ |
889 | SET_CONFIG, \ | |
f431bf7d | 890 | GET_TUNNEL_CONFIG, \ |
a36de779 PS |
891 | BUILD_HEADER, \ |
892 | PUSH_HEADER, \ | |
893 | POP_HEADER, \ | |
7dec44fe | 894 | NULL, /* get_numa_id */ \ |
050c60bf | 895 | NULL, /* set_tx_multiq */ \ |
2b9d6589 | 896 | \ |
552e20d0 | 897 | NULL, /* send */ \ |
2b9d6589 BP |
898 | NULL, /* send_wait */ \ |
899 | \ | |
900 | netdev_vport_set_etheraddr, \ | |
901 | netdev_vport_get_etheraddr, \ | |
14622f22 BP |
902 | NULL, /* get_mtu */ \ |
903 | NULL, /* set_mtu */ \ | |
01b25786 | 904 | GET_IFINDEX, \ |
85da620e | 905 | NULL, /* get_carrier */ \ |
65c3058c | 906 | NULL, /* get_carrier_resets */ \ |
63331829 | 907 | NULL, /* get_miimon */ \ |
b9ad7294 | 908 | get_stats, \ |
2b9d6589 BP |
909 | \ |
910 | NULL, /* get_features */ \ | |
911 | NULL, /* set_advertisements */ \ | |
875ab130 | 912 | get_pt_mode, \ |
2b9d6589 BP |
913 | \ |
914 | NULL, /* set_policing */ \ | |
915 | NULL, /* get_qos_types */ \ | |
916 | NULL, /* get_qos_capabilities */ \ | |
917 | NULL, /* get_qos */ \ | |
918 | NULL, /* set_qos */ \ | |
919 | NULL, /* get_queue */ \ | |
920 | NULL, /* set_queue */ \ | |
921 | NULL, /* delete_queue */ \ | |
922 | NULL, /* get_queue_stats */ \ | |
89454bf4 BP |
923 | NULL, /* queue_dump_start */ \ |
924 | NULL, /* queue_dump_next */ \ | |
925 | NULL, /* queue_dump_done */ \ | |
2b9d6589 BP |
926 | NULL, /* dump_queue_stats */ \ |
927 | \ | |
2b9d6589 | 928 | NULL, /* set_in4 */ \ |
a8704b50 | 929 | NULL, /* get_addr_list */ \ |
2b9d6589 BP |
930 | NULL, /* add_router */ \ |
931 | NULL, /* get_next_hop */ \ | |
ea763e0e | 932 | GET_STATUS, \ |
2b9d6589 BP |
933 | NULL, /* arp_lookup */ \ |
934 | \ | |
935 | netdev_vport_update_flags, \ | |
790fb3b7 | 936 | NULL, /* reconfigure */ \ |
2b9d6589 | 937 | \ |
9dc63482 BP |
938 | NULL, /* rx_alloc */ \ |
939 | NULL, /* rx_construct */ \ | |
940 | NULL, /* rx_destruct */ \ | |
941 | NULL, /* rx_dealloc */ \ | |
942 | NULL, /* rx_recv */ \ | |
943 | NULL, /* rx_wait */ \ | |
18ebd48c PB |
944 | NULL, /* rx_drain */ \ |
945 | \ | |
01b25786 | 946 | NETDEV_FLOW_OFFLOAD_API |
2b9d6589 | 947 | |
a36de779 | 948 | |
01b25786 PB |
949 | #define TUNNEL_CLASS(NAME, DPIF_PORT, BUILD_HEADER, PUSH_HEADER, POP_HEADER, \ |
950 | GET_IFINDEX) \ | |
a36de779 | 951 | { DPIF_PORT, \ |
118c77b1 IM |
952 | { NAME, false, \ |
953 | VPORT_FUNCTIONS(get_tunnel_config, \ | |
954 | set_tunnel_config, \ | |
955 | get_netdev_tunnel_config, \ | |
956 | tunnel_get_status, \ | |
01b25786 PB |
957 | BUILD_HEADER, PUSH_HEADER, POP_HEADER, \ |
958 | GET_IFINDEX) }} | |
db078f85 | 959 | |
2b9d6589 | 960 | void |
c060c4cf | 961 | netdev_vport_tunnel_register(void) |
2b9d6589 | 962 | { |
a5d4fadd JG |
963 | /* The name of the dpif_port should be short enough to accomodate adding |
964 | * a port number to the end if one is necessary. */ | |
c3827f61 | 965 | static const struct vport_class vport_classes[] = { |
e5a1caee | 966 | TUNNEL_CLASS("geneve", "genev_sys", netdev_geneve_build_header, |
6b241d64 | 967 | netdev_tnl_push_udp_header, |
01b25786 PB |
968 | netdev_geneve_pop_header, |
969 | NULL), | |
a36de779 PS |
970 | TUNNEL_CLASS("gre", "gre_sys", netdev_gre_build_header, |
971 | netdev_gre_push_header, | |
01b25786 PB |
972 | netdev_gre_pop_header, |
973 | NULL), | |
a36de779 | 974 | TUNNEL_CLASS("vxlan", "vxlan_sys", netdev_vxlan_build_header, |
6b241d64 | 975 | netdev_tnl_push_udp_header, |
01b25786 PB |
976 | netdev_vxlan_pop_header, |
977 | NETDEV_VPORT_GET_IFINDEX), | |
978 | TUNNEL_CLASS("lisp", "lisp_sys", NULL, NULL, NULL, NULL), | |
979 | TUNNEL_CLASS("stt", "stt_sys", NULL, NULL, NULL, NULL), | |
c3827f61 | 980 | }; |
86383816 | 981 | static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; |
c3827f61 | 982 | |
86383816 BP |
983 | if (ovsthread_once_start(&once)) { |
984 | int i; | |
c3827f61 | 985 | |
7c54c27f BP |
986 | for (i = 0; i < ARRAY_SIZE(vport_classes); i++) { |
987 | netdev_register_provider(&vport_classes[i].netdev_class); | |
988 | } | |
a36de779 PS |
989 | |
990 | unixctl_command_register("tnl/egress_port_range", "min max", 0, 2, | |
6b241d64 | 991 | netdev_tnl_egress_port_range, NULL); |
a36de779 | 992 | |
86383816 | 993 | ovsthread_once_done(&once); |
c3827f61 | 994 | } |
2b9d6589 | 995 | } |
c060c4cf EJ |
996 | |
997 | void | |
998 | netdev_vport_patch_register(void) | |
999 | { | |
1000 | static const struct vport_class patch_class = | |
1001 | { NULL, | |
118c77b1 IM |
1002 | { "patch", false, |
1003 | VPORT_FUNCTIONS(get_patch_config, | |
1004 | set_patch_config, | |
1005 | NULL, | |
01b25786 | 1006 | NULL, NULL, NULL, NULL, NULL) }}; |
c060c4cf EJ |
1007 | netdev_register_provider(&patch_class.netdev_class); |
1008 | } |