]>
Commit | Line | Data |
---|---|---|
777ece09 JG |
1 | /* |
2 | * Copyright (c) 2010 Nicira Networks. | |
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> | |
2b9d6589 | 23 | #include <net/if.h> |
777ece09 JG |
24 | #include <sys/ioctl.h> |
25 | ||
26 | #include "list.h" | |
2b9d6589 | 27 | #include "netdev-provider.h" |
777ece09 | 28 | #include "openvswitch/datapath-protocol.h" |
2b9d6589 BP |
29 | #include "openvswitch/tunnel.h" |
30 | #include "packets.h" | |
777ece09 JG |
31 | #include "shash.h" |
32 | #include "socket-util.h" | |
777ece09 JG |
33 | #include "vlog.h" |
34 | ||
d98e6007 | 35 | VLOG_DEFINE_THIS_MODULE(netdev_vport); |
5136ce49 | 36 | |
777ece09 JG |
37 | struct netdev_vport_notifier { |
38 | struct netdev_notifier notifier; | |
39 | struct list list_node; | |
d295e8e9 | 40 | struct shash_node *shash_node; |
777ece09 JG |
41 | }; |
42 | ||
2b9d6589 BP |
43 | struct netdev_dev_vport { |
44 | struct netdev_dev netdev_dev; | |
45 | }; | |
46 | ||
47 | struct netdev_vport { | |
48 | struct netdev netdev; | |
49 | }; | |
50 | ||
51 | struct vport_info { | |
52 | const char *devname; | |
53 | const char *type; | |
54 | void *config; | |
55 | }; | |
56 | ||
57 | struct vport_class { | |
58 | const struct netdev_class netdev_class; | |
59 | int (*parse_config)(struct vport_info *port, const struct shash *args); | |
60 | }; | |
61 | ||
777ece09 JG |
62 | static struct shash netdev_vport_notifiers = |
63 | SHASH_INITIALIZER(&netdev_vport_notifiers); | |
64 | ||
65 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20); | |
66 | ||
2b9d6589 BP |
67 | static int netdev_vport_do_ioctl(int cmd, void *arg); |
68 | static int netdev_vport_create(const struct netdev_class *, const char *, | |
69 | const struct shash *, struct netdev_dev **); | |
70 | static void netdev_vport_poll_notify(const struct netdev *); | |
71 | ||
72 | static bool | |
73 | is_vport_class(const struct netdev_class *class) | |
777ece09 | 74 | { |
2b9d6589 BP |
75 | return class->create == netdev_vport_create; |
76 | } | |
777ece09 | 77 | |
2b9d6589 BP |
78 | static const struct vport_class * |
79 | vport_class_cast(const struct netdev_class *class) | |
80 | { | |
81 | assert(is_vport_class(class)); | |
82 | return CONTAINER_OF(class, struct vport_class, netdev_class); | |
83 | } | |
84 | ||
85 | static struct netdev_dev_vport * | |
86 | netdev_dev_vport_cast(const struct netdev_dev *netdev_dev) | |
87 | { | |
88 | assert(is_vport_class(netdev_dev_get_class(netdev_dev))); | |
89 | return CONTAINER_OF(netdev_dev, struct netdev_dev_vport, netdev_dev); | |
90 | } | |
91 | ||
92 | static struct netdev_vport * | |
93 | netdev_vport_cast(const struct netdev *netdev) | |
94 | { | |
95 | struct netdev_dev *netdev_dev = netdev_get_dev(netdev); | |
96 | assert(is_vport_class(netdev_dev_get_class(netdev_dev))); | |
97 | return CONTAINER_OF(netdev, struct netdev_vport, netdev); | |
98 | } | |
99 | ||
100 | static int | |
101 | netdev_vport_parse_config(const struct netdev_class *netdev_class, | |
102 | const char *name, const struct shash *args, | |
103 | void **configp) | |
104 | { | |
105 | const struct vport_class *c = vport_class_cast(netdev_class); | |
106 | if (c->parse_config) { | |
107 | struct vport_info info; | |
108 | int error; | |
109 | ||
110 | info.devname = name; | |
111 | info.type = netdev_class->type; | |
112 | error = (c->parse_config)(&info, args); | |
113 | *configp = info.config; | |
114 | return error; | |
115 | } else { | |
116 | if (!shash_is_empty(args)) { | |
117 | VLOG_WARN("%s: arguments for %s vports should be empty", | |
118 | name, netdev_class->type); | |
777ece09 | 119 | } |
2b9d6589 BP |
120 | *configp = NULL; |
121 | return 0; | |
777ece09 | 122 | } |
2b9d6589 | 123 | } |
777ece09 | 124 | |
2b9d6589 BP |
125 | static int |
126 | netdev_vport_create(const struct netdev_class *class, const char *name, | |
127 | const struct shash *args OVS_UNUSED, | |
128 | struct netdev_dev **netdev_devp) | |
129 | { | |
130 | int err; | |
131 | struct odp_vport_add ova; | |
132 | struct netdev_dev_vport *netdev_dev; | |
133 | ||
134 | ovs_strlcpy(ova.port_type, class->type, sizeof ova.port_type); | |
135 | ovs_strlcpy(ova.devname, name, sizeof ova.devname); | |
136 | err = netdev_vport_parse_config(class, name, args, &ova.config); | |
137 | if (err) { | |
138 | goto exit; | |
139 | } | |
140 | ||
141 | err = netdev_vport_do_ioctl(ODP_VPORT_ADD, &ova); | |
142 | ||
143 | if (err == EBUSY) { | |
144 | VLOG_WARN("%s: destroying existing device", name); | |
145 | ||
146 | err = netdev_vport_do_ioctl(ODP_VPORT_DEL, ova.devname); | |
147 | if (err) { | |
148 | goto exit; | |
149 | } | |
150 | ||
151 | err = netdev_vport_do_ioctl(ODP_VPORT_ADD, &ova); | |
152 | } | |
153 | if (err) { | |
154 | goto exit; | |
155 | } | |
156 | ||
157 | netdev_dev = xmalloc(sizeof *netdev_dev); | |
158 | netdev_dev_init(&netdev_dev->netdev_dev, name, class); | |
159 | ||
160 | *netdev_devp = &netdev_dev->netdev_dev; | |
161 | ||
162 | exit: | |
163 | free(ova.config); | |
164 | return err; | |
777ece09 JG |
165 | } |
166 | ||
2b9d6589 BP |
167 | static void |
168 | netdev_vport_destroy(struct netdev_dev *netdev_dev_) | |
169 | { | |
170 | struct netdev_dev_vport *netdev_dev = netdev_dev_vport_cast(netdev_dev_); | |
171 | ||
65d9664a JP |
172 | netdev_vport_do_ioctl(ODP_VPORT_DEL, |
173 | (char *)netdev_dev_get_name(netdev_dev_)); | |
2b9d6589 BP |
174 | free(netdev_dev); |
175 | } | |
176 | ||
177 | static int | |
178 | netdev_vport_open(struct netdev_dev *netdev_dev_, int ethertype OVS_UNUSED, | |
179 | struct netdev **netdevp) | |
180 | { | |
181 | struct netdev_vport *netdev; | |
182 | ||
183 | netdev = xmalloc(sizeof *netdev); | |
184 | netdev_init(&netdev->netdev, netdev_dev_); | |
185 | ||
186 | *netdevp = &netdev->netdev; | |
187 | return 0; | |
188 | } | |
189 | ||
190 | static void | |
191 | netdev_vport_close(struct netdev *netdev_) | |
192 | { | |
193 | struct netdev_vport *netdev = netdev_vport_cast(netdev_); | |
194 | free(netdev); | |
195 | } | |
196 | ||
197 | static int | |
198 | netdev_vport_reconfigure(struct netdev_dev *netdev_dev, | |
199 | const struct shash *args) | |
200 | { | |
201 | const char *name = netdev_dev_get_name(netdev_dev); | |
202 | struct odp_vport_mod ovm; | |
203 | int err; | |
204 | ||
205 | ovs_strlcpy(ovm.devname, name, sizeof ovm.devname); | |
206 | err = netdev_vport_parse_config(netdev_dev_get_class(netdev_dev), name, | |
207 | args, &ovm.config); | |
208 | if (err) { | |
209 | return err; | |
210 | } | |
211 | ||
212 | err = netdev_vport_do_ioctl(ODP_VPORT_MOD, &ovm); | |
213 | free(ovm.config); | |
214 | return err; | |
215 | } | |
216 | ||
217 | static int | |
777ece09 JG |
218 | netdev_vport_set_etheraddr(struct netdev *netdev, |
219 | const uint8_t mac[ETH_ADDR_LEN]) | |
220 | { | |
221 | struct odp_vport_ether vport_ether; | |
222 | int err; | |
223 | ||
224 | ovs_strlcpy(vport_ether.devname, netdev_get_name(netdev), | |
225 | sizeof vport_ether.devname); | |
226 | ||
227 | memcpy(vport_ether.ether_addr, mac, ETH_ADDR_LEN); | |
228 | ||
229 | err = netdev_vport_do_ioctl(ODP_VPORT_ETHER_SET, &vport_ether); | |
230 | if (err) { | |
231 | return err; | |
232 | } | |
233 | ||
234 | netdev_vport_poll_notify(netdev); | |
235 | return 0; | |
236 | } | |
237 | ||
2b9d6589 | 238 | static int |
777ece09 JG |
239 | netdev_vport_get_etheraddr(const struct netdev *netdev, |
240 | uint8_t mac[ETH_ADDR_LEN]) | |
241 | { | |
242 | struct odp_vport_ether vport_ether; | |
243 | int err; | |
244 | ||
245 | ovs_strlcpy(vport_ether.devname, netdev_get_name(netdev), | |
246 | sizeof vport_ether.devname); | |
247 | ||
248 | err = netdev_vport_do_ioctl(ODP_VPORT_ETHER_GET, &vport_ether); | |
249 | if (err) { | |
250 | return err; | |
251 | } | |
252 | ||
253 | memcpy(mac, vport_ether.ether_addr, ETH_ADDR_LEN); | |
254 | return 0; | |
255 | } | |
256 | ||
2b9d6589 | 257 | static int |
777ece09 JG |
258 | netdev_vport_get_mtu(const struct netdev *netdev, int *mtup) |
259 | { | |
260 | struct odp_vport_mtu vport_mtu; | |
261 | int err; | |
262 | ||
263 | ovs_strlcpy(vport_mtu.devname, netdev_get_name(netdev), | |
264 | sizeof vport_mtu.devname); | |
265 | ||
266 | err = netdev_vport_do_ioctl(ODP_VPORT_MTU_GET, &vport_mtu); | |
267 | if (err) { | |
268 | return err; | |
269 | } | |
270 | ||
271 | *mtup = vport_mtu.mtu; | |
272 | return 0; | |
273 | } | |
274 | ||
777ece09 JG |
275 | int |
276 | netdev_vport_get_stats(const struct netdev *netdev, struct netdev_stats *stats) | |
277 | { | |
278 | const char *name = netdev_get_name(netdev); | |
279 | struct odp_vport_stats_req ovsr; | |
280 | int err; | |
281 | ||
282 | ovs_strlcpy(ovsr.devname, name, sizeof ovsr.devname); | |
283 | err = netdev_vport_do_ioctl(ODP_VPORT_STATS_GET, &ovsr); | |
284 | if (err) { | |
285 | return err; | |
286 | } | |
287 | ||
288 | stats->rx_packets = ovsr.stats.rx_packets; | |
289 | stats->tx_packets = ovsr.stats.tx_packets; | |
290 | stats->rx_bytes = ovsr.stats.rx_bytes; | |
291 | stats->tx_bytes = ovsr.stats.tx_bytes; | |
292 | stats->rx_errors = ovsr.stats.rx_errors; | |
293 | stats->tx_errors = ovsr.stats.tx_errors; | |
294 | stats->rx_dropped = ovsr.stats.rx_dropped; | |
295 | stats->tx_dropped = ovsr.stats.tx_dropped; | |
296 | stats->multicast = UINT64_MAX; | |
297 | stats->collisions = ovsr.stats.collisions; | |
298 | stats->rx_length_errors = UINT64_MAX; | |
299 | stats->rx_over_errors = ovsr.stats.rx_over_err; | |
300 | stats->rx_crc_errors = ovsr.stats.rx_crc_err; | |
301 | stats->rx_frame_errors = ovsr.stats.rx_frame_err; | |
302 | stats->rx_fifo_errors = UINT64_MAX; | |
303 | stats->rx_missed_errors = UINT64_MAX; | |
304 | stats->tx_aborted_errors = UINT64_MAX; | |
305 | stats->tx_carrier_errors = UINT64_MAX; | |
306 | stats->tx_fifo_errors = UINT64_MAX; | |
307 | stats->tx_heartbeat_errors = UINT64_MAX; | |
308 | stats->tx_window_errors = UINT64_MAX; | |
309 | ||
310 | return 0; | |
311 | } | |
312 | ||
f4b6076a JG |
313 | int |
314 | netdev_vport_set_stats(struct netdev *netdev, const struct netdev_stats *stats) | |
315 | { | |
316 | struct odp_vport_stats_req ovsr; | |
317 | int err; | |
318 | ||
319 | ovs_strlcpy(ovsr.devname, netdev_get_name(netdev), sizeof ovsr.devname); | |
320 | ||
321 | ovsr.stats.rx_packets = stats->rx_packets; | |
322 | ovsr.stats.tx_packets = stats->tx_packets; | |
323 | ovsr.stats.rx_bytes = stats->rx_bytes; | |
324 | ovsr.stats.tx_bytes = stats->tx_bytes; | |
325 | ovsr.stats.rx_errors = stats->rx_errors; | |
326 | ovsr.stats.tx_errors = stats->tx_errors; | |
327 | ovsr.stats.rx_dropped = stats->rx_dropped; | |
328 | ovsr.stats.tx_dropped = stats->tx_dropped; | |
329 | ovsr.stats.collisions = stats->collisions; | |
330 | ovsr.stats.rx_over_err = stats->rx_over_errors; | |
331 | ovsr.stats.rx_crc_err = stats->rx_crc_errors; | |
332 | ovsr.stats.rx_frame_err = stats->rx_frame_errors; | |
333 | ||
334 | err = netdev_vport_do_ioctl(ODP_VPORT_STATS_SET, &ovsr); | |
335 | ||
336 | /* If the vport layer doesn't know about the device, that doesn't mean it | |
337 | * doesn't exist (after all were able to open it when netdev_open() was | |
338 | * called), it just means that it isn't attached and we'll be getting | |
339 | * stats a different way. */ | |
340 | if (err == ENODEV) { | |
341 | err = EOPNOTSUPP; | |
342 | } | |
343 | ||
344 | return err; | |
345 | } | |
346 | ||
2b9d6589 | 347 | static int |
777ece09 JG |
348 | netdev_vport_update_flags(struct netdev *netdev OVS_UNUSED, |
349 | enum netdev_flags off, enum netdev_flags on OVS_UNUSED, | |
350 | enum netdev_flags *old_flagsp) | |
351 | { | |
352 | if (off & (NETDEV_UP | NETDEV_PROMISC)) { | |
353 | return EOPNOTSUPP; | |
354 | } | |
355 | ||
356 | *old_flagsp = NETDEV_UP | NETDEV_PROMISC; | |
357 | return 0; | |
358 | } | |
359 | ||
360 | static char * | |
361 | make_poll_name(const struct netdev *netdev) | |
362 | { | |
363 | return xasprintf("%s:%s", netdev_get_type(netdev), netdev_get_name(netdev)); | |
364 | } | |
365 | ||
2b9d6589 | 366 | static int |
777ece09 JG |
367 | netdev_vport_poll_add(struct netdev *netdev, |
368 | void (*cb)(struct netdev_notifier *), void *aux, | |
369 | struct netdev_notifier **notifierp) | |
370 | { | |
371 | char *poll_name = make_poll_name(netdev); | |
372 | struct netdev_vport_notifier *notifier; | |
373 | struct list *list; | |
374 | struct shash_node *shash_node; | |
375 | ||
376 | shash_node = shash_find_data(&netdev_vport_notifiers, poll_name); | |
377 | if (!shash_node) { | |
378 | list = xmalloc(sizeof *list); | |
379 | list_init(list); | |
380 | shash_node = shash_add(&netdev_vport_notifiers, | |
381 | netdev_get_name(netdev), list); | |
382 | } else { | |
383 | list = shash_node->data; | |
384 | } | |
385 | ||
386 | notifier = xmalloc(sizeof *notifier); | |
387 | netdev_notifier_init(¬ifier->notifier, netdev, cb, aux); | |
388 | list_push_back(list, ¬ifier->list_node); | |
389 | notifier->shash_node = shash_node; | |
390 | ||
391 | *notifierp = ¬ifier->notifier; | |
392 | free(poll_name); | |
393 | ||
394 | return 0; | |
395 | } | |
396 | ||
2b9d6589 | 397 | static void |
777ece09 JG |
398 | netdev_vport_poll_remove(struct netdev_notifier *notifier_) |
399 | { | |
400 | struct netdev_vport_notifier *notifier = | |
401 | CONTAINER_OF(notifier_, struct netdev_vport_notifier, notifier); | |
402 | ||
403 | struct list *list; | |
404 | ||
405 | list = list_remove(¬ifier->list_node); | |
406 | if (list_is_empty(list)) { | |
407 | shash_delete(&netdev_vport_notifiers, notifier->shash_node); | |
408 | free(list); | |
409 | } | |
410 | ||
411 | free(notifier); | |
412 | } | |
2b9d6589 BP |
413 | \f |
414 | /* Helper functions. */ | |
777ece09 | 415 | |
2b9d6589 BP |
416 | static int |
417 | netdev_vport_do_ioctl(int cmd, void *arg) | |
418 | { | |
419 | static int ioctl_fd = -1; | |
420 | ||
421 | if (ioctl_fd < 0) { | |
422 | ioctl_fd = open("/dev/net/dp0", O_RDONLY | O_NONBLOCK); | |
423 | if (ioctl_fd < 0) { | |
424 | VLOG_ERR_RL(&rl, "failed to open ioctl fd: %s", strerror(errno)); | |
425 | return errno; | |
426 | } | |
427 | } | |
428 | ||
429 | return ioctl(ioctl_fd, cmd, arg) ? errno : 0; | |
430 | } | |
431 | ||
432 | static void | |
777ece09 JG |
433 | netdev_vport_poll_notify(const struct netdev *netdev) |
434 | { | |
435 | char *poll_name = make_poll_name(netdev); | |
436 | struct list *list = shash_find_data(&netdev_vport_notifiers, | |
437 | poll_name); | |
438 | ||
439 | if (list) { | |
440 | struct netdev_vport_notifier *notifier; | |
441 | ||
4e8e4213 | 442 | LIST_FOR_EACH (notifier, list_node, list) { |
777ece09 JG |
443 | struct netdev_notifier *n = ¬ifier->notifier; |
444 | n->cb(n); | |
445 | } | |
446 | } | |
447 | ||
448 | free(poll_name); | |
449 | } | |
2b9d6589 BP |
450 | \f |
451 | /* Code specific to individual vport types. */ | |
452 | ||
453 | static int | |
454 | parse_tunnel_config(struct vport_info *port, const struct shash *args) | |
455 | { | |
456 | const char *name = port->devname; | |
457 | bool is_gre = !strcmp(port->type, "gre"); | |
458 | struct tnl_port_config *config; | |
459 | struct shash_node *node; | |
460 | bool ipsec_ip_set = false; | |
461 | bool ipsec_mech_set = false; | |
462 | ||
463 | config = port->config = xzalloc(sizeof *config); | |
464 | config->flags |= TNL_F_PMTUD; | |
465 | config->flags |= TNL_F_HDR_CACHE; | |
466 | ||
467 | SHASH_FOR_EACH (node, args) { | |
468 | if (!strcmp(node->name, "remote_ip")) { | |
469 | struct in_addr in_addr; | |
470 | if (lookup_ip(node->data, &in_addr)) { | |
471 | VLOG_WARN("%s: bad %s 'remote_ip'", name, port->type); | |
472 | } else { | |
473 | config->daddr = in_addr.s_addr; | |
474 | } | |
475 | } else if (!strcmp(node->name, "local_ip")) { | |
476 | struct in_addr in_addr; | |
477 | if (lookup_ip(node->data, &in_addr)) { | |
478 | VLOG_WARN("%s: bad %s 'local_ip'", name, port->type); | |
479 | } else { | |
480 | config->saddr = in_addr.s_addr; | |
481 | } | |
482 | } else if (!strcmp(node->name, "key") && is_gre) { | |
483 | if (!strcmp(node->data, "flow")) { | |
484 | config->flags |= TNL_F_IN_KEY_MATCH; | |
485 | config->flags |= TNL_F_OUT_KEY_ACTION; | |
486 | } else { | |
487 | config->out_key = config->in_key = htonl(atoi(node->data)); | |
488 | } | |
489 | } else if (!strcmp(node->name, "in_key") && is_gre) { | |
490 | if (!strcmp(node->data, "flow")) { | |
491 | config->flags |= TNL_F_IN_KEY_MATCH; | |
492 | } else { | |
493 | config->in_key = htonl(atoi(node->data)); | |
494 | } | |
495 | } else if (!strcmp(node->name, "out_key") && is_gre) { | |
496 | if (!strcmp(node->data, "flow")) { | |
497 | config->flags |= TNL_F_OUT_KEY_ACTION; | |
498 | } else { | |
499 | config->out_key = htonl(atoi(node->data)); | |
500 | } | |
501 | } else if (!strcmp(node->name, "tos")) { | |
502 | if (!strcmp(node->data, "inherit")) { | |
503 | config->flags |= TNL_F_TOS_INHERIT; | |
504 | } else { | |
505 | config->tos = atoi(node->data); | |
506 | } | |
507 | } else if (!strcmp(node->name, "ttl")) { | |
508 | if (!strcmp(node->data, "inherit")) { | |
509 | config->flags |= TNL_F_TTL_INHERIT; | |
510 | } else { | |
511 | config->ttl = atoi(node->data); | |
512 | } | |
513 | } else if (!strcmp(node->name, "csum") && is_gre) { | |
514 | if (!strcmp(node->data, "true")) { | |
515 | config->flags |= TNL_F_CSUM; | |
516 | } | |
517 | } else if (!strcmp(node->name, "pmtud")) { | |
518 | if (!strcmp(node->data, "false")) { | |
519 | config->flags &= ~TNL_F_PMTUD; | |
520 | } | |
521 | } else if (!strcmp(node->name, "header_cache")) { | |
522 | if (!strcmp(node->data, "false")) { | |
523 | config->flags &= ~TNL_F_HDR_CACHE; | |
524 | } | |
525 | } else if (!strcmp(node->name, "ipsec_local_ip")) { | |
526 | ipsec_ip_set = true; | |
527 | } else if (!strcmp(node->name, "ipsec_cert") | |
528 | || !strcmp(node->name, "ipsec_psk")) { | |
529 | ipsec_mech_set = true; | |
530 | } else { | |
531 | VLOG_WARN("%s: unknown %s argument '%s'", | |
532 | name, port->type, node->name); | |
533 | } | |
534 | } | |
535 | ||
536 | /* IPsec doesn't work when header caching is enabled. Disable it if the | |
537 | * IPsec local IP address and authentication mechanism have been defined. */ | |
538 | if (ipsec_ip_set && ipsec_mech_set) { | |
539 | VLOG_INFO("%s: header caching disabled due to use of IPsec", name); | |
540 | config->flags &= ~TNL_F_HDR_CACHE; | |
541 | } | |
542 | ||
543 | if (!config->daddr) { | |
544 | VLOG_WARN("%s: %s type requires valid 'remote_ip' argument", | |
545 | name, port->type); | |
546 | return EINVAL; | |
547 | } | |
548 | ||
549 | return 0; | |
550 | } | |
551 | ||
552 | static int | |
553 | parse_patch_config(struct vport_info *port, const struct shash *args) | |
554 | { | |
555 | const char *name = port->devname; | |
556 | const char *peer; | |
557 | ||
558 | peer = shash_find_data(args, "peer"); | |
559 | if (!peer) { | |
560 | VLOG_WARN("%s: patch type requires valid 'peer' argument", name); | |
561 | return EINVAL; | |
562 | } | |
563 | ||
564 | if (shash_count(args) > 1) { | |
565 | VLOG_WARN("%s: patch type takes only a 'peer' argument", name); | |
566 | return EINVAL; | |
567 | } | |
568 | ||
569 | if (strlen(peer) >= IFNAMSIZ) { | |
570 | VLOG_WARN("%s: patch 'peer' arg too long", name); | |
571 | return EINVAL; | |
572 | } | |
573 | ||
574 | if (!strcmp(name, peer)) { | |
575 | VLOG_WARN("%s: patch peer must not be self", name); | |
576 | return EINVAL; | |
577 | } | |
578 | ||
579 | port->config = xstrdup(peer); | |
580 | ||
581 | return 0; | |
582 | } | |
583 | \f | |
584 | #define VPORT_FUNCTIONS \ | |
585 | NULL, /* init */ \ | |
586 | NULL, /* run */ \ | |
587 | NULL, /* wait */ \ | |
588 | \ | |
589 | netdev_vport_create, \ | |
590 | netdev_vport_destroy, \ | |
591 | netdev_vport_reconfigure, \ | |
592 | \ | |
593 | netdev_vport_open, \ | |
594 | netdev_vport_close, \ | |
595 | \ | |
596 | NULL, /* enumerate */ \ | |
597 | \ | |
598 | NULL, /* recv */ \ | |
599 | NULL, /* recv_wait */ \ | |
600 | NULL, /* drain */ \ | |
601 | \ | |
602 | NULL, /* send */ \ | |
603 | NULL, /* send_wait */ \ | |
604 | \ | |
605 | netdev_vport_set_etheraddr, \ | |
606 | netdev_vport_get_etheraddr, \ | |
607 | netdev_vport_get_mtu, \ | |
608 | NULL, /* get_ifindex */ \ | |
85da620e | 609 | NULL, /* get_carrier */ \ |
2b9d6589 BP |
610 | netdev_vport_get_stats, \ |
611 | netdev_vport_set_stats, \ | |
612 | \ | |
613 | NULL, /* get_features */ \ | |
614 | NULL, /* set_advertisements */ \ | |
615 | NULL, /* get_vlan_vid */ \ | |
616 | \ | |
617 | NULL, /* set_policing */ \ | |
618 | NULL, /* get_qos_types */ \ | |
619 | NULL, /* get_qos_capabilities */ \ | |
620 | NULL, /* get_qos */ \ | |
621 | NULL, /* set_qos */ \ | |
622 | NULL, /* get_queue */ \ | |
623 | NULL, /* set_queue */ \ | |
624 | NULL, /* delete_queue */ \ | |
625 | NULL, /* get_queue_stats */ \ | |
626 | NULL, /* dump_queues */ \ | |
627 | NULL, /* dump_queue_stats */ \ | |
628 | \ | |
629 | NULL, /* get_in4 */ \ | |
630 | NULL, /* set_in4 */ \ | |
631 | NULL, /* get_in6 */ \ | |
632 | NULL, /* add_router */ \ | |
633 | NULL, /* get_next_hop */ \ | |
634 | NULL, /* arp_lookup */ \ | |
635 | \ | |
636 | netdev_vport_update_flags, \ | |
637 | \ | |
638 | netdev_vport_poll_add, \ | |
639 | netdev_vport_poll_remove, | |
640 | ||
641 | static const struct vport_class vport_gre_class | |
642 | = { { "gre", VPORT_FUNCTIONS }, parse_tunnel_config }; | |
643 | ||
644 | static const struct vport_class vport_capwap_class | |
645 | = { { "capwap", VPORT_FUNCTIONS }, parse_tunnel_config }; | |
646 | ||
647 | static const struct vport_class vport_patch_class | |
648 | = { { "patch", VPORT_FUNCTIONS }, parse_patch_config }; | |
649 | ||
650 | void | |
651 | netdev_vport_register(void) | |
652 | { | |
653 | netdev_register_provider(&vport_gre_class.netdev_class); | |
654 | netdev_register_provider(&vport_capwap_class.netdev_class); | |
655 | netdev_register_provider(&vport_patch_class.netdev_class); | |
656 | } |