]> git.proxmox.com Git - mirror_frr.git/blobdiff - zebra/rt_netlink.c
zebra: Create zebra_dplane.c and .h
[mirror_frr.git] / zebra / rt_netlink.c
index 485abc3f1230331d214f325be3922f6af3a0fb3d..fe42ab5be601376857fe4e6c807eddd278f9fabc 100644 (file)
@@ -64,6 +64,7 @@
 #include "zebra/rt_netlink.h"
 #include "zebra/zebra_mroute.h"
 #include "zebra/zebra_vxlan.h"
+#include "zebra/zebra_errors.h"
 
 #ifndef AF_MPLS
 #define AF_MPLS 28
@@ -99,7 +100,7 @@ static inline int is_selfroute(int proto)
            || (proto == RTPROT_NHRP) || (proto == RTPROT_EIGRP)
            || (proto == RTPROT_LDP) || (proto == RTPROT_BABEL)
            || (proto == RTPROT_RIP) || (proto == RTPROT_SHARP)
-           || (proto == RTPROT_PBR)) {
+           || (proto == RTPROT_PBR) || (proto == RTPROT_OPENFABRIC)) {
                return 1;
        }
 
@@ -146,6 +147,9 @@ static inline int zebra2proto(int proto)
        case ZEBRA_ROUTE_PBR:
                proto = RTPROT_PBR;
                break;
+       case ZEBRA_ROUTE_OPENFABRIC:
+               proto = RTPROT_OPENFABRIC;
+               break;
        default:
                /*
                 * When a user adds a new protocol this will show up
@@ -153,8 +157,9 @@ static inline int zebra2proto(int proto)
                 * is intentionally a warn because we should see
                 * this as part of development of a new protocol
                 */
-               zlog_warn("%s: Please add this protocol(%d) to proper rt_netlink.c handling",
-                         __PRETTY_FUNCTION__, proto);
+               zlog_debug(
+                       "%s: Please add this protocol(%d) to proper rt_netlink.c handling",
+                       __PRETTY_FUNCTION__, proto);
                proto = RTPROT_ZEBRA;
                break;
        }
@@ -203,6 +208,9 @@ static inline int proto2zebra(int proto, int family)
        case RTPROT_PBR:
                proto = ZEBRA_ROUTE_PBR;
                break;
+       case RTPROT_OPENFABRIC:
+               proto = ZEBRA_ROUTE_OPENFABRIC;
+               break;
        default:
                /*
                 * When a user adds a new protocol this will show up
@@ -210,9 +218,9 @@ static inline int proto2zebra(int proto, int family)
                 * is intentionally a warn because we should see
                 * this as part of development of a new protocol
                 */
-               zlog_warn("%s: Please add this protocol(%d) to proper rt_netlink.c handling",
-                         __PRETTY_FUNCTION__,
-                         proto);
+               zlog_debug(
+                       "%s: Please add this protocol(%d) to proper rt_netlink.c handling",
+                       __PRETTY_FUNCTION__, proto);
                proto = ZEBRA_ROUTE_KERNEL;
                break;
        }
@@ -246,6 +254,33 @@ static vrf_id_t vrf_lookup_by_table(uint32_t table_id, ns_id_t ns_id)
        return VRF_DEFAULT;
 }
 
+/**
+ * @parse_encap_mpls() - Parses encapsulated mpls attributes
+ * @tb:         Pointer to rtattr to look for nested items in.
+ * @labels:     Pointer to store labels in.
+ *
+ * Return:      Number of mpls labels found.
+ */
+static int parse_encap_mpls(struct rtattr *tb, mpls_label_t *labels)
+{
+       struct rtattr *tb_encap[MPLS_IPTUNNEL_MAX + 1] = {0};
+       mpls_lse_t *lses = NULL;
+       int num_labels = 0;
+       uint32_t ttl = 0;
+       uint32_t bos = 0;
+       uint32_t exp = 0;
+       mpls_label_t label = 0;
+
+       netlink_parse_rtattr_nested(tb_encap, MPLS_IPTUNNEL_MAX, tb);
+       lses = (mpls_lse_t *)RTA_DATA(tb_encap[MPLS_IPTUNNEL_DST]);
+       while (!bos && num_labels < MPLS_MAX_LABELS) {
+               mpls_lse_decode(lses[num_labels], &label, &ttl, &exp, &bos);
+               labels[num_labels++] = label;
+       }
+
+       return num_labels;
+}
+
 /* Looking up routing table by netlink interface. */
 static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id,
                                             int startup)
@@ -274,6 +309,10 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id,
        void *src = NULL;     /* IPv6 srcdest   source prefix */
        enum blackhole_type bh_type = BLACKHOLE_UNSPEC;
 
+       /* MPLS labels */
+       mpls_label_t labels[MPLS_MAX_LABELS] = {0};
+       int num_labels = 0;
+
        rtm = NLMSG_DATA(h);
 
        if (startup && h->nlmsg_type != RTM_NEWROUTE)
@@ -384,29 +423,48 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id,
 
        if (rtm->rtm_family == AF_INET) {
                p.family = AF_INET;
+               if (rtm->rtm_dst_len > IPV4_MAX_BITLEN) {
+                       zlog_err(
+                               "Invalid destination prefix length: %u received from kernel route change",
+                               rtm->rtm_dst_len);
+                       return -1;
+               }
                memcpy(&p.u.prefix4, dest, 4);
                p.prefixlen = rtm->rtm_dst_len;
 
-               src_p.prefixlen =
-                       0; // Forces debug below to not display anything
+               if (rtm->rtm_src_len != 0) {
+                       char buf[PREFIX_STRLEN];
+                       flog_warn(
+                               EC_ZEBRA_UNSUPPORTED_V4_SRCDEST,
+                               "unsupported IPv4 sourcedest route (dest %s vrf %u)",
+                               prefix2str(&p, buf, sizeof(buf)), vrf_id);
+                       return 0;
+               }
+
+               /* Force debug below to not display anything for source */
+               src_p.prefixlen = 0;
        } else if (rtm->rtm_family == AF_INET6) {
                p.family = AF_INET6;
+               if (rtm->rtm_dst_len > IPV6_MAX_BITLEN) {
+                       zlog_err(
+                               "Invalid destination prefix length: %u received from kernel route change",
+                               rtm->rtm_dst_len);
+                       return -1;
+               }
                memcpy(&p.u.prefix6, dest, 16);
                p.prefixlen = rtm->rtm_dst_len;
 
                src_p.family = AF_INET6;
+               if (rtm->rtm_src_len > IPV6_MAX_BITLEN) {
+                       zlog_err(
+                               "Invalid source prefix length: %u received from kernel route change",
+                               rtm->rtm_src_len);
+                       return -1;
+               }
                memcpy(&src_p.prefix, src, 16);
                src_p.prefixlen = rtm->rtm_src_len;
        }
 
-       if (rtm->rtm_src_len != 0) {
-               char buf[PREFIX_STRLEN];
-               zlog_warn(
-                       "unsupported IPv[4|6] sourcedest route (dest %s vrf %u)",
-                       prefix2str(&p, buf, sizeof(buf)), vrf_id);
-               return 0;
-       }
-
        /*
         * For ZEBRA_ROUTE_KERNEL types:
         *
@@ -491,8 +549,19 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id,
                        }
                        nh.vrf_id = nh_vrf_id;
 
+                       if (tb[RTA_ENCAP] && tb[RTA_ENCAP_TYPE]
+                           && *(uint16_t *)RTA_DATA(tb[RTA_ENCAP_TYPE])
+                                      == LWTUNNEL_ENCAP_MPLS) {
+                               num_labels =
+                                       parse_encap_mpls(tb[RTA_ENCAP], labels);
+                       }
+
+                       if (num_labels)
+                               nexthop_add_labels(&nh, ZEBRA_LSP_STATIC,
+                                                  num_labels, labels);
+
                        rib_add(afi, SAFI_UNICAST, vrf_id, proto, 0, flags, &p,
-                               NULL, &nh, table, metric, mtu, distance, tag);
+                               &src_p, &nh, table, metric, mtu, distance, tag);
                } else {
                        /* This is a multipath route */
 
