[{"id":3681502,"web_url":"http://patchwork.ozlabs.org/comment/3681502/","msgid":"<0da010a6-d6b2-482f-b0f3-30b289159081@redhat.com>","list_archive_url":null,"date":"2026-04-23T14:40:36","subject":"Re: [ovs-dev] [PATCH ovn 2/3] ic: Add transit switch port.","submitter":{"id":76591,"url":"http://patchwork.ozlabs.org/api/people/76591/","name":"Dumitru Ceara","email":"dceara@redhat.com"},"content":"On 4/21/26 12:14 PM, Mairtin O'Loingsigh via dev wrote:\n> Enable the creation and management of transit switch ports, when\n> created, these ports are available across multiple AZs and may share\n> port binding depending on configuration.\n> \n> Commands to add, set address and delete port\n>     ovn-ic-nbctl tsp-add ts0 ts0-p0 chassis=chassis\n>     ovn-ic-nbctl tsp-set-addr ts0-p0 \"00:11:22:11:22:34\n>         192.168.10.11/24\"\n>     ovn-ic-nbctl tsp-del ts0-p0\n> \n> Signed-off-by: Mairtin O'Loingsigh <moloings@redhat.com>\n> ---\n\nHi Mairtin,\n\nThanks for the patch!  I'm a bit confused about how this should work,\nplease see my comment in the ovn-ic-nbctl section below.\n\nThere are some more other things listed inline.\n\n>  ic/ovn-ic.c              | 223 +++++++++++++++++++++++++++++++++++++--\n>  lib/ovn-util.c           |  44 ++++++++\n>  lib/ovn-util.h           |   4 +\n>  utilities/ovn-ic-nbctl.c | 201 ++++++++++++++++++++++++++++++++++-\n>  utilities/ovn-nbctl.c    |  52 +--------\n>  5 files changed, 468 insertions(+), 56 deletions(-)\n> \n> diff --git a/ic/ovn-ic.c b/ic/ovn-ic.c\n> index ba9490658..f0312e71d 100644\n> --- a/ic/ovn-ic.c\n> +++ b/ic/ovn-ic.c\n> @@ -631,6 +631,33 @@ find_tr_in_nb(struct ic_context *ctx, char *tr_name)\n>      return NULL;\n>  }\n>  \n> +static const struct nbrec_logical_router *\n> +find_router_by_port(struct ic_context *ctx, const char *port_name)\n> +{\n> +    const struct nbrec_logical_router_port *key =\n> +        nbrec_logical_router_port_index_init_row(ctx->nbrec_lrp_by_name);\n> +    nbrec_logical_router_port_index_set_name(key, port_name);\n> +\n> +    const struct nbrec_logical_router *lr;\n> +    const struct nbrec_logical_router_port *lrp;\n> +    bool found = false;\n> +    NBREC_LOGICAL_ROUTER_FOR_EACH (lr, ctx->ovnnb_idl) {\n\nWe're not using the key, we're doing a full table traversal instead.\nYou'd need _FOR_EACH_EQUAL().\n\n> +        for (size_t i = 0; i < lr->n_ports; i++) {\n> +            lrp = lr->ports[i];\n> +            if (!strcmp(lrp->name, port_name)) {\n> +                found = true;\n> +                break;\n\nWe only break the inner loop, lr will be overwritten in the next\niteration of the outer loop.  We need to (destroy the index and) return\nhere.\n\n> +            }\n> +        }\n> +    }\n> +\n> +    nbrec_logical_router_port_index_destroy_row(key);\n> +    if (found) {\n> +        return lr;\n> +    }\n> +    return NULL;\n> +}\n> +\n>  static const struct sbrec_port_binding *\n>  find_sb_pb_by_name(struct ovsdb_idl_index *sbrec_port_binding_by_name,\n>                     const char *name)\n> @@ -733,6 +760,29 @@ get_lp_address_for_sb_pb(struct ic_context *ctx,\n>      return peer->n_mac ? *peer->mac : NULL;\n>  }\n>  \n> +static const char *\n> +get_lp_address_for_tr_pb(struct ic_context *ctx,\n> +                         struct icnbrec_transit_switch_port *tsp)\n> +{\n> +    const struct nbrec_logical_switch_port *nb_lsp;\n> +\n> +    nb_lsp = get_lsp_by_ts_port_name(ctx, tsp->name);\n\nCan this ever return NULL?\n\n> +    if (!strcmp(nb_lsp->type, \"switch\")) {\n> +        /* Switches always have implicit \"unknown\" address, and IC-SB port\n> +         * binding can only have one address specified. */\n> +        return \"unknown\";\n> +    }\n> +\n> +    if (!tsp->option) {\n> +        return NULL;\n> +    }\n> +\n> +    const struct sbrec_port_binding *peer =\n> +        find_sb_pb_by_name(ctx->sbrec_port_binding_by_name, tsp->option);\n> +\n\nIn other places in ovn-ic.c we assume find_sb_pb_by_name() can return\nNULL.  We should check here too I guess.\n\n> +    return peer->n_mac ? *peer->mac : NULL;\n> +}\n> +\n>  static const struct sbrec_chassis *\n>  find_sb_chassis(struct ic_context *ctx, const char *name)\n>  {\n> @@ -793,8 +843,8 @@ get_router_uuid_by_sb_pb(struct ic_context *ctx,\n>  }\n>  \n>  static void\n> -update_isb_pb_external_ids(struct ic_context *ctx,\n> -                           const struct sbrec_port_binding *sb_pb,\n> +update_isb_pb_external_ids(struct ic_context *ctx OVS_UNUSED,\n> +                           const struct sbrec_port_binding *sb_pb OVS_UNUSED,\n\nBoth ctx and sb_pb are actually used, we shouldn't add OVS_UNUSED.\n\n>                             const struct icsbrec_port_binding *isb_pb)\n>  {\n>      struct uuid lr_uuid;\n> @@ -864,6 +914,52 @@ sync_local_port(struct ic_context *ctx,\n>      /* Sync back tunnel key from ISB to NB */\n>      sync_lsp_tnl_key(lsp, isb_pb->tunnel_key);\n>  }\n\nNit: we need an empty line here.\n\n> +/* For each local port:\n> + *   - Sync from ISB to NB.\n> + *   - Sync from ISB to SB.\n> + */\n> +static void\n> +sync_switch_port(struct ic_context *ctx OVS_UNUSED,\n\nSame comment about OVS_UNUSED.\n\n> +                 struct icnbrec_transit_switch_port *tsp,\n> +                 const struct icsbrec_port_binding *isb_pb,\n> +                 const struct nbrec_logical_switch_port *lsp,\n> +                 const struct sbrec_port_binding *sb_pb)\n> +{\n> +    const char *address = get_lp_address_for_tr_pb(ctx, tsp);\n> +    if (!address) {\n> +        VLOG_DBG(\"Can't get router/switch port address for logical\"\n> +                 \" switch port %s\", lsp->name);\n\nNit: I'd put the space before \"switch\" on the previous line.\n\n> +        if (isb_pb->address[0]) {\n> +            icsbrec_port_binding_set_address(isb_pb, \"\");\n> +        }\n> +    } else {\n> +        if (strcmp(address, isb_pb->address)) {\n> +            icsbrec_port_binding_set_address(isb_pb, address);\n> +        }\n> +    }\n> +\n> +    /* Sync gateway from SB to ISB */\n> +    const struct sbrec_port_binding *crp = find_crp_for_sb_pb(ctx, sb_pb);\n> +    if (crp && crp->chassis) {\n> +        if (strcmp(crp->chassis->name, isb_pb->gateway)) {\n> +            icsbrec_port_binding_set_gateway(isb_pb, crp->chassis->name);\n> +        }\n> +    } else if (!strcmp(tsp->type, \"switch\") && sb_pb->chassis) {\n> +        if (strcmp(sb_pb->chassis->name, isb_pb->gateway)) {\n> +            icsbrec_port_binding_set_gateway(isb_pb, sb_pb->chassis->name);\n> +        }\n> +    } else {\n> +        if (isb_pb->gateway[0]) {\n> +            icsbrec_port_binding_set_gateway(isb_pb, \"\");\n> +        }\n> +    }\n> +\n> +    /* Sync external_ids:router-id to ISB */\n> +    update_isb_pb_external_ids(ctx, sb_pb, isb_pb);\n> +\n> +    /* Sync back tunnel key from ISB to NB */\n> +    sync_lsp_tnl_key(lsp, isb_pb->tunnel_key);\n> +}\n>  \n>  /* For each remote port:\n>   *   - Sync from ISB to NB\n> @@ -991,6 +1087,21 @@ allocate_port_key(struct hmap *pb_tnlids)\n>                                1, (1u << 15) - 1, &hint);\n>  }\n>  \n> +static void\n> +set_isb_pb_peer(struct ic_context *ctx,\n> +                const struct icnbrec_transit_switch_port *tsp,\n> +                const struct icsbrec_port_binding *isb_pb)\n> +{\n> +    const struct nbrec_logical_router *lr;\n> +    lr = find_router_by_port(ctx, tsp->option);\n> +    if (lr) {\n> +        char *uuid_s = xasprintf(UUID_FMT, UUID_ARGS(&lr->header_.uuid));\n> +        icsbrec_port_binding_update_external_ids_setkey(isb_pb, \"router-id\",\n> +                                                        uuid_s);\n> +        free(uuid_s);\n> +    }\n> +}\n> +\n>  static const struct icsbrec_port_binding *\n>  create_isb_pb(struct ic_context *ctx, const char *logical_port,\n>                const struct icsbrec_availability_zone *az, const char *ts_name,\n> @@ -1010,6 +1121,7 @@ create_isb_pb(struct ic_context *ctx, const char *logical_port,\n>      icsbrec_port_binding_set_tunnel_key(isb_pb, pb_tnl_key);\n>      icsbrec_port_binding_set_nb_ic_uuid(isb_pb, nb_ic_uuid, 1);\n>      icsbrec_port_binding_set_type(isb_pb, type);\n> +\n>      return isb_pb;\n>  }\n>  \n> @@ -1028,7 +1140,7 @@ get_lrp_by_lrp_name(struct ic_context *ctx, const char *lrp_name)\n>  }\n>  \n>  static bool\n> -trp_is_remote(struct ic_context *ctx, const char *chassis_name)\n> +chassis_is_remote(struct ic_context *ctx, const char *chassis_name)\n>  {\n>      if (chassis_name) {\n>          const struct sbrec_chassis *chassis =\n> @@ -1057,11 +1169,41 @@ lrp_create(struct ic_context *ctx, const struct nbrec_logical_router *lr,\n>      return lrp;\n>  }\n>  \n> +static struct nbrec_logical_switch_port *\n> +lsp_create(struct ic_context *ctx, const struct nbrec_logical_switch *lr,\n\nMaybe s/lr/ls/ ?\n\n> +           const struct icsbrec_port_binding *isb_pb,\n> +           const struct icnbrec_transit_switch_port *tsp)\n> +{\n> +    bool router_port = !strcmp(tsp->type, \"router\");\n> +    const char *type = router_port ? \"router\" : \"localnet\";\n> +    const char *option_name = router_port ? \"router-port\" : \"network_name\";\n> +    struct smap options = SMAP_CONST1(&options, option_name, tsp->option);\n> +\n> +    struct nbrec_logical_switch_port *lsp =\n> +        nbrec_logical_switch_port_insert(ctx->ovnnb_txn);\n> +    nbrec_logical_switch_port_set_name(lsp, tsp->name);\n> +\n> +    nbrec_logical_switch_port_update_options_setkey(lsp, \"interconn-ts\",\n> +                                                    tsp->name);\n> +    nbrec_logical_switch_port_set_type(lsp, type);\n> +\n> +    nbrec_logical_switch_port_set_options(lsp, &options);\n\nDoesn't this overwrite \"options:interconn-ts\" we set just above?\n\n> +\n> +    nbrec_logical_switch_update_ports_addvalue(lr, lsp);\n> +\n> +    char *uuid_s = xasprintf(UUID_FMT, UUID_ARGS(&lr->header_.uuid));\n> +    icsbrec_port_binding_update_external_ids_setkey(isb_pb, \"router-id\",\n> +                                                    uuid_s);\n\nHmm, \"router-id\"?  This is a port binding corresponding to a switch\nport, and (as mentioned above) \"lr\" is actually a switch.\n\n> +    free(uuid_s);\n> +    return lsp;\n> +}\n> +\n>  static void\n>  sync_ts_isb_pb(struct ic_context *ctx, const struct sbrec_port_binding *sb_pb,\n>                 const struct icsbrec_port_binding *isb_pb)\n>  {\n>      const char *address = get_lp_address_for_sb_pb(ctx, sb_pb);\n> +\n>      if (address) {\n>          icsbrec_port_binding_set_address(isb_pb, address);\n>      }\n> @@ -1118,7 +1260,6 @@ port_binding_run(struct ic_context *ctx)\n>      }\n>      icsbrec_port_binding_index_destroy_row(isb_pb_key);\n>  \n> -    const struct sbrec_port_binding *sb_pb;\n>      const struct icnbrec_transit_switch *ts;\n>      ICNBREC_TRANSIT_SWITCH_FOR_EACH (ts, ctx->ovninb_idl) {\n>          const struct nbrec_logical_switch *ls = find_ts_in_nb(ctx, ts->name);\n> @@ -1126,9 +1267,20 @@ port_binding_run(struct ic_context *ctx)\n>              VLOG_DBG(\"Transit switch %s not found in NB.\", ts->name);\n>              continue;\n>          }\n> +        struct shash nb_ports = SHASH_INITIALIZER(&nb_ports);\n> +        struct shash old_nb_ports = SHASH_INITIALIZER(&old_nb_ports);\n>          struct shash local_pbs = SHASH_INITIALIZER(&local_pbs);\n>          struct shash remote_pbs = SHASH_INITIALIZER(&remote_pbs);\n>  \n> +        for (size_t i = 0; i < ls->n_ports; i++) {\n> +            const struct nbrec_logical_switch_port *lsp = ls->ports[i];\n> +            if (smap_get_def(&lsp->options, \"interconn-ts\", NULL)) {\n\nThis can be smap_get().\n\n> +                shash_add(&nb_ports, lsp->name, lsp);\n> +            } else {\n> +                shash_add(&old_nb_ports, lsp->name, lsp);\n> +            }\n> +        }\n> +\n>          isb_pb_key = icsbrec_port_binding_index_init_row(\n>              ctx->icsbrec_port_binding_by_ts);\n>          icsbrec_port_binding_index_set_transit_switch(isb_pb_key, ts->name);\n> @@ -1145,9 +1297,59 @@ port_binding_run(struct ic_context *ctx)\n>          }\n>          icsbrec_port_binding_index_destroy_row(isb_pb_key);\n>  \n> +        for (int i = 0; i < ts->n_ports; i++) {\n\nsize_t\n\n> +            struct icnbrec_transit_switch_port *tsp = ts->ports[i];\n> +            if (!chassis_is_remote(ctx, tsp->chassis)) {\n> +                isb_pb = shash_find_and_delete(&local_pbs, tsp->name);\n> +                if (!isb_pb) {\n> +                    isb_pb = create_isb_pb(ctx, tsp->name, ctx->runned_az,\n> +                                           ts->name, &ts->header_.uuid,\n> +                                           \"transit-switch-port\", &pb_tnlids);\n\nisb_pb can be NULL here, if we failed allocating keys.\n\n> +                    set_isb_pb_peer(ctx, tsp, isb_pb);\n> +                }\n> +\n> +                if (isb_pb->type) {\n> +                    icsbrec_port_binding_set_type(isb_pb,\n> +                                                  \"transit-switch-port\");\n> +                }\n> +\n> +                if (isb_pb->nb_ic_uuid) {\n> +                    icsbrec_port_binding_set_nb_ic_uuid(isb_pb,\n> +                                                        &ts->header_.uuid, 1);\n> +                }\n\nDidn't we just set ty[e/nb_ic_uuid in create_isb_pb() above (if this is\na new PB)?  Should we check first whether there's a difference?\n\n> +                const struct nbrec_logical_switch_port *lsp =\n> +                    shash_find_and_delete(&nb_ports, tsp->name);\n> +                if (!lsp) {\n> +                    lsp = lsp_create(ctx, ls, isb_pb, tsp);\n> +                }\n> +\n> +                const struct sbrec_port_binding *sb_pb;\n> +                sb_pb = find_lsp_in_sb(ctx, lsp);\n> +                if (sb_pb) {\n> +                    sync_switch_port(ctx, tsp, isb_pb, lsp, sb_pb);\n> +                }\n> +            } else {\n> +                /* Remote port sync */\n> +                isb_pb = shash_find_and_delete(&remote_pbs, tsp->name);\n> +                if (isb_pb) {\n> +                    const struct nbrec_logical_switch_port *lsp =\n> +                        shash_find_and_delete(&nb_ports, tsp->name);\n> +                    if (!lsp) {\n> +                        lsp = lsp_create(ctx, ls, isb_pb, tsp);\n> +                    }\n> +                    const struct sbrec_port_binding *sb_pb;\n> +                    sb_pb = find_lsp_in_sb(ctx, lsp);\n> +                    if (sb_pb) {\n> +                        sync_remote_port(ctx, isb_pb, lsp, sb_pb);\n> +                    }\n> +                }\n> +            }\n> +        }\n\nNit: I'd add an empty line.\n\n> +        /* Support legacy way of adding transit switch ports*/\n\nComments should end with dot.  Also missing space.\n\n> +        const struct sbrec_port_binding *sb_pb;\n>          const struct nbrec_logical_switch_port *lsp;\n> -        for (int i = 0; i < ls->n_ports; i++) {\n\nsize_t\n\n> -            lsp = ls->ports[i];\n> +        SHASH_FOR_EACH (node, &old_nb_ports) {\n> +            lsp = node->data;\n>  \n>              if (!strcmp(lsp->type, \"router\")\n>                  || !strcmp(lsp->type, \"switch\")) {\n> @@ -1193,6 +1395,11 @@ port_binding_run(struct ic_context *ctx)\n>              }\n>          }\n>  \n> +        SHASH_FOR_EACH (node, &nb_ports) {\n> +            nbrec_logical_switch_port_delete(node->data);\n> +            nbrec_logical_switch_update_ports_delvalue(ls, node->data);\n> +        }\n> +\n>          /* Delete extra port-binding from ISB */\n>          SHASH_FOR_EACH (node, &local_pbs) {\n>              icsbrec_port_binding_delete(node->data);\n> @@ -1203,8 +1410,10 @@ port_binding_run(struct ic_context *ctx)\n>              create_nb_lsp(ctx, node->data, ls);\n>          }\n>  \n> +        shash_destroy(&nb_ports);\n>          shash_destroy(&local_pbs);\n>          shash_destroy(&remote_pbs);\n> +        shash_destroy(&old_nb_ports);\n>      }\n>  \n>      SHASH_FOR_EACH (node, &switch_all_local_pbs) {\n> @@ -1250,7 +1459,7 @@ port_binding_run(struct ic_context *ctx)\n>          for (size_t i = 0; i < tr->n_ports; i++) {\n>              const struct icnbrec_transit_router_port *trp = tr->ports[i];\n>  \n> -            if (trp_is_remote(ctx, trp->chassis)) {\n> +            if (chassis_is_remote(ctx, trp->chassis)) {\n>                  isb_pb = shash_find_and_delete(&remote_pbs, trp->name);\n>              } else {\n>                  isb_pb = shash_find_and_delete(&local_pbs, trp->name);\n> diff --git a/lib/ovn-util.c b/lib/ovn-util.c\n> index fb02825ac..d41ab366f 100644\n> --- a/lib/ovn-util.c\n> +++ b/lib/ovn-util.c\n> @@ -1869,3 +1869,47 @@ eth_addr_parse_masked(const char *s, struct eth_addr *ea, unsigned int *plen)\n>      *ea = eth_addr_zero;\n>      return false;\n>  }\n> +\n> +bool\n> +sp_contains_duplicate_ip(struct lport_addresses *laddrs1,\n> +                          struct lport_addresses *laddrs2,\n> +                          char *port_name,\n> +                          char **error_str)\n> +{\n> +    for (size_t i = 0; i < laddrs1->n_ipv4_addrs; i++) {\n> +        for (size_t j = 0; j < laddrs2->n_ipv4_addrs; j++) {\n> +            if (laddrs1->ipv4_addrs[i].addr == laddrs2->ipv4_addrs[j].addr) {\n> +                if (error_str) {\n> +                    *error_str = xasprintf(\"duplicate IPv4 address '%s' \"\n> +                                           \"found on logical switch \"\n> +                                           \"port '%s'\",\n> +                                           laddrs1->ipv4_addrs[i].addr_s,\n> +                                           port_name);\n> +                }\n> +                return true;\n> +            }\n> +        }\n> +    }\n> +\n> +    for (size_t i = 0; i < laddrs1->n_ipv6_addrs; i++) {\n> +        for (size_t j = 0; j < laddrs2->n_ipv6_addrs; j++) {\n> +            if (IN6_ARE_ADDR_EQUAL(&laddrs1->ipv6_addrs[i].addr,\n> +                                   &laddrs2->ipv6_addrs[j].addr)) {\n> +                if (error_str) {\n> +                    *error_str = xasprintf(\"duplicate IPv6 address \"\n> +                                           \"'%s' found on logical \"\n> +                                           \"switch port '%s'\",\n> +                                           laddrs1->ipv6_addrs[i].addr_s,\n> +                                           port_name);\n> +                }\n> +                return true;\n> +            }\n> +        }\n> +    }\n> +\n> +    if (error_str) {\n> +        *error_str = NULL;\n> +    }\n> +\n> +    return false;\n> +}\n> diff --git a/lib/ovn-util.h b/lib/ovn-util.h\n> index bcb344de4..df78c7342 100644\n> --- a/lib/ovn-util.h\n> +++ b/lib/ovn-util.h\n> @@ -790,6 +790,10 @@ char *normalize_ipv6_addr_str(const char *orig_addr);\n>  \n>  char *normalize_addr_str(const char *orig_addr);\n>  \n> +bool sp_contains_duplicate_ip(struct lport_addresses *laddrs1,\n> +                              struct lport_addresses *laddrs2, char *name,\n> +                              char **error_str);\n> +\n>  #define NEIGH_REDISTRIBUTE_MODES    \\\n>      NEIGH_REDISTRIBUTE_MODE(FDB, 0) \\\n>      NEIGH_REDISTRIBUTE_MODE(IP, 1)\n> diff --git a/utilities/ovn-ic-nbctl.c b/utilities/ovn-ic-nbctl.c\n> index 50e975283..1ba11ddf9 100644\n> --- a/utilities/ovn-ic-nbctl.c\n> +++ b/utilities/ovn-ic-nbctl.c\n> @@ -336,6 +336,9 @@ Transit switch commands:\\n\\\n>    ts-add SWITCH              create a transit switch named SWITCH\\n\\\n>    ts-del SWITCH              delete SWITCH\\n\\\n>    ts-list                    print all transit switches\\n\\\n> +  tsp-add SWITCH PORT        add a transit switch PORT\\n\\\n> +  tsp-set-addr PORT NETWORK  add a transit switch PORT\\n\\\n\nset a transit switch PORT address?\n\nFor LSPs (in ovn-nbctl) we support multiple addresses, a transit switch\nport (in the IC DB) should be able to map one to one to a NB switch\nport, so should we support multiple addresses?\n\nAlso, why NETWORK?\n\n> +  tsp-del PORT               delete a transit switch PORT\\n\\\n>  \\n\\\n>  Transit router commands:\\n\\\n>    tr-add ROUTER              create a transit router named ROUTER\\n\\\n> @@ -617,6 +620,38 @@ trp_by_name_or_uuid(struct ctl_context *ctx, const char *id, bool must_exist,\n>      return NULL;\n>  }\n>  \n> +static char *\n> +tsp_by_name_or_uuid(struct ctl_context *ctx, const char *id, bool must_exist,\n> +                    const struct icnbrec_transit_switch_port **tsp_p)\n> +{\n> +    const struct icnbrec_transit_switch_port *tsp = NULL;\n> +    *tsp_p = NULL;\n> +    struct uuid tsp_uuid;\n> +    bool is_uuid = uuid_from_string(&tsp_uuid, id);\n> +    if (is_uuid) {\n> +        tsp = icnbrec_transit_switch_port_get_for_uuid(ctx->idl, &tsp_uuid);\n> +    }\n> +\n> +    if (!tsp) {\n> +        const struct icnbrec_transit_switch_port *iter;\n> +\n> +        ICNBREC_TRANSIT_SWITCH_PORT_FOR_EACH (iter, ctx->idl) {\n> +            if (!strcmp(iter->name, id)) {\n> +                tsp = iter;\n> +                break;\n> +            }\n> +        }\n> +    }\n> +\n> +    if (!tsp && must_exist) {\n> +        return xasprintf(\"%s: switch port %s not found\", id,\n> +                         is_uuid ? \"UUID\" : \"name\");\n> +    }\n> +\n> +    *tsp_p = tsp;\n> +    return NULL;\n> +}\n> +\n>  static void\n>  ic_nbctl_tr_del(struct ctl_context *ctx)\n>  {\n> @@ -664,6 +699,34 @@ ic_nbctl_trp_del(struct ctl_context *ctx)\n>      icnbrec_transit_router_port_delete(trp);\n>  }\n>  \n> +static void\n> +ic_nbctl_tsp_del(struct ctl_context *ctx)\n> +{\n> +    bool must_exist = !shash_find(&ctx->options, \"--if-exists\");\n> +    const char *tsp_name = ctx->argv[1];\n> +    const struct icnbrec_transit_switch_port *tsp = NULL;\n> +\n> +    ctx->error = tsp_by_name_or_uuid(ctx, tsp_name, must_exist, &tsp);\n> +    if (ctx->error) {\n> +        return;\n> +    }\n> +\n> +    if (!tsp) {\n> +        return;\n> +    }\n> +\n> +    const struct icnbrec_transit_switch *ts = NULL;\n> +    char *ts_uuid = uuid_to_string(&tsp->ts_uuid);\n> +    ctx->error = ts_by_name_or_uuid(ctx, ts_uuid, true, &ts);\n> +    free(ts_uuid);\n> +    if (ctx->error) {\n> +        return;\n> +    }\n> +\n> +    icnbrec_transit_switch_update_ports_delvalue(ts, tsp);\n> +    icnbrec_transit_switch_port_delete(tsp);\n> +}\n> +\n>  static void\n>  ic_nbctl_tr_list(struct ctl_context *ctx)\n>  {\n> @@ -802,6 +865,137 @@ ic_nbctl_trp_add(struct ctl_context *ctx)\n>      icnbrec_transit_router_update_ports_addvalue(tr, trp);\n>  }\n>  \n> +static void\n> +ic_nbctl_tsp_add(struct ctl_context *ctx)\n> +{\n> +    bool may_exist = shash_find(&ctx->options, \"--may-exist\") != NULL;\n> +    const char *ts_name = ctx->argv[1];\n> +    const char *tsp_name = ctx->argv[2];\n> +    const struct icnbrec_transit_switch *ts;\n> +\n> +    ctx->error = ts_by_name_or_uuid(ctx, ts_name, true, &ts);\n> +    if (ctx->error) {\n> +        return;\n> +    }\n> +\n> +    const struct icnbrec_transit_switch_port *tsp;\n> +    ctx->error = tsp_by_name_or_uuid(ctx, tsp_name, false, &tsp);\n> +    if (ctx->error) {\n> +        return;\n> +    }\n> +\n> +    if (tsp) {\n> +        if (!may_exist) {\n> +            ctl_error(ctx, \"%s: a port with this name already exists\",\n> +                      tsp_name);\n> +            return;\n> +        }\n> +        return;\n> +    }\n> +\n> +    tsp = icnbrec_transit_switch_port_insert(ctx->txn);\n> +    icnbrec_transit_switch_port_set_name(tsp, tsp_name);\n> +    icnbrec_transit_switch_port_set_ts_uuid(tsp, ts->header_.uuid);\n> +\n> +    int n_settings = ctx->argc - 3;\n> +    char **settings = (char **) &ctx->argv[3];\n> +    for (int i = 0; i < n_settings; i++) {\n\nsize_t\n\n> +        ctx->error = ctl_set_column(\"Transit_Switch_Port\", &tsp->header_,\n> +                                    settings[i], ctx->symtab);\n> +        if (ctx->error) {\n> +            return;\n> +        }\n> +    }\n> +\n> +    icnbrec_transit_switch_update_ports_addvalue(ts, tsp);\n> +}\n> +\n> +static char *\n> +tsp_contains_duplicates(const struct icnbrec_transit_switch *ts,\n> +                        const struct icnbrec_transit_switch_port *tsp,\n> +                        const char *address)\n> +{\n> +    char *sub_error = NULL;\n> +    struct lport_addresses laddrs;\n> +    if (!extract_lsp_addresses(address, &laddrs)) {\n> +        return NULL;\n> +    }\n> +\n> +    for (size_t i = 0; i < ts->n_ports; i++) {\n> +        struct icnbrec_transit_switch_port *tsp_test = ts->ports[i];\n> +        if (tsp_test == tsp) {\n> +            continue;\n> +        }\n> +        for (size_t j = 0; j < tsp_test->n_addresses; j++) {\n> +            struct lport_addresses laddrs_test;\n> +            char *addr = tsp_test->addresses[j];\n> +            if (extract_lsp_addresses(addr, &laddrs_test)) {\n> +                bool has_duplicate =\n> +                    sp_contains_duplicate_ip(&laddrs, &laddrs_test,\n> +                                             tsp_test->name, &sub_error);\n> +                destroy_lport_addresses(&laddrs_test);\n> +                if (has_duplicate) {\n> +                    goto err_out;\n> +                }\n> +            }\n> +        }\n> +    }\n> +\n> +err_out: ;\n> +    char *error = NULL;\n> +    if (sub_error) {\n> +        error = xasprintf(\"Error on switch %s: %s\", ts->name, sub_error);\n> +        free(sub_error);\n> +    }\n> +    destroy_lport_addresses(&laddrs);\n> +    return error;\n> +}\n> +\n> +static void\n> +ic_nbctl_tsp_set_addr(struct ctl_context *ctx)\n> +{\n> +    const char *tsp_name = ctx->argv[1];\n> +\n> +    const struct icnbrec_transit_switch_port *tsp;\n> +    ctx->error = tsp_by_name_or_uuid(ctx, tsp_name, true, &tsp);\n> +    if (ctx->error) {\n> +        return;\n> +    }\n> +\n> +    int i;\n> +    for (i = 2; i < ctx->argc; i++) {\n> +        char ipv6_s[IPV6_SCAN_LEN + 1];\n> +        struct eth_addr ea;\n> +        ovs_be32 ip;\n> +\n> +        if (strcmp(ctx->argv[i], \"unknown\") && strcmp(ctx->argv[i], \"dynamic\")\n> +            && strcmp(ctx->argv[i], \"router\")\n> +            && !ovs_scan(ctx->argv[i], ETH_ADDR_SCAN_FMT,\n> +                         ETH_ADDR_SCAN_ARGS(ea))\n> +            && !ovs_scan(ctx->argv[i], \"dynamic \"IPV6_SCAN_FMT, ipv6_s)\n> +            && !ovs_scan(ctx->argv[i], \"dynamic \"IP_SCAN_FMT,\n> +                         IP_SCAN_ARGS(&ip))) {\n> +            ctl_error(ctx, \"%s: Invalid address format. See ovn-nb(5). \"\n> +                      \"Hint: An Ethernet address must be \"\n> +                      \"listed before an IP address, together as a single \"\n> +                      \"argument.\", ctx->argv[i]);\n> +            return;\n> +        }\n> +\n> +        const struct icnbrec_transit_switch *ts;\n> +        ts = icnbrec_transit_switch_get_for_uuid(ctx->idl, &tsp->ts_uuid);\n> +        ctx->error = tsp_contains_duplicates(ts, tsp, ctx->argv[i]);\n> +        if (ctx->error) {\n> +            ctl_error(ctx, \"%s\", ctx->error);\n\nThis is a use after free (ctx->error is first freed by ctl_error()\ninternally).  Can't we just remove this line and let the db-ctl-base\nengine handle the ctx->error?\n\n> +            return;\n> +        }\n> +    }\n> +\n> +    icnbrec_transit_switch_port_set_addresses(tsp,\n> +                                              (const char **) ctx->argv + 2,\n> +                                              ctx->argc - 2);\n> +}\n> +\n>  static void\n>  verify_connections(struct ctl_context *ctx)\n>  {\n> @@ -1317,7 +1511,13 @@ static const struct ctl_command_syntax ic_nbctl_commands[] = {\n>      { \"ts-add\", 1, 1, \"SWITCH\", NULL, ic_nbctl_ts_add, NULL, \"--may-exist\", RW },\n>      { \"ts-del\", 1, 1, \"SWITCH\", NULL, ic_nbctl_ts_del, NULL, \"--if-exists\", RW },\n>      { \"ts-list\", 0, 0, \"\", NULL, ic_nbctl_ts_list, NULL, \"\", RO },\n> +    { \"tsp-add\", 2, INT_MAX, \"SWITCH PORT COLUMN[:KEY]=VALUE]...\",\n> +        NULL, ic_nbctl_tsp_add, NULL, \"--may-exist\", RW },\n\nMaybe I misunderstand the goal of the patch series but if I do this in a\nsandbox:\n\n$ ovn-ic-nbctl ts-add ts -- tsp-add ts tsp1\n$ ovn-ic-sbctl show\navailability-zone az-1\n$ ovn-ic-sbctl list port_binding\n$ ovn-nbctl show\nswitch 1479b061-3cd3-42e5-b8f6-1c54021fd1ed (ts)\n\nShouldn't we have IC-SB/SB port bindings and NB logical switch ports for\ntsp1 too?\n\n> +    { \"tsp-set-addr\", 2, INT_MAX, \"PORT [ADDRESS]...\",\n> +        NULL, ic_nbctl_tsp_set_addr, NULL, \"\", RW },\n>  \n> +    { \"tsp-del\", 1, 1, \"PORT\", NULL, ic_nbctl_tsp_del, NULL, \"--if-exists\",\n> +        RW },\n>      /* transit router commands. */\n>      { \"tr-add\", 1, 1, \"ROUTER\", NULL, ic_nbctl_tr_add, NULL, \"--may-exist\",\n>          RW },\n> @@ -1329,7 +1529,6 @@ static const struct ctl_command_syntax ic_nbctl_commands[] = {\n>          NULL, ic_nbctl_trp_add, NULL, \"--may-exist\", RW },\n>      { \"trp-del\", 1, 1, \"PORT\", NULL, ic_nbctl_trp_del, NULL, \"--if-exists\",\n>          RW },\n> -\n>      /* Connection commands. */\n>      {\"get-connection\", 0, 0, \"\", pre_connection, cmd_get_connection, NULL, \"\",\n>          RO},\n> diff --git a/utilities/ovn-nbctl.c b/utilities/ovn-nbctl.c\n> index 0ef207272..56cfdf5b4 100644\n> --- a/utilities/ovn-nbctl.c\n> +++ b/utilities/ovn-nbctl.c\n> @@ -1482,50 +1482,6 @@ nbctl_pre_lsp_set_addresses(struct ctl_context *ctx)\n>                           &nbrec_logical_switch_port_col_dynamic_addresses);\n>  }\n>  \n> -static bool\n> -lsp_contains_duplicate_ip(struct lport_addresses *laddrs1,\n> -                          struct lport_addresses *laddrs2,\n> -                          const struct nbrec_logical_switch_port *lsp_test,\n> -                          char **error_str)\n> -{\n> -    for (size_t i = 0; i < laddrs1->n_ipv4_addrs; i++) {\n> -        for (size_t j = 0; j < laddrs2->n_ipv4_addrs; j++) {\n> -            if (laddrs1->ipv4_addrs[i].addr == laddrs2->ipv4_addrs[j].addr) {\n> -                if (error_str) {\n> -                    *error_str = xasprintf(\"duplicate IPv4 address '%s' \"\n> -                                           \"found on logical switch \"\n> -                                           \"port '%s'\",\n> -                                           laddrs1->ipv4_addrs[i].addr_s,\n> -                                           lsp_test->name);\n> -                }\n> -                return true;\n> -            }\n> -        }\n> -    }\n> -\n> -    for (size_t i = 0; i < laddrs1->n_ipv6_addrs; i++) {\n> -        for (size_t j = 0; j < laddrs2->n_ipv6_addrs; j++) {\n> -            if (IN6_ARE_ADDR_EQUAL(&laddrs1->ipv6_addrs[i].addr,\n> -                                   &laddrs2->ipv6_addrs[j].addr)) {\n> -                if (error_str) {\n> -                    *error_str = xasprintf(\"duplicate IPv6 address \"\n> -                                           \"'%s' found on logical \"\n> -                                           \"switch port '%s'\",\n> -                                           laddrs1->ipv6_addrs[i].addr_s,\n> -                                           lsp_test->name);\n> -                }\n> -                return true;\n> -            }\n> -        }\n> -    }\n> -\n> -    if (error_str) {\n> -        *error_str = NULL;\n> -    }\n> -\n> -    return false;\n> -}\n> -\n>  static char *\n>  lsp_contains_duplicates(const struct nbrec_logical_switch *ls,\n>                          const struct nbrec_logical_switch_port *lsp,\n> @@ -1550,8 +1506,8 @@ lsp_contains_duplicates(const struct nbrec_logical_switch *ls,\n>              }\n>              if (extract_lsp_addresses(addr, &laddrs_test)) {\n>                  bool has_duplicate =\n> -                    lsp_contains_duplicate_ip(&laddrs, &laddrs_test,\n> -                                              lsp_test, &sub_error);\n> +                    sp_contains_duplicate_ip(&laddrs, &laddrs_test,\n> +                                             lsp_test->name, &sub_error);\n>                  destroy_lport_addresses(&laddrs_test);\n>                  if (has_duplicate) {\n>                      goto err_out;\n> @@ -8835,8 +8791,8 @@ lsp_health_check_parse_target_address(\n>              goto cleanup;\n>          }\n>  \n> -        if (lsp_contains_duplicate_ip(&target_address,\n> -                                      &lsp_address, lsp, NULL)) {\n> +        if (sp_contains_duplicate_ip(&target_address,\n> +                                      &lsp_address, lsp->name, NULL)) {\n>              ip_found_on_port = true;\n>          }\n>  \n\nThanks,\nDumitru","headers":{"Return-Path":"<ovs-dev-bounces@openvswitch.org>","X-Original-To":["incoming@patchwork.ozlabs.org","dev@openvswitch.org"],"Delivered-To":["patchwork-incoming@legolas.ozlabs.org","ovs-dev@lists.linuxfoundation.org"],"Authentication-Results":["legolas.ozlabs.org;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256\n header.s=mimecast20190719 header.b=XazGa/uq;\n\tdkim-atps=neutral","legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org\n (client-ip=2605:bc80:3010::137; helo=smtp4.osuosl.org;\n envelope-from=ovs-dev-bounces@openvswitch.org; receiver=patchwork.ozlabs.org)","smtp4.osuosl.org;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key,\n unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256\n header.s=mimecast20190719 header.b=XazGa/uq","smtp2.osuosl.org; dmarc=pass (p=quarantine dis=none)\n header.from=redhat.com","smtp2.osuosl.org; dkim=pass (1024-bit key,\n unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256\n header.s=mimecast20190719 header.b=XazGa/uq"],"Received":["from smtp4.osuosl.org (smtp4.osuosl.org [IPv6:2605:bc80:3010::137])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4g1f0f49X1z1yD5\n\tfor <incoming@patchwork.ozlabs.org>; Fri, 24 Apr 2026 00:41:04 +1000 (AEST)","from localhost (localhost [127.0.0.1])\n\tby smtp4.osuosl.org (Postfix) with ESMTP id BAC9A4100F;\n\tThu, 23 Apr 2026 14:41:02 +0000 (UTC)","from smtp4.osuosl.org ([127.0.0.1])\n by localhost (smtp4.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP\n id xCgr-nqYseOG; Thu, 23 Apr 2026 14:40:58 +0000 (UTC)","from lists.linuxfoundation.org (lf-lists.osuosl.org\n [IPv6:2605:bc80:3010:104::8cd3:938])\n\tby smtp4.osuosl.org (Postfix) with ESMTPS id BDFA941006;\n\tThu, 23 Apr 2026 14:40:58 +0000 (UTC)","from lf-lists.osuosl.org (localhost [127.0.0.1])\n\tby lists.linuxfoundation.org (Postfix) with ESMTP id 90F82C058E;\n\tThu, 23 Apr 2026 14:40:58 +0000 (UTC)","from smtp2.osuosl.org (smtp2.osuosl.org [IPv6:2605:bc80:3010::133])\n by lists.linuxfoundation.org (Postfix) with ESMTP id F16F9C058D\n for <dev@openvswitch.org>; Thu, 23 Apr 2026 14:40:56 +0000 (UTC)","from localhost (localhost [127.0.0.1])\n by smtp2.osuosl.org (Postfix) with ESMTP id DDBC540783\n for <dev@openvswitch.org>; Thu, 23 Apr 2026 14:40:56 +0000 (UTC)","from smtp2.osuosl.org ([127.0.0.1])\n by localhost (smtp2.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP\n id n0xZ2m56BW6m for <dev@openvswitch.org>;\n Thu, 23 Apr 2026 14:40:51 +0000 (UTC)","from us-smtp-delivery-124.mimecast.com\n (us-smtp-delivery-124.mimecast.com [170.10.133.124])\n by smtp2.osuosl.org (Postfix) with ESMTPS id 3D96C400BC\n for <dev@openvswitch.org>; Thu, 23 Apr 2026 14:40:50 +0000 (UTC)","from mail-dl1-f69.google.com (mail-dl1-f69.google.com\n [74.125.82.69]) by relay.mimecast.com with ESMTP with STARTTLS\n (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id\n us-mta-670-oHKHxFtcN6eYs3mH0ykGWA-1; Thu, 23 Apr 2026 10:40:47 -0400","by mail-dl1-f69.google.com with SMTP id\n a92af1059eb24-1275c6fc58aso9528450c88.0\n for <dev@openvswitch.org>; Thu, 23 Apr 2026 07:40:47 -0700 (PDT)","from ?IPV6:2001:1c05:1417:d800:d1ef:9817:2a26:625d?\n (2001-1c05-1417-d800-d1ef-9817-2a26-625d.cable.dynamic.v6.ziggo.nl.\n [2001:1c05:1417:d800:d1ef:9817:2a26:625d])\n by smtp.gmail.com with ESMTPSA id\n a92af1059eb24-12c74a185a8sm28724522c88.9.2026.04.23.07.40.40\n (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128);\n Thu, 23 Apr 2026 07:40:43 -0700 (PDT)"],"X-Virus-Scanned":["amavis at osuosl.org","amavis at osuosl.org"],"X-Comment":"SPF check N/A for local connections -\n client-ip=2605:bc80:3010:104::8cd3:938; helo=lists.linuxfoundation.org;\n envelope-from=ovs-dev-bounces@openvswitch.org; receiver=<UNKNOWN> ","DKIM-Filter":["OpenDKIM Filter v2.11.0 smtp4.osuosl.org BDFA941006","OpenDKIM Filter v2.11.0 smtp2.osuosl.org 3D96C400BC"],"Received-SPF":"Pass (mailfrom) identity=mailfrom; client-ip=170.10.133.124;\n helo=us-smtp-delivery-124.mimecast.com; envelope-from=dceara@redhat.com;\n receiver=<UNKNOWN>","DMARC-Filter":"OpenDMARC Filter v1.4.2 smtp2.osuosl.org 3D96C400BC","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n s=mimecast20190719; t=1776955248;\n h=from:from:reply-to:subject:subject:date:date:message-id:message-id:\n to:to:cc:mime-version:mime-version:content-type:content-type:\n content-transfer-encoding:content-transfer-encoding:\n in-reply-to:in-reply-to:references:references;\n bh=XX87UjzYEe6oqa1m+0QZLv2geO6sD+gUBdxTUeUw+O8=;\n b=XazGa/uqRD30Wn7PM2VJhoXYmhWwv58UdNuDBsnoDLz47pGgI9kea1xb+chCe/dkgJh0Fy\n ArIECfYB79Bwm/y+B9PMq4ZLbjwodeNJdr2pnXqNy4PrPwjbgy6z0me3vSBh98vTtvJfUy\n oha+vnZ3/0cfO0R1MzmzPvCxGF6+zUA=","X-MC-Unique":"oHKHxFtcN6eYs3mH0ykGWA-1","X-Mimecast-MFC-AGG-ID":"oHKHxFtcN6eYs3mH0ykGWA_1776955246","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20251104; t=1776955246; x=1777560046;\n h=content-transfer-encoding:in-reply-to:content-language:from\n :references:to:subject:user-agent:mime-version:date:message-id\n :x-gm-gg:x-gm-message-state:from:to:cc:subject:date:message-id\n :reply-to;\n bh=XX87UjzYEe6oqa1m+0QZLv2geO6sD+gUBdxTUeUw+O8=;\n b=PxRSEHtqQd+E1NEuX+o22GxzZJpckblqCRTtOgRNrIbZxFYJ9uaYgbfmwo866XzbRs\n U5dvD+o2TawdTNmebDxCM1xwhTh1KfZpmB9DCH7vPJ9qvAjYQPkyUWyFmzQDp9xDu/be\n lKI55pG62JrAWackS4FS7US5lRRciXjsiGdk0LQGMfkZrcCyZchUddg5xacczWEpdRaA\n v64Xgi9FZoTfhEbH1He/aMJ6Epj0VFQvHUZoXu/ZqU0cmTrOsTQEPZA23m8CAn6zW1DU\n UEKKNCjvzy5VBKdKn6E1iI8rmSmkwrEeFp7CgWcIol5MsmPPdZfrDdRRP+HzUC9hYsud\n Gmvw==","X-Forwarded-Encrypted":"i=1;\n AFNElJ9D3HNFlCqVhq4ti3Kwu8wxTouEuBj1o1/uZyG+0bDqLzy9yYeoCDF2oyErmEkUY6DvMww=@openvswitch.org","X-Gm-Message-State":"AOJu0YzQ89Pqky/04+ymA1FvbtQQe+mdHPTkTg27RV89fR5DofBavoKL\n ebkoznQiEfA+AKDxTX/QIN4Vd9v3eopnQHNyU8gzy9OjoW8PNGcwPutX6pyFNZuHWHzhF3h44oq\n dpRV/MtePgY9aTDaC4b6TP3ApF6Pzg9VrQ7amfm2Oly/IuxKWoXl3D/aF8P9kZw==","X-Gm-Gg":"AeBDieubW4m0JrRCUWyIh+Au7nhnzCbMfoPoslfmFQHQ0Qvl2rcPybDpCXowNK9UhUf\n BWqXQ4ICYtRAsCLVIIgc1uBtFm631kOJAkAtdRHlC6RPAsFO5CjjexxkIBkx2Ocft7cKZQ22KpT\n u24zaigqgjt2q4Pql5JTzp9caJLPwkquT+inFOLzkcsuieTyZPTB9SEticgqflUaAMc98Da3Uu2\n Sbyjubp9k4qBEmqDIy43/XLxY9vLFT7riLAfydVY/AIe/nkguJaVNIOtQJtRuAqbI7SiIH6xJo6\n v8yUwoz/6wPdoghT5vR91WzB0dIKz38qI1u55VhiCXSwrDzcyrNWxRHoqgFOv7tPq8uAElQUroI\n JlyZbTCOGQD6pyuUtTKvRPdxLIDWmBdoMpgk7/gSjbUbHlrv70h+N0CRS5OUorv0zTJx+Rpf5Gu\n x0mfzItwyYz+fec9y7kQbWaPCky/51tFr+VuvrOGu67ntD/vreSrcQt2SvuwpScrBs72XmKNfJS\n xId/DAz20U=","X-Received":["by 2002:a05:7022:f20c:b0:12c:8e70:c338 with SMTP id\n a92af1059eb24-12c8e70c757mr8955427c88.2.1776955245076;\n Thu, 23 Apr 2026 07:40:45 -0700 (PDT)","by 2002:a05:7022:f20c:b0:12c:8e70:c338 with SMTP id\n a92af1059eb24-12c8e70c757mr8955401c88.2.1776955244005;\n Thu, 23 Apr 2026 07:40:44 -0700 (PDT)"],"Message-ID":"<0da010a6-d6b2-482f-b0f3-30b289159081@redhat.com>","Date":"Thu, 23 Apr 2026 16:40:36 +0200","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","To":"Mairtin O'Loingsigh <moloings@redhat.com>, dev@openvswitch.org","References":"<20260421101422.3608058-1-moloings@redhat.com>\n <20260421101422.3608058-3-moloings@redhat.com>","In-Reply-To":"<20260421101422.3608058-3-moloings@redhat.com>","X-Mimecast-Spam-Score":"0","X-Mimecast-MFC-PROC-ID":"31S5I1LgnVIID-lbOfQX5GKG_wnA1VH1X-OqaDCmVnM_1776955246","X-Mimecast-Originator":"redhat.com","Content-Language":"en-US","Subject":"Re: [ovs-dev] [PATCH ovn 2/3] ic: Add transit switch port.","X-BeenThere":"ovs-dev@openvswitch.org","X-Mailman-Version":"2.1.30","Precedence":"list","List-Id":"<ovs-dev.openvswitch.org>","List-Unsubscribe":"<https://mail.openvswitch.org/mailman/options/ovs-dev>,\n <mailto:ovs-dev-request@openvswitch.org?subject=unsubscribe>","List-Archive":"<http://mail.openvswitch.org/pipermail/ovs-dev/>","List-Post":"<mailto:ovs-dev@openvswitch.org>","List-Help":"<mailto:ovs-dev-request@openvswitch.org?subject=help>","List-Subscribe":"<https://mail.openvswitch.org/mailman/listinfo/ovs-dev>,\n <mailto:ovs-dev-request@openvswitch.org?subject=subscribe>","From":"Dumitru Ceara via dev <ovs-dev@openvswitch.org>","Reply-To":"Dumitru Ceara <dceara@redhat.com>","Content-Type":"text/plain; charset=\"us-ascii\"","Content-Transfer-Encoding":"7bit","Errors-To":"ovs-dev-bounces@openvswitch.org","Sender":"\"dev\" <ovs-dev-bounces@openvswitch.org>"}}]