]>
Commit | Line | Data |
---|---|---|
777ece09 | 1 | /* |
b2f771ef | 2 | * Copyright (c) 2010, 2011, 2012, 2013, 2014 Nicira, Inc. |
777ece09 JG |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | * you may not use this file except in compliance with the License. | |
6 | * You may obtain a copy of the License at: | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
6fcfff1b | 10 | * Unless required by applicable law or agreed to in writing, software |
777ece09 JG |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | ||
17 | #include <config.h> | |
2b9d6589 BP |
18 | |
19 | #include "netdev-vport.h" | |
20 | ||
777ece09 JG |
21 | #include <errno.h> |
22 | #include <fcntl.h> | |
ea83a2fc | 23 | #include <sys/socket.h> |
2b9d6589 | 24 | #include <net/if.h> |
777ece09 JG |
25 | #include <sys/ioctl.h> |
26 | ||
b9298d3f | 27 | #include "byte-order.h" |
5059eff3 JP |
28 | #include "daemon.h" |
29 | #include "dirs.h" | |
0a740f48 | 30 | #include "dpif.h" |
ea83a2fc EJ |
31 | #include "hash.h" |
32 | #include "hmap.h" | |
777ece09 | 33 | #include "list.h" |
2b9d6589 | 34 | #include "netdev-provider.h" |
ea83a2fc | 35 | #include "ofpbuf.h" |
d9b4ebc5 | 36 | #include "ovs-router.h" |
2b9d6589 | 37 | #include "packets.h" |
41ca1e0a | 38 | #include "poll-loop.h" |
a132aa96 | 39 | #include "route-table.h" |
777ece09 JG |
40 | #include "shash.h" |
41 | #include "socket-util.h" | |
777ece09 JG |
42 | #include "vlog.h" |
43 | ||
d98e6007 | 44 | VLOG_DEFINE_THIS_MODULE(netdev_vport); |
5136ce49 | 45 | |
c1fc1411 | 46 | #define GENEVE_DST_PORT 6081 |
4f2abb7b | 47 | #define VXLAN_DST_PORT 4789 |
a6ae068b LJ |
48 | #define LISP_DST_PORT 4341 |
49 | ||
f431bf7d EJ |
50 | #define DEFAULT_TTL 64 |
51 | ||
b5d57fc8 BP |
52 | struct netdev_vport { |
53 | struct netdev up; | |
86383816 BP |
54 | |
55 | /* Protects all members below. */ | |
56 | struct ovs_mutex mutex; | |
57 | ||
35b769cb | 58 | uint8_t etheraddr[ETH_ADDR_LEN]; |
b9ad7294 | 59 | struct netdev_stats stats; |
0a740f48 EJ |
60 | |
61 | /* Tunnels. */ | |
f431bf7d | 62 | struct netdev_tunnel_config tnl_cfg; |
41ca1e0a AW |
63 | char egress_iface[IFNAMSIZ]; |
64 | bool carrier_status; | |
0a740f48 EJ |
65 | |
66 | /* Patch Ports. */ | |
0a740f48 | 67 | char *peer; |
2b9d6589 BP |
68 | }; |
69 | ||
2b9d6589 | 70 | struct vport_class { |
b9ad7294 | 71 | const char *dpif_port; |
c3827f61 | 72 | struct netdev_class netdev_class; |
2b9d6589 BP |
73 | }; |
74 | ||
41ca1e0a AW |
75 | /* Last read of the route-table's change number. */ |
76 | static uint64_t rt_change_seqno; | |
77 | ||
9dc63482 | 78 | static int netdev_vport_construct(struct netdev *); |
86383816 | 79 | static int get_patch_config(const struct netdev *netdev, struct smap *args); |
b5d57fc8 | 80 | static int get_tunnel_config(const struct netdev *, struct smap *args); |
41ca1e0a | 81 | static bool tunnel_check_status_change__(struct netdev_vport *); |
2b9d6589 BP |
82 | |
83 | static bool | |
84 | is_vport_class(const struct netdev_class *class) | |
777ece09 | 85 | { |
9dc63482 | 86 | return class->construct == netdev_vport_construct; |
2b9d6589 | 87 | } |
777ece09 | 88 | |
41ca1e0a AW |
89 | bool |
90 | netdev_vport_is_vport_class(const struct netdev_class *class) | |
91 | { | |
92 | return is_vport_class(class); | |
93 | } | |
94 | ||
2b9d6589 BP |
95 | static const struct vport_class * |
96 | vport_class_cast(const struct netdev_class *class) | |
97 | { | |
cb22974d | 98 | ovs_assert(is_vport_class(class)); |
2b9d6589 BP |
99 | return CONTAINER_OF(class, struct vport_class, netdev_class); |
100 | } | |
101 | ||
b5d57fc8 BP |
102 | static struct netdev_vport * |
103 | netdev_vport_cast(const struct netdev *netdev) | |
2b9d6589 | 104 | { |
b5d57fc8 BP |
105 | ovs_assert(is_vport_class(netdev_get_class(netdev))); |
106 | return CONTAINER_OF(netdev, struct netdev_vport, up); | |
df67d7ae EJ |
107 | } |
108 | ||
f431bf7d | 109 | static const struct netdev_tunnel_config * |
b5d57fc8 | 110 | get_netdev_tunnel_config(const struct netdev *netdev) |
f431bf7d | 111 | { |
b5d57fc8 | 112 | return &netdev_vport_cast(netdev)->tnl_cfg; |
f431bf7d EJ |
113 | } |
114 | ||
0a740f48 EJ |
115 | bool |
116 | netdev_vport_is_patch(const struct netdev *netdev) | |
117 | { | |
b5d57fc8 | 118 | const struct netdev_class *class = netdev_get_class(netdev); |
f18a39b7 | 119 | |
c060c4cf | 120 | return class->get_config == get_patch_config; |
0a740f48 EJ |
121 | } |
122 | ||
a6363cfd LJ |
123 | bool |
124 | netdev_vport_is_layer3(const struct netdev *dev) | |
125 | { | |
126 | const char *type = netdev_get_type(dev); | |
127 | ||
128 | return (!strcmp("lisp", type)); | |
129 | } | |
130 | ||
56b11f0b | 131 | static bool |
b5d57fc8 | 132 | netdev_vport_needs_dst_port(const struct netdev *dev) |
56b11f0b | 133 | { |
b5d57fc8 BP |
134 | const struct netdev_class *class = netdev_get_class(dev); |
135 | const char *type = netdev_get_type(dev); | |
56b11f0b | 136 | |
a6ae068b | 137 | return (class->get_config == get_tunnel_config && |
c1fc1411 JG |
138 | (!strcmp("geneve", type) || !strcmp("vxlan", type) || |
139 | !strcmp("lisp", type))); | |
56b11f0b KM |
140 | } |
141 | ||
94a53842 AW |
142 | const char * |
143 | netdev_vport_class_get_dpif_port(const struct netdev_class *class) | |
144 | { | |
145 | return is_vport_class(class) ? vport_class_cast(class)->dpif_port : NULL; | |
146 | } | |
147 | ||
de281153 | 148 | const char * |
3aa30359 BP |
149 | netdev_vport_get_dpif_port(const struct netdev *netdev, |
150 | char namebuf[], size_t bufsize) | |
de281153 | 151 | { |
a5d4fadd JG |
152 | const struct netdev_class *class = netdev_get_class(netdev); |
153 | const char *dpif_port = netdev_vport_class_get_dpif_port(class); | |
154 | ||
155 | if (!dpif_port) { | |
156 | return netdev_get_name(netdev); | |
157 | } | |
158 | ||
b5d57fc8 BP |
159 | if (netdev_vport_needs_dst_port(netdev)) { |
160 | const struct netdev_vport *vport = netdev_vport_cast(netdev); | |
56b11f0b KM |
161 | |
162 | /* | |
a5d4fadd JG |
163 | * Note: IFNAMSIZ is 16 bytes long. Implementations should choose |
164 | * a dpif port name that is short enough to fit including any | |
165 | * port numbers but assert just in case. | |
56b11f0b | 166 | */ |
3aa30359 | 167 | BUILD_ASSERT(NETDEV_VPORT_NAME_BUFSIZE >= IFNAMSIZ); |
a5d4fadd JG |
168 | ovs_assert(strlen(dpif_port) + 6 < IFNAMSIZ); |
169 | snprintf(namebuf, bufsize, "%s_%d", dpif_port, | |
56b11f0b | 170 | ntohs(vport->tnl_cfg.dst_port)); |
3aa30359 | 171 | return namebuf; |
56b11f0b | 172 | } else { |
a5d4fadd | 173 | return dpif_port; |
56b11f0b | 174 | } |
2b9d6589 | 175 | } |
777ece09 | 176 | |
3aa30359 BP |
177 | char * |
178 | netdev_vport_get_dpif_port_strdup(const struct netdev *netdev) | |
179 | { | |
180 | char namebuf[NETDEV_VPORT_NAME_BUFSIZE]; | |
181 | ||
182 | return xstrdup(netdev_vport_get_dpif_port(netdev, namebuf, | |
183 | sizeof namebuf)); | |
184 | } | |
185 | ||
41ca1e0a AW |
186 | /* Whenever the route-table change number is incremented, |
187 | * netdev_vport_route_changed() should be called to update | |
188 | * the corresponding tunnel interface status. */ | |
189 | static void | |
190 | netdev_vport_route_changed(void) | |
191 | { | |
192 | struct netdev **vports; | |
193 | size_t i, n_vports; | |
194 | ||
195 | vports = netdev_get_vports(&n_vports); | |
196 | for (i = 0; i < n_vports; i++) { | |
197 | struct netdev *netdev_ = vports[i]; | |
198 | struct netdev_vport *netdev = netdev_vport_cast(netdev_); | |
199 | ||
200 | ovs_mutex_lock(&netdev->mutex); | |
201 | /* Finds all tunnel vports. */ | |
202 | if (netdev->tnl_cfg.ip_dst) { | |
203 | if (tunnel_check_status_change__(netdev)) { | |
204 | netdev_change_seq_changed(netdev_); | |
205 | } | |
206 | } | |
41ca1e0a | 207 | ovs_mutex_unlock(&netdev->mutex); |
b2f771ef BP |
208 | |
209 | netdev_close(netdev_); | |
41ca1e0a AW |
210 | } |
211 | ||
212 | free(vports); | |
213 | } | |
214 | ||
9dc63482 BP |
215 | static struct netdev * |
216 | netdev_vport_alloc(void) | |
217 | { | |
218 | struct netdev_vport *netdev = xzalloc(sizeof *netdev); | |
219 | return &netdev->up; | |
220 | } | |
221 | ||
2b9d6589 | 222 | static int |
9dc63482 | 223 | netdev_vport_construct(struct netdev *netdev_) |
2b9d6589 | 224 | { |
9dc63482 | 225 | struct netdev_vport *netdev = netdev_vport_cast(netdev_); |
6d9e6eb4 | 226 | |
834d6caf | 227 | ovs_mutex_init(&netdev->mutex); |
9dc63482 | 228 | eth_addr_random(netdev->etheraddr); |
6d9e6eb4 | 229 | |
de5cdb90 | 230 | route_table_register(); |
6d9e6eb4 | 231 | |
de5cdb90 | 232 | return 0; |
777ece09 JG |
233 | } |
234 | ||
2b9d6589 | 235 | static void |
9dc63482 | 236 | netdev_vport_destruct(struct netdev *netdev_) |
2b9d6589 | 237 | { |
b5d57fc8 | 238 | struct netdev_vport *netdev = netdev_vport_cast(netdev_); |
2b9d6589 | 239 | |
a132aa96 | 240 | route_table_unregister(); |
b5d57fc8 | 241 | free(netdev->peer); |
86383816 | 242 | ovs_mutex_destroy(&netdev->mutex); |
9dc63482 BP |
243 | } |
244 | ||
245 | static void | |
246 | netdev_vport_dealloc(struct netdev *netdev_) | |
247 | { | |
248 | struct netdev_vport *netdev = netdev_vport_cast(netdev_); | |
2b9d6589 BP |
249 | free(netdev); |
250 | } | |
251 | ||
2b9d6589 | 252 | static int |
b5d57fc8 | 253 | netdev_vport_set_etheraddr(struct netdev *netdev_, |
777ece09 JG |
254 | const uint8_t mac[ETH_ADDR_LEN]) |
255 | { | |
b5d57fc8 | 256 | struct netdev_vport *netdev = netdev_vport_cast(netdev_); |
86383816 BP |
257 | |
258 | ovs_mutex_lock(&netdev->mutex); | |
b5d57fc8 | 259 | memcpy(netdev->etheraddr, mac, ETH_ADDR_LEN); |
86383816 | 260 | ovs_mutex_unlock(&netdev->mutex); |
3e912ffc | 261 | netdev_change_seq_changed(netdev_); |
86383816 | 262 | |
35b769cb | 263 | return 0; |
777ece09 JG |
264 | } |
265 | ||
2b9d6589 | 266 | static int |
86383816 | 267 | netdev_vport_get_etheraddr(const struct netdev *netdev_, |
777ece09 JG |
268 | uint8_t mac[ETH_ADDR_LEN]) |
269 | { | |
86383816 BP |
270 | struct netdev_vport *netdev = netdev_vport_cast(netdev_); |
271 | ||
272 | ovs_mutex_lock(&netdev->mutex); | |
273 | memcpy(mac, netdev->etheraddr, ETH_ADDR_LEN); | |
274 | ovs_mutex_unlock(&netdev->mutex); | |
275 | ||
35b769cb | 276 | return 0; |
777ece09 JG |
277 | } |
278 | ||
41ca1e0a AW |
279 | /* Checks if the tunnel status has changed and returns a boolean. |
280 | * Updates the tunnel status if it has changed. */ | |
281 | static bool | |
282 | tunnel_check_status_change__(struct netdev_vport *netdev) | |
283 | OVS_REQUIRES(netdev->mutex) | |
ea763e0e | 284 | { |
3dea0874 | 285 | char iface[IFNAMSIZ]; |
41ca1e0a | 286 | bool status = false; |
275707c3 | 287 | ovs_be32 route; |
d9b4ebc5 | 288 | ovs_be32 gw; |
ea763e0e | 289 | |
41ca1e0a | 290 | iface[0] = '\0'; |
86383816 | 291 | route = netdev->tnl_cfg.ip_dst; |
d9b4ebc5 | 292 | if (ovs_router_lookup(route, iface, &gw)) { |
a404826e AE |
293 | struct netdev *egress_netdev; |
294 | ||
18812dff | 295 | if (!netdev_open(iface, "system", &egress_netdev)) { |
41ca1e0a | 296 | status = netdev_get_carrier(egress_netdev); |
a404826e AE |
297 | netdev_close(egress_netdev); |
298 | } | |
ea763e0e EJ |
299 | } |
300 | ||
41ca1e0a AW |
301 | if (strcmp(netdev->egress_iface, iface) |
302 | || netdev->carrier_status != status) { | |
303 | ovs_strlcpy(netdev->egress_iface, iface, IFNAMSIZ); | |
304 | netdev->carrier_status = status; | |
305 | ||
306 | return true; | |
307 | } | |
308 | ||
309 | return false; | |
310 | } | |
311 | ||
312 | static int | |
313 | tunnel_get_status(const struct netdev *netdev_, struct smap *smap) | |
314 | { | |
315 | struct netdev_vport *netdev = netdev_vport_cast(netdev_); | |
316 | ||
317 | if (netdev->egress_iface[0]) { | |
318 | smap_add(smap, "tunnel_egress_iface", netdev->egress_iface); | |
319 | ||
320 | smap_add(smap, "tunnel_egress_iface_carrier", | |
321 | netdev->carrier_status ? "up" : "down"); | |
322 | } | |
323 | ||
ea763e0e EJ |
324 | return 0; |
325 | } | |
326 | ||
2b9d6589 | 327 | static int |
b5d57fc8 BP |
328 | netdev_vport_update_flags(struct netdev *netdev OVS_UNUSED, |
329 | enum netdev_flags off, | |
330 | enum netdev_flags on OVS_UNUSED, | |
331 | enum netdev_flags *old_flagsp) | |
777ece09 JG |
332 | { |
333 | if (off & (NETDEV_UP | NETDEV_PROMISC)) { | |
334 | return EOPNOTSUPP; | |
335 | } | |
336 | ||
337 | *old_flagsp = NETDEV_UP | NETDEV_PROMISC; | |
338 | return 0; | |
339 | } | |
340 | ||
ea83a2fc EJ |
341 | static void |
342 | netdev_vport_run(void) | |
343 | { | |
41ca1e0a AW |
344 | uint64_t seq; |
345 | ||
a132aa96 | 346 | route_table_run(); |
41ca1e0a AW |
347 | seq = route_table_get_change_seq(); |
348 | if (rt_change_seqno != seq) { | |
349 | rt_change_seqno = seq; | |
350 | netdev_vport_route_changed(); | |
351 | } | |
ea83a2fc EJ |
352 | } |
353 | ||
354 | static void | |
355 | netdev_vport_wait(void) | |
356 | { | |
41ca1e0a AW |
357 | uint64_t seq; |
358 | ||
a132aa96 | 359 | route_table_wait(); |
41ca1e0a AW |
360 | seq = route_table_get_change_seq(); |
361 | if (rt_change_seqno != seq) { | |
362 | poll_immediate_wake(); | |
363 | } | |
ea83a2fc EJ |
364 | } |
365 | \f | |
0a740f48 | 366 | /* Code specific to tunnel types. */ |
2b9d6589 | 367 | |
f431bf7d EJ |
368 | static ovs_be64 |
369 | parse_key(const struct smap *args, const char *name, | |
370 | bool *present, bool *flow) | |
c19e6535 BP |
371 | { |
372 | const char *s; | |
373 | ||
f431bf7d EJ |
374 | *present = false; |
375 | *flow = false; | |
376 | ||
79f1cbe9 | 377 | s = smap_get(args, name); |
c19e6535 | 378 | if (!s) { |
79f1cbe9 | 379 | s = smap_get(args, "key"); |
c19e6535 | 380 | if (!s) { |
f431bf7d | 381 | return 0; |
c19e6535 BP |
382 | } |
383 | } | |
384 | ||
f431bf7d EJ |
385 | *present = true; |
386 | ||
c19e6535 | 387 | if (!strcmp(s, "flow")) { |
f431bf7d EJ |
388 | *flow = true; |
389 | return 0; | |
c19e6535 | 390 | } else { |
f431bf7d | 391 | return htonll(strtoull(s, NULL, 0)); |
c19e6535 BP |
392 | } |
393 | } | |
394 | ||
2b9d6589 | 395 | static int |
b5d57fc8 | 396 | set_tunnel_config(struct netdev *dev_, const struct smap *args) |
2b9d6589 | 397 | { |
b5d57fc8 BP |
398 | struct netdev_vport *dev = netdev_vport_cast(dev_); |
399 | const char *name = netdev_get_name(dev_); | |
400 | const char *type = netdev_get_type(dev_); | |
f431bf7d EJ |
401 | bool ipsec_mech_set, needs_dst_port, has_csum; |
402 | struct netdev_tunnel_config tnl_cfg; | |
79f1cbe9 | 403 | struct smap_node *node; |
f431bf7d | 404 | |
f431bf7d EJ |
405 | has_csum = strstr(type, "gre"); |
406 | ipsec_mech_set = false; | |
407 | memset(&tnl_cfg, 0, sizeof tnl_cfg); | |
2b9d6589 | 408 | |
a6ae068b | 409 | needs_dst_port = netdev_vport_needs_dst_port(dev_); |
f431bf7d | 410 | tnl_cfg.ipsec = strstr(type, "ipsec"); |
f431bf7d | 411 | tnl_cfg.dont_fragment = true; |
e16a28b5 | 412 | |
79f1cbe9 EJ |
413 | SMAP_FOR_EACH (node, args) { |
414 | if (!strcmp(node->key, "remote_ip")) { | |
2b9d6589 | 415 | struct in_addr in_addr; |
0ad90c84 JR |
416 | if (!strcmp(node->value, "flow")) { |
417 | tnl_cfg.ip_dst_flow = true; | |
418 | tnl_cfg.ip_dst = htonl(0); | |
419 | } else if (lookup_ip(node->value, &in_addr)) { | |
c3827f61 | 420 | VLOG_WARN("%s: bad %s 'remote_ip'", name, type); |
85c9de19 PS |
421 | } else if (ip_is_multicast(in_addr.s_addr)) { |
422 | VLOG_WARN("%s: multicast remote_ip="IP_FMT" not allowed", | |
423 | name, IP_ARGS(in_addr.s_addr)); | |
424 | return EINVAL; | |
2b9d6589 | 425 | } else { |
f431bf7d | 426 | tnl_cfg.ip_dst = in_addr.s_addr; |
2b9d6589 | 427 | } |
79f1cbe9 | 428 | } else if (!strcmp(node->key, "local_ip")) { |
2b9d6589 | 429 | struct in_addr in_addr; |
0ad90c84 JR |
430 | if (!strcmp(node->value, "flow")) { |
431 | tnl_cfg.ip_src_flow = true; | |
432 | tnl_cfg.ip_src = htonl(0); | |
433 | } else if (lookup_ip(node->value, &in_addr)) { | |
c3827f61 | 434 | VLOG_WARN("%s: bad %s 'local_ip'", name, type); |
2b9d6589 | 435 | } else { |
f431bf7d | 436 | tnl_cfg.ip_src = in_addr.s_addr; |
2b9d6589 | 437 | } |
79f1cbe9 EJ |
438 | } else if (!strcmp(node->key, "tos")) { |
439 | if (!strcmp(node->value, "inherit")) { | |
f431bf7d | 440 | tnl_cfg.tos_inherit = true; |
2b9d6589 | 441 | } else { |
3fca7064 PS |
442 | char *endptr; |
443 | int tos; | |
79f1cbe9 | 444 | tos = strtol(node->value, &endptr, 0); |
91aff446 | 445 | if (*endptr == '\0' && tos == (tos & IP_DSCP_MASK)) { |
f431bf7d | 446 | tnl_cfg.tos = tos; |
91aff446 BP |
447 | } else { |
448 | VLOG_WARN("%s: invalid TOS %s", name, node->value); | |
3fca7064 | 449 | } |
2b9d6589 | 450 | } |
79f1cbe9 EJ |
451 | } else if (!strcmp(node->key, "ttl")) { |
452 | if (!strcmp(node->value, "inherit")) { | |
f431bf7d | 453 | tnl_cfg.ttl_inherit = true; |
2b9d6589 | 454 | } else { |
f431bf7d | 455 | tnl_cfg.ttl = atoi(node->value); |
2b9d6589 | 456 | } |
79f827fa | 457 | } else if (!strcmp(node->key, "dst_port") && needs_dst_port) { |
f431bf7d | 458 | tnl_cfg.dst_port = htons(atoi(node->value)); |
f431bf7d | 459 | } else if (!strcmp(node->key, "csum") && has_csum) { |
79f1cbe9 | 460 | if (!strcmp(node->value, "true")) { |
f431bf7d | 461 | tnl_cfg.csum = true; |
2b9d6589 | 462 | } |
79f1cbe9 EJ |
463 | } else if (!strcmp(node->key, "df_default")) { |
464 | if (!strcmp(node->value, "false")) { | |
f431bf7d | 465 | tnl_cfg.dont_fragment = false; |
66409d1b | 466 | } |
f431bf7d | 467 | } else if (!strcmp(node->key, "peer_cert") && tnl_cfg.ipsec) { |
79f1cbe9 | 468 | if (smap_get(args, "certificate")) { |
3c52fa7b JP |
469 | ipsec_mech_set = true; |
470 | } else { | |
ef7ee76a JP |
471 | const char *use_ssl_cert; |
472 | ||
473 | /* If the "use_ssl_cert" is true, then "certificate" and | |
474 | * "private_key" will be pulled from the SSL table. The | |
475 | * use of this option is strongly discouraged, since it | |
476 | * will like be removed when multiple SSL configurations | |
477 | * are supported by OVS. | |
478 | */ | |
79f1cbe9 | 479 | use_ssl_cert = smap_get(args, "use_ssl_cert"); |
ef7ee76a | 480 | if (!use_ssl_cert || strcmp(use_ssl_cert, "true")) { |
8283e514 JP |
481 | VLOG_ERR("%s: 'peer_cert' requires 'certificate' argument", |
482 | name); | |
b9ad7294 | 483 | return EINVAL; |
ef7ee76a JP |
484 | } |
485 | ipsec_mech_set = true; | |
3c52fa7b | 486 | } |
f431bf7d | 487 | } else if (!strcmp(node->key, "psk") && tnl_cfg.ipsec) { |
2b9d6589 | 488 | ipsec_mech_set = true; |
f431bf7d | 489 | } else if (tnl_cfg.ipsec |
79f1cbe9 EJ |
490 | && (!strcmp(node->key, "certificate") |
491 | || !strcmp(node->key, "private_key") | |
492 | || !strcmp(node->key, "use_ssl_cert"))) { | |
3c52fa7b | 493 | /* Ignore options not used by the netdev. */ |
79f1cbe9 EJ |
494 | } else if (!strcmp(node->key, "key") || |
495 | !strcmp(node->key, "in_key") || | |
496 | !strcmp(node->key, "out_key")) { | |
c19e6535 | 497 | /* Handled separately below. */ |
2b9d6589 | 498 | } else { |
79f1cbe9 | 499 | VLOG_WARN("%s: unknown %s argument '%s'", name, type, node->key); |
2b9d6589 BP |
500 | } |
501 | } | |
502 | ||
c1fc1411 JG |
503 | /* Add a default destination port for tunnel ports if none specified. */ |
504 | if (!strcmp(type, "geneve") && !tnl_cfg.dst_port) { | |
505 | tnl_cfg.dst_port = htons(GENEVE_DST_PORT); | |
506 | } | |
507 | ||
a6ae068b | 508 | if (!strcmp(type, "vxlan") && !tnl_cfg.dst_port) { |
f431bf7d | 509 | tnl_cfg.dst_port = htons(VXLAN_DST_PORT); |
79f827fa KM |
510 | } |
511 | ||
a6ae068b LJ |
512 | if (!strcmp(type, "lisp") && !tnl_cfg.dst_port) { |
513 | tnl_cfg.dst_port = htons(LISP_DST_PORT); | |
514 | } | |
515 | ||
f431bf7d | 516 | if (tnl_cfg.ipsec) { |
9366380a | 517 | static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER; |
2a586a5c | 518 | static pid_t pid = 0; |
6027d03d | 519 | |
c89809c9 | 520 | #ifndef _WIN32 |
9366380a | 521 | ovs_mutex_lock(&mutex); |
900f7601 | 522 | if (pid <= 0) { |
2a586a5c AS |
523 | char *file_name = xasprintf("%s/%s", ovs_rundir(), |
524 | "ovs-monitor-ipsec.pid"); | |
525 | pid = read_pidfile(file_name); | |
526 | free(file_name); | |
527 | } | |
9366380a | 528 | ovs_mutex_unlock(&mutex); |
c89809c9 | 529 | #endif |
2a586a5c | 530 | |
e7009c36 | 531 | if (pid < 0) { |
8283e514 JP |
532 | VLOG_ERR("%s: IPsec requires the ovs-monitor-ipsec daemon", |
533 | name); | |
b9ad7294 | 534 | return EINVAL; |
e7009c36 | 535 | } |
5059eff3 | 536 | |
79f1cbe9 | 537 | if (smap_get(args, "peer_cert") && smap_get(args, "psk")) { |
8283e514 | 538 | VLOG_ERR("%s: cannot define both 'peer_cert' and 'psk'", name); |
b9ad7294 | 539 | return EINVAL; |
3c52fa7b JP |
540 | } |
541 | ||
542 | if (!ipsec_mech_set) { | |
8283e514 JP |
543 | VLOG_ERR("%s: IPsec requires an 'peer_cert' or psk' argument", |
544 | name); | |
b9ad7294 | 545 | return EINVAL; |
3c52fa7b | 546 | } |
2b9d6589 BP |
547 | } |
548 | ||
0ad90c84 | 549 | if (!tnl_cfg.ip_dst && !tnl_cfg.ip_dst_flow) { |
8283e514 JP |
550 | VLOG_ERR("%s: %s type requires valid 'remote_ip' argument", |
551 | name, type); | |
b9ad7294 | 552 | return EINVAL; |
2b9d6589 | 553 | } |
0ad90c84 JR |
554 | if (tnl_cfg.ip_src_flow && !tnl_cfg.ip_dst_flow) { |
555 | VLOG_ERR("%s: %s type requires 'remote_ip=flow' with 'local_ip=flow'", | |
556 | name, type); | |
557 | return EINVAL; | |
558 | } | |
f431bf7d EJ |
559 | if (!tnl_cfg.ttl) { |
560 | tnl_cfg.ttl = DEFAULT_TTL; | |
561 | } | |
562 | ||
563 | tnl_cfg.in_key = parse_key(args, "in_key", | |
564 | &tnl_cfg.in_key_present, | |
565 | &tnl_cfg.in_key_flow); | |
f431bf7d EJ |
566 | |
567 | tnl_cfg.out_key = parse_key(args, "out_key", | |
568 | &tnl_cfg.out_key_present, | |
569 | &tnl_cfg.out_key_flow); | |
2b9d6589 | 570 | |
86383816 | 571 | ovs_mutex_lock(&dev->mutex); |
0a740f48 | 572 | dev->tnl_cfg = tnl_cfg; |
41ca1e0a | 573 | tunnel_check_status_change__(dev); |
3e912ffc | 574 | netdev_change_seq_changed(dev_); |
86383816 | 575 | ovs_mutex_unlock(&dev->mutex); |
f431bf7d | 576 | |
c19e6535 BP |
577 | return 0; |
578 | } | |
579 | ||
2b9d6589 | 580 | static int |
b5d57fc8 | 581 | get_tunnel_config(const struct netdev *dev, struct smap *args) |
6d9e6eb4 | 582 | { |
86383816 BP |
583 | struct netdev_vport *netdev = netdev_vport_cast(dev); |
584 | struct netdev_tunnel_config tnl_cfg; | |
585 | ||
586 | ovs_mutex_lock(&netdev->mutex); | |
587 | tnl_cfg = netdev->tnl_cfg; | |
588 | ovs_mutex_unlock(&netdev->mutex); | |
6d9e6eb4 | 589 | |
86383816 BP |
590 | if (tnl_cfg.ip_dst) { |
591 | smap_add_format(args, "remote_ip", IP_FMT, IP_ARGS(tnl_cfg.ip_dst)); | |
592 | } else if (tnl_cfg.ip_dst_flow) { | |
0ad90c84 | 593 | smap_add(args, "remote_ip", "flow"); |
0a740f48 EJ |
594 | } |
595 | ||
86383816 BP |
596 | if (tnl_cfg.ip_src) { |
597 | smap_add_format(args, "local_ip", IP_FMT, IP_ARGS(tnl_cfg.ip_src)); | |
598 | } else if (tnl_cfg.ip_src_flow) { | |
0ad90c84 | 599 | smap_add(args, "local_ip", "flow"); |
7f804ea5 | 600 | } |
c19e6535 | 601 | |
86383816 | 602 | if (tnl_cfg.in_key_flow && tnl_cfg.out_key_flow) { |
6d9e6eb4 | 603 | smap_add(args, "key", "flow"); |
86383816 BP |
604 | } else if (tnl_cfg.in_key_present && tnl_cfg.out_key_present |
605 | && tnl_cfg.in_key == tnl_cfg.out_key) { | |
606 | smap_add_format(args, "key", "%"PRIu64, ntohll(tnl_cfg.in_key)); | |
6d9e6eb4 | 607 | } else { |
86383816 | 608 | if (tnl_cfg.in_key_flow) { |
b9ad7294 | 609 | smap_add(args, "in_key", "flow"); |
86383816 | 610 | } else if (tnl_cfg.in_key_present) { |
b9ad7294 | 611 | smap_add_format(args, "in_key", "%"PRIu64, |
86383816 | 612 | ntohll(tnl_cfg.in_key)); |
b9ad7294 | 613 | } |
6d9e6eb4 | 614 | |
86383816 | 615 | if (tnl_cfg.out_key_flow) { |
b9ad7294 | 616 | smap_add(args, "out_key", "flow"); |
86383816 | 617 | } else if (tnl_cfg.out_key_present) { |
b9ad7294 | 618 | smap_add_format(args, "out_key", "%"PRIu64, |
86383816 | 619 | ntohll(tnl_cfg.out_key)); |
6d9e6eb4 BP |
620 | } |
621 | } | |
622 | ||
86383816 | 623 | if (tnl_cfg.ttl_inherit) { |
62827e6a | 624 | smap_add(args, "ttl", "inherit"); |
86383816 BP |
625 | } else if (tnl_cfg.ttl != DEFAULT_TTL) { |
626 | smap_add_format(args, "ttl", "%"PRIu8, tnl_cfg.ttl); | |
c19e6535 BP |
627 | } |
628 | ||
86383816 | 629 | if (tnl_cfg.tos_inherit) { |
6d9e6eb4 | 630 | smap_add(args, "tos", "inherit"); |
86383816 BP |
631 | } else if (tnl_cfg.tos) { |
632 | smap_add_format(args, "tos", "0x%x", tnl_cfg.tos); | |
6d9e6eb4 BP |
633 | } |
634 | ||
86383816 BP |
635 | if (tnl_cfg.dst_port) { |
636 | uint16_t dst_port = ntohs(tnl_cfg.dst_port); | |
b5d57fc8 | 637 | const char *type = netdev_get_type(dev); |
9eeb949b | 638 | |
c1fc1411 JG |
639 | if ((!strcmp("geneve", type) && dst_port != GENEVE_DST_PORT) || |
640 | (!strcmp("vxlan", type) && dst_port != VXLAN_DST_PORT) || | |
9eeb949b | 641 | (!strcmp("lisp", type) && dst_port != LISP_DST_PORT)) { |
79f827fa KM |
642 | smap_add_format(args, "dst_port", "%d", dst_port); |
643 | } | |
644 | } | |
645 | ||
86383816 | 646 | if (tnl_cfg.csum) { |
6d9e6eb4 BP |
647 | smap_add(args, "csum", "true"); |
648 | } | |
8a9ff93a | 649 | |
86383816 | 650 | if (!tnl_cfg.dont_fragment) { |
66409d1b AE |
651 | smap_add(args, "df_default", "false"); |
652 | } | |
6d9e6eb4 BP |
653 | |
654 | return 0; | |
655 | } | |
0a740f48 EJ |
656 | \f |
657 | /* Code specific to patch ports. */ | |
658 | ||
161b6042 BP |
659 | /* If 'netdev' is a patch port, returns the name of its peer as a malloc()'d |
660 | * string that the caller must free. | |
661 | * | |
662 | * If 'netdev' is not a patch port, returns NULL. */ | |
663 | char * | |
664 | netdev_vport_patch_peer(const struct netdev *netdev_) | |
0a740f48 | 665 | { |
161b6042 BP |
666 | char *peer = NULL; |
667 | ||
668 | if (netdev_vport_is_patch(netdev_)) { | |
669 | struct netdev_vport *netdev = netdev_vport_cast(netdev_); | |
86383816 BP |
670 | |
671 | ovs_mutex_lock(&netdev->mutex); | |
161b6042 BP |
672 | if (netdev->peer) { |
673 | peer = xstrdup(netdev->peer); | |
674 | } | |
86383816 | 675 | ovs_mutex_unlock(&netdev->mutex); |
161b6042 BP |
676 | } |
677 | ||
678 | return peer; | |
0a740f48 EJ |
679 | } |
680 | ||
681 | void | |
b9ad7294 | 682 | netdev_vport_inc_rx(const struct netdev *netdev, |
9e04d6f6 | 683 | const struct dpif_flow_stats *stats) |
0a740f48 | 684 | { |
b5d57fc8 BP |
685 | if (is_vport_class(netdev_get_class(netdev))) { |
686 | struct netdev_vport *dev = netdev_vport_cast(netdev); | |
86383816 BP |
687 | |
688 | ovs_mutex_lock(&dev->mutex); | |
0a740f48 EJ |
689 | dev->stats.rx_packets += stats->n_packets; |
690 | dev->stats.rx_bytes += stats->n_bytes; | |
86383816 | 691 | ovs_mutex_unlock(&dev->mutex); |
0a740f48 EJ |
692 | } |
693 | } | |
694 | ||
695 | void | |
b9ad7294 EJ |
696 | netdev_vport_inc_tx(const struct netdev *netdev, |
697 | const struct dpif_flow_stats *stats) | |
0a740f48 | 698 | { |
b5d57fc8 BP |
699 | if (is_vport_class(netdev_get_class(netdev))) { |
700 | struct netdev_vport *dev = netdev_vport_cast(netdev); | |
86383816 BP |
701 | |
702 | ovs_mutex_lock(&dev->mutex); | |
0a740f48 EJ |
703 | dev->stats.tx_packets += stats->n_packets; |
704 | dev->stats.tx_bytes += stats->n_bytes; | |
86383816 | 705 | ovs_mutex_unlock(&dev->mutex); |
0a740f48 EJ |
706 | } |
707 | } | |
708 | ||
709 | static int | |
b5d57fc8 | 710 | get_patch_config(const struct netdev *dev_, struct smap *args) |
0a740f48 | 711 | { |
b5d57fc8 | 712 | struct netdev_vport *dev = netdev_vport_cast(dev_); |
0a740f48 | 713 | |
86383816 | 714 | ovs_mutex_lock(&dev->mutex); |
0a740f48 EJ |
715 | if (dev->peer) { |
716 | smap_add(args, "peer", dev->peer); | |
717 | } | |
86383816 BP |
718 | ovs_mutex_unlock(&dev->mutex); |
719 | ||
0a740f48 EJ |
720 | return 0; |
721 | } | |
6d9e6eb4 BP |
722 | |
723 | static int | |
b5d57fc8 | 724 | set_patch_config(struct netdev *dev_, const struct smap *args) |
2b9d6589 | 725 | { |
b5d57fc8 BP |
726 | struct netdev_vport *dev = netdev_vport_cast(dev_); |
727 | const char *name = netdev_get_name(dev_); | |
2b9d6589 BP |
728 | const char *peer; |
729 | ||
79f1cbe9 | 730 | peer = smap_get(args, "peer"); |
2b9d6589 | 731 | if (!peer) { |
8283e514 | 732 | VLOG_ERR("%s: patch type requires valid 'peer' argument", name); |
2b9d6589 BP |
733 | return EINVAL; |
734 | } | |
735 | ||
79f1cbe9 | 736 | if (smap_count(args) > 1) { |
8283e514 | 737 | VLOG_ERR("%s: patch type takes only a 'peer' argument", name); |
2b9d6589 BP |
738 | return EINVAL; |
739 | } | |
740 | ||
2b9d6589 | 741 | if (!strcmp(name, peer)) { |
8283e514 | 742 | VLOG_ERR("%s: patch peer must not be self", name); |
2b9d6589 BP |
743 | return EINVAL; |
744 | } | |
745 | ||
86383816 | 746 | ovs_mutex_lock(&dev->mutex); |
0a740f48 EJ |
747 | free(dev->peer); |
748 | dev->peer = xstrdup(peer); | |
3e912ffc | 749 | netdev_change_seq_changed(dev_); |
86383816 BP |
750 | ovs_mutex_unlock(&dev->mutex); |
751 | ||
2b9d6589 BP |
752 | return 0; |
753 | } | |
6d9e6eb4 BP |
754 | |
755 | static int | |
b9ad7294 | 756 | get_stats(const struct netdev *netdev, struct netdev_stats *stats) |
0a740f48 | 757 | { |
b5d57fc8 | 758 | struct netdev_vport *dev = netdev_vport_cast(netdev); |
86383816 BP |
759 | |
760 | ovs_mutex_lock(&dev->mutex); | |
761 | *stats = dev->stats; | |
762 | ovs_mutex_unlock(&dev->mutex); | |
763 | ||
6d9e6eb4 BP |
764 | return 0; |
765 | } | |
2b9d6589 | 766 | \f |
0a740f48 | 767 | #define VPORT_FUNCTIONS(GET_CONFIG, SET_CONFIG, \ |
b9ad7294 | 768 | GET_TUNNEL_CONFIG, GET_STATUS) \ |
b46ccdf5 | 769 | NULL, \ |
ea83a2fc EJ |
770 | netdev_vport_run, \ |
771 | netdev_vport_wait, \ | |
2b9d6589 | 772 | \ |
9dc63482 BP |
773 | netdev_vport_alloc, \ |
774 | netdev_vport_construct, \ | |
775 | netdev_vport_destruct, \ | |
776 | netdev_vport_dealloc, \ | |
0a740f48 EJ |
777 | GET_CONFIG, \ |
778 | SET_CONFIG, \ | |
f431bf7d | 779 | GET_TUNNEL_CONFIG, \ |
7dec44fe | 780 | NULL, /* get_numa_id */ \ |
5496878c | 781 | NULL, /* set_multiq */ \ |
2b9d6589 | 782 | \ |
552e20d0 | 783 | NULL, /* send */ \ |
2b9d6589 BP |
784 | NULL, /* send_wait */ \ |
785 | \ | |
786 | netdev_vport_set_etheraddr, \ | |
787 | netdev_vport_get_etheraddr, \ | |
14622f22 BP |
788 | NULL, /* get_mtu */ \ |
789 | NULL, /* set_mtu */ \ | |
2b9d6589 | 790 | NULL, /* get_ifindex */ \ |
85da620e | 791 | NULL, /* get_carrier */ \ |
65c3058c | 792 | NULL, /* get_carrier_resets */ \ |
63331829 | 793 | NULL, /* get_miimon */ \ |
b9ad7294 | 794 | get_stats, \ |
2b9d6589 BP |
795 | \ |
796 | NULL, /* get_features */ \ | |
797 | NULL, /* set_advertisements */ \ | |
2b9d6589 BP |
798 | \ |
799 | NULL, /* set_policing */ \ | |
800 | NULL, /* get_qos_types */ \ | |
801 | NULL, /* get_qos_capabilities */ \ | |
802 | NULL, /* get_qos */ \ | |
803 | NULL, /* set_qos */ \ | |
804 | NULL, /* get_queue */ \ | |
805 | NULL, /* set_queue */ \ | |
806 | NULL, /* delete_queue */ \ | |
807 | NULL, /* get_queue_stats */ \ | |
89454bf4 BP |
808 | NULL, /* queue_dump_start */ \ |
809 | NULL, /* queue_dump_next */ \ | |
810 | NULL, /* queue_dump_done */ \ | |
2b9d6589 BP |
811 | NULL, /* dump_queue_stats */ \ |
812 | \ | |
813 | NULL, /* get_in4 */ \ | |
814 | NULL, /* set_in4 */ \ | |
815 | NULL, /* get_in6 */ \ | |
816 | NULL, /* add_router */ \ | |
817 | NULL, /* get_next_hop */ \ | |
ea763e0e | 818 | GET_STATUS, \ |
2b9d6589 BP |
819 | NULL, /* arp_lookup */ \ |
820 | \ | |
821 | netdev_vport_update_flags, \ | |
822 | \ | |
9dc63482 BP |
823 | NULL, /* rx_alloc */ \ |
824 | NULL, /* rx_construct */ \ | |
825 | NULL, /* rx_destruct */ \ | |
826 | NULL, /* rx_dealloc */ \ | |
827 | NULL, /* rx_recv */ \ | |
828 | NULL, /* rx_wait */ \ | |
829 | NULL, /* rx_drain */ | |
2b9d6589 | 830 | |
c060c4cf EJ |
831 | #define TUNNEL_CLASS(NAME, DPIF_PORT) \ |
832 | { DPIF_PORT, \ | |
0a740f48 EJ |
833 | { NAME, VPORT_FUNCTIONS(get_tunnel_config, \ |
834 | set_tunnel_config, \ | |
835 | get_netdev_tunnel_config, \ | |
0a740f48 | 836 | tunnel_get_status) }} |
db078f85 | 837 | |
2b9d6589 | 838 | void |
c060c4cf | 839 | netdev_vport_tunnel_register(void) |
2b9d6589 | 840 | { |
a5d4fadd JG |
841 | /* The name of the dpif_port should be short enough to accomodate adding |
842 | * a port number to the end if one is necessary. */ | |
c3827f61 | 843 | static const struct vport_class vport_classes[] = { |
c1fc1411 | 844 | TUNNEL_CLASS("geneve", "genev_sys"), |
a5d4fadd JG |
845 | TUNNEL_CLASS("gre", "gre_sys"), |
846 | TUNNEL_CLASS("ipsec_gre", "gre_sys"), | |
847 | TUNNEL_CLASS("gre64", "gre64_sys"), | |
848 | TUNNEL_CLASS("ipsec_gre64", "gre64_sys"), | |
849 | TUNNEL_CLASS("vxlan", "vxlan_sys"), | |
850 | TUNNEL_CLASS("lisp", "lisp_sys") | |
c3827f61 | 851 | }; |
86383816 | 852 | static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; |
c3827f61 | 853 | |
86383816 BP |
854 | if (ovsthread_once_start(&once)) { |
855 | int i; | |
c3827f61 | 856 | |
7c54c27f BP |
857 | for (i = 0; i < ARRAY_SIZE(vport_classes); i++) { |
858 | netdev_register_provider(&vport_classes[i].netdev_class); | |
859 | } | |
86383816 | 860 | ovsthread_once_done(&once); |
c3827f61 | 861 | } |
2b9d6589 | 862 | } |
c060c4cf EJ |
863 | |
864 | void | |
865 | netdev_vport_patch_register(void) | |
866 | { | |
867 | static const struct vport_class patch_class = | |
868 | { NULL, | |
869 | { "patch", VPORT_FUNCTIONS(get_patch_config, | |
870 | set_patch_config, | |
871 | NULL, | |
872 | NULL) }}; | |
873 | netdev_register_provider(&patch_class.netdev_class); | |
874 | } |