@@ -515,7 +584,8 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id,
                        re->tag = tag;
 
                        for (;;) {
-                               vrf_id_t nh_vrf_id;
+                               struct nexthop *nh = NULL;
+
                                if (len < (int)sizeof(*rtnh)
                                    || rtnh->rtnh_len > len)
                                        break;
@@ -534,7 +604,8 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id,
                                        if (ifp)
                                                nh_vrf_id = ifp->vrf_id;
                                        else {
-                                               zlog_warn(
+                                               flog_warn(
+                                                       EC_ZEBRA_UNKNOWN_INTERFACE,
                                                        "%s: Unknown interface %u specified, defaulting to VRF_DEFAULT",
                                                        __PRETTY_FUNCTION__,
                                                        index);
@@ -552,35 +623,49 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id,
                                        if (tb[RTA_GATEWAY])
                                                gate = RTA_DATA(
                                                        tb[RTA_GATEWAY]);
+                                       if (tb[RTA_ENCAP] && tb[RTA_ENCAP_TYPE]
+                                           && *(uint16_t *)RTA_DATA(
+                                                      tb[RTA_ENCAP_TYPE])
+                                                      == LWTUNNEL_ENCAP_MPLS) {
+                                               num_labels = parse_encap_mpls(
+                                                       tb[RTA_ENCAP], labels);
+                                       }
                                }
 
                                if (gate) {
                                        if (rtm->rtm_family == AF_INET) {
                                                if (index)
-                                                       route_entry_nexthop_ipv4_ifindex_add(
+                                                       nh = route_entry_nexthop_ipv4_ifindex_add(
                                                                re, gate,
                                                                prefsrc, index,
                                                                nh_vrf_id);
                                                else
-                                                       route_entry_nexthop_ipv4_add(
+                                                       nh = route_entry_nexthop_ipv4_add(
                                                                re, gate,
                                                                prefsrc,
                                                                nh_vrf_id);
                                        } else if (rtm->rtm_family
                                                   == AF_INET6) {
                                                if (index)
-                                                       route_entry_nexthop_ipv6_ifindex_add(
+                                                       nh = route_entry_nexthop_ipv6_ifindex_add(
                                                                re, gate, index,
                                                                nh_vrf_id);
                                                else
-                                                       route_entry_nexthop_ipv6_add(
+                                                       nh = route_entry_nexthop_ipv6_add(
                                                                re, gate,
                                                                nh_vrf_id);
                                        }
                                } else
-                                       route_entry_nexthop_ifindex_add(
+                                       nh = route_entry_nexthop_ifindex_add(
                                                re, index, nh_vrf_id);
 
+                               if (nh && num_labels)
+                                       nexthop_add_labels(nh, ZEBRA_LSP_STATIC,
+                                                          num_labels, labels);
+
+                               if (rtnh->rtnh_len == 0)
+                                       break;
+
                                len -= NLMSG_ALIGN(rtnh->rtnh_len);
                                rtnh = RTNH_NEXT(rtnh);
                        }
@@ -591,8 +676,8 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id,
                        if (re->nexthop_num == 0)
                                XFREE(MTYPE_RE, re);
                        else
-                               rib_add_multipath(afi, SAFI_UNICAST, &p, NULL,
-                                                 re);
+                               rib_add_multipath(afi, SAFI_UNICAST, &p,
+                                                 &src_p, re);
                }
        } else {
                if (!tb[RTA_MULTIPATH]) {
@@ -624,12 +709,14 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id,
                        if (gate)
                                memcpy(&nh.gate, gate, sz);
                        rib_delete(afi, SAFI_UNICAST, vrf_id, proto, 0, flags,
-                                  &p, NULL, &nh, table, metric, true);
+                                  &p, &src_p, &nh, table, metric, distance,
+                                  true);
                } else {
                        /* XXX: need to compare the entire list of nexthops
                         * here for NLM_F_APPEND stupidity */
                        rib_delete(afi, SAFI_UNICAST, vrf_id, proto, 0, flags,
-                                  &p, NULL, NULL, table, metric, true);
+                                  &p, &src_p, NULL, table, metric, distance,
+                                  true);
                }
        }
 
@@ -701,6 +788,9 @@ static int netlink_route_change_read_multicast(struct nlmsghdr *h,
                        oif[oif_count] = rtnh->rtnh_ifindex;
                        oif_count++;
 
+                       if (rtnh->rtnh_len == 0)
+                               break;
+
                        len -= NLMSG_ALIGN(rtnh->rtnh_len);
                        rtnh = RTNH_NEXT(rtnh);
                }
@@ -714,15 +804,17 @@ static int netlink_route_change_read_multicast(struct nlmsghdr *h,
                        ifp = if_lookup_by_index(oif[count], vrf);
                        char temp[256];
 
-                       sprintf(temp, "%s ", ifp->name);
+                       sprintf(temp, "%s(%d) ", ifp ? ifp->name : "Unknown",
+                               oif[count]);
                        strcat(oif_list, temp);
                }
                struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(vrf);
                ifp = if_lookup_by_index(iif, vrf);
-               zlog_debug(
-                       "MCAST VRF: %s(%d) %s (%s,%s) IIF: %s OIF: %s jiffies: %lld",
-                       zvrf->vrf->name, vrf, nl_msg_type_to_str(h->nlmsg_type),
-                       sbuf, gbuf, ifp->name, oif_list, m->lastused);
+               zlog_debug("MCAST VRF: %s(%d) %s (%s,%s) IIF: %s(%d) OIF: %s jiffies: %lld",
+                          zvrf->vrf->name, vrf,
+                          nl_msg_type_to_str(h->nlmsg_type),
+                          sbuf, gbuf, ifp ? ifp->name : "Unknown", iif,
+                          oif_list, m->lastused);
        }
        return 0;
 }
