#include <zebra.h>
+#if defined(HANDLE_NETLINK_FUZZING)
+#include <stdio.h>
+#include <string.h>
+#endif /* HANDLE_NETLINK_FUZZING */
+
#ifdef HAVE_NETLINK
#include "linklist.h"
#include "nexthop.h"
#include "vrf.h"
#include "mpls.h"
+#include "lib_errors.h"
#include "zebra/zserv.h"
#include "zebra/zebra_ns.h"
#include "zebra/rt_netlink.h"
#include "zebra/if_netlink.h"
#include "zebra/rule_netlink.h"
+#include "zebra/zebra_errors.h"
#ifndef SO_RCVBUFFORCE
#define SO_RCVBUFFORCE (33)
* received some other message in an unexpected
* way.
*/
- zlog_err("%s: ignoring message type 0x%04x(%s) NS %u",
- __PRETTY_FUNCTION__, h->nlmsg_type,
- nl_msg_type_to_str(h->nlmsg_type), ns_id);
+ zlog_debug("%s: ignoring message type 0x%04x(%s) NS %u", __func__,
+ h->nlmsg_type, nl_msg_type_to_str(h->nlmsg_type), ns_id);
return 0;
}
ret = getsockopt(nl->sock, SOL_SOCKET, SO_RCVBUF, &oldsize, &oldlen);
if (ret < 0) {
- zlog_err("Can't get %s receive buffer size: %s", nl->name,
- safe_strerror(errno));
+ flog_err_sys(EC_LIB_SOCKET,
+ "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");
+ frr_elevate_privs(&zserv_privs) {
+ ret = setsockopt(nl->sock, SOL_SOCKET, SO_RCVBUFFORCE,
+ &nl_rcvbufsize,
+ sizeof(nl_rcvbufsize));
+ }
if (ret < 0)
ret = setsockopt(nl->sock, SOL_SOCKET, SO_RCVBUF,
&nl_rcvbufsize, sizeof(nl_rcvbufsize));
if (ret < 0) {
- zlog_err("Can't set %s receive buffer size: %s", nl->name,
- safe_strerror(errno));
+ flog_err_sys(EC_LIB_SOCKET,
+ "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_err("Can't get %s receive buffer size: %s", nl->name,
- safe_strerror(errno));
+ flog_err_sys(EC_LIB_SOCKET,
+ "Can't get %s receive buffer size: %s", nl->name,
+ safe_strerror(errno));
return -1;
}
struct sockaddr_nl snl;
int sock;
int namelen;
- int save_errno;
- if (zserv_privs.change(ZPRIVS_RAISE)) {
- zlog_err("Can't raise privileges");
- return -1;
- }
-
- sock = ns_socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE, ns_id);
- if (sock < 0) {
- zlog_err("Can't open %s socket: %s", nl->name,
- safe_strerror(errno));
- return -1;
- }
+ frr_elevate_privs(&zserv_privs) {
+ sock = ns_socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE, ns_id);
+ if (sock < 0) {
+ zlog_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;
+ 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_err("Can't lower privileges");
+ /* Bind the socket to the netlink structure for anything. */
+ ret = bind(sock, (struct sockaddr *)&snl, sizeof snl);
+ }
if (ret < 0) {
zlog_err("Can't bind %s socket to group 0x%x: %s", nl->name,
- snl.nl_groups, safe_strerror(save_errno));
+ snl.nl_groups, safe_strerror(errno));
close(sock);
return -1;
}
namelen = sizeof snl;
ret = getsockname(sock, (struct sockaddr *)&snl, (socklen_t *)&namelen);
if (ret < 0 || namelen != sizeof snl) {
- zlog_err("Can't get %s socket name: %s", nl->name,
- safe_strerror(errno));
+ flog_err_sys(EC_LIB_SOCKET, "Can't get %s socket name: %s",
+ nl->name, safe_strerror(errno));
close(sock);
return -1;
}
* this message type or not ask for
* it to be sent up to us
*/
- zlog_err("Unknown netlink nlmsg_type %s(%d) vrf %u\n",
+ flog_err(EC_ZEBRA_UNKNOWN_NLMSG,
+ "Unknown netlink nlmsg_type %s(%d) vrf %u\n",
nl_msg_type_to_str(h->nlmsg_type), h->nlmsg_type,
ns_id);
break;
return 0;
}
+#if defined(HANDLE_NETLINK_FUZZING)
+/* Using globals here to avoid adding function parameters */
+
+/* Keep distinct filenames for netlink fuzzy collection */
+static unsigned int netlink_file_counter = 1;
+
+/* File name to read fuzzed netlink from */
+static char netlink_fuzz_file[MAXPATHLEN] = "";
+
+/* Flag for whether to read from file or not */
+bool netlink_read;
+
+/**
+ * netlink_read_init() - Starts the message parser
+ * @fname: Filename to read.
+ */
+void netlink_read_init(const char *fname)
+{
+ snprintf(netlink_fuzz_file, MAXPATHLEN, "%s", fname);
+ /* Creating this fake socket for testing purposes */
+ struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT);
+
+ netlink_parse_info(netlink_information_fetch, &zns->netlink, zns, 1, 0);
+}
+
+/**
+ * netlink_write_incoming() - Writes all data received from netlink to a file
+ * @buf: Data from netlink.
+ * @size: Size of data.
+ * @counter: Counter for keeping filenames distinct.
+ */
+static void netlink_write_incoming(const char *buf, const unsigned int size,
+ unsigned int counter)
+{
+ char fname[MAXPATHLEN];
+ FILE *f;
+
+ snprintf(fname, MAXPATHLEN, "%s/%s_%u", DAEMON_VTY_DIR, "netlink",
+ counter);
+ frr_elevate_privs(&zserv_privs) {
+ f = fopen(fname, "w");
+ }
+ if (f) {
+ fwrite(buf, 1, size, f);
+ fclose(f);
+ }
+}
+
+/**
+ * netlink_read_file() - Reads netlink data from file
+ * @buf: Netlink buffer being overwritten.
+ * @fname: File name to read from.
+ *
+ * Return: Size of file.
+ */
+static long netlink_read_file(char *buf, const char *fname)
+{
+ FILE *f;
+ long file_bytes = -1;
+
+ frr_elevate_privs(&zserv_privs) {
+ f = fopen(fname, "r");
+ }
+ if (f) {
+ fseek(f, 0, SEEK_END);
+ file_bytes = ftell(f);
+ rewind(f);
+ fread(buf, NL_RCV_PKT_BUF_SIZE, 1, f);
+ fclose(f);
+ }
+ return file_bytes;
+}
+
+#endif /* HANDLE_NETLINK_FUZZING */
+
static int kernel_read(struct thread *thread)
{
struct zebra_ns *zns = (struct zebra_ns *)THREAD_ARG(thread);
if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &prog, sizeof(prog))
< 0)
- zlog_warn("Can't install socket filter: %s\n",
- safe_strerror(errno));
+ flog_err_sys(EC_LIB_SOCKET, "Can't install socket filter: %s\n",
+ safe_strerror(errno));
}
void netlink_parse_rtattr(struct rtattr **tb, int max, struct rtattr *rta,
}
}
-int addattr_l(struct nlmsghdr *n, unsigned int maxlen, int type, void *data,
- unsigned int alen)
+/**
+ * netlink_parse_rtattr_nested() - Parses a nested route attribute
+ * @tb: Pointer to array for storing rtattr in.
+ * @max: Max number to store.
+ * @rta: Pointer to rtattr to look for nested items in.
+ */
+void netlink_parse_rtattr_nested(struct rtattr **tb, int max,
+ struct rtattr *rta)
+{
+ netlink_parse_rtattr(tb, max, RTA_DATA(rta), RTA_PAYLOAD(rta));
+}
+
+int addattr_l(struct nlmsghdr *n, unsigned int maxlen, int type,
+ const void *data, unsigned int alen)
{
int len;
struct rtattr *rta;
return 0;
}
-int rta_addattr_l(struct rtattr *rta, unsigned int maxlen, int type, void *data,
- unsigned int alen)
+int rta_addattr_l(struct rtattr *rta, unsigned int maxlen, int type,
+ const void *data, unsigned int alen)
{
unsigned int len;
struct rtattr *subrta;
* but noticing it for later.
*/
err_nlh = &err->msg;
- zlog_warn("%s: Received %d extended Ack",
- __PRETTY_FUNCTION__, err_nlh->nlmsg_type);
+ zlog_debug("%s: Received %d extended Ack",
+ __PRETTY_FUNCTION__, err_nlh->nlmsg_type);
}
}
if (is_err)
zlog_err("Extended Error: %s", msg);
else
- zlog_warn("Extended Warning: %s", msg);
+ flog_warn(EC_ZEBRA_NETLINK_EXTENDED_WARNING,
+ "Extended Warning: %s", msg);
}
}
if (count && read_in >= count)
return 0;
+#if defined(HANDLE_NETLINK_FUZZING)
+ /* Check if reading and filename is set */
+ if (netlink_read && '\0' != netlink_fuzz_file[0]) {
+ zlog_debug("Reading netlink fuzz file");
+ status = netlink_read_file(buf, netlink_fuzz_file);
+ snl.nl_pid = 0;
+ } else {
+ status = recvmsg(nl->sock, &msg, 0);
+ }
+#else
status = recvmsg(nl->sock, &msg, 0);
+#endif /* HANDLE_NETLINK_FUZZING */
if (status < 0) {
if (errno == EINTR)
continue;
if (errno == EWOULDBLOCK || errno == EAGAIN)
break;
- zlog_err("%s recvmsg overrun: %s", nl->name,
+ flog_err(EC_ZEBRA_RECVMSG_OVERRUN,
+ "%s recvmsg overrun: %s", nl->name,
safe_strerror(errno));
/*
* In this case we are screwed.
}
if (status == 0) {
- zlog_err("%s EOF", nl->name);
+ flog_err_sys(EC_LIB_SOCKET, "%s EOF", nl->name);
return -1;
}
if (msg.msg_namelen != sizeof snl) {
- zlog_err("%s sender address length error: length %d",
+ flog_err(EC_ZEBRA_NETLINK_LENGTH_ERROR,
+ "%s sender address length error: length %d",
nl->name, msg.msg_namelen);
return -1;
}
zlog_hexdump(buf, status);
}
+#if defined(HANDLE_NETLINK_FUZZING)
+ if (!netlink_read) {
+ zlog_debug("Writing incoming netlink message");
+ netlink_write_incoming(buf, status,
+ netlink_file_counter++);
+ }
+#endif /* HANDLE_NETLINK_FUZZING */
+
read_in++;
for (h = (struct nlmsghdr *)buf;
- NLMSG_OK(h, (unsigned int)status);
+ (status >= 0 && NLMSG_OK(h, (unsigned int)status));
h = NLMSG_NEXT(h, status)) {
/* Finish of reading. */
if (h->nlmsg_type == NLMSG_DONE)
continue;
}
+ if (h->nlmsg_len
+ < NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
+ flog_err(EC_ZEBRA_NETLINK_LENGTH_ERROR,
+ "%s error: message truncated",
+ nl->name);
+ return -1;
+ }
+
/* Deal with errors that occur because of races
* in link handling */
if (nl == &zns->netlink_cmd
err->msg.nlmsg_seq,
err->msg.nlmsg_pid);
} else
- zlog_err(
+ flog_err(
+ EC_ZEBRA_UNEXPECTED_MESSAGE,
"%s error: %s, type=%s(%u), seq=%u, pid=%u",
nl->name,
safe_strerror(-errnum),
* other actors besides the kernel
*/
if (snl.nl_pid != 0) {
- zlog_err("Ignoring message from pid %u",
- snl.nl_pid);
+ zlog_debug("Ignoring message from pid %u",
+ snl.nl_pid);
continue;
}
error = (*filter)(h, zns->ns_id, startup);
if (error < 0) {
- zlog_err("%s filter function error", nl->name);
- zlog_backtrace(LOG_ERR);
+ zlog_debug("%s filter function error",
+ nl->name);
ret = error;
}
}
/* After error care. */
if (msg.msg_flags & MSG_TRUNC) {
- zlog_err("%s error: message truncated", nl->name);
+ flog_err(EC_ZEBRA_NETLINK_LENGTH_ERROR,
+ "%s error: message truncated", nl->name);
continue;
}
if (status) {
- zlog_err("%s error: data remnant size %d", nl->name,
+ flog_err(EC_ZEBRA_NETLINK_LENGTH_ERROR,
+ "%s error: data remnant size %d", nl->name,
status);
return -1;
}
struct nlmsghdr *n, struct nlsock *nl, struct zebra_ns *zns,
int startup)
{
- int status;
+ int status = 0;
struct sockaddr_nl snl;
struct iovec iov;
struct msghdr msg;
- int save_errno;
+ int save_errno = 0;
memset(&snl, 0, sizeof snl);
memset(&iov, 0, sizeof iov);
n->nlmsg_seq = ++nl->seq;
n->nlmsg_pid = nl->snl.nl_pid;
- /* 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",
n->nlmsg_flags);
/* Send message to netlink interface. */
- if (zserv_privs.change(ZPRIVS_RAISE))
- zlog_err("Can't raise privileges");
- status = sendmsg(nl->sock, &msg, 0);
- save_errno = errno;
- if (zserv_privs.change(ZPRIVS_LOWER))
- zlog_err("Can't lower privileges");
+ frr_elevate_privs(&zserv_privs) {
+ status = sendmsg(nl->sock, &msg, 0);
+ save_errno = errno;
+ }
if (IS_ZEBRA_DEBUG_KERNEL_MSGDUMP_SEND) {
zlog_debug("%s: >> netlink message dump [sent]", __func__);
}
if (status < 0) {
- zlog_err("netlink_talk sendmsg() error: %s",
- safe_strerror(save_errno));
+ flog_err_sys(EC_LIB_SOCKET, "netlink_talk sendmsg() error: %s",
+ safe_strerror(save_errno));
return -1;
}
{
int ret;
struct sockaddr_nl snl;
- int save_errno;
/* Check netlink socket. */
if (nl->sock < 0) {
- zlog_err("%s socket isn't active.", nl->name);
+ flog_err_sys(EC_LIB_SOCKET, "%s socket isn't active.",
+ nl->name);
return -1;
}
snl.nl_family = AF_NETLINK;
/* Raise capabilities and send message, then lower capabilities. */
- if (zserv_privs.change(ZPRIVS_RAISE)) {
- zlog_err("Can't raise privileges");
- return -1;
+ frr_elevate_privs(&zserv_privs) {
+ ret = sendto(nl->sock, (void *)n, n->nlmsg_len, 0,
+ (struct sockaddr *)&snl, sizeof snl);
}
- ret = sendto(nl->sock, (void *)n, n->nlmsg_len, 0,
- (struct sockaddr *)&snl, sizeof snl);
- save_errno = errno;
-
- if (zserv_privs.change(ZPRIVS_LOWER))
- zlog_err("Can't lower privileges");
-
if (ret < 0) {
zlog_err("%s sendto failed: %s", nl->name,
- safe_strerror(save_errno));
+ safe_strerror(errno));
return -1;
}
snprintf(zns->netlink.name, sizeof(zns->netlink.name),
"netlink-listen (NS %u)", zns->ns_id);
zns->netlink.sock = -1;
- netlink_socket(&zns->netlink, groups, zns->ns_id);
+ if (netlink_socket(&zns->netlink, groups, zns->ns_id) < 0) {
+ zlog_err("Failure to create %s socket",
+ zns->netlink.name);
+ exit(-1);
+ }
snprintf(zns->netlink_cmd.name, sizeof(zns->netlink_cmd.name),
"netlink-cmd (NS %u)", zns->ns_id);
zns->netlink_cmd.sock = -1;
- netlink_socket(&zns->netlink_cmd, 0, zns->ns_id);
+ if (netlink_socket(&zns->netlink_cmd, 0, zns->ns_id) < 0) {
+ zlog_err("Failure to create %s socket",
+ zns->netlink_cmd.name);
+ exit(-1);
+ }
/*
* SOL_NETLINK is not available on all platforms yet
#endif
/* 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 = NULL;
-
- thread_add_read(zebrad.master, kernel_read, zns,
- zns->netlink.sock, &zns->t_netlink);
- }
+ if (fcntl(zns->netlink.sock, F_SETFL, O_NONBLOCK) < 0)
+ flog_err_sys(EC_LIB_SOCKET, "Can't set %s socket flags: %s",
+ zns->netlink.name, safe_strerror(errno));
+
+ if (fcntl(zns->netlink_cmd.sock, F_SETFL, O_NONBLOCK) < 0)
+ zlog_err("Can't set %s socket error: %s(%d)",
+ zns->netlink_cmd.name, safe_strerror(errno), errno);
+
+ /* Set receive buffer size if it's set from command line */
+ if (nl_rcvbufsize)
+ netlink_recvbuf(&zns->netlink, nl_rcvbufsize);
+
+ assert(zns->netlink.sock >= 0);
+ netlink_install_filter(zns->netlink.sock,
+ zns->netlink_cmd.snl.nl_pid);
+ zns->t_netlink = NULL;
+
+ thread_add_read(zebrad.master, kernel_read, zns,
+ zns->netlink.sock, &zns->t_netlink);
rt_netlink_init();
}