ofproto: Make handle_openflow's msg argument const.
[cascardo/ovs.git] / ofproto / ofproto.c
index 9bae971..7f9a88f 100644 (file)
@@ -199,10 +199,12 @@ static enum ofperr add_flow(struct ofproto *, struct ofconn *,
                             const struct ofputil_flow_mod *,
                             const struct ofp_header *);
 static void delete_flow__(struct rule *, struct ofopgroup *);
-static bool handle_openflow(struct ofconn *, struct ofpbuf *);
+static bool handle_openflow(struct ofconn *, const struct ofpbuf *);
 static enum ofperr handle_flow_mod__(struct ofproto *, struct ofconn *,
                                      const struct ofputil_flow_mod *,
                                      const struct ofp_header *);
+static void calc_duration(long long int start, long long int now,
+                          uint32_t *sec, uint32_t *nsec);
 
 /* ofproto. */
 static uint64_t pick_datapath_id(const struct ofproto *);
@@ -218,6 +220,8 @@ static const struct ofproto_class **ofproto_classes;
 static size_t n_ofproto_classes;
 static size_t allocated_ofproto_classes;
 
+unsigned flow_eviction_threshold = OFPROTO_FLOW_EVICTION_THRESHOLD_DEFAULT;
+
 /* Map from datapath name to struct ofproto, for use by unixctl commands. */
 static struct hmap all_ofprotos = HMAP_INITIALIZER(&all_ofprotos);
 
@@ -226,6 +230,9 @@ static struct shash init_ofp_ports = SHASH_INITIALIZER(&init_ofp_ports);
 
 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 
+/* The default value of true waits for flow restore. */
+static bool flow_restore_wait = true;
+
 /* Must be called to initialize the ofproto library.
  *
  * The caller may pass in 'iface_hints', which contains an shash of
@@ -403,8 +410,6 @@ ofproto_create(const char *datapath_name, const char *datapath_type,
     hmap_insert(&all_ofprotos, &ofproto->hmap_node,
                 hash_string(ofproto->name, 0));
     ofproto->datapath_id = 0;
-    ofproto_set_flow_eviction_threshold(ofproto,
-                                        OFPROTO_FLOW_EVICTION_THRESHOLD_DEFAULT);
     ofproto->forward_bpdu = false;
     ofproto->fallback_dpid = pick_fallback_dpid();
     ofproto->mfr_desc = NULL;
@@ -419,6 +424,7 @@ ofproto_create(const char *datapath_name, const char *datapath_type,
     ofproto->max_ports = OFPP_MAX;
     ofproto->tables = NULL;
     ofproto->n_tables = 0;
+    list_init(&ofproto->expirable);
     ofproto->connmgr = connmgr_create(ofproto, datapath_name, datapath_name);
     ofproto->state = S_OPENFLOW;
     list_init(&ofproto->pending);
@@ -560,13 +566,10 @@ ofproto_set_in_band_queue(struct ofproto *ofproto, int queue_id)
 /* Sets the number of flows at which eviction from the kernel flow table
  * will occur. */
 void
-ofproto_set_flow_eviction_threshold(struct ofproto *ofproto, unsigned threshold)
+ofproto_set_flow_eviction_threshold(unsigned threshold)
 {
-    if (threshold < OFPROTO_FLOW_EVICTION_THRESHOLD_MIN) {
-        ofproto->flow_eviction_threshold = OFPROTO_FLOW_EVICTION_THRESHOLD_MIN;
-    } else {
-        ofproto->flow_eviction_threshold = threshold;
-    }
+    flow_eviction_threshold = MAX(OFPROTO_FLOW_EVICTION_THRESHOLD_MIN,
+                                  threshold);
 }
 
 /* If forward_bpdu is true, the NORMAL action will forward frames with
@@ -639,6 +642,32 @@ ofproto_set_sflow(struct ofproto *ofproto,
         return oso ? EOPNOTSUPP : 0;
     }
 }
+
+int
+ofproto_set_ipfix(struct ofproto *ofproto,
+                  const struct ofproto_ipfix_bridge_exporter_options *bo,
+                  const struct ofproto_ipfix_flow_exporter_options *fo,
+                  size_t n_fo)
+{
+    if (ofproto->ofproto_class->set_ipfix) {
+        return ofproto->ofproto_class->set_ipfix(ofproto, bo, fo, n_fo);
+    } else {
+        return (bo || fo) ? EOPNOTSUPP : 0;
+    }
+}
+
+void
+ofproto_set_flow_restore_wait(bool flow_restore_wait_db)
+{
+    flow_restore_wait = flow_restore_wait_db;
+}
+
+bool
+ofproto_get_flow_restore_wait(void)
+{
+    return flow_restore_wait;
+}
+
 \f
 /* Spanning Tree Protocol (STP) configuration. */
 
