From: Ben Pfaff Date: Thu, 11 Feb 2010 19:11:23 +0000 (-0800) Subject: Merge "master" into "next". X-Git-Tag: v1.0.0~259^2~144 X-Git-Url: http://git.cascardo.eti.br/?a=commitdiff_plain;h=c69ee87c10818267f991236201150b1fa51ae519;hp=-c;p=cascardo%2Fovs.git Merge "master" into "next". The main change here is the need to update all of the uses of UNUSED in the next branch to OVS_UNUSED as it is now spelled on "master". --- c69ee87c10818267f991236201150b1fa51ae519 diff --combined datapath/datapath.c index 46bb6961c,9f415ac5c..1e878ebee --- a/datapath/datapath.c +++ b/datapath/datapath.c @@@ -81,7 -81,7 +81,7 @@@ struct datapath *get_dp(int dp_idx } EXPORT_SYMBOL_GPL(get_dp); - struct datapath *get_dp_locked(int dp_idx) + static struct datapath *get_dp_locked(int dp_idx) { struct datapath *dp; @@@ -176,7 -176,7 +176,7 @@@ static void release_dp(struct kobject * kfree(dp); } - struct kobj_type dp_ktype = { + static struct kobj_type dp_ktype = { .release = release_dp }; @@@ -325,7 -325,7 +325,7 @@@ static void release_nbp(struct kobject kfree(p); } - struct kobj_type brport_ktype = { + static struct kobj_type brport_ktype = { #ifdef CONFIG_SYSFS .sysfs_ops = &brport_sysfs_ops, #endif @@@ -420,10 -420,9 +420,10 @@@ got_port_no if (err) goto out_put; + set_dp_devs_mtu(dp, dev); dp_sysfs_add_if(dp->ports[port_no]); - err = __put_user(port_no, &port.port); + err = __put_user(port_no, &portp->port); out_put: dev_put(dev); @@@ -577,10 -576,9 +577,10 @@@ static int dp_frame_hook(struct net_bri #endif #if defined(CONFIG_XEN) && defined(HAVE_PROTO_DATA_VALID) -/* This code is copied verbatim from net/dev/core.c in Xen's - * linux-2.6.18-92.1.10.el5.xs5.0.0.394.644. We can't call those functions - * directly because they aren't exported. */ +/* This code is based on a skb_checksum_setup from net/dev/core.c from a + * combination of Lenny's 2.6.26 Xen kernel and Xen's + * linux-2.6.18-92.1.10.el5.xs5.0.0.394.644. We can't call this function + * directly because it isn't exported in all versions. */ static int skb_pull_up_to(struct sk_buff *skb, void *ptr) { if (ptr < (void *)skb->tail) @@@ -595,116 -593,37 +595,116 @@@ int vswitch_skb_checksum_setup(struct sk_buff *skb) { - if (skb->proto_csum_blank) { - if (skb->protocol != htons(ETH_P_IP)) - goto out; - if (!skb_pull_up_to(skb, skb->nh.iph + 1)) - goto out; - skb->h.raw = (unsigned char *)skb->nh.iph + 4*skb->nh.iph->ihl; - switch (skb->nh.iph->protocol) { - case IPPROTO_TCP: - skb->csum = offsetof(struct tcphdr, check); - break; - case IPPROTO_UDP: - skb->csum = offsetof(struct udphdr, check); - break; - default: - if (net_ratelimit()) - printk(KERN_ERR "Attempting to checksum a non-" - "TCP/UDP packet, dropping a protocol" - " %d packet", skb->nh.iph->protocol); - goto out; - } - if (!skb_pull_up_to(skb, skb->h.raw + skb->csum + 2)) - goto out; - skb->ip_summed = CHECKSUM_HW; - skb->proto_csum_blank = 0; + struct iphdr *iph; + unsigned char *th; + int err = -EPROTO; + __u16 csum_start, csum_offset; + + if (!skb->proto_csum_blank) + return 0; + + if (skb->protocol != htons(ETH_P_IP)) + goto out; + + if (!skb_pull_up_to(skb, skb_network_header(skb) + 1)) + goto out; + + iph = ip_hdr(skb); + th = skb_network_header(skb) + 4 * iph->ihl; + + csum_start = th - skb->head; + switch (iph->protocol) { + case IPPROTO_TCP: + csum_offset = offsetof(struct tcphdr, check); + break; + case IPPROTO_UDP: + csum_offset = offsetof(struct udphdr, check); + break; + default: + if (net_ratelimit()) + printk(KERN_ERR "Attempting to checksum a non-" + "TCP/UDP packet, dropping a protocol" + " %d packet", iph->protocol); + goto out; } - return 0; + + if (!skb_pull_up_to(skb, th + csum_offset + 2)) + goto out; + + skb->ip_summed = CHECKSUM_PARTIAL; + skb->proto_csum_blank = 0; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) + skb->csum_start = csum_start; + skb->csum_offset = csum_offset; +#else + skb_set_transport_header(skb, csum_start - skb_headroom(skb)); + skb->csum = csum_offset; +#endif + + err = 0; + out: - return -EPROTO; + return err; } #endif /* CONFIG_XEN && HAVE_PROTO_DATA_VALID */ + /* Types of checksums that we can receive (these all refer to L4 checksums): + * 1. CHECKSUM_NONE: Device that did not compute checksum, contains full + * (though not verified) checksum in packet but not in skb->csum. Packets + * from the bridge local port will also have this type. + * 2. CHECKSUM_COMPLETE (CHECKSUM_HW): Good device that computes checksums, + * also the GRE module. This is the same as CHECKSUM_NONE, except it has + * a valid skb->csum. Importantly, both contain a full checksum (not + * verified) in the packet itself. The only difference is that if the + * packet gets to L4 processing on this machine (not in DomU) we won't + * have to recompute the checksum to verify. Most hardware devices do not + * produce packets with this type, even if they support receive checksum + * offloading (they produce type #5). + * 3. CHECKSUM_PARTIAL (CHECKSUM_HW): Packet without full checksum and needs to + * be computed if it is sent off box. Unfortunately on earlier kernels, + * this case is impossible to distinguish from #2, despite having opposite + * meanings. Xen adds an extra field on earlier kernels (see #4) in order + * to distinguish the different states. The only real user of this type + * with bridging is Xen (on later kernels). + * 4. CHECKSUM_UNNECESSARY (with proto_csum_blank true): This packet was + * generated locally by a Xen DomU and has a partial checksum. If it is + * handled on this machine (Dom0 or DomU), then the checksum will not be + * computed. If it goes off box, the checksum in the packet needs to be + * completed. Calling skb_checksum_setup converts this to CHECKSUM_HW + * (CHECKSUM_PARTIAL) so that the checksum can be completed. In later + * kernels, this combination is replaced with CHECKSUM_PARTIAL. + * 5. CHECKSUM_UNNECESSARY (with proto_csum_blank false): Packet with a correct + * full checksum or using a protocol without a checksum. skb->csum is + * undefined. This is common from devices with receive checksum + * offloading. This is somewhat similar to CHECKSUM_NONE, except that + * nobody will try to verify the checksum with CHECKSUM_UNNECESSARY. + * + * Note that on earlier kernels, CHECKSUM_COMPLETE and CHECKSUM_PARTIAL are + * both defined as CHECKSUM_HW. Normally the meaning of CHECKSUM_HW is clear + * based on whether it is on the transmit or receive path. After the datapath + * it will be intepreted as CHECKSUM_PARTIAL. If the packet already has a + * checksum, we will panic. Since we can receive packets with checksums, we + * assume that all CHECKSUM_HW packets have checksums and map them to + * CHECKSUM_NONE, which has a similar meaning (the it is only different if the + * packet is processed by the local IP stack, in which case it will need to + * be reverified). If we receive a packet with CHECKSUM_HW that really means + * CHECKSUM_PARTIAL, it will be sent with the wrong checksum. However, there + * shouldn't be any devices that do this with bridging. + * + * The bridge has similar behavior and this function closely resembles + * skb_forward_csum(). It is slightly different because we are only concerned + * with bridging and not other types of forwarding and can get away with + * slightly more optimal behavior.*/ +void +forward_ip_summed(struct sk_buff *skb) +{ +#ifdef CHECKSUM_HW + if (skb->ip_summed == CHECKSUM_HW) + skb->ip_summed = CHECKSUM_NONE; +#endif +} + /* Append each packet in 'skb' list to 'queue'. There will be only one packet * unless we broke up a GSO packet. */ static int @@@ -801,8 -720,6 +801,8 @@@ dp_output_control(struct datapath *dp, if (skb_queue_len(queue) >= DP_MAX_QUEUE_LEN) goto err_kfree_skb; + forward_ip_summed(skb); + /* Break apart GSO packets into their component pieces. Otherwise * userspace may try to stuff a 64kB packet into a 1500-byte MTU. */ if (skb_is_gso(skb)) { @@@ -1318,29 -1235,6 +1318,29 @@@ int dp_min_mtu(const struct datapath *d return mtu ? mtu : ETH_DATA_LEN; } +/* Sets the MTU of all datapath devices to the minimum of the ports. 'dev' + * is the device whose MTU may have changed. Must be called with RTNL lock + * and dp_mutex. */ +void set_dp_devs_mtu(const struct datapath *dp, struct net_device *dev) +{ + struct net_bridge_port *p; + int mtu; + + ASSERT_RTNL(); + + if (is_dp_dev(dev)) + return; + + mtu = dp_min_mtu(dp); + + list_for_each_entry_rcu (p, &dp->port_list, node) { + struct net_device *br_dev = p->dev; + + if (is_dp_dev(br_dev)) + dev_set_mtu(br_dev, mtu); + } +} + static int put_port(const struct net_bridge_port *p, struct odp_port __user *uop) { diff --combined extras/ezio/ovs-switchui.c index 4f0843f39,e087aa00e..79f5552d2 --- a/extras/ezio/ovs-switchui.c +++ b/extras/ezio/ovs-switchui.c @@@ -144,7 -144,6 +144,7 @@@ main(int argc, char *argv[] long long int last_key_time = 0; int repeat_count = 0; + proctitle_init(argc, argv); set_program_name(argv[0]); time_init(); vlog_init(); @@@ -166,7 -165,7 +166,7 @@@ daemonize(); initialize_terminal(); - fatal_signal_add_hook(restore_terminal, NULL, true); + fatal_signal_add_hook(restore_terminal, NULL, NULL, true); msg = NULL; countdown = 0; @@@ -996,7 -995,7 +996,7 @@@ initialize_terminal(void } static void - restore_terminal(void *aux UNUSED) + restore_terminal(void *aux OVS_UNUSED) { endwin(); } @@@ -1248,7 -1247,7 +1248,7 @@@ allocate_message(struct message **msgp { if (!*msgp) { /* Allocate and initialize message. */ - *msgp = xcalloc(1, sizeof **msgp); + *msgp = xzalloc(sizeof **msgp); (*msgp)->index = n_messages; /* Add to list of messages. */ @@@ -1445,7 -1444,7 +1445,7 @@@ init_reboot_notifier(void } static void - sigusr1_handler(int signr UNUSED) + sigusr1_handler(int signr OVS_UNUSED) { sigusr1_triggered = true; } @@@ -1875,7 -1874,7 +1875,7 @@@ yesno(const char *title, bool def } static void - cmd_show_version(const struct dict *dict UNUSED) + cmd_show_version(const struct dict *dict OVS_UNUSED) { show_string(VERSION BUILDNR); } @@@ -1896,7 -1895,7 +1896,7 @@@ cmd_browse_status(const struct dict *di } static void - cmd_shell(const struct dict *dict UNUSED) + cmd_shell(const struct dict *dict OVS_UNUSED) { const char *home; @@@ -1920,7 -1919,7 +1920,7 @@@ } static void - cmd_show_motto(const struct dict *dict UNUSED) + cmd_show_motto(const struct dict *dict OVS_UNUSED) { show_string("\"Just Add Ice\""); } @@@ -2080,7 -2079,7 +2080,7 @@@ save_config(const struct svec *settings } svec_init(&argv); - svec_add(&argv, "/usr/share/openvswitch/commands/reconfigure"); + svec_add(&argv, "/usr/share/openvswitch-switchui/reconfigure"); svec_append(&argv, settings); svec_terminate(&argv); ok = run_and_report_failure(argv.names, "Save failed"); @@@ -2458,7 -2457,7 +2458,7 @@@ abbreviate_netdevs(const struct svec *n static void choose_netdevs(struct svec *choices) { - struct svec netdevs; + struct svec netdevs = SVEC_EMPTY_INITIALIZER; struct menu menu; size_t i; @@@ -2479,7 -2478,7 +2479,7 @@@ continue; } - retval = netdev_open(name, NETDEV_ETH_TYPE_NONE, &netdev); + retval = netdev_open_default(name, &netdev); if (!retval) { bool exclude = netdev_get_in4(netdev, NULL, NULL) == 0; netdev_close(netdev); @@@ -2548,7 -2547,7 +2548,7 @@@ disconnected_string(int value } static void - cmd_configure(const struct dict *dict UNUSED) + cmd_configure(const struct dict *dict OVS_UNUSED) { bool debug_mode = dict_get_bool(dict, "debug", false); struct dict config_dict; @@@ -2890,7 -2889,7 +2890,7 @@@ } static void - cmd_set_up_pki(const struct dict *dict UNUSED) + cmd_set_up_pki(const struct dict *dict OVS_UNUSED) { static const char def_privkey_file[] = "/etc/openflow-switch/of0-privkey.pem"; diff --combined extras/ezio/tty.c index 9b05480a2,fefeea155..432882852 --- a/extras/ezio/tty.c +++ b/extras/ezio/tty.c @@@ -263,7 -263,7 +263,7 @@@ tty_set_raw_mode(int fd, speed_t speed return errno; } s->tios = tios; - fatal_signal_add_hook(restore_termios, s, true); + fatal_signal_add_hook(restore_termios, NULL, s, true); tios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); @@@ -374,7 -374,9 +374,9 @@@ tty_fork_child(int master_fd, char *arg } int - tty_set_window_size(int fd UNUSED, int rows UNUSED, int columns UNUSED) + tty_set_window_size(int fd OVS_UNUSED, + int rows OVS_UNUSED, + int columns OVS_UNUSED) { #ifdef TIOCGWINSZ struct winsize win; diff --combined lib/command-line.c index 2f9979848,6b79c5eab..17344c36a --- a/lib/command-line.c +++ b/lib/command-line.c @@@ -1,5 -1,5 +1,5 @@@ /* - * Copyright (c) 2008 Nicira Networks. + * Copyright (c) 2008, 2009, 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. @@@ -18,8 -18,8 +18,8 @@@ #include "command-line.h" #include #include +#include #include "util.h" -#include "vlog.h" /* Given the GNU-style long options in 'options', returns a string that may be * passed to getopt() with the corresponding short options. The caller is @@@ -47,150 -47,3 +47,150 @@@ long_options_to_short_options(const str return xstrdup(short_options); } +/* Runs the command designated by argv[0] within the command table specified by + * 'commands', which must be terminated by a command whose 'name' member is a + * null pointer. + * + * Command-line options should be stripped off, so that a typical invocation + * looks like "run_command(argc - optind, argv + optind, my_commands);". */ +void +run_command(int argc, char *argv[], const struct command commands[]) +{ + const struct command *p; + + if (argc < 1) { + ovs_fatal(0, "missing command name; use --help for help"); + } + + for (p = commands; p->name != NULL; p++) { + if (!strcmp(p->name, argv[0])) { + int n_arg = argc - 1; + if (n_arg < p->min_args) { + ovs_fatal(0, "'%s' command requires at least %d arguments", + p->name, p->min_args); + } else if (n_arg > p->max_args) { + ovs_fatal(0, "'%s' command takes at most %d arguments", + p->name, p->max_args); + } else { + p->handler(argc, argv); + if (ferror(stdout)) { + ovs_fatal(0, "write to stdout failed"); + } + if (ferror(stderr)) { + ovs_fatal(0, "write to stderr failed"); + } + return; + } + } + } + + ovs_fatal(0, "unknown command '%s'; use --help for help", argv[0]); +} + +/* Process title. */ + +#ifdef __linux__ +static char *argv_start; /* Start of command-line arguments in memory. */ +static size_t argv_size; /* Number of bytes of command-line arguments. */ +static char *saved_proctitle; /* Saved command-line arguments. */ + +/* Prepares the process so that proctitle_set() can later succeed. + * + * This modifies the argv[] array so that it no longer points into the memory + * that it originally does. Later, proctitle_set() might overwrite that + * memory. That means that this function should be called before anything else + * that accesses the process's argv[] array. Ideally, it should be called + * before anything else, period, at the very beginning of program + * execution. */ +void +proctitle_init(int argc, char **argv) +{ + int i; + + if (!argc || !argv[0]) { + /* This situation should never occur, but... */ + return; + } + + /* Specialized version of first loop iteration below. */ + argv_start = argv[0]; + argv_size = strlen(argv[0]) + 1; + argv[0] = xstrdup(argv[0]); + + for (i = 1; i < argc; i++) { + size_t size = strlen(argv[i]) + 1; + + /* Add (argv[i], strlen(argv[i])+1) to (argv_start, argv_size). */ + if (argv[i] + size == argv_start) { + /* Arguments grow downward in memory. */ + argv_start -= size; + argv_size += size; + } else if (argv[i] == argv_start + argv_size) { + /* Arguments grow upward in memory. */ + argv_size += size; + } else { + /* Arguments not contiguous. (Is this really Linux?) */ + } + + /* Copy out the old argument so we can reuse the space. */ + argv[i] = xstrdup(argv[i]); + } +} + +/* Changes the name of the process, as shown by "ps", to 'format', which is + * formatted as if by printf(). */ +void +proctitle_set(const char *format, ...) +{ + va_list args; + int n; + + if (!argv_start || argv_size < 8) { + return; + } + + if (!saved_proctitle) { + saved_proctitle = xmemdup(argv_start, argv_size); + } + + va_start(args, format); + n = vsnprintf(argv_start, argv_size, format, args); + if (n >= argv_size) { + /* The name is too long, so add an ellipsis at the end. */ + strcpy(&argv_start[argv_size - 4], "..."); + } else { + /* Fill the extra space with null bytes, so that trailing bytes don't + * show up in the command line. */ + memset(&argv_start[n], '\0', argv_size - n); + } + va_end(args); +} + +/* Restores the process's original command line, as seen by "ps". */ +void +proctitle_restore(void) +{ + if (saved_proctitle) { + memcpy(argv_start, saved_proctitle, argv_size); + free(saved_proctitle); + saved_proctitle = NULL; + } +} +#else /* !__linux__ */ +/* Stubs that don't do anything on non-Linux systems. */ + +void - proctitle_init(int argc UNUSED, char **argv UNUSED) ++proctitle_init(int argc OVS_UNUSED, char **argv OVS_UNUSED) +{ +} + +void - proctitle_set(const char *format UNUSED, ...) ++proctitle_set(const char *format OVS_UNUSED, ...) +{ +} + +void +proctitle_restore(void) +{ +} +#endif /* !__linux__ */ diff --combined lib/compiler.h index 216dd6af0,0075ca87c..6c0db0fea --- a/lib/compiler.h +++ b/lib/compiler.h @@@ -1,5 -1,5 +1,5 @@@ /* - * Copyright (c) 2008, 2009 Nicira Networks. + * Copyright (c) 2008, 2009, 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. @@@ -18,7 -18,7 +18,7 @@@ #define COMPILER_H 1 #define NO_RETURN __attribute__((__noreturn__)) - #define UNUSED __attribute__((__unused__)) + #define OVS_UNUSED __attribute__((__unused__)) #define PACKED __attribute__((__packed__)) #define PRINTF_FORMAT(FMT, ARG1) __attribute__((__format__(printf, FMT, ARG1))) #define STRFTIME_FORMAT(FMT) __attribute__((__format__(__strftime__, FMT, 0))) @@@ -26,6 -26,5 +26,6 @@@ #define ALWAYS_INLINE __attribute__((always_inline)) #define likely(x) __builtin_expect((x),1) #define unlikely(x) __builtin_expect((x),0) +#define WARN_UNUSED_RESULT __attribute__((__warn_unused_result__)) #endif /* compiler.h */ diff --combined lib/coverage.c index 8b5e9d9c6,92bc2b891..5c99c18ad --- a/lib/coverage.c +++ b/lib/coverage.c @@@ -1,5 -1,5 +1,5 @@@ /* - * Copyright (c) 2009 Nicira Networks. + * Copyright (c) 2009, 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. @@@ -30,8 -30,7 +30,8 @@@ static unsigned int epoch; static void - coverage_unixctl_log(struct unixctl_conn *conn, const char *args UNUSED, - void *aux UNUSED) -coverage_unixctl_log(struct unixctl_conn *conn, const char *args OVS_UNUSED) ++coverage_unixctl_log(struct unixctl_conn *conn, const char *args OVS_UNUSED, ++ void *aux OVS_UNUSED) { coverage_log(VLL_WARN, false); unixctl_command_reply(conn, 200, NULL); @@@ -40,7 -39,7 +40,7 @@@ void coverage_init(void) { - unixctl_command_register("coverage/log", coverage_unixctl_log); + unixctl_command_register("coverage/log", coverage_unixctl_log, NULL); } /* Sorts coverage counters in descending order by count, within equal counts @@@ -109,7 -108,7 +109,7 @@@ coverage_hit(uint32_t hash unsigned int word_mask = 1u << (bit_index % BITS_PER_WORD); if (hit[word_index] & word_mask) { -- return true; ++ return true; } else { hit[word_index] |= word_mask; return false; diff --combined lib/dhcp-client.c index fb6835c19,bb331288e..30ac56c93 --- a/lib/dhcp-client.c +++ b/lib/dhcp-client.c @@@ -151,19 -151,12 +151,19 @@@ dhclient_create(const char *netdev_name void *aux, struct dhclient **cli_) { struct dhclient *cli; + struct netdev_options netdev_options; struct netdev *netdev; int error; *cli_ = NULL; - error = netdev_open(netdev_name, ETH_TYPE_IP, &netdev); + memset(&netdev_options, 0, sizeof netdev_options); + netdev_options.name = netdev_name; + netdev_options.ethertype = ETH_TYPE_IP; + netdev_options.may_create = true; + netdev_options.may_open = true; + + error = netdev_open(&netdev_options, &netdev); /* XXX install socket filter to catch only DHCP packets. */ if (error) { VLOG_ERR("could not open %s network device: %s", @@@ -179,7 -172,7 +179,7 @@@ return error; } - cli = xcalloc(1, sizeof *cli); + cli = xzalloc(sizeof *cli); cli->modify_request = modify_request; cli->validate_offer = validate_offer; cli->aux = aux; @@@ -775,7 -768,7 +775,7 @@@ dhclient_run_REBINDING(struct dhclient } static void - dhclient_run_RELEASED(struct dhclient *cli UNUSED) + dhclient_run_RELEASED(struct dhclient *cli OVS_UNUSED) { /* Nothing to do. */ } diff --combined lib/dhcp.c index 51d6ed070,a8f024482..e16176ba1 --- a/lib/dhcp.c +++ b/lib/dhcp.c @@@ -497,7 -497,7 +497,7 @@@ dhcp_option_equals(const struct dhcp_op { return ((a->data != NULL) == (b->data != NULL) && a->n == b->n - && !memcmp(a->data, b->data, a->n)); + && (!a->data || !memcmp(a->data, b->data, a->n))); } /* Replaces 'ds' by a string representation of 'msg'. If 'multiline' is @@@ -674,7 -674,7 +674,7 @@@ dhcp_parse(struct dhcp_msg *msg, const msg->giaddr = dhcp->giaddr; memcpy(msg->chaddr, dhcp->chaddr, ETH_ADDR_LEN); - cookie = ofpbuf_try_pull(&b, sizeof cookie); + cookie = ofpbuf_try_pull(&b, sizeof *cookie); if (cookie) { if (ntohl(*cookie) == DHCP_OPTS_COOKIE) { uint8_t overload; diff --combined lib/dpif-linux.c index ddf9298e5,3920d462f..4c3432565 --- a/lib/dpif-linux.c +++ b/lib/dpif-linux.c @@@ -95,10 -95,10 +95,10 @@@ dpif_linux_enumerate(struct svec *all_d int retval; sprintf(devname, "dp%d", i); - retval = dpif_open(devname, &dpif); + retval = dpif_open(devname, "system", &dpif); if (!retval) { svec_add(all_dps, devname); - dpif_close(dpif); + dpif_uninit(dpif, true); } else if (retval != ENODEV && !error) { error = retval; } @@@ -107,7 -107,7 +107,7 @@@ } static int - dpif_linux_open(const char *name, const char *type UNUSED, bool create, -dpif_linux_open(const char *name OVS_UNUSED, char *suffix, bool create, ++dpif_linux_open(const char *name, const char *type OVS_UNUSED, bool create, struct dpif **dpifp) { int minor; @@@ -116,11 -116,11 +116,11 @@@ && isdigit((unsigned char)name[2]) ? atoi(name + 2) : -1; if (create) { if (minor >= 0) { - return create_minor(suffix, minor, dpifp); + return create_minor(name, minor, dpifp); } else { /* Scan for unused minor number. */ for (minor = 0; minor < ODP_MAX; minor++) { - int error = create_minor(suffix, minor, dpifp); + int error = create_minor(name, minor, dpifp); if (error != EBUSY) { return error; } @@@ -135,7 -135,7 +135,7 @@@ int error; if (minor < 0) { - error = lookup_minor(suffix, &minor); + error = lookup_minor(name, &minor); if (error) { return error; } @@@ -157,7 -157,7 +157,7 @@@ VLOG_WARN("%s: probe returned unexpected error: %s", dpif_name(*dpifp), strerror(error)); } - dpif_close(*dpifp); + dpif_uninit(*dpifp, true); return error; } @@@ -188,7 -188,7 +188,7 @@@ dpif_linux_get_all_names(const struct d } static int -dpif_linux_delete(struct dpif *dpif_) +dpif_linux_destroy(struct dpif *dpif_) { return do_ioctl(dpif_, ODP_DP_DESTROY, NULL); } @@@ -460,14 -460,15 +460,14 @@@ dpif_linux_recv_wait(struct dpif *dpif_ } const struct dpif_class dpif_linux_class = { - "", /* This is the default class. */ - "linux", + "system", NULL, NULL, dpif_linux_enumerate, dpif_linux_open, dpif_linux_close, dpif_linux_get_all_names, - dpif_linux_delete, + dpif_linux_destroy, dpif_linux_get_stats, dpif_linux_get_drop_frags, dpif_linux_set_drop_frags, @@@ -685,11 -686,11 +685,11 @@@ static in finish_open(struct dpif *dpif_, const char *local_ifname) { struct dpif_linux *dpif = dpif_linux_cast(dpif_); - dpif->local_ifname = strdup(local_ifname); + dpif->local_ifname = xstrdup(local_ifname); dpif->local_ifindex = if_nametoindex(local_ifname); if (!dpif->local_ifindex) { int error = errno; - dpif_close(dpif_); + dpif_uninit(dpif_, true); VLOG_WARN("could not get ifindex of %s device: %s", local_ifname, strerror(errno)); return error; @@@ -706,7 -707,7 +706,7 @@@ create_minor(const char *name, int mino if (!error) { error = finish_open(*dpifp, name); } else { - dpif_close(*dpifp); + dpif_uninit(*dpifp, true); } } return error; diff --combined lib/dpif-netdev.c index 042837c13,e9f006c34..1dee14b47 --- a/lib/dpif-netdev.c +++ b/lib/dpif-netdev.c @@@ -64,7 -64,7 +64,7 @@@ struct dp_netdev struct list node; int dp_idx; int open_cnt; - bool deleted; + bool destroyed; bool drop_frags; /* Drop all IP fragments, if true. */ struct ovs_queue queues[N_QUEUES]; /* Messages queued for dpif_recv(). */ @@@ -196,7 -196,7 +196,7 @@@ create_dpif_netdev(struct dp_netdev *dp dp->open_cnt++; - dpname = xasprintf("netdev:dp%d", dp->dp_idx); + dpname = xasprintf("dp%d", dp->dp_idx); dpif = xmalloc(sizeof *dpif); dpif_init(&dpif->dpif, &dpif_netdev_class, dpname, dp->dp_idx, dp->dp_idx); dpif->dp = dp; @@@ -219,7 -219,7 +219,7 @@@ create_dp_netdev(const char *name, int } /* Create datapath. */ - dp_netdevs[dp_idx] = dp = xcalloc(1, sizeof *dp); + dp_netdevs[dp_idx] = dp = xzalloc(sizeof *dp); list_push_back(&dp_netdev_list, &dp->node); dp->dp_idx = dp_idx; dp->open_cnt = 0; @@@ -237,7 -237,7 +237,7 @@@ error = do_add_port(dp, name, ODP_PORT_INTERNAL, ODPP_LOCAL); if (error) { dp_netdev_free(dp); - return error; + return ENODEV; } *dpifp = create_dpif_netdev(dp); @@@ -245,20 -245,20 +245,20 @@@ } static int - dpif_netdev_open(const char *name, const char *type UNUSED, bool create, -dpif_netdev_open(const char *name OVS_UNUSED, char *suffix, bool create, ++dpif_netdev_open(const char *name, const char *type OVS_UNUSED, bool create, struct dpif **dpifp) { if (create) { - if (find_dp_netdev(suffix)) { + if (find_dp_netdev(name)) { return EEXIST; } else { - int dp_idx = name_to_dp_idx(suffix); + int dp_idx = name_to_dp_idx(name); if (dp_idx >= 0) { - return create_dp_netdev(suffix, dp_idx, dpifp); + return create_dp_netdev(name, dp_idx, dpifp); } else { /* Scan for unused dp_idx number. */ for (dp_idx = 0; dp_idx < N_DP_NETDEVS; dp_idx++) { - int error = create_dp_netdev(suffix, dp_idx, dpifp); + int error = create_dp_netdev(name, dp_idx, dpifp); if (error != EBUSY) { return error; } @@@ -269,7 -269,7 +269,7 @@@ } } } else { - struct dp_netdev *dp = find_dp_netdev(suffix); + struct dp_netdev *dp = find_dp_netdev(name); if (dp) { *dpifp = create_dpif_netdev(dp); return 0; @@@ -307,17 -307,17 +307,17 @@@ dpif_netdev_close(struct dpif *dpif { struct dp_netdev *dp = get_dp_netdev(dpif); assert(dp->open_cnt > 0); - if (--dp->open_cnt == 0 && dp->deleted) { + if (--dp->open_cnt == 0 && dp->destroyed) { dp_netdev_free(dp); } free(dpif); } static int -dpif_netdev_delete(struct dpif *dpif) +dpif_netdev_destroy(struct dpif *dpif) { struct dp_netdev *dp = get_dp_netdev(dpif); - dp->deleted = true; + dp->destroyed = true; return 0; } @@@ -363,7 -363,6 +363,7 @@@ do_add_port(struct dp_netdev *dp, cons { bool internal = (flags & ODP_PORT_INTERNAL) != 0; struct dp_netdev_port *port; + struct netdev_options netdev_options; struct netdev *netdev; int mtu; int error; @@@ -371,17 -370,17 +371,17 @@@ /* XXX reject devices already in some dp_netdev. */ /* Open and validate network device. */ - if (!internal) { - error = netdev_open(devname, NETDEV_ETH_TYPE_ANY, &netdev); + memset(&netdev_options, 0, sizeof netdev_options); + netdev_options.name = devname; + netdev_options.ethertype = NETDEV_ETH_TYPE_ANY; + netdev_options.may_create = true; + if (internal) { + netdev_options.type = "tap"; } else { - error = netdev_create(devname, "tap", NULL); - if (!error) { - error = netdev_open(devname, NETDEV_ETH_TYPE_ANY, &netdev); - if (error) { - netdev_destroy(devname); - } - } + netdev_options.may_open = true; } + + error = netdev_open(&netdev_options, &netdev); if (error) { return error; } @@@ -488,7 -487,9 +488,7 @@@ do_del_port(struct dp_netdev *dp, uint1 name = xstrdup(netdev_get_name(port->netdev)); netdev_close(port->netdev); - if (port->internal) { - netdev_destroy(name); - } + free(name); free(port); @@@ -582,7 -583,7 +582,7 @@@ dpif_netdev_port_list(const struct dpi } static int - dpif_netdev_port_poll(const struct dpif *dpif_, char **devnamep UNUSED) + dpif_netdev_port_poll(const struct dpif *dpif_, char **devnamep OVS_UNUSED) { struct dpif_netdev *dpif = dpif_netdev_cast(dpif_); if (dpif->dp_serial != dpif->dp->serial) { @@@ -803,7 -804,7 +803,7 @@@ add_flow(struct dpif *dpif, struct odp_ struct dp_netdev_flow *flow; int error; - flow = xcalloc(1, sizeof *flow); + flow = xzalloc(sizeof *flow); flow->key = odp_flow->key; flow->key.reserved = 0; @@@ -1303,6 -1304,7 +1303,6 @@@ dp_netdev_execute_actions(struct dp_net } const struct dpif_class dpif_netdev_class = { - "netdev", "netdev", dp_netdev_run, dp_netdev_wait, @@@ -1310,7 -1312,7 +1310,7 @@@ dpif_netdev_open, dpif_netdev_close, NULL, /* get_all_names */ - dpif_netdev_delete, + dpif_netdev_destroy, dpif_netdev_get_stats, dpif_netdev_get_drop_frags, dpif_netdev_set_drop_frags, diff --combined lib/fatal-signal.c index 60a188e6f,9aa50e420..80ecfc35a --- a/lib/fatal-signal.c +++ b/lib/fatal-signal.c @@@ -20,13 -20,10 +20,13 @@@ #include #include #include +#include #include #include #include +#include "poll-loop.h" #include "shash.h" +#include "socket-util.h" #include "util.h" #define THIS_MODULE VLM_fatal_signal @@@ -40,8 -37,7 +40,8 @@@ static sigset_t fatal_signal_set /* Hooks to call upon catching a signal */ struct hook { - void (*func)(void *aux); + void (*hook_cb)(void *aux); + void (*cancel_cb)(void *aux); void *aux; bool run_at_exit; }; @@@ -49,29 -45,53 +49,29 @@@ static struct hook hooks[MAX_HOOKS]; static size_t n_hooks; -/* Number of nesting signal blockers. */ -static int block_level = 0; +static int signal_fds[2]; +static volatile sig_atomic_t stored_sig_nr = SIG_ATOMIC_MAX; -/* Signal mask saved by outermost signal blocker. */ -static sigset_t saved_signal_mask; - -/* Disabled by fatal_signal_fork()? */ -static bool disabled; - -static void call_sigprocmask(int how, sigset_t* new_set, sigset_t* old_set); +static void fatal_signal_init(void); static void atexit_handler(void); static void call_hooks(int sig_nr); -/* Registers 'hook' to be called when a process termination signal is raised. - * If 'run_at_exit' is true, 'hook' is also called during normal process - * termination, e.g. when exit() is called or when main() returns. - * - * 'func' will be invoked from an asynchronous signal handler, so it must be - * written appropriately. For example, it must not call most C library - * functions, including malloc() or free(). */ -void -fatal_signal_add_hook(void (*func)(void *aux), void *aux, bool run_at_exit) -{ - fatal_signal_block(); - assert(n_hooks < MAX_HOOKS); - hooks[n_hooks].func = func; - hooks[n_hooks].aux = aux; - hooks[n_hooks].run_at_exit = run_at_exit; - n_hooks++; - fatal_signal_unblock(); -} - -/* Blocks program termination signals until fatal_signal_unblock() is called. - * May be called multiple times with nesting; if so, fatal_signal_unblock() - * must be called the same number of times to unblock signals. - * - * This is needed while adjusting a data structure that will be accessed by a - * fatal signal hook, so that the hook is not invoked while the data structure - * is in an inconsistent state. */ -void -fatal_signal_block(void) +static void +fatal_signal_init(void) { static bool inited = false; + if (!inited) { size_t i; inited = true; + + if (pipe(signal_fds)) { + ovs_fatal(errno, "could not create pipe"); + } + set_nonblocking(signal_fds[0]); + set_nonblocking(signal_fds[1]); + sigemptyset(&fatal_signal_set); for (i = 0; i < ARRAY_SIZE(fatal_signals); i++) { int sig_nr = fatal_signals[i]; @@@ -88,34 -108,23 +88,34 @@@ } atexit(atexit_handler); } - - if (++block_level == 1) { - call_sigprocmask(SIG_BLOCK, &fatal_signal_set, &saved_signal_mask); - } } -/* Unblocks program termination signals blocked by fatal_signal_block() is - * called. If multiple calls to fatal_signal_block() are nested, - * fatal_signal_unblock() must be called the same number of times to unblock - * signals. */ +/* Registers 'hook_cb' to be called when a process termination signal is + * raised. If 'run_at_exit' is true, 'hook_cb' is also called during normal + * process termination, e.g. when exit() is called or when main() returns. + * + * 'hook_cb' is not called immediately from the signal handler but rather the + * next time the poll loop iterates, so it is freed from the usual restrictions + * on signal handler functions. + * + * If the current process forks, fatal_signal_fork() may be called to clear the + * parent process's fatal signal hooks, so that 'hook_cb' is only called when + * the child terminates, not when the parent does. When fatal_signal_fork() is + * called, it calls the 'cancel_cb' function if it is nonnull, passing 'aux', + * to notify that the hook has been canceled. This allows the hook to free + * memory, etc. */ void -fatal_signal_unblock(void) +fatal_signal_add_hook(void (*hook_cb)(void *aux), void (*cancel_cb)(void *aux), + void *aux, bool run_at_exit) { - assert(block_level > 0); - if (--block_level == 0) { - call_sigprocmask(SIG_SETMASK, &saved_signal_mask, NULL); - } + fatal_signal_init(); + + assert(n_hooks < MAX_HOOKS); + hooks[n_hooks].hook_cb = hook_cb; + hooks[n_hooks].cancel_cb = cancel_cb; + hooks[n_hooks].aux = aux; + hooks[n_hooks].run_at_exit = run_at_exit; + n_hooks++; } /* Handles fatal signal number 'sig_nr'. @@@ -130,35 -139,20 +130,35 @@@ void fatal_signal_handler(int sig_nr) { - call_hooks(sig_nr); + ignore(write(signal_fds[1], "", 1)); + stored_sig_nr = sig_nr; +} + +void +fatal_signal_run(void) +{ + int sig_nr = stored_sig_nr; + + if (sig_nr != SIG_ATOMIC_MAX) { + call_hooks(sig_nr); - /* Re-raise the signal with the default handling so that the program - * termination status reflects that we were killed by this signal */ - signal(sig_nr, SIG_DFL); - raise(sig_nr); + /* Re-raise the signal with the default handling so that the program + * termination status reflects that we were killed by this signal */ + signal(sig_nr, SIG_DFL); + raise(sig_nr); + } +} + +void +fatal_signal_wait(void) +{ + poll_fd_wait(signal_fds[0], POLLIN); } static void atexit_handler(void) { - if (!disabled) { - call_hooks(0); - } + call_hooks(0); } static void @@@ -173,21 -167,15 +173,21 @@@ call_hooks(int sig_nr for (i = 0; i < n_hooks; i++) { struct hook *h = &hooks[i]; if (sig_nr || h->run_at_exit) { - h->func(h->aux); + h->hook_cb(h->aux); } } } } +/* Files to delete on exit. (The 'data' member of each node is unused.) */ static struct shash files = SHASH_INITIALIZER(&files); +/* Has a hook function been registered with fatal_signal_add_hook() (and not + * cleared by fatal_signal_fork())? */ +static bool added_hook; + static void unlink_files(void *aux); +static void cancel_files(void *aux); static void do_unlink_files(void); /* Registers 'file' to be unlinked when the program terminates via exit() or a @@@ -195,14 -183,17 +195,14 @@@ void fatal_signal_add_file_to_unlink(const char *file) { - static bool added_hook = false; if (!added_hook) { added_hook = true; - fatal_signal_add_hook(unlink_files, NULL, true); + fatal_signal_add_hook(unlink_files, cancel_files, NULL, true); } - fatal_signal_block(); if (!shash_find(&files, file)) { shash_add(&files, file, NULL); } - fatal_signal_unblock(); } /* Unregisters 'file' from being unlinked when the program terminates via @@@ -212,10 -203,12 +212,10 @@@ fatal_signal_remove_file_to_unlink(cons { struct shash_node *node; - fatal_signal_block(); node = shash_find(&files, file); if (node) { shash_delete(&files, node); } - fatal_signal_unblock(); } /* Like fatal_signal_remove_file_to_unlink(), but also unlinks 'file'. @@@ -234,18 -227,17 +234,18 @@@ fatal_signal_unlink_file_now(const cha } static void - unlink_files(void *aux UNUSED) + unlink_files(void *aux OVS_UNUSED) { do_unlink_files(); } -/* This is a fatal_signal_add_hook() callback (via unlink_files()). It will be - * invoked from an asynchronous signal handler, so it cannot call most C - * library functions (unlink() is an explicit exception, see - * http://www.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html). - * That includes free(), so it doesn't try to free the 'files' data - * structure. */ +static void - cancel_files(void *aux UNUSED) ++cancel_files(void *aux OVS_UNUSED) +{ + shash_clear(&files); + added_hook = false; +} + static void do_unlink_files(void) { @@@ -256,30 -248,30 +256,30 @@@ } } -/* Disables the fatal signal hook mechanism. Following a fork, one of the - * resulting processes can call this function to allow it to terminate without - * triggering fatal signal processing or removing files. Fatal signal - * processing is still enabled in the other process. */ +/* Clears all of the fatal signal hooks without executing them. If any of the + * hooks passed a 'cancel_cb' function to fatal_signal_add_hook(), then those + * functions will be called, allowing them to free resources, etc. + * + * Following a fork, one of the resulting processes can call this function to + * allow it to terminate without calling the hooks registered before calling + * this function. New hooks registered after calling this function will take + * effect normally. */ void fatal_signal_fork(void) { size_t i; - disabled = true; - - for (i = 0; i < ARRAY_SIZE(fatal_signals); i++) { - int sig_nr = fatal_signals[i]; - if (signal(sig_nr, SIG_DFL) == SIG_IGN) { - signal(sig_nr, SIG_IGN); + for (i = 0; i < n_hooks; i++) { + struct hook *h = &hooks[i]; + if (h->cancel_cb) { + h->cancel_cb(h->aux); } } -} - -static void -call_sigprocmask(int how, sigset_t* new_set, sigset_t* old_set) -{ - int error = sigprocmask(how, new_set, old_set); - if (error) { - fprintf(stderr, "sigprocmask: %s\n", strerror(errno)); + n_hooks = 0; + + /* Raise any signals that we have already received with the default + * handler. */ + if (stored_sig_nr != SIG_ATOMIC_MAX) { + raise(stored_sig_nr); } } diff --combined lib/learning-switch.c index 78346acbb,8ace3ba23..074a056e5 --- a/lib/learning-switch.c +++ b/lib/learning-switch.c @@@ -1,5 -1,5 +1,5 @@@ /* - * Copyright (c) 2008, 2009 Nicira Networks. + * Copyright (c) 2008, 2009, 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. @@@ -113,7 -113,7 +113,7 @@@ lswitch_create(struct rconn *rconn, boo struct lswitch *sw; size_t i; - sw = xcalloc(1, sizeof *sw); + sw = xzalloc(sizeof *sw); sw->max_idle = max_idle; sw->datapath_id = 0; sw->last_features_request = time_now() - 1; @@@ -517,7 -517,8 +517,8 @@@ process_port_status(struct lswitch *sw } static void - process_phy_port(struct lswitch *sw, struct rconn *rconn UNUSED, void *opp_) + process_phy_port(struct lswitch *sw, struct rconn *rconn OVS_UNUSED, + void *opp_) { const struct ofp_phy_port *opp = opp_; uint16_t port_no = ntohs(opp->port_no); diff --combined lib/netdev-linux.c index 6d46b09e9,95501b085..5341ed06f --- a/lib/netdev-linux.c +++ b/lib/netdev-linux.c @@@ -21,7 -21,6 +21,7 @@@ #include #include #include +#include #include #include #include @@@ -33,7 -32,6 +33,7 @@@ #include #include #include +#include #include #include #include @@@ -50,7 -48,6 +50,7 @@@ #include "netlink.h" #include "ofpbuf.h" #include "openflow/openflow.h" +#include "openvswitch/gre.h" #include "packets.h" #include "poll-loop.h" #include "rtnetlink.h" @@@ -58,10 -55,6 +58,10 @@@ #include "shash.h" #include "svec.h" +#ifndef GRE_IOCTL_ONLY +#include +#endif + #define THIS_MODULE VLM_netdev_linux #include "vlog.h" @@@ -74,8 -67,25 +74,8 @@@ #define ADVERTISED_Asym_Pause (1 << 14) #endif -/* Provider-specific netdev object. Netdev objects are devices that are - * created by the netdev library through a netdev_create() call. */ -struct netdev_obj_linux { - struct netdev_obj netdev_obj; - - int tap_fd; /* File descriptor for TAP device. */ -}; - -struct netdev_linux { - struct netdev netdev; - - /* File descriptors. For ordinary network devices, the two fds below are - * the same; for tap devices, they differ. */ - int netdev_fd; /* Network device. */ - int tap_fd; /* TAP character device, if any, otherwise the - * network device. */ - - struct netdev_linux_cache *cache; -}; +static struct rtnetlink_notifier netdev_linux_cache_notifier; +static int cache_notifier_refcount; enum { VALID_IFINDEX = 1 << 0, @@@ -87,15 -97,11 +87,15 @@@ VALID_IS_INTERNAL = 1 << 6 }; -/* Cached network device information. */ -struct netdev_linux_cache { +struct tap_state { + int fd; +}; + +struct netdev_dev_linux { + struct netdev_dev netdev_dev; + struct shash_node *shash_node; - unsigned int valid; - int ref_cnt; + unsigned int cache_valid; int ifindex; uint8_t etheraddr[ETH_ADDR_LEN]; @@@ -104,39 -110,14 +104,39 @@@ int mtu; int carrier; bool is_internal; + + union { + struct tap_state tap; + } state; }; -static struct shash cache_map = SHASH_INITIALIZER(&cache_map); -static struct rtnetlink_notifier netdev_linux_cache_notifier; +struct netdev_linux { + struct netdev netdev; + int fd; +}; /* An AF_INET socket (used for ioctl operations). */ static int af_inet_sock = -1; +struct gre_config { + uint32_t local_ip; + uint32_t remote_ip; + uint32_t in_key; + uint32_t out_key; + bool have_in_key; + bool have_out_key; + bool in_csum; + bool out_csum; +}; + +static struct { + union { + struct nl_sock *nl_sock; + int ioctl_fd; + }; + bool use_ioctl; +} gre_descriptors; + struct netdev_linux_notifier { struct netdev_notifier notifier; struct list node; @@@ -150,11 -131,10 +150,11 @@@ static struct rtnetlink_notifier netdev * additional log messages. */ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20); -static int netdev_linux_do_ethtool(struct netdev *, struct ethtool_cmd *, +static int destroy_gre(const char *name); +static int netdev_linux_do_ethtool(const char *name, struct ethtool_cmd *, int cmd, const char *cmd_name); -static int netdev_linux_do_ioctl(const struct netdev *, struct ifreq *, - int cmd, const char *cmd_name); +static int netdev_linux_do_ioctl(const char *name, struct ifreq *, int cmd, + const char *cmd_name); static int netdev_linux_get_ipv4(const struct netdev *, struct in_addr *, int cmd, const char *cmd_name); static int get_flags(const struct netdev *, int *flagsp); @@@ -170,21 -150,17 +170,21 @@@ static int set_etheraddr(const char *ne static int get_stats_via_netlink(int ifindex, struct netdev_stats *stats); static int get_stats_via_proc(const char *netdev_name, struct netdev_stats *stats); -static struct netdev_obj_linux * -netdev_obj_linux_cast(const struct netdev_obj *netdev_obj) +static struct netdev_dev_linux * +netdev_dev_linux_cast(const struct netdev_dev *netdev_dev) { - netdev_obj_assert_class(netdev_obj, &netdev_linux_class); - return CONTAINER_OF(netdev_obj, struct netdev_obj_linux, netdev_obj); + const char *type = netdev_dev_get_type(netdev_dev); + assert(!strcmp(type, "system") || !strcmp(type, "tap") + || !strcmp(type, "gre")); + return CONTAINER_OF(netdev_dev, struct netdev_dev_linux, netdev_dev); } static struct netdev_linux * netdev_linux_cast(const struct netdev *netdev) { - netdev_assert_class(netdev, &netdev_linux_class); + const char *type = netdev_get_type(netdev); + assert(!strcmp(type, "system") || !strcmp(type, "tap") + || !strcmp(type, "gre")); return CONTAINER_OF(netdev, struct netdev_linux, netdev); } @@@ -216,411 -192,50 +216,411 @@@ netdev_linux_wait(void static void netdev_linux_cache_cb(const struct rtnetlink_change *change, - void *aux UNUSED) + void *aux OVS_UNUSED) { - struct netdev_linux_cache *cache; + struct netdev_dev_linux *dev; if (change) { - cache = shash_find_data(&cache_map, change->ifname); - if (cache) { - cache->valid = 0; + struct netdev_dev *base_dev = netdev_dev_from_name(change->ifname); + if (base_dev) { + dev = netdev_dev_linux_cast(base_dev); + dev->cache_valid = 0; } } else { + struct shash device_shash; struct shash_node *node; - SHASH_FOR_EACH (node, &cache_map) { - cache = node->data; - cache->valid = 0; + + shash_init(&device_shash); + netdev_dev_get_devices(&netdev_linux_class, &device_shash); + SHASH_FOR_EACH (node, &device_shash) { + dev = node->data; + dev->cache_valid = 0; } + shash_destroy(&device_shash); } } -/* Creates the netdev object of 'type' with 'name'. */ +/* The arguments are marked as unused to prevent warnings on platforms where + * the Netlink interface isn't supported. */ static int - setup_gre_netlink(const char *name UNUSED, struct gre_config *config UNUSED, - bool create UNUSED) -netdev_linux_create(const char *name, const char *type, - const struct shash *args, bool created) ++setup_gre_netlink(const char *name OVS_UNUSED, ++ struct gre_config *config OVS_UNUSED, bool create OVS_UNUSED) { - struct netdev_obj_linux *netdev_obj; - static const char tap_dev[] = "/dev/net/tun"; +#ifdef GRE_IOCTL_ONLY + return EOPNOTSUPP; +#else + int error; + struct ofpbuf request, *reply; + unsigned int nl_flags; + struct ifinfomsg ifinfomsg; + struct nlattr *linkinfo_hdr; + struct nlattr *info_data_hdr; + uint16_t iflags = 0; + uint16_t oflags = 0; + uint8_t pmtudisc = 0; + + VLOG_DBG("%s: attempting to create gre device using netlink", name); + + if (!gre_descriptors.nl_sock) { + error = nl_sock_create(NETLINK_ROUTE, 0, 0, 0, + &gre_descriptors.nl_sock); + if (error) { + VLOG_WARN("couldn't create netlink socket: %s", strerror(error)); + goto error; + } + } + + ofpbuf_init(&request, 0); + + nl_flags = NLM_F_REQUEST; + if (create) { + nl_flags |= NLM_F_CREATE|NLM_F_EXCL; + } + + /* We over-reserve space, because we do some pointer arithmetic + * and don't want the buffer address shifting under us. */ + nl_msg_put_nlmsghdr(&request, gre_descriptors.nl_sock, 2048, RTM_NEWLINK, + nl_flags); + + memset(&ifinfomsg, 0, sizeof ifinfomsg); + ifinfomsg.ifi_family = AF_UNSPEC; + nl_msg_put(&request, &ifinfomsg, sizeof ifinfomsg); + + linkinfo_hdr = ofpbuf_tail(&request); + nl_msg_put_unspec(&request, IFLA_LINKINFO, NULL, 0); + + nl_msg_put_unspec(&request, IFLA_INFO_KIND, "gretap", 6); + + info_data_hdr = ofpbuf_tail(&request); + nl_msg_put_unspec(&request, IFLA_INFO_DATA, NULL, 0); + + /* Set flags */ + if (config->have_in_key) { + iflags |= GRE_KEY; + } + if (config->have_out_key) { + oflags |= GRE_KEY; + } + + if (config->in_csum) { + iflags |= GRE_CSUM; + } + if (config->out_csum) { + oflags |= GRE_CSUM; + } + + /* Add options */ + nl_msg_put_u32(&request, IFLA_GRE_IKEY, config->in_key); + nl_msg_put_u32(&request, IFLA_GRE_OKEY, config->out_key); + nl_msg_put_u16(&request, IFLA_GRE_IFLAGS, iflags); + nl_msg_put_u16(&request, IFLA_GRE_OFLAGS, oflags); + nl_msg_put_u32(&request, IFLA_GRE_LOCAL, config->local_ip); + nl_msg_put_u32(&request, IFLA_GRE_REMOTE, config->remote_ip); + nl_msg_put_u8(&request, IFLA_GRE_PMTUDISC, pmtudisc); + nl_msg_put_u8(&request, IFLA_GRE_TTL, 0); + nl_msg_put_u8(&request, IFLA_GRE_TOS, 0); + + info_data_hdr->nla_len = (char *)ofpbuf_tail(&request) + - (char *)info_data_hdr; + linkinfo_hdr->nla_len = (char *)ofpbuf_tail(&request) + - (char *)linkinfo_hdr; + + nl_msg_put_string(&request, IFLA_IFNAME, name); + + error = nl_sock_transact(gre_descriptors.nl_sock, &request, &reply); + ofpbuf_uninit(&request); + if (error) { + VLOG_WARN("couldn't transact netlink socket: %s", strerror(error)); + goto error; + } + ofpbuf_delete(reply); + +error: + return error; +#endif +} + +static int +setup_gre_ioctl(const char *name, struct gre_config *config, bool create) +{ + struct ip_tunnel_parm p; struct ifreq ifr; + + VLOG_DBG("%s: attempting to create gre device using ioctl", name); + + memset(&p, 0, sizeof p); + + strncpy(p.name, name, IFNAMSIZ); + + p.iph.version = 4; + p.iph.ihl = 5; + p.iph.protocol = IPPROTO_GRE; + p.iph.saddr = config->local_ip; + p.iph.daddr = config->remote_ip; + + if (config->have_in_key) { + p.i_flags |= GRE_KEY; + p.i_key = config->in_key; + } + if (config->have_out_key) { + p.o_flags |= GRE_KEY; + p.o_key = config->out_key; + } + + if (config->in_csum) { + p.i_flags |= GRE_CSUM; + } + if (config->out_csum) { + p.o_flags |= GRE_CSUM; + } + + strncpy(ifr.ifr_name, create ? GRE_IOCTL_DEVICE : name, IFNAMSIZ); + ifr.ifr_ifru.ifru_data = (void *)&p; + + if (!gre_descriptors.ioctl_fd) { + gre_descriptors.ioctl_fd = socket(AF_INET, SOCK_DGRAM, 0); + if (gre_descriptors.ioctl_fd < 0) { + VLOG_WARN("couldn't create gre ioctl socket: %s", strerror(errno)); + gre_descriptors.ioctl_fd = 0; + return errno; + } + } + + if (ioctl(gre_descriptors.ioctl_fd, create ? SIOCADDGRETAP : SIOCCHGGRETAP, + &ifr) < 0) { + VLOG_WARN("couldn't do gre ioctl: %s", strerror(errno)); + return errno; + } + + return 0; +} + +/* The arguments are marked as unused to prevent warnings on platforms where + * the Netlink interface isn't supported. */ +static bool - check_gre_device_netlink(const char *name UNUSED) ++check_gre_device_netlink(const char *name OVS_UNUSED) +{ +#ifdef GRE_IOCTL_ONLY + return false; +#else + static const struct nl_policy getlink_policy[] = { + [IFLA_LINKINFO] = { .type = NL_A_NESTED, .optional = false }, + }; + + static const struct nl_policy linkinfo_policy[] = { + [IFLA_INFO_KIND] = { .type = NL_A_STRING, .optional = false }, + }; + + int error; + bool ret = false; + struct ofpbuf request, *reply; + struct ifinfomsg ifinfomsg; + struct nlattr *getlink_attrs[ARRAY_SIZE(getlink_policy)]; + struct nlattr *linkinfo_attrs[ARRAY_SIZE(linkinfo_policy)]; + struct ofpbuf linkinfo; + const char *device_kind; + + ofpbuf_init(&request, 0); + + nl_msg_put_nlmsghdr(&request, gre_descriptors.nl_sock, + NLMSG_LENGTH(sizeof ifinfomsg), RTM_GETLINK, + NLM_F_REQUEST); + + memset(&ifinfomsg, 0, sizeof ifinfomsg); + ifinfomsg.ifi_family = AF_UNSPEC; + ifinfomsg.ifi_index = do_get_ifindex(name); + nl_msg_put(&request, &ifinfomsg, sizeof ifinfomsg); + + error = nl_sock_transact(gre_descriptors.nl_sock, &request, &reply); + ofpbuf_uninit(&request); + if (error) { + VLOG_WARN("couldn't transact netlink socket: %s", strerror(error)); + return false; + } + + if (!nl_policy_parse(reply, NLMSG_HDRLEN + sizeof(struct ifinfomsg), + getlink_policy, getlink_attrs, + ARRAY_SIZE(getlink_policy))) { + VLOG_WARN("received bad rtnl message (getlink policy)"); + goto error; + } + + linkinfo.data = (void *)nl_attr_get(getlink_attrs[IFLA_LINKINFO]); + linkinfo.size = nl_attr_get_size(getlink_attrs[IFLA_LINKINFO]); + if (!nl_policy_parse(&linkinfo, 0, linkinfo_policy, + linkinfo_attrs, ARRAY_SIZE(linkinfo_policy))) { + VLOG_WARN("received bad rtnl message (linkinfo policy)"); + goto error; + } + + device_kind = nl_attr_get_string(linkinfo_attrs[IFLA_INFO_KIND]); + ret = !strcmp(device_kind, "gretap"); + +error: + ofpbuf_delete(reply); + return ret; +#endif +} + +static bool +check_gre_device_ioctl(const char *name) +{ + struct ethtool_drvinfo drvinfo; + int error; + + memset(&drvinfo, 0, sizeof drvinfo); + error = netdev_linux_do_ethtool(name, (struct ethtool_cmd *)&drvinfo, + ETHTOOL_GDRVINFO, "ETHTOOL_GDRVINFO"); + + return !error && !strcmp(drvinfo.driver, "ip_gre") + && !strcmp(drvinfo.bus_info, "gretap"); +} + +static int +setup_gre(const char *name, const struct shash *args, bool create) +{ + int error; + struct in_addr in_addr; + struct shash_node *node; + struct gre_config config; + + memset(&config, 0, sizeof config); + config.in_csum = true; + config.out_csum = true; + + SHASH_FOR_EACH (node, args) { + if (!strcmp(node->name, "remote_ip")) { + if (lookup_ip(node->data, &in_addr)) { + VLOG_WARN("bad 'remote_ip' for gre device %s ", name); + } else { + config.remote_ip = in_addr.s_addr; + } + } else if (!strcmp(node->name, "local_ip")) { + if (lookup_ip(node->data, &in_addr)) { + VLOG_WARN("bad 'local_ip' for gre device %s ", name); + } else { + config.local_ip = in_addr.s_addr; + } + } else if (!strcmp(node->name, "key")) { + config.have_in_key = true; + config.have_out_key = true; + config.in_key = htonl(atoi(node->data)); + config.out_key = htonl(atoi(node->data)); + } else if (!strcmp(node->name, "in_key")) { + config.have_in_key = true; + config.in_key = htonl(atoi(node->data)); + } else if (!strcmp(node->name, "out_key")) { + config.have_out_key = true; + config.out_key = htonl(atoi(node->data)); + } else if (!strcmp(node->name, "csum")) { + if (!strcmp(node->data, "false")) { + config.in_csum = false; + config.out_csum = false; + } + } else { + VLOG_WARN("unknown gre argument '%s'", node->name); + } + } + + if (!config.remote_ip) { + VLOG_WARN("gre type requires valid 'remote_ip' argument"); + error = EINVAL; + goto error; + } + + if (!gre_descriptors.use_ioctl) { + error = setup_gre_netlink(name, &config, create); + if (error == EOPNOTSUPP) { + gre_descriptors.use_ioctl = true; + } + } + if (gre_descriptors.use_ioctl) { + error = setup_gre_ioctl(name, &config, create); + } + + if (create && error == EEXIST) { + bool gre_device; + + if (gre_descriptors.use_ioctl) { + gre_device = check_gre_device_ioctl(name); + } else { + gre_device = check_gre_device_netlink(name); + } + + if (!gre_device) { + goto error; + } + + VLOG_WARN("replacing existing gre device %s", name); + error = destroy_gre(name); + if (error) { + goto error; + } + + if (gre_descriptors.use_ioctl) { + error = setup_gre_ioctl(name, &config, create); + } else { + error = setup_gre_netlink(name, &config, create); + } + } + +error: + return error; +} + +/* Creates the netdev device of 'type' with 'name'. */ +static int - netdev_linux_create_system(const char *name, const char *type UNUSED, ++netdev_linux_create_system(const char *name, const char *type OVS_UNUSED, + const struct shash *args, struct netdev_dev **netdev_devp) +{ + struct netdev_dev_linux *netdev_dev; int error; if (!shash_is_empty(args)) { - VLOG_WARN("arguments for %s devices should be empty", type); + VLOG_WARN("%s: arguments for system devices should be empty", name); } - /* Create the name binding in the netdev library for this object. */ - netdev_obj = xcalloc(1, sizeof *netdev_obj); - netdev_obj_init(&netdev_obj->netdev_obj, name, &netdev_linux_class, - created); - netdev_obj->tap_fd = -1; + if (!cache_notifier_refcount) { + error = rtnetlink_notifier_register(&netdev_linux_cache_notifier, + netdev_linux_cache_cb, NULL); + if (error) { + return error; + } + } + cache_notifier_refcount++; - if (strcmp(type, "tap")) { - return 0; + netdev_dev = xzalloc(sizeof *netdev_dev); + netdev_dev_init(&netdev_dev->netdev_dev, name, &netdev_linux_class); + + *netdev_devp = &netdev_dev->netdev_dev; + return 0; +} + +/* For most types of netdevs we open the device for each call of + * netdev_open(). However, this is not the case with tap devices, + * since it is only possible to open the device once. In this + * situation we share a single file descriptor, and consequently + * buffers, across all readers. Therefore once data is read it will + * be unavailable to other reads for tap devices. */ +static int - netdev_linux_create_tap(const char *name, const char *type UNUSED, ++netdev_linux_create_tap(const char *name, const char *type OVS_UNUSED, + const struct shash *args, struct netdev_dev **netdev_devp) +{ + struct netdev_dev_linux *netdev_dev; + struct tap_state *state; + static const char tap_dev[] = "/dev/net/tun"; + struct ifreq ifr; + int error; + + if (!shash_is_empty(args)) { + VLOG_WARN("%s: arguments for TAP devices should be empty", name); } + netdev_dev = xzalloc(sizeof *netdev_dev); + state = &netdev_dev->state.tap; + /* Open tap device. */ - netdev_obj->tap_fd = open(tap_dev, O_RDWR); - if (netdev_obj->tap_fd < 0) { + state->fd = open(tap_dev, O_RDWR); + if (state->fd < 0) { error = errno; VLOG_WARN("opening \"%s\" failed: %s", tap_dev, strerror(error)); goto error; @@@ -629,7 -244,7 +629,7 @@@ /* Create tap device. */ ifr.ifr_flags = IFF_TAP | IFF_NO_PI; strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name); - if (ioctl(netdev_obj->tap_fd, TUNSETIFF, &ifr) == -1) { + if (ioctl(state->fd, TUNSETIFF, &ifr) == -1) { VLOG_WARN("%s: creating tap device failed: %s", name, strerror(errno)); error = errno; @@@ -637,198 -252,97 +637,198 @@@ } /* Make non-blocking. */ - error = set_nonblocking(netdev_obj->tap_fd); + error = set_nonblocking(state->fd); if (error) { goto error; } + netdev_dev_init(&netdev_dev->netdev_dev, name, &netdev_tap_class); + *netdev_devp = &netdev_dev->netdev_dev; return 0; error: - netdev_destroy(name); + free(netdev_dev); return error; } -/* Destroys the netdev object 'netdev_obj_'. */ -static void -netdev_linux_destroy(struct netdev_obj *netdev_obj_) +static int +if_up(const char *name) { - struct netdev_obj_linux *netdev_obj = netdev_obj_linux_cast(netdev_obj_); + struct ifreq ifr; + + strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name); + ifr.ifr_flags = IFF_UP; - if (netdev_obj->tap_fd >= 0) { - close(netdev_obj->tap_fd); + if (ioctl(af_inet_sock, SIOCSIFFLAGS, &ifr) == -1) { + VLOG_DBG_RL(&rl, "%s: failed to bring device up: %s", + name, strerror(errno)); + return errno; } - free(netdev_obj); - return; + return 0; } static int - netdev_linux_create_gre(const char *name, const char *type UNUSED, -netdev_linux_open(const char *name, int ethertype, struct netdev **netdevp) ++netdev_linux_create_gre(const char *name, const char *type OVS_UNUSED, + const struct shash *args, struct netdev_dev **netdev_devp) { - struct netdev_linux *netdev; - enum netdev_flags flags; + struct netdev_dev_linux *netdev_dev; int error; - /* Allocate network device. */ - netdev = xcalloc(1, sizeof *netdev); - netdev_init(&netdev->netdev, name, &netdev_linux_class); - netdev->netdev_fd = -1; - netdev->tap_fd = -1; - netdev->cache = shash_find_data(&cache_map, name); - if (!netdev->cache) { - if (shash_is_empty(&cache_map)) { - int error = rtnetlink_notifier_register( - &netdev_linux_cache_notifier, netdev_linux_cache_cb, NULL); - if (error) { - netdev_close(&netdev->netdev); - return error; - } - } - netdev->cache = xmalloc(sizeof *netdev->cache); - netdev->cache->shash_node = shash_add(&cache_map, name, - netdev->cache); - netdev->cache->valid = 0; - netdev->cache->ref_cnt = 0; + netdev_dev = xzalloc(sizeof *netdev_dev); + + error = setup_gre(name, args, true); + if (error) { + goto error; } - netdev->cache->ref_cnt++; - if (!strcmp(netdev_get_type(&netdev->netdev), "tap")) { - static const char tap_dev[] = "/dev/net/tun"; - struct ifreq ifr; + error = if_up(name); + if (error) { + goto error; + } - /* Open tap device. */ - netdev->tap_fd = open(tap_dev, O_RDWR); - if (netdev->tap_fd < 0) { - error = errno; - VLOG_WARN("opening \"%s\" failed: %s", tap_dev, strerror(error)); - goto error; - } + netdev_dev_init(&netdev_dev->netdev_dev, name, &netdev_gre_class); + *netdev_devp = &netdev_dev->netdev_dev; + return 0; - /* Create tap device. */ - ifr.ifr_flags = IFF_TAP | IFF_NO_PI; - strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name); - if (ioctl(netdev->tap_fd, TUNSETIFF, &ifr) == -1) { - VLOG_WARN("%s: creating tap device failed: %s", name, - strerror(errno)); - error = errno; - goto error; - } +error: + free(netdev_dev); + return error; +} - /* Make non-blocking. */ - error = set_nonblocking(netdev->tap_fd); - if (error) { - goto error; +static int +netdev_linux_reconfigure_gre(struct netdev_dev *netdev_dev_, + const struct shash *args) +{ + const char *name = netdev_dev_get_name(netdev_dev_); + + return setup_gre(name, args, false); +} + +/* The arguments are marked as unused to prevent warnings on platforms where + * the Netlink interface isn't supported. */ +static int - destroy_gre_netlink(const char *name UNUSED) ++destroy_gre_netlink(const char *name OVS_UNUSED) +{ +#ifdef GRE_IOCTL_ONLY + return EOPNOTSUPP; +#else + int error; + struct ofpbuf request, *reply; + struct ifinfomsg ifinfomsg; + int ifindex; + + ofpbuf_init(&request, 0); + + nl_msg_put_nlmsghdr(&request, gre_descriptors.nl_sock, 0, RTM_DELLINK, + NLM_F_REQUEST); + + memset(&ifinfomsg, 0, sizeof ifinfomsg); + ifinfomsg.ifi_family = AF_UNSPEC; + nl_msg_put(&request, &ifinfomsg, sizeof ifinfomsg); + + ifindex = do_get_ifindex(name); + nl_msg_put_u32(&request, IFLA_LINK, ifindex); + + nl_msg_put_string(&request, IFLA_IFNAME, name); + + error = nl_sock_transact(gre_descriptors.nl_sock, &request, &reply); + ofpbuf_uninit(&request); + if (error) { + VLOG_WARN("couldn't transact netlink socket: %s", strerror(error)); + goto error; + } + ofpbuf_delete(reply); + +error: + return 0; +#endif +} + +static int +destroy_gre_ioctl(const char *name) +{ + struct ip_tunnel_parm p; + struct ifreq ifr; + + memset(&p, 0, sizeof p); + strncpy(p.name, name, IFNAMSIZ); + + strncpy(ifr.ifr_name, name, IFNAMSIZ); + ifr.ifr_ifru.ifru_data = (void *)&p; + + if (ioctl(gre_descriptors.ioctl_fd, SIOCDELGRETAP, &ifr) < 0) { + VLOG_WARN("couldn't do gre ioctl: %s\n", strerror(errno)); + return errno; + } + + return 0; +} + +static void +destroy_tap(struct netdev_dev_linux *netdev_dev) +{ + struct tap_state *state = &netdev_dev->state.tap; + + if (state->fd >= 0) { + close(state->fd); + } +} + +static int +destroy_gre(const char *name) +{ + if (gre_descriptors.use_ioctl) { + return destroy_gre_ioctl(name); + } else { + return destroy_gre_netlink(name); + } +} + +/* Destroys the netdev device 'netdev_dev_'. */ +static void +netdev_linux_destroy(struct netdev_dev *netdev_dev_) +{ + struct netdev_dev_linux *netdev_dev = netdev_dev_linux_cast(netdev_dev_); + const char *type = netdev_dev_get_type(netdev_dev_); + + if (!strcmp(type, "system")) { + cache_notifier_refcount--; + + if (!cache_notifier_refcount) { + rtnetlink_notifier_unregister(&netdev_linux_cache_notifier); } + } else if (!strcmp(type, "tap")) { + destroy_tap(netdev_dev); + } else if (!strcmp(type, "gre")) { + destroy_gre(netdev_dev_get_name(&netdev_dev->netdev_dev)); } + free(netdev_dev_); +} + +static int +netdev_linux_open(struct netdev_dev *netdev_dev_, int ethertype, + struct netdev **netdevp) +{ + struct netdev_dev_linux *netdev_dev = netdev_dev_linux_cast(netdev_dev_); + struct netdev_linux *netdev; + enum netdev_flags flags; + int error; + + /* Allocate network device. */ + netdev = xzalloc(sizeof *netdev); + netdev->fd = -1; + netdev_init(&netdev->netdev, netdev_dev_); + error = netdev_get_flags(&netdev->netdev, &flags); if (error == ENODEV) { goto error; } - if (netdev->tap_fd >= 0 || ethertype != NETDEV_ETH_TYPE_NONE) { + if (!strcmp(netdev_dev_get_type(netdev_dev_), "tap")) { + netdev->fd = netdev_dev->state.tap.fd; + } else if (ethertype != NETDEV_ETH_TYPE_NONE) { struct sockaddr_ll sll; int protocol; int ifindex; @@@ -837,14 -351,17 +837,14 @@@ protocol = (ethertype == NETDEV_ETH_TYPE_ANY ? ETH_P_ALL : ethertype == NETDEV_ETH_TYPE_802_2 ? ETH_P_802_2 : ethertype); - netdev->netdev_fd = socket(PF_PACKET, SOCK_RAW, htons(protocol)); - if (netdev->netdev_fd < 0) { + netdev->fd = socket(PF_PACKET, SOCK_RAW, htons(protocol)); + if (netdev->fd < 0) { error = errno; goto error; } - if (netdev->tap_fd < 0) { - netdev->tap_fd = netdev->netdev_fd; - } /* Set non-blocking mode. */ - error = set_nonblocking(netdev->netdev_fd); + error = set_nonblocking(netdev->fd); if (error) { goto error; } @@@ -859,11 -376,10 +859,11 @@@ memset(&sll, 0, sizeof sll); sll.sll_family = AF_PACKET; sll.sll_ifindex = ifindex; - if (bind(netdev->netdev_fd, + if (bind(netdev->fd, (struct sockaddr *) &sll, sizeof sll) < 0) { error = errno; - VLOG_ERR("bind to %s failed: %s", name, strerror(error)); + VLOG_ERR("bind to %s failed: %s", netdev_dev_get_name(netdev_dev_), + strerror(error)); goto error; } @@@ -871,7 -387,7 +871,7 @@@ * packets of the requested type on all system interfaces. We do not * want to receive that data, but there is no way to avoid it. So we * must now drain out the receive queue. */ - error = drain_rcvbuf(netdev->netdev_fd); + error = drain_rcvbuf(netdev->fd); if (error) { goto error; } @@@ -881,7 -397,7 +881,7 @@@ return 0; error: - netdev_close(&netdev->netdev); + netdev_uninit(&netdev->netdev, true); return error; } @@@ -891,8 -407,19 +891,8 @@@ netdev_linux_close(struct netdev *netde { struct netdev_linux *netdev = netdev_linux_cast(netdev_); - if (netdev->cache && !--netdev->cache->ref_cnt) { - shash_delete(&cache_map, netdev->cache->shash_node); - free(netdev->cache); - - if (shash_is_empty(&cache_map)) { - rtnetlink_notifier_unregister(&netdev_linux_cache_notifier); - } - } - if (netdev->netdev_fd >= 0) { - close(netdev->netdev_fd); - } - if (netdev->tap_fd >= 0 && netdev->netdev_fd != netdev->tap_fd) { - close(netdev->tap_fd); + if (netdev->fd > 0 && strcmp(netdev_get_type(netdev_), "tap")) { + close(netdev->fd); } free(netdev); } @@@ -924,13 -451,13 +924,13 @@@ netdev_linux_recv(struct netdev *netdev { struct netdev_linux *netdev = netdev_linux_cast(netdev_); - if (netdev->tap_fd < 0) { + if (netdev->fd < 0) { /* Device was opened with NETDEV_ETH_TYPE_NONE. */ return -EAGAIN; } for (;;) { - ssize_t retval = read(netdev->tap_fd, data, size); + ssize_t retval = read(netdev->fd, data, size); if (retval >= 0) { return retval; } else if (errno != EINTR) { @@@ -949,8 -476,8 +949,8 @@@ static voi netdev_linux_recv_wait(struct netdev *netdev_) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); - if (netdev->tap_fd >= 0) { - poll_fd_wait(netdev->tap_fd, POLLIN); + if (netdev->fd >= 0) { + poll_fd_wait(netdev->fd, POLLIN); } } @@@ -959,19 -486,19 +959,19 @@@ static in netdev_linux_drain(struct netdev *netdev_) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); - if (netdev->tap_fd < 0 && netdev->netdev_fd < 0) { + if (netdev->fd < 0) { return 0; - } else if (netdev->tap_fd != netdev->netdev_fd) { + } else if (!strcmp(netdev_get_type(netdev_), "tap")) { struct ifreq ifr; - int error = netdev_linux_do_ioctl(netdev_, &ifr, + int error = netdev_linux_do_ioctl(netdev_get_name(netdev_), &ifr, SIOCGIFTXQLEN, "SIOCGIFTXQLEN"); if (error) { return error; } - drain_fd(netdev->tap_fd, ifr.ifr_qlen); + drain_fd(netdev->fd, ifr.ifr_qlen); return 0; } else { - return drain_rcvbuf(netdev->netdev_fd); + return drain_rcvbuf(netdev->fd); } } @@@ -991,12 -518,12 +991,12 @@@ netdev_linux_send(struct netdev *netdev /* XXX should support sending even if 'ethertype' was NETDEV_ETH_TYPE_NONE. */ - if (netdev->tap_fd < 0) { + if (netdev->fd < 0) { return EPIPE; } for (;;) { - ssize_t retval = write(netdev->tap_fd, data, size); + ssize_t retval = write(netdev->fd, data, size); if (retval < 0) { /* The Linux AF_PACKET implementation never blocks waiting for room * for packets, instead returning ENOBUFS. Translate this into @@@ -1031,10 -558,10 +1031,10 @@@ static voi netdev_linux_send_wait(struct netdev *netdev_) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); - if (netdev->tap_fd < 0 && netdev->netdev_fd < 0) { + if (netdev->fd < 0) { /* Nothing to do. */ - } else if (netdev->tap_fd == netdev->netdev_fd) { - poll_fd_wait(netdev->tap_fd, POLLOUT); + } else if (strcmp(netdev_get_type(netdev_), "tap")) { + poll_fd_wait(netdev->fd, POLLOUT); } else { /* TAP device always accepts packets.*/ poll_immediate_wake(); @@@ -1047,16 -574,15 +1047,16 @@@ static in netdev_linux_set_etheraddr(struct netdev *netdev_, const uint8_t mac[ETH_ADDR_LEN]) { - struct netdev_linux *netdev = netdev_linux_cast(netdev_); + struct netdev_dev_linux *netdev_dev = + netdev_dev_linux_cast(netdev_get_dev(netdev_)); int error; - if (!(netdev->cache->valid & VALID_ETHERADDR) - || !eth_addr_equals(netdev->cache->etheraddr, mac)) { + if (!(netdev_dev->cache_valid & VALID_ETHERADDR) + || !eth_addr_equals(netdev_dev->etheraddr, mac)) { error = set_etheraddr(netdev_get_name(netdev_), ARPHRD_ETHER, mac); if (!error) { - netdev->cache->valid |= VALID_ETHERADDR; - memcpy(netdev->cache->etheraddr, mac, ETH_ADDR_LEN); + netdev_dev->cache_valid |= VALID_ETHERADDR; + memcpy(netdev_dev->etheraddr, mac, ETH_ADDR_LEN); } } else { error = 0; @@@ -1070,17 -596,16 +1070,17 @@@ static in netdev_linux_get_etheraddr(const struct netdev *netdev_, uint8_t mac[ETH_ADDR_LEN]) { - struct netdev_linux *netdev = netdev_linux_cast(netdev_); - if (!(netdev->cache->valid & VALID_ETHERADDR)) { + struct netdev_dev_linux *netdev_dev = + netdev_dev_linux_cast(netdev_get_dev(netdev_)); + if (!(netdev_dev->cache_valid & VALID_ETHERADDR)) { int error = get_etheraddr(netdev_get_name(netdev_), - netdev->cache->etheraddr); + netdev_dev->etheraddr); if (error) { return error; } - netdev->cache->valid |= VALID_ETHERADDR; + netdev_dev->cache_valid |= VALID_ETHERADDR; } - memcpy(mac, netdev->cache->etheraddr, ETH_ADDR_LEN); + memcpy(mac, netdev_dev->etheraddr, ETH_ADDR_LEN); return 0; } @@@ -1090,21 -615,19 +1090,21 @@@ static int netdev_linux_get_mtu(const struct netdev *netdev_, int *mtup) { - struct netdev_linux *netdev = netdev_linux_cast(netdev_); - if (!(netdev->cache->valid & VALID_MTU)) { + struct netdev_dev_linux *netdev_dev = + netdev_dev_linux_cast(netdev_get_dev(netdev_)); + if (!(netdev_dev->cache_valid & VALID_MTU)) { struct ifreq ifr; int error; - error = netdev_linux_do_ioctl(netdev_, &ifr, SIOCGIFMTU, "SIOCGIFMTU"); + error = netdev_linux_do_ioctl(netdev_get_name(netdev_), &ifr, + SIOCGIFMTU, "SIOCGIFMTU"); if (error) { return error; } - netdev->cache->mtu = ifr.ifr_mtu; - netdev->cache->valid |= VALID_MTU; + netdev_dev->mtu = ifr.ifr_mtu; + netdev_dev->cache_valid |= VALID_MTU; } - *mtup = netdev->cache->mtu; + *mtup = netdev_dev->mtu; return 0; } @@@ -1122,18 -645,16 +1122,18 @@@ netdev_linux_get_ifindex(const struct n static int netdev_linux_get_carrier(const struct netdev *netdev_, bool *carrier) { - struct netdev_linux *netdev = netdev_linux_cast(netdev_); + struct netdev_dev_linux *netdev_dev = + netdev_dev_linux_cast(netdev_get_dev(netdev_)); int error = 0; char *fn = NULL; int fd = -1; - if (!(netdev->cache->valid & VALID_CARRIER)) { + if (!(netdev_dev->cache_valid & VALID_CARRIER)) { char line[8]; int retval; - fn = xasprintf("/sys/class/net/%s/carrier", netdev_get_name(netdev_)); + fn = xasprintf("/sys/class/net/%s/carrier", + netdev_get_name(netdev_)); fd = open(fn, O_RDONLY); if (fd < 0) { error = errno; @@@ -1163,10 -684,10 +1163,10 @@@ fn, line[0]); goto exit; } - netdev->cache->carrier = line[0] != '0'; - netdev->cache->valid |= VALID_CARRIER; + netdev_dev->carrier = line[0] != '0'; + netdev_dev->cache_valid |= VALID_CARRIER; } - *carrier = netdev->cache->carrier; + *carrier = netdev_dev->carrier; error = 0; exit: @@@ -1210,11 -731,9 +1210,11 @@@ check_for_working_netlink_stats(void * XXX All of the members of struct netdev_stats are 64 bits wide, but on * 32-bit architectures the Linux network stats are only 32 bits. */ static int -netdev_linux_get_stats(const struct netdev *netdev_, struct netdev_stats *stats) +netdev_linux_get_stats(const struct netdev *netdev_, + struct netdev_stats *stats) { - struct netdev_linux *netdev = netdev_linux_cast(netdev_); + struct netdev_dev_linux *netdev_dev = + netdev_dev_linux_cast(netdev_get_dev(netdev_)); static int use_netlink_stats = -1; int error; struct netdev_stats raw_stats; @@@ -1222,27 -741,28 +1222,27 @@@ COVERAGE_INC(netdev_get_stats); - if (!(netdev->cache->valid & VALID_IS_INTERNAL)) { - netdev->cache->is_internal = (netdev->tap_fd != -1); - - if (!netdev->cache->is_internal) { + if (!(netdev_dev->cache_valid & VALID_IS_INTERNAL)) { + netdev_dev->is_internal = !strcmp(netdev_get_type(netdev_), "tap"); + if (!netdev_dev->is_internal) { struct ethtool_drvinfo drvinfo; memset(&drvinfo, 0, sizeof drvinfo); - error = netdev_linux_do_ethtool(&netdev->netdev, + error = netdev_linux_do_ethtool(netdev_get_name(netdev_), (struct ethtool_cmd *)&drvinfo, ETHTOOL_GDRVINFO, "ETHTOOL_GDRVINFO"); if (!error) { - netdev->cache->is_internal = !strcmp(drvinfo.driver, - "openvswitch"); + netdev_dev->is_internal = !strcmp(drvinfo.driver, + "openvswitch"); } } - netdev->cache->valid |= VALID_IS_INTERNAL; + netdev_dev->cache_valid |= VALID_IS_INTERNAL; } - if (netdev->cache->is_internal) { + if (netdev_dev->is_internal) { collect_stats = &raw_stats; } @@@ -1252,19 -772,19 +1252,19 @@@ if (use_netlink_stats) { int ifindex; - error = get_ifindex(&netdev->netdev, &ifindex); + error = get_ifindex(netdev_, &ifindex); if (!error) { error = get_stats_via_netlink(ifindex, collect_stats); } } else { - error = get_stats_via_proc(netdev->netdev.name, collect_stats); + error = get_stats_via_proc(netdev_get_name(netdev_), collect_stats); } /* If this port is an internal port then the transmit and receive stats * will appear to be swapped relative to the other ports since we are the * one sending the data, not a remote computer. For consistency, we swap * them back here. */ - if (netdev->cache->is_internal) { + if (!error && netdev_dev->is_internal) { stats->rx_packets = raw_stats.tx_packets; stats->tx_packets = raw_stats.rx_packets; stats->rx_bytes = raw_stats.tx_bytes; @@@ -1304,7 -824,7 +1304,7 @@@ netdev_linux_get_features(struct netde int error; memset(&ecmd, 0, sizeof ecmd); - error = netdev_linux_do_ethtool(netdev, &ecmd, + error = netdev_linux_do_ethtool(netdev_get_name(netdev), &ecmd, ETHTOOL_GSET, "ETHTOOL_GSET"); if (error) { return error; @@@ -1425,7 -945,7 +1425,7 @@@ netdev_linux_set_advertisements(struct int error; memset(&ecmd, 0, sizeof ecmd); - error = netdev_linux_do_ethtool(netdev, &ecmd, + error = netdev_linux_do_ethtool(netdev_get_name(netdev), &ecmd, ETHTOOL_GSET, "ETHTOOL_GSET"); if (error) { return error; @@@ -1468,7 -988,7 +1468,7 @@@ if (advertise & OFPPF_PAUSE_ASYM) { ecmd.advertising |= ADVERTISED_Asym_Pause; } - return netdev_linux_do_ethtool(netdev, &ecmd, + return netdev_linux_do_ethtool(netdev_get_name(netdev), &ecmd, ETHTOOL_SSET, "ETHTOOL_SSET"); } @@@ -1546,8 -1066,8 +1546,8 @@@ netdev_linux_set_policing(struct netde COVERAGE_INC(netdev_set_policing); if (kbits_rate) { if (!kbits_burst) { - /* Default to 10 kilobits if not specified. */ - kbits_burst = 10; + /* Default to 1000 kilobits if not specified. */ + kbits_burst = 1000; } /* xxx This should be more careful about only adding if it @@@ -1584,28 -1104,26 +1584,28 @@@ static in netdev_linux_get_in4(const struct netdev *netdev_, struct in_addr *address, struct in_addr *netmask) { - struct netdev_linux *netdev = netdev_linux_cast(netdev_); - if (!(netdev->cache->valid & VALID_IN4)) { + struct netdev_dev_linux *netdev_dev = + netdev_dev_linux_cast(netdev_get_dev(netdev_)); + + if (!(netdev_dev->cache_valid & VALID_IN4)) { int error; - error = netdev_linux_get_ipv4(netdev_, &netdev->cache->address, + error = netdev_linux_get_ipv4(netdev_, &netdev_dev->address, SIOCGIFADDR, "SIOCGIFADDR"); if (error) { return error; } - error = netdev_linux_get_ipv4(netdev_, &netdev->cache->netmask, + error = netdev_linux_get_ipv4(netdev_, &netdev_dev->netmask, SIOCGIFNETMASK, "SIOCGIFNETMASK"); if (error) { return error; } - netdev->cache->valid |= VALID_IN4; + netdev_dev->cache_valid |= VALID_IN4; } - *address = netdev->cache->address; - *netmask = netdev->cache->netmask; + *address = netdev_dev->address; + *netmask = netdev_dev->netmask; return address->s_addr == INADDR_ANY ? EADDRNOTAVAIL : 0; } @@@ -1613,15 -1131,14 +1613,15 @@@ static in netdev_linux_set_in4(struct netdev *netdev_, struct in_addr address, struct in_addr netmask) { - struct netdev_linux *netdev = netdev_linux_cast(netdev_); + struct netdev_dev_linux *netdev_dev = + netdev_dev_linux_cast(netdev_get_dev(netdev_)); int error; error = do_set_addr(netdev_, SIOCSIFADDR, "SIOCSIFADDR", address); if (!error) { - netdev->cache->valid |= VALID_IN4; - netdev->cache->address = address; - netdev->cache->netmask = netmask; + netdev_dev->cache_valid |= VALID_IN4; + netdev_dev->address = address; + netdev_dev->netmask = netmask; if (address.s_addr != INADDR_ANY) { error = do_set_addr(netdev_, SIOCSIFNETMASK, "SIOCSIFNETMASK", netmask); @@@ -1651,13 -1168,12 +1651,13 @@@ parse_if_inet6_line(const char *line static int netdev_linux_get_in6(const struct netdev *netdev_, struct in6_addr *in6) { - struct netdev_linux *netdev = netdev_linux_cast(netdev_); - if (!(netdev->cache->valid & VALID_IN6)) { + struct netdev_dev_linux *netdev_dev = + netdev_dev_linux_cast(netdev_get_dev(netdev_)); + if (!(netdev_dev->cache_valid & VALID_IN6)) { FILE *file; char line[128]; - netdev->cache->in6 = in6addr_any; + netdev_dev->in6 = in6addr_any; file = fopen("/proc/net/if_inet6", "r"); if (file != NULL) { @@@ -1668,15 -1184,15 +1668,15 @@@ if (parse_if_inet6_line(line, &in6, ifname) && !strcmp(name, ifname)) { - netdev->cache->in6 = in6; + netdev_dev->in6 = in6; break; } } fclose(file); } - netdev->cache->valid |= VALID_IN6; + netdev_dev->cache_valid |= VALID_IN6; } - *in6 = netdev->cache->in6; + *in6 = netdev_dev->in6; return 0; } @@@ -1698,16 -1214,14 +1698,16 @@@ do_set_addr(struct netdev *netdev int ioctl_nr, const char *ioctl_name, struct in_addr addr) { struct ifreq ifr; - strncpy(ifr.ifr_name, netdev->name, sizeof ifr.ifr_name); + strncpy(ifr.ifr_name, netdev_get_name(netdev), sizeof ifr.ifr_name); make_in4_sockaddr(&ifr.ifr_addr, addr); - return netdev_linux_do_ioctl(netdev, &ifr, ioctl_nr, ioctl_name); + + return netdev_linux_do_ioctl(netdev_get_name(netdev), &ifr, ioctl_nr, + ioctl_name); } /* Adds 'router' as a default IP gateway. */ static int - netdev_linux_add_router(struct netdev *netdev UNUSED, struct in_addr router) + netdev_linux_add_router(struct netdev *netdev OVS_UNUSED, struct in_addr router) { struct in_addr any = { INADDR_ANY }; struct rtentry rt; @@@ -1796,24 -1310,24 +1796,24 @@@ netdev_linux_arp_lookup(const struct ne uint32_t ip, uint8_t mac[ETH_ADDR_LEN]) { struct arpreq r; - struct sockaddr_in *pa; + struct sockaddr_in sin; int retval; memset(&r, 0, sizeof r); - pa = (struct sockaddr_in *) &r.arp_pa; - pa->sin_family = AF_INET; - pa->sin_addr.s_addr = ip; - pa->sin_port = 0; + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = ip; + sin.sin_port = 0; + memcpy(&r.arp_pa, &sin, sizeof sin); r.arp_ha.sa_family = ARPHRD_ETHER; r.arp_flags = 0; - strncpy(r.arp_dev, netdev->name, sizeof r.arp_dev); + strncpy(r.arp_dev, netdev_get_name(netdev), sizeof r.arp_dev); COVERAGE_INC(netdev_arp_lookup); retval = ioctl(af_inet_sock, SIOCGARP, &r) < 0 ? errno : 0; if (!retval) { memcpy(mac, r.arp_ha.sa_data, ETH_ADDR_LEN); } else if (retval != ENXIO) { VLOG_WARN_RL(&rl, "%s: could not look up ARP entry for "IP_FMT": %s", - netdev->name, IP_ARGS(&ip), strerror(retval)); + netdev_get_name(netdev), IP_ARGS(&ip), strerror(retval)); } return retval; } @@@ -1874,7 -1388,7 +1874,7 @@@ poll_notify(struct list *list static void netdev_linux_poll_cb(const struct rtnetlink_change *change, - void *aux UNUSED) + void *aux OVS_UNUSED) { if (change) { struct list *list = shash_find_data(&netdev_linux_notifiers, @@@ -1946,13 -1460,13 +1946,13 @@@ netdev_linux_poll_remove(struct netdev_ } const struct netdev_class netdev_linux_class = { - "system", /* type */ + "system", netdev_linux_init, netdev_linux_run, netdev_linux_wait, - netdev_linux_create, + netdev_linux_create_system, netdev_linux_destroy, NULL, /* reconfigure */ @@@ -1994,68 -1508,20 +1994,68 @@@ }; const struct netdev_class netdev_tap_class = { - "tap", /* type */ + "tap", netdev_linux_init, - NULL, /* run */ - NULL, /* wait */ + netdev_linux_run, + netdev_linux_wait, - netdev_linux_create, + netdev_linux_create_tap, netdev_linux_destroy, NULL, /* reconfigure */ netdev_linux_open, netdev_linux_close, - netdev_linux_enumerate, + NULL, /* enumerate */ + + netdev_linux_recv, + netdev_linux_recv_wait, + netdev_linux_drain, + + netdev_linux_send, + netdev_linux_send_wait, + + netdev_linux_set_etheraddr, + netdev_linux_get_etheraddr, + netdev_linux_get_mtu, + netdev_linux_get_ifindex, + netdev_linux_get_carrier, + netdev_linux_get_stats, + + netdev_linux_get_features, + netdev_linux_set_advertisements, + netdev_linux_get_vlan_vid, + netdev_linux_set_policing, + + netdev_linux_get_in4, + netdev_linux_set_in4, + netdev_linux_get_in6, + netdev_linux_add_router, + netdev_linux_get_next_hop, + netdev_linux_arp_lookup, + + netdev_linux_update_flags, + + netdev_linux_poll_add, + netdev_linux_poll_remove, +}; + +const struct netdev_class netdev_gre_class = { + "gre", + + netdev_linux_init, + netdev_linux_run, + netdev_linux_wait, + + netdev_linux_create_gre, + netdev_linux_destroy, + netdev_linux_reconfigure_gre, + + netdev_linux_open, + netdev_linux_close, + + NULL, /* enumerate */ netdev_linux_recv, netdev_linux_recv_wait, @@@ -2236,8 -1702,7 +2236,8 @@@ get_flags(const struct netdev *netdev, struct ifreq ifr; int error; - error = netdev_linux_do_ioctl(netdev, &ifr, SIOCGIFFLAGS, "SIOCGIFFLAGS"); + error = netdev_linux_do_ioctl(netdev_get_name(netdev), &ifr, SIOCGIFFLAGS, + "SIOCGIFFLAGS"); *flags = ifr.ifr_flags; return error; } @@@ -2248,8 -1713,7 +2248,8 @@@ set_flags(struct netdev *netdev, int fl struct ifreq ifr; ifr.ifr_flags = flags; - return netdev_linux_do_ioctl(netdev, &ifr, SIOCSIFFLAGS, "SIOCSIFFLAGS"); + return netdev_linux_do_ioctl(netdev_get_name(netdev), &ifr, SIOCSIFFLAGS, + "SIOCSIFFLAGS"); } static int @@@ -2270,18 -1734,17 +2270,18 @@@ do_get_ifindex(const char *netdev_name static int get_ifindex(const struct netdev *netdev_, int *ifindexp) { - struct netdev_linux *netdev = netdev_linux_cast(netdev_); + struct netdev_dev_linux *netdev_dev = + netdev_dev_linux_cast(netdev_get_dev(netdev_)); *ifindexp = 0; - if (!(netdev->cache->valid & VALID_IFINDEX)) { + if (!(netdev_dev->cache_valid & VALID_IFINDEX)) { int ifindex = do_get_ifindex(netdev_get_name(netdev_)); if (ifindex < 0) { return -ifindex; } - netdev->cache->valid |= VALID_IFINDEX; - netdev->cache->ifindex = ifindex; + netdev_dev->cache_valid |= VALID_IFINDEX; + netdev_dev->ifindex = ifindex; } - *ifindexp = netdev->cache->ifindex; + *ifindexp = netdev_dev->ifindex; return 0; } @@@ -2328,13 -1791,13 +2328,13 @@@ set_etheraddr(const char *netdev_name, } static int -netdev_linux_do_ethtool(struct netdev *netdev, struct ethtool_cmd *ecmd, +netdev_linux_do_ethtool(const char *name, struct ethtool_cmd *ecmd, int cmd, const char *cmd_name) { struct ifreq ifr; memset(&ifr, 0, sizeof ifr); - strncpy(ifr.ifr_name, netdev->name, sizeof ifr.ifr_name); + strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name); ifr.ifr_data = (caddr_t) ecmd; ecmd->cmd = cmd; @@@ -2344,7 -1807,8 +2344,7 @@@ } else { if (errno != EOPNOTSUPP) { VLOG_WARN_RL(&rl, "ethtool command %s on network device %s " - "failed: %s", cmd_name, netdev->name, - strerror(errno)); + "failed: %s", cmd_name, name, strerror(errno)); } else { /* The device doesn't support this operation. That's pretty * common, so there's no point in logging anything. */ @@@ -2354,13 -1818,13 +2354,13 @@@ } static int -netdev_linux_do_ioctl(const struct netdev *netdev, struct ifreq *ifr, - int cmd, const char *cmd_name) +netdev_linux_do_ioctl(const char *name, struct ifreq *ifr, int cmd, + const char *cmd_name) { - strncpy(ifr->ifr_name, netdev_get_name(netdev), sizeof ifr->ifr_name); + strncpy(ifr->ifr_name, name, sizeof ifr->ifr_name); if (ioctl(af_inet_sock, cmd, ifr) == -1) { - VLOG_DBG_RL(&rl, "%s: ioctl(%s) failed: %s", - netdev_get_name(netdev), cmd_name, strerror(errno)); + VLOG_DBG_RL(&rl, "%s: ioctl(%s) failed: %s", name, cmd_name, + strerror(errno)); return errno; } return 0; @@@ -2374,7 -1838,7 +2374,7 @@@ netdev_linux_get_ipv4(const struct netd int error; ifr.ifr_addr.sa_family = AF_INET; - error = netdev_linux_do_ioctl(netdev, &ifr, cmd, cmd_name); + error = netdev_linux_do_ioctl(netdev_get_name(netdev), &ifr, cmd, cmd_name); if (!error) { const struct sockaddr_in *sin = (struct sockaddr_in *) &ifr.ifr_addr; *ip = sin->sin_addr; diff --combined lib/netdev.c index 78d6a9246,82a490419..2c7b260e1 --- a/lib/netdev.c +++ b/lib/netdev.c @@@ -28,7 -28,6 +28,7 @@@ #include "coverage.h" #include "dynamic-string.h" #include "fatal-signal.h" +#include "hash.h" #include "list.h" #include "netdev-provider.h" #include "ofpbuf.h" @@@ -41,16 -40,14 +41,16 @@@ #define THIS_MODULE VLM_netdev #include "vlog.h" -static const struct netdev_class *netdev_classes[] = { +static const struct netdev_class *base_netdev_classes[] = { &netdev_linux_class, &netdev_tap_class, + &netdev_gre_class, }; -static int n_netdev_classes = ARRAY_SIZE(netdev_classes); + +static struct shash netdev_classes = SHASH_INITIALIZER(&netdev_classes); /* All created network devices. */ -static struct shash netdev_obj_shash = SHASH_INITIALIZER(&netdev_obj_shash); +static struct shash netdev_dev_shash = SHASH_INITIALIZER(&netdev_dev_shash); /* All open network devices. */ static struct list netdev_list = LIST_INITIALIZER(&netdev_list); @@@ -59,25 -56,45 +59,25 @@@ * additional log messages. */ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20); - static void close_all_netdevs(void *aux UNUSED); -static void restore_all_flags(void *aux); ++static void close_all_netdevs(void *aux OVS_UNUSED); static int restore_flags(struct netdev *netdev); +void update_device_args(struct netdev_dev *, const struct shash *args); -/* Attempts to initialize the netdev module. Returns 0 if successful, - * otherwise a positive errno value. - * - * Calling this function is optional. If not called explicitly, it will - * automatically be called upon the first attempt to open or create a - * network device. */ -int +static void netdev_initialize(void) { static int status = -1; + if (status < 0) { - int i, j; + int i; - fatal_signal_add_hook(restore_all_flags, NULL, true); + fatal_signal_add_hook(close_all_netdevs, NULL, NULL, true); status = 0; - for (i = j = 0; i < n_netdev_classes; i++) { - const struct netdev_class *class = netdev_classes[i]; - if (class->init) { - int retval = class->init(); - if (!retval) { - netdev_classes[j++] = class; - } else { - VLOG_ERR("failed to initialize %s network device " - "class: %s", class->type, strerror(retval)); - if (!status) { - status = retval; - } - } - } else { - netdev_classes[j++] = class; - } + for (i = 0; i < ARRAY_SIZE(base_netdev_classes); i++) { + netdev_register_provider(base_netdev_classes[i]); } - n_netdev_classes = j; } - return status; } /* Performs periodic work needed by all the various kinds of netdevs. @@@ -87,11 -104,11 +87,11 @@@ void netdev_run(void) { - int i; - for (i = 0; i < n_netdev_classes; i++) { - const struct netdev_class *class = netdev_classes[i]; - if (class->run) { - class->run(); + struct shash_node *node; + SHASH_FOR_EACH(node, &netdev_classes) { + const struct netdev_class *netdev_class = node->data; + if (netdev_class->run) { + netdev_class->run(); } } } @@@ -103,313 -120,185 +103,313 @@@ void netdev_wait(void) { - int i; - for (i = 0; i < n_netdev_classes; i++) { - const struct netdev_class *class = netdev_classes[i]; - if (class->wait) { - class->wait(); + struct shash_node *node; + SHASH_FOR_EACH(node, &netdev_classes) { + const struct netdev_class *netdev_class = node->data; + if (netdev_class->wait) { + netdev_class->wait(); } } } -/* Attempts to create a network device object of 'type' with 'name'. 'type' - * corresponds to the 'type' field used in the netdev_class * structure. - * Arguments for creation are provided in 'args', which may be empty or NULL - * if none are needed. */ +/* Initializes and registers a new netdev provider. After successful + * registration, new netdevs of that type can be opened using netdev_open(). */ int -netdev_create(const char *name, const char *type, const struct shash *args) +netdev_register_provider(const struct netdev_class *new_class) { - struct shash empty_args = SHASH_INITIALIZER(&empty_args); - int i; + struct netdev_class *new_provider; - netdev_initialize(); + if (shash_find(&netdev_classes, new_class->type)) { + VLOG_WARN("attempted to register duplicate netdev provider: %s", + new_class->type); + return EEXIST; + } - if (!args) { - args = &empty_args; + if (new_class->init) { + int error = new_class->init(); + if (error) { + VLOG_ERR("failed to initialize %s network device class: %s", + new_class->type, strerror(error)); + return error; + } } - if (shash_find(&netdev_obj_shash, name)) { - VLOG_WARN("attempted to create a netdev object with bound name: %s", - name); - return EEXIST; + new_provider = xmalloc(sizeof *new_provider); + memcpy(new_provider, new_class, sizeof *new_provider); + + shash_add(&netdev_classes, new_class->type, new_provider); + + return 0; +} + +/* Unregisters a netdev provider. 'type' must have been previously + * registered and not currently be in use by any netdevs. After unregistration + * new netdevs of that type cannot be opened using netdev_open(). */ +int +netdev_unregister_provider(const char *type) +{ + struct shash_node *del_node, *netdev_dev_node; + + del_node = shash_find(&netdev_classes, type); + if (!del_node) { + VLOG_WARN("attempted to unregister a netdev provider that is not " + "registered: %s", type); + return EAFNOSUPPORT; } - for (i = 0; i < n_netdev_classes; i++) { - const struct netdev_class *class = netdev_classes[i]; - if (!strcmp(type, class->type)) { - return class->create(name, type, args, true); + SHASH_FOR_EACH(netdev_dev_node, &netdev_dev_shash) { + struct netdev_dev *netdev_dev = netdev_dev_node->data; + if (!strcmp(netdev_dev->netdev_class->type, type)) { + VLOG_WARN("attempted to unregister in use netdev provider: %s", + type); + return EBUSY; } } - VLOG_WARN("could not create netdev object of unknown type: %s", type); + shash_delete(&netdev_classes, del_node); + free(del_node->data); - return EINVAL; + return 0; } -/* Destroys netdev object 'name'. Netdev objects maintain a reference count - * which is incremented on netdev_open() and decremented on netdev_close(). - * If 'name' has a non-zero reference count, it will not destroy the object - * and return EBUSY. */ -int -netdev_destroy(const char *name) +/* Clears 'types' and enumerates the types of all currently registered netdev + * providers into it. The caller must first initialize the svec. */ +void +netdev_enumerate_types(struct svec *types) { struct shash_node *node; - struct netdev_obj *netdev_obj; - node = shash_find(&netdev_obj_shash, name); - if (!node) { - return ENODEV; + netdev_initialize(); + svec_clear(types); + + SHASH_FOR_EACH(node, &netdev_classes) { + const struct netdev_class *netdev_class = node->data; + svec_add(types, netdev_class->type); } +} + +/* Compares 'args' to those used to those used by 'dev'. Returns true + * if the arguments are the same, false otherwise. Does not update the + * values stored in 'dev'. */ +static bool +compare_device_args(const struct netdev_dev *dev, const struct shash *args) +{ + const struct shash_node **new_args; + bool result = true; + int i; - netdev_obj = node->data; - if (netdev_obj->ref_cnt != 0) { - VLOG_WARN("attempt to destroy open netdev object (%d): %s", - netdev_obj->ref_cnt, name); - return EBUSY; + if (shash_count(args) != dev->n_args) { + return false; } - shash_delete(&netdev_obj_shash, node); - netdev_obj->netdev_class->destroy(netdev_obj); + new_args = shash_sort(args); + for (i = 0; i < dev->n_args; i++) { + if (strcmp(dev->args[i].key, new_args[i]->name) || + strcmp(dev->args[i].value, new_args[i]->data)) { + result = false; + goto finish; + } + } - return 0; +finish: + free(new_args); + return result; } -/* Reconfigures the device object 'name' with 'args'. 'args' may be empty - * or NULL if none are needed. */ -int -netdev_reconfigure(const char *name, const struct shash *args) +static int +compare_args(const void *a_, const void *b_) { - struct shash empty_args = SHASH_INITIALIZER(&empty_args); - struct netdev_obj *netdev_obj; + const struct arg *a = a_; + const struct arg *b = b_; + return strcmp(a->key, b->key); +} - if (!args) { - args = &empty_args; +void +update_device_args(struct netdev_dev *dev, const struct shash *args) +{ + struct shash_node *node; + int i; + + if (dev->n_args) { + for (i = 0; i < dev->n_args; i++) { + free(dev->args[i].key); + free(dev->args[i].value); + } + + free(dev->args); + dev->n_args = 0; + } + + if (!args || shash_is_empty(args)) { + return; } - netdev_obj = shash_find_data(&netdev_obj_shash, name); - if (!netdev_obj) { + dev->n_args = shash_count(args); + dev->args = xmalloc(dev->n_args * sizeof *dev->args); + + i = 0; + SHASH_FOR_EACH(node, args) { + dev->args[i].key = xstrdup(node->name); + dev->args[i].value = xstrdup(node->data); + i++; + } + + qsort(dev->args, dev->n_args, sizeof *dev->args, compare_args); +} + +static int +create_device(struct netdev_options *options, struct netdev_dev **netdev_devp) +{ + struct netdev_class *netdev_class; + + if (!options->may_create) { + VLOG_WARN("attempted to create a device that may not be created: %s", + options->name); return ENODEV; } - if (netdev_obj->netdev_class->reconfigure) { - return netdev_obj->netdev_class->reconfigure(netdev_obj, args); + if (!options->type || strlen(options->type) == 0) { + /* Default to system. */ + options->type = "system"; } - return 0; + netdev_class = shash_find_data(&netdev_classes, options->type); + if (!netdev_class) { + VLOG_WARN("could not create netdev %s of unknown type %s", + options->name, options->type); + return EAFNOSUPPORT; + } + + return netdev_class->create(options->name, options->type, options->args, + netdev_devp); } /* Opens the network device named 'name' (e.g. "eth0") and returns zero if * successful, otherwise a positive errno value. On success, sets '*netdevp' * to the new network device, otherwise to null. * + * If this is the first time the device has been opened, then create is called + * before opening. The device is created using the given type and arguments. + * * 'ethertype' may be a 16-bit Ethernet protocol value in host byte order to * capture frames of that type received on the device. It may also be one of * the 'enum netdev_pseudo_ethertype' values to receive frames in one of those - * categories. */ + * categories. + * + * If the 'may_create' flag is set then this is allowed to be the first time + * the device is opened (i.e. the refcount will be 1 after this call). It + * may be set to false if the device should have already been created. + * + * If the 'may_open' flag is set then the call will succeed even if another + * caller has already opened it. It may be to false if the device should not + * currently be open. */ + int -netdev_open(const char *name, int ethertype, struct netdev **netdevp) +netdev_open(struct netdev_options *options, struct netdev **netdevp) { - struct netdev_obj *netdev_obj; - struct netdev *netdev = NULL; + struct shash empty_args = SHASH_INITIALIZER(&empty_args); + struct netdev_dev *netdev_dev; int error; - int i; + *netdevp = NULL; netdev_initialize(); - netdev_obj = shash_find_data(&netdev_obj_shash, name); - if (netdev_obj) { - netdev_obj->ref_cnt++; - error = netdev_obj->netdev_class->open(name, ethertype, &netdev); + if (!options->args) { + options->args = &empty_args; + } + + netdev_dev = shash_find_data(&netdev_dev_shash, options->name); + + if (!netdev_dev) { + error = create_device(options, &netdev_dev); + if (error) { + return error; + } + update_device_args(netdev_dev, options->args); + + } else if (options->may_open) { + if (!shash_is_empty(options->args) && + !compare_device_args(netdev_dev, options->args)) { + + VLOG_WARN("%s: attempted to open already created netdev with " + "different arguments", options->name); + return EINVAL; + } } else { - /* Default to "system". */ - error = EAFNOSUPPORT; - for (i = 0; i < n_netdev_classes; i++) { - const struct netdev_class *class = netdev_classes[i]; - if (!strcmp(class->type, "system")) { - struct shash empty_args = SHASH_INITIALIZER(&empty_args); - - /* Dynamically create the netdev object, but indicate - * that it should be destroyed when the the last user - * closes its handle. */ - error = class->create(name, "system", &empty_args, false); - if (!error) { - netdev_obj = shash_find_data(&netdev_obj_shash, name); - netdev_obj->ref_cnt++; - error = class->open(name, ethertype, &netdev); - } - break; - } + VLOG_WARN("%s: attempted to create a netdev device with bound name", + options->name); + return EEXIST; + } + + error = netdev_dev->netdev_class->open(netdev_dev, options->ethertype, + netdevp); + + if (!error) { + netdev_dev->ref_cnt++; + } else { + if (!netdev_dev->ref_cnt) { + netdev_dev_uninit(netdev_dev, true); } } - *netdevp = error ? NULL : netdev; return error; } +int +netdev_open_default(const char *name, struct netdev **netdevp) +{ + struct netdev_options options; + + memset(&options, 0, sizeof options); + + options.name = name; + options.ethertype = NETDEV_ETH_TYPE_NONE; + options.may_create = true; + options.may_open = true; + + return netdev_open(&options, netdevp); +} + +/* Reconfigures the device 'netdev' with 'args'. 'args' may be empty + * or NULL if none are needed. */ +int +netdev_reconfigure(struct netdev *netdev, const struct shash *args) +{ + struct shash empty_args = SHASH_INITIALIZER(&empty_args); + struct netdev_dev *netdev_dev = netdev_get_dev(netdev); + + if (!args) { + args = &empty_args; + } + + if (netdev_dev->netdev_class->reconfigure) { + if (!compare_device_args(netdev_dev, args)) { + update_device_args(netdev_dev, args); + return netdev_dev->netdev_class->reconfigure(netdev_dev, args); + } + } else if (!shash_is_empty(args)) { + VLOG_WARN("%s: arguments provided to device that does not have a " + "reconfigure function", netdev_get_name(netdev)); + } + + return 0; +} + /* Closes and destroys 'netdev'. */ void netdev_close(struct netdev *netdev) { if (netdev) { - struct netdev_obj *netdev_obj; - char *name = netdev->name; - int error; - - netdev_obj = shash_find_data(&netdev_obj_shash, name); - assert(netdev_obj); - if (netdev_obj->ref_cnt > 0) { - netdev_obj->ref_cnt--; - } else { - VLOG_WARN("netdev %s closed too many times", name); - } + struct netdev_dev *netdev_dev = netdev_get_dev(netdev); - /* If the reference count for the netdev object is zero, and it - * was dynamically created by netdev_open(), destroy it. */ - if (!netdev_obj->ref_cnt && !netdev_obj->created) { - netdev_destroy(name); - } + assert(netdev_dev->ref_cnt); + netdev_dev->ref_cnt--; + netdev_uninit(netdev, true); - /* Restore flags that we changed, if any. */ - fatal_signal_block(); - error = restore_flags(netdev); - list_remove(&netdev->node); - fatal_signal_unblock(); - if (error) { - VLOG_WARN("failed to restore network device flags on %s: %s", - name, strerror(error)); + /* If the reference count for the netdev device is zero, destroy it. */ + if (!netdev_dev->ref_cnt) { + netdev_dev_uninit(netdev_dev, true); } - - /* Free. */ - netdev->netdev_class->close(netdev); - free(name); } } @@@ -421,7 -310,7 +421,7 @@@ netdev_exists(const char *name struct netdev *netdev; int error; - error = netdev_open(name, NETDEV_ETH_TYPE_NONE, &netdev); + error = netdev_open_default(name, &netdev); if (!error) { netdev_close(netdev); return true; @@@ -434,30 -323,31 +434,30 @@@ } } -/* Initializes 'svec' with a list of the names of all known network devices. */ +/* Clears 'svec' and enumerates the names of all known network devices. */ int netdev_enumerate(struct svec *svec) { - int error; - int i; - - svec_init(svec); + struct shash_node *node; + int error = 0; netdev_initialize(); + svec_clear(svec); - error = 0; - for (i = 0; i < n_netdev_classes; i++) { - const struct netdev_class *class = netdev_classes[i]; - if (class->enumerate) { - int retval = class->enumerate(svec); + SHASH_FOR_EACH(node, &netdev_classes) { + const struct netdev_class *netdev_class = node->data; + if (netdev_class->enumerate) { + int retval = netdev_class->enumerate(svec); if (retval) { VLOG_WARN("failed to enumerate %s network devices: %s", - class->type, strerror(retval)); + netdev_class->type, strerror(retval)); if (!error) { error = retval; } } } } + return error; } @@@ -481,8 -371,8 +481,8 @@@ netdev_recv(struct netdev *netdev, stru assert(buffer->size == 0); assert(ofpbuf_tailroom(buffer) >= ETH_TOTAL_MIN); - retval = netdev->netdev_class->recv(netdev, - buffer->data, ofpbuf_tailroom(buffer)); + retval = netdev_get_dev(netdev)->netdev_class->recv(netdev, buffer->data, + ofpbuf_tailroom(buffer)); if (retval >= 0) { COVERAGE_INC(netdev_received); buffer->size += retval; @@@ -500,14 -390,14 +500,14 @@@ void netdev_recv_wait(struct netdev *netdev) { - netdev->netdev_class->recv_wait(netdev); + netdev_get_dev(netdev)->netdev_class->recv_wait(netdev); } /* Discards all packets waiting to be received from 'netdev'. */ int netdev_drain(struct netdev *netdev) { - return netdev->netdev_class->drain(netdev); + return netdev_get_dev(netdev)->netdev_class->drain(netdev); } /* Sends 'buffer' on 'netdev'. Returns 0 if successful, otherwise a positive @@@ -522,8 -412,7 +522,8 @@@ int netdev_send(struct netdev *netdev, const struct ofpbuf *buffer) { - int error = netdev->netdev_class->send(netdev, buffer->data, buffer->size); + int error = netdev_get_dev(netdev)->netdev_class->send(netdev, + buffer->data, buffer->size); if (!error) { COVERAGE_INC(netdev_sent); } @@@ -540,7 -429,7 +540,7 @@@ void netdev_send_wait(struct netdev *netdev) { - return netdev->netdev_class->send_wait(netdev); + return netdev_get_dev(netdev)->netdev_class->send_wait(netdev); } /* Attempts to set 'netdev''s MAC address to 'mac'. Returns 0 if successful, @@@ -548,7 -437,7 +548,7 @@@ int netdev_set_etheraddr(struct netdev *netdev, const uint8_t mac[ETH_ADDR_LEN]) { - return netdev->netdev_class->set_etheraddr(netdev, mac); + return netdev_get_dev(netdev)->netdev_class->set_etheraddr(netdev, mac); } /* Retrieves 'netdev''s MAC address. If successful, returns 0 and copies the @@@ -557,7 -446,7 +557,7 @@@ int netdev_get_etheraddr(const struct netdev *netdev, uint8_t mac[ETH_ADDR_LEN]) { - return netdev->netdev_class->get_etheraddr(netdev, mac); + return netdev_get_dev(netdev)->netdev_class->get_etheraddr(netdev, mac); } /* Returns the name of the network device that 'netdev' represents, @@@ -565,7 -454,7 +565,7 @@@ const char * netdev_get_name(const struct netdev *netdev) { - return netdev->name; + return netdev_get_dev(netdev)->name; } /* Retrieves the MTU of 'netdev'. The MTU is the maximum size of transmitted @@@ -578,7 -467,7 +578,7 @@@ int netdev_get_mtu(const struct netdev *netdev, int *mtup) { - int error = netdev->netdev_class->get_mtu(netdev, mtup); + int error = netdev_get_dev(netdev)->netdev_class->get_mtu(netdev, mtup); if (error) { VLOG_WARN_RL(&rl, "failed to retrieve MTU for network device %s: %s", netdev_get_name(netdev), strerror(error)); @@@ -599,7 -488,7 +599,7 @@@ int netdev_get_ifindex(const struct netdev *netdev) { - return netdev->netdev_class->get_ifindex(netdev); + return netdev_get_dev(netdev)->netdev_class->get_ifindex(netdev); } /* Stores the features supported by 'netdev' into each of '*current', @@@ -628,8 -517,8 +628,8 @@@ netdev_get_features(struct netdev *netd peer = &dummy[3]; } - error = netdev->netdev_class->get_features(netdev, current, advertised, - supported, peer); + error = netdev_get_dev(netdev)->netdev_class->get_features(netdev, current, + advertised, supported, peer); if (error) { *current = *advertised = *supported = *peer = 0; } @@@ -670,9 -559,8 +670,9 @@@ netdev_features_is_full_duplex(uint32_ int netdev_set_advertisements(struct netdev *netdev, uint32_t advertise) { - return (netdev->netdev_class->set_advertisements - ? netdev->netdev_class->set_advertisements(netdev, advertise) + return (netdev_get_dev(netdev)->netdev_class->set_advertisements + ? netdev_get_dev(netdev)->netdev_class->set_advertisements( + netdev, advertise) : EOPNOTSUPP); } @@@ -696,9 -584,8 +696,9 @@@ netdev_get_in4(const struct netdev *net struct in_addr netmask; int error; - error = (netdev->netdev_class->get_in4 - ? netdev->netdev_class->get_in4(netdev, &address, &netmask) + error = (netdev_get_dev(netdev)->netdev_class->get_in4 + ? netdev_get_dev(netdev)->netdev_class->get_in4(netdev, + &address, &netmask) : EOPNOTSUPP); if (address_) { address_->s_addr = error ? 0 : address.s_addr; @@@ -715,8 -602,8 +715,8 @@@ int netdev_set_in4(struct netdev *netdev, struct in_addr addr, struct in_addr mask) { - return (netdev->netdev_class->set_in4 - ? netdev->netdev_class->set_in4(netdev, addr, mask) + return (netdev_get_dev(netdev)->netdev_class->set_in4 + ? netdev_get_dev(netdev)->netdev_class->set_in4(netdev, addr, mask) : EOPNOTSUPP); } @@@ -726,8 -613,8 +726,8 @@@ in netdev_add_router(struct netdev *netdev, struct in_addr router) { COVERAGE_INC(netdev_add_router); - return (netdev->netdev_class->add_router - ? netdev->netdev_class->add_router(netdev, router) + return (netdev_get_dev(netdev)->netdev_class->add_router + ? netdev_get_dev(netdev)->netdev_class->add_router(netdev, router) : EOPNOTSUPP); } @@@ -743,9 -630,9 +743,9 @@@ netdev_get_next_hop(const struct netde const struct in_addr *host, struct in_addr *next_hop, char **netdev_name) { - int error = (netdev->netdev_class->get_next_hop - ? netdev->netdev_class->get_next_hop(host, next_hop, - netdev_name) + int error = (netdev_get_dev(netdev)->netdev_class->get_next_hop + ? netdev_get_dev(netdev)->netdev_class->get_next_hop( + host, next_hop, netdev_name) : EOPNOTSUPP); if (error) { next_hop->s_addr = 0; @@@ -771,9 -658,8 +771,9 @@@ netdev_get_in6(const struct netdev *net struct in6_addr dummy; int error; - error = (netdev->netdev_class->get_in6 - ? netdev->netdev_class->get_in6(netdev, in6 ? in6 : &dummy) + error = (netdev_get_dev(netdev)->netdev_class->get_in6 + ? netdev_get_dev(netdev)->netdev_class->get_in6(netdev, + in6 ? in6 : &dummy) : EOPNOTSUPP); if (error && in6) { memset(in6, 0, sizeof *in6); @@@ -793,8 -679,8 +793,8 @@@ do_update_flags(struct netdev *netdev, enum netdev_flags old_flags; int error; - error = netdev->netdev_class->update_flags(netdev, off & ~on, - on, &old_flags); + error = netdev_get_dev(netdev)->netdev_class->update_flags(netdev, + off & ~on, on, &old_flags); if (error) { VLOG_WARN_RL(&rl, "failed to %s flags for network device %s: %s", off || on ? "set" : "get", netdev_get_name(netdev), @@@ -867,9 -753,8 +867,9 @@@ in netdev_arp_lookup(const struct netdev *netdev, uint32_t ip, uint8_t mac[ETH_ADDR_LEN]) { - int error = (netdev->netdev_class->arp_lookup - ? netdev->netdev_class->arp_lookup(netdev, ip, mac) + int error = (netdev_get_dev(netdev)->netdev_class->arp_lookup + ? netdev_get_dev(netdev)->netdev_class->arp_lookup(netdev, + ip, mac) : EOPNOTSUPP); if (error) { memset(mac, 0, ETH_ADDR_LEN); @@@ -882,9 -767,8 +882,9 @@@ int netdev_get_carrier(const struct netdev *netdev, bool *carrier) { - int error = (netdev->netdev_class->get_carrier - ? netdev->netdev_class->get_carrier(netdev, carrier) + int error = (netdev_get_dev(netdev)->netdev_class->get_carrier + ? netdev_get_dev(netdev)->netdev_class->get_carrier(netdev, + carrier) : EOPNOTSUPP); if (error) { *carrier = false; @@@ -899,8 -783,8 +899,8 @@@ netdev_get_stats(const struct netdev *n int error; COVERAGE_INC(netdev_get_stats); - error = (netdev->netdev_class->get_stats - ? netdev->netdev_class->get_stats(netdev, stats) + error = (netdev_get_dev(netdev)->netdev_class->get_stats + ? netdev_get_dev(netdev)->netdev_class->get_stats(netdev, stats) : EOPNOTSUPP); if (error) { memset(stats, 0xff, sizeof *stats); @@@ -915,9 -799,9 +915,9 @@@ in netdev_set_policing(struct netdev *netdev, uint32_t kbits_rate, uint32_t kbits_burst) { - return (netdev->netdev_class->set_policing - ? netdev->netdev_class->set_policing(netdev, - kbits_rate, kbits_burst) + return (netdev_get_dev(netdev)->netdev_class->set_policing + ? netdev_get_dev(netdev)->netdev_class->set_policing(netdev, + kbits_rate, kbits_burst) : EOPNOTSUPP); } @@@ -929,9 -813,8 +929,9 @@@ int netdev_get_vlan_vid(const struct netdev *netdev, int *vlan_vid) { - int error = (netdev->netdev_class->get_vlan_vid - ? netdev->netdev_class->get_vlan_vid(netdev, vlan_vid) + int error = (netdev_get_dev(netdev)->netdev_class->get_vlan_vid + ? netdev_get_dev(netdev)->netdev_class->get_vlan_vid(netdev, + vlan_vid) : ENOENT); if (error) { *vlan_vid = 0; @@@ -945,7 -828,7 +945,7 @@@ struct netdev netdev_find_dev_by_in4(const struct in_addr *in4) { struct netdev *netdev; - struct svec dev_list; + struct svec dev_list = SVEC_EMPTY_INITIALIZER; size_t i; netdev_enumerate(&dev_list); @@@ -953,7 -836,7 +953,7 @@@ const char *name = dev_list.names[i]; struct in_addr dev_in4; - if (!netdev_open(name, NETDEV_ETH_TYPE_NONE, &netdev) + if (!netdev_open_default(name, &netdev) && !netdev_get_in4(netdev, &dev_in4, NULL) && dev_in4.s_addr == in4->s_addr) { goto exit; @@@ -967,140 -850,46 +967,140 @@@ exit return netdev; } -/* Initializes 'netdev_obj' as a netdev object named 'name' of the +/* Initializes 'netdev_dev' as a netdev device named 'name' of the * specified 'netdev_class'. * - * This function adds 'netdev_obj' to a netdev-owned shash, so it is - * very important that 'netdev_obj' only be freed after calling - * netdev_destroy(). */ + * This function adds 'netdev_dev' to a netdev-owned shash, so it is + * very important that 'netdev_dev' only be freed after calling + * the refcount drops to zero. */ void -netdev_obj_init(struct netdev_obj *netdev_obj, const char *name, - const struct netdev_class *netdev_class, bool created) +netdev_dev_init(struct netdev_dev *netdev_dev, const char *name, + const struct netdev_class *netdev_class) { - assert(!shash_find(&netdev_obj_shash, name)); + assert(!shash_find(&netdev_dev_shash, name)); - netdev_obj->netdev_class = netdev_class; - netdev_obj->ref_cnt = 0; - netdev_obj->created = created; - shash_add(&netdev_obj_shash, name, netdev_obj); + memset(netdev_dev, 0, sizeof *netdev_dev); + netdev_dev->netdev_class = netdev_class; + netdev_dev->name = xstrdup(name); + netdev_dev->node = shash_add(&netdev_dev_shash, name, netdev_dev); } -/* Initializes 'netdev' as a netdev named 'name' of the specified - * 'netdev_class'. +/* Undoes the results of initialization. + * + * Normally this function does not need to be called as netdev_close has + * the same effect when the refcount drops to zero. + * However, it may be called by providers due to an error on creation + * that occurs after initialization. It this case netdev_close() would + * never be called. */ +void +netdev_dev_uninit(struct netdev_dev *netdev_dev, bool destroy) +{ + char *name = netdev_dev->name; + + assert(!netdev_dev->ref_cnt); + + shash_delete(&netdev_dev_shash, netdev_dev->node); + update_device_args(netdev_dev, NULL); + + if (destroy) { + netdev_dev->netdev_class->destroy(netdev_dev); + } + free(name); +} + +/* Returns the class type of 'netdev_dev'. + * + * The caller must not free the returned value. */ +const char * +netdev_dev_get_type(const struct netdev_dev *netdev_dev) +{ + return netdev_dev->netdev_class->type; +} + +/* Returns the name of 'netdev_dev'. + * + * The caller must not free the returned value. */ +const char * +netdev_dev_get_name(const struct netdev_dev *netdev_dev) +{ + return netdev_dev->name; +} + +/* Returns the netdev_dev with 'name' or NULL if there is none. + * + * The caller must not free the returned value. */ +struct netdev_dev * +netdev_dev_from_name(const char *name) +{ + return shash_find_data(&netdev_dev_shash, name); +} + +/* Fills 'device_list' with devices that match 'netdev_class'. + * + * The caller is responsible for initializing and destroying 'device_list' + * but the contained netdev_devs must not be freed. */ +void +netdev_dev_get_devices(const struct netdev_class *netdev_class, + struct shash *device_list) +{ + struct shash_node *node; + SHASH_FOR_EACH (node, &netdev_dev_shash) { + struct netdev_dev *dev = node->data; + + if (dev->netdev_class == netdev_class) { + shash_add(device_list, node->name, node->data); + } + } +} + +/* Initializes 'netdev' as a instance of the netdev_dev. * * This function adds 'netdev' to a netdev-owned linked list, so it is very * important that 'netdev' only be freed after calling netdev_close(). */ void -netdev_init(struct netdev *netdev, const char *name, - const struct netdev_class *netdev_class) +netdev_init(struct netdev *netdev, struct netdev_dev *netdev_dev) { - netdev->netdev_class = netdev_class; - netdev->name = xstrdup(name); - netdev->save_flags = 0; - netdev->changed_flags = 0; + memset(netdev, 0, sizeof *netdev); + netdev->netdev_dev = netdev_dev; list_push_back(&netdev_list, &netdev->node); } +/* Undoes the results of initialization. + * + * Normally this function only needs to be called from netdev_close(). + * However, it may be called by providers due to an error on opening + * that occurs after initialization. It this case netdev_close() would + * never be called. */ +void +netdev_uninit(struct netdev *netdev, bool close) +{ + /* Restore flags that we changed, if any. */ + int error = restore_flags(netdev); + list_remove(&netdev->node); + if (error) { + VLOG_WARN("failed to restore network device flags on %s: %s", + netdev_get_name(netdev), strerror(error)); + } + + if (close) { + netdev_get_dev(netdev)->netdev_class->close(netdev); + } +} + + /* Returns the class type of 'netdev'. * * The caller must not free the returned value. */ -const char *netdev_get_type(const struct netdev *netdev) +const char * +netdev_get_type(const struct netdev *netdev) { - return netdev->netdev_class->type; + return netdev_get_dev(netdev)->netdev_class->type; +} + +struct netdev_dev * +netdev_get_dev(const struct netdev *netdev) +{ + return netdev->netdev_dev; } /* Initializes 'notifier' as a netdev notifier for 'netdev', for which @@@ -1140,8 -929,7 +1140,8 @@@ netdev_monitor_destroy(struct netdev_mo SHASH_FOR_EACH (node, &monitor->polled_netdevs) { struct netdev_notifier *notifier = node->data; - notifier->netdev->netdev_class->poll_remove(notifier); + netdev_get_dev(notifier->netdev)->netdev_class->poll_remove( + notifier); } shash_destroy(&monitor->polled_netdevs); @@@ -1171,11 -959,11 +1171,11 @@@ netdev_monitor_add(struct netdev_monito const char *netdev_name = netdev_get_name(netdev); int error = 0; if (!shash_find(&monitor->polled_netdevs, netdev_name) - && netdev->netdev_class->poll_add) + && netdev_get_dev(netdev)->netdev_class->poll_add) { struct netdev_notifier *notifier; - error = netdev->netdev_class->poll_add(netdev, netdev_monitor_cb, - monitor, ¬ifier); + error = netdev_get_dev(netdev)->netdev_class->poll_add(netdev, + netdev_monitor_cb, monitor, ¬ifier); if (!error) { assert(notifier->netdev == netdev); shash_add(&monitor->polled_netdevs, netdev_name, notifier); @@@ -1197,7 -985,7 +1197,7 @@@ netdev_monitor_remove(struct netdev_mon if (node) { /* Cancel future notifications. */ struct netdev_notifier *notifier = node->data; - netdev->netdev_class->poll_remove(notifier); + netdev_get_dev(netdev)->netdev_class->poll_remove(notifier); shash_delete(&monitor->polled_netdevs, node); /* Drop any pending notification. */ @@@ -1255,20 -1043,21 +1255,20 @@@ restore_flags(struct netdev *netdev if (netdev->changed_flags) { enum netdev_flags restore = netdev->save_flags & netdev->changed_flags; enum netdev_flags old_flags; - return netdev->netdev_class->update_flags(netdev, - netdev->changed_flags - & ~restore, - restore, &old_flags); + return netdev_get_dev(netdev)->netdev_class->update_flags(netdev, + netdev->changed_flags & ~restore, + restore, &old_flags); } return 0; } -/* Retores all the flags on all network devices that we modified. Called from - * a signal handler, so it does not attempt to report error conditions. */ +/* Close all netdevs on shutdown so they can do any needed cleanup such as + * destroying devices, restoring flags, etc. */ static void - close_all_netdevs(void *aux UNUSED) -restore_all_flags(void *aux OVS_UNUSED) ++close_all_netdevs(void *aux OVS_UNUSED) { - struct netdev *netdev; - LIST_FOR_EACH (netdev, struct netdev, node, &netdev_list) { - restore_flags(netdev); + struct netdev *netdev, *next; + LIST_FOR_EACH_SAFE(netdev, next, struct netdev, node, &netdev_list) { + netdev_close(netdev); } } diff --combined lib/netdev.h index e3a317693,653665af7..27eb82e5b --- a/lib/netdev.h +++ b/lib/netdev.h @@@ -1,5 -1,5 +1,5 @@@ /* - * Copyright (c) 2008, 2009 Nicira Networks. + * Copyright (c) 2008, 2009, 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. @@@ -21,6 -21,10 +21,10 @@@ #include #include + #ifdef __cplusplus + extern "C" { + #endif + /* Generic interface to network devices. * * Currently, there is a single implementation of this interface that supports @@@ -76,28 -80,18 +80,28 @@@ struct netdev_stats uint64_t tx_window_errors; }; +struct netdev_options { + const char *name; + const char *type; + const struct shash *args; + int ethertype; + bool may_create; + bool may_open; +}; + struct netdev; +struct netdev_class; -int netdev_initialize(void); void netdev_run(void); void netdev_wait(void); -int netdev_create(const char *name, const char *type, - const struct shash *args); -int netdev_destroy(const char *name); -int netdev_reconfigure(const char *name, const struct shash *args); +int netdev_register_provider(const struct netdev_class *); +int netdev_unregister_provider(const char *type); +void netdev_enumerate_types(struct svec *types); -int netdev_open(const char *name, int ethertype, struct netdev **); +int netdev_open(struct netdev_options *, struct netdev **); +int netdev_open_default(const char *name, struct netdev **); +int netdev_reconfigure(struct netdev *, const struct shash *args); void netdev_close(struct netdev *); bool netdev_exists(const char *name); @@@ -105,7 -99,6 +109,7 @@@ int netdev_enumerate(struct svec *); const char *netdev_get_name(const struct netdev *); +const char *netdev_get_type(const struct netdev *); int netdev_get_mtu(const struct netdev *, int *mtup); int netdev_get_ifindex(const struct netdev *); @@@ -155,4 -148,8 +159,8 @@@ void netdev_monitor_remove(struct netde int netdev_monitor_poll(struct netdev_monitor *, char **devnamep); void netdev_monitor_poll_wait(const struct netdev_monitor *); + #ifdef __cplusplus + } + #endif + #endif /* netdev.h */ diff --combined lib/packets.h index 6513f64b9,2c33078f2..eaffca939 --- a/lib/packets.h +++ b/lib/packets.h @@@ -1,5 -1,5 +1,5 @@@ /* - * Copyright (c) 2008, 2009 Nicira Networks. + * Copyright (c) 2008, 2009, 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. @@@ -26,11 -26,9 +26,11 @@@ struct ofpbuf; +bool dpid_from_string(const char *s, uint64_t *dpidp); + #define ETH_ADDR_LEN 6 - static const uint8_t eth_addr_broadcast[ETH_ADDR_LEN] UNUSED + static const uint8_t eth_addr_broadcast[ETH_ADDR_LEN] OVS_UNUSED = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; static inline bool eth_addr_is_broadcast(const uint8_t ea[6]) @@@ -38,16 -36,21 +38,16 @@@ return (ea[0] & ea[1] & ea[2] & ea[3] & ea[4] & ea[5]) == 0xff; } -/* Returns true if 'ea' is an Ethernet address used for virtual interfaces - * under XenServer. Generally the actual Ethernet address is FE:FF:FF:FF:FF:FF - * but it can be FE:FE:FE:FE:FE:FE in some cases. */ -static inline bool eth_addr_is_vif(const uint8_t ea[6]) -{ - return ea[0] == 0xfe && (ea[1] & ea[2] & ea[3] & ea[4] & ea[5]) >= 0xfe; -} - static inline bool eth_addr_is_multicast(const uint8_t ea[6]) { return ea[0] & 1; } static inline bool eth_addr_is_local(const uint8_t ea[6]) { - return ea[0] & 2; + /* Local if it is either a locally administered address or a Nicira random + * address. */ + return !!(ea[0] & 2) + || (ea[0] == 0x00 && ea[1] == 0x23 && ea[2] == 0x20 && !!(ea[3] & 0x80)); } static inline bool eth_addr_is_zero(const uint8_t ea[6]) { @@@ -86,18 -89,6 +86,18 @@@ static inline void eth_addr_random(uint random_bytes(ea, ETH_ADDR_LEN); eth_addr_mark_random(ea); } +static inline void eth_addr_nicira_random(uint8_t ea[ETH_ADDR_LEN]) +{ + eth_addr_random(ea); + + /* Set the OUI to the Nicira one. */ + ea[0] = 0x00; + ea[1] = 0x23; + ea[2] = 0x20; + + /* Set the top bit to indicate random Nicira address. */ + ea[3] |= 0x80; +} /* Returns true if 'ea' is a reserved multicast address, that a bridge must * never forward, false otherwise. */ static inline bool eth_addr_is_reserved(const uint8_t ea[ETH_ADDR_LEN]) @@@ -110,8 -101,6 +110,8 @@@ && (ea[5] & 0xf0) == 0x00); } +bool eth_addr_from_string(const char *, uint8_t ea[ETH_ADDR_LEN]); + void compose_benign_packet(struct ofpbuf *, const char *tag, uint16_t snap_type, const uint8_t eth_src[ETH_ADDR_LEN]); diff --combined lib/process.c index 3504dfb0d,1f6b00fc9..39168862f --- a/lib/process.c +++ b/lib/process.c @@@ -55,7 -55,7 +55,7 @@@ static struct list all_processes = LIST static bool sigchld_is_blocked(void); static void block_sigchld(sigset_t *); static void unblock_sigchld(const sigset_t *); - static void sigchld_handler(int signr UNUSED); + static void sigchld_handler(int signr OVS_UNUSED); static bool is_member(int x, const int *array, size_t); /* Initializes the process subsystem (if it is not already initialized). Calls @@@ -161,7 -161,7 +161,7 @@@ process_register(const char *name, pid_ assert(sigchld_is_blocked()); - p = xcalloc(1, sizeof *p); + p = xzalloc(sizeof *p); p->pid = pid; slash = strrchr(name, '/'); p->name = xstrdup(slash ? slash + 1 : name); @@@ -201,14 -201,17 +201,14 @@@ process_start(char **argv } block_sigchld(&oldsigs); - fatal_signal_block(); pid = fork(); if (pid < 0) { - fatal_signal_unblock(); unblock_sigchld(&oldsigs); VLOG_WARN("fork failed: %s", strerror(errno)); return errno; } else if (pid) { /* Running in parent process. */ *pp = process_register(argv[0], pid); - fatal_signal_unblock(); unblock_sigchld(&oldsigs); return 0; } else { @@@ -217,6 -220,7 +217,6 @@@ int fd; fatal_signal_fork(); - fatal_signal_unblock(); unblock_sigchld(&oldsigs); for (fd = 0; fd < fd_max; fd++) { if (is_member(fd, null_fds, n_null_fds)) { @@@ -285,7 -289,7 +285,7 @@@ process_exited(struct process *p return true; } else { char buf[_POSIX_PIPE_BUF]; - read(fds[0], buf, sizeof buf); + ignore(read(fds[0], buf, sizeof buf)); return false; } } @@@ -415,13 -419,15 +415,13 @@@ stream_open(struct stream *s static void stream_read(struct stream *s) { - int error = 0; - if (s->fds[0] < 0) { return; } - error = 0; for (;;) { char buffer[512]; + int error; size_t n; error = read_fully(s->fds[0], buffer, sizeof buffer, &n); @@@ -515,10 -521,12 +515,10 @@@ process_run_capture(char **argv, char * } block_sigchld(&oldsigs); - fatal_signal_block(); pid = fork(); if (pid < 0) { int error = errno; - fatal_signal_unblock(); unblock_sigchld(&oldsigs); VLOG_WARN("fork failed: %s", strerror(error)); @@@ -531,6 -539,7 +531,6 @@@ struct process *p; p = process_register(argv[0], pid); - fatal_signal_unblock(); unblock_sigchld(&oldsigs); close(s_stdout.fds[1]); @@@ -566,6 -575,7 +566,6 @@@ int i; fatal_signal_fork(); - fatal_signal_unblock(); unblock_sigchld(&oldsigs); dup2(get_null_fd(), 0); @@@ -585,7 -595,7 +585,7 @@@ } static void - sigchld_handler(int signr UNUSED) + sigchld_handler(int signr OVS_UNUSED) { struct process *p; @@@ -607,7 -617,7 +607,7 @@@ } } } - write(fds[1], "", 1); + ignore(write(fds[1], "", 1)); } static bool diff --combined lib/rconn.c index f2d074aa0,771b3a304..6bd43940e --- a/lib/rconn.c +++ b/lib/rconn.c @@@ -1,5 -1,5 +1,5 @@@ /* - * Copyright (c) 2008, 2009 Nicira Networks. + * Copyright (c) 2008, 2009, 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. @@@ -176,7 -176,7 +176,7 @@@ rconn_new_from_vconn(const char *name, struct rconn * rconn_create(int probe_interval, int max_backoff) { - struct rconn *rc = xcalloc(1, sizeof *rc); + struct rconn *rc = xzalloc(sizeof *rc); rc->state = S_VOID; rc->state_entered = time_now(); @@@ -313,13 -313,13 +313,13 @@@ rconn_destroy(struct rconn *rc } static unsigned int - timeout_VOID(const struct rconn *rc UNUSED) + timeout_VOID(const struct rconn *rc OVS_UNUSED) { return UINT_MAX; } static void - run_VOID(struct rconn *rc UNUSED) + run_VOID(struct rconn *rc OVS_UNUSED) { /* Nothing to do. */ } @@@ -459,15 -459,6 +459,15 @@@ voi rconn_run(struct rconn *rc) { int old_state; + size_t i; + + if (rc->vconn) { + vconn_run(rc->vconn); + } + for (i = 0; i < rc->n_monitors; i++) { + vconn_run(rc->monitors[i]); + } + do { old_state = rc->state; switch (rc->state) { @@@ -485,17 -476,7 +485,17 @@@ void rconn_run_wait(struct rconn *rc) { - unsigned int timeo = timeout(rc); + unsigned int timeo; + size_t i; + + if (rc->vconn) { + vconn_run_wait(rc->vconn); + } + for (i = 0; i < rc->n_monitors; i++) { + vconn_run_wait(rc->monitors[i]); + } + + timeo = timeout(rc); if (timeo != UINT_MAX) { unsigned int expires = sat_add(rc->state_entered, timeo); unsigned int remaining = sat_sub(expires, time_now()); @@@ -566,7 -547,7 +566,7 @@@ rconn_send(struct rconn *rc, struct ofp if (rconn_is_connected(rc)) { COVERAGE_INC(rconn_queued); copy_to_monitor(rc, b); - b->private = counter; + b->private_p = counter; if (counter) { rconn_packet_counter_inc(counter); } @@@ -864,7 -845,7 +864,7 @@@ try_send(struct rconn *rc { int retval = 0; struct ofpbuf *next = rc->txq.head->next; - struct rconn_packet_counter *counter = rc->txq.head->private; + struct rconn_packet_counter *counter = rc->txq.head->private_p; retval = vconn_send(rc->vconn, rc->txq.head); if (retval) { if (retval != EAGAIN) { @@@ -933,7 -914,7 +933,7 @@@ flush_queue(struct rconn *rc } while (rc->txq.n > 0) { struct ofpbuf *b = queue_pop_head(&rc->txq); - struct rconn_packet_counter *counter = b->private; + struct rconn_packet_counter *counter = b->private_p; if (counter) { rconn_packet_counter_dec(counter); } diff --combined lib/shash.h index 52cd4dca6,471918b27..3a6e27788 --- a/lib/shash.h +++ b/lib/shash.h @@@ -1,5 -1,5 +1,5 @@@ /* - * Copyright (c) 2009 Nicira Networks. + * Copyright (c) 2009, 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. @@@ -19,6 -19,10 +19,10 @@@ #include "hmap.h" + #ifdef __cplusplus + extern "C" { + #endif + struct shash_node { struct hmap_node node; char *name; @@@ -40,19 -44,16 +44,23 @@@ struct shash void shash_init(struct shash *); void shash_destroy(struct shash *); +void shash_swap(struct shash *, struct shash *); +void shash_moved(struct shash *); void shash_clear(struct shash *); bool shash_is_empty(const struct shash *); -struct shash_node *shash_add(struct shash *, const char *, void *); +size_t shash_count(const struct shash *); +struct shash_node *shash_add(struct shash *, const char *, const void *); +bool shash_add_once(struct shash *, const char *, const void *); void shash_delete(struct shash *, struct shash_node *); struct shash_node *shash_find(const struct shash *, const char *); void *shash_find_data(const struct shash *, const char *); +void *shash_find_and_delete(struct shash *, const char *); struct shash_node *shash_first(const struct shash *); +const struct shash_node **shash_sort(const struct shash *); +bool shash_equal_keys(const struct shash *, const struct shash *); + #ifdef __cplusplus + } + #endif + #endif /* shash.h */ diff --combined lib/socket-util.c index d2ee8ea4f,7c1975061..a8f805c56 --- a/lib/socket-util.c +++ b/lib/socket-util.c @@@ -204,7 -204,7 +204,7 @@@ make_sockaddr_un(const char *name, stru * * Returns the socket's fd if successful, otherwise a negative errno value. */ int - make_unix_socket(int style, bool nonblock, bool passcred UNUSED, + make_unix_socket(int style, bool nonblock, bool passcred OVS_UNUSED, const char *bind_path, const char *connect_path) { int error; @@@ -249,7 -249,6 +249,7 @@@ make_sockaddr_un(connect_path, &un, &un_len); if (connect(fd, (struct sockaddr*) &un, un_len) && errno != EINPROGRESS) { + printf("connect failed with %s\n", strerror(errno)); goto error; } } @@@ -266,10 -265,10 +266,10 @@@ return fd; error: + error = errno == EAGAIN ? EPROTO : errno; if (bind_path) { fatal_signal_remove_file_to_unlink(bind_path); } - error = errno; close(fd); return -error; } @@@ -385,35 -384,25 +385,35 @@@ exit /* Opens a non-blocking IPv4 socket of the specified 'style', binds to * 'target', and listens for incoming connections. 'target' should be a string - * in the format "[][:]". may be omitted if 'default_port' is - * nonzero, in which case it defaults to 'default_port'. If is omitted it - * defaults to the wildcard IP address. + * in the format "[][:]": + * + * - If 'default_port' is -1, then is required. Otherwise, if + * is omitted, then 'default_port' is used instead. + * + * - If (or 'default_port', if used) is 0, then no port is bound + * and the TCP/IP stack will select a port. + * + * - If is omitted then the IP address is wildcarded. * * 'style' should be SOCK_STREAM (for TCP) or SOCK_DGRAM (for UDP). * * For TCP, the socket will have SO_REUSEADDR turned on. * * On success, returns a non-negative file descriptor. On failure, returns a - * negative errno value. */ + * negative errno value. + * + * If 'sinp' is non-null, then on success the bound address is stored into + * '*sinp'. */ int -inet_open_passive(int style, const char *target_, uint16_t default_port) +inet_open_passive(int style, const char *target_, int default_port, + struct sockaddr_in *sinp) { char *target = xstrdup(target_); char *string_ptr = target; struct sockaddr_in sin; const char *host_name; const char *port_string; - int fd, error; + int fd, error, port; unsigned int yes = 1; /* Address defaults. */ @@@ -424,9 -413,9 +424,9 @@@ /* Parse optional port number. */ port_string = strsep(&string_ptr, ":"); - if (port_string && atoi(port_string)) { - sin.sin_port = htons(atoi(port_string)); - } else if (!default_port) { + if (port_string && str_to_int(port_string, 10, &port)) { + sin.sin_port = htons(port); + } else if (default_port < 0) { VLOG_ERR("%s: port number must be specified", target_); error = EAFNOSUPPORT; goto exit; @@@ -472,21 -461,6 +472,21 @@@ VLOG_ERR("%s: listen: %s", target_, strerror(error)); goto exit_close; } + + if (sinp) { + socklen_t sin_len = sizeof sin; + if (getsockname(fd, (struct sockaddr *) &sin, &sin_len) < 0){ + error = errno; + VLOG_ERR("%s: getsockname: %s", target_, strerror(error)); + goto exit_close; + } + if (sin.sin_family != AF_INET || sin_len != sizeof sin) { + VLOG_ERR("%s: getsockname: invalid socket name", target_); + goto exit_close; + } + *sinp = sin; + } + error = 0; goto exit; diff --combined lib/stream-ssl.c index 941f77914,000000000..51ce30635 mode 100644,000000..100644 --- a/lib/stream-ssl.c +++ b/lib/stream-ssl.c @@@ -1,1089 -1,0 +1,1089 @@@ +/* + * Copyright (c) 2008, 2009, 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 "stream-ssl.h" +#include "dhparams.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dynamic-string.h" +#include "leak-checker.h" +#include "ofpbuf.h" +#include "openflow/openflow.h" +#include "packets.h" +#include "poll-loop.h" +#include "socket-util.h" +#include "socket-util.h" +#include "util.h" +#include "stream-provider.h" +#include "stream.h" + +#include "vlog.h" +#define THIS_MODULE VLM_stream_ssl + +/* Active SSL. */ + +enum ssl_state { + STATE_TCP_CONNECTING, + STATE_SSL_CONNECTING +}; + +enum session_type { + CLIENT, + SERVER +}; + +struct ssl_stream +{ + struct stream stream; + enum ssl_state state; + int connect_error; + enum session_type type; + int fd; + SSL *ssl; + struct ofpbuf *txbuf; + + /* rx_want and tx_want record the result of the last call to SSL_read() + * and SSL_write(), respectively: + * + * - If the call reported that data needed to be read from the file + * descriptor, the corresponding member is set to SSL_READING. + * + * - If the call reported that data needed to be written to the file + * descriptor, the corresponding member is set to SSL_WRITING. + * + * - Otherwise, the member is set to SSL_NOTHING, indicating that the + * call completed successfully (or with an error) and that there is no + * need to block. + * + * These are needed because there is no way to ask OpenSSL what a data read + * or write would require without giving it a buffer to receive into or + * data to send, respectively. (Note that the SSL_want() status is + * overwritten by each SSL_read() or SSL_write() call, so we can't rely on + * its value.) + * + * A single call to SSL_read() or SSL_write() can perform both reading + * and writing and thus invalidate not one of these values but actually + * both. Consider this situation, for example: + * + * - SSL_write() blocks on a read, so tx_want gets SSL_READING. + * + * - SSL_read() laters succeeds reading from 'fd' and clears out the + * whole receive buffer, so rx_want gets SSL_READING. + * + * - Client calls stream_wait(STREAM_RECV) and stream_wait(STREAM_SEND) + * and blocks. + * + * - Now we're stuck blocking until the peer sends us data, even though + * SSL_write() could now succeed, which could easily be a deadlock + * condition. + * + * On the other hand, we can't reset both tx_want and rx_want on every call + * to SSL_read() or SSL_write(), because that would produce livelock, + * e.g. in this situation: + * + * - SSL_write() blocks, so tx_want gets SSL_READING or SSL_WRITING. + * + * - SSL_read() blocks, so rx_want gets SSL_READING or SSL_WRITING, + * but tx_want gets reset to SSL_NOTHING. + * + * - Client calls stream_wait(STREAM_RECV) and stream_wait(STREAM_SEND) + * and blocks. + * + * - Client wakes up immediately since SSL_NOTHING in tx_want indicates + * that no blocking is necessary. + * + * The solution we adopt here is to set tx_want to SSL_NOTHING after + * calling SSL_read() only if the SSL state of the connection changed, + * which indicates that an SSL-level renegotiation made some progress, and + * similarly for rx_want and SSL_write(). This prevents both the + * deadlock and livelock situations above. + */ + int rx_want, tx_want; +}; + +/* SSL context created by ssl_init(). */ +static SSL_CTX *ctx; + +/* Required configuration. */ +static bool has_private_key, has_certificate, has_ca_cert; + +/* Ordinarily, we require a CA certificate for the peer to be locally + * available. 'has_ca_cert' is true when this is the case, and neither of the + * following variables matter. + * + * We can, however, bootstrap the CA certificate from the peer at the beginning + * of our first connection then use that certificate on all subsequent + * connections, saving it to a file for use in future runs also. In this case, + * 'has_ca_cert' is false, 'bootstrap_ca_cert' is true, and 'ca_cert_file' + * names the file to be saved. */ +static bool bootstrap_ca_cert; +static char *ca_cert_file; + +/* Who knows what can trigger various SSL errors, so let's throttle them down + * quite a bit. */ +static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 25); + +static int ssl_init(void); +static int do_ssl_init(void); +static bool ssl_wants_io(int ssl_error); +static void ssl_close(struct stream *); +static void ssl_clear_txbuf(struct ssl_stream *); +static int interpret_ssl_error(const char *function, int ret, int error, + int *want); - static DH *tmp_dh_callback(SSL *ssl, int is_export UNUSED, int keylength); ++static DH *tmp_dh_callback(SSL *ssl, int is_export OVS_UNUSED, int keylength); +static void log_ca_cert(const char *file_name, X509 *cert); + +static short int +want_to_poll_events(int want) +{ + switch (want) { + case SSL_NOTHING: + NOT_REACHED(); + + case SSL_READING: + return POLLIN; + + case SSL_WRITING: + return POLLOUT; + + default: + NOT_REACHED(); + } +} + +static int +new_ssl_stream(const char *name, int fd, enum session_type type, + enum ssl_state state, const struct sockaddr_in *remote, + struct stream **streamp) +{ + struct sockaddr_in local; + socklen_t local_len = sizeof local; + struct ssl_stream *sslv; + SSL *ssl = NULL; + int on = 1; + int retval; + + /* Check for all the needful configuration. */ + retval = 0; + if (!has_private_key) { + VLOG_ERR("Private key must be configured to use SSL"); + retval = ENOPROTOOPT; + } + if (!has_certificate) { + VLOG_ERR("Certificate must be configured to use SSL"); + retval = ENOPROTOOPT; + } + if (!has_ca_cert && !bootstrap_ca_cert) { + VLOG_ERR("CA certificate must be configured to use SSL"); + retval = ENOPROTOOPT; + } + if (!SSL_CTX_check_private_key(ctx)) { + VLOG_ERR("Private key does not match certificate public key: %s", + ERR_error_string(ERR_get_error(), NULL)); + retval = ENOPROTOOPT; + } + if (retval) { + goto error; + } + + /* Get the local IP and port information */ + retval = getsockname(fd, (struct sockaddr *) &local, &local_len); + if (retval) { + memset(&local, 0, sizeof local); + } + + /* Disable Nagle. */ + retval = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof on); + if (retval) { + VLOG_ERR("%s: setsockopt(TCP_NODELAY): %s", name, strerror(errno)); + retval = errno; + goto error; + } + + /* Create and configure OpenSSL stream. */ + ssl = SSL_new(ctx); + if (ssl == NULL) { + VLOG_ERR("SSL_new: %s", ERR_error_string(ERR_get_error(), NULL)); + retval = ENOPROTOOPT; + goto error; + } + if (SSL_set_fd(ssl, fd) == 0) { + VLOG_ERR("SSL_set_fd: %s", ERR_error_string(ERR_get_error(), NULL)); + retval = ENOPROTOOPT; + goto error; + } + if (bootstrap_ca_cert && type == CLIENT) { + SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL); + } + + /* Create and return the ssl_stream. */ + sslv = xmalloc(sizeof *sslv); + stream_init(&sslv->stream, &ssl_stream_class, EAGAIN, name); + stream_set_remote_ip(&sslv->stream, remote->sin_addr.s_addr); + stream_set_remote_port(&sslv->stream, remote->sin_port); + stream_set_local_ip(&sslv->stream, local.sin_addr.s_addr); + stream_set_local_port(&sslv->stream, local.sin_port); + sslv->state = state; + sslv->type = type; + sslv->fd = fd; + sslv->ssl = ssl; + sslv->txbuf = NULL; + sslv->rx_want = sslv->tx_want = SSL_NOTHING; + *streamp = &sslv->stream; + return 0; + +error: + if (ssl) { + SSL_free(ssl); + } + close(fd); + return retval; +} + +static struct ssl_stream * +ssl_stream_cast(struct stream *stream) +{ + stream_assert_class(stream, &ssl_stream_class); + return CONTAINER_OF(stream, struct ssl_stream, stream); +} + +static int +ssl_open(const char *name, char *suffix, struct stream **streamp) +{ + struct sockaddr_in sin; + int error, fd; + + error = ssl_init(); + if (error) { + return error; + } + + error = inet_open_active(SOCK_STREAM, suffix, OFP_SSL_PORT, &sin, &fd); + if (fd >= 0) { + int state = error ? STATE_TCP_CONNECTING : STATE_SSL_CONNECTING; + return new_ssl_stream(name, fd, CLIENT, state, &sin, streamp); + } else { + VLOG_ERR("%s: connect: %s", name, strerror(error)); + return error; + } +} + +static int +do_ca_cert_bootstrap(struct stream *stream) +{ + struct ssl_stream *sslv = ssl_stream_cast(stream); + STACK_OF(X509) *chain; + X509 *ca_cert; + FILE *file; + int error; + int fd; + + chain = SSL_get_peer_cert_chain(sslv->ssl); + if (!chain || !sk_X509_num(chain)) { + VLOG_ERR("could not bootstrap CA cert: no certificate presented by " + "peer"); + return EPROTO; + } + ca_cert = sk_X509_value(chain, sk_X509_num(chain) - 1); + + /* Check that 'ca_cert' is self-signed. Otherwise it is not a CA + * certificate and we should not attempt to use it as one. */ + error = X509_check_issued(ca_cert, ca_cert); + if (error) { + VLOG_ERR("could not bootstrap CA cert: obtained certificate is " + "not self-signed (%s)", + X509_verify_cert_error_string(error)); + if (sk_X509_num(chain) < 2) { + VLOG_ERR("only one certificate was received, so probably the peer " + "is not configured to send its CA certificate"); + } + return EPROTO; + } + + fd = open(ca_cert_file, O_CREAT | O_EXCL | O_WRONLY, 0444); + if (fd < 0) { + VLOG_ERR("could not bootstrap CA cert: creating %s failed: %s", + ca_cert_file, strerror(errno)); + return errno; + } + + file = fdopen(fd, "w"); + if (!file) { + int error = errno; + VLOG_ERR("could not bootstrap CA cert: fdopen failed: %s", + strerror(error)); + unlink(ca_cert_file); + return error; + } + + if (!PEM_write_X509(file, ca_cert)) { + VLOG_ERR("could not bootstrap CA cert: PEM_write_X509 to %s failed: " + "%s", ca_cert_file, ERR_error_string(ERR_get_error(), NULL)); + fclose(file); + unlink(ca_cert_file); + return EIO; + } + + if (fclose(file)) { + int error = errno; + VLOG_ERR("could not bootstrap CA cert: writing %s failed: %s", + ca_cert_file, strerror(error)); + unlink(ca_cert_file); + return error; + } + + VLOG_INFO("successfully bootstrapped CA cert to %s", ca_cert_file); + log_ca_cert(ca_cert_file, ca_cert); + bootstrap_ca_cert = false; + has_ca_cert = true; + + /* SSL_CTX_add_client_CA makes a copy of ca_cert's relevant data. */ + SSL_CTX_add_client_CA(ctx, ca_cert); + + /* SSL_CTX_use_certificate() takes ownership of the certificate passed in. + * 'ca_cert' is owned by sslv->ssl, so we need to duplicate it. */ + ca_cert = X509_dup(ca_cert); + if (!ca_cert) { + out_of_memory(); + } + if (SSL_CTX_load_verify_locations(ctx, ca_cert_file, NULL) != 1) { + VLOG_ERR("SSL_CTX_load_verify_locations: %s", + ERR_error_string(ERR_get_error(), NULL)); + return EPROTO; + } + VLOG_INFO("killing successful connection to retry using CA cert"); + return EPROTO; +} + +static int +ssl_connect(struct stream *stream) +{ + struct ssl_stream *sslv = ssl_stream_cast(stream); + int retval; + + switch (sslv->state) { + case STATE_TCP_CONNECTING: + retval = check_connection_completion(sslv->fd); + if (retval) { + return retval; + } + sslv->state = STATE_SSL_CONNECTING; + /* Fall through. */ + + case STATE_SSL_CONNECTING: + retval = (sslv->type == CLIENT + ? SSL_connect(sslv->ssl) : SSL_accept(sslv->ssl)); + if (retval != 1) { + int error = SSL_get_error(sslv->ssl, retval); + if (retval < 0 && ssl_wants_io(error)) { + return EAGAIN; + } else { + int unused; + interpret_ssl_error((sslv->type == CLIENT ? "SSL_connect" + : "SSL_accept"), retval, error, &unused); + shutdown(sslv->fd, SHUT_RDWR); + return EPROTO; + } + } else if (bootstrap_ca_cert) { + return do_ca_cert_bootstrap(stream); + } else if ((SSL_get_verify_mode(sslv->ssl) + & (SSL_VERIFY_NONE | SSL_VERIFY_PEER)) + != SSL_VERIFY_PEER) { + /* Two or more SSL connections completed at the same time while we + * were in bootstrap mode. Only one of these can finish the + * bootstrap successfully. The other one(s) must be rejected + * because they were not verified against the bootstrapped CA + * certificate. (Alternatively we could verify them against the CA + * certificate, but that's more trouble than it's worth. These + * connections will succeed the next time they retry, assuming that + * they have a certificate against the correct CA.) */ + VLOG_ERR("rejecting SSL connection during bootstrap race window"); + return EPROTO; + } else { + return 0; + } + } + + NOT_REACHED(); +} + +static void +ssl_close(struct stream *stream) +{ + struct ssl_stream *sslv = ssl_stream_cast(stream); + ssl_clear_txbuf(sslv); + + /* Attempt clean shutdown of the SSL connection. This will work most of + * the time, as long as the kernel send buffer has some free space and the + * SSL connection isn't renegotiating, etc. That has to be good enough, + * since we don't have any way to continue the close operation in the + * background. */ + SSL_shutdown(sslv->ssl); + + SSL_free(sslv->ssl); + close(sslv->fd); + free(sslv); +} + +static int +interpret_ssl_error(const char *function, int ret, int error, + int *want) +{ + *want = SSL_NOTHING; + + switch (error) { + case SSL_ERROR_NONE: + VLOG_ERR_RL(&rl, "%s: unexpected SSL_ERROR_NONE", function); + break; + + case SSL_ERROR_ZERO_RETURN: + VLOG_ERR_RL(&rl, "%s: unexpected SSL_ERROR_ZERO_RETURN", function); + break; + + case SSL_ERROR_WANT_READ: + *want = SSL_READING; + return EAGAIN; + + case SSL_ERROR_WANT_WRITE: + *want = SSL_WRITING; + return EAGAIN; + + case SSL_ERROR_WANT_CONNECT: + VLOG_ERR_RL(&rl, "%s: unexpected SSL_ERROR_WANT_CONNECT", function); + break; + + case SSL_ERROR_WANT_ACCEPT: + VLOG_ERR_RL(&rl, "%s: unexpected SSL_ERROR_WANT_ACCEPT", function); + break; + + case SSL_ERROR_WANT_X509_LOOKUP: + VLOG_ERR_RL(&rl, "%s: unexpected SSL_ERROR_WANT_X509_LOOKUP", + function); + break; + + case SSL_ERROR_SYSCALL: { + int queued_error = ERR_get_error(); + if (queued_error == 0) { + if (ret < 0) { + int status = errno; + VLOG_WARN_RL(&rl, "%s: system error (%s)", + function, strerror(status)); + return status; + } else { + VLOG_WARN_RL(&rl, "%s: unexpected SSL connection close", + function); + return EPROTO; + } + } else { + VLOG_WARN_RL(&rl, "%s: %s", + function, ERR_error_string(queued_error, NULL)); + break; + } + } + + case SSL_ERROR_SSL: { + int queued_error = ERR_get_error(); + if (queued_error != 0) { + VLOG_WARN_RL(&rl, "%s: %s", + function, ERR_error_string(queued_error, NULL)); + } else { + VLOG_ERR_RL(&rl, "%s: SSL_ERROR_SSL without queued error", + function); + } + break; + } + + default: + VLOG_ERR_RL(&rl, "%s: bad SSL error code %d", function, error); + break; + } + return EIO; +} + +static ssize_t +ssl_recv(struct stream *stream, void *buffer, size_t n) +{ + struct ssl_stream *sslv = ssl_stream_cast(stream); + int old_state; + ssize_t ret; + + /* Behavior of zero-byte SSL_read is poorly defined. */ + assert(n > 0); + + old_state = SSL_get_state(sslv->ssl); + ret = SSL_read(sslv->ssl, buffer, n); + if (old_state != SSL_get_state(sslv->ssl)) { + sslv->tx_want = SSL_NOTHING; + } + sslv->rx_want = SSL_NOTHING; + + if (ret > 0) { + return ret; + } else { + int error = SSL_get_error(sslv->ssl, ret); + if (error == SSL_ERROR_ZERO_RETURN) { + return 0; + } else { + return -interpret_ssl_error("SSL_read", ret, error, + &sslv->rx_want); + } + } +} + +static void +ssl_clear_txbuf(struct ssl_stream *sslv) +{ + ofpbuf_delete(sslv->txbuf); + sslv->txbuf = NULL; +} + +static int +ssl_do_tx(struct stream *stream) +{ + struct ssl_stream *sslv = ssl_stream_cast(stream); + + for (;;) { + int old_state = SSL_get_state(sslv->ssl); + int ret = SSL_write(sslv->ssl, sslv->txbuf->data, sslv->txbuf->size); + if (old_state != SSL_get_state(sslv->ssl)) { + sslv->rx_want = SSL_NOTHING; + } + sslv->tx_want = SSL_NOTHING; + if (ret > 0) { + ofpbuf_pull(sslv->txbuf, ret); + if (sslv->txbuf->size == 0) { + return 0; + } + } else { + int ssl_error = SSL_get_error(sslv->ssl, ret); + if (ssl_error == SSL_ERROR_ZERO_RETURN) { + VLOG_WARN_RL(&rl, "SSL_write: connection closed"); + return EPIPE; + } else { + return interpret_ssl_error("SSL_write", ret, ssl_error, + &sslv->tx_want); + } + } + } +} + +static ssize_t +ssl_send(struct stream *stream, const void *buffer, size_t n) +{ + struct ssl_stream *sslv = ssl_stream_cast(stream); + + if (sslv->txbuf) { + return -EAGAIN; + } else { + int error; + + sslv->txbuf = ofpbuf_clone_data(buffer, n); + error = ssl_do_tx(stream); + switch (error) { + case 0: + ssl_clear_txbuf(sslv); + return n; + case EAGAIN: + leak_checker_claim(buffer); + return n; + default: + sslv->txbuf = NULL; + return -error; + } + } +} + +static void +ssl_run(struct stream *stream) +{ + struct ssl_stream *sslv = ssl_stream_cast(stream); + + if (sslv->txbuf && ssl_do_tx(stream) != EAGAIN) { + ssl_clear_txbuf(sslv); + } +} + +static void +ssl_run_wait(struct stream *stream) +{ + struct ssl_stream *sslv = ssl_stream_cast(stream); + + if (sslv->tx_want != SSL_NOTHING) { + poll_fd_wait(sslv->fd, want_to_poll_events(sslv->tx_want)); + } +} + +static void +ssl_wait(struct stream *stream, enum stream_wait_type wait) +{ + struct ssl_stream *sslv = ssl_stream_cast(stream); + + switch (wait) { + case STREAM_CONNECT: + if (stream_connect(stream) != EAGAIN) { + poll_immediate_wake(); + } else { + switch (sslv->state) { + case STATE_TCP_CONNECTING: + poll_fd_wait(sslv->fd, POLLOUT); + break; + + case STATE_SSL_CONNECTING: + /* ssl_connect() called SSL_accept() or SSL_connect(), which + * set up the status that we test here. */ + poll_fd_wait(sslv->fd, + want_to_poll_events(SSL_want(sslv->ssl))); + break; + + default: + NOT_REACHED(); + } + } + break; + + case STREAM_RECV: + if (sslv->rx_want != SSL_NOTHING) { + poll_fd_wait(sslv->fd, want_to_poll_events(sslv->rx_want)); + } else { + poll_immediate_wake(); + } + break; + + case STREAM_SEND: + if (!sslv->txbuf) { + /* We have room in our tx queue. */ + poll_immediate_wake(); + } else { + /* stream_run_wait() will do the right thing; don't bother with + * redundancy. */ + } + break; + + default: + NOT_REACHED(); + } +} + +struct stream_class ssl_stream_class = { + "ssl", /* name */ + ssl_open, /* open */ + ssl_close, /* close */ + ssl_connect, /* connect */ + ssl_recv, /* recv */ + ssl_send, /* send */ + ssl_run, /* run */ + ssl_run_wait, /* run_wait */ + ssl_wait, /* wait */ +}; + +/* Passive SSL. */ + +struct pssl_pstream +{ + struct pstream pstream; + int fd; +}; + +struct pstream_class pssl_pstream_class; + +static struct pssl_pstream * +pssl_pstream_cast(struct pstream *pstream) +{ + pstream_assert_class(pstream, &pssl_pstream_class); + return CONTAINER_OF(pstream, struct pssl_pstream, pstream); +} + +static int - pssl_open(const char *name UNUSED, char *suffix, struct pstream **pstreamp) ++pssl_open(const char *name OVS_UNUSED, char *suffix, struct pstream **pstreamp) +{ + struct pssl_pstream *pssl; + struct sockaddr_in sin; + char bound_name[128]; + int retval; + int fd; + + retval = ssl_init(); + if (retval) { + return retval; + } + + fd = inet_open_passive(SOCK_STREAM, suffix, OFP_SSL_PORT, &sin); + if (fd < 0) { + return -fd; + } + sprintf(bound_name, "pssl:%"PRIu16":"IP_FMT, + ntohs(sin.sin_port), IP_ARGS(&sin.sin_addr.s_addr)); + + pssl = xmalloc(sizeof *pssl); + pstream_init(&pssl->pstream, &pssl_pstream_class, bound_name); + pssl->fd = fd; + *pstreamp = &pssl->pstream; + return 0; +} + +static void +pssl_close(struct pstream *pstream) +{ + struct pssl_pstream *pssl = pssl_pstream_cast(pstream); + close(pssl->fd); + free(pssl); +} + +static int +pssl_accept(struct pstream *pstream, struct stream **new_streamp) +{ + struct pssl_pstream *pssl = pssl_pstream_cast(pstream); + struct sockaddr_in sin; + socklen_t sin_len = sizeof sin; + char name[128]; + int new_fd; + int error; + + new_fd = accept(pssl->fd, &sin, &sin_len); + if (new_fd < 0) { + int error = errno; + if (error != EAGAIN) { + VLOG_DBG_RL(&rl, "accept: %s", strerror(error)); + } + return error; + } + + error = set_nonblocking(new_fd); + if (error) { + close(new_fd); + return error; + } + + sprintf(name, "ssl:"IP_FMT, IP_ARGS(&sin.sin_addr)); + if (sin.sin_port != htons(OFP_SSL_PORT)) { + sprintf(strchr(name, '\0'), ":%"PRIu16, ntohs(sin.sin_port)); + } + return new_ssl_stream(name, new_fd, SERVER, STATE_SSL_CONNECTING, &sin, + new_streamp); +} + +static void +pssl_wait(struct pstream *pstream) +{ + struct pssl_pstream *pssl = pssl_pstream_cast(pstream); + poll_fd_wait(pssl->fd, POLLIN); +} + +struct pstream_class pssl_pstream_class = { + "pssl", + pssl_open, + pssl_close, + pssl_accept, + pssl_wait, +}; + +/* + * Returns true if OpenSSL error is WANT_READ or WANT_WRITE, indicating that + * OpenSSL is requesting that we call it back when the socket is ready for read + * or writing, respectively. + */ +static bool +ssl_wants_io(int ssl_error) +{ + return (ssl_error == SSL_ERROR_WANT_WRITE + || ssl_error == SSL_ERROR_WANT_READ); +} + +static int +ssl_init(void) +{ + static int init_status = -1; + if (init_status < 0) { + init_status = do_ssl_init(); + assert(init_status >= 0); + } + return init_status; +} + +static int +do_ssl_init(void) +{ + SSL_METHOD *method; + + SSL_library_init(); + SSL_load_error_strings(); + + method = TLSv1_method(); + if (method == NULL) { + VLOG_ERR("TLSv1_method: %s", ERR_error_string(ERR_get_error(), NULL)); + return ENOPROTOOPT; + } + + ctx = SSL_CTX_new(method); + if (ctx == NULL) { + VLOG_ERR("SSL_CTX_new: %s", ERR_error_string(ERR_get_error(), NULL)); + return ENOPROTOOPT; + } + SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); + SSL_CTX_set_tmp_dh_callback(ctx, tmp_dh_callback); + SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE); + SSL_CTX_set_mode(ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, + NULL); + + return 0; +} + +static DH * - tmp_dh_callback(SSL *ssl UNUSED, int is_export UNUSED, int keylength) ++tmp_dh_callback(SSL *ssl OVS_UNUSED, int is_export OVS_UNUSED, int keylength) +{ + struct dh { + int keylength; + DH *dh; + DH *(*constructor)(void); + }; + + static struct dh dh_table[] = { + {1024, NULL, get_dh1024}, + {2048, NULL, get_dh2048}, + {4096, NULL, get_dh4096}, + }; + + struct dh *dh; + + for (dh = dh_table; dh < &dh_table[ARRAY_SIZE(dh_table)]; dh++) { + if (dh->keylength == keylength) { + if (!dh->dh) { + dh->dh = dh->constructor(); + if (!dh->dh) { + ovs_fatal(ENOMEM, "out of memory constructing " + "Diffie-Hellman parameters"); + } + } + return dh->dh; + } + } + VLOG_ERR_RL(&rl, "no Diffie-Hellman parameters for key length %d", + keylength); + return NULL; +} + +/* Returns true if SSL is at least partially configured. */ +bool +stream_ssl_is_configured(void) +{ + return has_private_key || has_certificate || has_ca_cert; +} + +void +stream_ssl_set_private_key_file(const char *file_name) +{ + if (ssl_init()) { + return; + } + if (SSL_CTX_use_PrivateKey_file(ctx, file_name, SSL_FILETYPE_PEM) != 1) { + VLOG_ERR("SSL_use_PrivateKey_file: %s", + ERR_error_string(ERR_get_error(), NULL)); + return; + } + has_private_key = true; +} + +void +stream_ssl_set_certificate_file(const char *file_name) +{ + if (ssl_init()) { + return; + } + if (SSL_CTX_use_certificate_chain_file(ctx, file_name) != 1) { + VLOG_ERR("SSL_use_certificate_file: %s", + ERR_error_string(ERR_get_error(), NULL)); + return; + } + has_certificate = true; +} + +/* Reads the X509 certificate or certificates in file 'file_name'. On success, + * stores the address of the first element in an array of pointers to + * certificates in '*certs' and the number of certificates in the array in + * '*n_certs', and returns 0. On failure, stores a null pointer in '*certs', 0 + * in '*n_certs', and returns a positive errno value. + * + * The caller is responsible for freeing '*certs'. */ +static int +read_cert_file(const char *file_name, X509 ***certs, size_t *n_certs) +{ + FILE *file; + size_t allocated_certs = 0; + + *certs = NULL; + *n_certs = 0; + + file = fopen(file_name, "r"); + if (!file) { + VLOG_ERR("failed to open %s for reading: %s", + file_name, strerror(errno)); + return errno; + } + + for (;;) { + X509 *certificate; + int c; + + /* Read certificate from file. */ + certificate = PEM_read_X509(file, NULL, NULL, NULL); + if (!certificate) { + size_t i; + + VLOG_ERR("PEM_read_X509 failed reading %s: %s", + file_name, ERR_error_string(ERR_get_error(), NULL)); + for (i = 0; i < *n_certs; i++) { + X509_free((*certs)[i]); + } + free(*certs); + *certs = NULL; + *n_certs = 0; + return EIO; + } + + /* Add certificate to array. */ + if (*n_certs >= allocated_certs) { + *certs = x2nrealloc(*certs, &allocated_certs, sizeof **certs); + } + (*certs)[(*n_certs)++] = certificate; + + /* Are there additional certificates in the file? */ + do { + c = getc(file); + } while (isspace(c)); + if (c == EOF) { + break; + } + ungetc(c, file); + } + fclose(file); + return 0; +} + + +/* Sets 'file_name' as the name of a file containing one or more X509 + * certificates to send to the peer. Typical use in OpenFlow is to send the CA + * certificate to the peer, which enables a switch to pick up the controller's + * CA certificate on its first connection. */ +void +stream_ssl_set_peer_ca_cert_file(const char *file_name) +{ + X509 **certs; + size_t n_certs; + size_t i; + + if (ssl_init()) { + return; + } + + if (!read_cert_file(file_name, &certs, &n_certs)) { + for (i = 0; i < n_certs; i++) { + if (SSL_CTX_add_extra_chain_cert(ctx, certs[i]) != 1) { + VLOG_ERR("SSL_CTX_add_extra_chain_cert: %s", + ERR_error_string(ERR_get_error(), NULL)); + } + } + free(certs); + } +} + +/* Logs fingerprint of CA certificate 'cert' obtained from 'file_name'. */ +static void +log_ca_cert(const char *file_name, X509 *cert) +{ + unsigned char digest[EVP_MAX_MD_SIZE]; + unsigned int n_bytes; + struct ds fp; + char *subject; + + ds_init(&fp); + if (!X509_digest(cert, EVP_sha1(), digest, &n_bytes)) { + ds_put_cstr(&fp, ""); + } else { + unsigned int i; + for (i = 0; i < n_bytes; i++) { + if (i) { + ds_put_char(&fp, ':'); + } + ds_put_format(&fp, "%02hhx", digest[i]); + } + } + subject = X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0); + VLOG_INFO("Trusting CA cert from %s (%s) (fingerprint %s)", file_name, + subject ? subject : "", ds_cstr(&fp)); + free(subject); + ds_destroy(&fp); +} + +/* Sets 'file_name' as the name of the file from which to read the CA + * certificate used to verify the peer within SSL connections. If 'bootstrap' + * is false, the file must exist. If 'bootstrap' is false, then the file is + * read if it is exists; if it does not, then it will be created from the CA + * certificate received from the peer on the first SSL connection. */ +void +stream_ssl_set_ca_cert_file(const char *file_name, bool bootstrap) +{ + X509 **certs; + size_t n_certs; + struct stat s; + + if (ssl_init()) { + return; + } + + if (bootstrap && stat(file_name, &s) && errno == ENOENT) { + bootstrap_ca_cert = true; + ca_cert_file = xstrdup(file_name); + } else if (!read_cert_file(file_name, &certs, &n_certs)) { + size_t i; + + /* Set up list of CAs that the server will accept from the client. */ + for (i = 0; i < n_certs; i++) { + /* SSL_CTX_add_client_CA makes a copy of the relevant data. */ + if (SSL_CTX_add_client_CA(ctx, certs[i]) != 1) { + VLOG_ERR("failed to add client certificate %d from %s: %s", + i, file_name, + ERR_error_string(ERR_get_error(), NULL)); + } else { + log_ca_cert(file_name, certs[i]); + } + X509_free(certs[i]); + } + free(certs); + + /* Set up CAs for OpenSSL to trust in verifying the peer's + * certificate. */ + if (SSL_CTX_load_verify_locations(ctx, file_name, NULL) != 1) { + VLOG_ERR("SSL_CTX_load_verify_locations: %s", + ERR_error_string(ERR_get_error(), NULL)); + return; + } + + has_ca_cert = true; + } +} diff --combined lib/stream-tcp.c index e690e9c56,000000000..a9bcaeb47 mode 100644,000000..100644 --- a/lib/stream-tcp.c +++ b/lib/stream-tcp.c @@@ -1,143 -1,0 +1,143 @@@ +/* + * Copyright (c) 2008, 2009, 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 "stream.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "packets.h" +#include "socket-util.h" +#include "util.h" +#include "stream-provider.h" +#include "stream-fd.h" + +#include "vlog.h" +#define THIS_MODULE VLM_stream_tcp + +/* Active TCP. */ + +static int +new_tcp_stream(const char *name, int fd, int connect_status, + const struct sockaddr_in *remote, struct stream **streamp) +{ + struct sockaddr_in local; + socklen_t local_len = sizeof local; + int on = 1; + int retval; + + /* Get the local IP and port information */ + retval = getsockname(fd, (struct sockaddr *)&local, &local_len); + if (retval) { + memset(&local, 0, sizeof local); + } + + retval = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof on); + if (retval) { + VLOG_ERR("%s: setsockopt(TCP_NODELAY): %s", name, strerror(errno)); + close(fd); + return errno; + } + + retval = new_fd_stream(name, fd, connect_status, NULL, streamp); + if (!retval) { + struct stream *stream = *streamp; + stream_set_remote_ip(stream, remote->sin_addr.s_addr); + stream_set_remote_port(stream, remote->sin_port); + stream_set_local_ip(stream, local.sin_addr.s_addr); + stream_set_local_port(stream, local.sin_port); + } + return retval; +} + +static int +tcp_open(const char *name, char *suffix, struct stream **streamp) +{ + struct sockaddr_in sin; + int fd, error; + + error = inet_open_active(SOCK_STREAM, suffix, 0, &sin, &fd); + if (fd >= 0) { + return new_tcp_stream(name, fd, error, &sin, streamp); + } else { + VLOG_ERR("%s: connect: %s", name, strerror(error)); + return error; + } +} + +struct stream_class tcp_stream_class = { + "tcp", /* name */ + tcp_open, /* open */ + NULL, /* close */ + NULL, /* connect */ + NULL, /* recv */ + NULL, /* send */ + NULL, /* run */ + NULL, /* run_wait */ + NULL, /* wait */ +}; + +/* Passive TCP. */ + +static int ptcp_accept(int fd, const struct sockaddr *sa, size_t sa_len, + struct stream **streamp); + +static int - ptcp_open(const char *name UNUSED, char *suffix, struct pstream **pstreamp) ++ptcp_open(const char *name OVS_UNUSED, char *suffix, struct pstream **pstreamp) +{ + struct sockaddr_in sin; + char bound_name[128]; + int fd; + + fd = inet_open_passive(SOCK_STREAM, suffix, -1, &sin); + if (fd < 0) { + return -fd; + } + + sprintf(bound_name, "ptcp:%"PRIu16":"IP_FMT, + ntohs(sin.sin_port), IP_ARGS(&sin.sin_addr.s_addr)); + return new_fd_pstream(bound_name, fd, ptcp_accept, NULL, pstreamp); +} + +static int +ptcp_accept(int fd, const struct sockaddr *sa, size_t sa_len, + struct stream **streamp) +{ + const struct sockaddr_in *sin = (const struct sockaddr_in *) sa; + char name[128]; + + if (sa_len == sizeof(struct sockaddr_in) && sin->sin_family == AF_INET) { + sprintf(name, "tcp:"IP_FMT, IP_ARGS(&sin->sin_addr)); + sprintf(strchr(name, '\0'), ":%"PRIu16, ntohs(sin->sin_port)); + } else { + strcpy(name, "tcp"); + } + return new_tcp_stream(name, fd, 0, sin, streamp); +} + +struct pstream_class ptcp_pstream_class = { + "ptcp", + ptcp_open, + NULL, + NULL, + NULL +}; + diff --combined lib/stream-unix.c index 6ce7790b5,000000000..33f566b37 mode 100644,000000..100644 --- a/lib/stream-unix.c +++ b/lib/stream-unix.c @@@ -1,127 -1,0 +1,128 @@@ +/* - * Copyright (c) 2008, 2009 Nicira Networks. ++ * Copyright (c) 2008, 2009, 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 "stream.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "packets.h" +#include "poll-loop.h" +#include "socket-util.h" +#include "util.h" +#include "stream-provider.h" +#include "stream-fd.h" + +#include "vlog.h" +#define THIS_MODULE VLM_stream_unix + +/* Active UNIX socket. */ + +/* Number of unix sockets created so far, to ensure binding path uniqueness. */ +static int n_unix_sockets; + +static int +unix_open(const char *name, char *suffix, struct stream **streamp) +{ + const char *connect_path = suffix; + char *bind_path; + int fd; + + bind_path = xasprintf("/tmp/stream-unix.%ld.%d", + (long int) getpid(), n_unix_sockets++); + fd = make_unix_socket(SOCK_STREAM, true, false, bind_path, connect_path); + if (fd < 0) { + VLOG_ERR("%s: connection to %s failed: %s", + bind_path, connect_path, strerror(-fd)); + free(bind_path); + return -fd; + } + + return new_fd_stream(name, fd, check_connection_completion(fd), + bind_path, streamp); +} + +struct stream_class unix_stream_class = { + "unix", /* name */ + unix_open, /* open */ + NULL, /* close */ + NULL, /* connect */ + NULL, /* recv */ + NULL, /* send */ + NULL, /* run */ + NULL, /* run_wait */ + NULL, /* wait */ +}; + +/* Passive UNIX socket. */ + +static int punix_accept(int fd, const struct sockaddr *sa, size_t sa_len, + struct stream **streamp); + +static int - punix_open(const char *name UNUSED, char *suffix, struct pstream **pstreamp) ++punix_open(const char *name OVS_UNUSED, char *suffix, ++ struct pstream **pstreamp) +{ + int fd, error; + + fd = make_unix_socket(SOCK_STREAM, true, true, suffix, NULL); + if (fd < 0) { + VLOG_ERR("%s: binding failed: %s", suffix, strerror(errno)); + return errno; + } + + if (listen(fd, 10) < 0) { + error = errno; + VLOG_ERR("%s: listen: %s", name, strerror(error)); + close(fd); + return error; + } + + return new_fd_pstream("punix", fd, punix_accept, + xstrdup(suffix), pstreamp); +} + +static int +punix_accept(int fd, const struct sockaddr *sa, size_t sa_len, + struct stream **streamp) +{ + const struct sockaddr_un *sun = (const struct sockaddr_un *) sa; + int name_len = get_unix_name_len(sa_len); + char name[128]; + + if (name_len > 0) { + snprintf(name, sizeof name, "unix:%.*s", name_len, sun->sun_path); + } else { + strcpy(name, "unix"); + } + return new_fd_stream(name, fd, 0, NULL, streamp); +} + +struct pstream_class punix_pstream_class = { + "punix", + punix_open, + NULL, + NULL, + NULL +}; + diff --combined lib/stream.c index dcd8da585,000000000..db6ec61bb mode 100644,000000..100644 --- a/lib/stream.c +++ b/lib/stream.c @@@ -1,563 -1,0 +1,563 @@@ +/* + * Copyright (c) 2008, 2009, 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 "stream-provider.h" +#include +#include +#include +#include +#include +#include +#include +#include "coverage.h" +#include "dynamic-string.h" +#include "flow.h" +#include "ofp-print.h" +#include "ofpbuf.h" +#include "openflow/nicira-ext.h" +#include "openflow/openflow.h" +#include "packets.h" +#include "poll-loop.h" +#include "random.h" +#include "util.h" + +#define THIS_MODULE VLM_stream +#include "vlog.h" + +/* State of an active stream.*/ +enum stream_state { + SCS_CONNECTING, /* Underlying stream is not connected. */ + SCS_CONNECTED, /* Connection established. */ + SCS_DISCONNECTED /* Connection failed or connection closed. */ +}; + +static struct stream_class *stream_classes[] = { + &tcp_stream_class, + &unix_stream_class, +#ifdef HAVE_OPENSSL + &ssl_stream_class, +#endif +}; + +static struct pstream_class *pstream_classes[] = { + &ptcp_pstream_class, + &punix_pstream_class, +#ifdef HAVE_OPENSSL + &pssl_pstream_class, +#endif +}; + +/* Check the validity of the stream class structures. */ +static void +check_stream_classes(void) +{ +#ifndef NDEBUG + size_t i; + + for (i = 0; i < ARRAY_SIZE(stream_classes); i++) { + struct stream_class *class = stream_classes[i]; + assert(class->name != NULL); + assert(class->open != NULL); + if (class->close || class->recv || class->send || class->run + || class->run_wait || class->wait) { + assert(class->close != NULL); + assert(class->recv != NULL); + assert(class->send != NULL); + assert(class->wait != NULL); + } else { + /* This class delegates to another one. */ + } + } + + for (i = 0; i < ARRAY_SIZE(pstream_classes); i++) { + struct pstream_class *class = pstream_classes[i]; + assert(class->name != NULL); + assert(class->listen != NULL); + if (class->close || class->accept || class->wait) { + assert(class->close != NULL); + assert(class->accept != NULL); + assert(class->wait != NULL); + } else { + /* This class delegates to another one. */ + } + } +#endif +} + +/* Prints information on active (if 'active') and passive (if 'passive') + * connection methods supported by the stream. */ +void +stream_usage(const char *name, bool active, bool passive, - bool bootstrap UNUSED) ++ bool bootstrap OVS_UNUSED) +{ + /* Really this should be implemented via callbacks into the stream + * providers, but that seems too heavy-weight to bother with at the + * moment. */ + + printf("\n"); + if (active) { + printf("Active %s connection methods:\n", name); + printf(" tcp:IP:PORT " + "PORT at remote IP\n"); +#ifdef HAVE_OPENSSL + printf(" ssl:IP:PORT " + "SSL PORT at remote IP\n"); +#endif + printf(" unix:FILE " + "Unix domain socket named FILE\n"); + } + + if (passive) { + printf("Passive %s connection methods:\n", name); + printf(" ptcp:PORT[:IP] " + "listen to TCP PORT on IP\n"); +#ifdef HAVE_OPENSSL + printf(" pssl:PORT[:IP] " + "listen for SSL on PORT on IP\n"); +#endif + printf(" punix:FILE " + "listen on Unix domain socket FILE\n"); + } + +#ifdef HAVE_OPENSSL + printf("PKI configuration (required to use SSL):\n" + " -p, --private-key=FILE file with private key\n" + " -c, --certificate=FILE file with certificate for private key\n" + " -C, --ca-cert=FILE file with peer CA certificate\n"); + if (bootstrap) { + printf(" --bootstrap-ca-cert=FILE file with peer CA certificate " + "to read or create\n"); + } +#endif +} + +/* Attempts to connect a stream to a remote peer. 'name' is a connection name + * in the form "TYPE:ARGS", where TYPE is an active stream class's name and + * ARGS are stream class-specific. + * + * Returns 0 if successful, otherwise a positive errno value. If successful, + * stores a pointer to the new connection in '*streamp', otherwise a null + * pointer. */ +int +stream_open(const char *name, struct stream **streamp) +{ + size_t prefix_len; + size_t i; + + COVERAGE_INC(stream_open); + check_stream_classes(); + + *streamp = NULL; + prefix_len = strcspn(name, ":"); + if (prefix_len == strlen(name)) { + return EAFNOSUPPORT; + } + for (i = 0; i < ARRAY_SIZE(stream_classes); i++) { + struct stream_class *class = stream_classes[i]; + if (strlen(class->name) == prefix_len + && !memcmp(class->name, name, prefix_len)) { + struct stream *stream; + char *suffix_copy = xstrdup(name + prefix_len + 1); + int retval = class->open(name, suffix_copy, &stream); + free(suffix_copy); + if (!retval) { + assert(stream->state != SCS_CONNECTING + || stream->class->connect); + *streamp = stream; + } + return retval; + } + } + return EAFNOSUPPORT; +} + +int +stream_open_block(const char *name, struct stream **streamp) +{ + struct stream *stream; + int error; + + error = stream_open(name, &stream); + while (error == EAGAIN) { + stream_run(stream); + stream_run_wait(stream); + stream_connect_wait(stream); + poll_block(); + error = stream_connect(stream); + assert(error != EINPROGRESS); + } + if (error) { + stream_close(stream); + *streamp = NULL; + } else { + *streamp = stream; + } + return error; +} + +/* Closes 'stream'. */ +void +stream_close(struct stream *stream) +{ + if (stream != NULL) { + char *name = stream->name; + (stream->class->close)(stream); + free(name); + } +} + +/* Returns the name of 'stream', that is, the string passed to + * stream_open(). */ +const char * +stream_get_name(const struct stream *stream) +{ + return stream ? stream->name : "(null)"; +} + +/* Returns the IP address of the peer, or 0 if the peer is not connected over + * an IP-based protocol or if its IP address is not yet known. */ +uint32_t +stream_get_remote_ip(const struct stream *stream) +{ + return stream->remote_ip; +} + +/* Returns the transport port of the peer, or 0 if the connection does not + * contain a port or if the port is not yet known. */ +uint16_t +stream_get_remote_port(const struct stream *stream) +{ + return stream->remote_port; +} + +/* Returns the IP address used to connect to the peer, or 0 if the connection + * is not an IP-based protocol or if its IP address is not yet known. */ +uint32_t +stream_get_local_ip(const struct stream *stream) +{ + return stream->local_ip; +} + +/* Returns the transport port used to connect to the peer, or 0 if the + * connection does not contain a port or if the port is not yet known. */ +uint16_t +stream_get_local_port(const struct stream *stream) +{ + return stream->local_port; +} + +static void +scs_connecting(struct stream *stream) +{ + int retval = (stream->class->connect)(stream); + assert(retval != EINPROGRESS); + if (!retval) { + stream->state = SCS_CONNECTED; + } else if (retval != EAGAIN) { + stream->state = SCS_DISCONNECTED; + stream->error = retval; + } +} + +/* Tries to complete the connection on 'stream', which must be an active + * stream. If 'stream''s connection is complete, returns 0 if the connection + * was successful or a positive errno value if it failed. If the + * connection is still in progress, returns EAGAIN. */ +int +stream_connect(struct stream *stream) +{ + enum stream_state last_state; + + do { + last_state = stream->state; + switch (stream->state) { + case SCS_CONNECTING: + scs_connecting(stream); + break; + + case SCS_CONNECTED: + return 0; + + case SCS_DISCONNECTED: + return stream->error; + + default: + NOT_REACHED(); + } + } while (stream->state != last_state); + + return EAGAIN; +} + +/* Tries to receive up to 'n' bytes from 'stream' into 'buffer', and returns: + * + * - If successful, the number of bytes received (between 1 and 'n'). + * + * - On error, a negative errno value. + * + * - 0, if the connection has been closed in the normal fashion, or if 'n' + * is zero. + * + * The recv function will not block waiting for a packet to arrive. If no + * data have been received, it returns -EAGAIN immediately. */ +int +stream_recv(struct stream *stream, void *buffer, size_t n) +{ + int retval = stream_connect(stream); + return (retval ? -retval + : n == 0 ? 0 + : (stream->class->recv)(stream, buffer, n)); +} + +/* Tries to send up to 'n' bytes of 'buffer' on 'stream', and returns: + * + * - If successful, the number of bytes sent (between 1 and 'n'). 0 is + * only a valid return value if 'n' is 0. + * + * - On error, a negative errno value. + * + * The send function will not block. If no bytes can be immediately accepted + * for transmission, it returns -EAGAIN immediately. */ +int +stream_send(struct stream *stream, const void *buffer, size_t n) +{ + int retval = stream_connect(stream); + return (retval ? -retval + : n == 0 ? 0 + : (stream->class->send)(stream, buffer, n)); +} + +/* Allows 'stream' to perform maintenance activities, such as flushing + * output buffers. */ +void +stream_run(struct stream *stream) +{ + if (stream->class->run) { + (stream->class->run)(stream); + } +} + +/* Arranges for the poll loop to wake up when 'stream' needs to perform + * maintenance activities. */ +void +stream_run_wait(struct stream *stream) +{ + if (stream->class->run_wait) { + (stream->class->run_wait)(stream); + } +} + +/* Arranges for the poll loop to wake up when 'stream' is ready to take an + * action of the given 'type'. */ +void +stream_wait(struct stream *stream, enum stream_wait_type wait) +{ + assert(wait == STREAM_CONNECT || wait == STREAM_RECV + || wait == STREAM_SEND); + + switch (stream->state) { + case SCS_CONNECTING: + wait = STREAM_CONNECT; + break; + + case SCS_DISCONNECTED: + poll_immediate_wake(); + return; + } + (stream->class->wait)(stream, wait); +} + +void +stream_connect_wait(struct stream *stream) +{ + stream_wait(stream, STREAM_CONNECT); +} + +void +stream_recv_wait(struct stream *stream) +{ + stream_wait(stream, STREAM_RECV); +} + +void +stream_send_wait(struct stream *stream) +{ + stream_wait(stream, STREAM_SEND); +} + +/* Attempts to start listening for remote stream connections. 'name' is a + * connection name in the form "TYPE:ARGS", where TYPE is an passive stream + * class's name and ARGS are stream class-specific. + * + * Returns 0 if successful, otherwise a positive errno value. If successful, + * stores a pointer to the new connection in '*pstreamp', otherwise a null + * pointer. */ +int +pstream_open(const char *name, struct pstream **pstreamp) +{ + size_t prefix_len; + size_t i; + + check_stream_classes(); + + *pstreamp = NULL; + prefix_len = strcspn(name, ":"); + if (prefix_len == strlen(name)) { + return EAFNOSUPPORT; + } + for (i = 0; i < ARRAY_SIZE(pstream_classes); i++) { + struct pstream_class *class = pstream_classes[i]; + if (strlen(class->name) == prefix_len + && !memcmp(class->name, name, prefix_len)) { + char *suffix_copy = xstrdup(name + prefix_len + 1); + int retval = class->listen(name, suffix_copy, pstreamp); + free(suffix_copy); + if (retval) { + *pstreamp = NULL; + } + return retval; + } + } + return EAFNOSUPPORT; +} + +/* Returns the name that was used to open 'pstream'. The caller must not + * modify or free the name. */ +const char * +pstream_get_name(const struct pstream *pstream) +{ + return pstream->name; +} + +/* Closes 'pstream'. */ +void +pstream_close(struct pstream *pstream) +{ + if (pstream != NULL) { + char *name = pstream->name; + (pstream->class->close)(pstream); + free(name); + } +} + +/* Tries to accept a new connection on 'pstream'. If successful, stores the + * new connection in '*new_stream' and returns 0. Otherwise, returns a + * positive errno value. + * + * pstream_accept() will not block waiting for a connection. If no connection + * is ready to be accepted, it returns EAGAIN immediately. */ +int +pstream_accept(struct pstream *pstream, struct stream **new_stream) +{ + int retval = (pstream->class->accept)(pstream, new_stream); + if (retval) { + *new_stream = NULL; + } else { + assert((*new_stream)->state != SCS_CONNECTING + || (*new_stream)->class->connect); + } + return retval; +} + +/* Tries to accept a new connection on 'pstream'. If successful, stores the + * new connection in '*new_stream' and returns 0. Otherwise, returns a + * positive errno value. + * + * pstream_accept_block() blocks until a connection is ready or until an error + * occurs. It will not return EAGAIN. */ +int +pstream_accept_block(struct pstream *pstream, struct stream **new_stream) +{ + int error; + + while ((error = pstream_accept(pstream, new_stream)) == EAGAIN) { + pstream_wait(pstream); + poll_block(); + } + if (error) { + *new_stream = NULL; + } + return error; +} + +void +pstream_wait(struct pstream *pstream) +{ + (pstream->class->wait)(pstream); +} + +/* Initializes 'stream' as a new stream named 'name', implemented via 'class'. + * The initial connection status, supplied as 'connect_status', is interpreted + * as follows: + * + * - 0: 'stream' is connected. Its 'send' and 'recv' functions may be + * called in the normal fashion. + * + * - EAGAIN: 'stream' is trying to complete a connection. Its 'connect' + * function should be called to complete the connection. + * + * - Other positive errno values indicate that the connection failed with + * the specified error. + * + * After calling this function, stream_close() must be used to destroy + * 'stream', otherwise resources will be leaked. + * + * The caller retains ownership of 'name'. */ +void +stream_init(struct stream *stream, struct stream_class *class, + int connect_status, const char *name) +{ + stream->class = class; + stream->state = (connect_status == EAGAIN ? SCS_CONNECTING + : !connect_status ? SCS_CONNECTED + : SCS_DISCONNECTED); + stream->error = connect_status; + stream->name = xstrdup(name); + assert(stream->state != SCS_CONNECTING || class->connect); +} + +void +stream_set_remote_ip(struct stream *stream, uint32_t ip) +{ + stream->remote_ip = ip; +} + +void +stream_set_remote_port(struct stream *stream, uint16_t port) +{ + stream->remote_port = port; +} + +void +stream_set_local_ip(struct stream *stream, uint32_t ip) +{ + stream->local_ip = ip; +} + +void +stream_set_local_port(struct stream *stream, uint16_t port) +{ + stream->local_port = port; +} + +void +pstream_init(struct pstream *pstream, struct pstream_class *class, + const char *name) +{ + pstream->class = class; + pstream->name = xstrdup(name); +} diff --combined lib/timeval.h index 5ba903e3a,e94016262..89abe8036 --- a/lib/timeval.h +++ b/lib/timeval.h @@@ -1,5 -1,5 +1,5 @@@ /* -- * Copyright (c) 2008, 2009 Nicira Networks. ++ * Copyright (c) 2008, 2009, 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. @@@ -21,6 -21,10 +21,10 @@@ #include "type-props.h" #include "util.h" + #ifdef __cplusplus + extern "C" { + #endif + struct pollfd; struct timeval; @@@ -41,8 -45,6 +45,8 @@@ BUILD_ASSERT_DECL(TYPE_IS_SIGNED(time_t #define TIME_UPDATE_INTERVAL 100 void time_init(void); +void time_disable_restart(void); +void time_enable_restart(void); void time_postfork(void); void time_refresh(void); time_t time_now(void); @@@ -51,6 -53,8 +55,10 @@@ void time_timeval(struct timeval *) void time_alarm(unsigned int secs); int time_poll(struct pollfd *, int n_pollfds, int timeout); +long long int timeval_to_msec(const struct timeval *); + + #ifdef __cplusplus + } + #endif + #endif /* timeval.h */ diff --combined lib/unixctl.c index e648cc865,43a174ccb..f710ffd60 --- a/lib/unixctl.c +++ b/lib/unixctl.c @@@ -44,8 -44,7 +44,8 @@@ #include "vlog.h" struct unixctl_command { - void (*cb)(struct unixctl_conn *, const char *args); + unixctl_cb_func *cb; + void *aux; }; struct unixctl_conn { @@@ -77,8 -76,7 +77,8 @@@ static struct vlog_rate_limit rl = VLOG static struct shash commands = SHASH_INITIALIZER(&commands); static void - unixctl_help(struct unixctl_conn *conn, const char *args UNUSED, - void *aux UNUSED) -unixctl_help(struct unixctl_conn *conn, const char *args OVS_UNUSED) ++unixctl_help(struct unixctl_conn *conn, const char *args OVS_UNUSED, ++ void *aux OVS_UNUSED) { struct ds ds = DS_EMPTY_INITIALIZER; struct shash_node *node; @@@ -92,7 -90,8 +92,7 @@@ } void -unixctl_command_register(const char *name, - void (*cb)(struct unixctl_conn *, const char *args)) +unixctl_command_register(const char *name, unixctl_cb_func *cb, void *aux) { struct unixctl_command *command; @@@ -100,7 -99,6 +100,7 @@@ || shash_find_data(&commands, name) == cb); command = xmalloc(sizeof *command); command->cb = cb; + command->aux = aux; shash_add(&commands, name, command); } @@@ -180,7 -178,7 +180,7 @@@ unixctl_server_create(const char *path struct unixctl_server *server; int error; - unixctl_command_register("help", unixctl_help); + unixctl_command_register("help", unixctl_help, NULL); server = xmalloc(sizeof *server); list_init(&server->conns); @@@ -200,21 -198,22 +200,21 @@@ NULL); if (server->fd < 0) { error = -server->fd; - fprintf(stderr, "Could not initialize control socket %s (%s)\n", - server->path, strerror(error)); + ovs_error(error, "could not initialize control socket %s", + server->path); goto error; } if (chmod(server->path, S_IRUSR | S_IWUSR) < 0) { error = errno; - fprintf(stderr, "Failed to chmod control socket %s (%s)\n", - server->path, strerror(error)); + ovs_error(error, "failed to chmod control socket %s", server->path); goto error; } if (listen(server->fd, 10) < 0) { error = errno; - fprintf(stderr, "Failed to listen on control socket %s (%s)\n", - server->path, strerror(error)); + ovs_error(error, "Failed to listen on control socket %s", + server->path); goto error; } @@@ -283,7 -282,7 +283,7 @@@ process_command(struct unixctl_conn *co command = shash_find_data(&commands, name); if (command) { - command->cb(conn, args); + command->cb(conn, args, command->aux); } else { char *msg = xasprintf("\"%s\" is not a valid command", name); unixctl_command_reply(conn, 400, msg); diff --combined lib/util.c index e9284809e,f766d59e3..8f1892ed2 --- a/lib/util.c +++ b/lib/util.c @@@ -1,5 -1,5 +1,5 @@@ /* -- * Copyright (c) 2008, 2009 Nicira Networks. ++ * Copyright (c) 2008, 2009, 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. @@@ -42,12 -42,6 +42,12 @@@ xcalloc(size_t count, size_t size return p; } +void * +xzalloc(size_t size) +{ + return xcalloc(1, size); +} + void * xmalloc(size_t size) { @@@ -168,10 -162,8 +168,10 @@@ ovs_error(int err_no, const char *forma va_start(args, format); vfprintf(stderr, format, args); va_end(args); - if (err_no != 0) - fprintf(stderr, " (%s)", strerror(err_no)); + if (err_no != 0) { + fprintf(stderr, " (%s)", + err_no == EOF ? "end of file" : strerror(err_no)); + } putc('\n', stderr); errno = save_errno; @@@ -302,90 -294,3 +302,90 @@@ str_to_ullong(const char *s, int base, { return str_to_llong(s, base, (long long *) ull); } + +/* Converts floating-point string 's' into a double. If successful, stores + * the double in '*d' and returns true; on failure, stores 0 in '*d' and + * returns false. + * + * Underflow (e.g. "1e-9999") is not considered an error, but overflow + * (e.g. "1e9999)" is. */ +bool +str_to_double(const char *s, double *d) +{ + int save_errno = errno; + char *tail; + errno = 0; + *d = strtod(s, &tail); + if (errno == EINVAL || (errno == ERANGE && *d != 0) + || tail == s || *tail != '\0') { + errno = save_errno; + *d = 0; + return false; + } else { + errno = save_errno; + return true; + } +} + +/* Returns the value of 'c' as a hexadecimal digit. */ +int +hexit_value(int c) +{ + switch (c) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + return c - '0'; + + case 'a': case 'A': + return 0xa; + + case 'b': case 'B': + return 0xb; + + case 'c': case 'C': + return 0xc; + + case 'd': case 'D': + return 0xd; + + case 'e': case 'E': + return 0xe; + + case 'f': case 'F': + return 0xf; + } + + NOT_REACHED(); +} + +/* Returns the directory name portion of 'file_name' as a malloc()'d string, + * similar to the POSIX dirname() function but thread-safe. */ +char * +dir_name(const char *file_name) +{ + size_t len = strlen(file_name); + while (len > 0 && file_name[len - 1] == '/') { + len--; + } + while (len > 0 && file_name[len - 1] != '/') { + len--; + } + while (len > 0 && file_name[len - 1] == '/') { + len--; + } + if (!len) { + return xstrdup((file_name[0] == '/' + && file_name[1] == '/' + && file_name[2] != '/') ? "//" + : file_name[0] == '/' ? "/" + : "."); + } else { + return xmemdup0(file_name, len); + } +} + +/* Pass a value to this function if it is marked with + * __attribute__((warn_unused_result)) and you genuinely want to ignore + * its return value. (Note that every scalar type can be implicitly + * converted to bool.) */ - void ignore(bool x UNUSED) { } ++void ignore(bool x OVS_UNUSED) { } diff --combined lib/util.h index 0101bf7b5,962bad2f3..562f7e0e0 --- a/lib/util.h +++ b/lib/util.h @@@ -1,5 -1,5 +1,5 @@@ /* -- * Copyright (c) 2008, 2009 Nicira Networks. ++ * Copyright (c) 2008, 2009, 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. @@@ -98,7 -98,6 +98,7 @@@ void ovs_print_version(char *date, cha void out_of_memory(void) NO_RETURN; void *xmalloc(size_t) MALLOC_LIKE; void *xcalloc(size_t, size_t) MALLOC_LIKE; +void *xzalloc(size_t) MALLOC_LIKE; void *xrealloc(void *, size_t); void *xmemdup(const void *, size_t) MALLOC_LIKE; char *xmemdup0(const char *, size_t) MALLOC_LIKE; @@@ -121,14 -120,6 +121,14 @@@ bool str_to_uint(const char *, int base bool str_to_ulong(const char *, int base, unsigned long *); bool str_to_ullong(const char *, int base, unsigned long long *); +bool str_to_double(const char *, double *); + +int hexit_value(int c); + +char *dir_name(const char *file_name); + - void ignore(bool x UNUSED); ++void ignore(bool x OVS_UNUSED); + #ifdef __cplusplus } #endif diff --combined lib/vconn-stream.c index 594eded55,243d381f7..99eb21a89 --- a/lib/vconn-stream.c +++ b/lib/vconn-stream.c @@@ -15,6 -15,7 +15,6 @@@ */ #include -#include "vconn-stream.h" #include #include #include @@@ -28,7 -29,6 +28,7 @@@ #include "openflow/openflow.h" #include "poll-loop.h" #include "socket-util.h" +#include "stream.h" #include "util.h" #include "vconn-provider.h" #include "vconn.h" @@@ -38,92 -38,79 +38,92 @@@ /* Active stream socket vconn. */ -struct stream_vconn +struct vconn_stream { struct vconn vconn; - int fd; + struct stream *stream; struct ofpbuf *rxbuf; struct ofpbuf *txbuf; - struct poll_waiter *tx_waiter; - char *unlink_path; }; static struct vconn_class stream_vconn_class; static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 25); -static void stream_clear_txbuf(struct stream_vconn *); -static void maybe_unlink_and_free(char *path); +static void vconn_stream_clear_txbuf(struct vconn_stream *); +static int count_fields(const char *); -/* Creates a new vconn named 'name' that will send and receive data on 'fd' and - * stores a pointer to the vconn in '*vconnp'. Initial connection status - * 'connect_status' is interpreted as described for vconn_init(). - * - * When '*vconnp' is closed, then 'unlink_path' (if nonnull) will be passed to - * fatal_signal_unlink_file_now() and then freed with free(). - * - * Returns 0 if successful, otherwise a positive errno value. (The current - * implementation never fails.) */ -int -new_stream_vconn(const char *name, int fd, int connect_status, - char *unlink_path, struct vconn **vconnp) +static struct vconn * +vconn_stream_new(struct stream *stream, int connect_status) { - struct stream_vconn *s; + struct vconn_stream *s; s = xmalloc(sizeof *s); - vconn_init(&s->vconn, &stream_vconn_class, connect_status, name); - s->fd = fd; + vconn_init(&s->vconn, &stream_vconn_class, connect_status, + stream_get_name(stream)); + s->stream = stream; s->txbuf = NULL; - s->tx_waiter = NULL; s->rxbuf = NULL; - s->unlink_path = unlink_path; - *vconnp = &s->vconn; + return &s->vconn; +} + +/* Creates a new vconn that will send and receive data on a stream named 'name' + * and stores a pointer to the vconn in '*vconnp'. + * + * Returns 0 if successful, otherwise a positive errno value. */ +static int - vconn_stream_open(const char *name_, char *suffix UNUSED, ++vconn_stream_open(const char *name_, char *suffix OVS_UNUSED, + struct vconn **vconnp) +{ + struct stream *stream; + char *name; + int error; + + if (!strncmp(name_, "tcp:", 4) && count_fields(name_) < 3) { + name = xasprintf("%s:%d", name_, OFP_TCP_PORT); + } else if (!strncmp(name_, "ssl:", 4) && count_fields(name_) < 3) { + name = xasprintf("%s:%d", name_, OFP_SSL_PORT); + } else { + name = xstrdup(name_); + } + error = stream_open(name, &stream); + free(name); + + if (error && error != EAGAIN) { + return error; + } + + *vconnp = vconn_stream_new(stream, error); return 0; } -static struct stream_vconn * -stream_vconn_cast(struct vconn *vconn) +static struct vconn_stream * +vconn_stream_cast(struct vconn *vconn) { - vconn_assert_class(vconn, &stream_vconn_class); - return CONTAINER_OF(vconn, struct stream_vconn, vconn); + return CONTAINER_OF(vconn, struct vconn_stream, vconn); } static void -stream_close(struct vconn *vconn) +vconn_stream_close(struct vconn *vconn) { - struct stream_vconn *s = stream_vconn_cast(vconn); - poll_cancel(s->tx_waiter); - stream_clear_txbuf(s); + struct vconn_stream *s = vconn_stream_cast(vconn); + stream_close(s->stream); + vconn_stream_clear_txbuf(s); ofpbuf_delete(s->rxbuf); - close(s->fd); - maybe_unlink_and_free(s->unlink_path); free(s); } static int -stream_connect(struct vconn *vconn) +vconn_stream_connect(struct vconn *vconn) { - struct stream_vconn *s = stream_vconn_cast(vconn); - return check_connection_completion(s->fd); + struct vconn_stream *s = vconn_stream_cast(vconn); + return stream_connect(s->stream); } static int -stream_recv(struct vconn *vconn, struct ofpbuf **bufferp) +vconn_stream_recv(struct vconn *vconn, struct ofpbuf **bufferp) { - struct stream_vconn *s = stream_vconn_cast(vconn); + struct vconn_stream *s = vconn_stream_cast(vconn); struct ofpbuf *rx; size_t want_bytes; ssize_t retval; @@@ -153,7 -140,7 +153,7 @@@ again } ofpbuf_prealloc_tailroom(rx, want_bytes); - retval = read(s->fd, ofpbuf_tail(rx), want_bytes); + retval = stream_recv(s->stream, ofpbuf_tail(rx), want_bytes); if (retval > 0) { rx->size += retval; if (retval == want_bytes) { @@@ -174,244 -161,210 +174,244 @@@ return EOF; } } else { - return errno; + return -retval; } } static void -stream_clear_txbuf(struct stream_vconn *s) +vconn_stream_clear_txbuf(struct vconn_stream *s) { ofpbuf_delete(s->txbuf); s->txbuf = NULL; - s->tx_waiter = NULL; -} - -static void -stream_do_tx(int fd OVS_UNUSED, short int revents OVS_UNUSED, void *vconn_) -{ - struct vconn *vconn = vconn_; - struct stream_vconn *s = stream_vconn_cast(vconn); - ssize_t n = write(s->fd, s->txbuf->data, s->txbuf->size); - if (n < 0) { - if (errno != EAGAIN) { - VLOG_ERR_RL(&rl, "send: %s", strerror(errno)); - stream_clear_txbuf(s); - return; - } - } else if (n > 0) { - ofpbuf_pull(s->txbuf, n); - if (!s->txbuf->size) { - stream_clear_txbuf(s); - return; - } - } - s->tx_waiter = poll_fd_callback(s->fd, POLLOUT, stream_do_tx, vconn); } static int -stream_send(struct vconn *vconn, struct ofpbuf *buffer) +vconn_stream_send(struct vconn *vconn, struct ofpbuf *buffer) { - struct stream_vconn *s = stream_vconn_cast(vconn); + struct vconn_stream *s = vconn_stream_cast(vconn); ssize_t retval; if (s->txbuf) { return EAGAIN; } - retval = write(s->fd, buffer->data, buffer->size); + retval = stream_send(s->stream, buffer->data, buffer->size); if (retval == buffer->size) { ofpbuf_delete(buffer); return 0; - } else if (retval >= 0 || errno == EAGAIN) { + } else if (retval >= 0 || retval == -EAGAIN) { leak_checker_claim(buffer); s->txbuf = buffer; if (retval > 0) { ofpbuf_pull(buffer, retval); } - s->tx_waiter = poll_fd_callback(s->fd, POLLOUT, stream_do_tx, vconn); return 0; } else { - return errno; + return -retval; + } +} + +static void +vconn_stream_run(struct vconn *vconn) +{ + struct vconn_stream *s = vconn_stream_cast(vconn); + ssize_t retval; + + if (!s->txbuf) { + return; + } + + retval = stream_send(s->stream, s->txbuf->data, s->txbuf->size); + if (retval < 0) { + if (retval != -EAGAIN) { + VLOG_ERR_RL(&rl, "send: %s", strerror(-retval)); + vconn_stream_clear_txbuf(s); + return; + } + } else if (retval > 0) { + ofpbuf_pull(s->txbuf, retval); + if (!s->txbuf->size) { + vconn_stream_clear_txbuf(s); + return; + } + } +} + +static void +vconn_stream_run_wait(struct vconn *vconn) +{ + struct vconn_stream *s = vconn_stream_cast(vconn); + + if (s->txbuf) { + stream_send_wait(s->stream); } } static void -stream_wait(struct vconn *vconn, enum vconn_wait_type wait) +vconn_stream_wait(struct vconn *vconn, enum vconn_wait_type wait) { - struct stream_vconn *s = stream_vconn_cast(vconn); + struct vconn_stream *s = vconn_stream_cast(vconn); switch (wait) { case WAIT_CONNECT: - poll_fd_wait(s->fd, POLLOUT); + stream_connect_wait(s->stream); break; case WAIT_SEND: if (!s->txbuf) { - poll_fd_wait(s->fd, POLLOUT); + stream_send_wait(s->stream); } else { - /* Nothing to do: need to drain txbuf first. */ + /* Nothing to do: need to drain txbuf first. + * vconn_stream_run_wait() will arrange to wake up when there room + * to send data, so there's no point in calling poll_fd_wait() + * redundantly here. */ } break; case WAIT_RECV: - poll_fd_wait(s->fd, POLLIN); + stream_recv_wait(s->stream); break; default: NOT_REACHED(); } } - -static struct vconn_class stream_vconn_class = { - "stream", /* name */ - NULL, /* open */ - stream_close, /* close */ - stream_connect, /* connect */ - stream_recv, /* recv */ - stream_send, /* send */ - stream_wait, /* wait */ -}; /* Passive stream socket vconn. */ -struct pstream_pvconn +struct pvconn_pstream { struct pvconn pvconn; - int fd; - int (*accept_cb)(int fd, const struct sockaddr *, size_t sa_len, - struct vconn **); - char *unlink_path; + struct pstream *pstream; }; static struct pvconn_class pstream_pvconn_class; -static struct pstream_pvconn * -pstream_pvconn_cast(struct pvconn *pvconn) +static struct pvconn_pstream * +pvconn_pstream_cast(struct pvconn *pvconn) { - pvconn_assert_class(pvconn, &pstream_pvconn_class); - return CONTAINER_OF(pvconn, struct pstream_pvconn, pvconn); + return CONTAINER_OF(pvconn, struct pvconn_pstream, pvconn); } -/* Creates a new pvconn named 'name' that will accept new socket connections on - * 'fd' and stores a pointer to the vconn in '*pvconnp'. - * - * When a connection has been accepted, 'accept_cb' will be called with the new - * socket fd 'fd' and the remote address of the connection 'sa' and 'sa_len'. - * accept_cb must return 0 if the connection is successful, in which case it - * must initialize '*vconnp' to the new vconn, or a positive errno value on - * error. In either case accept_cb takes ownership of the 'fd' passed in. - * - * When '*pvconnp' is closed, then 'unlink_path' (if nonnull) will be passed to - * fatal_signal_unlink_file_now() and freed with free(). +/* Creates a new pvconn named 'name' that will accept new connections using + * pstream_accept() and stores a pointer to the pvconn in '*pvconnp'. * * Returns 0 if successful, otherwise a positive errno value. (The current * implementation never fails.) */ -int -new_pstream_pvconn(const char *name, int fd, - int (*accept_cb)(int fd, const struct sockaddr *sa, - size_t sa_len, struct vconn **vconnp), - char *unlink_path, struct pvconn **pvconnp) +static int - pvconn_pstream_listen(const char *name_, char *suffix UNUSED, ++pvconn_pstream_listen(const char *name_, char *suffix OVS_UNUSED, + struct pvconn **pvconnp) { - struct pstream_pvconn *ps = xmalloc(sizeof *ps); - pvconn_init(&ps->pvconn, &pstream_pvconn_class, name); - ps->fd = fd; - ps->accept_cb = accept_cb; - ps->unlink_path = unlink_path; + struct pvconn_pstream *ps; + struct pstream *pstream; + char *name; + int error; + + if (!strncmp(name_, "ptcp:", 5) && count_fields(name_) < 2) { + name = xasprintf("%s:%d", name_, OFP_TCP_PORT); + } else if (!strncmp(name_, "pssl:", 5) && count_fields(name_) < 2) { + name = xasprintf("%s:%d", name_, OFP_SSL_PORT); + } else { + name = xstrdup(name_); + } + error = pstream_open(name, &pstream); + free(name); + if (error) { + return error; + } + + ps = xmalloc(sizeof *ps); + pvconn_init(&ps->pvconn, &pstream_pvconn_class, name_); + ps->pstream = pstream; *pvconnp = &ps->pvconn; return 0; } static void -pstream_close(struct pvconn *pvconn) +pvconn_pstream_close(struct pvconn *pvconn) { - struct pstream_pvconn *ps = pstream_pvconn_cast(pvconn); - close(ps->fd); - maybe_unlink_and_free(ps->unlink_path); + struct pvconn_pstream *ps = pvconn_pstream_cast(pvconn); + pstream_close(ps->pstream); free(ps); } static int -pstream_accept(struct pvconn *pvconn, struct vconn **new_vconnp) +pvconn_pstream_accept(struct pvconn *pvconn, struct vconn **new_vconnp) { - struct pstream_pvconn *ps = pstream_pvconn_cast(pvconn); - struct sockaddr_storage ss; - socklen_t ss_len = sizeof ss; - int new_fd; - int retval; - - new_fd = accept(ps->fd, (struct sockaddr *) &ss, &ss_len); - if (new_fd < 0) { - int retval = errno; - if (retval != EAGAIN) { - VLOG_DBG_RL(&rl, "accept: %s", strerror(retval)); + struct pvconn_pstream *ps = pvconn_pstream_cast(pvconn); + struct stream *stream; + int error; + + error = pstream_accept(ps->pstream, &stream); + if (error) { + if (error != EAGAIN) { + VLOG_DBG_RL(&rl, "%s: accept: %s", + pstream_get_name(ps->pstream), strerror(error)); } - return retval; - } - - retval = set_nonblocking(new_fd); - if (retval) { - close(new_fd); - return retval; + return error; } - return ps->accept_cb(new_fd, (const struct sockaddr *) &ss, ss_len, - new_vconnp); + *new_vconnp = vconn_stream_new(stream, 0); + return 0; } static void -pstream_wait(struct pvconn *pvconn) +pvconn_pstream_wait(struct pvconn *pvconn) { - struct pstream_pvconn *ps = pstream_pvconn_cast(pvconn); - poll_fd_wait(ps->fd, POLLIN); + struct pvconn_pstream *ps = pvconn_pstream_cast(pvconn); + pstream_wait(ps->pstream); } - -static struct pvconn_class pstream_pvconn_class = { - "pstream", - NULL, - pstream_close, - pstream_accept, - pstream_wait -}; -/* Helper functions. */ -static void -maybe_unlink_and_free(char *path) +static int +count_fields(const char *s_) { - if (path) { - fatal_signal_unlink_file_now(path); - free(path); + char *s, *field, *save_ptr; + int n = 0; + + save_ptr = NULL; + s = xstrdup(s_); + for (field = strtok_r(s, ":", &save_ptr); field != NULL; + field = strtok_r(NULL, ":", &save_ptr)) { + n++; } + free(s); + + return n; } + +/* Stream-based vconns and pvconns. */ + +#define DEFINE_VCONN_STREAM_CLASS(NAME) \ + struct vconn_class NAME##_vconn_class = { \ + #NAME, \ + vconn_stream_open, \ + vconn_stream_close, \ + vconn_stream_connect, \ + vconn_stream_recv, \ + vconn_stream_send, \ + vconn_stream_run, \ + vconn_stream_run_wait, \ + vconn_stream_wait, \ + }; + +#define DEFINE_PVCONN_STREAM_CLASS(NAME) \ + struct pvconn_class NAME##_pvconn_class = { \ + #NAME, \ + pvconn_pstream_listen, \ + pvconn_pstream_close, \ + pvconn_pstream_accept, \ + pvconn_pstream_wait \ + }; + +static DEFINE_VCONN_STREAM_CLASS(stream); +static DEFINE_PVCONN_STREAM_CLASS(pstream); + +DEFINE_VCONN_STREAM_CLASS(tcp); +DEFINE_PVCONN_STREAM_CLASS(ptcp); + +DEFINE_VCONN_STREAM_CLASS(unix); +DEFINE_PVCONN_STREAM_CLASS(punix); + +#ifdef HAVE_OPENSSL +DEFINE_VCONN_STREAM_CLASS(ssl); +DEFINE_PVCONN_STREAM_CLASS(pssl); +#endif diff --combined lib/vconn.c index 1445be480,f72dbe3ed..c77b6524d --- a/lib/vconn.c +++ b/lib/vconn.c @@@ -90,8 -90,7 +90,8 @@@ check_vconn_classes(void struct vconn_class *class = vconn_classes[i]; assert(class->name != NULL); assert(class->open != NULL); - if (class->close || class->recv || class->send || class->wait) { + if (class->close || class->recv || class->send + || class->run || class->run_wait || class->wait) { assert(class->close != NULL); assert(class->recv != NULL); assert(class->send != NULL); @@@ -120,7 -119,7 +120,7 @@@ * connection methods supported by the vconn. If 'bootstrap' is true, also * advertises options to bootstrap the CA certificate. */ void - vconn_usage(bool active, bool passive, bool bootstrap UNUSED) + vconn_usage(bool active, bool passive, bool bootstrap OVS_UNUSED) { /* Really this should be implemented via callbacks into the vconn * providers, but that seems too heavy-weight to bother with at the @@@ -209,26 -208,6 +209,26 @@@ vconn_open(const char *name, int min_ve return EAFNOSUPPORT; } +/* Allows 'vconn' to perform maintenance activities, such as flushing output + * buffers. */ +void +vconn_run(struct vconn *vconn) +{ + if (vconn->class->run) { + (vconn->class->run)(vconn); + } +} + +/* Arranges for the poll loop to wake up when 'vconn' needs to perform + * maintenance activities. */ +void +vconn_run_wait(struct vconn *vconn) +{ + if (vconn->class->run_wait) { + (vconn->class->run_wait)(vconn); + } +} + int vconn_open_block(const char *name, int min_version, struct vconn **vconnp) { @@@ -237,8 -216,6 +237,8 @@@ error = vconn_open(name, min_version, &vconn); while (error == EAGAIN) { + vconn_run(vconn); + vconn_run_wait(vconn); vconn_connect_wait(vconn); poll_block(); error = vconn_connect(vconn); @@@ -570,8 -547,6 +570,8 @@@ vconn_send_block(struct vconn *vconn, s { int retval; while ((retval = vconn_send(vconn, msg)) == EAGAIN) { + vconn_run(vconn); + vconn_run_wait(vconn); vconn_send_wait(vconn); poll_block(); } @@@ -584,8 -559,6 +584,8 @@@ vconn_recv_block(struct vconn *vconn, s { int retval; while ((retval = vconn_recv(vconn, msgp)) == EAGAIN) { + vconn_run(vconn); + vconn_run_wait(vconn); vconn_recv_wait(vconn); poll_block(); } @@@ -1471,7 -1444,6 +1471,7 @@@ vconn_init(struct vconn *vconn, struct vconn->local_ip = 0; vconn->local_port = 0; vconn->name = xstrdup(name); + assert(vconn->state != VCS_CONNECTING || class->connect); } void diff --combined lib/vlog.c index b2d0c0690,0fab00f14..b534d1965 --- a/lib/vlog.c +++ b/lib/vlog.c @@@ -1,5 -1,5 +1,5 @@@ /* - * Copyright (c) 2008, 2009 Nicira Networks. + * Copyright (c) 2008, 2009, 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. @@@ -385,7 -385,7 +385,8 @@@ vlog_set_verbosity(const char *arg } static void - vlog_unixctl_set(struct unixctl_conn *conn, const char *args, void *aux UNUSED) -vlog_unixctl_set(struct unixctl_conn *conn, const char *args) ++vlog_unixctl_set(struct unixctl_conn *conn, ++ const char *args, void *aux OVS_UNUSED) { char *msg = vlog_set_levels_from_string(args); unixctl_command_reply(conn, msg ? 501 : 202, msg); @@@ -393,8 -393,7 +394,8 @@@ } static void -vlog_unixctl_list(struct unixctl_conn *conn, const char *args OVS_UNUSED) +vlog_unixctl_list(struct unixctl_conn *conn, - const char *args UNUSED, void *aux UNUSED) ++ const char *args OVS_UNUSED, void *aux OVS_UNUSED) { char *msg = vlog_get_levels(); unixctl_command_reply(conn, 200, msg); @@@ -402,8 -401,7 +403,8 @@@ } static void -vlog_unixctl_reopen(struct unixctl_conn *conn, const char *args OVS_UNUSED) +vlog_unixctl_reopen(struct unixctl_conn *conn, - const char *args UNUSED, void *aux UNUSED) ++ const char *args OVS_UNUSED, void *aux OVS_UNUSED) { if (log_file_name) { int error = vlog_reopen_log_file(); @@@ -437,9 -435,9 +438,9 @@@ vlog_init(void VLOG_ERR("current time is negative: %s (%ld)", s, (long int) now); } - unixctl_command_register("vlog/set", vlog_unixctl_set); - unixctl_command_register("vlog/list", vlog_unixctl_list); - unixctl_command_register("vlog/reopen", vlog_unixctl_reopen); + unixctl_command_register("vlog/set", vlog_unixctl_set, NULL); + unixctl_command_register("vlog/list", vlog_unixctl_list, NULL); + unixctl_command_register("vlog/reopen", vlog_unixctl_reopen, NULL); } /* Closes the logging subsystem. */ diff --combined ofproto/discovery.c index ce3e7ce54,372c3afe1..ae36b4dcb --- a/ofproto/discovery.c +++ b/ofproto/discovery.c @@@ -1,5 -1,5 +1,5 @@@ /* - * Copyright (c) 2008, 2009 Nicira Networks. + * Copyright (c) 2008, 2009, 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. @@@ -29,7 -29,7 +29,7 @@@ #include "openflow/openflow.h" #include "packets.h" #include "status.h" -#include "vconn-ssl.h" +#include "stream-ssl.h" #define THIS_MODULE VLM_discovery #include "vlog.h" @@@ -102,7 -102,7 +102,7 @@@ discovery_create(const char *re, bool u char local_name[IF_NAMESIZE]; int error; - d = xcalloc(1, sizeof *d); + d = xzalloc(sizeof *d); /* Controller regular expression. */ error = discovery_set_accept_controller_re(d, re); @@@ -169,7 -169,7 +169,7 @@@ discovery_set_accept_controller_re(stru int error; char *re; - re = (!re_ ? xstrdup(vconn_ssl_is_configured() ? "^ssl:.*" : "^tcp:.*") + re = (!re_ ? xstrdup(stream_ssl_is_configured() ? "^ssl:.*" : "^tcp:.*") : re_[0] == '^' ? xstrdup(re_) : xasprintf("^%s", re_)); regex = xmalloc(sizeof *regex); error = regcomp(regex, re, REG_NOSUB | REG_EXTENDED); @@@ -244,7 -244,7 +244,7 @@@ discovery_wait(struct discovery *d } static void - modify_dhcp_request(struct dhcp_msg *msg, void *aux UNUSED) + modify_dhcp_request(struct dhcp_msg *msg, void *aux OVS_UNUSED) { dhcp_msg_put_string(msg, DHCP_CODE_VENDOR_CLASS, "OpenFlow"); } diff --combined ofproto/ofproto-sflow.c index 44c94a124,45015ce7c..45372003d --- a/ofproto/ofproto-sflow.c +++ b/ofproto/ofproto-sflow.c @@@ -96,14 -96,16 +96,16 @@@ ofproto_sflow_options_destroy(struct of /* sFlow library callback to allocate memory. */ static void * - sflow_agent_alloc_cb(void *magic UNUSED, SFLAgent *agent UNUSED, size_t bytes) + sflow_agent_alloc_cb(void *magic OVS_UNUSED, SFLAgent *agent OVS_UNUSED, + size_t bytes) { return calloc(1, bytes); } /* sFlow library callback to free memory. */ static int - sflow_agent_free_cb(void *magic UNUSED, SFLAgent *agent UNUSED, void *obj) + sflow_agent_free_cb(void *magic OVS_UNUSED, SFLAgent *agent OVS_UNUSED, + void *obj) { free(obj); return 0; @@@ -111,15 -113,16 +113,16 @@@ /* sFlow library callback to report error. */ static void - sflow_agent_error_cb(void *magic UNUSED, SFLAgent *agent UNUSED, char *msg) + sflow_agent_error_cb(void *magic OVS_UNUSED, SFLAgent *agent OVS_UNUSED, + char *msg) { VLOG_WARN("sFlow agent error: %s", msg); } /* sFlow library callback to send datagram. */ static void - sflow_agent_send_packet_cb(void *os_, SFLAgent *agent UNUSED, - SFLReceiver *receiver UNUSED, u_char *pkt, + sflow_agent_send_packet_cb(void *os_, SFLAgent *agent OVS_UNUSED, + SFLReceiver *receiver OVS_UNUSED, u_char *pkt, uint32_t pktLen) { struct ofproto_sflow *os = os_; @@@ -212,7 -215,7 +215,7 @@@ sflow_choose_agent_address(const char * if (agent_device) { struct netdev *netdev; - if (!netdev_open(agent_device, NETDEV_ETH_TYPE_NONE, &netdev)) { + if (!netdev_open_default(agent_device, &netdev)) { int error = netdev_get_in4(netdev, &in4, NULL); netdev_close(netdev); if (!error) { @@@ -319,7 -322,7 +322,7 @@@ ofproto_sflow_add_port(struct ofproto_s ofproto_sflow_del_port(os, odp_port); /* Open network device. */ - error = netdev_open(netdev_name, NETDEV_ETH_TYPE_NONE, &netdev); + error = netdev_open_default(netdev_name, &netdev); if (error) { VLOG_WARN_RL(&rl, "failed to open network device \"%s\": %s", netdev_name, strerror(error)); @@@ -367,6 -370,7 +370,6 @@@ ofproto_sflow_set_options(struct ofprot unsigned int odp_port; SFLAddress agentIP; time_t now; - int error; if (!options->targets.n || !options->sampling_rate) { /* No point in doing any work if there are no targets or nothing to @@@ -384,8 -388,8 +387,8 @@@ if (options_changed || collectors_count(os->collectors) < options->targets.n) { collectors_destroy(os->collectors); - error = collectors_create(&options->targets, - SFL_DEFAULT_COLLECTOR_PORT, &os->collectors); + collectors_create(&options->targets, SFL_DEFAULT_COLLECTOR_PORT, + &os->collectors); if (os->collectors == NULL) { VLOG_WARN_RL(&rl, "no collectors could be initialized, " "sFlow disabled"); diff --combined ofproto/ofproto.c index b5f6f58e2,69587c8cf..7ab9dee67 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@@ -27,6 -27,7 +27,6 @@@ #include "discovery.h" #include "dpif.h" #include "dynamic-string.h" -#include "executer.h" #include "fail-open.h" #include "in-band.h" #include "mac-learning.h" @@@ -38,6 -39,7 +38,6 @@@ #include "ofpbuf.h" #include "openflow/nicira-ext.h" #include "openflow/openflow.h" -#include "openflow/openflow-mgmt.h" #include "openvswitch/datapath-protocol.h" #include "packets.h" #include "pinsched.h" @@@ -48,12 -50,12 +48,12 @@@ #include "shash.h" #include "status.h" #include "stp.h" +#include "stream-ssl.h" #include "svec.h" #include "tag.h" #include "timeval.h" #include "unixctl.h" #include "vconn.h" -#include "vconn-ssl.h" #include "xtoxll.h" #define THIS_MODULE VLM_ofproto @@@ -172,7 -174,7 +172,7 @@@ struct ofconn }; static struct ofconn *ofconn_create(struct ofproto *, struct rconn *); -static void ofconn_destroy(struct ofconn *, struct ofproto *); +static void ofconn_destroy(struct ofconn *); static void ofconn_run(struct ofconn *, struct ofproto *); static void ofconn_wait(struct ofconn *); static void queue_tx(struct ofpbuf *msg, const struct ofconn *ofconn, @@@ -182,6 -184,7 +182,6 @@@ struct ofproto /* Settings. */ uint64_t datapath_id; /* Datapath ID. */ uint64_t fallback_dpid; /* Datapath ID if no better choice found. */ - uint64_t mgmt_id; /* Management channel identifier. */ char *manufacturer; /* Manufacturer. */ char *hardware; /* Hardware. */ char *software; /* Software version. */ @@@ -202,6 -205,7 +202,6 @@@ struct discovery *discovery; struct fail_open *fail_open; struct pinsched *miss_sched, *action_sched; - struct executer *executer; struct netflow *netflow; struct ofproto_sflow *sflow; @@@ -255,8 -259,7 +255,8 @@@ static int init_ports(struct ofproto *) static void reinit_ports(struct ofproto *); int -ofproto_create(const char *datapath, const struct ofhooks *ofhooks, void *aux, +ofproto_create(const char *datapath, const char *datapath_type, + const struct ofhooks *ofhooks, void *aux, struct ofproto **ofprotop) { struct odp_stats stats; @@@ -267,7 -270,7 +267,7 @@@ *ofprotop = NULL; /* Connect to datapath and start listening for messages. */ - error = dpif_open(datapath, &dpif); + error = dpif_open(datapath, datapath_type, &dpif); if (error) { VLOG_ERR("failed to open datapath %s: %s", datapath, strerror(error)); return error; @@@ -290,7 -293,7 +290,7 @@@ dpif_recv_purge(dpif); /* Initialize settings. */ - p = xcalloc(1, sizeof *p); + p = xzalloc(sizeof *p); p->fallback_dpid = pick_fallback_dpid(); p->datapath_id = p->fallback_dpid; p->manufacturer = xstrdup("Nicira Networks, Inc."); @@@ -311,6 -314,7 +311,6 @@@ p->discovery = NULL; p->fail_open = NULL; p->miss_sched = p->action_sched = NULL; - p->executer = NULL; p->netflow = NULL; p->sflow = NULL; @@@ -345,6 -349,13 +345,6 @@@ p->ss_cat = switch_status_register(p->switch_status, "remote", rconn_status_cb, p->controller->rconn); - /* Almost done... */ - error = init_ports(p); - if (error) { - ofproto_destroy(p); - return error; - } - /* Pick final datapath ID. */ p->datapath_id = pick_datapath_id(p); VLOG_INFO("using datapath ID %012"PRIx64, p->datapath_id); @@@ -364,6 -375,12 +364,6 @@@ ofproto_set_datapath_id(struct ofproto } } -void -ofproto_set_mgmt_id(struct ofproto *p, uint64_t mgmt_id) -{ - p->mgmt_id = mgmt_id; -} - void ofproto_set_probe_interval(struct ofproto *p, int probe_interval) { @@@ -520,7 -537,7 +520,7 @@@ in ofproto_set_netflow(struct ofproto *ofproto, const struct netflow_options *nf_options) { - if (nf_options->collectors.n) { + if (nf_options && nf_options->collectors.n) { if (!ofproto->netflow) { ofproto->netflow = netflow_create(); } @@@ -600,7 -617,7 +600,7 @@@ ofproto_set_rate_limit(struct ofproto * } int - ofproto_set_stp(struct ofproto *ofproto UNUSED, bool enable_stp) + ofproto_set_stp(struct ofproto *ofproto OVS_UNUSED, bool enable_stp) { /* XXX */ if (enable_stp) { @@@ -611,12 -628,36 +611,12 @@@ } } -int -ofproto_set_remote_execution(struct ofproto *ofproto, const char *command_acl, - const char *command_dir) -{ - if (command_acl) { - if (!ofproto->executer) { - return executer_create(command_acl, command_dir, - &ofproto->executer); - } else { - executer_set_acl(ofproto->executer, command_acl, command_dir); - } - } else { - executer_destroy(ofproto->executer); - ofproto->executer = NULL; - } - return 0; -} - uint64_t ofproto_get_datapath_id(const struct ofproto *ofproto) { return ofproto->datapath_id; } -uint64_t -ofproto_get_mgmt_id(const struct ofproto *ofproto) -{ - return ofproto->mgmt_id; -} - int ofproto_get_probe_interval(const struct ofproto *ofproto) { @@@ -684,7 -725,7 +684,7 @@@ ofproto_destroy(struct ofproto *p LIST_FOR_EACH_SAFE (ofconn, next_ofconn, struct ofconn, node, &p->all_conns) { - ofconn_destroy(ofconn, p); + ofconn_destroy(ofconn); } dpif_close(p->dpif); @@@ -700,6 -741,7 +700,6 @@@ fail_open_destroy(p->fail_open); pinsched_destroy(p->miss_sched); pinsched_destroy(p->action_sched); - executer_destroy(p->executer); netflow_destroy(p->netflow); ofproto_sflow_destroy(p->sflow); @@@ -749,10 -791,6 +749,10 @@@ ofproto_run1(struct ofproto *p int error; int i; + if (shash_is_empty(&p->port_by_name)) { + init_ports(p); + } + for (i = 0; i < 50; i++) { struct ofpbuf *buf; int error; @@@ -800,6 -838,9 +800,6 @@@ } pinsched_run(p->miss_sched, send_packet_in_miss, p); pinsched_run(p->action_sched, send_packet_in_action, p); - if (p->executer) { - executer_run(p->executer); - } LIST_FOR_EACH_SAFE (ofconn, next_ofconn, struct ofconn, node, &p->all_conns) { @@@ -911,6 -952,9 +911,6 @@@ ofproto_wait(struct ofproto *p } pinsched_wait(p->miss_sched); pinsched_wait(p->action_sched); - if (p->executer) { - executer_wait(p->executer); - } if (p->sflow) { ofproto_sflow_wait(p->sflow); } @@@ -1090,19 -1134,13 +1090,19 @@@ refresh_port_groups(struct ofproto *p static struct ofport * make_ofport(const struct odp_port *odp_port) { + struct netdev_options netdev_options; enum netdev_flags flags; struct ofport *ofport; struct netdev *netdev; bool carrier; int error; - error = netdev_open(odp_port->devname, NETDEV_ETH_TYPE_NONE, &netdev); + memset(&netdev_options, 0, sizeof netdev_options); + netdev_options.name = odp_port->devname; + netdev_options.ethertype = NETDEV_ETH_TYPE_NONE; + netdev_options.may_open = true; + + error = netdev_open(&netdev_options, &netdev); if (error) { VLOG_WARN_RL(&rl, "ignoring port %s (%"PRIu16") because netdev %s " "cannot be opened (%s)", @@@ -1337,8 -1375,12 +1337,8 @@@ ofconn_create(struct ofproto *p, struc } static void -ofconn_destroy(struct ofconn *ofconn, struct ofproto *p) +ofconn_destroy(struct ofconn *ofconn) { - if (p->executer) { - executer_rconn_closing(p->executer, ofconn->rconn); - } - list_remove(&ofconn->node); rconn_destroy(ofconn->rconn); rconn_packet_counter_destroy(ofconn->packet_in_counter); @@@ -1371,7 -1413,7 +1371,7 @@@ ofconn_run(struct ofconn *ofconn, struc } if (ofconn != p->controller && !rconn_is_alive(ofconn->rconn)) { - ofconn_destroy(ofconn, p); + ofconn_destroy(ofconn); } } @@@ -1393,7 -1435,7 +1393,7 @@@ rule_create(struct ofproto *ofproto, st const union ofp_action *actions, size_t n_actions, uint16_t idle_timeout, uint16_t hard_timeout) { - struct rule *rule = xcalloc(1, sizeof *rule); + struct rule *rule = xzalloc(sizeof *rule); rule->idle_timeout = idle_timeout; rule->hard_timeout = hard_timeout; rule->used = rule->created = time_msec(); @@@ -2497,7 -2539,7 +2497,7 @@@ query_stats(struct ofproto *p, struct r byte_count = rule->byte_count; n_odp_flows = rule->cr.wc.wildcards ? list_size(&rule->list) : 1; - odp_flows = xcalloc(1, n_odp_flows * sizeof *odp_flows); + odp_flows = xzalloc(n_odp_flows * sizeof *odp_flows); if (rule->cr.wc.wildcards) { size_t i = 0; LIST_FOR_EACH (subrule, struct rule, list, &rule->list) { @@@ -2795,13 -2837,11 +2795,13 @@@ add_flow(struct ofproto *p, struct ofco ntohs(ofm->hard_timeout)); cls_rule_from_match(&rule->cr, &ofm->match, ntohs(ofm->priority)); - packet = NULL; error = 0; if (ofm->buffer_id != htonl(UINT32_MAX)) { error = pktbuf_retrieve(ofconn->pktbuf, ntohl(ofm->buffer_id), &packet, &in_port); + } else { + packet = NULL; + in_port = -1; } rule_insert(p, rule, packet, in_port); @@@ -2957,6 -2997,58 +2957,6 @@@ handle_flow_mod(struct ofproto *p, stru } } -static void -send_capability_reply(struct ofproto *p, struct ofconn *ofconn, uint32_t xid) -{ - struct ofmp_capability_reply *ocr; - struct ofpbuf *b; - char capabilities[] = "com.nicira.mgmt.manager=false\n"; - - ocr = make_openflow_xid(sizeof(*ocr), OFPT_VENDOR, xid, &b); - ocr->header.header.vendor = htonl(NX_VENDOR_ID); - ocr->header.header.subtype = htonl(NXT_MGMT); - ocr->header.type = htons(OFMPT_CAPABILITY_REPLY); - - ocr->format = htonl(OFMPCOF_SIMPLE); - ocr->mgmt_id = htonll(p->mgmt_id); - - ofpbuf_put(b, capabilities, strlen(capabilities)); - - queue_tx(b, ofconn, ofconn->reply_counter); -} - -static int -handle_ofmp(struct ofproto *p, struct ofconn *ofconn, - struct ofmp_header *ofmph) -{ - size_t msg_len = ntohs(ofmph->header.header.length); - if (msg_len < sizeof(*ofmph)) { - VLOG_WARN_RL(&rl, "dropping short managment message: %zu\n", msg_len); - return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LENGTH); - } - - if (ofmph->type == htons(OFMPT_CAPABILITY_REQUEST)) { - struct ofmp_capability_request *ofmpcr; - - if (msg_len < sizeof(struct ofmp_capability_request)) { - VLOG_WARN_RL(&rl, "dropping short capability request: %zu\n", - msg_len); - return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LENGTH); - } - - ofmpcr = (struct ofmp_capability_request *)ofmph; - if (ofmpcr->format != htonl(OFMPCAF_SIMPLE)) { - /* xxx Find a better type than bad subtype */ - return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE); - } - - send_capability_reply(p, ofconn, ofmph->header.header.xid); - return 0; - } else { - return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE); - } -} - static int handle_vendor(struct ofproto *p, struct ofconn *ofconn, void *msg) { @@@ -2978,6 -3070,21 +2978,6 @@@ case NXT_STATUS_REQUEST: return switch_status_handle_request(p->switch_status, ofconn->rconn, msg); - - case NXT_ACT_SET_CONFIG: - return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE); /* XXX */ - - case NXT_ACT_GET_CONFIG: - return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE); /* XXX */ - - case NXT_COMMAND_REQUEST: - if (p->executer) { - return executer_handle_request(p->executer, ofconn->rconn, msg); - } - break; - - case NXT_MGMT: - return handle_ofmp(p, ofconn, msg); } return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE); @@@ -3460,7 -3567,10 +3460,7 @@@ static uint64_ pick_fallback_dpid(void) { uint8_t ea[ETH_ADDR_LEN]; - eth_addr_random(ea); - ea[0] = 0x00; /* Set Nicira OUI. */ - ea[1] = 0x23; - ea[2] = 0x20; + eth_addr_nicira_random(ea); return eth_addr_to_uint64(ea); } diff --combined ovsdb/execution.c index 3e3d35636,000000000..b984b1b52 mode 100644,000000..100644 --- a/ovsdb/execution.c +++ b/ovsdb/execution.c @@@ -1,706 -1,0 +1,706 @@@ +/* Copyright (c) 2009, 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 +#include + +#include "column.h" +#include "condition.h" +#include "file.h" +#include "json.h" +#include "mutation.h" +#include "ovsdb-data.h" +#include "ovsdb-error.h" +#include "ovsdb-parser.h" +#include "ovsdb.h" +#include "query.h" +#include "row.h" +#include "table.h" +#include "timeval.h" +#include "transaction.h" + +struct ovsdb_execution { + struct ovsdb *db; + struct ovsdb_txn *txn; + struct ovsdb_symbol_table *symtab; + bool durable; + + /* Triggers. */ + long long int elapsed_msec; + long long int timeout_msec; +}; + +typedef struct ovsdb_error *ovsdb_operation_executor(struct ovsdb_execution *, + struct ovsdb_parser *, + struct json *result); + +static ovsdb_operation_executor ovsdb_execute_insert; +static ovsdb_operation_executor ovsdb_execute_select; +static ovsdb_operation_executor ovsdb_execute_update; +static ovsdb_operation_executor ovsdb_execute_mutate; +static ovsdb_operation_executor ovsdb_execute_delete; +static ovsdb_operation_executor ovsdb_execute_wait; +static ovsdb_operation_executor ovsdb_execute_commit; +static ovsdb_operation_executor ovsdb_execute_abort; +static ovsdb_operation_executor ovsdb_execute_comment; + +static ovsdb_operation_executor * +lookup_executor(const char *name) +{ + struct ovsdb_operation { + const char *name; + ovsdb_operation_executor *executor; + }; + + static const struct ovsdb_operation operations[] = { + { "insert", ovsdb_execute_insert }, + { "select", ovsdb_execute_select }, + { "update", ovsdb_execute_update }, + { "mutate", ovsdb_execute_mutate }, + { "delete", ovsdb_execute_delete }, + { "wait", ovsdb_execute_wait }, + { "commit", ovsdb_execute_commit }, + { "abort", ovsdb_execute_abort }, + { "comment", ovsdb_execute_comment }, + }; + + size_t i; + + for (i = 0; i < ARRAY_SIZE(operations); i++) { + const struct ovsdb_operation *c = &operations[i]; + if (!strcmp(c->name, name)) { + return c->executor; + } + } + return NULL; +} + +struct json * +ovsdb_execute(struct ovsdb *db, const struct json *params, + long long int elapsed_msec, long long int *timeout_msec) +{ + struct ovsdb_execution x; + struct ovsdb_error *error; + struct json *results; + size_t n_operations; + size_t i; + + if (params->type != JSON_ARRAY + || !params->u.array.n + || params->u.array.elems[0]->type != JSON_STRING + || strcmp(params->u.array.elems[0]->u.string, db->schema->name)) { + struct ovsdb_error *error; + + if (params->type != JSON_ARRAY) { + error = ovsdb_syntax_error(params, NULL, "array expected"); + } else { + error = ovsdb_syntax_error(params, NULL, "database name expected " + "as first parameter"); + } + + results = ovsdb_error_to_json(error); + ovsdb_error_destroy(error); + return results; + } + + x.db = db; + x.txn = ovsdb_txn_create(db); + x.symtab = ovsdb_symbol_table_create(); + x.durable = false; + x.elapsed_msec = elapsed_msec; + x.timeout_msec = LLONG_MAX; + results = NULL; + + results = json_array_create_empty(); + n_operations = params->u.array.n - 1; + error = NULL; + for (i = 1; i <= n_operations; i++) { + struct json *operation = params->u.array.elems[i]; + struct ovsdb_error *parse_error; + struct ovsdb_parser parser; + struct json *result; + const struct json *op; + + /* Parse and execute operation. */ + ovsdb_parser_init(&parser, operation, + "ovsdb operation %zu of %zu", i + 1, n_operations); + op = ovsdb_parser_member(&parser, "op", OP_ID); + result = json_object_create(); + if (op) { + const char *op_name = json_string(op); + ovsdb_operation_executor *executor = lookup_executor(op_name); + if (executor) { + error = executor(&x, &parser, result); + } else { + ovsdb_parser_raise_error(&parser, "No operation \"%s\"", + op_name); + } + } else { + assert(ovsdb_parser_has_error(&parser)); + } + + /* A parse error overrides any other error. + * An error overrides any other result. */ + parse_error = ovsdb_parser_finish(&parser); + if (parse_error) { + ovsdb_error_destroy(error); + error = parse_error; + } + if (error) { + json_destroy(result); + result = ovsdb_error_to_json(error); + } + if (error && !strcmp(ovsdb_error_get_tag(error), "not supported") + && timeout_msec) { + ovsdb_txn_abort(x.txn); + *timeout_msec = x.timeout_msec; + + json_destroy(result); + json_destroy(results); + results = NULL; + goto exit; + } + + /* Add result to array. */ + json_array_add(results, result); + if (error) { + break; + } + } + + if (!error) { + error = ovsdb_txn_commit(x.txn, x.durable); + if (error) { + json_array_add(results, ovsdb_error_to_json(error)); + } + } else { + ovsdb_txn_abort(x.txn); + } + + while (json_array(results)->n < n_operations) { + json_array_add(results, json_null_create()); + } + +exit: + ovsdb_error_destroy(error); + ovsdb_symbol_table_destroy(x.symtab); + + return results; +} + +struct ovsdb_error * +ovsdb_execute_commit(struct ovsdb_execution *x, struct ovsdb_parser *parser, - struct json *result UNUSED) ++ struct json *result OVS_UNUSED) +{ + const struct json *durable; + + durable = ovsdb_parser_member(parser, "durable", OP_BOOLEAN); + if (durable && json_boolean(durable)) { + x->durable = true; + } + return NULL; +} + +static struct ovsdb_error * - ovsdb_execute_abort(struct ovsdb_execution *x UNUSED, - struct ovsdb_parser *parser UNUSED, - struct json *result UNUSED) ++ovsdb_execute_abort(struct ovsdb_execution *x OVS_UNUSED, ++ struct ovsdb_parser *parser OVS_UNUSED, ++ struct json *result OVS_UNUSED) +{ + return ovsdb_error("aborted", "aborted by request"); +} + +static struct ovsdb_table * +parse_table(struct ovsdb_execution *x, + struct ovsdb_parser *parser, const char *member) +{ + struct ovsdb_table *table; + const char *table_name; + const struct json *json; + + json = ovsdb_parser_member(parser, member, OP_ID); + if (!json) { + return NULL; + } + table_name = json_string(json); + + table = shash_find_data(&x->db->tables, table_name); + if (!table) { + ovsdb_parser_raise_error(parser, "No table named %s.", table_name); + } + return table; +} + +static WARN_UNUSED_RESULT struct ovsdb_error * +parse_row(struct ovsdb_parser *parser, const char *member, + const struct ovsdb_table *table, + struct ovsdb_symbol_table *symtab, + struct ovsdb_row **rowp, struct ovsdb_column_set *columns) +{ + struct ovsdb_error *error; + const struct json *json; + struct ovsdb_row *row; + + *rowp = NULL; + + if (!table) { + return OVSDB_BUG("null table"); + } + json = ovsdb_parser_member(parser, member, OP_OBJECT); + if (!json) { + return OVSDB_BUG("null row member"); + } + + row = ovsdb_row_create(table); + error = ovsdb_row_from_json(row, json, symtab, columns); + if (error) { + ovsdb_row_destroy(row); + return error; + } else { + *rowp = row; + return NULL; + } +} + +struct ovsdb_error * +ovsdb_execute_insert(struct ovsdb_execution *x, struct ovsdb_parser *parser, + struct json *result) +{ + struct ovsdb_table *table; + struct ovsdb_row *row = NULL; + const struct json *uuid_name; + struct ovsdb_error *error; + struct uuid row_uuid; + + table = parse_table(x, parser, "table"); + uuid_name = ovsdb_parser_member(parser, "uuid-name", OP_ID | OP_OPTIONAL); + error = ovsdb_parser_get_error(parser); + + if (uuid_name) { + struct ovsdb_symbol *symbol; + + symbol = ovsdb_symbol_table_insert(x->symtab, json_string(uuid_name)); + if (symbol->used) { + return ovsdb_syntax_error(uuid_name, "duplicate uuid-name", + "This \"uuid-name\" appeared on an " + "earlier \"insert\" operation."); + } + row_uuid = symbol->uuid; + symbol->used = true; + } else { + uuid_generate(&row_uuid); + } + + if (!error) { + error = parse_row(parser, "row", table, x->symtab, &row, NULL); + } + if (!error) { + /* Check constraints for columns not included in "row", in case the + * default values do not satisfy the constraints. We could check only + * the columns that have their default values by supplying an + * ovsdb_column_set to parse_row() above, but I suspect that this is + * cheaper. */ + const struct shash_node *node; + + SHASH_FOR_EACH (node, &table->schema->columns) { + const struct ovsdb_column *column = node->data; + const struct ovsdb_datum *datum = &row->fields[column->index]; + + /* If there are 0 keys or pairs, there's nothing to check. + * If there is 1, it might be a default value. + * If there are more, it can't be a default value, so the value has + * already been checked. */ + if (datum->n == 1) { + error = ovsdb_datum_check_constraints(datum, &column->type); + if (error) { + ovsdb_row_destroy(row); + break; + } + } + } + } + if (!error) { + *ovsdb_row_get_uuid_rw(row) = row_uuid; + ovsdb_txn_row_insert(x->txn, row); + json_object_put(result, "uuid", + ovsdb_datum_to_json(&row->fields[OVSDB_COL_UUID], + &ovsdb_type_uuid)); + } + return error; +} + +struct ovsdb_error * +ovsdb_execute_select(struct ovsdb_execution *x, struct ovsdb_parser *parser, + struct json *result) +{ + struct ovsdb_table *table; + const struct json *where, *columns_json, *sort_json; + struct ovsdb_condition condition = OVSDB_CONDITION_INITIALIZER; + struct ovsdb_column_set columns = OVSDB_COLUMN_SET_INITIALIZER; + struct ovsdb_column_set sort = OVSDB_COLUMN_SET_INITIALIZER; + struct ovsdb_error *error; + + table = parse_table(x, parser, "table"); + where = ovsdb_parser_member(parser, "where", OP_ARRAY); + columns_json = ovsdb_parser_member(parser, "columns", + OP_ARRAY | OP_OPTIONAL); + sort_json = ovsdb_parser_member(parser, "sort", OP_ARRAY | OP_OPTIONAL); + + error = ovsdb_parser_get_error(parser); + if (!error) { + error = ovsdb_condition_from_json(table->schema, where, x->symtab, + &condition); + } + if (!error) { + error = ovsdb_column_set_from_json(columns_json, table, &columns); + } + if (!error) { + error = ovsdb_column_set_from_json(sort_json, table, &sort); + } + if (!error) { + struct ovsdb_row_set rows = OVSDB_ROW_SET_INITIALIZER; + + ovsdb_query_distinct(table, &condition, &columns, &rows); + ovsdb_row_set_sort(&rows, &sort); + json_object_put(result, "rows", + ovsdb_row_set_to_json(&rows, &columns)); + + ovsdb_row_set_destroy(&rows); + } + + ovsdb_column_set_destroy(&columns); + ovsdb_column_set_destroy(&sort); + ovsdb_condition_destroy(&condition); + + return error; +} + +struct update_row_cbdata { + size_t n_matches; + struct ovsdb_txn *txn; + const struct ovsdb_row *row; + const struct ovsdb_column_set *columns; +}; + +static bool +update_row_cb(const struct ovsdb_row *row, void *ur_) +{ + struct update_row_cbdata *ur = ur_; + + ur->n_matches++; + if (!ovsdb_row_equal_columns(row, ur->row, ur->columns)) { + ovsdb_row_update_columns(ovsdb_txn_row_modify(ur->txn, row), + ur->row, ur->columns); + } + + return true; +} + +struct ovsdb_error * +ovsdb_execute_update(struct ovsdb_execution *x, struct ovsdb_parser *parser, + struct json *result) +{ + struct ovsdb_table *table; + const struct json *where; + struct ovsdb_condition condition = OVSDB_CONDITION_INITIALIZER; + struct ovsdb_column_set columns = OVSDB_COLUMN_SET_INITIALIZER; + struct ovsdb_row *row = NULL; + struct update_row_cbdata ur; + struct ovsdb_error *error; + + table = parse_table(x, parser, "table"); + where = ovsdb_parser_member(parser, "where", OP_ARRAY); + error = ovsdb_parser_get_error(parser); + if (!error) { + error = parse_row(parser, "row", table, x->symtab, &row, &columns); + } + if (!error) { + error = ovsdb_condition_from_json(table->schema, where, x->symtab, + &condition); + } + if (!error) { + ur.n_matches = 0; + ur.txn = x->txn; + ur.row = row; + ur.columns = &columns; + ovsdb_query(table, &condition, update_row_cb, &ur); + json_object_put(result, "count", json_integer_create(ur.n_matches)); + } + + ovsdb_row_destroy(row); + ovsdb_column_set_destroy(&columns); + ovsdb_condition_destroy(&condition); + + return error; +} + +struct mutate_row_cbdata { + size_t n_matches; + struct ovsdb_txn *txn; + const struct ovsdb_mutation_set *mutations; +}; + +static bool +mutate_row_cb(const struct ovsdb_row *row, void *mr_) +{ + struct mutate_row_cbdata *mr = mr_; + + mr->n_matches++; + ovsdb_mutation_set_execute(ovsdb_txn_row_modify(mr->txn, row), + mr->mutations); + + return true; +} + +struct ovsdb_error * +ovsdb_execute_mutate(struct ovsdb_execution *x, struct ovsdb_parser *parser, + struct json *result) +{ + struct ovsdb_table *table; + const struct json *where; + const struct json *mutations_json; + struct ovsdb_condition condition = OVSDB_CONDITION_INITIALIZER; + struct ovsdb_mutation_set mutations = OVSDB_MUTATION_SET_INITIALIZER; + struct ovsdb_row *row = NULL; + struct mutate_row_cbdata mr; + struct ovsdb_error *error; + + table = parse_table(x, parser, "table"); + where = ovsdb_parser_member(parser, "where", OP_ARRAY); + mutations_json = ovsdb_parser_member(parser, "mutations", OP_ARRAY); + error = ovsdb_parser_get_error(parser); + if (!error) { + error = ovsdb_mutation_set_from_json(table->schema, mutations_json, + x->symtab, &mutations); + } + if (!error) { + error = ovsdb_condition_from_json(table->schema, where, x->symtab, + &condition); + } + if (!error) { + mr.n_matches = 0; + mr.txn = x->txn; + mr.mutations = &mutations; + ovsdb_query(table, &condition, mutate_row_cb, &mr); + json_object_put(result, "count", json_integer_create(mr.n_matches)); + } + + ovsdb_row_destroy(row); + ovsdb_mutation_set_destroy(&mutations); + ovsdb_condition_destroy(&condition); + + return error; +} + +struct delete_row_cbdata { + size_t n_matches; + const struct ovsdb_table *table; + struct ovsdb_txn *txn; +}; + +static bool +delete_row_cb(const struct ovsdb_row *row, void *dr_) +{ + struct delete_row_cbdata *dr = dr_; + + dr->n_matches++; + ovsdb_txn_row_delete(dr->txn, row); + + return true; +} + +struct ovsdb_error * +ovsdb_execute_delete(struct ovsdb_execution *x, struct ovsdb_parser *parser, + struct json *result) +{ + struct ovsdb_table *table; + const struct json *where; + struct ovsdb_condition condition = OVSDB_CONDITION_INITIALIZER; + struct ovsdb_error *error; + + where = ovsdb_parser_member(parser, "where", OP_ARRAY); + table = parse_table(x, parser, "table"); + error = ovsdb_parser_get_error(parser); + if (!error) { + error = ovsdb_condition_from_json(table->schema, where, x->symtab, + &condition); + } + if (!error) { + struct delete_row_cbdata dr; + + dr.n_matches = 0; + dr.table = table; + dr.txn = x->txn; + ovsdb_query(table, &condition, delete_row_cb, &dr); + + json_object_put(result, "count", json_integer_create(dr.n_matches)); + } + + ovsdb_condition_destroy(&condition); + + return error; +} + +struct wait_auxdata { + struct ovsdb_row_hash *actual; + struct ovsdb_row_hash *expected; + bool *equal; +}; + +static bool +ovsdb_execute_wait_query_cb(const struct ovsdb_row *row, void *aux_) +{ + struct wait_auxdata *aux = aux_; + + if (ovsdb_row_hash_contains(aux->expected, row)) { + ovsdb_row_hash_insert(aux->actual, row); + return true; + } else { + /* The query row isn't in the expected result set, so the actual and + * expected results sets definitely differ and we can short-circuit the + * rest of the query. */ + *aux->equal = false; + return false; + } +} + +static struct ovsdb_error * +ovsdb_execute_wait(struct ovsdb_execution *x, struct ovsdb_parser *parser, - struct json *result UNUSED) ++ struct json *result OVS_UNUSED) +{ + struct ovsdb_table *table; + const struct json *timeout, *where, *columns_json, *until, *rows; + struct ovsdb_condition condition = OVSDB_CONDITION_INITIALIZER; + struct ovsdb_column_set columns = OVSDB_COLUMN_SET_INITIALIZER; + struct ovsdb_row_hash expected = OVSDB_ROW_HASH_INITIALIZER(expected); + struct ovsdb_row_hash actual = OVSDB_ROW_HASH_INITIALIZER(actual); + struct ovsdb_error *error; + struct wait_auxdata aux; + long long int timeout_msec = 0; + size_t i; + + timeout = ovsdb_parser_member(parser, "timeout", OP_NUMBER | OP_OPTIONAL); + where = ovsdb_parser_member(parser, "where", OP_ARRAY); + columns_json = ovsdb_parser_member(parser, "columns", + OP_ARRAY | OP_OPTIONAL); + until = ovsdb_parser_member(parser, "until", OP_STRING); + rows = ovsdb_parser_member(parser, "rows", OP_ARRAY); + table = parse_table(x, parser, "table"); + error = ovsdb_parser_get_error(parser); + if (!error) { + error = ovsdb_condition_from_json(table->schema, where, x->symtab, + &condition); + } + if (!error) { + error = ovsdb_column_set_from_json(columns_json, table, &columns); + } + if (!error) { + if (timeout) { + timeout_msec = MIN(LLONG_MAX, json_real(timeout)); + if (timeout_msec < 0) { + error = ovsdb_syntax_error(timeout, NULL, + "timeout must be nonnegative"); + } else if (timeout_msec < x->timeout_msec) { + x->timeout_msec = timeout_msec; + } + } else { + timeout_msec = LLONG_MAX; + } + if (strcmp(json_string(until), "==") + && strcmp(json_string(until), "!=")) { + error = ovsdb_syntax_error(until, NULL, + "\"until\" must be \"==\" or \"!=\""); + } + } + if (!error) { + /* Parse "rows" into 'expected'. */ + ovsdb_row_hash_init(&expected, &columns); + for (i = 0; i < rows->u.array.n; i++) { + struct ovsdb_error *error; + struct ovsdb_row *row; + + row = ovsdb_row_create(table); + error = ovsdb_row_from_json(row, rows->u.array.elems[i], x->symtab, + NULL); + if (error) { + break; + } + + if (!ovsdb_row_hash_insert(&expected, row)) { + /* XXX Perhaps we should abort with an error or log a + * warning. */ + ovsdb_row_destroy(row); + } + } + } + if (!error) { + /* Execute query. */ + bool equal = true; + ovsdb_row_hash_init(&actual, &columns); + aux.actual = &actual; + aux.expected = &expected; + aux.equal = &equal; + ovsdb_query(table, &condition, ovsdb_execute_wait_query_cb, &aux); + if (equal) { + /* We know that every row in 'actual' is also in 'expected'. We + * also know that all of the rows in 'actual' are distinct and that + * all of the rows in 'expected' are distinct. Therefore, if + * 'actual' and 'expected' have the same number of rows, then they + * have the same content. */ + size_t n_actual = ovsdb_row_hash_count(&actual); + size_t n_expected = ovsdb_row_hash_count(&expected); + equal = n_actual == n_expected; + } + if (!strcmp(json_string(until), "==") != equal) { + if (timeout && x->elapsed_msec >= timeout_msec) { + if (x->elapsed_msec) { + error = ovsdb_error("timed out", + "\"wait\" timed out after %lld ms", + x->elapsed_msec); + } else { + error = ovsdb_error("timed out", "\"wait\" timed out"); + } + } else { + /* ovsdb_execute() will change this, if triggers really are + * supported. */ + error = ovsdb_error("not supported", "triggers not supported"); + } + } + } + + + ovsdb_row_hash_destroy(&expected, true); + ovsdb_row_hash_destroy(&actual, false); + ovsdb_column_set_destroy(&columns); + ovsdb_condition_destroy(&condition); + + return error; +} + +static struct ovsdb_error * +ovsdb_execute_comment(struct ovsdb_execution *x, struct ovsdb_parser *parser, - struct json *result UNUSED) ++ struct json *result OVS_UNUSED) +{ + const struct json *comment; + + comment = ovsdb_parser_member(parser, "comment", OP_STRING); + if (!comment) { + return NULL; + } + ovsdb_txn_add_comment(x->txn, json_string(comment)); + + return NULL; +} diff --combined ovsdb/jsonrpc-server.c index 26fabe52e,000000000..e39846473 mode 100644,000000..100644 --- a/ovsdb/jsonrpc-server.c +++ b/ovsdb/jsonrpc-server.c @@@ -1,980 -1,0 +1,981 @@@ +/* Copyright (c) 2009, 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 "jsonrpc-server.h" + +#include +#include + +#include "column.h" +#include "json.h" +#include "jsonrpc.h" +#include "ovsdb-error.h" +#include "ovsdb-parser.h" +#include "ovsdb.h" +#include "reconnect.h" +#include "row.h" +#include "stream.h" +#include "table.h" +#include "timeval.h" +#include "transaction.h" +#include "trigger.h" + +#define THIS_MODULE VLM_ovsdb_jsonrpc_server +#include "vlog.h" + +struct ovsdb_jsonrpc_remote; +struct ovsdb_jsonrpc_session; + +/* Message rate-limiting. */ +struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + +/* Sessions. */ +static struct ovsdb_jsonrpc_session *ovsdb_jsonrpc_session_create( + struct ovsdb_jsonrpc_remote *, struct jsonrpc_session *); +static void ovsdb_jsonrpc_session_run_all(struct ovsdb_jsonrpc_remote *); +static void ovsdb_jsonrpc_session_wait_all(struct ovsdb_jsonrpc_remote *); +static void ovsdb_jsonrpc_session_close_all(struct ovsdb_jsonrpc_remote *); + +/* Triggers. */ +static void ovsdb_jsonrpc_trigger_create(struct ovsdb_jsonrpc_session *, + struct json *id, struct json *params); +static struct ovsdb_jsonrpc_trigger *ovsdb_jsonrpc_trigger_find( + struct ovsdb_jsonrpc_session *, const struct json *id, size_t hash); +static void ovsdb_jsonrpc_trigger_complete(struct ovsdb_jsonrpc_trigger *); +static void ovsdb_jsonrpc_trigger_complete_all(struct ovsdb_jsonrpc_session *); +static void ovsdb_jsonrpc_trigger_complete_done( + struct ovsdb_jsonrpc_session *); + +/* Monitors. */ +static struct json *ovsdb_jsonrpc_monitor_create( + struct ovsdb_jsonrpc_session *, struct json *params); +static struct jsonrpc_msg *ovsdb_jsonrpc_monitor_cancel( + struct ovsdb_jsonrpc_session *, + struct json_array *params, + const struct json *request_id); +static void ovsdb_jsonrpc_monitor_remove_all(struct ovsdb_jsonrpc_session *); + +/* JSON-RPC database server. */ + +struct ovsdb_jsonrpc_server { + struct ovsdb *db; + unsigned int n_sessions, max_sessions; + struct shash remotes; /* Contains "struct ovsdb_jsonrpc_remote *"s. */ +}; + +/* A configured remote. This is either a passive stream listener plus a list + * of the currently connected sessions, or a list of exactly one active + * session. */ +struct ovsdb_jsonrpc_remote { + struct ovsdb_jsonrpc_server *server; + struct pstream *listener; /* Listener, if passive. */ + struct list sessions; /* List of "struct ovsdb_jsonrpc_session"s. */ +}; + +static void ovsdb_jsonrpc_server_add_remote(struct ovsdb_jsonrpc_server *, + const char *name); +static void ovsdb_jsonrpc_server_del_remote(struct shash_node *); + +struct ovsdb_jsonrpc_server * +ovsdb_jsonrpc_server_create(struct ovsdb *db) +{ + struct ovsdb_jsonrpc_server *server = xzalloc(sizeof *server); + server->db = db; + server->max_sessions = 64; + shash_init(&server->remotes); + return server; +} + +void +ovsdb_jsonrpc_server_destroy(struct ovsdb_jsonrpc_server *svr) +{ + struct shash_node *node, *next; + + SHASH_FOR_EACH_SAFE (node, next, &svr->remotes) { + ovsdb_jsonrpc_server_del_remote(node); + } + shash_destroy(&svr->remotes); + free(svr); +} + +/* Sets 'svr''s current set of remotes to the names in 'new_remotes'. The data + * values in 'new_remotes' are ignored. + * + * A remote is an active or passive stream connection method, e.g. "pssl:" or + * "tcp:1.2.3.4". */ +void +ovsdb_jsonrpc_server_set_remotes(struct ovsdb_jsonrpc_server *svr, + const struct shash *new_remotes) +{ + struct shash_node *node, *next; + + SHASH_FOR_EACH_SAFE (node, next, &svr->remotes) { + if (!shash_find(new_remotes, node->name)) { + ovsdb_jsonrpc_server_del_remote(node); + } + } + SHASH_FOR_EACH (node, new_remotes) { + if (!shash_find(&svr->remotes, node->name)) { + ovsdb_jsonrpc_server_add_remote(svr, node->name); + } + } +} + +static void +ovsdb_jsonrpc_server_add_remote(struct ovsdb_jsonrpc_server *svr, + const char *name) +{ + struct ovsdb_jsonrpc_remote *remote; + struct pstream *listener; + int error; + + error = pstream_open(name, &listener); + if (error && error != EAFNOSUPPORT) { + VLOG_ERR_RL(&rl, "%s: listen failed: %s", name, strerror(error)); + return; + } + + remote = xmalloc(sizeof *remote); + remote->server = svr; + remote->listener = listener; + list_init(&remote->sessions); + shash_add(&svr->remotes, name, remote); + + if (!listener) { + ovsdb_jsonrpc_session_create(remote, jsonrpc_session_open(name)); + } +} + +static void +ovsdb_jsonrpc_server_del_remote(struct shash_node *node) +{ + struct ovsdb_jsonrpc_remote *remote = node->data; + + ovsdb_jsonrpc_session_close_all(remote); + pstream_close(remote->listener); + shash_delete(&remote->server->remotes, node); + free(remote); +} + +void +ovsdb_jsonrpc_server_run(struct ovsdb_jsonrpc_server *svr) +{ + struct shash_node *node; + + SHASH_FOR_EACH (node, &svr->remotes) { + struct ovsdb_jsonrpc_remote *remote = node->data; + + if (remote->listener && svr->n_sessions < svr->max_sessions) { + struct stream *stream; + int error; + + error = pstream_accept(remote->listener, &stream); + if (!error) { + struct jsonrpc_session *js; + js = jsonrpc_session_open_unreliably(jsonrpc_open(stream)); + ovsdb_jsonrpc_session_create(remote, js); + } else if (error != EAGAIN) { + VLOG_WARN_RL(&rl, "%s: accept failed: %s", + pstream_get_name(remote->listener), + strerror(error)); + } + } + + ovsdb_jsonrpc_session_run_all(remote); + } +} + +void +ovsdb_jsonrpc_server_wait(struct ovsdb_jsonrpc_server *svr) +{ + struct shash_node *node; + + SHASH_FOR_EACH (node, &svr->remotes) { + struct ovsdb_jsonrpc_remote *remote = node->data; + + if (remote->listener && svr->n_sessions < svr->max_sessions) { + pstream_wait(remote->listener); + } + + ovsdb_jsonrpc_session_wait_all(remote); + } +} + +/* JSON-RPC database server session. */ + +struct ovsdb_jsonrpc_session { + struct ovsdb_jsonrpc_remote *remote; + struct list node; /* Element in remote's sessions list. */ + + /* Triggers. */ + struct hmap triggers; /* Hmap of "struct ovsdb_jsonrpc_trigger"s. */ + struct list completions; /* Completed triggers. */ + + /* Monitors. */ + struct hmap monitors; /* Hmap of "struct ovsdb_jsonrpc_monitor"s. */ + + /* Network connectivity. */ + struct jsonrpc_session *js; /* JSON-RPC session. */ + unsigned int js_seqno; /* Last jsonrpc_session_get_seqno() value. */ +}; + +static void ovsdb_jsonrpc_session_close(struct ovsdb_jsonrpc_session *); +static int ovsdb_jsonrpc_session_run(struct ovsdb_jsonrpc_session *); +static void ovsdb_jsonrpc_session_wait(struct ovsdb_jsonrpc_session *); +static void ovsdb_jsonrpc_session_got_request(struct ovsdb_jsonrpc_session *, + struct jsonrpc_msg *); +static void ovsdb_jsonrpc_session_got_notify(struct ovsdb_jsonrpc_session *, + struct jsonrpc_msg *); + +static struct ovsdb_jsonrpc_session * +ovsdb_jsonrpc_session_create(struct ovsdb_jsonrpc_remote *remote, + struct jsonrpc_session *js) +{ + struct ovsdb_jsonrpc_session *s; + + s = xzalloc(sizeof *s); + s->remote = remote; + list_push_back(&remote->sessions, &s->node); + hmap_init(&s->triggers); + hmap_init(&s->monitors); + list_init(&s->completions); + s->js = js; + s->js_seqno = jsonrpc_session_get_seqno(js); + + remote->server->n_sessions++; + + return s; +} + +static void +ovsdb_jsonrpc_session_close(struct ovsdb_jsonrpc_session *s) +{ + ovsdb_jsonrpc_monitor_remove_all(s); + jsonrpc_session_close(s->js); + list_remove(&s->node); + s->remote->server->n_sessions--; + free(s); +} + +static int +ovsdb_jsonrpc_session_run(struct ovsdb_jsonrpc_session *s) +{ + jsonrpc_session_run(s->js); + if (s->js_seqno != jsonrpc_session_get_seqno(s->js)) { + s->js_seqno = jsonrpc_session_get_seqno(s->js); + ovsdb_jsonrpc_trigger_complete_all(s); + ovsdb_jsonrpc_monitor_remove_all(s); + } + + ovsdb_jsonrpc_trigger_complete_done(s); + + if (!jsonrpc_session_get_backlog(s->js)) { + struct jsonrpc_msg *msg = jsonrpc_session_recv(s->js); + if (msg) { + if (msg->type == JSONRPC_REQUEST) { + ovsdb_jsonrpc_session_got_request(s, msg); + } else if (msg->type == JSONRPC_NOTIFY) { + ovsdb_jsonrpc_session_got_notify(s, msg); + } else { + VLOG_WARN("%s: received unexpected %s message", + jsonrpc_session_get_name(s->js), + jsonrpc_msg_type_to_string(msg->type)); + jsonrpc_session_force_reconnect(s->js); + jsonrpc_msg_destroy(msg); + } + } + } + return jsonrpc_session_is_alive(s->js) ? 0 : ETIMEDOUT; +} + +static void +ovsdb_jsonrpc_session_run_all(struct ovsdb_jsonrpc_remote *remote) +{ + struct ovsdb_jsonrpc_session *s, *next; + + LIST_FOR_EACH_SAFE (s, next, struct ovsdb_jsonrpc_session, node, + &remote->sessions) { + int error = ovsdb_jsonrpc_session_run(s); + if (error) { + ovsdb_jsonrpc_session_close(s); + } + } +} + +static void +ovsdb_jsonrpc_session_wait(struct ovsdb_jsonrpc_session *s) +{ + jsonrpc_session_wait(s->js); + if (!jsonrpc_session_get_backlog(s->js)) { + jsonrpc_session_recv_wait(s->js); + } +} + +static void +ovsdb_jsonrpc_session_wait_all(struct ovsdb_jsonrpc_remote *remote) +{ + struct ovsdb_jsonrpc_session *s; + + LIST_FOR_EACH (s, struct ovsdb_jsonrpc_session, node, &remote->sessions) { + ovsdb_jsonrpc_session_wait(s); + } +} + +static void +ovsdb_jsonrpc_session_close_all(struct ovsdb_jsonrpc_remote *remote) +{ + struct ovsdb_jsonrpc_session *s, *next; + + LIST_FOR_EACH_SAFE (s, next, struct ovsdb_jsonrpc_session, node, + &remote->sessions) { + ovsdb_jsonrpc_session_close(s); + } +} + +static const char * +get_db_name(const struct ovsdb_jsonrpc_session *s) +{ + return s->remote->server->db->schema->name; +} + +static struct jsonrpc_msg * +ovsdb_jsonrpc_check_db_name(const struct ovsdb_jsonrpc_session *s, + const struct jsonrpc_msg *request) +{ + struct json_array *params; + const char *want_db_name; + const char *have_db_name; + struct ovsdb_error *error; + struct jsonrpc_msg *reply; + + params = json_array(request->params); + if (!params->n || params->elems[0]->type != JSON_STRING) { + error = ovsdb_syntax_error( + request->params, NULL, + "%s request params must begin with ", request->method); + goto error; + } + + want_db_name = params->elems[0]->u.string; + have_db_name = get_db_name(s); + if (strcmp(want_db_name, have_db_name)) { + error = ovsdb_syntax_error( + request->params, "unknown database", + "%s request specifies unknown database %s", + request->method, want_db_name); + goto error; + } + + return NULL; + +error: + reply = jsonrpc_create_reply(ovsdb_error_to_json(error), request->id); + ovsdb_error_destroy(error); + return reply; +} + +static struct jsonrpc_msg * +execute_transaction(struct ovsdb_jsonrpc_session *s, + struct jsonrpc_msg *request) +{ + ovsdb_jsonrpc_trigger_create(s, request->id, request->params); + request->id = NULL; + request->params = NULL; + jsonrpc_msg_destroy(request); + return NULL; +} + +static void +ovsdb_jsonrpc_session_got_request(struct ovsdb_jsonrpc_session *s, + struct jsonrpc_msg *request) +{ + struct jsonrpc_msg *reply; + + if (!strcmp(request->method, "transact")) { + reply = ovsdb_jsonrpc_check_db_name(s, request); + if (!reply) { + reply = execute_transaction(s, request); + } + } else if (!strcmp(request->method, "monitor")) { + reply = ovsdb_jsonrpc_check_db_name(s, request); + if (!reply) { + reply = jsonrpc_create_reply( + ovsdb_jsonrpc_monitor_create(s, request->params), request->id); + } + } else if (!strcmp(request->method, "monitor_cancel")) { + reply = ovsdb_jsonrpc_monitor_cancel(s, json_array(request->params), + request->id); + } else if (!strcmp(request->method, "get_schema")) { + reply = ovsdb_jsonrpc_check_db_name(s, request); + if (!reply) { + reply = jsonrpc_create_reply( + ovsdb_schema_to_json(s->remote->server->db->schema), + request->id); + } + } else if (!strcmp(request->method, "list_dbs")) { + reply = jsonrpc_create_reply( + json_array_create_1(json_string_create(get_db_name(s))), + request->id); + } else if (!strcmp(request->method, "echo")) { + reply = jsonrpc_create_reply(json_clone(request->params), request->id); + } else { + reply = jsonrpc_create_error(json_string_create("unknown method"), + request->id); + } + + if (reply) { + jsonrpc_msg_destroy(request); + jsonrpc_session_send(s->js, reply); + } +} + +static void +execute_cancel(struct ovsdb_jsonrpc_session *s, struct jsonrpc_msg *request) +{ + if (json_array(request->params)->n == 1) { + struct ovsdb_jsonrpc_trigger *t; + struct json *id; + + id = request->params->u.array.elems[0]; + t = ovsdb_jsonrpc_trigger_find(s, id, json_hash(id, 0)); + if (t) { + ovsdb_jsonrpc_trigger_complete(t); + } + } +} + +static void +ovsdb_jsonrpc_session_got_notify(struct ovsdb_jsonrpc_session *s, + struct jsonrpc_msg *request) +{ + if (!strcmp(request->method, "cancel")) { + execute_cancel(s, request); + } + jsonrpc_msg_destroy(request); +} + +/* JSON-RPC database server triggers. + * + * (Every transaction is treated as a trigger even if it doesn't actually have + * any "wait" operations.) */ + +struct ovsdb_jsonrpc_trigger { + struct ovsdb_trigger trigger; + struct ovsdb_jsonrpc_session *session; + struct hmap_node hmap_node; /* In session's "triggers" hmap. */ + struct json *id; +}; + +static void +ovsdb_jsonrpc_trigger_create(struct ovsdb_jsonrpc_session *s, + struct json *id, struct json *params) +{ + struct ovsdb_jsonrpc_trigger *t; + size_t hash; + + /* Check for duplicate ID. */ + hash = json_hash(id, 0); + t = ovsdb_jsonrpc_trigger_find(s, id, hash); + if (t) { + struct jsonrpc_msg *msg; + + msg = jsonrpc_create_error(json_string_create("duplicate request ID"), + id); + jsonrpc_session_send(s->js, msg); + json_destroy(id); + json_destroy(params); + return; + } + + /* Insert into trigger table. */ + t = xmalloc(sizeof *t); + ovsdb_trigger_init(s->remote->server->db, + &t->trigger, params, &s->completions, + time_msec()); + t->session = s; + t->id = id; + hmap_insert(&s->triggers, &t->hmap_node, hash); + + /* Complete early if possible. */ + if (ovsdb_trigger_is_complete(&t->trigger)) { + ovsdb_jsonrpc_trigger_complete(t); + } +} + +static struct ovsdb_jsonrpc_trigger * +ovsdb_jsonrpc_trigger_find(struct ovsdb_jsonrpc_session *s, + const struct json *id, size_t hash) +{ + struct ovsdb_jsonrpc_trigger *t; + + HMAP_FOR_EACH_WITH_HASH (t, struct ovsdb_jsonrpc_trigger, hmap_node, hash, + &s->triggers) { + if (json_equal(t->id, id)) { + return t; + } + } + + return NULL; +} + +static void +ovsdb_jsonrpc_trigger_complete(struct ovsdb_jsonrpc_trigger *t) +{ + struct ovsdb_jsonrpc_session *s = t->session; + + if (jsonrpc_session_is_connected(s->js)) { + struct jsonrpc_msg *reply; + struct json *result; + + result = ovsdb_trigger_steal_result(&t->trigger); + if (result) { + reply = jsonrpc_create_reply(result, t->id); + } else { + reply = jsonrpc_create_error(json_string_create("canceled"), + t->id); + } + jsonrpc_session_send(s->js, reply); + } + + json_destroy(t->id); + ovsdb_trigger_destroy(&t->trigger); + hmap_remove(&s->triggers, &t->hmap_node); + free(t); +} + +static void +ovsdb_jsonrpc_trigger_complete_all(struct ovsdb_jsonrpc_session *s) +{ + struct ovsdb_jsonrpc_trigger *t, *next; + HMAP_FOR_EACH_SAFE (t, next, struct ovsdb_jsonrpc_trigger, hmap_node, + &s->triggers) { + ovsdb_jsonrpc_trigger_complete(t); + } +} + +static void +ovsdb_jsonrpc_trigger_complete_done(struct ovsdb_jsonrpc_session *s) +{ + while (!list_is_empty(&s->completions)) { + struct ovsdb_jsonrpc_trigger *t + = CONTAINER_OF(s->completions.next, + struct ovsdb_jsonrpc_trigger, trigger.node); + ovsdb_jsonrpc_trigger_complete(t); + } +} + +/* JSON-RPC database table monitors. */ + +enum ovsdb_jsonrpc_monitor_selection { + OJMS_INITIAL = 1 << 0, /* All rows when monitor is created. */ + OJMS_INSERT = 1 << 1, /* New rows. */ + OJMS_DELETE = 1 << 2, /* Deleted rows. */ + OJMS_MODIFY = 1 << 3 /* Modified rows. */ +}; + +struct ovsdb_jsonrpc_monitor_table { + const struct ovsdb_table *table; + enum ovsdb_jsonrpc_monitor_selection select; + struct ovsdb_column_set columns; +}; + +struct ovsdb_jsonrpc_monitor { + struct ovsdb_replica replica; + struct ovsdb_jsonrpc_session *session; + struct hmap_node node; /* In ovsdb_jsonrpc_session's "monitors". */ + + struct json *monitor_id; + struct shash tables; /* Holds "struct ovsdb_jsonrpc_monitor_table"s. */ +}; + +static const struct ovsdb_replica_class ovsdb_jsonrpc_replica_class; + +struct ovsdb_jsonrpc_monitor *ovsdb_jsonrpc_monitor_find( + struct ovsdb_jsonrpc_session *, const struct json *monitor_id); +static void ovsdb_jsonrpc_monitor_destroy(struct ovsdb_replica *); +static struct json *ovsdb_jsonrpc_monitor_get_initial( + const struct ovsdb_jsonrpc_monitor *); + +static bool +parse_bool(struct ovsdb_parser *parser, const char *name, bool default_value) +{ + const struct json *json; + + json = ovsdb_parser_member(parser, name, OP_BOOLEAN | OP_OPTIONAL); + return json ? json_boolean(json) : default_value; +} + +struct ovsdb_jsonrpc_monitor * +ovsdb_jsonrpc_monitor_find(struct ovsdb_jsonrpc_session *s, + const struct json *monitor_id) +{ + struct ovsdb_jsonrpc_monitor *m; + + HMAP_FOR_EACH_WITH_HASH (m, struct ovsdb_jsonrpc_monitor, node, + json_hash(monitor_id, 0), &s->monitors) { + if (json_equal(m->monitor_id, monitor_id)) { + return m; + } + } + + return NULL; +} + +static struct json * +ovsdb_jsonrpc_monitor_create(struct ovsdb_jsonrpc_session *s, + struct json *params) +{ + struct ovsdb_jsonrpc_monitor *m = NULL; + struct json *monitor_id, *monitor_requests; + struct ovsdb_error *error = NULL; + struct shash_node *node; + struct json *json; + + if (json_array(params)->n != 3) { + error = ovsdb_syntax_error(params, NULL, "invalid parameters"); + goto error; + } + monitor_id = params->u.array.elems[1]; + monitor_requests = params->u.array.elems[2]; + if (monitor_requests->type != JSON_OBJECT) { + error = ovsdb_syntax_error(monitor_requests, NULL, + "monitor-requests must be object"); + goto error; + } + + if (ovsdb_jsonrpc_monitor_find(s, monitor_id)) { + error = ovsdb_syntax_error(monitor_id, NULL, "duplicate monitor ID"); + goto error; + } + + m = xzalloc(sizeof *m); + ovsdb_replica_init(&m->replica, &ovsdb_jsonrpc_replica_class); + ovsdb_add_replica(s->remote->server->db, &m->replica); + m->session = s; + hmap_insert(&s->monitors, &m->node, json_hash(monitor_id, 0)); + m->monitor_id = json_clone(monitor_id); + shash_init(&m->tables); + + SHASH_FOR_EACH (node, json_object(monitor_requests)) { + const struct ovsdb_table *table; + struct ovsdb_jsonrpc_monitor_table *mt; + const struct json *columns_json, *select_json; + struct ovsdb_parser parser; + + table = ovsdb_get_table(s->remote->server->db, node->name); + if (!table) { + error = ovsdb_syntax_error(NULL, NULL, + "no table named %s", node->name); + goto error; + } + + mt = xzalloc(sizeof *mt); + mt->table = table; + mt->select = OJMS_INITIAL | OJMS_INSERT | OJMS_DELETE | OJMS_MODIFY; + ovsdb_column_set_init(&mt->columns); + shash_add(&m->tables, table->schema->name, mt); + + ovsdb_parser_init(&parser, node->data, "table %s", node->name); + columns_json = ovsdb_parser_member(&parser, "columns", + OP_ARRAY | OP_OPTIONAL); + select_json = ovsdb_parser_member(&parser, "select", + OP_OBJECT | OP_OPTIONAL); + error = ovsdb_parser_finish(&parser); + if (error) { + goto error; + } + + if (columns_json) { + error = ovsdb_column_set_from_json(columns_json, table, + &mt->columns); + if (error) { + goto error; + } + } else { + struct shash_node *node; + + SHASH_FOR_EACH (node, &table->schema->columns) { + const struct ovsdb_column *column = node->data; + if (column->index != OVSDB_COL_UUID) { + ovsdb_column_set_add(&mt->columns, column); + } + } + } + + if (select_json) { + mt->select = 0; + ovsdb_parser_init(&parser, select_json, "table %s select", + table->schema->name); + if (parse_bool(&parser, "initial", true)) { + mt->select |= OJMS_INITIAL; + } + if (parse_bool(&parser, "insert", true)) { + mt->select |= OJMS_INSERT; + } + if (parse_bool(&parser, "delete", true)) { + mt->select |= OJMS_DELETE; + } + if (parse_bool(&parser, "modify", true)) { + mt->select |= OJMS_MODIFY; + } + error = ovsdb_parser_finish(&parser); + if (error) { + goto error; + } + } + } + + return ovsdb_jsonrpc_monitor_get_initial(m); + +error: + if (m) { + ovsdb_remove_replica(s->remote->server->db, &m->replica); + } + + json = ovsdb_error_to_json(error); + ovsdb_error_destroy(error); + return json; +} + +static struct jsonrpc_msg * +ovsdb_jsonrpc_monitor_cancel(struct ovsdb_jsonrpc_session *s, + struct json_array *params, + const struct json *request_id) +{ + if (params->n != 1) { + return jsonrpc_create_error(json_string_create("invalid parameters"), + request_id); + } else { + struct ovsdb_jsonrpc_monitor *m; + + m = ovsdb_jsonrpc_monitor_find(s, params->elems[0]); + if (!m) { + return jsonrpc_create_error(json_string_create("unknown monitor"), + request_id); + } else { + ovsdb_remove_replica(s->remote->server->db, &m->replica); + return jsonrpc_create_reply(json_object_create(), request_id); + } + } +} + +static void +ovsdb_jsonrpc_monitor_remove_all(struct ovsdb_jsonrpc_session *s) +{ + struct ovsdb_jsonrpc_monitor *m, *next; + + HMAP_FOR_EACH_SAFE (m, next, + struct ovsdb_jsonrpc_monitor, node, &s->monitors) { + ovsdb_remove_replica(s->remote->server->db, &m->replica); + } +} + +static struct ovsdb_jsonrpc_monitor * +ovsdb_jsonrpc_monitor_cast(struct ovsdb_replica *replica) +{ + assert(replica->class == &ovsdb_jsonrpc_replica_class); + return CONTAINER_OF(replica, struct ovsdb_jsonrpc_monitor, replica); +} + +struct ovsdb_jsonrpc_monitor_aux { + bool initial; /* Sending initial contents of table? */ + const struct ovsdb_jsonrpc_monitor *monitor; + struct json *json; /* JSON for the whole transaction. */ + + /* Current table. */ + struct ovsdb_jsonrpc_monitor_table *mt; + struct json *table_json; /* JSON for table's transaction. */ +}; + +static bool +ovsdb_jsonrpc_monitor_change_cb(const struct ovsdb_row *old, + const struct ovsdb_row *new, + void *aux_) +{ + struct ovsdb_jsonrpc_monitor_aux *aux = aux_; + const struct ovsdb_jsonrpc_monitor *m = aux->monitor; + struct ovsdb_table *table = new ? new->table : old->table; + enum ovsdb_jsonrpc_monitor_selection type; + struct json *old_json, *new_json; + struct json *row_json; + char uuid[UUID_LEN + 1]; + int n_changed; + size_t i; + + if (!aux->mt || table != aux->mt->table) { + aux->mt = shash_find_data(&m->tables, table->schema->name); + aux->table_json = NULL; + if (!aux->mt) { + /* We don't care about rows in this table at all. Tell the caller + * to skip it. */ + return false; + } + } + + type = (aux->initial ? OJMS_INITIAL + : !old ? OJMS_INSERT + : !new ? OJMS_DELETE + : OJMS_MODIFY); + if (!(aux->mt->select & type)) { + /* We don't care about this type of change (but do want to be called + * back for changes to other rows in the same table). */ + return true; + } + + old_json = new_json = NULL; + n_changed = 0; + for (i = 0; i < aux->mt->columns.n_columns; i++) { + const struct ovsdb_column *column = aux->mt->columns.columns[i]; + unsigned int idx = column->index; + bool changed = false; + + if (type == OJMS_MODIFY) { + changed = !ovsdb_datum_equals(&old->fields[idx], + &new->fields[idx], &column->type); + n_changed += changed; + } + if (changed || type == OJMS_DELETE) { + if (!old_json) { + old_json = json_object_create(); + } + json_object_put(old_json, column->name, + ovsdb_datum_to_json(&old->fields[idx], + &column->type)); + } + if (type & (OJMS_INITIAL | OJMS_INSERT | OJMS_MODIFY)) { + if (!new_json) { + new_json = json_object_create(); + } + json_object_put(new_json, column->name, + ovsdb_datum_to_json(&new->fields[idx], + &column->type)); + } + } + if ((type == OJMS_MODIFY && !n_changed) || (!old_json && !new_json)) { + /* No reportable changes. */ + json_destroy(old_json); + json_destroy(new_json); + return true; + } + + /* Create JSON object for transaction overall. */ + if (!aux->json) { + aux->json = json_object_create(); + } + + /* Create JSON object for transaction on this table. */ + if (!aux->table_json) { + aux->table_json = json_object_create(); + json_object_put(aux->json, aux->mt->table->schema->name, + aux->table_json); + } + + /* Create JSON object for transaction on this row. */ + row_json = json_object_create(); + if (old_json) { + json_object_put(row_json, "old", old_json); + } + if (new_json) { + json_object_put(row_json, "new", new_json); + } + + /* Add JSON row to JSON table. */ + snprintf(uuid, sizeof uuid, + UUID_FMT, UUID_ARGS(ovsdb_row_get_uuid(new ? new : old))); + json_object_put(aux->table_json, uuid, row_json); + + return true; +} + +static void +ovsdb_jsonrpc_monitor_init_aux(struct ovsdb_jsonrpc_monitor_aux *aux, + const struct ovsdb_jsonrpc_monitor *m, + bool initial) +{ + aux->initial = initial; + aux->monitor = m; + aux->json = NULL; + aux->mt = NULL; + aux->table_json = NULL; +} + +static struct ovsdb_error * +ovsdb_jsonrpc_monitor_commit(struct ovsdb_replica *replica, - const struct ovsdb_txn *txn, bool durable UNUSED) ++ const struct ovsdb_txn *txn, ++ bool durable OVS_UNUSED) +{ + struct ovsdb_jsonrpc_monitor *m = ovsdb_jsonrpc_monitor_cast(replica); + struct ovsdb_jsonrpc_monitor_aux aux; + + ovsdb_jsonrpc_monitor_init_aux(&aux, m, false); + ovsdb_txn_for_each_change(txn, ovsdb_jsonrpc_monitor_change_cb, &aux); + if (aux.json) { + struct jsonrpc_msg *msg; + struct json *params; + + params = json_array_create_2(json_clone(aux.monitor->monitor_id), + aux.json); + msg = jsonrpc_create_notify("update", params); + jsonrpc_session_send(aux.monitor->session->js, msg); + } + + return NULL; +} + +static struct json * +ovsdb_jsonrpc_monitor_get_initial(const struct ovsdb_jsonrpc_monitor *m) +{ + struct ovsdb_jsonrpc_monitor_aux aux; + struct shash_node *node; + + ovsdb_jsonrpc_monitor_init_aux(&aux, m, true); + SHASH_FOR_EACH (node, &m->tables) { + struct ovsdb_jsonrpc_monitor_table *mt = node->data; + + if (mt->select & OJMS_INITIAL) { + struct ovsdb_row *row; + + HMAP_FOR_EACH (row, struct ovsdb_row, hmap_node, + &mt->table->rows) { + ovsdb_jsonrpc_monitor_change_cb(NULL, row, &aux); + } + } + } + return aux.json ? aux.json : json_object_create(); +} + +static void +ovsdb_jsonrpc_monitor_destroy(struct ovsdb_replica *replica) +{ + struct ovsdb_jsonrpc_monitor *m = ovsdb_jsonrpc_monitor_cast(replica); + struct shash_node *node; + + json_destroy(m->monitor_id); + SHASH_FOR_EACH (node, &m->tables) { + struct ovsdb_jsonrpc_monitor_table *mt = node->data; + ovsdb_column_set_destroy(&mt->columns); + free(mt); + } + shash_destroy(&m->tables); + hmap_remove(&m->session->monitors, &m->node); + free(m); +} + +static const struct ovsdb_replica_class ovsdb_jsonrpc_replica_class = { + ovsdb_jsonrpc_monitor_commit, + ovsdb_jsonrpc_monitor_destroy +}; diff --combined ovsdb/ovsdb-client.c index df24e371c,000000000..acc418542 mode 100644,000000..100644 --- a/ovsdb/ovsdb-client.c +++ b/ovsdb/ovsdb-client.c @@@ -1,926 -1,0 +1,926 @@@ +/* + * Copyright (c) 2009, 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 +#include +#include +#include +#include +#include +#include +#include + +#include "command-line.h" +#include "column.h" +#include "compiler.h" +#include "daemon.h" +#include "dynamic-string.h" +#include "json.h" +#include "jsonrpc.h" +#include "ovsdb.h" +#include "ovsdb-error.h" +#include "stream.h" +#include "stream-ssl.h" +#include "table.h" +#include "timeval.h" +#include "util.h" + +#include "vlog.h" +#define THIS_MODULE VLM_ovsdb_client + +/* --format: Output formatting. */ +static enum { + FMT_TABLE, /* Textual table. */ + FMT_HTML, /* HTML table. */ + FMT_CSV /* Comma-separated lines. */ +} output_format; + +/* --wide: For --format=table, the maximum output width. */ +static int output_width; + +/* --no-headings: Whether table output should include headings. */ +static int output_headings = true; + +/* --pretty: Flags to pass to json_to_string(). */ +static int json_flags = JSSF_SORT; + +static const struct command all_commands[]; + +static void usage(void) NO_RETURN; +static void parse_options(int argc, char *argv[]); + +int +main(int argc, char *argv[]) +{ + proctitle_init(argc, argv); + set_program_name(argv[0]); + time_init(); + vlog_init(); + parse_options(argc, argv); + signal(SIGPIPE, SIG_IGN); + run_command(argc - optind, argv + optind, all_commands); + return 0; +} + +static void +parse_options(int argc, char *argv[]) +{ + enum { + OPT_BOOTSTRAP_CA_CERT = UCHAR_MAX + 1 + }; + static struct option long_options[] = { + {"wide", no_argument, &output_width, INT_MAX}, + {"format", required_argument, 0, 'f'}, + {"no-headings", no_argument, &output_headings, 0}, + {"pretty", no_argument, &json_flags, JSSF_PRETTY | JSSF_SORT}, + {"verbose", optional_argument, 0, 'v'}, + {"help", no_argument, 0, 'h'}, + {"version", no_argument, 0, 'V'}, + DAEMON_LONG_OPTIONS, +#ifdef HAVE_OPENSSL + {"bootstrap-ca-cert", required_argument, 0, OPT_BOOTSTRAP_CA_CERT}, + STREAM_SSL_LONG_OPTIONS +#endif + {0, 0, 0, 0}, + }; + char *short_options = long_options_to_short_options(long_options); + + output_width = isatty(fileno(stdout)) ? 79 : INT_MAX; + for (;;) { + int c; + + c = getopt_long(argc, argv, short_options, long_options, NULL); + if (c == -1) { + break; + } + + switch (c) { + case 'f': + if (!strcmp(optarg, "table")) { + output_format = FMT_TABLE; + } else if (!strcmp(optarg, "html")) { + output_format = FMT_HTML; + } else if (!strcmp(optarg, "csv")) { + output_format = FMT_CSV; + } else { + ovs_fatal(0, "unknown output format \"%s\"", optarg); + } + break; + + case 'w': + output_width = INT_MAX; + break; + + case 'h': + usage(); + + case 'V': + OVS_PRINT_VERSION(0, 0); + exit(EXIT_SUCCESS); + + case 'v': + vlog_set_verbosity(optarg); + break; + + DAEMON_OPTION_HANDLERS + +#ifdef HAVE_OPENSSL + STREAM_SSL_OPTION_HANDLERS + + case OPT_BOOTSTRAP_CA_CERT: + stream_ssl_set_ca_cert_file(optarg, true); + break; +#endif + + case '?': + exit(EXIT_FAILURE); + + case 0: + /* getopt_long() already set the value for us. */ + break; + + default: + abort(); + } + } + free(short_options); +} + +static void +usage(void) +{ + printf("%s: Open vSwitch database JSON-RPC client\n" + "usage: %s [OPTIONS] COMMAND [ARG...]\n" + "\nValid commands are:\n" + "\n list-dbs SERVER\n" + " list databases available on SERVER\n" + "\n get-schema SERVER DATABASE\n" + " retrieve schema for DATABASE from SERVER\n" + "\n list-tables SERVER DATABSE\n" + " list tables for DATABSAE on SERVER\n" + "\n list-columns SERVER DATABASE [TABLE]\n" + " list columns in TABLE (or all tables) in DATABASE on SERVER\n" + "\n transact SERVER TRANSACTION\n" + " run TRANSACTION (a JSON array of operations) on SERVER\n" + " and print the results as JSON on stdout\n" + "\n monitor SERVER DATABASE TABLE [COLUMN,...] [SELECT,...]\n" + " monitor contents of (COLUMNs in) TABLE in DATABASE on SERVER\n" + " Valid SELECTs are: initial, insert, delete, modify\n", + program_name, program_name); + stream_usage("SERVER", true, true, true); + printf("\nOutput formatting options:\n" + " -f, --format=FORMAT set output formatting to FORMAT\n" + " (\"table\", \"html\", or \"csv\"\n" + " --wide don't limit TTY lines to 79 bytes\n" + " --no-headings omit table heading row\n" + " --pretty pretty-print JSON in output"); + daemon_usage(); + vlog_usage(); + printf("\nOther options:\n" + " -h, --help display this help message\n" + " -V, --version display version information\n"); + exit(EXIT_SUCCESS); +} + +static struct json * +parse_json(const char *s) +{ + struct json *json = json_from_string(s); + if (json->type == JSON_STRING) { + ovs_fatal(0, "\"%s\": %s", s, json->u.string); + } + return json; +} + +static struct jsonrpc * +open_jsonrpc(const char *server) +{ + struct stream *stream; + int error; + + error = stream_open_block(server, &stream); + if (error == EAFNOSUPPORT) { + struct pstream *pstream; + + error = pstream_open(server, &pstream); + if (error) { + ovs_fatal(error, "failed to connect or listen to \"%s\"", server); + } + + VLOG_INFO("%s: waiting for connection...", server); + error = pstream_accept_block(pstream, &stream); + if (error) { + ovs_fatal(error, "failed to accept connection on \"%s\"", server); + } + + pstream_close(pstream); + } else if (error) { + ovs_fatal(error, "failed to connect to \"%s\"", server); + } + + return jsonrpc_open(stream); +} + +static void +print_json(struct json *json) +{ + char *string = json_to_string(json, json_flags); + fputs(string, stdout); + free(string); +} + +static void +print_and_free_json(struct json *json) +{ + print_json(json); + json_destroy(json); +} + +static void +check_ovsdb_error(struct ovsdb_error *error) +{ + if (error) { + ovs_fatal(0, "%s", ovsdb_error_to_string(error)); + } +} + +static struct ovsdb_schema * +fetch_schema_from_rpc(struct jsonrpc *rpc, const char *database) +{ + struct jsonrpc_msg *request, *reply; + struct ovsdb_schema *schema; + int error; + + request = jsonrpc_create_request("get_schema", + json_array_create_1( + json_string_create(database)), + NULL); + error = jsonrpc_transact_block(rpc, request, &reply); + if (error) { + ovs_fatal(error, "transaction failed"); + } + check_ovsdb_error(ovsdb_schema_from_json(reply->result, &schema)); + jsonrpc_msg_destroy(reply); + + return schema; +} + +static struct ovsdb_schema * +fetch_schema(const char *server, const char *database) +{ + struct ovsdb_schema *schema; + struct jsonrpc *rpc; + + rpc = open_jsonrpc(server); + schema = fetch_schema_from_rpc(rpc, database); + jsonrpc_close(rpc); + + return schema; +} + +struct column { + char *heading; + int width; +}; + +struct table { + char **cells; + struct column *columns; + size_t n_columns, allocated_columns; + size_t n_rows, allocated_rows; + size_t current_column; +}; + +static void +table_init(struct table *table) +{ + memset(table, 0, sizeof *table); +} + +static void +table_destroy(struct table *table) +{ + size_t i; + + for (i = 0; i < table->n_columns; i++) { + free(table->columns[i].heading); + } + free(table->columns); + + for (i = 0; i < table->n_columns * table->n_rows; i++) { + free(table->cells[i]); + } + free(table->cells); +} + +static void +table_add_column(struct table *table, const char *heading, ...) + PRINTF_FORMAT(2, 3); + +static void +table_add_column(struct table *table, const char *heading, ...) +{ + struct column *column; + va_list args; + + assert(!table->n_rows); + if (table->n_columns >= table->allocated_columns) { + table->columns = x2nrealloc(table->columns, &table->allocated_columns, + sizeof *table->columns); + } + column = &table->columns[table->n_columns++]; + + va_start(args, heading); + column->heading = xvasprintf(heading, args); + column->width = strlen(column->heading); + va_end(args); +} + +static char ** +table_cell__(const struct table *table, size_t row, size_t column) +{ + return &table->cells[column + row * table->n_columns]; +} + +static void +table_add_row(struct table *table) +{ + size_t x, y; + + if (table->n_rows >= table->allocated_rows) { + table->cells = x2nrealloc(table->cells, &table->allocated_rows, + table->n_columns * sizeof *table->cells); + } + + y = table->n_rows++; + table->current_column = 0; + for (x = 0; x < table->n_columns; x++) { + *table_cell__(table, y, x) = NULL; + } +} + +static void +table_add_cell_nocopy(struct table *table, char *s) +{ + size_t x, y; + int length; + + assert(table->n_rows > 0); + assert(table->current_column < table->n_columns); + + x = table->current_column++; + y = table->n_rows - 1; + *table_cell__(table, y, x) = s; + + length = strlen(s); + if (length > table->columns[x].width) { + table->columns[x].width = length; + } +} + +static void +table_add_cell(struct table *table, const char *format, ...) +{ + va_list args; + + va_start(args, format); + table_add_cell_nocopy(table, xvasprintf(format, args)); + va_end(args); +} + +static void +table_print_table_line__(struct ds *line, size_t max_width) +{ + ds_truncate(line, max_width); + puts(ds_cstr(line)); + ds_clear(line); +} + +static void +table_print_table__(const struct table *table) +{ + struct ds line = DS_EMPTY_INITIALIZER; + size_t x, y; + + if (output_headings) { + for (x = 0; x < table->n_columns; x++) { + const struct column *column = &table->columns[x]; + if (x) { + ds_put_char(&line, ' '); + } + ds_put_format(&line, "%-*s", column->width, column->heading); + } + table_print_table_line__(&line, output_width); + + for (x = 0; x < table->n_columns; x++) { + const struct column *column = &table->columns[x]; + int i; + + if (x) { + ds_put_char(&line, ' '); + } + for (i = 0; i < column->width; i++) { + ds_put_char(&line, '-'); + } + } + table_print_table_line__(&line, output_width); + } + + for (y = 0; y < table->n_rows; y++) { + for (x = 0; x < table->n_columns; x++) { + const char *cell = *table_cell__(table, y, x); + if (x) { + ds_put_char(&line, ' '); + } + ds_put_format(&line, "%-*s", table->columns[x].width, cell); + } + table_print_table_line__(&line, output_width); + } + + ds_destroy(&line); +} + +static void +table_print_html_cell__(const char *element, const char *content) +{ + const char *p; + + printf(" <%s>", element); + for (p = content; *p != '\0'; p++) { + switch (*p) { + case '&': + fputs("&", stdout); + break; + case '<': + fputs("<", stdout); + break; + case '>': + fputs(">", stdout); + break; + default: + putchar(*p); + break; + } + } + printf("\n", element); +} + +static void +table_print_html__(const struct table *table) +{ + size_t x, y; + + fputs("\n", stdout); + + if (output_headings) { + fputs(" \n", stdout); + for (x = 0; x < table->n_columns; x++) { + const struct column *column = &table->columns[x]; + table_print_html_cell__("th", column->heading); + } + fputs(" \n", stdout); + } + + for (y = 0; y < table->n_rows; y++) { + fputs(" \n", stdout); + for (x = 0; x < table->n_columns; x++) { + table_print_html_cell__("td", *table_cell__(table, y, x)); + } + fputs(" \n", stdout); + } + + fputs("
\n", stdout); +} + +static void +table_print_csv_cell__(const char *content) +{ + const char *p; + + if (!strpbrk(content, "\n\",")) { + fputs(content, stdout); + } else { + putchar('"'); + for (p = content; *p != '\0'; p++) { + switch (*p) { + case '"': + fputs("\"\"", stdout); + break; + default: + putchar(*p); + break; + } + } + putchar('"'); + } +} + +static void +table_print_csv__(const struct table *table) +{ + size_t x, y; + + if (output_headings) { + for (x = 0; x < table->n_columns; x++) { + const struct column *column = &table->columns[x]; + if (x) { + putchar(','); + } + table_print_csv_cell__(column->heading); + } + putchar('\n'); + } + + for (y = 0; y < table->n_rows; y++) { + for (x = 0; x < table->n_columns; x++) { + if (x) { + putchar(','); + } + table_print_csv_cell__(*table_cell__(table, y, x)); + } + putchar('\n'); + } +} + +static void +table_print(const struct table *table) +{ + switch (output_format) { + case FMT_TABLE: + table_print_table__(table); + break; + + case FMT_HTML: + table_print_html__(table); + break; + + case FMT_CSV: + table_print_csv__(table); + break; + } +} + +static void - do_list_dbs(int argc UNUSED, char *argv[]) ++do_list_dbs(int argc OVS_UNUSED, char *argv[]) +{ + struct jsonrpc_msg *request, *reply; + struct jsonrpc *rpc; + int error; + size_t i; + + rpc = open_jsonrpc(argv[1]); + request = jsonrpc_create_request("list_dbs", json_array_create_empty(), + NULL); + error = jsonrpc_transact_block(rpc, request, &reply); + if (error) { + ovs_fatal(error, "transaction failed"); + } + + if (reply->result->type != JSON_ARRAY) { + ovs_fatal(0, "list_dbs response is not array"); + } + + for (i = 0; i < reply->result->u.array.n; i++) { + const struct json *name = reply->result->u.array.elems[i]; + + if (name->type != JSON_STRING) { + ovs_fatal(0, "list_dbs response %zu is not string", i); + } + puts(name->u.string); + } + jsonrpc_msg_destroy(reply); +} + +static void - do_get_schema(int argc UNUSED, char *argv[]) ++do_get_schema(int argc OVS_UNUSED, char *argv[]) +{ + struct ovsdb_schema *schema = fetch_schema(argv[1], argv[2]); + print_and_free_json(ovsdb_schema_to_json(schema)); + ovsdb_schema_destroy(schema); +} + +static void - do_list_tables(int argc UNUSED, char *argv[]) ++do_list_tables(int argc OVS_UNUSED, char *argv[]) +{ + struct ovsdb_schema *schema; + struct shash_node *node; + struct table t; + + schema = fetch_schema(argv[1], argv[2]); + table_init(&t); + table_add_column(&t, "Table"); + table_add_column(&t, "Comment"); + SHASH_FOR_EACH (node, &schema->tables) { + struct ovsdb_table_schema *ts = node->data; + + table_add_row(&t); + table_add_cell(&t, ts->name); + if (ts->comment) { + table_add_cell(&t, ts->comment); + } + } + ovsdb_schema_destroy(schema); + table_print(&t); +} + +static void - do_list_columns(int argc UNUSED, char *argv[]) ++do_list_columns(int argc OVS_UNUSED, char *argv[]) +{ + const char *table_name = argv[3]; + struct ovsdb_schema *schema; + struct shash_node *table_node; + struct table t; + + schema = fetch_schema(argv[1], argv[2]); + table_init(&t); + if (!table_name) { + table_add_column(&t, "Table"); + } + table_add_column(&t, "Column"); + table_add_column(&t, "Type"); + table_add_column(&t, "Comment"); + SHASH_FOR_EACH (table_node, &schema->tables) { + struct ovsdb_table_schema *ts = table_node->data; + + if (!table_name || !strcmp(table_name, ts->name)) { + struct shash_node *column_node; + + SHASH_FOR_EACH (column_node, &ts->columns) { + const struct ovsdb_column *column = column_node->data; + struct json *type = ovsdb_type_to_json(&column->type); + + table_add_row(&t); + if (!table_name) { + table_add_cell(&t, ts->name); + } + table_add_cell(&t, column->name); + table_add_cell_nocopy(&t, json_to_string(type, JSSF_SORT)); + if (column->comment) { + table_add_cell(&t, column->comment); + } + + json_destroy(type); + } + } + } + ovsdb_schema_destroy(schema); + table_print(&t); +} + +static void - do_transact(int argc UNUSED, char *argv[]) ++do_transact(int argc OVS_UNUSED, char *argv[]) +{ + struct jsonrpc_msg *request, *reply; + struct json *transaction; + struct jsonrpc *rpc; + int error; + + transaction = parse_json(argv[2]); + + rpc = open_jsonrpc(argv[1]); + request = jsonrpc_create_request("transact", transaction, NULL); + error = jsonrpc_transact_block(rpc, request, &reply); + if (error) { + ovs_fatal(error, "transaction failed"); + } + if (reply->error) { + ovs_fatal(error, "transaction returned error: %s", + json_to_string(reply->error, json_flags)); + } + print_json(reply->result); + putchar('\n'); + jsonrpc_msg_destroy(reply); + jsonrpc_close(rpc); +} + +static void +monitor_print_row(struct json *row, const char *type, const char *uuid, + const struct ovsdb_column_set *columns, struct table *t) +{ + size_t i; + + if (!row) { + ovs_error(0, "missing %s row", type); + return; + } else if (row->type != JSON_OBJECT) { + ovs_error(0, " is not object"); + return; + } + + table_add_row(t); + table_add_cell(t, uuid); + table_add_cell(t, type); + for (i = 0; i < columns->n_columns; i++) { + const struct ovsdb_column *column = columns->columns[i]; + struct json *value = shash_find_data(json_object(row), column->name); + if (value) { + table_add_cell_nocopy(t, json_to_string(value, JSSF_SORT)); + } else { + table_add_cell(t, ""); + } + } +} + +static void +monitor_print(struct json *table_updates, + const struct ovsdb_table_schema *table, + const struct ovsdb_column_set *columns, bool initial) +{ + struct json *table_update; + struct shash_node *node; + struct table t; + size_t i; + + table_init(&t); + + if (table_updates->type != JSON_OBJECT) { + ovs_error(0, " is not object"); + return; + } + table_update = shash_find_data(json_object(table_updates), table->name); + if (!table_update) { + return; + } + if (table_update->type != JSON_OBJECT) { + ovs_error(0, " is not object"); + return; + } + + table_add_column(&t, "row"); + table_add_column(&t, "action"); + for (i = 0; i < columns->n_columns; i++) { + table_add_column(&t, "%s", columns->columns[i]->name); + } + SHASH_FOR_EACH (node, json_object(table_update)) { + struct json *row_update = node->data; + struct json *old, *new; + + if (row_update->type != JSON_OBJECT) { + ovs_error(0, " is not object"); + continue; + } + old = shash_find_data(json_object(row_update), "old"); + new = shash_find_data(json_object(row_update), "new"); + if (initial) { + monitor_print_row(new, "initial", node->name, columns, &t); + } else if (!old) { + monitor_print_row(new, "insert", node->name, columns, &t); + } else if (!new) { + monitor_print_row(old, "delete", node->name, columns, &t); + } else { + monitor_print_row(old, "old", node->name, columns, &t); + monitor_print_row(new, "new", "", columns, &t); + } + } + table_print(&t); + table_destroy(&t); +} + +static void +do_monitor(int argc, char *argv[]) +{ + const char *server = argv[1]; + const char *database = argv[2]; + const char *table_name = argv[3]; + struct ovsdb_column_set columns = OVSDB_COLUMN_SET_INITIALIZER; + struct ovsdb_table_schema *table; + struct ovsdb_schema *schema; + struct jsonrpc_msg *request; + struct jsonrpc *rpc; + struct json *select, *monitor, *monitor_request, *monitor_requests, + *request_id; + + rpc = open_jsonrpc(server); + + schema = fetch_schema_from_rpc(rpc, database); + table = shash_find_data(&schema->tables, table_name); + if (!table) { + ovs_fatal(0, "%s: %s does not have a table named \"%s\"", + server, database, table_name); + } + + if (argc >= 5 && *argv[4] != '\0') { + char *save_ptr = NULL; + char *token; + + for (token = strtok_r(argv[4], ",", &save_ptr); token != NULL; + token = strtok_r(NULL, ",", &save_ptr)) { + const struct ovsdb_column *column; + column = ovsdb_table_schema_get_column(table, token); + if (!column) { + ovs_fatal(0, "%s: table \"%s\" in %s does not have a " + "column named \"%s\"", + server, table_name, database, token); + } + ovsdb_column_set_add(&columns, column); + } + } else { + struct shash_node *node; + + SHASH_FOR_EACH (node, &table->columns) { + const struct ovsdb_column *column = node->data; + if (column->index != OVSDB_COL_UUID) { + ovsdb_column_set_add(&columns, column); + } + } + } + + if (argc >= 6 && *argv[5] != '\0') { + char *save_ptr = NULL; + char *token; + + select = json_object_create(); + for (token = strtok_r(argv[5], ",", &save_ptr); token != NULL; + token = strtok_r(NULL, ",", &save_ptr)) { + json_object_put(select, token, json_boolean_create(true)); + } + } else { + select = NULL; + } + + monitor_request = json_object_create(); + json_object_put(monitor_request, + "columns", ovsdb_column_set_to_json(&columns)); + if (select) { + json_object_put(monitor_request, "select", select); + } + + monitor_requests = json_object_create(); + json_object_put(monitor_requests, table_name, monitor_request); + + monitor = json_array_create_3(json_string_create(database), + json_null_create(), monitor_requests); + request = jsonrpc_create_request("monitor", monitor, NULL); + request_id = json_clone(request->id); + jsonrpc_send(rpc, request); + for (;;) { + struct jsonrpc_msg *msg; + int error; + + error = jsonrpc_recv_block(rpc, &msg); + if (error) { + ovsdb_schema_destroy(schema); + ovs_fatal(error, "%s: receive failed", server); + } + + if (msg->type == JSONRPC_REQUEST && !strcmp(msg->method, "echo")) { + jsonrpc_send(rpc, jsonrpc_create_reply(json_clone(msg->params), + msg->id)); + } else if (msg->type == JSONRPC_REPLY + && json_equal(msg->id, request_id)) { + monitor_print(msg->result, table, &columns, true); + fflush(stdout); + if (get_detach()) { + /* daemonize() closes the standard file descriptors. We output + * to stdout, so we need to save and restore STDOUT_FILENO. */ + int fd = dup(STDOUT_FILENO); + daemonize(); + dup2(fd, STDOUT_FILENO); + close(fd); + } + } else if (msg->type == JSONRPC_NOTIFY + && !strcmp(msg->method, "update")) { + struct json *params = msg->params; + if (params->type == JSON_ARRAY + && params->u.array.n == 2 + && params->u.array.elems[0]->type == JSON_NULL) { + monitor_print(params->u.array.elems[1], + table, &columns, false); + fflush(stdout); + } + } + jsonrpc_msg_destroy(msg); + } +} + +static void - do_help(int argc UNUSED, char *argv[] UNUSED) ++do_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) +{ + usage(); +} + +static const struct command all_commands[] = { + { "list-dbs", 1, 1, do_list_dbs }, + { "get-schema", 2, 2, do_get_schema }, + { "list-tables", 2, 2, do_list_tables }, + { "list-columns", 2, 3, do_list_columns }, + { "transact", 2, 2, do_transact }, + { "monitor", 3, 5, do_monitor }, + { "help", 0, INT_MAX, do_help }, + { NULL, 0, 0, NULL }, +}; diff --combined ovsdb/ovsdb-idlc.in index a0f4a56dc,000000000..9a90679ec mode 100755,000000..100755 --- a/ovsdb/ovsdb-idlc.in +++ b/ovsdb/ovsdb-idlc.in @@@ -1,866 -1,0 +1,866 @@@ +#! @PYTHON@ + +import getopt +import os +import re +import sys + +sys.path.insert(0, "@abs_top_srcdir@/ovsdb") +import simplejson as json + +argv0 = sys.argv[0] + +class Error(Exception): + def __init__(self, msg): + Exception.__init__(self) + self.msg = msg + +def getMember(json, name, validTypes, description, default=None): + if name in json: + member = json[name] + if type(member) not in validTypes: + raise Error("%s: type mismatch for '%s' member" + % (description, name)) + return member + return default + +def mustGetMember(json, name, expectedType, description): + member = getMember(json, name, expectedType, description) + if member == None: + raise Error("%s: missing '%s' member" % (description, name)) + return member + +class DbSchema: + def __init__(self, name, comment, tables, idlPrefix, idlHeader): + self.name = name + self.comment = comment + self.tables = tables + self.idlPrefix = idlPrefix + self.idlHeader = idlHeader + + @staticmethod + def fromJson(json): + name = mustGetMember(json, 'name', [unicode], 'database') + comment = getMember(json, 'comment', [unicode], 'database') + tablesJson = mustGetMember(json, 'tables', [dict], 'database') + tables = {} + for tableName, tableJson in tablesJson.iteritems(): + tables[tableName] = TableSchema.fromJson(tableJson, + "%s table" % tableName) + idlPrefix = mustGetMember(json, 'idlPrefix', [unicode], 'database') + idlHeader = mustGetMember(json, 'idlHeader', [unicode], 'database') + return DbSchema(name, comment, tables, idlPrefix, idlHeader) + +class TableSchema: + def __init__(self, comment, columns): + self.comment = comment + self.columns = columns + + @staticmethod + def fromJson(json, description): + comment = getMember(json, 'comment', [unicode], description) + columnsJson = mustGetMember(json, 'columns', [dict], description) + columns = {} + for name, json in columnsJson.iteritems(): + columns[name] = ColumnSchema.fromJson( + json, "column %s in %s" % (name, description)) + return TableSchema(comment, columns) + +class ColumnSchema: + def __init__(self, comment, type, persistent): + self.comment = comment + self.type = type + self.persistent = persistent + + @staticmethod + def fromJson(json, description): + comment = getMember(json, 'comment', [unicode], description) + type = Type.fromJson(mustGetMember(json, 'type', [dict, unicode], + description), + 'type of %s' % description) + ephemeral = getMember(json, 'ephemeral', [bool], description) + persistent = ephemeral != True + return ColumnSchema(comment, type, persistent) + +def escapeCString(src): + dst = "" + for c in src: + if c in "\\\"": + dst += "\\" + c + elif ord(c) < 32: + if c == '\n': + dst += '\\n' + elif c == '\r': + dst += '\\r' + elif c == '\a': + dst += '\\a' + elif c == '\b': + dst += '\\b' + elif c == '\f': + dst += '\\f' + elif c == '\t': + dst += '\\t' + elif c == '\v': + dst += '\\v' + else: + dst += '\\%03o' % ord(c) + else: + dst += c + return dst + +class BaseType: + def __init__(self, type, refTable=None, minInteger=None, maxInteger=None, + minReal=None, maxReal=None, reMatch=None, reComment=None, + minLength=None, maxLength=None): + self.type = type + self.refTable = refTable + self.minInteger = minInteger + self.maxInteger = maxInteger + self.minReal = minReal + self.maxReal = maxReal + self.reMatch = reMatch + self.reComment = reComment + self.minLength = minLength + self.maxLength = maxLength + + @staticmethod + def fromJson(json, description): + if type(json) == unicode: + return BaseType(json) + else: + atomicType = mustGetMember(json, 'type', [unicode], description) + refTable = getMember(json, 'refTable', [unicode], description) + minInteger = getMember(json, 'minInteger', [int, long], description) + maxInteger = getMember(json, 'maxInteger', [int, long], description) + minReal = getMember(json, 'minReal', [int, long, float], description) + maxReal = getMember(json, 'maxReal', [int, long, float], description) + reMatch = getMember(json, 'reMatch', [unicode], description) + reComment = getMember(json, 'reComment', [unicode], description) + minLength = getMember(json, 'minLength', [int], description) + maxLength = getMember(json, 'minLength', [int], description) + return BaseType(atomicType, refTable, minInteger, maxInteger, minReal, maxReal, reMatch, reComment, minLength, maxLength) + + def toEnglish(self): + if self.type == 'uuid' and self.refTable: + return self.refTable + else: + return self.type + + def toCType(self, prefix): + if self.refTable: + return "struct %s%s *" % (prefix, self.refTable.lower()) + else: + return {'integer': 'int64_t ', + 'real': 'double ', + 'uuid': 'struct uuid ', + 'boolean': 'bool ', + 'string': 'char *'}[self.type] + + def copyCValue(self, dst, src): + args = {'dst': dst, 'src': src} + if self.refTable: + return ("%(dst)s = %(src)s->header_.uuid;") % args + elif self.type == 'string': + return "%(dst)s = xstrdup(%(src)s);" % args + else: + return "%(dst)s = %(src)s;" % args + + def initCDefault(self, var, isOptional): + if self.refTable: + return "%s = NULL;" % var + elif self.type == 'string' and not isOptional: + return "%s = \"\";" % var + else: + return {'integer': '%s = 0;', + 'real': '%s = 0.0;', + 'uuid': 'uuid_zero(&%s);', + 'boolean': '%s = false;', + 'string': '%s = NULL;'}[self.type] % var + + def cInitBaseType(self, indent, var): + stmts = [] + stmts.append('ovsdb_base_type_init(&%s, OVSDB_TYPE_%s);' % ( + var, self.type.upper()),) + if self.type == 'integer': + if self.minInteger != None: + stmts.append('%s.u.integer.min = %d;' % (var, self.minInteger)) + if self.maxInteger != None: + stmts.append('%s.u.integer.max = %d;' % (var, self.maxInteger)) + elif self.type == 'real': + if self.minReal != None: + stmts.append('%s.u.real.min = %d;' % (var, self.minReal)) + if self.maxReal != None: + stmts.append('%s.u.real.max = %d;' % (var, self.maxReal)) + elif self.type == 'string': + if self.reMatch != None: + if self.reComment != None: + reComment = '"%s"' % escapeCString(self.reComment) + else: + reComment = NULL + stmts.append('do_set_regex(&%s, "%s", %s);' % ( + var, escapeCString(self.reMatch), reComment)) + if self.minLength != None: + stmts.append('%s.u.string.minLen = %d;' % (var, self.minLength)) + if self.maxLength != None: + stmts.append('%s.u.string.maxLen = %d;' % (var, self.maxLength)) + elif self.type == 'uuid': + if self.refTable != None: + stmts.append('%s.u.uuid.refTableName = "%s";' % (var, escapeCString(self.refTable))) + return '\n'.join([indent + stmt for stmt in stmts]) + +class Type: + def __init__(self, key, value=None, min=1, max=1): + self.key = key + self.value = value + self.min = min + self.max = max + + @staticmethod + def fromJson(json, description): + if type(json) == unicode: + return Type(BaseType(json)) + else: + keyJson = mustGetMember(json, 'key', [dict, unicode], description) + key = BaseType.fromJson(keyJson, 'key in %s' % description) + + valueJson = getMember(json, 'value', [dict, unicode], description) + if valueJson: + value = BaseType.fromJson(valueJson, + 'value in %s' % description) + else: + value = None + + min = getMember(json, 'min', [int], description, 1) + max = getMember(json, 'max', [int, unicode], description, 1) + return Type(key, value, min, max) + + def isScalar(self): + return self.min == 1 and self.max == 1 and not self.value + + def isOptional(self): + return self.min == 0 and self.max == 1 + + def isOptionalPointer(self): + return (self.min == 0 and self.max == 1 and not self.value + and (self.key.type == 'string' or self.key.refTable)) + + def toEnglish(self): + keyName = self.key.toEnglish() + if self.value: + valueName = self.value.toEnglish() + + if self.isScalar(): + return keyName + elif self.isOptional(): + if self.value: + return "optional %s-%s pair" % (keyName, valueName) + else: + return "optional %s" % keyName + else: + if self.max == "unlimited": + if self.min: + quantity = "%d or more " % self.min + else: + quantity = "" + elif self.min: + quantity = "%d to %d " % (self.min, self.max) + else: + quantity = "up to %d " % self.max + + if self.value: + return "map of %s%s-%s pairs" % (quantity, keyName, valueName) + else: + return "set of %s%s" % (quantity, keyName) + + def cDeclComment(self): + if self.min == 1 and self.max == 1 and self.key.type == "string": + return "\t/* Always nonnull. */" + else: + return "" + + def cInitType(self, indent, var): + initKey = self.key.cInitBaseType(indent, "%s.key" % var) + if self.value: + initValue = self.value.cInitBaseType(indent, "%s.value" % var) + else: + initValue = ('%sovsdb_base_type_init(&%s.value, ' + 'OVSDB_TYPE_VOID);' % (indent, var)) + initMin = "%s%s.n_min = %s;" % (indent, var, self.min) + if self.max == "unlimited": + max = "UINT_MAX" + else: + max = self.max + initMax = "%s%s.n_max = %s;" % (indent, var, max) + return "\n".join((initKey, initValue, initMin, initMax)) + +def parseSchema(filename): + return DbSchema.fromJson(json.load(open(filename, "r"))) + +def annotateSchema(schemaFile, annotationFile): + schemaJson = json.load(open(schemaFile, "r")) + execfile(annotationFile, globals(), {"s": schemaJson}) + json.dump(schemaJson, sys.stdout) + +def constify(cType, const): + if (const + and cType.endswith('*') and not cType.endswith('**') + and (cType.startswith('struct uuid') or cType.startswith('char'))): + return 'const %s' % cType + else: + return cType + +def cMembers(prefix, columnName, column, const): + type = column.type + if type.min == 1 and type.max == 1: + singleton = True + pointer = '' + else: + singleton = False + if type.isOptionalPointer(): + pointer = '' + else: + pointer = '*' + + if type.value: + key = {'name': "key_%s" % columnName, + 'type': constify(type.key.toCType(prefix) + pointer, const), + 'comment': ''} + value = {'name': "value_%s" % columnName, + 'type': constify(type.value.toCType(prefix) + pointer, const), + 'comment': ''} + members = [key, value] + else: + m = {'name': columnName, + 'type': constify(type.key.toCType(prefix) + pointer, const), + 'comment': type.cDeclComment()} + members = [m] + + if not singleton and not type.isOptionalPointer(): + members.append({'name': 'n_%s' % columnName, + 'type': 'size_t ', + 'comment': ''}) + return members + +def printCIDLHeader(schemaFile): + schema = parseSchema(schemaFile) + prefix = schema.idlPrefix + print '''\ +/* Generated automatically -- do not modify! -*- buffer-read-only: t -*- */ + +#ifndef %(prefix)sIDL_HEADER +#define %(prefix)sIDL_HEADER 1 + +#include +#include +#include +#include "ovsdb-idl-provider.h" +#include "uuid.h"''' % {'prefix': prefix.upper()} + + for tableName, table in sorted(schema.tables.iteritems()): + structName = "%s%s" % (prefix, tableName.lower()) + + print " " + print "/* %s table. */" % tableName + print "struct %s {" % structName + print "\tstruct ovsdb_idl_row header_;" + for columnName, column in sorted(table.columns.iteritems()): + print "\n\t/* %s column. */" % columnName + for member in cMembers(prefix, columnName, column, False): + print "\t%(type)s%(name)s;%(comment)s" % member + print "};" + + # Column indexes. + printEnum(["%s_COL_%s" % (structName.upper(), columnName.upper()) + for columnName in sorted(table.columns)] + + ["%s_N_COLUMNS" % structName.upper()]) + + print + for columnName in table.columns: + print "#define %(s)s_col_%(c)s (%(s)s_columns[%(S)s_COL_%(C)s])" % { + 's': structName, + 'S': structName.upper(), + 'c': columnName, + 'C': columnName.upper()} + + print "\nextern struct ovsdb_idl_column %s_columns[%s_N_COLUMNS];" % (structName, structName.upper()) + + print ''' +const struct %(s)s *%(s)s_first(const struct ovsdb_idl *); +const struct %(s)s *%(s)s_next(const struct %(s)s *); +#define %(S)s_FOR_EACH(ROW, IDL) for ((ROW) = %(s)s_first(IDL); (ROW); (ROW) = %(s)s_next(ROW)) + +void %(s)s_delete(const struct %(s)s *); +struct %(s)s *%(s)s_insert(struct ovsdb_idl_txn *); +''' % {'s': structName, 'S': structName.upper()} + + for columnName, column in sorted(table.columns.iteritems()): + print 'void %(s)s_verify_%(c)s(const struct %(s)s *);' % {'s': structName, 'c': columnName} + + print + for columnName, column in sorted(table.columns.iteritems()): + + print 'void %(s)s_set_%(c)s(const struct %(s)s *,' % {'s': structName, 'c': columnName}, + args = ['%(type)s%(name)s' % member for member + in cMembers(prefix, columnName, column, True)] + print '%s);' % ', '.join(args) + + # Table indexes. + printEnum(["%sTABLE_%s" % (prefix.upper(), tableName.upper()) for tableName in sorted(schema.tables)] + ["%sN_TABLES" % prefix.upper()]) + print + for tableName in schema.tables: + print "#define %(p)stable_%(t)s (%(p)stable_classes[%(P)sTABLE_%(T)s])" % { + 'p': prefix, + 'P': prefix.upper(), + 't': tableName.lower(), + 'T': tableName.upper()} + print "\nextern struct ovsdb_idl_table_class %stable_classes[%sN_TABLES];" % (prefix, prefix.upper()) + + print "\nextern struct ovsdb_idl_class %sidl_class;" % prefix + print "\nvoid %sinit(void);" % prefix + print "\n#endif /* %(prefix)sIDL_HEADER */" % {'prefix': prefix.upper()} + +def printEnum(members): + if len(members) == 0: + return + + print "\nenum {"; + for member in members[:-1]: + print " %s," % member + print " %s" % members[-1] + print "};" + +def printCIDLSource(schemaFile): + schema = parseSchema(schemaFile) + prefix = schema.idlPrefix + print '''\ +/* Generated automatically -- do not modify! -*- buffer-read-only: t -*- */ + +#include +#include %s +#include +#include +#include "ovsdb-data.h" +#include "ovsdb-error.h" + +static bool inited; + - static void UNUSED ++static void OVS_UNUSED +do_set_regex(struct ovsdb_base_type *base, const char *reMatch, + const char *reComment) +{ + struct ovsdb_error *error; + + error = ovsdb_base_type_set_regex(base, reMatch, reComment); + if (error) { + char *s = ovsdb_error_to_string(error); + ovs_error(0, "%%s", s); + free(s); + ovsdb_error_destroy(error); + } +}''' % schema.idlHeader + + # Cast functions. + for tableName, table in sorted(schema.tables.iteritems()): + structName = "%s%s" % (prefix, tableName.lower()) + print ''' +static struct %(s)s * +%(s)s_cast(const struct ovsdb_idl_row *row) +{ + return row ? CONTAINER_OF(row, struct %(s)s, header_) : NULL; +}\ +''' % {'s': structName} + + + for tableName, table in sorted(schema.tables.iteritems()): + structName = "%s%s" % (prefix, tableName.lower()) + print " " + if table.comment != None: + print "/* %s table (%s). */" % (tableName, table.comment) + else: + print "/* %s table. */" % (tableName) + + # Parse functions. + for columnName, column in sorted(table.columns.iteritems()): + print ''' +static void +%(s)s_parse_%(c)s(struct ovsdb_idl_row *row_, const struct ovsdb_datum *datum) +{ + struct %(s)s *row = %(s)s_cast(row_);''' % {'s': structName, + 'c': columnName} + + type = column.type + if type.value: + keyVar = "row->key_%s" % columnName + valueVar = "row->value_%s" % columnName + else: + keyVar = "row->%s" % columnName + valueVar = None + + if (type.min == 1 and type.max == 1) or type.isOptionalPointer(): + print + print " assert(inited);" + print " if (datum->n >= 1) {" + if not type.key.refTable: + print " %s = datum->keys[0].%s;" % (keyVar, type.key.type) + else: + print " %s = %s%s_cast(ovsdb_idl_get_row_arc(row_, &%stable_classes[%sTABLE_%s], &datum->keys[0].uuid));" % (keyVar, prefix, type.key.refTable.lower(), prefix, prefix.upper(), type.key.refTable.upper()) + + if valueVar: + if type.value.refTable: + print " %s = datum->values[0].%s;" % (valueVar, type.value.type) + else: + print " %s = %s%s_cast(ovsdb_idl_get_row_arc(row_, &%stable_classes[%sTABLE_%s], &datum->values[0].uuid));" % (valueVar, prefix, type.value.refTable.lower(), prefix, prefix.upper(), type.value.refTable.upper()) + print " } else {" + print " %s" % type.key.initCDefault(keyVar, type.min == 0) + if valueVar: + print " %s" % type.value.initCDefault(valueVar, type.min == 0) + print " }" + else: + if type.max != 'unlimited': + print " size_t n = MIN(%d, datum->n);" % type.max + nMax = "n" + else: + nMax = "datum->n" + print " size_t i;" + print + print " assert(inited);" + print " %s = NULL;" % keyVar + if valueVar: + print " %s = NULL;" % valueVar + print " row->n_%s = 0;" % columnName + print " for (i = 0; i < %s; i++) {" % nMax + refs = [] + if type.key.refTable: + print " struct %s%s *keyRow = %s%s_cast(ovsdb_idl_get_row_arc(row_, &%stable_classes[%sTABLE_%s], &datum->keys[i].uuid));" % (prefix, type.key.refTable.lower(), prefix, type.key.refTable.lower(), prefix, prefix.upper(), type.key.refTable.upper()) + keySrc = "keyRow" + refs.append('keyRow') + else: + keySrc = "datum->keys[i].%s" % type.key.type + if type.value and type.value.refTable: + print " struct %s%s *valueRow = %s%s_cast(ovsdb_idl_get_row_arc(row_, &%stable_classes[%sTABLE_%s], &datum->values[i].uuid));" % (prefix, type.value.refTable.lower(), prefix, type.value.refTable.lower(), prefix, prefix.upper(), type.value.refTable.upper()) + valueSrc = "valueRow" + refs.append('valueRow') + elif valueVar: + valueSrc = "datum->values[i].%s" % type.value.type + if refs: + print " if (%s) {" % ' && '.join(refs) + indent = " " + else: + indent = " " + print "%sif (!row->n_%s) {" % (indent, columnName) + print "%s %s = xmalloc(%s * sizeof *%s);" % (indent, keyVar, nMax, keyVar) + if valueVar: + print "%s %s = xmalloc(%s * sizeof %s);" % (indent, valueVar, nMax, valueVar) + print "%s}" % indent + print "%s%s[row->n_%s] = %s;" % (indent, keyVar, columnName, keySrc) + if valueVar: + print "%s%s[row->n_%s] = %s;" % (indent, valueVar, columnName, valueSrc) + print "%srow->n_%s++;" % (indent, columnName) + if refs: + print " }" + print " }" + print "}" + + # Unparse functions. + for columnName, column in sorted(table.columns.iteritems()): + type = column.type + if (type.min != 1 or type.max != 1) and not type.isOptionalPointer(): + print ''' +static void +%(s)s_unparse_%(c)s(struct ovsdb_idl_row *row_) +{ + struct %(s)s *row = %(s)s_cast(row_); + + assert(inited);''' % {'s': structName, 'c': columnName} + if type.value: + keyVar = "row->key_%s" % columnName + valueVar = "row->value_%s" % columnName + else: + keyVar = "row->%s" % columnName + valueVar = None + print " free(%s);" % keyVar + if valueVar: + print " free(%s);" % valueVar + print '}' + else: + print ''' +static void - %(s)s_unparse_%(c)s(struct ovsdb_idl_row *row UNUSED) ++%(s)s_unparse_%(c)s(struct ovsdb_idl_row *row OVS_UNUSED) +{ + /* Nothing to do. */ +}''' % {'s': structName, 'c': columnName} + + # First, next functions. + print ''' +const struct %(s)s * +%(s)s_first(const struct ovsdb_idl *idl) +{ + return %(s)s_cast(ovsdb_idl_first_row(idl, &%(p)stable_classes[%(P)sTABLE_%(T)s])); +} + +const struct %(s)s * +%(s)s_next(const struct %(s)s *row) +{ + return %(s)s_cast(ovsdb_idl_next_row(&row->header_)); +}''' % {'s': structName, + 'p': prefix, + 'P': prefix.upper(), + 'T': tableName.upper()} + + print ''' +void +%(s)s_delete(const struct %(s)s *row) +{ + ovsdb_idl_txn_delete(&row->header_); +} + +struct %(s)s * +%(s)s_insert(struct ovsdb_idl_txn *txn) +{ + return %(s)s_cast(ovsdb_idl_txn_insert(txn, &%(p)stable_classes[%(P)sTABLE_%(T)s])); +} +''' % {'s': structName, + 'p': prefix, + 'P': prefix.upper(), + 'T': tableName.upper()} + + # Verify functions. + for columnName, column in sorted(table.columns.iteritems()): + print ''' +void +%(s)s_verify_%(c)s(const struct %(s)s *row) +{ + assert(inited); + ovsdb_idl_txn_verify(&row->header_, &%(s)s_columns[%(S)s_COL_%(C)s]); +}''' % {'s': structName, + 'S': structName.upper(), + 'c': columnName, + 'C': columnName.upper()} + + # Set functions. + for columnName, column in sorted(table.columns.iteritems()): + type = column.type + print '\nvoid' + members = cMembers(prefix, columnName, column, True) + keyVar = members[0]['name'] + nVar = None + valueVar = None + if type.value: + valueVar = members[1]['name'] + if len(members) > 2: + nVar = members[2]['name'] + else: + if len(members) > 1: + nVar = members[1]['name'] + print '%(s)s_set_%(c)s(const struct %(s)s *row, %(args)s)' % \ + {'s': structName, 'c': columnName, + 'args': ', '.join(['%(type)s%(name)s' % m for m in members])} + print "{" + print " struct ovsdb_datum datum;" + if type.min == 1 and type.max == 1: + print + print " assert(inited);" + print " datum.n = 1;" + print " datum.keys = xmalloc(sizeof *datum.keys);" + print " " + type.key.copyCValue("datum.keys[0].%s" % type.key.type, keyVar) + if type.value: + print " datum.values = xmalloc(sizeof *datum.values);" + print " "+ type.value.copyCValue("datum.values[0].%s" % type.value.type, valueVar) + else: + print " datum.values = NULL;" + elif type.isOptionalPointer(): + print + print " assert(inited);" + print " if (%s) {" % keyVar + print " datum.n = 1;" + print " datum.keys = xmalloc(sizeof *datum.keys);" + print " " + type.key.copyCValue("datum.keys[0].%s" % type.key.type, keyVar) + print " } else {" + print " datum.n = 0;" + print " datum.keys = NULL;" + print " }" + print " datum.values = NULL;" + else: + print " size_t i;" + print + print " assert(inited);" + print " datum.n = %s;" % nVar + print " datum.keys = xmalloc(%s * sizeof *datum.keys);" % nVar + if type.value: + print " datum.values = xmalloc(%s * sizeof *datum.values);" % nVar + else: + print " datum.values = NULL;" + print " for (i = 0; i < %s; i++) {" % nVar + print " " + type.key.copyCValue("datum.keys[i].%s" % type.key.type, "%s[i]" % keyVar) + if type.value: + print " " + type.value.copyCValue("datum.values[i].%s" % type.value.type, "%s[i]" % valueVar) + print " }" + print " ovsdb_idl_txn_write(&row->header_, &%(s)s_columns[%(S)s_COL_%(C)s], &datum);" \ + % {'s': structName, + 'S': structName.upper(), + 'C': columnName.upper()} + print "}" + + # Table columns. + print "\nstruct ovsdb_idl_column %s_columns[%s_N_COLUMNS];" % ( + structName, structName.upper()) + print """ +static void\n%s_columns_init(void) +{ + struct ovsdb_idl_column *c;\ +""" % structName + for columnName, column in sorted(table.columns.iteritems()): + cs = "%s_col_%s" % (structName, columnName) + d = {'cs': cs, 'c': columnName, 's': structName} + print + print " /* Initialize %(cs)s. */" % d + print " c = &%(cs)s;" % d + print " c->name = \"%(c)s\";" % d + print column.type.cInitType(" ", "c->type") + print " c->parse = %(s)s_parse_%(c)s;" % d + print " c->unparse = %(s)s_unparse_%(c)s;" % d + print "}" + + # Table classes. + print " " + print "struct ovsdb_idl_table_class %stable_classes[%sN_TABLES] = {" % (prefix, prefix.upper()) + for tableName, table in sorted(schema.tables.iteritems()): + structName = "%s%s" % (prefix, tableName.lower()) + print " {\"%s\"," % tableName + print " %s_columns, ARRAY_SIZE(%s_columns)," % ( + structName, structName) + print " sizeof(struct %s)}," % structName + print "};" + + # IDL class. + print "\nstruct ovsdb_idl_class %sidl_class = {" % prefix + print " \"%s\", %stable_classes, ARRAY_SIZE(%stable_classes)" % ( + schema.name, prefix, prefix) + print "};" + + # global init function + print """ +void +%sinit(void) +{ + if (inited) { + return; + } + inited = true; +""" % prefix + for tableName, table in sorted(schema.tables.iteritems()): + structName = "%s%s" % (prefix, tableName.lower()) + print " %s_columns_init();" % structName + print "}" + +def ovsdb_escape(string): + def escape(match): + c = match.group(0) + if c == '\0': + raise Error("strings may not contain null bytes") + elif c == '\\': + return '\\\\' + elif c == '\n': + return '\\n' + elif c == '\r': + return '\\r' + elif c == '\t': + return '\\t' + elif c == '\b': + return '\\b' + elif c == '\a': + return '\\a' + else: + return '\\x%02x' % ord(c) + return re.sub(r'["\\\000-\037]', escape, string) + +def printDoc(schemaFile): + schema = parseSchema(schemaFile) + print schema.name + if schema.comment: + print schema.comment + + for tableName, table in sorted(schema.tables.iteritems()): + title = "%s table" % tableName + print + print title + print '-' * len(title) + if table.comment: + print table.comment + + for columnName, column in sorted(table.columns.iteritems()): + print + print "%s (%s)" % (columnName, column.type.toEnglish()) + if column.comment: + print "\t%s" % column.comment + +def usage(): + print """\ +%(argv0)s: ovsdb schema compiler +usage: %(argv0)s [OPTIONS] COMMAND ARG... + +The following commands are supported: + annotate SCHEMA ANNOTATIONS print SCHEMA combined with ANNOTATIONS + c-idl-header IDL print C header file for IDL + c-idl-source IDL print C source file for IDL implementation + doc IDL print schema documentation + +The following options are also available: + -h, --help display this help message + -V, --version display version information\ +""" % {'argv0': argv0} + sys.exit(0) + +if __name__ == "__main__": + try: + try: + options, args = getopt.gnu_getopt(sys.argv[1:], 'C:hV', + ['directory', + 'help', + 'version']) + except getopt.GetoptError, geo: + sys.stderr.write("%s: %s\n" % (argv0, geo.msg)) + sys.exit(1) + + for key, value in options: + if key in ['-h', '--help']: + usage() + elif key in ['-V', '--version']: + print "ovsdb-idlc (Open vSwitch) @VERSION@" + elif key in ['-C', '--directory']: + os.chdir(value) + else: + sys.exit(0) + + optKeys = [key for key, value in options] + + if not args: + sys.stderr.write("%s: missing command argument " + "(use --help for help)\n" % argv0) + sys.exit(1) + + commands = {"annotate": (annotateSchema, 2), + "c-idl-header": (printCIDLHeader, 1), + "c-idl-source": (printCIDLSource, 1), + "doc": (printDoc, 1)} + + if not args[0] in commands: + sys.stderr.write("%s: unknown command \"%s\" " + "(use --help for help)\n" % (argv0, args[0])) + sys.exit(1) + + func, n_args = commands[args[0]] + if len(args) - 1 != n_args: + sys.stderr.write("%s: \"%s\" requires %d arguments but %d " + "provided\n" + % (argv0, args[0], n_args, len(args) - 1)) + sys.exit(1) + + func(*args[1:]) + except Error, e: + sys.stderr.write("%s: %s\n" % (argv0, e.msg)) + sys.exit(1) + +# Local variables: +# mode: python +# End: diff --combined ovsdb/ovsdb-server.c index 03ebba171,000000000..eccbf7d14 mode 100644,000000..100644 --- a/ovsdb/ovsdb-server.c +++ b/ovsdb/ovsdb-server.c @@@ -1,308 -1,0 +1,308 @@@ +/* Copyright (c) 2009, 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 "ovsdb.h" + +#include +#include +#include +#include + +#include "column.h" +#include "command-line.h" +#include "daemon.h" +#include "file.h" +#include "json.h" +#include "jsonrpc.h" +#include "jsonrpc-server.h" +#include "leak-checker.h" +#include "list.h" +#include "ovsdb-data.h" +#include "ovsdb-types.h" +#include "ovsdb-error.h" +#include "poll-loop.h" +#include "process.h" +#include "row.h" +#include "stream-ssl.h" +#include "stream.h" +#include "svec.h" +#include "table.h" +#include "timeval.h" +#include "trigger.h" +#include "util.h" +#include "unixctl.h" + +#include "vlog.h" +#define THIS_MODULE VLM_ovsdb_server + +static unixctl_cb_func ovsdb_server_exit; + +static void parse_options(int argc, char *argv[], char **file_namep, + struct shash *remotes, char **unixctl_pathp); +static void usage(void) NO_RETURN; + +static void set_remotes(struct ovsdb_jsonrpc_server *jsonrpc, + const struct ovsdb *db, struct shash *remotes); + +int +main(int argc, char *argv[]) +{ + char *unixctl_path = NULL; + struct unixctl_server *unixctl; + struct ovsdb_jsonrpc_server *jsonrpc; + struct shash remotes; + struct ovsdb_error *error; + struct ovsdb *db; + char *file_name; + bool exiting; + int retval; + + proctitle_init(argc, argv); + set_program_name(argv[0]); + time_init(); + vlog_init(); + signal(SIGPIPE, SIG_IGN); + process_init(); + + parse_options(argc, argv, &file_name, &remotes, &unixctl_path); + + die_if_already_running(); + daemonize_start(); + + error = ovsdb_file_open(file_name, false, &db); + if (error) { + ovs_fatal(0, "%s", ovsdb_error_to_string(error)); + } + + jsonrpc = ovsdb_jsonrpc_server_create(db); + set_remotes(jsonrpc, db, &remotes); + + retval = unixctl_server_create(unixctl_path, &unixctl); + if (retval) { + exit(EXIT_FAILURE); + } + + daemonize_complete(); + + unixctl_command_register("exit", ovsdb_server_exit, &exiting); + + exiting = false; + while (!exiting) { + set_remotes(jsonrpc, db, &remotes); + ovsdb_jsonrpc_server_run(jsonrpc); + unixctl_server_run(unixctl); + ovsdb_trigger_run(db, time_msec()); + + ovsdb_jsonrpc_server_wait(jsonrpc); + unixctl_server_wait(unixctl); + ovsdb_trigger_wait(db, time_msec()); + poll_block(); + } + ovsdb_jsonrpc_server_destroy(jsonrpc); + ovsdb_destroy(db); + shash_destroy(&remotes); + unixctl_server_destroy(unixctl); + + return 0; +} + +static void +query_db_remotes(const char *name_, const struct ovsdb *db, + struct shash *remotes) +{ + char *name, *table_name, *column_name; + const struct ovsdb_column *column; + const struct ovsdb_table *table; + const struct ovsdb_row *row; + char *save_ptr = NULL; + + name = xstrdup(name_); + strtok_r(name, ":", &save_ptr); /* "db:" */ + table_name = strtok_r(NULL, ",", &save_ptr); + column_name = strtok_r(NULL, ",", &save_ptr); + if (!table_name || !column_name) { + ovs_fatal(0, "remote \"%s\": invalid syntax", name_); + } + + table = ovsdb_get_table(db, table_name); + if (!table) { + ovs_fatal(0, "remote \"%s\": no table named %s", name_, table_name); + } + + column = ovsdb_table_schema_get_column(table->schema, column_name); + if (!column) { + ovs_fatal(0, "remote \"%s\": table \"%s\" has no column \"%s\"", + name_, table_name, column_name); + } + + if (column->type.key.type != OVSDB_TYPE_STRING + || column->type.value.type != OVSDB_TYPE_VOID) { + ovs_fatal(0, "remote \"%s\": type of table \"%s\" column \"%s\" is " + "not string or set of strings", + name_, table_name, column_name); + } + + HMAP_FOR_EACH (row, struct ovsdb_row, hmap_node, &table->rows) { + const struct ovsdb_datum *datum; + size_t i; + + datum = &row->fields[column->index]; + for (i = 0; i < datum->n; i++) { + shash_add_once(remotes, datum->keys[i].string, NULL); + } + } + + free(name); +} + +static void +set_remotes(struct ovsdb_jsonrpc_server *jsonrpc, + const struct ovsdb *db, struct shash *remotes) +{ + struct shash resolved_remotes; + struct shash_node *node; + + shash_init(&resolved_remotes); + SHASH_FOR_EACH (node, remotes) { + const char *name = node->name; + + if (!strncmp(name, "db:", 3)) { + query_db_remotes(name, db, &resolved_remotes); + } else { + shash_add_once(&resolved_remotes, name, NULL); + } + } + ovsdb_jsonrpc_server_set_remotes(jsonrpc, &resolved_remotes); + shash_destroy(&resolved_remotes); +} + + +static void - ovsdb_server_exit(struct unixctl_conn *conn, const char *args UNUSED, ++ovsdb_server_exit(struct unixctl_conn *conn, const char *args OVS_UNUSED, + void *exiting_) +{ + bool *exiting = exiting_; + *exiting = true; + unixctl_command_reply(conn, 200, NULL); +} + +static void +parse_options(int argc, char *argv[], char **file_namep, + struct shash *remotes, char **unixctl_pathp) +{ + enum { + OPT_DUMMY = UCHAR_MAX + 1, + OPT_REMOTE, + OPT_UNIXCTL, + OPT_BOOTSTRAP_CA_CERT, + VLOG_OPTION_ENUMS, + LEAK_CHECKER_OPTION_ENUMS + }; + static struct option long_options[] = { + {"remote", required_argument, 0, OPT_REMOTE}, + {"unixctl", required_argument, 0, OPT_UNIXCTL}, + {"help", no_argument, 0, 'h'}, + {"version", no_argument, 0, 'V'}, + DAEMON_LONG_OPTIONS, + VLOG_LONG_OPTIONS, + LEAK_CHECKER_LONG_OPTIONS, +#ifdef HAVE_OPENSSL + {"bootstrap-ca-cert", required_argument, 0, OPT_BOOTSTRAP_CA_CERT}, + STREAM_SSL_LONG_OPTIONS +#endif + {0, 0, 0, 0}, + }; + char *short_options = long_options_to_short_options(long_options); + + shash_init(remotes); + for (;;) { + int c; + + c = getopt_long(argc, argv, short_options, long_options, NULL); + if (c == -1) { + break; + } + + switch (c) { + case OPT_REMOTE: + shash_add_once(remotes, optarg, NULL); + break; + + case OPT_UNIXCTL: + *unixctl_pathp = optarg; + break; + + case 'h': + usage(); + + case 'V': + OVS_PRINT_VERSION(0, 0); + exit(EXIT_SUCCESS); + + VLOG_OPTION_HANDLERS + DAEMON_OPTION_HANDLERS + LEAK_CHECKER_OPTION_HANDLERS + +#ifdef HAVE_OPENSSL + STREAM_SSL_OPTION_HANDLERS + + case OPT_BOOTSTRAP_CA_CERT: + stream_ssl_set_ca_cert_file(optarg, true); + break; +#endif + + + case '?': + exit(EXIT_FAILURE); + + default: + abort(); + } + } + free(short_options); + + argc -= optind; + argv += optind; + + if (argc > 1) { + ovs_fatal(0, "database file is only non-option argument; " + "use --help for usage"); + } else if (argc < 1) { + ovs_fatal(0, "missing database file argument; use --help for usage"); + } + + *file_namep = argv[0]; +} + +static void +usage(void) +{ + printf("%s: Open vSwitch database server\n" + "usage: %s [OPTIONS] DATABASE\n" + "where DATABASE is a database file in ovsdb format.\n", + program_name, program_name); + printf("\nJSON-RPC options (may be specified any number of times):\n" + " --remote=REMOTE connect or listen to REMOTE\n"); + stream_usage("JSON-RPC", true, true, true); + daemon_usage(); + vlog_usage(); + printf("\nOther options:\n" + " -h, --help display this help message\n" + " -V, --version display version information\n"); + leak_checker_usage(); + exit(EXIT_SUCCESS); +} diff --combined ovsdb/ovsdb-tool.c index 1f2a5ef7e,000000000..34c767618 mode 100644,000000..100644 --- a/ovsdb/ovsdb-tool.c +++ b/ovsdb/ovsdb-tool.c @@@ -1,344 -1,0 +1,344 @@@ +/* + * Copyright (c) 2009, 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 +#include +#include +#include +#include +#include + +#include "command-line.h" +#include "compiler.h" +#include "file.h" +#include "log.h" +#include "json.h" +#include "ovsdb.h" +#include "ovsdb-error.h" +#include "table.h" +#include "timeval.h" +#include "util.h" + +#include "vlog.h" +#define THIS_MODULE VLM_ovsdb_tool + +/* -m, --more: Verbosity level for "show-log" command output. */ +static int show_log_verbosity; + +static const struct command all_commands[]; + +static void usage(void) NO_RETURN; +static void parse_options(int argc, char *argv[]); + +int +main(int argc, char *argv[]) +{ + set_program_name(argv[0]); + time_init(); + vlog_init(); + parse_options(argc, argv); + signal(SIGPIPE, SIG_IGN); + run_command(argc - optind, argv + optind, all_commands); + return 0; +} + +static void +parse_options(int argc, char *argv[]) +{ + static struct option long_options[] = { + {"more", no_argument, 0, 'm'}, + {"verbose", optional_argument, 0, 'v'}, + {"help", no_argument, 0, 'h'}, + {"version", no_argument, 0, 'V'}, + {0, 0, 0, 0}, + }; + char *short_options = long_options_to_short_options(long_options); + + for (;;) { + int c; + + c = getopt_long(argc, argv, short_options, long_options, NULL); + if (c == -1) { + break; + } + + switch (c) { + case 'm': + show_log_verbosity++; + break; + + case 'h': + usage(); + + case 'V': + OVS_PRINT_VERSION(0, 0); + exit(EXIT_SUCCESS); + + case 'v': + vlog_set_verbosity(optarg); + break; + + case '?': + exit(EXIT_FAILURE); + + default: + abort(); + } + } + free(short_options); +} + +static void +usage(void) +{ + printf("%s: Open vSwitch database management utility\n" + "usage: %s [OPTIONS] COMMAND [ARG...]\n" + " create DB SCHEMA create DB with the given SCHEMA\n" + " compact DB [DST] compact DB in-place (or to DST)\n" + " extract-schema DB print DB's schema on stdout\n" + " query DB TRNS execute read-only transaction on DB\n" + " transact DB TRNS execute read/write transaction on DB\n" + " show-log DB prints information about DB's log entries\n", + program_name, program_name); + vlog_usage(); + printf("\nOther options:\n" + " -m, --more increase show-log verbosity\n" + " -h, --help display this help message\n" + " -V, --version display version information\n"); + exit(EXIT_SUCCESS); +} + +static struct json * +parse_json(const char *s) +{ + struct json *json = json_from_string(s); + if (json->type == JSON_STRING) { + ovs_fatal(0, "\"%s\": %s", s, json->u.string); + } + return json; +} + +static void +print_and_free_json(struct json *json) +{ + char *string = json_to_string(json, JSSF_SORT); + json_destroy(json); + puts(string); + free(string); +} + +static void +check_ovsdb_error(struct ovsdb_error *error) +{ + if (error) { + ovs_fatal(0, "%s", ovsdb_error_to_string(error)); + } +} + +static void - do_create(int argc UNUSED, char *argv[]) ++do_create(int argc OVS_UNUSED, char *argv[]) +{ + const char *db_file_name = argv[1]; + const char *schema_file_name = argv[2]; + struct ovsdb_schema *schema; + struct ovsdb_log *log; + struct json *json; + + /* Read schema from file and convert to JSON. */ + check_ovsdb_error(ovsdb_schema_from_file(schema_file_name, &schema)); + json = ovsdb_schema_to_json(schema); + ovsdb_schema_destroy(schema); + + /* Create database file. */ + check_ovsdb_error(ovsdb_log_open(db_file_name, O_RDWR | O_CREAT | O_EXCL, + &log)); + check_ovsdb_error(ovsdb_log_write(log, json)); + check_ovsdb_error(ovsdb_log_commit(log)); + ovsdb_log_close(log); + + json_destroy(json); +} + +static void +transact(bool read_only, const char *db_file_name, const char *transaction) +{ + struct json *request, *result; + struct ovsdb *db; + + check_ovsdb_error(ovsdb_file_open(db_file_name, read_only, &db)); + + request = parse_json(transaction); + result = ovsdb_execute(db, request, 0, NULL); + json_destroy(request); + + print_and_free_json(result); + ovsdb_destroy(db); +} + +static void - do_query(int argc UNUSED, char *argv[]) ++do_query(int argc OVS_UNUSED, char *argv[]) +{ + transact(true, argv[1], argv[2]); +} + +static void - do_transact(int argc UNUSED, char *argv[]) ++do_transact(int argc OVS_UNUSED, char *argv[]) +{ + transact(false, argv[1], argv[2]); +} + +static void +print_db_changes(struct shash *tables, struct shash *names) +{ + struct shash_node *n1; + + SHASH_FOR_EACH (n1, tables) { + const char *table = n1->name; + struct json *rows = n1->data; + struct shash_node *n2; + + if (n1->name[0] == '_' || rows->type != JSON_OBJECT) { + continue; + } + + SHASH_FOR_EACH (n2, json_object(rows)) { + const char *row_uuid = n2->name; + struct json *columns = n2->data; + struct shash_node *n3; + char *old_name, *new_name; + bool free_new_name = false; + + old_name = new_name = shash_find_data(names, row_uuid); + if (columns->type == JSON_OBJECT) { + struct json *new_name_json; + + new_name_json = shash_find_data(json_object(columns), "name"); + if (new_name_json) { + new_name = json_to_string(new_name_json, JSSF_SORT); + free_new_name = true; + } + } + + printf("\ttable %s", table); + + if (!old_name) { + if (new_name) { + printf(" insert row %s:\n", new_name); + } else { + printf(" insert row %.8s:\n", row_uuid); + } + } else { + printf(" row %s:\n", old_name); + } + + if (columns->type == JSON_OBJECT) { + if (show_log_verbosity > 1) { + SHASH_FOR_EACH (n3, json_object(columns)) { + const char *column = n3->name; + struct json *value = n3->data; + char *value_string; + + value_string = json_to_string(value, JSSF_SORT); + printf("\t\t%s=%s\n", column, value_string); + free(value_string); + } + } + if (!old_name + || (new_name != old_name && strcmp(old_name, new_name))) { + if (old_name) { + shash_delete(names, shash_find(names, row_uuid)); + free(old_name); + } + shash_add(names, row_uuid, (new_name + ? xstrdup(new_name) + : xmemdup0(row_uuid, 8))); + } + } else if (columns->type == JSON_NULL) { + printf("\t\tdelete row\n"); + shash_delete(names, shash_find(names, row_uuid)); + free(old_name); + } + + if (free_new_name) { + free(new_name); + } + } + } +} + +static void - do_show_log(int argc UNUSED, char *argv[]) ++do_show_log(int argc OVS_UNUSED, char *argv[]) +{ + const char *db_file_name = argv[1]; + struct shash names; + struct ovsdb_log *log; + unsigned int i; + + check_ovsdb_error(ovsdb_log_open(db_file_name, O_RDONLY, &log)); + shash_init(&names); + for (i = 0; ; i++) { + struct json *json; + + check_ovsdb_error(ovsdb_log_read(log, &json)); + if (!json) { + break; + } + + printf("record %u:", i); + if (json->type == JSON_OBJECT) { + struct json *date, *comment; + + date = shash_find_data(json_object(json), "_date"); + if (date && date->type == JSON_INTEGER) { + time_t t = json_integer(date); + char s[128]; + + strftime(s, sizeof s, "%Y-%m-%d %H:%M:%S", localtime(&t)); + printf(" %s", s); + } + + comment = shash_find_data(json_object(json), "_comment"); + if (comment && comment->type == JSON_STRING) { + printf(" \"%s\"", json_string(comment)); + } + + if (i > 0 && show_log_verbosity > 0) { + putchar('\n'); + print_db_changes(json_object(json), &names); + } + } + json_destroy(json); + putchar('\n'); + } + + /* XXX free 'names'. */ +} + +static void - do_help(int argc UNUSED, char *argv[] UNUSED) ++do_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) +{ + usage(); +} + +static const struct command all_commands[] = { + { "create", 2, 2, do_create }, + { "query", 2, 2, do_query }, + { "transact", 2, 2, do_transact }, + { "show-log", 1, 1, do_show_log }, + { "help", 0, INT_MAX, do_help }, + { NULL, 0, 0, NULL }, +}; diff --combined ovsdb/ovsdb.c index 2dea507cb,000000000..2b5bdc32e mode 100644,000000..100644 --- a/ovsdb/ovsdb.c +++ b/ovsdb/ovsdb.c @@@ -1,294 -1,0 +1,294 @@@ +/* Copyright (c) 2009, 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 "ovsdb.h" + +#include "column.h" +#include "json.h" +#include "ovsdb-error.h" +#include "ovsdb-parser.h" +#include "ovsdb-types.h" +#include "table.h" +#include "transaction.h" + +struct ovsdb_schema * +ovsdb_schema_create(const char *name, const char *comment) +{ + struct ovsdb_schema *schema; + + schema = xzalloc(sizeof *schema); + schema->name = xstrdup(name); + schema->comment = comment ? xstrdup(comment) : NULL; + shash_init(&schema->tables); + + return schema; +} + +void +ovsdb_schema_destroy(struct ovsdb_schema *schema) +{ + struct shash_node *node; + + SHASH_FOR_EACH (node, &schema->tables) { + ovsdb_table_schema_destroy(node->data); + } + shash_destroy(&schema->tables); + free(schema->comment); + free(schema->name); + free(schema); +} + +struct ovsdb_error * +ovsdb_schema_from_file(const char *file_name, struct ovsdb_schema **schemap) +{ + struct ovsdb_schema *schema; + struct ovsdb_error *error; + struct json *json; + + *schemap = NULL; + json = json_from_file(file_name); + if (json->type == JSON_STRING) { + error = ovsdb_error("failed to read schema", + "\"%s\" could not be read as JSON (%s)", + file_name, json_string(json)); + json_destroy(json); + return error; + } + + error = ovsdb_schema_from_json(json, &schema); + json_destroy(json); + if (error) { + return ovsdb_wrap_error(error, + "failed to parse \"%s\" as ovsdb schema", + file_name); + } + + *schemap = schema; + return NULL; +} + +static struct ovsdb_error * WARN_UNUSED_RESULT +ovsdb_schema_check_ref_table(const struct ovsdb_column *column, + const struct shash *tables, + const struct ovsdb_base_type *base, + const char *base_name) +{ + if (base->type == OVSDB_TYPE_UUID && base->u.uuid.refTableName + && !shash_find(tables, base->u.uuid.refTableName)) { + return ovsdb_syntax_error(NULL, NULL, + "column %s %s refers to undefined table %s", + column->name, base_name, + base->u.uuid.refTableName); + } else { + return NULL; + } +} + +struct ovsdb_error * +ovsdb_schema_from_json(struct json *json, struct ovsdb_schema **schemap) +{ + struct ovsdb_schema *schema; + const struct json *name, *comment, *tables; + struct ovsdb_error *error; + struct shash_node *node; + struct ovsdb_parser parser; + + *schemap = NULL; + + ovsdb_parser_init(&parser, json, "database schema"); + name = ovsdb_parser_member(&parser, "name", OP_ID); + comment = ovsdb_parser_member(&parser, "comment", OP_STRING | OP_OPTIONAL); + tables = ovsdb_parser_member(&parser, "tables", OP_OBJECT); + error = ovsdb_parser_finish(&parser); + if (error) { + return error; + } + + schema = ovsdb_schema_create(json_string(name), + comment ? json_string(comment) : NULL); + SHASH_FOR_EACH (node, json_object(tables)) { + struct ovsdb_table_schema *table; + + if (node->name[0] == '_') { + error = ovsdb_syntax_error(json, NULL, "names beginning with " + "\"_\" are reserved"); + } else if (!ovsdb_parser_is_id(node->name)) { + error = ovsdb_syntax_error(json, NULL, "name must be a valid id"); + } else { + error = ovsdb_table_schema_from_json(node->data, node->name, + &table); + } + if (error) { + ovsdb_schema_destroy(schema); + return error; + } + + shash_add(&schema->tables, table->name, table); + } + + /* Validate that all refTables refer to the names of tables that exist. */ + SHASH_FOR_EACH (node, &schema->tables) { + struct ovsdb_table_schema *table = node->data; + struct shash_node *node2; + + SHASH_FOR_EACH (node2, &table->columns) { + struct ovsdb_column *column = node2->data; + + error = ovsdb_schema_check_ref_table(column, &schema->tables, + &column->type.key, "key"); + if (!error) { + error = ovsdb_schema_check_ref_table(column, &schema->tables, + &column->type.value, + "value"); + } + if (error) { + ovsdb_schema_destroy(schema); + return error; + } + } + } + + *schemap = schema; + return 0; +} + +struct json * +ovsdb_schema_to_json(const struct ovsdb_schema *schema) +{ + struct json *json, *tables; + struct shash_node *node; + + json = json_object_create(); + json_object_put_string(json, "name", schema->name); + if (schema->comment) { + json_object_put_string(json, "comment", schema->comment); + } + + tables = json_object_create(); + + SHASH_FOR_EACH (node, &schema->tables) { + struct ovsdb_table_schema *table = node->data; + json_object_put(tables, table->name, + ovsdb_table_schema_to_json(table)); + } + json_object_put(json, "tables", tables); + + return json; +} + +static void +ovsdb_set_ref_table(const struct shash *tables, + struct ovsdb_base_type *base) +{ + if (base->type == OVSDB_TYPE_UUID && base->u.uuid.refTableName) { + struct ovsdb_table *table; + + table = shash_find_data(tables, base->u.uuid.refTableName); + base->u.uuid.refTable = table; + } +} + +struct ovsdb * +ovsdb_create(struct ovsdb_schema *schema) +{ + struct shash_node *node; + struct ovsdb *db; + + db = xmalloc(sizeof *db); + db->schema = schema; + list_init(&db->replicas); + list_init(&db->triggers); + db->run_triggers = false; + + shash_init(&db->tables); + SHASH_FOR_EACH (node, &schema->tables) { + struct ovsdb_table_schema *ts = node->data; + shash_add(&db->tables, node->name, ovsdb_table_create(ts)); + } + + /* Set all the refTables. */ + SHASH_FOR_EACH (node, &schema->tables) { + struct ovsdb_table_schema *table = node->data; + struct shash_node *node2; + + SHASH_FOR_EACH (node2, &table->columns) { + struct ovsdb_column *column = node2->data; + + ovsdb_set_ref_table(&db->tables, &column->type.key); + ovsdb_set_ref_table(&db->tables, &column->type.value); + } + } + + return db; +} + +void +ovsdb_destroy(struct ovsdb *db) +{ + if (db) { + struct shash_node *node; + + /* Remove all the replicas. */ + while (!list_is_empty(&db->replicas)) { + struct ovsdb_replica *r + = CONTAINER_OF(list_pop_back(&db->replicas), + struct ovsdb_replica, node); + ovsdb_remove_replica(db, r); + } + + /* Delete all the tables. This also deletes their schemas. */ + SHASH_FOR_EACH (node, &db->tables) { + struct ovsdb_table *table = node->data; + ovsdb_table_destroy(table); + } + shash_destroy(&db->tables); + + /* The schemas, but not the table that points to them, were deleted in + * the previous step, so we need to clear out the table. We can't + * destroy the table, because ovsdb_schema_destroy() will do that. */ + shash_clear(&db->schema->tables); + + ovsdb_schema_destroy(db->schema); + free(db); + } +} + +struct ovsdb_table * +ovsdb_get_table(const struct ovsdb *db, const char *name) +{ + return shash_find_data(&db->tables, name); +} + +void +ovsdb_replica_init(struct ovsdb_replica *r, + const struct ovsdb_replica_class *class) +{ + r->class = class; +} + +void +ovsdb_add_replica(struct ovsdb *db, struct ovsdb_replica *r) +{ + list_push_back(&db->replicas, &r->node); +} + +void - ovsdb_remove_replica(struct ovsdb *db UNUSED, struct ovsdb_replica *r) ++ovsdb_remove_replica(struct ovsdb *db OVS_UNUSED, struct ovsdb_replica *r) +{ + list_remove(&r->node); + (r->class->destroy)(r); +} diff --combined tests/test-dhcp-client.c index 3b35dac69,f38d438fc..79d17736b --- a/tests/test-dhcp-client.c +++ b/tests/test-dhcp-client.c @@@ -23,6 -23,7 +23,6 @@@ #include "command-line.h" #include "dhcp.h" #include "fatal-signal.h" -#include "fault.h" #include "poll-loop.h" #include "util.h" #include "vlog.h" @@@ -50,6 -51,7 +50,6 @@@ main(int argc, char *argv[] int error; set_program_name(argv[0]); - register_fault_handlers(); vlog_init(); parse_options(argc, argv); @@@ -65,9 -67,10 +65,9 @@@ ovs_fatal(error, "dhclient_create failed"); } dhclient_init(cli, request_ip.s_addr); - fatal_signal_add_hook(release, cli, true); + fatal_signal_add_hook(release, NULL, cli, true); for (;;) { - fatal_signal_block(); dhclient_run(cli); if (dhclient_changed(cli)) { dhclient_configure_netdev(cli); @@@ -76,6 -79,7 +76,6 @@@ } } dhclient_wait(cli); - fatal_signal_unblock(); poll_block(); } } @@@ -91,7 -95,7 +91,7 @@@ release(void *cli_ } static void - modify_dhcp_request(struct dhcp_msg *msg, void *aux UNUSED) + modify_dhcp_request(struct dhcp_msg *msg, void *aux OVS_UNUSED) { if (vendor_class) { dhcp_msg_put_string(msg, DHCP_CODE_VENDOR_CLASS, vendor_class); diff --combined tests/test-jsonrpc.c index 03d300011,000000000..06b1cf475 mode 100644,000000..100644 --- a/tests/test-jsonrpc.c +++ b/tests/test-jsonrpc.c @@@ -1,341 -1,0 +1,341 @@@ +/* + * Copyright (c) 2009, 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 "jsonrpc.h" + +#include +#include +#include +#include +#include + +#include "command-line.h" +#include "daemon.h" +#include "json.h" +#include "poll-loop.h" +#include "stream-ssl.h" +#include "stream.h" +#include "timeval.h" +#include "util.h" +#include "vlog.h" + +static struct command all_commands[]; + +static void usage(void) NO_RETURN; +static void parse_options(int argc, char *argv[]); + +int +main(int argc, char *argv[]) +{ + proctitle_init(argc, argv); + set_program_name(argv[0]); + time_init(); + vlog_init(); + parse_options(argc, argv); + run_command(argc - optind, argv + optind, all_commands); + return 0; +} + +static void +parse_options(int argc, char *argv[]) +{ + enum { + OPT_BOOTSTRAP_CA_CERT = UCHAR_MAX + 1 + }; + static struct option long_options[] = { + {"verbose", optional_argument, 0, 'v'}, + {"help", no_argument, 0, 'h'}, + DAEMON_LONG_OPTIONS, +#ifdef HAVE_OPENSSL + {"bootstrap-ca-cert", required_argument, 0, OPT_BOOTSTRAP_CA_CERT}, + STREAM_SSL_LONG_OPTIONS +#endif + {0, 0, 0, 0}, + }; + char *short_options = long_options_to_short_options(long_options); + + for (;;) { + int c = getopt_long(argc, argv, short_options, long_options, NULL); + if (c == -1) { + break; + } + + switch (c) { + case 'h': + usage(); + + case 'v': + vlog_set_verbosity(optarg); + break; + + DAEMON_OPTION_HANDLERS + +#ifdef HAVE_OPENSSL + STREAM_SSL_OPTION_HANDLERS + + case OPT_BOOTSTRAP_CA_CERT: + stream_ssl_set_ca_cert_file(optarg, true); + break; +#endif + + case '?': + exit(EXIT_FAILURE); + + default: + abort(); + } + } + free(short_options); +} + +static void +usage(void) +{ + printf("%s: JSON-RPC test utility\n" + "usage: %s [OPTIONS] COMMAND [ARG...]\n" + " listen LOCAL listen for connections on LOCAL\n" + " request REMOTE METHOD PARAMS send request, print reply\n" + " notify REMOTE METHOD PARAMS send notification and exit\n", + program_name, program_name); + stream_usage("JSON-RPC", true, true, true); + daemon_usage(); + vlog_usage(); + printf("\nOther options:\n" + " -h, --help display this help message\n"); + exit(EXIT_SUCCESS); +} + +/* Command helper functions. */ + +static struct json * +parse_json(const char *s) +{ + struct json *json = json_from_string(s); + if (json->type == JSON_STRING) { + ovs_fatal(0, "\"%s\": %s", s, json->u.string); + } + return json; +} + +static void +print_and_free_json(struct json *json) +{ + char *string = json_to_string(json, JSSF_SORT); + json_destroy(json); + puts(string); + free(string); +} + +/* Command implementations. */ + +static void +handle_rpc(struct jsonrpc *rpc, struct jsonrpc_msg *msg, bool *done) +{ + struct jsonrpc_msg *reply = NULL; + if (msg->type == JSONRPC_REQUEST) { + if (!strcmp(msg->method, "echo")) { + reply = jsonrpc_create_reply(json_clone(msg->params), msg->id); + } else { + struct json *error = json_object_create(); + json_object_put_string(error, "error", "unknown method"); + reply = jsonrpc_create_error(error, msg->id); + ovs_error(0, "unknown request %s", msg->method); + } + + } else if (msg->type == JSONRPC_NOTIFY) { + if (!strcmp(msg->method, "shutdown")) { + *done = true; + } else { + jsonrpc_error(rpc, ENOTTY); + ovs_error(0, "unknown notification %s", msg->method); + } + } else { + jsonrpc_error(rpc, EPROTO); + ovs_error(0, "unsolicited JSON-RPC reply or error"); + } + + if (reply) { + jsonrpc_send(rpc, reply); + } +} + +static void - do_listen(int argc UNUSED, char *argv[]) ++do_listen(int argc OVS_UNUSED, char *argv[]) +{ + struct pstream *pstream; + struct jsonrpc **rpcs; + size_t n_rpcs, allocated_rpcs; + bool done; + int error; + + die_if_already_running(); + + error = pstream_open(argv[1], &pstream); + if (error) { + ovs_fatal(error, "could not listen on \"%s\"", argv[1]); + } + + daemonize(); + + rpcs = NULL; + n_rpcs = allocated_rpcs = 0; + done = false; + for (;;) { + struct stream *stream; + size_t i; + + /* Accept new connections. */ + error = pstream_accept(pstream, &stream); + if (!error) { + if (n_rpcs >= allocated_rpcs) { + rpcs = x2nrealloc(rpcs, &allocated_rpcs, sizeof *rpcs); + } + rpcs[n_rpcs++] = jsonrpc_open(stream); + } else if (error != EAGAIN) { + ovs_fatal(error, "pstream_accept failed"); + } + + /* Service existing connections. */ + for (i = 0; i < n_rpcs; ) { + struct jsonrpc *rpc = rpcs[i]; + struct jsonrpc_msg *msg; + + jsonrpc_run(rpc); + if (!jsonrpc_get_backlog(rpc)) { + error = jsonrpc_recv(rpc, &msg); + if (!error) { + handle_rpc(rpc, msg, &done); + jsonrpc_msg_destroy(msg); + } + } + + error = jsonrpc_get_status(rpc); + if (error) { + jsonrpc_close(rpc); + ovs_error(error, "connection closed"); + memmove(&rpcs[i], &rpcs[i + 1], + (n_rpcs - i - 1) * sizeof *rpcs); + n_rpcs--; + } else { + i++; + } + } + + /* Wait for something to do. */ + if (done && !n_rpcs) { + break; + } + pstream_wait(pstream); + for (i = 0; i < n_rpcs; i++) { + struct jsonrpc *rpc = rpcs[i]; + + jsonrpc_wait(rpc); + if (!jsonrpc_get_backlog(rpc)) { + jsonrpc_recv_wait(rpc); + } + } + poll_block(); + } + free(rpcs); + pstream_close(pstream); +} + +static void - do_request(int argc UNUSED, char *argv[]) ++do_request(int argc OVS_UNUSED, char *argv[]) +{ + struct jsonrpc_msg *msg; + struct jsonrpc *rpc; + struct json *params; + struct stream *stream; + const char *method; + char *string; + int error; + + method = argv[2]; + params = parse_json(argv[3]); + msg = jsonrpc_create_request(method, params, NULL); + string = jsonrpc_msg_is_valid(msg); + if (string) { + ovs_fatal(0, "not a valid JSON-RPC request: %s", string); + } + + error = stream_open_block(argv[1], &stream); + if (error) { + ovs_fatal(error, "could not open \"%s\"", argv[1]); + } + rpc = jsonrpc_open(stream); + + error = jsonrpc_send(rpc, msg); + if (error) { + ovs_fatal(error, "could not send request"); + } + + error = jsonrpc_recv_block(rpc, &msg); + if (error) { + ovs_fatal(error, "error waiting for reply"); + } + print_and_free_json(jsonrpc_msg_to_json(msg)); + + jsonrpc_close(rpc); +} + +static void - do_notify(int argc UNUSED, char *argv[]) ++do_notify(int argc OVS_UNUSED, char *argv[]) +{ + struct jsonrpc_msg *msg; + struct jsonrpc *rpc; + struct json *params; + struct stream *stream; + const char *method; + char *string; + int error; + + method = argv[2]; + params = parse_json(argv[3]); + msg = jsonrpc_create_notify(method, params); + string = jsonrpc_msg_is_valid(msg); + if (string) { + ovs_fatal(0, "not a JSON RPC-valid notification: %s", string); + } + + error = stream_open_block(argv[1], &stream); + if (error) { + ovs_fatal(error, "could not open \"%s\"", argv[1]); + } + rpc = jsonrpc_open(stream); + + error = jsonrpc_send_block(rpc, msg); + if (error) { + ovs_fatal(error, "could not send request"); + } + jsonrpc_close(rpc); +} + +static void - do_help(int argc UNUSED, char *argv[] UNUSED) ++do_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) +{ + usage(); +} + +static struct command all_commands[] = { + { "listen", 1, 1, do_listen }, + { "request", 3, 3, do_request }, + { "notify", 3, 3, do_notify }, + { "help", 0, INT_MAX, do_help }, + { NULL, 0, 0, NULL }, +}; diff --combined tests/test-ovsdb.c index 3025ce390,000000000..2e12d495b mode 100644,000000..100644 --- a/tests/test-ovsdb.c +++ b/tests/test-ovsdb.c @@@ -1,1865 -1,0 +1,1865 @@@ +/* + * Copyright (c) 2009, 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 +#include +#include +#include +#include +#include + +#include "command-line.h" +#include "dynamic-string.h" +#include "json.h" +#include "jsonrpc.h" +#include "ovsdb-data.h" +#include "ovsdb-error.h" +#include "ovsdb-idl.h" +#include "ovsdb-types.h" +#include "ovsdb/column.h" +#include "ovsdb/condition.h" +#include "ovsdb/file.h" +#include "ovsdb/log.h" +#include "ovsdb/mutation.h" +#include "ovsdb/ovsdb.h" +#include "ovsdb/query.h" +#include "ovsdb/row.h" +#include "ovsdb/table.h" +#include "ovsdb/transaction.h" +#include "ovsdb/trigger.h" +#include "poll-loop.h" +#include "stream.h" +#include "svec.h" +#include "tests/idltest.h" +#include "timeval.h" +#include "util.h" +#include "vlog.h" + +static struct command all_commands[]; + +static void usage(void) NO_RETURN; +static void parse_options(int argc, char *argv[]); + +int +main(int argc, char *argv[]) +{ + set_program_name(argv[0]); + time_init(); + vlog_init(); + parse_options(argc, argv); + run_command(argc - optind, argv + optind, all_commands); + return 0; +} + +static void +parse_options(int argc, char *argv[]) +{ + static struct option long_options[] = { + {"timeout", required_argument, 0, 't'}, + {"verbose", optional_argument, 0, 'v'}, + {"help", no_argument, 0, 'h'}, + {0, 0, 0, 0}, + }; + char *short_options = long_options_to_short_options(long_options); + + for (;;) { + unsigned long int timeout; + int c; + + c = getopt_long(argc, argv, short_options, long_options, NULL); + if (c == -1) { + break; + } + + switch (c) { + case 't': + timeout = strtoul(optarg, NULL, 10); + if (timeout <= 0) { + ovs_fatal(0, "value %s on -t or --timeout is not at least 1", + optarg); + } else { + time_alarm(timeout); + } + break; + + case 'h': + usage(); + + case 'v': + vlog_set_verbosity(optarg); + break; + + case '?': + exit(EXIT_FAILURE); + + default: + abort(); + } + } + free(short_options); +} + +static void +usage(void) +{ + printf("%s: Open vSwitch database test utility\n" + "usage: %s [OPTIONS] COMMAND [ARG...]\n\n" + " log-io FILE FLAGS COMMAND...\n" + " open FILE with FLAGS, run COMMANDs\n" + " parse-atomic-type TYPE\n" + " parse TYPE as OVSDB atomic type, and re-serialize\n" + " parse-base-type TYPE\n" + " parse TYPE as OVSDB base type, and re-serialize\n" + " parse-type JSON\n" + " parse JSON as OVSDB type, and re-serialize\n" + " parse-atoms TYPE ATOM...\n" + " parse JSON ATOMs as atoms of TYPE, and re-serialize\n" + " parse-atom-strings TYPE ATOM...\n" + " parse string ATOMs as atoms of given TYPE, and re-serialize\n" + " sort-atoms TYPE ATOM...\n" + " print JSON ATOMs in sorted order\n" + " parse-data TYPE DATUM...\n" + " parse JSON DATUMs as data of given TYPE, and re-serialize\n" + " parse-data-strings TYPE DATUM...\n" + " parse string DATUMs as data of given TYPE, and re-serialize\n" + " parse-column NAME OBJECT\n" + " parse column NAME with info OBJECT, and re-serialize\n" + " parse-table NAME OBJECT\n" + " parse table NAME with info OBJECT\n" + " parse-row TABLE ROW..., and re-serialize\n" + " parse each ROW of defined TABLE\n" + " compare-row TABLE ROW...\n" + " mutually compare all of the ROWs, print those that are equal\n" + " parse-conditions TABLE CONDITION...\n" + " parse each CONDITION on TABLE, and re-serialize\n" + " evaluate-conditions TABLE [CONDITION,...] [ROW,...]\n" + " test CONDITIONS on TABLE against each ROW, print results\n" + " parse-mutations TABLE MUTATION...\n" + " parse each MUTATION on TABLE, and re-serialize\n" + " execute-mutations TABLE [MUTATION,...] [ROW,...]\n" + " execute MUTATIONS on TABLE on each ROW, print results\n" + " query TABLE [ROW,...] [CONDITION,...]\n" + " add each ROW to TABLE, then query and print the rows that\n" + " satisfy each CONDITION.\n" + " query-distinct TABLE [ROW,...] [CONDITION,...] COLUMNS\n" + " add each ROW to TABLE, then query and print the rows that\n" + " satisfy each CONDITION and have distinct COLUMNS.\n" + " parse-schema JSON\n" + " parse JSON as an OVSDB schema, and re-serialize\n" + " transact COMMAND\n" + " execute each specified transactional COMMAND:\n" + " commit\n" + " abort\n" + " insert UUID I J\n" + " delete UUID\n" + " modify UUID I J\n" + " print\n" + " execute SCHEMA TRANSACTION...\n" + " executes each TRANSACTION on an initially empty database\n" + " the specified SCHEMA\n" + " trigger SCHEMA TRANSACTION...\n" + " executes each TRANSACTION on an initially empty database\n" + " the specified SCHEMA. A TRANSACTION of the form\n" + " [\"advance\", NUMBER] advances NUMBER milliseconds in\n" + " simulated time, for causing triggers to time out.\n" + " idl SERVER [TRANSACTION...]\n" + " connect to SERVER and dump the contents of the database\n" + " as seen initially by the IDL implementation and after\n" + " executing each TRANSACTION. (Each TRANSACTION must modify\n" + " the database or this command will hang.)\n", + program_name, program_name); + vlog_usage(); + printf("\nOther options:\n" + " -t, --timeout=SECS give up after SECS seconds\n" + " -h, --help display this help message\n"); + exit(EXIT_SUCCESS); +} + +/* Command helper functions. */ + +static struct json * +parse_json(const char *s) +{ + struct json *json = json_from_string(s); + if (json->type == JSON_STRING) { + ovs_fatal(0, "\"%s\": %s", s, json->u.string); + } + return json; +} + +static struct json * +unbox_json(struct json *json) +{ + if (json->type == JSON_ARRAY && json->u.array.n == 1) { + struct json *inner = json->u.array.elems[0]; + json->u.array.elems[0] = NULL; + json_destroy(json); + return inner; + } else { + return json; + } +} + +static void +print_and_free_json(struct json *json) +{ + char *string = json_to_string(json, JSSF_SORT); + json_destroy(json); + puts(string); + free(string); +} + +static void +print_and_free_ovsdb_error(struct ovsdb_error *error) +{ + char *string = ovsdb_error_to_string(error); + ovsdb_error_destroy(error); + puts(string); + free(string); +} + +static void +check_ovsdb_error(struct ovsdb_error *error) +{ + if (error) { + char *s = ovsdb_error_to_string(error); + ovsdb_error_destroy(error); + ovs_fatal(0, "%s", s); + } +} + +static void +die_if_error(char *error) +{ + if (error) { + ovs_fatal(0, "%s", error); + } +} + +/* Command implementations. */ + +static void +do_log_io(int argc, char *argv[]) +{ + const char *name = argv[1]; + char *mode = argv[2]; + + struct ovsdb_error *error; + struct ovsdb_log *log; + char *save_ptr = NULL; + const char *token; + int flags; + int i; + + for (flags = 0, token = strtok_r(mode, " |", &save_ptr); token != NULL; + token = strtok_r(NULL, " |", &save_ptr)) + { + if (!strcmp(token, "O_RDONLY")) { + flags |= O_RDONLY; + } else if (!strcmp(token, "O_RDWR")) { + flags |= O_RDWR; + } else if (!strcmp(token, "O_TRUNC")) { + flags |= O_TRUNC; + } else if (!strcmp(token, "O_CREAT")) { + flags |= O_CREAT; + } else if (!strcmp(token, "O_EXCL")) { + flags |= O_EXCL; + } else if (!strcmp(token, "O_TRUNC")) { + flags |= O_TRUNC; + } + } + + check_ovsdb_error(ovsdb_log_open(name, flags, &log)); + printf("%s: open successful\n", name); + + for (i = 3; i < argc; i++) { + const char *command = argv[i]; + if (!strcmp(command, "read")) { + struct json *json; + + error = ovsdb_log_read(log, &json); + if (!error) { + printf("%s: read: ", name); + if (json) { + print_and_free_json(json); + } else { + printf("end of log\n"); + } + continue; + } + } else if (!strncmp(command, "write:", 6)) { + struct json *json = parse_json(command + 6); + error = ovsdb_log_write(log, json); + json_destroy(json); + } else if (!strcmp(command, "commit")) { + error = ovsdb_log_commit(log); + } else { + ovs_fatal(0, "unknown log-io command \"%s\"", command); + } + if (error) { + char *s = ovsdb_error_to_string(error); + printf("%s: %s failed: %s\n", name, command, s); + free(s); + ovsdb_error_destroy(error); + } else { + printf("%s: %s successful\n", name, command); + } + } + + ovsdb_log_close(log); +} + +static void - do_parse_atomic_type(int argc UNUSED, char *argv[]) ++do_parse_atomic_type(int argc OVS_UNUSED, char *argv[]) +{ + enum ovsdb_atomic_type type; + struct json *json; + + json = unbox_json(parse_json(argv[1])); + check_ovsdb_error(ovsdb_atomic_type_from_json(&type, json)); + json_destroy(json); + print_and_free_json(ovsdb_atomic_type_to_json(type)); +} + +static void - do_parse_base_type(int argc UNUSED, char *argv[]) ++do_parse_base_type(int argc OVS_UNUSED, char *argv[]) +{ + struct ovsdb_base_type base; + struct json *json; + + json = unbox_json(parse_json(argv[1])); + check_ovsdb_error(ovsdb_base_type_from_json(&base, json)); + json_destroy(json); + print_and_free_json(ovsdb_base_type_to_json(&base)); + ovsdb_base_type_destroy(&base); +} + +static void - do_parse_type(int argc UNUSED, char *argv[]) ++do_parse_type(int argc OVS_UNUSED, char *argv[]) +{ + struct ovsdb_type type; + struct json *json; + + json = unbox_json(parse_json(argv[1])); + check_ovsdb_error(ovsdb_type_from_json(&type, json)); + json_destroy(json); + print_and_free_json(ovsdb_type_to_json(&type)); + ovsdb_type_destroy(&type); +} + +static void +do_parse_atoms(int argc, char *argv[]) +{ + struct ovsdb_base_type base; + struct json *json; + int i; + + json = unbox_json(parse_json(argv[1])); + check_ovsdb_error(ovsdb_base_type_from_json(&base, json)); + json_destroy(json); + + for (i = 2; i < argc; i++) { + struct ovsdb_error *error; + union ovsdb_atom atom; + + json = unbox_json(parse_json(argv[i])); + error = ovsdb_atom_from_json(&atom, &base, json, NULL); + json_destroy(json); + + if (error) { + print_and_free_ovsdb_error(error); + } else { + print_and_free_json(ovsdb_atom_to_json(&atom, base.type)); + ovsdb_atom_destroy(&atom, base.type); + } + } + ovsdb_base_type_destroy(&base); +} + +static void +do_parse_atom_strings(int argc, char *argv[]) +{ + struct ovsdb_base_type base; + struct json *json; + int i; + + json = unbox_json(parse_json(argv[1])); + check_ovsdb_error(ovsdb_base_type_from_json(&base, json)); + json_destroy(json); + + for (i = 2; i < argc; i++) { + union ovsdb_atom atom; + struct ds out; + + die_if_error(ovsdb_atom_from_string(&atom, &base, argv[i])); + + ds_init(&out); + ovsdb_atom_to_string(&atom, base.type, &out); + puts(ds_cstr(&out)); + ds_destroy(&out); + + ovsdb_atom_destroy(&atom, base.type); + } + ovsdb_base_type_destroy(&base); +} + +static void +do_parse_data(int argc, char *argv[]) +{ + struct ovsdb_type type; + struct json *json; + int i; + + json = unbox_json(parse_json(argv[1])); + check_ovsdb_error(ovsdb_type_from_json(&type, json)); + json_destroy(json); + + for (i = 2; i < argc; i++) { + struct ovsdb_datum datum; + + json = unbox_json(parse_json(argv[i])); + check_ovsdb_error(ovsdb_datum_from_json(&datum, &type, json, NULL)); + json_destroy(json); + + print_and_free_json(ovsdb_datum_to_json(&datum, &type)); + + ovsdb_datum_destroy(&datum, &type); + } + ovsdb_type_destroy(&type); +} + +static void +do_parse_data_strings(int argc, char *argv[]) +{ + struct ovsdb_type type; + struct json *json; + int i; + + json = unbox_json(parse_json(argv[1])); + check_ovsdb_error(ovsdb_type_from_json(&type, json)); + json_destroy(json); + + for (i = 2; i < argc; i++) { + struct ovsdb_datum datum; + struct ds out; + + die_if_error(ovsdb_datum_from_string(&datum, &type, argv[i])); + + ds_init(&out); + ovsdb_datum_to_string(&datum, &type, &out); + puts(ds_cstr(&out)); + ds_destroy(&out); + + ovsdb_datum_destroy(&datum, &type); + } + ovsdb_type_destroy(&type); +} + +static enum ovsdb_atomic_type compare_atoms_atomic_type; + +static int +compare_atoms(const void *a_, const void *b_) +{ + const union ovsdb_atom *a = a_; + const union ovsdb_atom *b = b_; + + return ovsdb_atom_compare_3way(a, b, compare_atoms_atomic_type); +} + +static void - do_sort_atoms(int argc UNUSED, char *argv[]) ++do_sort_atoms(int argc OVS_UNUSED, char *argv[]) +{ + struct ovsdb_base_type base; + union ovsdb_atom *atoms; + struct json *json, **json_atoms; + size_t n_atoms; + int i; + + json = unbox_json(parse_json(argv[1])); + check_ovsdb_error(ovsdb_base_type_from_json(&base, json)); + json_destroy(json); + + json = unbox_json(parse_json(argv[2])); + if (json->type != JSON_ARRAY) { + ovs_fatal(0, "second argument must be array"); + } + + /* Convert JSON atoms to internal representation. */ + n_atoms = json->u.array.n; + atoms = xmalloc(n_atoms * sizeof *atoms); + for (i = 0; i < n_atoms; i++) { + check_ovsdb_error(ovsdb_atom_from_json(&atoms[i], &base, + json->u.array.elems[i], NULL)); + } + json_destroy(json); + + /* Sort atoms. */ + compare_atoms_atomic_type = base.type; + qsort(atoms, n_atoms, sizeof *atoms, compare_atoms); + + /* Convert internal representation back to JSON. */ + json_atoms = xmalloc(n_atoms * sizeof *json_atoms); + for (i = 0; i < n_atoms; i++) { + json_atoms[i] = ovsdb_atom_to_json(&atoms[i], base.type); + ovsdb_atom_destroy(&atoms[i], base.type); + } + print_and_free_json(json_array_create(json_atoms, n_atoms)); + free(atoms); + ovsdb_base_type_destroy(&base); +} + +static void - do_parse_column(int argc UNUSED, char *argv[]) ++do_parse_column(int argc OVS_UNUSED, char *argv[]) +{ + struct ovsdb_column *column; + struct json *json; + + json = parse_json(argv[2]); + check_ovsdb_error(ovsdb_column_from_json(json, argv[1], &column)); + json_destroy(json); + print_and_free_json(ovsdb_column_to_json(column)); + ovsdb_column_destroy(column); +} + +static void - do_parse_table(int argc UNUSED, char *argv[]) ++do_parse_table(int argc OVS_UNUSED, char *argv[]) +{ + struct ovsdb_table_schema *ts; + struct json *json; + + json = parse_json(argv[2]); + check_ovsdb_error(ovsdb_table_schema_from_json(json, argv[1], &ts)); + json_destroy(json); + print_and_free_json(ovsdb_table_schema_to_json(ts)); + ovsdb_table_schema_destroy(ts); +} + +static void +do_parse_rows(int argc, char *argv[]) +{ + struct ovsdb_column_set all_columns; + struct ovsdb_table_schema *ts; + struct ovsdb_table *table; + struct json *json; + int i; + + json = unbox_json(parse_json(argv[1])); + check_ovsdb_error(ovsdb_table_schema_from_json(json, "mytable", &ts)); + json_destroy(json); + + table = ovsdb_table_create(ts); + ovsdb_column_set_init(&all_columns); + ovsdb_column_set_add_all(&all_columns, table); + + for (i = 2; i < argc; i++) { + struct ovsdb_column_set columns; + struct ovsdb_row *row; + + ovsdb_column_set_init(&columns); + row = ovsdb_row_create(table); + + json = unbox_json(parse_json(argv[i])); + check_ovsdb_error(ovsdb_row_from_json(row, json, NULL, &columns)); + json_destroy(json); + + print_and_free_json(ovsdb_row_to_json(row, &all_columns)); + + if (columns.n_columns) { + struct svec names; + size_t j; + char *s; + + svec_init(&names); + for (j = 0; j < columns.n_columns; j++) { + svec_add(&names, columns.columns[j]->name); + } + svec_sort(&names); + s = svec_join(&names, ", ", ""); + puts(s); + free(s); + svec_destroy(&names); + } else { + printf("\n"); + } + + ovsdb_column_set_destroy(&columns); + ovsdb_row_destroy(row); + } + + ovsdb_column_set_destroy(&all_columns); + ovsdb_table_destroy(table); /* Also destroys 'ts'. */ +} + +static void +do_compare_rows(int argc, char *argv[]) +{ + struct ovsdb_column_set all_columns; + struct ovsdb_table_schema *ts; + struct ovsdb_table *table; + struct ovsdb_row **rows; + struct json *json; + char **names; + int n_rows; + int i, j; + + json = unbox_json(parse_json(argv[1])); + check_ovsdb_error(ovsdb_table_schema_from_json(json, "mytable", &ts)); + json_destroy(json); + + table = ovsdb_table_create(ts); + ovsdb_column_set_init(&all_columns); + ovsdb_column_set_add_all(&all_columns, table); + + n_rows = argc - 2; + rows = xmalloc(sizeof *rows * n_rows); + names = xmalloc(sizeof *names * n_rows); + for (i = 0; i < n_rows; i++) { + rows[i] = ovsdb_row_create(table); + + json = parse_json(argv[i + 2]); + if (json->type != JSON_ARRAY || json->u.array.n != 2 + || json->u.array.elems[0]->type != JSON_STRING) { + ovs_fatal(0, "\"%s\" does not have expected form " + "[\"name\", {data}]", argv[i]); + } + names[i] = xstrdup(json->u.array.elems[0]->u.string); + check_ovsdb_error(ovsdb_row_from_json(rows[i], json->u.array.elems[1], + NULL, NULL)); + json_destroy(json); + } + for (i = 0; i < n_rows; i++) { + uint32_t i_hash = ovsdb_row_hash_columns(rows[i], &all_columns, 0); + for (j = i + 1; j < n_rows; j++) { + uint32_t j_hash = ovsdb_row_hash_columns(rows[j], &all_columns, 0); + if (ovsdb_row_equal_columns(rows[i], rows[j], &all_columns)) { + printf("%s == %s\n", names[i], names[j]); + if (i_hash != j_hash) { + printf("but hash(%s) != hash(%s)\n", names[i], names[j]); + abort(); + } + } else if (i_hash == j_hash) { + printf("hash(%s) == hash(%s)\n", names[i], names[j]); + } + } + } + for (i = 0; i < n_rows; i++) { + ovsdb_row_destroy(rows[i]); + free(names[i]); + } + free(rows); + free(names); + + ovsdb_column_set_destroy(&all_columns); + ovsdb_table_destroy(table); /* Also destroys 'ts'. */ +} + +static void +do_parse_conditions(int argc, char *argv[]) +{ + struct ovsdb_table_schema *ts; + struct json *json; + int exit_code = 0; + int i; + + json = unbox_json(parse_json(argv[1])); + check_ovsdb_error(ovsdb_table_schema_from_json(json, "mytable", &ts)); + json_destroy(json); + + for (i = 2; i < argc; i++) { + struct ovsdb_condition cnd; + struct ovsdb_error *error; + + json = parse_json(argv[i]); + error = ovsdb_condition_from_json(ts, json, NULL, &cnd); + if (!error) { + print_and_free_json(ovsdb_condition_to_json(&cnd)); + } else { + char *s = ovsdb_error_to_string(error); + ovs_error(0, "%s", s); + free(s); + ovsdb_error_destroy(error); + exit_code = 1; + } + json_destroy(json); + + ovsdb_condition_destroy(&cnd); + } + ovsdb_table_schema_destroy(ts); + + exit(exit_code); +} + +static void - do_evaluate_conditions(int argc UNUSED, char *argv[]) ++do_evaluate_conditions(int argc OVS_UNUSED, char *argv[]) +{ + struct ovsdb_table_schema *ts; + struct ovsdb_table *table; + struct ovsdb_condition *conditions; + size_t n_conditions; + struct ovsdb_row **rows; + size_t n_rows; + struct json *json; + size_t i, j; + + /* Parse table schema, create table. */ + json = unbox_json(parse_json(argv[1])); + check_ovsdb_error(ovsdb_table_schema_from_json(json, "mytable", &ts)); + json_destroy(json); + + table = ovsdb_table_create(ts); + + /* Parse conditions. */ + json = parse_json(argv[2]); + if (json->type != JSON_ARRAY) { + ovs_fatal(0, "CONDITION argument is not JSON array"); + } + n_conditions = json->u.array.n; + conditions = xmalloc(n_conditions * sizeof *conditions); + for (i = 0; i < n_conditions; i++) { + check_ovsdb_error(ovsdb_condition_from_json(ts, json->u.array.elems[i], + NULL, &conditions[i])); + } + json_destroy(json); + + /* Parse rows. */ + json = parse_json(argv[3]); + if (json->type != JSON_ARRAY) { + ovs_fatal(0, "ROW argument is not JSON array"); + } + n_rows = json->u.array.n; + rows = xmalloc(n_rows * sizeof *rows); + for (i = 0; i < n_rows; i++) { + rows[i] = ovsdb_row_create(table); + check_ovsdb_error(ovsdb_row_from_json(rows[i], json->u.array.elems[i], + NULL, NULL)); + } + json_destroy(json); + + for (i = 0; i < n_conditions; i++) { + printf("condition %2d:", i); + for (j = 0; j < n_rows; j++) { + bool result = ovsdb_condition_evaluate(rows[j], &conditions[i]); + if (j % 5 == 0) { + putchar(' '); + } + putchar(result ? 'T' : '-'); + } + printf("\n"); + } + + for (i = 0; i < n_conditions; i++) { + ovsdb_condition_destroy(&conditions[i]); + } + free(conditions); + for (i = 0; i < n_rows; i++) { + ovsdb_row_destroy(rows[i]); + } + free(rows); + ovsdb_table_destroy(table); /* Also destroys 'ts'. */ +} + +static void +do_parse_mutations(int argc, char *argv[]) +{ + struct ovsdb_table_schema *ts; + struct json *json; + int exit_code = 0; + int i; + + json = unbox_json(parse_json(argv[1])); + check_ovsdb_error(ovsdb_table_schema_from_json(json, "mytable", &ts)); + json_destroy(json); + + for (i = 2; i < argc; i++) { + struct ovsdb_mutation_set set; + struct ovsdb_error *error; + + json = parse_json(argv[i]); + error = ovsdb_mutation_set_from_json(ts, json, NULL, &set); + if (!error) { + print_and_free_json(ovsdb_mutation_set_to_json(&set)); + } else { + char *s = ovsdb_error_to_string(error); + ovs_error(0, "%s", s); + free(s); + ovsdb_error_destroy(error); + exit_code = 1; + } + json_destroy(json); + + ovsdb_mutation_set_destroy(&set); + } + ovsdb_table_schema_destroy(ts); + + exit(exit_code); +} + +static void - do_execute_mutations(int argc UNUSED, char *argv[]) ++do_execute_mutations(int argc OVS_UNUSED, char *argv[]) +{ + struct ovsdb_table_schema *ts; + struct ovsdb_table *table; + struct ovsdb_mutation_set *sets; + size_t n_sets; + struct ovsdb_row **rows; + size_t n_rows; + struct json *json; + size_t i, j; + + /* Parse table schema, create table. */ + json = unbox_json(parse_json(argv[1])); + check_ovsdb_error(ovsdb_table_schema_from_json(json, "mytable", &ts)); + json_destroy(json); + + table = ovsdb_table_create(ts); + + /* Parse mutations. */ + json = parse_json(argv[2]); + if (json->type != JSON_ARRAY) { + ovs_fatal(0, "MUTATION argument is not JSON array"); + } + n_sets = json->u.array.n; + sets = xmalloc(n_sets * sizeof *sets); + for (i = 0; i < n_sets; i++) { + check_ovsdb_error(ovsdb_mutation_set_from_json(ts, + json->u.array.elems[i], + NULL, &sets[i])); + } + json_destroy(json); + + /* Parse rows. */ + json = parse_json(argv[3]); + if (json->type != JSON_ARRAY) { + ovs_fatal(0, "ROW argument is not JSON array"); + } + n_rows = json->u.array.n; + rows = xmalloc(n_rows * sizeof *rows); + for (i = 0; i < n_rows; i++) { + rows[i] = ovsdb_row_create(table); + check_ovsdb_error(ovsdb_row_from_json(rows[i], json->u.array.elems[i], + NULL, NULL)); + } + json_destroy(json); + + for (i = 0; i < n_sets; i++) { + printf("mutation %2d:\n", i); + for (j = 0; j < n_rows; j++) { + struct ovsdb_error *error; + struct ovsdb_row *row; + + row = ovsdb_row_clone(rows[j]); + error = ovsdb_mutation_set_execute(row, &sets[i]); + + printf("row %zu: ", j); + if (error) { + print_and_free_ovsdb_error(error); + } else { + struct ovsdb_column_set columns; + struct shash_node *node; + + ovsdb_column_set_init(&columns); + SHASH_FOR_EACH (node, &ts->columns) { + struct ovsdb_column *c = node->data; + if (!ovsdb_datum_equals(&row->fields[c->index], + &rows[j]->fields[c->index], + &c->type)) { + ovsdb_column_set_add(&columns, c); + } + } + if (columns.n_columns) { + print_and_free_json(ovsdb_row_to_json(row, &columns)); + } else { + printf("no change\n"); + } + ovsdb_column_set_destroy(&columns); + } + ovsdb_row_destroy(row); + } + printf("\n"); + } + + for (i = 0; i < n_sets; i++) { + ovsdb_mutation_set_destroy(&sets[i]); + } + free(sets); + for (i = 0; i < n_rows; i++) { + ovsdb_row_destroy(rows[i]); + } + free(rows); + ovsdb_table_destroy(table); /* Also destroys 'ts'. */ +} + +struct do_query_cbdata { + struct uuid *row_uuids; + int *counts; + size_t n_rows; +}; + +static bool +do_query_cb(const struct ovsdb_row *row, void *cbdata_) +{ + struct do_query_cbdata *cbdata = cbdata_; + size_t i; + + for (i = 0; i < cbdata->n_rows; i++) { + if (uuid_equals(ovsdb_row_get_uuid(row), &cbdata->row_uuids[i])) { + cbdata->counts[i]++; + } + } + + return true; +} + +static void - do_query(int argc UNUSED, char *argv[]) ++do_query(int argc OVS_UNUSED, char *argv[]) +{ + struct do_query_cbdata cbdata; + struct ovsdb_table_schema *ts; + struct ovsdb_table *table; + struct json *json; + int exit_code = 0; + size_t i; + + /* Parse table schema, create table. */ + json = unbox_json(parse_json(argv[1])); + check_ovsdb_error(ovsdb_table_schema_from_json(json, "mytable", &ts)); + json_destroy(json); + + table = ovsdb_table_create(ts); + + /* Parse rows, add to table. */ + json = parse_json(argv[2]); + if (json->type != JSON_ARRAY) { + ovs_fatal(0, "ROW argument is not JSON array"); + } + cbdata.n_rows = json->u.array.n; + cbdata.row_uuids = xmalloc(cbdata.n_rows * sizeof *cbdata.row_uuids); + cbdata.counts = xmalloc(cbdata.n_rows * sizeof *cbdata.counts); + for (i = 0; i < cbdata.n_rows; i++) { + struct ovsdb_row *row = ovsdb_row_create(table); + uuid_generate(ovsdb_row_get_uuid_rw(row)); + check_ovsdb_error(ovsdb_row_from_json(row, json->u.array.elems[i], + NULL, NULL)); + if (ovsdb_table_get_row(table, ovsdb_row_get_uuid(row))) { + ovs_fatal(0, "duplicate UUID "UUID_FMT" in table", + UUID_ARGS(ovsdb_row_get_uuid(row))); + } + cbdata.row_uuids[i] = *ovsdb_row_get_uuid(row); + ovsdb_table_put_row(table, row); + } + json_destroy(json); + + /* Parse conditions and execute queries. */ + json = parse_json(argv[3]); + if (json->type != JSON_ARRAY) { + ovs_fatal(0, "CONDITION argument is not JSON array"); + } + for (i = 0; i < json->u.array.n; i++) { + struct ovsdb_condition cnd; + size_t j; + + check_ovsdb_error(ovsdb_condition_from_json(ts, json->u.array.elems[i], + NULL, &cnd)); + + memset(cbdata.counts, 0, cbdata.n_rows * sizeof *cbdata.counts); + ovsdb_query(table, &cnd, do_query_cb, &cbdata); + + printf("query %2d:", i); + for (j = 0; j < cbdata.n_rows; j++) { + if (j % 5 == 0) { + putchar(' '); + } + if (cbdata.counts[j]) { + printf("%d", cbdata.counts[j]); + if (cbdata.counts[j] > 1) { + /* Dup! */ + exit_code = 1; + } + } else { + putchar('-'); + } + } + putchar('\n'); + + ovsdb_condition_destroy(&cnd); + } + json_destroy(json); + + ovsdb_table_destroy(table); /* Also destroys 'ts'. */ + + exit(exit_code); +} + +struct do_query_distinct_class { + struct ovsdb_row *example; + int count; +}; + +struct do_query_distinct_row { + struct uuid uuid; + struct do_query_distinct_class *class; +}; + +static void - do_query_distinct(int argc UNUSED, char *argv[]) ++do_query_distinct(int argc OVS_UNUSED, char *argv[]) +{ + struct ovsdb_column_set columns; + struct ovsdb_table_schema *ts; + struct ovsdb_table *table; + struct do_query_distinct_row *rows; + size_t n_rows; + struct do_query_distinct_class *classes; + size_t n_classes; + struct json *json; + int exit_code = 0; + size_t i, j, k; + + /* Parse table schema, create table. */ + json = unbox_json(parse_json(argv[1])); + check_ovsdb_error(ovsdb_table_schema_from_json(json, "mytable", &ts)); + json_destroy(json); + + table = ovsdb_table_create(ts); + + /* Parse column set. */ + json = parse_json(argv[4]); + check_ovsdb_error(ovsdb_column_set_from_json(json, table, &columns)); + json_destroy(json); + + /* Parse rows, add to table. */ + json = parse_json(argv[2]); + if (json->type != JSON_ARRAY) { + ovs_fatal(0, "ROW argument is not JSON array"); + } + n_rows = json->u.array.n; + rows = xmalloc(n_rows * sizeof *rows); + classes = xmalloc(n_rows * sizeof *classes); + n_classes = 0; + for (i = 0; i < n_rows; i++) { + struct ovsdb_row *row; + size_t j; + + /* Parse row. */ + row = ovsdb_row_create(table); + uuid_generate(ovsdb_row_get_uuid_rw(row)); + check_ovsdb_error(ovsdb_row_from_json(row, json->u.array.elems[i], + NULL, NULL)); + + /* Initialize row and find equivalence class. */ + rows[i].uuid = *ovsdb_row_get_uuid(row); + rows[i].class = NULL; + for (j = 0; j < n_classes; j++) { + if (ovsdb_row_equal_columns(row, classes[j].example, &columns)) { + rows[i].class = &classes[j]; + break; + } + } + if (!rows[i].class) { + rows[i].class = &classes[n_classes]; + classes[n_classes].example = ovsdb_row_clone(row); + n_classes++; + } + + /* Add row to table. */ + if (ovsdb_table_get_row(table, ovsdb_row_get_uuid(row))) { + ovs_fatal(0, "duplicate UUID "UUID_FMT" in table", + UUID_ARGS(ovsdb_row_get_uuid(row))); + } + ovsdb_table_put_row(table, row); + + } + json_destroy(json); + + /* Parse conditions and execute queries. */ + json = parse_json(argv[3]); + if (json->type != JSON_ARRAY) { + ovs_fatal(0, "CONDITION argument is not JSON array"); + } + for (i = 0; i < json->u.array.n; i++) { + struct ovsdb_row_set results; + struct ovsdb_condition cnd; + + check_ovsdb_error(ovsdb_condition_from_json(ts, json->u.array.elems[i], + NULL, &cnd)); + + for (j = 0; j < n_classes; j++) { + classes[j].count = 0; + } + ovsdb_row_set_init(&results); + ovsdb_query_distinct(table, &cnd, &columns, &results); + for (j = 0; j < results.n_rows; j++) { + for (k = 0; k < n_rows; k++) { + if (uuid_equals(ovsdb_row_get_uuid(results.rows[j]), + &rows[k].uuid)) { + rows[k].class->count++; + } + } + } + ovsdb_row_set_destroy(&results); + + printf("query %2d:", i); + for (j = 0; j < n_rows; j++) { + int count = rows[j].class->count; + + if (j % 5 == 0) { + putchar(' '); + } + if (count > 1) { + /* Dup! */ + printf("%d", count); + exit_code = 1; + } else if (count == 1) { + putchar("abcdefghijklmnopqrstuvwxyz"[rows[j].class - classes]); + } else { + putchar('-'); + } + } + putchar('\n'); + + ovsdb_condition_destroy(&cnd); + } + json_destroy(json); + + ovsdb_table_destroy(table); /* Also destroys 'ts'. */ + + exit(exit_code); +} + +static void - do_parse_schema(int argc UNUSED, char *argv[]) ++do_parse_schema(int argc OVS_UNUSED, char *argv[]) +{ + struct ovsdb_schema *schema; + struct json *json; + + json = parse_json(argv[1]); + check_ovsdb_error(ovsdb_schema_from_json(json, &schema)); + json_destroy(json); + print_and_free_json(ovsdb_schema_to_json(schema)); + ovsdb_schema_destroy(schema); +} + +static void - do_execute(int argc UNUSED, char *argv[]) ++do_execute(int argc OVS_UNUSED, char *argv[]) +{ + struct ovsdb_schema *schema; + struct json *json; + struct ovsdb *db; + int i; + + /* Create database. */ + json = parse_json(argv[1]); + check_ovsdb_error(ovsdb_schema_from_json(json, &schema)); + json_destroy(json); + db = ovsdb_create(schema); + + for (i = 2; i < argc; i++) { + struct json *params, *result; + char *s; + + params = parse_json(argv[i]); + result = ovsdb_execute(db, params, 0, NULL); + s = json_to_string(result, JSSF_SORT); + printf("%s\n", s); + free(s); + json_destroy(params); + json_destroy(result); + } + + ovsdb_destroy(db); +} + +struct test_trigger { + struct ovsdb_trigger trigger; + int number; +}; + +static void +do_trigger_dump(struct test_trigger *t, long long int now, const char *title) +{ + struct json *result; + char *s; + + result = ovsdb_trigger_steal_result(&t->trigger); + s = json_to_string(result, JSSF_SORT); + printf("t=%lld: trigger %d (%s): %s\n", now, t->number, title, s); + free(s); + json_destroy(result); + ovsdb_trigger_destroy(&t->trigger); + free(t); +} + +static void - do_trigger(int argc UNUSED, char *argv[]) ++do_trigger(int argc OVS_UNUSED, char *argv[]) +{ + struct ovsdb_schema *schema; + struct list completions; + struct json *json; + struct ovsdb *db; + long long int now; + int number; + int i; + + /* Create database. */ + json = parse_json(argv[1]); + check_ovsdb_error(ovsdb_schema_from_json(json, &schema)); + json_destroy(json); + db = ovsdb_create(schema); + + list_init(&completions); + now = 0; + number = 0; + for (i = 2; i < argc; i++) { + struct json *params = parse_json(argv[i]); + if (params->type == JSON_ARRAY + && json_array(params)->n == 2 + && json_array(params)->elems[0]->type == JSON_STRING + && !strcmp(json_string(json_array(params)->elems[0]), "advance") + && json_array(params)->elems[1]->type == JSON_INTEGER) { + now += json_integer(json_array(params)->elems[1]); + json_destroy(params); + } else { + struct test_trigger *t = xmalloc(sizeof *t); + ovsdb_trigger_init(db, &t->trigger, params, &completions, now); + t->number = number++; + if (ovsdb_trigger_is_complete(&t->trigger)) { + do_trigger_dump(t, now, "immediate"); + } else { + printf("t=%lld: new trigger %d\n", now, t->number); + } + } + + ovsdb_trigger_run(db, now); + while (!list_is_empty(&completions)) { + do_trigger_dump(CONTAINER_OF(list_pop_front(&completions), + struct test_trigger, trigger.node), + now, "delayed"); + } + + ovsdb_trigger_wait(db, now); + poll_immediate_wake(); + poll_block(); + } + + ovsdb_destroy(db); +} + +static void - do_help(int argc UNUSED, char *argv[] UNUSED) ++do_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) +{ + usage(); +} + +/* "transact" command. */ + +static struct ovsdb *do_transact_db; +static struct ovsdb_txn *do_transact_txn; +static struct ovsdb_table *do_transact_table; + +static void - do_transact_commit(int argc UNUSED, char *argv[] UNUSED) ++do_transact_commit(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) +{ + ovsdb_txn_commit(do_transact_txn, false); + do_transact_txn = NULL; +} + +static void - do_transact_abort(int argc UNUSED, char *argv[] UNUSED) ++do_transact_abort(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) +{ + ovsdb_txn_abort(do_transact_txn); + do_transact_txn = NULL; +} + +static void +uuid_from_integer(int integer, struct uuid *uuid) +{ + uuid_zero(uuid); + uuid->parts[3] = integer; +} + +static const struct ovsdb_row * +do_transact_find_row(const char *uuid_string) +{ + const struct ovsdb_row *row; + struct uuid uuid; + + uuid_from_integer(atoi(uuid_string), &uuid); + row = ovsdb_table_get_row(do_transact_table, &uuid); + if (!row) { + ovs_fatal(0, "table does not contain row with UUID "UUID_FMT, + UUID_ARGS(&uuid)); + } + return row; +} + +static void +do_transact_set_integer(struct ovsdb_row *row, const char *column_name, + int integer) +{ + if (integer != -1) { + const struct ovsdb_column *column; + + column = ovsdb_table_schema_get_column(do_transact_table->schema, + column_name); + row->fields[column->index].keys[0].integer = integer; + } +} + +static int +do_transact_get_integer(const struct ovsdb_row *row, const char *column_name) +{ + const struct ovsdb_column *column; + + column = ovsdb_table_schema_get_column(do_transact_table->schema, + column_name); + return row->fields[column->index].keys[0].integer; +} + +static void +do_transact_set_i_j(struct ovsdb_row *row, + const char *i_string, const char *j_string) +{ + do_transact_set_integer(row, "i", atoi(i_string)); + do_transact_set_integer(row, "j", atoi(j_string)); +} + +static void - do_transact_insert(int argc UNUSED, char *argv[] UNUSED) ++do_transact_insert(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) +{ + struct ovsdb_row *row; + struct uuid *uuid; + + row = ovsdb_row_create(do_transact_table); + + /* Set UUID. */ + uuid = ovsdb_row_get_uuid_rw(row); + uuid_from_integer(atoi(argv[1]), uuid); + if (ovsdb_table_get_row(do_transact_table, uuid)) { + ovs_fatal(0, "table already contains row with UUID "UUID_FMT, + UUID_ARGS(uuid)); + } + + do_transact_set_i_j(row, argv[2], argv[3]); + + /* Insert row. */ + ovsdb_txn_row_insert(do_transact_txn, row); +} + +static void - do_transact_delete(int argc UNUSED, char *argv[] UNUSED) ++do_transact_delete(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) +{ + const struct ovsdb_row *row = do_transact_find_row(argv[1]); + ovsdb_txn_row_delete(do_transact_txn, row); +} + +static void - do_transact_modify(int argc UNUSED, char *argv[] UNUSED) ++do_transact_modify(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) +{ + const struct ovsdb_row *row_ro; + struct ovsdb_row *row_rw; + + row_ro = do_transact_find_row(argv[1]); + row_rw = ovsdb_txn_row_modify(do_transact_txn, row_ro); + do_transact_set_i_j(row_rw, argv[2], argv[3]); +} + +static int +compare_rows_by_uuid(const void *a_, const void *b_) +{ + struct ovsdb_row *const *ap = a_; + struct ovsdb_row *const *bp = b_; + + return uuid_compare_3way(ovsdb_row_get_uuid(*ap), ovsdb_row_get_uuid(*bp)); +} + +static void - do_transact_print(int argc UNUSED, char *argv[] UNUSED) ++do_transact_print(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) +{ + const struct ovsdb_row **rows; + const struct ovsdb_row *row; + size_t n_rows; + size_t i; + + n_rows = hmap_count(&do_transact_table->rows); + rows = xmalloc(n_rows * sizeof *rows); + i = 0; + HMAP_FOR_EACH (row, struct ovsdb_row, hmap_node, + &do_transact_table->rows) { + rows[i++] = row; + } + assert(i == n_rows); + + qsort(rows, n_rows, sizeof *rows, compare_rows_by_uuid); + + for (i = 0; i < n_rows; i++) { + printf("\n%"PRId32": i=%d, j=%d", + ovsdb_row_get_uuid(rows[i])->parts[3], + do_transact_get_integer(rows[i], "i"), + do_transact_get_integer(rows[i], "j")); + } + + free(rows); +} + +static void +do_transact(int argc, char *argv[]) +{ + static const struct command do_transact_commands[] = { + { "commit", 0, 0, do_transact_commit }, + { "abort", 0, 0, do_transact_abort }, + { "insert", 2, 3, do_transact_insert }, + { "delete", 1, 1, do_transact_delete }, + { "modify", 2, 3, do_transact_modify }, + { "print", 0, 0, do_transact_print }, + { NULL, 0, 0, NULL }, + }; + + struct ovsdb_schema *schema; + struct json *json; + int i; + + /* Create table. */ + json = parse_json("{\"name\": \"testdb\", " + " \"tables\": " + " {\"mytable\": " + " {\"columns\": " + " {\"i\": {\"type\": \"integer\"}, " + " \"j\": {\"type\": \"integer\"}}}}}"); + check_ovsdb_error(ovsdb_schema_from_json(json, &schema)); + json_destroy(json); + do_transact_db = ovsdb_create(schema); + do_transact_table = ovsdb_get_table(do_transact_db, "mytable"); + assert(do_transact_table != NULL); + + for (i = 1; i < argc; i++) { + struct json *command; + size_t n_args; + char **args; + int j; + + command = parse_json(argv[i]); + if (command->type != JSON_ARRAY) { + ovs_fatal(0, "transaction %d must be JSON array " + "with at least 1 element", i); + } + + n_args = command->u.array.n; + args = xmalloc((n_args + 1) * sizeof *args); + for (j = 0; j < n_args; j++) { + struct json *s = command->u.array.elems[j]; + if (s->type != JSON_STRING) { + ovs_fatal(0, "transaction %d argument %d must be JSON string", + i, j); + } + args[j] = xstrdup(json_string(s)); + } + args[n_args] = NULL; + + if (!do_transact_txn) { + do_transact_txn = ovsdb_txn_create(do_transact_db); + } + + for (j = 0; j < n_args; j++) { + if (j) { + putchar(' '); + } + fputs(args[j], stdout); + } + fputs(":", stdout); + run_command(n_args, args, do_transact_commands); + putchar('\n'); + + for (j = 0; j < n_args; j++) { + free(args[j]); + } + free(args); + json_destroy(command); + } + ovsdb_txn_abort(do_transact_txn); + ovsdb_destroy(do_transact_db); /* Also destroys 'schema'. */ +} + +static int +compare_link1(const void *a_, const void *b_) +{ + const struct idltest_link1 *const *ap = a_; + const struct idltest_link1 *const *bp = b_; + const struct idltest_link1 *a = *ap; + const struct idltest_link1 *b = *bp; + + return a->i < b->i ? -1 : a->i > b->i; +} + +static void +print_idl(struct ovsdb_idl *idl, int step) +{ + const struct idltest_simple *s; + const struct idltest_link1 *l1; + const struct idltest_link2 *l2; + int n = 0; + + IDLTEST_SIMPLE_FOR_EACH (s, idl) { + size_t i; + + printf("%03d: i=%"PRId64" r=%g b=%s s=%s u="UUID_FMT" ia=[", + step, s->i, s->r, s->b ? "true" : "false", + s->s, UUID_ARGS(&s->u)); + for (i = 0; i < s->n_ia; i++) { + printf("%s%"PRId64, i ? " " : "", s->ia[i]); + } + printf("] ra=["); + for (i = 0; i < s->n_ra; i++) { + printf("%s%g", i ? " " : "", s->ra[i]); + } + printf("] ba=["); + for (i = 0; i < s->n_ba; i++) { + printf("%s%s", i ? " " : "", s->ba[i] ? "true" : "false"); + } + printf("] sa=["); + for (i = 0; i < s->n_sa; i++) { + printf("%s%s", i ? " " : "", s->sa[i]); + } + printf("] ua=["); + for (i = 0; i < s->n_ua; i++) { + printf("%s"UUID_FMT, i ? " " : "", UUID_ARGS(&s->ua[i])); + } + printf("] uuid="UUID_FMT"\n", UUID_ARGS(&s->header_.uuid)); + n++; + } + IDLTEST_LINK1_FOR_EACH (l1, idl) { + struct idltest_link1 **links; + size_t i; + + printf("%03d: i=%"PRId64" k=", step, l1->i); + if (l1->k) { + printf("%"PRId64, l1->k->i); + } + printf(" ka=["); + links = xmemdup(l1->ka, l1->n_ka * sizeof *l1->ka); + qsort(links, l1->n_ka, sizeof *links, compare_link1); + for (i = 0; i < l1->n_ka; i++) { + printf("%s%"PRId64, i ? " " : "", links[i]->i); + } + free(links); + printf("] l2="); + if (l1->l2) { + printf("%"PRId64, l1->l2->i); + } + printf(" uuid="UUID_FMT"\n", UUID_ARGS(&l1->header_.uuid)); + n++; + } + IDLTEST_LINK2_FOR_EACH (l2, idl) { + printf("%03d: i=%"PRId64" l1=", step, l2->i); + if (l2->l1) { + printf("%"PRId64, l2->l1->i); + } + printf(" uuid="UUID_FMT"\n", UUID_ARGS(&l2->header_.uuid)); + n++; + } + if (!n) { + printf("%03d: empty\n", step); + } +} + +static unsigned int +print_updated_idl(struct ovsdb_idl *idl, struct jsonrpc *rpc, + int step, unsigned int seqno) +{ + for (;;) { + unsigned int new_seqno; + + if (rpc) { + jsonrpc_run(rpc); + } + ovsdb_idl_run(idl); + new_seqno = ovsdb_idl_get_seqno(idl); + if (new_seqno != seqno) { + print_idl(idl, step); + return new_seqno; + } + + if (rpc) { + jsonrpc_wait(rpc); + } + ovsdb_idl_wait(idl); + poll_block(); + } +} + +static void +parse_uuids(const struct json *json, struct ovsdb_symbol_table *symtab, + size_t *n) +{ + struct uuid uuid; + + if (json->type == JSON_STRING && uuid_from_string(&uuid, json->u.string)) { + char *name = xasprintf("#%d#", *n); + fprintf(stderr, "%s = "UUID_FMT"\n", name, UUID_ARGS(&uuid)); + ovsdb_symbol_table_put(symtab, name, &uuid, false); + free(name); + *n += 1; + } else if (json->type == JSON_ARRAY) { + size_t i; + + for (i = 0; i < json->u.array.n; i++) { + parse_uuids(json->u.array.elems[i], symtab, n); + } + } else if (json->type == JSON_OBJECT) { + const struct shash_node *node; + + SHASH_FOR_EACH (node, json_object(json)) { + parse_uuids(node->data, symtab, n); + } + } +} + +static void +substitute_uuids(struct json *json, const struct ovsdb_symbol_table *symtab) +{ + if (json->type == JSON_STRING) { + const struct ovsdb_symbol *symbol; + + symbol = ovsdb_symbol_table_get(symtab, json->u.string); + if (symbol) { + free(json->u.string); + json->u.string = xasprintf(UUID_FMT, UUID_ARGS(&symbol->uuid)); + } + } else if (json->type == JSON_ARRAY) { + size_t i; + + for (i = 0; i < json->u.array.n; i++) { + substitute_uuids(json->u.array.elems[i], symtab); + } + } else if (json->type == JSON_OBJECT) { + const struct shash_node *node; + + SHASH_FOR_EACH (node, json_object(json)) { + substitute_uuids(node->data, symtab); + } + } +} + +static const struct idltest_simple * +idltest_find_simple(struct ovsdb_idl *idl, int i) +{ + const struct idltest_simple *s; + + IDLTEST_SIMPLE_FOR_EACH (s, idl) { + if (s->i == i) { + return s; + } + } + return NULL; +} + +static void +idl_set(struct ovsdb_idl *idl, char *commands, int step) +{ + char *cmd, *save_ptr1 = NULL; + struct ovsdb_idl_txn *txn; + enum ovsdb_idl_txn_status status; + bool increment = false; + + txn = ovsdb_idl_txn_create(idl); + for (cmd = strtok_r(commands, ",", &save_ptr1); cmd; + cmd = strtok_r(NULL, ",", &save_ptr1)) { + char *save_ptr2 = NULL; + char *name, *arg1, *arg2, *arg3; + + name = strtok_r(cmd, " ", &save_ptr2); + arg1 = strtok_r(NULL, " ", &save_ptr2); + arg2 = strtok_r(NULL, " ", &save_ptr2); + arg3 = strtok_r(NULL, " ", &save_ptr2); + + if (!strcmp(name, "set")) { + const struct idltest_simple *s; + + if (!arg3) { + ovs_fatal(0, "\"set\" command requires 3 arguments"); + } + + s = idltest_find_simple(idl, atoi(arg1)); + if (!s) { + ovs_fatal(0, "\"set\" command asks for nonexistent " + "i=%d", atoi(arg1)); + } + + if (!strcmp(arg2, "b")) { + idltest_simple_set_b(s, atoi(arg3)); + } else if (!strcmp(arg2, "s")) { + idltest_simple_set_s(s, arg3); + } else if (!strcmp(arg2, "u")) { + struct uuid uuid; + uuid_from_string(&uuid, arg3); + idltest_simple_set_u(s, uuid); + } else if (!strcmp(arg2, "r")) { + idltest_simple_set_r(s, atof(arg3)); + } else { + ovs_fatal(0, "\"set\" command asks for unknown column %s", + arg2); + } + } else if (!strcmp(name, "insert")) { + struct idltest_simple *s; + + if (!arg1 || arg2) { + ovs_fatal(0, "\"set\" command requires 1 argument"); + } + + s = idltest_simple_insert(txn); + idltest_simple_set_i(s, atoi(arg1)); + } else if (!strcmp(name, "delete")) { + const struct idltest_simple *s; + + if (!arg1 || arg2) { + ovs_fatal(0, "\"set\" command requires 1 argument"); + } + + s = idltest_find_simple(idl, atoi(arg1)); + if (!s) { + ovs_fatal(0, "\"set\" command asks for nonexistent " + "i=%d", atoi(arg1)); + } + idltest_simple_delete(s); + } else if (!strcmp(name, "increment")) { + if (!arg2 || arg3) { + ovs_fatal(0, "\"set\" command requires 2 arguments"); + } + ovsdb_idl_txn_increment(txn, arg1, arg2, NULL); + increment = true; + } else { + ovs_fatal(0, "unknown command %s", name); + } + } + + while ((status = ovsdb_idl_txn_commit(txn)) == TXN_INCOMPLETE) { + ovsdb_idl_run(idl); + ovsdb_idl_wait(idl); + ovsdb_idl_txn_wait(txn); + poll_block(); + } + printf("%03d: commit, status=%s", + step, ovsdb_idl_txn_status_to_string(status)); + if (increment) { + printf(", increment=%"PRId64, + ovsdb_idl_txn_get_increment_new_value(txn)); + } + putchar('\n'); + ovsdb_idl_txn_destroy(txn); +} + +static void +do_idl(int argc, char *argv[]) +{ + struct jsonrpc *rpc; + struct ovsdb_idl *idl; + unsigned int seqno = 0; + struct ovsdb_symbol_table *symtab; + size_t n_uuids = 0; + int step = 0; + int error; + int i; + + idltest_init(); + + idl = ovsdb_idl_create(argv[1], &idltest_idl_class); + if (argc > 2) { + struct stream *stream; + + error = stream_open_block(argv[1], &stream); + if (error) { + ovs_fatal(error, "failed to connect to \"%s\"", argv[1]); + } + rpc = jsonrpc_open(stream); + } else { + rpc = NULL; + } + + setvbuf(stdout, NULL, _IOLBF, 0); + + symtab = ovsdb_symbol_table_create(); + for (i = 2; i < argc; i++) { + char *arg = argv[i]; + struct jsonrpc_msg *request, *reply; + int error; + + if (*arg == '+') { + /* The previous transaction didn't change anything. */ + arg++; + } else { + seqno = print_updated_idl(idl, rpc, step++, seqno); + } + + if (!strcmp(arg, "reconnect")) { + printf("%03d: reconnect\n", step++); + ovsdb_idl_force_reconnect(idl); + } else if (arg[0] != '[') { + idl_set(idl, arg, step++); + } else { + struct json *json = parse_json(arg); + substitute_uuids(json, symtab); + request = jsonrpc_create_request("transact", json, NULL); + error = jsonrpc_transact_block(rpc, request, &reply); + if (error) { + ovs_fatal(error, "jsonrpc transaction failed"); + } + printf("%03d: ", step++); + if (reply->result) { + parse_uuids(reply->result, symtab, &n_uuids); + } + json_destroy(reply->id); + reply->id = NULL; + print_and_free_json(jsonrpc_msg_to_json(reply)); + } + } + ovsdb_symbol_table_destroy(symtab); + + if (rpc) { + jsonrpc_close(rpc); + } + print_updated_idl(idl, NULL, step++, seqno); + ovsdb_idl_destroy(idl); + printf("%03d: done\n", step); +} + +static struct command all_commands[] = { + { "log-io", 2, INT_MAX, do_log_io }, + { "parse-atomic-type", 1, 1, do_parse_atomic_type }, + { "parse-base-type", 1, 1, do_parse_base_type }, + { "parse-type", 1, 1, do_parse_type }, + { "parse-atoms", 2, INT_MAX, do_parse_atoms }, + { "parse-atom-strings", 2, INT_MAX, do_parse_atom_strings }, + { "parse-data", 2, INT_MAX, do_parse_data }, + { "parse-data-strings", 2, INT_MAX, do_parse_data_strings }, + { "sort-atoms", 2, 2, do_sort_atoms }, + { "parse-column", 2, 2, do_parse_column }, + { "parse-table", 2, 2, do_parse_table }, + { "parse-rows", 2, INT_MAX, do_parse_rows }, + { "compare-rows", 2, INT_MAX, do_compare_rows }, + { "parse-conditions", 2, INT_MAX, do_parse_conditions }, + { "evaluate-conditions", 3, 3, do_evaluate_conditions }, + { "parse-mutations", 2, INT_MAX, do_parse_mutations }, + { "execute-mutations", 3, 3, do_execute_mutations }, + { "query", 3, 3, do_query }, + { "query-distinct", 4, 4, do_query_distinct }, + { "transact", 1, INT_MAX, do_transact }, + { "parse-schema", 1, 1, do_parse_schema }, + { "execute", 2, INT_MAX, do_execute }, + { "trigger", 2, INT_MAX, do_trigger }, + { "idl", 1, INT_MAX, do_idl }, + { "help", 0, INT_MAX, do_help }, + { NULL, 0, 0, NULL }, +}; diff --combined tests/test-reconnect.c index 8441fadd3,000000000..93991ff56 mode 100644,000000..100644 --- a/tests/test-reconnect.c +++ b/tests/test-reconnect.c @@@ -1,253 -1,0 +1,253 @@@ +/* - * Copyright (c) 2009 Nicira Networks. ++ * Copyright (c) 2009, 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 "reconnect.h" + +#include +#include +#include +#include + +#include "command-line.h" +#include "compiler.h" +#include "svec.h" +#include "util.h" + +static struct reconnect *reconnect; +static int now; + +static const struct command commands[]; + +static void diff_stats(const struct reconnect_stats *old, + const struct reconnect_stats *new); + +int +main(void) +{ + struct reconnect_stats prev; + unsigned int old_max_tries; + int old_time; + char line[128]; + + now = 1000; + reconnect = reconnect_create(now); + reconnect_set_name(reconnect, "remote"); + reconnect_get_stats(reconnect, now, &prev); + printf("### t=%d ###\n", now); + old_time = now; + old_max_tries = reconnect_get_max_tries(reconnect); + while (fgets(line, sizeof line, stdin)) { + struct reconnect_stats cur; + struct svec args; + + fputs(line, stdout); + if (line[0] == '#') { + continue; + } + + svec_init(&args); + svec_parse_words(&args, line); + svec_terminate(&args); + if (!svec_is_empty(&args)) { + run_command(args.n, args.names, commands); + } + svec_destroy(&args); + + if (old_time != now) { + printf("\n### t=%d ###\n", now); + old_time = now; + } + + reconnect_get_stats(reconnect, now, &cur); + diff_stats(&prev, &cur); + prev = cur; + if (reconnect_get_max_tries(reconnect) != old_max_tries) { + old_max_tries = reconnect_get_max_tries(reconnect); + printf(" %u tries left\n", old_max_tries); + } + } + + return 0; +} + +static void - do_enable(int argc UNUSED, char *argv[] UNUSED) ++do_enable(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) +{ + reconnect_enable(reconnect, now); +} + +static void - do_disable(int argc UNUSED, char *argv[] UNUSED) ++do_disable(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) +{ + reconnect_disable(reconnect, now); +} + +static void - do_force_reconnect(int argc UNUSED, char *argv[] UNUSED) ++do_force_reconnect(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) +{ + reconnect_force_reconnect(reconnect, now); +} + +static int +error_from_string(const char *s) +{ + if (!s) { + return 0; + } else if (!strcmp(s, "ECONNREFUSED")) { + return ECONNREFUSED; + } else if (!strcmp(s, "EOF")) { + return EOF; + } else { + ovs_fatal(0, "unknown error '%s'", s); + } +} + +static void - do_disconnected(int argc UNUSED, char *argv[]) ++do_disconnected(int argc OVS_UNUSED, char *argv[]) +{ + reconnect_disconnected(reconnect, now, error_from_string(argv[1])); +} + +static void - do_connecting(int argc UNUSED, char *argv[] UNUSED) ++do_connecting(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) +{ + reconnect_connecting(reconnect, now); +} + +static void - do_connect_failed(int argc UNUSED, char *argv[]) ++do_connect_failed(int argc OVS_UNUSED, char *argv[]) +{ + reconnect_connect_failed(reconnect, now, error_from_string(argv[1])); +} + +static void - do_connected(int argc UNUSED, char *argv[] UNUSED) ++do_connected(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) +{ + reconnect_connected(reconnect, now); +} + +static void - do_received(int argc UNUSED, char *argv[] UNUSED) ++do_received(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) +{ + reconnect_received(reconnect, now); +} + +static void +do_run(int argc, char *argv[]) +{ + enum reconnect_action action; + + if (argc > 1) { + now += atoi(argv[1]); + } + + action = reconnect_run(reconnect, now); + switch (action) { + default: + if (action != 0) { + NOT_REACHED(); + } + break; + + case RECONNECT_CONNECT: + printf(" should connect\n"); + break; + + case RECONNECT_DISCONNECT: + printf(" should disconnect\n"); + break; + + case RECONNECT_PROBE: + printf(" should send probe\n"); + break; + } +} + +static void - do_advance(int argc UNUSED, char *argv[]) ++do_advance(int argc OVS_UNUSED, char *argv[]) +{ + now += atoi(argv[1]); +} + +static void - do_timeout(int argc UNUSED, char *argv[] UNUSED) ++do_timeout(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) +{ + int timeout = reconnect_timeout(reconnect, now); + if (timeout >= 0) { + printf(" advance %d ms\n", timeout); + now += timeout; + } else { + printf(" no timeout\n"); + } +} + +static void - do_set_max_tries(int argc UNUSED, char *argv[]) ++do_set_max_tries(int argc OVS_UNUSED, char *argv[]) +{ + reconnect_set_max_tries(reconnect, atoi(argv[1])); +} + +static void +diff_stats(const struct reconnect_stats *old, + const struct reconnect_stats *new) +{ + if (old->state != new->state + || old->state_elapsed != new->state_elapsed + || old->backoff != new->backoff) { + printf(" in %s for %u ms (%d ms backoff)\n", + new->state, new->state_elapsed, new->backoff); + } + if (old->creation_time != new->creation_time + || old->last_received != new->last_received + || old->last_connected != new->last_connected) { + printf(" created %lld, last received %lld, last connected %lld\n", + new->creation_time, new->last_received, new->last_connected); + } + if (old->n_successful_connections != new->n_successful_connections + || old->n_attempted_connections != new->n_attempted_connections + || old->seqno != new->seqno) { + printf(" %u successful connections out of %u attempts, seqno %u\n", + new->n_successful_connections, new->n_attempted_connections, + new->seqno); + } + if (old->is_connected != new->is_connected + || old->current_connection_duration != new->current_connection_duration + || old->total_connected_duration != new->total_connected_duration) { + printf(" %sconnected (%u ms), total %u ms connected\n", + new->is_connected ? "" : "not ", + new->current_connection_duration, + new->total_connected_duration); + } +} + +static const struct command commands[] = { + { "enable", 0, 0, do_enable }, + { "disable", 0, 0, do_disable }, + { "force-reconnect", 0, 0, do_force_reconnect }, + { "disconnected", 0, 1, do_disconnected }, + { "connecting", 0, 0, do_connecting }, + { "connect-failed", 0, 1, do_connect_failed }, + { "connected", 0, 0, do_connected }, + { "received", 0, 0, do_received }, + { "run", 0, 1, do_run }, + { "advance", 1, 1, do_advance }, + { "timeout", 0, 0, do_timeout }, + { "set-max-tries", 1, 1, do_set_max_tries }, + { NULL, 0, 0, NULL }, +}; + diff --combined tests/test-vconn.c index f12e378fc,ff4d9ece3..a394f6cfb --- a/tests/test-vconn.c +++ b/tests/test-vconn.c @@@ -21,11 -21,8 +21,11 @@@ #include #include #include +#include "command-line.h" #include "poll-loop.h" #include "socket-util.h" +#include "stream.h" +#include "stream-ssl.h" #include "timeval.h" #include "util.h" #include "vlog.h" @@@ -37,90 -34,94 +37,90 @@@ struct fake_pvconn const char *type; char *pvconn_name; char *vconn_name; - int fd; + struct pstream *pstream; }; +static void +check(int a, int b, const char *as, const char *file, int line) +{ + if (a != b) { + ovs_fatal(0, "%s:%d: %s is %d but should be %d", file, line, as, a, b); + } +} + + +#define CHECK(A, B) check(A, B, #A, __FILE__, __LINE__) + +static void +check_errno(int a, int b, const char *as, const char *file, int line) +{ + if (a != b) { + ovs_fatal(0, "%s:%d: %s is %d (%s) but should be %d (%s)", + file, line, as, a, strerror(abs(a)), b, strerror(abs(b))); + } +} + +#define CHECK_ERRNO(A, B) check_errno(A, B, #A, __FILE__, __LINE__) + static void fpv_create(const char *type, struct fake_pvconn *fpv) { +#ifdef HAVE_OPENSSL + if (!strcmp(type, "ssl")) { + stream_ssl_set_private_key_file("testpki-privkey.pem"); + stream_ssl_set_certificate_file("testpki-cert.pem"); + stream_ssl_set_ca_cert_file("testpki-cacert.pem", false); + } +#endif + fpv->type = type; if (!strcmp(type, "unix")) { static int unix_count = 0; char *bind_path; - int fd; bind_path = xasprintf("fake-pvconn.%d", unix_count++); - fd = make_unix_socket(SOCK_STREAM, false, false, bind_path, NULL); - if (fd < 0) { - ovs_fatal(-fd, "%s: could not bind to Unix domain socket", - bind_path); - } - fpv->pvconn_name = xasprintf("punix:%s", bind_path); fpv->vconn_name = xasprintf("unix:%s", bind_path); - fpv->fd = fd; + CHECK_ERRNO(pstream_open(fpv->pvconn_name, &fpv->pstream), 0); free(bind_path); - } else if (!strcmp(type, "tcp")) { - struct sockaddr_in sin; - socklen_t sin_len; - int fd; - - /* Create TCP socket. */ - fd = socket(PF_INET, SOCK_STREAM, 0); - if (fd < 0) { - ovs_fatal(errno, "failed to create TCP socket"); - } + } else if (!strcmp(type, "tcp") || !strcmp(type, "ssl")) { + char *s, *port, *save_ptr = NULL; + char *open_name; - /* Bind TCP socket to localhost on any available port. */ - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - sin.sin_port = htons(0); - if (bind(fd, (struct sockaddr *) &sin, sizeof sin) < 0) { - ovs_fatal(errno, "failed to bind TCP socket"); - } + open_name = xasprintf("p%s:0:127.0.0.1", type); + CHECK_ERRNO(pstream_open(open_name, &fpv->pstream), 0); - /* Retrieve socket's port number. */ - sin_len = sizeof sin; - if (getsockname(fd, (struct sockaddr *)&sin, &sin_len) < 0) { - ovs_fatal(errno, "failed to read TCP socket name"); - } - if (sin_len != sizeof sin || sin.sin_family != AF_INET) { - ovs_fatal(errno, "bad TCP socket name"); - } + /* Extract bound port number from pstream name. */ + s = xstrdup(pstream_get_name(fpv->pstream)); + strtok_r(s, ":", &save_ptr); + port = strtok_r(NULL, ":", &save_ptr); /* Save info. */ - fpv->pvconn_name = xasprintf("ptcp:%"PRIu16":127.0.0.1", - ntohs(sin.sin_port)); - fpv->vconn_name = xasprintf("tcp:127.0.0.1:%"PRIu16, - ntohs(sin.sin_port)); - fpv->fd = fd; + fpv->pvconn_name = xstrdup(pstream_get_name(fpv->pstream)); + fpv->vconn_name = xasprintf("%s:127.0.0.1:%s", type, port); + + free(open_name); + free(s); } else { abort(); } - - /* Listen. */ - if (listen(fpv->fd, 0) < 0) { - ovs_fatal(errno, "%s: listen failed", fpv->vconn_name); - } } -static int +static struct stream * fpv_accept(struct fake_pvconn *fpv) { - int fd; + struct stream *stream; - fd = accept(fpv->fd, NULL, NULL); - if (fd < 0) { - ovs_fatal(errno, "%s: accept failed", fpv->pvconn_name); - } - return fd; + CHECK_ERRNO(pstream_accept_block(fpv->pstream, &stream), 0); + + return stream; } static void fpv_close(struct fake_pvconn *fpv) { - if (fpv->fd >= 0) { - if (close(fpv->fd) < 0) { - ovs_fatal(errno, "failed to close %s fake pvconn", fpv->type); - } - fpv->fd = -1; - } + pstream_close(fpv->pstream); + fpv->pstream = NULL; } static void @@@ -134,20 -135,15 +134,20 @@@ fpv_destroy(struct fake_pvconn *fpv /* Connects to a fake_pvconn with vconn_open(), then closes the listener and * verifies that vconn_connect() reports 'expected_error'. */ static void - test_refuse_connection(int argc UNUSED, char *argv[]) -test_refuse_connection(const char *type, int expected_error) ++test_refuse_connection(int argc OVS_UNUSED, char *argv[]) { + const char *type = argv[1]; + int expected_error; struct fake_pvconn fpv; struct vconn *vconn; + expected_error = !strcmp(type, "unix") ? EPIPE : ECONNRESET; + fpv_create(type, &fpv); - assert(!vconn_open(fpv.vconn_name, OFP_VERSION, &vconn)); + CHECK_ERRNO(vconn_open(fpv.vconn_name, OFP_VERSION, &vconn), 0); fpv_close(&fpv); - assert(vconn_connect(vconn) == expected_error); + vconn_run(vconn); + CHECK_ERRNO(vconn_connect(vconn), expected_error); vconn_close(vconn); fpv_destroy(&fpv); } @@@ -156,23 -152,16 +156,23 @@@ * closes it immediately, and verifies that vconn_connect() reports * 'expected_error'. */ static void - test_accept_then_close(int argc UNUSED, char *argv[]) -test_accept_then_close(const char *type, int expected_error) ++test_accept_then_close(int argc OVS_UNUSED, char *argv[]) { + const char *type = argv[1]; + int expected_error; struct fake_pvconn fpv; struct vconn *vconn; + expected_error = (!strcmp(type, "unix") ? EPIPE + : !strcmp(type, "tcp") ? ECONNRESET + : EPROTO); + fpv_create(type, &fpv); - assert(!vconn_open(fpv.vconn_name, OFP_VERSION, &vconn)); - close(fpv_accept(&fpv)); + CHECK_ERRNO(vconn_open(fpv.vconn_name, OFP_VERSION, &vconn), 0); + vconn_run(vconn); + stream_close(fpv_accept(&fpv)); fpv_close(&fpv); - assert(vconn_connect(vconn) == expected_error); + CHECK_ERRNO(vconn_connect(vconn), expected_error); vconn_close(vconn); fpv_destroy(&fpv); } @@@ -181,41 -170,38 +181,41 @@@ * reads the hello message from it, then closes the connection and verifies * that vconn_connect() reports 'expected_error'. */ static void - test_read_hello(int argc UNUSED, char *argv[]) -test_read_hello(const char *type, int expected_error) ++test_read_hello(int argc OVS_UNUSED, char *argv[]) { + const char *type = argv[1]; struct fake_pvconn fpv; struct vconn *vconn; - int fd; + struct stream *stream; fpv_create(type, &fpv); - assert(!vconn_open(fpv.vconn_name, OFP_VERSION, &vconn)); - fd = fpv_accept(&fpv); + CHECK_ERRNO(vconn_open(fpv.vconn_name, OFP_VERSION, &vconn), 0); + vconn_run(vconn); + stream = fpv_accept(&fpv); fpv_destroy(&fpv); - assert(!set_nonblocking(fd)); for (;;) { struct ofp_header hello; int retval; - retval = read(fd, &hello, sizeof hello); + retval = stream_recv(stream, &hello, sizeof hello); if (retval == sizeof hello) { - assert(hello.version == OFP_VERSION); - assert(hello.type == OFPT_HELLO); - assert(hello.length == htons(sizeof hello)); + CHECK(hello.version, OFP_VERSION); + CHECK(hello.type, OFPT_HELLO); + CHECK(hello.length, htons(sizeof hello)); break; } else { - assert(errno == EAGAIN); + CHECK_ERRNO(retval, -EAGAIN); } - assert(vconn_connect(vconn) == EAGAIN); + vconn_run(vconn); + CHECK_ERRNO(vconn_connect(vconn), EAGAIN); + vconn_run_wait(vconn); vconn_connect_wait(vconn); - poll_fd_wait(fd, POLLIN); + stream_recv_wait(stream); poll_block(); } - close(fd); - assert(vconn_connect(vconn) == expected_error); + stream_close(stream); + CHECK_ERRNO(vconn_connect(vconn), ECONNRESET); vconn_close(vconn); } @@@ -231,62 -217,44 +231,62 @@@ test_send_hello(const char *type, cons struct vconn *vconn; bool read_hello, connected; struct ofpbuf *msg; - int fd; + struct stream *stream; + size_t n_sent; fpv_create(type, &fpv); - assert(!vconn_open(fpv.vconn_name, OFP_VERSION, &vconn)); - fd = fpv_accept(&fpv); + CHECK_ERRNO(vconn_open(fpv.vconn_name, OFP_VERSION, &vconn), 0); + vconn_run(vconn); + stream = fpv_accept(&fpv); fpv_destroy(&fpv); - write(fd, out, out_size); - - assert(!set_nonblocking(fd)); + n_sent = 0; + while (n_sent < out_size) { + int retval; + + retval = stream_send(stream, (char *) out + n_sent, out_size - n_sent); + if (retval > 0) { + n_sent += retval; + } else if (retval == -EAGAIN) { + stream_run(stream); + vconn_run(vconn); + stream_recv_wait(stream); + vconn_connect_wait(vconn); + vconn_run_wait(vconn); + poll_block(); + } else { + ovs_fatal(0, "stream_send returned unexpected value %d", retval); + } + } read_hello = connected = false; for (;;) { if (!read_hello) { struct ofp_header hello; - int retval = read(fd, &hello, sizeof hello); + int retval = stream_recv(stream, &hello, sizeof hello); if (retval == sizeof hello) { - assert(hello.version == OFP_VERSION); - assert(hello.type == OFPT_HELLO); - assert(hello.length == htons(sizeof hello)); + CHECK(hello.version, OFP_VERSION); + CHECK(hello.type, OFPT_HELLO); + CHECK(hello.length, htons(sizeof hello)); read_hello = true; } else { - assert(errno == EAGAIN); + CHECK_ERRNO(retval, -EAGAIN); } } + vconn_run(vconn); if (!connected) { int error = vconn_connect(vconn); if (error == expect_connect_error) { if (!error) { connected = true; } else { - close(fd); + stream_close(stream); vconn_close(vconn); return; } } else { - assert(error == EAGAIN); + CHECK_ERRNO(error, EAGAIN); } } @@@ -294,25 -262,23 +294,25 @@@ break; } + vconn_run_wait(vconn); if (!connected) { vconn_connect_wait(vconn); } if (!read_hello) { - poll_fd_wait(fd, POLLIN); + stream_recv_wait(stream); } poll_block(); } - close(fd); - assert(vconn_recv(vconn, &msg) == EOF); + stream_close(stream); + CHECK_ERRNO(vconn_recv(vconn, &msg), EOF); vconn_close(vconn); } /* Try connecting and sending a normal hello, which should succeed. */ static void - test_send_plain_hello(int argc UNUSED, char *argv[]) -test_send_plain_hello(const char *type) ++test_send_plain_hello(int argc OVS_UNUSED, char *argv[]) { + const char *type = argv[1]; struct ofp_header hello; hello.version = OFP_VERSION; @@@ -326,9 -292,8 +326,9 @@@ * the specification says that implementations must accept and ignore extra * data). */ static void - test_send_long_hello(int argc UNUSED, char *argv[]) -test_send_long_hello(const char *type) ++test_send_long_hello(int argc OVS_UNUSED, char *argv[]) { + const char *type = argv[1]; struct ofp_header hello; char buffer[sizeof hello * 2]; @@@ -344,9 -309,8 +344,9 @@@ /* Try connecting and sending an echo request instead of a hello, which should * fail with EPROTO. */ static void - test_send_echo_hello(int argc UNUSED, char *argv[]) -test_send_echo_hello(const char *type) ++test_send_echo_hello(int argc OVS_UNUSED, char *argv[]) { + const char *type = argv[1]; struct ofp_header echo; echo.version = OFP_VERSION; @@@ -359,9 -323,8 +359,9 @@@ /* Try connecting and sending a hello packet that has its length field as 0, * which should fail with EPROTO. */ static void - test_send_short_hello(int argc UNUSED, char *argv[]) -test_send_short_hello(const char *type) ++test_send_short_hello(int argc OVS_UNUSED, char *argv[]) { + const char *type = argv[1]; struct ofp_header hello; memset(&hello, 0, sizeof hello); @@@ -371,9 -334,8 +371,9 @@@ /* Try connecting and sending a hello packet that has a bad version, which * should fail with EPROTO. */ static void - test_send_invalid_version_hello(int argc UNUSED, char *argv[]) -test_send_invalid_version_hello(const char *type) ++test_send_invalid_version_hello(int argc OVS_UNUSED, char *argv[]) { + const char *type = argv[1]; struct ofp_header hello; hello.version = OFP_VERSION - 1; @@@ -383,31 -345,40 +383,31 @@@ test_send_hello(type, &hello, sizeof hello, EPROTO); } +static const struct command commands[] = { + {"refuse-connection", 1, 1, test_refuse_connection}, + {"accept-then-close", 1, 1, test_accept_then_close}, + {"read-hello", 1, 1, test_read_hello}, + {"send-plain-hello", 1, 1, test_send_plain_hello}, + {"send-long-hello", 1, 1, test_send_long_hello}, + {"send-echo-hello", 1, 1, test_send_echo_hello}, + {"send-short-hello", 1, 1, test_send_short_hello}, + {"send-invalid-version-hello", 1, 1, test_send_invalid_version_hello}, + {NULL, 0, 0, NULL}, +}; + int - main(int argc, char *argv[]) + main(int argc OVS_UNUSED, char *argv[]) { set_program_name(argv[0]); time_init(); vlog_init(); - signal(SIGPIPE, SIG_IGN); vlog_set_levels(VLM_ANY_MODULE, VLF_ANY_FACILITY, VLL_EMER); + vlog_set_levels(VLM_ANY_MODULE, VLF_CONSOLE, VLL_DBG); + signal(SIGPIPE, SIG_IGN); time_alarm(10); - test_refuse_connection("unix", EPIPE); - test_refuse_connection("tcp", ECONNRESET); - - test_accept_then_close("unix", EPIPE); - test_accept_then_close("tcp", ECONNRESET); - - test_read_hello("unix", ECONNRESET); - test_read_hello("tcp", ECONNRESET); - - test_send_plain_hello("unix"); - test_send_plain_hello("tcp"); - - test_send_long_hello("unix"); - test_send_long_hello("tcp"); - - test_send_echo_hello("unix"); - test_send_echo_hello("tcp"); - - test_send_short_hello("unix"); - test_send_short_hello("tcp"); - - test_send_invalid_version_hello("unix"); - test_send_invalid_version_hello("tcp"); + run_command(argc - 1, argv + 1, commands); return 0; } diff --combined utilities/ovs-discover.c index 0b7d92d69,b304878e1..3830e0739 --- a/utilities/ovs-discover.c +++ b/utilities/ovs-discover.c @@@ -60,7 -60,7 +60,7 @@@ static bool exit_without_bind static bool exit_after_bind; static bool iface_init(struct iface *, const char *netdev_name); - static void release_ifaces(void *aux UNUSED); + static void release_ifaces(void *aux OVS_UNUSED); static void parse_options(int argc, char *argv[]); static void usage(void) NO_RETURN; @@@ -75,7 -75,6 +75,7 @@@ main(int argc, char *argv[] int retval; int i; + proctitle_init(argc, argv); set_program_name(argv[0]); time_init(); vlog_init(); @@@ -103,7 -102,7 +103,7 @@@ struct iface *iface = &ifaces[i]; dhclient_init(iface->dhcp, 0); } - fatal_signal_add_hook(release_ifaces, NULL, true); + fatal_signal_add_hook(release_ifaces, NULL, NULL, true); retval = regcomp(&accept_controller_regex, accept_controller_re, REG_NOSUB | REG_EXTENDED); @@@ -116,13 -115,14 +116,13 @@@ retval = unixctl_server_create(NULL, &unixctl); if (retval) { - ovs_fatal(retval, "Could not listen for unixctl connections"); + exit(EXIT_FAILURE); } die_if_already_running(); signal(SIGPIPE, SIG_IGN); for (;;) { - fatal_signal_block(); for (i = 0; i < n_ifaces; i++) { struct iface *iface = &ifaces[i]; dhclient_run(iface->dhcp); @@@ -195,6 -195,7 +195,6 @@@ dhclient_wait(iface->dhcp); } unixctl_server_wait(unixctl); - fatal_signal_unblock(); poll_block(); } @@@ -214,7 -215,7 +214,7 @@@ iface_init(struct iface *iface, const c * persists past program termination. */ struct netdev *netdev; - retval = netdev_open(iface->name, NETDEV_ETH_TYPE_NONE, &netdev); + retval = netdev_open_default(iface->name, &netdev); if (retval) { ovs_error(retval, "Could not open %s device", iface->name); return false; @@@ -238,7 -239,7 +238,7 @@@ } static void - release_ifaces(void *aux UNUSED) + release_ifaces(void *aux OVS_UNUSED) { int i; @@@ -252,13 -253,13 +252,13 @@@ } static void - modify_dhcp_request(struct dhcp_msg *msg, void *aux UNUSED) + modify_dhcp_request(struct dhcp_msg *msg, void *aux OVS_UNUSED) { dhcp_msg_put_string(msg, DHCP_CODE_VENDOR_CLASS, "OpenFlow"); } static bool - validate_dhcp_offer(const struct dhcp_msg *msg, void *aux UNUSED) + validate_dhcp_offer(const struct dhcp_msg *msg, void *aux OVS_UNUSED) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(60, 60); char *vconn_name; diff --combined utilities/ovs-dpctl.c index ebcf2e23d,133dd401e..eb78a573b --- a/utilities/ovs-dpctl.c +++ b/utilities/ovs-dpctl.c @@@ -43,20 -43,56 +43,20 @@@ #include "vlog.h" #define THIS_MODULE VLM_dpctl -struct command { - const char *name; - int min_args; - int max_args; - void (*handler)(int argc, char *argv[]); -}; - -static struct command all_commands[]; +static const struct command all_commands[]; static void usage(void) NO_RETURN; static void parse_options(int argc, char *argv[]); -int main(int argc, char *argv[]) +int +main(int argc, char *argv[]) { - struct command *p; - set_program_name(argv[0]); time_init(); vlog_init(); parse_options(argc, argv); signal(SIGPIPE, SIG_IGN); - - argc -= optind; - argv += optind; - if (argc < 1) - ovs_fatal(0, "missing command name; use --help for help"); - - for (p = all_commands; p->name != NULL; p++) { - if (!strcmp(p->name, argv[0])) { - int n_arg = argc - 1; - if (n_arg < p->min_args) - ovs_fatal(0, "'%s' command requires at least %d arguments", - p->name, p->min_args); - else if (n_arg > p->max_args) - ovs_fatal(0, "'%s' command takes at most %d arguments", - p->name, p->max_args); - else { - p->handler(argc, argv); - if (ferror(stdout)) { - ovs_fatal(0, "write to stdout failed"); - } - if (ferror(stderr)) { - ovs_fatal(0, "write to stderr failed"); - } - exit(0); - } - } - } - ovs_fatal(0, "unknown command '%s'; use --help for help", argv[0]); - + run_command(argc - optind, argv + optind, all_commands); return 0; } @@@ -168,7 -204,7 +168,7 @@@ static int if_up(const char *netdev_nam struct netdev *netdev; int retval; - retval = netdev_open(netdev_name, NETDEV_ETH_TYPE_NONE, &netdev); + retval = netdev_open_default(netdev_name, &netdev); if (!retval) { retval = netdev_turn_flags_on(netdev, NETDEV_UP, true); netdev_close(netdev); @@@ -176,30 -212,11 +176,30 @@@ return retval; } +static int +parsed_dpif_open(const char *arg_, bool create, struct dpif **dpifp) +{ + int result; + char *name, *type; + + dp_parse_name(arg_, &name, &type); + + if (create) { + result = dpif_create(name, type, dpifp); + } else { + result = dpif_open(name, type, dpifp); + } + + free(name); + free(type); + return result; +} + static void - do_add_dp(int argc UNUSED, char *argv[]) + do_add_dp(int argc OVS_UNUSED, char *argv[]) { struct dpif *dpif; - run(dpif_create(argv[1], &dpif), "add_dp"); + run(parsed_dpif_open(argv[1], true, &dpif), "add_dp"); dpif_close(dpif); if (argc > 2) { do_add_if(argc, argv); @@@ -207,10 -224,10 +207,10 @@@ } static void - do_del_dp(int argc UNUSED, char *argv[]) + do_del_dp(int argc OVS_UNUSED, char *argv[]) { struct dpif *dpif; - run(dpif_open(argv[1], &dpif), "opening datapath"); + run(parsed_dpif_open(argv[1], false, &dpif), "opening datapath"); run(dpif_delete(dpif), "del_dp"); dpif_close(dpif); } @@@ -231,13 -248,13 +231,13 @@@ query_ports(struct dpif *dpif, struct o } static void - do_add_if(int argc UNUSED, char *argv[]) + do_add_if(int argc OVS_UNUSED, char *argv[]) { bool failure = false; struct dpif *dpif; int i; - run(dpif_open(argv[1], &dpif), "opening datapath"); + run(parsed_dpif_open(argv[1], false, &dpif), "opening datapath"); for (i = 2; i < argc; i++) { char *save_ptr = NULL; char *devname, *suboptions; @@@ -309,13 -326,13 +309,13 @@@ get_port_number(struct dpif *dpif, cons } static void - do_del_if(int argc UNUSED, char *argv[]) + do_del_if(int argc OVS_UNUSED, char *argv[]) { bool failure = false; struct dpif *dpif; int i; - run(dpif_open(argv[1], &dpif), "opening datapath"); + run(parsed_dpif_open(argv[1], false, &dpif), "opening datapath"); for (i = 2; i < argc; i++) { const char *name = argv[i]; uint16_t port; @@@ -387,7 -404,7 +387,7 @@@ do_show(int argc, char *argv[] struct dpif *dpif; int error; - error = dpif_open(name, &dpif); + error = parsed_dpif_open(name, false, &dpif); if (!error) { show_dpif(dpif); } else { @@@ -403,7 -420,7 +403,7 @@@ int error; sprintf(name, "dp%u", i); - error = dpif_open(name, &dpif); + error = parsed_dpif_open(name, false, &dpif); if (!error) { show_dpif(dpif); } else if (error != ENODEV) { @@@ -418,43 -435,31 +418,43 @@@ } static void - do_dump_dps(int argc UNUSED, char *argv[] UNUSED) + do_dump_dps(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) { - struct svec all_dps; + struct svec dpif_names, dpif_types; unsigned int i; - int error; + int error = 0; + + svec_init(&dpif_names); + svec_init(&dpif_types); + dp_enumerate_types(&dpif_types); + + for (i = 0; i < dpif_types.n; i++) { + unsigned int j; + int retval; - svec_init(&all_dps); - error = dp_enumerate(&all_dps); + retval = dp_enumerate_names(dpif_types.names[i], &dpif_names); + if (retval) { + error = retval; + } - for (i = 0; i < all_dps.n; i++) { - struct dpif *dpif; - if (!dpif_open(all_dps.names[i], &dpif)) { - printf("%s\n", dpif_name(dpif)); - dpif_close(dpif); + for (j = 0; j < dpif_names.n; j++) { + struct dpif *dpif; + if (!dpif_open(dpif_names.names[j], dpif_types.names[i], &dpif)) { + printf("%s\n", dpif_name(dpif)); + dpif_close(dpif); + } } } - svec_destroy(&all_dps); + svec_destroy(&dpif_names); + svec_destroy(&dpif_types); if (error) { exit(EXIT_FAILURE); } } static void - do_dump_flows(int argc UNUSED, char *argv[]) + do_dump_flows(int argc OVS_UNUSED, char *argv[]) { struct odp_flow *flows; struct dpif *dpif; @@@ -462,7 -467,7 +462,7 @@@ struct ds ds; size_t i; - run(dpif_open(argv[1], &dpif), "opening datapath"); + run(parsed_dpif_open(argv[1], false, &dpif), "opening datapath"); run(dpif_flow_list_all(dpif, &flows, &n_flows), "listing all flows"); ds_init(&ds); @@@ -484,23 -489,23 +484,23 @@@ } static void - do_del_flows(int argc UNUSED, char *argv[]) + do_del_flows(int argc OVS_UNUSED, char *argv[]) { struct dpif *dpif; - run(dpif_open(argv[1], &dpif), "opening datapath"); + run(parsed_dpif_open(argv[1], false, &dpif), "opening datapath"); run(dpif_flow_flush(dpif), "deleting all flows"); dpif_close(dpif); } static void - do_dump_groups(int argc UNUSED, char *argv[]) + do_dump_groups(int argc OVS_UNUSED, char *argv[]) { struct odp_stats stats; struct dpif *dpif; unsigned int i; - run(dpif_open(argv[1], &dpif), "opening datapath"); + run(parsed_dpif_open(argv[1], false, &dpif), "opening datapath"); run(dpif_get_dp_stats(dpif, &stats), "get datapath stats"); for (i = 0; i < stats.max_groups; i++) { uint16_t *ports; @@@ -521,12 -526,12 +521,12 @@@ } static void - do_help(int argc UNUSED, char *argv[] UNUSED) + do_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) { usage(); } -static struct command all_commands[] = { +static const struct command all_commands[] = { { "add-dp", 1, INT_MAX, do_add_dp }, { "del-dp", 1, 1, do_del_dp }, { "add-if", 2, INT_MAX, do_add_if }, diff --combined utilities/ovs-ofctl.c index 7f24309b2,c36a3bc81..2a171533e --- a/utilities/ovs-ofctl.c +++ b/utilities/ovs-ofctl.c @@@ -1,5 -1,5 +1,5 @@@ /* - * Copyright (c) 2008, 2009 Nicira Networks. + * Copyright (c) 2008, 2009, 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. @@@ -44,9 -44,9 +44,9 @@@ #include "packets.h" #include "random.h" #include "socket-util.h" +#include "stream-ssl.h" #include "timeval.h" #include "util.h" -#include "vconn-ssl.h" #include "vconn.h" #include "vlog.h" @@@ -59,28 -59,68 +59,28 @@@ #define MOD_PORT_CMD_FLOOD "flood" #define MOD_PORT_CMD_NOFLOOD "noflood" +/* Use strict matching for flow mod commands? */ +static bool strict; -/* Settings that may be configured by the user. */ -struct settings { - bool strict; /* Use strict matching for flow mod commands */ -}; - -struct command { - const char *name; - int min_args; - int max_args; - void (*handler)(const struct settings *, int argc, char *argv[]); -}; - -static struct command all_commands[]; +static const struct command all_commands[]; static void usage(void) NO_RETURN; -static void parse_options(int argc, char *argv[], struct settings *); +static void parse_options(int argc, char *argv[]); -int main(int argc, char *argv[]) +int +main(int argc, char *argv[]) { - struct settings s; - struct command *p; - set_program_name(argv[0]); time_init(); vlog_init(); - parse_options(argc, argv, &s); + parse_options(argc, argv); signal(SIGPIPE, SIG_IGN); - - argc -= optind; - argv += optind; - if (argc < 1) - ovs_fatal(0, "missing command name; use --help for help"); - - for (p = all_commands; p->name != NULL; p++) { - if (!strcmp(p->name, argv[0])) { - int n_arg = argc - 1; - if (n_arg < p->min_args) - ovs_fatal(0, "'%s' command requires at least %d arguments", - p->name, p->min_args); - else if (n_arg > p->max_args) - ovs_fatal(0, "'%s' command takes at most %d arguments", - p->name, p->max_args); - else { - p->handler(&s, argc, argv); - if (ferror(stdout)) { - ovs_fatal(0, "write to stdout failed"); - } - if (ferror(stderr)) { - ovs_fatal(0, "write to stderr failed"); - } - exit(0); - } - } - } - ovs_fatal(0, "unknown command '%s'; use --help for help", argv[0]); - + run_command(argc - optind, argv + optind, all_commands); return 0; } static void -parse_options(int argc, char *argv[], struct settings *s) +parse_options(int argc, char *argv[]) { enum { OPT_STRICT = UCHAR_MAX + 1, @@@ -92,11 -132,14 +92,11 @@@ {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'}, VLOG_LONG_OPTIONS, - VCONN_SSL_LONG_OPTIONS + STREAM_SSL_LONG_OPTIONS {0, 0, 0, 0}, }; char *short_options = long_options_to_short_options(long_options); - /* Set defaults that we can figure out before parsing options. */ - s->strict = false; - for (;;) { unsigned long int timeout; int c; @@@ -125,11 -168,11 +125,11 @@@ exit(EXIT_SUCCESS); case OPT_STRICT: - s->strict = true; + strict = true; break; VLOG_OPTION_HANDLERS - VCONN_SSL_OPTION_HANDLERS + STREAM_SSL_OPTION_HANDLERS case '?': exit(EXIT_FAILURE); @@@ -162,6 -205,7 +162,6 @@@ usage(void " mod-flows SWITCH FLOW modify actions of matching FLOWs\n" " del-flows SWITCH [FLOW] delete matching FLOWs\n" " monitor SWITCH MISSLEN EXP print packets received from SWITCH\n" - " execute SWITCH CMD [ARG...] execute CMD with ARGS on SWITCH\n" "\nFor OpenFlow switches and controllers:\n" " probe VCONN probe whether VCONN is up\n" " ping VCONN [N] latency of N-byte echos\n" @@@ -202,36 -246,25 +202,36 @@@ static void run(int retval, const char /* Generic commands. */ +static void +open_vconn_socket(const char *name, struct vconn **vconnp) +{ + char *vconn_name = xasprintf("unix:%s", name); + VLOG_INFO("connecting to %s", vconn_name); + run(vconn_open_block(vconn_name, OFP_VERSION, vconnp), + "connecting to %s", vconn_name); + free(vconn_name); +} + static void open_vconn(const char *name, struct vconn **vconnp) { struct dpif *dpif; struct stat s; + char *bridge_path, *datapath_name, *datapath_type; + + bridge_path = xasprintf("%s/%s.mgmt", ovs_rundir, name); + dp_parse_name(name, &datapath_name, &datapath_type); if (strstr(name, ":")) { run(vconn_open_block(name, OFP_VERSION, vconnp), "connecting to %s", name); } else if (!stat(name, &s) && S_ISSOCK(s.st_mode)) { - char *vconn_name = xasprintf("unix:%s", name); - VLOG_INFO("connecting to %s", vconn_name); - run(vconn_open_block(vconn_name, OFP_VERSION, vconnp), - "connecting to %s", vconn_name); - free(vconn_name); - } else if (!dpif_open(name, &dpif)) { + open_vconn_socket(name, vconnp); + } else if (!stat(bridge_path, &s) && S_ISSOCK(s.st_mode)) { + open_vconn_socket(bridge_path, vconnp); + } else if (!dpif_open(datapath_name, datapath_type, &dpif)) { char dpif_name[IF_NAMESIZE + 1]; char *socket_name; - char *vconn_name; run(dpif_port_get_name(dpif, ODPP_LOCAL, dpif_name, sizeof dpif_name), "obtaining name of %s", dpif_name); @@@ -249,15 -282,15 +249,15 @@@ name, socket_name); } - vconn_name = xasprintf("unix:%s", socket_name); - VLOG_INFO("connecting to %s", vconn_name); - run(vconn_open_block(vconn_name, OFP_VERSION, vconnp), - "connecting to %s", vconn_name); + open_vconn_socket(socket_name, vconnp); free(socket_name); - free(vconn_name); } else { ovs_fatal(0, "%s is not a valid connection method", name); } + + free(datapath_name); + free(datapath_type); + free(bridge_path); } static void * @@@ -339,14 -372,14 +339,14 @@@ dump_trivial_stats_transaction(const ch } static void - do_show(int argc UNUSED, char *argv[]) -do_show(const struct settings *s OVS_UNUSED, int argc OVS_UNUSED, char *argv[]) ++do_show(int argc OVS_UNUSED, char *argv[]) { dump_trivial_transaction(argv[1], OFPT_FEATURES_REQUEST); dump_trivial_transaction(argv[1], OFPT_GET_CONFIG_REQUEST); } static void -do_status(const struct settings *s OVS_UNUSED, int argc, char *argv[]) +do_status(int argc, char *argv[]) { struct nicira_header *request, *reply; struct vconn *vconn; @@@ -378,13 -411,15 +378,13 @@@ } static void - do_dump_desc(int argc UNUSED, char *argv[]) -do_dump_desc(const struct settings *s OVS_UNUSED, - int argc OVS_UNUSED, char *argv[]) ++do_dump_desc(int argc OVS_UNUSED, char *argv[]) { dump_trivial_stats_transaction(argv[1], OFPST_DESC); } static void - do_dump_tables(int argc UNUSED, char *argv[]) -do_dump_tables(const struct settings *s OVS_UNUSED, - int argc OVS_UNUSED, char *argv[]) ++do_dump_tables(int argc OVS_UNUSED, char *argv[]) { dump_trivial_stats_transaction(argv[1], OFPST_TABLE); } @@@ -780,7 -815,7 +780,7 @@@ str_to_flow(char *string, struct ofp_ma } static void -do_dump_flows(const struct settings *s OVS_UNUSED, int argc, char *argv[]) +do_dump_flows(int argc, char *argv[]) { struct ofp_flow_stats_request *req; uint16_t out_port; @@@ -796,7 -831,7 +796,7 @@@ } static void -do_dump_aggregate(const struct settings *s OVS_UNUSED, int argc, char *argv[]) +do_dump_aggregate(int argc, char *argv[]) { struct ofp_aggregate_stats_request *req; struct ofpbuf *request; @@@ -812,7 -847,8 +812,7 @@@ } static void - do_add_flow(int argc UNUSED, char *argv[]) -do_add_flow(const struct settings *s OVS_UNUSED, int argc OVS_UNUSED, - char *argv[]) ++do_add_flow(int argc OVS_UNUSED, char *argv[]) { struct vconn *vconn; struct ofpbuf *buffer; @@@ -840,7 -876,8 +840,7 @@@ } static void - do_add_flows(int argc UNUSED, char *argv[]) -do_add_flows(const struct settings *s OVS_UNUSED, int argc OVS_UNUSED, - char *argv[]) ++do_add_flows(int argc OVS_UNUSED, char *argv[]) { struct vconn *vconn; FILE *file; @@@ -874,7 -911,7 +874,7 @@@ /* Parse and send. str_to_flow() will expand and reallocate the data * in 'buffer', so we can't keep pointers to across the str_to_flow() * call. */ - ofm = make_openflow(sizeof *ofm, OFPT_FLOW_MOD, &buffer); + make_openflow(sizeof *ofm, OFPT_FLOW_MOD, &buffer); str_to_flow(line, &match, buffer, NULL, NULL, &priority, &idle_timeout, &hard_timeout); ofm = buffer->data; @@@ -893,7 -930,7 +893,7 @@@ } static void - do_mod_flows(int argc UNUSED, char *argv[]) -do_mod_flows(const struct settings *s, int argc OVS_UNUSED, char *argv[]) ++do_mod_flows(int argc OVS_UNUSED, char *argv[]) { uint16_t priority, idle_timeout, hard_timeout; struct vconn *vconn; @@@ -908,7 -945,7 +908,7 @@@ NULL, NULL, &priority, &idle_timeout, &hard_timeout); ofm = buffer->data; ofm->match = match; - if (s->strict) { + if (strict) { ofm->command = htons(OFPFC_MODIFY_STRICT); } else { ofm->command = htons(OFPFC_MODIFY); @@@ -924,7 -961,7 +924,7 @@@ vconn_close(vconn); } -static void do_del_flows(const struct settings *s, int argc, char *argv[]) +static void do_del_flows(int argc, char *argv[]) { struct vconn *vconn; uint16_t priority; @@@ -936,7 -973,7 +936,7 @@@ ofm = make_openflow(sizeof *ofm, OFPT_FLOW_MOD, &buffer); str_to_flow(argc > 2 ? argv[2] : "", &ofm->match, NULL, NULL, &out_port, &priority, NULL, NULL); - if (s->strict) { + if (strict) { ofm->command = htons(OFPFC_DELETE_STRICT); } else { ofm->command = htons(OFPFC_DELETE); @@@ -954,7 -991,8 +954,7 @@@ } static void - do_monitor(int argc UNUSED, char *argv[]) -do_monitor(const struct settings *s OVS_UNUSED, - int argc OVS_UNUSED, char *argv[]) ++do_monitor(int argc OVS_UNUSED, char *argv[]) { struct vconn *vconn; @@@ -979,13 -1017,15 +979,13 @@@ } static void - do_dump_ports(int argc UNUSED, char *argv[]) -do_dump_ports(const struct settings *s OVS_UNUSED, - int argc OVS_UNUSED, char *argv[]) ++do_dump_ports(int argc OVS_UNUSED, char *argv[]) { dump_trivial_stats_transaction(argv[1], OFPST_PORT); } static void - do_probe(int argc UNUSED, char *argv[]) -do_probe(const struct settings *s OVS_UNUSED, - int argc OVS_UNUSED, char *argv[]) ++do_probe(int argc OVS_UNUSED, char *argv[]) { struct ofpbuf *request; struct vconn *vconn; @@@ -1002,7 -1042,8 +1002,7 @@@ } static void - do_mod_port(int argc UNUSED, char *argv[]) -do_mod_port(const struct settings *s OVS_UNUSED, - int argc OVS_UNUSED, char *argv[]) ++do_mod_port(int argc OVS_UNUSED, char *argv[]) { struct ofpbuf *request, *reply; struct ofp_switch_features *osf; @@@ -1082,7 -1123,7 +1082,7 @@@ } static void -do_ping(const struct settings *s OVS_UNUSED, int argc, char *argv[]) +do_ping(int argc, char *argv[]) { size_t max_payload = 65535 - sizeof(struct ofp_header); unsigned int payload; @@@ -1129,7 -1170,8 +1129,7 @@@ } static void - do_benchmark(int argc UNUSED, char *argv[]) -do_benchmark(const struct settings *s OVS_UNUSED, - int argc OVS_UNUSED, char *argv[]) ++do_benchmark(int argc OVS_UNUSED, char *argv[]) { size_t max_payload = 65535 - sizeof(struct ofp_header); struct timeval start, end; @@@ -1172,12 -1214,78 +1172,12 @@@ } static void - do_help(int argc UNUSED, char *argv[] UNUSED) -do_execute(const struct settings *s OVS_UNUSED, int argc, char *argv[]) -{ - struct vconn *vconn; - struct ofpbuf *request; - struct nicira_header *nicira; - struct nx_command_reply *ncr; - uint32_t xid; - int i; - - nicira = make_openflow(sizeof *nicira, OFPT_VENDOR, &request); - xid = nicira->header.xid; - nicira->vendor = htonl(NX_VENDOR_ID); - nicira->subtype = htonl(NXT_COMMAND_REQUEST); - ofpbuf_put(request, argv[2], strlen(argv[2])); - for (i = 3; i < argc; i++) { - ofpbuf_put_zeros(request, 1); - ofpbuf_put(request, argv[i], strlen(argv[i])); - } - update_openflow_length(request); - - open_vconn(argv[1], &vconn); - run(vconn_send_block(vconn, request), "send"); - - for (;;) { - struct ofpbuf *reply; - uint32_t status; - - run(vconn_recv_xid(vconn, xid, &reply), "recv_xid"); - if (reply->size < sizeof *ncr) { - ovs_fatal(0, "reply is too short (%zu bytes < %zu bytes)", - reply->size, sizeof *ncr); - } - ncr = reply->data; - if (ncr->nxh.header.type != OFPT_VENDOR - || ncr->nxh.vendor != htonl(NX_VENDOR_ID) - || ncr->nxh.subtype != htonl(NXT_COMMAND_REPLY)) { - ovs_fatal(0, "reply is invalid"); - } - - status = ntohl(ncr->status); - if (status & NXT_STATUS_STARTED) { - /* Wait for a second reply. */ - continue; - } else if (status & NXT_STATUS_EXITED) { - fprintf(stderr, "process terminated normally with exit code %d", - status & NXT_STATUS_EXITSTATUS); - } else if (status & NXT_STATUS_SIGNALED) { - fprintf(stderr, "process terminated by signal %d", - status & NXT_STATUS_TERMSIG); - } else if (status & NXT_STATUS_ERROR) { - fprintf(stderr, "error executing command"); - } else { - fprintf(stderr, "process terminated for unknown reason"); - } - if (status & NXT_STATUS_COREDUMP) { - fprintf(stderr, " (core dumped)"); - } - putc('\n', stderr); - - fwrite(ncr + 1, reply->size - sizeof *ncr, 1, stdout); - break; - } -} - -static void -do_help(const struct settings *s OVS_UNUSED, - int argc OVS_UNUSED, char *argv[] OVS_UNUSED) ++do_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) { usage(); } -static struct command all_commands[] = { +static const struct command all_commands[] = { { "show", 1, 1, do_show }, { "status", 1, 2, do_status }, { "monitor", 1, 3, do_monitor }, @@@ -1194,6 -1302,7 +1194,6 @@@ { "probe", 1, 1, do_probe }, { "ping", 1, 2, do_ping }, { "benchmark", 3, 3, do_benchmark }, - { "execute", 2, INT_MAX, do_execute }, { "help", 0, INT_MAX, do_help }, { NULL, 0, 0, NULL }, }; diff --combined utilities/ovs-vsctl.c index 498a0b0b3,000000000..84db728bc mode 100644,000000..100644 --- a/utilities/ovs-vsctl.c +++ b/utilities/ovs-vsctl.c @@@ -1,2519 -1,0 +1,2519 @@@ +/* + * Copyright (c) 2009, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "command-line.h" +#include "compiler.h" +#include "dirs.h" +#include "dynamic-string.h" +#include "json.h" +#include "ovsdb-data.h" +#include "ovsdb-idl.h" +#include "poll-loop.h" +#include "process.h" +#include "svec.h" +#include "vswitchd/vswitch-idl.h" +#include "timeval.h" +#include "util.h" + +#include "vlog.h" +#define THIS_MODULE VLM_vsctl + +/* vsctl_fatal() also logs the error, so it is preferred in this file. */ +#define ovs_fatal please_use_vsctl_fatal_instead_of_ovs_fatal + +struct vsctl_context; + +typedef void vsctl_handler_func(struct vsctl_context *); + +struct vsctl_command_syntax { + const char *name; + int min_args; + int max_args; + vsctl_handler_func *run; + vsctl_handler_func *postprocess; + const char *options; +}; + +struct vsctl_command { + /* Data that remains constant after initialization. */ + const struct vsctl_command_syntax *syntax; + int argc; + char **argv; + struct shash options; + + /* Data modified by commands. */ + struct ds output; +}; + +/* --db: The database server to contact. */ +static const char *db; + +/* --oneline: Write each command's output as a single line? */ +static bool oneline; + +/* --dry-run: Do not commit any changes. */ +static bool dry_run; + +/* --no-wait: Wait for ovs-vswitchd to reload its configuration? */ +static bool wait_for_reload = true; + +/* --timeout: Time to wait for a connection to 'db'. */ +static int timeout = 5; + +/* All supported commands. */ +static const struct vsctl_command_syntax all_commands[]; + +/* The IDL we're using and the current transaction, if any. + * This is for use by vsctl_exit() only, to allow it to clean up. + * Other code should use its context arguments. */ +static struct ovsdb_idl *the_idl; +static struct ovsdb_idl_txn *the_idl_txn; + +static void vsctl_exit(int status) NO_RETURN; +static void vsctl_fatal(const char *, ...) PRINTF_FORMAT(1, 2) NO_RETURN; +static char *default_db(void); +static void usage(void) NO_RETURN; +static void parse_options(int argc, char *argv[]); + +static struct vsctl_command *parse_commands(int argc, char *argv[], + size_t *n_commandsp); +static void parse_command(int argc, char *argv[], struct vsctl_command *); +static void do_vsctl(const char *args, + struct vsctl_command *, size_t n_commands, + struct ovsdb_idl *); + +int +main(int argc, char *argv[]) +{ + struct ovsdb_idl *idl; + unsigned int seqno; + struct vsctl_command *commands; + size_t n_commands; + char *args; + int trials; + + set_program_name(argv[0]); + signal(SIGPIPE, SIG_IGN); + time_init(); + vlog_init(); + vlog_set_levels(VLM_ANY_MODULE, VLF_CONSOLE, VLL_WARN); + vlog_set_levels(VLM_reconnect, VLF_ANY_FACILITY, VLL_WARN); + ovsrec_init(); + + /* Log our arguments. This is often valuable for debugging systems. */ + args = process_escape_args(argv); + VLOG_INFO("Called as %s", args); + + /* Parse command line. */ + parse_options(argc, argv); + commands = parse_commands(argc - optind, argv + optind, &n_commands); + + if (timeout) { + time_alarm(timeout); + } + + /* Now execute the commands. */ + idl = the_idl = ovsdb_idl_create(db, &ovsrec_idl_class); + seqno = ovsdb_idl_get_seqno(idl); + trials = 0; + for (;;) { + unsigned int new_seqno; + + ovsdb_idl_run(idl); + new_seqno = ovsdb_idl_get_seqno(idl); + if (new_seqno != seqno) { + if (++trials > 5) { + vsctl_fatal("too many database inconsistency failures"); + } + do_vsctl(args, commands, n_commands, idl); + seqno = new_seqno; + } + + ovsdb_idl_wait(idl); + poll_block(); + } +} + +static void +parse_options(int argc, char *argv[]) +{ + enum { + OPT_DB = UCHAR_MAX + 1, + OPT_ONELINE, + OPT_NO_SYSLOG, + OPT_NO_WAIT, + OPT_DRY_RUN, + VLOG_OPTION_ENUMS + }; + static struct option long_options[] = { + {"db", required_argument, 0, OPT_DB}, + {"no-syslog", no_argument, 0, OPT_NO_SYSLOG}, + {"no-wait", no_argument, 0, OPT_NO_WAIT}, + {"dry-run", no_argument, 0, OPT_DRY_RUN}, + {"oneline", no_argument, 0, OPT_ONELINE}, + {"timeout", required_argument, 0, 't'}, + {"help", no_argument, 0, 'h'}, + {"version", no_argument, 0, 'V'}, + VLOG_LONG_OPTIONS, + {0, 0, 0, 0}, + }; + + + for (;;) { + int c; + + c = getopt_long(argc, argv, "+v::hVt:", long_options, NULL); + if (c == -1) { + break; + } + + switch (c) { + case OPT_DB: + db = optarg; + break; + + case OPT_ONELINE: + oneline = true; + break; + + case OPT_NO_SYSLOG: + vlog_set_levels(VLM_vsctl, VLF_SYSLOG, VLL_WARN); + break; + + case OPT_NO_WAIT: + wait_for_reload = false; + break; + + case OPT_DRY_RUN: + dry_run = true; + break; + + case 'h': + usage(); + + case 'V': + OVS_PRINT_VERSION(0, 0); + exit(EXIT_SUCCESS); + + case 't': + timeout = strtoul(optarg, NULL, 10); + if (timeout < 0) { + vsctl_fatal("value %s on -t or --timeout is invalid", + optarg); + } + break; + + VLOG_OPTION_HANDLERS + + case '?': + exit(EXIT_FAILURE); + + default: + abort(); + } + } + + if (!db) { + db = default_db(); + } +} + +static struct vsctl_command * +parse_commands(int argc, char *argv[], size_t *n_commandsp) +{ + struct vsctl_command *commands; + size_t n_commands, allocated_commands; + int i, start; + + commands = NULL; + n_commands = allocated_commands = 0; + + for (start = i = 0; i <= argc; i++) { + if (i == argc || !strcmp(argv[i], "--")) { + if (i > start) { + if (n_commands >= allocated_commands) { + struct vsctl_command *c; + + commands = x2nrealloc(commands, &allocated_commands, + sizeof *commands); + for (c = commands; c < &commands[n_commands]; c++) { + shash_moved(&c->options); + } + } + parse_command(i - start, &argv[start], + &commands[n_commands++]); + } + start = i + 1; + } + } + if (!n_commands) { + vsctl_fatal("missing command name (use --help for help)"); + } + *n_commandsp = n_commands; + return commands; +} + +static void +parse_command(int argc, char *argv[], struct vsctl_command *command) +{ + const struct vsctl_command_syntax *p; + int i; + + shash_init(&command->options); + for (i = 0; i < argc; i++) { + if (argv[i][0] != '-') { + break; + } + if (!shash_add_once(&command->options, argv[i], NULL)) { + vsctl_fatal("'%s' option specified multiple times", argv[i]); + } + } + if (i == argc) { + vsctl_fatal("missing command name"); + } + + for (p = all_commands; p->name; p++) { + if (!strcmp(p->name, argv[i])) { + struct shash_node *node; + int n_arg; + + SHASH_FOR_EACH (node, &command->options) { + const char *s = strstr(p->options, node->name); + int end = s ? s[strlen(node->name)] : EOF; + if (end != ',' && end != ' ' && end != '\0') { + vsctl_fatal("'%s' command has no '%s' option", + argv[i], node->name); + } + } + + n_arg = argc - i - 1; + if (n_arg < p->min_args) { + vsctl_fatal("'%s' command requires at least %d arguments", + p->name, p->min_args); + } else if (n_arg > p->max_args) { + int j; + + for (j = i + 1; j < argc; j++) { + if (argv[j][0] == '-') { + vsctl_fatal("'%s' command takes at most %d arguments " + "(note that options must precede command " + "names and follow a \"--\" argument)", + p->name, p->max_args); + } + } + + vsctl_fatal("'%s' command takes at most %d arguments", + p->name, p->max_args); + } else { + command->syntax = p; + command->argc = n_arg + 1; + command->argv = &argv[i]; + return; + } + } + } + + vsctl_fatal("unknown command '%s'; use --help for help", argv[i]); +} + +static void +vsctl_fatal(const char *format, ...) +{ + char *message; + va_list args; + + va_start(args, format); + message = xvasprintf(format, args); + va_end(args); + + vlog_set_levels(VLM_vsctl, VLF_CONSOLE, VLL_EMER); + VLOG_ERR("%s", message); + ovs_error(0, "%s", message); + vsctl_exit(EXIT_FAILURE); +} + +/* Frees the current transaction and the underlying IDL and then calls + * exit(status). + * + * Freeing the transaction and the IDL is not strictly necessary, but it makes + * for a clean memory leak report from valgrind in the normal case. That makes + * it easier to notice real memory leaks. */ +static void +vsctl_exit(int status) +{ + if (the_idl_txn) { + ovsdb_idl_txn_abort(the_idl_txn); + ovsdb_idl_txn_destroy(the_idl_txn); + } + ovsdb_idl_destroy(the_idl); + exit(status); +} + +static void +usage(void) +{ + printf("\ +%s: ovs-vswitchd management utility\n\ +usage: %s [OPTIONS] COMMAND [ARG...]\n\ +\n\ +Bridge commands:\n\ + add-br BRIDGE create a new bridge named BRIDGE\n\ + add-br BRIDGE PARENT VLAN create new fake BRIDGE in PARENT on VLAN\n\ + del-br BRIDGE delete BRIDGE and all of its ports\n\ + list-br print the names of all the bridges\n\ + br-exists BRIDGE test whether BRIDGE exists\n\ + br-to-vlan BRIDGE print the VLAN which BRIDGE is on\n\ + br-to-parent BRIDGE print the parent of BRIDGE\n\ + br-set-external-id BRIDGE KEY VALUE set KEY on BRIDGE to VALUE\n\ + br-set-external-id BRIDGE KEY unset KEY on BRIDGE\n\ + br-get-external-id BRIDGE KEY print value of KEY on BRIDGE\n\ + br-get-external-id BRIDGE list key-value pairs on BRIDGE\n\ +\n\ +Port commands:\n\ + list-ports BRIDGE print the names of all the ports on BRIDGE\n\ + add-port BRIDGE PORT add network device PORT to BRIDGE\n\ + add-bond BRIDGE PORT IFACE... add bonded port PORT in BRIDGE from IFACES\n\ + del-port [BRIDGE] PORT delete PORT (which may be bonded) from BRIDGE\n\ + port-to-br PORT print name of bridge that contains PORT\n\ +A bond is considered to be a single port.\n\ +\n\ +Interface commands (a bond consists of multiple interfaces):\n\ + list-ifaces BRIDGE print the names of all interfaces on BRIDGE\n\ + iface-to-br IFACE print name of bridge that contains IFACE\n\ +\n\ +Controller commands:\n\ + get-controller [BRIDGE] print the controller for BRIDGE\n\ + del-controller [BRIDGE] delete the controller for BRIDGE\n\ + set-controller [BRIDGE] TARGET set the controller for BRIDGE to TARGET\n\ + get-fail-mode [BRIDGE] print the fail-mode for BRIDGE\n\ + del-fail-mode [BRIDGE] delete the fail-mode for BRIDGE\n\ + set-fail-mode [BRIDGE] MODE set the fail-mode for BRIDGE to MODE\n\ +\n\ +SSL commands:\n\ + get-ssl print the SSL configuration\n\ + del-ssl delete the SSL configuration\n\ + set-ssl PRIV-KEY CERT CA-CERT set the SSL configuration\n\ +\n\ +Database commands:\n\ + list TBL [REC] list RECord (or all records) in TBL\n\ + get TBL REC COL[:KEY] print values of COLumns in RECORD in TBL\n\ + set TBL REC COL[:KEY]=VALUE set COLumn values in RECord in TBL\n\ + add TBL REC COL [KEY=]VALUE add (KEY=)VALUE to COLumn in RECord in TBL\n\ + remove TBL REC COL [KEY=]VALUE remove (KEY=)VALUE from COLumn\n\ + clear TBL REC COL clear values from COLumn in RECord in TBL\n\ + create TBL COL[:KEY]=VALUE create and initialize new record\n\ + destroy TBL REC delete REC from TBL\n\ +Potentially unsafe database commands require --force option.\n\ +\n\ +Options:\n\ + --db=DATABASE connect to DATABASE\n\ + (default: %s)\n\ + --oneline print exactly one line of output per command\n", + program_name, program_name, default_db()); + vlog_usage(); + printf("\n\ +Other options:\n\ + -h, --help display this help message\n\ + -V, --version display version information\n"); + exit(EXIT_SUCCESS); +} + +static char * +default_db(void) +{ + static char *def; + if (!def) { + def = xasprintf("unix:%s/ovsdb-server", ovs_rundir); + } + return def; +} + +struct vsctl_context { + /* Read-only. */ + int argc; + char **argv; + struct shash options; + + /* Modifiable state. */ + struct ds output; + struct ovsdb_idl *idl; + struct ovsdb_idl_txn *txn; + const struct ovsrec_open_vswitch *ovs; +}; + +struct vsctl_bridge { + struct ovsrec_bridge *br_cfg; + char *name; + struct ovsrec_controller *ctrl; + struct vsctl_bridge *parent; + int vlan; +}; + +struct vsctl_port { + struct ovsrec_port *port_cfg; + struct vsctl_bridge *bridge; +}; + +struct vsctl_iface { + struct ovsrec_interface *iface_cfg; + struct vsctl_port *port; +}; + +struct vsctl_info { + struct shash bridges; + struct shash ports; + struct shash ifaces; + struct ovsrec_controller *ctrl; +}; + +static char * +vsctl_context_to_string(const struct vsctl_context *ctx) +{ + const struct shash_node *node; + struct svec words; + char *s; + int i; + + svec_init(&words); + SHASH_FOR_EACH (node, &ctx->options) { + svec_add(&words, node->name); + } + for (i = 0; i < ctx->argc; i++) { + svec_add(&words, ctx->argv[i]); + } + svec_terminate(&words); + + s = process_escape_args(words.names); + + svec_destroy(&words); + + return s; +} + +static struct vsctl_bridge * +add_bridge(struct vsctl_info *b, + struct ovsrec_bridge *br_cfg, const char *name, + struct vsctl_bridge *parent, int vlan) +{ + struct vsctl_bridge *br = xmalloc(sizeof *br); + br->br_cfg = br_cfg; + br->name = xstrdup(name); + br->parent = parent; + br->vlan = vlan; + br->ctrl = parent ? parent->br_cfg->controller : br_cfg->controller; + shash_add(&b->bridges, br->name, br); + return br; +} + +static bool +port_is_fake_bridge(const struct ovsrec_port *port_cfg) +{ + return (port_cfg->fake_bridge + && port_cfg->tag + && *port_cfg->tag >= 1 && *port_cfg->tag <= 4095); +} + +static struct vsctl_bridge * +find_vlan_bridge(struct vsctl_info *info, + struct vsctl_bridge *parent, int vlan) +{ + struct shash_node *node; + + SHASH_FOR_EACH (node, &info->bridges) { + struct vsctl_bridge *br = node->data; + if (br->parent == parent && br->vlan == vlan) { + return br; + } + } + + return NULL; +} + +static void +free_info(struct vsctl_info *info) +{ + struct shash_node *node; + + SHASH_FOR_EACH (node, &info->bridges) { + struct vsctl_bridge *bridge = node->data; + free(bridge->name); + free(bridge); + } + shash_destroy(&info->bridges); + + SHASH_FOR_EACH (node, &info->ports) { + struct vsctl_port *port = node->data; + free(port); + } + shash_destroy(&info->ports); + + SHASH_FOR_EACH (node, &info->ifaces) { + struct vsctl_iface *iface = node->data; + free(iface); + } + shash_destroy(&info->ifaces); +} + +static void +get_info(const struct ovsrec_open_vswitch *ovs, struct vsctl_info *info) +{ + struct shash bridges, ports; + size_t i; + + shash_init(&info->bridges); + shash_init(&info->ports); + shash_init(&info->ifaces); + + info->ctrl = ovs->controller; + + shash_init(&bridges); + shash_init(&ports); + for (i = 0; i < ovs->n_bridges; i++) { + struct ovsrec_bridge *br_cfg = ovs->bridges[i]; + struct vsctl_bridge *br; + size_t j; + + if (!shash_add_once(&bridges, br_cfg->name, NULL)) { + VLOG_WARN("%s: database contains duplicate bridge name", + br_cfg->name); + continue; + } + br = add_bridge(info, br_cfg, br_cfg->name, NULL, 0); + if (!br) { + continue; + } + + for (j = 0; j < br_cfg->n_ports; j++) { + struct ovsrec_port *port_cfg = br_cfg->ports[j]; + + if (!shash_add_once(&ports, port_cfg->name, NULL)) { + VLOG_WARN("%s: database contains duplicate port name", + port_cfg->name); + continue; + } + + if (port_is_fake_bridge(port_cfg) + && shash_add_once(&bridges, port_cfg->name, NULL)) { + add_bridge(info, NULL, port_cfg->name, br, *port_cfg->tag); + } + } + } + shash_destroy(&bridges); + shash_destroy(&ports); + + shash_init(&bridges); + shash_init(&ports); + for (i = 0; i < ovs->n_bridges; i++) { + struct ovsrec_bridge *br_cfg = ovs->bridges[i]; + struct vsctl_bridge *br; + size_t j; + + if (!shash_add_once(&bridges, br_cfg->name, NULL)) { + continue; + } + br = shash_find_data(&info->bridges, br_cfg->name); + for (j = 0; j < br_cfg->n_ports; j++) { + struct ovsrec_port *port_cfg = br_cfg->ports[j]; + struct vsctl_port *port; + size_t k; + + if (!shash_add_once(&ports, port_cfg->name, NULL)) { + continue; + } + + if (port_is_fake_bridge(port_cfg) + && !shash_add_once(&bridges, port_cfg->name, NULL)) { + continue; + } + + port = xmalloc(sizeof *port); + port->port_cfg = port_cfg; + if (port_cfg->tag + && *port_cfg->tag >= 1 && *port_cfg->tag <= 4095) { + port->bridge = find_vlan_bridge(info, br, *port_cfg->tag); + if (!port->bridge) { + port->bridge = br; + } + } else { + port->bridge = br; + } + shash_add(&info->ports, port_cfg->name, port); + + for (k = 0; k < port_cfg->n_interfaces; k++) { + struct ovsrec_interface *iface_cfg = port_cfg->interfaces[k]; + struct vsctl_iface *iface; + + if (shash_find(&info->ifaces, iface_cfg->name)) { + VLOG_WARN("%s: database contains duplicate interface name", + iface_cfg->name); + continue; + } + + iface = xmalloc(sizeof *iface); + iface->iface_cfg = iface_cfg; + iface->port = port; + shash_add(&info->ifaces, iface_cfg->name, iface); + } + } + } + shash_destroy(&bridges); + shash_destroy(&ports); +} + +static void +check_conflicts(struct vsctl_info *info, const char *name, + char *msg) +{ + struct vsctl_iface *iface; + struct vsctl_port *port; + + if (shash_find(&info->bridges, name)) { + vsctl_fatal("%s because a bridge named %s already exists", + msg, name); + } + + port = shash_find_data(&info->ports, name); + if (port) { + vsctl_fatal("%s because a port named %s already exists on " + "bridge %s", msg, name, port->bridge->name); + } + + iface = shash_find_data(&info->ifaces, name); + if (iface) { + vsctl_fatal("%s because an interface named %s already exists " + "on bridge %s", msg, name, iface->port->bridge->name); + } + + free(msg); +} + +static struct vsctl_bridge * +find_bridge(struct vsctl_info *info, const char *name, bool must_exist) +{ + struct vsctl_bridge *br = shash_find_data(&info->bridges, name); + if (must_exist && !br) { + vsctl_fatal("no bridge named %s", name); + } + return br; +} + +static struct vsctl_bridge * +find_real_bridge(struct vsctl_info *info, const char *name, bool must_exist) +{ + struct vsctl_bridge *br = find_bridge(info, name, must_exist); + if (br && br->parent) { + vsctl_fatal("%s is a fake bridge", name); + } + return br; +} + +static struct vsctl_port * +find_port(struct vsctl_info *info, const char *name, bool must_exist) +{ + struct vsctl_port *port = shash_find_data(&info->ports, name); + if (port && !strcmp(name, port->bridge->name)) { + port = NULL; + } + if (must_exist && !port) { + vsctl_fatal("no port named %s", name); + } + return port; +} + +static struct vsctl_iface * +find_iface(struct vsctl_info *info, const char *name, bool must_exist) +{ + struct vsctl_iface *iface = shash_find_data(&info->ifaces, name); + if (iface && !strcmp(name, iface->port->bridge->name)) { + iface = NULL; + } + if (must_exist && !iface) { + vsctl_fatal("no interface named %s", name); + } + return iface; +} + +static void +bridge_insert_port(struct ovsrec_bridge *br, struct ovsrec_port *port) +{ + struct ovsrec_port **ports; + size_t i; + + ports = xmalloc(sizeof *br->ports * (br->n_ports + 1)); + for (i = 0; i < br->n_ports; i++) { + ports[i] = br->ports[i]; + } + ports[br->n_ports] = port; + ovsrec_bridge_set_ports(br, ports, br->n_ports + 1); + free(ports); +} + +static void +bridge_delete_port(struct ovsrec_bridge *br, struct ovsrec_port *port) +{ + struct ovsrec_port **ports; + size_t i, n; + + ports = xmalloc(sizeof *br->ports * br->n_ports); + for (i = n = 0; i < br->n_ports; i++) { + if (br->ports[i] != port) { + ports[n++] = br->ports[i]; + } + } + ovsrec_bridge_set_ports(br, ports, n); + free(ports); +} + +static void +ovs_insert_bridge(const struct ovsrec_open_vswitch *ovs, + struct ovsrec_bridge *bridge) +{ + struct ovsrec_bridge **bridges; + size_t i; + + bridges = xmalloc(sizeof *ovs->bridges * (ovs->n_bridges + 1)); + for (i = 0; i < ovs->n_bridges; i++) { + bridges[i] = ovs->bridges[i]; + } + bridges[ovs->n_bridges] = bridge; + ovsrec_open_vswitch_set_bridges(ovs, bridges, ovs->n_bridges + 1); + free(bridges); +} + +static void +ovs_delete_bridge(const struct ovsrec_open_vswitch *ovs, + struct ovsrec_bridge *bridge) +{ + struct ovsrec_bridge **bridges; + size_t i, n; + + bridges = xmalloc(sizeof *ovs->bridges * ovs->n_bridges); + for (i = n = 0; i < ovs->n_bridges; i++) { + if (ovs->bridges[i] != bridge) { + bridges[n++] = ovs->bridges[i]; + } + } + ovsrec_open_vswitch_set_bridges(ovs, bridges, n); + free(bridges); +} + +static void - cmd_init(struct vsctl_context *ctx UNUSED) ++cmd_init(struct vsctl_context *ctx OVS_UNUSED) +{ +} + +static void +cmd_add_br(struct vsctl_context *ctx) +{ + bool may_exist = shash_find(&ctx->options, "--may-exist") != 0; + const char *br_name, *parent_name; + struct vsctl_info info; + int vlan; + + br_name = ctx->argv[1]; + if (ctx->argc == 2) { + parent_name = NULL; + vlan = 0; + } else if (ctx->argc == 4) { + parent_name = ctx->argv[2]; + vlan = atoi(ctx->argv[3]); + if (vlan < 1 || vlan > 4095) { + vsctl_fatal("%s: vlan must be between 1 and 4095", ctx->argv[0]); + } + } else { + vsctl_fatal("'%s' command takes exactly 1 or 3 arguments", + ctx->argv[0]); + } + + get_info(ctx->ovs, &info); + if (may_exist) { + struct vsctl_bridge *br; + + br = find_bridge(&info, br_name, false); + if (br) { + if (!parent_name) { + if (br->parent) { + vsctl_fatal("\"--may-exist add-br %s\" but %s is " + "a VLAN bridge for VLAN %d", + br_name, br_name, br->vlan); + } + } else { + if (!br->parent) { + vsctl_fatal("\"--may-exist add-br %s %s %d\" but %s " + "is not a VLAN bridge", + br_name, parent_name, vlan, br_name); + } else if (strcmp(br->parent->name, parent_name)) { + vsctl_fatal("\"--may-exist add-br %s %s %d\" but %s " + "has the wrong parent %s", + br_name, parent_name, vlan, + br_name, br->parent->name); + } else if (br->vlan != vlan) { + vsctl_fatal("\"--may-exist add-br %s %s %d\" but %s " + "is a VLAN bridge for the wrong VLAN %d", + br_name, parent_name, vlan, br_name, br->vlan); + } + } + return; + } + } + check_conflicts(&info, br_name, + xasprintf("cannot create a bridge named %s", br_name)); + + if (!parent_name) { + struct ovsrec_port *port; + struct ovsrec_interface *iface; + struct ovsrec_bridge *br; + + iface = ovsrec_interface_insert(ctx->txn); + ovsrec_interface_set_name(iface, br_name); + + port = ovsrec_port_insert(ctx->txn); + ovsrec_port_set_name(port, br_name); + ovsrec_port_set_interfaces(port, &iface, 1); + + br = ovsrec_bridge_insert(ctx->txn); + ovsrec_bridge_set_name(br, br_name); + ovsrec_bridge_set_ports(br, &port, 1); + + ovs_insert_bridge(ctx->ovs, br); + } else { + struct vsctl_bridge *parent; + struct ovsrec_port *port; + struct ovsrec_interface *iface; + struct ovsrec_bridge *br; + int64_t tag = vlan; + + parent = find_bridge(&info, parent_name, false); + if (parent && parent->vlan) { + vsctl_fatal("cannot create bridge with fake bridge as parent"); + } + if (!parent) { + vsctl_fatal("parent bridge %s does not exist", parent_name); + } + br = parent->br_cfg; + + iface = ovsrec_interface_insert(ctx->txn); + ovsrec_interface_set_name(iface, br_name); + ovsrec_interface_set_type(iface, "internal"); + + port = ovsrec_port_insert(ctx->txn); + ovsrec_port_set_name(port, br_name); + ovsrec_port_set_interfaces(port, &iface, 1); + ovsrec_port_set_fake_bridge(port, true); + ovsrec_port_set_tag(port, &tag, 1); + + bridge_insert_port(br, port); + } + + free_info(&info); +} + +static void +del_port(struct vsctl_info *info, struct vsctl_port *port) +{ + struct shash_node *node; + + SHASH_FOR_EACH (node, &info->ifaces) { + struct vsctl_iface *iface = node->data; + if (iface->port == port) { + ovsrec_interface_delete(iface->iface_cfg); + } + } + ovsrec_port_delete(port->port_cfg); + + bridge_delete_port((port->bridge->parent + ? port->bridge->parent->br_cfg + : port->bridge->br_cfg), port->port_cfg); +} + +static void +cmd_del_br(struct vsctl_context *ctx) +{ + bool must_exist = !shash_find(&ctx->options, "--if-exists"); + struct vsctl_bridge *bridge; + struct vsctl_info info; + + get_info(ctx->ovs, &info); + bridge = find_bridge(&info, ctx->argv[1], must_exist); + if (bridge) { + struct shash_node *node; + + SHASH_FOR_EACH (node, &info.ports) { + struct vsctl_port *port = node->data; + if (port->bridge == bridge || port->bridge->parent == bridge + || !strcmp(port->port_cfg->name, bridge->name)) { + del_port(&info, port); + } + } + if (bridge->br_cfg) { + ovsrec_bridge_delete(bridge->br_cfg); + ovs_delete_bridge(ctx->ovs, bridge->br_cfg); + } + } + free_info(&info); +} + +static void +output_sorted(struct svec *svec, struct ds *output) +{ + const char *name; + size_t i; + + svec_sort(svec); + SVEC_FOR_EACH (i, name, svec) { + ds_put_format(output, "%s\n", name); + } +} + +static void +cmd_list_br(struct vsctl_context *ctx) +{ + struct shash_node *node; + struct vsctl_info info; + struct svec bridges; + + get_info(ctx->ovs, &info); + + svec_init(&bridges); + SHASH_FOR_EACH (node, &info.bridges) { + struct vsctl_bridge *br = node->data; + svec_add(&bridges, br->name); + } + output_sorted(&bridges, &ctx->output); + svec_destroy(&bridges); + + free_info(&info); +} + +static void +cmd_br_exists(struct vsctl_context *ctx) +{ + struct vsctl_info info; + + get_info(ctx->ovs, &info); + if (!find_bridge(&info, ctx->argv[1], false)) { + vsctl_exit(2); + } + free_info(&info); +} + +/* Returns true if 'b_prefix' (of length 'b_prefix_len') concatenated with 'b' + * equals 'a', false otherwise. */ +static bool +key_matches(const char *a, + const char *b_prefix, size_t b_prefix_len, const char *b) +{ + return !strncmp(a, b_prefix, b_prefix_len) && !strcmp(a + b_prefix_len, b); +} + +static void +set_external_id(char **old_keys, char **old_values, size_t old_n, + char *key, char *value, + char ***new_keysp, char ***new_valuesp, size_t *new_np) +{ + char **new_keys; + char **new_values; + size_t new_n; + size_t i; + + new_keys = xmalloc(sizeof *new_keys * (old_n + 1)); + new_values = xmalloc(sizeof *new_values * (old_n + 1)); + new_n = 0; + for (i = 0; i < old_n; i++) { + if (strcmp(key, old_keys[i])) { + new_keys[new_n] = old_keys[i]; + new_values[new_n] = old_values[i]; + new_n++; + } + } + if (value) { + new_keys[new_n] = key; + new_values[new_n] = value; + new_n++; + } + *new_keysp = new_keys; + *new_valuesp = new_values; + *new_np = new_n; +} + +static void +cmd_br_set_external_id(struct vsctl_context *ctx) +{ + struct vsctl_info info; + struct vsctl_bridge *bridge; + char **keys, **values; + size_t n; + + get_info(ctx->ovs, &info); + bridge = find_bridge(&info, ctx->argv[1], true); + if (bridge->br_cfg) { + set_external_id(bridge->br_cfg->key_external_ids, + bridge->br_cfg->value_external_ids, + bridge->br_cfg->n_external_ids, + ctx->argv[2], ctx->argc >= 4 ? ctx->argv[3] : NULL, + &keys, &values, &n); + ovsrec_bridge_set_external_ids(bridge->br_cfg, keys, values, n); + } else { + char *key = xasprintf("fake-bridge-%s", ctx->argv[2]); + struct vsctl_port *port = shash_find_data(&info.ports, ctx->argv[1]); + set_external_id(port->port_cfg->key_external_ids, + port->port_cfg->value_external_ids, + port->port_cfg->n_external_ids, + key, ctx->argc >= 4 ? ctx->argv[3] : NULL, + &keys, &values, &n); + ovsrec_port_set_external_ids(port->port_cfg, keys, values, n); + free(key); + } + free(keys); + free(values); + + free_info(&info); +} + +static void +get_external_id(char **keys, char **values, size_t n, + const char *prefix, const char *key, + struct ds *output) +{ + size_t prefix_len = strlen(prefix); + struct svec svec; + size_t i; + + svec_init(&svec); + for (i = 0; i < n; i++) { + if (!key && !strncmp(keys[i], prefix, prefix_len)) { + svec_add_nocopy(&svec, xasprintf("%s=%s", + keys[i] + prefix_len, values[i])); + } else if (key_matches(keys[i], prefix, prefix_len, key)) { + svec_add(&svec, values[i]); + break; + } + } + output_sorted(&svec, output); + svec_destroy(&svec); +} + +static void +cmd_br_get_external_id(struct vsctl_context *ctx) +{ + struct vsctl_info info; + struct vsctl_bridge *bridge; + + get_info(ctx->ovs, &info); + bridge = find_bridge(&info, ctx->argv[1], true); + if (bridge->br_cfg) { + get_external_id(bridge->br_cfg->key_external_ids, + bridge->br_cfg->value_external_ids, + bridge->br_cfg->n_external_ids, + "", ctx->argc >= 3 ? ctx->argv[2] : NULL, + &ctx->output); + } else { + struct vsctl_port *port = shash_find_data(&info.ports, ctx->argv[1]); + get_external_id(port->port_cfg->key_external_ids, + port->port_cfg->value_external_ids, + port->port_cfg->n_external_ids, + "fake-bridge-", ctx->argc >= 3 ? ctx->argv[2] : NULL, &ctx->output); + } + free_info(&info); +} + + +static void +cmd_list_ports(struct vsctl_context *ctx) +{ + struct vsctl_bridge *br; + struct shash_node *node; + struct vsctl_info info; + struct svec ports; + + get_info(ctx->ovs, &info); + br = find_bridge(&info, ctx->argv[1], true); + + svec_init(&ports); + SHASH_FOR_EACH (node, &info.ports) { + struct vsctl_port *port = node->data; + + if (strcmp(port->port_cfg->name, br->name) && br == port->bridge) { + svec_add(&ports, port->port_cfg->name); + } + } + output_sorted(&ports, &ctx->output); + svec_destroy(&ports); + + free_info(&info); +} + +static void +add_port(struct vsctl_context *ctx, + const char *br_name, const char *port_name, + bool may_exist, bool fake_iface, + char *iface_names[], int n_ifaces) +{ + struct vsctl_info info; + struct vsctl_bridge *bridge; + struct ovsrec_interface **ifaces; + struct ovsrec_port *port; + size_t i; + + get_info(ctx->ovs, &info); + if (may_exist) { + struct vsctl_port *port; + + port = find_port(&info, port_name, false); + if (port) { + struct svec want_names, have_names; + size_t i; + + svec_init(&want_names); + for (i = 0; i < n_ifaces; i++) { + svec_add(&want_names, iface_names[i]); + } + svec_sort(&want_names); + + svec_init(&have_names); + for (i = 0; i < port->port_cfg->n_interfaces; i++) { + svec_add(&have_names, port->port_cfg->interfaces[i]->name); + } + svec_sort(&have_names); + + if (strcmp(port->bridge->name, br_name)) { + char *command = vsctl_context_to_string(ctx); + vsctl_fatal("\"%s\" but %s is actually attached to bridge %s", + command, port_name, port->bridge->name); + } + + if (!svec_equal(&want_names, &have_names)) { + char *have_names_string = svec_join(&have_names, ", ", ""); + char *command = vsctl_context_to_string(ctx); + + vsctl_fatal("\"%s\" but %s actually has interface(s) %s", + command, port_name, have_names_string); + } + + svec_destroy(&want_names); + svec_destroy(&have_names); + + return; + } + } + check_conflicts(&info, port_name, + xasprintf("cannot create a port named %s", port_name)); + for (i = 0; i < n_ifaces; i++) { + check_conflicts(&info, iface_names[i], + xasprintf("cannot create an interface named %s", + iface_names[i])); + } + bridge = find_bridge(&info, br_name, true); + + ifaces = xmalloc(n_ifaces * sizeof *ifaces); + for (i = 0; i < n_ifaces; i++) { + ifaces[i] = ovsrec_interface_insert(ctx->txn); + ovsrec_interface_set_name(ifaces[i], iface_names[i]); + } + + port = ovsrec_port_insert(ctx->txn); + ovsrec_port_set_name(port, port_name); + ovsrec_port_set_interfaces(port, ifaces, n_ifaces); + ovsrec_port_set_bond_fake_iface(port, fake_iface); + free(ifaces); + + if (bridge->vlan) { + int64_t tag = bridge->vlan; + ovsrec_port_set_tag(port, &tag, 1); + } + + bridge_insert_port((bridge->parent ? bridge->parent->br_cfg + : bridge->br_cfg), port); + + free_info(&info); +} + +static void +cmd_add_port(struct vsctl_context *ctx) +{ + bool may_exist = shash_find(&ctx->options, "--may-exist") != 0; + + add_port(ctx, ctx->argv[1], ctx->argv[2], may_exist, false, + &ctx->argv[2], 1); +} + +static void +cmd_add_bond(struct vsctl_context *ctx) +{ + bool may_exist = shash_find(&ctx->options, "--may-exist") != 0; + bool fake_iface = shash_find(&ctx->options, "--fake-iface"); + + add_port(ctx, ctx->argv[1], ctx->argv[2], may_exist, fake_iface, + &ctx->argv[3], ctx->argc - 3); +} + +static void +cmd_del_port(struct vsctl_context *ctx) +{ + bool must_exist = !shash_find(&ctx->options, "--if-exists"); + struct vsctl_info info; + + get_info(ctx->ovs, &info); + if (ctx->argc == 2) { + struct vsctl_port *port = find_port(&info, ctx->argv[1], must_exist); + if (port) { + del_port(&info, port); + } + } else if (ctx->argc == 3) { + struct vsctl_bridge *bridge = find_bridge(&info, ctx->argv[1], true); + struct vsctl_port *port = find_port(&info, ctx->argv[2], must_exist); + + if (port) { + if (port->bridge == bridge) { + del_port(&info, port); + } else if (port->bridge->parent == bridge) { + vsctl_fatal("bridge %s does not have a port %s (although its " + "parent bridge %s does)", + ctx->argv[1], ctx->argv[2], bridge->parent->name); + } else { + vsctl_fatal("bridge %s does not have a port %s", + ctx->argv[1], ctx->argv[2]); + } + } + } + free_info(&info); +} + +static void +cmd_port_to_br(struct vsctl_context *ctx) +{ + struct vsctl_port *port; + struct vsctl_info info; + + get_info(ctx->ovs, &info); + port = find_port(&info, ctx->argv[1], true); + ds_put_format(&ctx->output, "%s\n", port->bridge->name); + free_info(&info); +} + +static void +cmd_br_to_vlan(struct vsctl_context *ctx) +{ + struct vsctl_bridge *bridge; + struct vsctl_info info; + + get_info(ctx->ovs, &info); + bridge = find_bridge(&info, ctx->argv[1], true); + ds_put_format(&ctx->output, "%d\n", bridge->vlan); + free_info(&info); +} + +static void +cmd_br_to_parent(struct vsctl_context *ctx) +{ + struct vsctl_bridge *bridge; + struct vsctl_info info; + + get_info(ctx->ovs, &info); + bridge = find_bridge(&info, ctx->argv[1], true); + if (bridge->parent) { + bridge = bridge->parent; + } + ds_put_format(&ctx->output, "%s\n", bridge->name); + free_info(&info); +} + +static void +cmd_list_ifaces(struct vsctl_context *ctx) +{ + struct vsctl_bridge *br; + struct shash_node *node; + struct vsctl_info info; + struct svec ifaces; + + get_info(ctx->ovs, &info); + br = find_bridge(&info, ctx->argv[1], true); + + svec_init(&ifaces); + SHASH_FOR_EACH (node, &info.ifaces) { + struct vsctl_iface *iface = node->data; + + if (strcmp(iface->iface_cfg->name, br->name) + && br == iface->port->bridge) { + svec_add(&ifaces, iface->iface_cfg->name); + } + } + output_sorted(&ifaces, &ctx->output); + svec_destroy(&ifaces); + + free_info(&info); +} + +static void +cmd_iface_to_br(struct vsctl_context *ctx) +{ + struct vsctl_iface *iface; + struct vsctl_info info; + + get_info(ctx->ovs, &info); + iface = find_iface(&info, ctx->argv[1], true); + ds_put_format(&ctx->output, "%s\n", iface->port->bridge->name); + free_info(&info); +} + +static void +cmd_get_controller(struct vsctl_context *ctx) +{ + struct vsctl_info info; + + get_info(ctx->ovs, &info); + + if (ctx->argc == 1) { + /* Return the controller from the "Open_vSwitch" table */ + if (info.ctrl) { + ds_put_format(&ctx->output, "%s\n", info.ctrl->target); + } + } else { + /* Return the controller for a particular bridge. */ + struct vsctl_bridge *br = find_bridge(&info, ctx->argv[1], true); + + /* If no controller is explicitly defined for the requested + * bridge, fallback to the "Open_vSwitch" table's controller. */ + if (br->ctrl) { + ds_put_format(&ctx->output, "%s\n", br->ctrl->target); + } else if (info.ctrl) { + ds_put_format(&ctx->output, "%s\n", info.ctrl->target); + } + } + + free_info(&info); +} + +static void +cmd_del_controller(struct vsctl_context *ctx) +{ + struct vsctl_info info; + + get_info(ctx->ovs, &info); + + if (ctx->argc == 1) { + if (info.ctrl) { + ovsrec_controller_delete(info.ctrl); + ovsrec_open_vswitch_set_controller(ctx->ovs, NULL); + } + } else { + struct vsctl_bridge *br = find_real_bridge(&info, ctx->argv[1], true); + + if (br->ctrl) { + ovsrec_controller_delete(br->ctrl); + ovsrec_bridge_set_controller(br->br_cfg, NULL); + } + } + + free_info(&info); +} + +static void +cmd_set_controller(struct vsctl_context *ctx) +{ + struct vsctl_info info; + struct ovsrec_controller *ctrl; + + get_info(ctx->ovs, &info); + + if (ctx->argc == 2) { + /* Set the controller in the "Open_vSwitch" table. */ + if (info.ctrl) { + ovsrec_controller_delete(info.ctrl); + } + ctrl = ovsrec_controller_insert(ctx->txn); + ovsrec_controller_set_target(ctrl, ctx->argv[1]); + ovsrec_open_vswitch_set_controller(ctx->ovs, ctrl); + } else { + /* Set the controller for a particular bridge. */ + struct vsctl_bridge *br = find_real_bridge(&info, ctx->argv[1], true); + + if (br->ctrl) { + ovsrec_controller_delete(br->ctrl); + } + ctrl = ovsrec_controller_insert(ctx->txn); + ovsrec_controller_set_target(ctrl, ctx->argv[2]); + ovsrec_bridge_set_controller(br->br_cfg, ctrl); + } + + free_info(&info); +} + +static void +cmd_get_fail_mode(struct vsctl_context *ctx) +{ + struct vsctl_info info; + const char *fail_mode = NULL; + + get_info(ctx->ovs, &info); + + if (ctx->argc == 1) { + /* Return the fail-mode from the "Open_vSwitch" table */ + if (info.ctrl && info.ctrl->fail_mode) { + fail_mode = info.ctrl->fail_mode; + } + } else { + /* Return the fail-mode for a particular bridge. */ + struct vsctl_bridge *br = find_bridge(&info, ctx->argv[1], true); + + /* If no controller or fail-mode is explicitly defined for the + * requested bridge, fallback to the "Open_vSwitch" table's + * setting. */ + if (br->ctrl && br->ctrl->fail_mode) { + fail_mode = br->ctrl->fail_mode; + } else if (info.ctrl && info.ctrl->fail_mode) { + fail_mode = info.ctrl->fail_mode; + } + } + + if (fail_mode && strlen(fail_mode)) { + ds_put_format(&ctx->output, "%s\n", fail_mode); + } + + free_info(&info); +} + +static void +cmd_del_fail_mode(struct vsctl_context *ctx) +{ + struct vsctl_info info; + + get_info(ctx->ovs, &info); + + if (ctx->argc == 1) { + if (info.ctrl && info.ctrl->fail_mode) { + ovsrec_controller_set_fail_mode(info.ctrl, NULL); + } + } else { + struct vsctl_bridge *br = find_real_bridge(&info, ctx->argv[1], true); + + if (br->ctrl && br->ctrl->fail_mode) { + ovsrec_controller_set_fail_mode(br->ctrl, NULL); + } + } + + free_info(&info); +} + +static void +cmd_set_fail_mode(struct vsctl_context *ctx) +{ + struct vsctl_info info; + const char *fail_mode; + + get_info(ctx->ovs, &info); + + fail_mode = (ctx->argc == 2) ? ctx->argv[1] : ctx->argv[2]; + + if (strcmp(fail_mode, "standalone") && strcmp(fail_mode, "secure")) { + vsctl_fatal("fail-mode must be \"standalone\" or \"secure\""); + } + + if (ctx->argc == 2) { + /* Set the fail-mode in the "Open_vSwitch" table. */ + if (!info.ctrl) { + vsctl_fatal("no controller declared"); + } + ovsrec_controller_set_fail_mode(info.ctrl, fail_mode); + } else { + struct vsctl_bridge *br = find_real_bridge(&info, ctx->argv[1], true); + + if (!br->ctrl) { + vsctl_fatal("no controller declared for %s", br->name); + } + ovsrec_controller_set_fail_mode(br->ctrl, fail_mode); + } + + free_info(&info); +} + +static void +cmd_get_ssl(struct vsctl_context *ctx) +{ + struct ovsrec_ssl *ssl = ctx->ovs->ssl; + + if (ssl) { + ds_put_format(&ctx->output, "Private key: %s\n", ssl->private_key); + ds_put_format(&ctx->output, "Certificate: %s\n", ssl->certificate); + ds_put_format(&ctx->output, "CA Certificate: %s\n", ssl->ca_cert); + ds_put_format(&ctx->output, "Bootstrap: %s\n", + ssl->bootstrap_ca_cert ? "true" : "false"); + } +} + +static void +cmd_del_ssl(struct vsctl_context *ctx) +{ + struct ovsrec_ssl *ssl = ctx->ovs->ssl; + + if (ssl) { + ovsrec_ssl_delete(ssl); + ovsrec_open_vswitch_set_ssl(ctx->ovs, NULL); + } +} + +static void +cmd_set_ssl(struct vsctl_context *ctx) +{ + bool bootstrap = shash_find(&ctx->options, "--bootstrap"); + struct ovsrec_ssl *ssl = ctx->ovs->ssl; + + if (ssl) { + ovsrec_ssl_delete(ssl); + } + ssl = ovsrec_ssl_insert(ctx->txn); + + ovsrec_ssl_set_private_key(ssl, ctx->argv[1]); + ovsrec_ssl_set_certificate(ssl, ctx->argv[2]); + ovsrec_ssl_set_ca_cert(ssl, ctx->argv[3]); + + ovsrec_ssl_set_bootstrap_ca_cert(ssl, bootstrap); + + ovsrec_open_vswitch_set_ssl(ctx->ovs, ssl); +} + +/* Parameter commands. */ + +struct vsctl_row_id { + const struct ovsdb_idl_table_class *table; + const struct ovsdb_idl_column *name_column; + const struct ovsdb_idl_column *uuid_column; +}; + +struct vsctl_table_class { + struct ovsdb_idl_table_class *class; + struct vsctl_row_id row_ids[2]; +}; + +static const struct vsctl_table_class tables[] = { + {&ovsrec_table_bridge, + {{&ovsrec_table_bridge, &ovsrec_bridge_col_name, NULL}, + {NULL, NULL, NULL}}}, + + {&ovsrec_table_controller, + {{&ovsrec_table_bridge, + &ovsrec_bridge_col_name, + &ovsrec_bridge_col_controller}, + {&ovsrec_table_open_vswitch, + NULL, + &ovsrec_open_vswitch_col_controller}}}, + + {&ovsrec_table_interface, + {{&ovsrec_table_interface, &ovsrec_interface_col_name, NULL}, + {NULL, NULL, NULL}}}, + + {&ovsrec_table_mirror, + {{&ovsrec_table_mirror, &ovsrec_mirror_col_name, NULL}, + {NULL, NULL, NULL}}}, + + {&ovsrec_table_netflow, + {{&ovsrec_table_bridge, + &ovsrec_bridge_col_name, + &ovsrec_bridge_col_netflow}, + {NULL, NULL, NULL}}}, + + {&ovsrec_table_open_vswitch, + {{&ovsrec_table_open_vswitch, NULL, NULL}, + {NULL, NULL, NULL}}}, + + {&ovsrec_table_port, + {{&ovsrec_table_port, &ovsrec_port_col_name, NULL}, + {NULL, NULL, NULL}}}, + + {&ovsrec_table_ssl, + {{&ovsrec_table_open_vswitch, NULL, &ovsrec_open_vswitch_col_ssl}}}, + + {NULL, {{NULL, NULL, NULL}, {NULL, NULL, NULL}}} +}; + +static void +die_if_error(char *error) +{ + if (error) { + vsctl_fatal("%s", error); + } +} + +static int +to_lower_and_underscores(unsigned c) +{ + return c == '-' ? '_' : tolower(c); +} + +static unsigned int +score_partial_match(const char *name, const char *s) +{ + int score; + + if (!strcmp(name, s)) { + return UINT_MAX; + } + for (score = 0; ; score++, name++, s++) { + if (to_lower_and_underscores(*name) != to_lower_and_underscores(*s)) { + break; + } else if (*name == '\0') { + return UINT_MAX - 1; + } + } + return *s == '\0' ? score : 0; +} + +static const struct vsctl_table_class * +get_table(const char *table_name) +{ + const struct vsctl_table_class *table; + const struct vsctl_table_class *best_match = NULL; + unsigned int best_score = 0; + + for (table = tables; table->class; table++) { + unsigned int score = score_partial_match(table->class->name, + table_name); + if (score > best_score) { + best_match = table; + best_score = score; + } else if (score == best_score) { + best_match = NULL; + } + } + if (best_match) { + return best_match; + } else if (best_score) { + vsctl_fatal("multiple table names match \"%s\"", table_name); + } else { + vsctl_fatal("unknown table \"%s\"", table_name); + } +} + +static const struct ovsdb_idl_row * +get_row_by_id(struct vsctl_context *ctx, const struct vsctl_table_class *table, + const struct vsctl_row_id *id, const char *record_id) +{ + const struct ovsdb_idl_row *referrer, *final; + + if (!id->table) { + return NULL; + } + + if (!id->name_column) { + if (strcmp(record_id, ".")) { + return NULL; + } + referrer = ovsdb_idl_first_row(ctx->idl, id->table); + if (!referrer || ovsdb_idl_next_row(referrer)) { + return NULL; + } + } else { + const struct ovsdb_idl_row *row; + unsigned int best_score = 0; + + /* It might make sense to relax this assertion. */ + assert(id->name_column->type.key.type == OVSDB_TYPE_STRING); + + referrer = NULL; + for (row = ovsdb_idl_first_row(ctx->idl, id->table); + row != NULL && best_score != UINT_MAX; + row = ovsdb_idl_next_row(row)) + { + struct ovsdb_datum name; + + ovsdb_idl_txn_read(row, id->name_column, &name); + if (name.n == 1) { + unsigned int score = score_partial_match(name.keys[0].string, + record_id); + if (score > best_score) { + referrer = row; + best_score = score; + } else if (score == best_score) { + referrer = NULL; + } + } + ovsdb_datum_destroy(&name, &id->name_column->type); + } + if (best_score && !referrer) { + vsctl_fatal("multiple rows in %s match \"%s\"", + table->class->name, record_id); + } + } + if (!referrer) { + return NULL; + } + + final = NULL; + if (id->uuid_column) { + struct ovsdb_datum uuid; + + assert(id->uuid_column->type.key.type == OVSDB_TYPE_UUID); + assert(id->uuid_column->type.value.type == OVSDB_TYPE_VOID); + + ovsdb_idl_txn_read(referrer, id->uuid_column, &uuid); + if (uuid.n == 1) { + final = ovsdb_idl_get_row_for_uuid(ctx->idl, table->class, + &uuid.keys[0].uuid); + } + ovsdb_datum_destroy(&uuid, &id->uuid_column->type); + } else { + final = referrer; + } + + return final; +} + +static const struct ovsdb_idl_row * +get_row(struct vsctl_context *ctx, + const struct vsctl_table_class *table, const char *record_id) +{ + const struct ovsdb_idl_row *row; + struct uuid uuid; + + if (uuid_from_string(&uuid, record_id)) { + row = ovsdb_idl_get_row_for_uuid(ctx->idl, table->class, &uuid); + } else { + int i; + + for (i = 0; i < ARRAY_SIZE(table->row_ids); i++) { + row = get_row_by_id(ctx, table, &table->row_ids[i], record_id); + if (row) { + break; + } + } + } + return row; +} + +static const struct ovsdb_idl_row * +must_get_row(struct vsctl_context *ctx, + const struct vsctl_table_class *table, const char *record_id) +{ + const struct ovsdb_idl_row *row = get_row(ctx, table, record_id); + if (!row) { + vsctl_fatal("no row \"%s\" in table %s", + record_id, table->class->name); + } + return row; +} + +static char * +get_column(const struct vsctl_table_class *table, const char *column_name, + const struct ovsdb_idl_column **columnp) +{ + const struct ovsdb_idl_column *best_match = NULL; + unsigned int best_score = 0; + size_t i; + + for (i = 0; i < table->class->n_columns; i++) { + const struct ovsdb_idl_column *column = &table->class->columns[i]; + unsigned int score = score_partial_match(column->name, column_name); + if (score > best_score) { + best_match = column; + best_score = score; + } else if (score == best_score) { + best_match = NULL; + } + } + + *columnp = best_match; + if (best_match) { + return NULL; + } else if (best_score) { + return xasprintf("%s contains more than one column whose name " + "matches \"%s\"", table->class->name, column_name); + } else { + return xasprintf("%s does not contain a column whose name matches " + "\"%s\"", table->class->name, column_name); + } +} + +static char * WARN_UNUSED_RESULT +parse_column_key_value(const char *arg, const struct vsctl_table_class *table, + const struct ovsdb_idl_column **columnp, + char **keyp, char **valuep) +{ + const char *p = arg; + char *error; + + assert(columnp || keyp); + if (keyp) { + *keyp = NULL; + } + if (valuep) { + *valuep = NULL; + } + + /* Parse column name. */ + if (columnp) { + char *column_name; + + error = ovsdb_token_parse(&p, &column_name); + if (error) { + goto error; + } + if (column_name[0] == '\0') { + free(column_name); + error = xasprintf("%s: missing column name", arg); + goto error; + } + error = get_column(table, column_name, columnp); + free(column_name); + if (error) { + goto error; + } + } + + /* Parse key string. */ + if (*p == ':' || !columnp) { + if (columnp) { + p++; + } else if (!keyp) { + error = xasprintf("%s: key not accepted here", arg); + goto error; + } + error = ovsdb_token_parse(&p, keyp); + if (error) { + goto error; + } + } else if (keyp) { + *keyp = NULL; + } + + /* Parse value string. */ + if (*p == '=') { + if (!valuep) { + error = xasprintf("%s: value not accepted here", arg); + goto error; + } + *valuep = xstrdup(p + 1); + } else { + if (valuep) { + *valuep = NULL; + } + if (*p != '\0') { + error = xasprintf("%s: trailing garbage \"%s\" in argument", + arg, p); + goto error; + } + } + return NULL; + +error: + if (columnp) { + *columnp = NULL; + } + if (keyp) { + free(*keyp); + *keyp = NULL; + } + if (valuep) { + free(*valuep); + *valuep = NULL; + } + return error; +} + +static void +cmd_get(struct vsctl_context *ctx) +{ + bool if_exists = shash_find(&ctx->options, "--if-exists"); + const char *table_name = ctx->argv[1]; + const char *record_id = ctx->argv[2]; + const struct vsctl_table_class *table; + const struct ovsdb_idl_row *row; + struct ds *out = &ctx->output; + int i; + + table = get_table(table_name); + row = must_get_row(ctx, table, record_id); + for (i = 3; i < ctx->argc; i++) { + const struct ovsdb_idl_column *column; + struct ovsdb_datum datum; + char *key_string; + + die_if_error(parse_column_key_value(ctx->argv[i], table, + &column, &key_string, NULL)); + + ovsdb_idl_txn_read(row, column, &datum); + if (key_string) { + union ovsdb_atom key; + unsigned int idx; + + if (column->type.value.type == OVSDB_TYPE_VOID) { + vsctl_fatal("cannot specify key to get for non-map column %s", + column->name); + } + + die_if_error(ovsdb_atom_from_string(&key, + &column->type.key, + key_string)); + + idx = ovsdb_datum_find_key(&datum, &key, + column->type.key.type); + if (idx == UINT_MAX) { + if (!if_exists) { + vsctl_fatal("no key \"%s\" in %s record \"%s\" column %s", + key_string, table->class->name, record_id, + column->name); + } + } else { + ovsdb_atom_to_string(&datum.values[idx], + column->type.value.type, out); + } + ovsdb_atom_destroy(&key, column->type.key.type); + } else { + ovsdb_datum_to_string(&datum, &column->type, out); + } + ds_put_char(out, '\n'); + ovsdb_datum_destroy(&datum, &column->type); + + free(key_string); + } +} + +static void +list_record(const struct vsctl_table_class *table, + const struct ovsdb_idl_row *row, struct ds *out) +{ + size_t i; + + ds_put_format(out, "%-20s: "UUID_FMT"\n", "_uuid", + UUID_ARGS(&row->uuid)); + for (i = 0; i < table->class->n_columns; i++) { + const struct ovsdb_idl_column *column = &table->class->columns[i]; + struct ovsdb_datum datum; + + ovsdb_idl_txn_read(row, column, &datum); + + ds_put_format(out, "%-20s: ", column->name); + ovsdb_datum_to_string(&datum, &column->type, out); + ds_put_char(out, '\n'); + + ovsdb_datum_destroy(&datum, &column->type); + } +} + +static void +cmd_list(struct vsctl_context *ctx) +{ + const char *table_name = ctx->argv[1]; + const struct vsctl_table_class *table; + struct ds *out = &ctx->output; + int i; + + table = get_table(table_name); + if (ctx->argc > 2) { + for (i = 2; i < ctx->argc; i++) { + if (i > 2) { + ds_put_char(out, '\n'); + } + list_record(table, must_get_row(ctx, table, ctx->argv[i]), out); + } + } else { + const struct ovsdb_idl_row *row; + bool first; + + for (row = ovsdb_idl_first_row(ctx->idl, table->class), first = true; + row != NULL; + row = ovsdb_idl_next_row(row), first = false) { + if (!first) { + ds_put_char(out, '\n'); + } + list_record(table, row, out); + } + } +} + +static void +set_column(const struct vsctl_table_class *table, + const struct ovsdb_idl_row *row, const char *arg) +{ + const struct ovsdb_idl_column *column; + char *key_string, *value_string; + char *error; + + error = parse_column_key_value(arg, table, &column, &key_string, + &value_string); + die_if_error(error); + if (!value_string) { + vsctl_fatal("%s: missing value", arg); + } + + if (key_string) { + union ovsdb_atom key, value; + struct ovsdb_datum old, new; + + if (column->type.value.type == OVSDB_TYPE_VOID) { + vsctl_fatal("cannot specify key to set for non-map column %s", + column->name); + } + + die_if_error(ovsdb_atom_from_string(&key, &column->type.key, + key_string)); + die_if_error(ovsdb_atom_from_string(&value, &column->type.value, + value_string)); + + ovsdb_datum_init_empty(&new); + ovsdb_datum_add_unsafe(&new, &key, &value, &column->type); + + ovsdb_atom_destroy(&key, column->type.key.type); + ovsdb_atom_destroy(&value, column->type.value.type); + + ovsdb_idl_txn_read(row, column, &old); + ovsdb_datum_union(&old, &new, &column->type, true); + ovsdb_idl_txn_write(row, column, &old); + + ovsdb_datum_destroy(&new, &column->type); + } else { + struct ovsdb_datum datum; + + die_if_error(ovsdb_datum_from_string(&datum, &column->type, + value_string)); + ovsdb_idl_txn_write(row, column, &datum); + } + + free(key_string); + free(value_string); +} + +static void +cmd_set(struct vsctl_context *ctx) +{ + const char *table_name = ctx->argv[1]; + const char *record_id = ctx->argv[2]; + const struct vsctl_table_class *table; + const struct ovsdb_idl_row *row; + int i; + + table = get_table(table_name); + row = must_get_row(ctx, table, record_id); + for (i = 3; i < ctx->argc; i++) { + set_column(table, row, ctx->argv[i]); + } +} + +static void +cmd_add(struct vsctl_context *ctx) +{ + const char *table_name = ctx->argv[1]; + const char *record_id = ctx->argv[2]; + const char *column_name = ctx->argv[3]; + const struct vsctl_table_class *table; + const struct ovsdb_idl_column *column; + const struct ovsdb_idl_row *row; + const struct ovsdb_type *type; + struct ovsdb_datum old; + int i; + + table = get_table(table_name); + row = must_get_row(ctx, table, record_id); + die_if_error(get_column(table, column_name, &column)); + + type = &column->type; + ovsdb_idl_txn_read(row, column, &old); + for (i = 4; i < ctx->argc; i++) { + struct ovsdb_type add_type; + struct ovsdb_datum add; + + add_type = *type; + add_type.n_min = 1; + add_type.n_max = UINT_MAX; + die_if_error(ovsdb_datum_from_string(&add, &add_type, ctx->argv[i])); + ovsdb_datum_union(&old, &add, type, false); + ovsdb_datum_destroy(&add, type); + } + if (old.n > type->n_max) { + vsctl_fatal("\"add\" operation would put %u %s in column %s of " + "table %s but the maximum number is %u", + old.n, + type->value.type == OVSDB_TYPE_VOID ? "values" : "pairs", + column->name, table->class->name, type->n_max); + } + ovsdb_idl_txn_write(row, column, &old); +} + +static void +cmd_remove(struct vsctl_context *ctx) +{ + const char *table_name = ctx->argv[1]; + const char *record_id = ctx->argv[2]; + const char *column_name = ctx->argv[3]; + const struct vsctl_table_class *table; + const struct ovsdb_idl_column *column; + const struct ovsdb_idl_row *row; + const struct ovsdb_type *type; + struct ovsdb_datum old; + int i; + + table = get_table(table_name); + row = must_get_row(ctx, table, record_id); + die_if_error(get_column(table, column_name, &column)); + + type = &column->type; + ovsdb_idl_txn_read(row, column, &old); + for (i = 4; i < ctx->argc; i++) { + struct ovsdb_type rm_type; + struct ovsdb_datum rm; + char *error; + + rm_type = *type; + rm_type.n_min = 1; + rm_type.n_max = UINT_MAX; + error = ovsdb_datum_from_string(&rm, &rm_type, ctx->argv[i]); + if (error && ovsdb_type_is_map(&rm_type)) { + free(error); + rm_type.value.type = OVSDB_TYPE_VOID; + die_if_error(ovsdb_datum_from_string(&rm, &rm_type, ctx->argv[i])); + } + ovsdb_datum_subtract(&old, type, &rm, &rm_type); + ovsdb_datum_destroy(&rm, &rm_type); + } + if (old.n < type->n_min) { + vsctl_fatal("\"remove\" operation would put %u %s in column %s of " + "table %s but the minimun number is %u", + old.n, + type->value.type == OVSDB_TYPE_VOID ? "values" : "pairs", + column->name, table->class->name, type->n_min); + } + ovsdb_idl_txn_write(row, column, &old); +} + +static void +cmd_clear(struct vsctl_context *ctx) +{ + const char *table_name = ctx->argv[1]; + const char *record_id = ctx->argv[2]; + const struct vsctl_table_class *table; + const struct ovsdb_idl_row *row; + int i; + + table = get_table(table_name); + row = must_get_row(ctx, table, record_id); + for (i = 3; i < ctx->argc; i++) { + const struct ovsdb_idl_column *column; + const struct ovsdb_type *type; + struct ovsdb_datum datum; + + die_if_error(get_column(table, ctx->argv[i], &column)); + + type = &column->type; + if (type->n_min > 0) { + vsctl_fatal("\"clear\" operation cannot be applied to column %s " + "of table %s, which is not allowed to be empty", + column->name, table->class->name); + } + + ovsdb_datum_init_empty(&datum); + ovsdb_idl_txn_write(row, column, &datum); + } +} + +static void +cmd_create(struct vsctl_context *ctx) +{ + const char *table_name = ctx->argv[1]; + const struct vsctl_table_class *table; + const struct ovsdb_idl_row *row; + int i; + + table = get_table(table_name); + row = ovsdb_idl_txn_insert(ctx->txn, table->class); + for (i = 2; i < ctx->argc; i++) { + set_column(table, row, ctx->argv[i]); + } + ds_put_format(&ctx->output, UUID_FMT, UUID_ARGS(&row->uuid)); +} + +/* This function may be used as the 'postprocess' function for commands that + * insert new rows into the database. It expects that the command's 'run' + * function prints the UUID reported by ovsdb_idl_txn_insert() as the command's + * sole output. It replaces that output by the row's permanent UUID assigned + * by the database server and appends a new-line. + * + * Currently we use this only for "create", because the higher-level commands + * are supposed to be independent of the actual structure of the vswitch + * configuration. */ +static void +post_create(struct vsctl_context *ctx) +{ + const struct uuid *real; + struct uuid dummy; + + uuid_from_string(&dummy, ds_cstr(&ctx->output)); + real = ovsdb_idl_txn_get_insert_uuid(ctx->txn, &dummy); + if (real) { + ds_clear(&ctx->output); + ds_put_format(&ctx->output, UUID_FMT, UUID_ARGS(real)); + } + ds_put_char(&ctx->output, '\n'); +} + +static void +cmd_destroy(struct vsctl_context *ctx) +{ + bool must_exist = !shash_find(&ctx->options, "--if-exists"); + const char *table_name = ctx->argv[1]; + const struct vsctl_table_class *table; + int i; + + table = get_table(table_name); + for (i = 2; i < ctx->argc; i++) { + const struct ovsdb_idl_row *row; + + row = (must_exist ? must_get_row : get_row)(ctx, table, ctx->argv[i]); + if (row) { + ovsdb_idl_txn_delete(row); + } + } +} + +static struct json * +where_uuid_equals(const struct uuid *uuid) +{ + return + json_array_create_1( + json_array_create_3( + json_string_create("_uuid"), + json_string_create("=="), + json_array_create_2( + json_string_create("uuid"), + json_string_create_nocopy( + xasprintf(UUID_FMT, UUID_ARGS(uuid)))))); +} + +static void +vsctl_context_init(struct vsctl_context *ctx, struct vsctl_command *command, + struct ovsdb_idl *idl, struct ovsdb_idl_txn *txn, + const struct ovsrec_open_vswitch *ovs) +{ + ctx->argc = command->argc; + ctx->argv = command->argv; + ctx->options = command->options; + + ds_swap(&ctx->output, &command->output); + ctx->idl = idl; + ctx->txn = txn; + ctx->ovs = ovs; + +} + +static void +vsctl_context_done(struct vsctl_context *ctx, struct vsctl_command *command) +{ + ds_swap(&ctx->output, &command->output); +} + +static void +do_vsctl(const char *args, struct vsctl_command *commands, size_t n_commands, + struct ovsdb_idl *idl) +{ + struct ovsdb_idl_txn *txn; + const struct ovsrec_open_vswitch *ovs; + enum ovsdb_idl_txn_status status; + struct vsctl_command *c; + int64_t next_cfg = 0; + char *comment; + char *error; + + txn = the_idl_txn = ovsdb_idl_txn_create(idl); + if (dry_run) { + ovsdb_idl_txn_set_dry_run(txn); + } + + comment = xasprintf("ovs-vsctl: %s", args); + ovsdb_idl_txn_add_comment(txn, comment); + free(comment); + + ovs = ovsrec_open_vswitch_first(idl); + if (!ovs) { + /* XXX add verification that table is empty */ + ovs = ovsrec_open_vswitch_insert(txn); + } + + if (wait_for_reload) { + struct json *where = where_uuid_equals(&ovs->header_.uuid); + ovsdb_idl_txn_increment(txn, "Open_vSwitch", "next_cfg", where); + json_destroy(where); + } + + for (c = commands; c < &commands[n_commands]; c++) { + struct vsctl_context ctx; + + ds_init(&c->output); + vsctl_context_init(&ctx, c, idl, txn, ovs); + (c->syntax->run)(&ctx); + vsctl_context_done(&ctx, c); + } + + while ((status = ovsdb_idl_txn_commit(txn)) == TXN_INCOMPLETE) { + ovsdb_idl_run(idl); + ovsdb_idl_wait(idl); + ovsdb_idl_txn_wait(txn); + poll_block(); + } + if (wait_for_reload && status == TXN_SUCCESS) { + next_cfg = ovsdb_idl_txn_get_increment_new_value(txn); + } + for (c = commands; c < &commands[n_commands]; c++) { + if (c->syntax->postprocess) { + struct vsctl_context ctx; + + vsctl_context_init(&ctx, c, idl, txn, ovs); + (c->syntax->postprocess)(&ctx); + vsctl_context_done(&ctx, c); + } + } + error = xstrdup(ovsdb_idl_txn_get_error(txn)); + ovsdb_idl_txn_destroy(txn); + the_idl_txn = NULL; + + switch (status) { + case TXN_INCOMPLETE: + NOT_REACHED(); + + case TXN_ABORTED: + /* Should not happen--we never call ovsdb_idl_txn_abort(). */ + vsctl_fatal("transaction aborted"); + + case TXN_UNCHANGED: + case TXN_SUCCESS: + break; + + case TXN_TRY_AGAIN: + for (c = commands; c < &commands[n_commands]; c++) { + ds_destroy(&c->output); + } + free(error); + return; + + case TXN_ERROR: + vsctl_fatal("transaction error: %s", error); + + default: + NOT_REACHED(); + } + free(error); + + for (c = commands; c < &commands[n_commands]; c++) { + struct ds *ds = &c->output; + if (oneline) { + size_t j; + + ds_chomp(ds, '\n'); + for (j = 0; j < ds->length; j++) { + int c = ds->string[j]; + switch (c) { + case '\n': + fputs("\\n", stdout); + break; + + case '\\': + fputs("\\\\", stdout); + break; + + default: + putchar(c); + } + } + putchar('\n'); + } else { + fputs(ds_cstr(ds), stdout); + } + ds_destroy(&c->output); + shash_destroy(&c->options); + } + free(commands); + + if (wait_for_reload && status != TXN_UNCHANGED) { + for (;;) { + const struct ovsrec_open_vswitch *ovs; + + ovsdb_idl_run(idl); + OVSREC_OPEN_VSWITCH_FOR_EACH (ovs, idl) { + if (ovs->cur_cfg >= next_cfg) { + goto done; + } + } + ovsdb_idl_wait(idl); + poll_block(); + } + done: ; + } + ovsdb_idl_destroy(idl); + + exit(EXIT_SUCCESS); +} + +static const struct vsctl_command_syntax all_commands[] = { + /* Open vSwitch commands. */ + {"init", 0, 0, cmd_init, NULL, ""}, + + /* Bridge commands. */ + {"add-br", 1, 3, cmd_add_br, NULL, "--may-exist"}, + {"del-br", 1, 1, cmd_del_br, NULL, "--if-exists"}, + {"list-br", 0, 0, cmd_list_br, NULL, ""}, + {"br-exists", 1, 1, cmd_br_exists, NULL, ""}, + {"br-to-vlan", 1, 1, cmd_br_to_vlan, NULL, ""}, + {"br-to-parent", 1, 1, cmd_br_to_parent, NULL, ""}, + {"br-set-external-id", 2, 3, cmd_br_set_external_id, NULL, ""}, + {"br-get-external-id", 1, 2, cmd_br_get_external_id, NULL, ""}, + + /* Port commands. */ + {"list-ports", 1, 1, cmd_list_ports, NULL, ""}, + {"add-port", 2, 2, cmd_add_port, NULL, "--may-exist"}, + {"add-bond", 4, INT_MAX, cmd_add_bond, NULL, "--may-exist,--fake-iface"}, + {"del-port", 1, 2, cmd_del_port, NULL, "--if-exists"}, + {"port-to-br", 1, 1, cmd_port_to_br, NULL, ""}, + + /* Interface commands. */ + {"list-ifaces", 1, 1, cmd_list_ifaces, NULL, ""}, + {"iface-to-br", 1, 1, cmd_iface_to_br, NULL, ""}, + + /* Controller commands. */ + {"get-controller", 0, 1, cmd_get_controller, NULL, ""}, + {"del-controller", 0, 1, cmd_del_controller, NULL, ""}, + {"set-controller", 1, 2, cmd_set_controller, NULL, ""}, + {"get-fail-mode", 0, 1, cmd_get_fail_mode, NULL, ""}, + {"del-fail-mode", 0, 1, cmd_del_fail_mode, NULL, ""}, + {"set-fail-mode", 1, 2, cmd_set_fail_mode, NULL, ""}, + + /* SSL commands. */ + {"get-ssl", 0, 0, cmd_get_ssl, NULL, ""}, + {"del-ssl", 0, 0, cmd_del_ssl, NULL, ""}, + {"set-ssl", 3, 3, cmd_set_ssl, NULL, "--bootstrap"}, + + /* Parameter commands. */ + {"get", 3, INT_MAX, cmd_get, NULL, "--if-exists"}, + {"list", 1, INT_MAX, cmd_list, NULL, ""}, + {"set", 3, INT_MAX, cmd_set, NULL, ""}, + {"add", 4, INT_MAX, cmd_add, NULL, ""}, + {"remove", 4, INT_MAX, cmd_remove, NULL, ""}, + {"clear", 3, INT_MAX, cmd_clear, NULL, ""}, + {"create", 2, INT_MAX, cmd_create, post_create, ""}, + {"destroy", 1, INT_MAX, cmd_destroy, NULL, "--if-exists"}, + + {NULL, 0, 0, NULL, NULL, NULL}, +}; + diff --combined vswitchd/bridge.c index f097e183c,9152062b4..c2e89eb83 --- a/vswitchd/bridge.c +++ b/vswitchd/bridge.c @@@ -30,6 -30,7 +30,6 @@@ #include #include #include "bitmap.h" -#include "cfg.h" #include "coverage.h" #include "dirs.h" #include "dpif.h" @@@ -49,16 -50,15 +49,16 @@@ #include "port-array.h" #include "proc-net-compat.h" #include "process.h" +#include "sha1.h" #include "shash.h" #include "socket-util.h" -#include "stp.h" +#include "stream-ssl.h" #include "svec.h" #include "timeval.h" #include "util.h" #include "unixctl.h" #include "vconn.h" -#include "vconn-ssl.h" +#include "vswitchd/vswitch-idl.h" #include "xenserver.h" #include "xtoxll.h" #include "sflow_api.h" @@@ -71,6 -71,8 +71,6 @@@ struct dst uint16_t dp_ifidx; }; -extern uint64_t mgmt_id; - struct iface { /* These members are always valid. */ struct port *port; /* Containing port. */ @@@ -84,9 -86,6 +84,9 @@@ int dp_ifidx; /* Index within kernel datapath. */ struct netdev *netdev; /* Network device. */ bool enabled; /* May be chosen for flows? */ + + /* This member is only valid *during* bridge_reconfigure(). */ + const struct ovsrec_interface *cfg; }; #define BOND_MASK 0xff @@@ -106,8 -105,8 +106,8 @@@ struct mirror char *name; /* Selection criteria. */ - struct svec src_ports; - struct svec dst_ports; + struct shash src_ports; /* Name is port name; data is always NULL. */ + struct shash dst_ports; /* Name is port name; data is always NULL. */ int *vlans; size_t n_vlans; @@@ -136,15 -135,15 +136,15 @@@ struct port tag_type no_ifaces_tag; /* Tag for flows when all ifaces disabled. */ int updelay, downdelay; /* Delay before iface goes up/down, in ms. */ bool bond_compat_is_stale; /* Need to call port_update_bond_compat()? */ + bool bond_fake_iface; /* Fake a bond interface for legacy compat? */ /* Port mirroring info. */ mirror_mask_t src_mirrors; /* Mirrors triggered when packet received. */ mirror_mask_t dst_mirrors; /* Mirrors triggered when packet sent. */ bool is_mirror_output_port; /* Does port mirroring send frames here? */ - /* Spanning tree info. */ - enum stp_state stp_state; /* Always STP_FORWARDING if STP not in use. */ - tag_type stp_state_tag; /* Tag for STP state change. */ + /* This member is only valid *during* bridge_reconfigure(). */ + const struct ovsrec_port *cfg; }; #define DP_MAX_PORTS 255 @@@ -184,8 -183,9 +184,8 @@@ struct bridge /* Port mirroring. */ struct mirror *mirrors[MAX_MIRRORS]; - /* Spanning tree. */ - struct stp *stp; - long long int stp_last_tick; + /* This member is only valid *during* bridge_reconfigure(). */ + const struct ovsrec_bridge *cfg; }; /* List of all bridges. */ @@@ -194,19 -194,14 +194,19 @@@ static struct list all_bridges = LIST_I /* Maximum number of datapaths. */ enum { DP_MAX = 256 }; -static struct bridge *bridge_create(const char *name); +static struct bridge *bridge_create(const struct ovsrec_bridge *br_cfg); static void bridge_destroy(struct bridge *); static struct bridge *bridge_lookup(const char *name); -static void bridge_unixctl_dump_flows(struct unixctl_conn *, const char *); +static unixctl_cb_func bridge_unixctl_dump_flows; static int bridge_run_one(struct bridge *); -static void bridge_reconfigure_one(struct bridge *); -static void bridge_reconfigure_controller(struct bridge *); -static void bridge_get_all_ifaces(const struct bridge *, struct svec *ifaces); +static const struct ovsrec_controller *bridge_get_controller( + const struct ovsrec_open_vswitch *ovs_cfg, + const struct bridge *br); +static void bridge_reconfigure_one(const struct ovsrec_open_vswitch *, + struct bridge *); +static void bridge_reconfigure_controller(const struct ovsrec_open_vswitch *, + struct bridge *); +static void bridge_get_all_ifaces(const struct bridge *, struct shash *ifaces); static void bridge_fetch_dp_ifaces(struct bridge *); static void bridge_flush(struct bridge *); static void bridge_pick_local_hw_addr(struct bridge *, @@@ -216,9 -211,10 +216,9 @@@ static uint64_t bridge_pick_datapath_id const uint8_t bridge_ea[ETH_ADDR_LEN], struct iface *hw_addr_iface); static struct iface *bridge_get_local_iface(struct bridge *); -static const char *bridge_get_controller(const struct bridge *br); static uint64_t dpid_from_hash(const void *, size_t nbytes); -static void bridge_unixctl_fdb_show(struct unixctl_conn *, const char *args); +static unixctl_cb_func bridge_unixctl_fdb_show; static void bond_init(void); static void bond_run(struct bridge *); @@@ -227,8 -223,8 +227,8 @@@ static void bond_rebalance_port(struct static void bond_send_learning_packets(struct port *); static void bond_enable_slave(struct iface *iface, bool enable); -static void port_create(struct bridge *, const char *name); -static void port_reconfigure(struct port *); +static struct port *port_create(struct bridge *, const char *name); +static void port_reconfigure(struct port *, const struct ovsrec_port *); static void port_destroy(struct port *); static struct port *port_lookup(const struct bridge *, const char *name); static struct iface *port_lookup_iface(const struct port *, const char *name); @@@ -238,14 -234,18 +238,14 @@@ static void port_update_bond_compat(str static void port_update_vlan_compat(struct port *); static void port_update_bonding(struct port *); -static void mirror_create(struct bridge *, const char *name); +static struct mirror *mirror_create(struct bridge *, const char *name); static void mirror_destroy(struct mirror *); static void mirror_reconfigure(struct bridge *); -static void mirror_reconfigure_one(struct mirror *); +static void mirror_reconfigure_one(struct mirror *, struct ovsrec_mirror *); static bool vlan_is_mirrored(const struct mirror *, int vlan); -static void brstp_reconfigure(struct bridge *); -static void brstp_adjust_timers(struct bridge *); -static void brstp_run(struct bridge *); -static void brstp_wait(struct bridge *); - -static void iface_create(struct port *, const char *name); +static struct iface *iface_create(struct port *port, + const struct ovsrec_interface *if_cfg); static void iface_destroy(struct iface *); static struct iface *iface_lookup(const struct bridge *, const char *name); static struct iface *iface_from_dp_ifidx(const struct bridge *, @@@ -285,65 -285,53 +285,65 @@@ bridge_get_ifaces(struct svec *svec } } -/* The caller must already have called cfg_read(). */ void -bridge_init(void) +bridge_init(const struct ovsrec_open_vswitch *cfg) { - struct svec dpif_names; + struct svec bridge_names; + struct svec dpif_names, dpif_types; size_t i; - unixctl_command_register("fdb/show", bridge_unixctl_fdb_show); + unixctl_command_register("fdb/show", bridge_unixctl_fdb_show, NULL); + + svec_init(&bridge_names); + for (i = 0; i < cfg->n_bridges; i++) { + svec_add(&bridge_names, cfg->bridges[i]->name); + } + svec_sort(&bridge_names); svec_init(&dpif_names); - dp_enumerate(&dpif_names); - for (i = 0; i < dpif_names.n; i++) { - const char *dpif_name = dpif_names.names[i]; + svec_init(&dpif_types); + dp_enumerate_types(&dpif_types); + for (i = 0; i < dpif_types.n; i++) { struct dpif *dpif; int retval; + size_t j; - retval = dpif_open(dpif_name, &dpif); - if (!retval) { - struct svec all_names; - size_t j; + dp_enumerate_names(dpif_types.names[i], &dpif_names); - svec_init(&all_names); - dpif_get_all_names(dpif, &all_names); - for (j = 0; j < all_names.n; j++) { - if (cfg_has("bridge.%s.port", all_names.names[j])) { - goto found; + for (j = 0; j < dpif_names.n; j++) { + retval = dpif_open(dpif_names.names[j], dpif_types.names[i], &dpif); + if (!retval) { + struct svec all_names; + size_t k; + + svec_init(&all_names); + dpif_get_all_names(dpif, &all_names); + for (k = 0; k < all_names.n; k++) { + if (svec_contains(&bridge_names, all_names.names[k])) { + goto found; + } } + dpif_delete(dpif); + found: + svec_destroy(&all_names); + dpif_close(dpif); } - dpif_delete(dpif); - found: - svec_destroy(&all_names); - dpif_close(dpif); } } svec_destroy(&dpif_names); + svec_destroy(&dpif_types); - unixctl_command_register("bridge/dump-flows", bridge_unixctl_dump_flows); + unixctl_command_register("bridge/dump-flows", bridge_unixctl_dump_flows, + NULL); bond_init(); - bridge_reconfigure(); + bridge_reconfigure(cfg); } #ifdef HAVE_OPENSSL static bool -config_string_change(const char *key, char **valuep) +config_string_change(const char *value, char **valuep) { - const char *value = cfg_get_string(0, "%s", key); if (value && (!*valuep || strcmp(value, *valuep))) { free(*valuep); *valuep = xstrdup(value); @@@ -354,7 -342,7 +354,7 @@@ } static void -bridge_configure_ssl(void) +bridge_configure_ssl(const struct ovsrec_ssl *ssl) { /* XXX SSL should be configurable on a per-bridge basis. * XXX should be possible to de-configure SSL. */ @@@ -363,17 -351,12 +363,17 @@@ static char *cacert_file; struct stat s; - if (config_string_change("ssl.private-key", &private_key_file)) { - vconn_ssl_set_private_key_file(private_key_file); + if (!ssl) { + /* XXX We can't un-set SSL settings. */ + return; + } + + if (config_string_change(ssl->private_key, &private_key_file)) { + stream_ssl_set_private_key_file(private_key_file); } - if (config_string_change("ssl.certificate", &certificate_file)) { - vconn_ssl_set_certificate_file(certificate_file); + if (config_string_change(ssl->certificate, &certificate_file)) { + stream_ssl_set_certificate_file(certificate_file); } /* We assume that even if the filename hasn't changed, if the CA cert @@@ -381,9 -364,10 +381,9 @@@ * boot-strapping mode. This opens a small security hole, because * the old certificate will still be trusted until vSwitch is * restarted. We may want to address this in vconn's SSL library. */ - if (config_string_change("ssl.ca-cert", &cacert_file) + if (config_string_change(ssl->ca_cert, &cacert_file) || (cacert_file && stat(cacert_file, &s) && errno == ENOENT)) { - vconn_ssl_set_ca_cert_file(cacert_file, - cfg_get_bool(0, "ssl.bootstrap-ca-cert")); + stream_ssl_set_ca_cert_file(cacert_file, ssl->bootstrap_ca_cert); } } #endif @@@ -391,84 -375,90 +391,85 @@@ /* Attempt to create the network device 'iface_name' through the netdev * library. */ static int -set_up_iface(const char *iface_name, bool create) +set_up_iface(const struct ovsrec_interface *iface_cfg, struct iface *iface, + bool create) { - const char *type; - const char *arg; - struct svec arg_svec; - struct shash args; - int error; + struct shash_node *node; + struct shash options; + int error = 0; size_t i; - /* If a type is not explicitly declared, then assume it's an existing - * "system" device. */ - type = cfg_get_string(0, "iface.%s.type", iface_name); - if (!type || !strcmp(type, "system")) { - return 0; + shash_init(&options); + for (i = 0; i < iface_cfg->n_options; i++) { + shash_add(&options, iface_cfg->key_options[i], + xstrdup(iface_cfg->value_options[i])); } - svec_init(&arg_svec); - cfg_get_subsections(&arg_svec, "iface.%s.args", iface_name); + if (create) { + struct netdev_options netdev_options; + + memset(&netdev_options, 0, sizeof netdev_options); + netdev_options.name = iface_cfg->name; + netdev_options.type = iface_cfg->type; + netdev_options.args = &options; + netdev_options.ethertype = NETDEV_ETH_TYPE_NONE; + netdev_options.may_create = true; + if (iface_is_internal(iface->port->bridge, iface_cfg->name)) { + netdev_options.may_open = true; + } - shash_init(&args); - SVEC_FOR_EACH (i, arg, &arg_svec) { - const char *value; + error = netdev_open(&netdev_options, &iface->netdev); - value = cfg_get_string(0, "iface.%s.args.%s", iface_name, arg); - if (value) { - shash_add(&args, arg, xstrdup(value)); + if (iface->netdev) { + netdev_get_carrier(iface->netdev, &iface->enabled); } - } + } else if (iface->netdev) { + const char *netdev_type = netdev_get_type(iface->netdev); + const char *iface_type = iface_cfg->type && strlen(iface_cfg->type) + ? iface_cfg->type : NULL; - if (create) { - error = netdev_create(iface_name, type, &args); - } else { - /* xxx Check to make sure that the type hasn't changed. */ - error = netdev_reconfigure(iface_name, &args); + if (!iface_type || !strcmp(netdev_type, iface_type)) { + error = netdev_reconfigure(iface->netdev, &options); + } else { + VLOG_WARN("%s: attempting change device type from %s to %s", + iface_cfg->name, netdev_type, iface_type); + error = EINVAL; + } } - svec_destroy(&arg_svec); - shash_destroy(&args); + SHASH_FOR_EACH (node, &options) { + free(node->data); + } + shash_destroy(&options); return error; } static int -create_iface(const char *iface_name) -{ - return set_up_iface(iface_name, true); -} - -static int -reconfigure_iface(const char *iface_name) +reconfigure_iface(const struct ovsrec_interface *iface_cfg, struct iface *iface) { - return set_up_iface(iface_name, false); + return set_up_iface(iface_cfg, iface, false); } -static void -destroy_iface(const char *iface_name) -{ - netdev_destroy(iface_name); -} - - -/* iterate_and_prune_ifaces() callback function that opens the network device - * for 'iface', if it is not already open, and retrieves the interface's MAC - * address and carrier status. */ static bool - check_iface_netdev(struct bridge *br UNUSED, struct iface *iface, - void *aux UNUSED) -init_iface_netdev(struct bridge *br OVS_UNUSED, struct iface *iface, - void *aux OVS_UNUSED) ++check_iface_netdev(struct bridge *br OVS_UNUSED, struct iface *iface, ++ void *aux OVS_UNUSED) { - if (iface->netdev) { - return true; - } else if (!netdev_open(iface->name, NETDEV_ETH_TYPE_NONE, - &iface->netdev)) { - netdev_get_carrier(iface->netdev, &iface->enabled); - return true; - } else { - /* If the network device can't be opened, then we're not going to try - * to do anything with this interface. */ - return false; + if (!iface->netdev) { + int error = set_up_iface(iface->cfg, iface, true); + if (error) { + VLOG_WARN("could not open netdev on %s, dropping: %s", iface->name, + strerror(error)); + return false; + } } + + return true; } static bool - check_iface_dp_ifidx(struct bridge *br, struct iface *iface, void *aux UNUSED) + check_iface_dp_ifidx(struct bridge *br, struct iface *iface, + void *aux OVS_UNUSED) { if (iface->dp_ifidx >= 0) { VLOG_DBG("%s has interface %s on port %d", @@@ -483,13 -473,15 +484,13 @@@ } static bool - set_iface_properties(struct bridge *br UNUSED, struct iface *iface, - void *aux UNUSED) + set_iface_properties(struct bridge *br OVS_UNUSED, struct iface *iface, + void *aux OVS_UNUSED) { - int rate, burst; - /* Set policing attributes. */ - rate = cfg_get_int(0, "port.%s.ingress.policing-rate", iface->name); - burst = cfg_get_int(0, "port.%s.ingress.policing-burst", iface->name); - netdev_set_policing(iface->netdev, rate, burst); + netdev_set_policing(iface->netdev, + iface->cfg->ingress_policing_rate, + iface->cfg->ingress_policing_burst); /* Set MAC address of internal interfaces other than the local * interface. */ @@@ -533,67 -525,50 +534,67 @@@ iterate_and_prune_ifaces(struct bridge } void -bridge_reconfigure(void) +bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg) { - struct svec old_br, new_br; + struct ovsdb_idl_txn *txn; + struct shash old_br, new_br; + struct shash_node *node; struct bridge *br, *next; size_t i; int sflow_bridge_number; COVERAGE_INC(bridge_reconfigure); + txn = ovsdb_idl_txn_create(ovs_cfg->header_.table->idl); + /* Collect old and new bridges. */ - svec_init(&old_br); - svec_init(&new_br); + shash_init(&old_br); + shash_init(&new_br); LIST_FOR_EACH (br, struct bridge, node, &all_bridges) { - svec_add(&old_br, br->name); + shash_add(&old_br, br->name, br); + } + for (i = 0; i < ovs_cfg->n_bridges; i++) { + const struct ovsrec_bridge *br_cfg = ovs_cfg->bridges[i]; + if (!shash_add_once(&new_br, br_cfg->name, br_cfg)) { + VLOG_WARN("more than one bridge named %s", br_cfg->name); + } } - cfg_get_subsections(&new_br, "bridge"); /* Get rid of deleted bridges and add new bridges. */ - svec_sort(&old_br); - svec_sort(&new_br); - assert(svec_is_unique(&old_br)); - assert(svec_is_unique(&new_br)); LIST_FOR_EACH_SAFE (br, next, struct bridge, node, &all_bridges) { - if (!svec_contains(&new_br, br->name)) { + struct ovsrec_bridge *br_cfg = shash_find_data(&new_br, br->name); + if (br_cfg) { + br->cfg = br_cfg; + } else { bridge_destroy(br); } } - for (i = 0; i < new_br.n; i++) { - const char *name = new_br.names[i]; - if (!svec_contains(&old_br, name)) { - bridge_create(name); + SHASH_FOR_EACH (node, &new_br) { + const char *br_name = node->name; + const struct ovsrec_bridge *br_cfg = node->data; + br = shash_find_data(&old_br, br_name); + if (br) { + /* If the bridge datapath type has changed, we need to tear it + * down and recreate. */ + if (strcmp(br->cfg->datapath_type, br_cfg->datapath_type)) { + bridge_destroy(br); + bridge_create(br_cfg); + } + } else { + bridge_create(br_cfg); } } - svec_destroy(&old_br); - svec_destroy(&new_br); + shash_destroy(&old_br); + shash_destroy(&new_br); #ifdef HAVE_OPENSSL /* Configure SSL. */ - bridge_configure_ssl(); + bridge_configure_ssl(ovs_cfg->ssl); #endif /* Reconfigure all bridges. */ LIST_FOR_EACH (br, struct bridge, node, &all_bridges) { - bridge_reconfigure_one(br); + bridge_reconfigure_one(ovs_cfg, br); } /* Add and delete ports on all datapaths. @@@ -604,13 -579,13 +605,13 @@@ LIST_FOR_EACH (br, struct bridge, node, &all_bridges) { struct odp_port *dpif_ports; size_t n_dpif_ports; - struct svec want_ifaces; + struct shash want_ifaces; dpif_port_list(br->dpif, &dpif_ports, &n_dpif_ports); bridge_get_all_ifaces(br, &want_ifaces); for (i = 0; i < n_dpif_ports; i++) { const struct odp_port *p = &dpif_ports[i]; - if (!svec_contains(&want_ifaces, p->devname) + if (!shash_find(&want_ifaces, p->devname) && strcmp(p->devname, br->name)) { int retval = dpif_port_del(br->dpif, p->port); if (retval) { @@@ -618,61 -593,62 +619,61 @@@ p->devname, dpif_name(br->dpif), strerror(retval)); } - destroy_iface(p->devname); } } - svec_destroy(&want_ifaces); + shash_destroy(&want_ifaces); free(dpif_ports); } LIST_FOR_EACH (br, struct bridge, node, &all_bridges) { struct odp_port *dpif_ports; size_t n_dpif_ports; - struct svec cur_ifaces, want_ifaces, add_ifaces; + struct shash cur_ifaces, want_ifaces; + struct shash_node *node; + /* Get the set of interfaces currently in this datapath. */ dpif_port_list(br->dpif, &dpif_ports, &n_dpif_ports); - svec_init(&cur_ifaces); + shash_init(&cur_ifaces); for (i = 0; i < n_dpif_ports; i++) { - svec_add(&cur_ifaces, dpif_ports[i].devname); + const char *name = dpif_ports[i].devname; + if (!shash_find(&cur_ifaces, name)) { + shash_add(&cur_ifaces, name, NULL); + } } free(dpif_ports); - svec_sort_unique(&cur_ifaces); - bridge_get_all_ifaces(br, &want_ifaces); - svec_diff(&want_ifaces, &cur_ifaces, &add_ifaces, NULL, NULL); - - for (i = 0; i < cur_ifaces.n; i++) { - const char *if_name = cur_ifaces.names[i]; - reconfigure_iface(if_name); - } - for (i = 0; i < add_ifaces.n; i++) { - const char *if_name = add_ifaces.names[i]; - bool internal; - int error; + /* Get the set of interfaces we want on this datapath. */ + bridge_get_all_ifaces(br, &want_ifaces); - /* Attempt to create the network interface in case it - * doesn't exist yet. */ - error = create_iface(if_name); - if (error) { - VLOG_WARN("could not create iface %s: %s\n", if_name, - strerror(error)); - continue; - } + SHASH_FOR_EACH (node, &want_ifaces) { + const char *if_name = node->name; + struct iface *iface = node->data; - /* Add to datapath. */ - internal = iface_is_internal(br, if_name); - error = dpif_port_add(br->dpif, if_name, - internal ? ODP_PORT_INTERNAL : 0, NULL); - if (error == EFBIG) { - VLOG_ERR("ran out of valid port numbers on %s", - dpif_name(br->dpif)); - break; - } else if (error) { - VLOG_ERR("failed to add %s interface to %s: %s", - if_name, dpif_name(br->dpif), strerror(error)); + if (shash_find(&cur_ifaces, if_name)) { + /* Already exists, just reconfigure it. */ + if (iface) { + reconfigure_iface(iface->cfg, iface); + } + } else { + /* Need to add to datapath. */ + bool internal; + int error; + + /* Add to datapath. */ + internal = iface_is_internal(br, if_name); + error = dpif_port_add(br->dpif, if_name, + internal ? ODP_PORT_INTERNAL : 0, NULL); + if (error == EFBIG) { + VLOG_ERR("ran out of valid port numbers on %s", + dpif_name(br->dpif)); + break; + } else if (error) { + VLOG_ERR("failed to add %s interface to %s: %s", + if_name, dpif_name(br->dpif), strerror(error)); + } } } - svec_destroy(&cur_ifaces); - svec_destroy(&want_ifaces); - svec_destroy(&add_ifaces); + shash_destroy(&cur_ifaces); + shash_destroy(&want_ifaces); } sflow_bridge_number = 0; LIST_FOR_EACH (br, struct bridge, node, &all_bridges) { @@@ -680,11 -656,11 +681,11 @@@ uint64_t dpid; struct iface *local_iface; struct iface *hw_addr_iface; - struct netflow_options nf_options; + char *dpid_string; bridge_fetch_dp_ifaces(br); - iterate_and_prune_ifaces(br, init_iface_netdev, NULL); + iterate_and_prune_ifaces(br, check_iface_netdev, NULL); iterate_and_prune_ifaces(br, check_iface_dp_ifidx, NULL); /* Pick local port hardware address, datapath ID. */ @@@ -703,90 -679,77 +704,90 @@@ dpid = bridge_pick_datapath_id(br, ea, hw_addr_iface); ofproto_set_datapath_id(br->ofproto, dpid); + dpid_string = xasprintf("%012"PRIx64, dpid); + ovsrec_bridge_set_datapath_id(br->cfg, dpid_string); + free(dpid_string); + /* Set NetFlow configuration on this bridge. */ - memset(&nf_options, 0, sizeof nf_options); - dpif_get_netflow_ids(br->dpif, &nf_options.engine_type, - &nf_options.engine_id); - nf_options.active_timeout = -1; - - if (cfg_has("netflow.%s.engine-type", br->name)) { - nf_options.engine_type = cfg_get_int(0, "netflow.%s.engine-type", - br->name); - } - if (cfg_has("netflow.%s.engine-id", br->name)) { - nf_options.engine_id = cfg_get_int(0, "netflow.%s.engine-id", - br->name); - } - if (cfg_has("netflow.%s.active-timeout", br->name)) { - nf_options.active_timeout = cfg_get_int(0, - "netflow.%s.active-timeout", - br->name); - } - if (cfg_has("netflow.%s.add-id-to-iface", br->name)) { - nf_options.add_id_to_iface = cfg_get_bool(0, - "netflow.%s.add-id-to-iface", - br->name); - } - if (nf_options.add_id_to_iface && nf_options.engine_id > 0x7f) { - VLOG_WARN("bridge %s: netflow port mangling may conflict with " - "another vswitch, choose an engine id less than 128", - br->name); - } - if (nf_options.add_id_to_iface && br->n_ports > 508) { - VLOG_WARN("bridge %s: netflow port mangling will conflict with " - "another port when more than 508 ports are used", - br->name); - } - svec_init(&nf_options.collectors); - cfg_get_all_keys(&nf_options.collectors, "netflow.%s.host", br->name); - if (ofproto_set_netflow(br->ofproto, &nf_options)) { - VLOG_ERR("bridge %s: problem setting netflow collectors", - br->name); - } - svec_destroy(&nf_options.collectors); - - if (cfg_has("sflow.%s.host", br->name)) { + if (br->cfg->netflow) { + struct ovsrec_netflow *nf_cfg = br->cfg->netflow; + struct netflow_options opts; + + memset(&opts, 0, sizeof opts); + + dpif_get_netflow_ids(br->dpif, &opts.engine_type, &opts.engine_id); + if (nf_cfg->engine_type) { + opts.engine_type = *nf_cfg->engine_type; + } + if (nf_cfg->engine_id) { + opts.engine_id = *nf_cfg->engine_id; + } + + opts.active_timeout = nf_cfg->active_timeout; + if (!opts.active_timeout) { + opts.active_timeout = -1; + } else if (opts.active_timeout < 0) { + VLOG_WARN("bridge %s: active timeout interval set to negative " + "value, using default instead (%d seconds)", br->name, + NF_ACTIVE_TIMEOUT_DEFAULT); + opts.active_timeout = -1; + } + + opts.add_id_to_iface = nf_cfg->add_id_to_interface; + if (opts.add_id_to_iface) { + if (opts.engine_id > 0x7f) { + VLOG_WARN("bridge %s: netflow port mangling may conflict " + "with another vswitch, choose an engine id less " + "than 128", br->name); + } + if (br->n_ports > 508) { + VLOG_WARN("bridge %s: netflow port mangling will conflict " + "with another port when more than 508 ports are " + "used", br->name); + } + } + + opts.collectors.n = nf_cfg->n_targets; + opts.collectors.names = nf_cfg->targets; + if (ofproto_set_netflow(br->ofproto, &opts)) { + VLOG_ERR("bridge %s: problem setting netflow collectors", + br->name); + } + } else { + ofproto_set_netflow(br->ofproto, NULL); + } + + /* Set sFlow configuration on this bridge. */ + if (br->cfg->sflow) { + const struct ovsrec_sflow *sflow_cfg = br->cfg->sflow; + const struct ovsrec_controller *ctrl; struct ofproto_sflow_options oso; - svec_init(&oso.targets); - cfg_get_all_keys(&oso.targets, "sflow.%s.host", br->name); + memset(&oso, 0, sizeof oso); + + oso.targets.n = sflow_cfg->n_targets; + oso.targets.names = sflow_cfg->targets; oso.sampling_rate = SFL_DEFAULT_SAMPLING_RATE; - if (cfg_has("sflow.%s.sampling", br->name)) { - oso.sampling_rate = cfg_get_int(0, "sflow.%s.sampling", - br->name); + if (sflow_cfg->sampling) { + oso.sampling_rate = *sflow_cfg->sampling; } oso.polling_interval = SFL_DEFAULT_POLLING_INTERVAL; - if (cfg_has("sflow.%s.polling", br->name)) { - oso.polling_interval = cfg_get_int(0, "sflow.%s.polling", - br->name); + if (sflow_cfg->polling) { + oso.polling_interval = *sflow_cfg->polling; } oso.header_len = SFL_DEFAULT_HEADER_SIZE; - if (cfg_has("sflow.%s.header", br->name)) { - oso.header_len = cfg_get_int(0, "sflow.%s.header", br->name); + if (sflow_cfg->header) { + oso.header_len = *sflow_cfg->header; } oso.sub_id = sflow_bridge_number++; - oso.agent_device = (char *) cfg_get_string(0, "sflow.%s.agent", - br->name); - oso.control_ip = (char *) cfg_get_string(0, - "bridge.%s.controller.ip", - br->name); + oso.agent_device = sflow_cfg->agent; + + ctrl = bridge_get_controller(ovs_cfg, br); + oso.control_ip = ctrl ? ctrl->local_ip : NULL; ofproto_set_sflow(br->ofproto, &oso); svec_destroy(&oso.targets); @@@ -803,7 -766,7 +804,7 @@@ * yet; when a controller is configured, resetting the datapath ID will * immediately disconnect from the controller, so it's better to set * the datapath ID before the controller. */ - bridge_reconfigure_controller(br); + bridge_reconfigure_controller(ovs_cfg, br); } LIST_FOR_EACH (br, struct bridge, node, &all_bridges) { for (i = 0; i < br->n_ports; i++) { @@@ -814,41 -777,25 +815,41 @@@ } } LIST_FOR_EACH (br, struct bridge, node, &all_bridges) { - brstp_reconfigure(br); iterate_and_prune_ifaces(br, set_iface_properties, NULL); } + + ovsrec_open_vswitch_set_cur_cfg(ovs_cfg, ovs_cfg->next_cfg); + + ovsdb_idl_txn_commit(txn); + ovsdb_idl_txn_destroy(txn); /* XXX */ +} + +static const char * +bridge_get_other_config(const struct ovsrec_bridge *br_cfg, const char *key) +{ + size_t i; + + for (i = 0; i < br_cfg->n_other_config; i++) { + if (!strcmp(br_cfg->key_other_config[i], key)) { + return br_cfg->value_other_config[i]; + } + } + return NULL; } static void bridge_pick_local_hw_addr(struct bridge *br, uint8_t ea[ETH_ADDR_LEN], struct iface **hw_addr_iface) { - uint64_t requested_ea; + const char *hwaddr; size_t i, j; int error; *hw_addr_iface = NULL; /* Did the user request a particular MAC? */ - requested_ea = cfg_get_mac(0, "bridge.%s.mac", br->name); - if (requested_ea) { - eth_addr_from_uint64(requested_ea, ea); + hwaddr = bridge_get_other_config(br->cfg, "hwaddr"); + if (hwaddr && eth_addr_from_string(hwaddr, ea)) { if (eth_addr_is_multicast(ea)) { VLOG_ERR("bridge %s: cannot set MAC address to multicast " "address "ETH_ADDR_FMT, br->name, ETH_ADDR_ARGS(ea)); @@@ -859,12 -806,14 +860,12 @@@ } } - /* Otherwise choose the minimum MAC address among all of the interfaces. - * (Xen uses FE:FF:FF:FF:FF:FF for virtual interfaces so this will get the - * MAC of the physical interface in such an environment.) */ + /* Otherwise choose the minimum non-local MAC address among all of the + * interfaces. */ memset(ea, 0xff, sizeof ea); for (i = 0; i < br->n_ports; i++) { struct port *port = br->ports[i]; uint8_t iface_ea[ETH_ADDR_LEN]; - uint64_t iface_ea_u64; struct iface *iface; /* Mirror output ports don't participate. */ @@@ -873,7 -822,11 +874,7 @@@ } /* Choose the MAC address to represent the port. */ - iface_ea_u64 = cfg_get_mac(0, "port.%s.mac", port->name); - if (iface_ea_u64) { - /* User specified explicitly. */ - eth_addr_from_uint64(iface_ea_u64, iface_ea); - + if (port->cfg->mac && eth_addr_from_string(port->cfg->mac, iface_ea)) { /* Find the interface with this Ethernet address (if any) so that * we can provide the correct devname to the caller. */ iface = NULL; @@@ -901,8 -854,11 +902,8 @@@ } /* The local port doesn't count (since we're trying to choose its - * MAC address anyway). Other internal ports don't count because - * we really want a physical MAC if we can get it, and internal - * ports typically have randomly generated MACs. */ - if (iface->dp_ifidx == ODPP_LOCAL - || cfg_get_bool(0, "iface.%s.internal", iface->name)) { + * MAC address anyway). */ + if (iface->dp_ifidx == ODPP_LOCAL) { continue; } @@@ -918,7 -874,6 +919,7 @@@ /* Compare against our current choice. */ if (!eth_addr_is_multicast(iface_ea) && + !eth_addr_is_local(iface_ea) && !eth_addr_is_reserved(iface_ea) && !eth_addr_is_zero(iface_ea) && memcmp(iface_ea, ea, ETH_ADDR_LEN) < 0) @@@ -927,7 -882,7 +928,7 @@@ *hw_addr_iface = iface; } } - if (eth_addr_is_multicast(ea) || eth_addr_is_vif(ea)) { + if (eth_addr_is_multicast(ea)) { memcpy(ea, br->default_ea, ETH_ADDR_LEN); *hw_addr_iface = NULL; VLOG_WARN("bridge %s: using default bridge Ethernet " @@@ -960,11 -915,10 +961,11 @@@ bridge_pick_datapath_id(struct bridge * * stable from one run to the next, so that policy set on a datapath * "sticks". */ + const char *datapath_id; uint64_t dpid; - dpid = cfg_get_dpid(0, "bridge.%s.datapath-id", br->name); - if (dpid) { + datapath_id = bridge_get_other_config(br->cfg, "datapath-id"); + if (datapath_id && dpid_from_string(datapath_id, &dpid)) { return dpid; } @@@ -1063,6 -1017,7 +1064,6 @@@ bridge_wait(void mac_learning_wait(br->ml); bond_wait(br); - brstp_wait(br); } } @@@ -1098,8 -1053,7 +1099,8 @@@ bridge_get_local_iface(struct bridge *b /* Bridge unixctl user interface functions. */ static void -bridge_unixctl_fdb_show(struct unixctl_conn *conn, const char *args) +bridge_unixctl_fdb_show(struct unixctl_conn *conn, - const char *args, void *aux UNUSED) ++ const char *args, void *aux OVS_UNUSED) { struct ds ds = DS_EMPTY_INITIALIZER; const struct bridge *br; @@@ -1125,39 -1079,36 +1126,39 @@@ } /* Bridge reconfiguration functions. */ - static struct bridge * -bridge_create(const char *name) +bridge_create(const struct ovsrec_bridge *br_cfg) { struct bridge *br; int error; - assert(!bridge_lookup(name)); - br = xcalloc(1, sizeof *br); + assert(!bridge_lookup(br_cfg->name)); + br = xzalloc(sizeof *br); - error = dpif_create_and_open(name, &br->dpif); + error = dpif_create_and_open(br_cfg->name, br_cfg->datapath_type, + &br->dpif); if (error) { free(br); return NULL; } dpif_flow_flush(br->dpif); - error = ofproto_create(name, &bridge_ofhooks, br, &br->ofproto); + error = ofproto_create(br_cfg->name, br_cfg->datapath_type, &bridge_ofhooks, + br, &br->ofproto); if (error) { - VLOG_ERR("failed to create switch %s: %s", name, strerror(error)); + VLOG_ERR("failed to create switch %s: %s", br_cfg->name, + strerror(error)); dpif_delete(br->dpif); dpif_close(br->dpif); free(br); return NULL; } - br->name = xstrdup(name); + br->name = xstrdup(br_cfg->name); + br->cfg = br_cfg; br->ml = mac_learning_create(); br->sent_config_request = false; - eth_addr_random(br->default_ea); + eth_addr_nicira_random(br->default_ea); port_array_init(&br->ifaces); @@@ -1226,8 -1177,7 +1227,8 @@@ bridge_get_datapathid(const char *name /* Handle requests for a listing of all flows known by the OpenFlow * stack, including those normally hidden. */ static void -bridge_unixctl_dump_flows(struct unixctl_conn *conn, const char *args) +bridge_unixctl_dump_flows(struct unixctl_conn *conn, - const char *args, void *aux UNUSED) ++ const char *args, void *aux OVS_UNUSED) { struct bridge *br; struct ds results; @@@ -1257,6 -1207,7 +1258,6 @@@ bridge_run_one(struct bridge *br mac_learning_run(br->ml, ofproto_get_revalidate_set(br->ofproto)); bond_run(br); - brstp_run(br); error = ofproto_run2(br->ofproto, br->flush); br->flush = false; @@@ -1264,21 -1215,16 +1265,21 @@@ return error; } -static const char * -bridge_get_controller(const struct bridge *br) +static const struct ovsrec_controller * +bridge_get_controller(const struct ovsrec_open_vswitch *ovs_cfg, + const struct bridge *br) { - const char *controller; + const struct ovsrec_controller *controller; - controller = cfg_get_string(0, "bridge.%s.controller", br->name); - if (!controller) { - controller = cfg_get_string(0, "mgmt.controller"); + controller = (br->cfg->controller ? br->cfg->controller + : ovs_cfg->controller ? ovs_cfg->controller + : NULL); + + if (controller && !strcmp(controller->target, "none")) { + return NULL; } - return controller && controller[0] ? controller : NULL; + + return controller; } static bool @@@ -1298,64 -1244,66 +1299,64 @@@ check_duplicate_ifaces(struct bridge *b } static void -bridge_reconfigure_one(struct bridge *br) +bridge_reconfigure_one(const struct ovsrec_open_vswitch *ovs_cfg, + struct bridge *br) { - struct svec old_ports, new_ports, ifaces; + struct shash old_ports, new_ports; + struct svec ifaces; struct svec listeners, old_listeners; struct svec snoops, old_snoops; + struct shash_node *node; size_t i; /* Collect old ports. */ - svec_init(&old_ports); + shash_init(&old_ports); for (i = 0; i < br->n_ports; i++) { - svec_add(&old_ports, br->ports[i]->name); + shash_add(&old_ports, br->ports[i]->name, br->ports[i]); } - svec_sort(&old_ports); - assert(svec_is_unique(&old_ports)); /* Collect new ports. */ - svec_init(&new_ports); - cfg_get_all_keys(&new_ports, "bridge.%s.port", br->name); - svec_sort(&new_ports); - if (bridge_get_controller(br)) { + shash_init(&new_ports); + for (i = 0; i < br->cfg->n_ports; i++) { + const char *name = br->cfg->ports[i]->name; + if (!shash_add_once(&new_ports, name, br->cfg->ports[i])) { + VLOG_WARN("bridge %s: %s specified twice as bridge port", + br->name, name); + } + } + + /* If we have a controller, then we need a local port. Complain if the + * user didn't specify one. + * + * XXX perhaps we should synthesize a port ourselves in this case. */ + if (bridge_get_controller(ovs_cfg, br)) { char local_name[IF_NAMESIZE]; int error; error = dpif_port_get_name(br->dpif, ODPP_LOCAL, local_name, sizeof local_name); - if (!error && !svec_contains(&new_ports, local_name)) { - svec_add(&new_ports, local_name); - svec_sort(&new_ports); + if (!error && !shash_find(&new_ports, local_name)) { + VLOG_WARN("bridge %s: controller specified but no local port " + "(port named %s) defined", + br->name, local_name); } } - if (!svec_is_unique(&new_ports)) { - VLOG_WARN("bridge %s: %s specified twice as bridge port", - br->name, svec_get_duplicate(&new_ports)); - svec_unique(&new_ports); - } - - ofproto_set_mgmt_id(br->ofproto, mgmt_id); /* Get rid of deleted ports and add new ports. */ - for (i = 0; i < br->n_ports; ) { - struct port *port = br->ports[i]; - if (!svec_contains(&new_ports, port->name)) { - port_destroy(port); - } else { - i++; + SHASH_FOR_EACH (node, &old_ports) { + if (!shash_find(&new_ports, node->name)) { + port_destroy(node->data); } } - for (i = 0; i < new_ports.n; i++) { - const char *name = new_ports.names[i]; - if (!svec_contains(&old_ports, name)) { - port_create(br, name); + SHASH_FOR_EACH (node, &new_ports) { + struct port *port = shash_find_data(&old_ports, node->name); + if (!port) { + port = port_create(br, node->name); } + port_reconfigure(port, node->data); } - svec_destroy(&old_ports); - svec_destroy(&new_ports); - - /* Reconfigure all ports. */ - for (i = 0; i < br->n_ports; i++) { - port_reconfigure(br->ports[i]); - } + shash_destroy(&old_ports); + shash_destroy(&new_ports); /* Check and delete duplicate interfaces. */ svec_init(&ifaces); @@@ -1366,7 -1314,6 +1367,7 @@@ * versa. (XXX Should we delete all flows if we are switching from one * controller to another?) */ +#if 0 /* Configure OpenFlow management listeners. */ svec_init(&listeners); cfg_get_all_strings(&listeners, "bridge.%s.openflow.listeners", br->name); @@@ -1408,79 -1355,56 +1409,79 @@@ } svec_destroy(&snoops); svec_destroy(&old_snoops); +#else + /* Default listener. */ + svec_init(&listeners); + svec_add_nocopy(&listeners, xasprintf("punix:%s/%s.mgmt", + ovs_rundir, br->name)); + svec_init(&old_listeners); + ofproto_get_listeners(br->ofproto, &old_listeners); + if (!svec_equal(&listeners, &old_listeners)) { + ofproto_set_listeners(br->ofproto, &listeners); + } + svec_destroy(&listeners); + svec_destroy(&old_listeners); + + /* Default snoop. */ + svec_init(&snoops); + svec_add_nocopy(&snoops, xasprintf("punix:%s/%s.snoop", + ovs_rundir, br->name)); + svec_init(&old_snoops); + ofproto_get_snoops(br->ofproto, &old_snoops); + if (!svec_equal(&snoops, &old_snoops)) { + ofproto_set_snoops(br->ofproto, &snoops); + } + svec_destroy(&snoops); + svec_destroy(&old_snoops); +#endif mirror_reconfigure(br); } static void -bridge_reconfigure_controller(struct bridge *br) +bridge_reconfigure_controller(const struct ovsrec_open_vswitch *ovs_cfg, + struct bridge *br) { char *pfx = xasprintf("bridge.%s.controller", br->name); - const char *controller; + const struct ovsrec_controller *c; - controller = bridge_get_controller(br); - if ((br->controller != NULL) != (controller != NULL)) { + c = bridge_get_controller(ovs_cfg, br); + if ((br->controller != NULL) != (c != NULL)) { ofproto_flush_flows(br->ofproto); } free(br->controller); - br->controller = controller ? xstrdup(controller) : NULL; + br->controller = c ? xstrdup(c->target) : NULL; - if (controller) { - const char *fail_mode; + if (c) { int max_backoff, probe; int rate_limit, burst_limit; - if (!strcmp(controller, "discover")) { - bool update_resolv_conf = true; - - if (cfg_has("%s.update-resolv.conf", pfx)) { - update_resolv_conf = cfg_get_bool(0, "%s.update-resolv.conf", - pfx); - } + if (!strcmp(c->target, "discover")) { ofproto_set_discovery(br->ofproto, true, - cfg_get_string(0, "%s.accept-regex", pfx), - update_resolv_conf); + c->discover_accept_regex, + c->discover_update_resolv_conf); } else { struct iface *local_iface; + struct in_addr ip; bool in_band; - in_band = (!cfg_is_valid(CFG_BOOL | CFG_REQUIRED, - "%s.in-band", pfx) - || cfg_get_bool(0, "%s.in-band", pfx)); + in_band = (!c->connection_mode + || !strcmp(c->connection_mode, "out-of-band")); ofproto_set_discovery(br->ofproto, false, NULL, NULL); ofproto_set_in_band(br->ofproto, in_band); local_iface = bridge_get_local_iface(br); - if (local_iface - && cfg_is_valid(CFG_IP | CFG_REQUIRED, "%s.ip", pfx)) { + if (local_iface && c->local_ip && inet_aton(c->local_ip, &ip)) { struct netdev *netdev = local_iface->netdev; - struct in_addr ip, mask, gateway; - ip.s_addr = cfg_get_ip(0, "%s.ip", pfx); - mask.s_addr = cfg_get_ip(0, "%s.netmask", pfx); - gateway.s_addr = cfg_get_ip(0, "%s.gateway", pfx); + struct in_addr mask, gateway; + + if (!c->local_netmask || !inet_aton(c->local_netmask, &mask)) { + mask.s_addr = 0; + } + if (!c->local_gateway + || !inet_aton(c->local_gateway, &gateway)) { + gateway.s_addr = 0; + } netdev_turn_flags_on(netdev, NETDEV_UP, true); if (!mask.s_addr) { @@@ -1502,20 -1426,62 +1503,20 @@@ } } - fail_mode = cfg_get_string(0, "%s.fail-mode", pfx); - if (!fail_mode) { - fail_mode = cfg_get_string(0, "mgmt.fail-mode"); - } ofproto_set_failure(br->ofproto, - (!fail_mode - || !strcmp(fail_mode, "standalone") - || !strcmp(fail_mode, "open"))); - - probe = cfg_get_int(0, "%s.inactivity-probe", pfx); - if (probe < 5) { - probe = cfg_get_int(0, "mgmt.inactivity-probe"); - if (probe < 5) { - probe = 5; - } - } + (!c->fail_mode + || !strcmp(c->fail_mode, "standalone") + || !strcmp(c->fail_mode, "open"))); + + probe = c->inactivity_probe ? *c->inactivity_probe / 1000 : 5; ofproto_set_probe_interval(br->ofproto, probe); - max_backoff = cfg_get_int(0, "%s.max-backoff", pfx); - if (!max_backoff) { - max_backoff = cfg_get_int(0, "mgmt.max-backoff"); - if (!max_backoff) { - max_backoff = 8; - } - } + max_backoff = c->max_backoff ? *c->max_backoff / 1000 : 8; ofproto_set_max_backoff(br->ofproto, max_backoff); - rate_limit = cfg_get_int(0, "%s.rate-limit", pfx); - if (!rate_limit) { - rate_limit = cfg_get_int(0, "mgmt.rate-limit"); - } - burst_limit = cfg_get_int(0, "%s.burst-limit", pfx); - if (!burst_limit) { - burst_limit = cfg_get_int(0, "mgmt.burst-limit"); - } + rate_limit = c->controller_rate_limit ? *c->controller_rate_limit : 0; + burst_limit = c->controller_burst_limit ? *c->controller_burst_limit : 0; ofproto_set_rate_limit(br->ofproto, rate_limit, burst_limit); - - ofproto_set_stp(br->ofproto, cfg_get_bool(0, "%s.stp", pfx)); - - if (cfg_has("%s.commands.acl", pfx)) { - struct svec command_acls; - char *command_acl; - - svec_init(&command_acls); - cfg_get_all_strings(&command_acls, "%s.commands.acl", pfx); - command_acl = svec_join(&command_acls, ",", ""); - - ofproto_set_remote_execution(br->ofproto, command_acl, - cfg_get_string(0, "%s.commands.dir", - pfx)); - - svec_destroy(&command_acls); - free(command_acl); - } else { - ofproto_set_remote_execution(br->ofproto, NULL, NULL); - } } else { union ofp_action action; flow_t flow; @@@ -1534,6 -1500,7 +1535,6 @@@ ofproto_set_max_backoff(br->ofproto, 1); ofproto_set_probe_interval(br->ofproto, 5); ofproto_set_failure(br->ofproto, false); - ofproto_set_stp(br->ofproto, false); } free(pfx); @@@ -1541,21 -1508,23 +1542,21 @@@ } static void -bridge_get_all_ifaces(const struct bridge *br, struct svec *ifaces) +bridge_get_all_ifaces(const struct bridge *br, struct shash *ifaces) { size_t i, j; - svec_init(ifaces); + shash_init(ifaces); for (i = 0; i < br->n_ports; i++) { struct port *port = br->ports[i]; for (j = 0; j < port->n_ifaces; j++) { struct iface *iface = port->ifaces[j]; - svec_add(ifaces, iface->name); + shash_add_once(ifaces, iface->name, iface); } - if (port->n_ifaces > 1 - && cfg_get_bool(0, "bonding.%s.fake-iface", port->name)) { - svec_add(ifaces, port->name); + if (port->n_ifaces > 1 && port->cfg->bond_fake_iface) { + shash_add_once(ifaces, port->name, NULL); } } - svec_sort_unique(ifaces); } /* For robustness, in case the administrator moves around datapath ports behind @@@ -1598,13 -1567,6 +1599,13 @@@ bridge_fetch_dp_ifaces(struct bridge *b port_array_set(&br->ifaces, p->port, iface); iface->dp_ifidx = p->port; } + + if (iface->cfg) { + int64_t ofport = (iface->dp_ifidx >= 0 + ? odp_port_to_ofp_port(iface->dp_ifidx) + : -1); + ovsrec_interface_set_ofport(iface->cfg, &ofport, 1); + } } } free(dpif_ports); @@@ -1834,6 -1796,18 +1835,6 @@@ set_dst(struct dst *p, const flow_t *fl const struct port *in_port, const struct port *out_port, tag_type *tags) { - /* STP handling. - * - * XXX This uses too many tags: any broadcast flow will get one tag per - * destination port, and thus a broadcast on a switch of any size is likely - * to have all tag bits set. We should figure out a way to be smarter. - * - * This is OK when STP is disabled, because stp_state_tag is 0 then. */ - *tags |= out_port->stp_state_tag; - if (!(out_port->stp_state & (STP_DISABLED | STP_FORWARDING))) { - return false; - } - p->vlan = (out_port->vlan >= 0 ? OFP_VLAN_NONE : in_port->vlan >= 0 ? in_port->vlan : ntohs(flow->dl_vlan)); @@@ -1926,6 -1900,7 +1927,6 @@@ compose_dsts(const struct bridge *br, c struct dst *dst = dsts; size_t i; - *tags |= in_port->stp_state_tag; if (out_port == FLOOD_PORT) { /* XXX use ODP_FLOOD if no vlans or bonding. */ /* XXX even better, define each VLAN as a datapath port group */ @@@ -1994,7 -1969,7 +1995,7 @@@ return dst - dsts; } - static void UNUSED + static void OVS_UNUSED print_dsts(const struct dst *dsts, size_t n) { for (; n--; dsts++) { @@@ -2154,6 -2129,13 +2155,6 @@@ process_flow(struct bridge *br, const f goto done; } - /* Drop frames for ports that STP wants entirely killed (both for - * forwarding and for learning). Later, after we do learning, we'll drop - * the frames that STP wants to do learning but not forwarding on. */ - if (in_port->stp_state & (STP_LISTENING | STP_BLOCKING)) { - goto done; - } - /* Drop frames for reserved multicast addresses. */ if (eth_addr_is_reserved(flow->dl_dst)) { goto done; @@@ -2212,8 -2194,9 +2213,8 @@@ return false; } - /* Don't send packets out their input ports. Don't forward frames that STP - * wants us to discard. */ - if (in_port == out_port || in_port->stp_state == STP_LEARNING) { + /* Don't send packets out their input ports. */ + if (in_port == out_port) { out_port = NULL; } @@@ -2268,6 -2251,14 +2269,6 @@@ bridge_normal_ofhook_cb(const flow_t *f { struct bridge *br = br_; -#if 0 - if (flow->dl_type == htons(OFP_DL_TYPE_NOT_ETH_TYPE) - && eth_addr_equals(flow->dl_dst, stp_eth_addr)) { - brstp_receive(br, flow, payload); - return true; - } -#endif - COVERAGE_INC(bridge_process_flow); return process_flow(br, flow, packet, actions, tags, nf_output_iface); } @@@ -2708,8 -2699,7 +2709,8 @@@ bond_send_learning_packets(struct port /* Bonding unixctl user interface functions. */ static void -bond_unixctl_list(struct unixctl_conn *conn, const char *args OVS_UNUSED) +bond_unixctl_list(struct unixctl_conn *conn, - const char *args UNUSED, void *aux UNUSED) ++ const char *args OVS_UNUSED, void *aux OVS_UNUSED) { struct ds ds = DS_EMPTY_INITIALIZER; const struct bridge *br; @@@ -2759,8 -2749,7 +2760,8 @@@ bond_find(const char *name } static void -bond_unixctl_show(struct unixctl_conn *conn, const char *args) +bond_unixctl_show(struct unixctl_conn *conn, - const char *args, void *aux UNUSED) ++ const char *args, void *aux OVS_UNUSED) { struct ds ds = DS_EMPTY_INITIALIZER; const struct port *port; @@@ -2825,8 -2814,7 +2826,8 @@@ } static void -bond_unixctl_migrate(struct unixctl_conn *conn, const char *args_) +bond_unixctl_migrate(struct unixctl_conn *conn, const char *args_, - void *aux UNUSED) ++ void *aux OVS_UNUSED) { char *args = (char *) args_; char *save_ptr = NULL; @@@ -2882,8 -2870,7 +2883,8 @@@ } static void -bond_unixctl_set_active_slave(struct unixctl_conn *conn, const char *args_) +bond_unixctl_set_active_slave(struct unixctl_conn *conn, const char *args_, - void *aux UNUSED) ++ void *aux OVS_UNUSED) { char *args = (char *) args_; char *save_ptr = NULL; @@@ -2963,22 -2950,19 +2964,22 @@@ enable_slave(struct unixctl_conn *conn } static void -bond_unixctl_enable_slave(struct unixctl_conn *conn, const char *args) +bond_unixctl_enable_slave(struct unixctl_conn *conn, const char *args, - void *aux UNUSED) ++ void *aux OVS_UNUSED) { enable_slave(conn, args, true); } static void -bond_unixctl_disable_slave(struct unixctl_conn *conn, const char *args) +bond_unixctl_disable_slave(struct unixctl_conn *conn, const char *args, - void *aux UNUSED) ++ void *aux OVS_UNUSED) { enable_slave(conn, args, false); } static void -bond_unixctl_hash(struct unixctl_conn *conn, const char *args) +bond_unixctl_hash(struct unixctl_conn *conn, const char *args, - void *aux UNUSED) ++ void *aux OVS_UNUSED) { uint8_t mac[ETH_ADDR_LEN]; uint8_t hash; @@@ -2999,32 -2983,32 +3000,32 @@@ static void bond_init(void) { - unixctl_command_register("bond/list", bond_unixctl_list); - unixctl_command_register("bond/show", bond_unixctl_show); - unixctl_command_register("bond/migrate", bond_unixctl_migrate); + unixctl_command_register("bond/list", bond_unixctl_list, NULL); + unixctl_command_register("bond/show", bond_unixctl_show, NULL); + unixctl_command_register("bond/migrate", bond_unixctl_migrate, NULL); unixctl_command_register("bond/set-active-slave", - bond_unixctl_set_active_slave); - unixctl_command_register("bond/enable-slave", bond_unixctl_enable_slave); - unixctl_command_register("bond/disable-slave", bond_unixctl_disable_slave); - unixctl_command_register("bond/hash", bond_unixctl_hash); + bond_unixctl_set_active_slave, NULL); + unixctl_command_register("bond/enable-slave", bond_unixctl_enable_slave, + NULL); + unixctl_command_register("bond/disable-slave", bond_unixctl_disable_slave, + NULL); + unixctl_command_register("bond/hash", bond_unixctl_hash, NULL); } /* Port functions. */ -static void +static struct port * port_create(struct bridge *br, const char *name) { struct port *port; - port = xcalloc(1, sizeof *port); + port = xzalloc(sizeof *port); port->bridge = br; port->port_idx = br->n_ports; port->vlan = -1; port->trunks = NULL; port->name = xstrdup(name); port->active_iface = -1; - port->stp_state = STP_DISABLED; - port->stp_state_tag = 0; if (br->n_ports >= br->allocated_ports) { br->ports = x2nrealloc(br->ports, &br->allocated_ports, @@@ -3034,70 -3018,70 +3035,70 @@@ VLOG_INFO("created port %s on bridge %s", port->name, br->name); bridge_flush(br); + + return port; } static void -port_reconfigure(struct port *port) +port_reconfigure(struct port *port, const struct ovsrec_port *cfg) { - bool bonded = cfg_has_section("bonding.%s", port->name); - struct svec old_ifaces, new_ifaces; + struct shash old_ifaces, new_ifaces; + struct shash_node *node; unsigned long *trunks; int vlan; size_t i; + port->cfg = cfg; + /* Collect old and new interfaces. */ - svec_init(&old_ifaces); - svec_init(&new_ifaces); + shash_init(&old_ifaces); + shash_init(&new_ifaces); for (i = 0; i < port->n_ifaces; i++) { - svec_add(&old_ifaces, port->ifaces[i]->name); + shash_add(&old_ifaces, port->ifaces[i]->name, port->ifaces[i]); } - svec_sort(&old_ifaces); - if (bonded) { - cfg_get_all_keys(&new_ifaces, "bonding.%s.slave", port->name); - if (!new_ifaces.n) { - VLOG_ERR("port %s: no interfaces specified for bonded port", - port->name); - } else if (new_ifaces.n == 1) { - VLOG_WARN("port %s: only 1 interface specified for bonded port", - port->name); - } - - port->updelay = cfg_get_int(0, "bonding.%s.updelay", port->name); - if (port->updelay < 0) { - port->updelay = 0; + for (i = 0; i < cfg->n_interfaces; i++) { + const char *name = cfg->interfaces[i]->name; + if (!shash_add_once(&new_ifaces, name, cfg->interfaces[i])) { + VLOG_WARN("port %s: %s specified twice as port interface", + port->name, name); } - port->downdelay = cfg_get_int(0, "bonding.%s.downdelay", port->name); - if (port->downdelay < 0) { - port->downdelay = 0; - } - } else { - svec_init(&new_ifaces); - svec_add(&new_ifaces, port->name); + } + port->updelay = cfg->bond_updelay; + if (port->updelay < 0) { + port->updelay = 0; + } + port->updelay = cfg->bond_downdelay; + if (port->downdelay < 0) { + port->downdelay = 0; } /* Get rid of deleted interfaces and add new interfaces. */ - for (i = 0; i < port->n_ifaces; i++) { - struct iface *iface = port->ifaces[i]; - if (!svec_contains(&new_ifaces, iface->name)) { - iface_destroy(iface); - } else { - i++; + SHASH_FOR_EACH (node, &old_ifaces) { + if (!shash_find(&new_ifaces, node->name)) { + iface_destroy(node->data); } } - for (i = 0; i < new_ifaces.n; i++) { - const char *name = new_ifaces.names[i]; - if (!svec_contains(&old_ifaces, name)) { - iface_create(port, name); + SHASH_FOR_EACH (node, &new_ifaces) { + const struct ovsrec_interface *if_cfg = node->data; + struct iface *iface; + + iface = shash_find_data(&old_ifaces, if_cfg->name); + if (!iface) { + iface_create(port, if_cfg); + } else { + iface->cfg = if_cfg; } } /* Get VLAN tag. */ vlan = -1; - if (cfg_has("vlan.%s.tag", port->name)) { - if (!bonded) { - vlan = cfg_get_vlan(0, "vlan.%s.tag", port->name); + if (cfg->tag) { + if (port->n_ifaces < 2) { + vlan = *cfg->tag; if (vlan >= 0 && vlan <= 4095) { VLOG_DBG("port %s: assigning VLAN tag %d", port->name, vlan); + } else { + vlan = -1; } } else { /* It's possible that bonded, VLAN-tagged ports make sense. Maybe @@@ -3114,13 -3098,14 +3115,13 @@@ /* Get trunked VLANs. */ trunks = NULL; if (vlan < 0) { - size_t n_trunks, n_errors; + size_t n_errors; size_t i; trunks = bitmap_allocate(4096); - n_trunks = cfg_count("vlan.%s.trunks", port->name); n_errors = 0; - for (i = 0; i < n_trunks; i++) { - int trunk = cfg_get_vlan(i, "vlan.%s.trunks", port->name); + for (i = 0; i < cfg->n_trunks; i++) { + int trunk = cfg->trunks[i]; if (trunk >= 0) { bitmap_set1(trunks, trunk); } else { @@@ -3129,9 -3114,9 +3130,9 @@@ } if (n_errors) { VLOG_ERR("port %s: invalid values for %zu trunk VLANs", - port->name, n_trunks); + port->name, cfg->n_trunks); } - if (n_errors == n_trunks) { + if (n_errors == cfg->n_trunks) { if (n_errors) { VLOG_ERR("port %s: no valid trunks, trunking all VLANs", port->name); @@@ -3139,9 -3124,9 +3140,9 @@@ bitmap_set_multiple(trunks, 0, 4096, 1); } } else { - if (cfg_has("vlan.%s.trunks", port->name)) { - VLOG_ERR("ignoring vlan.%s.trunks in favor of vlan.%s.vlan", - port->name, port->name); + if (cfg->n_trunks) { + VLOG_ERR("port %s: ignoring trunks in favor of implicit vlan", + port->name); } } if (trunks == NULL @@@ -3152,8 -3137,8 +3153,8 @@@ bitmap_free(port->trunks); port->trunks = trunks; - svec_destroy(&old_ifaces); - svec_destroy(&new_ifaces); + shash_destroy(&old_ifaces); + shash_destroy(&new_ifaces); } static void @@@ -3162,7 -3147,7 +3163,7 @@@ port_destroy(struct port *port if (port) { struct bridge *br = port->bridge; struct port *del; - size_t i; + int i; proc_net_compat_update_vlan(port->name, NULL, 0); proc_net_compat_update_bond(port->name, NULL); @@@ -3233,7 -3218,6 +3234,7 @@@ port_update_bonding(struct port *port free(port->bond_hash); port->bond_hash = NULL; port->bond_compat_is_stale = true; + port->bond_fake_iface = false; } } else { if (!port->bond_hash) { @@@ -3249,7 -3233,6 +3250,7 @@@ bond_choose_active_iface(port); } port->bond_compat_is_stale = true; + port->bond_fake_iface = port->cfg->bond_fake_iface; } } @@@ -3309,10 -3292,10 +3310,10 @@@ port_update_bond_compat(struct port *po netdev_get_etheraddr(iface->netdev, slave->mac); } - if (cfg_get_bool(0, "bonding.%s.fake-iface", port->name)) { + if (port->bond_fake_iface) { struct netdev *bond_netdev; - if (!netdev_open(port->name, NETDEV_ETH_TYPE_NONE, &bond_netdev)) { + if (!netdev_open_default(port->name, &bond_netdev)) { if (bond.up) { netdev_turn_flags_on(bond_netdev, NETDEV_UP, true); } else { @@@ -3365,14 -3348,12 +3366,14 @@@ port_update_vlan_compat(struct port *po /* Interface functions. */ -static void -iface_create(struct port *port, const char *name) +static struct iface * +iface_create(struct port *port, const struct ovsrec_interface *if_cfg) { struct iface *iface; + char *name = if_cfg->name; + int error; - iface = xcalloc(1, sizeof *iface); + iface = xzalloc(sizeof *iface); iface->port = port; iface->port_ifidx = port->n_ifaces; iface->name = xstrdup(name); @@@ -3380,7 -3361,6 +3381,7 @@@ iface->tag = tag_create_random(); iface->delay_expires = LLONG_MAX; iface->netdev = NULL; + iface->cfg = if_cfg; if (port->n_ifaces >= port->allocated_ifaces) { port->ifaces = x2nrealloc(port->ifaces, &port->allocated_ifaces, @@@ -3391,21 -3371,9 +3392,21 @@@ port->bridge->has_bonded_ports = true; } + /* Attempt to create the network interface in case it + * doesn't exist yet. */ + if (!iface_is_internal(port->bridge, iface->name)) { + error = set_up_iface(if_cfg, iface, true); + if (error) { + VLOG_WARN("could not create iface %s: %s", iface->name, + strerror(error)); + } + } + VLOG_DBG("attached network device %s to port %s", iface->name, port->name); bridge_flush(port->bridge); + + return iface; } static void @@@ -3425,6 -3393,8 +3426,6 @@@ iface_destroy(struct iface *iface del->port_ifidx = iface->port_ifidx; netdev_close(iface->netdev); - free(iface->name); - free(iface); if (del_active) { ofproto_revalidate(port->bridge->ofproto, port->active_iface_tag); @@@ -3432,9 -3402,6 +3433,9 @@@ bond_send_learning_packets(port); } + free(iface->name); + free(iface); + bridge_flush(port->bridge); } } @@@ -3473,25 -3440,20 +3474,25 @@@ iface_from_dp_ifidx(const struct bridg * reason why this function takes a name instead of a struct iface: the fake * interfaces created this way do not have a struct iface. */ static bool -iface_is_internal(const struct bridge *br, const char *iface) +iface_is_internal(const struct bridge *br, const char *if_name) { - if (!strcmp(iface, br->name) - || cfg_get_bool(0, "iface.%s.internal", iface)) { + /* XXX wastes time */ + struct iface *iface; + struct port *port; + + if (!strcmp(if_name, br->name)) { return true; } - if (cfg_get_bool(0, "bonding.%s.fake-iface", iface)) { - struct port *port = port_lookup(br, iface); - if (port && port->n_ifaces > 1) { - return true; - } + iface = iface_lookup(br, if_name); + if (iface && !strcmp(iface->cfg->type, "internal")) { + return true; } + port = port_lookup(br, if_name); + if (port && port->n_ifaces > 1 && port->cfg->bond_fake_iface) { + return true; + } return false; } @@@ -3500,9 -3462,11 +3501,9 @@@ static void iface_set_mac(struct iface *iface) { - uint64_t mac = cfg_get_mac(0, "iface.%s.mac", iface->name); - if (mac) { - static uint8_t ea[ETH_ADDR_LEN]; + uint8_t ea[ETH_ADDR_LEN]; - eth_addr_from_uint64(mac, ea); + if (iface->cfg->mac && eth_addr_from_string(iface->cfg->mac, ea)) { if (eth_addr_is_multicast(ea)) { VLOG_ERR("interface %s: cannot set MAC to multicast address", iface->name); @@@ -3524,47 -3488,46 +3525,47 @@@ static void mirror_reconfigure(struct bridge *br) { - struct svec old_mirrors, new_mirrors; - size_t i, n_rspan_vlans; + struct shash old_mirrors, new_mirrors; + struct shash_node *node; unsigned long *rspan_vlans; + int i; - /* Collect old and new mirrors. */ - svec_init(&old_mirrors); - svec_init(&new_mirrors); - cfg_get_subsections(&new_mirrors, "mirror.%s", br->name); + /* Collect old mirrors. */ + shash_init(&old_mirrors); for (i = 0; i < MAX_MIRRORS; i++) { if (br->mirrors[i]) { - svec_add(&old_mirrors, br->mirrors[i]->name); + shash_add(&old_mirrors, br->mirrors[i]->name, br->mirrors[i]); } } - /* Get rid of deleted mirrors and add new mirrors. */ - svec_sort(&old_mirrors); - assert(svec_is_unique(&old_mirrors)); - svec_sort(&new_mirrors); - assert(svec_is_unique(&new_mirrors)); - for (i = 0; i < MAX_MIRRORS; i++) { - struct mirror *m = br->mirrors[i]; - if (m && !svec_contains(&new_mirrors, m->name)) { - mirror_destroy(m); + /* Collect new mirrors. */ + shash_init(&new_mirrors); + for (i = 0; i < br->cfg->n_mirrors; i++) { + struct ovsrec_mirror *cfg = br->cfg->mirrors[i]; + if (!shash_add_once(&new_mirrors, cfg->name, cfg)) { + VLOG_WARN("bridge %s: %s specified twice as mirror", + br->name, cfg->name); } } - for (i = 0; i < new_mirrors.n; i++) { - const char *name = new_mirrors.names[i]; - if (!svec_contains(&old_mirrors, name)) { - mirror_create(br, name); + + /* Get rid of deleted mirrors and add new mirrors. */ + SHASH_FOR_EACH (node, &old_mirrors) { + if (!shash_find(&new_mirrors, node->name)) { + mirror_destroy(node->data); } } - svec_destroy(&old_mirrors); - svec_destroy(&new_mirrors); - - /* Reconfigure all mirrors. */ - for (i = 0; i < MAX_MIRRORS; i++) { - if (br->mirrors[i]) { - mirror_reconfigure_one(br->mirrors[i]); + SHASH_FOR_EACH (node, &new_mirrors) { + struct mirror *mirror = shash_find_data(&old_mirrors, node->name); + if (!mirror) { + mirror = mirror_create(br, node->name); + if (!mirror) { + break; + } } + mirror_reconfigure_one(mirror, node->data); } + shash_destroy(&old_mirrors); + shash_destroy(&new_mirrors); /* Update port reserved status. */ for (i = 0; i < br->n_ports; i++) { @@@ -3577,29 -3540,31 +3578,29 @@@ } } - /* Update learning disabled vlans (for RSPAN). */ + /* Update flooded vlans (for RSPAN). */ rspan_vlans = NULL; - n_rspan_vlans = cfg_count("vlan.%s.disable-learning", br->name); - if (n_rspan_vlans) { + if (br->cfg->n_flood_vlans) { rspan_vlans = bitmap_allocate(4096); - for (i = 0; i < n_rspan_vlans; i++) { - int vlan = cfg_get_vlan(i, "vlan.%s.disable-learning", br->name); - if (vlan >= 0) { + for (i = 0; i < br->cfg->n_flood_vlans; i++) { + int64_t vlan = br->cfg->flood_vlans[i]; + if (vlan >= 0 && vlan < 4096) { bitmap_set1(rspan_vlans, vlan); - VLOG_INFO("bridge %s: disabling learning on vlan %d\n", + VLOG_INFO("bridge %s: disabling learning on vlan %"PRId64, br->name, vlan); } else { - VLOG_ERR("bridge %s: invalid value '%s' for learning disabled " - "VLAN", br->name, - cfg_get_string(i, "vlan.%s.disable-learning", br->name)); + VLOG_ERR("bridge %s: invalid value %"PRId64 "for flood VLAN", + br->name, vlan); } } } - if (mac_learning_set_disabled_vlans(br->ml, rspan_vlans)) { + if (mac_learning_set_flood_vlans(br->ml, rspan_vlans)) { bridge_flush(br); } } -static void +static struct mirror * mirror_create(struct bridge *br, const char *name) { struct mirror *m; @@@ -3609,7 -3574,7 +3610,7 @@@ if (i >= MAX_MIRRORS) { VLOG_WARN("bridge %s: maximum of %d port mirrors reached, " "cannot create %s", br->name, MAX_MIRRORS, name); - return; + return NULL; } if (!br->mirrors[i]) { break; @@@ -3619,18 -3584,16 +3620,18 @@@ VLOG_INFO("created port mirror %s on bridge %s", name, br->name); bridge_flush(br); - br->mirrors[i] = m = xcalloc(1, sizeof *m); + br->mirrors[i] = m = xzalloc(sizeof *m); m->bridge = br; m->idx = i; m->name = xstrdup(name); - svec_init(&m->src_ports); - svec_init(&m->dst_ports); + shash_init(&m->src_ports); + shash_init(&m->dst_ports); m->vlans = NULL; m->n_vlans = 0; m->out_vlan = -1; m->out_port = NULL; + + return m; } static void @@@ -3645,8 -3608,8 +3646,8 @@@ mirror_destroy(struct mirror *m br->ports[i]->dst_mirrors &= ~(MIRROR_MASK_C(1) << m->idx); } - svec_destroy(&m->src_ports); - svec_destroy(&m->dst_ports); + shash_destroy(&m->src_ports); + shash_destroy(&m->dst_ports); free(m->vlans); m->bridge->mirrors[m->idx] = NULL; @@@ -3657,36 -3620,45 +3658,36 @@@ } static void -prune_ports(struct mirror *m, struct svec *ports) +mirror_collect_ports(struct mirror *m, struct ovsrec_port **ports, int n_ports, + struct shash *names) { - struct svec tmp; size_t i; - svec_sort_unique(ports); - - svec_init(&tmp); - for (i = 0; i < ports->n; i++) { - const char *name = ports->names[i]; + for (i = 0; i < n_ports; i++) { + const char *name = ports[i]->name; if (port_lookup(m->bridge, name)) { - svec_add(&tmp, name); + shash_add_once(names, name, NULL); } else { - VLOG_WARN("mirror.%s.%s: cannot match on nonexistent port %s", - m->bridge->name, m->name, name); + VLOG_WARN("bridge %s: mirror %s cannot match on nonexistent " + "port %s", m->bridge->name, m->name, name); } } - svec_swap(ports, &tmp); - svec_destroy(&tmp); } static size_t -prune_vlans(struct mirror *m, struct svec *vlan_strings, int **vlans) +mirror_collect_vlans(struct mirror *m, const struct ovsrec_mirror *cfg, + int **vlans) { - size_t n_vlans, i; - - /* This isn't perfect: it won't combine "0" and "00", and the textual sort - * order won't give us numeric sort order. But that's good enough for what - * we need right now. */ - svec_sort_unique(vlan_strings); + size_t n_vlans; + size_t i; - *vlans = xmalloc(sizeof *vlans * vlan_strings->n); + *vlans = xmalloc(sizeof **vlans * cfg->n_select_vlan); n_vlans = 0; - for (i = 0; i < vlan_strings->n; i++) { - const char *name = vlan_strings->names[i]; - int vlan; - if (!str_to_int(name, 10, &vlan) || vlan < 0 || vlan > 4095) { - VLOG_WARN("mirror.%s.%s.select.vlan: ignoring invalid VLAN %s", - m->bridge->name, m->name, name); + for (i = 0; i < cfg->n_select_vlan; i++) { + int64_t vlan = cfg->select_vlan[i]; + if (vlan < 0 || vlan > 4095) { + VLOG_WARN("bridge %s: mirror %s selects invalid VLAN %"PRId64, + m->bridge->name, m->name, vlan); } else { (*vlans)[n_vlans++] = vlan; } @@@ -3721,10 -3693,13 +3722,10 @@@ port_trunks_any_mirrored_vlan(const str } static void -mirror_reconfigure_one(struct mirror *m) +mirror_reconfigure_one(struct mirror *m, struct ovsrec_mirror *cfg) { - char *pfx = xasprintf("mirror.%s.%s", m->bridge->name, m->name); - struct svec src_ports, dst_ports, ports; - struct svec vlan_strings; + struct shash src_ports, dst_ports; mirror_mask_t mirror_bit; - const char *out_port_name; struct port *out_port; int out_vlan; size_t n_vlans; @@@ -3732,71 -3707,74 +3733,71 @@@ size_t i; bool mirror_all_ports; bool any_ports_specified; + bool any_vlans_specified; /* Get output port. */ - out_port_name = cfg_get_key(0, "mirror.%s.%s.output.port", - m->bridge->name, m->name); - if (out_port_name) { - out_port = port_lookup(m->bridge, out_port_name); + if (cfg->output_port) { + out_port = port_lookup(m->bridge, cfg->output_port->name); if (!out_port) { - VLOG_ERR("%s.output.port: bridge %s does not have a port " - "named %s", pfx, m->bridge->name, out_port_name); + VLOG_ERR("bridge %s: mirror %s outputs to port not on bridge", + m->bridge->name, m->name); mirror_destroy(m); - free(pfx); return; } out_vlan = -1; - if (cfg_has("%s.output.vlan", pfx)) { - VLOG_ERR("%s.output.port and %s.output.vlan both specified; " - "ignoring %s.output.vlan", pfx, pfx, pfx); + if (cfg->output_vlan) { + VLOG_ERR("bridge %s: mirror %s specifies both output port and " + "output vlan; ignoring output vlan", + m->bridge->name, m->name); } - } else if (cfg_has("%s.output.vlan", pfx)) { + } else if (cfg->output_vlan) { out_port = NULL; - out_vlan = cfg_get_vlan(0, "%s.output.vlan", pfx); + out_vlan = *cfg->output_vlan; } else { - VLOG_ERR("%s: neither %s.output.port nor %s.output.vlan specified, " - "but exactly one is required; disabling port mirror %s", - pfx, pfx, pfx, pfx); + VLOG_ERR("bridge %s: mirror %s does not specify output; ignoring", + m->bridge->name, m->name); mirror_destroy(m); - free(pfx); return; } /* Get all the ports, and drop duplicates and ports that don't exist. */ - svec_init(&src_ports); - svec_init(&dst_ports); - svec_init(&ports); - cfg_get_all_keys(&src_ports, "%s.select.src-port", pfx); - cfg_get_all_keys(&dst_ports, "%s.select.dst-port", pfx); - cfg_get_all_keys(&ports, "%s.select.port", pfx); - any_ports_specified = src_ports.n || dst_ports.n || ports.n; - svec_append(&src_ports, &ports); - svec_append(&dst_ports, &ports); - svec_destroy(&ports); - prune_ports(m, &src_ports); - prune_ports(m, &dst_ports); - if (any_ports_specified && !src_ports.n && !dst_ports.n) { - VLOG_ERR("%s: none of the specified ports exist; " - "disabling port mirror %s", pfx, pfx); + shash_init(&src_ports); + shash_init(&dst_ports); + mirror_collect_ports(m, cfg->select_src_port, cfg->n_select_src_port, + &src_ports); + mirror_collect_ports(m, cfg->select_dst_port, cfg->n_select_dst_port, + &dst_ports); + any_ports_specified = cfg->n_select_dst_port || cfg->n_select_dst_port; + if (any_ports_specified + && shash_is_empty(&src_ports) && shash_is_empty(&dst_ports)) { + VLOG_ERR("bridge %s: disabling mirror %s since none of the specified " + "selection ports exists", m->bridge->name, m->name); mirror_destroy(m); goto exit; } /* Get all the vlans, and drop duplicate and invalid vlans. */ - svec_init(&vlan_strings); - cfg_get_all_keys(&vlan_strings, "%s.select.vlan", pfx); - n_vlans = prune_vlans(m, &vlan_strings, &vlans); - svec_destroy(&vlan_strings); + n_vlans = mirror_collect_vlans(m, cfg, &vlans); + any_vlans_specified = cfg->n_select_vlan > 0; + if (any_vlans_specified && !n_vlans) { + VLOG_ERR("bridge %s: disabling mirror %s since none of the specified " + "VLANs exists", m->bridge->name, m->name); + mirror_destroy(m); + goto exit; + } /* Update mirror data. */ - if (!svec_equal(&m->src_ports, &src_ports) - || !svec_equal(&m->dst_ports, &dst_ports) + if (!shash_equal_keys(&m->src_ports, &src_ports) + || !shash_equal_keys(&m->dst_ports, &dst_ports) || m->n_vlans != n_vlans || memcmp(m->vlans, vlans, sizeof *vlans * n_vlans) || m->out_port != out_port || m->out_vlan != out_vlan) { bridge_flush(m->bridge); } - svec_swap(&m->src_ports, &src_ports); - svec_swap(&m->dst_ports, &dst_ports); + shash_swap(&m->src_ports, &src_ports); + shash_swap(&m->dst_ports, &dst_ports); free(m->vlans); m->vlans = vlans; m->n_vlans = n_vlans; @@@ -3804,7 -3782,7 +3805,7 @@@ m->out_vlan = out_vlan; /* If no selection criteria have been given, mirror for all ports. */ - mirror_all_ports = (!m->src_ports.n) && (!m->dst_ports.n) && (!m->n_vlans); + mirror_all_ports = !any_ports_specified && !any_vlans_specified; /* Update ports. */ mirror_bit = MIRROR_MASK_C(1) << m->idx; @@@ -3812,7 -3790,7 +3813,7 @@@ struct port *port = m->bridge->ports[i]; if (mirror_all_ports - || svec_contains(&m->src_ports, port->name) + || shash_find(&m->src_ports, port->name) || (m->n_vlans && (!port->vlan ? port_trunks_any_mirrored_vlan(m, port) @@@ -3822,7 -3800,7 +3823,7 @@@ port->src_mirrors &= ~mirror_bit; } - if (mirror_all_ports || svec_contains(&m->dst_ports, port->name)) { + if (mirror_all_ports || shash_find(&m->dst_ports, port->name)) { port->dst_mirrors |= mirror_bit; } else { port->dst_mirrors &= ~mirror_bit; @@@ -3831,6 -3809,216 +3832,6 @@@ /* Clean up. */ exit: - svec_destroy(&src_ports); - svec_destroy(&dst_ports); - free(pfx); -} - -/* Spanning tree protocol. */ - -static void brstp_update_port_state(struct port *); - -static void -brstp_send_bpdu(struct ofpbuf *pkt, int port_no, void *br_) -{ - struct bridge *br = br_; - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); - struct iface *iface = iface_from_dp_ifidx(br, port_no); - if (!iface) { - VLOG_WARN_RL(&rl, "%s: cannot send BPDU on unknown port %d", - br->name, port_no); - } else { - struct eth_header *eth = pkt->l2; - - netdev_get_etheraddr(iface->netdev, eth->eth_src); - if (eth_addr_is_zero(eth->eth_src)) { - VLOG_WARN_RL(&rl, "%s: cannot send BPDU on port %d " - "with unknown MAC", br->name, port_no); - } else { - union ofp_action action; - flow_t flow; - - memset(&action, 0, sizeof action); - action.type = htons(OFPAT_OUTPUT); - action.output.len = htons(sizeof action); - action.output.port = htons(port_no); - - flow_extract(pkt, ODPP_NONE, &flow); - ofproto_send_packet(br->ofproto, &flow, &action, 1, pkt); - } - } - ofpbuf_delete(pkt); -} - -static void -brstp_reconfigure(struct bridge *br) -{ - size_t i; - - if (!cfg_get_bool(0, "stp.%s.enabled", br->name)) { - if (br->stp) { - stp_destroy(br->stp); - br->stp = NULL; - - bridge_flush(br); - } - } else { - uint64_t bridge_address, bridge_id; - int bridge_priority; - - bridge_address = cfg_get_mac(0, "stp.%s.address", br->name); - if (!bridge_address) { - if (br->stp) { - bridge_address = (stp_get_bridge_id(br->stp) - & ((UINT64_C(1) << 48) - 1)); - } else { - uint8_t mac[ETH_ADDR_LEN]; - eth_addr_random(mac); - bridge_address = eth_addr_to_uint64(mac); - } - } - - if (cfg_is_valid(CFG_INT | CFG_REQUIRED, "stp.%s.priority", - br->name)) { - bridge_priority = cfg_get_int(0, "stp.%s.priority", br->name); - } else { - bridge_priority = STP_DEFAULT_BRIDGE_PRIORITY; - } - - bridge_id = bridge_address | ((uint64_t) bridge_priority << 48); - if (!br->stp) { - br->stp = stp_create(br->name, bridge_id, brstp_send_bpdu, br); - br->stp_last_tick = time_msec(); - bridge_flush(br); - } else { - if (bridge_id != stp_get_bridge_id(br->stp)) { - stp_set_bridge_id(br->stp, bridge_id); - bridge_flush(br); - } - } - - for (i = 0; i < br->n_ports; i++) { - struct port *p = br->ports[i]; - int dp_ifidx; - struct stp_port *sp; - int path_cost, priority; - bool enable; - - if (!p->n_ifaces) { - continue; - } - dp_ifidx = p->ifaces[0]->dp_ifidx; - if (dp_ifidx < 0 || dp_ifidx >= STP_MAX_PORTS) { - continue; - } - - sp = stp_get_port(br->stp, dp_ifidx); - enable = (!cfg_is_valid(CFG_BOOL | CFG_REQUIRED, - "stp.%s.port.%s.enabled", - br->name, p->name) - || cfg_get_bool(0, "stp.%s.port.%s.enabled", - br->name, p->name)); - if (p->is_mirror_output_port) { - enable = false; - } - if (enable != (stp_port_get_state(sp) != STP_DISABLED)) { - bridge_flush(br); /* Might not be necessary. */ - if (enable) { - stp_port_enable(sp); - } else { - stp_port_disable(sp); - } - } - - path_cost = cfg_get_int(0, "stp.%s.port.%s.path-cost", - br->name, p->name); - stp_port_set_path_cost(sp, path_cost ? path_cost : 19 /* XXX */); - - priority = (cfg_is_valid(CFG_INT | CFG_REQUIRED, - "stp.%s.port.%s.priority", - br->name, p->name) - ? cfg_get_int(0, "stp.%s.port.%s.priority", - br->name, p->name) - : STP_DEFAULT_PORT_PRIORITY); - stp_port_set_priority(sp, priority); - } - - brstp_adjust_timers(br); - } - for (i = 0; i < br->n_ports; i++) { - brstp_update_port_state(br->ports[i]); - } -} - -static void -brstp_update_port_state(struct port *p) -{ - struct bridge *br = p->bridge; - enum stp_state state; - - /* Figure out new state. */ - state = STP_DISABLED; - if (br->stp && p->n_ifaces > 0) { - int dp_ifidx = p->ifaces[0]->dp_ifidx; - if (dp_ifidx >= 0 && dp_ifidx < STP_MAX_PORTS) { - state = stp_port_get_state(stp_get_port(br->stp, dp_ifidx)); - } - } - - /* Update state. */ - if (p->stp_state != state) { - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 10); - VLOG_INFO_RL(&rl, "port %s: STP state changed from %s to %s", - p->name, stp_state_name(p->stp_state), - stp_state_name(state)); - if (p->stp_state == STP_DISABLED) { - bridge_flush(br); - } else { - ofproto_revalidate(p->bridge->ofproto, p->stp_state_tag); - } - p->stp_state = state; - p->stp_state_tag = (p->stp_state == STP_DISABLED ? 0 - : tag_create_random()); - } -} - -static void -brstp_adjust_timers(struct bridge *br) -{ - int hello_time = cfg_get_int(0, "stp.%s.hello-time", br->name); - int max_age = cfg_get_int(0, "stp.%s.max-age", br->name); - int forward_delay = cfg_get_int(0, "stp.%s.forward-delay", br->name); - - stp_set_hello_time(br->stp, hello_time ? hello_time : 2000); - stp_set_max_age(br->stp, max_age ? max_age : 20000); - stp_set_forward_delay(br->stp, forward_delay ? forward_delay : 15000); -} - -static void -brstp_run(struct bridge *br) -{ - if (br->stp) { - long long int now = time_msec(); - long long int elapsed = now - br->stp_last_tick; - struct stp_port *sp; - - if (elapsed > 0) { - stp_tick(br->stp, MIN(INT_MAX, elapsed)); - br->stp_last_tick = now; - } - while (stp_get_changed_port(br->stp, &sp)) { - struct port *p = port_from_dp_ifidx(br, stp_port_no(sp)); - if (p) { - brstp_update_port_state(p); - } - } - } -} - -static void -brstp_wait(struct bridge *br) -{ - if (br->stp) { - poll_timer_wait(1000); - } + shash_destroy(&src_ports); + shash_destroy(&dst_ports); } diff --combined xenserver/opt_xensource_libexec_InterfaceReconfigureVswitch.py index 6e5deb7ed,7ba0d24d1..59bce6f57 --- a/xenserver/opt_xensource_libexec_InterfaceReconfigureVswitch.py +++ b/xenserver/opt_xensource_libexec_InterfaceReconfigureVswitch.py @@@ -1,5 -1,5 +1,5 @@@ # Copyright (c) 2008,2009 Citrix Systems, Inc. -# Copyright (c) 2009 Nicira Networks. +# Copyright (c) 2009,2010 Nicira Networks. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published @@@ -118,20 -118,25 +118,20 @@@ A VLAN PIF cannot be a datapath PIF return [pif] def datapath_deconfigure_physical(netdev): - # The use of [!0-9] keeps an interface of 'eth0' from matching - # VLANs attached to eth0 (such as 'eth0.123'), which are distinct - # interfaces. - return ['--del-match=bridge.*.port=%s' % netdev, - '--del-match=port.%s.[!0-9]*' % netdev, - '--del-match=bonding.*.slave=%s' % netdev, - '--del-match=iface.%s.[!0-9]*' % netdev] + return ['--', '--if-exists', 'del-port', netdev] def datapath_configure_bond(pif,slaves): + bridge = pif_bridge_name(pif) pifrec = db().get_pif_record(pif) interface = pif_netdev_name(pif) - argv = ['--del-match=bonding.%s.[!0-9]*' % interface] - argv += ["--add=bonding.%s.slave=%s" % (interface, pif_netdev_name(slave)) - for slave in slaves] - argv += ['--add=bonding.%s.fake-iface=true' % interface] + argv = ['--', '--fake-iface', 'add-bond', bridge, interface] + for slave in slaves: + argv += [pif_netdev_name(slave)] - if pifrec['MAC'] != "": - argv += ['--add=port.%s.mac=%s' % (interface, pifrec['MAC'])] + # XXX need ovs-vsctl support + #if pifrec['MAC'] != "": + # argv += ['--add=port.%s.mac=%s' % (interface, pifrec['MAC'])] # Bonding options. bond_options = { @@@ -149,33 -154,44 +149,33 @@@ overrides = map(lambda (key,val): (key[5:], val), overrides) bond_options.update(overrides) for (name,val) in bond_options.items(): - argv += ["--add=bonding.%s.%s=%s" % (interface, name, val)] + # XXX need ovs-vsctl support for bond options + #argv += ["--add=bonding.%s.%s=%s" % (interface, name, val)] + pass return argv def datapath_deconfigure_bond(netdev): - # The use of [!0-9] keeps an interface of 'eth0' from matching - # VLANs attached to eth0 (such as 'eth0.123'), which are distinct - # interfaces. - return ['--del-match=bonding.%s.[!0-9]*' % netdev, - '--del-match=port.%s.[!0-9]*' % netdev] + return ['--', '--if-exists', 'del-port', netdev] def datapath_deconfigure_ipdev(interface): - # The use of [!0-9] keeps an interface of 'eth0' from matching - # VLANs attached to eth0 (such as 'eth0.123'), which are distinct - # interfaces. - return ['--del-match=bridge.*.port=%s' % interface, - '--del-match=port.%s.[!0-9]*' % interface, - '--del-match=iface.%s.[!0-9]*' % interface, - '--del-match=vlan.%s.trunks=*' % interface, - '--del-match=vlan.%s.tag=*' % interface] + return ['--', '--if-exists', 'del-port', interface] def datapath_modify_config(commands): #log("modifying configuration:") #for c in commands: # log(" %s" % c) - - rc = run_command(['/usr/bin/ovs-cfg-mod', '-vANY:console:emer', - '-F', '/etc/ovs-vswitchd.conf'] - + [c for c in commands if c[0] != '#'] + ['-c']) - if not rc: + + rc = run_command(['/usr/bin/ovs-vsctl'] + ['--timeout=20'] + + [c for c in commands if not c.startswith('#')]) + if not rc: raise Error("Failed to modify vswitch configuration") - run_command(['/sbin/service', 'vswitch', 'reload']) return True # # Toplevel Datapath Configuration. # -def configure_datapath(pif): +def configure_datapath(pif, parent=None, vlan=None): """Bring up the datapath configuration for PIF. Should be careful not to glitch existing users of the datapath, e.g. other VLANs etc. @@@ -183,12 -199,12 +183,12 @@@ Should take care of tearing down other PIFs which encompass common physical devices. Returns a tuple containing - - A list containing the necessary cfgmod command line arguments + - A list containing the necessary vsctl command line arguments - A list of additional devices which should be brought up after the configuration is applied. """ - cfgmod_argv = [] + vsctl_argv = [] extra_up_ports = [] bridge = pif_bridge_name(pif) @@@ -244,46 -260,42 +244,46 @@@ #ifdown(b) # XXX netdev_down(b) - cfgmod_argv += ['# remove bridge %s' % b] - cfgmod_argv += ['--del-match=bridge.%s.*' % b] + vsctl_argv += ['# remove bridge %s' % b] + vsctl_argv += ['--', '--if-exists', 'del-br', b] for n in extra_down_ports: dev = pif_netdev_name(n) - cfgmod_argv += ['# deconfigure sibling physical device %s' % dev] - cfgmod_argv += datapath_deconfigure_physical(dev) + vsctl_argv += ['# deconfigure sibling physical device %s' % dev] + vsctl_argv += datapath_deconfigure_physical(dev) netdev_down(dev) for n in extra_down_bonds: dev = pif_netdev_name(n) - cfgmod_argv += ['# deconfigure bond device %s' % dev] - cfgmod_argv += datapath_deconfigure_bond(dev) + vsctl_argv += ['# deconfigure bond device %s' % dev] + vsctl_argv += datapath_deconfigure_bond(dev) netdev_down(dev) for p in physical_devices: dev = pif_netdev_name(p) - cfgmod_argv += ['# deconfigure physical port %s' % dev] - cfgmod_argv += datapath_deconfigure_physical(dev) + vsctl_argv += ['# deconfigure physical port %s' % dev] + vsctl_argv += datapath_deconfigure_physical(dev) + + if parent and datapath: + vsctl_argv += ['--', '--may-exist', 'add-br', bridge, parent, vlan] + else: + vsctl_argv += ['--', '--may-exist', 'add-br', bridge] + if len(physical_devices) > 1: - cfgmod_argv += ['# deconfigure bond %s' % pif_netdev_name(pif)] - cfgmod_argv += datapath_deconfigure_bond(pif_netdev_name(pif)) - cfgmod_argv += ['--del-entry=bridge.%s.port=%s' % (bridge,pif_netdev_name(pif))] - cfgmod_argv += ['# configure bond %s' % pif_netdev_name(pif)] - cfgmod_argv += datapath_configure_bond(pif, physical_devices) - cfgmod_argv += ['--add=bridge.%s.port=%s' % (bridge,pif_netdev_name(pif)) ] + vsctl_argv += ['# deconfigure bond %s' % pif_netdev_name(pif)] + vsctl_argv += datapath_deconfigure_bond(pif_netdev_name(pif)) + vsctl_argv += ['# configure bond %s' % pif_netdev_name(pif)] + vsctl_argv += datapath_configure_bond(pif, physical_devices) extra_up_ports += [pif_netdev_name(pif)] else: iface = pif_netdev_name(physical_devices[0]) - cfgmod_argv += ['# add physical device %s' % iface] - cfgmod_argv += ['--add=bridge.%s.port=%s' % (bridge,iface) ] + vsctl_argv += ['# add physical device %s' % iface] + vsctl_argv += ['--', '--may-exist', 'add-port', bridge, iface] - return cfgmod_argv,extra_up_ports + return vsctl_argv,extra_up_ports def deconfigure_datapath(pif): - cfgmod_argv = [] + vsctl_argv = [] bridge = pif_bridge_name(pif) @@@ -294,18 -306,18 +294,18 @@@ for p in physical_devices: dev = pif_netdev_name(p) - cfgmod_argv += ['# deconfigure physical port %s' % dev] - cfgmod_argv += datapath_deconfigure_physical(dev) + vsctl_argv += ['# deconfigure physical port %s' % dev] + vsctl_argv += datapath_deconfigure_physical(dev) netdev_down(dev) if len(physical_devices) > 1: - cfgmod_argv += ['# deconfigure bond %s' % pif_netdev_name(pif)] - cfgmod_argv += datapath_deconfigure_bond(pif_netdev_name(pif)) + vsctl_argv += ['# deconfigure bond %s' % pif_netdev_name(pif)] + vsctl_argv += datapath_deconfigure_bond(pif_netdev_name(pif)) - cfgmod_argv += ['# deconfigure bridge %s' % bridge] - cfgmod_argv += ['--del-match=bridge.%s.*' % bridge] + vsctl_argv += ['# deconfigure bridge %s' % bridge] + vsctl_argv += ['--', '--if-exists', 'del-br', bridge] - return cfgmod_argv + return vsctl_argv # # @@@ -326,22 -338,21 +326,23 @@@ class DatapathVswitch(Datapath) cfg.write("TYPE=Ethernet\n") def preconfigure(self, parent): - cfgmod_argv = [] + vsctl_argv = [] extra_ports = [] pifrec = db().get_pif_record(self._pif) + dprec = db().get_pif_record(self._dp) ipdev = self._ipdev bridge = pif_bridge_name(self._dp) - c,e = configure_datapath(self._dp) - cfgmod_argv += c + if pif_is_vlan(self._pif): + datapath = pif_datapath(self._pif) + c,e = configure_datapath(self._dp, datapath, pifrec['VLAN']) + else: + c,e = configure_datapath(self._dp) + vsctl_argv += c extra_ports += e - cfgmod_argv += ['# configure xs-network-uuids'] - cfgmod_argv += ['--del-match=bridge.%s.xs-network-uuids=*' % bridge] - + xs_network_uuids = [] for nwpif in db().get_pifs_by_device(db().get_pif_record(self._pif)['device']): rec = db().get_pif_record(nwpif) @@@ -352,25 -363,23 +353,25 @@@ # log("Network PIF %s not currently attached (%s)" % (rec['uuid'],pifrec['uuid'])) # continue nwrec = db().get_network_record(rec['network']) - cfgmod_argv += ['--add=bridge.%s.xs-network-uuids=%s' % (bridge, nwrec['uuid'])] - - cfgmod_argv += ["# deconfigure ipdev %s" % ipdev] - cfgmod_argv += datapath_deconfigure_ipdev(ipdev) - cfgmod_argv += ["# reconfigure ipdev %s" % ipdev] - cfgmod_argv += ['--add=bridge.%s.port=%s' % (bridge, ipdev)] - if bridge == ipdev: - cfgmod_argv += ['--add=bridge.%s.mac=%s' % (bridge, dprec['MAC'])] - else: - cfgmod_argv += ['--add=iface.%s.mac=%s' % (ipdev, dprec['MAC'])] - - if pif_is_vlan(self._pif): - cfgmod_argv += ['--add=vlan.%s.tag=%s' % (ipdev, pifrec['VLAN'])] - cfgmod_argv += ['--add=iface.%s.internal=true' % (ipdev)] - cfgmod_argv += ['--add=iface.%s.fake-bridge=true' % (ipdev)] + xs_network_uuids += [nwrec['uuid']] + + vsctl_argv += ['# configure xs-network-uuids'] + vsctl_argv += ['--', 'br-set-external-id', bridge, + 'xs-network-uuids', ';'.join(xs_network_uuids)] + + if ipdev != bridge: + vsctl_argv += ["# deconfigure ipdev %s" % ipdev] + vsctl_argv += datapath_deconfigure_ipdev(ipdev) + vsctl_argv += ["# reconfigure ipdev %s" % ipdev] + vsctl_argv += ['--', 'add-port', bridge, ipdev] + + # XXX Needs support in ovs-vsctl + #if bridge == ipdev: - # vsctl_argv += ['--add=bridge.%s.mac=%s' % (bridge, pifrec['MAC'])] ++ # vsctl_argv += ['--add=bridge.%s.mac=%s' % (bridge, dprec['MAC'])] + #else: - # vsctl_argv += ['--add=iface.%s.mac=%s' % (ipdev, pifrec['MAC'])] ++ # vsctl_argv += ['--add=iface.%s.mac=%s' % (ipdev, dprec['MAC'])] - self._cfgmod_argv = cfgmod_argv + self._vsctl_argv = vsctl_argv self._extra_ports = extra_ports def bring_down_existing(self): @@@ -398,7 -407,7 +399,7 @@@ if len(offload): run_command(['/sbin/ethtool', '-K', dev] + offload) - datapath_modify_config(self._cfgmod_argv) + datapath_modify_config(self._vsctl_argv) def post(self): for p in self._extra_ports: @@@ -406,7 -415,7 +407,7 @@@ netdev_up(p) def bring_down(self): - cfgmod_argv = [] + vsctl_argv = [] dp = self._dp ipdev = self._ipdev @@@ -415,12 -424,12 +416,12 @@@ #nw = db().get_pif_record(self._pif)['network'] #nwrec = db().get_network_record(nw) - #cfgmod_argv += ['# deconfigure xs-network-uuids'] - #cfgmod_argv += ['--del-entry=bridge.%s.xs-network-uuids=%s' % (bridge,nwrec['uuid'])] + #vsctl_argv += ['# deconfigure xs-network-uuids'] + #vsctl_argv += ['--del-entry=bridge.%s.xs-network-uuids=%s' % (bridge,nwrec['uuid'])] log("deconfigure ipdev %s on %s" % (ipdev,bridge)) - cfgmod_argv += ["# deconfigure ipdev %s" % ipdev] - cfgmod_argv += datapath_deconfigure_ipdev(ipdev) + vsctl_argv += ["# deconfigure ipdev %s" % ipdev] + vsctl_argv += datapath_deconfigure_ipdev(ipdev) if pif_is_vlan(self._pif): # If the VLAN's slave is attached, leave datapath setup. @@@ -446,5 -455,5 +447,5 @@@ dp = None if dp: - cfgmod_argv += deconfigure_datapath(dp) - datapath_modify_config(cfgmod_argv) + vsctl_argv += deconfigure_datapath(dp) + datapath_modify_config(vsctl_argv)