Subject: Re: [PATCH net-2.6.26] netlink: make socket filters work on netlink

From: Stephen Hemminger <[email-masked]>
Date: Mon, 31 Mar 2008 12:33:11 -0700

On Wed, 26 Mar 2008 21:19:56 +0100
Patrick McHardy <[email-masked]> wrote:

> Stephen Hemminger wrote:
> > Make socket filters work for netlink unicast and notifications.
> > This is useful for applications like Zebra that get overrun with
> > messages that are then ignored.
> > 
> > Note: netlink messages are in host byte order, but packet filter
> > state machine operations are done as network byte order.
> 
> 
> Do you have an example for a filter for this? I have a similar
> patch that adds a new filter instruction for parsing netlink
> attributes, which seemed necessary for getting at nested
> attributes without too much trouble.
> 
> Attached for reference together with a libnl testing
> patch for ctnetlink.
> 

Here is the example program:
   it uses netlink IPC and has one thread send route notifications
   and the other filters.

   to test the mulitcast path used a hacked version of ip_monitor from iproute

   see attachment for the quagga patch.
>>From 2bc693173112ea436884aca19352624504e2246a Mon Sep 17 00:00:00 2001
From: Stephen Hemminger <[email-masked]>
Date: Mon, 31 Mar 2008 12:23:25 -0700
Subject: [PATCH] Filter unwanted netlink messages

Use socket filter to drop unwanted messages on the netlink listen socket.
This prevents problems where the listener socket buffer gets overrruns
with echos of the new route update that occurs when link changes.
---
 lib/zebra.h        |    1 +
 zebra/rt_netlink.c |   33 ++++++++++++++++++++++++++++++++-
 2 files changed, 33 insertions(+), 1 deletions(-)

diff --git a/lib/zebra.h b/lib/zebra.h
index 150aa2c..d4f68cf 100644
--- a/lib/zebra.h
+++ b/lib/zebra.h
@@ -162,6 +162,7 @@ typedef int socklen_t;
 #ifdef HAVE_NETLINK
 #include <linux/netlink.h>
 #include <linux/rtnetlink.h>
+#include <linux/filter.h>
 #else
 #define RT_TABLE_MAIN		0
 #endif /* HAVE_NETLINK */
diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c
index f071eb2..07e473b 100644
--- a/zebra/rt_netlink.c
+++ b/zebra/rt_netlink.c
@@ -41,6 +41,7 @@
 #include "zebra/redistribute.h"
 #include "zebra/interface.h"
 #include "zebra/debug.h"
+#include <stddef.h>
 
 /* Socket interface to kernel */
 struct nlsock
@@ -1938,6 +1939,33 @@ kernel_read (struct thread *thread)
   return 0;
 }
 
+/* Filter out messages from self that occur on listener socket */
+static void netlink_install_filter (int sock)
+{
+  /* BPF code to exclude all RTM_NEWROUTE messages from ZEBRA */
+  struct sock_filter filter[] = {
+    BPF_STMT(BPF_LD|BPF_ABS|BPF_H, offsetof(struct nlmsghdr, nlmsg_type)),
+    						/* 0: ldh [4]	          */
+    BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(RTM_NEWROUTE), 0, 3),	
+						/* 1: jeq 0x18 jt 2 jf 5  */
+    BPF_STMT(BPF_LD|BPF_ABS|BPF_B, 
+	     sizeof(struct nlmsghdr) + offsetof(struct rtmsg, rtm_protocol)),
+    						/* 2: ldb [23]		  */
+    BPF_JUMP(BPF_JMP+ BPF_B, RTPROT_ZEBRA, 2, 0),
+    						/* 3: jeq 0xb jt 4  jf 5  */
+    BPF_STMT(BPF_RET|BPF_K, 0),			/* 4: ret 0               */
+    BPF_STMT(BPF_RET|BPF_K, 0xffff),		/* 5: ret 0xffff          */
+  };
+
+  struct sock_fprog prog = {
+    .len = sizeof(filter) / sizeof(filter[0]),
+    .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));
+}
+
 /* Exported interface function.  This function simply calls
    netlink_socket (). */
 void
@@ -1954,5 +1982,8 @@ kernel_init (void)
 
   /* Register kernel socket. */
   if (netlink.sock > 0)
-    thread_add_read (zebrad.master, kernel_read, NULL, netlink.sock);
+    {
+      netlink_install_filter (netlink.sock);
+      thread_add_read (zebrad.master, kernel_read, NULL, netlink.sock);
+    }
 }
-- 
1.5.4.3