static int
 output_all(struct datapath *dp, struct sk_buff *skb, int flood)
 {
-       u32 disable = flood ? OFPPFL_NO_FLOOD : 0;
+       u32 disable = flood ? OFPPC_NO_FLOOD : 0;
        struct net_bridge_port *p;
        int prev_port = -1;
 
        list_for_each_entry_rcu (p, &dp->port_list, node) {
-               if (skb->dev == p->dev || p->flags & disable)
+               if (skb->dev == p->dev || p->config & disable)
                        continue;
                if (prev_port != -1) {
                        struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC);
                                printk("can't directly forward to input port\n");
                        return -EINVAL;
                }
-               if (p->flags & OFPPFL_NO_FWD && !ignore_no_fwd) {
+               if (p->config & OFPPC_NO_FWD && !ignore_no_fwd) {
                        kfree_skb(skb);
                        return 0;
                }
        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 = 0;
-       desc->features = 0;
-       desc->speed = 0;
+       desc->curr = 0;
+       desc->supported = 0;
+       desc->advertised = 0;
+       desc->peer = 0;
 
        spin_lock_irqsave(&p->lock, flags);
-       desc->flags = htonl(p->flags | p->status);
+       desc->config = htonl(p->config);
+       desc->state = htonl(p->state);
        spin_unlock_irqrestore(&p->lock, flags);
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,24)
                struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET };
 
                if (!p->dev->ethtool_ops->get_settings(p->dev, &ecmd)) {
+                       /* Set the supported features */
                        if (ecmd.supported & SUPPORTED_10baseT_Half) 
-                               desc->features |= OFPPF_10MB_HD;
+                               desc->supported |= OFPPF_10MB_HD;
                        if (ecmd.supported & SUPPORTED_10baseT_Full)
-                               desc->features |= OFPPF_10MB_FD;
+                               desc->supported |= OFPPF_10MB_FD;
                        if (ecmd.supported & SUPPORTED_100baseT_Half) 
-                               desc->features |= OFPPF_100MB_HD;
+                               desc->supported |= OFPPF_100MB_HD;
                        if (ecmd.supported & SUPPORTED_100baseT_Full)
-                               desc->features |= OFPPF_100MB_FD;
+                               desc->supported |= OFPPF_100MB_FD;
                        if (ecmd.supported & SUPPORTED_1000baseT_Half)
-                               desc->features |= OFPPF_1GB_HD;
+                               desc->supported |= OFPPF_1GB_HD;
                        if (ecmd.supported & SUPPORTED_1000baseT_Full)
-                               desc->features |= OFPPF_1GB_FD;
-                       /* 10Gbps half-duplex doesn't exist... */
+                               desc->supported |= OFPPF_1GB_FD;
                        if (ecmd.supported & SUPPORTED_10000baseT_Full)
-                               desc->features |= OFPPF_10GB_FD;
-
-                       desc->speed = htonl(ecmd.speed);
+                               desc->supported |= OFPPF_10GB_FD;
+                       if (ecmd.supported & SUPPORTED_TP)
+                               desc->supported |= OFPPF_COPPER;
+                       if (ecmd.supported & SUPPORTED_FIBRE)
+                               desc->supported |= OFPPF_FIBER;
+                       if (ecmd.supported & SUPPORTED_Autoneg)
+                               desc->supported |= OFPPF_AUTONEG;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,14)
+                       if (ecmd.supported & SUPPORTED_Pause)
+                               desc->supported |= OFPPF_PAUSE;
+                       if (ecmd.supported & SUPPORTED_Asym_Pause)
+                               desc->supported |= OFPPF_PAUSE_ASYM;
+#endif /* kernel >= 2.6.14 */
+
+                       /* Set the advertised features */
+                       if (ecmd.advertising & ADVERTISED_10baseT_Half) 
+                               desc->advertised |= OFPPF_10MB_HD;
+                       if (ecmd.advertising & ADVERTISED_10baseT_Full)
+                               desc->advertised |= OFPPF_10MB_FD;
+                       if (ecmd.advertising & ADVERTISED_100baseT_Half) 
+                               desc->advertised |= OFPPF_100MB_HD;
+                       if (ecmd.advertising & ADVERTISED_100baseT_Full)
+                               desc->advertised |= OFPPF_100MB_FD;
+                       if (ecmd.advertising & ADVERTISED_1000baseT_Half)
+                               desc->advertised |= OFPPF_1GB_HD;
+                       if (ecmd.advertising & ADVERTISED_1000baseT_Full)
+                               desc->advertised |= OFPPF_1GB_FD;
+                       if (ecmd.advertising & ADVERTISED_10000baseT_Full)
+                               desc->advertised |= OFPPF_10GB_FD;
+                       if (ecmd.advertising & ADVERTISED_TP)
+                               desc->advertised |= OFPPF_COPPER;
+                       if (ecmd.advertising & ADVERTISED_FIBRE)
+                               desc->advertised |= OFPPF_FIBER;
+                       if (ecmd.advertising & ADVERTISED_Autoneg)
+                               desc->advertised |= OFPPF_AUTONEG;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,14)
+                       if (ecmd.advertising & ADVERTISED_Pause)
+                               desc->advertised |= OFPPF_PAUSE;
+                       if (ecmd.advertising & ADVERTISED_Asym_Pause)
+                               desc->advertised |= OFPPF_PAUSE_ASYM;
+#endif /* kernel >= 2.6.14 */
+
+                       /* Set the current features */
+                       if (ecmd.speed == SPEED_10)
+                               desc->curr = (ecmd.duplex) ? OFPPF_10MB_FD : OFPPF_10MB_HD;
+                       else if (ecmd.speed == SPEED_100)
+                               desc->curr = (ecmd.duplex) ? OFPPF_100MB_FD : OFPPF_100MB_HD;
+                       else if (ecmd.speed == SPEED_1000)
+                               desc->curr = (ecmd.duplex) ? OFPPF_1GB_FD : OFPPF_1GB_HD;
+                       else if (ecmd.speed == SPEED_10000)
+                               desc->curr = OFPPF_10GB_FD;
+
+                       if (ecmd.port == PORT_TP) 
+                               desc->curr |= OFPPF_COPPER;
+                       else if (ecmd.port == PORT_FIBRE) 
+                               desc->curr |= OFPPF_FIBER;
+
+                       if (ecmd.autoneg)
+                               desc->curr |= OFPPF_AUTONEG;
                }
        }
 #endif
-       desc->features = htonl(desc->features);
+       desc->curr = htonl(desc->curr);
+       desc->supported = htonl(desc->supported);
+       desc->advertised = htonl(desc->advertised);
+       desc->peer = htonl(desc->peer);
 }
 
 static int 
                if (net_ratelimit())
                        printk("problem bringing up port %s\n", p->dev->name);
        rtnl_unlock();
-       p->status |= OFPPFL_PORT_DOWN;
+       p->config |= OFPPC_PORT_DOWN;
 }
 
 /* Callback function for a workqueue to enable an interface */
                if (net_ratelimit())
                        printk("problem bringing down port %s\n", p->dev->name);
        rtnl_unlock();
-       p->status &= ~OFPPFL_PORT_DOWN;
+       p->config &= ~OFPPC_PORT_DOWN;
 }
 
 int
 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);
