From f1c6daebf32ffabeb2d2e7c35f2cb00057dff47b Mon Sep 17 00:00:00 2001 From: Justin Pettit Date: Fri, 22 Aug 2008 12:47:54 -0700 Subject: [PATCH] Send PORT_STATUS messages on port and link changes. Add ability to remotely enable and disable a port through the PORT_MOD message. --- INSTALL | 5 + datapath/datapath.c | 134 +++++++++++++++++- datapath/datapath.h | 2 +- datapath/forward.c | 2 +- .../compat-2.4/include/linux/module.h | 18 ++- .../compat-2.4/include/linux/workqueue.h | 14 ++ include/netdev.h | 1 + include/openflow.h | 19 ++- lib/netdev.c | 25 ++++ switch/datapath.c | 103 ++++++++++++-- utilities/dpctl.8 | 27 ++++ utilities/dpctl.c | 85 +++++++++++ 12 files changed, 404 insertions(+), 31 deletions(-) create mode 100644 datapath/linux-2.4/compat-2.4/include/linux/workqueue.h diff --git a/INSTALL b/INSTALL index 618affdf..3ade549b 100644 --- a/INSTALL +++ b/INSTALL @@ -77,6 +77,11 @@ software listed in the "Base Prerequisites" section above: "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: diff --git a/datapath/datapath.c b/datapath/datapath.c index 5c3a84c4..f88cd28f 100644 --- a/datapath/datapath.c +++ b/datapath/datapath.c @@ -29,6 +29,7 @@ #include #include #include +#include #include "openflow-netlink.h" #include "datapath.h" @@ -61,7 +62,14 @@ MODULE_PARM(sw_desc, "s"); /* 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 @@ -69,7 +77,10 @@ MODULE_PARM(sw_desc, "s"); 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. */ @@ -94,6 +105,7 @@ DEFINE_MUTEX(dp_mutex); 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 *, @@ -355,6 +367,8 @@ static struct net_bridge_port *new_nbp(struct datapath *dp, 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) @@ -381,6 +395,8 @@ int add_switch_port(struct datapath *dp, struct net_device *dev) 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); @@ -391,6 +407,7 @@ int add_switch_port(struct datapath *dp, struct net_device *dev) 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(); @@ -443,6 +460,16 @@ static int dp_maint_func(void *data) 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); } @@ -507,7 +534,7 @@ static inline unsigned packet_length(const struct sk_buff *skb) 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; @@ -674,14 +701,24 @@ out: 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 }; @@ -775,9 +812,41 @@ dp_send_config_reply(struct datapath *dp, const struct sender *sender) 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 @@ -785,10 +854,63 @@ dp_update_port_flags(struct datapath *dp, const struct ofp_phy_port *opp) /* 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) diff --git a/datapath/datapath.h b/datapath/datapath.h index 5461e051..44a3b695 100644 --- a/datapath/datapath.h +++ b/datapath/datapath.h @@ -80,7 +80,7 @@ int dp_send_flow_expired(struct datapath *, struct sw_flow *, 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 *); diff --git a/datapath/forward.c b/datapath/forward.c index 9717fcd0..239e8015 100644 --- a/datapath/forward.c +++ b/datapath/forward.c @@ -378,7 +378,7 @@ recv_port_mod(struct sw_chain *chain, const struct sender *sender, { const struct ofp_port_mod *opm = msg; - dp_update_port_flags(chain->dp, &opm->desc); + dp_update_port_flags(chain->dp, opm); return 0; } diff --git a/datapath/linux-2.4/compat-2.4/include/linux/module.h b/datapath/linux-2.4/compat-2.4/include/linux/module.h index 8ba5b781..b13121d4 100644 --- a/datapath/linux-2.4/compat-2.4/include/linux/module.h +++ b/datapath/linux-2.4/compat-2.4/include/linux/module.h @@ -6,15 +6,25 @@ 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 */ diff --git a/datapath/linux-2.4/compat-2.4/include/linux/workqueue.h b/datapath/linux-2.4/compat-2.4/include/linux/workqueue.h new file mode 100644 index 00000000..c2603bf9 --- /dev/null +++ b/datapath/linux-2.4/compat-2.4/include/linux/workqueue.h @@ -0,0 +1,14 @@ +#ifndef __LINUX_WORKQUEUE_H +#define __LINUX_WORKQUEUE_H 1 + +#include + +#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 diff --git a/include/netdev.h b/include/netdev.h index e58a90dd..0111e528 100644 --- a/include/netdev.h +++ b/include/netdev.h @@ -69,6 +69,7 @@ const uint8_t *netdev_get_etheraddr(const struct netdev *); 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); diff --git a/include/openflow.h b/include/openflow.h index 677bf263..de230722 100644 --- a/include/openflow.h +++ b/include/openflow.h @@ -138,10 +138,10 @@ enum ofp_config_flags { 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. */ @@ -166,7 +166,10 @@ enum ofp_capabilities { /* 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. */ @@ -213,7 +216,7 @@ struct ofp_switch_features { 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. */ }; @@ -238,9 +241,11 @@ OFP_ASSERT(sizeof(struct ofp_port_status) == 48); /* 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 { diff --git a/lib/netdev.c b/lib/netdev.c index 0cd3e6d4..4eadc9c0 100644 --- a/lib/netdev.c +++ b/lib/netdev.c @@ -522,6 +522,31 @@ netdev_get_speed(const struct netdev *netdev) 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 diff --git a/switch/datapath.c b/switch/datapath.c index 77b3aaec..26fb06c6 100644 --- a/switch/datapath.c +++ b/switch/datapath.c @@ -54,7 +54,14 @@ #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; @@ -77,7 +84,8 @@ extern char sw_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. */ @@ -138,11 +146,12 @@ static void remote_destroy(struct remote *); 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 *, @@ -234,7 +243,7 @@ dp_add_port(struct datapath *dp, const char *name) } 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; } @@ -286,6 +295,12 @@ dp_run(struct datapath *dp) 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); @@ -316,9 +331,8 @@ dp_run(struct datapath *dp) 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); @@ -508,7 +522,7 @@ output_all(struct datapath *dp, struct buffer *buffer, int in_port, int flood) 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) { @@ -529,7 +543,7 @@ output_packet(struct datapath *dp, struct buffer *buffer, int out_port) { 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; @@ -644,9 +658,17 @@ static void fill_port_desc(struct datapath *dp, struct sw_port *p, 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 @@ -675,8 +697,9 @@ dp_send_features_reply(struct datapath *dp, const struct sender *sender) } 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]; @@ -686,10 +709,66 @@ dp_update_port_flags(struct datapath *dp, const struct ofp_phy_port *opp) 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) { @@ -1060,7 +1139,7 @@ recv_port_mod(struct datapath *dp, const struct sender *sender UNUSED, { const struct ofp_port_mod *opm = msg; - dp_update_port_flags(dp, &opm->desc); + dp_update_port_flags(dp, opm); return 0; } diff --git a/utilities/dpctl.8 b/utilities/dpctl.8 index 5b595d0d..5a1181eb 100644 --- a/utilities/dpctl.8 +++ b/utilities/dpctl.8 @@ -108,6 +108,33 @@ datapath \fIswitch\fR. 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 diff --git a/utilities/dpctl.c b/utilities/dpctl.c index ef74a44d..2601ac97 100644 --- a/utilities/dpctl.c +++ b/utilities/dpctl.c @@ -70,6 +70,11 @@ 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; @@ -190,6 +195,7 @@ usage(void) " 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" @@ -866,6 +872,84 @@ do_probe(int argc, char *argv[]) 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[]) { @@ -981,6 +1065,7 @@ static struct command all_commands[] = { { "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 }, -- 2.30.2