@@ -736,7 +828,18 @@ int netlink_route_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
 
        if (!(h->nlmsg_type == RTM_NEWROUTE || h->nlmsg_type == RTM_DELROUTE)) {
                /* If this is not route add/delete message print warning. */
-               zlog_warn("Kernel message: %d NS %u\n", h->nlmsg_type, ns_id);
+               zlog_debug("Kernel message: %s NS %u\n",
+                          nl_msg_type_to_str(h->nlmsg_type), ns_id);
+               return 0;
+       }
+
+       if (!(rtm->rtm_family == AF_INET ||
+             rtm->rtm_family == AF_INET6 ||
+             rtm->rtm_family == RTNL_FAMILY_IPMR )) {
+               flog_warn(
+                       EC_ZEBRA_UNKNOWN_FAMILY,
+                       "Invalid address family: %u received from kernel route change: %s",
+                       rtm->rtm_family, nl_msg_type_to_str(h->nlmsg_type));
                return 0;
        }
 
@@ -748,10 +851,6 @@ int netlink_route_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
                           nl_rttype_to_str(rtm->rtm_type),
                           nl_rtproto_to_str(rtm->rtm_protocol), ns_id);
 
-       /* We don't care about change notifications for the MPLS table. */
-       /* TODO: Revisit this. */
-       if (rtm->rtm_family == AF_MPLS)
-               return 0;
 
        len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct rtmsg));
        if (len < 0) {
@@ -1668,6 +1767,7 @@ skip:
 
 int kernel_get_ipmr_sg_stats(struct zebra_vrf *zvrf, void *in)
 {
+       uint32_t actual_table;
        int suc = 0;
        struct mcast_route_data *mr = (struct mcast_route_data *)in;
        struct {
@@ -1693,7 +1793,23 @@ int kernel_get_ipmr_sg_stats(struct zebra_vrf *zvrf, void *in)
        addattr_l(&req.n, sizeof(req), RTA_OIF, &mroute->ifindex, 4);
        addattr_l(&req.n, sizeof(req), RTA_SRC, &mroute->sg.src.s_addr, 4);
        addattr_l(&req.n, sizeof(req), RTA_DST, &mroute->sg.grp.s_addr, 4);
-       addattr_l(&req.n, sizeof(req), RTA_TABLE, &zvrf->table_id, 4);
+       /*
+        * What?
+        *
+        * So during the namespace cleanup we started storing
+        * the zvrf table_id for the default table as RT_TABLE_MAIN
+        * which is what the normal routing table for ip routing is.
+        * This change caused this to break our lookups of sg data
+        * because prior to this change the zvrf->table_id was 0
+        * and when the pim multicast kernel code saw a 0,
+        * it was auto-translated to RT_TABLE_DEFAULT.  But since
+        * we are now passing in RT_TABLE_MAIN there is no auto-translation
+        * and the kernel goes screw you and the delicious cookies you
+        * are trying to give me.  So now we have this little hack.
+        */
+       actual_table = (zvrf->table_id == RT_TABLE_MAIN) ? RT_TABLE_DEFAULT :
+               zvrf->table_id;
+       addattr_l(&req.n, sizeof(req), RTA_TABLE, &actual_table, 4);
 
        suc = netlink_talk(netlink_route_change_read_multicast, &req.n,
                           &zns->netlink_cmd, zns, 0);
@@ -1702,11 +1818,11 @@ int kernel_get_ipmr_sg_stats(struct zebra_vrf *zvrf, void *in)
        return suc;
 }
 
-enum dp_req_result kernel_route_rib(struct route_node *rn,
-                                   const struct prefix *p,
-                                   const struct prefix *src_p,
-                                   struct route_entry *old,
-                                   struct route_entry *new)
+enum zebra_dplane_result kernel_route_rib(struct route_node *rn,
+                                         const struct prefix *p,
+                                         const struct prefix *src_p,
+                                         struct route_entry *old,
+                                         struct route_entry *new)
 {
        int ret = 0;
 
@@ -1736,20 +1852,20 @@ enum dp_req_result kernel_route_rib(struct route_node *rn,
                                                      new, 0);
                }
                kernel_route_rib_pass_fail(rn, p, new,
-                                          (!ret) ? DP_INSTALL_SUCCESS
-                                                 : DP_INSTALL_FAILURE);
-               return DP_REQUEST_SUCCESS;
+                                          (!ret) ? ZEBRA_DPLANE_INSTALL_SUCCESS
+                                                 : ZEBRA_DPLANE_INSTALL_FAILURE);
+               return ZEBRA_DPLANE_REQUEST_SUCCESS;
        }
 
        if (old) {
                ret = netlink_route_multipath(RTM_DELROUTE, p, src_p, old, 0);
 
                kernel_route_rib_pass_fail(rn, p, old,
-                                          (!ret) ? DP_DELETE_SUCCESS
-                                                 : DP_DELETE_FAILURE);
+                                          (!ret) ? ZEBRA_DPLANE_DELETE_SUCCESS
+                                                 : ZEBRA_DPLANE_DELETE_FAILURE);
        }
 
