From: Karthik Chandrashekar <[email protected]> Add a new NB MAC_Binding table. This allows programming of static mac_bindings for a logical_router. OVN northd is responsible for propagating these static entries to the existing SB MAC_Binding table.
Signed-off-by: Karthik Chandrashekar <[email protected]> --- controller/pinctrl.c | 20 +---- lib/automake.mk | 2 + lib/mac-binding-index.c | 43 +++++++++ lib/mac-binding-index.h | 29 ++++++ northd/northd.c | 31 +++++++ northd/northd.h | 1 + northd/ovn-northd.c | 5 ++ northd/ovn_northd.dl | 9 ++ ovn-nb.ovsschema | 13 ++- ovn-nb.xml | 25 ++++++ tests/ovn-nbctl.at | 69 ++++++++++++++ tests/ovn-northd.at | 26 ++++++ utilities/ovn-nbctl.c | 193 +++++++++++++++++++++++++++++++++++++++- 13 files changed, 441 insertions(+), 25 deletions(-) create mode 100644 lib/mac-binding-index.c create mode 100644 lib/mac-binding-index.h diff --git a/controller/pinctrl.c b/controller/pinctrl.c index 7d01b18a3..af12909fa 100644 --- a/controller/pinctrl.c +++ b/controller/pinctrl.c @@ -48,6 +48,7 @@ #include "ovn/lex.h" #include "lib/acl-log.h" #include "lib/ip-mcast-index.h" +#include "lib/mac-binding-index.h" #include "lib/mcast-group-index.h" #include "lib/ovn-l7.h" #include "lib/ovn-util.h" @@ -4090,25 +4091,6 @@ send_mac_binding_buffered_pkts(struct rconn *swconn) ovs_list_init(&buffered_mac_bindings); } -static const struct sbrec_mac_binding * -mac_binding_lookup(struct ovsdb_idl_index *sbrec_mac_binding_by_lport_ip, - const char *logical_port, - const char *ip) -{ - struct sbrec_mac_binding *mb = sbrec_mac_binding_index_init_row( - sbrec_mac_binding_by_lport_ip); - sbrec_mac_binding_index_set_logical_port(mb, logical_port); - sbrec_mac_binding_index_set_ip(mb, ip); - - const struct sbrec_mac_binding *retval - = sbrec_mac_binding_index_find(sbrec_mac_binding_by_lport_ip, - mb); - - sbrec_mac_binding_index_destroy_row(mb); - - return retval; -} - /* Update or add an IP-MAC binding for 'logical_port'. * Caller should make sure that 'ovnsb_idl_txn' is valid. */ static void diff --git a/lib/automake.mk b/lib/automake.mk index 9f9f447d5..b58acf1d0 100644 --- a/lib/automake.mk +++ b/lib/automake.mk @@ -21,6 +21,8 @@ lib_libovn_la_SOURCES = \ lib/ovn-parallel-hmap.c \ lib/ip-mcast-index.c \ lib/ip-mcast-index.h \ + lib/mac-binding-index.c \ + lib/mac-binding-index.h \ lib/mcast-group-index.c \ lib/mcast-group-index.h \ lib/lex.c \ diff --git a/lib/mac-binding-index.c b/lib/mac-binding-index.c new file mode 100644 index 000000000..1d34dfbdc --- /dev/null +++ b/lib/mac-binding-index.c @@ -0,0 +1,43 @@ +/* Copyright (c) 2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <config.h> + +#include "lib/mac-binding-index.h" +#include "lib/ovn-sb-idl.h" + +struct ovsdb_idl_index * +mac_binding_index_create(struct ovsdb_idl *idl) +{ + return ovsdb_idl_index_create2(idl, + &sbrec_mac_binding_col_logical_port, + &sbrec_mac_binding_col_ip); +} + +const struct sbrec_mac_binding * +mac_binding_lookup(struct ovsdb_idl_index *mac_binding_index, + const char *logical_port, const char *ip) +{ + struct sbrec_mac_binding *target = sbrec_mac_binding_index_init_row( + mac_binding_index); + sbrec_mac_binding_index_set_logical_port(target, logical_port); + sbrec_mac_binding_index_set_ip(target, ip); + + struct sbrec_mac_binding *mac_binding = + sbrec_mac_binding_index_find(mac_binding_index, target); + sbrec_mac_binding_index_destroy_row(target); + + return mac_binding; +} diff --git a/lib/mac-binding-index.h b/lib/mac-binding-index.h new file mode 100644 index 000000000..d7434f688 --- /dev/null +++ b/lib/mac-binding-index.h @@ -0,0 +1,29 @@ +/* Copyright (c) 2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OVN_MAC_BINDING_INDEX_H +#define OVN_MAC_BINDING_INDEX_H 1 + +struct ovsdb_idl; + +struct sbrec_datapath_binding; + +struct ovsdb_idl_index *mac_binding_index_create(struct ovsdb_idl *); +const struct sbrec_mac_binding *mac_binding_lookup( + struct ovsdb_idl_index *mac_binding_index, + const char *logical_port, + const char *ip); + +#endif /* lib/mac-binding-index.h */ diff --git a/northd/northd.c b/northd/northd.c index 0bf66e0b2..b2270fd0e 100644 --- a/northd/northd.c +++ b/northd/northd.c @@ -29,6 +29,7 @@ #include "lib/chassis-index.h" #include "lib/ip-mcast-index.h" #include "lib/copp.h" +#include "lib/mac-binding-index.h" #include "lib/mcast-group-index.h" #include "lib/ovn-l7.h" #include "lib/ovn-nb-idl.h" @@ -8368,6 +8369,35 @@ build_bfd_table(struct northd_context *ctx, struct hmap *bfd_connections, bitmap_free(bfd_src_ports); } +static void +build_mac_binding_table(struct northd_context *ctx, struct hmap *ports) +{ + const struct nbrec_mac_binding *nb_mac_binding; + NBREC_MAC_BINDING_FOR_EACH (nb_mac_binding, ctx->ovnnb_idl) { + struct ovn_port *op = ovn_port_find( + ports, nb_mac_binding->logical_port); + if (op && op->nbrp) { + struct ovn_datapath *od = op->od; + if (od && od->sb) { + const struct sbrec_mac_binding *mb = + mac_binding_lookup(ctx->sbrec_mac_binding_by_lport_ip, + nb_mac_binding->logical_port, + nb_mac_binding->ip); + if (!mb) { + mb = sbrec_mac_binding_insert(ctx->ovnsb_txn); + sbrec_mac_binding_set_logical_port( + mb, nb_mac_binding->logical_port); + sbrec_mac_binding_set_ip(mb, nb_mac_binding->ip); + sbrec_mac_binding_set_mac(mb, nb_mac_binding->mac); + sbrec_mac_binding_set_datapath(mb, od->sb); + } else { + sbrec_mac_binding_set_mac(mb, nb_mac_binding->mac); + } + } + } + } +} + /* Returns a string of the IP address of the router port 'op' that * overlaps with 'ip_s". If one is not found, returns NULL. * @@ -14465,6 +14495,7 @@ ovnnb_db_run(struct northd_context *ctx, build_mcast_groups(ctx, datapaths, ports, &mcast_groups, &igmp_groups); build_meter_groups(ctx, &meter_groups); build_bfd_table(ctx, &bfd_connections, ports); + build_mac_binding_table(ctx, ports); stopwatch_stop(BUILD_LFLOWS_CTX_STOPWATCH_NAME, time_msec()); stopwatch_start(BUILD_LFLOWS_STOPWATCH_NAME, time_msec()); build_lflows(ctx, datapaths, ports, &port_groups, &mcast_groups, diff --git a/northd/northd.h b/northd/northd.h index ffa2bbb4e..59f407332 100644 --- a/northd/northd.h +++ b/northd/northd.h @@ -27,6 +27,7 @@ struct northd_context { struct ovsdb_idl_index *sbrec_ha_chassis_grp_by_name; struct ovsdb_idl_index *sbrec_mcast_group_by_name_dp; struct ovsdb_idl_index *sbrec_ip_mcast_by_dp; + struct ovsdb_idl_index *sbrec_mac_binding_by_lport_ip; bool use_parallel_build; }; diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c index 39aa96055..5fba98812 100644 --- a/northd/ovn-northd.c +++ b/northd/ovn-northd.c @@ -23,6 +23,7 @@ #include "daemon.h" #include "fatal-signal.h" #include "lib/ip-mcast-index.h" +#include "lib/mac-binding-index.h" #include "lib/mcast-group-index.h" #include "memory.h" #include "northd.h" @@ -904,6 +905,9 @@ main(int argc, char *argv[]) struct ovsdb_idl_index *sbrec_ip_mcast_by_dp = ip_mcast_index_create(ovnsb_idl_loop.idl); + struct ovsdb_idl_index *sbrec_mac_binding_by_lport_ip + = mac_binding_index_create(ovnsb_idl_loop.idl); + unixctl_command_register("sb-connection-status", "", 0, 0, ovn_conn_show, ovnsb_idl_loop.idl); @@ -959,6 +963,7 @@ main(int argc, char *argv[]) .sbrec_ha_chassis_grp_by_name = sbrec_ha_chassis_grp_by_name, .sbrec_mcast_group_by_name_dp = sbrec_mcast_group_by_name_dp, .sbrec_ip_mcast_by_dp = sbrec_ip_mcast_by_dp, + .sbrec_mac_binding_by_lport_ip = sbrec_mac_binding_by_lport_ip, .use_parallel_build = use_parallel_build, }; diff --git a/northd/ovn_northd.dl b/northd/ovn_northd.dl index 99772932d..0e96b5de1 100644 --- a/northd/ovn_northd.dl +++ b/northd/ovn_northd.dl @@ -1018,6 +1018,15 @@ sb::Out_MAC_Binding (._uuid = mb._uuid, sb::Out_Port_Binding(.logical_port = mb.logical_port), sb::Out_Datapath_Binding(._uuid = mb.datapath). +sb::Out_MAC_Binding (._uuid = hash, + .logical_port = nb.logical_port, + .ip = nb.ip, + .mac = nb.mac, + .datapath = router._uuid) :- + nb in nb::MAC_Binding(), + rp in &RouterPort(.router = router, .json_name = json_escape(nb.logical_port)), + var hash = hash128((nb.logical_port, nb.ip)). + /* * DHCP options: fixed table */ diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema index 5dee04fe9..27dc91438 100644 --- a/ovn-nb.ovsschema +++ b/ovn-nb.ovsschema @@ -1,7 +1,7 @@ { "name": "OVN_Northbound", - "version": "5.33.1", - "cksum": "1931852754 30731", + "version": "5.34.0", + "cksum": "1872933846 30998", "tables": { "NB_Global": { "columns": { @@ -595,5 +595,12 @@ "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}, "indexes": [["logical_port", "dst_ip"]], - "isRoot": true}} + "isRoot": true}, + "MAC_Binding": { + "columns": { + "logical_port": {"type": "string"}, + "ip": {"type": "string"}, + "mac": {"type": "string"}}, + "indexes": [["logical_port", "ip"]], + "isRoot": true}} } diff --git a/ovn-nb.xml b/ovn-nb.xml index e31578fb6..327039d95 100644 --- a/ovn-nb.xml +++ b/ovn-nb.xml @@ -4130,4 +4130,29 @@ </column> </group> </table> + + <table name="MAC_Binding"> + <p> + Each record represents a MAC Binding entry. + </p> + + <group title="Configuration"> + <p> + <code>ovn-northd</code> reads configuration from these columns + and propagates the value to SBDB. + </p> + + <column name="logical_port"> + The logical port on which the binding was discovered. + </column> + + <column name="ip"> + The bound IP address. + </column> + + <column name="mac"> + The Ethernet address to which the IP is bound. + </column> + </group> + </table> </database> diff --git a/tests/ovn-nbctl.at b/tests/ovn-nbctl.at index 9b80ae410..673c9637d 100644 --- a/tests/ovn-nbctl.at +++ b/tests/ovn-nbctl.at @@ -2057,6 +2057,75 @@ AT_CHECK([ovn-nbctl list forwarding_group], [0], []) dnl --------------------------------------------------------------------- +OVN_NBCTL_TEST([ovn_nbctl_mac_binding], [lr mac_binding], [ + +AT_CHECK([ovn-nbctl lr-add lr0]) +AT_CHECK([ovn-nbctl lrp-add lr0 lr0-p0 00:00:01:01:02:03 192.168.10.1/24]) +AT_CHECK([ovn-nbctl lrp-add lr0 lr0-p1 00:00:02:02:03:04 192.168.11.1/24]) + +AT_CHECK([ovn-nbctl mac-binding-add lr0-p0 192.168.10.10 00:00:11:22:33:44]) +AT_CHECK([ovn-nbctl mac-binding-add lr0-p0 192.168.10.100 00:00:22:33:44:55]) +AT_CHECK([ovn-nbctl mac-binding-add lr0-p0 10.0.0.10 00:00:33:44:55:66]) +AT_CHECK([ovn-nbctl mac-binding-add lr0-p0 172.16.0.11 00:00:44:55:66:88]) + +AT_CHECK([ovn-nbctl mac-binding-add lr0-p0 foo 00:00:44:55:66:88], [1], [], + [ovn-nbctl: foo: Not a valid IPv4 or IPv6 address. +]) +AT_CHECK([ovn-nbctl mac-binding-add lr0-p0 172.16.0.200 foo], [1], [], + [ovn-nbctl: invalid mac address foo. +]) +AT_CHECK([ovn-nbctl mac-binding-add lr0-p0 172.16.0.11 00:00:44:55:66:77], [1], [], + [ovn-nbctl: lr0-p0, 172.16.0.11: a MAC_Binding with this logical_port and ip already exists +]) + +AT_CHECK([ovn-nbctl --may-exist mac-binding-add lr0-p0 172.16.0.11 00:00:44:55:66:77]) + +AT_CHECK([ovn-nbctl mac-binding-add lr0-p1 10.0.0.10 00:00:33:44:55:66]) +AT_CHECK([ovn-nbctl mac-binding-add lr0-p1 172.16.0.11 00:00:44:55:66:88]) + +AT_CHECK([ovn-nbctl mac-binding-list], [0], [dnl +LOGICAL_PORT IP MAC +lr0-p0 10.0.0.10 00:00:33:44:55:66 +lr0-p0 172.16.0.11 00:00:44:55:66:77 +lr0-p0 192.168.10.10 00:00:11:22:33:44 +lr0-p0 192.168.10.100 00:00:22:33:44:55 +lr0-p1 10.0.0.10 00:00:33:44:55:66 +lr0-p1 172.16.0.11 00:00:44:55:66:88 +]) + +AT_CHECK([ovn-nbctl mac-binding-del lr0-p0 foo], [1], [], + [ovn-nbctl: foo: Not a valid IPv4 or IPv6 address. +]) + +AT_CHECK([ovn-nbctl mac-binding-del lr0-p1 10.0.0.100], [1], [], + [ovn-nbctl: no matching MAC_Binding with port (lr0-p1) and ip (10.0.0.100) +]) + +AT_CHECK([ovn-nbctl --if-exists mac-binding-del lr0-p1 10.0.0.100]) + +AT_CHECK([ovn-nbctl mac-binding-del lr0-p0 10.0.0.10]) +AT_CHECK([ovn-nbctl mac-binding-del lr0-p0 192.168.10.100]) + +AT_CHECK([ovn-nbctl mac-binding-list], [0], [dnl +LOGICAL_PORT IP MAC +lr0-p0 172.16.0.11 00:00:44:55:66:77 +lr0-p0 192.168.10.10 00:00:11:22:33:44 +lr0-p1 10.0.0.10 00:00:33:44:55:66 +lr0-p1 172.16.0.11 00:00:44:55:66:88 +]) + +AT_CHECK([ovn-nbctl mac-binding-del lr0-p1 10.0.0.10]) +AT_CHECK([ovn-nbctl mac-binding-list], [0], [dnl +LOGICAL_PORT IP MAC +lr0-p0 172.16.0.11 00:00:44:55:66:77 +lr0-p0 192.168.10.10 00:00:11:22:33:44 +lr0-p1 172.16.0.11 00:00:44:55:66:88 +]) + +]) + +dnl --------------------------------------------------------------------- + OVN_NBCTL_TEST([ovn_nbctl_negative], [basic negative tests], [ AT_CHECK([ovn-nbctl --id=@ls create logical_switch name=foo -- \ set logical_switch foo1 name=bar], diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at index 8b9049899..2e6a51f2e 100644 --- a/tests/ovn-northd.at +++ b/tests/ovn-northd.at @@ -5401,3 +5401,29 @@ AT_CHECK([grep lr_in_gw_redirect lrflows | grep cr-DR | sed 's/table=../table=?? AT_CLEANUP ]) + +OVN_FOR_EACH_NORTHD([ +AT_SETUP([LR NB MAC_Binding table]) +ovn_start + +# Create logical routers +ovn-nbctl lr-add lr0 +ovn-nbctl lrp-add lr0 lr0-p0 00:00:01:01:02:03 192.168.10.1/24 +ovn-nbctl lrp-add lr0 lr0-p1 00:00:02:02:03:04 192.168.11.1/24 + +ovn-nbctl mac-binding-add lr0-p0 192.168.10.10 00:00:11:22:33:44 +ovn-nbctl mac-binding-add lr0-p0 192.168.10.100 00:00:22:33:44:55 + +wait_row_count nb:MAC_Binding 2 logical_port=lr0-p0 +wait_row_count MAC_Binding 1 logical_port=lr0-p0 ip=192.168.10.10 mac="00\:00\:11\:22\:33\:44" +wait_row_count MAC_Binding 1 logical_port=lr0-p0 ip=192.168.10.100 mac="00\:00\:22\:33\:44\:55" + +ovn-nbctl mac-binding-add lr0-p1 10.0.0.10 00:00:33:44:55:66 +wait_row_count nb:MAC_Binding 1 logical_port=lr0-p1 +wait_row_count MAC_Binding 1 logical_port=lr0-p1 ip=10.0.0.10 mac="00\:00\:33\:44\:55\:66" + +ovn-nbctl --may-exist mac-binding-add lr0-p0 192.168.10.100 00:00:22:33:55:66 +wait_row_count MAC_Binding 1 logical_port=lr0-p0 ip=192.168.10.100 mac="00\:00\:22\:33\:55\:66" + +AT_CLEANUP +]) diff --git a/utilities/ovn-nbctl.c b/utilities/ovn-nbctl.c index b04b24d4b..527fe1644 100644 --- a/utilities/ovn-nbctl.c +++ b/utilities/ovn-nbctl.c @@ -365,7 +365,8 @@ Policy commands:\n\ lr-policy-del ROUTER [{PRIORITY | UUID} [MATCH]]\n\ remove policies from ROUTER\n\ lr-policy-list ROUTER print policies for ROUTER\n\ -\n\ +\n\n",program_name, program_name); + printf("\ NAT commands:\n\ [--stateless]\n\ [--portrange]\n\ @@ -408,8 +409,7 @@ Connection commands:\n\ del-connection delete the connections\n\ [--inactivity-probe=MSECS]\n\ set-connection TARGET... set the list of connections to TARGET...\n\ -\n\n",program_name, program_name); - printf("\ +\n\ SSL commands:\n\ get-ssl print the SSL configuration\n\ del-ssl delete the SSL configuration\n\ @@ -450,6 +450,13 @@ Control Plane Protection Policy commands:\n\ List all copp policies defined for control\n\ protocols on ROUTER.\n\ \n\ +MAC_Binding commands:\n\ + mac-binding-add LOGICAL_PORT IP MAC \n\ + Add a MAC_Binding entry\n\ + mac-binding-del LOGICAL_PORT IP \n\ + Delete MAC_Binding entry\n\ + mac-binding-list List all MAC_Binding \n\ +\n\ %s\ %s\ \n\ @@ -5602,6 +5609,176 @@ nbctl_lrp_get_redirect_type(struct ctl_context *ctx) !redirect_type ? "overlay": redirect_type); } +static const struct nbrec_mac_binding * +mac_binding_by_port_ip(struct ctl_context *ctx, + const char *logical_port, const char *ip) +{ + const struct nbrec_mac_binding *nb_mac_binding = NULL; + + NBREC_MAC_BINDING_FOR_EACH(nb_mac_binding, ctx->idl) { + if (!strcmp(nb_mac_binding->logical_port, logical_port) && + !strcmp(nb_mac_binding->ip, ip)) { + break; + } + } + + return nb_mac_binding; +} + +static void +nbctl_pre_mac_binding_add(struct ctl_context *ctx) +{ + ovsdb_idl_add_column(ctx->idl, &nbrec_logical_router_port_col_name); + + ovsdb_idl_add_column(ctx->idl, &nbrec_mac_binding_col_logical_port); + ovsdb_idl_add_column(ctx->idl, &nbrec_mac_binding_col_ip); + ovsdb_idl_add_column(ctx->idl, &nbrec_mac_binding_col_mac); +} + +static void +nbctl_mac_binding_add(struct ctl_context *ctx) +{ + const char *logical_port = ctx->argv[1]; + const char *ip = ctx->argv[2]; + const char *mac = ctx->argv[3]; + char *new_ip = NULL; + + const struct nbrec_logical_router_port *lrp; + char *error = lrp_by_name_or_uuid(ctx, logical_port, true, &lrp); + if (error) { + ctx->error = error; + goto cleanup; + } + + new_ip = normalize_addr_str(ip); + if (!new_ip) { + ctl_error(ctx, "%s: Not a valid IPv4 or IPv6 address.", ip); + return; + } + + struct eth_addr ea; + if (!eth_addr_from_string(mac, &ea)) { + ctl_error(ctx, "invalid mac address %s.", mac); + goto cleanup; + } + + bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL; + const struct nbrec_mac_binding *nb_mac_binding = mac_binding_by_port_ip( + ctx, logical_port, ip); + if (nb_mac_binding) { + char *old_ip; + bool should_return = false; + old_ip = normalize_addr_str(nb_mac_binding->ip); + + if (!strcmp(nb_mac_binding->logical_port, logical_port)) { + if (!strcmp(old_ip, new_ip)) { + if (may_exist) { + nbrec_mac_binding_verify_mac(nb_mac_binding); + nbrec_mac_binding_set_mac(nb_mac_binding, mac); + should_return = true; + } else { + ctl_error(ctx, "%s, %s: a MAC_Binding with this " + "logical_port and ip already exists", + logical_port, new_ip); + should_return = true; + } + } + } + free(old_ip); + if (should_return) { + goto cleanup; + } + } + + /* Create MAC_Binding entry */ + nb_mac_binding = nbrec_mac_binding_insert(ctx->txn); + nbrec_mac_binding_set_logical_port(nb_mac_binding, logical_port); + nbrec_mac_binding_set_ip(nb_mac_binding, new_ip); + nbrec_mac_binding_set_mac(nb_mac_binding, mac); + +cleanup: + free(new_ip); +} + +static void +nbctl_pre_mac_binding_del(struct ctl_context *ctx) +{ + ovsdb_idl_add_column(ctx->idl, &nbrec_logical_router_port_col_name); + + ovsdb_idl_add_column(ctx->idl, &nbrec_mac_binding_col_logical_port); + ovsdb_idl_add_column(ctx->idl, &nbrec_mac_binding_col_ip); + ovsdb_idl_add_column(ctx->idl, &nbrec_mac_binding_col_mac); +} + +static void +nbctl_mac_binding_del(struct ctl_context *ctx) +{ + bool must_exist = !shash_find(&ctx->options, "--if-exists"); + const char *logical_port = ctx->argv[1]; + const struct nbrec_logical_router_port *lrp; + char *error = lrp_by_name_or_uuid(ctx, logical_port, true, &lrp); + if (error) { + ctx->error = error; + return; + } + + char *ip = normalize_addr_str(ctx->argv[2]); + if (!ip) { + ctl_error(ctx, "%s: Not a valid IPv4 or IPv6 address.", ctx->argv[2]); + return; + } + + const struct nbrec_mac_binding *nb_mac_binding = mac_binding_by_port_ip( + ctx, logical_port, ip); + + if (nb_mac_binding) { + /* Remove the matching MAC_Binding. */ + nbrec_mac_binding_delete(nb_mac_binding); + goto cleanup; + } + + if (must_exist) { + ctl_error(ctx, "no matching MAC_Binding with port (%s) and ip (%s)", + logical_port, ip); + } + +cleanup: + free(ip); +} + +static void +nbctl_pre_mac_binding_list(struct ctl_context *ctx) +{ + ovsdb_idl_add_column(ctx->idl, &nbrec_logical_router_col_name); + + ovsdb_idl_add_column(ctx->idl, &nbrec_mac_binding_col_logical_port); + ovsdb_idl_add_column(ctx->idl, &nbrec_mac_binding_col_ip); + ovsdb_idl_add_column(ctx->idl, &nbrec_mac_binding_col_mac); +} + +static void +nbctl_mac_binding_list(struct ctl_context *ctx) +{ + struct smap lr_mac_bindings = SMAP_INITIALIZER(&lr_mac_bindings); + const struct nbrec_mac_binding *nb_mac_binding = NULL; + NBREC_MAC_BINDING_FOR_EACH(nb_mac_binding, ctx->idl) { + char *key = xasprintf("%-25s%-25s", nb_mac_binding->logical_port, + nb_mac_binding->ip); + smap_add_format(&lr_mac_bindings, key, "%s", nb_mac_binding->mac); + free(key); + } + + const struct smap_node **nodes = smap_sort(&lr_mac_bindings); + if (nodes) { + ds_put_format(&ctx->output, "%-25s%-25s%s\n", + "LOGICAL_PORT", "IP", "MAC"); + for (size_t i = 0; i < smap_count(&lr_mac_bindings); i++) { + const struct smap_node *node = nodes[i]; + ds_put_format(&ctx->output, "%-25s%s\n", node->key, node->value); + } + } +} + static const struct nbrec_forwarding_group * fwd_group_by_name_or_uuid(struct ctl_context *ctx, const char *id) { @@ -7063,6 +7240,16 @@ static const struct ctl_command_syntax nbctl_commands[] = { pre_ha_ch_grp_set_chassis_prio, cmd_ha_ch_grp_set_chassis_prio, NULL, "", RW }, + /* MAC_Binding commands */ + { "mac-binding-add", 3, 3, "LOGICAL_PORT IP MAC", + nbctl_pre_mac_binding_add, nbctl_mac_binding_add, NULL, + "--may-exist", RW }, + { "mac-binding-del", 2, 2, "LOGICAL_PORT IP", + nbctl_pre_mac_binding_del, nbctl_mac_binding_del, + NULL, "--if-exists", RW }, + { "mac-binding-list", 0, 1, "[LOGICAL_PORT]", + nbctl_pre_mac_binding_list, nbctl_mac_binding_list, NULL, "", RO }, + {NULL, 0, 0, NULL, NULL, NULL, NULL, "", RO}, }; -- 2.25.1 _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
