From: Dumitru Ceara <dce...@redhat.com> This implements an an interface for maintaining (Linux) neighbor entries through Netlink. Netlink was chosen for the same reason it was selected for maintaining (Linux) route tables for control planes managed by OVN in 0ed52a4d5aaf ("controller: Introduce route-exchange-netlink.").
Signed-off-by: Dumitru Ceara <dce...@redhat.com> --- controller/automake.mk | 4 + controller/neighbor-exchange-netlink.c | 475 +++++++++++++++++++++++++ controller/neighbor-exchange-netlink.h | 66 ++++ controller/neighbor.c | 59 +++ controller/neighbor.h | 72 ++++ 5 files changed, 676 insertions(+) create mode 100644 controller/neighbor-exchange-netlink.c create mode 100644 controller/neighbor-exchange-netlink.h create mode 100644 controller/neighbor.c create mode 100644 controller/neighbor.h diff --git a/controller/automake.mk b/controller/automake.mk index f0638ea97..3eb45475c 100644 --- a/controller/automake.mk +++ b/controller/automake.mk @@ -30,6 +30,8 @@ controller_ovn_controller_SOURCES = \ controller/ofctrl.h \ controller/ofctrl-seqno.c \ controller/ofctrl-seqno.h \ + controller/neighbor.c \ + controller/neighbor.h \ controller/pinctrl.c \ controller/pinctrl.h \ controller/patch.c \ @@ -65,6 +67,8 @@ controller_ovn_controller_SOURCES = \ if HAVE_NETLINK controller_ovn_controller_SOURCES += \ + controller/neighbor-exchange-netlink.h \ + controller/neighbor-exchange-netlink.c \ controller/route-exchange-netlink.h \ controller/route-exchange-netlink.c \ controller/route-exchange.c \ diff --git a/controller/neighbor-exchange-netlink.c b/controller/neighbor-exchange-netlink.c new file mode 100644 index 000000000..f59aef6d3 --- /dev/null +++ b/controller/neighbor-exchange-netlink.c @@ -0,0 +1,475 @@ +/* Copyright (c) 2025, Red Hat, Inc. + * + * 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 <stdbool.h> +#include <linux/if_ether.h> +#include <linux/rtnetlink.h> + +#include "hmapx.h" +#include "lib/netlink.h" +#include "lib/netlink-socket.h" +#include "lib/packets.h" +#include "openvswitch/vlog.h" + +#include "neighbor-exchange-netlink.h" +#include "neighbor.h" + +VLOG_DEFINE_THIS_MODULE(neighbor_exchange_netlink); + +static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20); + +/* Inspired from route_table_dump_one_table() in OVS. */ +typedef void ne_table_handle_msg_callback(const struct ne_table_msg *, + void *aux); +static bool ne_table_dump_one_ifindex(unsigned char address_family, + int32_t if_index, + ne_table_handle_msg_callback *, + void *aux); +struct ne_msg_handle_data { + /* Stores 'struct advertise_neighbor_entry'. */ + struct hmapx *neighbors_to_advertise; + + /* Stores 'struct ne_nl_received_neigh'. */ + struct vector *learned_neighbors; + + /* Stores 'struct advertise_neighbor_entry'. */ + const struct hmap *neighbors; + + /* Filters only relevant neighbor entries to be learned. */ + ne_nl_neigh_filter *neigh_filter; + + /* Non-zero error code if any netlink operation failed. */ + int ret; +}; + +static void handle_ne_msg(const struct ne_table_msg *, void *data); + +static int ne_table_parse__(struct ofpbuf *, size_t ofs, + const struct nlmsghdr *, + const struct ndmsg *, + struct ne_table_msg *); +static int ne_nl_add_neigh(int32_t if_index, uint8_t family, + uint16_t state, uint8_t flags, + const struct eth_addr *, + const struct in6_addr *, + uint16_t port, uint16_t vlan); +static int ne_nl_del_neigh(int32_t if_index, uint8_t family, + const struct eth_addr *, + const struct in6_addr *, + uint16_t port, uint16_t vlan); + +/* Inserts all neigh entries listed in 'neighbors' (of type + * 'struct advertise_neighbor_entry') in the table associated to + * 'if_index'. Populates 'learned_neighbors' with all neigh entries + * (struct ne_nl_received_neigh) that exist in the table associated to + * 'if_index'. + * + * Optionally, 'neigh_filter' can be passed as argument to further filter + * what entries should be learned. + * + * Returns 0 on success, errno on failure. */ +int +ne_nl_sync_neigh(uint8_t family, int32_t if_index, + ne_nl_neigh_filter neigh_filter, + const struct hmap *neighbors, + struct vector *learned_neighbors) +{ + struct hmapx neighbors_to_advertise = + HMAPX_INITIALIZER(&neighbors_to_advertise); + struct advertise_neighbor_entry *an; + int ret; + + HMAP_FOR_EACH (an, node, neighbors) { + hmapx_add(&neighbors_to_advertise, an); + } + + struct ne_msg_handle_data data = { + .neighbors = neighbors, + .neighbors_to_advertise = &neighbors_to_advertise, + .learned_neighbors = learned_neighbors, + .neigh_filter = neigh_filter, + }; + ne_table_dump_one_ifindex(family, if_index, handle_ne_msg, &data); + ret = data.ret; + + /* Add any remaining routes in the routes_to_advertise hmapx to the + * system routing table. */ + struct hmapx_node *hn; + HMAPX_FOR_EACH (hn, &neighbors_to_advertise) { + an = hn->data; + int err = ne_nl_add_neigh(if_index, family, + NUD_NOARP, /* state = static */ + 0, /* flags */ + &an->lladdr, &an->addr, + 0, /* port */ + 0); /* vlan */ + if (err) { + char addr_s[INET6_ADDRSTRLEN + 1]; + VLOG_WARN_RL(&rl, "Add neigh ifindex=%"PRId32" eth=" ETH_ADDR_FMT + " dst=%s: %s", + if_index, ETH_ADDR_ARGS(an->lladdr), + ipv6_string_mapped( + addr_s, &an->addr) ? addr_s : "(invalid)", + ovs_strerror(err)); + if (!ret) { + /* Report the first error value to the caller. */ + ret = err; + } + } + } + hmapx_destroy(&neighbors_to_advertise); + return ret; +} + +/* OVN expects all static entries added on this ifindex to be OVN-owned. + * Everything else must be learnt. */ +bool +ne_is_ovn_owned(const struct ne_nl_received_neigh *nd) +{ + return !(nd->state & NUD_PERMANENT) && (nd->state & NUD_NOARP) + && !(nd->flags & NTF_EXT_LEARNED); +} + +static bool +ne_table_dump_one_ifindex(unsigned char address_family, int32_t if_index, + ne_table_handle_msg_callback *handle_msg_cb, + void *aux) +{ + uint64_t reply_stub[NL_DUMP_BUFSIZE / 8]; + struct ofpbuf request, reply, buf; + struct ndmsg *rq_msg; + bool filtered = true; + struct nl_dump dump; + + ofpbuf_init(&request, 0); + + nl_msg_put_nlmsghdr(&request, sizeof *rq_msg, RTM_GETNEIGH, NLM_F_REQUEST); + rq_msg = ofpbuf_put_zeros(&request, sizeof *rq_msg); + rq_msg->ndm_family = address_family; + if (if_index) { + nl_msg_put_u32(&request, NDA_IFINDEX, if_index); + } + + nl_dump_start(&dump, NETLINK_ROUTE, &request); + ofpbuf_uninit(&request); + + ofpbuf_use_stub(&buf, reply_stub, sizeof reply_stub); + while (nl_dump_next(&dump, &reply, &buf)) { + struct ne_table_msg msg; + + if (ne_table_parse(&reply, &msg)) { + struct nlmsghdr *nlmsghdr = nl_msg_nlmsghdr(&reply); + + /* Older kernels do not support filtering. */ + if (!(nlmsghdr->nlmsg_flags & NLM_F_DUMP_FILTERED)) { + filtered = false; + } + handle_msg_cb(&msg, aux); + } + } + ofpbuf_uninit(&buf); + nl_dump_done(&dump); + + return filtered; +} + +static int +ne_table_parse__(struct ofpbuf *buf, size_t ofs, const struct nlmsghdr *nlmsg, + const struct ndmsg *nd, struct ne_table_msg *change) +{ + bool parsed; + + static const struct nl_policy policy[] = { + [NDA_DST] = { .type = NL_A_U32, .optional = true }, + [NDA_LLADDR] = { .type = NL_A_LL_ADDR, .optional = true }, + [NDA_PORT] = { .type = NL_A_U16, .optional = true }, + }; + + static const struct nl_policy policy6[] = { + [NDA_DST] = { .type = NL_A_IPV6, .optional = true }, + [NDA_LLADDR] = { .type = NL_A_LL_ADDR, .optional = true }, + [NDA_PORT] = { .type = NL_A_U16, .optional = true }, + }; + + static const struct nl_policy policy_bridge[] = { + [NDA_DST] = { .type = NL_A_UNSPEC, .optional = true, + .min_len = sizeof(struct in_addr), + .max_len = sizeof(struct in6_addr)}, + [NDA_LLADDR] = { .type = NL_A_LL_ADDR, .optional = true }, + [NDA_PORT] = { .type = NL_A_U16, .optional = true }, + [NDA_VLAN] = { .type = NL_A_U16, .optional = true }, + }; + + BUILD_ASSERT(ARRAY_SIZE(policy) == ARRAY_SIZE(policy6)); + BUILD_ASSERT(ARRAY_SIZE(policy) == ARRAY_SIZE(policy_bridge)); + struct nlattr *attrs[ARRAY_SIZE(policy)]; + + if (nd->ndm_family == AF_INET) { + parsed = nl_policy_parse(buf, ofs, policy, attrs, + ARRAY_SIZE(policy)); + } else if (nd->ndm_family == AF_INET6) { + parsed = nl_policy_parse(buf, ofs, policy6, attrs, + ARRAY_SIZE(policy6)); + } else if (nd->ndm_family == AF_BRIDGE) { + parsed = nl_policy_parse(buf, ofs, policy_bridge, attrs, + ARRAY_SIZE(policy_bridge)); + } else { + VLOG_WARN_RL(&rl, "received non AF_INET/AF_INET6/AF_BRIDGE rtnetlink " + "neigh message"); + return 0; + } + + if (parsed) { + memset(change, 0, sizeof *change); + + change->nlmsg_type = nlmsg->nlmsg_type; + change->nd.if_index = nd->ndm_ifindex; + change->nd.family = nd->ndm_family; + change->nd.state = nd->ndm_state; + change->nd.flags = nd->ndm_flags; + change->nd.type = nd->ndm_type; + + if (attrs[NDA_DST]) { + size_t nda_dst_size = nl_attr_get_size(attrs[NDA_DST]); + + switch (nda_dst_size) { + case sizeof(uint32_t): + in6_addr_set_mapped_ipv4(&change->nd.addr, + nl_attr_get_be32(attrs[NDA_DST])); + break; + case sizeof(struct in6_addr): + change->nd.addr = nl_attr_get_in6_addr(attrs[NDA_DST]); + break; + default: + VLOG_DBG_RL(&rl, + "neigh message contains non-IPv4/IPv6 NDA_DST"); + return 0; + } + } + + if (attrs[NDA_LLADDR]) { + if (nl_attr_get_size(attrs[NDA_LLADDR]) != ETH_ALEN) { + VLOG_DBG_RL(&rl, "neigh message contains non-ETH NDA_LLADDR"); + return 0; + } + change->nd.lladdr = nl_attr_get_eth_addr(attrs[NDA_LLADDR]); + } + + if (attrs[NDA_PORT]) { + change->nd.port = nl_attr_get_u16(attrs[NDA_PORT]); + } + + if (attrs[NDA_VLAN]) { + change->nd.vlan = nl_attr_get_u16(attrs[NDA_VLAN]); + } + } else { + VLOG_DBG_RL(&rl, "received unparseable rtnetlink neigh message"); + return 0; + } + + /* Success. */ + return RTNLGRP_NEIGH; +} + +static void +handle_ne_msg(const struct ne_table_msg *msg, void *data) +{ + struct ne_msg_handle_data *handle_data = data; + const struct ne_nl_received_neigh *nd = &msg->nd; + + if (!ne_is_ovn_owned(nd)) { + if (!handle_data->learned_neighbors) { + return; + } + + /* Learn the non-OVN entry if it matches the filter or if no + * filtering was requested. */ + if (!handle_data->neigh_filter || handle_data->neigh_filter(nd)) { + vector_push(handle_data->learned_neighbors, nd); + } + return; + } + + /* This neighbor was presumably added by OVN, see if it's still valid. + * OVN only adds neighbors with vlan and port set to 0, all others + * can be removed. */ + if (!nd->vlan && !nd->port && handle_data->neighbors_to_advertise) { + uint32_t hash = advertise_neigh_hash(&nd->lladdr, &nd->addr); + struct advertise_neighbor_entry *an; + HMAP_FOR_EACH_WITH_HASH (an, node, hash, handle_data->neighbors) { + if (eth_addr_equals(an->lladdr, nd->lladdr) + && ipv6_addr_equals(&an->addr, &nd->addr)) { + hmapx_find_and_delete(handle_data->neighbors_to_advertise, an); + return; + } + } + } + int err = ne_nl_del_neigh(nd->if_index, nd->family, + &nd->lladdr, &nd->addr, + nd->port, nd->vlan); + if (err) { + char addr_s[INET6_ADDRSTRLEN + 1]; + VLOG_WARN_RL(&rl, "Delete neigh ifindex=%"PRId32" eth=" ETH_ADDR_FMT + " dst=%s port=%"PRIu16" failed: %s", + nd->if_index, ETH_ADDR_ARGS(nd->lladdr), + ipv6_string_mapped(addr_s, &nd->addr) + ? addr_s : "(invalid)", + nd->port, + ovs_strerror(err)); + + if (!handle_data->ret) { + /* Report the first error value to the caller. */ + handle_data->ret = err; + } + } +} + +static int +ne_nl_add_neigh(int32_t if_index, uint8_t family, + uint16_t state, uint8_t flags, + const struct eth_addr *lladdr, + const struct in6_addr *addr, + uint16_t port, uint16_t vlan) +{ + uint32_t nl_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL; + bool dst_set = !ipv6_is_zero(addr); + bool is_ipv4 = IN6_IS_ADDR_V4MAPPED(addr); + struct ofpbuf request; + + ofpbuf_init(&request, 0); + nl_msg_put_nlmsghdr(&request, 0, RTM_NEWNEIGH, nl_flags); + + struct ndmsg *nd = ofpbuf_put_zeros(&request, sizeof *nd); + nd->ndm_family = family; + nd->ndm_ifindex = if_index; + nd->ndm_state = state; + nd->ndm_flags = flags; + + nl_msg_put_unspec(&request, NDA_LLADDR, lladdr, sizeof *lladdr); + if (dst_set) { + if (is_ipv4) { + nl_msg_put_be32(&request, NDA_DST, in6_addr_get_mapped_ipv4(addr)); + } else { + nl_msg_put_in6_addr(&request, NDA_DST, addr); + } + } + if (port) { + nl_msg_put_u16(&request, NDA_PORT, port); + } + if (vlan) { + nl_msg_put_u16(&request, NDA_VLAN, vlan); + } + + if (VLOG_IS_DBG_ENABLED()) { + struct ds msg = DS_EMPTY_INITIALIZER; + + ds_put_format(&msg, "Adding neighbor ifindex %"PRId32 " for eth " + ETH_ADDR_FMT " port %"PRIu16" vlan %"PRIu16, + if_index, ETH_ADDR_ARGS(*lladdr), + port, vlan); + if (dst_set) { + if (is_ipv4) { + ds_put_format(&msg, IP_FMT, + IP_ARGS(in6_addr_get_mapped_ipv4(addr))); + } else { + ipv6_format_addr(addr, &msg); + } + } + VLOG_DBG("%s", ds_cstr(&msg)); + ds_destroy(&msg); + } + + int err = nl_transact(NETLINK_ROUTE, &request, NULL); + + ofpbuf_uninit(&request); + return err; +} + +static int +ne_nl_del_neigh(int32_t if_index, uint8_t family, + const struct eth_addr *lladdr, + const struct in6_addr *addr, + uint16_t port, uint16_t vlan) +{ + uint32_t flags = NLM_F_REQUEST | NLM_F_ACK; + bool dst_set = !ipv6_is_zero(addr); + bool is_ipv4 = IN6_IS_ADDR_V4MAPPED(addr); + struct ofpbuf request; + + ofpbuf_init(&request, 0); + nl_msg_put_nlmsghdr(&request, 0, RTM_DELNEIGH, flags); + + struct ndmsg *nd = ofpbuf_put_zeros(&request, sizeof *nd); + nd->ndm_family = family; + nd->ndm_ifindex = if_index; + + nl_msg_put_unspec(&request, NDA_LLADDR, lladdr, sizeof *lladdr); + if (dst_set) { + if (is_ipv4) { + nl_msg_put_be32(&request, NDA_DST, in6_addr_get_mapped_ipv4(addr)); + } else { + nl_msg_put_in6_addr(&request, NDA_DST, addr); + } + } + if (port) { + nl_msg_put_u16(&request, NDA_PORT, port); + } + if (vlan) { + nl_msg_put_u16(&request, NDA_VLAN, vlan); + } + + if (VLOG_IS_DBG_ENABLED()) { + struct ds msg = DS_EMPTY_INITIALIZER; + + ds_put_format(&msg, "Removing neighbor ifindex %"PRId32 " for eth " + ETH_ADDR_FMT " port %"PRIu16" vlan %"PRIu16, + if_index, ETH_ADDR_ARGS(*lladdr), + port, vlan); + if (dst_set) { + if (is_ipv4) { + ds_put_format(&msg, IP_FMT, + IP_ARGS(in6_addr_get_mapped_ipv4(addr))); + } else { + ipv6_format_addr(addr, &msg); + } + } + VLOG_DBG("%s", ds_cstr(&msg)); + ds_destroy(&msg); + } + + int err = nl_transact(NETLINK_ROUTE, &request, NULL); + + ofpbuf_uninit(&request); + return err; +} + +/* Parse Netlink message in buf, which is expected to contain a UAPI ndmsg + * header and associated neighbor attributes. + * + * Return RTNLGRP_NEIGH on success, and 0 on a parse error. */ +int +ne_table_parse(struct ofpbuf *buf, void *change) +{ + struct nlmsghdr *nlmsg = ofpbuf_at(buf, 0, NLMSG_HDRLEN); + struct ndmsg *nd = ofpbuf_at(buf, NLMSG_HDRLEN, sizeof *nd); + + if (!nlmsg || !nd) { + return 0; + } + + return ne_table_parse__(buf, NLMSG_HDRLEN + sizeof *nd, + nlmsg, nd, change); +} diff --git a/controller/neighbor-exchange-netlink.h b/controller/neighbor-exchange-netlink.h new file mode 100644 index 000000000..758007d1e --- /dev/null +++ b/controller/neighbor-exchange-netlink.h @@ -0,0 +1,66 @@ +/* Copyright (c) 2025, Red Hat, Inc. + * + * 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 NEIGHBOR_EXCHANGE_NETLINK_H +#define NEIGHBOR_EXCHANGE_NETLINK_H 1 + +#include <netinet/in.h> +#include <stdint.h> + +#include "openvswitch/hmap.h" +#include "openvswitch/ofpbuf.h" + +#include "vec.h" + +struct ne_nl_received_neigh { + int32_t if_index; + uint8_t family; /* AF_INET/AF_INET6/AF_BRIDGE. */ + + struct eth_addr lladdr; /* Interface index where the neigh is learnt on. */ + struct in6_addr addr; /* In case of 'dst' entries non-zero; + * all zero otherwise. */ + uint16_t vlan; /* Parsed from NDA_VLAN. */ + uint16_t port; /* UDP port, e.g., for VXLAN, + * parsed from NDA_PORT. */ + uint16_t state; /* A value out of NUD_*, + * from linux/neighbour.h. */ + uint8_t flags; /* A combination of NTF_* flags, + * from linux/neighbour.h. */ + uint8_t type; /* A value out of 'rtm_type' from linux/rtnetlink.h + * e.g., RTN_UNICAST, RTN_MULTICAST. */ +}; + +/* A digested version of a neigh message sent down by the kernel to indicate + * that a neigh entry has changed. */ +struct ne_table_msg { + uint16_t nlmsg_type; /* E.g. RTM_NEWNEIGH, RTM_DELNEIGH. */ + struct ne_nl_received_neigh nd; /* Data parsed from this message. */ +}; + +/* Callback to be used by callers of 'ne_nl_sync_neigh' in order to + * filter what neighbor entries should be learned. It must return 'true' + * in those cases and 'false' otherwise. */ +typedef bool ne_nl_neigh_filter(const struct ne_nl_received_neigh *); + +int ne_nl_sync_neigh(uint8_t family, int32_t if_index, + ne_nl_neigh_filter neigh_filter, + const struct hmap *neighbors, + struct vector *learned_neighbors); + +bool ne_is_ovn_owned(const struct ne_nl_received_neigh *nd); + +int ne_table_parse(struct ofpbuf *, void *change); + +#endif /* NEIGHBOR_EXCHANGE_NETLINK_H */ diff --git a/controller/neighbor.c b/controller/neighbor.c new file mode 100644 index 000000000..229c679dd --- /dev/null +++ b/controller/neighbor.c @@ -0,0 +1,59 @@ +/* Copyright (c) 2025, Red Hat, Inc. + * + * 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/hash.h" +#include "lib/packets.h" +#include "lib/sset.h" + +#include "neighbor.h" + +static void neighbor_interface_monitor_destroy( + struct neighbor_interface_monitor *); + +uint32_t +advertise_neigh_hash(const struct eth_addr *eth, const struct in6_addr *ip) +{ + return hash_bytes(ip, sizeof *ip, hash_bytes(eth, sizeof *eth, 0)); +} + +void +neighbor_run(struct neighbor_ctx_in *n_ctx_in OVS_UNUSED, + struct neighbor_ctx_out *n_ctx_out OVS_UNUSED) +{ + /* XXX: Not implemented yet. */ +} + +void +neighbor_cleanup(struct vector *monitored_interfaces) +{ + struct neighbor_interface_monitor *nim; + VECTOR_FOR_EACH (monitored_interfaces, nim) { + neighbor_interface_monitor_destroy(nim); + } + vector_clear(monitored_interfaces); +} + +static void +neighbor_interface_monitor_destroy(struct neighbor_interface_monitor *nim) +{ + struct advertise_neighbor_entry *an; + + HMAP_FOR_EACH_POP (an, node, &nim->announced_neighbors) { + free(an); + } + free(nim); +} diff --git a/controller/neighbor.h b/controller/neighbor.h new file mode 100644 index 000000000..3abc6e923 --- /dev/null +++ b/controller/neighbor.h @@ -0,0 +1,72 @@ +/* Copyright (c) 2025, Red Hat, Inc. + * + * 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 NEIGHBOR_H +#define NEIGHBOR_H 1 + +#include <sys/types.h> +#include <netinet/in.h> +#include <net/if.h> +#include <stdint.h> + +#include "lib/sset.h" +#include "openvswitch/hmap.h" + +#include "vec.h" + +/* XXX: AF_BRIDGE doesn't seem to be defined on OSX. */ +#ifdef __APPLE__ +#ifndef AF_BRIDGE +#define AF_BRIDGE AF_UNSPEC +#endif +#endif /* __APPLE__ */ + +enum neighbor_family { + NEIGH_AF_INET = AF_INET, + NEIGH_AF_INET6 = AF_INET6, + NEIGH_AF_BRIDGE = AF_BRIDGE, +}; + +struct neighbor_ctx_in { +}; + +struct neighbor_ctx_out { + /* Contains struct neighbor_interface_monitor pointers. */ + struct vector *monitored_interfaces; +}; + +struct neighbor_interface_monitor { + enum neighbor_family family; + char if_name[IFNAMSIZ + 1]; + + /* Contains struct advertise_neighbor_entry - the entries that OVN + * advertises on this interface. */ + struct hmap announced_neighbors; +}; + +struct advertise_neighbor_entry { + struct hmap_node node; + + struct eth_addr lladdr; + struct in6_addr addr; /* In case of 'dst' entries non-zero; + * all zero otherwise. */ +}; + +uint32_t advertise_neigh_hash(const struct eth_addr *, + const struct in6_addr *); +void neighbor_run(struct neighbor_ctx_in *, struct neighbor_ctx_out *); +void neighbor_cleanup(struct vector *monitored_interfaces); + +#endif /* NEIGHBOR_H */ -- 2.50.0 _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev