[ovs-dev] [PATCH ovn v1] Add a northbound interface to program MAC_Binding table
svc.eng.git-mail at nutanix.com
svc.eng.git-mail at nutanix.com
Wed Aug 25 18:52:29 UTC 2021
From: Karthik Chandrashekar <karthik.c at nutanix.com>
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 <karthik.c at nutanix.com>
---
lib/automake.mk | 2 +
lib/mac-binding-index.c | 43 ++++++++
lib/mac-binding-index.h | 29 ++++++
manpages.mk | 1 -
northd/ovn-northd.c | 42 ++++++++
northd/ovn_northd.dl | 9 ++
ovn-nb.ovsschema | 17 ++-
ovn-nb.xml | 28 +++++
tests/ovn-nbctl.at | 64 ++++++++++++
tests/ovn.at | 26 +++++
utilities/ovn-nbctl.c | 222 +++++++++++++++++++++++++++++++++++++++-
11 files changed, 478 insertions(+), 5 deletions(-)
create mode 100644 lib/mac-binding-index.c
create mode 100644 lib/mac-binding-index.h
diff --git a/lib/automake.mk b/lib/automake.mk
index ddfe33948..4c7974ee2 100644
--- a/lib/automake.mk
+++ b/lib/automake.mk
@@ -20,6 +20,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/manpages.mk b/manpages.mk
index 3334b38f9..9f7a0ced3 100644
--- a/manpages.mk
+++ b/manpages.mk
@@ -9,4 +9,3 @@ utilities/ovn-detrace.1.in:
lib/common-syn.man:
lib/common.man:
lib/ovs.tmac:
-
diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
index af413aba4..6113b1e1c 100644
--- a/northd/ovn-northd.c
+++ b/northd/ovn-northd.c
@@ -30,6 +30,7 @@
#include "openvswitch/hmap.h"
#include "openvswitch/json.h"
#include "ovn/lex.h"
+#include "lib/mac-binding-index.h"
#include "lib/chassis-index.h"
#include "lib/ip-mcast-index.h"
#include "lib/copp.h"
@@ -79,6 +80,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;
};
struct northd_state {
@@ -8197,6 +8199,41 @@ 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 *datapaths)
+{
+ const struct nbrec_logical_router *nbr;
+ NBREC_LOGICAL_ROUTER_FOR_EACH (nbr, ctx->ovnnb_idl) {
+ if (!lrouter_is_enabled(nbr)) {
+ continue;
+ }
+
+ struct ovn_datapath *od = ovn_datapath_find(datapaths,
+ &nbr->header_.uuid);
+
+ if (od && od->sb) {
+ for (int i = 0; i < od->nbr->n_mac_bindings; i++) {
+ const struct nbrec_mac_binding *nb_mac_binding;
+ nb_mac_binding = od->nbr->mac_bindings[i];
+
+ 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.
*
@@ -14121,6 +14158,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, datapaths);
build_lflows(ctx, datapaths, ports, &port_groups, &mcast_groups,
&igmp_groups, &meter_groups, &lbs, &bfd_connections,
ovn_internal_version_changed);
@@ -15272,6 +15310,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);
@@ -15317,6 +15358,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,
};
if (!state.had_lock && ovsdb_idl_has_lock(ovnsb_idl_loop.idl)) {
diff --git a/northd/ovn_northd.dl b/northd/ovn_northd.dl
index dcfa1fb5d..e1b3b516c 100644
--- a/northd/ovn_northd.dl
+++ b/northd/ovn_northd.dl
@@ -1010,6 +1010,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_string_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 2ac8ef3ea..734d9649e 100644
--- a/ovn-nb.ovsschema
+++ b/ovn-nb.ovsschema
@@ -1,7 +1,7 @@
{
"name": "OVN_Northbound",
- "version": "5.32.1",
- "cksum": "2805328215 29734",
+ "version": "5.33.0",
+ "cksum": "3031667641 30300",
"tables": {
"NB_Global": {
"columns": {
@@ -325,6 +325,12 @@
"refType": "strong"},
"min": 0,
"max": "unlimited"}},
+ "mac_bindings": {
+ "type": {"key": {"type": "uuid",
+ "refTable": "MAC_Binding",
+ "refType": "strong"},
+ "min": 0,
+ "max": "unlimited"}},
"enabled": {"type": {"key": "boolean", "min": 0, "max": 1}},
"nat": {"type": {"key": {"type": "uuid",
"refTable": "NAT",
@@ -575,5 +581,12 @@
"type": {"key": "string", "value": "string",
"min": 0, "max": "unlimited"}}},
"indexes": [["logical_port", "dst_ip"]],
+ "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 390cc5a44..34252581a 100644
--- a/ovn-nb.xml
+++ b/ovn-nb.xml
@@ -2043,6 +2043,10 @@
Zero or more routing policies for the router.
</column>
+ <column name="mac_bindings">
+ Zero or more statically configured mac_bindings for the router.
+ </column>
+
<column name="enabled">
This column is used to administratively set router state. If this column
is empty or is set to <code>true</code>, the router is enabled. If this
@@ -4103,4 +4107,28 @@
</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.
+ </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..7f6761abd 100644
--- a/tests/ovn-nbctl.at
+++ b/tests/ovn-nbctl.at
@@ -2057,6 +2057,70 @@ 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 lr-mac-binding-add lr0 lr0-p0 192.168.10.10 00:00:11:22:33:44])
+AT_CHECK([ovn-nbctl lr-mac-binding-add lr0 lr0-p0 192.168.10.100 00:00:22:33:44:55])
+AT_CHECK([ovn-nbctl lr-mac-binding-add lr0 lr0-p0 10.0.0.10 00:00:33:44:55:66])
+AT_CHECK([ovn-nbctl lr-mac-binding-add lr0 lr0-p0 172.16.0.11 00:00:44:55:66:88])
+
+AT_CHECK([ovn-nbctl lr-mac-binding-add lr0 lr0-p0 foo 00:00:44:55:66:88], [1], [],
+ [ovn-nbctl: foo: Not a valid IPv4 or IPv6 address.
+])
+AT_CHECK([ovn-nbctl lr-mac-binding-add lr0 lr0-p0 172.16.0.200 foo], [1], [],
+ [ovn-nbctl: invalid mac address foo.
+])
+AT_CHECK([ovn-nbctl lr-mac-binding-add lr0 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 lr-mac-binding-add lr0 lr0-p0 172.16.0.11 00:00:44:55:66:77])
+
+AT_CHECK([ovn-nbctl lr-mac-binding-add lr0 lr0-p1 10.0.0.10 00:00:33:44:55:66])
+AT_CHECK([ovn-nbctl lr-mac-binding-add lr0 lr0-p1 172.16.0.11 00:00:44:55:66:88])
+
+AT_CHECK([ovn-nbctl lr-mac-binding-list lr0], [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 lr-mac-binding-del lr0 lr0-p0 foo], [1], [],
+ [ovn-nbctl: foo: Not a valid IPv4 or IPv6 address.
+])
+
+AT_CHECK([ovn-nbctl lr-mac-binding-del lr0 lr0-p1 10.0.0.100], [1], [],
+ [ovn-nbctl: no matching MAC_Binding with the port (lr0-p1) and ip (10.0.0.100)
+])
+
+AT_CHECK([ovn-nbctl --if-exists lr-mac-binding-del lr0 lr0-p1 10.0.0.100])
+
+AT_CHECK([ovn-nbctl lr-mac-binding-del lr0 lr0-p0])
+
+AT_CHECK([ovn-nbctl lr-mac-binding-list lr0], [0], [dnl
+LOGICAL_PORT IP MAC
+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 lr-mac-binding-del lr0 lr0-p1 10.0.0.10])
+AT_CHECK([ovn-nbctl lr-mac-binding-list lr0], [0], [dnl
+LOGICAL_PORT IP MAC
+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.at b/tests/ovn.at
index 4957a1063..3a719cb06 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -28110,3 +28110,29 @@ grep lr0-sw1], [1], [])
OVN_CLEANUP([hv1])
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 lr-mac-binding-add lr0 lr0-p0 192.168.10.10 00:00:11:22:33:44
+ovn-nbctl lr-mac-binding-add lr0 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 lr-mac-binding-add lr0 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 lr-mac-binding-add lr0 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 10217dcd5..b0356d3aa 100644
--- a/utilities/ovn-nbctl.c
+++ b/utilities/ovn-nbctl.c
@@ -366,6 +366,15 @@ Policy commands:\n\
remove policies from ROUTER\n\
lr-policy-list ROUTER print policies for ROUTER\n\
\n\
+MAC_Binding commands:\n\
+ lr-mac-binding-add ROUTER LOGICAL_PORT IP MAC\n\
+ add a MAC_Binding entry to ROUTER\n\
+ lr-mac-binding-del ROUTER LOGICAL_PORT [IP]\n\
+ remove MAC_Binding entry from ROUTER and\n\
+ delete the MAC_Binding row\n\
+ lr-mac-binding-list ROUTER print MAC_Bindings for ROUTER\n\
+\n\n",program_name, program_name);
+ printf("\
NAT commands:\n\
[--stateless]\n\
[--portrange]\n\
@@ -408,8 +417,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\
@@ -5595,6 +5603,205 @@ nbctl_lrp_get_redirect_type(struct ctl_context *ctx)
!redirect_type ? "overlay": redirect_type);
}
+static void
+nbctl_pre_lr_mac_binding_add(struct ctl_context *ctx)
+{
+ ovsdb_idl_add_column(ctx->idl, &nbrec_logical_router_col_name);
+ ovsdb_idl_add_column(ctx->idl, &nbrec_logical_router_port_col_name);
+ ovsdb_idl_add_column(ctx->idl, &nbrec_logical_router_col_mac_bindings);
+
+ 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_lr_mac_binding_add(struct ctl_context *ctx)
+{
+ const struct nbrec_logical_router *lr = NULL;
+ const char *logical_port = ctx->argv[2];
+ const char *ip = ctx->argv[3];
+ const char *mac = ctx->argv[4];
+ char *new_ip = NULL;
+
+ char *error = lr_by_name_or_uuid(ctx, ctx->argv[1], true, &lr);
+ if (error) {
+ ctx->error = error;
+ return;
+ }
+
+ const struct nbrec_logical_router_port *lrp;
+ 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;
+
+ for (size_t i = 0; i < lr->n_mac_bindings; i++) {
+ const struct nbrec_mac_binding *mac_binding = lr->mac_bindings[i];
+
+ char *old_ip;
+ bool should_return = false;
+ old_ip = normalize_addr_str(mac_binding->ip);
+
+ if (!strcmp(mac_binding->logical_port, logical_port)) {
+ if (!strcmp(old_ip, new_ip)) {
+ if (may_exist) {
+ nbrec_mac_binding_verify_mac(mac_binding);
+ nbrec_mac_binding_set_mac(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 */
+ struct nbrec_mac_binding *mac_binding = nbrec_mac_binding_insert(ctx->txn);
+ nbrec_mac_binding_set_logical_port(mac_binding, logical_port);
+ nbrec_mac_binding_set_ip(mac_binding, new_ip);
+ nbrec_mac_binding_set_mac(mac_binding, mac);
+
+ /* Insert MAC_Binding entry into logical router */
+ nbrec_logical_router_update_mac_bindings_addvalue(lr, mac_binding);
+
+cleanup:
+ free(new_ip);
+}
+
+static void
+nbctl_pre_lr_mac_binding_del(struct ctl_context *ctx)
+{
+ ovsdb_idl_add_column(ctx->idl, &nbrec_logical_router_col_name);
+ ovsdb_idl_add_column(ctx->idl, &nbrec_logical_router_port_col_name);
+ ovsdb_idl_add_column(ctx->idl, &nbrec_logical_router_col_mac_bindings);
+
+ 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_lr_mac_binding_del(struct ctl_context *ctx)
+{
+ const struct nbrec_logical_router *lr = NULL;
+ bool must_exist = !shash_find(&ctx->options, "--if-exists");
+ char *error = lr_by_name_or_uuid(ctx, ctx->argv[1], true, &lr);
+ if (error) {
+ ctx->error = error;
+ return;
+ }
+
+ const char *logical_port = ctx->argv[2];
+ if (ctx->argc == 3) {
+ /* Deletes all NB MAC_binding entries on the given logical port. */
+ for (size_t i = 0; i < lr->n_mac_bindings; i++) {
+ if (!strcmp(logical_port, lr->mac_bindings[i]->logical_port)) {
+ struct nbrec_mac_binding *mac_binding = lr->mac_bindings[i];
+ nbrec_logical_router_update_mac_bindings_delvalue(lr, mac_binding);
+ nbrec_mac_binding_delete(mac_binding);
+ }
+ }
+ return;
+ }
+
+ char *ip = normalize_addr_str(ctx->argv[3]);
+ if (!ip) {
+ ctl_error(ctx, "%s: Not a valid IPv4 or IPv6 address.", ctx->argv[3]);
+ return;
+ }
+
+ /* Remove the matching MAC_Binding. */
+ for (size_t i = 0; i < lr->n_mac_bindings; i++) {
+ struct nbrec_mac_binding *mac_binding = lr->mac_bindings[i];
+ bool should_return = false;
+ char *mac_binding_ip = normalize_addr_str(mac_binding->ip);
+ if (!mac_binding_ip) {
+ continue;
+ }
+ if (!strcmp(ip, mac_binding_ip)) {
+ nbrec_logical_router_update_mac_bindings_delvalue(lr, mac_binding);
+ nbrec_mac_binding_delete(mac_binding);
+ should_return = true;
+ }
+ free(mac_binding_ip);
+ if (should_return) {
+ goto cleanup;
+ }
+ }
+
+ if (must_exist) {
+ ctl_error(ctx, "no matching MAC_Binding with the port (%s) and ip (%s)",
+ logical_port, ip);
+ }
+
+cleanup:
+ free(ip);
+}
+
+static void
+nbctl_pre_lr_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_logical_router_col_mac_bindings);
+
+ 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_lr_mac_binding_list(struct ctl_context *ctx)
+{
+ const struct nbrec_logical_router *lr;
+ char *error = lr_by_name_or_uuid(ctx, ctx->argv[1], true, &lr);
+ if (error) {
+ ctx->error = error;
+ return;
+ }
+
+ struct smap lr_mac_bindings = SMAP_INITIALIZER(&lr_mac_bindings);
+ for (size_t i = 0; i < lr->n_mac_bindings; i++) {
+ const struct nbrec_mac_binding *mac_binding = lr->mac_bindings[i];
+ char *key = xasprintf("%-25s%-25s", mac_binding->logical_port,
+ mac_binding->ip);
+ smap_add_format(&lr_mac_bindings, key, "%s", 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)
{
@@ -6958,6 +7165,17 @@ static const struct ctl_command_syntax nbctl_commands[] = {
{ "lr-policy-list", 1, 1, "ROUTER", nbctl_pre_lr_policy_list,
nbctl_lr_policy_list, NULL, "", RO },
+ /* MAC_Binding commands */
+ { "lr-mac-binding-add", 4, 4,
+ "ROUTER LOGICAL_PORT IP MAC",
+ nbctl_pre_lr_mac_binding_add, nbctl_lr_mac_binding_add, NULL,
+ "--may-exist", RW },
+ { "lr-mac-binding-del", 2, 3, "ROUTER LOGICAL_PORT [IP]",
+ nbctl_pre_lr_mac_binding_del, nbctl_lr_mac_binding_del,
+ NULL, "--if-exists", RW },
+ { "lr-mac-binding-list", 1, 1, "ROUTER", nbctl_pre_lr_mac_binding_list,
+ nbctl_lr_mac_binding_list, NULL, "", RO },
+
/* NAT commands. */
{ "lr-nat-add", 4, 7,
"ROUTER TYPE EXTERNAL_IP LOGICAL_IP"
--
2.22.3
More information about the dev
mailing list