zebra: Make the --nl-bufsize arg set the input parse buffer too
* See bug #887. Existing statically sized NL_PKT_BUF_SIZE input parse buffer
in netlink_parse_info may not be enough. As an initial hacky fix, at least
give admins a runtime way to change this buffer, with the existing
--nl-bufsize argument to zebra.
* rt_netlink.c: (nl_rcvbuf) static input buffer and length.
(netlink_parse_info) replace the local fixed size buffer with nl_rcvbuf.
Improve warning on MSG_TRUNC to advise admin on what to do.
(kernel_init) Dynamically allocate nl_rcvbuf input parse buffer to
at least 2 pages, or nl_rcvbufsize argument, whichever is greater.
Based on the debugging and investigation of:
Konstantin <tempest921@gmail.com>
diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c
index 1a91426..fc6e373 100644
--- a/zebra/rt_netlink.c
+++ b/zebra/rt_netlink.c
@@ -67,6 +67,11 @@
extern u_int32_t nl_rcvbufsize;
+static struct {
+ char *p;
+ size_t size;
+} nl_rcvbuf;
+
/* Note: on netlink systems, there should be a 1-to-1 mapping between interface
names and ifindex values. */
static void
@@ -275,10 +280,9 @@
while (1)
{
- char buf[NL_PKT_BUF_SIZE];
struct iovec iov = {
- .iov_base = buf,
- .iov_len = sizeof buf
+ .iov_base = nl_rcvbuf.p,
+ .iov_len = nl_rcvbuf.size,
};
struct sockaddr_nl snl;
struct msghdr msg = {
@@ -314,7 +318,8 @@
return -1;
}
- for (h = (struct nlmsghdr *) buf; NLMSG_OK (h, (unsigned int) status);
+ for (h = (struct nlmsghdr *) nl_rcvbuf.p;
+ NLMSG_OK (h, (unsigned int) status);
h = NLMSG_NEXT (h, status))
{
/* Finish of reading. */
@@ -407,7 +412,9 @@
/* After error care. */
if (msg.msg_flags & MSG_TRUNC)
{
- zlog (NULL, LOG_ERR, "%s error: message truncated", nl->name);
+ zlog (NULL, LOG_ERR, "%s error: message truncated!", nl->name);
+ zlog (NULL, LOG_ERR,
+ "Must restart with larger --nl-bufsize value!");
continue;
}
if (status)
@@ -2005,6 +2012,8 @@
/* Register kernel socket. */
if (zvrf->netlink.sock > 0)
{
+ size_t bufsize = MAX(nl_rcvbufsize, 2 * sysconf(_SC_PAGESIZE));
+
/* Only want non-blocking on the netlink event socket */
if (fcntl (zvrf->netlink.sock, F_SETFL, O_NONBLOCK) < 0)
zlog_err ("Can't set %s socket flags: %s", zvrf->netlink.name,
@@ -2013,7 +2022,10 @@
/* Set receive buffer size if it's set from command line */
if (nl_rcvbufsize)
netlink_recvbuf (&zvrf->netlink, nl_rcvbufsize);
-
+
+ nl_rcvbuf.p = XMALLOC (MTYPE_NETLINK_RCVBUF, bufsize);
+ nl_rcvbuf.size = bufsize;
+
netlink_install_filter (zvrf->netlink.sock, zvrf->netlink_cmd.snl.nl_pid);
zvrf->t_netlink = thread_add_read (zebrad.master, kernel_read, zvrf,
zvrf->netlink.sock);