Add ability to direct "packet-in"s to particular controllers.
[sliver-openvswitch.git] / ofproto / ofproto-dpif.c
index d19b6f7..feb1342 100644 (file)
@@ -190,6 +190,8 @@ static struct ofbundle ofpp_none_bundle = {
 
 static void stp_run(struct ofproto_dpif *ofproto);
 static void stp_wait(struct ofproto_dpif *ofproto);
+static int set_stp_port(struct ofport *,
+                        const struct ofproto_port_stp_settings *);
 
 static bool ofbundle_includes_vlan(const struct ofbundle *, uint16_t vlan);
 
@@ -216,6 +218,11 @@ struct action_xlate_ctx {
     /* The rule that we are currently translating, or NULL. */
     struct rule_dpif *rule;
 
+    /* Union of the set of TCP flags seen so far in this flow.  (Used only by
+     * NXAST_FIN_TIMEOUT.  Set to zero to avoid updating updating rules'
+     * timeouts.) */
+    uint8_t tcp_flags;
+
     /* If nonnull, called just before executing a resubmit action.
      *
      * This is normally null so the client has to set it manually after
@@ -231,6 +238,7 @@ struct action_xlate_ctx {
                                  * be reassessed for every packet. */
     bool has_learn;             /* Actions include NXAST_LEARN? */
     bool has_normal;            /* Actions output to OFPP_NORMAL? */
+    bool has_fin_timeout;       /* Actions include NXAST_FIN_TIMEOUT? */
     uint16_t nf_output_iface;   /* Output interface index for NetFlow. */
     mirror_mask_t mirrors;      /* Bitmap of associated mirrors. */
 
@@ -250,7 +258,7 @@ struct action_xlate_ctx {
 static void action_xlate_ctx_init(struct action_xlate_ctx *,
                                   struct ofproto_dpif *, const struct flow *,
                                   ovs_be16 initial_tci, struct rule_dpif *,
-                                  const struct ofpbuf *);
+                                  uint8_t tcp_flags, const struct ofpbuf *);
 static struct ofpbuf *xlate_actions(struct action_xlate_ctx *,
                                     const union ofp_action *in, size_t n_in);
 
@@ -304,6 +312,7 @@ struct facet {
     /* Accounting. */
     uint64_t accounted_bytes;    /* Bytes processed by facet_account(). */
     struct netflow_flow nf_flow; /* Per-flow NetFlow tracking data. */
+    uint8_t tcp_flags;           /* TCP flags seen for this 'rule'. */
 
     /* Properties of datapath actions.
      *
@@ -314,6 +323,7 @@ struct facet {
     bool may_install;            /* Reassess actions for every packet? */
     bool has_learn;              /* Actions include NXAST_LEARN? */
     bool has_normal;             /* Actions output to OFPP_NORMAL? */
+    bool has_fin_timeout;        /* Actions include NXAST_FIN_TIMEOUT? */
     tag_type tags;               /* Tags that would require revalidation. */
     mirror_mask_t mirrors;       /* Bitmap of dependent mirrors. */
 };
@@ -1147,6 +1157,12 @@ set_stp(struct ofproto *ofproto_, const struct ofproto_stp_settings *s)
         stp_set_max_age(ofproto->stp, s->max_age);
         stp_set_forward_delay(ofproto->stp, s->fwd_delay);
     }  else {
+        struct ofport *ofport;
+
+        HMAP_FOR_EACH (ofport, hmap_node, &ofproto->up.ports) {
+            set_stp_port(ofport, NULL);
+        }
+
         stp_destroy(ofproto->stp);
         ofproto->stp = NULL;
     }
@@ -2440,6 +2456,7 @@ send_packet_in_miss(struct ofproto_dpif *ofproto, const struct ofpbuf *packet,
     pin.packet_len = packet->size;
     pin.total_len = packet->size;
     pin.reason = OFPR_NO_MATCH;
+    pin.controller_id = 0;
 
     pin.table_id = 0;
     pin.cookie = 0;
@@ -2954,6 +2971,8 @@ update_stats(struct ofproto_dpif *p)
             subfacet->dp_packet_count = stats->n_packets;
             subfacet->dp_byte_count = stats->n_bytes;
 
+            facet->tcp_flags |= stats->tcp_flags;
+
             subfacet_update_time(subfacet, stats->used);
             facet_account(facet);
             facet_push_stats(facet);
@@ -3092,7 +3111,7 @@ rule_expire(struct rule_dpif *rule)
     if (rule->up.hard_timeout
         && now > rule->up.modified + rule->up.hard_timeout * 1000) {
         reason = OFPRR_HARD_TIMEOUT;
-    } else if (rule->up.idle_timeout && list_is_empty(&rule->facets)
+    } else if (rule->up.idle_timeout
                && now > rule->up.used + rule->up.idle_timeout * 1000) {
         reason = OFPRR_IDLE_TIMEOUT;
     } else {
@@ -3225,12 +3244,14 @@ facet_account(struct facet *facet)
     /* Feed information from the active flows back into the learning table to
      * ensure that table is always in sync with what is actually flowing
      * through the datapath. */
-    if (facet->has_learn || facet->has_normal) {
+    if (facet->has_learn || facet->has_normal
+        || (facet->has_fin_timeout
+            && facet->tcp_flags & (TCP_FIN | TCP_RST))) {
         struct action_xlate_ctx ctx;
 
         action_xlate_ctx_init(&ctx, ofproto, &facet->flow,
                               facet->flow.vlan_tci,
-                              facet->rule, NULL);
+                              facet->rule, facet->tcp_flags, NULL);
         ctx.may_learn = true;
         ofpbuf_delete(xlate_actions(&ctx, facet->rule->up.actions,
                                     facet->rule->up.n_actions));
@@ -3324,6 +3345,7 @@ facet_flush_stats(struct facet *facet)
     facet_reset_counters(facet);
 
     netflow_flow_clear(&facet->nf_flow);
+    facet->tcp_flags = 0;
 }
 
 /* Searches 'ofproto''s table of facets for one exactly equal to 'flow'.
@@ -3420,7 +3442,7 @@ facet_check_consistency(struct facet *facet)
         bool should_install;
 
         action_xlate_ctx_init(&ctx, ofproto, &facet->flow,
-                              subfacet->initial_tci, rule, NULL);
+                              subfacet->initial_tci, rule, 0, NULL);
         odp_actions = xlate_actions(&ctx, rule->up.actions,
                                     rule->up.n_actions);
 
@@ -3539,7 +3561,7 @@ facet_revalidate(struct facet *facet)
         bool should_install;
 
         action_xlate_ctx_init(&ctx, ofproto, &facet->flow,
-                              subfacet->initial_tci, new_rule, NULL);
+                              subfacet->initial_tci, new_rule, 0, NULL);
         odp_actions = xlate_actions(&ctx, new_rule->up.actions,
                                     new_rule->up.n_actions);
         actions_changed = (subfacet->actions_len != odp_actions->size
@@ -3581,6 +3603,7 @@ facet_revalidate(struct facet *facet)
     facet->may_install = ctx.may_set_up_flow;
     facet->has_learn = ctx.has_learn;
     facet->has_normal = ctx.has_normal;
+    facet->has_fin_timeout = ctx.has_fin_timeout;
     facet->mirrors = ctx.mirrors;
     if (new_actions) {
         i = 0;
@@ -3690,7 +3713,7 @@ flow_push_stats(struct rule_dpif *rule,
     ofproto_rule_update_used(&rule->up, used);
 
     action_xlate_ctx_init(&push.ctx, ofproto, flow, flow->vlan_tci, rule,
-                          NULL);
+                          0, NULL);
     push.ctx.resubmit_hook = push_resubmit;
     ofpbuf_delete(xlate_actions(&push.ctx,
                                 rule->up.actions, rule->up.n_actions));
@@ -3835,12 +3858,13 @@ subfacet_make_actions(struct subfacet *subfacet, const struct ofpbuf *packet)
     struct action_xlate_ctx ctx;
 
     action_xlate_ctx_init(&ctx, ofproto, &facet->flow, subfacet->initial_tci,
-                          rule, packet);
+                          rule, 0, packet);
     odp_actions = xlate_actions(&ctx, rule->up.actions, rule->up.n_actions);
     facet->tags = ctx.tags;
     facet->may_install = ctx.may_set_up_flow;
     facet->has_learn = ctx.has_learn;
     facet->has_normal = ctx.has_normal;
+    facet->has_fin_timeout = ctx.has_fin_timeout;
     facet->nf_flow.output_iface = ctx.nf_output_iface;
     facet->mirrors = ctx.mirrors;
 
@@ -3960,6 +3984,7 @@ subfacet_update_stats(struct subfacet *subfacet,
         subfacet_update_time(subfacet, stats->used);
         facet->packet_count += stats->n_packets;
         facet->byte_count += stats->n_bytes;
+        facet->tcp_flags |= stats->tcp_flags;
         facet_push_stats(facet);
         netflow_flow_update_flags(&facet->nf_flow, stats->tcp_flags);
     }
@@ -4115,7 +4140,7 @@ rule_execute(struct rule *rule_, const struct flow *flow,
     size_t size;
 
     action_xlate_ctx_init(&ctx, ofproto, flow, flow->vlan_tci,
-                          rule, packet);
+                          rule, packet_get_tcp_flags(packet, flow), packet);
     odp_actions = xlate_actions(&ctx, rule->up.actions, rule->up.n_actions);
     size = packet->size;
     if (execute_odp_actions(ofproto, flow, odp_actions->data,
@@ -4441,7 +4466,8 @@ flood_packets(struct action_xlate_ctx *ctx, bool all)
 
 static void
 execute_controller_action(struct action_xlate_ctx *ctx, int len,
-                          enum ofp_packet_in_reason reason)
+                          enum ofp_packet_in_reason reason,
+                          uint16_t controller_id)
 {
     struct ofputil_packet_in pin;
     struct ofpbuf *packet;
@@ -4487,6 +4513,7 @@ execute_controller_action(struct action_xlate_ctx *ctx, int len,
     pin.packet = packet->data;
     pin.packet_len = packet->size;
     pin.reason = reason;
+    pin.controller_id = controller_id;
     pin.table_id = ctx->table_id;
     pin.cookie = ctx->rule ? ctx->rule->up.flow_cookie : 0;
 
@@ -4511,7 +4538,7 @@ compose_dec_ttl(struct action_xlate_ctx *ctx)
         ctx->flow.nw_ttl--;
         return false;
     } else {
-        execute_controller_action(ctx, UINT16_MAX, OFPR_INVALID_TTL);
+        execute_controller_action(ctx, UINT16_MAX, OFPR_INVALID_TTL, 0);
 
         /* Stop processing for current table. */
         return true;
@@ -4543,13 +4570,11 @@ xlate_output_action__(struct action_xlate_ctx *ctx,
         flood_packets(ctx, true);
         break;
     case OFPP_CONTROLLER:
-        execute_controller_action(ctx, max_len, OFPR_ACTION);
-        break;
-    case OFPP_LOCAL:
-        compose_output_action(ctx, OFPP_LOCAL);
+        execute_controller_action(ctx, max_len, OFPR_ACTION, 0);
         break;
     case OFPP_NONE:
         break;
+    case OFPP_LOCAL:
     default:
         if (port != ctx->flow.in_port) {
             compose_output_action(ctx, port);
@@ -4711,6 +4736,28 @@ xlate_learn_action(struct action_xlate_ctx *ctx,
     free(fm.actions);
 }
 
+/* Reduces '*timeout' to no more than 'max'.  A value of zero in either case
+ * means "infinite". */
+static void
+reduce_timeout(uint16_t max, uint16_t *timeout)
+{
+    if (max && (!*timeout || *timeout > max)) {
+        *timeout = max;
+    }
+}
+
+static void
+xlate_fin_timeout(struct action_xlate_ctx *ctx,
+                  const struct nx_action_fin_timeout *naft)
+{
+    if (ctx->tcp_flags & (TCP_FIN | TCP_RST) && ctx->rule) {
+        struct rule_dpif *rule = ctx->rule;
+
+        reduce_timeout(ntohs(naft->fin_idle_timeout), &rule->up.idle_timeout);
+        reduce_timeout(ntohs(naft->fin_hard_timeout), &rule->up.hard_timeout);
+    }
+}
+
 static bool
 may_receive(const struct ofport_dpif *port, struct action_xlate_ctx *ctx)
 {
@@ -4761,6 +4808,7 @@ do_xlate_actions(const union ofp_action *in, size_t n_in,
         const struct nx_action_autopath *naa;
         const struct nx_action_bundle *nab;
         const struct nx_action_output_reg *naor;
+        const struct nx_action_controller *nac;
         enum ofputil_action_code code;
         ovs_be64 tun_id;
 
@@ -4916,6 +4964,17 @@ do_xlate_actions(const union ofp_action *in, size_t n_in,
         case OFPUTIL_NXAST_EXIT:
             ctx->exit = true;
             break;
+
+        case OFPUTIL_NXAST_FIN_TIMEOUT:
+            ctx->has_fin_timeout = true;
+            xlate_fin_timeout(ctx, (const struct nx_action_fin_timeout *) ia);
+            break;
+
+        case OFPUTIL_NXAST_CONTROLLER:
+            nac = (const struct nx_action_controller *) ia;
+            execute_controller_action(ctx, ntohs(nac->max_len), nac->reason,
+                                      ntohs(nac->controller_id));
+            break;
         }
     }
 
@@ -4935,7 +4994,7 @@ static void
 action_xlate_ctx_init(struct action_xlate_ctx *ctx,
                       struct ofproto_dpif *ofproto, const struct flow *flow,
                       ovs_be16 initial_tci, struct rule_dpif *rule,
-                      const struct ofpbuf *packet)
+                      uint8_t tcp_flags, const struct ofpbuf *packet)
 {
     ctx->ofproto = ofproto;
     ctx->flow = *flow;
@@ -4945,6 +5004,7 @@ action_xlate_ctx_init(struct action_xlate_ctx *ctx,
     ctx->rule = rule;
     ctx->packet = packet;
     ctx->may_learn = packet != NULL;
+    ctx->tcp_flags = tcp_flags;
     ctx->resubmit_hook = NULL;
 }
 
@@ -4962,6 +5022,7 @@ xlate_actions(struct action_xlate_ctx *ctx,
     ctx->may_set_up_flow = true;
     ctx->has_learn = false;
     ctx->has_normal = false;
+    ctx->has_fin_timeout = false;
     ctx->nf_output_iface = NF_OUT_DROP;
     ctx->mirrors = 0;
     ctx->recurse = 0;
@@ -5732,7 +5793,7 @@ packet_out(struct ofproto *ofproto_, struct ofpbuf *packet,
         odp_flow_key_from_flow(&key, flow);
 
         action_xlate_ctx_init(&push.ctx, ofproto, flow, flow->vlan_tci, NULL,
-                              packet);
+                              packet_get_tcp_flags(packet, flow), packet);
 
         /* Ensure that resubmits in 'ofp_actions' get accounted to their
          * matching rules. */
@@ -5932,6 +5993,18 @@ trace_format_regs(struct ds *result, int level, const char *title,
     ds_put_char(result, '\n');
 }
 
+static void
+trace_format_odp(struct ds *result, int level, const char *title,
+                 struct ofproto_trace *trace)
+{
+    struct ofpbuf *odp_actions = trace->ctx.odp_actions;
+
+    ds_put_char_multiple(result, '\t', level);
+    ds_put_format(result, "%s: ", title);
+    format_odp_actions(result, odp_actions->data, odp_actions->size);
+    ds_put_char(result, '\n');
+}
+
 static void
 trace_resubmit(struct action_xlate_ctx *ctx, struct rule_dpif *rule)
 {
@@ -5941,6 +6014,7 @@ trace_resubmit(struct action_xlate_ctx *ctx, struct rule_dpif *rule)
     ds_put_char(result, '\n');
     trace_format_flow(result, ctx->recurse + 1, "Resubmitted flow", trace);
     trace_format_regs(result, ctx->recurse + 1, "Resubmitted regs", trace);
+    trace_format_odp(result,  ctx->recurse + 1, "Resubmitted  odp", trace);
     trace_format_rule(result, ctx->table_id, ctx->recurse + 1, rule);
 }
 
@@ -6034,11 +6108,13 @@ ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[],
     if (rule) {
         struct ofproto_trace trace;
         struct ofpbuf *odp_actions;
+        uint8_t tcp_flags;
 
+        tcp_flags = packet ? packet_get_tcp_flags(packet, &flow) : 0;
         trace.result = &result;
         trace.flow = flow;
         action_xlate_ctx_init(&trace.ctx, ofproto, &flow, initial_tci,
-                              rule, packet);
+                              rule, tcp_flags, packet);
         trace.ctx.resubmit_hook = trace_resubmit;
         odp_actions = xlate_actions(&trace.ctx,
                                     rule->up.actions, rule->up.n_actions);