xenserver: Set fail_mode on internal bridges.
[openvswitch] / vswitchd / bridge.c
index 39a94e1253548c8b3aef382e4aa4db510841e27a..9c614fb343d39e40b8a1ef96a8e27e6fbab5f64a 100644 (file)
@@ -35,6 +35,7 @@
 #include "cfm.h"
 #include "classifier.h"
 #include "coverage.h"
+#include "daemon.h"
 #include "dirs.h"
 #include "dpif.h"
 #include "dynamic-string.h"
@@ -75,13 +76,10 @@ VLOG_DEFINE_THIS_MODULE(bridge);
 
 COVERAGE_DEFINE(bridge_flush);
 COVERAGE_DEFINE(bridge_process_flow);
+COVERAGE_DEFINE(bridge_process_cfm);
+COVERAGE_DEFINE(bridge_process_lacp);
 COVERAGE_DEFINE(bridge_reconfigure);
-
-enum lacp_status {
-    LACP_STATUS_CURRENT,  /* Partner is up to date. */
-    LACP_STATUS_EXPIRED,  /* Partner is out of date. Attempt to re-sync. */
-    LACP_STATUS_DEFAULTED /* Partner information is unknown. */
-};
+COVERAGE_DEFINE(bridge_lacp_update);
 
 struct dst {
     uint16_t vlan;
@@ -98,6 +96,13 @@ static void dst_set_init(struct dst_set *);
 static void dst_set_add(struct dst_set *, const struct dst *);
 static void dst_set_free(struct dst_set *);
 
+enum lacp_status {
+    LACP_CURRENT   = 0x01, /* Current State. */
+    LACP_EXPIRED   = 0x02, /* Expired State. */
+    LACP_DEFAULTED = 0x04, /* Partner is defaulted. */
+    LACP_ATTACHED  = 0x08, /* Attached. Interface may be choosen for flows. */
+};
+
 struct iface {
     /* These members are always valid. */
     struct port *port;          /* Containing port. */
@@ -118,14 +123,12 @@ struct iface {
     const struct ovsrec_interface *cfg;
 
     /* LACP information. */
-    enum lacp_status lacp_status;  /* LACP state machine status. */
+    enum lacp_status lacp_status;  /* LACP status. */
     uint16_t lacp_priority;        /* LACP port priority. */
     struct lacp_info lacp_actor;   /* LACP actor information. */
     struct lacp_info lacp_partner; /* LACP partner information. */
     long long int lacp_tx;         /* Next LACP message transmission time. */
     long long int lacp_rx;         /* Next LACP message receive time. */
-    bool lacp_attached;            /* Attached to its aggregator?  LACP allows
-                                      this link to be chosen for flows. */
 };
 
 #define BOND_MASK 0xff
@@ -136,7 +139,8 @@ struct bond_entry {
 };
 
 enum bond_mode {
-    BM_SLB, /* Source Load Balance (Default). */
+    BM_TCP, /* Transport Layer Load Balance. */
+    BM_SLB, /* Source Load Balance. */
     BM_AB   /* Active Backup. */
 };
 
@@ -932,6 +936,10 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
     }
 
     free(managers);
+
+    /* ovs-vswitchd has completed initialization, so allow the process that
+     * forked us to exit successfully. */
+    daemonize_complete();
 }
 
 static const char *
@@ -1206,7 +1214,7 @@ iface_refresh_status(struct iface *iface)
                                     ? "up" : "down");
 
     error = netdev_get_mtu(iface->netdev, &mtu);
