zebra: use SO_RCVBUFFORCE for netlink socket

so net.core.rmem_max must not be adjusted. Requires
linux kernel >= 2.6.14, falls back to SO_RCVBUF on error

Signed-off-by: Ulrich Weber <ulrich.weber@sophos.com>
Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
diff --git a/doc/zebra.8 b/doc/zebra.8
index 23703e7..a40909a 100644
--- a/doc/zebra.8
+++ b/doc/zebra.8
@@ -80,7 +80,7 @@
 messages in zebra log, you are in trouble.
 
 Solution is to increase receive buffer of netlink socket. Note that kernel
-doesn't allow to increase it over maximum value defined in
+< 2.6.14 doesn't allow to increase it over maximum value defined in
 \fI/proc/sys/net/core/rmem_max\fR. If you want to do it, you have to increase
 maximum before starting zebra.
 
diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c
index fa446a5..bab1703 100644
--- a/zebra/rt_netlink.c
+++ b/zebra/rt_netlink.c
@@ -101,6 +101,10 @@
   ifp->ifindex = ifi_index;
 }
 
+#ifndef SO_RCVBUFFORCE
+#define SO_RCVBUFFORCE  (33)
+#endif
+
 static int
 netlink_recvbuf (struct nlsock *nl, uint32_t newsize)
 {
@@ -117,8 +121,16 @@
       return -1;
     }
 
-  ret = setsockopt(nl->sock, SOL_SOCKET, SO_RCVBUF, &nl_rcvbufsize,
+  /* 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,