From 614c4892032f424efa5f0ec404b2d499acad254d Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Mon, 29 Nov 2010 12:21:08 -0800 Subject: [PATCH] Add new "dummy" netdev and dpif implementations for use in unit tests. --- lib/automake.mk | 3 + lib/dpif-netdev.c | 38 ++++- lib/dummy.c | 31 ++++ lib/dummy.h | 27 ++++ lib/netdev-dummy.c | 335 ++++++++++++++++++++++++++++++++++++++++ lib/unixctl.c | 19 ++- lib/unixctl.man | 2 + lib/vlog-modules.def | 1 + vswitchd/ovs-vswitchd.c | 9 +- 9 files changed, 455 insertions(+), 10 deletions(-) create mode 100644 lib/dummy.c create mode 100644 lib/dummy.h create mode 100644 lib/netdev-dummy.c diff --git a/lib/automake.mk b/lib/automake.mk index 719ae488..5cd4722e 100644 --- a/lib/automake.mk +++ b/lib/automake.mk @@ -31,6 +31,8 @@ lib_libopenvswitch_a_SOURCES = \ lib/csum.h \ lib/daemon.c \ lib/daemon.h \ + lib/dummy.c \ + lib/dummy.h \ lib/dhcp-client.c \ lib/dhcp-client.h \ lib/dhcp.c \ @@ -67,6 +69,7 @@ lib_libopenvswitch_a_SOURCES = \ lib/lockfile.h \ lib/mac-learning.c \ lib/mac-learning.h \ + lib/netdev-dummy.c \ lib/netdev-provider.h \ lib/netdev.c \ lib/netdev.h \ diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c index 3cecde71..096656e6 100644 --- a/lib/dpif-netdev.c +++ b/lib/dpif-netdev.c @@ -32,7 +32,9 @@ #include #include "csum.h" +#include "dpif.h" #include "dpif-provider.h" +#include "dummy.h" #include "flow.h" #include "hmap.h" #include "list.h" @@ -62,6 +64,7 @@ enum { DP_NETDEV_HEADROOM = 2 + VLAN_HEADER_LEN }; /* Datapath based on the network device interface from netdev.h. */ struct dp_netdev { + const struct dpif_class *class; char *name; int open_cnt; bool destroyed; @@ -130,16 +133,20 @@ static void dp_netdev_flow_flush(struct dp_netdev *); static int do_add_port(struct dp_netdev *, const char *devname, uint16_t flags, uint16_t port_no); static int do_del_port(struct dp_netdev *, uint16_t port_no); +static int dpif_netdev_open(const struct dpif_class *, const char *name, + bool create, struct dpif **); static int dp_netdev_output_control(struct dp_netdev *, const struct ofpbuf *, int queue_no, int port_no, uint32_t arg); static int dp_netdev_execute_actions(struct dp_netdev *, struct ofpbuf *, struct flow *, const union odp_action *, int n); +static struct dpif_class dpif_dummy_class; + static struct dpif_netdev * dpif_netdev_cast(const struct dpif *dpif) { - dpif_assert_class(dpif, &dpif_netdev_class); + assert(dpif->dpif_class->open == dpif_netdev_open); return CONTAINER_OF(dpif, struct dpif_netdev, dpif); } @@ -158,8 +165,7 @@ create_dpif_netdev(struct dp_netdev *dp) dp->open_cnt++; dpif = xmalloc(sizeof *dpif); - dpif_init(&dpif->dpif, &dpif_netdev_class, dp->name, - netflow_id >> 8, netflow_id); + dpif_init(&dpif->dpif, dp->class, dp->name, netflow_id >> 8, netflow_id); dpif->dp = dp; dpif->listen_mask = 0; dpif->dp_serial = dp->serial; @@ -168,13 +174,15 @@ create_dpif_netdev(struct dp_netdev *dp) } static int -create_dp_netdev(const char *name, struct dp_netdev **dpp) +create_dp_netdev(const char *name, const struct dpif_class *class, + struct dp_netdev **dpp) { struct dp_netdev *dp; int error; int i; dp = xzalloc(sizeof *dp); + dp->class = class; dp->name = xstrdup(name); dp->open_cnt = 0; dp->drop_frags = false; @@ -196,7 +204,7 @@ create_dp_netdev(const char *name, struct dp_netdev **dpp) } static int -dpif_netdev_open(const struct dpif_class *class OVS_UNUSED, const char *name, +dpif_netdev_open(const struct dpif_class *class, const char *name, bool create, struct dpif **dpifp) { struct dp_netdev *dp; @@ -206,14 +214,16 @@ dpif_netdev_open(const struct dpif_class *class OVS_UNUSED, const char *name, if (!create) { return ENODEV; } else { - int error = create_dp_netdev(name, &dp); + int error = create_dp_netdev(name, class, &dp); if (error) { return error; } assert(dp != NULL); } } else { - if (create) { + if (dp->class != class) { + return EINVAL; + } else if (create) { return EEXIST; } } @@ -313,7 +323,9 @@ do_add_port(struct dp_netdev *dp, const char *devname, uint16_t flags, memset(&netdev_options, 0, sizeof netdev_options); netdev_options.name = devname; netdev_options.ethertype = NETDEV_ETH_TYPE_ANY; - if (internal) { + if (dp->class == &dpif_dummy_class) { + netdev_options.type = "dummy"; + } else if (internal) { netdev_options.type = "tap"; } @@ -1249,3 +1261,13 @@ const struct dpif_class dpif_netdev_class = { dpif_netdev_recv, dpif_netdev_recv_wait, }; + +void +dpif_dummy_register(void) +{ + if (!dpif_dummy_class.type) { + dpif_dummy_class = dpif_netdev_class; + dpif_dummy_class.type = "dummy"; + dp_register_provider(&dpif_dummy_class); + } +} diff --git a/lib/dummy.c b/lib/dummy.c new file mode 100644 index 00000000..3916febd --- /dev/null +++ b/lib/dummy.c @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2010 Nicira Networks. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "dummy.h" + +/* Enables support for "dummy" network devices and dpifs, which are useful for + * testing. A client program might call this function if it is designed + * specifically for testing or the user enables it on the command line. + * + * There is no strong reason why dummy devices shouldn't always be enabled. */ +void +dummy_enable(void) +{ + netdev_dummy_register(); + dpif_dummy_register(); +} diff --git a/lib/dummy.h b/lib/dummy.h new file mode 100644 index 00000000..fe150870 --- /dev/null +++ b/lib/dummy.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2010 Nicira Networks. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DUMMY_H +#define DUMMY_H 1 + +/* For client programs to call directly to enable dummy support. */ +void dummy_enable(void); + +/* Implementation details. */ +void dpif_dummy_register(void); +void netdev_dummy_register(void); + +#endif /* dummy.h */ diff --git a/lib/netdev-dummy.c b/lib/netdev-dummy.c new file mode 100644 index 00000000..ddcbe363 --- /dev/null +++ b/lib/netdev-dummy.c @@ -0,0 +1,335 @@ +/* + * Copyright (c) 2010 Nicira Networks. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "dummy.h" + +#include + +#include "list.h" +#include "netdev-provider.h" +#include "packets.h" +#include "shash.h" +#include "vlog.h" + +VLOG_DEFINE_THIS_MODULE(netdev_dummy); + +struct netdev_dummy_notifier { + struct netdev_notifier notifier; + struct list list_node; + struct shash_node *shash_node; +}; + +struct netdev_dev_dummy { + struct netdev_dev netdev_dev; + uint8_t hwaddr[ETH_ADDR_LEN]; + int mtu; + struct netdev_stats stats; + enum netdev_flags flags; +}; + +struct netdev_dummy { + struct netdev netdev; +}; + +static struct shash netdev_dummy_notifiers = + SHASH_INITIALIZER(&netdev_dummy_notifiers); + +static int netdev_dummy_create(const struct netdev_class *, const char *, + const struct shash *, struct netdev_dev **); +static void netdev_dummy_poll_notify(const struct netdev *); + +static bool +is_dummy_class(const struct netdev_class *class) +{ + return class->create == netdev_dummy_create; +} + +static struct netdev_dev_dummy * +netdev_dev_dummy_cast(const struct netdev_dev *netdev_dev) +{ + assert(is_dummy_class(netdev_dev_get_class(netdev_dev))); + return CONTAINER_OF(netdev_dev, struct netdev_dev_dummy, netdev_dev); +} + +static struct netdev_dummy * +netdev_dummy_cast(const struct netdev *netdev) +{ + struct netdev_dev *netdev_dev = netdev_get_dev(netdev); + assert(is_dummy_class(netdev_dev_get_class(netdev_dev))); + return CONTAINER_OF(netdev, struct netdev_dummy, netdev); +} + +static int +netdev_dummy_create(const struct netdev_class *class, const char *name, + const struct shash *args OVS_UNUSED, + struct netdev_dev **netdev_devp) +{ + static unsigned int n = 0xaa550000; + struct netdev_dev_dummy *netdev_dev; + + netdev_dev = xzalloc(sizeof *netdev_dev); + netdev_dev_init(&netdev_dev->netdev_dev, name, class); + netdev_dev->hwaddr[0] = 0xaa; + netdev_dev->hwaddr[1] = 0x55; + netdev_dev->hwaddr[2] = n >> 24; + netdev_dev->hwaddr[3] = n >> 16; + netdev_dev->hwaddr[4] = n >> 8; + netdev_dev->hwaddr[5] = n; + netdev_dev->mtu = 1500; + netdev_dev->flags = 0; + + n++; + + *netdev_devp = &netdev_dev->netdev_dev; + + return 0; +} + +static void +netdev_dummy_destroy(struct netdev_dev *netdev_dev_) +{ + struct netdev_dev_dummy *netdev_dev = netdev_dev_dummy_cast(netdev_dev_); + + free(netdev_dev); +} + +static int +netdev_dummy_open(struct netdev_dev *netdev_dev_, int ethertype OVS_UNUSED, + struct netdev **netdevp) +{ + struct netdev_dummy *netdev; + + netdev = xmalloc(sizeof *netdev); + netdev_init(&netdev->netdev, netdev_dev_); + + *netdevp = &netdev->netdev; + return 0; +} + +static void +netdev_dummy_close(struct netdev *netdev_) +{ + struct netdev_dummy *netdev = netdev_dummy_cast(netdev_); + free(netdev); +} + +static int +netdev_dummy_set_etheraddr(struct netdev *netdev, + const uint8_t mac[ETH_ADDR_LEN]) +{ + struct netdev_dev_dummy *dev = + netdev_dev_dummy_cast(netdev_get_dev(netdev)); + + if (!eth_addr_equals(dev->hwaddr, mac)) { + memcpy(dev->hwaddr, mac, ETH_ADDR_LEN); + netdev_dummy_poll_notify(netdev); + } + + return 0; +} + +static int +netdev_dummy_get_etheraddr(const struct netdev *netdev, + uint8_t mac[ETH_ADDR_LEN]) +{ + const struct netdev_dev_dummy *dev = + netdev_dev_dummy_cast(netdev_get_dev(netdev)); + + memcpy(mac, dev->hwaddr, ETH_ADDR_LEN); + return 0; +} + +static int +netdev_dummy_get_mtu(const struct netdev *netdev, int *mtup) +{ + const struct netdev_dev_dummy *dev = + netdev_dev_dummy_cast(netdev_get_dev(netdev)); + + *mtup = dev->mtu; + return 0; +} + +static int +netdev_dummy_get_stats(const struct netdev *netdev, struct netdev_stats *stats) +{ + const struct netdev_dev_dummy *dev = + netdev_dev_dummy_cast(netdev_get_dev(netdev)); + + *stats = dev->stats; + return 0; +} + +static int +netdev_dummy_set_stats(struct netdev *netdev, const struct netdev_stats *stats) +{ + struct netdev_dev_dummy *dev = + netdev_dev_dummy_cast(netdev_get_dev(netdev)); + + dev->stats = *stats; + return 0; +} + +static int +netdev_dummy_update_flags(struct netdev *netdev, + enum netdev_flags off, enum netdev_flags on, + enum netdev_flags *old_flagsp) +{ + struct netdev_dev_dummy *dev = + netdev_dev_dummy_cast(netdev_get_dev(netdev)); + + if ((off | on) & ~(NETDEV_UP | NETDEV_PROMISC)) { + return EINVAL; + } + + *old_flagsp = dev->flags; + dev->flags |= on; + dev->flags &= ~off; + if (*old_flagsp != dev->flags) { + netdev_dummy_poll_notify(netdev); + } + return 0; +} + +static int +netdev_dummy_poll_add(struct netdev *netdev, + void (*cb)(struct netdev_notifier *), void *aux, + struct netdev_notifier **notifierp) +{ + const char *name = netdev_get_name(netdev); + struct netdev_dummy_notifier *notifier; + struct list *list; + struct shash_node *shash_node; + + shash_node = shash_find_data(&netdev_dummy_notifiers, name); + if (!shash_node) { + list = xmalloc(sizeof *list); + list_init(list); + shash_node = shash_add(&netdev_dummy_notifiers, name, list); + } else { + list = shash_node->data; + } + + notifier = xmalloc(sizeof *notifier); + netdev_notifier_init(¬ifier->notifier, netdev, cb, aux); + list_push_back(list, ¬ifier->list_node); + notifier->shash_node = shash_node; + + *notifierp = ¬ifier->notifier; + + return 0; +} + +static void +netdev_dummy_poll_remove(struct netdev_notifier *notifier_) +{ + struct netdev_dummy_notifier *notifier = + CONTAINER_OF(notifier_, struct netdev_dummy_notifier, notifier); + + struct list *list; + + list = list_remove(¬ifier->list_node); + if (list_is_empty(list)) { + shash_delete(&netdev_dummy_notifiers, notifier->shash_node); + free(list); + } + + free(notifier); +} + +/* Helper functions. */ + +static void +netdev_dummy_poll_notify(const struct netdev *netdev) +{ + const char *name = netdev_get_name(netdev); + struct list *list = shash_find_data(&netdev_dummy_notifiers, name); + + if (list) { + struct netdev_dummy_notifier *notifier; + + LIST_FOR_EACH (notifier, list_node, list) { + struct netdev_notifier *n = ¬ifier->notifier; + n->cb(n); + } + } +} + +static const struct netdev_class dummy_class = { + "dummy", + NULL, /* init */ + NULL, /* run */ + NULL, /* wait */ + + netdev_dummy_create, + netdev_dummy_destroy, + NULL, + + netdev_dummy_open, + netdev_dummy_close, + + NULL, /* enumerate */ + + NULL, /* recv */ + NULL, /* recv_wait */ + NULL, /* drain */ + + NULL, /* send */ + NULL, /* send_wait */ + + netdev_dummy_set_etheraddr, + netdev_dummy_get_etheraddr, + netdev_dummy_get_mtu, + NULL, /* get_ifindex */ + NULL, /* get_carrier */ + netdev_dummy_get_stats, + netdev_dummy_set_stats, + + NULL, /* get_features */ + NULL, /* set_advertisements */ + NULL, /* get_vlan_vid */ + + NULL, /* set_policing */ + NULL, /* get_qos_types */ + NULL, /* get_qos_capabilities */ + NULL, /* get_qos */ + NULL, /* set_qos */ + NULL, /* get_queue */ + NULL, /* set_queue */ + NULL, /* delete_queue */ + NULL, /* get_queue_stats */ + NULL, /* dump_queues */ + NULL, /* dump_queue_stats */ + + NULL, /* get_in4 */ + NULL, /* set_in4 */ + NULL, /* get_in6 */ + NULL, /* add_router */ + NULL, /* get_next_hop */ + NULL, /* arp_lookup */ + + netdev_dummy_update_flags, + + netdev_dummy_poll_add, + netdev_dummy_poll_remove, +}; + +void +netdev_dummy_register(void) +{ + netdev_register_provider(&dummy_class); +} diff --git a/lib/unixctl.c b/lib/unixctl.c index 6abb3328..e10de491 100644 --- a/lib/unixctl.c +++ b/lib/unixctl.c @@ -174,6 +174,9 @@ unixctl_command_reply(struct unixctl_conn *conn, * * - NULL, in which case /..ctl is used. * + * - "none", in which case the function will return successfully but + * no socket will actually be created. + * * - A name that does not start with '/', in which case it is put in * . * @@ -186,13 +189,19 @@ unixctl_command_reply(struct unixctl_conn *conn, * "ovs-appctl --target=" will fail.) * * Returns 0 if successful, otherwise a positive errno value. If successful, - * sets '*serverp' to the new unixctl_server, otherwise to NULL. */ + * sets '*serverp' to the new unixctl_server (or to NULL if 'path' was "none"), + * otherwise to NULL. */ int unixctl_server_create(const char *path, struct unixctl_server **serverp) { struct unixctl_server *server; int error; + if (path && !strcmp(path, "none")) { + *serverp = NULL; + return 0; + } + unixctl_command_register("help", unixctl_help, NULL); server = xmalloc(sizeof *server); @@ -400,6 +409,10 @@ unixctl_server_run(struct unixctl_server *server) struct unixctl_conn *conn, *next; int i; + if (!server) { + return; + } + for (i = 0; i < 10; i++) { int fd = accept(server->fd, NULL, NULL); if (fd < 0) { @@ -424,6 +437,10 @@ unixctl_server_wait(struct unixctl_server *server) { struct unixctl_conn *conn; + if (!server) { + return; + } + poll_fd_wait(server->fd, POLLIN); LIST_FOR_EACH (conn, node, &server->conns) { if (conn->state == S_RECV) { diff --git a/lib/unixctl.man b/lib/unixctl.man index 6ea7cf24..a1540866 100644 --- a/lib/unixctl.man +++ b/lib/unixctl.man @@ -6,3 +6,5 @@ interpreted as relative to \fB@RUNDIR@\fR. If \fB\-\-unixctl\fR is not used at all, the default socket is \fB@RUNDIR@/\*(PN.\fIpid\fB.ctl\fR, where \fIpid\fR is \fB\*(PN\fR's process ID. +Specifying \fBnone\fR for \fIsocket\fR disables the control socket +feature. diff --git a/lib/vlog-modules.def b/lib/vlog-modules.def index 20d7bb3b..f891613e 100644 --- a/lib/vlog-modules.def +++ b/lib/vlog-modules.def @@ -41,6 +41,7 @@ VLOG_MODULE(learning_switch) VLOG_MODULE(lockfile) VLOG_MODULE(mac_learning) VLOG_MODULE(netdev) +VLOG_MODULE(netdev_dummy) VLOG_MODULE(netdev_linux) VLOG_MODULE(netdev_vport) VLOG_MODULE(netflow) diff --git a/vswitchd/ovs-vswitchd.c b/vswitchd/ovs-vswitchd.c index 0371e571..01f2ac29 100644 --- a/vswitchd/ovs-vswitchd.c +++ b/vswitchd/ovs-vswitchd.c @@ -31,6 +31,7 @@ #include "compiler.h" #include "daemon.h" #include "dpif.h" +#include "dummy.h" #include "leak-checker.h" #include "netdev.h" #include "ovsdb-idl.h" @@ -117,7 +118,8 @@ parse_options(int argc, char *argv[]) OPT_FAKE_PROC_NET, VLOG_OPTION_ENUMS, LEAK_CHECKER_OPTION_ENUMS, - OPT_BOOTSTRAP_CA_CERT + OPT_BOOTSTRAP_CA_CERT, + OPT_ENABLE_DUMMY }; static struct option long_options[] = { {"help", no_argument, 0, 'h'}, @@ -132,6 +134,7 @@ parse_options(int argc, char *argv[]) {"peer-ca-cert", required_argument, 0, OPT_PEER_CA_CERT}, {"bootstrap-ca-cert", required_argument, 0, OPT_BOOTSTRAP_CA_CERT}, #endif + {"enable-dummy", no_argument, 0, OPT_ENABLE_DUMMY}, {0, 0, 0, 0}, }; char *short_options = long_options_to_short_options(long_options); @@ -188,6 +191,10 @@ parse_options(int argc, char *argv[]) break; #endif + case OPT_ENABLE_DUMMY: + dummy_enable(); + break; + case '?': exit(EXIT_FAILURE); -- 2.30.2