]> git.puffer.fish Git - mirror/frr.git/commitdiff
[zebra/linux] Use BPF to filter out responses, to try avoid netlink overruns
authorPaul Jakma <paul.jakma@sun.com>
Thu, 29 May 2008 18:23:08 +0000 (18:23 +0000)
committerPaul Jakma <paul.jakma@sun.com>
Thu, 29 May 2008 18:23:08 +0000 (18:23 +0000)
2008-05-29 Stephen Hemminger <stephen.hemminger@vyatta.com>

* rt_netlink.c: (netlink_install_filter) BPF filter to catch and
  drop responses to zebra's own route messages.
  (kernel_init) add BPF filter on the netlink socket.

lib/zebra.h
zebra/ChangeLog
zebra/rt_netlink.c

index 150aa2c59cf977ddc31545ffaa20d10f49a77448..2716460f52f20024ba24ce6a92fe5878a4f57492 100644 (file)
@@ -162,6 +162,8 @@ typedef int socklen_t;
 #ifdef HAVE_NETLINK
 #include <linux/netlink.h>
 #include <linux/rtnetlink.h>
+#include <linux/filter.h>
+#include <stddef.h>
 #else
 #define RT_TABLE_MAIN          0
 #endif /* HAVE_NETLINK */
index d9cae28363b7b5bd978f7470567c7f8e83c9f4fc..6f6dfa2c2e8990a8b4344dc635b9dc551165ce87 100644 (file)
@@ -1,3 +1,9 @@
+2008-05-29 Stephen Hemminger <stephen.hemminger@vyatta.com>
+
+       * rt_netlink.c: (netlink_install_filter) BPF filter to catch and
+         drop responses to zebra's own route messages.
+         (kernel_init) add BPF filter on the netlink socket.
+
 2008-02-26 Denis Ovsienko
        * zebra_rib.[ch]: (rib_lookup_and_pushup) New function, which makes sure,
          that if_set_prefix() has nothing in its way of assigning an address.
index 5b592f948a23b2468bedfd44f1f409fb08ee5488..0bf2d9ebb0f04027f90c119e3ddc161d7c813d35 100644 (file)
@@ -1938,6 +1938,56 @@ kernel_read (struct thread *thread)
   return 0;
 }
 
+/* Filter out messages from self that occur on listener socket */
+static void netlink_install_filter (int sock)
+{
+  /*
+   * Filter is equivalent to netlink_route_change
+   *
+   * if (h->nlmsg_type == RTM_DELROUTE || h->nlmsg_type == RTM_NEWROUTE) {
+   *    if (rtm->rtm_type != RTM_UNICAST)
+   *           return 0;
+   *    if (rtm->rtm_flags & RTM_F_CLONED)
+   *           return 0;
+   *    if (rtm->rtm_protocol == RTPROT_REDIRECT)
+   *           return 0;
+   *    if (rtm->rtm_protocol == RTPROT_KERNEL)
+   *        return 0;
+   *    if (rtm->rtm_protocol == RTPROT_ZEBRA && h->nlmsg_type == RTM_NEWROUTE)
+   *   return 0;
+   * }
+   * return 0xffff;
+   */
+  struct sock_filter filter[] = {
+    /* 0*/ BPF_STMT(BPF_LD|BPF_ABS|BPF_H, offsetof(struct nlmsghdr, nlmsg_type)),
+    /* 1*/ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(RTM_DELROUTE), 1, 0),
+    /* 2*/ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(RTM_NEWROUTE), 0, 11),
+    /* 3*/ BPF_STMT(BPF_LD|BPF_ABS|BPF_B,
+                   sizeof(struct nlmsghdr) + offsetof(struct rtmsg, rtm_type)),
+    /* 4*/ BPF_JUMP(BPF_JMP|BPF_B, RTN_UNICAST, 0, 8),
+    /* 5*/ BPF_STMT(BPF_LD|BPF_ABS|BPF_B,
+                   sizeof(struct nlmsghdr) + offsetof(struct rtmsg, rtm_flags)),
+    /* 6*/ BPF_JUMP(BPF_JMP|BPF_JSET|BPF_K, RTM_F_CLONED, 6, 0),
+    /* 7*/ BPF_STMT(BPF_LD|BPF_ABS|BPF_B,
+                   sizeof(struct nlmsghdr) + offsetof(struct rtmsg, rtm_protocol)),
+    /* 8*/ BPF_JUMP(BPF_JMP+ BPF_B, RTPROT_REDIRECT, 4, 0),
+    /* 9*/ BPF_JUMP(BPF_JMP+ BPF_B, RTPROT_KERNEL, 0, 1),
+    /*10*/ BPF_JUMP(BPF_JMP+ BPF_B, RTPROT_ZEBRA, 0, 3),
+    /*11*/ BPF_STMT(BPF_LD|BPF_ABS|BPF_H, offsetof(struct nlmsghdr, nlmsg_type)),
+    /*12*/ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(RTM_NEWROUTE), 0, 1),
+    /*13*/ BPF_STMT(BPF_RET|BPF_K, 0),         /* drop */
+    /*14*/ BPF_STMT(BPF_RET|BPF_K, 0xffff),    /* keep */
+  };
+
+  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 +2004,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);
+    }
 }