]>
Commit | Line | Data |
---|---|---|
777ece09 | 1 | /* |
e0edde6f | 2 | * Copyright (c) 2010, 2011, 2012 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> |
077257b8 | 24 | #include <linux/openvswitch.h> |
ea83a2fc | 25 | #include <linux/rtnetlink.h> |
2b9d6589 | 26 | #include <net/if.h> |
777ece09 JG |
27 | #include <sys/ioctl.h> |
28 | ||
b9298d3f | 29 | #include "byte-order.h" |
5059eff3 JP |
30 | #include "daemon.h" |
31 | #include "dirs.h" | |
c19e6535 | 32 | #include "dpif-linux.h" |
ea83a2fc EJ |
33 | #include "hash.h" |
34 | #include "hmap.h" | |
777ece09 | 35 | #include "list.h" |
d3980822 | 36 | #include "netdev-linux.h" |
2b9d6589 | 37 | #include "netdev-provider.h" |
ea83a2fc | 38 | #include "netlink.h" |
45c8d3a1 | 39 | #include "netlink-notifier.h" |
ea83a2fc EJ |
40 | #include "netlink-socket.h" |
41 | #include "ofpbuf.h" | |
2b9d6589 BP |
42 | #include "openvswitch/tunnel.h" |
43 | #include "packets.h" | |
a132aa96 | 44 | #include "route-table.h" |
777ece09 JG |
45 | #include "shash.h" |
46 | #include "socket-util.h" | |
69ebca1e | 47 | #include "unaligned.h" |
777ece09 JG |
48 | #include "vlog.h" |
49 | ||
d98e6007 | 50 | VLOG_DEFINE_THIS_MODULE(netdev_vport); |
5136ce49 | 51 | |
79f827fa KM |
52 | /* Default to the OTV port, per the VXLAN IETF draft. */ |
53 | #define VXLAN_DST_PORT 8472 | |
54 | ||
2b9d6589 BP |
55 | struct netdev_dev_vport { |
56 | struct netdev_dev netdev_dev; | |
c19e6535 | 57 | struct ofpbuf *options; |
7feba1ac BP |
58 | int dp_ifindex; /* -1 if unknown. */ |
59 | uint32_t port_no; /* UINT32_MAX if unknown. */ | |
ac4d3bcb | 60 | unsigned int change_seq; |
2b9d6589 BP |
61 | }; |
62 | ||
63 | struct netdev_vport { | |
64 | struct netdev netdev; | |
65 | }; | |
66 | ||
2b9d6589 | 67 | struct vport_class { |
df2c07f4 | 68 | enum ovs_vport_type type; |
c3827f61 | 69 | struct netdev_class netdev_class; |
6d9e6eb4 | 70 | int (*parse_config)(const char *name, const char *type, |
79f1cbe9 | 71 | const struct smap *args, struct ofpbuf *options); |
6d9e6eb4 | 72 | int (*unparse_config)(const char *name, const char *type, |
c19e6535 | 73 | const struct nlattr *options, size_t options_len, |
79f1cbe9 | 74 | struct smap *args); |
2b9d6589 BP |
75 | }; |
76 | ||
777ece09 JG |
77 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20); |
78 | ||
2b9d6589 | 79 | static int netdev_vport_create(const struct netdev_class *, const char *, |
de5cdb90 | 80 | struct netdev_dev **); |
2b9d6589 | 81 | static void netdev_vport_poll_notify(const struct netdev *); |
c19e6535 BP |
82 | static int tnl_port_config_from_nlattr(const struct nlattr *options, |
83 | size_t options_len, | |
df2c07f4 | 84 | struct nlattr *a[OVS_TUNNEL_ATTR_MAX + 1]); |
2b9d6589 | 85 | |
ea763e0e | 86 | static const char *netdev_vport_get_tnl_iface(const struct netdev *netdev); |
ea83a2fc | 87 | |
2b9d6589 BP |
88 | static bool |
89 | is_vport_class(const struct netdev_class *class) | |
777ece09 | 90 | { |
2b9d6589 BP |
91 | return class->create == netdev_vport_create; |
92 | } | |
777ece09 | 93 | |
2b9d6589 BP |
94 | static const struct vport_class * |
95 | vport_class_cast(const struct netdev_class *class) | |
96 | { | |
97 | assert(is_vport_class(class)); | |
98 | return CONTAINER_OF(class, struct vport_class, netdev_class); | |
99 | } | |
100 | ||
101 | static struct netdev_dev_vport * | |
102 | netdev_dev_vport_cast(const struct netdev_dev *netdev_dev) | |
103 | { | |
104 | assert(is_vport_class(netdev_dev_get_class(netdev_dev))); | |
105 | return CONTAINER_OF(netdev_dev, struct netdev_dev_vport, netdev_dev); | |
106 | } | |
107 | ||
108 | static struct netdev_vport * | |
109 | netdev_vport_cast(const struct netdev *netdev) | |
110 | { | |
111 | struct netdev_dev *netdev_dev = netdev_get_dev(netdev); | |
112 | assert(is_vport_class(netdev_dev_get_class(netdev_dev))); | |
113 | return CONTAINER_OF(netdev, struct netdev_vport, netdev); | |
114 | } | |
115 | ||
c19e6535 | 116 | /* If 'netdev' is a vport netdev, returns an ofpbuf that contains Netlink |
df2c07f4 | 117 | * options to include in OVS_VPORT_ATTR_OPTIONS for configuring that vport. |
c19e6535 BP |
118 | * Otherwise returns NULL. */ |
119 | const struct ofpbuf * | |
120 | netdev_vport_get_options(const struct netdev *netdev) | |
121 | { | |
122 | const struct netdev_dev *dev = netdev_get_dev(netdev); | |
123 | ||
124 | return (is_vport_class(netdev_dev_get_class(dev)) | |
125 | ? netdev_dev_vport_cast(dev)->options | |
126 | : NULL); | |
127 | } | |
128 | ||
df2c07f4 | 129 | enum ovs_vport_type |
c19e6535 | 130 | netdev_vport_get_vport_type(const struct netdev *netdev) |
2b9d6589 | 131 | { |
c3827f61 | 132 | const struct netdev_dev *dev = netdev_get_dev(netdev); |
c19e6535 BP |
133 | const struct netdev_class *class = netdev_dev_get_class(dev); |
134 | ||
135 | return (is_vport_class(class) ? vport_class_cast(class)->type | |
df2c07f4 | 136 | : class == &netdev_internal_class ? OVS_VPORT_TYPE_INTERNAL |
52fa1bcf BP |
137 | : (class == &netdev_linux_class || |
138 | class == &netdev_tap_class) ? OVS_VPORT_TYPE_NETDEV | |
df2c07f4 | 139 | : OVS_VPORT_TYPE_UNSPEC); |
c19e6535 BP |
140 | } |
141 | ||
142 | const char * | |
143 | netdev_vport_get_netdev_type(const struct dpif_linux_vport *vport) | |
144 | { | |
df2c07f4 | 145 | struct nlattr *a[OVS_TUNNEL_ATTR_MAX + 1]; |
c19e6535 BP |
146 | |
147 | switch (vport->type) { | |
df2c07f4 | 148 | case OVS_VPORT_TYPE_UNSPEC: |
c19e6535 BP |
149 | break; |
150 | ||
df2c07f4 | 151 | case OVS_VPORT_TYPE_NETDEV: |
c19e6535 BP |
152 | return "system"; |
153 | ||
df2c07f4 | 154 | case OVS_VPORT_TYPE_INTERNAL: |
c19e6535 | 155 | return "internal"; |
c3827f61 | 156 | |
df2c07f4 | 157 | case OVS_VPORT_TYPE_PATCH: |
c19e6535 BP |
158 | return "patch"; |
159 | ||
df2c07f4 | 160 | case OVS_VPORT_TYPE_GRE: |
c19e6535 BP |
161 | if (tnl_port_config_from_nlattr(vport->options, vport->options_len, |
162 | a)) { | |
163 | break; | |
164 | } | |
df2c07f4 | 165 | return (nl_attr_get_u32(a[OVS_TUNNEL_ATTR_FLAGS]) & TNL_F_IPSEC |
c19e6535 BP |
166 | ? "ipsec_gre" : "gre"); |
167 | ||
2de795ad PS |
168 | case OVS_VPORT_TYPE_GRE64: |
169 | if (tnl_port_config_from_nlattr(vport->options, vport->options_len, | |
170 | a)) { | |
171 | break; | |
172 | } | |
173 | return (nl_attr_get_u32(a[OVS_TUNNEL_ATTR_FLAGS]) & TNL_F_IPSEC | |
174 | ? "ipsec_gre64" : "gre64"); | |
175 | ||
df2c07f4 | 176 | case OVS_VPORT_TYPE_CAPWAP: |
c19e6535 BP |
177 | return "capwap"; |
178 | ||
79f827fa KM |
179 | case OVS_VPORT_TYPE_VXLAN: |
180 | return ("vxlan"); | |
181 | ||
3c82d068 | 182 | case OVS_VPORT_TYPE_FT_GRE: |
df2c07f4 | 183 | case __OVS_VPORT_TYPE_MAX: |
c19e6535 | 184 | break; |
777ece09 | 185 | } |
c19e6535 BP |
186 | |
187 | VLOG_WARN_RL(&rl, "dp%d: port `%s' has unsupported type %u", | |
254f2dc8 | 188 | vport->dp_ifindex, vport->name, (unsigned int) vport->type); |
c19e6535 | 189 | return "unknown"; |
2b9d6589 | 190 | } |
777ece09 | 191 | |
2b9d6589 | 192 | static int |
c3827f61 | 193 | netdev_vport_create(const struct netdev_class *netdev_class, const char *name, |
c3827f61 | 194 | struct netdev_dev **netdev_devp) |
2b9d6589 | 195 | { |
de5cdb90 | 196 | struct netdev_dev_vport *dev; |
6d9e6eb4 | 197 | |
de5cdb90 BP |
198 | dev = xmalloc(sizeof *dev); |
199 | netdev_dev_init(&dev->netdev_dev, name, netdev_class); | |
200 | dev->options = NULL; | |
201 | dev->dp_ifindex = -1; | |
202 | dev->port_no = UINT32_MAX; | |
203 | dev->change_seq = 1; | |
6d9e6eb4 | 204 | |
de5cdb90 BP |
205 | *netdev_devp = &dev->netdev_dev; |
206 | route_table_register(); | |
6d9e6eb4 | 207 | |
de5cdb90 | 208 | return 0; |
777ece09 JG |
209 | } |
210 | ||
2b9d6589 BP |
211 | static void |
212 | netdev_vport_destroy(struct netdev_dev *netdev_dev_) | |
213 | { | |
214 | struct netdev_dev_vport *netdev_dev = netdev_dev_vport_cast(netdev_dev_); | |
215 | ||
896b3272 | 216 | ofpbuf_delete(netdev_dev->options); |
a132aa96 | 217 | route_table_unregister(); |
2b9d6589 BP |
218 | free(netdev_dev); |
219 | } | |
220 | ||
221 | static int | |
7b6b0ef4 | 222 | netdev_vport_open(struct netdev_dev *netdev_dev_, struct netdev **netdevp) |
2b9d6589 BP |
223 | { |
224 | struct netdev_vport *netdev; | |
225 | ||
226 | netdev = xmalloc(sizeof *netdev); | |
227 | netdev_init(&netdev->netdev, netdev_dev_); | |
228 | ||
229 | *netdevp = &netdev->netdev; | |
230 | return 0; | |
231 | } | |
232 | ||
233 | static void | |
234 | netdev_vport_close(struct netdev *netdev_) | |
235 | { | |
236 | struct netdev_vport *netdev = netdev_vport_cast(netdev_); | |
237 | free(netdev); | |
238 | } | |
239 | ||
de5cdb90 | 240 | static int |
79f1cbe9 | 241 | netdev_vport_get_config(struct netdev_dev *dev_, struct smap *args) |
de5cdb90 BP |
242 | { |
243 | const struct netdev_class *netdev_class = netdev_dev_get_class(dev_); | |
244 | const struct vport_class *vport_class = vport_class_cast(netdev_class); | |
245 | struct netdev_dev_vport *dev = netdev_dev_vport_cast(dev_); | |
246 | const char *name = netdev_dev_get_name(dev_); | |
247 | int error; | |
248 | ||
249 | if (!dev->options) { | |
250 | struct dpif_linux_vport reply; | |
251 | struct ofpbuf *buf; | |
252 | ||
253 | error = dpif_linux_vport_get(name, &reply, &buf); | |
254 | if (error) { | |
255 | VLOG_ERR_RL(&rl, "%s: vport query failed (%s)", | |
256 | name, strerror(error)); | |
257 | return error; | |
258 | } | |
259 | ||
260 | dev->options = ofpbuf_clone_data(reply.options, reply.options_len); | |
261 | dev->dp_ifindex = reply.dp_ifindex; | |
262 | dev->port_no = reply.port_no; | |
263 | ofpbuf_delete(buf); | |
264 | } | |
265 | ||
266 | error = vport_class->unparse_config(name, netdev_class->type, | |
267 | dev->options->data, | |
268 | dev->options->size, | |
269 | args); | |
270 | if (error) { | |
271 | VLOG_ERR_RL(&rl, "%s: failed to parse kernel config (%s)", | |
272 | name, strerror(error)); | |
273 | } | |
274 | return error; | |
275 | } | |
276 | ||
2b9d6589 | 277 | static int |
79f1cbe9 | 278 | netdev_vport_set_config(struct netdev_dev *dev_, const struct smap *args) |
2b9d6589 | 279 | { |
c3827f61 BP |
280 | const struct netdev_class *netdev_class = netdev_dev_get_class(dev_); |
281 | const struct vport_class *vport_class = vport_class_cast(netdev_class); | |
282 | struct netdev_dev_vport *dev = netdev_dev_vport_cast(dev_); | |
c19e6535 BP |
283 | const char *name = netdev_dev_get_name(dev_); |
284 | struct ofpbuf *options; | |
c3827f61 BP |
285 | int error; |
286 | ||
c19e6535 BP |
287 | options = ofpbuf_new(64); |
288 | error = vport_class->parse_config(name, netdev_dev_get_type(dev_), | |
289 | args, options); | |
290 | if (!error | |
de5cdb90 BP |
291 | && (!dev->options |
292 | || options->size != dev->options->size | |
c19e6535 BP |
293 | || memcmp(options->data, dev->options->data, options->size))) { |
294 | struct dpif_linux_vport vport; | |
295 | ||
296 | dpif_linux_vport_init(&vport); | |
df2c07f4 | 297 | vport.cmd = OVS_VPORT_CMD_SET; |
c19e6535 BP |
298 | vport.name = name; |
299 | vport.options = options->data; | |
300 | vport.options_len = options->size; | |
301 | error = dpif_linux_vport_transact(&vport, NULL, NULL); | |
c3827f61 BP |
302 | if (!error || error == ENODEV) { |
303 | /* Either reconfiguration succeeded or this vport is not installed | |
304 | * in the kernel (e.g. it hasn't been added to a dpif yet with | |
305 | * dpif_port_add()). */ | |
c19e6535 BP |
306 | ofpbuf_delete(dev->options); |
307 | dev->options = options; | |
308 | options = NULL; | |
309 | error = 0; | |
c3827f61 | 310 | } |
2b9d6589 | 311 | } |
c19e6535 BP |
312 | ofpbuf_delete(options); |
313 | ||
c3827f61 | 314 | return error; |
2b9d6589 BP |
315 | } |
316 | ||
7feba1ac BP |
317 | static int |
318 | netdev_vport_send(struct netdev *netdev, const void *data, size_t size) | |
319 | { | |
320 | struct netdev_dev *dev_ = netdev_get_dev(netdev); | |
321 | struct netdev_dev_vport *dev = netdev_dev_vport_cast(dev_); | |
322 | ||
323 | if (dev->dp_ifindex == -1) { | |
324 | const char *name = netdev_get_name(netdev); | |
325 | struct dpif_linux_vport reply; | |
326 | struct ofpbuf *buf; | |
327 | int error; | |
328 | ||
329 | error = dpif_linux_vport_get(name, &reply, &buf); | |
330 | if (error) { | |
331 | VLOG_ERR_RL(&rl, "%s: failed to query vport for send (%s)", | |
332 | name, strerror(error)); | |
333 | return error; | |
334 | } | |
335 | dev->dp_ifindex = reply.dp_ifindex; | |
336 | dev->port_no = reply.port_no; | |
337 | ofpbuf_delete(buf); | |
338 | } | |
339 | ||
340 | return dpif_linux_vport_send(dev->dp_ifindex, dev->port_no, data, size); | |
341 | } | |
342 | ||
2b9d6589 | 343 | static int |
777ece09 JG |
344 | netdev_vport_set_etheraddr(struct netdev *netdev, |
345 | const uint8_t mac[ETH_ADDR_LEN]) | |
346 | { | |
c19e6535 BP |
347 | struct dpif_linux_vport vport; |
348 | int error; | |
777ece09 | 349 | |
c19e6535 | 350 | dpif_linux_vport_init(&vport); |
df2c07f4 | 351 | vport.cmd = OVS_VPORT_CMD_SET; |
c19e6535 BP |
352 | vport.name = netdev_get_name(netdev); |
353 | vport.address = mac; | |
777ece09 | 354 | |
c19e6535 BP |
355 | error = dpif_linux_vport_transact(&vport, NULL, NULL); |
356 | if (!error) { | |
357 | netdev_vport_poll_notify(netdev); | |
777ece09 | 358 | } |
c19e6535 | 359 | return error; |
777ece09 JG |
360 | } |
361 | ||
2b9d6589 | 362 | static int |
777ece09 JG |
363 | netdev_vport_get_etheraddr(const struct netdev *netdev, |
364 | uint8_t mac[ETH_ADDR_LEN]) | |
365 | { | |
c19e6535 BP |
366 | struct dpif_linux_vport reply; |
367 | struct ofpbuf *buf; | |
368 | int error; | |
777ece09 | 369 | |
c19e6535 BP |
370 | error = dpif_linux_vport_get(netdev_get_name(netdev), &reply, &buf); |
371 | if (!error) { | |
372 | if (reply.address) { | |
373 | memcpy(mac, reply.address, ETH_ADDR_LEN); | |
374 | } else { | |
375 | error = EOPNOTSUPP; | |
376 | } | |
377 | ofpbuf_delete(buf); | |
777ece09 | 378 | } |
c19e6535 | 379 | return error; |
777ece09 JG |
380 | } |
381 | ||
69ebca1e BP |
382 | /* Copies 'src' into 'dst', performing format conversion in the process. |
383 | * | |
384 | * 'src' is allowed to be misaligned. */ | |
f613a0d7 PS |
385 | static void |
386 | netdev_stats_from_ovs_vport_stats(struct netdev_stats *dst, | |
387 | const struct ovs_vport_stats *src) | |
388 | { | |
69ebca1e BP |
389 | dst->rx_packets = get_unaligned_u64(&src->rx_packets); |
390 | dst->tx_packets = get_unaligned_u64(&src->tx_packets); | |
391 | dst->rx_bytes = get_unaligned_u64(&src->rx_bytes); | |
392 | dst->tx_bytes = get_unaligned_u64(&src->tx_bytes); | |
393 | dst->rx_errors = get_unaligned_u64(&src->rx_errors); | |
394 | dst->tx_errors = get_unaligned_u64(&src->tx_errors); | |
395 | dst->rx_dropped = get_unaligned_u64(&src->rx_dropped); | |
396 | dst->tx_dropped = get_unaligned_u64(&src->tx_dropped); | |
f613a0d7 PS |
397 | dst->multicast = 0; |
398 | dst->collisions = 0; | |
399 | dst->rx_length_errors = 0; | |
400 | dst->rx_over_errors = 0; | |
401 | dst->rx_crc_errors = 0; | |
402 | dst->rx_frame_errors = 0; | |
403 | dst->rx_fifo_errors = 0; | |
404 | dst->rx_missed_errors = 0; | |
405 | dst->tx_aborted_errors = 0; | |
406 | dst->tx_carrier_errors = 0; | |
407 | dst->tx_fifo_errors = 0; | |
408 | dst->tx_heartbeat_errors = 0; | |
409 | dst->tx_window_errors = 0; | |
410 | } | |
411 | ||
412 | /* Copies 'src' into 'dst', performing format conversion in the process. */ | |
413 | static void | |
414 | netdev_stats_to_ovs_vport_stats(struct ovs_vport_stats *dst, | |
415 | const struct netdev_stats *src) | |
416 | { | |
69ebca1e BP |
417 | dst->rx_packets = src->rx_packets; |
418 | dst->tx_packets = src->tx_packets; | |
419 | dst->rx_bytes = src->rx_bytes; | |
420 | dst->tx_bytes = src->tx_bytes; | |
421 | dst->rx_errors = src->rx_errors; | |
422 | dst->tx_errors = src->tx_errors; | |
423 | dst->rx_dropped = src->rx_dropped; | |
424 | dst->tx_dropped = src->tx_dropped; | |
f613a0d7 PS |
425 | } |
426 | ||
777ece09 JG |
427 | int |
428 | netdev_vport_get_stats(const struct netdev *netdev, struct netdev_stats *stats) | |
429 | { | |
c19e6535 BP |
430 | struct dpif_linux_vport reply; |
431 | struct ofpbuf *buf; | |
432 | int error; | |
777ece09 | 433 | |
c19e6535 BP |
434 | error = dpif_linux_vport_get(netdev_get_name(netdev), &reply, &buf); |
435 | if (error) { | |
436 | return error; | |
437 | } else if (!reply.stats) { | |
438 | ofpbuf_delete(buf); | |
439 | return EOPNOTSUPP; | |
440 | } | |
441 | ||
f613a0d7 | 442 | netdev_stats_from_ovs_vport_stats(stats, reply.stats); |
c19e6535 BP |
443 | |
444 | ofpbuf_delete(buf); | |
777ece09 JG |
445 | |
446 | return 0; | |
447 | } | |
448 | ||
f4b6076a JG |
449 | int |
450 | netdev_vport_set_stats(struct netdev *netdev, const struct netdev_stats *stats) | |
451 | { | |
f613a0d7 | 452 | struct ovs_vport_stats rtnl_stats; |
c19e6535 | 453 | struct dpif_linux_vport vport; |
f4b6076a JG |
454 | int err; |
455 | ||
f613a0d7 | 456 | netdev_stats_to_ovs_vport_stats(&rtnl_stats, stats); |
c19e6535 BP |
457 | |
458 | dpif_linux_vport_init(&vport); | |
df2c07f4 | 459 | vport.cmd = OVS_VPORT_CMD_SET; |
c19e6535 BP |
460 | vport.name = netdev_get_name(netdev); |
461 | vport.stats = &rtnl_stats; | |
462 | ||
463 | err = dpif_linux_vport_transact(&vport, NULL, NULL); | |
f4b6076a JG |
464 | |
465 | /* If the vport layer doesn't know about the device, that doesn't mean it | |
466 | * doesn't exist (after all were able to open it when netdev_open() was | |
467 | * called), it just means that it isn't attached and we'll be getting | |
468 | * stats a different way. */ | |
469 | if (err == ENODEV) { | |
470 | err = EOPNOTSUPP; | |
471 | } | |
472 | ||
473 | return err; | |
474 | } | |
475 | ||
ea763e0e | 476 | static int |
79f1cbe9 | 477 | netdev_vport_get_drv_info(const struct netdev *netdev, struct smap *smap) |
ea763e0e EJ |
478 | { |
479 | const char *iface = netdev_vport_get_tnl_iface(netdev); | |
480 | ||
481 | if (iface) { | |
a404826e AE |
482 | struct netdev *egress_netdev; |
483 | ||
79f1cbe9 | 484 | smap_add(smap, "tunnel_egress_iface", iface); |
a404826e | 485 | |
18812dff | 486 | if (!netdev_open(iface, "system", &egress_netdev)) { |
79f1cbe9 EJ |
487 | smap_add(smap, "tunnel_egress_iface_carrier", |
488 | netdev_get_carrier(egress_netdev) ? "up" : "down"); | |
a404826e AE |
489 | netdev_close(egress_netdev); |
490 | } | |
ea763e0e EJ |
491 | } |
492 | ||
493 | return 0; | |
494 | } | |
495 | ||
2b9d6589 | 496 | static int |
777ece09 JG |
497 | netdev_vport_update_flags(struct netdev *netdev OVS_UNUSED, |
498 | enum netdev_flags off, enum netdev_flags on OVS_UNUSED, | |
499 | enum netdev_flags *old_flagsp) | |
500 | { | |
501 | if (off & (NETDEV_UP | NETDEV_PROMISC)) { | |
502 | return EOPNOTSUPP; | |
503 | } | |
504 | ||
505 | *old_flagsp = NETDEV_UP | NETDEV_PROMISC; | |
506 | return 0; | |
507 | } | |
508 | ||
ac4d3bcb EJ |
509 | static unsigned int |
510 | netdev_vport_change_seq(const struct netdev *netdev) | |
511 | { | |
512 | return netdev_dev_vport_cast(netdev_get_dev(netdev))->change_seq; | |
513 | } | |
514 | ||
ea83a2fc EJ |
515 | static void |
516 | netdev_vport_run(void) | |
517 | { | |
a132aa96 | 518 | route_table_run(); |
ea83a2fc EJ |
519 | } |
520 | ||
521 | static void | |
522 | netdev_vport_wait(void) | |
523 | { | |
a132aa96 | 524 | route_table_wait(); |
ea83a2fc EJ |
525 | } |
526 | \f | |
527 | /* get_tnl_iface() implementation. */ | |
ea83a2fc EJ |
528 | static const char * |
529 | netdev_vport_get_tnl_iface(const struct netdev *netdev) | |
530 | { | |
df2c07f4 | 531 | struct nlattr *a[OVS_TUNNEL_ATTR_MAX + 1]; |
d84d4b88 | 532 | ovs_be32 route; |
ea83a2fc | 533 | struct netdev_dev_vport *ndv; |
b46ccdf5 | 534 | static char name[IFNAMSIZ]; |
ea83a2fc EJ |
535 | |
536 | ndv = netdev_dev_vport_cast(netdev_get_dev(netdev)); | |
c19e6535 BP |
537 | if (tnl_port_config_from_nlattr(ndv->options->data, ndv->options->size, |
538 | a)) { | |
539 | return NULL; | |
540 | } | |
df2c07f4 | 541 | route = nl_attr_get_be32(a[OVS_TUNNEL_ATTR_DST_IPV4]); |
ea83a2fc | 542 | |
b46ccdf5 EJ |
543 | if (route_table_get_name(route, name)) { |
544 | return name; | |
ea83a2fc EJ |
545 | } |
546 | ||
547 | return NULL; | |
548 | } | |
2b9d6589 BP |
549 | \f |
550 | /* Helper functions. */ | |
777ece09 | 551 | |
2b9d6589 | 552 | static void |
777ece09 JG |
553 | netdev_vport_poll_notify(const struct netdev *netdev) |
554 | { | |
ac4d3bcb EJ |
555 | struct netdev_dev_vport *ndv; |
556 | ||
557 | ndv = netdev_dev_vport_cast(netdev_get_dev(netdev)); | |
777ece09 | 558 | |
ac4d3bcb EJ |
559 | ndv->change_seq++; |
560 | if (!ndv->change_seq) { | |
561 | ndv->change_seq++; | |
562 | } | |
777ece09 | 563 | } |
2b9d6589 BP |
564 | \f |
565 | /* Code specific to individual vport types. */ | |
566 | ||
c19e6535 | 567 | static void |
79f1cbe9 | 568 | set_key(const struct smap *args, const char *name, uint16_t type, |
c19e6535 BP |
569 | struct ofpbuf *options) |
570 | { | |
571 | const char *s; | |
572 | ||
79f1cbe9 | 573 | s = smap_get(args, name); |
c19e6535 | 574 | if (!s) { |
79f1cbe9 | 575 | s = smap_get(args, "key"); |
c19e6535 BP |
576 | if (!s) { |
577 | s = "0"; | |
578 | } | |
579 | } | |
580 | ||
581 | if (!strcmp(s, "flow")) { | |
582 | /* This is the default if no attribute is present. */ | |
583 | } else { | |
584 | nl_msg_put_be64(options, type, htonll(strtoull(s, NULL, 0))); | |
585 | } | |
586 | } | |
587 | ||
2b9d6589 | 588 | static int |
6d9e6eb4 | 589 | parse_tunnel_config(const char *name, const char *type, |
79f1cbe9 | 590 | const struct smap *args, struct ofpbuf *options) |
2b9d6589 | 591 | { |
e16a28b5 JP |
592 | bool is_gre = false; |
593 | bool is_ipsec = false; | |
79f827fa KM |
594 | bool needs_dst_port = false; |
595 | bool found_dst_port = false; | |
79f1cbe9 | 596 | struct smap_node *node; |
2b9d6589 | 597 | bool ipsec_mech_set = false; |
c19e6535 | 598 | ovs_be32 daddr = htonl(0); |
b37e6334 | 599 | ovs_be32 saddr = htonl(0); |
c19e6535 | 600 | uint32_t flags; |
2b9d6589 | 601 | |
1280bf0e PS |
602 | if (!strcmp(type, "capwap")) { |
603 | VLOG_WARN_ONCE("CAPWAP tunnel support is deprecated."); | |
604 | } | |
605 | ||
05a9d485 | 606 | flags = TNL_F_DF_DEFAULT; |
2de795ad | 607 | if (!strcmp(type, "gre") || !strcmp(type, "gre64")) { |
e16a28b5 | 608 | is_gre = true; |
2de795ad | 609 | } else if (!strcmp(type, "ipsec_gre") || !strcmp(type, "ipsec_gre64")) { |
e16a28b5 JP |
610 | is_gre = true; |
611 | is_ipsec = true; | |
c19e6535 | 612 | flags |= TNL_F_IPSEC; |
79f827fa KM |
613 | } else if (!strcmp(type, "vxlan")) { |
614 | needs_dst_port = true; | |
e16a28b5 JP |
615 | } |
616 | ||
79f1cbe9 EJ |
617 | SMAP_FOR_EACH (node, args) { |
618 | if (!strcmp(node->key, "remote_ip")) { | |
2b9d6589 | 619 | struct in_addr in_addr; |
79f1cbe9 | 620 | if (lookup_ip(node->value, &in_addr)) { |
c3827f61 | 621 | VLOG_WARN("%s: bad %s 'remote_ip'", name, type); |
2b9d6589 | 622 | } else { |
c19e6535 | 623 | daddr = in_addr.s_addr; |
2b9d6589 | 624 | } |
79f1cbe9 | 625 | } else if (!strcmp(node->key, "local_ip")) { |
2b9d6589 | 626 | struct in_addr in_addr; |
79f1cbe9 | 627 | if (lookup_ip(node->value, &in_addr)) { |
c3827f61 | 628 | VLOG_WARN("%s: bad %s 'local_ip'", name, type); |
2b9d6589 | 629 | } else { |
b37e6334 | 630 | saddr = in_addr.s_addr; |
2b9d6589 | 631 | } |
79f1cbe9 EJ |
632 | } else if (!strcmp(node->key, "tos")) { |
633 | if (!strcmp(node->value, "inherit")) { | |
c19e6535 | 634 | flags |= TNL_F_TOS_INHERIT; |
2b9d6589 | 635 | } else { |
3fca7064 PS |
636 | char *endptr; |
637 | int tos; | |
79f1cbe9 | 638 | tos = strtol(node->value, &endptr, 0); |
91aff446 | 639 | if (*endptr == '\0' && tos == (tos & IP_DSCP_MASK)) { |
3fca7064 | 640 | nl_msg_put_u8(options, OVS_TUNNEL_ATTR_TOS, tos); |
91aff446 BP |
641 | } else { |
642 | VLOG_WARN("%s: invalid TOS %s", name, node->value); | |
3fca7064 | 643 | } |
2b9d6589 | 644 | } |
79f1cbe9 EJ |
645 | } else if (!strcmp(node->key, "ttl")) { |
646 | if (!strcmp(node->value, "inherit")) { | |
c19e6535 | 647 | flags |= TNL_F_TTL_INHERIT; |
2b9d6589 | 648 | } else { |
79f1cbe9 | 649 | nl_msg_put_u8(options, OVS_TUNNEL_ATTR_TTL, atoi(node->value)); |
2b9d6589 | 650 | } |
79f827fa KM |
651 | } else if (!strcmp(node->key, "dst_port") && needs_dst_port) { |
652 | nl_msg_put_u16(options, OVS_TUNNEL_ATTR_DST_PORT, | |
653 | atoi(node->value)); | |
654 | found_dst_port = true; | |
79f1cbe9 EJ |
655 | } else if (!strcmp(node->key, "csum") && is_gre) { |
656 | if (!strcmp(node->value, "true")) { | |
c19e6535 | 657 | flags |= TNL_F_CSUM; |
2b9d6589 | 658 | } |
79f1cbe9 EJ |
659 | } else if (!strcmp(node->key, "df_inherit")) { |
660 | if (!strcmp(node->value, "true")) { | |
66409d1b AE |
661 | flags |= TNL_F_DF_INHERIT; |
662 | } | |
79f1cbe9 EJ |
663 | } else if (!strcmp(node->key, "df_default")) { |
664 | if (!strcmp(node->value, "false")) { | |
66409d1b AE |
665 | flags &= ~TNL_F_DF_DEFAULT; |
666 | } | |
79f1cbe9 | 667 | } else if (!strcmp(node->key, "pmtud")) { |
85b53b31 | 668 | if (!strcmp(node->value, "true")) { |
85340733 AA |
669 | VLOG_WARN_ONCE("%s: The tunnel Path MTU discovery is " |
670 | "deprecated and may be removed in February " | |
671 | "2013. Please email dev@openvswitch.org with " | |
672 | "concerns.", name); | |
85b53b31 | 673 | flags |= TNL_F_PMTUD; |
2b9d6589 | 674 | } |
79f1cbe9 EJ |
675 | } else if (!strcmp(node->key, "peer_cert") && is_ipsec) { |
676 | if (smap_get(args, "certificate")) { | |
3c52fa7b JP |
677 | ipsec_mech_set = true; |
678 | } else { | |
ef7ee76a JP |
679 | const char *use_ssl_cert; |
680 | ||
681 | /* If the "use_ssl_cert" is true, then "certificate" and | |
682 | * "private_key" will be pulled from the SSL table. The | |
683 | * use of this option is strongly discouraged, since it | |
684 | * will like be removed when multiple SSL configurations | |
685 | * are supported by OVS. | |
686 | */ | |
79f1cbe9 | 687 | use_ssl_cert = smap_get(args, "use_ssl_cert"); |
ef7ee76a | 688 | if (!use_ssl_cert || strcmp(use_ssl_cert, "true")) { |
8283e514 JP |
689 | VLOG_ERR("%s: 'peer_cert' requires 'certificate' argument", |
690 | name); | |
ef7ee76a JP |
691 | return EINVAL; |
692 | } | |
693 | ipsec_mech_set = true; | |
3c52fa7b | 694 | } |
79f1cbe9 | 695 | } else if (!strcmp(node->key, "psk") && is_ipsec) { |
2b9d6589 | 696 | ipsec_mech_set = true; |
ea83a2fc | 697 | } else if (is_ipsec |
79f1cbe9 EJ |
698 | && (!strcmp(node->key, "certificate") |
699 | || !strcmp(node->key, "private_key") | |
700 | || !strcmp(node->key, "use_ssl_cert"))) { | |
3c52fa7b | 701 | /* Ignore options not used by the netdev. */ |
79f1cbe9 EJ |
702 | } else if (!strcmp(node->key, "key") || |
703 | !strcmp(node->key, "in_key") || | |
704 | !strcmp(node->key, "out_key")) { | |
c19e6535 | 705 | /* Handled separately below. */ |
2b9d6589 | 706 | } else { |
79f1cbe9 | 707 | VLOG_WARN("%s: unknown %s argument '%s'", name, type, node->key); |
2b9d6589 BP |
708 | } |
709 | } | |
710 | ||
79f827fa KM |
711 | /* Add a default destination port for VXLAN if none specified. */ |
712 | if (needs_dst_port && !found_dst_port) { | |
713 | nl_msg_put_u16(options, OVS_TUNNEL_ATTR_DST_PORT, VXLAN_DST_PORT); | |
714 | } | |
715 | ||
3c52fa7b | 716 | if (is_ipsec) { |
2a586a5c | 717 | static pid_t pid = 0; |
900f7601 | 718 | if (pid <= 0) { |
2a586a5c AS |
719 | char *file_name = xasprintf("%s/%s", ovs_rundir(), |
720 | "ovs-monitor-ipsec.pid"); | |
721 | pid = read_pidfile(file_name); | |
722 | free(file_name); | |
723 | } | |
724 | ||
e7009c36 | 725 | if (pid < 0) { |
8283e514 JP |
726 | VLOG_ERR("%s: IPsec requires the ovs-monitor-ipsec daemon", |
727 | name); | |
e7009c36 JP |
728 | return EINVAL; |
729 | } | |
5059eff3 | 730 | |
79f1cbe9 | 731 | if (smap_get(args, "peer_cert") && smap_get(args, "psk")) { |
8283e514 | 732 | VLOG_ERR("%s: cannot define both 'peer_cert' and 'psk'", name); |
3c52fa7b JP |
733 | return EINVAL; |
734 | } | |
735 | ||
736 | if (!ipsec_mech_set) { | |
8283e514 JP |
737 | VLOG_ERR("%s: IPsec requires an 'peer_cert' or psk' argument", |
738 | name); | |
3c52fa7b JP |
739 | return EINVAL; |
740 | } | |
2b9d6589 BP |
741 | } |
742 | ||
40a75177 VG |
743 | set_key(args, "in_key", OVS_TUNNEL_ATTR_IN_KEY, options); |
744 | set_key(args, "out_key", OVS_TUNNEL_ATTR_OUT_KEY, options); | |
c19e6535 BP |
745 | |
746 | if (!daddr) { | |
8283e514 JP |
747 | VLOG_ERR("%s: %s type requires valid 'remote_ip' argument", |
748 | name, type); | |
2b9d6589 BP |
749 | return EINVAL; |
750 | } | |
df2c07f4 | 751 | nl_msg_put_be32(options, OVS_TUNNEL_ATTR_DST_IPV4, daddr); |
c19e6535 | 752 | |
b37e6334 BP |
753 | if (saddr) { |
754 | if (ip_is_multicast(daddr)) { | |
755 | VLOG_WARN("%s: remote_ip is multicast, ignoring local_ip", name); | |
756 | } else { | |
757 | nl_msg_put_be32(options, OVS_TUNNEL_ATTR_SRC_IPV4, saddr); | |
758 | } | |
759 | } | |
760 | ||
df2c07f4 | 761 | nl_msg_put_u32(options, OVS_TUNNEL_ATTR_FLAGS, flags); |
2b9d6589 BP |
762 | |
763 | return 0; | |
764 | } | |
765 | ||
c19e6535 BP |
766 | static int |
767 | tnl_port_config_from_nlattr(const struct nlattr *options, size_t options_len, | |
df2c07f4 JP |
768 | struct nlattr *a[OVS_TUNNEL_ATTR_MAX + 1]) |
769 | { | |
770 | static const struct nl_policy ovs_tunnel_policy[] = { | |
771 | [OVS_TUNNEL_ATTR_FLAGS] = { .type = NL_A_U32 }, | |
772 | [OVS_TUNNEL_ATTR_DST_IPV4] = { .type = NL_A_BE32 }, | |
773 | [OVS_TUNNEL_ATTR_SRC_IPV4] = { .type = NL_A_BE32, .optional = true }, | |
774 | [OVS_TUNNEL_ATTR_IN_KEY] = { .type = NL_A_BE64, .optional = true }, | |
775 | [OVS_TUNNEL_ATTR_OUT_KEY] = { .type = NL_A_BE64, .optional = true }, | |
776 | [OVS_TUNNEL_ATTR_TOS] = { .type = NL_A_U8, .optional = true }, | |
777 | [OVS_TUNNEL_ATTR_TTL] = { .type = NL_A_U8, .optional = true }, | |
79f827fa | 778 | [OVS_TUNNEL_ATTR_DST_PORT] = { .type = NL_A_U16, .optional = true }, |
c19e6535 BP |
779 | }; |
780 | struct ofpbuf buf; | |
781 | ||
782 | ofpbuf_use_const(&buf, options, options_len); | |
df2c07f4 JP |
783 | if (!nl_policy_parse(&buf, 0, ovs_tunnel_policy, |
784 | a, ARRAY_SIZE(ovs_tunnel_policy))) { | |
c19e6535 BP |
785 | return EINVAL; |
786 | } | |
787 | return 0; | |
788 | } | |
789 | ||
790 | static uint64_t | |
791 | get_be64_or_zero(const struct nlattr *a) | |
792 | { | |
793 | return a ? ntohll(nl_attr_get_be64(a)) : 0; | |
794 | } | |
795 | ||
2b9d6589 | 796 | static int |
6d9e6eb4 | 797 | unparse_tunnel_config(const char *name OVS_UNUSED, const char *type OVS_UNUSED, |
c19e6535 | 798 | const struct nlattr *options, size_t options_len, |
79f1cbe9 | 799 | struct smap *args) |
6d9e6eb4 | 800 | { |
df2c07f4 | 801 | struct nlattr *a[OVS_TUNNEL_ATTR_MAX + 1]; |
c19e6535 BP |
802 | ovs_be32 daddr; |
803 | uint32_t flags; | |
804 | int error; | |
6d9e6eb4 | 805 | |
c19e6535 BP |
806 | error = tnl_port_config_from_nlattr(options, options_len, a); |
807 | if (error) { | |
808 | return error; | |
809 | } | |
810 | ||
c19e6535 | 811 | |
df2c07f4 | 812 | daddr = nl_attr_get_be32(a[OVS_TUNNEL_ATTR_DST_IPV4]); |
79f1cbe9 | 813 | smap_add_format(args, "remote_ip", IP_FMT, IP_ARGS(&daddr)); |
c19e6535 | 814 | |
df2c07f4 JP |
815 | if (a[OVS_TUNNEL_ATTR_SRC_IPV4]) { |
816 | ovs_be32 saddr = nl_attr_get_be32(a[OVS_TUNNEL_ATTR_SRC_IPV4]); | |
79f1cbe9 | 817 | smap_add_format(args, "local_ip", IP_FMT, IP_ARGS(&saddr)); |
6d9e6eb4 BP |
818 | } |
819 | ||
df2c07f4 | 820 | if (!a[OVS_TUNNEL_ATTR_IN_KEY] && !a[OVS_TUNNEL_ATTR_OUT_KEY]) { |
6d9e6eb4 | 821 | smap_add(args, "key", "flow"); |
6d9e6eb4 | 822 | } else { |
df2c07f4 JP |
823 | uint64_t in_key = get_be64_or_zero(a[OVS_TUNNEL_ATTR_IN_KEY]); |
824 | uint64_t out_key = get_be64_or_zero(a[OVS_TUNNEL_ATTR_OUT_KEY]); | |
c19e6535 BP |
825 | |
826 | if (in_key && in_key == out_key) { | |
79f1cbe9 | 827 | smap_add_format(args, "key", "%"PRIu64, in_key); |
c19e6535 | 828 | } else { |
df2c07f4 | 829 | if (!a[OVS_TUNNEL_ATTR_IN_KEY]) { |
c19e6535 BP |
830 | smap_add(args, "in_key", "flow"); |
831 | } else if (in_key) { | |
79f1cbe9 | 832 | smap_add_format(args, "in_key", "%"PRIu64, in_key); |
c19e6535 | 833 | } |
6d9e6eb4 | 834 | |
df2c07f4 | 835 | if (!a[OVS_TUNNEL_ATTR_OUT_KEY]) { |
c19e6535 BP |
836 | smap_add(args, "out_key", "flow"); |
837 | } else if (out_key) { | |
79f1cbe9 | 838 | smap_add_format(args, "out_key", "%"PRIu64, out_key); |
c19e6535 | 839 | } |
6d9e6eb4 BP |
840 | } |
841 | } | |
842 | ||
05a9d485 | 843 | flags = nl_attr_get_u32(a[OVS_TUNNEL_ATTR_FLAGS]); |
c19e6535 | 844 | if (flags & TNL_F_TTL_INHERIT) { |
62827e6a | 845 | smap_add(args, "ttl", "inherit"); |
df2c07f4 JP |
846 | } else if (a[OVS_TUNNEL_ATTR_TTL]) { |
847 | int ttl = nl_attr_get_u8(a[OVS_TUNNEL_ATTR_TTL]); | |
62827e6a | 848 | smap_add_format(args, "ttl", "%d", ttl); |
c19e6535 BP |
849 | } |
850 | ||
851 | if (flags & TNL_F_TOS_INHERIT) { | |
6d9e6eb4 | 852 | smap_add(args, "tos", "inherit"); |
df2c07f4 JP |
853 | } else if (a[OVS_TUNNEL_ATTR_TOS]) { |
854 | int tos = nl_attr_get_u8(a[OVS_TUNNEL_ATTR_TOS]); | |
79f1cbe9 | 855 | smap_add_format(args, "tos", "0x%x", tos); |
6d9e6eb4 BP |
856 | } |
857 | ||
79f827fa KM |
858 | if (a[OVS_TUNNEL_ATTR_DST_PORT]) { |
859 | uint16_t dst_port = nl_attr_get_u16(a[OVS_TUNNEL_ATTR_DST_PORT]); | |
860 | if (dst_port != VXLAN_DST_PORT) { | |
861 | smap_add_format(args, "dst_port", "%d", dst_port); | |
862 | } | |
863 | } | |
864 | ||
c19e6535 | 865 | if (flags & TNL_F_CSUM) { |
6d9e6eb4 BP |
866 | smap_add(args, "csum", "true"); |
867 | } | |
66409d1b AE |
868 | if (flags & TNL_F_DF_INHERIT) { |
869 | smap_add(args, "df_inherit", "true"); | |
870 | } | |
871 | if (!(flags & TNL_F_DF_DEFAULT)) { | |
872 | smap_add(args, "df_default", "false"); | |
873 | } | |
85b53b31 AA |
874 | if (flags & TNL_F_PMTUD) { |
875 | smap_add(args, "pmtud", "true"); | |
6d9e6eb4 BP |
876 | } |
877 | ||
878 | return 0; | |
879 | } | |
880 | ||
881 | static int | |
882 | parse_patch_config(const char *name, const char *type OVS_UNUSED, | |
79f1cbe9 | 883 | const struct smap *args, struct ofpbuf *options) |
2b9d6589 | 884 | { |
2b9d6589 BP |
885 | const char *peer; |
886 | ||
79f1cbe9 | 887 | peer = smap_get(args, "peer"); |
2b9d6589 | 888 | if (!peer) { |
8283e514 | 889 | VLOG_ERR("%s: patch type requires valid 'peer' argument", name); |
2b9d6589 BP |
890 | return EINVAL; |
891 | } | |
892 | ||
79f1cbe9 | 893 | if (smap_count(args) > 1) { |
8283e514 | 894 | VLOG_ERR("%s: patch type takes only a 'peer' argument", name); |
2b9d6589 BP |
895 | return EINVAL; |
896 | } | |
897 | ||
c19e6535 | 898 | if (strlen(peer) >= IFNAMSIZ) { |
8283e514 | 899 | VLOG_ERR("%s: patch 'peer' arg too long", name); |
2b9d6589 BP |
900 | return EINVAL; |
901 | } | |
902 | ||
903 | if (!strcmp(name, peer)) { | |
8283e514 | 904 | VLOG_ERR("%s: patch peer must not be self", name); |
2b9d6589 BP |
905 | return EINVAL; |
906 | } | |
907 | ||
df2c07f4 | 908 | nl_msg_put_string(options, OVS_PATCH_ATTR_PEER, peer); |
2b9d6589 BP |
909 | |
910 | return 0; | |
911 | } | |
6d9e6eb4 BP |
912 | |
913 | static int | |
914 | unparse_patch_config(const char *name OVS_UNUSED, const char *type OVS_UNUSED, | |
c19e6535 | 915 | const struct nlattr *options, size_t options_len, |
79f1cbe9 | 916 | struct smap *args) |
6d9e6eb4 | 917 | { |
df2c07f4 JP |
918 | static const struct nl_policy ovs_patch_policy[] = { |
919 | [OVS_PATCH_ATTR_PEER] = { .type = NL_A_STRING, | |
c19e6535 BP |
920 | .max_len = IFNAMSIZ, |
921 | .optional = false } | |
922 | }; | |
923 | ||
df2c07f4 | 924 | struct nlattr *a[ARRAY_SIZE(ovs_patch_policy)]; |
c19e6535 BP |
925 | struct ofpbuf buf; |
926 | ||
927 | ofpbuf_use_const(&buf, options, options_len); | |
df2c07f4 JP |
928 | if (!nl_policy_parse(&buf, 0, ovs_patch_policy, |
929 | a, ARRAY_SIZE(ovs_patch_policy))) { | |
c19e6535 | 930 | return EINVAL; |
6d9e6eb4 BP |
931 | } |
932 | ||
df2c07f4 | 933 | smap_add(args, "peer", nl_attr_get_string(a[OVS_PATCH_ATTR_PEER])); |
6d9e6eb4 BP |
934 | return 0; |
935 | } | |
2b9d6589 | 936 | \f |
ea763e0e | 937 | #define VPORT_FUNCTIONS(GET_STATUS) \ |
b46ccdf5 | 938 | NULL, \ |
ea83a2fc EJ |
939 | netdev_vport_run, \ |
940 | netdev_vport_wait, \ | |
2b9d6589 BP |
941 | \ |
942 | netdev_vport_create, \ | |
943 | netdev_vport_destroy, \ | |
de5cdb90 | 944 | netdev_vport_get_config, \ |
6d9e6eb4 | 945 | netdev_vport_set_config, \ |
2b9d6589 BP |
946 | \ |
947 | netdev_vport_open, \ | |
948 | netdev_vport_close, \ | |
949 | \ | |
7b6b0ef4 | 950 | NULL, /* listen */ \ |
2b9d6589 BP |
951 | NULL, /* recv */ \ |
952 | NULL, /* recv_wait */ \ | |
953 | NULL, /* drain */ \ | |
954 | \ | |
7feba1ac | 955 | netdev_vport_send, /* send */ \ |
2b9d6589 BP |
956 | NULL, /* send_wait */ \ |
957 | \ | |
958 | netdev_vport_set_etheraddr, \ | |
959 | netdev_vport_get_etheraddr, \ | |
14622f22 BP |
960 | NULL, /* get_mtu */ \ |
961 | NULL, /* set_mtu */ \ | |
2b9d6589 | 962 | NULL, /* get_ifindex */ \ |
85da620e | 963 | NULL, /* get_carrier */ \ |
65c3058c | 964 | NULL, /* get_carrier_resets */ \ |
63331829 | 965 | NULL, /* get_miimon */ \ |
2b9d6589 BP |
966 | netdev_vport_get_stats, \ |
967 | netdev_vport_set_stats, \ | |
968 | \ | |
969 | NULL, /* get_features */ \ | |
970 | NULL, /* set_advertisements */ \ | |
2b9d6589 BP |
971 | \ |
972 | NULL, /* set_policing */ \ | |
973 | NULL, /* get_qos_types */ \ | |
974 | NULL, /* get_qos_capabilities */ \ | |
975 | NULL, /* get_qos */ \ | |
976 | NULL, /* set_qos */ \ | |
977 | NULL, /* get_queue */ \ | |
978 | NULL, /* set_queue */ \ | |
979 | NULL, /* delete_queue */ \ | |
980 | NULL, /* get_queue_stats */ \ | |
981 | NULL, /* dump_queues */ \ | |
982 | NULL, /* dump_queue_stats */ \ | |
983 | \ | |
984 | NULL, /* get_in4 */ \ | |
985 | NULL, /* set_in4 */ \ | |
986 | NULL, /* get_in6 */ \ | |
987 | NULL, /* add_router */ \ | |
988 | NULL, /* get_next_hop */ \ | |
ea763e0e | 989 | GET_STATUS, \ |
2b9d6589 BP |
990 | NULL, /* arp_lookup */ \ |
991 | \ | |
992 | netdev_vport_update_flags, \ | |
993 | \ | |
ac4d3bcb | 994 | netdev_vport_change_seq |
2b9d6589 | 995 | |
2b9d6589 BP |
996 | void |
997 | netdev_vport_register(void) | |
998 | { | |
c3827f61 | 999 | static const struct vport_class vport_classes[] = { |
df2c07f4 | 1000 | { OVS_VPORT_TYPE_GRE, |
2c2ea5a8 | 1001 | { "gre", VPORT_FUNCTIONS(netdev_vport_get_drv_info) }, |
de5cdb90 | 1002 | parse_tunnel_config, unparse_tunnel_config }, |
c283069c | 1003 | |
df2c07f4 | 1004 | { OVS_VPORT_TYPE_GRE, |
2c2ea5a8 | 1005 | { "ipsec_gre", VPORT_FUNCTIONS(netdev_vport_get_drv_info) }, |
de5cdb90 | 1006 | parse_tunnel_config, unparse_tunnel_config }, |
c283069c | 1007 | |
2de795ad PS |
1008 | { OVS_VPORT_TYPE_GRE64, |
1009 | { "gre64", VPORT_FUNCTIONS(netdev_vport_get_drv_info) }, | |
1010 | parse_tunnel_config, unparse_tunnel_config }, | |
1011 | ||
1012 | { OVS_VPORT_TYPE_GRE64, | |
1013 | { "ipsec_gre64", VPORT_FUNCTIONS(netdev_vport_get_drv_info) }, | |
1014 | parse_tunnel_config, unparse_tunnel_config }, | |
1015 | ||
df2c07f4 | 1016 | { OVS_VPORT_TYPE_CAPWAP, |
2c2ea5a8 | 1017 | { "capwap", VPORT_FUNCTIONS(netdev_vport_get_drv_info) }, |
de5cdb90 | 1018 | parse_tunnel_config, unparse_tunnel_config }, |
c283069c | 1019 | |
79f827fa KM |
1020 | { OVS_VPORT_TYPE_VXLAN, |
1021 | { "vxlan", VPORT_FUNCTIONS(netdev_vport_get_drv_info) }, | |
1022 | parse_tunnel_config, unparse_tunnel_config }, | |
1023 | ||
df2c07f4 | 1024 | { OVS_VPORT_TYPE_PATCH, |
c283069c | 1025 | { "patch", VPORT_FUNCTIONS(NULL) }, |
de5cdb90 | 1026 | parse_patch_config, unparse_patch_config } |
c3827f61 BP |
1027 | }; |
1028 | ||
1029 | int i; | |
1030 | ||
1031 | for (i = 0; i < ARRAY_SIZE(vport_classes); i++) { | |
1032 | netdev_register_provider(&vport_classes[i].netdev_class); | |
1033 | } | |
2b9d6589 | 1034 | } |