@@ -785,6 +814,46 @@ ofproto_port_set_cfm(struct ofproto *ofproto, uint16_t ofp_port,
     }
 }
 
+/* Configures BFD on 'ofp_port' in 'ofproto'.  This function has no effect if
+ * 'ofproto' does not have a port 'ofp_port'. */
+void
+ofproto_port_set_bfd(struct ofproto *ofproto, uint16_t ofp_port,
+                     const struct smap *cfg)
+{
+    struct ofport *ofport;
+    int error;
+
+    ofport = ofproto_get_port(ofproto, ofp_port);
+    if (!ofport) {
+        VLOG_WARN("%s: cannot configure bfd on nonexistent port %"PRIu16,
+                  ofproto->name, ofp_port);
+        return;
+    }
+
+    error = (ofproto->ofproto_class->set_bfd
+             ? ofproto->ofproto_class->set_bfd(ofport, cfg)
+             : EOPNOTSUPP);
+    if (error) {
+        VLOG_WARN("%s: bfd configuration on port %"PRIu16" (%s) failed (%s)",
+                  ofproto->name, ofp_port, netdev_get_name(ofport->netdev),
+                  strerror(error));
+    }
+}
+
+/* Populates 'status' with key value pairs indicating the status of the BFD
+ * session on 'ofp_port'.  This information is intended to be populated in the
+ * OVS database.  Has no effect if 'ofp_port' is not na OpenFlow port in
+ * 'ofproto'. */
+int
+ofproto_port_get_bfd_status(struct ofproto *ofproto, uint16_t ofp_port,
+                            struct smap *status)
+{
+    struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
+    return (ofport && ofproto->ofproto_class->get_bfd_status
+            ? ofproto->ofproto_class->get_bfd_status(ofport, status)
+            : EOPNOTSUPP);
+}
+
 /* Checks the status of LACP negotiation for 'ofp_port' within ofproto.
  * Returns 1 if LACP partner information for 'ofp_port' is up-to-date,
  * 0 if LACP partner information is not current (generally indicating a
@@ -1644,7 +1713,9 @@ alloc_ofp_port(struct ofproto *ofproto, const char *netdev_name)
 static void
 dealloc_ofp_port(const struct ofproto *ofproto, uint16_t ofp_port)
 {
-    bitmap_set0(ofproto->ofp_port_ids, ofp_port);
+    if (ofp_port < ofproto->max_ports) {
+        bitmap_set0(ofproto->ofp_port_ids, ofp_port);
+    }
 }
 
 /* Opens and returns a netdev for 'ofproto_port' in 'ofproto', or a null
@@ -1731,6 +1802,7 @@ ofport_install(struct ofproto *p,
     ofport->change_seq = netdev_change_seq(netdev);
     ofport->pp = *pp;
     ofport->ofp_port = pp->port_no;
+    ofport->created = time_msec();
 
     /* Add port to 'p'. */
     hmap_insert(&p->ports, &ofport->hmap_node, hash_int(ofport->ofp_port, 0));
