Hi, I’ve updated the patch and sent out v6. There was a memory leak in ovn-nbctl.c which is fixed now. Hopefully the build should pass now.
Thank you, Karthik C On 3/24/22, 9:27 AM, "Lorenzo Bianconi" <lorenzo.bianc...@redhat.com> wrote: > From: Karthik Chandrashekar > <karthi...@nutanix.com<mailto:karthi...@nutanix.com>> > > Add a new NB and SB table for managing Static MAC_Binding entries. > This table is currently supported for logical routers. OVN northd > is responsible for propagating the values from NB to SB. OVN controller > is responsible for installation MAC lookup flows. The priority of > the installed flows are based on override_dynamic_mac flag. This helps > control the precedence of statically programmed vs dynamically learnt > MAC Bindings. Hi Karthik, thx for working on v5. I think this is mostly fine. Just two comments inline. Regards, Lorenzo > > Signed-off-by: Karthik Chandrashekar > <karthi...@nutanix.com<mailto:karthi...@nutanix.com>> > --- > controller/lflow.c | 103 ++++++++++++++++----- > controller/lflow.h | 10 +- > controller/ovn-controller.c | 38 +++++++- > lib/automake.mk | 2 + > lib/static-mac-binding-index.c | 43 +++++++++ > lib/static-mac-binding-index.h | 27 ++++++ > northd/en-northd.c | 8 ++ > northd/inc-proc-northd.c | 14 ++- > northd/northd.c | 76 ++++++++++++++++ > northd/northd.h | 5 + > ovn-nb.ovsschema | 15 ++- > ovn-nb.xml | 29 ++++++ > ovn-sb.ovsschema | 15 ++- > ovn-sb.xml | 27 ++++++ > tests/ovn-nbctl.at | 69 ++++++++++++++ > tests/ovn-northd.at | 25 ++++- > tests/ovn.at | 90 ++++++++++++++++++ > utilities/ovn-nbctl.c | 162 ++++++++++++++++++++++++++++++++- > 18 files changed, 718 insertions(+), 40 deletions(-) > create mode 100644 lib/static-mac-binding-index.c > create mode 100644 lib/static-mac-binding-index.h > > diff --git a/controller/lflow.c b/controller/lflow.c > index e169edef1..075373e7b 100644 > --- a/controller/lflow.c > +++ b/controller/lflow.c > @@ -1622,40 +1622,50 @@ static void > consider_neighbor_flow(struct ovsdb_idl_index *sbrec_port_binding_by_name, > const struct hmap *local_datapaths, > const struct sbrec_mac_binding *b, > - struct ovn_desired_flow_table *flow_table) > + const struct sbrec_static_mac_binding *smb, > + struct ovn_desired_flow_table *flow_table, > + uint16_t priority) > { > + if (!b && !smb) { > + return; > + } > + > + char *logical_port = !b ? smb->logical_port : b->logical_port; > + char *ip = !b ? smb->ip : b->ip; > + char *mac = !b ? smb->mac : b->mac; > + > const struct sbrec_port_binding *pb > - = lport_lookup_by_name(sbrec_port_binding_by_name, b->logical_port); > + = lport_lookup_by_name(sbrec_port_binding_by_name, logical_port); > if (!pb || !get_local_datapath(local_datapaths, > pb->datapath->tunnel_key)) { > return; > } > > - struct eth_addr mac; > - if (!eth_addr_from_string(b->mac, &mac)) { > + struct eth_addr mac_addr; > + if (!eth_addr_from_string(mac, &mac_addr)) { > static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); > - VLOG_WARN_RL(&rl, "bad 'mac' %s", b->mac); > + VLOG_WARN_RL(&rl, "bad 'mac' %s", mac); > return; > } > > struct match get_arp_match = MATCH_CATCHALL_INITIALIZER; > struct match lookup_arp_match = MATCH_CATCHALL_INITIALIZER; > > - if (strchr(b->ip, '.')) { > - ovs_be32 ip; > - if (!ip_parse(b->ip, &ip)) { > + if (strchr(ip, '.')) { > + ovs_be32 ip_addr; > + if (!ip_parse(ip, &ip_addr)) { > static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); > - VLOG_WARN_RL(&rl, "bad 'ip' %s", b->ip); > + VLOG_WARN_RL(&rl, "bad 'ip' %s", ip); > return; > } > - match_set_reg(&get_arp_match, 0, ntohl(ip)); > - match_set_reg(&lookup_arp_match, 0, ntohl(ip)); > + match_set_reg(&get_arp_match, 0, ntohl(ip_addr)); > + match_set_reg(&lookup_arp_match, 0, ntohl(ip_addr)); > match_set_dl_type(&lookup_arp_match, htons(ETH_TYPE_ARP)); > } else { > struct in6_addr ip6; > - if (!ipv6_parse(b->ip, &ip6)) { > + if (!ipv6_parse(ip, &ip6)) { > static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); > - VLOG_WARN_RL(&rl, "bad 'ip' %s", b->ip); > + VLOG_WARN_RL(&rl, "bad 'ip' %s", ip); > return; > } > ovs_be128 value; > @@ -1678,20 +1688,22 @@ consider_neighbor_flow(struct ovsdb_idl_index > *sbrec_port_binding_by_name, > uint64_t stub[1024 / 8]; > struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(stub); > uint8_t value = 1; > - put_load(mac.ea, sizeof mac.ea, MFF_ETH_DST, 0, 48, &ofpacts); > + put_load(mac_addr.ea, sizeof mac_addr.ea, MFF_ETH_DST, 0, 48, &ofpacts); > put_load(&value, sizeof value, MFF_LOG_FLAGS, MLF_LOOKUP_MAC_BIT, 1, > &ofpacts); > - ofctrl_add_flow(flow_table, OFTABLE_MAC_BINDING, 100, > - b->header_.uuid.parts[0], &get_arp_match, > - &ofpacts, &b->header_.uuid); > + ofctrl_add_flow(flow_table, OFTABLE_MAC_BINDING, priority, > + !b ? smb->header_.uuid.parts[0] : > b->header_.uuid.parts[0], > + &get_arp_match, &ofpacts, > + !b ? &smb->header_.uuid : &b->header_.uuid); > > ofpbuf_clear(&ofpacts); > put_load(&value, sizeof value, MFF_LOG_FLAGS, MLF_LOOKUP_MAC_BIT, 1, > &ofpacts); > - match_set_dl_src(&lookup_arp_match, mac); > - ofctrl_add_flow(flow_table, OFTABLE_MAC_LOOKUP, 100, > - b->header_.uuid.parts[0], &lookup_arp_match, > - &ofpacts, &b->header_.uuid); > + match_set_dl_src(&lookup_arp_match, mac_addr); > + ofctrl_add_flow(flow_table, OFTABLE_MAC_LOOKUP, priority, > + !b ? smb->header_.uuid.parts[0] : > b->header_.uuid.parts[0], > + &lookup_arp_match, &ofpacts, > + !b ? &smb->header_.uuid : &b->header_.uuid); > > ofpbuf_uninit(&ofpacts); > } > @@ -1701,13 +1713,23 @@ consider_neighbor_flow(struct ovsdb_idl_index > *sbrec_port_binding_by_name, > static void > add_neighbor_flows(struct ovsdb_idl_index *sbrec_port_binding_by_name, > const struct sbrec_mac_binding_table *mac_binding_table, > + const struct sbrec_static_mac_binding_table *smb_table, > const struct hmap *local_datapaths, > struct ovn_desired_flow_table *flow_table) > { > + /* Add flows for learnt MAC bindings */ > const struct sbrec_mac_binding *b; > SBREC_MAC_BINDING_TABLE_FOR_EACH (b, mac_binding_table) { > consider_neighbor_flow(sbrec_port_binding_by_name, local_datapaths, > - b, flow_table); > + b, NULL, flow_table, 100); > + } > + > + /* Add flows for statically configured MAC bindings */ > + const struct sbrec_static_mac_binding *smb; > + SBREC_STATIC_MAC_BINDING_TABLE_FOR_EACH (smb, smb_table) { > + consider_neighbor_flow(sbrec_port_binding_by_name, local_datapaths, > + NULL, smb, flow_table, > + smb->override_dynamic_mac ? 150 : 50); > } > } > > @@ -2346,7 +2368,7 @@ add_lb_hairpin_flows(const struct > sbrec_load_balancer_table *lb_table, > > /* Handles neighbor changes in mac_binding table. */ > void > -lflow_handle_changed_neighbors( > +lflow_handle_changed_mac_bindings( > struct ovsdb_idl_index *sbrec_port_binding_by_name, > const struct sbrec_mac_binding_table *mac_binding_table, > const struct hmap *local_datapaths, > @@ -2373,7 +2395,36 @@ lflow_handle_changed_neighbors( > VLOG_DBG("handle new mac_binding "UUID_FMT, > UUID_ARGS(&mb->header_.uuid)); > consider_neighbor_flow(sbrec_port_binding_by_name, > local_datapaths, > - mb, flow_table); > + mb, NULL, flow_table, 100); > + } > + } > +} > + > +/* Handles changes to static_mac_binding table. */ > +void > +lflow_handle_changed_static_mac_bindings( > + struct ovsdb_idl_index *sbrec_port_binding_by_name, > + const struct sbrec_static_mac_binding_table *smb_table, > + const struct hmap *local_datapaths, > + struct ovn_desired_flow_table *flow_table) > +{ > + const struct sbrec_static_mac_binding *smb; > + SBREC_STATIC_MAC_BINDING_TABLE_FOR_EACH_TRACKED (smb, smb_table) { > + if (sbrec_static_mac_binding_is_deleted(smb)) { > + VLOG_DBG("handle deleted static_mac_binding "UUID_FMT, > + UUID_ARGS(&smb->header_.uuid)); > + ofctrl_remove_flows(flow_table, &smb->header_.uuid); > + } else { > + if (!sbrec_static_mac_binding_is_new(smb)) { > + VLOG_DBG("handle updated static_mac_binding "UUID_FMT, > + UUID_ARGS(&smb->header_.uuid)); > + ofctrl_remove_flows(flow_table, &smb->header_.uuid); > + } > + VLOG_DBG("handle new static_mac_binding "UUID_FMT, > + UUID_ARGS(&smb->header_.uuid)); > + consider_neighbor_flow(sbrec_port_binding_by_name, > local_datapaths, > + NULL, smb, flow_table, > + smb->override_dynamic_mac ? 150 : 50); here we will end up with 2 ~ identical flows (with different priorities) if static mac binding and dynamic one collides. I think it will not a big deal since this would be a rare event. @Numan: what do you think? > } > } > } > @@ -2443,7 +2494,9 @@ lflow_run(struct lflow_ctx_in *l_ctx_in, struct > lflow_ctx_out *l_ctx_out) > > add_logical_flows(l_ctx_in, l_ctx_out); > add_neighbor_flows(l_ctx_in->sbrec_port_binding_by_name, > - l_ctx_in->mac_binding_table, > l_ctx_in->local_datapaths, > + l_ctx_in->mac_binding_table, > + l_ctx_in->static_mac_binding_table, > + l_ctx_in->local_datapaths, > l_ctx_out->flow_table); > add_lb_hairpin_flows(l_ctx_in->lb_table, l_ctx_in->local_datapaths, > l_ctx_out->flow_table, > diff --git a/controller/lflow.h b/controller/lflow.h > index d61733bc2..c2944378b 100644 > --- a/controller/lflow.h > +++ b/controller/lflow.h > @@ -147,6 +147,7 @@ struct lflow_ctx_in { > const struct sbrec_fdb_table *fdb_table; > const struct sbrec_chassis *chassis; > const struct sbrec_load_balancer_table *lb_table; > + const struct sbrec_static_mac_binding_table *static_mac_binding_table; > const struct hmap *local_datapaths; > const struct shash *addr_sets; > const struct shash *port_groups; > @@ -191,9 +192,14 @@ bool lflow_handle_addr_set_update(const char *as_name, > struct addr_set_diff *, > struct lflow_ctx_out *, > bool *changed); > > -void lflow_handle_changed_neighbors( > +void lflow_handle_changed_mac_bindings( > struct ovsdb_idl_index *sbrec_port_binding_by_name, > - const struct sbrec_mac_binding_table *, > + const struct sbrec_mac_binding_table *mac_binding_table, > + const struct hmap *local_datapaths, > + struct ovn_desired_flow_table *); > +void lflow_handle_changed_static_mac_bindings( > + struct ovsdb_idl_index *sbrec_port_binding_by_name, > + const struct sbrec_static_mac_binding_table *smb_table, > const struct hmap *local_datapaths, > struct ovn_desired_flow_table *); > bool lflow_handle_changed_lbs(struct lflow_ctx_in *, struct lflow_ctx_out *); > diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c > index ea5e9df41..2ec9aee32 100644 > --- a/controller/ovn-controller.c > +++ b/controller/ovn-controller.c > @@ -969,7 +969,8 @@ ctrl_register_ovs_idl(struct ovsdb_idl *ovs_idl) > SB_NODE(dns, "dns") \ > SB_NODE(load_balancer, "load_balancer") \ > SB_NODE(fdb, "fdb") \ > - SB_NODE(meter, "meter") > + SB_NODE(meter, "meter") \ > + SB_NODE(static_mac_binding, "static_mac_binding") > > enum sb_engine_node { > #define SB_NODE(NAME, NAME_STR) SB_##NAME, > @@ -2295,6 +2296,10 @@ init_lflow_ctx(struct engine_node *node, > (struct sbrec_fdb_table *)EN_OVSDB_GET( > engine_get_input("SB_fdb", node)); > > + struct sbrec_static_mac_binding_table *smb_table = > + (struct sbrec_static_mac_binding_table *)EN_OVSDB_GET( > + engine_get_input("SB_static_mac_binding", node)); > + > struct ovsrec_open_vswitch_table *ovs_table = > (struct ovsrec_open_vswitch_table *)EN_OVSDB_GET( > engine_get_input("OVS_open_vswitch", node)); > @@ -2343,6 +2348,7 @@ init_lflow_ctx(struct engine_node *node, > l_ctx_in->fdb_table = fdb_table, > l_ctx_in->chassis = chassis; > l_ctx_in->lb_table = lb_table; > + l_ctx_in->static_mac_binding_table = smb_table; > l_ctx_in->local_datapaths = &rt_data->local_datapaths; > l_ctx_in->addr_sets = addr_sets; > l_ctx_in->port_groups = port_groups; > @@ -2485,13 +2491,39 @@ lflow_output_sb_mac_binding_handler(struct > engine_node *node, void *data) > > struct ed_type_lflow_output *lfo = data; > > - lflow_handle_changed_neighbors(sbrec_port_binding_by_name, > + lflow_handle_changed_mac_bindings(sbrec_port_binding_by_name, > mac_binding_table, local_datapaths, &lfo->flow_table); > > engine_set_node_state(node, EN_UPDATED); > return true; > } > > +static bool > +lflow_output_sb_static_mac_binding_handler(struct engine_node *node, > + void *data) > +{ > + struct ovsdb_idl_index *sbrec_port_binding_by_name = > + engine_ovsdb_node_get_index( > + engine_get_input("SB_port_binding", node), > + "name"); > + > + struct sbrec_static_mac_binding_table *smb_table = > + (struct sbrec_static_mac_binding_table *)EN_OVSDB_GET( > + engine_get_input("SB_static_mac_binding", node)); > + > + struct ed_type_runtime_data *rt_data = > + engine_get_input_data("runtime_data", node); > + const struct hmap *local_datapaths = &rt_data->local_datapaths; > + > + struct ed_type_lflow_output *lfo = data; > + > + lflow_handle_changed_static_mac_bindings(sbrec_port_binding_by_name, > + smb_table, local_datapaths, &lfo->flow_table); > + > + engine_set_node_state(node, EN_UPDATED); > + return true; > +} > + > static bool > lflow_output_sb_multicast_group_handler(struct engine_node *node, void *data) > { > @@ -3322,6 +3354,8 @@ main(int argc, char *argv[]) > > engine_add_input(&en_lflow_output, &en_sb_mac_binding, > lflow_output_sb_mac_binding_handler); > + engine_add_input(&en_lflow_output, &en_sb_static_mac_binding, > + lflow_output_sb_static_mac_binding_handler); > engine_add_input(&en_lflow_output, &en_sb_logical_flow, > lflow_output_sb_logical_flow_handler); > /* Using a noop handler since we don't really need any data from datapath > diff --git a/lib/automake.mk b/lib/automake.mk > index 829aedfc5..3a2da1fe4 100644 > --- a/lib/automake.mk > +++ b/lib/automake.mk > @@ -38,6 +38,8 @@ lib_libovn_la_SOURCES = \ > lib/inc-proc-eng.h \ > lib/lb.c \ > lib/lb.h \ > + lib/static-mac-binding-index.c \ > + lib/static-mac-binding-index.h \ > lib/stopwatch-names.h \ > lib/vif-plug-provider.h \ > lib/vif-plug-provider.c \ > diff --git a/lib/static-mac-binding-index.c b/lib/static-mac-binding-index.c > new file mode 100644 > index 000000000..fecc9b74f > --- /dev/null > +++ b/lib/static-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/static-mac-binding-index.h" > +#include "lib/ovn-sb-idl.h" > + > +struct ovsdb_idl_index * > +static_mac_binding_index_create(struct ovsdb_idl *idl) > +{ > + return ovsdb_idl_index_create2(idl, > + > &sbrec_static_mac_binding_col_logical_port, > + &sbrec_static_mac_binding_col_ip); > +} > + > +const struct sbrec_static_mac_binding * > +static_mac_binding_lookup(struct ovsdb_idl_index *smb_index, > + const char *logical_port, const char *ip) > +{ > + struct sbrec_static_mac_binding *target = > + sbrec_static_mac_binding_index_init_row(smb_index); > + sbrec_static_mac_binding_index_set_logical_port(target, logical_port); > + sbrec_static_mac_binding_index_set_ip(target, ip); > + > + struct sbrec_static_mac_binding *smb = > + sbrec_static_mac_binding_index_find(smb_index, target); > + sbrec_static_mac_binding_index_destroy_row(target); > + > + return smb; > +} > diff --git a/lib/static-mac-binding-index.h b/lib/static-mac-binding-index.h > new file mode 100644 > index 000000000..3d4ff06a2 > --- /dev/null > +++ b/lib/static-mac-binding-index.h > @@ -0,0 +1,27 @@ > +/* 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_STATIC_MAC_BINDING_INDEX_H > +#define OVN_STATIC_MAC_BINDING_INDEX_H 1 > + > +struct ovsdb_idl; > + > +struct ovsdb_idl_index *static_mac_binding_index_create(struct ovsdb_idl *); > +const struct sbrec_static_mac_binding *static_mac_binding_lookup( > + struct ovsdb_idl_index *smb_index, > + const char *logical_port, > + const char *ip); > + > +#endif /* lib/static-mac-binding-index.h */ > diff --git a/northd/en-northd.c b/northd/en-northd.c > index 79da7e1c4..4907a1ff2 100644 > --- a/northd/en-northd.c > +++ b/northd/en-northd.c > @@ -55,6 +55,10 @@ void en_northd_run(struct engine_node *node, void *data) > engine_ovsdb_node_get_index( > engine_get_input("SB_ip_multicast", node), > "sbrec_ip_mcast_by_dp"); > + input_data.sbrec_static_mac_binding_by_lport_ip = > + engine_ovsdb_node_get_index( > + engine_get_input("SB_static_mac_binding", node), > + "sbrec_static_mac_binding_by_lport_ip"); > > input_data.nbrec_nb_global_table = > EN_OVSDB_GET(engine_get_input("NB_nb_global", node)); > @@ -72,6 +76,8 @@ void en_northd_run(struct engine_node *node, void *data) > EN_OVSDB_GET(engine_get_input("NB_meter", node)); > input_data.nbrec_acl_table = > EN_OVSDB_GET(engine_get_input("NB_acl", node)); > + input_data.nbrec_static_mac_binding_table = > + EN_OVSDB_GET(engine_get_input("NB_static_mac_binding", node)); > > input_data.sbrec_sb_global_table = > EN_OVSDB_GET(engine_get_input("SB_sb_global", node)); > @@ -103,6 +109,8 @@ void en_northd_run(struct engine_node *node, void *data) > EN_OVSDB_GET(engine_get_input("SB_ip_multicast", node)); > input_data.sbrec_chassis_private_table = > EN_OVSDB_GET(engine_get_input("SB_chassis_private", node)); > + input_data.sbrec_static_mac_binding_table = > + EN_OVSDB_GET(engine_get_input("SB_static_mac_binding", node)); > > northd_run(&input_data, data, > eng_ctx->ovnnb_idl_txn, > diff --git a/northd/inc-proc-northd.c b/northd/inc-proc-northd.c > index af55221e3..43093cb5a 100644 > --- a/northd/inc-proc-northd.c > +++ b/northd/inc-proc-northd.c > @@ -20,6 +20,7 @@ > > #include "chassis-index.h" > #include "ip-mcast-index.h" > +#include "static-mac-binding-index.h" > #include "lib/inc-proc-eng.h" > #include "lib/ovn-nb-idl.h" > #include "lib/ovn-sb-idl.h" > @@ -60,7 +61,8 @@ VLOG_DEFINE_THIS_MODULE(inc_proc_northd); > NB_NODE(gateway_chassis, "gateway_chassis") \ > NB_NODE(ha_chassis_group, "ha_chassis_group") \ > NB_NODE(ha_chassis, "ha_chassis") \ > - NB_NODE(bfd, "bfd") > + NB_NODE(bfd, "bfd") \ > + NB_NODE(static_mac_binding, "static_mac_binding") > > enum nb_engine_node { > #define NB_NODE(NAME, NAME_STR) NB_##NAME, > @@ -109,7 +111,8 @@ VLOG_DEFINE_THIS_MODULE(inc_proc_northd); > SB_NODE(service_monitor, "service_monitor") \ > SB_NODE(load_balancer, "load_balancer") \ > SB_NODE(bfd, "bfd") \ > - SB_NODE(fdb, "fdb") > + SB_NODE(fdb, "fdb") \ > + SB_NODE(static_mac_binding, "static_mac_binding") > > enum sb_engine_node { > #define SB_NODE(NAME, NAME_STR) SB_##NAME, > @@ -178,6 +181,7 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb, > engine_add_input(&en_northd, &en_nb_gateway_chassis, NULL); > engine_add_input(&en_northd, &en_nb_ha_chassis_group, NULL); > engine_add_input(&en_northd, &en_nb_ha_chassis, NULL); > + engine_add_input(&en_northd, &en_nb_static_mac_binding, NULL); > > engine_add_input(&en_northd, &en_sb_sb_global, NULL); > engine_add_input(&en_northd, &en_sb_chassis, NULL); > @@ -206,6 +210,7 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb, > engine_add_input(&en_northd, &en_sb_service_monitor, NULL); > engine_add_input(&en_northd, &en_sb_load_balancer, NULL); > engine_add_input(&en_northd, &en_sb_fdb, NULL); > + engine_add_input(&en_northd, &en_sb_static_mac_binding, NULL); > engine_add_input(&en_lflow, &en_nb_bfd, NULL); > engine_add_input(&en_lflow, &en_sb_bfd, NULL); > engine_add_input(&en_lflow, &en_sb_logical_flow, NULL); > @@ -228,6 +233,8 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb, > ip_mcast_index_create(sb->idl); > struct ovsdb_idl_index *sbrec_chassis_by_hostname = > chassis_hostname_index_create(sb->idl); > + struct ovsdb_idl_index *sbrec_static_mac_binding_by_lport_ip > + = static_mac_binding_index_create(sb->idl); > > engine_init(&en_lflow, &engine_arg); > > @@ -246,6 +253,9 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb, > engine_ovsdb_node_add_index(&en_sb_ip_multicast, > "sbrec_ip_mcast_by_dp", > sbrec_ip_mcast_by_dp); > + engine_ovsdb_node_add_index(&en_sb_static_mac_binding, > + "sbrec_static_mac_binding_by_lport_ip", > + sbrec_static_mac_binding_by_lport_ip); > } > > void inc_proc_northd_run(struct ovsdb_idl_txn *ovnnb_txn, > diff --git a/northd/northd.c b/northd/northd.c > index a2cf8d6fc..ce161f592 100644 > --- a/northd/northd.c > +++ b/northd/northd.c > @@ -28,6 +28,7 @@ > #include "ovn/lex.h" > #include "lib/chassis-index.h" > #include "lib/ip-mcast-index.h" > +#include "lib/static-mac-binding-index.h" > #include "lib/copp.h" > #include "lib/mcast-group-index.h" > #include "lib/ovn-l7.h" > @@ -14988,6 +14989,80 @@ build_meter_groups(struct northd_input *input_data, > } > } > > +static const struct nbrec_static_mac_binding * > +static_mac_binding_by_port_ip(struct northd_input *input_data, > + const char *logical_port, const char *ip) > +{ > + const struct nbrec_static_mac_binding *nb_smb = NULL; > + > + NBREC_STATIC_MAC_BINDING_TABLE_FOR_EACH ( > + nb_smb, input_data->nbrec_static_mac_binding_table) { > + if (!strcmp(nb_smb->logical_port, logical_port) && > + !strcmp(nb_smb->ip, ip)) { > + break; > + } > + } > + > + return nb_smb; > +} > + > +static void > +build_static_mac_binding_table(struct northd_input *input_data, > + struct ovsdb_idl_txn *ovnsb_txn, > + struct hmap *ports) > +{ > + /* Cleanup SB Static_MAC_Binding entries which do not have corresponding > + * NB Static_MAC_Binding entries. */ > + const struct nbrec_static_mac_binding *nb_smb; > + const struct sbrec_static_mac_binding *sb_smb, *sb_smb_next; > + SBREC_STATIC_MAC_BINDING_TABLE_FOR_EACH_SAFE (sb_smb, sb_smb_next, > + input_data->sbrec_static_mac_binding_table) { > + nb_smb = static_mac_binding_by_port_ip(input_data, > + sb_smb->logical_port, > + sb_smb->ip); > + if (!nb_smb) { > + sbrec_static_mac_binding_delete(sb_smb); > + } > + } > + > + /* Create/Update SB Static_MAC_Binding entries with corresponding values > + * from NB Static_MAC_Binding entries. */ > + NBREC_STATIC_MAC_BINDING_TABLE_FOR_EACH ( > + nb_smb, input_data->nbrec_static_mac_binding_table) { > + struct ovn_port *op = ovn_port_find(ports, nb_smb->logical_port); > + if (op && op->nbrp) { > + struct ovn_datapath *od = op->od; > + if (od && od->sb) { > + const struct sbrec_static_mac_binding *mb = > + static_mac_binding_lookup( > + input_data->sbrec_static_mac_binding_by_lport_ip, > + nb_smb->logical_port, nb_smb->ip); > + if (!mb) { > + /* Create new entry */ > + mb = sbrec_static_mac_binding_insert(ovnsb_txn); > + sbrec_static_mac_binding_set_logical_port( > + mb, nb_smb->logical_port); > + sbrec_static_mac_binding_set_ip(mb, nb_smb->ip); > + sbrec_static_mac_binding_set_mac(mb, nb_smb->mac); > + sbrec_static_mac_binding_set_override_dynamic_mac(mb, > + nb_smb->override_dynamic_mac); > + sbrec_static_mac_binding_set_datapath(mb, od->sb); > + } else { > + /* Update existing entry if there is a change*/ > + if (strcmp(mb->mac, nb_smb->mac)) { > + sbrec_static_mac_binding_set_mac(mb, nb_smb->mac); > + } > + if (mb->override_dynamic_mac != > + nb_smb->override_dynamic_mac) { > + sbrec_static_mac_binding_set_override_dynamic_mac(mb, > + nb_smb->override_dynamic_mac); > + } > + } > + } > + } > + } > +} > + > void > northd_init(struct northd_data *data) > { > @@ -15141,6 +15216,7 @@ ovnnb_db_run(struct northd_input *input_data, > build_lrouter_groups(&data->ports, &data->lr_list); > build_ip_mcast(input_data, ovnsb_txn, &data->datapaths); > build_meter_groups(input_data, &data->meter_groups); > + build_static_mac_binding_table(input_data, ovnsb_txn, &data->ports); > stopwatch_stop(BUILD_LFLOWS_CTX_STOPWATCH_NAME, time_msec()); > stopwatch_start(CLEAR_LFLOWS_CTX_STOPWATCH_NAME, time_msec()); > ovn_update_ipv6_prefix(&data->ports); > diff --git a/northd/northd.h b/northd/northd.h > index ebcb40de7..2d804a22e 100644 > --- a/northd/northd.h > +++ b/northd/northd.h > @@ -28,6 +28,8 @@ struct northd_input { > const struct nbrec_address_set_table *nbrec_address_set_table; > const struct nbrec_meter_table *nbrec_meter_table; > const struct nbrec_acl_table *nbrec_acl_table; > + const struct nbrec_static_mac_binding_table > + *nbrec_static_mac_binding_table; > > /* Southbound table references */ > const struct sbrec_sb_global_table *sbrec_sb_global_table; > @@ -45,12 +47,15 @@ struct northd_input { > const struct sbrec_dns_table *sbrec_dns_table; > const struct sbrec_ip_multicast_table *sbrec_ip_multicast_table; > const struct sbrec_chassis_private_table *sbrec_chassis_private_table; > + const struct sbrec_static_mac_binding_table > + *sbrec_static_mac_binding_table; > > /* Indexes */ > struct ovsdb_idl_index *sbrec_chassis_by_name; > struct ovsdb_idl_index *sbrec_chassis_by_hostname; > struct ovsdb_idl_index *sbrec_ha_chassis_grp_by_name; > struct ovsdb_idl_index *sbrec_ip_mcast_by_dp; > + struct ovsdb_idl_index *sbrec_static_mac_binding_by_lport_ip; > }; > > struct northd_data { > diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema > index 80b830629..043f71dd5 100644 > --- a/ovn-nb.ovsschema > +++ b/ovn-nb.ovsschema > @@ -1,7 +1,7 @@ > { > "name": "OVN_Northbound", > - "version": "6.1.0", > - "cksum": "4010776751 31237", > + "version": "6.2.0", > + "cksum": "3076170876 31573", > "tables": { > "NB_Global": { > "columns": { > @@ -606,5 +606,14 @@ > "type": {"key": "string", "value": "string", > "min": 0, "max": "unlimited"}}}, > "indexes": [["logical_port", "dst_ip"]], > - "isRoot": true}} > + "isRoot": true}, > + "Static_MAC_Binding": { > + "columns": { > + "logical_port": {"type": "string"}, > + "ip": {"type": "string"}, > + "mac": {"type": "string"}, > + "override_dynamic_mac": {"type": "boolean"}}, > + "indexes": [["logical_port", "ip"]], > + "isRoot": true} > } > +} > diff --git a/ovn-nb.xml b/ovn-nb.xml > index 4d7a23c52..ab7e7615c 100644 > --- a/ovn-nb.xml > +++ b/ovn-nb.xml > @@ -4293,4 +4293,33 @@ > </column> > </group> > </table> > + > + <table name="Static_MAC_Binding"> > + <p> > + Each record represents a Static_MAC_Binding entry for a logical router. > + </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 router port for the binding. > + </column> > + > + <column name="ip"> > + The bound IP address. > + </column> > + > + <column name="mac"> > + The Ethernet address to which the IP is bound. > + </column> > + > + <column name="override_dynamic_mac"> > + Override dynamically learnt MACs. > + </column> > + </group> > + </table> > </database> > diff --git a/ovn-sb.ovsschema b/ovn-sb.ovsschema > index 122614dd5..66664c840 100644 > --- a/ovn-sb.ovsschema > +++ b/ovn-sb.ovsschema > @@ -1,7 +1,7 @@ > { > "name": "OVN_Southbound", > - "version": "20.21.0", > - "cksum": "2362446865 26963", > + "version": "20.22.0", > + "cksum": "1686121686 27471", > "tables": { > "SB_Global": { > "columns": { > @@ -533,6 +533,17 @@ > "minInteger": 1, > "maxInteger": 16777215}}}}, > "indexes": [["mac", "dp_key"]], > + "isRoot": true}, > + "Static_MAC_Binding": { > + "columns": { > + "logical_port": {"type": "string"}, > + "ip": {"type": "string"}, > + "mac": {"type": "string"}, > + "override_dynamic_mac": {"type": "boolean"}, > + "datapath": {"type": { > + "key": {"type": "uuid", > + "refTable": "Datapath_Binding"}}}}, > + "indexes": [["logical_port", "ip"]], > "isRoot": true} > } > } > diff --git a/ovn-sb.xml b/ovn-sb.xml > index f7c41ccdc..9831b9fdb 100644 > --- a/ovn-sb.xml > +++ b/ovn-sb.xml > @@ -4531,4 +4531,31 @@ tcp.flags = RST; > The key of the port binding on which this FDB was learnt. > </column> > </table> > + > + <table name="Static_MAC_Binding" title="IP to MAC bindings"> > + <p> > + Each record represents a Static_MAC_Binding entry for a logical router. > + </p> > + > + > + <column name="logical_port"> > + The logical router port for the binding. > + </column> > + > + <column name="ip"> > + The bound IP address. > + </column> > + > + <column name="mac"> > + The Ethernet address to which the IP is bound. > + </column> > + > + <column name="override_dynamic_mac"> > + Override dynamically learnt MACs. > + </column> > + > + <column name="datapath"> > + The logical datapath to which the logical router port belongs. > + </column> > + </table> > </database> > diff --git a/tests/ovn-nbctl.at b/tests/ovn-nbctl.at > index 539a121c0..4e65443c9 100644 > --- a/tests/ovn-nbctl.at > +++ b/tests/ovn-nbctl.at > @@ -2269,6 +2269,75 @@ AT_CHECK([ovn-nbctl list forwarding_group], [0], []) > 1992: ovn-nbctl - lr static_mac_binding - direct failed. Can you please double check? > dnl --------------------------------------------------------------------- > > +OVN_NBCTL_TEST([ovn_nbctl_static_mac_binding], [lr static_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 static-mac-binding-add lr0-p0 192.168.10.10 > 00:00:11:22:33:44]) > +AT_CHECK([ovn-nbctl static-mac-binding-add lr0-p0 192.168.10.100 > 00:00:22:33:44:55]) > +AT_CHECK([ovn-nbctl static-mac-binding-add lr0-p0 10.0.0.10 > 00:00:33:44:55:66]) > +AT_CHECK([ovn-nbctl static-mac-binding-add lr0-p0 172.16.0.11 > 00:00:44:55:66:88]) > + > +AT_CHECK([ovn-nbctl static-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 static-mac-binding-add lr0-p0 172.16.0.200 foo], [1], [], > + [ovn-nbctl: invalid mac address foo. > +]) > +AT_CHECK([ovn-nbctl static-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 Static_MAC_Binding with this > logical_port and ip already exists > +]) > + > +AT_CHECK([ovn-nbctl --may-exist static-mac-binding-add lr0-p0 172.16.0.11 > 00:00:44:55:66:77]) > + > +AT_CHECK([ovn-nbctl static-mac-binding-add lr0-p1 10.0.0.10 > 00:00:33:44:55:66]) > +AT_CHECK([ovn-nbctl static-mac-binding-add lr0-p1 172.16.0.11 > 00:00:44:55:66:88]) > + > +AT_CHECK([ovn-nbctl static-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 static-mac-binding-del lr0-p0 foo], [1], [], > + [ovn-nbctl: foo: Not a valid IPv4 or IPv6 address. > +]) > + > +AT_CHECK([ovn-nbctl static-mac-binding-del lr0-p1 10.0.0.100], [1], [], > + [ovn-nbctl: no matching Static_MAC_Binding with port lr0-p1 and ip > 10.0.0.100 > +]) > + > +AT_CHECK([ovn-nbctl --if-exists static-mac-binding-del lr0-p1 10.0.0.100]) > + > +AT_CHECK([ovn-nbctl static-mac-binding-del lr0-p0 10.0.0.10]) > +AT_CHECK([ovn-nbctl static-mac-binding-del lr0-p0 192.168.10.100]) > + > +AT_CHECK([ovn-nbctl static-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 static-mac-binding-del lr0-p1 10.0.0.10]) > +AT_CHECK([ovn-nbctl static-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 17d4f31b3..8665e6be9 100644 > --- a/tests/ovn-northd.at > +++ b/tests/ovn-northd.at > @@ -6474,4 +6474,27 @@ AT_CHECK([grep -e "ls_in_stateful" lsflows | sed > 's/table=../table=??/' | sort], > ]) > > AT_CLEANUP > -]) > + > +AT_SETUP([LR NB Static_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 static-mac-binding-add lr0-p0 192.168.10.10 00:00:11:22:33:44 > +ovn-nbctl static-mac-binding-add lr0-p0 192.168.10.100 00:00:22:33:44:55 > + > +wait_row_count nb:Static_MAC_Binding 2 logical_port=lr0-p0 > +wait_row_count Static_MAC_Binding 1 logical_port=lr0-p0 ip=192.168.10.10 > mac="00\:00\:11\:22\:33\:44" > +wait_row_count Static_MAC_Binding 1 logical_port=lr0-p0 ip=192.168.10.100 > mac="00\:00\:22\:33\:44\:55" > + > +ovn-nbctl static-mac-binding-add lr0-p1 10.0.0.10 00:00:33:44:55:66 > +wait_row_count nb:Static_MAC_Binding 1 logical_port=lr0-p1 > +wait_row_count Static_MAC_Binding 1 logical_port=lr0-p1 ip=10.0.0.10 > mac="00\:00\:33\:44\:55\:66" > + > +ovn-nbctl --may-exist static-mac-binding-add lr0-p0 192.168.10.100 > 00:00:22:33:55:66 > +wait_row_count Static_MAC_Binding 1 logical_port=lr0-p0 ip=192.168.10.100 > mac="00\:00\:22\:33\:55\:66" > + > +AT_CLEANUP > diff --git a/tests/ovn.at b/tests/ovn.at > index 166b5f72e..682a52394 100644 > --- a/tests/ovn.at > +++ b/tests/ovn.at > @@ -30082,3 +30082,93 @@ AT_CHECK([as hv1 ovs-ofctl -OOpenFlow15 dump-meters > br-int | grep -q rate=100], > OVN_CLEANUP([hv1]) > AT_CLEANUP > ]) > + > +AT_SETUP([ovn -- lr static_mac_binding]) > +AT_KEYWORDS([static_mac_binding]) > +ovn_start > + > +# Add chassis > +net_add n1 > +sim_add hv1 > +as hv1 > +ovs-vsctl add-br br-phys > +ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys > +ovn_attach n1 br-phys 192.168.0.1 > + > +# Create a logical router > +ovn-nbctl lr-add lr0 > +ovn-nbctl lrp-add lr0 lr0-ls0 00:00:11:11:22:22 20.0.0.1/24 > +ovn-nbctl lrp-add lr0 lr0-ext-ls0 00:00:11:11:33:33 172.16.1.10/24 > + > +# Create logical switch and connect to logical router > +ovn-nbctl ls-add ls0 > +ovn-nbctl lsp-add ls0 ls0-lr0 > +ovn-nbctl lsp-set-type ls0-lr0 router > +ovn-nbctl lsp-set-addresses ls0-lr0 router > +ovn-nbctl --wait=sb lsp-set-options ls0-lr0 router-port=lr0-ls0 > + > +# Create external gateway switch and connect to logical router > +ovn-nbctl ls-add ext-ls0 > +ovn-nbctl lsp-add ext-ls0 ext-ls0-lr0 > +ovn-nbctl lsp-set-type ext-ls0-lr0 router > +ovn-nbctl lsp-set-addresses ext-ls0-lr0 router > +ovn-nbctl --wait=sb lsp-set-options ext-ls0-lr0 router-port=lr0-ext-ls0 > + > +ovn-nbctl lsp-add ext-ls0 ln0 "" 1000 > +ovn-nbctl lsp-set-addresses ln0 unknown > +ovn-nbctl lsp-set-type ln0 localnet > +ovn-nbctl lsp-set-options ln0 network_name=phys > + > +# Add the lsp lp11 to ls0. This will map to VIF11. > +ovn-nbctl lsp-add ls0 lp11 > +ovn-nbctl lsp-set-addresses lp11 "00:00:11:11:44:44 20.0.0.10" > + > +# Add a vif on HV1 > +ovs-vsctl add-port br-int vif11 -- \ > + set Interface vif11 external-ids:iface-id=lp11 \ > + options:tx_pcap=hv1/vif11-tx.pcap \ > + options:rxq_pcap=hv1/vif11-rx.pcap \ > + ofport-request=11 > +OVS_WAIT_UNTIL([test x$(ovn-nbctl lsp-get-up lp11) = xup]) > + > +ovn-nbctl lrp-set-gateway-chassis lr0-ext-ls0 hv1 > + > +ovn-nbctl --wait=sb sync > +OVN_POPULATE_ARP > + > +ovn-nbctl --wait=hv lr-nat-add lr0 snat 172.16.1.10 20.0.0.0/24 > +ovn-nbctl --wait=hv lr-route-add lr0 0.0.0.0/0 172.16.1.1 > + > +test_mac_binding_flows() { > + local priority=$1 mac=$2 count=$3 > + OVS_WAIT_UNTIL([test $(ovs-ofctl dump-flows br-int | grep table=66 | > grep priority=${priority} | grep actions=mod_dl_dst:${mac} | wc -l) -eq > ${count}]) > +} > +# Create SB MAC_Binding entry on external gateway port > +lr0_dp_uuid=$(fetch_column datapath_binding _uuid external_ids:name=lr0) > + > +ovn-sbctl create mac_binding ip=172.16.1.1 logical_port=lr0-ext-ls0 > mac="00\:00\:11\:22\:33\:44" datapath=$lr0_dp_uuid > +test_mac_binding_flows 100 00:00:11:22:33:44 1 > + > +# Create Static_MAC_Binding entry on external gateway port. This should have > +# higher priority than MAC_Binding entry > +ovn-nbctl static-mac-binding-add lr0-ext-ls0 172.16.1.1 00:00:11:22:33:66 > +test_mac_binding_flows 50 00:00:11:22:33:66 1 > + > +# Update MAC for existing Static_MAC_Binding. Existing flow should be > updated. > +ovn-nbctl --may-exist static-mac-binding-add lr0-ext-ls0 172.16.1.1 > 00:00:11:22:33:88 > +test_mac_binding_flows 50 00:00:11:22:33:66 0 > +test_mac_binding_flows 50 00:00:11:22:33:88 1 > + > +# Update override_dynamic_mac for existing Static_MAC_Binding. Existing flow > should be updated. > +smb_uuid=$(fetch_column nb:static_mac_binding _uuid ip=172.16.1.1) > + > +ovn-nbctl set static_mac_binding $smb_uuid override_dynamic_mac=true > +test_mac_binding_flows 50 00:00:11:22:33:88 0 > +test_mac_binding_flows 150 00:00:11:22:33:88 1 > + > +# Delete Static_MAC_Binding. Higher priority flow should get deleted. > +ovn-nbctl static-mac-binding-del lr0-ext-ls0 172.16.1.1 > +test_mac_binding_flows 150 00:00:11:22:33:88 0 > + > +OVN_CLEANUP([hv1]) > +AT_CLEANUP > diff --git a/utilities/ovn-nbctl.c b/utilities/ovn-nbctl.c > index 7bcc2c66a..37cf4da35 100644 > --- a/utilities/ovn-nbctl.c > +++ b/utilities/ovn-nbctl.c > @@ -373,7 +373,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\ > @@ -416,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\ > @@ -452,6 +452,13 @@ Control Plane Protection Policy commands:\n\ > lr-copp-add NAME ROUTER\n\ > Add a NAME copp policy on ROUTER logical > router.\n\ > \n\ > +MAC_Binding commands:\n\ > + static-mac-binding-add LOGICAL_PORT IP MAC\n\ > + Add a Static_MAC_Binding entry\n\ > + static-mac-binding-del LOGICAL_PORT IP\n\ > + Delete Static_MAC_Binding entry\n\ > + static-mac-binding-list List all Static_MAC_Binding entries\n\ > +\n\ > %s\ > %s\ > \n\ > @@ -5721,6 +5728,144 @@ nbctl_lrp_get_redirect_type(struct ctl_context *ctx) > !redirect_type ? "overlay": redirect_type); > } > > +static const struct nbrec_static_mac_binding * > +static_mac_binding_by_port_ip(struct ctl_context *ctx, > + const char *logical_port, const char *ip) > +{ > + const struct nbrec_static_mac_binding *nb_smb = NULL; > + > + NBREC_STATIC_MAC_BINDING_FOR_EACH (nb_smb, ctx->idl) { > + if (!strcmp(nb_smb->logical_port, logical_port) && > + !strcmp(nb_smb->ip, ip)) { > + break; > + } > + } > + > + return nb_smb; > +} > + > +static void > +nbctl_pre_static_mac_binding(struct ctl_context *ctx) > +{ > + ovsdb_idl_add_column(ctx->idl, &nbrec_logical_router_port_col_name); > + > + ovsdb_idl_add_column(ctx->idl, > &nbrec_static_mac_binding_col_logical_port); > + ovsdb_idl_add_column(ctx->idl, &nbrec_static_mac_binding_col_ip); > + ovsdb_idl_add_column(ctx->idl, &nbrec_static_mac_binding_col_mac); > +} > + > +static void > +nbctl_static_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; > + return; > + } > + > + 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_static_mac_binding *nb_smb = > + static_mac_binding_by_port_ip(ctx, logical_port, ip); > + if (nb_smb) { > + if (may_exist) { > + if (strcmp(nb_smb->mac, mac)) { > + nbrec_static_mac_binding_verify_mac(nb_smb); > + nbrec_static_mac_binding_set_mac(nb_smb, mac); > + } > + } else { > + ctl_error(ctx, "%s, %s: a Static_MAC_Binding with this " > + "logical_port and ip already exists", > + logical_port, new_ip); > + } > + goto cleanup; > + } > + > + /* Create Static_MAC_Binding entry */ > + nb_smb = nbrec_static_mac_binding_insert(ctx->txn); > + nbrec_static_mac_binding_set_logical_port(nb_smb, logical_port); > + nbrec_static_mac_binding_set_ip(nb_smb, new_ip); > + nbrec_static_mac_binding_set_mac(nb_smb, mac); > + > +cleanup: > + free(new_ip); > +} > + > +static void > +nbctl_static_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_static_mac_binding *nb_smb = > + static_mac_binding_by_port_ip(ctx, logical_port, ip); > + > + if (nb_smb) { > + /* Remove the matching Static_MAC_Binding. */ > + nbrec_static_mac_binding_delete(nb_smb); > + goto cleanup; > + } > + > + if (must_exist) { > + ctl_error(ctx, "no matching Static_MAC_Binding with port %s and ip > %s", > + logical_port, ip); > + } > + > +cleanup: > + free(ip); > +} > + > +static void > +nbctl_static_mac_binding_list(struct ctl_context *ctx) > +{ > + struct smap lr_mac_bindings = SMAP_INITIALIZER(&lr_mac_bindings); > + const struct nbrec_static_mac_binding *nb_smb = NULL; > + NBREC_STATIC_MAC_BINDING_FOR_EACH (nb_smb, ctx->idl) { > + char *key = xasprintf("%-25s%-25s", nb_smb->logical_port, > nb_smb->ip); > + smap_add_format(&lr_mac_bindings, key, "%s", nb_smb->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) > { > @@ -7197,6 +7342,17 @@ static const struct ctl_command_syntax > nbctl_commands[] = { > pre_ha_ch_grp_set_chassis_prio, cmd_ha_ch_grp_set_chassis_prio, NULL, > "", RW }, > > + /* Static_MAC_Binding commands */ > + { "static-mac-binding-add", 3, 3, "LOGICAL_PORT IP MAC", > + nbctl_pre_static_mac_binding, nbctl_static_mac_binding_add, NULL, > + "--may-exist", RW }, > + { "static-mac-binding-del", 2, 2, "LOGICAL_PORT IP", > + nbctl_pre_static_mac_binding, nbctl_static_mac_binding_del, NULL, > + "--if-exists", RW }, > + { "static-mac-binding-list", 0, 0, "", > + nbctl_pre_static_mac_binding, nbctl_static_mac_binding_list, NULL, > + "", RO }, > + > {NULL, 0, 0, NULL, NULL, NULL, NULL, "", RO}, > }; > > -- > 2.25.1 > > _______________________________________________ > dev mailing list > d...@openvswitch.org<mailto:d...@openvswitch.org> > https://mail.openvswitch.org/mailman/listinfo/ovs-dev > _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev