int brc_sysfs_add_if(struct net_bridge_port *p);
int brc_sysfs_del_if(struct net_bridge_port *p);
-#include <linux/version.h>
-#if LINUX_VERSION_CODE == KERNEL_VERSION(2,6,18)
-#define SUPPORT_SYSFS 1
-#else
-/* We only support sysfs on Linux 2.6.18 because that's the only place we
- * really need it (on Xen, for brcompat) and it's a big pain to try to support
- * multiple versions. */
-#endif
-
#endif /* brc_sysfs.h */
#include "datapath.h"
#include "dp_dev.h"
-#ifdef SUPPORT_SYSFS
+#ifdef CONFIG_SYSFS
#define to_dev(obj) container_of(obj, struct device, kobj)
/* Hack to attempt to build on more platforms. */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)
-#define to_kobj(d) &(d)->class_dev.kobj
#define BRC_DEVICE_ATTR CLASS_DEVICE_ATTR
+#define DEVICE_PARAMS struct class_device *d
+#define DEVICE_ARGS d
+#define DEV_ATTR(NAME) class_device_attr_##NAME
#else
-#define to_kobj(d) &(d)->dev.kobj
#define BRC_DEVICE_ATTR DEVICE_ATTR
+#define DEVICE_PARAMS struct device *d, struct device_attribute *attr
+#define DEVICE_ARGS d, attr
+#define DEV_ATTR(NAME) dev_attr_##NAME
#endif
/*
* Common code for storing bridge parameters.
*/
-static ssize_t store_bridge_parm(struct class_device *d,
+static ssize_t store_bridge_parm(DEVICE_PARAMS,
const char *buf, size_t len,
void (*set)(struct datapath *, unsigned long))
{
}
-static ssize_t show_forward_delay(struct class_device *d,
- char *buf)
+static ssize_t show_forward_delay(DEVICE_PARAMS, char *buf)
{
#if 0
struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
#endif
}
-static ssize_t store_forward_delay(struct class_device *d,
+static ssize_t store_forward_delay(DEVICE_PARAMS,
const char *buf, size_t len)
{
- return store_bridge_parm(d, buf, len, set_forward_delay);
+ return store_bridge_parm(DEVICE_ARGS, buf, len, set_forward_delay);
}
static BRC_DEVICE_ATTR(forward_delay, S_IRUGO | S_IWUSR,
show_forward_delay, store_forward_delay);
-static ssize_t show_hello_time(struct class_device *d, char *buf)
+static ssize_t show_hello_time(DEVICE_PARAMS, char *buf)
{
#if 0
return sprintf(buf, "%lu\n",
#endif
}
-static ssize_t store_hello_time(struct class_device *d,
+static ssize_t store_hello_time(DEVICE_PARAMS,
const char *buf,
size_t len)
{
- return store_bridge_parm(d, buf, len, set_hello_time);
+ return store_bridge_parm(DEVICE_ARGS, buf, len, set_hello_time);
}
static BRC_DEVICE_ATTR(hello_time, S_IRUGO | S_IWUSR, show_hello_time,
store_hello_time);
-static ssize_t show_max_age(struct class_device *d,
- char *buf)
+static ssize_t show_max_age(DEVICE_PARAMS, char *buf)
{
#if 0
return sprintf(buf, "%lu\n",
#endif
}
-static ssize_t store_max_age(struct class_device *d,
+static ssize_t store_max_age(DEVICE_PARAMS,
const char *buf, size_t len)
{
- return store_bridge_parm(d, buf, len, set_max_age);
+ return store_bridge_parm(DEVICE_ARGS, buf, len, set_max_age);
}
static BRC_DEVICE_ATTR(max_age, S_IRUGO | S_IWUSR, show_max_age, store_max_age);
-static ssize_t show_ageing_time(struct class_device *d,
- char *buf)
+static ssize_t show_ageing_time(DEVICE_PARAMS, char *buf)
{
#if 0
struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
#endif
}
-static ssize_t store_ageing_time(struct class_device *d,
+static ssize_t store_ageing_time(DEVICE_PARAMS,
const char *buf, size_t len)
{
- return store_bridge_parm(d, buf, len, set_ageing_time);
+ return store_bridge_parm(DEVICE_ARGS, buf, len, set_ageing_time);
}
static BRC_DEVICE_ATTR(ageing_time, S_IRUGO | S_IWUSR, show_ageing_time,
store_ageing_time);
-static ssize_t show_stp_state(struct class_device *d,
- char *buf)
+static ssize_t show_stp_state(DEVICE_PARAMS, char *buf)
{
#if 0
struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
}
-static ssize_t store_stp_state(struct class_device *d,
+static ssize_t store_stp_state(DEVICE_PARAMS,
const char *buf,
size_t len)
{
static BRC_DEVICE_ATTR(stp_state, S_IRUGO | S_IWUSR, show_stp_state,
store_stp_state);
-static ssize_t show_priority(struct class_device *d,
- char *buf)
+static ssize_t show_priority(DEVICE_PARAMS, char *buf)
{
#if 0
struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
#endif
}
-static ssize_t store_priority(struct class_device *d,
+static ssize_t store_priority(DEVICE_PARAMS,
const char *buf, size_t len)
{
- return store_bridge_parm(d, buf, len, set_priority);
+ return store_bridge_parm(DEVICE_ARGS, buf, len, set_priority);
}
static BRC_DEVICE_ATTR(priority, S_IRUGO | S_IWUSR, show_priority, store_priority);
-static ssize_t show_root_id(struct class_device *d,
- char *buf)
+static ssize_t show_root_id(DEVICE_PARAMS, char *buf)
{
#if 0
return br_show_bridge_id(buf, &to_bridge(d)->designated_root);
}
static BRC_DEVICE_ATTR(root_id, S_IRUGO, show_root_id, NULL);
-static ssize_t show_bridge_id(struct class_device *d,
- char *buf)
+static ssize_t show_bridge_id(DEVICE_PARAMS, char *buf)
{
struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
const unsigned char *addr = dp->ports[ODPP_LOCAL]->dev->dev_addr;
}
static BRC_DEVICE_ATTR(bridge_id, S_IRUGO, show_bridge_id, NULL);
-static ssize_t show_root_port(struct class_device *d,
- char *buf)
+static ssize_t show_root_port(DEVICE_PARAMS, char *buf)
{
#if 0
return sprintf(buf, "%d\n", to_bridge(d)->root_port);
}
static BRC_DEVICE_ATTR(root_port, S_IRUGO, show_root_port, NULL);
-static ssize_t show_root_path_cost(struct class_device *d,
- char *buf)
+static ssize_t show_root_path_cost(DEVICE_PARAMS, char *buf)
{
#if 0
return sprintf(buf, "%d\n", to_bridge(d)->root_path_cost);
}
static BRC_DEVICE_ATTR(root_path_cost, S_IRUGO, show_root_path_cost, NULL);
-static ssize_t show_topology_change(struct class_device *d,
- char *buf)
+static ssize_t show_topology_change(DEVICE_PARAMS, char *buf)
{
#if 0
return sprintf(buf, "%d\n", to_bridge(d)->topology_change);
}
static BRC_DEVICE_ATTR(topology_change, S_IRUGO, show_topology_change, NULL);
-static ssize_t show_topology_change_detected(struct class_device *d,
- char *buf)
+static ssize_t show_topology_change_detected(DEVICE_PARAMS, char *buf)
{
#if 0
struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
static BRC_DEVICE_ATTR(topology_change_detected, S_IRUGO,
show_topology_change_detected, NULL);
-static ssize_t show_hello_timer(struct class_device *d,
- char *buf)
+static ssize_t show_hello_timer(DEVICE_PARAMS, char *buf)
{
#if 0
struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
}
static BRC_DEVICE_ATTR(hello_timer, S_IRUGO, show_hello_timer, NULL);
-static ssize_t show_tcn_timer(struct class_device *d,
- char *buf)
+static ssize_t show_tcn_timer(DEVICE_PARAMS, char *buf)
{
#if 0
struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
}
static BRC_DEVICE_ATTR(tcn_timer, S_IRUGO, show_tcn_timer, NULL);
-static ssize_t show_topology_change_timer(struct class_device *d,
- char *buf)
+static ssize_t show_topology_change_timer(DEVICE_PARAMS, char *buf)
{
#if 0
struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
static BRC_DEVICE_ATTR(topology_change_timer, S_IRUGO, show_topology_change_timer,
NULL);
-static ssize_t show_gc_timer(struct class_device *d,
- char *buf)
+static ssize_t show_gc_timer(DEVICE_PARAMS, char *buf)
{
#if 0
struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
}
static BRC_DEVICE_ATTR(gc_timer, S_IRUGO, show_gc_timer, NULL);
-static ssize_t show_group_addr(struct class_device *d,
- char *buf)
+static ssize_t show_group_addr(DEVICE_PARAMS, char *buf)
{
#if 0
struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
#endif
}
-static ssize_t store_group_addr(struct class_device *d,
+static ssize_t store_group_addr(DEVICE_PARAMS,
const char *buf, size_t len)
{
struct datapath *dp = dp_dev_get_dp(to_net_dev(d));
show_group_addr, store_group_addr);
static struct attribute *bridge_attrs[] = {
- &class_device_attr_forward_delay.attr,
- &class_device_attr_hello_time.attr,
- &class_device_attr_max_age.attr,
- &class_device_attr_ageing_time.attr,
- &class_device_attr_stp_state.attr,
- &class_device_attr_priority.attr,
- &class_device_attr_bridge_id.attr,
- &class_device_attr_root_id.attr,
- &class_device_attr_root_path_cost.attr,
- &class_device_attr_root_port.attr,
- &class_device_attr_topology_change.attr,
- &class_device_attr_topology_change_detected.attr,
- &class_device_attr_hello_timer.attr,
- &class_device_attr_tcn_timer.attr,
- &class_device_attr_topology_change_timer.attr,
- &class_device_attr_gc_timer.attr,
- &class_device_attr_group_addr.attr,
+ &DEV_ATTR(forward_delay).attr,
+ &DEV_ATTR(hello_time).attr,
+ &DEV_ATTR(max_age).attr,
+ &DEV_ATTR(ageing_time).attr,
+ &DEV_ATTR(stp_state).attr,
+ &DEV_ATTR(priority).attr,
+ &DEV_ATTR(bridge_id).attr,
+ &DEV_ATTR(root_id).attr,
+ &DEV_ATTR(root_path_cost).attr,
+ &DEV_ATTR(root_port).attr,
+ &DEV_ATTR(topology_change).attr,
+ &DEV_ATTR(topology_change_detected).attr,
+ &DEV_ATTR(hello_timer).attr,
+ &DEV_ATTR(tcn_timer).attr,
+ &DEV_ATTR(topology_change_timer).attr,
+ &DEV_ATTR(gc_timer).attr,
+ &DEV_ATTR(group_addr).attr,
NULL
};
*/
int brc_sysfs_add_dp(struct datapath *dp)
{
- struct kobject *kobj = to_kobj(dp->ports[ODPP_LOCAL]->dev);
+ struct kobject *kobj = &dp->ports[ODPP_LOCAL]->dev->NETDEV_DEV_MEMBER.kobj;
int err;
err = sysfs_create_group(kobj, &bridge_group);
err = kobject_register(&dp->ifobj);
if (err) {
pr_info("%s: can't add kobject (directory) %s/%s\n",
- __FUNCTION__, dp_name(dp), dp->ifobj.name);
+ __FUNCTION__, dp_name(dp), kobject_name(&dp->ifobj));
goto out2;
}
#else
- br->ifobj = kobject_create_and_add(SYSFS_BRIDGE_PORT_SUBDIR, kobj);
- if (!br->ifobj) {
+ dp->ifobj = kobject_create_and_add(SYSFS_BRIDGE_PORT_SUBDIR, kobj);
+ if (!dp->ifobj) {
pr_info("%s: can't add kobject (directory) %s/%s\n",
__func__, dp_name(dp), SYSFS_BRIDGE_PORT_SUBDIR);
goto out2;
int brc_sysfs_del_dp(struct datapath *dp)
{
- struct kobject *kobj = to_kobj(dp->ports[ODPP_LOCAL]->dev);
+ struct kobject *kobj = &dp->ports[ODPP_LOCAL]->dev->NETDEV_DEV_MEMBER.kobj;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
kobject_unregister(&dp->ifobj);
return 0;
}
-#else /* !SUPPORT_SYSFS */
+#else /* !CONFIG_SYSFS */
int brc_sysfs_add_dp(struct datapath *dp) { return 0; }
int brc_sysfs_del_dp(struct datapath *dp) { return 0; }
int brc_sysfs_add_if(struct net_bridge_port *p) { return 0; }
kfree(p);
return 0;
}
-#endif /* !SUPPORT_SYSFS */
+#endif /* !CONFIG_SYSFS */
#include "brc_sysfs.h"
#include "datapath.h"
-#ifdef SUPPORT_SYSFS
+#ifdef CONFIG_SYSFS
struct brport_attribute {
struct attribute attr;
struct brport_attribute **a;
int err;
- kobject_init(&p->kobj);
- kobject_set_name(&p->kobj, SYSFS_BRIDGE_PORT_ATTR);
- p->kobj.ktype = &brport_ktype;
- p->kobj.kset = NULL;
- p->kobj.parent = &(p->dev->class_dev.kobj);
-
- err = kobject_add(&p->kobj);
+ err = kobject_init_and_add(&p->kobj, &brport_ktype,
+ &(p->dev->NETDEV_DEV_MEMBER.kobj),
+ SYSFS_BRIDGE_PORT_ATTR);
if (err)
- goto err_put;
+ goto err;
err = sysfs_create_link(&p->kobj,
- &dp->ports[ODPP_LOCAL]->dev->class_dev.kobj,
+ &dp->ports[ODPP_LOCAL]->dev->NETDEV_DEV_MEMBER.kobj,
SYSFS_BRIDGE_PORT_LINK);
if (err)
goto err_del;
goto err_del;
}
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
err = sysfs_create_link(&dp->ifobj, &p->kobj, p->dev->name);
+#else
+ err = sysfs_create_link(dp->ifobj, &p->kobj, p->dev->name);
+#endif
if (err)
goto err_del;
err_del:
kobject_del(&p->kobj);
-err_put:
kobject_put(&p->kobj);
+err:
return err;
}
return 0;
}
-#endif /* SUPPORT_SYSFS */
+#endif /* CONFIG_SYSFS */
{
if (!try_module_get(THIS_MODULE))
return -ENODEV;
-#ifdef SUPPORT_SYSFS
brc_sysfs_add_dp(dp);
-#endif
return 0;
}
int brc_del_dp(struct datapath *dp)
{
-#ifdef SUPPORT_SYSFS
brc_sysfs_del_dp(dp);
-#endif
module_put(THIS_MODULE);
return 0;
dp_del_dp_hook = brc_del_dp;
/* Register hooks for interface adds and deletes */
-#ifdef SUPPORT_SYSFS
dp_add_if_hook = brc_sysfs_add_if;
dp_del_if_hook = brc_sysfs_del_if;
-#endif
/* Randomize the initial sequence number. This is not a security
* feature; it only helps avoid crossed wires between userspace and
{
ASSERT_RTNL();
-#ifdef SUPPORT_SYSFS
- if (p->port_no != ODPP_LOCAL && dp_del_if_hook)
+ if (p->port_no != ODPP_LOCAL && dp_del_if_hook) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
sysfs_remove_link(&p->dp->ifobj, p->dev->name);
+#else
+ sysfs_remove_link(p->dp->ifobj, p->dev->name);
#endif
+ }
dp_ifinfo_notify(RTM_DELLINK, p);
p->dp->n_ports--;
#endif
#ifdef CONFIG_XEN
+#if LINUX_VERSION_CODE == KERNEL_VERSION(2,6,18)
/* 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. */
out:
return -EPROTO;
}
-#endif
+#endif /* linux == 2.6.18 */
+#endif /* CONFIG_XEN */
int
dp_output_control(struct datapath *dp, struct sk_buff *skb, int queue_no,
#include <linux/netdevice.h>
#include <linux/workqueue.h>
#include <linux/skbuff.h>
+#include <linux/version.h>
#include "flow.h"
#include "brc_sysfs.h"
struct mutex mutex;
int dp_idx;
-#ifdef SUPPORT_SYSFS
+#ifdef CONFIG_SYSFS
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
struct kobject ifobj;
+#else
+ struct kobject *ifobj;
+#endif
#endif
int drop_frags;
u16 port_no;
struct datapath *dp;
struct net_device *dev;
-#ifdef SUPPORT_SYSFS
+#ifdef CONFIG_SYSFS
struct kobject kobj;
#endif
struct list_head node; /* Element in datapath.ports. */
linux-2.6/compat-2.6/include/linux/ipv6.h \
linux-2.6/compat-2.6/include/linux/jiffies.h \
linux-2.6/compat-2.6/include/linux/kernel.h \
+ linux-2.6/compat-2.6/include/linux/kobject.h \
linux-2.6/compat-2.6/include/linux/log2.h \
linux-2.6/compat-2.6/include/linux/lockdep.h \
linux-2.6/compat-2.6/include/linux/mutex.h \
--- /dev/null
+#ifndef __LINUX_KOBJECT_WRAPPER_H
+#define __LINUX_KOBJECT_WRAPPER_H 1
+
+#include_next <linux/kobject.h>
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
+static inline int kobject_init_and_add(struct kobject *kobj,
+ struct kobj_type *ktype,
+ struct kobject *parent,
+ const char *name)
+{
+ kobject_init(kobj);
+ kobject_set_name(kobj, "%s", name);
+ kobj->ktype = ktype;
+ kobj->kset = NULL;
+ kobj->parent = parent;
+
+ return kobject_add(kobj);
+}
+#endif
+
+#endif /* linux/kobject.h wrapper */
struct net;
+/* Before 2.6.21, struct net_device has a "struct class_device" member named
+ * class_dev. Beginning with 2.6.21, struct net_device instead has a "struct
+ * device" member named dev. Otherwise the usage of these members is pretty
+ * much the same. */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)
+#define NETDEV_DEV_MEMBER class_dev
+#else
+#define NETDEV_DEV_MEMBER dev
+#endif
+
#ifndef to_net_dev
-#define to_net_dev(class) container_of(class, struct net_device, class_dev)
+#define to_net_dev(class) \
+ container_of(class, struct net_device, NETDEV_DEV_MEMBER)
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)
#include <poll.h>
#include <stddef.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include <sys/resource.h>
#include <sys/un.h>
: htonl(0)); /* ??? */
}
+/* Opens a non-blocking TCP socket and connects to 'target', which should be a
+ * string in the format "<host>[:<port>]", where <host> is required and <port>
+ * is optional, with 'default_port' assumed if <port> is omitted.
+ *
+ * On success, returns 0 (indicating connection complete) or EAGAIN (indicating
+ * connection in progress), in which case the new file descriptor is stored
+ * into '*fdp'. On failure, returns a positive errno value other than EAGAIN
+ * and stores -1 into '*fdp'.
+ *
+ * If 'sinp' is non-null, then on success the target address is stored into
+ * '*sinp'. */
+int
+tcp_open_active(const char *target_, uint16_t default_port,
+ struct sockaddr_in *sinp, int *fdp)
+{
+ char *target = xstrdup(target_);
+ char *save_ptr = NULL;
+ const char *host_name;
+ const char *port_string;
+ struct sockaddr_in sin;
+ int fd = -1;
+ int error;
+
+ /* Defaults. */
+ memset(&sin, 0, sizeof sin);
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(default_port);
+
+ /* Tokenize. */
+ host_name = strtok_r(target, ":", &save_ptr);
+ port_string = strtok_r(NULL, ":", &save_ptr);
+ if (!host_name) {
+ ovs_error(0, "%s: bad peer name format", target_);
+ error = EAFNOSUPPORT;
+ goto exit;
+ }
+
+ /* Look up IP, port. */
+ error = lookup_ip(host_name, &sin.sin_addr);
+ if (error) {
+ goto exit;
+ }
+ if (port_string && atoi(port_string)) {
+ sin.sin_port = htons(atoi(port_string));
+ }
+
+ /* Create non-blocking socket. */
+ fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (fd < 0) {
+ VLOG_ERR("%s: socket: %s", target_, strerror(errno));
+ error = errno;
+ goto exit;
+ }
+ error = set_nonblocking(fd);
+ if (error) {
+ goto exit_close;
+ }
+
+ /* Connect. */
+ error = connect(fd, (struct sockaddr *) &sin, sizeof sin) == 0 ? 0 : errno;
+ if (error == EINPROGRESS) {
+ error = EAGAIN;
+ } else if (error && error != EAGAIN) {
+ goto exit_close;
+ }
+
+ /* Success: error is 0 or EAGAIN. */
+ goto exit;
+
+exit_close:
+ close(fd);
+exit:
+ if (!error || error == EAGAIN) {
+ if (sinp) {
+ *sinp = sin;
+ }
+ *fdp = fd;
+ } else {
+ *fdp = -1;
+ }
+ free(target);
+ return error;
+}
+
+/* Opens a non-blocking TCP socket, binds to 'target', and listens for incoming
+ * connections. 'target' should be a string in the format "[<port>][:<ip>]",
+ * where both <port> and <ip> are optional. If <port> is omitted, it defaults
+ * to 'default_port'; if <ip> is omitted it defaults to the wildcard IP
+ * address.
+ *
+ * The socket will have SO_REUSEADDR turned on.
+ *
+ * On success, returns a non-negative file descriptor. On failure, returns a
+ * negative errno value. */
+int
+tcp_open_passive(const char *target_, uint16_t default_port)
+{
+ char *target = xstrdup(target_);
+ char *string_ptr = target;
+ struct sockaddr_in sin;
+ const char *host_name;
+ const char *port_string;
+ int fd, error;
+ unsigned int yes = 1;
+
+ /* Address defaults. */
+ memset(&sin, 0, sizeof sin);
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_ANY);
+ sin.sin_port = htons(default_port);
+
+ /* Parse optional port number. */
+ port_string = strsep(&string_ptr, ":");
+ if (port_string && atoi(port_string)) {
+ sin.sin_port = htons(atoi(port_string));
+ }
+
+ /* Parse optional bind IP. */
+ host_name = strsep(&string_ptr, ":");
+ if (host_name && host_name[0]) {
+ error = lookup_ip(host_name, &sin.sin_addr);
+ if (error) {
+ goto exit;
+ }
+ }
+
+ /* Create non-blocking socket, set SO_REUSEADDR. */
+ fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (fd < 0) {
+ error = errno;
+ VLOG_ERR("%s: socket: %s", target_, strerror(error));
+ goto exit;
+ }
+ error = set_nonblocking(fd);
+ if (error) {
+ goto exit_close;
+ }
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes) < 0) {
+ error = errno;
+ VLOG_ERR("%s: setsockopt(SO_REUSEADDR): %s", target_, strerror(error));
+ goto exit_close;
+ }
+
+ /* Bind. */
+ if (bind(fd, (struct sockaddr *) &sin, sizeof sin) < 0) {
+ error = errno;
+ VLOG_ERR("%s: bind: %s", target_, strerror(error));
+ goto exit_close;
+ }
+
+ /* Listen. */
+ if (listen(fd, 10) < 0) {
+ error = errno;
+ VLOG_ERR("%s: listen: %s", target_, strerror(error));
+ goto exit_close;
+ }
+ error = 0;
+ goto exit;
+
+exit_close:
+ close(fd);
+exit:
+ free(target);
+ return error ? -error : fd;
+}
+
int
read_fully(int fd, void *p_, size_t size, size_t *bytes_read)
{
int get_unix_name_len(socklen_t sun_len);
uint32_t guess_netmask(uint32_t ip);
+int tcp_open_active(const char *target, uint16_t default_port,
+ struct sockaddr_in *sinp, int *fdp);
+int tcp_open_passive(const char *target, uint16_t default_port);
+
int read_fully(int fd, void *, size_t, size_t *bytes_read);
int write_fully(int fd, const void *, size_t, size_t *bytes_written);
static int
ssl_open(const char *name, char *suffix, struct vconn **vconnp)
{
- char *save_ptr, *host_name, *port_string;
struct sockaddr_in sin;
- int retval;
- int fd;
-
- retval = ssl_init();
- if (retval) {
- return retval;
- }
-
- host_name = strtok_r(suffix, ":", &save_ptr);
- port_string = strtok_r(NULL, ":", &save_ptr);
- if (!host_name) {
- ovs_error(0, "%s: bad peer name format", name);
- return EAFNOSUPPORT;
- }
-
- memset(&sin, 0, sizeof sin);
- sin.sin_family = AF_INET;
- if (lookup_ip(host_name, &sin.sin_addr)) {
- return ENOENT;
- }
- sin.sin_port = htons(port_string && *port_string ? atoi(port_string)
- : OFP_SSL_PORT);
+ int error, fd;
- /* Create socket. */
- fd = socket(AF_INET, SOCK_STREAM, 0);
- if (fd < 0) {
- VLOG_ERR("%s: socket: %s", name, strerror(errno));
- return errno;
- }
- retval = set_nonblocking(fd);
- if (retval) {
- close(fd);
- return retval;
+ error = ssl_init();
+ if (error) {
+ return error;
}
- /* Connect socket. */
- retval = connect(fd, (struct sockaddr *) &sin, sizeof sin);
- if (retval < 0) {
- if (errno == EINPROGRESS) {
- return new_ssl_vconn(name, fd, CLIENT, STATE_TCP_CONNECTING,
- &sin, vconnp);
- } else {
- int error = errno;
- VLOG_ERR("%s: connect: %s", name, strerror(error));
- close(fd);
- return error;
- }
+ error = tcp_open_active(suffix, OFP_SSL_PORT, &sin, &fd);
+ if (fd >= 0) {
+ int state = error ? STATE_TCP_CONNECTING : STATE_SSL_CONNECTING;
+ return new_ssl_vconn(name, fd, CLIENT, state, &sin, vconnp);
} else {
- return new_ssl_vconn(name, fd, CLIENT, STATE_SSL_CONNECTING,
- &sin, vconnp);
+ VLOG_ERR("%s: connect: %s", name, strerror(error));
+ return error;
}
}
static int
pssl_open(const char *name, char *suffix, struct pvconn **pvconnp)
{
- struct sockaddr_in sin;
struct pssl_pvconn *pssl;
int retval;
int fd;
- unsigned int yes = 1;
retval = ssl_init();
if (retval) {
return retval;
}
- /* Create socket. */
- fd = socket(AF_INET, SOCK_STREAM, 0);
+ fd = tcp_open_passive(suffix, OFP_SSL_PORT);
if (fd < 0) {
- int error = errno;
- VLOG_ERR("%s: socket: %s", name, strerror(error));
- return error;
- }
-
- if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes) < 0) {
- int error = errno;
- VLOG_ERR("%s: setsockopt(SO_REUSEADDR): %s", name, strerror(errno));
- return error;
- }
-
- memset(&sin, 0, sizeof sin);
- sin.sin_family = AF_INET;
- sin.sin_addr.s_addr = htonl(INADDR_ANY);
- sin.sin_port = htons(atoi(suffix) ? atoi(suffix) : OFP_SSL_PORT);
- retval = bind(fd, (struct sockaddr *) &sin, sizeof sin);
- if (retval < 0) {
- int error = errno;
- VLOG_ERR("%s: bind: %s", name, strerror(error));
- close(fd);
- return error;
- }
-
- retval = listen(fd, 10);
- if (retval < 0) {
- int error = errno;
- VLOG_ERR("%s: listen: %s", name, strerror(error));
- close(fd);
- return error;
- }
-
- retval = set_nonblocking(fd);
- if (retval) {
- close(fd);
- return retval;
+ return -fd;
}
pssl = xmalloc(sizeof *pssl);
size_t sa_len, struct vconn **),
struct pvconn **pvconnp)
{
- struct pstream_pvconn *ps;
- int retval;
-
- retval = set_nonblocking(fd);
- if (retval) {
- close(fd);
- return retval;
- }
-
- if (listen(fd, 10) < 0) {
- int error = errno;
- VLOG_ERR("%s: listen: %s", name, strerror(error));
- close(fd);
- return error;
- }
-
- ps = xmalloc(sizeof *ps);
+ struct pstream_pvconn *ps = xmalloc(sizeof *ps);
pvconn_init(&ps->pvconn, &pstream_pvconn_class, name);
ps->fd = fd;
ps->accept_cb = accept_cb;
static int
tcp_open(const char *name, char *suffix, struct vconn **vconnp)
{
- char *save_ptr;
- const char *host_name;
- const char *port_string;
struct sockaddr_in sin;
- int retval;
- int fd;
-
- host_name = strtok_r(suffix, ":", &save_ptr);
- port_string = strtok_r(NULL, ":", &save_ptr);
- if (!host_name) {
- ovs_error(0, "%s: bad peer name format", name);
- return EAFNOSUPPORT;
- }
-
- memset(&sin, 0, sizeof sin);
- sin.sin_family = AF_INET;
- if (lookup_ip(host_name, &sin.sin_addr)) {
- return ENOENT;
- }
- sin.sin_port = htons(port_string ? atoi(port_string) : OFP_TCP_PORT);
-
- fd = socket(AF_INET, SOCK_STREAM, 0);
- if (fd < 0) {
- VLOG_ERR("%s: socket: %s", name, strerror(errno));
- return errno;
- }
-
- retval = set_nonblocking(fd);
- if (retval) {
- close(fd);
- return retval;
- }
+ int fd, error;
- retval = connect(fd, (struct sockaddr *) &sin, sizeof sin);
- if (retval < 0) {
- if (errno == EINPROGRESS) {
- return new_tcp_vconn(name, fd, EAGAIN, &sin, vconnp);
- } else {
- int error = errno;
- VLOG_ERR("%s: connect: %s", name, strerror(error));
- close(fd);
- return error;
- }
+ error = tcp_open_active(suffix, OFP_TCP_PORT, NULL, &fd);
+ if (fd >= 0) {
+ return new_tcp_vconn(name, fd, error, &sin, vconnp);
} else {
- return new_tcp_vconn(name, fd, 0, &sin, vconnp);
+ VLOG_ERR("%s: connect: %s", name, strerror(error));
+ return error;
}
}
struct vconn **vconnp);
static int
-ptcp_open(const char *name, char *suffix, struct pvconn **pvconnp)
+ptcp_open(const char *name UNUSED, char *suffix, struct pvconn **pvconnp)
{
- struct sockaddr_in sin;
- int retval;
int fd;
- unsigned int yes = 1;
- fd = socket(AF_INET, SOCK_STREAM, 0);
+ fd = tcp_open_passive(suffix, OFP_TCP_PORT);
if (fd < 0) {
- VLOG_ERR("%s: socket: %s", name, strerror(errno));
- return errno;
- }
-
- if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes) < 0) {
- VLOG_ERR("%s: setsockopt(SO_REUSEADDR): %s", name, strerror(errno));
- return errno;
- }
-
- memset(&sin, 0, sizeof sin);
- sin.sin_family = AF_INET;
- sin.sin_addr.s_addr = htonl(INADDR_ANY);
- sin.sin_port = htons(atoi(suffix) ? atoi(suffix) : OFP_TCP_PORT);
- retval = bind(fd, (struct sockaddr *) &sin, sizeof sin);
- if (retval < 0) {
- int error = errno;
- VLOG_ERR("%s: bind: %s", name, strerror(error));
- close(fd);
- return error;
+ return -fd;
+ } else {
+ return new_pstream_pvconn("ptcp", fd, ptcp_accept, pvconnp);
}
-
- return new_pstream_pvconn("ptcp", fd, ptcp_accept, pvconnp);
}
static int
static int
punix_open(const char *name UNUSED, char *suffix, struct pvconn **pvconnp)
{
- int fd;
+ int fd, error;
fd = make_unix_socket(SOCK_STREAM, true, true, suffix, NULL);
if (fd < 0) {
return errno;
}
+ error = set_nonblocking(fd);
+ if (error) {
+ close(fd);
+ return error;
+ }
+
+ if (listen(fd, 10) < 0) {
+ error = errno;
+ VLOG_ERR("%s: listen: %s", name, strerror(error));
+ close(fd);
+ return error;
+ }
+
return new_pstream_pvconn("punix", fd, punix_accept, pvconnp);
}
if (passive) {
printf("Passive OpenFlow connection methods:\n");
- printf(" ptcp:[PORT] "
- "listen to TCP PORT (default: %d)\n",
+ printf(" ptcp:[PORT][:IP] "
+ "listen to TCP PORT (default: %d) on IP\n",
OFP_TCP_PORT);
#ifdef HAVE_OPENSSL
- printf(" pssl:[PORT] "
- "listen for SSL on PORT (default: %d)\n",
+ printf(" pssl:[PORT][:IP] "
+ "listen for SSL on PORT (default: %d) on IP\n",
OFP_SSL_PORT);
#endif
printf(" punix:FILE "
if (retval != EAGAIN) {
vconn->state = VCS_DISCONNECTED;
- vconn->error = retval;
+ vconn->error = retval == EOF ? ECONNRESET : retval;
}
}
static int
do_recv(struct vconn *vconn, struct ofpbuf **msgp)
{
- int retval;
-
-again:
- retval = (vconn->class->recv)(vconn, msgp);
+ int retval = (vconn->class->recv)(vconn, msgp);
if (!retval) {
struct ofp_header *oh;
&& oh->type != OFPT_VENDOR)
{
if (vconn->version < 0) {
- if (oh->type == OFPT_PACKET_IN
- || oh->type == OFPT_FLOW_EXPIRED
- || oh->type == OFPT_PORT_STATUS) {
- /* The kernel datapath is stateless and doesn't really
- * support version negotiation, so it can end up sending
- * these asynchronous message before version negotiation
- * is complete. Just ignore them.
- *
- * (After we move OFPT_PORT_STATUS messages from the kernel
- * into secchan, we won't get those here, since secchan
- * does proper version negotiation.) */
- ofpbuf_delete(*msgp);
- goto again;
- }
VLOG_ERR_RL(&bad_ofmsg_rl,
"%s: received OpenFlow message type %"PRIu8" "
"before version negotiation complete",
int error;
char *re;
- re = (!re_ ? xstrdup(vconn_ssl_is_configured() ? "^ssl:.*" : ".*")
+ re = (!re_ ? xstrdup(vconn_ssl_is_configured() ? "^ssl:.*" : "^tcp:.*")
: re_[0] == '^' ? xstrdup(re_) : xasprintf("^%s", re_));
regex = xmalloc(sizeof *regex);
error = regcomp(regex, re, REG_NOSUB | REG_EXTENDED);
/* Set accept_controller_regex. */
if (!s->accept_controller_re) {
- s->accept_controller_re = vconn_ssl_is_configured() ? "^ssl:.*" : ".*";
+ s->accept_controller_re
+ = vconn_ssl_is_configured() ? "^ssl:.*" : "^tcp:.*";
}
/* Mode of operation. */
The default regular expression is \fBssl:.*\fR (meaning that only SSL
controller connections will be accepted) when any of the SSL
configuration options \fB--private-key\fR, \fB--certificate\fR, or
-\fB--ca-cert\fR is specified. The default is \fB.*\fR otherwise
-(meaning that any controller will be accepted).
+\fB--ca-cert\fR is specified. The default is \fB^tcp:.*\fR otherwise
+(meaning that only TCP controller connections will be accepted).
The \fIregex\fR is implicitly anchored at the beginning of the
controller location string, as if it begins with \fB^\fR.
.RS
.TP
-\fBpssl:\fR[\fIport\fR]
+\fBpssl:\fR[\fIport\fR][\fB:\fIip\fR]
Listens for SSL connections on \fIport\fR (default: 6633). The
\fB--private-key\fR, \fB--certificate\fR, and \fB--ca-cert\fR options
are mandatory when this form is used.
+By default, \fB\*(PN\fR listens for connections to any local IP
+address, but \fIip\fR may be specified to listen only for connections
+to the given \fIip\fR.
.TP
-\fBptcp:\fR[\fIport\fR]
+\fBptcp:\fR[\fIport\fR][\fB:\fIip\fR]
Listens for TCP connections on \fIport\fR (default: 6633).
+By default, \fB\*(PN\fR listens for connections to any local IP
+address, but \fIip\fR may be specified to listen only for connections
+to the given \fIip\fR.
.TP
\fBpunix:\fIfile\fR
TESTS_ENVIRONMENT += stp_files='$(stp_files)'
EXTRA_DIST += $(stp_files)
+
+TESTS += tests/test-vconn
+noinst_PROGRAMS += tests/test-vconn
+tests_test_vconn_SOURCES = tests/test-vconn.c
+tests_test_vconn_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+
--- /dev/null
+/*
+ * Copyright (c) 2009 Nicira Networks.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <config.h>
+#include "vconn.h"
+#include <errno.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "poll-loop.h"
+#include "socket-util.h"
+#include "timeval.h"
+#include "util.h"
+#include "vlog.h"
+
+#undef NDEBUG
+#include <assert.h>
+
+struct fake_pvconn {
+ const char *type;
+ char *pvconn_name;
+ char *vconn_name;
+ int fd;
+};
+
+static void
+fpv_create(const char *type, struct fake_pvconn *fpv)
+{
+ 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;
+ 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");
+ }
+
+ /* 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");
+ }
+
+ /* Retrieve socket's port number. */
+ sin_len = sizeof sin;
+ if (getsockname(fd, &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");
+ }
+
+ /* 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;
+ } else {
+ abort();
+ }
+
+ /* Listen. */
+ if (listen(fpv->fd, 0) < 0) {
+ ovs_fatal(errno, "%s: listen failed", fpv->vconn_name);
+ }
+}
+
+static int
+fpv_accept(struct fake_pvconn *fpv)
+{
+ int fd;
+
+ fd = accept(fpv->fd, NULL, NULL);
+ if (fd < 0) {
+ ovs_fatal(errno, "%s: accept failed", fpv->pvconn_name);
+ }
+ return fd;
+}
+
+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;
+ }
+}
+
+static void
+fpv_destroy(struct fake_pvconn *fpv)
+{
+ fpv_close(fpv);
+ free(fpv->pvconn_name);
+ free(fpv->vconn_name);
+}
+
+/* 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(const char *type, int expected_error)
+{
+ struct fake_pvconn fpv;
+ struct vconn *vconn;
+
+ fpv_create(type, &fpv);
+ assert(!vconn_open(fpv.vconn_name, OFP_VERSION, &vconn));
+ fpv_close(&fpv);
+ assert(vconn_connect(vconn) == expected_error);
+ vconn_close(vconn);
+ fpv_destroy(&fpv);
+}
+
+/* Connects to a fake_pvconn with vconn_open(), accepts that connection and
+ * closes it immediately, and verifies that vconn_connect() reports
+ * 'expected_error'. */
+static void
+test_accept_then_close(const char *type, int expected_error)
+{
+ struct fake_pvconn fpv;
+ struct vconn *vconn;
+
+ fpv_create(type, &fpv);
+ assert(!vconn_open(fpv.vconn_name, OFP_VERSION, &vconn));
+ close(fpv_accept(&fpv));
+ fpv_close(&fpv);
+ assert(vconn_connect(vconn) == expected_error);
+ vconn_close(vconn);
+ fpv_destroy(&fpv);
+}
+
+/* Connects to a fake_pvconn with vconn_open(), accepts that connection and
+ * reads the hello message from it, then closes the connection and verifies
+ * that vconn_connect() reports 'expected_error'. */
+static void
+test_read_hello(const char *type, int expected_error)
+{
+ struct fake_pvconn fpv;
+ struct vconn *vconn;
+ int fd;
+
+ fpv_create(type, &fpv);
+ assert(!vconn_open(fpv.vconn_name, OFP_VERSION, &vconn));
+ fd = fpv_accept(&fpv);
+ fpv_destroy(&fpv);
+ assert(!set_nonblocking(fd));
+ for (;;) {
+ struct ofp_header hello;
+ int retval;
+
+ retval = read(fd, &hello, sizeof hello);
+ if (retval == sizeof hello) {
+ assert(hello.version == OFP_VERSION);
+ assert(hello.type == OFPT_HELLO);
+ assert(hello.length == htons(sizeof hello));
+ break;
+ } else {
+ assert(errno == EAGAIN);
+ }
+
+ assert(vconn_connect(vconn) == EAGAIN);
+ vconn_connect_wait(vconn);
+ poll_fd_wait(fd, POLLIN);
+ poll_block();
+ }
+ close(fd);
+ assert(vconn_connect(vconn) == expected_error);
+ vconn_close(vconn);
+}
+
+/* Connects to a fake_pvconn with vconn_open(), accepts that connection and
+ * sends the 'out' bytes in 'out_size' to it (presumably an OFPT_HELLO
+ * message), then verifies that vconn_connect() reports
+ * 'expect_connect_error'. */
+static void
+test_send_hello(const char *type, const void *out, size_t out_size,
+ int expect_connect_error)
+{
+ struct fake_pvconn fpv;
+ struct vconn *vconn;
+ bool read_hello, connected;
+ struct ofpbuf *msg;
+ int fd;
+
+ fpv_create(type, &fpv);
+ assert(!vconn_open(fpv.vconn_name, OFP_VERSION, &vconn));
+ fd = fpv_accept(&fpv);
+ fpv_destroy(&fpv);
+
+ write(fd, out, out_size);
+
+ assert(!set_nonblocking(fd));
+
+ read_hello = connected = false;
+ for (;;) {
+ if (!read_hello) {
+ struct ofp_header hello;
+ int retval = read(fd, &hello, sizeof hello);
+ if (retval == sizeof hello) {
+ assert(hello.version == OFP_VERSION);
+ assert(hello.type == OFPT_HELLO);
+ assert(hello.length == htons(sizeof hello));
+ read_hello = true;
+ } else {
+ assert(errno == EAGAIN);
+ }
+ }
+
+ if (!connected) {
+ int error = vconn_connect(vconn);
+ if (error == expect_connect_error) {
+ if (!error) {
+ connected = true;
+ } else {
+ close(fd);
+ vconn_close(vconn);
+ return;
+ }
+ } else {
+ assert(error == EAGAIN);
+ }
+ }
+
+ if (read_hello && connected) {
+ break;
+ }
+
+ if (!connected) {
+ vconn_connect_wait(vconn);
+ }
+ if (!read_hello) {
+ poll_fd_wait(fd, POLLIN);
+ }
+ poll_block();
+ }
+ close(fd);
+ assert(vconn_recv(vconn, &msg) == EOF);
+ vconn_close(vconn);
+}
+
+/* Try connecting and sending a normal hello, which should succeed. */
+static void
+test_send_plain_hello(const char *type)
+{
+ struct ofp_header hello;
+
+ hello.version = OFP_VERSION;
+ hello.type = OFPT_HELLO;
+ hello.length = htons(sizeof hello);
+ hello.xid = htonl(0x12345678);
+ test_send_hello(type, &hello, sizeof hello, 0);
+}
+
+/* Try connecting and sending an extra-long hello, which should succeed (since
+ * the specification says that implementations must accept and ignore extra
+ * data). */
+static void
+test_send_long_hello(const char *type)
+{
+ struct ofp_header hello;
+ char buffer[sizeof hello * 2];
+
+ hello.version = OFP_VERSION;
+ hello.type = OFPT_HELLO;
+ hello.length = htons(sizeof buffer);
+ hello.xid = htonl(0x12345678);
+ memset(buffer, 0, sizeof buffer);
+ memcpy(buffer, &hello, sizeof hello);
+ test_send_hello(type, buffer, sizeof buffer, 0);
+}
+
+/* Try connecting and sending an echo request instead of a hello, which should
+ * fail with EPROTO. */
+static void
+test_send_echo_hello(const char *type)
+{
+ struct ofp_header echo;
+
+ echo.version = OFP_VERSION;
+ echo.type = OFPT_ECHO_REQUEST;
+ echo.length = htons(sizeof echo);
+ echo.xid = htonl(0x89abcdef);
+ test_send_hello(type, &echo, sizeof echo, EPROTO);
+}
+
+/* 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(const char *type)
+{
+ struct ofp_header hello;
+
+ memset(&hello, 0, sizeof hello);
+ test_send_hello(type, &hello, sizeof hello, EPROTO);
+}
+
+/* Try connecting and sending a hello packet that has a bad version, which
+ * should fail with EPROTO. */
+static void
+test_send_invalid_version_hello(const char *type)
+{
+ struct ofp_header hello;
+
+ hello.version = OFP_VERSION - 1;
+ hello.type = OFPT_HELLO;
+ hello.length = htons(sizeof hello);
+ hello.xid = htonl(0x12345678);
+ test_send_hello(type, &hello, sizeof hello, EPROTO);
+}
+
+int
+main(int argc 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);
+
+ 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");
+
+ return 0;
+}
one or more of the following OpenFlow connection methods:
.TP
-\fBpssl:\fR[\fIport\fR]
+\fBpssl:\fR[\fIport\fR][\fB:\fIip\fR]
Listens for SSL connections from remote OpenFlow switches on
\fIport\fR (default: 6633). The \fB--private-key\fR,
\fB--certificate\fR, and \fB--ca-cert\fR options are mandatory when
this form is used.
+By default, \fB\*(PN\fR listens for connections to any local IP
+address, but \fIip\fR may be specified to listen only for connections
+to the given \fIip\fR.
.TP
-\fBptcp:\fR[\fIport\fR]
+\fBptcp:\fR[\fIport\fR][\fB:\fIip\fR]
Listens for TCP connections from remote OpenFlow switches on
\fIport\fR (default: 6633).
+By default, \fB\*(PN\fR listens for connections to any local IP
+address, but \fIip\fR may be specified to listen only for connections
+to the given \fIip\fR.
.TP
\fBpunix:\fIfile\fR
.SH OPTIONS
.TP
\fB--accept-vconn=\fIregex\fR
-By default, \fBovs\-discover\fR accepts any controller location
-advertised over DHCP. With this option, only controllers whose names
-match POSIX extended regular expression \fIregex\fR will be accepted.
-Specifying \fBssl:.*\fR for \fIregex\fR, for example, would cause only
-SSL controller connections to be accepted.
+With this option, only controllers whose names match POSIX extended
+regular expression \fIregex\fR will be accepted. Specifying
+\fBssl:.*\fR for \fIregex\fR, for example, would cause only SSL
+controller connections to be accepted.
The \fIregex\fR is implicitly anchored at the beginning of the
controller location string, as if it begins with \fB^\fR.
+When this option is not given, the default \fIregex\fR is
+\fBtcp:.*\fR.
.TP
\fB--exit-without-bind\fR
By default, \fBovs\-discover\fR binds the network device that receives
/* --accept-vconn: Regular expression specifying the class of controller vconns
* that we will accept during autodiscovery. */
-static const char *accept_controller_re = ".*";
+static const char *accept_controller_re = "tcp:.*";
static regex_t accept_controller_regex;
/* --exit-without-bind: Exit after discovering the controller, without binding
.IP
The default regular expression is \fBssl:.*\fR, meaning that only SSL
controller connections will be accepted, when SSL is configured (see
-\fBSSL Configuration\fR), and \fB.*\fR otherwise, meaning that any
-controller will be accepted.
+\fBSSL Configuration\fR), and \fBtcp:.*\fR otherwise, meaning that only
+TCP controller connections will be accepted.
.IP
The regular expression is implicitly anchored at the beginning of the
controller location string, as if it begins with \fB^\fR.
Listens for SSL connections on \fIport\fR (default: 6633). SSL must
be configured when this form is used (see \fBSSL Configuration\fR,
above).
-.IP "\fBptcp:\fR[\fIport\fR]"
+.IP "\fBptcp:\fR[\fIport\fR][\fB:\fIip\fR]"
Listens for TCP connections on \fIport\fR (default: 6633).
+By default, \fB\ovs\-vswitchd\fR listens for connections to any local
+IP address, but \fIip\fR may be specified to limit connections to the
+specified local \fIip\fR.
.RE
To entirely disable listening for management connections, set
\fBbridge.\fIname\fB.openflow.listeners\fR to the single value
$RPM_BUILD_ROOT/root/vswitch/bin/ovs-switchui \
$RPM_BUILD_ROOT/root/vswitch/bin/ovs-wdt \
$RPM_BUILD_ROOT/root/vswitch/bin/secchan \
+ $RPM_BUILD_ROOT/root/vswitch/kernel_modules/veth_mod.ko \
$RPM_BUILD_ROOT/root/vswitch/sbin/ovs-monitor \
$RPM_BUILD_ROOT/root/vswitch/share/man/man8/ovs-controller.8 \
$RPM_BUILD_ROOT/root/vswitch/share/man/man8/ovs-discover.8 \
/etc/profile.d/vswitch.sh
/root/vswitch/kernel_modules/brcompat_mod.ko
/root/vswitch/kernel_modules/openvswitch_mod.ko
-/root/vswitch/kernel_modules/veth_mod.ko
/root/vswitch/scripts/dump-vif-details
/root/vswitch/scripts/interface-reconfigure
/root/vswitch/scripts/vif