@@ -2235,7 +2307,7 @@ handle_set_config(struct ofconn *ofconn, const struct ofp_header *oh)
     uint16_t flags = ntohs(osc->flags);
 
     if (ofconn_get_type(ofconn) != OFCONN_PRIMARY
-        || ofconn_get_role(ofconn) != NX_ROLE_SLAVE) {
+        || ofconn_get_role(ofconn) != OFPCR12_ROLE_SLAVE) {
         enum ofp_config_flags cur = ofproto->frag_handling;
         enum ofp_config_flags next = flags & OFPC_FRAG_MASK;
 
@@ -2269,7 +2341,7 @@ static enum ofperr
 reject_slave_controller(struct ofconn *ofconn)
 {
     if (ofconn_get_type(ofconn) == OFCONN_PRIMARY
-        && ofconn_get_role(ofconn) == NX_ROLE_SLAVE) {
+        && ofconn_get_role(ofconn) == OFPCR12_ROLE_SLAVE) {
         return OFPERR_OFPBRC_EPERM;
     } else {
         return 0;
@@ -2343,9 +2415,9 @@ update_port_config(struct ofport *port,
     toggle = (config ^ port->pp.config) & mask;
     if (toggle & OFPUTIL_PC_PORT_DOWN) {
         if (config & OFPUTIL_PC_PORT_DOWN) {
-            netdev_turn_flags_off(port->netdev, NETDEV_UP, true);
+            netdev_turn_flags_off(port->netdev, NETDEV_UP, NULL);
         } else {
-            netdev_turn_flags_on(port->netdev, NETDEV_UP, true);
+            netdev_turn_flags_on(port->netdev, NETDEV_UP, NULL);
         }
         toggle &= ~OFPUTIL_PC_PORT_DOWN;
     }
@@ -2488,6 +2560,9 @@ append_port_stat(struct ofport *port, struct list *replies)
 {
     struct ofputil_port_stats ops = { .port_no = port->pp.port_no };
 
+    calc_duration(port->created, time_msec(),
+                  &ops.duration_sec, &ops.duration_nsec);
+
     /* Intentionally ignore return value, since errors will set
      * 'stats' to all-1s, which is correct for OpenFlow, and
      * netdev_get_stats() will log errors. */
@@ -2548,8 +2623,8 @@ handle_port_desc_stats_request(struct ofconn *ofconn,
 }
 
 static void
-calc_flow_duration__(long long int start, long long int now,
-                     uint32_t *sec, uint32_t *nsec)
+calc_duration(long long int start, long long int now,
+              uint32_t *sec, uint32_t *nsec)
 {
     long long int msecs = now - start;
     *sec = msecs / 1000;
@@ -2768,8 +2843,7 @@ handle_flow_stats_request(struct ofconn *ofconn,
         fs.priority = rule->cr.priority;
         fs.cookie = rule->flow_cookie;
         fs.table_id = rule->table_id;
-        calc_flow_duration__(rule->created, now, &fs.duration_sec,
-                             &fs.duration_nsec);
+        calc_duration(rule->created, now, &fs.duration_sec, &fs.duration_nsec);
         fs.idle_timeout = rule->idle_timeout;
         fs.hard_timeout = rule->hard_timeout;
         fs.idle_age = age_secs(now - rule->used);
@@ -2840,62 +2914,21 @@ ofproto_get_netflow_ids(const struct ofproto *ofproto,
     ofproto->ofproto_class->get_netflow_ids(ofproto, engine_type, engine_id);
 }
 
-/* Checks the fault status of CFM for 'ofp_port' within 'ofproto'.  Returns a
- * bitmask of 'cfm_fault_reason's to indicate a CFM fault (generally
- * indicating a connectivity problem).  Returns zero if CFM is not faulted,
- * and -1 if CFM is not enabled on 'ofp_port'. */
-int
-ofproto_port_get_cfm_fault(const struct ofproto *ofproto, uint16_t ofp_port)
-{
-    struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
-    return (ofport && ofproto->ofproto_class->get_cfm_fault
-            ? ofproto->ofproto_class->get_cfm_fault(ofport)
-            : -1);
-}
-
-/* Checks the operational status reported by the remote CFM endpoint of
- * 'ofp_port'  Returns 1 if operationally up, 0 if operationally down, and -1
- * if CFM is not enabled on 'ofp_port' or does not support operational status.
- */
-int
-ofproto_port_get_cfm_opup(const struct ofproto *ofproto, uint16_t ofp_port)
-{
-    struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
-    return (ofport && ofproto->ofproto_class->get_cfm_opup
-            ? ofproto->ofproto_class->get_cfm_opup(ofport)
-            : -1);
-}
-
-/* Gets the MPIDs of the remote maintenance points broadcasting to 'ofp_port'
- * within 'ofproto'.  Populates 'rmps' with an array of MPIDs owned by
- * 'ofproto', and 'n_rmps' with the number of MPIDs in 'rmps'.  Returns a
- * number less than 0 if CFM is not enabled on 'ofp_port'. */
-int
-ofproto_port_get_cfm_remote_mpids(const struct ofproto *ofproto,
-                                  uint16_t ofp_port, const uint64_t **rmps,
-                                  size_t *n_rmps)
+/* Checks the status of CFM configured on 'ofp_port' within 'ofproto'.  Returns
+ * true if the port's CFM status was successfully stored into '*status'.
+ * Returns false if the port did not have CFM configured, in which case
+ * '*status' is indeterminate.
+ *
+ * The caller must provide and owns '*status', but it does not own and must not
+ * modify or free the array returned in 'status->rmps'. */
+bool
+ofproto_port_get_cfm_status(const struct ofproto *ofproto, uint16_t ofp_port,
+                            struct ofproto_cfm_status *status)
 {
     struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
-
-    *rmps = NULL;
-    *n_rmps = 0;
-    return (ofport && ofproto->ofproto_class->get_cfm_remote_mpids
-            ? ofproto->ofproto_class->get_cfm_remote_mpids(ofport, rmps,
-                                                           n_rmps)
-            : -1);
-}
-
-/* Checks the health of the CFM for 'ofp_port' within 'ofproto'.  Returns an
- * integer value between 0 and 100 to indicate the health of the port as a
- * percentage which is the average of cfm health of all the remote_mpids or
- * returns -1 if CFM is not enabled on 'ofport'. */
-int
-ofproto_port_get_cfm_health(const struct ofproto *ofproto, uint16_t ofp_port)
-{
-    struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
-    return (ofport && ofproto->ofproto_class->get_cfm_health
-            ? ofproto->ofproto_class->get_cfm_health(ofport)
-            : -1);
+    return (ofport
+            && ofproto->ofproto_class->get_cfm_status
+            && ofproto->ofproto_class->get_cfm_status(ofport, status));
 }
 
 static enum ofperr
@@ -3088,8 +3121,8 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn,
     struct oftable *table;
     struct ofopgroup *group;
     struct rule *victim;
-    struct cls_rule cr;
     struct rule *rule;
+    uint8_t table_id;
     int error;
 
     error = check_table_id(ofproto, fm->table_id);
@@ -3099,7 +3132,6 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn,
 
     /* Pick table. */
     if (fm->table_id == 0xff) {
-        uint8_t table_id;
         if (ofproto->ofproto_class->rule_choose_table) {
             error = ofproto->ofproto_class->rule_choose_table(ofproto,
                                                               &fm->match,
@@ -3108,16 +3140,17 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn,
                 return error;
             }
             ovs_assert(table_id < ofproto->n_tables);
-            table = &ofproto->tables[table_id];
         } else {
-            table = &ofproto->tables[0];
+            table_id = 0;
         }
     } else if (fm->table_id < ofproto->n_tables) {
-        table = &ofproto->tables[fm->table_id];
+        table_id = fm->table_id;
     } else {
         return OFPERR_OFPBRC_BAD_TABLE_ID;
     }
 
+    table = &ofproto->tables[table_id];
+
     if (table->flags & OFTABLE_READONLY) {
         return OFPERR_OFPBRC_EPERM;
     }
@@ -3132,7 +3165,7 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn,
     cls_rule_init(&rule->cr, &fm->match, fm->priority);
 
     /* Serialize against pending deletion. */
-    if (is_flow_deletion_pending(ofproto, &cr, table - ofproto->tables)) {
+    if (is_flow_deletion_pending(ofproto, &rule->cr, table_id)) {
         cls_rule_destroy(&rule->cr);
         ofproto->ofproto_class->rule_dealloc(rule);
         return OFPROTO_POSTPONE;
@@ -3162,6 +3195,7 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn,
     rule->ofpacts_len = fm->ofpacts_len;
     rule->evictable = true;
     rule->eviction_group = NULL;
+    list_init(&rule->expirable);
     rule->monitor_flags = 0;
     rule->add_seqno = 0;
     rule->modify_seqno = 0;
@@ -3257,10 +3291,6 @@ modify_flows__(struct ofproto *ofproto, struct ofconn *ofconn,
         new_cookie = (fm->new_cookie != htonll(UINT64_MAX)
                       ? fm->new_cookie
                       : rule->flow_cookie);
-        if (!actions_changed && new_cookie == rule->flow_cookie) {
-            /* No change at all. */
-            continue;
-        }
 
         op = ofoperation_create(group, rule, OFOPERATION_MODIFY, 0);
         rule->flow_cookie = new_cookie;
@@ -3426,8 +3456,8 @@ ofproto_rule_send_removed(struct rule *rule, uint8_t reason)
     fr.cookie = rule->flow_cookie;
     fr.reason = reason;
     fr.table_id = rule->table_id;
-    calc_flow_duration__(rule->created, time_msec(),
-                         &fr.duration_sec, &fr.duration_nsec);
+    calc_duration(rule->created, time_msec(),
+                  &fr.duration_sec, &fr.duration_nsec);
     fr.idle_timeout = rule->idle_timeout;
     fr.hard_timeout = rule->hard_timeout;
     rule->ofproto->ofproto_class->rule_get_stats(rule, &fr.packet_count,
@@ -3576,38 +3606,34 @@ handle_flow_mod__(struct ofproto *ofproto, struct ofconn *ofconn,
 static enum ofperr
 handle_role_request(struct ofconn *ofconn, const struct ofp_header *oh)
 {
-    struct ofputil_role_request rr;
+    struct ofputil_role_request request;
+    struct ofputil_role_request reply;
     struct ofpbuf *buf;
-    uint32_t role;
     enum ofperr error;
 
-    error = ofputil_decode_role_message(oh, &rr);
+    error = ofputil_decode_role_message(oh, &request);
     if (error) {
         return error;
     }
 
-    if (rr.request_current_role_only) {
-        role = ofconn_get_role(ofconn); /* NX_ROLE_* */
-        goto reply;
-    }
-
-    role = rr.role;
-
-    if (ofconn_get_role(ofconn) != role
-        && ofconn_has_pending_opgroups(ofconn)) {
-        return OFPROTO_POSTPONE;
-    }
+    if (request.role != OFPCR12_ROLE_NOCHANGE) {
+        if (ofconn_get_role(ofconn) != request.role
+            && ofconn_has_pending_opgroups(ofconn)) {
+            return OFPROTO_POSTPONE;
+        }
 
-    if (rr.have_generation_id) {
-        if (!ofconn_set_master_election_id(ofconn, rr.generation_id)) {
-            return OFPERR_OFPRRFC_STALE;
+        if (request.have_generation_id
+            && !ofconn_set_master_election_id(ofconn, request.generation_id)) {
+                return OFPERR_OFPRRFC_STALE;
         }
-    }
 
-    ofconn_set_role(ofconn, role);
+        ofconn_set_role(ofconn, request.role);
+    }
 
-reply:
-    buf = ofputil_encode_role_reply(oh, role);
+    reply.role = ofconn_get_role(ofconn);
+    reply.have_generation_id = ofconn_get_master_election_id(
+        ofconn, &reply.generation_id);
+    buf = ofputil_encode_role_reply(oh, &reply);
     ofconn_send_reply(ofconn, buf);
 
     return 0;
@@ -4120,7 +4146,7 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
 }
 
 static bool
-handle_openflow(struct ofconn *ofconn, struct ofpbuf *ofp_msg)
+handle_openflow(struct ofconn *ofconn, const struct ofpbuf *ofp_msg)
 {
     int error = handle_openflow__(ofconn, ofp_msg);
     if (error && error != OFPROTO_POSTPONE) {
@@ -4243,7 +4269,19 @@ ofopgroup_complete(struct ofopgroup *group)
     LIST_FOR_EACH_SAFE (op, next_op, group_node, &group->ops) {
         struct rule *rule = op->rule;
 
-        if (!op->error && !ofproto_rule_is_hidden(rule)) {
+        /* We generally want to report the change to active OpenFlow flow
+           monitors (e.g. NXST_FLOW_MONITOR).  There are three exceptions:
+
+              - The operation failed.
+
+              - The affected rule is not visible to controllers.
+
+              - The operation's only effect was to update rule->modified. */
+        if (!(op->error
+              || ofproto_rule_is_hidden(rule)
+              || (op->type == OFOPERATION_MODIFY
+                  && op->ofpacts
+                  && rule->flow_cookie == op->flow_cookie))) {
             /* Check that we can just cast from ofoperation_type to
              * nx_flow_update_event. */
             BUILD_ASSERT_DECL((enum nx_flow_update_event) OFOPERATION_ADD
@@ -4824,6 +4862,9 @@ oftable_remove_rule(struct rule *rule)
 
     classifier_remove(&table->cls, &rule->cr);
     eviction_group_remove_rule(rule);
+    if (!list_is_empty(&rule->expirable)) {
+        list_remove(&rule->expirable);
+    }
 }
 
 /* Inserts 'rule' into its oftable.  Removes any existing rule from 'rule''s
@@ -4835,9 +4876,17 @@ oftable_replace_rule(struct rule *rule)
     struct ofproto *ofproto = rule->ofproto;
     struct oftable *table = &ofproto->tables[rule->table_id];
     struct rule *victim;
+    bool may_expire = rule->hard_timeout || rule->idle_timeout;
+
+    if (may_expire) {
+        list_insert(&ofproto->expirable, &rule->expirable);
+    }
 
     victim = rule_from_cls_rule(classifier_replace(&table->cls, &rule->cr));
     if (victim) {
+        if (!list_is_empty(&victim->expirable)) {
+            list_remove(&victim->expirable);
+        }
         eviction_group_remove_rule(victim);
     }
     eviction_group_add_rule(rule);