-    if (!error) {
+    if (!error && mtu != INT_MAX) {
         mtu_64 = mtu;
         ovsrec_interface_set_mtu(iface->cfg, &mtu_64, 1);
     }
@@ -2105,18 +2113,42 @@ bridge_fetch_dp_ifaces(struct bridge *br)
 \f
 /* Bridge packet processing functions. */
 
+static bool
+bond_is_tcp_hash(const struct port *port)
+{
+    return port->bond_mode == BM_TCP && port->lacp & LACP_NEGOTIATED;
+}
+
 static int
-bond_hash(const uint8_t mac[ETH_ADDR_LEN], uint16_t vlan)
+bond_hash_src(const uint8_t mac[ETH_ADDR_LEN], uint16_t vlan)
 {
     return hash_bytes(mac, ETH_ADDR_LEN, vlan) & BOND_MASK;
 }
 
+static int bond_hash_tcp(const struct flow *flow, uint16_t vlan)
+{
+    struct flow hash_flow;
+
+    memcpy(&hash_flow, flow, sizeof hash_flow);
+    hash_flow.vlan_tci = 0;
+
+    /* The symmetric quality of this hash function is not required, but
+     * flow_hash_symmetric_l4 already exists, and is sufficient for our
+     * purposes, so we use it out of convenience. */
+    return flow_hash_symmetric_l4(&hash_flow, vlan) & BOND_MASK;
+}
+
 static struct bond_entry *
-lookup_bond_entry(const struct port *port, const uint8_t mac[ETH_ADDR_LEN],
+lookup_bond_entry(const struct port *port, const struct flow *flow,
                   uint16_t vlan)
 {
-    assert(port->bond_mode == BM_SLB);
-    return &port->bond_hash[bond_hash(mac, vlan)];
+    assert(port->bond_mode != BM_AB);
+
+    if (bond_is_tcp_hash(port)) {
+        return &port->bond_hash[bond_hash_tcp(flow, vlan)];
+    } else {
+        return &port->bond_hash[bond_hash_src(flow->dl_src, vlan)];
+    }
 }
 
 static int
@@ -2132,7 +2164,7 @@ bond_choose_iface(const struct port *port)
         if (iface->enabled) {
             return i;
         } else if (iface->delay_expires < next_delay_expiration
-                   && (iface->lacp_attached
+                   && (iface->lacp_status & LACP_ATTACHED
                        || !(port->lacp & LACP_NEGOTIATED))) {
             best_down_slave = i;
             next_delay_expiration = iface->delay_expires;
@@ -2152,7 +2184,7 @@ bond_choose_iface(const struct port *port)
 }
 
 static bool
-choose_output_iface(const struct port *port, const uint8_t *dl_src,
+choose_output_iface(const struct port *port, const struct flow *flow,
                     uint16_t vlan, uint16_t *dp_ifidx, tag_type *tags)
 {
     struct iface *iface;
@@ -2166,8 +2198,8 @@ choose_output_iface(const struct port *port, const uint8_t *dl_src,
             return false;
         }
         iface = port->ifaces[port->active_iface];
-    } else if (port->bond_mode == BM_SLB){
-        struct bond_entry *e = lookup_bond_entry(port, dl_src, vlan);
+    } else {
+        struct bond_entry *e = lookup_bond_entry(port, flow, vlan);
         if (e->iface_idx < 0 || e->iface_idx >= port->n_ifaces
             || !port->ifaces[e->iface_idx]->enabled) {
             /* XXX select interface properly.  The current interface selection
@@ -2182,8 +2214,6 @@ choose_output_iface(const struct port *port, const uint8_t *dl_src,
         }
         *tags |= e->iface_tag;
         iface = port->ifaces[e->iface_idx];
-    } else {
-        NOT_REACHED();
     }
     *dp_ifidx = iface->dp_ifidx;
     *tags |= iface->tag;        /* Currently only used for bonding. */
@@ -2212,9 +2242,9 @@ bond_link_status_update(struct iface *iface)
          * They are not required to have synchronized partners because they
          * have no partners at all.  However, they will only be attached if
          * negotiations failed on all interfaces in the bond. */
-        up = iface->lacp_attached
+        up = iface->lacp_status & LACP_ATTACHED
             && (iface->lacp_partner.state & LACP_STATE_SYNC
-                 || iface->lacp_status == LACP_STATUS_DEFAULTED);
+                 || iface->lacp_status & LACP_DEFAULTED);
     }
 
 
@@ -2357,7 +2387,7 @@ bond_link_carrier_update(struct iface *iface, bool carrier)
         return;
     }
 
-    if (iface->lacp_status == LACP_STATUS_CURRENT) {
+    if (iface->lacp_status & LACP_CURRENT) {
         iface_set_lacp_expired(iface);
     }
 
@@ -2470,7 +2500,7 @@ set_dst(struct dst *dst, const struct flow *flow,
               : in_port->vlan >= 0 ? in_port->vlan
               : flow->vlan_tci == 0 ? OFP_VLAN_NONE
               : vlan_tci_to_vid(flow->vlan_tci));
-    return choose_output_iface(out_port, flow->dl_src, dst->vlan,
+    return choose_output_iface(out_port, flow, dst->vlan,
                                &dst->dp_ifidx, tags);
 }
 
@@ -2977,26 +3007,38 @@ bridge_normal_ofhook_cb(const struct flow *flow, const struct ofpbuf *packet,
                         struct ofpbuf *actions, tag_type *tags,
                         uint16_t *nf_output_iface, void *br_)
 {
-    struct iface *iface;
     struct bridge *br = br_;
 
     COVERAGE_INC(bridge_process_flow);
+    return process_flow(br, flow, packet, actions, tags, nf_output_iface);
+}
+
+static bool
+bridge_special_ofhook_cb(const struct flow *flow,
+                         const struct ofpbuf *packet, void *br_)
+{
+    struct iface *iface;
+    struct bridge *br = br_;
 
     iface = iface_from_dp_ifidx(br, flow->in_port);
 
     if (cfm_should_process_flow(flow)) {
-        if (packet && iface->cfm) {
+
+        if (iface && packet && iface->cfm) {
+            COVERAGE_INC(bridge_process_cfm);
             cfm_process_heartbeat(iface->cfm, packet);
         }
         return false;
     } else if (flow->dl_type == htons(ETH_TYPE_LACP)) {
-        if (packet) {
+
+        if (iface && packet) {
+            COVERAGE_INC(bridge_process_lacp);
             lacp_process_packet(packet, iface);
         }
         return false;
     }
 
-    return process_flow(br, flow, packet, actions, tags, nf_output_iface);
+    return true;
 }
 
 static void
@@ -3035,8 +3077,7 @@ bridge_account_flow_ofhook_cb(const struct flow *flow, tag_type tags,
                 uint16_t vlan = (flow->vlan_tci
                                  ? vlan_tci_to_vid(flow->vlan_tci)
                                  : OFP_VLAN_NONE);
-                struct bond_entry *e = lookup_bond_entry(out_port,
-                                                         flow->dl_src, vlan);
+                struct bond_entry *e = lookup_bond_entry(out_port, flow, vlan);
                 e->tx_bytes += n_bytes;
             }
         }
@@ -3067,6 +3108,7 @@ bridge_account_checkpoint_ofhook_cb(void *br_)
 
 static struct ofhooks bridge_ofhooks = {
     bridge_normal_ofhook_cb,
+    bridge_special_ofhook_cb,
     bridge_account_flow_ofhook_cb,
     bridge_account_checkpoint_ofhook_cb,
 };
@@ -3087,7 +3129,8 @@ lacp_process_packet(const struct ofpbuf *packet, struct iface *iface)
         return;
     }
 
-    iface->lacp_status = LACP_STATUS_CURRENT;
+    iface->lacp_status |= LACP_CURRENT;
+    iface->lacp_status &= ~(LACP_EXPIRED | LACP_DEFAULTED);
     iface->lacp_rx = time_msec() + LACP_SLOW_TIME_RX;
 
     iface->lacp_actor.state = iface_get_lacp_state(iface);
@@ -3107,32 +3150,35 @@ lacp_update_ifaces(struct port *port)
     size_t i;
     struct iface *lead;
     struct lacp_info lead_pri;
+    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 10);
 
     port->lacp_need_update = false;
+    COVERAGE_INC(bridge_lacp_update);
 
     if (!port->lacp) {
         return;
     }
 
+    VLOG_DBG_RL(&rl, "port %s: re-evaluating LACP link status", port->name);
+
     lead = NULL;
     for (i = 0; i < port->n_ifaces; i++) {
         struct iface *iface = port->ifaces[i];
         struct lacp_info pri;
 
-        iface->lacp_attached = true;
+        iface->lacp_status |= LACP_ATTACHED;
         ofproto_revalidate(port->bridge->ofproto, iface->tag);
 
         /* Don't allow loopback interfaces to send traffic or lead. */
         if (eth_addr_equals(iface->lacp_partner.sysid,
                             iface->lacp_actor.sysid)) {
-            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 10);
             VLOG_WARN_RL(&rl, "iface %s: Loopback detected. Interface is "
                          "connected to its own bridge", iface->name);
-            iface->lacp_attached = false;
+            iface->lacp_status &= ~LACP_ATTACHED;
             continue;
         }
 
-        if (iface->lacp_status == LACP_STATUS_DEFAULTED) {
+        if (iface->lacp_status & LACP_DEFAULTED) {
             continue;
         }
 
@@ -3154,11 +3200,11 @@ lacp_update_ifaces(struct port *port)
     for (i = 0; i < port->n_ifaces; i++) {
         struct iface *iface = port->ifaces[i];
 
-        if (iface->lacp_status == LACP_STATUS_DEFAULTED
+        if (iface->lacp_status & LACP_DEFAULTED
             || lead->lacp_partner.key != iface->lacp_partner.key
             || !eth_addr_equals(lead->lacp_partner.sysid,
                                 iface->lacp_partner.sysid)) {
-            iface->lacp_attached = false;
+            iface->lacp_status &= ~LACP_ATTACHED;
         }
     }
 }
@@ -3167,7 +3213,7 @@ static bool
 lacp_iface_may_tx(const struct iface *iface)
 {
     return iface->port->lacp & LACP_ACTIVE
-        || iface->lacp_status != LACP_STATUS_DEFAULTED;
+        || iface->lacp_status & (LACP_CURRENT | LACP_EXPIRED);
 }
 
 static void
@@ -3189,9 +3235,9 @@ lacp_run(struct bridge *br)
             struct iface *iface = port->ifaces[j];
 
             if (time_msec() > iface->lacp_rx) {
-                if (iface->lacp_status == LACP_STATUS_CURRENT) {
+                if (iface->lacp_status & LACP_CURRENT) {
                     iface_set_lacp_expired(iface);
-                } else if (iface->lacp_status == LACP_STATUS_EXPIRED) {
+                } else if (iface->lacp_status & LACP_EXPIRED) {
                     iface_set_lacp_defaulted(iface);
                 }
             }
@@ -3250,7 +3296,7 @@ lacp_wait(struct bridge *br)
                 poll_timer_wait_until(iface->lacp_tx);
             }
 
-            if (iface->lacp_status != LACP_STATUS_DEFAULTED) {
+            if (iface->lacp_status & (LACP_CURRENT | LACP_EXPIRED)) {
                 poll_timer_wait_until(iface->lacp_rx);
             }
         }
@@ -3275,10 +3321,12 @@ static const char *
 bond_mode_to_string(enum bond_mode bm) {
     static char *bm_slb = "balance-slb";
     static char *bm_ab  = "active-backup";
+    static char *bm_tcp = "balance-tcp";
 
     switch (bm) {
     case BM_SLB: return bm_slb;
     case BM_AB:  return bm_ab;
+    case BM_TCP: return bm_tcp;
     }
 
     NOT_REACHED();
@@ -3437,7 +3485,7 @@ bond_rebalance_port(struct port *port)
     struct bond_entry *e;
     size_t i;
 
-    assert(port->bond_mode == BM_SLB);
+    assert(port->bond_mode != BM_AB);
 
     /* Sets up 'bals' to describe each of the port's interfaces, sorted in
      * descending order of tx_bytes, so that bals[0] represents the most
@@ -3585,7 +3633,7 @@ bond_send_learning_packets(struct port *port)
     struct ofpbuf packet;
     int error, n_packets, n_errors;
 
-    if (!port->n_ifaces || port->active_iface < 0) {
+    if (!port->n_ifaces || port->active_iface < 0 || bond_is_tcp_hash(port)) {
         return;
     }
 
@@ -3598,8 +3646,15 @@ bond_send_learning_packets(struct port *port)
         struct flow flow;
         int retval;
 
-        if (e->port == port->port_idx
-            || !choose_output_iface(port, e->mac, e->vlan, &dp_ifidx, &tags)) {
+        if (e->port == port->port_idx) {
+            continue;
+        }
+
+        compose_benign_packet(&packet, "Open vSwitch Bond Failover", 0xf177,
+                              e->mac);
+        flow_extract(&packet, 0, ODPP_NONE, &flow);
+
+        if (!choose_output_iface(port, &flow, e->vlan, &dp_ifidx, &tags)) {
             continue;
         }
 
@@ -3619,9 +3674,6 @@ bond_send_learning_packets(struct port *port)
 
         /* Send packet. */
         n_packets++;
-        compose_benign_packet(&packet, "Open vSwitch Bond Failover", 0xf177,
-                              e->mac);
-        flow_extract(&packet, 0, ODPP_NONE, &flow);
         retval = ofproto_send_packet(br->ofproto, &flow, actions, a - actions,
                                      &packet);
         if (retval) {
@@ -3750,12 +3802,18 @@ bond_unixctl_show(struct unixctl_conn *conn,
                   bond_mode_to_string(port->bond_mode));
 
     if (port->lacp) {
-        ds_put_format(&ds, "\tlacp: %s\n",
+        ds_put_format(&ds, "lacp: %s\n",
                       port->lacp & LACP_ACTIVE ? "active" : "passive");
     } else {
-        ds_put_cstr(&ds, "\tlacp: off\n");
+        ds_put_cstr(&ds, "lacp: off\n");
     }
 
+    if (port->bond_mode != BM_AB) {
+        ds_put_format(&ds, "bond-hash-algorithm: %s\n",
+                      bond_is_tcp_hash(port) ? "balance-tcp" : "balance-slb");
+    }
+
+
     ds_put_format(&ds, "bond-detect-mode: %s\n",
                   port->miimon ? "miimon" : "carrier");
 
@@ -3767,7 +3825,7 @@ bond_unixctl_show(struct unixctl_conn *conn,
     ds_put_format(&ds, "updelay: %d ms\n", port->updelay);
     ds_put_format(&ds, "downdelay: %d ms\n", port->downdelay);
 
-    if (port->bond_mode == BM_SLB) {
+    if (port->bond_mode != BM_AB) {
         ds_put_format(&ds, "next rebalance: %lld ms\n",
                       port->bond_next_rebalance - time_msec());
     }
@@ -3775,9 +3833,10 @@ bond_unixctl_show(struct unixctl_conn *conn,
     for (j = 0; j < port->n_ifaces; j++) {
         const struct iface *iface = port->ifaces[j];
         struct bond_entry *be;
+        struct flow flow;
 
         /* Basic info. */
-        ds_put_format(&ds, "slave %s: %s\n",
+        ds_put_format(&ds, "\nslave %s: %s\n",
                       iface->name, iface->enabled ? "enabled" : "disabled");
         if (j == port->active_iface) {
             ds_put_cstr(&ds, "\tactive slave\n");
@@ -3791,15 +3850,19 @@ bond_unixctl_show(struct unixctl_conn *conn,
         if (port->lacp) {
             ds_put_cstr(&ds, "\tstatus: ");
 
-            if (iface->lacp_status == LACP_STATUS_CURRENT) {
+            if (iface->lacp_status & LACP_CURRENT) {
                 ds_put_cstr(&ds, "current ");
-            } else if (iface->lacp_status == LACP_STATUS_EXPIRED) {
+            }
+
+            if (iface->lacp_status & LACP_EXPIRED) {
                 ds_put_cstr(&ds, "expired ");
-            } else {
+            }
+
+            if (iface->lacp_status & LACP_DEFAULTED) {
                 ds_put_cstr(&ds, "defaulted ");
             }
 
-            if (iface->lacp_attached) {
+            if (iface->lacp_status & LACP_ATTACHED) {
                 ds_put_cstr(&ds, "attached ");
             }
 
@@ -3845,14 +3908,15 @@ bond_unixctl_show(struct unixctl_conn *conn,
 
             ds_put_cstr(&ds, "\tpartner state: ");
             ds_put_lacp_state(&ds, iface->lacp_partner.state);
-            ds_put_cstr(&ds, "\n\n");
+            ds_put_cstr(&ds, "\n");
         }
 
-        if (port->bond_mode != BM_SLB) {
+        if (port->bond_mode == BM_AB) {
             continue;
         }
 
         /* Hashes. */
+        memset(&flow, 0, sizeof flow);
         for (be = port->bond_hash; be <= &port->bond_hash[BOND_MASK]; be++) {
             int hash = be - port->bond_hash;
             struct mac_entry *me;
@@ -3864,13 +3928,19 @@ bond_unixctl_show(struct unixctl_conn *conn,
             ds_put_format(&ds, "\thash %d: %"PRIu64" kB load\n",
                           hash, be->tx_bytes / 1024);
 
+            if (port->bond_mode != BM_SLB) {
+                continue;
+            }
+
             /* MACs. */
             LIST_FOR_EACH (me, lru_node, &port->bridge->ml->lrus) {
                 uint16_t dp_ifidx;
                 tag_type tags = 0;
-                if (bond_hash(me->mac, me->vlan) == hash
+
+                memcpy(flow.dl_src, me->mac, ETH_ADDR_LEN);
+                if (bond_hash_src(me->mac, me->vlan) == hash
                     && me->port != port->port_idx
-                    && choose_output_iface(port, me->mac, me->vlan,
+                    && choose_output_iface(port, &flow, me->vlan,
                                            &dp_ifidx, &tags)
                     && dp_ifidx == iface->dp_ifidx)
                 {
@@ -4063,7 +4133,7 @@ bond_unixctl_hash(struct unixctl_conn *conn, const char *args_,
 
     if (sscanf(mac_s, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(mac))
         == ETH_ADDR_SCAN_COUNT) {
-        hash = bond_hash(mac, vlan);
+        hash = bond_hash_src(mac, vlan);
 
         hash_cstr = xasprintf("%u", hash);
         unixctl_command_reply(conn, 200, hash_cstr);
@@ -4222,6 +4292,8 @@ port_reconfigure(struct port *port, const struct ovsrec_port *cfg)
         port->bond_mode = BM_SLB;
     } else if (!strcmp(port->cfg->bond_mode, bond_mode_to_string(BM_AB))) {
         port->bond_mode = BM_AB;
+    } else if (!strcmp(port->cfg->bond_mode, bond_mode_to_string(BM_TCP))) {
+        port->bond_mode = BM_TCP;
     } else {
         port->bond_mode = BM_SLB;
         VLOG_WARN("port %s: unknown bond_mode %s, defaulting to %s",
@@ -4425,6 +4497,9 @@ port_update_lacp(struct port *port)
     bool key_changed;
 
     if (!port->lacp || port->n_ifaces < 1) {
+        for (i = 0; i < port->n_ifaces; i++) {
+            iface_set_lacp_defaulted(port->ifaces[i]);
+        }
         return;
     }
 
@@ -4480,7 +4555,7 @@ port_update_bonding(struct port *port)
     } else {
         size_t i;
 
-        if (port->bond_mode == BM_SLB && !port->bond_hash) {
+        if (port->bond_mode != BM_AB && !port->bond_hash) {
             port->bond_hash = xcalloc(BOND_MASK + 1, sizeof *port->bond_hash);
             for (i = 0; i <= BOND_MASK; i++) {
                 struct bond_entry *e = &port->bond_hash[i];
@@ -4495,7 +4570,7 @@ port_update_bonding(struct port *port)
             if (port->cfg->bond_fake_iface) {
                 port->bond_next_fake_iface_update = time_msec();
             }
-        } else if (port->bond_mode != BM_SLB) {
+        } else if (port->bond_mode == BM_AB) {
             free(port->bond_hash);
             port->bond_hash = NULL;
         }
@@ -4626,10 +4701,10 @@ port_update_vlan_compat(struct port *port)
 static void
 iface_set_lacp_defaulted(struct iface *iface)
 {
-    memset(&iface->lacp_partner, 0xff, sizeof iface->lacp_partner);
-    iface->lacp_partner.state = 0;
+    memset(&iface->lacp_partner, 0, sizeof iface->lacp_partner);
 
-    iface->lacp_status = LACP_STATUS_DEFAULTED;
+    iface->lacp_status |= LACP_DEFAULTED;
+    iface->lacp_status &= ~(LACP_CURRENT | LACP_EXPIRED);
     iface->lacp_tx = 0;
     iface->port->lacp_need_update = true;
 }
@@ -4637,7 +4712,8 @@ iface_set_lacp_defaulted(struct iface *iface)
 static void
 iface_set_lacp_expired(struct iface *iface)
 {
-    iface->lacp_status = LACP_STATUS_EXPIRED;
+    iface->lacp_status &= ~LACP_CURRENT;
+    iface->lacp_status |= LACP_EXPIRED;
     iface->lacp_partner.state |= LACP_STATE_TIME;
     iface->lacp_partner.state &= ~LACP_STATE_SYNC;
 
@@ -4654,13 +4730,15 @@ iface_get_lacp_state(const struct iface *iface)
         state |= LACP_STATE_ACT;
     }
 
-    if (iface->lacp_status == LACP_STATUS_DEFAULTED) {
-        state |= LACP_STATE_DEF;
-    } else if (iface->lacp_attached) {
+    if (iface->lacp_status & LACP_ATTACHED) {
         state |= LACP_STATE_SYNC;
     }
 
-    if (iface->lacp_status == LACP_STATUS_EXPIRED) {
+    if (iface->lacp_status & LACP_DEFAULTED) {
+        state |= LACP_STATE_DEF;
+    }
+
+    if (iface->lacp_status & LACP_EXPIRED) {
         state |= LACP_STATE_EXP;
     }
 
@@ -4977,6 +5055,8 @@ iface_update_cfm(struct iface *iface)
     mon = iface->cfg->monitor;
 
     if (!mon) {
+        cfm_destroy(iface->cfm);
+        iface->cfm = NULL;
         return;
     }