+       int port_no = ntohs(opm->port_no);
        struct net_bridge_port *p = (port_no < OFPP_MAX ? dp->ports[port_no]
                                     : port_no == OFPP_LOCAL ? dp->local_port
                                     : NULL);
-       uint32_t flag_mask;
 
        /* Make sure the port id hasn't changed since this was sent */
-       if (!p || memcmp(opp->hw_addr, p->dev->dev_addr, ETH_ALEN))
+       if (!p || memcmp(opm->hw_addr, p->dev->dev_addr, ETH_ALEN))
                return -1;
 
        spin_lock_irqsave(&p->lock, flags);
-       flag_mask = ntohl(opm->mask) & PORT_FLAG_BITS;
-       if (flag_mask) {
-               p->flags &= ~flag_mask;
-               p->flags |= ntohl(opp->flags) & flag_mask;
+       if (opm->mask) {
+               uint32_t config_mask = ntohl(opm->mask);
+               p->config &= ~config_mask;
+               p->config |= ntohl(opm->config) & config_mask;
        }
 
        /* 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 & OFPPFL_PORT_DOWN) == 0) {
+       if (opm->mask & htonl(OFPPC_PORT_DOWN)) {
+               if ((opm->config & htonl(OFPPC_PORT_DOWN))
+                   && (p->config & OFPPC_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 & OFPPFL_PORT_DOWN)) {
+               } else if ((opm->config & htonl(OFPPC_PORT_DOWN)) == 0
+                          && (p->config & OFPPC_PORT_DOWN)) {
                        PREPARE_WORK(&p->port_task, up_port_cb);
                        schedule_work(&p->port_task);
                }
        spin_lock_irqsave(&p->lock, flags);
 
        if (p->dev->flags & IFF_UP) 
-               p->status &= ~OFPPFL_PORT_DOWN;
+               p->config &= ~OFPPC_PORT_DOWN;
        else
-               p->status |= OFPPFL_PORT_DOWN;
+               p->config |= OFPPC_PORT_DOWN;
 
        if (netif_carrier_ok(p->dev))
-               p->status &= ~OFPPFL_LINK_DOWN;
+               p->state &= ~OFPPS_LINK_DOWN;
        else
-               p->status |= OFPPFL_LINK_DOWN;
+               p->state |= OFPPS_LINK_DOWN;
 
        spin_unlock_irqrestore(&p->lock, flags);
 }
 
        uint32_t seq;           /* Netlink sequence ID of request. */
 };
 
-#define PORT_STATUS_BITS (OFPPFL_PORT_DOWN | OFPPFL_LINK_DOWN)
-#define PORT_FLAG_BITS (~PORT_STATUS_BITS)
-
 struct net_bridge_port {
        u16     port_no;
-       u32 flags;              /* Some subset of PORT_FLAG_BITS. */
-       u32 status;             /* Some subset of PORT_STATUS_BITS. */
+       u32 config;             /* Some subset of OFPPC_* flags. */
+       u32 state;              /* Some subset of OFPPS_* flags. */
        spinlock_t lock;
        struct work_struct port_task;
        struct datapath *dp;
 
        struct net_device *dev = ptr;
        struct net_bridge_port *p = dev->br_port;
        unsigned long int flags;
-       uint32_t orig_status;
+       uint32_t orig_state, orig_config;
 
 
        /* Check if monitored port */
                return NOTIFY_DONE;
 
        spin_lock_irqsave(&p->lock, flags);
-       orig_status = p->status;
+       orig_state = p->state;
+       orig_config = p->config;
 
        switch (event) {
                case NETDEV_CHANGE:
                        if (netif_carrier_ok(p->dev))
-                               p->status &= ~OFPPFL_LINK_DOWN;
+                               p->state &= ~OFPPS_LINK_DOWN;
                        else
-                               p->status |= OFPPFL_LINK_DOWN;
+                               p->state |= OFPPS_LINK_DOWN;
                        break;
 
                case NETDEV_DOWN:
-                       p->status |= OFPPFL_PORT_DOWN;
+                       p->config |= OFPPC_PORT_DOWN;
                        break;
 
                case NETDEV_UP:
-                       p->status &= ~OFPPFL_PORT_DOWN;
+                       p->config &= ~OFPPC_PORT_DOWN;
                        break;
 
                case NETDEV_UNREGISTER:
-                       /* xxx Make sure this is correct */
                        spin_unlock_irqrestore(&p->lock, flags);
                        dp_del_switch_port(p);
                        return NOTIFY_DONE;
        }
        spin_unlock_irqrestore(&p->lock, flags);
 
-       if (orig_status != p->status) 
-               dp_send_port_status(p, OFPPR_MOD);
+       if ((orig_state != p->state) || (orig_config != p->config))
+               dp_send_port_status(p, OFPPR_MODIFY);
 
        return NOTIFY_DONE;
 }
 
                kfree_skb(skb);
                return 0;
        }
