"lsmod | grep bridge"), you must remove it ("rmmod bridge")
before starting the datapath.
+ In kernels prior to 2.6.9, VLAN support (CONFIG_VLAN_8021Q) must
+ be compiled either directly or as a module. Failure to do this
+ will cause an error on module insertion due to the
+ "dev_change_flags" symbol being undefined.
+
- The correct version of GCC for the kernel that you are building
the module against:
#include <linux/inetdevice.h>
#include <linux/list.h>
#include <linux/rculist.h>
+#include <linux/workqueue.h>
#include "openflow-netlink.h"
#include "datapath.h"
/* Number of milliseconds between runs of the maintenance thread. */
#define MAINT_SLEEP_MSECS 1000
-#define BRIDGE_PORT_NO_FLOOD 0x00000001
+enum br_port_flags {
+ BRPF_NO_FLOOD = 1 << 0,
+};
+
+enum br_port_status {
+ BRPS_PORT_DOWN = 1 << 0,
+ BRPS_LINK_DOWN = 1 << 1,
+};
#define UINT32_MAX 4294967295U
#define UINT16_MAX 65535
struct net_bridge_port {
u16 port_no;
- u32 flags;
+ u32 flags; /* BRPF_* flags */
+ u32 status; /* BRPS_* flags */
+ spinlock_t lock;
+ struct work_struct port_task;
struct datapath *dp;
struct net_device *dev;
struct list_head node; /* Element in datapath.ports. */
EXPORT_SYMBOL(dp_mutex);
static int dp_maint_func(void *data);
+static int update_port_status(struct net_bridge_port *p);
static int send_port_status(struct net_bridge_port *p, uint8_t status);
static int dp_genl_openflow_done(struct netlink_callback *);
static struct net_bridge_port *new_nbp(struct datapath *,
p->dp = dp;
p->dev = dev;
p->port_no = port_no;
+ spin_lock_init(&p->lock);
+ INIT_WORK(&p->port_task, NULL);
if (port_no != OFPP_LOCAL)
rcu_assign_pointer(dev->br_port, p);
if (port_no < OFPP_MAX)
if (IS_ERR(p))
return PTR_ERR(p);
+ update_port_status(p);
+
/* Notify the ctlpath that this port has been added */
send_port_status(p, OFPPR_ADD);
static int del_switch_port(struct net_bridge_port *p)
{
/* First drop references to device. */
+ cancel_work_sync(p->work);
rtnl_lock();
dev_set_promiscuity(p->dev, -1);
rtnl_unlock();
struct datapath *dp = (struct datapath *) data;
while (!kthread_should_stop()) {
+ struct net_bridge_port *p;
+
+ /* Check if port status has changed */
+ rcu_read_lock();
+ list_for_each_entry_rcu (p, &dp->port_list, node)
+ if (update_port_status(p))
+ send_port_status(p, OFPPR_MOD);
+ rcu_read_unlock();
+
+ /* Timeout old entries */
chain_timeout(dp->chain);
msleep_interruptible(MAINT_SLEEP_MSECS);
}
static int
output_all(struct datapath *dp, struct sk_buff *skb, int flood)
{
- u32 disable = flood ? BRIDGE_PORT_NO_FLOOD : 0;
+ u32 disable = flood ? BRPF_NO_FLOOD : 0;
struct net_bridge_port *p;
int prev_port = -1;
static void fill_port_desc(struct net_bridge_port *p, struct ofp_phy_port *desc)
{
+ unsigned long flags;
desc->port_no = htons(p->port_no);
strncpy(desc->name, p->dev->name, OFP_MAX_PORT_NAME_LEN);
desc->name[OFP_MAX_PORT_NAME_LEN-1] = '\0';
memcpy(desc->hw_addr, p->dev->dev_addr, ETH_ALEN);
- desc->flags = htonl(p->flags);
+ desc->flags = 0;
desc->features = 0;
desc->speed = 0;
+ spin_lock_irqsave(&p->lock, flags);
+ if (p->flags & BRPF_NO_FLOOD)
+ desc->flags |= htonl(OFPPFL_NO_FLOOD);
+ else if (p->status & BRPS_PORT_DOWN)
+ desc->flags |= htonl(OFPPFL_PORT_DOWN);
+ else if (p->status & BRPS_LINK_DOWN)
+ desc->flags |= htonl(OFPPFL_LINK_DOWN);
+ spin_unlock_irqrestore(&p->lock, flags);
+
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,24)
if (p->dev->ethtool_ops && p->dev->ethtool_ops->get_settings) {
struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET };
return send_openflow_skb(skb, sender);
}
+/* Callback function for a workqueue to disable an interface */
+static void
+down_port_cb(struct work_struct *work)
+{
+ struct net_bridge_port *p = container_of(work, struct net_bridge_port,
+ port_task);
+
+ rtnl_lock();
+ if (dev_change_flags(p->dev, p->dev->flags & ~IFF_UP) < 0)
+ if (net_ratelimit())
+ printk("problem bringing up port %s\n", p->dev->name);
+ rtnl_unlock();
+ p->status |= BRPS_PORT_DOWN;
+}
+
+/* Callback function for a workqueue to enable an interface */
+static void
+up_port_cb(struct work_struct *work)
+{
+ struct net_bridge_port *p = container_of(work, struct net_bridge_port,
+ port_task);
+
+ rtnl_lock();
+ if (dev_change_flags(p->dev, p->dev->flags | IFF_UP) < 0)
+ if (net_ratelimit())
+ printk("problem bringing down port %s\n", p->dev->name);
+ rtnl_unlock();
+ p->status &= ~BRPS_PORT_DOWN;
+}
+
int
-dp_update_port_flags(struct datapath *dp, const struct ofp_phy_port *opp)
+dp_update_port_flags(struct datapath *dp, const struct ofp_port_mod *opm)
{
+ unsigned long int flags;
+ const struct ofp_phy_port *opp = &opm->desc;
int port_no = ntohs(opp->port_no);
struct net_bridge_port *p = (port_no < OFPP_MAX ? dp->ports[port_no]
: port_no == OFPP_LOCAL ? dp->local_port
/* Make sure the port id hasn't changed since this was sent */
if (!p || memcmp(opp->hw_addr, p->dev->dev_addr, ETH_ALEN))
return -1;
- p->flags = htonl(opp->flags);
+
+ spin_lock_irqsave(&p->lock, flags);
+ if (opm->mask & htonl(OFPPFL_NO_FLOOD)) {
+ if (opp->flags & htonl(OFPPFL_NO_FLOOD))
+ p->flags |= BRPF_NO_FLOOD;
+ else
+ p->flags &= ~BRPF_NO_FLOOD;
+ }
+
+ /* Modifying the status of an interface requires taking a lock
+ * that cannot be done from here. For this reason, we use a shared
+ * workqueue, which will cause it to be executed from a safer
+ * context. */
+ if (opm->mask & htonl(OFPPFL_PORT_DOWN)) {
+ if ((opp->flags & htonl(OFPPFL_PORT_DOWN))
+ && (p->status & BRPS_PORT_DOWN) == 0) {
+ PREPARE_WORK(&p->port_task, down_port_cb);
+ schedule_work(&p->port_task);
+ } else if ((opp->flags & htonl(OFPPFL_PORT_DOWN)) == 0
+ && (p->status & BRPS_PORT_DOWN)) {
+ PREPARE_WORK(&p->port_task, up_port_cb);
+ schedule_work(&p->port_task);
+ }
+ }
+ spin_unlock_irqrestore(&p->lock, flags);
+
return 0;
}
+/* Update the port status field of the bridge port. A non-zero return
+ * value indicates some field has changed.
+ *
+ * NB: Callers of this function may hold the RCU read lock, so any
+ * additional checks must not sleep.
+ */
+static int
+update_port_status(struct net_bridge_port *p)
+{
+ unsigned long int flags;
+ uint32_t orig_status;
+
+ spin_lock_irqsave(&p->lock, flags);
+ orig_status = p->status;
+
+ if (p->dev->flags & IFF_UP)
+ p->status &= ~BRPS_PORT_DOWN;
+ else
+ p->status |= BRPS_PORT_DOWN;
+
+ if (netif_carrier_ok(p->dev))
+ p->status &= ~BRPS_LINK_DOWN;
+ else
+ p->status |= BRPS_LINK_DOWN;
+
+ spin_unlock_irqrestore(&p->lock, flags);
+ return (orig_status != p->status);
+}
static int
send_port_status(struct net_bridge_port *p, uint8_t status)
enum ofp_flow_expired_reason);
int dp_send_error_msg(struct datapath *, const struct sender *,
uint16_t, uint16_t, const uint8_t *, size_t);
-int dp_update_port_flags(struct datapath *dp, const struct ofp_phy_port *opp);
+int dp_update_port_flags(struct datapath *dp, const struct ofp_port_mod *opm);
int dp_send_echo_reply(struct datapath *, const struct sender *,
const struct ofp_header *);
{
const struct ofp_port_mod *opm = msg;
- dp_update_port_flags(chain->dp, &opm->desc);
+ dp_update_port_flags(chain->dp, opm);
return 0;
}
static inline int try_module_get(struct module *module)
{
- BUG_ON(module != THIS_MODULE);
- MOD_INC_USE_COUNT;
+ if (module) {
+ if (module == THIS_MODULE)
+ MOD_INC_USE_COUNT;
+ else
+ printk("warning: try_module_get: module(%p) != THIS_MODULE(%p)\n",
+ module, THIS_MODULE);
+ }
return 1;
}
static inline void module_put(struct module *module)
{
- BUG_ON(module != THIS_MODULE);
- MOD_DEC_USE_COUNT;
+ if (module) {
+ if (module == THIS_MODULE)
+ MOD_DEC_USE_COUNT;
+ else
+ printk("warning: module_put: module(%p) != THIS_MODULE(%p)\n",
+ module, THIS_MODULE);
+ }
}
#endif /* module.h */
--- /dev/null
+#ifndef __LINUX_WORKQUEUE_H
+#define __LINUX_WORKQUEUE_H 1
+
+#include <linux/tqueue.h>
+
+#define work_struct tq_struct
+#define INIT_WORK(_work, _routine) \
+ INIT_TQUEUE((_work), (_routine), (_work))
+#define PREPARE_WORK(_work, _routine) \
+ PREPARE_TQUEUE((_work), (_routine), (_work))
+#define schedule_work schedule_task
+#define flush_scheduled_work flush_scheduled_tasks
+
+#endif
const char *netdev_get_name(const struct netdev *);
int netdev_get_mtu(const struct netdev *);
int netdev_get_speed(const struct netdev *);
+int netdev_get_link_status(const struct netdev *);
uint32_t netdev_get_features(const struct netdev *);
bool netdev_get_in4(const struct netdev *, struct in_addr *);
int netdev_set_in4(struct netdev *, struct in_addr addr, struct in_addr mask);
OFPC_SEND_FLOW_EXP = 1 << 0,
/* Handling of IP fragments. */
- OFPC_FRAG_NORMAL = 0 << 1, /* No special handling for fragments. */
- OFPC_FRAG_DROP = 1 << 1, /* Drop fragments. */
- OFPC_FRAG_REASM = 2 << 1, /* Reassemble (only if OFPC_IP_REASM set). */
- OFPC_FRAG_MASK = 3 << 1
+ OFPC_FRAG_NORMAL = 0 << 1, /* No special handling for fragments. */
+ OFPC_FRAG_DROP = 1 << 1, /* Drop fragments. */
+ OFPC_FRAG_REASM = 2 << 1, /* Reassemble (only if OFPC_IP_REASM set). */
+ OFPC_FRAG_MASK = 3 << 1
};
/* Switch configuration. */
/* Flags to indicate behavior of the physical port */
enum ofp_port_flags {
- OFPPFL_NO_FLOOD = 1 << 0, /* Do not include this port when flooding */
+ OFPPFL_NO_FLOOD = 1 << 0, /* Do not include this port when flooding. */
+ OFPPFL_PORT_DOWN = 1 << 1, /* Port is configured down. */
+ OFPPFL_LINK_DOWN = 1 << 2, /* No physical link on interface.
+ NOTE: Non-settable field */
};
/* Features of physical ports available in a datapath. */
uint8_t pad[4]; /* Align to 64-bits. */
/* Port info.*/
- struct ofp_phy_port ports[0]; /* Port definitions. The number of ports
+ struct ofp_phy_port ports[0]; /* Port definitions. The number of ports
is inferred from the length field in
the header. */
};
/* Modify behavior of the physical port */
struct ofp_port_mod {
struct ofp_header header;
+ uint32_t mask; /* Bitmap of "ofp_port_flags" that should be
+ changed. */
struct ofp_phy_port desc;
};
-OFP_ASSERT(sizeof(struct ofp_port_mod) == 44);
+OFP_ASSERT(sizeof(struct ofp_port_mod) == 48);
/* Why is this packet being sent to the controller? */
enum ofp_packet_in_reason {
return netdev->speed;
}
+/* Checks the link status. Returns 1 or 0 to indicate the link is active
+ * or not, respectively. Any other return value indicates an error. */
+int
+netdev_get_link_status(const struct netdev *netdev)
+{
+ struct ifreq ifr;
+ struct ethtool_value edata;
+
+ memset(&ifr, 0, sizeof ifr);
+ strncpy(ifr.ifr_name, netdev->name, sizeof ifr.ifr_name);
+ ifr.ifr_data = (caddr_t) &edata;
+
+ memset(&edata, 0, sizeof edata);
+ edata.cmd = ETHTOOL_GLINK;
+ if (ioctl(netdev->fd, SIOCETHTOOL, &ifr) == 0) {
+ if (edata.data) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
/* Returns the features supported by 'netdev', as a bitmap of bits from enum
* ofp_phy_port, in host byte order. */
uint32_t
#define THIS_MODULE VLM_datapath
#include "vlog.h"
-#define BRIDGE_PORT_NO_FLOOD 0x00000001
+enum br_port_flags {
+ BRPF_NO_FLOOD = 1 << 0,
+};
+
+enum br_port_status {
+ BRPS_PORT_DOWN = 1 << 0,
+ BRPS_LINK_DOWN = 1 << 1,
+};
extern char mfr_desc;
extern char hw_desc;
| (1 << OFPAT_SET_TP_DST) )
struct sw_port {
- uint32_t flags;
+ uint32_t flags; /* BRPF_* flags */
+ uint32_t status; /* BRPS_* flags */
struct datapath *dp;
struct netdev *netdev;
struct list node; /* Element in datapath.ports. */
void dp_output_port(struct datapath *, struct buffer *,
int in_port, int out_port);
-void dp_update_port_flags(struct datapath *dp, const struct ofp_phy_port *opp);
+void dp_update_port_flags(struct datapath *dp, const struct ofp_port_mod *opm);
void dp_output_control(struct datapath *, struct buffer *, int in_port,
size_t max_len, int reason);
static void send_flow_expired(struct datapath *, struct sw_flow *,
enum ofp_flow_expired_reason);
+static int update_port_status(struct sw_port *p);
static void send_port_status(struct sw_port *p, uint8_t status);
static void del_switch_port(struct sw_port *p);
static void execute_actions(struct datapath *, struct buffer *,
}
error = netdev_set_flags(netdev, NETDEV_UP | NETDEV_PROMISC, false);
if (error) {
- VLOG_ERR("Couldn't set promiscuous mode on %s device", name);
+ VLOG_ERR("couldn't set promiscuous mode on %s device", name);
netdev_close(netdev);
return error;
}
struct list deleted = LIST_INITIALIZER(&deleted);
struct sw_flow *f, *n;
+ LIST_FOR_EACH (p, struct sw_port, node, &dp->port_list) {
+ if (update_port_status(p)) {
+ send_port_status(p, OFPPR_MOD);
+ }
+ }
+
chain_timeout(dp->chain, &deleted);
LIST_FOR_EACH_SAFE (f, n, struct sw_flow, node, &deleted) {
send_flow_expired(dp, f, f->reason);
fwd_port_input(dp, buffer, port_no(dp, p));
buffer = NULL;
} else if (error != EAGAIN) {
- VLOG_ERR("Error receiving data from %s: %s",
+ VLOG_ERR("error receiving data from %s: %s",
netdev_get_name(p->netdev), strerror(error));
- del_switch_port(p);
}
}
buffer_delete(buffer);
if (port_no(dp, p) == in_port) {
continue;
}
- if (flood && p->flags & BRIDGE_PORT_NO_FLOOD) {
+ if (flood && p->flags & BRPF_NO_FLOOD) {
continue;
}
if (prev_port != -1) {
{
if (out_port >= 0 && out_port < OFPP_MAX) {
struct sw_port *p = &dp->ports[out_port];
- if (p->netdev != NULL) {
+ if (p->netdev != NULL && !(p->status & BRPS_PORT_DOWN)) {
if (!netdev_send(p->netdev, buffer)) {
p->tx_packets++;
p->tx_bytes += buffer->size;
sizeof desc->name);
desc->name[sizeof desc->name - 1] = '\0';
memcpy(desc->hw_addr, netdev_get_etheraddr(p->netdev), ETH_ADDR_LEN);
- desc->flags = htonl(p->flags);
+ desc->flags = 0;
desc->features = htonl(netdev_get_features(p->netdev));
desc->speed = htonl(netdev_get_speed(p->netdev));
+
+ if (p->flags & BRPF_NO_FLOOD) {
+ desc->flags |= htonl(OFPPFL_NO_FLOOD);
+ } else if (p->status & BRPS_PORT_DOWN) {
+ desc->flags |= htonl(OFPPFL_PORT_DOWN);
+ } else if (p->status & BRPS_LINK_DOWN) {
+ desc->flags |= htonl(OFPPFL_LINK_DOWN);
+ }
}
static void
}
void
-dp_update_port_flags(struct datapath *dp, const struct ofp_phy_port *opp)
+dp_update_port_flags(struct datapath *dp, const struct ofp_port_mod *opm)
{
+ const struct ofp_phy_port *opp = &opm->desc;
int port_no = ntohs(opp->port_no);
if (port_no < OFPP_MAX) {
struct sw_port *p = &dp->ports[port_no];
ETH_ADDR_LEN) != 0) {
return;
}
- p->flags = htonl(opp->flags);
+
+
+ if (opm->mask & htonl(OFPPFL_NO_FLOOD)) {
+ if (opp->flags & htonl(OFPPFL_NO_FLOOD))
+ p->flags |= BRPF_NO_FLOOD;
+ else
+ p->flags &= ~BRPF_NO_FLOOD;
+ }
+
+ if (opm->mask & htonl(OFPPFL_PORT_DOWN)) {
+ if ((opp->flags & htonl(OFPPFL_PORT_DOWN))
+ && (p->status & BRPS_PORT_DOWN) == 0) {
+ p->status |= BRPS_PORT_DOWN;
+ netdev_turn_flags_off(p->netdev, NETDEV_UP, true);
+ } else if ((opp->flags & htonl(OFPPFL_PORT_DOWN)) == 0
+ && (p->status & BRPS_PORT_DOWN)) {
+ p->status &= ~BRPS_PORT_DOWN;
+ netdev_turn_flags_on(p->netdev, NETDEV_UP, true);
+ }
+ }
}
}
+/* Update the port status field of the bridge port. A non-zero return
+ * value indicates some field has changed.
+ *
+ * NB: Callers of this function may hold the RCU read lock, so any
+ * additional checks must not sleep.
+ */
+static int
+update_port_status(struct sw_port *p)
+{
+ int retval;
+ enum netdev_flags flags;
+ uint32_t orig_status = p->status;
+
+ if (netdev_get_flags(p->netdev, &flags) < 0) {
+ VLOG_WARN("could not get netdev flags for %s",
+ netdev_get_name(p->netdev));
+ return 0;
+ } else {
+ if (flags & NETDEV_UP) {
+ p->status &= ~BRPS_PORT_DOWN;
+ } else {
+ p->status |= BRPS_PORT_DOWN;
+ }
+ }
+
+ /* Not all cards support this getting link status, so don't warn on
+ * error. */
+ retval = netdev_get_link_status(p->netdev);
+ if (retval == 1) {
+ p->status &= ~BRPS_LINK_DOWN;
+ } else if (retval == 0) {
+ p->status |= BRPS_LINK_DOWN;
+ }
+
+ return (orig_status != p->status);
+}
+
static void
send_port_status(struct sw_port *p, uint8_t status)
{
{
const struct ofp_port_mod *opm = msg;
- dp_update_port_flags(dp, &opm->desc);
+ dp_update_port_flags(dp, opm);
return 0;
}
Prints to the console statistics for each of the network devices
associated with datapath \fIswitch\fR.
+.TP
+\fBmod-port \fIswitch\fR \fInetdev\fR \fIaction\fR
+Modify characteristics of an interface monitored by \fIswitch\fR.
+\fInetdev\fR can be referred to by its OpenFlow assigned port number or
+the device name, e.g. \fBeth0\fR. The \fIaction\fR may be any one of the
+following:
+
+.RS
+.IP \fBup\fR
+Enables the interface. This is equivalent to ``ifconfig up'' on a Unix
+system.
+
+.IP \fBdown\fR
+Disables the interface. This is equivalent to ``ifconfig down'' on a Unix
+system.
+
+.IP \fBflood\fR
+When a \fIflood\fR action is specified, traffic will be sent out this
+interface. This is the default posture for monitored ports.
+
+.IP \fBnoflood\fR
+When a \fIflood\fR action is specified, traffic will not be sent out
+this interface. This is primarily useful to prevent loops when a
+spanning tree protocol is not in use.
+
+.RE
+
.TP
\fBdump-flows \fIswitch \fR[\fIflows\fR]
Prints to the console all flow entries in datapath \fIswitch\fR's
static const char* ifconfigbin = "/sbin/ifconfig";
+#define MOD_PORT_CMD_UP "up"
+#define MOD_PORT_CMD_DOWN "down"
+#define MOD_PORT_CMD_FLOOD "flood"
+#define MOD_PORT_CMD_NOFLOOD "noflood"
+
struct command {
const char *name;
int min_args;
" show SWITCH show information\n"
" dump-version SWITCH print version information\n"
" dump-tables SWITCH print table stats\n"
+ " mod-port SWITCH IFACE ACT modify port behavior\n"
" dump-ports SWITCH print port statistics\n"
" dump-flows SWITCH print all flow entries\n"
" dump-flows SWITCH FLOW print matching FLOWs\n"
vconn_close(vconn);
}
+static void
+do_mod_port(int argc, char *argv[])
+{
+ struct buffer *request, *reply;
+ struct ofp_switch_features *osf;
+ struct ofp_port_mod *opm;
+ struct vconn *vconn;
+ char *endptr;
+ int n_ports;
+ int port_idx;
+ int port_no;
+
+
+ /* Check if the argument is a port index. Otherwise, treat it as
+ * the port name. */
+ port_no = strtol(argv[2], &endptr, 10);
+ if (port_no == 0 && endptr == argv[2]) {
+ port_no = -1;
+ }
+
+ /* Send a "Features Request" to get the information we need in order
+ * to modify the port. */
+ make_openflow(sizeof(struct ofp_header), OFPT_FEATURES_REQUEST, &request);
+ run(vconn_open_block(argv[1], &vconn), "connecting to %s", argv[1]);
+ run(vconn_transact(vconn, request, &reply), "talking to %s", argv[1]);
+
+ osf = reply->data;
+ n_ports = (reply->size - sizeof *osf) / sizeof *osf->ports;
+
+ for (port_idx = 0; port_idx < n_ports; port_idx++) {
+ if (port_no != -1) {
+ /* Check argument as a port index */
+ if (osf->ports[port_idx].port_no == htons(port_no)) {
+ break;
+ }
+ } else {
+ /* Check argument as an interface name */
+ if (!strncmp((char *)osf->ports[port_idx].name, argv[2],
+ sizeof osf->ports[0].name)) {
+ break;
+ }
+
+ }
+ }
+ if (port_idx == n_ports) {
+ fatal(0, "couldn't find monitored port: %s", argv[2]);
+ }
+
+ opm = make_openflow(sizeof(struct ofp_port_mod), OFPT_PORT_MOD, &request);
+ memcpy(&opm->desc, &osf->ports[port_idx], sizeof osf->ports[0]);
+ opm->mask = 0;
+ opm->desc.flags = 0;
+
+ printf("modifying port: %s\n", osf->ports[port_idx].name);
+
+ if (!strncasecmp(argv[3], MOD_PORT_CMD_UP, sizeof MOD_PORT_CMD_UP)) {
+ opm->mask |= htonl(OFPPFL_PORT_DOWN);
+ } else if (!strncasecmp(argv[3], MOD_PORT_CMD_DOWN,
+ sizeof MOD_PORT_CMD_DOWN)) {
+ opm->mask |= htonl(OFPPFL_PORT_DOWN);
+ opm->desc.flags |= htonl(OFPPFL_PORT_DOWN);
+ } else if (!strncasecmp(argv[3], MOD_PORT_CMD_FLOOD,
+ sizeof MOD_PORT_CMD_FLOOD)) {
+ opm->mask |= htonl(OFPPFL_NO_FLOOD);
+ } else if (!strncasecmp(argv[3], MOD_PORT_CMD_NOFLOOD,
+ sizeof MOD_PORT_CMD_NOFLOOD)) {
+ opm->mask |= htonl(OFPPFL_NO_FLOOD);
+ opm->desc.flags |= htonl(OFPPFL_NO_FLOOD);
+ } else {
+ fatal(0, "unknown mod-port command '%s'", argv[3]);
+ }
+
+ send_openflow_buffer(vconn, request);
+
+ buffer_delete(reply);
+ vconn_close(vconn);
+}
+
static void
do_ping(int argc, char *argv[])
{
{ "add-flows", 2, 2, do_add_flows },
{ "del-flows", 1, 2, do_del_flows },
{ "dump-ports", 1, 1, do_dump_ports },
+ { "mod-port", 3, 3, do_mod_port },
{ "probe", 1, 1, do_probe },
{ "ping", 1, 2, do_ping },
{ "benchmark", 3, 3, do_benchmark },