summaryrefslogtreecommitdiff
path: root/zebra/kernel_netlink.c
diff options
context:
space:
mode:
Diffstat (limited to 'zebra/kernel_netlink.c')
-rw-r--r--zebra/kernel_netlink.c739
1 files changed, 739 insertions, 0 deletions
diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c
index 23b2153ec2..ef7a891693 100644
--- a/zebra/kernel_netlink.c
+++ b/zebra/kernel_netlink.c
@@ -18,3 +18,742 @@
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
+
+#include <zebra.h>
+
+#include "linklist.h"
+#include "if.h"
+#include "log.h"
+#include "prefix.h"
+#include "connected.h"
+#include "table.h"
+#include "memory.h"
+#include "zebra_memory.h"
+#include "rib.h"
+#include "thread.h"
+#include "privs.h"
+#include "nexthop.h"
+#include "vrf.h"
+#include "mpls.h"
+
+#include "zebra/zserv.h"
+#include "zebra/zebra_ns.h"
+#include "zebra/zebra_vrf.h"
+#include "zebra/debug.h"
+#include "zebra/kernel_netlink.h"
+#include "zebra/rt_netlink.h"
+#include "zebra/if_netlink.h"
+
+#ifndef SO_RCVBUFFORCE
+#define SO_RCVBUFFORCE (33)
+#endif
+
+/* Hack for GNU libc version 2. */
+#ifndef MSG_TRUNC
+#define MSG_TRUNC 0x20
+#endif /* MSG_TRUNC */
+
+#ifndef NLMSG_TAIL
+#define NLMSG_TAIL(nmsg) \
+ ((struct rtattr *) (((u_char *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
+#endif
+
+#ifndef RTA_TAIL
+#define RTA_TAIL(rta) \
+ ((struct rtattr *) (((u_char *) (rta)) + RTA_ALIGN((rta)->rta_len)))
+#endif
+
+static const struct message nlmsg_str[] = {
+ {RTM_NEWROUTE, "RTM_NEWROUTE"},
+ {RTM_DELROUTE, "RTM_DELROUTE"},
+ {RTM_GETROUTE, "RTM_GETROUTE"},
+ {RTM_NEWLINK, "RTM_NEWLINK"},
+ {RTM_DELLINK, "RTM_DELLINK"},
+ {RTM_GETLINK, "RTM_GETLINK"},
+ {RTM_NEWADDR, "RTM_NEWADDR"},
+ {RTM_DELADDR, "RTM_DELADDR"},
+ {RTM_GETADDR, "RTM_GETADDR"},
+ {RTM_NEWNEIGH, "RTM_NEWNEIGH"},
+ {RTM_DELNEIGH, "RTM_DELNEIGH"},
+ {RTM_GETNEIGH, "RTM_GETNEIGH"},
+ {0, NULL}
+};
+
+static const struct message rtproto_str[] = {
+ {RTPROT_REDIRECT, "redirect"},
+ {RTPROT_KERNEL, "kernel"},
+ {RTPROT_BOOT, "boot"},
+ {RTPROT_STATIC, "static"},
+ {RTPROT_GATED, "GateD"},
+ {RTPROT_RA, "router advertisement"},
+ {RTPROT_MRT, "MRT"},
+ {RTPROT_ZEBRA, "Zebra"},
+#ifdef RTPROT_BIRD
+ {RTPROT_BIRD, "BIRD"},
+#endif /* RTPROT_BIRD */
+ {0, NULL}
+};
+
+extern struct thread_master *master;
+extern u_int32_t nl_rcvbufsize;
+
+extern struct zebra_privs_t zserv_privs;
+
+static int
+netlink_talk_filter (struct sockaddr_nl *snl, struct nlmsghdr *h,
+ ns_id_t ns_id)
+{
+ zlog_warn ("netlink_talk: ignoring message type 0x%04x NS %u", h->nlmsg_type,
+ ns_id);
+ return 0;
+}
+
+static int
+netlink_recvbuf (struct nlsock *nl, uint32_t newsize)
+{
+ u_int32_t oldsize;
+ socklen_t newlen = sizeof(newsize);
+ socklen_t oldlen = sizeof(oldsize);
+ int ret;
+
+ ret = getsockopt(nl->sock, SOL_SOCKET, SO_RCVBUF, &oldsize, &oldlen);
+ if (ret < 0)
+ {
+ zlog (NULL, LOG_ERR, "Can't get %s receive buffer size: %s", nl->name,
+ safe_strerror (errno));
+ return -1;
+ }
+
+ /* Try force option (linux >= 2.6.14) and fall back to normal set */
+ if ( zserv_privs.change (ZPRIVS_RAISE) )
+ zlog_err ("routing_socket: Can't raise privileges");
+ ret = setsockopt(nl->sock, SOL_SOCKET, SO_RCVBUFFORCE, &nl_rcvbufsize,
+ sizeof(nl_rcvbufsize));
+ if ( zserv_privs.change (ZPRIVS_LOWER) )
+ zlog_err ("routing_socket: Can't lower privileges");
+ if (ret < 0)
+ ret = setsockopt(nl->sock, SOL_SOCKET, SO_RCVBUF, &nl_rcvbufsize,
+ sizeof(nl_rcvbufsize));
+ if (ret < 0)
+ {
+ zlog (NULL, LOG_ERR, "Can't set %s receive buffer size: %s", nl->name,
+ safe_strerror (errno));
+ return -1;
+ }
+
+ ret = getsockopt(nl->sock, SOL_SOCKET, SO_RCVBUF, &newsize, &newlen);
+ if (ret < 0)
+ {
+ zlog (NULL, LOG_ERR, "Can't get %s receive buffer size: %s", nl->name,
+ safe_strerror (errno));
+ return -1;
+ }
+
+ zlog (NULL, LOG_INFO,
+ "Setting netlink socket receive buffer size: %u -> %u",
+ oldsize, newsize);
+ return 0;
+}
+
+/* Make socket for Linux netlink interface. */
+static int
+netlink_socket (struct nlsock *nl, unsigned long groups, ns_id_t ns_id)
+{
+ int ret;
+ struct sockaddr_nl snl;
+ int sock;
+ int namelen;
+ int save_errno;
+
+ if (zserv_privs.change (ZPRIVS_RAISE))
+ {
+ zlog (NULL, LOG_ERR, "Can't raise privileges");
+ return -1;
+ }
+
+ sock = socket (AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+ if (sock < 0)
+ {
+ zlog (NULL, LOG_ERR, "Can't open %s socket: %s", nl->name,
+ safe_strerror (errno));
+ return -1;
+ }
+
+ memset (&snl, 0, sizeof snl);
+ snl.nl_family = AF_NETLINK;
+ snl.nl_groups = groups;
+
+ /* Bind the socket to the netlink structure for anything. */
+ ret = bind (sock, (struct sockaddr *) &snl, sizeof snl);
+ save_errno = errno;
+ if (zserv_privs.change (ZPRIVS_LOWER))
+ zlog (NULL, LOG_ERR, "Can't lower privileges");
+
+ if (ret < 0)
+ {
+ zlog (NULL, LOG_ERR, "Can't bind %s socket to group 0x%x: %s",
+ nl->name, snl.nl_groups, safe_strerror (save_errno));
+ close (sock);
+ return -1;
+ }
+
+ /* multiple netlink sockets will have different nl_pid */
+ namelen = sizeof snl;
+ ret = getsockname (sock, (struct sockaddr *) &snl, (socklen_t *) &namelen);
+ if (ret < 0 || namelen != sizeof snl)
+ {
+ zlog (NULL, LOG_ERR, "Can't get %s socket name: %s", nl->name,
+ safe_strerror (errno));
+ close (sock);
+ return -1;
+ }
+
+ nl->snl = snl;
+ nl->sock = sock;
+ return ret;
+}
+
+static int
+netlink_information_fetch (struct sockaddr_nl *snl, struct nlmsghdr *h,
+ ns_id_t ns_id)
+{
+ /* JF: Ignore messages that aren't from the kernel */
+ if ( snl->nl_pid != 0 )
+ {
+ zlog ( NULL, LOG_ERR, "Ignoring message from pid %u", snl->nl_pid );
+ return 0;
+ }
+
+ switch (h->nlmsg_type)
+ {
+ case RTM_NEWROUTE:
+ return netlink_route_change (snl, h, ns_id);
+ break;
+ case RTM_DELROUTE:
+ return netlink_route_change (snl, h, ns_id);
+ break;
+ case RTM_NEWLINK:
+ return netlink_link_change (snl, h, ns_id);
+ break;
+ case RTM_DELLINK:
+ return netlink_link_change (snl, h, ns_id);
+ break;
+ case RTM_NEWADDR:
+ return netlink_interface_addr (snl, h, ns_id);
+ break;
+ case RTM_DELADDR:
+ return netlink_interface_addr (snl, h, ns_id);
+ break;
+ default:
+ zlog_warn ("Unknown netlink nlmsg_type %d vrf %u\n", h->nlmsg_type,
+ ns_id);
+ break;
+ }
+ return 0;
+}
+
+static int
+kernel_read (struct thread *thread)
+{
+ struct zebra_ns *zns = (struct zebra_ns *)THREAD_ARG (thread);
+ netlink_parse_info (netlink_information_fetch, &zns->netlink, zns, 5);
+ zns->t_netlink = thread_add_read (zebrad.master, kernel_read, zns,
+ zns->netlink.sock);
+
+ return 0;
+}
+
+/* Filter out messages from self that occur on listener socket,
+ * caused by our actions on the command socket
+ */
+static void netlink_install_filter (int sock, __u32 pid)
+{
+ struct sock_filter filter[] = {
+ /* 0: ldh [4] */
+ BPF_STMT(BPF_LD|BPF_ABS|BPF_H, offsetof(struct nlmsghdr, nlmsg_type)),
+ /* 1: jeq 0x18 jt 3 jf 6 */
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(RTM_NEWROUTE), 1, 0),
+ /* 2: jeq 0x19 jt 3 jf 6 */
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(RTM_DELROUTE), 0, 3),
+ /* 3: ldw [12] */
+ BPF_STMT(BPF_LD|BPF_ABS|BPF_W, offsetof(struct nlmsghdr, nlmsg_pid)),
+ /* 4: jeq XX jt 5 jf 6 */
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htonl(pid), 0, 1),
+ /* 5: ret 0 (skip) */
+ BPF_STMT(BPF_RET|BPF_K, 0),
+ /* 6: ret 0xffff (keep) */
+ BPF_STMT(BPF_RET|BPF_K, 0xffff),
+ };
+
+ struct sock_fprog prog = {
+ .len = array_size(filter),
+ .filter = filter,
+ };
+
+ if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &prog, sizeof(prog)) < 0)
+ zlog_warn ("Can't install socket filter: %s\n", safe_strerror(errno));
+}
+
+void
+netlink_parse_rtattr (struct rtattr **tb, int max, struct rtattr *rta,
+ int len)
+{
+ while (RTA_OK (rta, len))
+ {
+ if (rta->rta_type <= max)
+ tb[rta->rta_type] = rta;
+ rta = RTA_NEXT (rta, len);
+ }
+}
+
+int
+addattr_l (struct nlmsghdr *n, unsigned int maxlen, int type,
+ void *data, unsigned int alen)
+{
+ int len;
+ struct rtattr *rta;
+
+ len = RTA_LENGTH (alen);
+
+ if (NLMSG_ALIGN (n->nlmsg_len) + RTA_ALIGN (len) > maxlen)
+ return -1;
+
+ rta = (struct rtattr *) (((char *) n) + NLMSG_ALIGN (n->nlmsg_len));
+ rta->rta_type = type;
+ rta->rta_len = len;
+ memcpy (RTA_DATA (rta), data, alen);
+ n->nlmsg_len = NLMSG_ALIGN (n->nlmsg_len) + RTA_ALIGN (len);
+
+ return 0;
+}
+
+int
+rta_addattr_l (struct rtattr *rta, unsigned int maxlen, int type,
+ void *data, unsigned int alen)
+{
+ unsigned int len;
+ struct rtattr *subrta;
+
+ len = RTA_LENGTH (alen);
+
+ if (RTA_ALIGN (rta->rta_len) + RTA_ALIGN (len) > maxlen)
+ return -1;
+
+ subrta = (struct rtattr *) (((char *) rta) + RTA_ALIGN (rta->rta_len));
+ subrta->rta_type = type;
+ subrta->rta_len = len;
+ memcpy (RTA_DATA (subrta), data, alen);
+ rta->rta_len = NLMSG_ALIGN (rta->rta_len) + RTA_ALIGN (len);
+
+ return 0;
+}
+
+int
+addattr32 (struct nlmsghdr *n, unsigned int maxlen, int type, int data)
+{
+ return addattr_l(n, maxlen, type, &data, sizeof(u_int32_t));
+}
+
+struct rtattr *
+addattr_nest(struct nlmsghdr *n, int maxlen, int type)
+{
+ struct rtattr *nest = NLMSG_TAIL(n);
+
+ addattr_l(n, maxlen, type, NULL, 0);
+ return nest;
+}
+
+int
+addattr_nest_end(struct nlmsghdr *n, struct rtattr *nest)
+{
+ nest->rta_len = (u_char *)NLMSG_TAIL(n) - (u_char *)nest;
+ return n->nlmsg_len;
+}
+
+struct rtattr *
+rta_nest(struct rtattr *rta, int maxlen, int type)
+{
+ struct rtattr *nest = RTA_TAIL(rta);
+
+ rta_addattr_l(rta, maxlen, type, NULL, 0);
+ return nest;
+}
+
+int
+rta_nest_end(struct rtattr *rta, struct rtattr *nest)
+{
+ nest->rta_len = (u_char *)RTA_TAIL(rta) - (u_char *)nest;
+ return rta->rta_len;
+}
+
+const char *
+nl_msg_type_to_str (uint16_t msg_type)
+{
+ return lookup (nlmsg_str, msg_type);
+}
+
+const char *
+nl_rtproto_to_str (u_char rtproto)
+{
+ return lookup (rtproto_str, rtproto);
+}
+/* Receive message from netlink interface and pass those information
+ to the given function. */
+int
+netlink_parse_info (int (*filter) (struct sockaddr_nl *, struct nlmsghdr *,
+ ns_id_t),
+ struct nlsock *nl, struct zebra_ns *zns, int count)
+{
+ int status;
+ int ret = 0;
+ int error;
+ int read_in = 0;
+
+ while (1)
+ {
+ char buf[NL_PKT_BUF_SIZE];
+ struct iovec iov = {
+ .iov_base = buf,
+ .iov_len = sizeof buf
+ };
+ struct sockaddr_nl snl;
+ struct msghdr msg = {
+ .msg_name = (void *) &snl,
+ .msg_namelen = sizeof snl,
+ .msg_iov = &iov,
+ .msg_iovlen = 1
+ };
+ struct nlmsghdr *h;
+
+ if (count && read_in >= count)
+ return 0;
+
+ status = recvmsg (nl->sock, &msg, 0);
+ if (status < 0)
+ {
+ if (errno == EINTR)
+ continue;
+ if (errno == EWOULDBLOCK || errno == EAGAIN)
+ break;
+ zlog (NULL, LOG_ERR, "%s recvmsg overrun: %s",
+ nl->name, safe_strerror(errno));
+ /*
+ * In this case we are screwed.
+ * There is no good way to
+ * recover zebra at this point.
+ */
+ exit (-1);
+ continue;
+ }
+
+ if (status == 0)
+ {
+ zlog (NULL, LOG_ERR, "%s EOF", nl->name);
+ return -1;
+ }
+
+ if (msg.msg_namelen != sizeof snl)
+ {
+ zlog (NULL, LOG_ERR, "%s sender address length error: length %d",
+ nl->name, msg.msg_namelen);
+ return -1;
+ }
+
+ if (IS_ZEBRA_DEBUG_KERNEL_MSGDUMP_RECV)
+ {
+ zlog_debug("%s: << netlink message dump [recv]", __func__);
+ zlog_hexdump(&msg, sizeof(msg));
+ }
+
+ read_in++;
+ for (h = (struct nlmsghdr *) buf; NLMSG_OK (h, (unsigned int) status);
+ h = NLMSG_NEXT (h, status))
+ {
+ /* Finish of reading. */
+ if (h->nlmsg_type == NLMSG_DONE)
+ return ret;
+
+ /* Error handling. */
+ if (h->nlmsg_type == NLMSG_ERROR)
+ {
+ struct nlmsgerr *err = (struct nlmsgerr *) NLMSG_DATA (h);
+ int errnum = err->error;
+ int msg_type = err->msg.nlmsg_type;
+
+ /* If the error field is zero, then this is an ACK */
+ if (err->error == 0)
+ {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ {
+ zlog_debug ("%s: %s ACK: type=%s(%u), seq=%u, pid=%u",
+ __FUNCTION__, nl->name,
+ nl_msg_type_to_str (err->msg.nlmsg_type),
+ err->msg.nlmsg_type, err->msg.nlmsg_seq,
+ err->msg.nlmsg_pid);
+ }
+
+ /* return if not a multipart message, otherwise continue */
+ if (!(h->nlmsg_flags & NLM_F_MULTI))
+ return 0;
+ continue;
+ }
+
+ if (h->nlmsg_len < NLMSG_LENGTH (sizeof (struct nlmsgerr)))
+ {
+ zlog (NULL, LOG_ERR, "%s error: message truncated",
+ nl->name);
+ return -1;
+ }
+
+ /* Deal with errors that occur because of races in link handling */
+ if (nl == &zns->netlink_cmd
+ && ((msg_type == RTM_DELROUTE &&
+ (-errnum == ENODEV || -errnum == ESRCH))
+ || (msg_type == RTM_NEWROUTE && -errnum == EEXIST)))
+ {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug ("%s: error: %s type=%s(%u), seq=%u, pid=%u",
+ nl->name, safe_strerror (-errnum),
+ nl_msg_type_to_str (msg_type),
+ msg_type, err->msg.nlmsg_seq, err->msg.nlmsg_pid);
+ return 0;
+ }
+
+ /* We see RTM_DELNEIGH when shutting down an interface with an IPv4
+ * link-local. The kernel should have already deleted the neighbor
+ * so do not log these as an error.
+ */
+ if (msg_type == RTM_DELNEIGH ||
+ (nl == &zns->netlink_cmd && msg_type == RTM_NEWROUTE &&
+ (-errnum == ESRCH || -errnum == ENETUNREACH)))
+ {
+ /* This is known to happen in some situations, don't log
+ * as error.
+ */
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug ("%s error: %s, type=%s(%u), seq=%u, pid=%u",
+ nl->name, safe_strerror (-errnum),
+ nl_msg_type_to_str (msg_type),
+ msg_type, err->msg.nlmsg_seq, err->msg.nlmsg_pid);
+ }
+ else
+ zlog_err ("%s error: %s, type=%s(%u), seq=%u, pid=%u",
+ nl->name, safe_strerror (-errnum),
+ nl_msg_type_to_str (msg_type),
+ msg_type, err->msg.nlmsg_seq, err->msg.nlmsg_pid);
+
+ return -1;
+ }
+
+ /* OK we got netlink message. */
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug ("netlink_parse_info: %s type %s(%u), len=%d, seq=%u, pid=%u",
+ nl->name,
+ nl_msg_type_to_str (h->nlmsg_type), h->nlmsg_type,
+ h->nlmsg_len, h->nlmsg_seq, h->nlmsg_pid);
+
+ /* skip unsolicited messages originating from command socket
+ * linux sets the originators port-id for {NEW|DEL}ADDR messages,
+ * so this has to be checked here. */
+ if (nl != &zns->netlink_cmd
+ && h->nlmsg_pid == zns->netlink_cmd.snl.nl_pid
+ && (h->nlmsg_type != RTM_NEWADDR && h->nlmsg_type != RTM_DELADDR))
+ {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug ("netlink_parse_info: %s packet comes from %s",
+ zns->netlink_cmd.name, nl->name);
+ continue;
+ }
+
+ error = (*filter) (&snl, h, zns->ns_id);
+ if (error < 0)
+ {
+ zlog (NULL, LOG_ERR, "%s filter function error", nl->name);
+ ret = error;
+ }
+ }
+
+ /* After error care. */
+ if (msg.msg_flags & MSG_TRUNC)
+ {
+ zlog (NULL, LOG_ERR, "%s error: message truncated", nl->name);
+ continue;
+ }
+ if (status)
+ {
+ zlog (NULL, LOG_ERR, "%s error: data remnant size %d", nl->name,
+ status);
+ return -1;
+ }
+ }
+ return ret;
+}
+
+/* sendmsg() to netlink socket then recvmsg(). */
+int
+netlink_talk (struct nlmsghdr *n, struct nlsock *nl, struct zebra_ns *zns)
+{
+ int status;
+ struct sockaddr_nl snl;
+ struct iovec iov = {
+ .iov_base = (void *) n,
+ .iov_len = n->nlmsg_len
+ };
+ struct msghdr msg = {
+ .msg_name = (void *) &snl,
+ .msg_namelen = sizeof snl,
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+ int save_errno;
+
+ memset (&snl, 0, sizeof snl);
+ snl.nl_family = AF_NETLINK;
+
+ n->nlmsg_seq = ++nl->seq;
+
+ /* Request an acknowledgement by setting NLM_F_ACK */
+ n->nlmsg_flags |= NLM_F_ACK;
+
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug ("netlink_talk: %s type %s(%u), len=%d seq=%u flags 0x%x",
+ nl->name,
+ nl_msg_type_to_str (n->nlmsg_type), n->nlmsg_type,
+ n->nlmsg_len, n->nlmsg_seq, n->nlmsg_flags);
+
+ /* Send message to netlink interface. */
+ if (zserv_privs.change (ZPRIVS_RAISE))
+ zlog (NULL, LOG_ERR, "Can't raise privileges");
+ status = sendmsg (nl->sock, &msg, 0);
+ save_errno = errno;
+ if (zserv_privs.change (ZPRIVS_LOWER))
+ zlog (NULL, LOG_ERR, "Can't lower privileges");
+
+ if (IS_ZEBRA_DEBUG_KERNEL_MSGDUMP_SEND)
+ {
+ zlog_debug("%s: >> netlink message dump [sent]", __func__);
+ zlog_hexdump(&msg, sizeof(msg));
+ }
+
+ if (status < 0)
+ {
+ zlog (NULL, LOG_ERR, "netlink_talk sendmsg() error: %s",
+ safe_strerror (save_errno));
+ return -1;
+ }
+
+
+ /*
+ * Get reply from netlink socket.
+ * The reply should either be an acknowlegement or an error.
+ */
+ return netlink_parse_info (netlink_talk_filter, nl, zns, 0);
+}
+
+/* Get type specified information from netlink. */
+int
+netlink_request (int family, int type, struct nlsock *nl)
+{
+ int ret;
+ struct sockaddr_nl snl;
+ int save_errno;
+
+ struct
+ {
+ struct nlmsghdr nlh;
+ struct rtgenmsg g;
+ } req;
+
+ /* Check netlink socket. */
+ if (nl->sock < 0)
+ {
+ zlog (NULL, LOG_ERR, "%s socket isn't active.", nl->name);
+ return -1;
+ }
+
+ memset (&snl, 0, sizeof snl);
+ snl.nl_family = AF_NETLINK;
+
+ memset (&req, 0, sizeof req);
+ req.nlh.nlmsg_len = sizeof req;
+ req.nlh.nlmsg_type = type;
+ req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
+ req.nlh.nlmsg_pid = nl->snl.nl_pid;
+ req.nlh.nlmsg_seq = ++nl->seq;
+ req.g.rtgen_family = family;
+
+ /* linux appears to check capabilities on every message
+ * have to raise caps for every message sent
+ */
+ if (zserv_privs.change (ZPRIVS_RAISE))
+ {
+ zlog (NULL, LOG_ERR, "Can't raise privileges");
+ return -1;
+ }
+
+ ret = sendto (nl->sock, (void *) &req, sizeof req, 0,
+ (struct sockaddr *) &snl, sizeof snl);
+ save_errno = errno;
+
+ if (zserv_privs.change (ZPRIVS_LOWER))
+ zlog (NULL, LOG_ERR, "Can't lower privileges");
+
+ if (ret < 0)
+ {
+ zlog (NULL, LOG_ERR, "%s sendto failed: %s", nl->name,
+ safe_strerror (save_errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Exported interface function. This function simply calls
+ netlink_socket (). */
+void
+kernel_init (struct zebra_ns *zns)
+{
+ unsigned long groups;
+
+ groups = RTMGRP_LINK | RTMGRP_IPV4_ROUTE | RTMGRP_IPV4_IFADDR;
+#ifdef HAVE_IPV6
+ groups |= RTMGRP_IPV6_ROUTE | RTMGRP_IPV6_IFADDR;
+#endif /* HAVE_IPV6 */
+ netlink_socket (&zns->netlink, groups, zns->ns_id);
+ netlink_socket (&zns->netlink_cmd, 0, zns->ns_id);
+
+ /* Register kernel socket. */
+ if (zns->netlink.sock > 0)
+ {
+ /* Only want non-blocking on the netlink event socket */
+ if (fcntl (zns->netlink.sock, F_SETFL, O_NONBLOCK) < 0)
+ zlog_err ("Can't set %s socket flags: %s", zns->netlink.name,
+ safe_strerror (errno));
+
+ /* Set receive buffer size if it's set from command line */
+ if (nl_rcvbufsize)
+ netlink_recvbuf (&zns->netlink, nl_rcvbufsize);
+
+ netlink_install_filter (zns->netlink.sock, zns->netlink_cmd.snl.nl_pid);
+ zns->t_netlink = thread_add_read (zebrad.master, kernel_read, zns,
+ zns->netlink.sock);
+ }
+}
+
+void
+kernel_terminate (struct zebra_ns *zns)
+{
+ THREAD_READ_OFF (zns->t_netlink);
+
+ if (zns->netlink.sock >= 0)
+ {
+ close (zns->netlink.sock);
+ zns->netlink.sock = -1;
+ }
+
+ if (zns->netlink_cmd.sock >= 0)
+ {
+ close (zns->netlink_cmd.sock);
+ zns->netlink_cmd.sock = -1;
+ }
+}