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/lib/memtypes.c b/lib/memtypes.c
index f9eae93..e5b3546 100644
--- a/lib/memtypes.c
+++ b/lib/memtypes.c
@@ -87,6 +87,7 @@
   { MTYPE_RIB_DEST,		"RIB destination"		},
   { MTYPE_RIB_TABLE_INFO,	"RIB table info"		},
   { MTYPE_NETLINK_NAME,	"Netlink name"			},
+  { MTYPE_NETLINK_RCVBUF,	"Netlink receive buffer"	},
   { MTYPE_RNH,		        "Nexthop tracking object"	},
   { -1, NULL },
 };
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);