diff --git a/controller/lflow.c b/controller/lflow.c index 6055097b59..eef44389fc 100644 --- a/controller/lflow.c +++ b/controller/lflow.c @@ -2063,6 +2063,20 @@ add_lb_vip_hairpin_flows(struct ovn_controller_lb *lb, ofpbuf_uninit(&ofpacts); } +static void +add_lb_ct_snat_hairpin_for_dp(const struct ovn_controller_lb *lb, + const struct sbrec_datapath_binding *datapath, + struct match *dp_match, + struct ofpbuf *dp_acts, + struct ovn_desired_flow_table *flow_table) +{ + match_set_metadata(dp_match, htonll(datapath->tunnel_key)); + ofctrl_add_or_append_flow(flow_table, OFTABLE_CT_SNAT_HAIRPIN, 200, + lb->slb->header_.uuid.parts[0], + dp_match, dp_acts, &lb->slb->header_.uuid, + NX_CTLR_NO_METER, NULL); +} + static void add_lb_ct_snat_hairpin_dp_flows(struct ovn_controller_lb *lb, uint32_t id, @@ -2088,12 +2102,15 @@ add_lb_ct_snat_hairpin_dp_flows(struct ovn_controller_lb *lb, struct match dp_match = MATCH_CATCHALL_INITIALIZER; for (size_t i = 0; i < lb->slb->n_datapaths; i++) { - match_set_metadata(&dp_match, - htonll(lb->slb->datapaths[i]->tunnel_key)); - ofctrl_add_or_append_flow(flow_table, OFTABLE_CT_SNAT_HAIRPIN, 200, - lb->slb->header_.uuid.parts[0], - &dp_match, &dp_acts, &lb->slb->header_.uuid, - NX_CTLR_NO_METER, NULL); + add_lb_ct_snat_hairpin_for_dp(lb, lb->slb->datapaths[i], + &dp_match, &dp_acts, flow_table); + } + if (lb->slb->datapath_group) { + for (size_t i = 0; i < lb->slb->datapath_group->n_datapaths; i++) { + add_lb_ct_snat_hairpin_for_dp( + lb, lb->slb->datapath_group->datapaths[i], + &dp_match, &dp_acts, flow_table); + } } ofpbuf_uninit(&dp_acts); @@ -2351,7 +2368,20 @@ consider_lb_hairpin_flows(const struct sbrec_load_balancer *sbrec_lb, } } - if (i == sbrec_lb->n_datapaths) { + if (sbrec_lb->n_datapaths && i == sbrec_lb->n_datapaths) { + return; + } + + struct sbrec_logical_dp_group *dp_group = sbrec_lb->datapath_group; + + for (i = 0; dp_group && i < dp_group->n_datapaths; i++) { + if (get_local_datapath(local_datapaths, + dp_group->datapaths[i]->tunnel_key)) { + break; + } + } + + if (dp_group && i == dp_group->n_datapaths) { return; } diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c index 2e91380364..8e4c4b17bb 100644 --- a/controller/ovn-controller.c +++ b/controller/ovn-controller.c @@ -2210,6 +2210,33 @@ load_balancers_by_dp_find(struct hmap *lbs, return NULL; } +static void +load_balancers_by_dp_add_one(const struct hmap *local_datapaths, + const struct sbrec_datapath_binding *datapath, + const struct sbrec_load_balancer *lb, + struct hmap *lbs) +{ + struct local_datapath *ldp = + get_local_datapath(local_datapaths, datapath->tunnel_key); + + if (!ldp) { + return; + } + + struct load_balancers_by_dp *lbs_by_dp = + load_balancers_by_dp_find(lbs, ldp->datapath); + if (!lbs_by_dp) { + lbs_by_dp = load_balancers_by_dp_create(lbs, ldp->datapath); + } + + if (lbs_by_dp->n_dp_lbs == lbs_by_dp->n_allocated_dp_lbs) { + lbs_by_dp->dp_lbs = x2nrealloc(lbs_by_dp->dp_lbs, + &lbs_by_dp->n_allocated_dp_lbs, + sizeof *lbs_by_dp->dp_lbs); + } + lbs_by_dp->dp_lbs[lbs_by_dp->n_dp_lbs++] = lb; +} + /* Builds and returns a hmap of 'load_balancers_by_dp', one record for each * local datapath. */ @@ -2223,25 +2250,14 @@ load_balancers_by_dp_init(const struct hmap *local_datapaths, const struct sbrec_load_balancer *lb; SBREC_LOAD_BALANCER_TABLE_FOR_EACH (lb, lb_table) { for (size_t i = 0; i < lb->n_datapaths; i++) { - struct local_datapath *ldp = - get_local_datapath(local_datapaths, - lb->datapaths[i]->tunnel_key); - if (!ldp) { - continue; - } - - struct load_balancers_by_dp *lbs_by_dp = - load_balancers_by_dp_find(lbs, ldp->datapath); - if (!lbs_by_dp) { - lbs_by_dp = load_balancers_by_dp_create(lbs, ldp->datapath); - } - - if (lbs_by_dp->n_dp_lbs == lbs_by_dp->n_allocated_dp_lbs) { - lbs_by_dp->dp_lbs = x2nrealloc(lbs_by_dp->dp_lbs, - &lbs_by_dp->n_allocated_dp_lbs, - sizeof *lbs_by_dp->dp_lbs); - } - lbs_by_dp->dp_lbs[lbs_by_dp->n_dp_lbs++] = lb; + load_balancers_by_dp_add_one(local_datapaths, + lb->datapaths[i], lb, lbs); + } + for (size_t i = 0; lb->datapath_group + && i < lb->datapath_group->n_datapaths; i++) { + load_balancers_by_dp_add_one(local_datapaths, + lb->datapath_group->datapaths[i], + lb, lbs); } } return lbs; diff --git a/northd/northd.c b/northd/northd.c index f6b84c3185..2ff2d8ef94 100644 --- a/northd/northd.c +++ b/northd/northd.c @@ -4159,6 +4159,48 @@ build_lb_port_related_data(struct hmap *datapaths, struct hmap *ports, build_lb_svcs(input_data, ovnsb_txn, ports, lbs); } + +struct ovn_dp_group { + struct hmapx map; + struct sbrec_logical_dp_group *dp_group; + struct hmap_node node; +}; + +static struct ovn_dp_group * +ovn_dp_group_find(const struct hmap *dp_groups, + const struct hmapx *od, uint32_t hash) +{ + struct ovn_dp_group *dpg; + + HMAP_FOR_EACH_WITH_HASH (dpg, node, hash, dp_groups) { + if (hmapx_equals(&dpg->map, od)) { + return dpg; + } + } + return NULL; +} + +static struct sbrec_logical_dp_group * +ovn_sb_insert_logical_dp_group(struct ovsdb_idl_txn *ovnsb_txn, + const struct hmapx *od) +{ + struct sbrec_logical_dp_group *dp_group; + const struct sbrec_datapath_binding **sb; + const struct hmapx_node *node; + int n = 0; + + sb = xmalloc(hmapx_count(od) * sizeof *sb); + HMAPX_FOR_EACH (node, od) { + sb[n++] = ((struct ovn_datapath *) node->data)->sb; + } + dp_group = sbrec_logical_dp_group_insert(ovnsb_txn); + sbrec_logical_dp_group_set_datapaths( + dp_group, (struct sbrec_datapath_binding **) sb, n); + free(sb); + + return dp_group; +} + /* Syncs relevant load balancers (applied to logical switches) to the * Southbound database. */ @@ -4166,9 +4208,13 @@ static void sync_lbs(struct northd_input *input_data, struct ovsdb_idl_txn *ovnsb_txn, struct hmap *datapaths, struct hmap *lbs) { + struct hmap dp_groups = HMAP_INITIALIZER(&dp_groups); struct ovn_northd_lb *lb; - /* Delete any stale SB load balancer rows. */ + /* Delete any stale SB load balancer rows and collect existing valid + * datapath groups. */ + struct hmapx existing_sb_dp_groups = + HMAPX_INITIALIZER(&existing_sb_dp_groups); struct hmapx existing_lbs = HMAPX_INITIALIZER(&existing_lbs); const struct sbrec_load_balancer *sbrec_lb; SBREC_LOAD_BALANCER_TABLE_FOR_EACH_SAFE (sbrec_lb, @@ -4191,11 +4237,46 @@ sync_lbs(struct northd_input *input_data, struct ovsdb_idl_txn *ovnsb_txn, lb = ovn_northd_lb_find(lbs, &lb_uuid); if (!lb || !lb->n_nb_ls || !hmapx_add(&existing_lbs, lb)) { sbrec_load_balancer_delete(sbrec_lb); - } else { - lb->slb = sbrec_lb; + continue; + } + + lb->slb = sbrec_lb; + + /* Collect the datapath group. */ + struct sbrec_logical_dp_group *dp_group = sbrec_lb->datapath_group; + + if (!dp_group || !hmapx_add(&existing_sb_dp_groups, dp_group)) { + continue; + } + + struct ovn_dp_group *dpg = xzalloc(sizeof *dpg); + size_t i; + + hmapx_init(&dpg->map); + for (i = 0; i < dp_group->n_datapaths; i++) { + struct ovn_datapath *datapath_od; + + datapath_od = ovn_datapath_from_sbrec(datapaths, + dp_group->datapaths[i]); + if (!datapath_od || ovn_datapath_is_stale(datapath_od)) { + break; + } + hmapx_add(&dpg->map, datapath_od); + } + if (i == dp_group->n_datapaths) { + uint32_t hash = hash_int(hmapx_count(&dpg->map), 0); + + if (!ovn_dp_group_find(&dp_groups, &dpg->map, hash)) { + dpg->dp_group = dp_group; + hmap_insert(&dp_groups, &dpg->node, hash); + continue; + } } + hmapx_destroy(&dpg->map); + free(dpg); } hmapx_destroy(&existing_lbs); + hmapx_destroy(&existing_sb_dp_groups); /* Create SB Load balancer records if not present and sync * the SB load balancer columns. */ @@ -4212,13 +4293,6 @@ sync_lbs(struct northd_input *input_data, struct ovsdb_idl_txn *ovnsb_txn, smap_clone(&options, &lb->nlb->options); smap_replace(&options, "hairpin_orig_tuple", "true"); - struct sbrec_datapath_binding **lb_dps = - xmalloc(lb->n_nb_ls * sizeof *lb_dps); - for (size_t i = 0; i < lb->n_nb_ls; i++) { - lb_dps[i] = CONST_CAST(struct sbrec_datapath_binding *, - lb->nb_ls[i]->sb); - } - if (!lb->slb) { sbrec_lb = sbrec_load_balancer_insert(ovnsb_txn); lb->slb = sbrec_lb; @@ -4229,15 +4303,44 @@ sync_lbs(struct northd_input *input_data, struct ovsdb_idl_txn *ovnsb_txn, sbrec_load_balancer_set_external_ids(sbrec_lb, &external_ids); free(lb_id); } + + /* Find datapath group for this load balancer. */ + struct hmapx lb_dps = HMAPX_INITIALIZER(&lb_dps); + struct ovn_dp_group *dpg; + uint32_t hash; + + for (size_t i = 0; i < lb->n_nb_ls; i++) { + hmapx_add(&lb_dps, lb->nb_ls[i]); + } + + hash = hash_int(hmapx_count(&lb_dps), 0); + dpg = ovn_dp_group_find(&dp_groups, &lb_dps, hash); + if (!dpg) { + dpg = xzalloc(sizeof *dpg); + dpg->dp_group = ovn_sb_insert_logical_dp_group(ovnsb_txn, &lb_dps); + hmapx_clone(&dpg->map, &lb_dps); + hmap_insert(&dp_groups, &dpg->node, hash); + } + hmapx_destroy(&lb_dps); + + /* Update columns. */ sbrec_load_balancer_set_name(lb->slb, lb->nlb->name); sbrec_load_balancer_set_vips(lb->slb, &lb->nlb->vips); sbrec_load_balancer_set_protocol(lb->slb, lb->nlb->protocol); - sbrec_load_balancer_set_datapaths(lb->slb, lb_dps, lb->n_nb_ls); + sbrec_load_balancer_set_datapath_group(lb->slb, dpg->dp_group); sbrec_load_balancer_set_options(lb->slb, &options); + /* Clearing 'datapaths' column, since 'dp_group' is in use. */ + sbrec_load_balancer_set_datapaths(lb->slb, NULL, 0); smap_destroy(&options); - free(lb_dps); } + struct ovn_dp_group *dpg; + HMAP_FOR_EACH_POP (dpg, node, &dp_groups) { + hmapx_destroy(&dpg->map); + free(dpg); + } + hmap_destroy(&dp_groups); + /* Datapath_Binding.load_balancers is not used anymore, it's still in the * schema for compatibility reasons. Reset it to empty, just in case. */ @@ -14023,47 +14126,6 @@ build_lswitch_and_lrouter_flows(const struct hmap *datapaths, build_lswitch_flows(datapaths, lflows); } -struct ovn_dp_group { - struct hmapx map; - struct sbrec_logical_dp_group *dp_group; - struct hmap_node node; -}; - -static struct ovn_dp_group * -ovn_dp_group_find(const struct hmap *dp_groups, - const struct hmapx *od, uint32_t hash) -{ - struct ovn_dp_group *dpg; - - HMAP_FOR_EACH_WITH_HASH (dpg, node, hash, dp_groups) { - if (hmapx_equals(&dpg->map, od)) { - return dpg; - } - } - return NULL; -} - -static struct sbrec_logical_dp_group * -ovn_sb_insert_logical_dp_group(struct ovsdb_idl_txn *ovnsb_txn, - const struct hmapx *od) -{ - struct sbrec_logical_dp_group *dp_group; - const struct sbrec_datapath_binding **sb; - const struct hmapx_node *node; - int n = 0; - - sb = xmalloc(hmapx_count(od) * sizeof *sb); - HMAPX_FOR_EACH (node, od) { - sb[n++] = ((struct ovn_datapath *) node->data)->sb; - } - dp_group = sbrec_logical_dp_group_insert(ovnsb_txn); - sbrec_logical_dp_group_set_datapaths( - dp_group, (struct sbrec_datapath_binding **) sb, n); - free(sb); - - return dp_group; -} - static void ovn_sb_set_lflow_logical_dp_group( struct ovsdb_idl_txn *ovnsb_txn, diff --git a/ovn-sb.ovsschema b/ovn-sb.ovsschema index 3b78ea6f6d..8770fc01dd 100644 --- a/ovn-sb.ovsschema +++ b/ovn-sb.ovsschema @@ -1,7 +1,7 @@ { "name": "OVN_Southbound", - "version": "20.23.0", - "cksum": "4045988377 28575", + "version": "20.24.0", + "cksum": "3074645903 28786", "tables": { "SB_Global": { "columns": { @@ -505,6 +505,10 @@ "type": {"key": {"type": "uuid", "refTable": "Datapath_Binding"}, "min": 0, "max": "unlimited"}}, + "datapath_group": + {"type": {"key": {"type": "uuid", + "refTable": "Logical_DP_Group"}, + "min": 0, "max": 1}}, "options": { "type": {"key": "string", "value": "string", diff --git a/ovn-sb.xml b/ovn-sb.xml index 49e851e2a2..bdc6f819bf 100644 --- a/ovn-sb.xml +++ b/ovn-sb.xml @@ -4589,6 +4589,11 @@ tcp.flags = RST; Datapaths to which this load balancer applies to. + + The group of datapaths to which this load balancer applies to. This + means that the same load balancer applies to all datapaths in a group. + + IP to be used as source IP for packets that have been hair-pinned after diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at index 773904f95e..a5f3943659 100644 --- a/tests/ovn-northd.at +++ b/tests/ovn-northd.at @@ -2477,13 +2477,23 @@ lbg0_uuid=$(fetch_column sb:load_balancer _uuid name=lbg0) echo echo "__file__:__line__: Check that SB lb0 has sw0 in datapaths column." -check_column "$sw0_sb_uuid" sb:load_balancer datapaths name=lb0 +lb0_dp_group=$(fetch_column sb:load_balancer datapath_group name=lb0) +AT_CHECK_UNQUOTED([ovn-sbctl --bare --columns _uuid,datapaths find Logical_DP_Group dnl + | grep -A1 $lb0_dp_group | tail -1], [0], [dnl +$sw0_sb_uuid +]) + check_column "" sb:datapath_binding load_balancers external_ids:name=sw0 echo echo "__file__:__line__: Check that SB lbg0 has sw0 in datapaths column." -check_column "$sw0_sb_uuid" sb:load_balancer datapaths name=lbg0 +lbg0_dp_group=$(fetch_column sb:load_balancer datapath_group name=lbg0) +AT_CHECK_UNQUOTED([ovn-sbctl --bare --columns _uuid,datapaths find Logical_DP_Group dnl + | grep -A1 $lbg0_dp_group | tail -1], [0], [dnl +$sw0_sb_uuid +]) + check_column "" sb:datapath_binding load_balancers external_ids:name=sw0 check ovn-nbctl --wait=sb set load_balancer lb0 vips:"10.0.0.20\:90"="20.0.0.4:8080,30.0.0.4:8080" @@ -2507,23 +2517,43 @@ check ovn-nbctl --wait=sb lr-lb-add lr0 lb0 echo echo "__file__:__line__: Check that SB lb0 has only sw0 in datapaths column." -check_column "$sw0_sb_uuid" sb:load_balancer datapaths name=lb0 +lb0_dp_group=$(fetch_column sb:load_balancer datapath_group name=lb0) +AT_CHECK_UNQUOTED([ovn-sbctl --bare --columns _uuid,datapaths find Logical_DP_Group dnl + | grep -A1 $lb0_dp_group | tail -1], [0], [dnl +$sw0_sb_uuid +]) echo echo "__file__:__line__: Check that SB lbg0 has only sw0 in datapaths column." -check_column "$sw0_sb_uuid" sb:load_balancer datapaths name=lbg0 +lbg0_dp_group=$(fetch_column sb:load_balancer datapath_group name=lbg0) +AT_CHECK_UNQUOTED([ovn-sbctl --bare --columns _uuid,datapaths find Logical_DP_Group dnl + | grep -A1 $lbg0_dp_group | tail -1], [0], [dnl +$sw0_sb_uuid +]) check ovn-nbctl ls-add sw1 -- add logical_switch sw1 load_balancer_group $lbg check ovn-nbctl --wait=sb ls-lb-add sw1 lb0 sw1_sb_uuid=$(fetch_column datapath_binding _uuid external_ids:name=sw1) +echo "$sw0_sb_uuid" > sw_sb_uuids +echo "$sw1_sb_uuid" >> sw_sb_uuids + echo echo "__file__:__line__: Check that SB lb0 has sw0 and sw1 in datapaths column." -check_column "$sw0_sb_uuid $sw1_sb_uuid" sb:load_balancer datapaths name=lb0 +lb0_dp_group=$(fetch_column sb:load_balancer datapath_group name=lb0) +AT_CHECK_UNQUOTED([ovn-sbctl --bare --columns _uuid,datapaths find Logical_DP_Group dnl + | grep -A1 $lb0_dp_group | tail -1 | tr ' ' '\n' | sort], [0], [dnl +$(cat sw_sb_uuids | sort) +]) echo echo "__file__:__line__: Check that SB lbg0 has sw0 and sw1 in datapaths column." -check_column "$sw0_sb_uuid $sw1_sb_uuid" sb:load_balancer datapaths name=lbg0 +lbg0_dp_group=$(fetch_column sb:load_balancer datapath_group name=lbg0) +AT_CHECK_UNQUOTED([ovn-sbctl --bare --columns _uuid,datapaths find Logical_DP_Group dnl + | grep -A1 $lbg0_dp_group | tail -1 | tr ' ' '\n' | sort], [0], [dnl +$(cat sw_sb_uuids | sort) +]) + check_column "" sb:datapath_binding load_balancers external_ids:name=sw1 check ovn-nbctl --wait=sb lb-add lb1 10.0.0.30:80 20.0.0.50:8080 udp @@ -2550,18 +2580,26 @@ echo "__file__:__line__: Check that SB lbg1 has vips and protocol columns are se check_column "20.0.0.30:80=20.0.0.50:8080 udp" sb:load_balancer vips,protocol name=lbg1 lb1_uuid=$(fetch_column sb:load_balancer _uuid name=lb1) +lb1_dp_group=$(fetch_column sb:load_balancer datapath_group name=lb1) echo echo "__file__:__line__: Check that SB lb1 has sw1 in datapaths column." -check_column "$sw1_sb_uuid" sb:load_balancer datapaths name=lb1 +AT_CHECK_UNQUOTED([ovn-sbctl --bare --columns _uuid,datapaths find Logical_DP_Group dnl + | grep -A1 $lb1_dp_group | tail -1], [0], [dnl +$sw1_sb_uuid +]) lbg1_uuid=$(fetch_column sb:load_balancer _uuid name=lbg1) +lbg1_dp_group=$(fetch_column sb:load_balancer datapath_group name=lbg1) echo echo "__file__:__line__: Check that SB lbg1 has sw0 and sw1 in datapaths column." -check_column "$sw0_sb_uuid $sw1_sb_uuid" sb:load_balancer datapaths name=lbg1 +AT_CHECK_UNQUOTED([ovn-sbctl --bare --columns _uuid,datapaths find Logical_DP_Group dnl + | grep -A1 $lbg1_dp_group | tail -1 | tr ' ' '\n' | sort], [0], [dnl +$(cat sw_sb_uuids | sort) +]) echo echo "__file__:__line__: check that datapath sw1 has no entry in the load_balancers column." @@ -5432,9 +5470,9 @@ ovn_start check ovn-nbctl ls-add ls -- lb-add lb1 10.0.0.1:80 10.0.0.2:80 -- ls-lb-add ls lb1 check ovn-nbctl --wait=sb sync -dps=$(fetch_column Load_Balancer datapaths) +dps=$(fetch_column Load_Balancer datapath_group) nlb=$(fetch_column nb:Load_Balancer _uuid) -AT_CHECK([ovn-sbctl create Load_Balancer name=lb1 datapaths="$dps" external_ids="lb_id=$nlb"], [0], [ignore]) +AT_CHECK([ovn-sbctl create Load_Balancer name=lb1 datapath_group="$dps" external_ids="lb_id=$nlb"], [0], [ignore]) check ovn-nbctl --wait=sb sync check_row_count Load_Balancer 1 diff --git a/utilities/ovn-sbctl.c b/utilities/ovn-sbctl.c index b008b5d0b2..e35556d34a 100644 --- a/utilities/ovn-sbctl.c +++ b/utilities/ovn-sbctl.c @@ -335,6 +335,7 @@ pre_get_info(struct ctl_context *ctx) ovsdb_idl_add_column(ctx->idl, &sbrec_mac_binding_col_mac); ovsdb_idl_add_column(ctx->idl, &sbrec_load_balancer_col_datapaths); + ovsdb_idl_add_column(ctx->idl, &sbrec_load_balancer_col_datapath_group); ovsdb_idl_add_column(ctx->idl, &sbrec_load_balancer_col_vips); ovsdb_idl_add_column(ctx->idl, &sbrec_load_balancer_col_name); ovsdb_idl_add_column(ctx->idl, &sbrec_load_balancer_col_protocol); @@ -826,6 +827,21 @@ cmd_lflow_list_chassis(struct ctl_context *ctx, struct vconn *vconn, } } +static bool +datapath_group_contains_datapath(const struct sbrec_logical_dp_group *g, + const struct sbrec_datapath_binding *dp) +{ + if (!g || !dp) { + return false; + } + for (size_t i = 0; i < g->n_datapaths; i++) { + if (g->datapaths[i] == dp) { + return true; + } + } + return false; +} + static void cmd_lflow_list_load_balancers(struct ctl_context *ctx, struct vconn *vconn, const struct sbrec_datapath_binding *datapath, @@ -843,6 +859,10 @@ cmd_lflow_list_load_balancers(struct ctl_context *ctx, struct vconn *vconn, break; } } + if (lb->datapath_group && !dp_found) { + dp_found = datapath_group_contains_datapath(lb->datapath_group, + datapath); + } if (!dp_found) { continue; } @@ -861,6 +881,11 @@ cmd_lflow_list_load_balancers(struct ctl_context *ctx, struct vconn *vconn, print_vflow_datapath_name(lb->datapaths[i], true, &ctx->output); } + for (size_t i = 0; lb->datapath_group + && i < lb->datapath_group->n_datapaths; i++) { + print_vflow_datapath_name(lb->datapath_group->datapaths[i], + true, &ctx->output); + } } ds_put_cstr(&ctx->output, "\n vips:\n"); @@ -879,21 +904,6 @@ cmd_lflow_list_load_balancers(struct ctl_context *ctx, struct vconn *vconn, } } -static bool -datapath_group_contains_datapath(const struct sbrec_logical_dp_group *g, - const struct sbrec_datapath_binding *dp) -{ - if (!g || !dp) { - return false; - } - for (size_t i = 0; i < g->n_datapaths; i++) { - if (g->datapaths[i] == dp) { - return true; - } - } - return false; -} - static void sbctl_lflow_add(struct sbctl_lflow **lflows, size_t *n_flows, size_t *n_capacity,