-       if (p && p->flags & (OFPPFL_NO_RECV | OFPPFL_NO_RECV_STP) &&
-           p->flags & (compare_ether_addr(key.dl_dst, stp_eth_addr)
-                       ? OFPPFL_NO_RECV : OFPPFL_NO_RECV_STP)) {
+       if (p && p->config & (OFPPC_NO_RECV | OFPPC_NO_RECV_STP) &&
+           p->config & (compare_ether_addr(key.dl_dst, stp_eth_addr)
+                       ? OFPPC_NO_RECV : OFPPC_NO_RECV_STP)) {
                kfree_skb(skb);
                return 0;
        }
 
 struct in_addr;
 struct in6_addr;
 
+enum netdev_feature_type {
+    NETDEV_FEAT_CURRENT,
+    NETDEV_FEAT_ADVERTISED,
+    NETDEV_FEAT_SUPPORTED,
+    NETDEV_FEAT_PEER
+};
+
 enum netdev_flags {
     NETDEV_UP = 0x0001,         /* Device enabled? */
     NETDEV_PROMISC = 0x0002     /* Promiscuous mode? */
 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 *);
+uint32_t netdev_get_features(struct netdev *, int);
 bool netdev_get_in4(const struct netdev *, struct in_addr *);
 int netdev_set_in4(struct netdev *, struct in_addr addr, struct in_addr mask);
 int netdev_add_router(struct netdev *, struct in_addr router);
 
 /* The most significant bit being set in the version field indicates an
  * experimental OpenFlow version.  
  */
-#define OFP_VERSION   0x92
+#define OFP_VERSION   0x93
 
 #define OFP_MAX_TABLE_NAME_LEN 32
 #define OFP_MAX_PORT_NAME_LEN  16
 
 /* Capabilities supported by the datapath. */
 enum ofp_capabilities {
-    OFPC_FLOW_STATS   = 1 << 0, /* Flow statistics. */
-    OFPC_TABLE_STATS  = 1 << 1, /* Table statistics. */
-    OFPC_PORT_STATS   = 1 << 2, /* Port statistics. */
-    OFPC_STP          = 1 << 3, /* 802.11d spanning tree. */
-    OFPC_MULTI_PHY_TX = 1 << 4, /* Supports transmitting through multiple
-                                   physical interfaces */
-    OFPC_IP_REASM     = 1 << 5  /* Can reassemble IP fragments. */
-};
-
-/* Flags to indicate behavior of the physical port. */
-enum ofp_port_flags {
-    /* Read/write bits. */
-    OFPPFL_PORT_DOWN    = 1 << 1, /* Port is configured down. */
-    OFPPFL_NO_STP       = 1 << 3, /* Disable 802.1D spanning tree on port. */
-    OFPPFL_NO_RECV      = 1 << 4, /* Drop most packets received on port. */
-    OFPPFL_NO_RECV_STP  = 1 << 5, /* Drop received 802.1D STP packets. */
-    OFPPFL_NO_FWD       = 1 << 6, /* Drop packets forwarded to port. */
-    OFPPFL_NO_PACKET_IN = 1 << 7, /* Do not send packet-in msgs for port. */
-
-    /* Read-only bits. */
-    OFPPFL_LINK_DOWN    = 1 << 2, /* No physical link present. */
-
-    /* Read-only when STP is enabled (when OFPPFL_NO_STP is not set).
-     * Read/write when STP is disabled (when OFPPFL_NO_STP is set).
-     *
-     * The OFPPFL_STP_* bits have no effect on switch operation.  The
+    OFPC_FLOW_STATS     = 1 << 0,  /* Flow statistics. */
+    OFPC_TABLE_STATS    = 1 << 1,  /* Table statistics. */
+    OFPC_PORT_STATS     = 1 << 2,  /* Port statistics. */
+    OFPC_STP            = 1 << 3,  /* 802.1d spanning tree. */
+    OFPC_MULTI_PHY_TX   = 1 << 4,  /* Supports transmitting through multiple
+                                      physical interfaces */
+    OFPC_IP_REASM       = 1 << 5   /* Can reassemble IP fragments. */
+};
+
+/* Flags to indicate behavior of the physical port.  These flags are
+ * used in ofp_phy_port to describe the current configuration.  They are
+ * used in the ofp_port_mod message to configure the port's behavior. 
+ */
+enum ofp_port_config {
+    OFPPC_PORT_DOWN    = 1 << 0,  /* Port is administratively down. */
+
+    OFPPC_NO_STP       = 1 << 1,  /* Disable 802.1D spanning tree on port. */
+    OFPPC_NO_RECV      = 1 << 2,  /* Drop most packets received on port. */
+    OFPPC_NO_RECV_STP  = 1 << 3,  /* Drop received 802.1D STP packets. */
+    OFPPC_NO_FLOOD     = 1 << 4,  /* Do not include this port when flooding. */
+    OFPPC_NO_FWD       = 1 << 5,  /* Drop packets forwarded to port. */
+    OFPPC_NO_PACKET_IN = 1 << 6   /* Do not send packet-in msgs for port. */
+};
+
+/* Current state of the physical port.  These are not configurable from
+ * the controller.
+ */
+enum ofp_port_state {
+    OFPPS_LINK_DOWN   = 1 << 0, /* No physical link present. */
+
+    /* The OFPPFL_STP_* bits have no effect on switch operation.  The
      * controller must adjust OFPPFL_NO_RECV, OFPPFL_NO_FWD, and
      * OFPPFL_NO_PACKET_IN appropriately to fully implement an 802.1D spanning
      * tree. */
-    OFPPFL_NO_FLOOD     = 1 << 0, /* Do not include this port when flooding. */
-    OFPPFL_STP_LISTEN   = 0 << 8, /* Not learning or relaying frames. */
-    OFPPFL_STP_LEARN    = 1 << 8, /* Learning but not relaying frames. */
-    OFPPFL_STP_FORWARD  = 2 << 8, /* Learning and relaying frames. */
-    OFPPFL_STP_BLOCK    = 3 << 8, /* Not part of spanning tree. */
-    OFPPFL_STP_MASK     = 3 << 8, /* Bit mask for OFPPFL_STP_* values. */
+    OFPPS_STP_LISTEN  = 0 << 8, /* Not learning or relaying frames. */
+    OFPPS_STP_LEARN   = 1 << 8, /* Learning but not relaying frames. */
+    OFPPS_STP_FORWARD = 2 << 8, /* Learning and relaying frames. */
+    OFPPS_STP_BLOCK   = 3 << 8, /* Not part of spanning tree. */
+    OFPPS_STP_MASK    = 3 << 8  /* Bit mask for OFPPFL_STP_* values. */
 };
 
 /* Features of physical ports available in a datapath. */
 enum ofp_port_features {
-    OFPPF_10MB_HD    = 1 << 0, /* 10 Mb half-duplex rate support. */
-    OFPPF_10MB_FD    = 1 << 1, /* 10 Mb full-duplex rate support. */
-    OFPPF_100MB_HD   = 1 << 2, /* 100 Mb half-duplex rate support. */
-    OFPPF_100MB_FD   = 1 << 3, /* 100 Mb full-duplex rate support. */
-    OFPPF_1GB_HD     = 1 << 4, /* 1 Gb half-duplex rate support. */
-    OFPPF_1GB_FD     = 1 << 5, /* 1 Gb full-duplex rate support. */
-    OFPPF_10GB_FD    = 1 << 6, /* 10 Gb full-duplex rate support. */
+    OFPPF_10MB_HD    = 1 << 0,  /* 10 Mb half-duplex rate support. */
+    OFPPF_10MB_FD    = 1 << 1,  /* 10 Mb full-duplex rate support. */
+    OFPPF_100MB_HD   = 1 << 2,  /* 100 Mb half-duplex rate support. */
+    OFPPF_100MB_FD   = 1 << 3,  /* 100 Mb full-duplex rate support. */
+    OFPPF_1GB_HD     = 1 << 4,  /* 1 Gb half-duplex rate support. */
+    OFPPF_1GB_FD     = 1 << 5,  /* 1 Gb full-duplex rate support. */
+    OFPPF_10GB_FD    = 1 << 6,  /* 10 Gb full-duplex rate support. */
+    OFPPF_COPPER     = 1 << 7,  /* Copper medium */
+    OFPPF_FIBER      = 1 << 8,  /* Fiber medium */
+    OFPPF_AUTONEG    = 1 << 9,  /* Auto-negotiation */
+    OFPPF_PAUSE      = 1 << 10, /* Pause */
+    OFPPF_PAUSE_ASYM = 1 << 11  /* Asymmetric pause */
 };
 
-
 /* Description of a physical port */
 struct ofp_phy_port {
     uint16_t port_no;
     uint8_t hw_addr[OFP_ETH_ALEN];
     uint8_t name[OFP_MAX_PORT_NAME_LEN]; /* Null-terminated */
-    uint32_t flags;         /* Bitmap of "ofp_port_flags". */
-    uint32_t speed;         /* Current speed in Mbps */
-    uint32_t features;      /* Bitmap of supported "ofp_port_features"s. */
+
+    uint32_t config;        /* Bitmap of OFPPC_* flags. */
+    uint32_t state;         /* Bitmap of OFPPS_* flags. */
+
+    /* Bitmaps of OFPPF_* that describe features.  All bits zeroed if
+     * unsupported or unavailable. */
+    uint32_t curr;          /* Current features. */
+    uint32_t advertised;    /* Features being advertised by the port. */
+    uint32_t supported;     /* Features supported by the port. */
+    uint32_t peer;          /* Features advertised by peer. */
 };
-OFP_ASSERT(sizeof(struct ofp_phy_port) == 36);
+OFP_ASSERT(sizeof(struct ofp_phy_port) == 48);
 
 /* Switch features. */
 struct ofp_switch_features {
 enum ofp_port_reason {
     OFPPR_ADD,              /* The port was added */
     OFPPR_DELETE,           /* The port was removed */
-    OFPPR_MOD               /* Some attribute of the port has changed */
+    OFPPR_MODIFY            /* Some attribute of the port has changed */
 };
 
 /* A physical port has changed in the datapath */
 struct ofp_port_status {
     struct ofp_header header;
     uint8_t reason;          /* One of OFPPR_* */
-    uint8_t pad[3];          /* Align to 32-bits */
+    uint8_t pad[7];          /* Align to 64-bits */
     struct ofp_phy_port desc;
 };
-OFP_ASSERT(sizeof(struct ofp_port_status) == 48);
+OFP_ASSERT(sizeof(struct ofp_port_status) == 64);
 
 /* 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;
+    uint16_t port_no;
+    uint8_t hw_addr[OFP_ETH_ALEN]; /* The hardware address is not 
+                                      configurable.  This is used to 
+                                      sanity-check the request, so it must 
+                                      be the same as returned in an
+                                      ofp_phy_port struct. */
+
+    uint32_t config;        /* Bitmap of OFPPC_* flags. */
+    uint32_t mask;          /* Bitmap of OFPPC_* flags to be changed. */
+
+    uint32_t advertise;     /* Bitmap of "ofp_port_features"s.  Zero all 
+                               bits to prevent any action taking place. */
+    uint8_t pad[4];         /* Pad to 64-bits. */
 };
-OFP_ASSERT(sizeof(struct ofp_port_mod) == 48);
+OFP_ASSERT(sizeof(struct ofp_port_mod) == 32);
 
 /* Why is this packet being sent to the controller? */
 enum ofp_packet_in_reason {
 OFP_ASSERT(sizeof(struct ofp_stats_request) == 12);
 
 enum ofp_stats_reply_flags {
-    OFPSF_REPLY_MORE  = 1 << 0, /* More replies to follow */
+    OFPSF_REPLY_MORE  = 1 << 0  /* More replies to follow */
 };
 
 struct ofp_stats_reply {
 
                  const struct ofp_phy_port *opp)
 {
     if (sw->capabilities & OFPC_STP && ntohs(opp->port_no) < OFPP_MAX) {
-        uint32_t flags = ntohl(opp->flags);
-        uint32_t new_flags = flags & ~(OFPPFL_NO_RECV | OFPPFL_NO_RECV_STP
-                                       | OFPPFL_NO_FWD | OFPPFL_NO_PACKET_IN);
-        if (!(flags & (OFPPFL_NO_STP | OFPPFL_PORT_DOWN | OFPPFL_LINK_DOWN))) {
+        uint32_t config = ntohl(opp->config);
+        uint32_t state = ntohl(opp->state);
+        uint32_t new_config = config & ~(OFPPC_NO_RECV | OFPPC_NO_RECV_STP
+                                         | OFPPC_NO_FWD | OFPPC_NO_PACKET_IN);
+        if (!(config & (OFPPC_NO_STP | OFPPC_PORT_DOWN))
+                    && !(state & OFPPS_LINK_DOWN)) {
             bool forward = false;
             bool learn = false;
-            switch (flags & OFPPFL_STP_MASK) {
-            case OFPPFL_STP_LISTEN:
-            case OFPPFL_STP_BLOCK:
+            switch (state & OFPPS_STP_MASK) {
+            case OFPPS_STP_LISTEN:
+            case OFPPS_STP_BLOCK:
                 break;
-            case OFPPFL_STP_LEARN:
+            case OFPPS_STP_LEARN:
                 learn = true;
                 break;
-            case OFPPFL_STP_FORWARD:
+            case OFPPS_STP_FORWARD:
                 forward = learn = true;
                 break;
             }
             if (!forward) {
-                new_flags |= OFPPFL_NO_RECV | OFPPFL_NO_FWD;
+                new_config |= OFPPC_NO_RECV | OFPPC_NO_FWD;
             }
             if (!learn) {
-                new_flags |= OFPPFL_NO_PACKET_IN;
+                new_config |= OFPPC_NO_PACKET_IN;
             }
         }
-        if (flags != new_flags) {
+        if (config != new_config) {
             struct ofp_port_mod *opm;
             struct ofpbuf *b;
             int retval;
 
-            VLOG_WARN("port %d: flags=%x new_flags=%x",
-                      ntohs(opp->port_no), flags, new_flags);
+            VLOG_WARN("port %d: config=%x new_config=%x",
+                      ntohs(opp->port_no), config, new_config);
             opm = make_openflow(sizeof *opm, OFPT_PORT_MOD, &b);
-            opm->mask = htonl(flags ^ new_flags);
-            opm->desc = *opp;
-            opm->desc.flags = htonl(new_flags);
+            memcpy(opm->hw_addr, opp->hw_addr, OFP_ETH_ALEN);
+            opm->config = htonl(new_config);
+            opm->mask = htonl(config ^ new_config);
+            opm->advertise = htonl(0);
             retval = rconn_send(rconn, b, NULL);
             if (retval) {
                 if (retval != ENOTCONN) {
 
 #include <linux/types.h>
 #include <linux/ethtool.h>
 #include <linux/sockios.h>
+#include <linux/version.h>
 #include <sys/types.h>
 #include <sys/ioctl.h>
 #include <sys/socket.h>
     uint8_t etheraddr[ETH_ADDR_LEN];
     int speed;
     int mtu;
-    uint32_t features;
+
+    /* Bitmaps of OFPPF_* that describe features.  All bits disabled if
+     * unsupported or unavailable. */
+    uint32_t curr;              /* Current features. */
+    uint32_t advertised;        /* Features being advertised by the port. */
+    uint32_t supported;         /* Features supported by the port. */
+    uint32_t peer;              /* Features advertised by the peer. */
+
     struct in6_addr in6;
     int save_flags;             /* Initial device flags. */
     int changed_flags;          /* Flags that we changed. */
     struct ifreq ifr;
     struct ethtool_cmd ecmd;
 
-    netdev->speed = 0;
-    netdev->features = 0;
+    netdev->curr = 0;
+    netdev->supported = 0;
+    netdev->advertised = 0;
+    netdev->peer = 0;
 
     memset(&ifr, 0, sizeof ifr);
     strncpy(ifr.ifr_name, netdev->name, sizeof ifr.ifr_name);
     ecmd.cmd = ETHTOOL_GSET;
     if (ioctl(netdev->fd, SIOCETHTOOL, &ifr) == 0) {
         if (ecmd.supported & SUPPORTED_10baseT_Half) {
-            netdev->features |= OFPPF_10MB_HD;
+            netdev->supported |= OFPPF_10MB_HD;
         }
         if (ecmd.supported & SUPPORTED_10baseT_Full) {
-            netdev->features |= OFPPF_10MB_FD;
+            netdev->supported |= OFPPF_10MB_FD;
         }
         if (ecmd.supported & SUPPORTED_100baseT_Half)  {
-            netdev->features |= OFPPF_100MB_HD;
+            netdev->supported |= OFPPF_100MB_HD;
         }
         if (ecmd.supported & SUPPORTED_100baseT_Full) {
-            netdev->features |= OFPPF_100MB_FD;
+            netdev->supported |= OFPPF_100MB_FD;
         }
         if (ecmd.supported & SUPPORTED_1000baseT_Half) {
-            netdev->features |= OFPPF_1GB_HD;
+            netdev->supported |= OFPPF_1GB_HD;
         }
         if (ecmd.supported & SUPPORTED_1000baseT_Full) {
-            netdev->features |= OFPPF_1GB_FD;
+            netdev->supported |= OFPPF_1GB_FD;
         }
-        /* 10Gbps half-duplex doesn't exist... */
         if (ecmd.supported & SUPPORTED_10000baseT_Full) {
-            netdev->features |= OFPPF_10GB_FD;
+            netdev->supported |= OFPPF_10GB_FD;
         }
+        if (ecmd.supported & SUPPORTED_TP) {
+            netdev->supported |= OFPPF_COPPER;
+        }
+        if (ecmd.supported & SUPPORTED_FIBRE) {
+            netdev->supported |= OFPPF_FIBER;
+        }
+        if (ecmd.supported & SUPPORTED_Autoneg) {
+            netdev->supported |= OFPPF_AUTONEG;
+        }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,14)
+        if (ecmd.supported & SUPPORTED_Pause) {
+            netdev->supported |= OFPPF_PAUSE;
+        }
+        if (ecmd.supported & SUPPORTED_Asym_Pause) {
+            netdev->supported |= OFPPF_PAUSE_ASYM;
+        }
+#endif /* kernel >= 2.6.14 */
 
-        switch (ecmd.speed) {
-        case SPEED_10:
-            netdev->speed = 10;
-            break;
-
-        case SPEED_100:
-            netdev->speed = 100;
-            break;
+        /* Set the advertised features */
+        if (ecmd.advertising & ADVERTISED_10baseT_Half) {
+            netdev->advertised |= OFPPF_10MB_HD;
+        }
+        if (ecmd.advertising & ADVERTISED_10baseT_Full) {
+            netdev->advertised |= OFPPF_10MB_FD;
+        }
+        if (ecmd.advertising & ADVERTISED_100baseT_Half) {
+            netdev->advertised |= OFPPF_100MB_HD;
+        }
+        if (ecmd.advertising & ADVERTISED_100baseT_Full) {
+            netdev->advertised |= OFPPF_100MB_FD;
+        }
+        if (ecmd.advertising & ADVERTISED_1000baseT_Half) {
+            netdev->advertised |= OFPPF_1GB_HD;
+        }
+        if (ecmd.advertising & ADVERTISED_1000baseT_Full) {
+            netdev->advertised |= OFPPF_1GB_FD;
+        }
+        if (ecmd.advertising & ADVERTISED_10000baseT_Full) {
+            netdev->advertised |= OFPPF_10GB_FD;
+        }
+        if (ecmd.advertising & ADVERTISED_TP) {
+            netdev->advertised |= OFPPF_COPPER;
+        }
+        if (ecmd.advertising & ADVERTISED_FIBRE) {
+            netdev->advertised |= OFPPF_FIBER;
+        }
+        if (ecmd.advertising & ADVERTISED_Autoneg) {
+            netdev->advertised |= OFPPF_AUTONEG;
+        }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,14)
+        if (ecmd.advertising & ADVERTISED_Pause) {
+            netdev->advertised |= OFPPF_PAUSE;
+        }
+        if (ecmd.advertising & ADVERTISED_Asym_Pause) {
+            netdev->advertised |= OFPPF_PAUSE_ASYM;
+        }
+#endif /* kernel >= 2.6.14 */
 
-        case SPEED_1000:
-            netdev->speed = 1000;
-            break;
+        /* Set the current features */
+        if (ecmd.speed == SPEED_10) {
+            netdev->curr = (ecmd.duplex) ? OFPPF_10MB_FD : OFPPF_10MB_HD;
+        }
+        else if (ecmd.speed == SPEED_100) {
+            netdev->curr = (ecmd.duplex) ? OFPPF_100MB_FD : OFPPF_100MB_HD;
+        }
+        else if (ecmd.speed == SPEED_1000) {
+            netdev->curr = (ecmd.duplex) ? OFPPF_1GB_FD : OFPPF_1GB_HD;
+        }
+        else if (ecmd.speed == SPEED_10000) {
+            netdev->curr = OFPPF_10GB_FD;
+        }
 
-        case SPEED_2500:
-            netdev->speed = 2500;
-            break;
+        if (ecmd.port == PORT_TP) {
+            netdev->curr |= OFPPF_COPPER;
+        }
+        else if (ecmd.port == PORT_FIBRE) {
+            netdev->curr |= OFPPF_FIBER;
+        }
 
-        case SPEED_10000:
-            netdev->speed = 10000;
-            break;
+        if (ecmd.autoneg) {
+            netdev->curr |= OFPPF_AUTONEG;
         }
     } else {
         VLOG_DBG("ioctl(SIOCETHTOOL) failed: %s", strerror(errno));
     return netdev->mtu;
 }
 
-/* Returns the current speed of the network device that 'netdev' represents, in
- * megabits per second, or 0 if the speed is unknown. */
-int
-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
     return -1;
 }
 
-/* Returns the features supported by 'netdev', as a bitmap of bits from enum
- * ofp_phy_port, in host byte order. */
+/* Returns the features supported by 'netdev' of type 'type', as a bitmap 
+ * of bits from enum ofp_phy_features, in host byte order. */
 uint32_t
-netdev_get_features(const struct netdev *netdev) 
+netdev_get_features(struct netdev *netdev, int type) 
 {
-    return netdev->features;
+    do_ethtool(netdev);
+    switch (type) {
+    case NETDEV_FEAT_CURRENT:
+        return netdev->curr;
+    case NETDEV_FEAT_ADVERTISED:
+        return netdev->advertised;
+    case NETDEV_FEAT_SUPPORTED:
+        return netdev->supported;
+    case NETDEV_FEAT_PEER:
+        return netdev->peer;
+    default:
+        VLOG_WARN("Unknown feature type: %d\n", type);
+        return 0;
+    }
 }
 
 /* If 'netdev' has an assigned IPv4 address, sets '*in4' to that address (if
 
     return ap < bp ? -1 : ap > bp;
 }
 
+static void ofp_print_port_features(struct ds *string, uint32_t features)
+{
+    if (features == 0) {
+        ds_put_cstr(string, "Unsupported\n");
+        return;
+    }
+    if (features & OFPPF_10MB_HD) {
+        ds_put_cstr(string, "10MB-HD ");
+    }
+    if (features & OFPPF_10MB_FD) {
+        ds_put_cstr(string, "10MB-FD ");
+    }
+    if (features & OFPPF_100MB_HD) {
+        ds_put_cstr(string, "100MB-HD ");
+    }
+    if (features & OFPPF_100MB_FD) {
+        ds_put_cstr(string, "100MB-FD ");
+    }
+    if (features & OFPPF_1GB_HD) {
+        ds_put_cstr(string, "1GB-HD ");
+    }
+    if (features & OFPPF_1GB_FD) {
+        ds_put_cstr(string, "1GB-FD ");
+    }
+    if (features & OFPPF_10GB_FD) {
+        ds_put_cstr(string, "10GB-FD ");
+    }
+    if (features & OFPPF_COPPER) {
+        ds_put_cstr(string, "COPPER ");
+    }
+    if (features & OFPPF_FIBER) {
+        ds_put_cstr(string, "FIBER ");
+    }
+    if (features & OFPPF_AUTONEG) {
+        ds_put_cstr(string, "AUTO_NEG ");
+    }
+    if (features & OFPPF_PAUSE) {
+        ds_put_cstr(string, "AUTO_PAUSE ");
+    }
+    if (features & OFPPF_PAUSE_ASYM) {
+        ds_put_cstr(string, "AUTO_PAUSE_ASYM ");
+    }
+    ds_put_char(string, '\n');
+}
+
 static void
 ofp_print_phy_port(struct ds *string, const struct ofp_phy_port *port)
 {
 
     ds_put_char(string, ' ');
     ofp_print_port_name(string, ntohs(port->port_no));
-    ds_put_format(string, "(%s): addr:"ETH_ADDR_FMT", speed:%d, flags:%#x, "
-            "feat:%#x\n", name, 
-            ETH_ADDR_ARGS(port->hw_addr), ntohl(port->speed),
-            ntohl(port->flags), ntohl(port->features));
+    ds_put_format(string, "(%s): addr:"ETH_ADDR_FMT", config: %#x, state:%#x\n",
+            name, ETH_ADDR_ARGS(port->hw_addr), ntohl(port->config),
+            ntohl(port->state));
+    if (port->curr) {
+        ds_put_format(string, "     current:    ");
+        ofp_print_port_features(string, ntohl(port->curr));
+    }
+    if (port->advertised) {
+        ds_put_format(string, "     advertised: ");
+        ofp_print_port_features(string, ntohl(port->advertised));
+    }
+    if (port->supported) {
+        ds_put_format(string, "     supported:  ");
+        ofp_print_port_features(string, ntohl(port->supported));
+    }
+    if (port->peer) {
+        ds_put_format(string, "     peer:       ");
+        ofp_print_port_features(string, ntohl(port->peer));
+    }
 }
 
 /* Pretty-print the struct ofp_switch_features of 'len' bytes at 'oh' to
         ds_put_format(string, " ADD:");
     } else if (ops->reason == OFPPR_DELETE) {
         ds_put_format(string, " DEL:");
-    } else if (ops->reason == OFPPR_MOD) {
+    } else if (ops->reason == OFPPR_MODIFY) {
         ds_put_format(string, " MOD:");
     }
 
 
 static struct hook port_watcher_create(struct rconn *local,
                                        struct rconn *remote,
                                        struct port_watcher **);
-static uint32_t port_watcher_get_flags(const struct port_watcher *,
+static uint32_t port_watcher_get_config(const struct port_watcher *,
                                        int port_no);
-static void port_watcher_set_flags(struct port_watcher *,
-                                   int port_no, uint32_t flags, uint32_t mask);
+static void port_watcher_set_flags(struct port_watcher *, int port_no, 
+                                   uint32_t config, uint32_t c_mask,
+                                   uint32_t state, uint32_t s_mask);
 
 static struct hook stp_hook_create(const struct settings *,
                                    struct port_watcher *,
 static int
 opp_differs(const struct ofp_phy_port *a, const struct ofp_phy_port *b)
 {
-    BUILD_ASSERT_DECL(sizeof *a == 36); /* Trips when we add or remove fields. */
+    BUILD_ASSERT_DECL(sizeof *a == 48); /* Trips when we add or remove fields. */
     return ((a->port_no != b->port_no)
             + (memcmp(a->hw_addr, b->hw_addr, sizeof a->hw_addr) != 0)
             + (memcmp(a->name, b->name, sizeof a->name) != 0)
-            + (a->flags != b->flags)
-            + (a->speed != b->speed)
-            + (a->features != b->features));
+            + (a->config != b->config)
+            + (a->state != b->state)
+            + (a->curr != b->curr)
+            + (a->advertised != b->advertised)
+            + (a->supported != b->supported)
+            + (a->peer != b->peer));
 }
 
 static void
     if (reason == OFPPR_DELETE) {
         memset(pw_opp, 0, sizeof *pw_opp);
         pw_opp->port_no = htons(OFPP_NONE);
-    } else if (reason == OFPPR_MOD || reason == OFPPR_ADD) {
+    } else if (reason == OFPPR_MODIFY || reason == OFPPR_ADD) {
         *pw_opp = *opp;
         sanitize_opp(pw_opp);
     }
                    / sizeof *osf->ports);
         for (i = 0; i < n_ports; i++) {
             struct ofp_phy_port *opp = &osf->ports[i];
-            update_phy_port(pw, opp, OFPPR_MOD, seen);
+            update_phy_port(pw, opp, OFPPR_MODIFY, seen);
         }
 
         /* Delete all the ports not included in the message. */
     if (oh->type == OFPT_PORT_MOD
         && msg->size >= sizeof(struct ofp_port_mod)) {
         struct ofp_port_mod *opm = msg->data;
-        uint16_t port_no = ntohs(opm->desc.port_no);
+        uint16_t port_no = ntohs(opm->port_no);
         int idx = port_no_to_pw_idx(port_no);
         if (idx >= 0) {
             struct ofp_phy_port *pw_opp = &pw->ports[idx];
             if (pw_opp->port_no != htons(OFPP_NONE)) {
                 struct ofp_phy_port old = *pw_opp;
-                pw_opp->flags = ((pw_opp->flags & ~opm->mask)
-                                 | (opm->desc.flags & opm->mask));
+                pw_opp->config = ((pw_opp->config & ~opm->mask)
+                                 | (opm->config & opm->mask));
                 call_port_changed_callbacks(pw, port_no, &old, pw_opp);
             }
         }
     }
 }
 
+static void
+put_features(struct ds *ds, const char *name, uint32_t features) {
+    if (features & (OFPPF_10MB_HD | OFPPF_10MB_FD
+                    | OFPPF_100MB_HD | OFPPF_100MB_FD
+                    | OFPPF_1GB_HD | OFPPF_1GB_FD | OFPPF_10GB_FD)) {
+        ds_put_cstr(ds, name);
+        put_duplexes(ds, "10M", features, OFPPF_10MB_HD, OFPPF_10MB_FD);
+        put_duplexes(ds, "100M", features,
+                     OFPPF_100MB_HD, OFPPF_100MB_FD);
+        put_duplexes(ds, "1G", features, OFPPF_100MB_HD, OFPPF_100MB_FD);
+        if (features & OFPPF_10GB_FD) {
+            ds_put_cstr(ds, " 10G");
+        }
+        if (features & OFPPF_AUTONEG) {
+            ds_put_cstr(ds, " AUTO_NEG");
+        }
+        if (features & OFPPF_PAUSE) {
+            ds_put_cstr(ds, " PAUSE");
+        }
+        if (features & OFPPF_PAUSE_ASYM) {
+            ds_put_cstr(ds, " PAUSE_ASYM");
+        }
+    }
+}
+
 static void
 log_port_status(uint16_t port_no,
                 const struct ofp_phy_port *old,
     if (VLOG_IS_DBG_ENABLED()) {
         bool was_enabled = old->port_no != htons(OFPP_NONE);
         bool now_enabled = new->port_no != htons(OFPP_NONE);
-        uint32_t features = ntohl(new->features);
+        uint32_t curr = ntohl(new->curr);
+        uint32_t supported = ntohl(new->supported);
         struct ds ds;
 
-        if (old->flags != new->flags && opp_differs(old, new) == 1) {
+        if (((old->config != new->config) || (old->state != new->state))
+                && opp_differs(old, new) == 1) {
             /* Don't care if only flags changed. */
             return;
         }
         ds_init(&ds);
         ds_put_format(&ds, "\"%s\", "ETH_ADDR_FMT, new->name,
                       ETH_ADDR_ARGS(new->hw_addr));
-        if (ntohl(new->speed)) {
-            ds_put_format(&ds, ", speed %"PRIu32, ntohl(new->speed));
+        if (curr) {
+            put_features(&ds, ", current", curr);
         }
-        if (features & (OFPPF_10MB_HD | OFPPF_10MB_FD
-                        | OFPPF_100MB_HD | OFPPF_100MB_FD
-                        | OFPPF_1GB_HD | OFPPF_1GB_FD | OFPPF_10GB_FD)) {
-            ds_put_cstr(&ds, ", supports");
-            put_duplexes(&ds, "10M", features, OFPPF_10MB_HD, OFPPF_10MB_FD);
-            put_duplexes(&ds, "100M", features,
-                         OFPPF_100MB_HD, OFPPF_100MB_FD);
-            put_duplexes(&ds, "1G", features, OFPPF_100MB_HD, OFPPF_100MB_FD);
-            if (features & OFPPF_10GB_FD) {
-                ds_put_cstr(&ds, " 10G");
-            }
+        if (supported) {
+            put_features(&ds, ", supports", supported);
         }
         if (was_enabled != now_enabled) {
             if (now_enabled) {
 }
 
 static uint32_t
-port_watcher_get_flags(const struct port_watcher *pw, int port_no)
+port_watcher_get_config(const struct port_watcher *pw, int port_no)
 {
     int idx = port_no_to_pw_idx(port_no);
-    return idx >= 0 ? ntohl(pw->ports[idx].flags) : 0;
+    return idx >= 0 ? ntohl(pw->ports[idx].config) : 0;
 }
 
 static void
-port_watcher_set_flags(struct port_watcher *pw,
-                       int port_no, uint32_t flags, uint32_t mask)
+port_watcher_set_flags(struct port_watcher *pw, int port_no, 
+                       uint32_t config, uint32_t c_mask,
+                       uint32_t state, uint32_t s_mask)
 {
     struct ofp_phy_port old;
     struct ofp_phy_port *p;
     }
 
     p = &pw->ports[idx];
-    if (!((ntohl(p->flags) ^ flags) & mask)) {
+    if (!((ntohl(p->state) ^ state) & s_mask) 
+            && (!((ntohl(p->config) ^ config) & c_mask))) {
         return;
     }
     old = *p;
 
     /* Update our idea of the flags. */
-    p->flags = htonl((ntohl(p->flags) & ~mask) | (flags & mask));
+    p->config = htonl((ntohl(p->config) & ~c_mask) | (config & c_mask));
+    p->state = htonl((ntohl(p->state) & ~s_mask) | (state & s_mask));
     call_port_changed_callbacks(pw, port_no, &old, p);
 
     /* Change the flags in the datapath. */
     opm = make_openflow(sizeof *opm, OFPT_PORT_MOD, &b);
-    opm->mask = htonl(mask);
-    opm->desc = *p;
+    opm->port_no = p->port_no;
+    memcpy(opm->hw_addr, p->hw_addr, OFP_ETH_ALEN);
+    opm->config = p->config;
+    opm->mask = htonl(c_mask);
+    opm->advertise = htonl(0);
     rconn_send(pw->local_rconn, b, NULL);
 
     /* Notify the controller that the flags changed. */
     ops = make_openflow(sizeof *ops, OFPT_PORT_STATUS, &b);
-    ops->reason = OFPPR_MOD;
+    ops->reason = OFPPR_MODIFY;
     ops->desc = *p;
     rconn_send(pw->remote_rconn, b, NULL);
 }
         /* STP only supports 255 ports. */
         return false;
     }
-    if (port_watcher_get_flags(stp->pw, port_no) & OFPPFL_NO_STP) {
+    if (port_watcher_get_config(stp->pw, port_no) & OFPPC_NO_STP) {
         /* We're not doing STP on this port. */
         return false;
     }
 
     while (stp_get_changed_port(stp->stp, &p)) {
         int port_no = stp_port_no(p);
-        enum stp_state state = stp_port_get_state(p);
+        enum stp_state s_state = stp_port_get_state(p);
 
-        if (state != STP_DISABLED) {
+        if (s_state != STP_DISABLED) {
             VLOG_WARN("STP: Port %d entered %s state",
-                      port_no, stp_state_name(state));
+                      port_no, stp_state_name(s_state));
         }
-        if (!(port_watcher_get_flags(stp->pw, port_no) & OFPPFL_NO_STP)) {
-            uint32_t flags;
-            switch (state) {
+        if (!(port_watcher_get_config(stp->pw, port_no) & OFPPC_NO_STP)) {
+            uint32_t p_config = 0;
+            uint32_t p_state;
+            switch (s_state) {
             case STP_LISTENING:
-                flags = OFPPFL_STP_LISTEN;
+                p_state = OFPPS_STP_LISTEN;
                 break;
             case STP_LEARNING:
-                flags = OFPPFL_STP_LEARN;
+                p_state = OFPPS_STP_LEARN;
                 break;
             case STP_DISABLED:
             case STP_FORWARDING:
-                flags = OFPPFL_STP_FORWARD;
+                p_state = OFPPS_STP_FORWARD;
                 break;
             case STP_BLOCKING:
-                flags = OFPPFL_STP_BLOCK;
+                p_state = OFPPS_STP_BLOCK;
                 break;
             default:
                 VLOG_DBG_RL(&vrl, "STP: Port %d has bad state %x",
-                            port_no, state);
-                flags = OFPPFL_STP_FORWARD;
+                            port_no, s_state);
+                p_state = OFPPS_STP_FORWARD;
                 break;
             }
-            if (!stp_forward_in_state(state)) {
-                flags |= OFPPFL_NO_FLOOD;
+            if (!stp_forward_in_state(s_state)) {
+                p_config = OFPPC_NO_FLOOD;
             }
-            port_watcher_set_flags(stp->pw, port_no, flags,
-                                   OFPPFL_STP_MASK | OFPPFL_NO_FLOOD);
+            port_watcher_set_flags(stp->pw, port_no, 
+                                   p_config, OFPPC_NO_FLOOD,
+                                   p_state, OFPPS_STP_MASK);
         } else {
             /* We don't own those flags. */
         }
 
     p = stp_get_port(stp->stp, port_no);
     if (new->port_no == htons(OFPP_NONE)
-        || new->flags & htonl(OFPPFL_NO_STP)) {
+        || new->config & htonl(OFPPC_NO_STP)) {
         stp_port_disable(p);
     } else {
+        int speed = 0;
         stp_port_enable(p);
-        stp_port_set_speed(p, new->speed);
+        if (new->curr & (OFPPF_10MB_HD | OFPPF_10MB_FD)) {
+            speed = 10;
+        } else if (new->curr & (OFPPF_100MB_HD | OFPPF_100MB_FD)) {
+            speed = 100;
+        } else if (new->curr & (OFPPF_1GB_HD | OFPPF_1GB_FD)) {
+            speed = 1000;
+        } else if (new->curr & OFPPF_100MB_FD) {
+            speed = 10000;
+        }
+        stp_port_set_speed(p, speed);
     }
 }
 
 
                                 | (1 << OFPAT_SET_TP_SRC)   \
                                 | (1 << OFPAT_SET_TP_DST) )
 
-#define PORT_STATUS_BITS (OFPPFL_PORT_DOWN | OFPPFL_LINK_DOWN)
-#define PORT_FLAG_BITS (~PORT_STATUS_BITS)
-
 struct sw_port {
-    uint32_t flags;             /* Some subset of PORT_FLAG_BITS. */
-    uint32_t status;            /* Some subset of PORT_STATUS_BITS. */
+    uint32_t config;            /* Some subset of OFPPC_* flags. */
+    uint32_t state;             /* Some subset of OFPPS_* flags. */
     struct datapath *dp;
     struct netdev *netdev;
     struct list node; /* Element in datapath.ports. */
 
         LIST_FOR_EACH (p, struct sw_port, node, &dp->port_list) {
             if (update_port_status(p)) {
-                send_port_status(p, OFPPR_MOD);
+                send_port_status(p, OFPPR_MODIFY);
             }
         }
 
         if (port_no(dp, p) == in_port) {
             continue;
         }
-        if (flood && p->flags & OFPPFL_NO_FLOOD) {
+        if (flood && p->config & OFPPC_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 && !(p->status & OFPPFL_PORT_DOWN)) {
+        if (p->netdev != NULL && !(p->config & OFPPC_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 = 0;
-    desc->features = htonl(netdev_get_features(p->netdev));
-    desc->speed = htonl(netdev_get_speed(p->netdev));
-    desc->flags = htonl(p->flags | p->status);
+    desc->config = htonl(p->config);
+    desc->state = htonl(p->state);
+    desc->curr = htonl(netdev_get_features(p->netdev, NETDEV_FEAT_CURRENT));
+    desc->supported = htonl(netdev_get_features(p->netdev, 
+                NETDEV_FEAT_SUPPORTED));
+    desc->advertised = htonl(netdev_get_features(p->netdev, 
+                NETDEV_FEAT_ADVERTISED));
+    desc->peer = htonl(netdev_get_features(p->netdev, NETDEV_FEAT_PEER));
 }
 
 static void
 void
 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);
+    int port_no = ntohs(opm->port_no);
     if (port_no < OFPP_MAX) {
         struct sw_port *p = &dp->ports[port_no];
-        uint32_t flag_mask;
 
         /* Make sure the port id hasn't changed since this was sent */
-        if (!p || memcmp(opp->hw_addr, netdev_get_etheraddr(p->netdev),
+        if (!p || memcmp(opm->hw_addr, netdev_get_etheraddr(p->netdev),
                          ETH_ADDR_LEN) != 0) {
             return;
         }
 
 
-        flag_mask = ntohl(opm->mask) & PORT_FLAG_BITS;
-        if (flag_mask) {
-            p->flags &= ~flag_mask;
-            p->flags |= ntohl(opp->flags) & flag_mask;
+        if (opm->mask) {
+            uint32_t config_mask = ntohl(opm->mask);
+            p->config &= ~config_mask;
+            p->config |= ntohl(opm->config) & config_mask;
         }
 
-        if (opm->mask & htonl(OFPPFL_PORT_DOWN)) {
-            if ((opp->flags & htonl(OFPPFL_PORT_DOWN))
-                && (p->status & OFPPFL_PORT_DOWN) == 0) {
-                p->status |= OFPPFL_PORT_DOWN;
+        if (opm->mask & htonl(OFPPC_PORT_DOWN)) {
+            if ((opm->config & htonl(OFPPC_PORT_DOWN))
+                && (p->config & OFPPC_PORT_DOWN) == 0) {
+                p->config |= OFPPC_PORT_DOWN;
                 netdev_turn_flags_off(p->netdev, NETDEV_UP, true);
-            } else if ((opp->flags & htonl(OFPPFL_PORT_DOWN)) == 0
-                       && (p->status & OFPPFL_PORT_DOWN)) {
-                p->status &= ~OFPPFL_PORT_DOWN;
+            } else if ((opm->config & htonl(OFPPC_PORT_DOWN)) == 0
+                       && (p->config & OFPPC_PORT_DOWN)) {
+                p->config &= ~OFPPC_PORT_DOWN;
                 netdev_turn_flags_on(p->netdev, NETDEV_UP, true);
             }
         }
 {
     int retval;
     enum netdev_flags flags;
-    uint32_t orig_status = p->status;
+    uint32_t orig_config = p->config;
+    uint32_t orig_state = p->state;
 
     if (netdev_get_flags(p->netdev, &flags) < 0) {
         VLOG_WARN_RL(&rl, "could not get netdev flags for %s", 
         return 0;
     } else {
         if (flags & NETDEV_UP) {
-            p->status &= ~OFPPFL_PORT_DOWN;
+            p->config &= ~OFPPC_PORT_DOWN;
         } else {
-            p->status |= OFPPFL_PORT_DOWN;
+            p->config |= OFPPC_PORT_DOWN;
         } 
     }
 
      * error. */
     retval = netdev_get_link_status(p->netdev);
     if (retval == 1) {
-        p->status &= ~OFPPFL_LINK_DOWN;
+        p->state &= ~OFPPS_LINK_DOWN;
     } else if (retval == 0) {
-        p->status |= OFPPFL_LINK_DOWN;
+        p->state |= OFPPS_LINK_DOWN;
     } 
 
-    return (orig_status != p->status);
+    return ((orig_config != p->config) || (orig_state != p->state));
 }
 
 static void
         ofpbuf_delete(buffer);
         return 0;
     }
-       if (p && p->flags & (OFPPFL_NO_RECV | OFPPFL_NO_RECV_STP)
-        && p->flags & (!eth_addr_equals(key.flow.dl_dst, stp_eth_addr)
-                       ? OFPPFL_NO_RECV : OFPPFL_NO_RECV_STP)) {
+       if (p && p->config & (OFPPC_NO_RECV | OFPPC_NO_RECV_STP)
+        && p->config & (!eth_addr_equals(key.flow.dl_dst, stp_eth_addr)
+                       ? OFPPC_NO_RECV : OFPPC_NO_RECV_STP)) {
                ofpbuf_delete(buffer);
                return 0;
        }
 
     }
 
     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;
+    opm->port_no = osf->ports[port_idx].port_no;
+    memcpy(opm->hw_addr, osf->ports[port_idx].hw_addr, sizeof opm->hw_addr);
+    opm->config = htonl(0);
+    opm->mask = htonl(0);
+    opm->advertise = htonl(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);
+        opm->mask |= htonl(OFPPC_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);
+        opm->mask |= htonl(OFPPC_PORT_DOWN);
+        opm->config |= htonl(OFPPC_PORT_DOWN);
     } else if (!strncasecmp(argv[3], MOD_PORT_CMD_FLOOD, 
                 sizeof MOD_PORT_CMD_FLOOD)) {
-        opm->mask |= htonl(OFPPFL_NO_FLOOD);
+        opm->mask |= htonl(OFPPC_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);
+        opm->mask |= htonl(OFPPC_NO_FLOOD);
+        opm->config |= htonl(OFPPC_NO_FLOOD);
     } else {
         ofp_fatal(0, "unknown mod-port command '%s'", argv[3]);
     }