diff --git a/SOURCES/openvswitch-3.1.0.patch b/SOURCES/openvswitch-3.1.0.patch index 6ef6570..4376ddf 100644 --- a/SOURCES/openvswitch-3.1.0.patch +++ b/SOURCES/openvswitch-3.1.0.patch @@ -4383,8 +4383,21 @@ index e05ffe312..b44c72969 100644 } if (result != UKEY_KEEP) { /* Clears 'recircs' if filled by revalidate_ukey(). */ +diff --git a/ofproto/ofproto-dpif-xlate-cache.c b/ofproto/ofproto-dpif-xlate-cache.c +index 9224ee2e6..2e1fcb3a6 100644 +--- a/ofproto/ofproto-dpif-xlate-cache.c ++++ b/ofproto/ofproto-dpif-xlate-cache.c +@@ -125,7 +125,7 @@ xlate_push_stats_entry(struct xc_entry *entry, + case XC_LEARN: { + enum ofperr error; + error = ofproto_flow_mod_learn(entry->learn.ofm, true, +- entry->learn.limit, NULL); ++ entry->learn.limit, NULL, stats->used); + if (error) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + VLOG_WARN_RL(&rl, "xcache LEARN action execution failed."); diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c -index a9cf3cbee..5dd3c30cd 100644 +index a9cf3cbee..18ff9e5a4 100644 --- a/ofproto/ofproto-dpif-xlate.c +++ b/ofproto/ofproto-dpif-xlate.c @@ -66,6 +66,7 @@ @@ -4500,7 +4513,45 @@ index a9cf3cbee..5dd3c30cd 100644 free(xbundle->name); free(xbundle); } -@@ -3906,20 +3989,17 @@ static void +@@ -1532,7 +1615,8 @@ xlate_lookup_ofproto_(const struct dpif_backer *backer, + } + + ofp_port_t in_port = recirc_id_node->state.metadata.in_port; +- if (in_port != OFPP_NONE && in_port != OFPP_CONTROLLER) { ++ if (in_port != OFPP_NONE && in_port != OFPP_CONTROLLER && ++ !uuid_is_zero(&recirc_id_node->state.xport_uuid)) { + struct uuid xport_uuid = recirc_id_node->state.xport_uuid; + xport = xport_lookup_by_uuid(xcfg, &xport_uuid); + if (xport && xport->xbridge && xport->xbridge->ofproto) { +@@ -1543,11 +1627,19 @@ xlate_lookup_ofproto_(const struct dpif_backer *backer, + * that the packet originated from the controller via an OpenFlow + * "packet-out". The right thing to do is to find just the + * ofproto. There is no xport, which is OK. ++ * Also a zeroed xport_uuid with a valid in_port, means that ++ * the packet originated from OFPP_CONTROLLER passed ++ * through a patch port. + * + * OFPP_NONE can also indicate that a bond caused recirculation. */ + struct uuid uuid = recirc_id_node->state.ofproto_uuid; + const struct xbridge *bridge = xbridge_lookup_by_uuid(xcfg, &uuid); ++ + if (bridge && bridge->ofproto) { ++ if (in_port != OFPP_CONTROLLER && in_port != OFPP_NONE && ++ !get_ofp_port(bridge, in_port)) { ++ goto xport_lookup; ++ } + if (errorp) { + *errorp = NULL; + } +@@ -1560,6 +1652,7 @@ xlate_lookup_ofproto_(const struct dpif_backer *backer, + } + } + ++xport_lookup: + xport = xport_lookup(xcfg, tnl_port_should_receive(flow) + ? tnl_port_receive(flow) + : odp_port_to_ofport(backer, flow->in_port.odp_port)); +@@ -3906,20 +3999,17 @@ static void patch_port_output(struct xlate_ctx *ctx, const struct xport *in_dev, struct xport *out_dev, bool is_last_action) { @@ -4528,7 +4579,7 @@ index a9cf3cbee..5dd3c30cd 100644 flow->in_port.ofp_port = out_dev->ofp_port; flow->metadata = htonll(0); memset(&flow->tunnel, 0, sizeof flow->tunnel); -@@ -3958,14 +4038,15 @@ patch_port_output(struct xlate_ctx *ctx, const struct xport *in_dev, +@@ -3958,14 +4048,15 @@ patch_port_output(struct xlate_ctx *ctx, const struct xport *in_dev, } else { /* Forwarding is disabled by STP and RSTP. Let OFPP_NORMAL and * the learning action look at the packet, then drop it. */ @@ -4546,7 +4597,7 @@ index a9cf3cbee..5dd3c30cd 100644 ctx->odp_actions->size = old_size; /* Undo changes that may have been done for freezing. */ -@@ -3977,18 +4058,15 @@ patch_port_output(struct xlate_ctx *ctx, const struct xport *in_dev, +@@ -3977,18 +4068,15 @@ patch_port_output(struct xlate_ctx *ctx, const struct xport *in_dev, if (independent_mirrors) { ctx->mirrors = old_mirrors; } @@ -4569,7 +4620,7 @@ index a9cf3cbee..5dd3c30cd 100644 /* The out bridge popping MPLS should have no effect on the original * bridge. */ -@@ -4238,7 +4316,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port, +@@ -4238,7 +4326,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port, const struct xport *xport = get_ofp_port(ctx->xbridge, ofp_port); struct flow_wildcards *wc = ctx->wc; struct flow *flow = &ctx->xin->flow; @@ -4578,7 +4629,7 @@ index a9cf3cbee..5dd3c30cd 100644 union flow_vlan_hdr flow_vlans[FLOW_MAX_VLAN_HEADERS]; uint8_t flow_nw_tos; odp_port_t out_port, odp_port, odp_tnl_port; -@@ -4252,7 +4330,6 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port, +@@ -4252,7 +4340,6 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port, /* If 'struct flow' gets additional metadata, we'll need to zero it out * before traversing a patch port. */ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42); @@ -4586,7 +4637,7 @@ index a9cf3cbee..5dd3c30cd 100644 if (!check_output_prerequisites(ctx, xport, flow, check_stp)) { return; -@@ -4296,7 +4373,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port, +@@ -4296,7 +4383,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port, * the Logical (tunnel) Port are not visible for any further * matches, while explicit set actions on tunnel metadata are. */ @@ -4595,7 +4646,7 @@ index a9cf3cbee..5dd3c30cd 100644 odp_port = tnl_port_send(xport->ofport, flow, ctx->wc); if (odp_port == ODPP_NONE) { xlate_report(ctx, OFT_WARN, "Tunneling decided against output"); -@@ -4327,7 +4404,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port, +@@ -4327,7 +4414,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port, tnl_type = tnl_port_get_type(xport->ofport); commit_odp_tunnel_action(flow, &ctx->base_flow, ctx->odp_actions, tnl_type); @@ -4604,7 +4655,7 @@ index a9cf3cbee..5dd3c30cd 100644 } } else { odp_port = xport->odp_port; -@@ -4371,7 +4448,8 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port, +@@ -4371,7 +4458,8 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port, /* Output to native tunnel port. */ native_tunnel_output(ctx, xport, flow, odp_port, truncate, is_last_action); @@ -4614,7 +4665,7 @@ index a9cf3cbee..5dd3c30cd 100644 } else if (terminate_native_tunnel(ctx, xport, flow, wc, &odp_tnl_port)) { -@@ -4414,7 +4492,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port, +@@ -4414,7 +4502,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port, xport->xbundle)); } @@ -4623,7 +4674,7 @@ index a9cf3cbee..5dd3c30cd 100644 /* Restore flow */ memcpy(flow->vlans, flow_vlans, sizeof flow->vlans); flow->nw_tos = flow_nw_tos; -@@ -4422,6 +4500,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port, +@@ -4422,6 +4510,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port, flow->dl_src = flow_dl_src; flow->packet_type = flow_packet_type; flow->dl_type = flow_dl_type; @@ -4631,7 +4682,7 @@ index a9cf3cbee..5dd3c30cd 100644 } static void -@@ -5211,6 +5290,7 @@ compose_dec_ttl(struct xlate_ctx *ctx, struct ofpact_cnt_ids *ids) +@@ -5211,6 +5300,7 @@ compose_dec_ttl(struct xlate_ctx *ctx, struct ofpact_cnt_ids *ids) } ctx->wc->masks.nw_ttl = 0xff; @@ -4639,7 +4690,7 @@ index a9cf3cbee..5dd3c30cd 100644 if (flow->nw_ttl > 1) { flow->nw_ttl--; return false; -@@ -5399,15 +5479,15 @@ xlate_output_reg_action(struct xlate_ctx *ctx, +@@ -5399,15 +5489,15 @@ xlate_output_reg_action(struct xlate_ctx *ctx, { uint64_t port = mf_get_subfield(&or->src, &ctx->xin->flow); if (port <= UINT16_MAX) { @@ -4660,7 +4711,25 @@ index a9cf3cbee..5dd3c30cd 100644 } else { xlate_report(ctx, OFT_WARN, "output port %"PRIu64" is out of range", port); -@@ -5748,13 +5828,15 @@ xlate_sample_action(struct xlate_ctx *ctx, +@@ -5616,8 +5706,16 @@ xlate_learn_action(struct xlate_ctx *ctx, const struct ofpact_learn *learn) + if (!error) { + bool success = true; + if (ctx->xin->allow_side_effects) { ++ long long int last_used; ++ ++ if (ctx->xin->resubmit_stats) { ++ last_used = ctx->xin->resubmit_stats->used; ++ } else { ++ last_used = time_msec(); ++ } + error = ofproto_flow_mod_learn(ofm, ctx->xin->xcache != NULL, +- learn->limit, &success); ++ learn->limit, &success, ++ last_used); + } else if (learn->limit) { + if (!ofm->temp_rule + || ofm->temp_rule->state != RULE_INSERTED) { +@@ -5748,13 +5846,15 @@ xlate_sample_action(struct xlate_ctx *ctx, struct flow *flow = &ctx->xin->flow; tnl_port_send(xport->ofport, flow, ctx->wc); if (!ovs_native_tunneling_is_on(ctx->xbridge->ofproto)) { @@ -4678,7 +4747,7 @@ index a9cf3cbee..5dd3c30cd 100644 } } else { xlate_report_error(ctx, -@@ -5864,21 +5946,12 @@ clone_xlate_actions(const struct ofpact *actions, size_t actions_len, +@@ -5864,21 +5964,12 @@ clone_xlate_actions(const struct ofpact *actions, size_t actions_len, struct xlate_ctx *ctx, bool is_last_action, bool group_bucket_action OVS_UNUSED) { @@ -4703,7 +4772,7 @@ index a9cf3cbee..5dd3c30cd 100644 do_xlate_actions(actions, actions_len, ctx, is_last_action, false); if (!ctx->freezing) { xlate_action_set(ctx); -@@ -5893,7 +5966,8 @@ clone_xlate_actions(const struct ofpact *actions, size_t actions_len, +@@ -5893,7 +5984,8 @@ clone_xlate_actions(const struct ofpact *actions, size_t actions_len, * avoid emitting those actions twice. Once inside * the clone, another time for the action after clone. */ xlate_commit_actions(ctx); @@ -4713,7 +4782,7 @@ index a9cf3cbee..5dd3c30cd 100644 bool old_was_mpls = ctx->was_mpls; bool old_conntracked = ctx->conntracked; -@@ -5950,14 +6024,10 @@ dp_clone_done: +@@ -5950,14 +6042,10 @@ dp_clone_done: ctx->was_mpls = old_was_mpls; /* Restore the 'base_flow' for the next action. */ @@ -4730,7 +4799,7 @@ index a9cf3cbee..5dd3c30cd 100644 } static void -@@ -6333,8 +6403,8 @@ compose_conntrack_action(struct xlate_ctx *ctx, struct ofpact_conntrack *ofc, +@@ -6333,8 +6421,8 @@ compose_conntrack_action(struct xlate_ctx *ctx, struct ofpact_conntrack *ofc, { uint16_t zone; if (ofc->zone_src.field) { @@ -4741,7 +4810,7 @@ index a9cf3cbee..5dd3c30cd 100644 zone = mf_get_subfield(&ofc->zone_src, &ctx->xin->flow); if (ctx->xin->frozen_state) { -@@ -6344,12 +6414,13 @@ compose_conntrack_action(struct xlate_ctx *ctx, struct ofpact_conntrack *ofc, +@@ -6344,12 +6432,13 @@ compose_conntrack_action(struct xlate_ctx *ctx, struct ofpact_conntrack *ofc, * which will invalidate the megaflow with old the recirc_id. */ if (!mf_is_frozen_metadata(ofc->zone_src.field)) { @@ -4757,7 +4826,7 @@ index a9cf3cbee..5dd3c30cd 100644 } else { zone = ofc->zone_imm; } -@@ -6439,16 +6510,16 @@ xlate_check_pkt_larger(struct xlate_ctx *ctx, +@@ -6439,16 +6528,16 @@ xlate_check_pkt_larger(struct xlate_ctx *ctx, const struct ofpact *remaining_acts, size_t remaining_acts_len) { @@ -4778,7 +4847,7 @@ index a9cf3cbee..5dd3c30cd 100644 &ctx->xin->flow); /* If datapath doesn't support check_pkt_len action, then set the * SLOW_ACTION flag. If we don't set SLOW_ACTION, we -@@ -6458,22 +6529,17 @@ xlate_check_pkt_larger(struct xlate_ctx *ctx, +@@ -6458,22 +6547,17 @@ xlate_check_pkt_larger(struct xlate_ctx *ctx, * the packet length. This results in wrong actions being applied. */ ctx->xout->slow |= SLOW_ACTION; @@ -4806,7 +4875,7 @@ index a9cf3cbee..5dd3c30cd 100644 bool old_was_mpls = ctx->was_mpls; bool old_conntracked = ctx->conntracked; -@@ -6483,8 +6549,8 @@ xlate_check_pkt_larger(struct xlate_ctx *ctx, +@@ -6483,8 +6567,8 @@ xlate_check_pkt_larger(struct xlate_ctx *ctx, check_pkt_larger->pkt_len); size_t offset_attr = nl_msg_start_nested( ctx->odp_actions, OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER); @@ -4817,7 +4886,7 @@ index a9cf3cbee..5dd3c30cd 100644 do_xlate_actions(remaining_acts, remaining_acts_len, ctx, true, false); if (!ctx->freezing) { xlate_action_set(ctx); -@@ -6494,10 +6560,10 @@ xlate_check_pkt_larger(struct xlate_ctx *ctx, +@@ -6494,10 +6578,10 @@ xlate_check_pkt_larger(struct xlate_ctx *ctx, } nl_msg_end_nested(ctx->odp_actions, offset_attr); @@ -4830,7 +4899,7 @@ index a9cf3cbee..5dd3c30cd 100644 /* If the flow translation for the IF_GREATER case requires freezing, * then ctx->exit would be true. Reset to false so that we can -@@ -6508,8 +6574,8 @@ xlate_check_pkt_larger(struct xlate_ctx *ctx, +@@ -6508,8 +6592,8 @@ xlate_check_pkt_larger(struct xlate_ctx *ctx, offset_attr = nl_msg_start_nested( ctx->odp_actions, OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL); @@ -4841,7 +4910,7 @@ index a9cf3cbee..5dd3c30cd 100644 do_xlate_actions(remaining_acts, remaining_acts_len, ctx, true, false); if (!ctx->freezing) { xlate_action_set(ctx); -@@ -6520,15 +6586,12 @@ xlate_check_pkt_larger(struct xlate_ctx *ctx, +@@ -6520,15 +6604,12 @@ xlate_check_pkt_larger(struct xlate_ctx *ctx, nl_msg_end_nested(ctx->odp_actions, offset_attr); nl_msg_end_nested(ctx->odp_actions, offset); @@ -4860,7 +4929,7 @@ index a9cf3cbee..5dd3c30cd 100644 } static void -@@ -6979,6 +7042,132 @@ xlate_ofpact_unroll_xlate(struct xlate_ctx *ctx, +@@ -6979,6 +7060,132 @@ xlate_ofpact_unroll_xlate(struct xlate_ctx *ctx, "cookie=%#"PRIx64, a->rule_table_id, a->rule_cookie); } @@ -4993,7 +5062,7 @@ index a9cf3cbee..5dd3c30cd 100644 static void do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, struct xlate_ctx *ctx, bool is_last_action, -@@ -7020,21 +7209,10 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, +@@ -7020,21 +7227,10 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, break; } @@ -5018,7 +5087,7 @@ index a9cf3cbee..5dd3c30cd 100644 } switch (a->type) { -@@ -7128,6 +7306,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, +@@ -7128,6 +7324,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, case OFPACT_SET_IPV4_SRC: if (flow->dl_type == htons(ETH_TYPE_IP)) { memset(&wc->masks.nw_src, 0xff, sizeof wc->masks.nw_src); @@ -5026,7 +5095,7 @@ index a9cf3cbee..5dd3c30cd 100644 flow->nw_src = ofpact_get_SET_IPV4_SRC(a)->ipv4; } break; -@@ -7135,12 +7314,14 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, +@@ -7135,12 +7332,14 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, case OFPACT_SET_IPV4_DST: if (flow->dl_type == htons(ETH_TYPE_IP)) { memset(&wc->masks.nw_dst, 0xff, sizeof wc->masks.nw_dst); @@ -5041,7 +5110,7 @@ index a9cf3cbee..5dd3c30cd 100644 wc->masks.nw_tos |= IP_DSCP_MASK; flow->nw_tos &= ~IP_DSCP_MASK; flow->nw_tos |= ofpact_get_SET_IP_DSCP(a)->dscp; -@@ -7149,6 +7330,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, +@@ -7149,6 +7348,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, case OFPACT_SET_IP_ECN: if (is_ip_any(flow)) { @@ -5049,7 +5118,7 @@ index a9cf3cbee..5dd3c30cd 100644 wc->masks.nw_tos |= IP_ECN_MASK; flow->nw_tos &= ~IP_ECN_MASK; flow->nw_tos |= ofpact_get_SET_IP_ECN(a)->ecn; -@@ -7157,6 +7339,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, +@@ -7157,6 +7357,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, case OFPACT_SET_IP_TTL: if (is_ip_any(flow)) { @@ -5057,7 +5126,7 @@ index a9cf3cbee..5dd3c30cd 100644 wc->masks.nw_ttl = 0xff; flow->nw_ttl = ofpact_get_SET_IP_TTL(a)->ttl; } -@@ -7224,6 +7407,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, +@@ -7224,6 +7425,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, /* Set the field only if the packet actually has it. */ if (mf_are_prereqs_ok(mf, flow, wc)) { @@ -5065,7 +5134,7 @@ index a9cf3cbee..5dd3c30cd 100644 mf_mask_field_masked(mf, ofpact_set_field_mask(set_field), wc); mf_set_flow_value_masked(mf, set_field->value, ofpact_set_field_mask(set_field), -@@ -7280,6 +7464,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, +@@ -7280,6 +7482,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, case OFPACT_DEC_TTL: wc->masks.nw_ttl = 0xff; @@ -5074,7 +5143,7 @@ index a9cf3cbee..5dd3c30cd 100644 return; } diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c -index f87e27a8c..e22ca757a 100644 +index f87e27a8c..ba5706f6a 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -714,12 +714,6 @@ close_dpif_backer(struct dpif_backer *backer, bool del) @@ -5169,8 +5238,17 @@ index f87e27a8c..e22ca757a 100644 } else if (del) { /* The underlying device is already deleted (e.g. tunctl -d). * Calling dpif_port_remove to do local cleanup for the netdev */ +@@ -4886,7 +4880,7 @@ packet_xlate(struct ofproto *ofproto_, struct ofproto_packet_out *opo) + if (entry->type == XC_LEARN) { + struct ofproto_flow_mod *ofm = entry->learn.ofm; + +- error = ofproto_flow_mod_learn_refresh(ofm); ++ error = ofproto_flow_mod_learn_refresh(ofm, time_msec()); + if (error) { + goto error_out; + } diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h -index a84ddc1d0..143ded690 100644 +index a84ddc1d0..9f7b8b6e8 100644 --- a/ofproto/ofproto-provider.h +++ b/ofproto/ofproto-provider.h @@ -541,6 +541,11 @@ extern unsigned ofproto_max_revalidator; @@ -5185,8 +5263,22 @@ index a84ddc1d0..143ded690 100644 /* Number of upcall handler and revalidator threads. Only affects the * ofproto-dpif implementation. */ extern uint32_t n_handlers, n_revalidators; +@@ -2022,9 +2027,11 @@ enum ofperr ofproto_flow_mod_init_for_learn(struct ofproto *, + struct ofproto_flow_mod *) + OVS_EXCLUDED(ofproto_mutex); + enum ofperr ofproto_flow_mod_learn(struct ofproto_flow_mod *, bool keep_ref, +- unsigned limit, bool *below_limit) ++ unsigned limit, bool *below_limit, ++ long long int last_used) + OVS_EXCLUDED(ofproto_mutex); +-enum ofperr ofproto_flow_mod_learn_refresh(struct ofproto_flow_mod *ofm); ++enum ofperr ofproto_flow_mod_learn_refresh(struct ofproto_flow_mod *ofm, ++ long long int last_used); + enum ofperr ofproto_flow_mod_learn_start(struct ofproto_flow_mod *ofm) + OVS_REQUIRES(ofproto_mutex); + void ofproto_flow_mod_learn_revert(struct ofproto_flow_mod *ofm) diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c -index 17f636ed9..5fc1a7409 100644 +index 17f636ed9..9a2f7f801 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -311,6 +311,7 @@ unsigned ofproto_flow_limit = OFPROTO_FLOW_LIMIT_DEFAULT; @@ -5213,6 +5305,119 @@ index 17f636ed9..5fc1a7409 100644 /* If forward_bpdu is true, the NORMAL action will forward frames with * reserved (e.g. STP) destination Ethernet addresses. if forward_bpdu is false, * the NORMAL action will drop these frames. */ +@@ -5456,7 +5466,8 @@ ofproto_flow_mod_init_for_learn(struct ofproto *ofproto, + } + + enum ofperr +-ofproto_flow_mod_learn_refresh(struct ofproto_flow_mod *ofm) ++ofproto_flow_mod_learn_refresh(struct ofproto_flow_mod *ofm, ++ long long int last_used) + { + enum ofperr error = 0; + +@@ -5477,9 +5488,37 @@ ofproto_flow_mod_learn_refresh(struct ofproto_flow_mod *ofm) + * this function is executed the rule will be reinstated. */ + if (rule->state == RULE_REMOVED) { + struct cls_rule cr; ++ struct oftable *table = &rule->ofproto->tables[rule->table_id]; ++ ovs_version_t tables_version = rule->ofproto->tables_version; ++ ++ if (!cls_rule_visible_in_version(&rule->cr, tables_version)) { ++ const struct cls_rule *curr_cls_rule; ++ ++ /* Only check for matching classifier rules and their modified ++ * time, instead of also checking all rule metadata, with the goal ++ * of suppressing a learn action update that would replace a more ++ * recent rule in the classifier. */ ++ curr_cls_rule = classifier_find_rule_exactly(&table->cls, ++ &rule->cr, ++ tables_version); ++ if (curr_cls_rule) { ++ struct rule *curr_rule = rule_from_cls_rule(curr_cls_rule); ++ long long int curr_last_used; ++ ++ ovs_mutex_lock(&curr_rule->mutex); ++ curr_last_used = curr_rule->modified; ++ ovs_mutex_unlock(&curr_rule->mutex); ++ ++ if (curr_last_used > last_used) { ++ /* In the case of a newer visible rule, don't recreate the ++ * current rule. */ ++ return 0; ++ } ++ } ++ } + +- cls_rule_clone(&cr, &rule->cr); + ovs_mutex_lock(&rule->mutex); ++ cls_rule_clone(&cr, &rule->cr); + error = ofproto_rule_create(rule->ofproto, &cr, rule->table_id, + rule->flow_cookie, + rule->idle_timeout, +@@ -5490,6 +5529,7 @@ ofproto_flow_mod_learn_refresh(struct ofproto_flow_mod *ofm) + rule->match_tlv_bitmap, + rule->ofpacts_tlv_bitmap, + &ofm->temp_rule); ++ ofm->temp_rule->modified = last_used; + ovs_mutex_unlock(&rule->mutex); + if (!error) { + ofproto_rule_unref(rule); /* Release old reference. */ +@@ -5497,7 +5537,7 @@ ofproto_flow_mod_learn_refresh(struct ofproto_flow_mod *ofm) + } else { + /* Refresh the existing rule. */ + ovs_mutex_lock(&rule->mutex); +- rule->modified = time_msec(); ++ rule->modified = last_used; + ovs_mutex_unlock(&rule->mutex); + } + return error; +@@ -5549,10 +5589,16 @@ ofproto_flow_mod_learn_finish(struct ofproto_flow_mod *ofm, + + /* Refresh 'ofm->temp_rule', for which the caller holds a reference, if already + * in the classifier, insert it otherwise. If the rule has already been +- * removed from the classifier, a new rule is created using 'ofm->temp_rule' as +- * a template and the reference to the old 'ofm->temp_rule' is freed. If +- * 'keep_ref' is true, then a reference to the current rule is held, otherwise +- * it is released and 'ofm->temp_rule' is set to NULL. ++ * removed from the classifier and replaced by another rule, the 'last_used' ++ * parameter is used to determine whether the newer rule is replaced or kept. ++ * If 'last_used' is greater than the last modified time of an identical rule ++ * in the classifier, then a new rule is created using 'ofm->temp_rule' as a ++ * template and the reference to the old 'ofm->temp_rule' is freed. If the ++ * rule has been removed but another identical rule doesn't exist in the ++ * classifier, then it will be recreated. If the rule hasn't been removed ++ * from the classifier, then 'last_used' is used to update the rules modified ++ * time. If 'keep_ref' is true, then a reference to the current rule is held, ++ * otherwise it is released and 'ofm->temp_rule' is set to NULL. + * + * If 'limit' != 0, insertion will fail if there are more than 'limit' rules + * in the same table with the same cookie. If insertion succeeds, +@@ -5563,10 +5609,11 @@ ofproto_flow_mod_learn_finish(struct ofproto_flow_mod *ofm, + * during the call. */ + enum ofperr + ofproto_flow_mod_learn(struct ofproto_flow_mod *ofm, bool keep_ref, +- unsigned limit, bool *below_limitp) ++ unsigned limit, bool *below_limitp, ++ long long int last_used) + OVS_EXCLUDED(ofproto_mutex) + { +- enum ofperr error = ofproto_flow_mod_learn_refresh(ofm); ++ enum ofperr error = ofproto_flow_mod_learn_refresh(ofm, last_used); + struct rule *rule = ofm->temp_rule; + bool below_limit = true; + +@@ -5599,6 +5646,11 @@ ofproto_flow_mod_learn(struct ofproto_flow_mod *ofm, bool keep_ref, + + error = ofproto_flow_mod_learn_start(ofm); + if (!error) { ++ /* ofproto_flow_mod_learn_start may have overwritten ++ * modified with current time. */ ++ ovs_mutex_lock(&ofm->temp_rule->mutex); ++ ofm->temp_rule->modified = last_used; ++ ovs_mutex_unlock(&ofm->temp_rule->mutex); + error = ofproto_flow_mod_learn_finish(ofm, NULL); + } + } else { diff --git a/ofproto/ofproto.h b/ofproto/ofproto.h index 4e15167ab..fa7973ac7 100644 --- a/ofproto/ofproto.h @@ -6101,6 +6306,74 @@ index 7454a51ec..d2f1046f8 100644 +OVS_VSWITCHD_STOP +AT_CLEANUP \ No newline at end of file +diff --git a/tests/learn.at b/tests/learn.at +index d127fed34..d0bcc8363 100644 +--- a/tests/learn.at ++++ b/tests/learn.at +@@ -836,3 +836,63 @@ AT_CHECK([ovs-vsctl add-br br1 -- set b br1 datapath_type=dummy]) + + OVS_VSWITCHD_STOP + AT_CLEANUP ++ ++AT_SETUP([learning action - flapping learn rule]) ++OVS_VSWITCHD_START ++add_of_ports br0 1 2 3 ++ ++AT_CHECK([ovs-appctl time/stop], [0], [ignore]) ++AT_CHECK([[ovs-ofctl add-flow br0 'table=0,priority=2,in_port=1,actions=resubmit(,2)']]) ++AT_CHECK([[ovs-ofctl add-flow br0 'table=0,priority=2,in_port=2,actions=resubmit(,2)']]) ++AT_CHECK([[ovs-ofctl add-flow br0 'table=2,actions=learn(table=0,hard_timeout=3,priority=1,cookie=0x123,NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],output:OXM_OF_IN_PORT[]),output:3']]) ++ ++packet="eth(src=50:54:00:00:00:06,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)" ++ ++dnl Run this test a few times in a loop to reduce the likelyhood that it passes by chance. ++for i in 1 2 3; do ++ AT_CHECK([ovs-appctl revalidator/pause], [0]) ++ AT_CHECK([ovs-appctl netdev-dummy/receive p2 $packet], [0]) ++ AT_CHECK([ovs-appctl time/warp 75], [0], [ignore]) ++ AT_CHECK([ovs-appctl netdev-dummy/receive p1 $packet], [0]) ++ AT_CHECK([ovs-appctl time/warp 75], [0], [ignore]) ++ AT_CHECK([ovs-appctl netdev-dummy/receive p2 $packet], [0]) ++ AT_CHECK([ovs-appctl time/warp 75], [0], [ignore]) ++ AT_CHECK([ovs-appctl netdev-dummy/receive p1 $packet], [0]) ++ AT_CHECK([ovs-appctl time/warp 75], [0], [ignore]) ++ ++ AT_CHECK([ovs-appctl revalidator/resume], [0]) ++ AT_CHECK([ovs-appctl revalidator/wait], [0]) ++ ++ AT_CHECK([ovs-ofctl --no-stats dump-flows br0 | ofctl_strip | sort | grep 0x123], [0], [dnl ++ cookie=0x123, hard_timeout=3, priority=1,dl_dst=50:54:00:00:00:06 actions=output:1 ++ table=2, actions=learn(table=0,hard_timeout=3,priority=1,cookie=0x123,NXM_OF_ETH_DST[[]]=NXM_OF_ETH_SRC[[]],output:OXM_OF_IN_PORT[[]]),output:3 ++]) ++ ++ AT_CHECK([ovs-appctl revalidator/pause], [0]) ++ AT_CHECK([ovs-appctl netdev-dummy/receive p1 $packet], [0]) ++ AT_CHECK([ovs-appctl time/warp 75], [0], [ignore]) ++ AT_CHECK([ovs-appctl netdev-dummy/receive p2 $packet], [0]) ++ AT_CHECK([ovs-appctl time/warp 75], [0], [ignore]) ++ AT_CHECK([ovs-appctl netdev-dummy/receive p1 $packet], [0]) ++ AT_CHECK([ovs-appctl time/warp 75], [0], [ignore]) ++ AT_CHECK([ovs-appctl netdev-dummy/receive p2 $packet], [0]) ++ AT_CHECK([ovs-appctl time/warp 75], [0], [ignore]) ++ ++ AT_CHECK([ovs-appctl revalidator/resume], [0]) ++ AT_CHECK([ovs-appctl revalidator/wait], [0]) ++ ++ AT_CHECK([ovs-ofctl --no-stats dump-flows br0 | ofctl_strip | sort | grep 0x123], [0], [dnl ++ cookie=0x123, hard_timeout=3, priority=1,dl_dst=50:54:00:00:00:06 actions=output:2 ++ table=2, actions=learn(table=0,hard_timeout=3,priority=1,cookie=0x123,NXM_OF_ETH_DST[[]]=NXM_OF_ETH_SRC[[]],output:OXM_OF_IN_PORT[[]]),output:3 ++]) ++done ++ ++dnl Wait and check for learned rule eviction due to hard timeout. ++AT_CHECK([ovs-appctl time/warp 3200], [0], [ignore]) ++ ++AT_CHECK([ovs-ofctl --no-stats dump-flows br0 | ofctl_strip | grep 0x123], [0], [dnl ++ table=2, actions=learn(table=0,hard_timeout=3,priority=1,cookie=0x123,NXM_OF_ETH_DST[[]]=NXM_OF_ETH_SRC[[]],output:OXM_OF_IN_PORT[[]]),output:3 ++]) ++ ++OVS_VSWITCHD_STOP ++AT_CLEANUP diff --git a/tests/learning-switch.at b/tests/learning-switch.at new file mode 100644 index 000000000..ac2fc1b80 @@ -6131,7 +6404,7 @@ index 000000000..ac2fc1b80 +OVS_VSWITCHD_STOP(["/cannot find route for controller/d"]) +AT_CLEANUP diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at -index fa6111c1e..119b78ef9 100644 +index fa6111c1e..450483bee 100644 --- a/tests/ofproto-dpif.at +++ b/tests/ofproto-dpif.at @@ -849,7 +849,7 @@ table=2 ip actions=set_field:192.168.3.91->ip_src,output(11) @@ -6227,7 +6500,48 @@ index fa6111c1e..119b78ef9 100644 ]) flow="in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)" -@@ -11884,7 +11884,7 @@ ovs-ofctl dump-flows br0 +@@ -5854,6 +5854,40 @@ OVS_WAIT_UNTIL([check_flows], [ovs-ofctl dump-flows br0]) + OVS_VSWITCHD_STOP + AT_CLEANUP + ++dnl Checks for regression against a bug in which OVS dropped packets ++dnl originating from a controller passing through a patch port. ++AT_SETUP([ofproto-dpif - packet-out recirculation OFPP_CONTROLLER and patch port]) ++OVS_VSWITCHD_START( ++ [add-port br0 patch-br1 -- \ ++ set interface patch-br1 type=patch options:peer=patch-br0 -- \ ++ add-br br1 -- set bridge br1 datapath-type=dummy fail-mode=secure -- \ ++ add-port br1 patch-br0 -- set interface patch-br0 type=patch options:peer=patch-br1 ++]) ++ ++add_of_ports --pcap br1 1 ++ ++AT_DATA([flows-br0.txt], [dnl ++table=0 icmp actions=output:patch-br1 ++]) ++AT_CHECK([ovs-ofctl add-flows br0 flows-br0.txt]) ++ ++AT_DATA([flows-br1.txt], [dnl ++table=0, icmp actions=ct(table=1,zone=1) ++table=1, ct_state=+trk, icmp actions=p1 ++]) ++AT_CHECK([ovs-ofctl add-flows br1 flows-br1.txt]) ++ ++packet=50540000000750540000000508004500005c000000008001b94dc0a80001c0a80002080013fc00000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f ++AT_CHECK([ovs-ofctl packet-out br0 "in_port=CONTROLLER packet=$packet actions=table"]) ++ ++OVS_WAIT_UNTIL_EQUAL([ovs-ofctl dump-flows -m br1 | grep "ct_state" | ofctl_strip], [dnl ++ table=1, n_packets=1, n_bytes=106, ct_state=+trk,icmp actions=output:2]) ++ ++OVS_WAIT_UNTIL([ovs-pcap p1-tx.pcap | grep -q "$packet"]) ++ ++OVS_VSWITCHD_STOP ++AT_CLEANUP ++ + AT_SETUP([ofproto-dpif - debug_slow action]) + OVS_VSWITCHD_START + add_of_ports br0 1 2 3 +@@ -11884,7 +11918,7 @@ ovs-ofctl dump-flows br0 AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) AT_CHECK([tail -3 stdout], [0], [dnl diff --git a/SPECS/openvswitch3.1.spec b/SPECS/openvswitch3.1.spec index 96aaf33..17a6770 100644 --- a/SPECS/openvswitch3.1.spec +++ b/SPECS/openvswitch3.1.spec @@ -63,7 +63,7 @@ Summary: Open vSwitch Group: System Environment/Daemons daemon/database/utilities URL: http://www.openvswitch.org/ Version: 3.1.0 -Release: 58%{?dist} +Release: 60%{?dist} # Nearly all of openvswitch is ASL 2.0. The bugtool is LGPLv2+, and the # lib/sflow*.[ch] files are SISSL @@ -757,6 +757,18 @@ exit 0 %endif %changelog +* Wed Sep 06 2023 Open vSwitch CI - 3.1.0-60 +- Merging upstream branch-3.1 [RH git: 0b6a4adefd] + Commit list: + 2ac4059b9a ofproto-dpif-xlate: Fix recirculation with patch port and controller. (#2170920) + + +* Wed Sep 06 2023 Open vSwitch CI - 3.1.0-59 +- Merging upstream branch-3.1 [RH git: 8a931c86c6] + Commit list: + 04b8fe40a5 ofproto-dpif-xlate: Don't reinstall removed XC_LEARN rule. (#2213892) + + * Thu Aug 31 2023 Open vSwitch CI - 3.1.0-58 - Merging upstream branch-3.1 [RH git: d2918cee34] Commit list: