Send PORT_STATUS messages on port and link changes. Add ability to remotely enable...
authorJustin Pettit <jpettit@nicira.com>
Fri, 22 Aug 2008 19:47:54 +0000 (12:47 -0700)
committerJustin Pettit <jpettit@nicira.com>
Fri, 22 Aug 2008 19:47:54 +0000 (12:47 -0700)
12 files changed:
INSTALL
datapath/datapath.c
datapath/datapath.h
datapath/forward.c
datapath/linux-2.4/compat-2.4/include/linux/module.h
datapath/linux-2.4/compat-2.4/include/linux/workqueue.h [new file with mode: 0644]
include/netdev.h
include/openflow.h
lib/netdev.c
switch/datapath.c
utilities/dpctl.8
utilities/dpctl.c

diff --git a/INSTALL b/INSTALL
index 618affdfc6f461fe3a22b582b16017732c2b04d5..3ade549bf638f2180633c9eac0192749e71b517b 100644 (file)
--- 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:
 
index 5c3a84c4f3551abdbd7e0f4e8af6d98e2b84b5d1..f88cd28fb5d9558add90dc7994c0e508f8c35221 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/inetdevice.h>
 #include <linux/list.h>
 #include <linux/rculist.h>
+#include <linux/workqueue.h>
 
 #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)
index 5461e051595afdb1ae0ca40108f1ef321ac42108..44a3b6952342cf941588c26b39a1d5fb9fd4b71b 100644 (file)
@@ -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 *);
 
index 9717fcd0afa11f2400627600e3ba8ed6f36d10b8..239e80153d6a79b9325b2e76c58cdade09ea3ae3 100644 (file)
@@ -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;
 }
index 8ba5b781d5c340cca020c7b66ac138b3b5be6455..b13121d44acc3bd766570aa7d1a70f63822fabe8 100644 (file)
@@ -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 (file)
index 0000000..c2603bf
--- /dev/null
@@ -0,0 +1,14 @@
+#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 
index e58a90dd954d624c482b12c07956bd7338d26724..0111e528f88c44f60474112eed3af3f9af3aea53 100644 (file)
@@ -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);
index 677bf263e09795a18d1048c224d713b4e402dd72..de230722b0ecd0b78fc7958b46e5dc71ba37dc07 100644 (file)
@@ -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 {
index 0cd3e6d442f4a638394f1c622f1f63eae9eb3afa..4eadc9c0d989a91fb650d68d7afa4131a75d394c 100644 (file)
@@ -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
index 77b3aaec32e50c4e2ca6ffc4b454547a3fd07851..26fb06c60623e53dd5c005bfbfd51840ee58c205 100644 (file)
 #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;
 }
index 5b595d0da02ea905e2ef78a5bb1d33b323f48aec..5a1181eb469472eed7b4c243d8fddb9f09941013 100644 (file)
@@ -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
index ef74a44d26c6322449542c78bd1e13cbbc1ecc3e..2601ac9717cd087aff43273fb04322eafadb692c 100644 (file)
 
 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 },