For this commit, only the most trivial dynamic string matches have been replaced with a thread-local buffer. This prevents a great deal of memory allocations and frees.
Follow-up commits will take care of some of the more complex uses of dynamic string matches. Signed-off-by: Mark Michelson <[email protected]> --- northd/ovn-northd.c | 376 +++++++++++++++++++++----------------------- 1 file changed, 180 insertions(+), 196 deletions(-) diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c index d872f6a3c..6b51d12de 100644 --- a/northd/ovn-northd.c +++ b/northd/ovn-northd.c @@ -56,6 +56,7 @@ #include "util.h" #include "uuid.h" #include "openvswitch/vlog.h" +#include "ovs-thread.h" VLOG_DEFINE_THIS_MODULE(ovn_northd); @@ -4083,6 +4084,16 @@ static bool use_parallel_build = true; static struct hashrow_locks lflow_locks; +DEFINE_STATIC_PER_THREAD_DATA(struct ds, match, DS_EMPTY_INITIALIZER); + +static struct ds * +match_get_clear(void) +{ + struct ds *match = match_get(); + ds_clear(match); + return match; +} + /* Adds a row with the specified contents to the Logical_Flow table. * Version to use when locking is required. */ @@ -4338,21 +4349,21 @@ static void build_port_security_nd(struct ovn_port *op, struct hmap *lflows, const struct ovsdb_idl_row *stage_hint) { - struct ds match = DS_EMPTY_INITIALIZER; + struct ds *match = match_get(); for (size_t i = 0; i < op->n_ps_addrs; i++) { struct lport_addresses *ps = &op->ps_addrs[i]; bool no_ip = !(ps->n_ipv4_addrs || ps->n_ipv6_addrs); - ds_clear(&match); + ds_clear(match); if (ps->n_ipv4_addrs || no_ip) { - ds_put_format(&match, + ds_put_format(match, "inport == %s && eth.src == %s && arp.sha == %s", op->json_key, ps->ea_s, ps->ea_s); if (ps->n_ipv4_addrs) { - ds_put_cstr(&match, " && arp.spa == {"); + ds_put_cstr(match, " && arp.spa == {"); for (size_t j = 0; j < ps->n_ipv4_addrs; j++) { /* When the netmask is applied, if the host portion is * non-zero, the host can only use the specified @@ -4360,38 +4371,37 @@ build_port_security_nd(struct ovn_port *op, struct hmap *lflows, * to use any address in the subnet. */ if (ps->ipv4_addrs[j].plen == 32 || ps->ipv4_addrs[j].addr & ~ps->ipv4_addrs[j].mask) { - ds_put_cstr(&match, ps->ipv4_addrs[j].addr_s); + ds_put_cstr(match, ps->ipv4_addrs[j].addr_s); } else { - ds_put_format(&match, "%s/%d", + ds_put_format(match, "%s/%d", ps->ipv4_addrs[j].network_s, ps->ipv4_addrs[j].plen); } - ds_put_cstr(&match, ", "); + ds_put_cstr(match, ", "); } - ds_chomp(&match, ' '); - ds_chomp(&match, ','); - ds_put_cstr(&match, "}"); + ds_chomp(match, ' '); + ds_chomp(match, ','); + ds_put_cstr(match, "}"); } ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_PORT_SEC_ND, - 90, ds_cstr(&match), "next;", stage_hint); + 90, ds_cstr(match), "next;", stage_hint); } if (ps->n_ipv6_addrs || no_ip) { - ds_clear(&match); - ds_put_format(&match, "inport == %s && eth.src == %s", + ds_clear(match); + ds_put_format(match, "inport == %s && eth.src == %s", op->json_key, ps->ea_s); - build_port_security_ipv6_nd_flow(&match, ps->ea, ps->ipv6_addrs, + build_port_security_ipv6_nd_flow(match, ps->ea, ps->ipv6_addrs, ps->n_ipv6_addrs); ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_PORT_SEC_ND, - 90, ds_cstr(&match), "next;", stage_hint); + 90, ds_cstr(match), "next;", stage_hint); } } - ds_clear(&match); - ds_put_format(&match, "inport == %s && (arp || nd)", op->json_key); + ds_clear(match); + ds_put_format(match, "inport == %s && (arp || nd)", op->json_key); ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_PORT_SEC_ND, 80, - ds_cstr(&match), "drop;", stage_hint); - ds_destroy(&match); + ds_cstr(match), "drop;", stage_hint); } /** @@ -4432,7 +4442,7 @@ build_port_security_ip(enum ovn_pipeline pipeline, struct ovn_port *op, } if (ps->n_ipv4_addrs) { - struct ds match = DS_EMPTY_INITIALIZER; + struct ds *match = match_get_clear(); if (pipeline == P_IN) { /* Permit use of the unspecified address for DHCP discovery */ struct ds dhcp_match = DS_EMPTY_INITIALIZER; @@ -4446,11 +4456,11 @@ build_port_security_ip(enum ovn_pipeline pipeline, struct ovn_port *op, ds_cstr(&dhcp_match), "next;", stage_hint); ds_destroy(&dhcp_match); - ds_put_format(&match, "inport == %s && eth.src == %s" + ds_put_format(match, "inport == %s && eth.src == %s" " && ip4.src == {", op->json_key, ps->ea_s); } else { - ds_put_format(&match, "outport == %s && eth.dst == %s" + ds_put_format(match, "outport == %s && eth.dst == %s" " && ip4.dst == {255.255.255.255, 224.0.0.0/4, ", op->json_key, ps->ea_s); } @@ -4464,33 +4474,32 @@ build_port_security_ip(enum ovn_pipeline pipeline, struct ovn_port *op, */ if (ps->ipv4_addrs[j].plen == 32 || ps->ipv4_addrs[j].addr & ~mask) { - ds_put_format(&match, "%s", ps->ipv4_addrs[j].addr_s); + ds_put_format(match, "%s", ps->ipv4_addrs[j].addr_s); if (pipeline == P_OUT && ps->ipv4_addrs[j].plen != 32) { /* Host is also allowed to receive packets to the * broadcast address in the specified subnet. */ - ds_put_format(&match, ", %s", + ds_put_format(match, ", %s", ps->ipv4_addrs[j].bcast_s); } } else { /* host portion is zero */ - ds_put_format(&match, "%s/%d", ps->ipv4_addrs[j].network_s, + ds_put_format(match, "%s/%d", ps->ipv4_addrs[j].network_s, ps->ipv4_addrs[j].plen); } - ds_put_cstr(&match, ", "); + ds_put_cstr(match, ", "); } /* Replace ", " by "}". */ - ds_chomp(&match, ' '); - ds_chomp(&match, ','); - ds_put_cstr(&match, "}"); + ds_chomp(match, ' '); + ds_chomp(match, ','); + ds_put_cstr(match, "}"); ovn_lflow_add_with_hint(lflows, op->od, stage, 90, - ds_cstr(&match), "next;", + ds_cstr(match), "next;", stage_hint); - ds_destroy(&match); } if (ps->n_ipv6_addrs) { - struct ds match = DS_EMPTY_INITIALIZER; + struct ds *match = match_get_clear(); if (pipeline == P_IN) { /* Permit use of unspecified address for duplicate address * detection */ @@ -4506,15 +4515,14 @@ build_port_security_ip(enum ovn_pipeline pipeline, struct ovn_port *op, stage_hint); ds_destroy(&dad_match); } - ds_put_format(&match, "%s == %s && %s == %s", + ds_put_format(match, "%s == %s && %s == %s", port_direction, op->json_key, pipeline == P_IN ? "eth.src" : "eth.dst", ps->ea_s); - build_port_security_ipv6_flow(pipeline, &match, ps->ea, + build_port_security_ipv6_flow(pipeline, match, ps->ea, ps->ipv6_addrs, ps->n_ipv6_addrs); ovn_lflow_add_with_hint(lflows, op->od, stage, 90, - ds_cstr(&match), "next;", + ds_cstr(match), "next;", stage_hint); - ds_destroy(&match); } char *match = xasprintf("%s == %s && %s == %s && ip", @@ -5122,19 +5130,19 @@ build_empty_lb_event_flow(struct ovn_datapath *od, struct hmap *lflows, } bool ipv4 = IN6_IS_ADDR_V4MAPPED(&lb_vip->vip); - struct ds match = DS_EMPTY_INITIALIZER; + struct ds *match = match_get_clear(); char *meter = "", *action; if (meter_groups && shash_find(meter_groups, "event-elb")) { meter = "event-elb"; } - ds_put_format(&match, "ip%s.dst == %s && %s", + ds_put_format(match, "ip%s.dst == %s && %s", ipv4 ? "4": "6", lb_vip->vip_str, lb->protocol); char *vip = lb_vip->vip_str; if (lb_vip->vip_port) { - ds_put_format(&match, " && %s.dst == %u", lb->protocol, + ds_put_format(match, " && %s.dst == %u", lb->protocol, lb_vip->vip_port); vip = xasprintf("%s%s%s:%u", ipv4 ? "" : "[", lb_vip->vip_str, ipv4 ? "" : "]", lb_vip->vip_port); @@ -5147,9 +5155,8 @@ build_empty_lb_event_flow(struct ovn_datapath *od, struct hmap *lflows, event_to_string(OVN_EVENT_EMPTY_LB_BACKENDS), meter, vip, lb->protocol, UUID_ARGS(&lb->header_.uuid)); - ovn_lflow_add_with_hint(lflows, od, pl, 130, ds_cstr(&match), action, + ovn_lflow_add_with_hint(lflows, od, pl, 130, ds_cstr(match), action, &lb->header_); - ds_destroy(&match); if (lb_vip->vip_port) { free(vip); } @@ -5270,32 +5277,31 @@ build_pre_stateful(struct ovn_datapath *od, struct hmap *lflows) const char *lb_protocols[] = {"tcp", "udp", "sctp"}; struct ds actions = DS_EMPTY_INITIALIZER; - struct ds match = DS_EMPTY_INITIALIZER; + struct ds *match = match_get(); for (size_t i = 0; i < ARRAY_SIZE(lb_protocols); i++) { - ds_clear(&match); + ds_clear(match); ds_clear(&actions); - ds_put_format(&match, REGBIT_CONNTRACK_NAT" == 1 && ip4 && %s", + ds_put_format(match, REGBIT_CONNTRACK_NAT" == 1 && ip4 && %s", lb_protocols[i]); ds_put_format(&actions, REG_ORIG_DIP_IPV4 " = ip4.dst; " REG_ORIG_TP_DPORT " = %s.dst; ct_lb;", lb_protocols[i]); ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_STATEFUL, 120, - ds_cstr(&match), ds_cstr(&actions)); + ds_cstr(match), ds_cstr(&actions)); - ds_clear(&match); + ds_clear(match); ds_clear(&actions); - ds_put_format(&match, REGBIT_CONNTRACK_NAT" == 1 && ip6 && %s", + ds_put_format(match, REGBIT_CONNTRACK_NAT" == 1 && ip6 && %s", lb_protocols[i]); ds_put_format(&actions, REG_ORIG_DIP_IPV6 " = ip6.dst; " REG_ORIG_TP_DPORT " = %s.dst; ct_lb;", lb_protocols[i]); ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_STATEFUL, 120, - ds_cstr(&match), ds_cstr(&actions)); + ds_cstr(match), ds_cstr(&actions)); } ds_destroy(&actions); - ds_destroy(&match); ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_STATEFUL, 110, REGBIT_CONNTRACK_NAT" == 1", "ct_lb;"); @@ -5559,7 +5565,7 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od, &acl->header_); ds_destroy(&actions); } else { - struct ds match = DS_EMPTY_INITIALIZER; + struct ds *match = match_get_clear(); struct ds actions = DS_EMPTY_INITIALIZER; /* Commit the connection tracking entry if it's a new @@ -5574,14 +5580,14 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od, * by ct_commit in the "stateful" stage) to indicate that the * connection should be allowed to resume. */ - ds_put_format(&match, REGBIT_ACL_HINT_ALLOW_NEW " == 1 && (%s)", + ds_put_format(match, REGBIT_ACL_HINT_ALLOW_NEW " == 1 && (%s)", acl->match); ds_put_cstr(&actions, REGBIT_CONNTRACK_COMMIT" = 1; "); build_acl_log(&actions, acl, meter_groups); ds_put_cstr(&actions, "next;"); ovn_lflow_add_with_hint(lflows, od, stage, acl->priority + OVN_ACL_PRI_OFFSET, - ds_cstr(&match), + ds_cstr(match), ds_cstr(&actions), &acl->header_); @@ -5591,24 +5597,23 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od, * proceed to the next table. We use this to ensure that this * connection is still allowed by the currently defined * policy. Match untracked packets too. */ - ds_clear(&match); + ds_clear(match); ds_clear(&actions); - ds_put_format(&match, REGBIT_ACL_HINT_ALLOW " == 1 && (%s)", + ds_put_format(match, REGBIT_ACL_HINT_ALLOW " == 1 && (%s)", acl->match); build_acl_log(&actions, acl, meter_groups); ds_put_cstr(&actions, "next;"); ovn_lflow_add_with_hint(lflows, od, stage, acl->priority + OVN_ACL_PRI_OFFSET, - ds_cstr(&match), ds_cstr(&actions), + ds_cstr(match), ds_cstr(&actions), &acl->header_); - ds_destroy(&match); ds_destroy(&actions); } } else if (!strcmp(acl->action, "drop") || !strcmp(acl->action, "reject")) { - struct ds match = DS_EMPTY_INITIALIZER; + struct ds *match = match_get_clear(); struct ds actions = DS_EMPTY_INITIALIZER; /* The implementation of "drop" differs if stateful ACLs are in @@ -5618,17 +5623,17 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od, if (has_stateful) { /* If the packet is not tracked or not part of an established * connection, then we can simply reject/drop it. */ - ds_put_cstr(&match, REGBIT_ACL_HINT_DROP " == 1"); + ds_put_cstr(match, REGBIT_ACL_HINT_DROP " == 1"); if (!strcmp(acl->action, "reject")) { - build_reject_acl_rules(od, lflows, stage, acl, &match, + build_reject_acl_rules(od, lflows, stage, acl, match, &actions, &acl->header_, meter_groups); } else { - ds_put_format(&match, " && (%s)", acl->match); + ds_put_format(match, " && (%s)", acl->match); build_acl_log(&actions, acl, meter_groups); ds_put_cstr(&actions, "/* drop */"); ovn_lflow_add_with_hint(lflows, od, stage, acl->priority + OVN_ACL_PRI_OFFSET, - ds_cstr(&match), ds_cstr(&actions), + ds_cstr(match), ds_cstr(&actions), &acl->header_); } /* For an existing connection without ct_label set, we've @@ -5642,20 +5647,20 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od, * ct_commit() to the "stateful" stage, but since we're * rejecting/dropping the packet, we go ahead and do it here. */ - ds_clear(&match); + ds_clear(match); ds_clear(&actions); - ds_put_cstr(&match, REGBIT_ACL_HINT_BLOCK " == 1"); + ds_put_cstr(match, REGBIT_ACL_HINT_BLOCK " == 1"); ds_put_cstr(&actions, "ct_commit { ct_label.blocked = 1; }; "); if (!strcmp(acl->action, "reject")) { - build_reject_acl_rules(od, lflows, stage, acl, &match, + build_reject_acl_rules(od, lflows, stage, acl, match, &actions, &acl->header_, meter_groups); } else { - ds_put_format(&match, " && (%s)", acl->match); + ds_put_format(match, " && (%s)", acl->match); build_acl_log(&actions, acl, meter_groups); ds_put_cstr(&actions, "/* drop */"); ovn_lflow_add_with_hint(lflows, od, stage, acl->priority + OVN_ACL_PRI_OFFSET, - ds_cstr(&match), ds_cstr(&actions), + ds_cstr(match), ds_cstr(&actions), &acl->header_); } } else { @@ -5663,7 +5668,7 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od, * so a "reject/drop" ACL is simply the "reject/drop" * logical flow action in all cases. */ if (!strcmp(acl->action, "reject")) { - build_reject_acl_rules(od, lflows, stage, acl, &match, + build_reject_acl_rules(od, lflows, stage, acl, match, &actions, &acl->header_, meter_groups); } else { build_acl_log(&actions, acl, meter_groups); @@ -5674,7 +5679,6 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od, &acl->header_); } } - ds_destroy(&match); ds_destroy(&actions); } } @@ -5889,17 +5893,16 @@ build_acls(struct ovn_datapath *od, struct hmap *lflows, const char *lease_time = smap_get( &od->nbs->ports[i]->dhcpv4_options->options, "lease_time"); if (server_id && server_mac && lease_time) { - struct ds match = DS_EMPTY_INITIALIZER; + struct ds *match = match_get_clear(); const char *actions = has_stateful ? "ct_commit; next;" : "next;"; - ds_put_format(&match, "outport == \"%s\" && eth.src == %s " + ds_put_format(match, "outport == \"%s\" && eth.src == %s " "&& ip4.src == %s && udp && udp.src == 67 " "&& udp.dst == 68", od->nbs->ports[i]->name, server_mac, server_id); ovn_lflow_add_with_hint( - lflows, od, S_SWITCH_OUT_ACL, 34000, ds_cstr(&match), + lflows, od, S_SWITCH_OUT_ACL, 34000, ds_cstr(match), actions, &od->nbs->ports[i]->dhcpv4_options->header_); - ds_destroy(&match); } } @@ -5916,17 +5919,16 @@ build_acls(struct ovn_datapath *od, struct hmap *lflows, char server_ip[INET6_ADDRSTRLEN + 1]; ipv6_string_mapped(server_ip, &lla); - struct ds match = DS_EMPTY_INITIALIZER; + struct ds *match = match_get_clear(); const char *actions = has_stateful ? "ct_commit; next;" : "next;"; - ds_put_format(&match, "outport == \"%s\" && eth.src == %s " + ds_put_format(match, "outport == \"%s\" && eth.src == %s " "&& ip6.src == %s && udp && udp.src == 547 " "&& udp.dst == 546", od->nbs->ports[i]->name, server_mac, server_ip); ovn_lflow_add_with_hint( - lflows, od, S_SWITCH_OUT_ACL, 34000, ds_cstr(&match), + lflows, od, S_SWITCH_OUT_ACL, 34000, ds_cstr(match), actions, &od->nbs->ports[i]->dhcpv6_options->header_); - ds_destroy(&match); } } } @@ -6021,7 +6023,7 @@ build_lb_rules(struct ovn_datapath *od, struct hmap *lflows, struct ovn_northd_lb *lb) { struct ds action = DS_EMPTY_INITIALIZER; - struct ds match = DS_EMPTY_INITIALIZER; + struct ds *match = match_get(); for (size_t i = 0; i < lb->n_vips; i++) { struct ovn_lb_vip *lb_vip = &lb->vips[i]; @@ -6029,7 +6031,7 @@ build_lb_rules(struct ovn_datapath *od, struct hmap *lflows, const char *ip_match = NULL; ds_clear(&action); - ds_clear(&match); + ds_clear(match); /* Store the original destination IP to be used when generating * hairpin flows. @@ -6066,21 +6068,20 @@ build_lb_rules(struct ovn_datapath *od, struct hmap *lflows, build_lb_vip_actions(lb_vip, lb_vip_nb, &action, lb->selection_fields, true); - ds_put_format(&match, "ct.new && %s.dst == %s", ip_match, + ds_put_format(match, "ct.new && %s.dst == %s", ip_match, lb_vip->vip_str); if (lb_vip->vip_port) { - ds_put_format(&match, " && %s.dst == %d", proto, lb_vip->vip_port); + ds_put_format(match, " && %s.dst == %d", proto, lb_vip->vip_port); ovn_lflow_add_with_hint(lflows, od, S_SWITCH_IN_STATEFUL, 120, - ds_cstr(&match), ds_cstr(&action), + ds_cstr(match), ds_cstr(&action), &lb->nlb->header_); } else { ovn_lflow_add_with_hint(lflows, od, S_SWITCH_IN_STATEFUL, 110, - ds_cstr(&match), ds_cstr(&action), + ds_cstr(match), ds_cstr(&action), &lb->nlb->header_); } } ds_destroy(&action); - ds_destroy(&match); } static void @@ -6179,7 +6180,7 @@ build_fwd_group_lflows(struct ovn_datapath *od, struct hmap *lflows) { if (!(!od->nbs || !od->nbs->n_forwarding_groups)) { - struct ds match = DS_EMPTY_INITIALIZER; + struct ds *match = match_get_clear(); struct ds actions = DS_EMPTY_INITIALIZER; struct ds group_ports = DS_EMPTY_INITIALIZER; @@ -6191,7 +6192,7 @@ build_fwd_group_lflows(struct ovn_datapath *od, struct hmap *lflows) } /* ARP responder for the forwarding group's virtual IP */ - ds_put_format(&match, "arp.tpa == %s && arp.op == 1", + ds_put_format(match, "arp.tpa == %s && arp.op == 1", fwd_group->vip); ds_put_format(&actions, "eth.dst = eth.src; " @@ -6207,12 +6208,12 @@ build_fwd_group_lflows(struct ovn_datapath *od, struct hmap *lflows) fwd_group->vmac, fwd_group->vmac, fwd_group->vip); ovn_lflow_add_with_hint(lflows, od, S_SWITCH_IN_ARP_ND_RSP, 50, - ds_cstr(&match), ds_cstr(&actions), + ds_cstr(match), ds_cstr(&actions), &fwd_group->header_); /* L2 lookup for the forwarding group's virtual MAC */ - ds_clear(&match); - ds_put_format(&match, "eth.dst == %s", fwd_group->vmac); + ds_clear(match); + ds_put_format(match, "eth.dst == %s", fwd_group->vmac); /* Create a comma separated string of child ports */ ds_clear(&group_ports); @@ -6230,11 +6231,10 @@ build_fwd_group_lflows(struct ovn_datapath *od, struct hmap *lflows) ds_clear(&actions); ds_put_format(&actions, "fwd_group(%s);", ds_cstr(&group_ports)); ovn_lflow_add_with_hint(lflows, od, S_SWITCH_IN_L2_LKUP, 50, - ds_cstr(&match), ds_cstr(&actions), + ds_cstr(match), ds_cstr(&actions), &fwd_group->header_); } - ds_destroy(&match); ds_destroy(&actions); ds_destroy(&group_ports); } @@ -6390,7 +6390,7 @@ build_lswitch_rport_arp_req_self_orig_flow(struct ovn_port *op, { struct sset all_eth_addrs = SSET_INITIALIZER(&all_eth_addrs); struct ds eth_src = DS_EMPTY_INITIALIZER; - struct ds match = DS_EMPTY_INITIALIZER; + struct ds *match = match_get_clear(); sset_add(&all_eth_addrs, op->lrp_networks.ea_s); @@ -6445,15 +6445,14 @@ build_lswitch_rport_arp_req_self_orig_flow(struct ovn_port *op, ds_chomp(ð_src, ','); ds_put_cstr(ð_src, "}"); - ds_put_format(&match, "eth.src == %s && (arp.op == 1 || nd_ns)", + ds_put_format(match, "eth.src == %s && (arp.op == 1 || nd_ns)", ds_cstr(ð_src)); ovn_lflow_add_unique(lflows, od, S_SWITCH_IN_L2_LKUP, priority, - ds_cstr(&match), + ds_cstr(match), "outport = \""MC_FLOOD_L2"\"; output;"); sset_destroy(&all_eth_addrs); ds_destroy(ð_src); - ds_destroy(&match); } /* @@ -6470,7 +6469,7 @@ build_lswitch_rport_arp_req_flow_for_ip(struct sset *ips, struct hmap *lflows, const struct ovsdb_idl_row *stage_hint) { - struct ds match = DS_EMPTY_INITIALIZER; + struct ds *match = match_get_clear(); struct ds actions = DS_EMPTY_INITIALIZER; /* Packets received from VXLAN tunnels have already been through the @@ -6478,22 +6477,22 @@ build_lswitch_rport_arp_req_flow_for_ip(struct sset *ips, * multicast_group implementation (VXLAN packets skip table 32 which * delivers to patch ports) but we're bypassing multicast_groups. */ - ds_put_cstr(&match, FLAGBIT_NOT_VXLAN " && "); + ds_put_cstr(match, FLAGBIT_NOT_VXLAN " && "); if (addr_family == AF_INET) { - ds_put_cstr(&match, "arp.op == 1 && arp.tpa == { "); + ds_put_cstr(match, "arp.op == 1 && arp.tpa == { "); } else { - ds_put_cstr(&match, "nd_ns && nd.target == { "); + ds_put_cstr(match, "nd_ns && nd.target == { "); } const char *ip_address; SSET_FOR_EACH (ip_address, ips) { - ds_put_format(&match, "%s, ", ip_address); + ds_put_format(match, "%s, ", ip_address); } - ds_chomp(&match, ' '); - ds_chomp(&match, ','); - ds_put_cstr(&match, "}"); + ds_chomp(match, ' '); + ds_chomp(match, ','); + ds_put_cstr(match, "}"); /* Send a the packet to the router pipeline. If the switch has non-router * ports then flood it there as well. @@ -6503,16 +6502,15 @@ build_lswitch_rport_arp_req_flow_for_ip(struct sset *ips, "outport = \""MC_FLOOD_L2"\"; output;", patch_op->json_key); ovn_lflow_add_unique_with_hint(lflows, od, S_SWITCH_IN_L2_LKUP, - priority, ds_cstr(&match), + priority, ds_cstr(match), ds_cstr(&actions), stage_hint); } else { ds_put_format(&actions, "outport = %s; output;", patch_op->json_key); ovn_lflow_add_with_hint(lflows, od, S_SWITCH_IN_L2_LKUP, priority, - ds_cstr(&match), ds_cstr(&actions), + ds_cstr(match), ds_cstr(&actions), stage_hint); } - ds_destroy(&match); ds_destroy(&actions); } @@ -6645,7 +6643,7 @@ build_dhcpv4_options_flows(struct ovn_port *op, const char *json_key, bool is_external, struct hmap *lflows) { - struct ds match = DS_EMPTY_INITIALIZER; + struct ds *match = match_get(); for (size_t j = 0; j < lsp_addrs->n_ipv4_addrs; j++) { struct ds options_action = DS_EMPTY_INITIALIZER; @@ -6654,24 +6652,24 @@ build_dhcpv4_options_flows(struct ovn_port *op, if (build_dhcpv4_action( op, lsp_addrs->ipv4_addrs[j].addr, &options_action, &response_action, &ipv4_addr_match)) { - ds_clear(&match); + ds_clear(match); ds_put_format( - &match, "inport == %s && eth.src == %s && " + match, "inport == %s && eth.src == %s && " "ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && " "udp.src == 68 && udp.dst == 67", json_key, lsp_addrs->ea_s); if (is_external) { - ds_put_format(&match, " && is_chassis_resident(%s)", + ds_put_format(match, " && is_chassis_resident(%s)", op->json_key); } ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_DHCP_OPTIONS, 100, - ds_cstr(&match), + ds_cstr(match), ds_cstr(&options_action), &op->nbsp->dhcpv4_options->header_); - ds_clear(&match); + ds_clear(match); /* Allow ip4.src = OFFER_IP and * ip4.dst = {SERVER_IP, 255.255.255.255} for the below * cases @@ -6681,38 +6679,38 @@ build_dhcpv4_options_flows(struct ovn_port *op, * broadcasting the DHCPREQUEST. */ ds_put_format( - &match, "inport == %s && eth.src == %s && " + match, "inport == %s && eth.src == %s && " "%s && udp.src == 68 && udp.dst == 67", json_key, lsp_addrs->ea_s, ds_cstr(&ipv4_addr_match)); if (is_external) { - ds_put_format(&match, " && is_chassis_resident(%s)", + ds_put_format(match, " && is_chassis_resident(%s)", op->json_key); } ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_DHCP_OPTIONS, 100, - ds_cstr(&match), + ds_cstr(match), ds_cstr(&options_action), &op->nbsp->dhcpv4_options->header_); - ds_clear(&match); + ds_clear(match); /* If REGBIT_DHCP_OPTS_RESULT is set, it means the * put_dhcp_opts action is successful. */ ds_put_format( - &match, "inport == %s && eth.src == %s && " + match, "inport == %s && eth.src == %s && " "ip4 && udp.src == 68 && udp.dst == 67" " && "REGBIT_DHCP_OPTS_RESULT, json_key, lsp_addrs->ea_s); if (is_external) { - ds_put_format(&match, " && is_chassis_resident(%s)", + ds_put_format(match, " && is_chassis_resident(%s)", op->json_key); } ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_DHCP_RESPONSE, 100, - ds_cstr(&match), + ds_cstr(match), ds_cstr(&response_action), &op->nbsp->dhcpv4_options->header_); ds_destroy(&options_action); @@ -6721,7 +6719,6 @@ build_dhcpv4_options_flows(struct ovn_port *op, break; } } - ds_destroy(&match); } static void @@ -6730,7 +6727,7 @@ build_dhcpv6_options_flows(struct ovn_port *op, const char *json_key, bool is_external, struct hmap *lflows) { - struct ds match = DS_EMPTY_INITIALIZER; + struct ds *match = match_get(); for (size_t j = 0; j < lsp_addrs->n_ipv6_addrs; j++) { struct ds options_action = DS_EMPTY_INITIALIZER; @@ -6738,30 +6735,30 @@ build_dhcpv6_options_flows(struct ovn_port *op, if (build_dhcpv6_action( op, &lsp_addrs->ipv6_addrs[j].addr, &options_action, &response_action)) { - ds_clear(&match); + ds_clear(match); ds_put_format( - &match, "inport == %s && eth.src == %s" + match, "inport == %s && eth.src == %s" " && ip6.dst == ff02::1:2 && udp.src == 546 &&" " udp.dst == 547", json_key, lsp_addrs->ea_s); if (is_external) { - ds_put_format(&match, " && is_chassis_resident(%s)", + ds_put_format(match, " && is_chassis_resident(%s)", op->json_key); } ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_DHCP_OPTIONS, 100, - ds_cstr(&match), + ds_cstr(match), ds_cstr(&options_action), &op->nbsp->dhcpv6_options->header_); /* If REGBIT_DHCP_OPTS_RESULT is set to 1, it means the * put_dhcpv6_opts action is successful */ - ds_put_cstr(&match, " && "REGBIT_DHCP_OPTS_RESULT); + ds_put_cstr(match, " && "REGBIT_DHCP_OPTS_RESULT); ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_DHCP_RESPONSE, 100, - ds_cstr(&match), + ds_cstr(match), ds_cstr(&response_action), &op->nbsp->dhcpv6_options->header_); ds_destroy(&options_action); @@ -6769,7 +6766,6 @@ build_dhcpv6_options_flows(struct ovn_port *op, break; } } - ds_destroy(&match); } static void @@ -6777,16 +6773,16 @@ build_drop_arp_nd_flows_for_unbound_router_ports(struct ovn_port *op, const struct ovn_port *port, struct hmap *lflows) { - struct ds match = DS_EMPTY_INITIALIZER; + struct ds *match = match_get(); for (size_t i = 0; i < op->n_lsp_addrs; i++) { for (size_t j = 0; j < op->od->n_router_ports; j++) { struct ovn_port *rp = op->od->router_ports[j]; for (size_t k = 0; k < rp->n_lsp_addrs; k++) { for (size_t l = 0; l < rp->lsp_addrs[k].n_ipv4_addrs; l++) { - ds_clear(&match); + ds_clear(match); ds_put_format( - &match, "inport == %s && eth.src == %s" + match, "inport == %s && eth.src == %s" " && !is_chassis_resident(%s)" " && arp.tpa == %s && arp.op == 1", port->json_key, @@ -6794,13 +6790,13 @@ build_drop_arp_nd_flows_for_unbound_router_ports(struct ovn_port *op, rp->lsp_addrs[k].ipv4_addrs[l].addr_s); ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_EXTERNAL_PORT, - 100, ds_cstr(&match), "drop;", + 100, ds_cstr(match), "drop;", &op->nbsp->header_); } for (size_t l = 0; l < rp->lsp_addrs[k].n_ipv6_addrs; l++) { - ds_clear(&match); + ds_clear(match); ds_put_format( - &match, "inport == %s && eth.src == %s" + match, "inport == %s && eth.src == %s" " && !is_chassis_resident(%s)" " && nd_ns && ip6.dst == {%s, %s} && nd.target == %s", port->json_key, @@ -6810,13 +6806,13 @@ build_drop_arp_nd_flows_for_unbound_router_ports(struct ovn_port *op, rp->lsp_addrs[k].ipv6_addrs[l].addr_s); ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_EXTERNAL_PORT, 100, - ds_cstr(&match), "drop;", + ds_cstr(match), "drop;", &op->nbsp->header_); } - ds_clear(&match); + ds_clear(match); ds_put_format( - &match, "inport == %s && eth.src == %s" + match, "inport == %s && eth.src == %s" " && eth.dst == %s" " && !is_chassis_resident(%s)", port->json_key, @@ -6824,12 +6820,11 @@ build_drop_arp_nd_flows_for_unbound_router_ports(struct ovn_port *op, op->json_key); ovn_lflow_add_with_hint(lflows, op->od, S_SWITCH_IN_EXTERNAL_PORT, - 100, ds_cstr(&match), "drop;", + 100, ds_cstr(match), "drop;", &op->nbsp->header_); } } } - ds_destroy(&match); } static bool @@ -6844,7 +6839,6 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *lflows) /* This flow table structure is documented in ovn-northd(8), so please * update ovn-northd.8.xml if you change anything. */ - struct ds match = DS_EMPTY_INITIALIZER; struct ds actions = DS_EMPTY_INITIALIZER; struct ovn_datapath *od; @@ -6869,7 +6863,6 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *lflows) "output;"); } - ds_destroy(&match); ds_destroy(&actions); } @@ -7870,7 +7863,7 @@ build_routing_policy_flow(struct hmap *lflows, struct ovn_datapath *od, const struct nbrec_logical_router_policy *rule, const struct ovsdb_idl_row *stage_hint) { - struct ds match = DS_EMPTY_INITIALIZER; + struct ds *match = match_get_clear(); struct ds actions = DS_EMPTY_INITIALIZER; if (!strcmp(rule->action, "reroute")) { @@ -7921,11 +7914,10 @@ build_routing_policy_flow(struct hmap *lflows, struct ovn_datapath *od, } ds_put_cstr(&actions, REG_ECMP_GROUP_ID" = 0; next;"); } - ds_put_format(&match, "%s", rule->match); + ds_put_format(match, "%s", rule->match); ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_POLICY, rule->priority, - ds_cstr(&match), ds_cstr(&actions), stage_hint); - ds_destroy(&match); + ds_cstr(match), ds_cstr(&actions), stage_hint); ds_destroy(&actions); } @@ -7958,7 +7950,7 @@ build_ecmp_routing_policy_flows(struct hmap *lflows, struct ovn_datapath *od, } } - struct ds match = DS_EMPTY_INITIALIZER; + struct ds *match = match_get(); struct ds actions = DS_EMPTY_INITIALIZER; for (size_t i = 0; i < rule->n_nexthops; i++) { @@ -7999,12 +7991,12 @@ build_ecmp_routing_policy_flows(struct hmap *lflows, struct ovn_datapath *od, out_port->lrp_networks.ea_s, out_port->json_key); - ds_clear(&match); - ds_put_format(&match, REG_ECMP_GROUP_ID" == %"PRIu16" && " + ds_clear(match); + ds_put_format(match, REG_ECMP_GROUP_ID" == %"PRIu16" && " REG_ECMP_MEMBER_ID" == %"PRIuSIZE, ecmp_group_id, i + 1); ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_POLICY_ECMP, - 100, ds_cstr(&match), + 100, ds_cstr(match), ds_cstr(&actions), &rule->header_); } @@ -8026,7 +8018,6 @@ build_ecmp_routing_policy_flows(struct hmap *lflows, struct ovn_datapath *od, ds_cstr(&actions), &rule->header_); cleanup: - ds_destroy(&match); ds_destroy(&actions); } @@ -8390,7 +8381,7 @@ add_ecmp_symmetric_reply_flows(struct hmap *lflows, struct ds *route_match) { const struct nbrec_logical_router_static_route *st_route = route->route; - struct ds match = DS_EMPTY_INITIALIZER; + struct ds *match = match_get_clear(); struct ds actions = DS_EMPTY_INITIALIZER; struct ds ecmp_reply = DS_EMPTY_INITIALIZER; char *cidr = normalize_v46_prefix(&route->prefix, route->plen); @@ -8398,14 +8389,14 @@ add_ecmp_symmetric_reply_flows(struct hmap *lflows, /* If symmetric ECMP replies are enabled, then packets that arrive over * an ECMP route need to go through conntrack. */ - ds_put_format(&match, "inport == %s && ip%s.%s == %s", + ds_put_format(match, "inport == %s && ip%s.%s == %s", out_port->json_key, IN6_IS_ADDR_V4MAPPED(&route->prefix) ? "4" : "6", route->is_src_route ? "dst" : "src", cidr); free(cidr); ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_DEFRAG, 100, - ds_cstr(&match), "ct_next;", + ds_cstr(match), "ct_next;", &st_route->header_); /* And packets that go out over an ECMP route need conntrack */ @@ -8419,13 +8410,13 @@ add_ecmp_symmetric_reply_flows(struct hmap *lflows, * NOTE: we purposely are not clearing match before this * ds_put_cstr() call. The previous contents are needed. */ - ds_put_cstr(&match, " && (ct.new && !ct.est)"); + ds_put_cstr(match, " && (ct.new && !ct.est)"); ds_put_format(&actions, "ct_commit { ct_label.ecmp_reply_eth = eth.src;" " ct_label.ecmp_reply_port = %" PRId64 ";}; next;", out_port->sb->tunnel_key); ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_ECMP_STATEFUL, 100, - ds_cstr(&match), ds_cstr(&actions), + ds_cstr(match), ds_cstr(&actions), &st_route->header_); /* Bypass ECMP selection if we already have ct_label information @@ -8433,8 +8424,8 @@ add_ecmp_symmetric_reply_flows(struct hmap *lflows, */ ds_put_format(&ecmp_reply, "ct.rpl && ct_label.ecmp_reply_port == %" PRId64, out_port->sb->tunnel_key); - ds_clear(&match); - ds_put_format(&match, "%s && %s", ds_cstr(&ecmp_reply), + ds_clear(match); + ds_put_format(match, "%s && %s", ds_cstr(&ecmp_reply), ds_cstr(route_match)); ds_clear(&actions); ds_put_format(&actions, "ip.ttl--; flags.loopback = 1; " @@ -8443,7 +8434,7 @@ add_ecmp_symmetric_reply_flows(struct hmap *lflows, IN6_IS_ADDR_V4MAPPED(&route->prefix) ? "" : "xx", port_ip, out_port->json_key); ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_IP_ROUTING, 300, - ds_cstr(&match), ds_cstr(&actions), + ds_cstr(match), ds_cstr(&actions), &st_route->header_); /* Egress reply traffic for symmetric ECMP routes skips router policies. */ @@ -8456,7 +8447,6 @@ add_ecmp_symmetric_reply_flows(struct hmap *lflows, 200, ds_cstr(&ecmp_reply), action, &st_route->header_); - ds_destroy(&match); ds_destroy(&actions); ds_destroy(&ecmp_reply); } @@ -8497,7 +8487,7 @@ build_ecmp_route_flow(struct hmap *lflows, struct ovn_datapath *od, ds_cstr(&route_match), ds_cstr(&actions)); /* Add per member flow */ - struct ds match = DS_EMPTY_INITIALIZER; + struct ds *match = match_get(); struct sset visited_ports = SSET_INITIALIZER(&visited_ports); LIST_FOR_EACH (er, list_node, &eg->route_list) { const struct parsed_route *route_ = er->route; @@ -8518,8 +8508,8 @@ build_ecmp_route_flow(struct hmap *lflows, struct ovn_datapath *od, add_ecmp_symmetric_reply_flows(lflows, od, lrp_addr_s, out_port, route_, &route_match); } - ds_clear(&match); - ds_put_format(&match, REG_ECMP_GROUP_ID" == %"PRIu16" && " + ds_clear(match); + ds_put_format(match, REG_ECMP_GROUP_ID" == %"PRIu16" && " REG_ECMP_MEMBER_ID" == %"PRIu16, eg->id, er->id); ds_clear(&actions); @@ -8535,11 +8525,10 @@ build_ecmp_route_flow(struct hmap *lflows, struct ovn_datapath *od, out_port->lrp_networks.ea_s, out_port->json_key); ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_IP_ROUTING_ECMP, 100, - ds_cstr(&match), ds_cstr(&actions), + ds_cstr(match), ds_cstr(&actions), &route->header_); } sset_destroy(&visited_ports); - ds_destroy(&match); ds_destroy(&route_match); ds_destroy(&actions); } @@ -8552,7 +8541,7 @@ add_route(struct hmap *lflows, struct ovn_datapath *od, bool is_discard_route) { bool is_ipv4 = strchr(network_s, '.') ? true : false; - struct ds match = DS_EMPTY_INITIALIZER; + struct ds *match = match_get_clear(); uint16_t priority; const struct ovn_port *op_inport = NULL; @@ -8565,7 +8554,7 @@ add_route(struct hmap *lflows, struct ovn_datapath *od, } } build_route_match(op_inport, network_s, plen, is_src_route, is_ipv4, - &match, &priority); + match, &priority); struct ds common_actions = DS_EMPTY_INITIALIZER; struct ds actions = DS_EMPTY_INITIALIZER; @@ -8593,15 +8582,14 @@ add_route(struct hmap *lflows, struct ovn_datapath *od, } ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_IP_ROUTING, priority, - ds_cstr(&match), ds_cstr(&actions), + ds_cstr(match), ds_cstr(&actions), stage_hint); if (op && op->has_bfd) { - ds_put_format(&match, " && udp.dst == 3784"); + ds_put_format(match, " && udp.dst == 3784"); ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_ROUTING, - priority + 1, ds_cstr(&match), + priority + 1, ds_cstr(match), ds_cstr(&common_actions), stage_hint); } - ds_destroy(&match); ds_destroy(&common_actions); ds_destroy(&actions); } @@ -9224,7 +9212,7 @@ build_lrouter_port_nat_arp_nd_flow(struct ovn_port *op, { struct lport_addresses *ext_addrs = &nat_entry->ext_addrs; const struct nbrec_nat *nat = nat_entry->nb; - struct ds match = DS_EMPTY_INITIALIZER; + struct ds *match = match_get_clear(); /* Mac address to use when replying to ARP/NS. */ const char *mac_s = REG_INPORT_ETH_ADDR; @@ -9240,7 +9228,7 @@ build_lrouter_port_nat_arp_nd_flow(struct ovn_port *op, * resident, so that upstream MAC learning points to the * correct chassis. Also need to avoid generation of * multiple ARP responses from different chassis. */ - ds_put_format(&match, "is_chassis_resident(\"%s\")", + ds_put_format(match, "is_chassis_resident(\"%s\")", nat->logical_port); } else { mac_s = REG_INPORT_ETH_ADDR; @@ -9250,7 +9238,7 @@ build_lrouter_port_nat_arp_nd_flow(struct ovn_port *op, * Also need to avoid generation of multiple ARP responses * from different chassis. */ if (op->od->l3redirect_port) { - ds_put_format(&match, "is_chassis_resident(%s)", + ds_put_format(match, "is_chassis_resident(%s)", op->od->l3redirect_port->json_key); } } @@ -9262,7 +9250,7 @@ build_lrouter_port_nat_arp_nd_flow(struct ovn_port *op, build_lrouter_nd_flow(op->od, op, "nd_na", ext_addrs->ipv6_addrs[0].addr_s, ext_addrs->ipv6_addrs[0].sn_addr_s, - mac_s, &match, false, 92, + mac_s, match, false, 92, &nat->header_, lflows); build_lrouter_nd_flow(op->od, op, "nd_na", ext_addrs->ipv6_addrs[0].addr_s, @@ -9272,15 +9260,13 @@ build_lrouter_port_nat_arp_nd_flow(struct ovn_port *op, } else { build_lrouter_arp_flow(op->od, op, ext_addrs->ipv4_addrs[0].addr_s, - mac_s, &match, false, 92, + mac_s, match, false, 92, &nat->header_, lflows); build_lrouter_arp_flow(op->od, op, ext_addrs->ipv4_addrs[0].addr_s, mac_s, NULL, true, 91, &nat->header_, lflows); } - - ds_destroy(&match); } static void @@ -9347,24 +9333,23 @@ build_lrouter_force_snat_flows(struct hmap *lflows, struct ovn_datapath *od, const char *ip_version, const char *ip_addr, const char *context) { - struct ds match = DS_EMPTY_INITIALIZER; + struct ds *match = match_get_clear(); struct ds actions = DS_EMPTY_INITIALIZER; - ds_put_format(&match, "ip%s && ip%s.dst == %s", + ds_put_format(match, "ip%s && ip%s.dst == %s", ip_version, ip_version, ip_addr); ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 110, - ds_cstr(&match), "ct_snat;"); + ds_cstr(match), "ct_snat;"); /* Higher priority rules to force SNAT with the IP addresses * configured in the Gateway router. This only takes effect * when the packet has already been DNATed or load balanced once. */ - ds_clear(&match); - ds_put_format(&match, "flags.force_snat_for_%s == 1 && ip%s", + ds_clear(match); + ds_put_format(match, "flags.force_snat_for_%s == 1 && ip%s", context, ip_version); ds_put_format(&actions, "ct_snat(%s);", ip_addr); ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT, 100, - ds_cstr(&match), ds_cstr(&actions)); + ds_cstr(match), ds_cstr(&actions)); - ds_destroy(&match); ds_destroy(&actions); } @@ -9448,42 +9433,41 @@ build_lrouter_bfd_flows(struct hmap *lflows, struct ovn_port *op) } struct ds ip_list = DS_EMPTY_INITIALIZER; - struct ds match = DS_EMPTY_INITIALIZER; + struct ds *match = match_get_clear(); if (op->lrp_networks.n_ipv4_addrs) { op_put_v4_networks(&ip_list, op, false); - ds_put_format(&match, "ip4.src == %s && udp.dst == 3784", + ds_put_format(match, "ip4.src == %s && udp.dst == 3784", ds_cstr(&ip_list)); ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 110, - ds_cstr(&match), "next; ", + ds_cstr(match), "next; ", &op->nbrp->header_); - ds_clear(&match); - ds_put_format(&match, "ip4.dst == %s && udp.dst == 3784", + ds_clear(match); + ds_put_format(match, "ip4.dst == %s && udp.dst == 3784", ds_cstr(&ip_list)); ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 110, - ds_cstr(&match), "handle_bfd_msg(); ", + ds_cstr(match), "handle_bfd_msg(); ", &op->nbrp->header_); } if (op->lrp_networks.n_ipv6_addrs) { ds_clear(&ip_list); - ds_clear(&match); + ds_clear(match); op_put_v6_networks(&ip_list, op); - ds_put_format(&match, "ip6.src == %s && udp.dst == 3784", + ds_put_format(match, "ip6.src == %s && udp.dst == 3784", ds_cstr(&ip_list)); ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 110, - ds_cstr(&match), "next; ", + ds_cstr(match), "next; ", &op->nbrp->header_); - ds_clear(&match); - ds_put_format(&match, "ip6.dst == %s && udp.dst == 3784", + ds_clear(match); + ds_put_format(match, "ip6.dst == %s && udp.dst == 3784", ds_cstr(&ip_list)); ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 110, - ds_cstr(&match), "handle_bfd_msg(); ", + ds_cstr(match), "handle_bfd_msg(); ", &op->nbrp->header_); } ds_destroy(&ip_list); - ds_destroy(&match); } /* Logical router ingress Table 0: L2 Admission Control -- 2.31.1 _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
