summaryrefslogtreecommitdiff
path: root/zebra/rt_netlink.c
diff options
context:
space:
mode:
Diffstat (limited to 'zebra/rt_netlink.c')
-rw-r--r--zebra/rt_netlink.c288
1 files changed, 288 insertions, 0 deletions
diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c
index b36fbb2008..b30805b982 100644
--- a/zebra/rt_netlink.c
+++ b/zebra/rt_netlink.c
@@ -27,6 +27,7 @@
#include <linux/mpls_iptunnel.h>
#include <linux/neighbour.h>
#include <linux/rtnetlink.h>
+#include <linux/nexthop.h>
/* Hack for GNU libc version 2. */
#ifndef MSG_TRUNC
@@ -62,6 +63,7 @@
#include "zebra/zebra_mpls.h"
#include "zebra/kernel_netlink.h"
#include "zebra/rt_netlink.h"
+#include "zebra/zebra_nhg.h"
#include "zebra/zebra_mroute.h"
#include "zebra/zebra_vxlan.h"
#include "zebra/zebra_errors.h"
@@ -1920,6 +1922,292 @@ enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx)
ZEBRA_DPLANE_REQUEST_SUCCESS : ZEBRA_DPLANE_REQUEST_FAILURE);
}
+/**
+ * netlink_nexthop_process_nh() - Parse the gatway/if info from a new nexthop
+ *
+ * @tb: Netlink RTA data
+ * @family: Address family in the nhmsg
+ * @ns_id: Namspace id
+ *
+ * Return: New nexthop
+ */
+static struct nexthop netlink_nexthop_process_nh(struct rtattr **tb,
+ unsigned char family,
+ ns_id_t ns_id)
+{
+ struct nexthop nh = {0};
+ void *gate = NULL;
+ struct interface *ifp = NULL;
+ int if_index;
+ size_t sz;
+
+ if_index = *(int *)RTA_DATA(tb[NHA_OIF]);
+
+ if (tb[NHA_GATEWAY]) {
+ switch (family) {
+ case AF_INET:
+ nh.type = NEXTHOP_TYPE_IPV4_IFINDEX;
+ sz = 4;
+ break;
+ case AF_INET6:
+ nh.type = NEXTHOP_TYPE_IPV6_IFINDEX;
+ sz = 16;
+ break;
+ default:
+ flog_warn(
+ EC_ZEBRA_BAD_NHG_MESSAGE,
+ "Nexthop with bad address family (%d) received from kernel",
+ family);
+ // TODO: Different return value?
+ return nh;
+ }
+ gate = RTA_DATA(tb[NHA_GATEWAY]);
+ memcpy(&(nh.gate), gate, sz);
+ } else {
+ nh.type = NEXTHOP_TYPE_IFINDEX;
+ }
+
+ nh.ifindex = if_index;
+
+ ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id), nh.ifindex);
+ if (ifp) {
+ nh.vrf_id = ifp->vrf_id;
+ } else {
+ flog_warn(
+ EC_ZEBRA_UNKNOWN_INTERFACE,
+ "%s: Unknown nexthop interface %u received, defaulting to VRF_DEFAULT",
+ __PRETTY_FUNCTION__, nh.ifindex);
+
+ nh.vrf_id = VRF_DEFAULT;
+ }
+
+ if (tb[NHA_ENCAP] && tb[NHA_ENCAP_TYPE]) {
+ uint16_t encap_type = *(uint16_t *)RTA_DATA(tb[NHA_ENCAP_TYPE]);
+ int num_labels = 0;
+ mpls_label_t labels[MPLS_MAX_LABELS] = {0};
+
+ if (encap_type == LWTUNNEL_ENCAP_MPLS) {
+ num_labels = parse_encap_mpls(tb[NHA_ENCAP], labels);
+ }
+
+ if (num_labels) {
+ nexthop_add_labels(&nh, ZEBRA_LSP_STATIC, num_labels,
+ labels);
+ }
+ }
+
+ return nh;
+}
+
+/**
+ * netlink_nexthop_process_group() - Iterate over nhmsg nexthop group
+ *
+ * @tb: Netlink RTA data
+ *
+ * Return: TODO: Not sure yet
+ */
+static int netlink_nexthop_process_group(struct rtattr **tb)
+{
+ int count;
+ struct nexthop_grp *n_grp = NULL;
+
+ n_grp = RTA_DATA(tb[NHA_GROUP]);
+ count = (RTA_PAYLOAD(tb[NHA_GROUP]) / sizeof(*n_grp));
+
+ if (!count || (count * sizeof(*n_grp)) != RTA_PAYLOAD(tb[NHA_GROUP])) {
+ flog_warn(EC_ZEBRA_BAD_NHG_MESSAGE,
+ "Invalid nexthop group received from the kernel");
+ return -1;
+ }
+
+ // TODO: Need type for something?
+ // zlog_debug("group type: %d",
+ // *((uint16_t *)RTA_DATA(tb[NHA_GROUP_TYPE])));
+
+
+ for (int i = 0; i < count; i++) {
+ // TODO: Lookup by id and if we already have entries
+ // for that id, delete it?
+
+ /* We do not care about nexthop_grp.weight at
+ * this time. But we should figure out
+ * how to adapt this to our code in
+ * the future.
+ */
+ }
+
+ return count;
+}
+
+/**
+ * netlink_nexthop_change() - Read in change about nexthops from the kernel
+ *
+ * @h: Netlink message header
+ * @ns_id: Namspace id
+ * @startup: Are we reading under startup conditions?
+ *
+ * Return: Result status
+ */
+int netlink_nexthop_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
+{
+ int len;
+ /* nexthop group id */
+ uint32_t id;
+ unsigned char family;
+ struct nhmsg *nhm = NULL;
+ /* struct for nexthop group abstraction */
+ struct nexthop_group nhg = {0};
+ /* zebra's version of nexthops */
+ struct nexthop nh = {0};
+ /* struct that goes into our tables */
+ struct nhg_hash_entry *nhe = NULL;
+ struct rtattr *tb[NHA_MAX + 1];
+
+
+ nhm = NLMSG_DATA(h);
+
+ if (startup && h->nlmsg_type != RTM_NEWNEXTHOP)
+ return 0;
+
+ len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct nhmsg));
+ if (len < 0) {
+ zlog_warn(
+ "%s: Message received from netlink is of a broken size %d %zu",
+ __PRETTY_FUNCTION__, h->nlmsg_len,
+ (size_t)NLMSG_LENGTH(sizeof(struct nhmsg)));
+ return -1;
+ }
+
+ memset(tb, 0, sizeof(tb));
+ netlink_parse_rtattr(tb, NHA_MAX, RTM_NHA(nhm), len);
+
+
+ if (!tb[NHA_ID]) {
+ flog_warn(
+ EC_ZEBRA_BAD_NHG_MESSAGE,
+ "Nexthop group without an ID received from the kernel");
+ return -1;
+ }
+
+ /* We use the ID key'd nhg table for kernel updates */
+ id = *((uint32_t *)RTA_DATA(tb[NHA_ID]));
+ family = nhm->nh_family;
+
+ if (IS_ZEBRA_DEBUG_KERNEL) {
+ zlog_debug("Nexthop ID (%u) update from the kernel", id);
+ }
+
+ /* Lookup via the id */
+ nhe = zebra_nhg_lookup_id(id);
+
+ if (h->nlmsg_type == RTM_NEWNEXTHOP) {
+ if (tb[NHA_GROUP]) {
+ /**
+ * If this is a group message its only going to have
+ * an array of nexthop IDs associated with it
+ */
+ return -1;
+ netlink_nexthop_process_group(tb);
+ } else if (tb[NHA_BLACKHOLE]) {
+ /**
+ * This nexthop is just for blackhole-ing traffic,
+ * it should not have an OIF, GATEWAY, or ENCAP
+ */
+ nh.type = NEXTHOP_TYPE_BLACKHOLE;
+ // TODO: Handle blackhole case
+ nh.bh_type = BLACKHOLE_UNSPEC;
+ } else if (tb[NHA_OIF]) {
+ /**
+ * This is a true new nexthop, so we need
+ * to parse the gateway and device info
+ */
+ nh = netlink_nexthop_process_nh(tb, family, ns_id);
+ }
+
+
+ nexthop_group_add_sorted(&nhg, &nh);
+
+ if (nhe) {
+ /* This is a change to a group we already have */
+ nexthops_free(nhe->nhg.nexthop);
+ nhe->nhg.nexthop = NULL;
+ nexthop_group_copy(&nhe->nhg, &nhg);
+ } else {
+ /* This is a new nexthop group */
+ nhe = zebra_nhg_find(&nhg, nh.vrf_id, id);
+ if (nhe) {
+ nhe->is_kernel_nh = true;
+ } else {
+ return -1;
+ }
+ }
+ } else if (h->nlmsg_type == RTM_DELNEXTHOP) {
+ if (!nhe) {
+ flog_warn(
+ EC_ZEBRA_BAD_NHG_MESSAGE,
+ "Kernel delete message received for nexthop group ID (%u) that we do not have in our ID table",
+ id);
+ return -1;
+ }
+
+ // TODO: Run some active check on all route_entry's?
+ zebra_nhg_release(nhe);
+ }
+
+
+ return 0;
+}
+
+/**
+ * netlink_request_nexthop() - Request nextop information from the kernel
+ * @zns: Zebra namespace
+ * @family: AF_* netlink family
+ * @type: RTM_* route type
+ *
+ * Return: Result status
+ */
+static int netlink_request_nexthop(struct zebra_ns *zns, int family, int type)
+{
+ struct {
+ struct nlmsghdr n;
+ struct nhmsg nhm;
+ } req;
+
+ /* Form the request, specifying filter (rtattr) if needed. */
+ memset(&req, 0, sizeof(req));
+ req.n.nlmsg_type = type;
+ req.n.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg));
+ req.nhm.nh_family = family;
+
+ return netlink_request(&zns->netlink_cmd, &req.n);
+}
+
+/**
+ * netlink_nexthop_read() - Nexthop read function using netlink interface
+ *
+ * @zns: Zebra name space
+ *
+ * Return: Result status
+ * Only called at bootstrap time.
+ */
+int netlink_nexthop_read(struct zebra_ns *zns)
+{
+ int ret;
+ struct zebra_dplane_info dp_info;
+
+ zebra_dplane_info_from_zns(&dp_info, zns, true /*is_cmd*/);
+
+ /* Get nexthop objects */
+ ret = netlink_request_nexthop(zns, AF_UNSPEC, RTM_GETNEXTHOP);
+ if (ret < 0)
+ return ret;
+ ret = netlink_parse_info(netlink_nexthop_change, &zns->netlink_cmd,
+ &dp_info, 0, 1);
+ return 0;
+}
+
+
int kernel_neigh_update(int add, int ifindex, uint32_t addr, char *lla,
int llalen, ns_id_t ns_id)
{