-       return DP_REQUEST_SUCCESS;
+       return ZEBRA_DPLANE_REQUEST_SUCCESS;
 }
 
 int kernel_neigh_update(int add, int ifindex, uint32_t addr, char *lla,
@@ -1867,10 +1983,10 @@ static int netlink_macfdb_change(struct nlmsghdr *h, int len, ns_id_t ns_id)
 
        zif = (struct zebra_if *)ifp->info;
        if ((br_if = zif->brslave_info.br_if) == NULL) {
-               zlog_warn("%s family %s IF %s(%u) brIF %u - no bridge master",
-                         nl_msg_type_to_str(h->nlmsg_type),
-                         nl_family_to_str(ndm->ndm_family), ifp->name,
-                         ndm->ndm_ifindex, zif->brslave_info.bridge_ifindex);
+               zlog_debug("%s family %s IF %s(%u) brIF %u - no bridge master",
+                          nl_msg_type_to_str(h->nlmsg_type),
+                          nl_family_to_str(ndm->ndm_family), ifp->name,
+                          ndm->ndm_ifindex, zif->brslave_info.bridge_ifindex);
                return 0;
        }
 
@@ -1879,15 +1995,15 @@ static int netlink_macfdb_change(struct nlmsghdr *h, int len, ns_id_t ns_id)
        netlink_parse_rtattr(tb, NDA_MAX, NDA_RTA(ndm), len);
 
        if (!tb[NDA_LLADDR]) {
-               zlog_warn("%s family %s IF %s(%u) brIF %u - no LLADDR",
-                         nl_msg_type_to_str(h->nlmsg_type),
-                         nl_family_to_str(ndm->ndm_family), ifp->name,
-                         ndm->ndm_ifindex, zif->brslave_info.bridge_ifindex);
+               zlog_debug("%s family %s IF %s(%u) brIF %u - no LLADDR",
+                          nl_msg_type_to_str(h->nlmsg_type),
+                          nl_family_to_str(ndm->ndm_family), ifp->name,
+                          ndm->ndm_ifindex, zif->brslave_info.bridge_ifindex);
                return 0;
        }
 
        if (RTA_PAYLOAD(tb[NDA_LLADDR]) != ETH_ALEN) {
-               zlog_warn(
+               zlog_debug(
                        "%s family %s IF %s(%u) brIF %u - LLADDR is not MAC, len %lu",
                        nl_msg_type_to_str(h->nlmsg_type),
                        nl_family_to_str(ndm->ndm_family), ifp->name,
@@ -2083,9 +2199,9 @@ static int netlink_macfdb_update(struct interface *ifp, vlanid_t vid,
        zns = zvrf->zns;
        zif = ifp->info;
        if ((br_if = zif->brslave_info.br_if) == NULL) {
-               zlog_warn("MAC %s on IF %s(%u) - no mapping to bridge",
-                         (cmd == RTM_NEWNEIGH) ? "add" : "del", ifp->name,
-                         ifp->ifindex);
+               zlog_debug("MAC %s on IF %s(%u) - no mapping to bridge",
+                          (cmd == RTM_NEWNEIGH) ? "add" : "del", ifp->name,
+                          ifp->ifindex);
                return -1;
        }
 
@@ -2151,6 +2267,7 @@ static int netlink_ipneigh_change(struct nlmsghdr *h, int len, ns_id_t ns_id)
        char buf2[INET6_ADDRSTRLEN];
        int mac_present = 0;
        uint8_t ext_learned;
+       uint8_t router_flag;
 
        ndm = NLMSG_DATA(h);
 
@@ -2167,10 +2284,10 @@ static int netlink_ipneigh_change(struct nlmsghdr *h, int len, ns_id_t ns_id)
        netlink_parse_rtattr(tb, NDA_MAX, NDA_RTA(ndm), len);
 
        if (!tb[NDA_DST]) {
-               zlog_warn("%s family %s IF %s(%u) - no DST",
-                         nl_msg_type_to_str(h->nlmsg_type),
-                         nl_family_to_str(ndm->ndm_family), ifp->name,
-                         ndm->ndm_ifindex);
+               zlog_debug("%s family %s IF %s(%u) - no DST",
+                          nl_msg_type_to_str(h->nlmsg_type),
+                          nl_family_to_str(ndm->ndm_family), ifp->name,
+                          ndm->ndm_ifindex);
                return 0;
        }
 
@@ -2180,7 +2297,7 @@ static int netlink_ipneigh_change(struct nlmsghdr *h, int len, ns_id_t ns_id)
 
        /* Drop some "permanent" entries. */
        if (ndm->ndm_state & NUD_PERMANENT) {
-               char buf[16] = "169.254.0.1";
+               char b[16] = "169.254.0.1";
                struct in_addr ipv4_ll;
 
                if (ndm->ndm_family != AF_INET)
@@ -2192,7 +2309,7 @@ static int netlink_ipneigh_change(struct nlmsghdr *h, int len, ns_id_t ns_id)
                if (h->nlmsg_type != RTM_DELNEIGH)
                        return 0;
 
-               inet_pton(AF_INET, buf, &ipv4_ll);
+               inet_pton(AF_INET, b, &ipv4_ll);
                if (ipv4_ll.s_addr != ip.ip._v4_addr.s_addr)
                        return 0;
 
@@ -2226,7 +2343,7 @@ static int netlink_ipneigh_change(struct nlmsghdr *h, int len, ns_id_t ns_id)
        if (h->nlmsg_type == RTM_NEWNEIGH) {
                if (tb[NDA_LLADDR]) {
                        if (RTA_PAYLOAD(tb[NDA_LLADDR]) != ETH_ALEN) {
-                               zlog_warn(
+                               zlog_debug(
                                        "%s family %s IF %s(%u) - LLADDR is not MAC, len %lu",
                                        nl_msg_type_to_str(h->nlmsg_type),
                                        nl_family_to_str(ndm->ndm_family),
@@ -2241,6 +2358,7 @@ static int netlink_ipneigh_change(struct nlmsghdr *h, int len, ns_id_t ns_id)
                }
 
                ext_learned = (ndm->ndm_flags & NTF_EXT_LEARNED) ? 1 : 0;
+               router_flag = (ndm->ndm_flags & NTF_ROUTER) ? 1 : 0;
 
                if (IS_ZEBRA_DEBUG_KERNEL)
                        zlog_debug(
@@ -2263,7 +2381,7 @@ static int netlink_ipneigh_change(struct nlmsghdr *h, int len, ns_id_t ns_id)
                if (ndm->ndm_state & NUD_VALID)
                        return zebra_vxlan_handle_kernel_neigh_update(
                                ifp, link_if, &ip, &mac, ndm->ndm_state,
-                               ext_learned);
+                               ext_learned, router_flag);
 
                return zebra_vxlan_handle_kernel_neigh_del(ifp, link_if, &ip);
        }
@@ -2386,12 +2504,20 @@ int netlink_neigh_change(struct nlmsghdr *h, ns_id_t ns_id)
 
        if (ndm->ndm_family == AF_INET || ndm->ndm_family == AF_INET6)
                return netlink_ipneigh_change(h, len, ns_id);
+       else {
+               flog_warn(
+                       EC_ZEBRA_UNKNOWN_FAMILY,
+                       "Invalid address family: %u received from kernel neighbor change: %s",
+                       ndm->ndm_family, nl_msg_type_to_str(h->nlmsg_type));
+               return 0;
+       }
 
        return 0;
 }
 
 static int netlink_neigh_update2(struct interface *ifp, struct ipaddr *ip,
-                                struct ethaddr *mac, uint32_t flags, int cmd)
+                                struct ethaddr *mac, uint8_t flags,
+                                uint16_t state, int cmd)
 {
        struct {
                struct nlmsghdr n;
@@ -2414,11 +2540,10 @@ static int netlink_neigh_update2(struct interface *ifp, struct ipaddr *ip,
                req.n.nlmsg_flags |= (NLM_F_CREATE | NLM_F_REPLACE);
        req.n.nlmsg_type = cmd; // RTM_NEWNEIGH or RTM_DELNEIGH
        req.ndm.ndm_family = IS_IPADDR_V4(ip) ? AF_INET : AF_INET6;
-       req.ndm.ndm_state = flags;
+       req.ndm.ndm_state = state;
        req.ndm.ndm_ifindex = ifp->ifindex;
        req.ndm.ndm_type = RTN_UNICAST;
-       req.ndm.ndm_flags = NTF_EXT_LEARNED;
-
+       req.ndm.ndm_flags = flags;
 
        ipa_len = IS_IPADDR_V4(ip) ? IPV4_MAX_BYTELEN : IPV6_MAX_BYTELEN;
        addattr_l(&req.n, sizeof(req), NDA_DST, &ip->ip.addr, ipa_len);
@@ -2426,12 +2551,12 @@ static int netlink_neigh_update2(struct interface *ifp, struct ipaddr *ip,
                addattr_l(&req.n, sizeof(req), NDA_LLADDR, mac, 6);
 
        if (IS_ZEBRA_DEBUG_KERNEL)
-               zlog_debug("Tx %s family %s IF %s(%u) Neigh %s MAC %s",
+               zlog_debug("Tx %s family %s IF %s(%u) Neigh %s MAC %s flags 0x%x",
                           nl_msg_type_to_str(cmd),
                           nl_family_to_str(req.ndm.ndm_family), ifp->name,
                           ifp->ifindex, ipaddr2str(ip, buf, sizeof(buf)),
                           mac ? prefix_mac2str(mac, buf2, sizeof(buf2))
-                              : "null");
+                              : "null", flags);
 
        return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns,
                            0);
@@ -2452,14 +2577,15 @@ int kernel_del_mac(struct interface *ifp, vlanid_t vid, struct ethaddr *mac,
 }
 
 int kernel_add_neigh(struct interface *ifp, struct ipaddr *ip,
-                    struct ethaddr *mac)
+                    struct ethaddr *mac, uint8_t flags)
 {
-       return netlink_neigh_update2(ifp, ip, mac, NUD_NOARP, RTM_NEWNEIGH);
+       return netlink_neigh_update2(ifp, ip, mac, flags,
+                                    NUD_NOARP, RTM_NEWNEIGH);
 }
 
 int kernel_del_neigh(struct interface *ifp, struct ipaddr *ip)
 {
-       return netlink_neigh_update2(ifp, ip, NULL, 0, RTM_DELNEIGH);
+       return netlink_neigh_update2(ifp, ip, NULL, 0, 0, RTM_DELNEIGH);
 }
 
 /*