]> git.proxmox.com Git - mirror_frr.git/blobdiff - zebra/rt_netlink.c
[zebra] Record NEWADDR metric on PF_ROUTE, print CACHEINFO debug on netlink
[mirror_frr.git] / zebra / rt_netlink.c
index 60043c9b6784f9f5f9aa495740863be0b394a7b8..f1c1a300b8004981cb631fe4782c05d03f0d4fe7 100644 (file)
 #include "table.h"
 #include "rib.h"
 #include "thread.h"
+#include "privs.h"
 
 #include "zebra/zserv.h"
+#include "zebra/rt.h"
 #include "zebra/redistribute.h"
 #include "zebra/interface.h"
 #include "zebra/debug.h"
@@ -46,13 +48,11 @@ struct nlsock
   int sock;
   int seq;
   struct sockaddr_nl snl;
-  char *name;
-} netlink =    { -1, 0, {0}, "netlink-listen" },       /* kernel messages */
-  netlink_cmd = { -1, 0, {0}, "netlink-cmd" },          /* command channel */
-  netlink_addr = {-1, 0, {0}, "netlink-addr" };                /* address channel */
+  const char *name;
+} netlink      = { -1, 0, {0}, "netlink-listen"},     /* kernel messages */
+  netlink_cmd  = { -1, 0, {0}, "netlink-cmd"};        /* command channel */
 
-struct message nlmsg_str[] =
-{
+struct message nlmsg_str[] = {
   {RTM_NEWROUTE, "RTM_NEWROUTE"},
   {RTM_DELROUTE, "RTM_DELROUTE"},
   {RTM_GETROUTE, "RTM_GETROUTE"},
@@ -62,10 +62,56 @@ struct message nlmsg_str[] =
   {RTM_NEWADDR,  "RTM_NEWADDR"},
   {RTM_DELADDR,  "RTM_DELADDR"},
   {RTM_GETADDR,  "RTM_GETADDR"},
-  {0,            NULL}
+  {0, NULL}
+};
+
+const char *nexthop_types_desc[] =  
+{
+  "none",
+  "Directly connected",
+  "Interface route",
+  "IPv4 nexthop",
+  "IPv4 nexthop with ifindex",
+  "IPv4 nexthop with ifname",
+  "IPv6 nexthop",
+  "IPv6 nexthop with ifindex",
+  "IPv6 nexthop with ifname",
+  "Null0 nexthop",
 };
 
-extern int rtm_table_default;
+
+extern struct zebra_t zebrad;
+
+extern struct zebra_privs_t zserv_privs;
+
+extern u_int32_t nl_rcvbufsize;
+
+/* Note: on netlink systems, there should be a 1-to-1 mapping between interface
+   names and ifindex values. */
+static void
+set_ifindex(struct interface *ifp, unsigned int ifi_index)
+{
+  struct interface *oifp;
+
+  if (((oifp = if_lookup_by_index(ifi_index)) != NULL) && (oifp != ifp))
+    {
+      if (ifi_index == IFINDEX_INTERNAL)
+        zlog_err("Netlink is setting interface %s ifindex to reserved "
+                "internal value %u", ifp->name, ifi_index);
+      else
+        {
+         if (IS_ZEBRA_DEBUG_KERNEL)
+           zlog_debug("interface index %d was renamed from %s to %s",
+                      ifi_index, oifp->name, ifp->name);
+         if (if_is_up(oifp))
+           zlog_err("interface rename detected on up interface: index %d "
+                    "was renamed from %s to %s, results are uncertain!", 
+                    ifi_index, oifp->name, ifp->name);
+         if_delete_update(oifp);
+        }
+    }
+  ifp->ifindex = ifi_index;
+}
 
 /* Make socket for Linux netlink interface. */
 static int
@@ -75,12 +121,13 @@ netlink_socket (struct nlsock *nl, unsigned long groups)
   struct sockaddr_nl snl;
   int sock;
   int namelen;
+  int save_errno;
 
   sock = socket (AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
   if (sock < 0)
     {
       zlog (NULL, LOG_ERR, "Can't open %s socket: %s", nl->name,
-           strerror (errno));
+            safe_strerror (errno));
       return -1;
     }
 
@@ -88,32 +135,84 @@ netlink_socket (struct nlsock *nl, unsigned long groups)
   if (ret < 0)
     {
       zlog (NULL, LOG_ERR, "Can't set %s socket flags: %s", nl->name,
-           strerror (errno));
+            safe_strerror (errno));
       close (sock);
       return -1;
     }
-  
+
+  /* Set receive buffer size if it's set from command line */
+  if (nl_rcvbufsize)
+    {
+      u_int32_t oldsize, oldlen;
+      u_int32_t newsize, newlen;
+
+      oldlen = sizeof(oldsize);
+      newlen = sizeof(newsize);
+
+      ret = getsockopt(sock, SOL_SOCKET, SO_RCVBUF, &oldsize, &oldlen);
+      if (ret < 0)
+       {
+         zlog (NULL, LOG_ERR, "Can't get %s receive buffer size: %s", nl->name,
+               safe_strerror (errno));
+         close (sock);
+         return -1;
+       }
+
+      ret = setsockopt(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,
+               safe_strerror (errno));
+         close (sock);
+         return -1;
+       }
+
+      ret = getsockopt(sock, SOL_SOCKET, SO_RCVBUF, &newsize, &newlen);
+      if (ret < 0)
+       {
+         zlog (NULL, LOG_ERR, "Can't get %s receive buffer size: %s", nl->name,
+               safe_strerror (errno));
+         close (sock);
+         return -1;
+       }
+
+      zlog (NULL, LOG_INFO,
+           "Setting netlink socket receive buffer size: %u -> %u",
+           oldsize, newsize);
+    }
+
   memset (&snl, 0, sizeof snl);
   snl.nl_family = AF_NETLINK;
   snl.nl_groups = groups;
 
   /* Bind the socket to the netlink structure for anything. */
+  if (zserv_privs.change (ZPRIVS_RAISE))
+    {
+      zlog (NULL, LOG_ERR, "Can't raise privileges");
+      return -1;
+    }
+
   ret = bind (sock, (struct sockaddr *) &snl, sizeof snl);
+  save_errno = errno;
+  if (zserv_privs.change (ZPRIVS_LOWER))
+    zlog (NULL, LOG_ERR, "Can't lower privileges");
+
   if (ret < 0)
     {
-      zlog (NULL, LOG_ERR, "Can't bind %s socket to group 0x%x: %s", 
-           nl->name, snl.nl_groups, strerror (errno));
+      zlog (NULL, LOG_ERR, "Can't bind %s socket to group 0x%x: %s",
+            nl->name, snl.nl_groups, safe_strerror (save_errno));
       close (sock);
       return -1;
     }
 
   /* multiple netlink sockets will have different nl_pid */
   namelen = sizeof snl;
-  ret = getsockname (sock, (struct sockaddr *) &snl, &namelen);
+  ret = getsockname (sock, (struct sockaddr *) &snl, (socklen_t *) &namelen);
   if (ret < 0 || namelen != sizeof snl)
     {
       zlog (NULL, LOG_ERR, "Can't get %s socket name: %s", nl->name,
-           strerror (errno));
+            safe_strerror (errno));
       close (sock);
       return -1;
     }
@@ -123,34 +222,36 @@ netlink_socket (struct nlsock *nl, unsigned long groups)
   return ret;
 }
 
-int set_netlink_blocking(struct nlsock *nl, int *flags)
+int
+set_netlink_blocking (struct nlsock *nl, int *flags)
 {
 
   /* Change socket flags for blocking I/O.  */
-  if((*flags = fcntl(nl->sock, F_GETFL, 0)) < 0) 
+  if ((*flags = fcntl (nl->sock, F_GETFL, 0)) < 0)
     {
-      zlog (NULL, LOG_ERR, "%s:%i F_GETFL error: %s", 
-              __FUNCTION__, __LINE__, strerror (errno));
+      zlog (NULL, LOG_ERR, "%s:%i F_GETFL error: %s",
+            __FUNCTION__, __LINE__, safe_strerror (errno));
       return -1;
     }
   *flags &= ~O_NONBLOCK;
-  if(fcntl(nl->sock, F_SETFL, *flags) < 0) 
+  if (fcntl (nl->sock, F_SETFL, *flags) < 0)
     {
-      zlog (NULL, LOG_ERR, "%s:%i F_SETFL error: %s", 
-              __FUNCTION__, __LINE__, strerror (errno));
+      zlog (NULL, LOG_ERR, "%s:%i F_SETFL error: %s",
+            __FUNCTION__, __LINE__, safe_strerror (errno));
       return -1;
     }
   return 0;
 }
 
-int set_netlink_nonblocking(struct nlsock *nl, int *flags)
-{  
+int
+set_netlink_nonblocking (struct nlsock *nl, int *flags)
+{
   /* Restore socket flags for nonblocking I/O */
   *flags |= O_NONBLOCK;
-  if(fcntl(nl->sock, F_SETFL, *flags) < 0) 
+  if (fcntl (nl->sock, F_SETFL, *flags) < 0)
     {
-      zlog (NULL, LOG_ERR, "%s:%i F_SETFL error: %s", 
-              __FUNCTION__, __LINE__, strerror (errno));
+      zlog (NULL, LOG_ERR, "%s:%i F_SETFL error: %s",
+            __FUNCTION__, __LINE__, safe_strerror (errno));
       return -1;
     }
   return 0;
@@ -162,6 +263,7 @@ netlink_request (int family, int type, struct nlsock *nl)
 {
   int ret;
   struct sockaddr_nl snl;
+  int save_errno;
 
   struct
   {
@@ -180,20 +282,37 @@ netlink_request (int family, int type, struct nlsock *nl)
   memset (&snl, 0, sizeof snl);
   snl.nl_family = AF_NETLINK;
 
+  memset (&req, 0, sizeof req);
   req.nlh.nlmsg_len = sizeof req;
   req.nlh.nlmsg_type = type;
   req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
   req.nlh.nlmsg_pid = 0;
   req.nlh.nlmsg_seq = ++nl->seq;
   req.g.rtgen_family = family;
-  ret = sendto (nl->sock, (void*) &req, sizeof req, 0, 
-               (struct sockaddr*) &snl, sizeof snl);
+
+  /* linux appears to check capabilities on every message 
+   * have to raise caps for every message sent
+   */
+  if (zserv_privs.change (ZPRIVS_RAISE))
+    {
+      zlog (NULL, LOG_ERR, "Can't raise privileges");
+      return -1;
+    }
+
+  ret = sendto (nl->sock, (void *) &req, sizeof req, 0,
+                (struct sockaddr *) &snl, sizeof snl);
+  save_errno = errno;
+
+  if (zserv_privs.change (ZPRIVS_LOWER))
+    zlog (NULL, LOG_ERR, "Can't lower privileges");
+
   if (ret < 0)
     {
-      zlog (NULL, LOG_ERR, "%s sendto failed: %s", nl->name, strerror (errno));
+      zlog (NULL, LOG_ERR, "%s sendto failed: %s", nl->name,
+            safe_strerror (save_errno));
       return -1;
     }
+
   return 0;
 }
 
@@ -201,7 +320,7 @@ netlink_request (int family, int type, struct nlsock *nl)
    to the given function. */
 static int
 netlink_parse_info (int (*filter) (struct sockaddr_nl *, struct nlmsghdr *),
-                   struct nlsock *nl)
+                    struct nlsock *nl)
 {
   int status;
   int ret = 0;
@@ -212,148 +331,163 @@ netlink_parse_info (int (*filter) (struct sockaddr_nl *, struct nlmsghdr *),
       char buf[4096];
       struct iovec iov = { buf, sizeof buf };
       struct sockaddr_nl snl;
-      struct msghdr msg = { (void*)&snl, sizeof snl, &iov, 1, NULL, 0, 0};
+      struct msghdr msg = { (void *) &snl, sizeof snl, &iov, 1, NULL, 0, 0 };
       struct nlmsghdr *h;
+      int save_errno;
+
+      if (zserv_privs.change (ZPRIVS_RAISE))
+        zlog (NULL, LOG_ERR, "Can't raise privileges");
 
       status = recvmsg (nl->sock, &msg, 0);
+      save_errno = errno;
+
+      if (zserv_privs.change (ZPRIVS_LOWER))
+        zlog (NULL, LOG_ERR, "Can't lower privileges");
 
       if (status < 0)
-       {
-         if (errno == EINTR)
-           continue;
-         if (errno == EWOULDBLOCK || errno == EAGAIN)
-           break;
-         zlog (NULL, LOG_ERR, "%s recvmsg overrun", nl->name);
-         continue;
-       }
+        {
+          if (save_errno == EINTR)
+            continue;
+          if (save_errno == EWOULDBLOCK || save_errno == EAGAIN)
+            break;
+          zlog (NULL, LOG_ERR, "%s recvmsg overrun: %s",
+               nl->name, safe_strerror(save_errno));
+          continue;
+        }
 
       if (status == 0)
-       {
-         zlog (NULL, LOG_ERR, "%s EOF", nl->name);
-         return -1;
-       }
+        {
+          zlog (NULL, LOG_ERR, "%s EOF", nl->name);
+          return -1;
+        }
 
       if (msg.msg_namelen != sizeof snl)
-       {
-         zlog (NULL, LOG_ERR, "%s sender address length error: length %d",
-              nl->name, msg.msg_namelen);
-         return -1;
-       }
+        {
+          zlog (NULL, LOG_ERR, "%s sender address length error: length %d",
+                nl->name, msg.msg_namelen);
+          return -1;
+        }
+      
+      /* JF: Ignore messages that aren't from the kernel */
+      if ( snl.nl_pid != 0 )
+        {
+          zlog ( NULL, LOG_ERR, "Ignoring message from pid %u", snl.nl_pid );
+          continue;
+        }
+
+      for (h = (struct nlmsghdr *) buf; NLMSG_OK (h, (unsigned int) status);
+           h = NLMSG_NEXT (h, status))
+        {
+          /* Finish of reading. */
+          if (h->nlmsg_type == NLMSG_DONE)
+            return ret;
+
+          /* Error handling. */
+          if (h->nlmsg_type == NLMSG_ERROR)
+            {
+              struct nlmsgerr *err = (struct nlmsgerr *) NLMSG_DATA (h);
 
-      for (h = (struct nlmsghdr *) buf; NLMSG_OK (h, status); 
-          h = NLMSG_NEXT (h, status))
-       {
-         /* Finish of reading. */
-         if (h->nlmsg_type == NLMSG_DONE)
-           return ret;
-
-         /* Error handling. */
-         if (h->nlmsg_type == NLMSG_ERROR)
-           {
-             struct nlmsgerr *err = (struct nlmsgerr *) NLMSG_DATA (h);
-             
               /* If the error field is zero, then this is an ACK */
-              if (err->error == 0) 
+              if (err->error == 0)
                 {
-                  if (IS_ZEBRA_DEBUG_KERNEL) 
-                    {  
-                      zlog_info("%s: %s ACK: type=%s(%u), seq=%u, pid=%d", 
-                        __FUNCTION__, nl->name,
-                        lookup (nlmsg_str, err->msg.nlmsg_type),
-                        err->msg.nlmsg_type, err->msg.nlmsg_seq,
-                       err->msg.nlmsg_pid);
+                  if (IS_ZEBRA_DEBUG_KERNEL)
+                    {
+                      zlog_debug ("%s: %s ACK: type=%s(%u), seq=%u, pid=%u",
+                                 __FUNCTION__, nl->name,
+                                 lookup (nlmsg_str, err->msg.nlmsg_type),
+                                 err->msg.nlmsg_type, err->msg.nlmsg_seq,
+                                 err->msg.nlmsg_pid);
                     }
-                
-                  /* return if not a multipart message, otherwise continue */  
-                  if(!(h->nlmsg_flags & NLM_F_MULTI)) 
-                    { 
-                      return 0;    
+
+                  /* return if not a multipart message, otherwise continue */
+                  if (!(h->nlmsg_flags & NLM_F_MULTI))
+                    {
+                      return 0;
                     }
-                  continue; 
+                  continue;
                 }
-              
+
               if (h->nlmsg_len < NLMSG_LENGTH (sizeof (struct nlmsgerr)))
-               {
-                 zlog (NULL, LOG_ERR, "%s error: message truncated",
-                       nl->name);
-                 return -1;
-               }
-             
-             /* Deal with Error Noise  - MAG*/
-             {
-               int loglvl = LOG_ERR;
-               int errnum = err->error;
-               int msg_type = err->msg.nlmsg_type;
-               
-               if (nl == &netlink_cmd 
-                   && (-errnum == ENODEV || -errnum == ESRCH)
-                   && (msg_type == RTM_NEWROUTE 
-                       || msg_type == RTM_DELROUTE)) 
-                   loglvl = LOG_DEBUG;
-
-               zlog (NULL, loglvl, "%s error: %s, type=%s(%u), "
-                     "seq=%u, pid=%d",
-                     nl->name, strerror (-errnum),
-                     lookup (nlmsg_str, msg_type),
-                     msg_type, err->msg.nlmsg_seq,
-                   err->msg.nlmsg_pid);
-             }
-             /*
-             ret = -1;
-             continue;
-             */
-             return -1;
-           }
-
-         /* OK we got netlink message. */
-         if (IS_ZEBRA_DEBUG_KERNEL)
-           zlog_info ("netlink_parse_info: %s type %s(%u), seq=%u, pid=%d",
-                     nl->name,
-                     lookup (nlmsg_str, h->nlmsg_type), h->nlmsg_type,
-                     h->nlmsg_seq, h->nlmsg_pid);
-
-         /* skip unsolicited messages originating from command socket */
-         if (nl != &netlink_cmd && h->nlmsg_pid == netlink_cmd.snl.nl_pid)
-           {
-             if (IS_ZEBRA_DEBUG_KERNEL)
-               zlog_info ("netlink_parse_info: %s packet comes from %s",
-                         nl->name, netlink_cmd.name);
-             continue;
-           }
-
-         error = (*filter) (&snl, h);
-         if (error < 0)
-           {
-             zlog (NULL, LOG_ERR, "%s filter function error", nl->name);
-             ret = error;
-           }
-       }
+                {
+                  zlog (NULL, LOG_ERR, "%s error: message truncated",
+                        nl->name);
+                  return -1;
+                }
+
+              /* Deal with Error Noise  - MAG */
+              {
+                int loglvl = LOG_ERR;
+                int errnum = err->error;
+                int msg_type = err->msg.nlmsg_type;
+
+                if (nl == &netlink_cmd
+                    && (-errnum == ENODEV || -errnum == ESRCH)
+                    && (msg_type == RTM_NEWROUTE || msg_type == RTM_DELROUTE))
+                  loglvl = LOG_DEBUG;
+
+                zlog (NULL, loglvl, "%s error: %s, type=%s(%u), "
+                      "seq=%u, pid=%u",
+                      nl->name, safe_strerror (-errnum),
+                      lookup (nlmsg_str, msg_type),
+                      msg_type, err->msg.nlmsg_seq, err->msg.nlmsg_pid);
+              }
+              /*
+                 ret = -1;
+                 continue;
+               */
+              return -1;
+            }
+
+          /* OK we got netlink message. */
+          if (IS_ZEBRA_DEBUG_KERNEL)
+            zlog_debug ("netlink_parse_info: %s type %s(%u), seq=%u, pid=%u",
+                       nl->name,
+                       lookup (nlmsg_str, h->nlmsg_type), h->nlmsg_type,
+                       h->nlmsg_seq, h->nlmsg_pid);
+
+          /* skip unsolicited messages originating from command socket */
+          if (nl != &netlink_cmd && h->nlmsg_pid == netlink_cmd.snl.nl_pid)
+            {
+              if (IS_ZEBRA_DEBUG_KERNEL)
+                zlog_debug ("netlink_parse_info: %s packet comes from %s",
+                            netlink_cmd.name, nl->name);
+              continue;
+            }
+
+          error = (*filter) (&snl, h);
+          if (error < 0)
+            {
+              zlog (NULL, LOG_ERR, "%s filter function error", nl->name);
+              ret = error;
+            }
+        }
 
       /* After error care. */
       if (msg.msg_flags & MSG_TRUNC)
-       {
-         zlog (NULL, LOG_ERR, "%s error: message truncated", nl->name);
-         continue;
-       }
+        {
+          zlog (NULL, LOG_ERR, "%s error: message truncated", nl->name);
+          continue;
+        }
       if (status)
-       {
-         zlog (NULL, LOG_ERR, "%s error: data remnant size %d", nl->name,
-               status);
-         return -1;
-       }
+        {
+          zlog (NULL, LOG_ERR, "%s error: data remnant size %d", nl->name,
+                status);
+          return -1;
+        }
     }
   return ret;
 }
 
 /* Utility function for parse rtattr. */
 static void
-netlink_parse_rtattr (struct rtattr **tb, int max, struct rtattr *rta, int len)
+netlink_parse_rtattr (struct rtattr **tb, int max, struct rtattr *rta,
+                      int len)
 {
-  while (RTA_OK(rta, len)) 
+  while (RTA_OK (rta, len))
     {
       if (rta->rta_type <= max)
-       tb[rta->rta_type] = rta;
-      rta = RTA_NEXT(rta,len);
+        tb[rta->rta_type] = rta;
+      rta = RTA_NEXT (rta, len);
     }
 }
 
@@ -381,16 +515,26 @@ netlink_interface (struct sockaddr_nl *snl, struct nlmsghdr *h)
   /* Looking up interface name. */
   memset (tb, 0, sizeof tb);
   netlink_parse_rtattr (tb, IFLA_MAX, IFLA_RTA (ifi), len);
+  
+#ifdef IFLA_WIRELESS
+  /* check for wireless messages to ignore */
+  if ((tb[IFLA_WIRELESS] != NULL) && (ifi->ifi_change == 0))
+    {
+      if (IS_ZEBRA_DEBUG_KERNEL)
+        zlog_debug ("%s: ignoring IFLA_WIRELESS message", __func__);
+      return 0;
+    }
+#endif /* IFLA_WIRELESS */
+
   if (tb[IFLA_IFNAME] == NULL)
     return -1;
-  name = (char *)RTA_DATA(tb[IFLA_IFNAME]);
+  name = (char *) RTA_DATA (tb[IFLA_IFNAME]);
 
   /* Add interface. */
   ifp = if_get_by_name (name);
-  
-  ifp->ifindex = ifi->ifi_index;
+  set_ifindex(ifp, ifi->ifi_index);
   ifp->flags = ifi->ifi_flags & 0x0000fffff;
-  ifp->mtu = *(int *)RTA_DATA (tb[IFLA_MTU]);
+  ifp->mtu6 = ifp->mtu = *(int *) RTA_DATA (tb[IFLA_MTU]);
   ifp->metric = 1;
 
   /* Hardware type and address. */
@@ -400,24 +544,24 @@ netlink_interface (struct sockaddr_nl *snl, struct nlmsghdr *h)
     {
       int hw_addr_len;
 
-      hw_addr_len = RTA_PAYLOAD(tb[IFLA_ADDRESS]);
+      hw_addr_len = RTA_PAYLOAD (tb[IFLA_ADDRESS]);
 
       if (hw_addr_len > INTERFACE_HWADDR_MAX)
-       zlog_warn ("Hardware address is too large: %d", hw_addr_len);
+        zlog_warn ("Hardware address is too large: %d", hw_addr_len);
       else
-       {      
-         ifp->hw_addr_len = hw_addr_len;
-         memcpy (ifp->hw_addr, RTA_DATA(tb[IFLA_ADDRESS]), hw_addr_len);
-
-         for (i = 0; i < hw_addr_len; i++)
-           if (ifp->hw_addr[i] != 0)
-             break;
-
-         if (i == hw_addr_len)
-           ifp->hw_addr_len = 0;
-         else
-           ifp->hw_addr_len = hw_addr_len;
-       }
+        {
+          ifp->hw_addr_len = hw_addr_len;
+          memcpy (ifp->hw_addr, RTA_DATA (tb[IFLA_ADDRESS]), hw_addr_len);
+
+          for (i = 0; i < hw_addr_len; i++)
+            if (ifp->hw_addr[i] != 0)
+              break;
+
+          if (i == hw_addr_len)
+            ifp->hw_addr_len = 0;
+          else
+            ifp->hw_addr_len = hw_addr_len;
+        }
     }
 
   if_add_update (ifp);
@@ -431,27 +575,26 @@ netlink_interface_addr (struct sockaddr_nl *snl, struct nlmsghdr *h)
 {
   int len;
   struct ifaddrmsg *ifa;
-  struct rtattr *tb [IFA_MAX + 1];
+  struct rtattr *tb[IFA_MAX + 1];
   struct interface *ifp;
   void *addr = NULL;
   void *broad = NULL;
   u_char flags = 0;
   char *label = NULL;
-  int peeronly = 0;
 
   ifa = NLMSG_DATA (h);
 
-  if (ifa->ifa_family != AF_INET 
+  if (ifa->ifa_family != AF_INET
 #ifdef HAVE_IPV6
       && ifa->ifa_family != AF_INET6
 #endif /* HAVE_IPV6 */
-      )
+    )
     return 0;
 
   if (h->nlmsg_type != RTM_NEWADDR && h->nlmsg_type != RTM_DELADDR)
     return 0;
 
-  len = h->nlmsg_len - NLMSG_LENGTH(sizeof (struct ifaddrmsg));
+  len = h->nlmsg_len - NLMSG_LENGTH (sizeof (struct ifaddrmsg));
   if (len < 0)
     return -1;
 
@@ -462,59 +605,73 @@ netlink_interface_addr (struct sockaddr_nl *snl, struct nlmsghdr *h)
   if (ifp == NULL)
     {
       zlog_err ("netlink_interface_addr can't find interface by index %d",
-               ifa->ifa_index);
+                ifa->ifa_index);
       return -1;
     }
 
-  if (IS_ZEBRA_DEBUG_KERNEL)   /* remove this line to see initial ifcfg */
+  if (IS_ZEBRA_DEBUG_KERNEL)    /* remove this line to see initial ifcfg */
     {
       char buf[BUFSIZ];
-      zlog_info ("netlink_interface_addr %s %s/%d:",
-                lookup (nlmsg_str, h->nlmsg_type),
-                ifp->name, ifa->ifa_prefixlen);
+      zlog_debug ("netlink_interface_addr %s %s:",
+                 lookup (nlmsg_str, h->nlmsg_type), ifp->name);
       if (tb[IFA_LOCAL])
-       zlog_info ("  IFA_LOCAL     %s", inet_ntop (ifa->ifa_family,
-                 RTA_DATA (tb[IFA_LOCAL]), buf, BUFSIZ));
+        zlog_debug ("  IFA_LOCAL     %s/%d",
+                   inet_ntop (ifa->ifa_family, RTA_DATA (tb[IFA_LOCAL]),
+                              buf, BUFSIZ), ifa->ifa_prefixlen);
       if (tb[IFA_ADDRESS])
-       zlog_info ("  IFA_ADDRESS   %s", inet_ntop (ifa->ifa_family,
-                 RTA_DATA (tb[IFA_ADDRESS]), buf, BUFSIZ));
+        zlog_debug ("  IFA_ADDRESS   %s/%d",
+                   inet_ntop (ifa->ifa_family, RTA_DATA (tb[IFA_ADDRESS]),
+                               buf, BUFSIZ), ifa->ifa_prefixlen);
       if (tb[IFA_BROADCAST])
-       zlog_info ("  IFA_BROADCAST %s", inet_ntop (ifa->ifa_family,
-                 RTA_DATA (tb[IFA_BROADCAST]), buf, BUFSIZ));
+        zlog_debug ("  IFA_BROADCAST %s/%d",
+                   inet_ntop (ifa->ifa_family, RTA_DATA (tb[IFA_BROADCAST]),
+                              buf, BUFSIZ), ifa->ifa_prefixlen);
       if (tb[IFA_LABEL] && strcmp (ifp->name, RTA_DATA (tb[IFA_LABEL])))
-       zlog_info ("  IFA_LABEL     %s", RTA_DATA (tb[IFA_LABEL]));
+        zlog_debug ("  IFA_LABEL     %s", (char *)RTA_DATA (tb[IFA_LABEL]));
+      
+      if (tb[IFA_CACHEINFO])
+        {
+          struct ifa_cacheinfo *ci = RTA_DATA (tb[IFA_CACHEINFO]);
+          zlog_debug ("  IFA_CACHEINFO pref %d, valid %d",
+                      ci->ifa_prefered, ci->ifa_valid);
+        }
+    }
+  
+  if (tb[IFA_ADDRESS] == NULL)
+    tb[IFA_ADDRESS] = tb[IFA_LOCAL];
+  
+  if (ifp->flags & IFF_POINTOPOINT)
+    {
+      if (tb[IFA_LOCAL])
+        {
+          addr = RTA_DATA (tb[IFA_LOCAL]);
+          if (tb[IFA_ADDRESS] &&
+             memcmp(RTA_DATA(tb[IFA_ADDRESS]),RTA_DATA(tb[IFA_LOCAL]),4))
+           /* if IFA_ADDRESS != IFA_LOCAL, then it's the peer address */
+            broad = RTA_DATA (tb[IFA_ADDRESS]);
+          else
+            broad = NULL;
+        }
+      else
+        {
+          if (tb[IFA_ADDRESS])
+            addr = RTA_DATA (tb[IFA_ADDRESS]);
+          else
+            addr = NULL;
+        }
     }
-
-  /* peer or broadcast network? */
-  if (ifa->ifa_family == AF_INET)
-    peeronly = if_is_pointopoint (ifp) ||
-              ifa->ifa_prefixlen >= IPV4_MAX_PREFIXLEN - 1;
-#ifdef HAVE_IPV6
-  if (ifa->ifa_family == AF_INET6) {
-    peeronly = if_is_pointopoint (ifp) ||
-              ifa->ifa_prefixlen >= IPV6_MAX_PREFIXLEN - 1;
-  }
-#endif /* HAVE_IPV6*/
-  if (!(tb[IFA_LOCAL] && tb[IFA_ADDRESS])) {
-       /* FIXME: IPv6 Appears to have only IFA_ADDRESS */
-       peeronly=0;
-  }
-
-  /* network. prefixlen applies to IFA_ADDRESS rather than IFA_LOCAL */
-  if (tb[IFA_ADDRESS] && !peeronly)
-    addr = RTA_DATA (tb[IFA_ADDRESS]);
-  else if (tb[IFA_LOCAL])
-    addr = RTA_DATA (tb[IFA_LOCAL]);
-  else
-    addr = NULL;
-
-  /* broadcast/peer */
-  if (tb[IFA_BROADCAST])
-    broad = RTA_DATA (tb[IFA_BROADCAST]);
-  else if (tb[IFA_ADDRESS] && peeronly)
-    broad = RTA_DATA (tb[IFA_ADDRESS]);                /* peer address specified */
   else
-    broad = NULL;
+    {
+      if (tb[IFA_ADDRESS])
+        addr = RTA_DATA (tb[IFA_ADDRESS]);
+      else
+        addr = NULL;
+      
+      if (tb[IFA_BROADCAST])
+        broad = RTA_DATA(tb[IFA_BROADCAST]);
+      else
+        broad = NULL;
+    }
 
   /* Flags. */
   if (ifa->ifa_flags & IFA_F_SECONDARY)
@@ -530,28 +687,28 @@ netlink_interface_addr (struct sockaddr_nl *snl, struct nlmsghdr *h)
   /* Register interface address to the interface. */
   if (ifa->ifa_family == AF_INET)
     {
-      if (h->nlmsg_type == RTM_NEWADDR) 
-       connected_add_ipv4 (ifp, flags,
-                           (struct in_addr *) addr, ifa->ifa_prefixlen, 
-                           (struct in_addr *) broad, label);
-      else 
-       connected_delete_ipv4 (ifp, flags,
-                              (struct in_addr *) addr, ifa->ifa_prefixlen, 
-                              (struct in_addr *) broad, label);
+      if (h->nlmsg_type == RTM_NEWADDR)
+        connected_add_ipv4 (ifp, flags,
+                            (struct in_addr *) addr, ifa->ifa_prefixlen,
+                            (struct in_addr *) broad, label);
+      else
+        connected_delete_ipv4 (ifp, flags,
+                               (struct in_addr *) addr, ifa->ifa_prefixlen,
+                               (struct in_addr *) broad);
     }
 #ifdef HAVE_IPV6
   if (ifa->ifa_family == AF_INET6)
     {
       if (h->nlmsg_type == RTM_NEWADDR)
-       connected_add_ipv6 (ifp, 
-                           (struct in6_addr *) addr, ifa->ifa_prefixlen, 
-                           (struct in6_addr *) broad);
+        connected_add_ipv6 (ifp,
+                            (struct in6_addr *) addr, ifa->ifa_prefixlen,
+                            (struct in6_addr *) broad, label);
       else
-       connected_delete_ipv6 (ifp, 
-                              (struct in6_addr *) addr, ifa->ifa_prefixlen, 
-                              (struct in6_addr *) broad);
+        connected_delete_ipv6 (ifp,
+                               (struct in6_addr *) addr, ifa->ifa_prefixlen,
+                               (struct in6_addr *) broad);
     }
-#endif /* HAVE_IPV6*/
+#endif /* HAVE_IPV6 */
 
   return 0;
 }
@@ -562,13 +719,15 @@ netlink_routing_table (struct sockaddr_nl *snl, struct nlmsghdr *h)
 {
   int len;
   struct rtmsg *rtm;
-  struct rtattr *tb [RTA_MAX + 1];
+  struct rtattr *tb[RTA_MAX + 1];
   u_char flags = 0;
-  
-  char anyaddr[16] = {0};
+
+  char anyaddr[16] = { 0 };
 
   int index;
   int table;
+  int metric;
+
   void *dest;
   void *gate;
 
@@ -580,12 +739,12 @@ netlink_routing_table (struct sockaddr_nl *snl, struct nlmsghdr *h)
     return 0;
 
   table = rtm->rtm_table;
-#if 0          /* we weed them out later in rib_weed_tables () */
-  if (table != RT_TABLE_MAIN && table != rtm_table_default)
+#if 0                           /* we weed them out later in rib_weed_tables () */
+  if (table != RT_TABLE_MAIN && table != zebrad.rtm_table_default)
     return 0;
 #endif
 
-  len = h->nlmsg_len - NLMSG_LENGTH(sizeof (struct rtmsg));
+  len = h->nlmsg_len - NLMSG_LENGTH (sizeof (struct rtmsg));
   if (len < 0)
     return -1;
 
@@ -605,8 +764,9 @@ netlink_routing_table (struct sockaddr_nl *snl, struct nlmsghdr *h)
   /* Route which inserted by Zebra. */
   if (rtm->rtm_protocol == RTPROT_ZEBRA)
     flags |= ZEBRA_FLAG_SELFROUTE;
-  
+
   index = 0;
+  metric = 0;
   dest = NULL;
   gate = NULL;
 
@@ -622,6 +782,9 @@ netlink_routing_table (struct sockaddr_nl *snl, struct nlmsghdr *h)
   if (tb[RTA_GATEWAY])
     gate = RTA_DATA (tb[RTA_GATEWAY]);
 
+  if (tb[RTA_PRIORITY])
+    metric = *(int *) RTA_DATA(tb[RTA_PRIORITY]);
+
   if (rtm->rtm_family == AF_INET)
     {
       struct prefix_ipv4 p;
@@ -629,7 +792,7 @@ netlink_routing_table (struct sockaddr_nl *snl, struct nlmsghdr *h)
       memcpy (&p.prefix, dest, 4);
       p.prefixlen = rtm->rtm_dst_len;
 
-      rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, flags, &p, gate, index, table, 0, 0);
+      rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, flags, &p, gate, index, table, metric, 0);
     }
 #ifdef HAVE_IPV6
   if (rtm->rtm_family == AF_INET6)
@@ -639,15 +802,15 @@ netlink_routing_table (struct sockaddr_nl *snl, struct nlmsghdr *h)
       memcpy (&p.prefix, dest, 16);
       p.prefixlen = rtm->rtm_dst_len;
 
-      rib_add_ipv6 (ZEBRA_ROUTE_KERNEL, flags, &p, gate, index, table);
+      rib_add_ipv6 (ZEBRA_ROUTE_KERNEL, flags, &p, gate, index, table,
+                   metric, 0);
     }
 #endif /* HAVE_IPV6 */
 
   return 0;
 }
 
-struct message rtproto_str [] = 
-{
+struct message rtproto_str[] = {
   {RTPROT_REDIRECT, "redirect"},
   {RTPROT_KERNEL,   "kernel"},
   {RTPROT_BOOT,     "boot"},
@@ -668,9 +831,9 @@ netlink_route_change (struct sockaddr_nl *snl, struct nlmsghdr *h)
 {
   int len;
   struct rtmsg *rtm;
-  struct rtattr *tb [RTA_MAX + 1];
-  
-  char anyaddr[16] = {0};
+  struct rtattr *tb[RTA_MAX + 1];
+
+  char anyaddr[16] = { 0 };
 
   int index;
   int table;
@@ -679,7 +842,7 @@ netlink_route_change (struct sockaddr_nl *snl, struct nlmsghdr *h)
 
   rtm = NLMSG_DATA (h);
 
-  if (! (h->nlmsg_type == RTM_NEWROUTE || h->nlmsg_type == RTM_DELROUTE))
+  if (!(h->nlmsg_type == RTM_NEWROUTE || h->nlmsg_type == RTM_DELROUTE))
     {
       /* If this is not route add/delete message print warning. */
       zlog_warn ("Kernel message: %d\n", h->nlmsg_type);
@@ -688,11 +851,12 @@ netlink_route_change (struct sockaddr_nl *snl, struct nlmsghdr *h)
 
   /* Connected route. */
   if (IS_ZEBRA_DEBUG_KERNEL)
-    zlog_info ("%s %s %s proto %s",
-              h->nlmsg_type == RTM_NEWROUTE ? "RTM_NEWROUTE" : "RTM_DELROUTE",
-              rtm->rtm_family == AF_INET ? "ipv4" : "ipv6",
-              rtm->rtm_type == RTN_UNICAST ? "unicast" : "multicast",
-              lookup (rtproto_str, rtm->rtm_protocol));
+    zlog_debug ("%s %s %s proto %s",
+               h->nlmsg_type ==
+               RTM_NEWROUTE ? "RTM_NEWROUTE" : "RTM_DELROUTE",
+               rtm->rtm_family == AF_INET ? "ipv4" : "ipv6",
+               rtm->rtm_type == RTN_UNICAST ? "unicast" : "multicast",
+               lookup (rtproto_str, rtm->rtm_protocol));
 
   if (rtm->rtm_type != RTN_UNICAST)
     {
@@ -700,12 +864,12 @@ netlink_route_change (struct sockaddr_nl *snl, struct nlmsghdr *h)
     }
 
   table = rtm->rtm_table;
-  if (table != RT_TABLE_MAIN && table != rtm_table_default)
+  if (table != RT_TABLE_MAIN && table != zebrad.rtm_table_default)
     {
       return 0;
     }
 
-  len = h->nlmsg_len - NLMSG_LENGTH(sizeof (struct rtmsg));
+  len = h->nlmsg_len - NLMSG_LENGTH (sizeof (struct rtmsg));
   if (len < 0)
     return -1;
 
@@ -727,7 +891,7 @@ netlink_route_change (struct sockaddr_nl *snl, struct nlmsghdr *h)
       zlog_warn ("netlink_route_change(): no src len");
       return 0;
     }
-  
+
   index = 0;
   dest = NULL;
   gate = NULL;
@@ -751,19 +915,19 @@ netlink_route_change (struct sockaddr_nl *snl, struct nlmsghdr *h)
       p.prefixlen = rtm->rtm_dst_len;
 
       if (IS_ZEBRA_DEBUG_KERNEL)
-       {
-         if (h->nlmsg_type == RTM_NEWROUTE)
-           zlog_info ("RTM_NEWROUTE %s/%d",
-                      inet_ntoa (p.prefix), p.prefixlen);
-         else
-           zlog_info ("RTM_DELROUTE %s/%d",
-                      inet_ntoa (p.prefix), p.prefixlen);
-       }
+        {
+          if (h->nlmsg_type == RTM_NEWROUTE)
+            zlog_debug ("RTM_NEWROUTE %s/%d",
+                       inet_ntoa (p.prefix), p.prefixlen);
+          else
+            zlog_debug ("RTM_DELROUTE %s/%d",
+                       inet_ntoa (p.prefix), p.prefixlen);
+        }
 
       if (h->nlmsg_type == RTM_NEWROUTE)
-       rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, index, table, 0, 0);
+        rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, index, table, 0, 0);
       else
-       rib_delete_ipv4 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, index, table);
+        rib_delete_ipv4 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, index, table);
     }
 
 #ifdef HAVE_IPV6
@@ -777,21 +941,21 @@ netlink_route_change (struct sockaddr_nl *snl, struct nlmsghdr *h)
       p.prefixlen = rtm->rtm_dst_len;
 
       if (IS_ZEBRA_DEBUG_KERNEL)
-       {
-         if (h->nlmsg_type == RTM_NEWROUTE)
-           zlog_info ("RTM_NEWROUTE %s/%d",
-                      inet_ntop (AF_INET6, &p.prefix, buf, BUFSIZ),
-                      p.prefixlen);
-         else
-           zlog_info ("RTM_DELROUTE %s/%d",
-                      inet_ntop (AF_INET6, &p.prefix, buf, BUFSIZ),
-                      p.prefixlen);
-       }
+        {
+          if (h->nlmsg_type == RTM_NEWROUTE)
+            zlog_debug ("RTM_NEWROUTE %s/%d",
+                       inet_ntop (AF_INET6, &p.prefix, buf, BUFSIZ),
+                       p.prefixlen);
+          else
+            zlog_debug ("RTM_DELROUTE %s/%d",
+                       inet_ntop (AF_INET6, &p.prefix, buf, BUFSIZ),
+                       p.prefixlen);
+        }
 
       if (h->nlmsg_type == RTM_NEWROUTE)
-       rib_add_ipv6 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, index, 0);
+        rib_add_ipv6 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, index, 0, 0, 0);
       else
-       rib_delete_ipv6 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, index, 0);
+        rib_delete_ipv6 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, index, 0);
     }
 #endif /* HAVE_IPV6 */
 
@@ -803,17 +967,17 @@ netlink_link_change (struct sockaddr_nl *snl, struct nlmsghdr *h)
 {
   int len;
   struct ifinfomsg *ifi;
-  struct rtattr *tb [IFLA_MAX + 1];
+  struct rtattr *tb[IFLA_MAX + 1];
   struct interface *ifp;
   char *name;
 
   ifi = NLMSG_DATA (h);
 
-  if (! (h->nlmsg_type == RTM_NEWLINK || h->nlmsg_type == RTM_DELLINK))
+  if (!(h->nlmsg_type == RTM_NEWLINK || h->nlmsg_type == RTM_DELLINK))
     {
       /* If this is not link add/delete message so print warning. */
       zlog_warn ("netlink_link_change: wrong kernel message %d\n",
-                h->nlmsg_type);
+                 h->nlmsg_type);
       return 0;
     }
 
@@ -824,48 +988,62 @@ netlink_link_change (struct sockaddr_nl *snl, struct nlmsghdr *h)
   /* Looking up interface name. */
   memset (tb, 0, sizeof tb);
   netlink_parse_rtattr (tb, IFLA_MAX, IFLA_RTA (ifi), len);
+
+#ifdef IFLA_WIRELESS
+  /* check for wireless messages to ignore */
+  if ((tb[IFLA_WIRELESS] != NULL) && (ifi->ifi_change == 0))
+    {
+      if (IS_ZEBRA_DEBUG_KERNEL)
+        zlog_debug ("%s: ignoring IFLA_WIRELESS message", __func__);
+      return 0;
+    }
+#endif /* IFLA_WIRELESS */
+  
   if (tb[IFLA_IFNAME] == NULL)
     return -1;
-  name = (char *)RTA_DATA(tb[IFLA_IFNAME]);
+  name = (char *) RTA_DATA (tb[IFLA_IFNAME]);
 
   /* Add interface. */
   if (h->nlmsg_type == RTM_NEWLINK)
     {
       ifp = if_lookup_by_name (name);
 
-      if (ifp == NULL || ! CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE))
-       {
-         if (ifp == NULL)
-           ifp = if_get_by_name (name);
+      if (ifp == NULL || !CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE))
+        {
+          if (ifp == NULL)
+            ifp = if_get_by_name (name);
 
-         ifp->ifindex = ifi->ifi_index;
-         ifp->flags = ifi->ifi_flags & 0x0000fffff;
-         ifp->mtu = *(int *)RTA_DATA (tb[IFLA_MTU]);
-         ifp->metric = 1;
+          set_ifindex(ifp, ifi->ifi_index);
+          ifp->flags = ifi->ifi_flags & 0x0000fffff;
+          ifp->mtu6 = ifp->mtu = *(int *) RTA_DATA (tb[IFLA_MTU]);
+          ifp->metric = 1;
 
-         /* If new link is added. */
-         if_add_update(ifp);
-       }      
+          /* If new link is added. */
+          if_add_update (ifp);
+        }
       else
-       {
-         /* Interface status change. */
-         ifp->ifindex = ifi->ifi_index;
-         ifp->mtu = *(int *)RTA_DATA (tb[IFLA_MTU]);
-         ifp->metric = 1;
-
-         if (if_is_operative (ifp))
-           {
-             ifp->flags = ifi->ifi_flags & 0x0000fffff;
-             if (! if_is_operative (ifp))
-               if_down (ifp);
-           }
-         else
-           {
-             ifp->flags = ifi->ifi_flags & 0x0000fffff;
-             if (if_is_operative (ifp))
-               if_up (ifp);
-           }
-       }
+        {
+          /* Interface status change. */
+          set_ifindex(ifp, ifi->ifi_index);
+          ifp->mtu6 = ifp->mtu = *(int *) RTA_DATA (tb[IFLA_MTU]);
+          ifp->metric = 1;
+
+          if (if_is_operative (ifp))
+            {
+              ifp->flags = ifi->ifi_flags & 0x0000fffff;
+              if (!if_is_operative (ifp))
+                if_down (ifp);
+             else
+               /* Must notify client daemons of new interface status. */
+               zebra_interface_up_update (ifp);
+            }
+          else
+            {
+              ifp->flags = ifi->ifi_flags & 0x0000fffff;
+              if (if_is_operative (ifp))
+                if_up (ifp);
+            }
+        }
     }
   else
     {
@@ -873,12 +1051,12 @@ netlink_link_change (struct sockaddr_nl *snl, struct nlmsghdr *h)
       ifp = if_lookup_by_name (name);
 
       if (ifp == NULL)
-       {
-         zlog (NULL, LOG_WARNING, "interface %s is deleted but can't find",
+        {
+          zlog (NULL, LOG_WARNING, "interface %s is deleted but can't find",
                 name);
-         return 0;
-       }
-      
+          return 0;
+        }
+
       if_delete_update (ifp);
     }
 
@@ -917,22 +1095,22 @@ netlink_information_fetch (struct sockaddr_nl *snl, struct nlmsghdr *h)
 
 /* Interface lookup by netlink socket. */
 int
-interface_lookup_netlink ()
+interface_lookup_netlink (void)
 {
   int ret;
   int flags;
   int snb_ret;
+
   /* 
    * Change netlink socket flags to blocking to ensure we get 
    * a reply via nelink_parse_info
-   */ 
-  snb_ret = set_netlink_blocking(&netlink_cmd, &flags);
-  if(snb_ret < 0) 
-     zlog (NULL, LOG_WARNING, 
-             "%s:%i Warning: Could not set netlink socket to blocking.", 
-             __FUNCTION__, __LINE__);
-  
+   */
+  snb_ret = set_netlink_blocking (&netlink_cmd, &flags);
+  if (snb_ret < 0)
+    zlog (NULL, LOG_WARNING,
+          "%s:%i Warning: Could not set netlink socket to blocking.",
+          __FUNCTION__, __LINE__);
+
   /* Get interface information. */
   ret = netlink_request (AF_PACKET, RTM_GETLINK, &netlink_cmd);
   if (ret < 0)
@@ -959,31 +1137,31 @@ interface_lookup_netlink ()
     return ret;
 #endif /* HAVE_IPV6 */
 
-  /* restore socket flags */   
-  if(snb_ret == 0)
-    set_netlink_nonblocking(&netlink_cmd, &flags);
+  /* restore socket flags */
+  if (snb_ret == 0)
+    set_netlink_nonblocking (&netlink_cmd, &flags);
   return 0;
 }
 
 /* Routing table read function using netlink interface.  Only called
    bootstrap time. */
 int
-netlink_route_read ()
+netlink_route_read (void)
 {
   int ret;
   int flags;
   int snb_ret;
-  
+
   /* 
    * Change netlink socket flags to blocking to ensure we get 
    * a reply via nelink_parse_info
-   */ 
-  snb_ret = set_netlink_blocking(&netlink_cmd, &flags);
-  if(snb_ret < 0) 
-     zlog (NULL, LOG_WARNING, 
-             "%s:%i Warning: Could not set netlink socket to blocking.", 
-             __FUNCTION__, __LINE__);
-  
+   */
+  snb_ret = set_netlink_blocking (&netlink_cmd, &flags);
+  if (snb_ret < 0)
+    zlog (NULL, LOG_WARNING,
+          "%s:%i Warning: Could not set netlink socket to blocking.",
+          __FUNCTION__, __LINE__);
+
   /* Get IPv4 routing table. */
   ret = netlink_request (AF_INET, RTM_GETROUTE, &netlink_cmd);
   if (ret < 0)
@@ -1003,8 +1181,8 @@ netlink_route_read ()
 #endif /* HAVE_IPV6 */
 
   /* restore flags */
-  if(snb_ret == 0)
-    set_netlink_nonblocking(&netlink_cmd, &flags);
+  if (snb_ret == 0)
+    set_netlink_nonblocking (&netlink_cmd, &flags);
   return 0;
 }
 
@@ -1016,15 +1194,15 @@ addattr_l (struct nlmsghdr *n, int maxlen, int type, void *data, int alen)
   int len;
   struct rtattr *rta;
 
-  len = RTA_LENGTH(alen);
+  len = RTA_LENGTH (alen);
 
-  if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen)
+  if (NLMSG_ALIGN (n->nlmsg_len) + len > maxlen)
     return -1;
 
-  rta = (struct rtattr*) (((char*)n) + NLMSG_ALIGN (n->nlmsg_len));
+  rta = (struct rtattr *) (((char *) n) + NLMSG_ALIGN (n->nlmsg_len));
   rta->rta_type = type;
   rta->rta_len = len;
-  memcpy (RTA_DATA(rta), data, alen);
+  memcpy (RTA_DATA (rta), data, alen);
   n->nlmsg_len = NLMSG_ALIGN (n->nlmsg_len) + len;
 
   return 0;
@@ -1036,15 +1214,15 @@ rta_addattr_l (struct rtattr *rta, int maxlen, int type, void *data, int alen)
   int len;
   struct rtattr *subrta;
 
-  len = RTA_LENGTH(alen);
+  len = RTA_LENGTH (alen);
 
-  if (RTA_ALIGN(rta->rta_len) + len > maxlen)
+  if (RTA_ALIGN (rta->rta_len) + len > maxlen)
     return -1;
 
-  subrta = (struct rtattr*) (((char*)rta) + RTA_ALIGN (rta->rta_len));
+  subrta = (struct rtattr *) (((char *) rta) + RTA_ALIGN (rta->rta_len));
   subrta->rta_type = type;
   subrta->rta_len = len;
-  memcpy (RTA_DATA(subrta), data, alen);
+  memcpy (RTA_DATA (subrta), data, alen);
   rta->rta_len = NLMSG_ALIGN (rta->rta_len) + len;
 
   return 0;
@@ -1057,16 +1235,16 @@ addattr32 (struct nlmsghdr *n, int maxlen, int type, int data)
 {
   int len;
   struct rtattr *rta;
-  
-  len = RTA_LENGTH(4);
-  
+
+  len = RTA_LENGTH (4);
+
   if (NLMSG_ALIGN (n->nlmsg_len) + len > maxlen)
     return -1;
 
-  rta = (struct rtattr*) (((char*)n) + NLMSG_ALIGN (n->nlmsg_len));
+  rta = (struct rtattr *) (((char *) n) + NLMSG_ALIGN (n->nlmsg_len));
   rta->rta_type = type;
   rta->rta_len = len;
-  memcpy (RTA_DATA(rta), &data, 4);
+  memcpy (RTA_DATA (rta), &data, 4);
   n->nlmsg_len = NLMSG_ALIGN (n->nlmsg_len) + len;
 
   return 0;
@@ -1085,67 +1263,74 @@ netlink_talk (struct nlmsghdr *n, struct nlsock *nl)
 {
   int status;
   struct sockaddr_nl snl;
-  struct iovec iov = { (void*) n, n->nlmsg_len };
-  struct msghdr msg = {(void*) &snl, sizeof snl, &iov, 1, NULL, 0, 0};
+  struct iovec iov = { (void *) n, n->nlmsg_len };
+  struct msghdr msg = { (void *) &snl, sizeof snl, &iov, 1, NULL, 0, 0 };
   int flags = 0;
   int snb_ret;
-  
+  int save_errno;
+
   memset (&snl, 0, sizeof snl);
   snl.nl_family = AF_NETLINK;
-  
-  n->nlmsg_seq = ++netlink_cmd.seq;
+
+  n->nlmsg_seq = ++nl->seq;
 
   /* Request an acknowledgement by setting NLM_F_ACK */
   n->nlmsg_flags |= NLM_F_ACK;
-  
-  if (IS_ZEBRA_DEBUG_KERNEL) 
-    zlog_info ("netlink_talk: %s type %s(%u), seq=%u", netlink_cmd.name,
-             lookup (nlmsg_str, n->nlmsg_type), n->nlmsg_type,
-             n->nlmsg_seq);
+
+  if (IS_ZEBRA_DEBUG_KERNEL)
+    zlog_debug ("netlink_talk: %s type %s(%u), seq=%u", nl->name,
+               lookup (nlmsg_str, n->nlmsg_type), n->nlmsg_type,
+               n->nlmsg_seq);
 
   /* Send message to netlink interface. */
+  if (zserv_privs.change (ZPRIVS_RAISE))
+    zlog (NULL, LOG_ERR, "Can't raise privileges");
   status = sendmsg (nl->sock, &msg, 0);
+  save_errno = errno;
+  if (zserv_privs.change (ZPRIVS_LOWER))
+    zlog (NULL, LOG_ERR, "Can't lower privileges");
+
   if (status < 0)
     {
       zlog (NULL, LOG_ERR, "netlink_talk sendmsg() error: %s",
-           strerror (errno));
+            safe_strerror (save_errno));
       return -1;
     }
-  
+
   /* 
    * Change socket flags for blocking I/O. 
    * This ensures we wait for a reply in netlink_parse_info().
    */
-  snb_ret = set_netlink_blocking(nl, &flags);
-  if(snb_ret < 0) 
-     zlog (NULL, LOG_WARNING, 
-             "%s:%i Warning: Could not set netlink socket to blocking.", 
-             __FUNCTION__, __LINE__);
+  snb_ret = set_netlink_blocking (nl, &flags);
+  if (snb_ret < 0)
+    zlog (NULL, LOG_WARNING,
+          "%s:%i Warning: Could not set netlink socket to blocking.",
+          __FUNCTION__, __LINE__);
 
   /* 
    * Get reply from netlink socket. 
    * The reply should either be an acknowlegement or an error.
    */
   status = netlink_parse_info (netlink_talk_filter, nl);
-  
+
   /* Restore socket flags for nonblocking I/O */
-  if(snb_ret == 0)
-     set_netlink_nonblocking(nl, &flags);
-  
+  if (snb_ret == 0)
+    set_netlink_nonblocking (nl, &flags);
+
   return status;
 }
 
 /* Routing table change via netlink interface. */
 int
 netlink_route (int cmd, int family, void *dest, int length, void *gate,
-              int index, int zebra_flags, int table)
+               int index, int zebra_flags, int table)
 {
   int ret;
   int bytelen;
   struct sockaddr_nl snl;
   int discard;
 
-  struct 
+  struct
   {
     struct nlmsghdr n;
     struct rtmsg r;
@@ -1169,30 +1354,33 @@ netlink_route (int cmd, int family, void *dest, int length, void *gate,
   else
     discard = 0;
 
-  if (cmd == RTM_NEWROUTE) 
+  if (cmd == RTM_NEWROUTE)
     {
       req.r.rtm_protocol = RTPROT_ZEBRA;
       req.r.rtm_scope = RT_SCOPE_UNIVERSE;
 
-      if (discard) {
-       if (zebra_flags & ZEBRA_FLAG_BLACKHOLE)
-         req.r.rtm_type = RTN_BLACKHOLE;
-       else if (zebra_flags & ZEBRA_FLAG_REJECT)
-         req.r.rtm_type = RTN_UNREACHABLE;
-       else assert(RTN_BLACKHOLE != RTN_UNREACHABLE); /* false */
-      } else
-       req.r.rtm_type = RTN_UNICAST;
+      if (discard)
+        {
+          if (zebra_flags & ZEBRA_FLAG_BLACKHOLE)
+            req.r.rtm_type = RTN_BLACKHOLE;
+          else if (zebra_flags & ZEBRA_FLAG_REJECT)
+            req.r.rtm_type = RTN_UNREACHABLE;
+          else
+            assert (RTN_BLACKHOLE != RTN_UNREACHABLE);  /* false */
+        }
+      else
+        req.r.rtm_type = RTN_UNICAST;
     }
 
   if (dest)
     addattr_l (&req.n, sizeof req, RTA_DST, dest, bytelen);
 
-  if (! discard)
+  if (!discard)
     {
       if (gate)
-       addattr_l (&req.n, sizeof req, RTA_GATEWAY, gate, bytelen);
+        addattr_l (&req.n, sizeof req, RTA_GATEWAY, gate, bytelen);
       if (index > 0)
-       addattr32 (&req.n, sizeof req, RTA_OIF, index);
+        addattr32 (&req.n, sizeof req, RTA_OIF, index);
     }
 
   /* Destination netlink address. */
@@ -1200,7 +1388,7 @@ netlink_route (int cmd, int family, void *dest, int length, void *gate,
   snl.nl_family = AF_NETLINK;
 
   /* Talk to netlink socket. */
-  ret = netlink_talk (&req.n, &netlink);
+  ret = netlink_talk (&req.n, &netlink_cmd);
   if (ret < 0)
     return -1;
 
@@ -1210,16 +1398,15 @@ netlink_route (int cmd, int family, void *dest, int length, void *gate,
 /* Routing table change via netlink interface. */
 int
 netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib,
-                        int family)
+                         int family)
 {
   int bytelen;
   struct sockaddr_nl snl;
   struct nexthop *nexthop = NULL;
   int nexthop_num = 0;
-  struct nlsock *nl;
   int discard;
 
-  struct 
+  struct
   {
     struct nlmsghdr n;
     struct rtmsg r;
@@ -1237,29 +1424,27 @@ netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib,
   req.r.rtm_table = rib->table;
   req.r.rtm_dst_len = p->prefixlen;
 
-#ifdef RTM_F_EQUALIZE
-  req.r.rtm_flags |= RTM_F_EQUALIZE;
-#endif /* RTM_F_EQUALIZE */
-
-  if ((rib->flags & ZEBRA_FLAG_BLACKHOLE)
-      || (rib->flags & ZEBRA_FLAG_REJECT))
+  if ((rib->flags & ZEBRA_FLAG_BLACKHOLE) || (rib->flags & ZEBRA_FLAG_REJECT))
     discard = 1;
   else
     discard = 0;
 
-  if (cmd == RTM_NEWROUTE) 
+  if (cmd == RTM_NEWROUTE)
     {
       req.r.rtm_protocol = RTPROT_ZEBRA;
       req.r.rtm_scope = RT_SCOPE_UNIVERSE;
 
-      if (discard) {
-       if (rib->flags & ZEBRA_FLAG_BLACKHOLE)
-         req.r.rtm_type = RTN_BLACKHOLE;
-       else if (rib->flags & ZEBRA_FLAG_REJECT)
-         req.r.rtm_type = RTN_UNREACHABLE;
-       else assert(RTN_BLACKHOLE != RTN_UNREACHABLE); /* false */
-      } else
-       req.r.rtm_type = RTN_UNICAST;
+      if (discard)
+        {
+          if (rib->flags & ZEBRA_FLAG_BLACKHOLE)
+            req.r.rtm_type = RTN_BLACKHOLE;
+          else if (rib->flags & ZEBRA_FLAG_REJECT)
+            req.r.rtm_type = RTN_UNREACHABLE;
+          else
+            assert (RTN_BLACKHOLE != RTN_UNREACHABLE);  /* false */
+        }
+      else
+        req.r.rtm_type = RTN_UNICAST;
     }
 
   addattr_l (&req.n, sizeof req, RTA_DST, &p->u.prefix, bytelen);
@@ -1270,8 +1455,8 @@ netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib,
   if (discard)
     {
       if (cmd == RTM_NEWROUTE)
-       for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
-         SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
+        for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
+          SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
       goto skip;
     }
 
@@ -1279,61 +1464,137 @@ netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib,
   if (rib->nexthop_active_num == 1 || MULTIPATH_NUM == 1)
     {
       for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
-       {
-         if ((cmd == RTM_NEWROUTE 
-              && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))
-             || (cmd == RTM_DELROUTE
-                 && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)))
-           {
-             if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
-               {
-                 if (nexthop->rtype == NEXTHOP_TYPE_IPV4 
-                     || nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX)
-                   addattr_l (&req.n, sizeof req, RTA_GATEWAY,
-                              &nexthop->rgate.ipv4, bytelen);
+        {
+
+          if ((cmd == RTM_NEWROUTE
+               && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))
+              || (cmd == RTM_DELROUTE
+                  && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)))
+            {
+
+              if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
+                {
+                  if (IS_ZEBRA_DEBUG_KERNEL)
+                    {
+                      zlog_debug
+                        ("netlink_route_multipath() (recursive, 1 hop): "
+                         "%s %s/%d, type %s", lookup (nlmsg_str, cmd),
 #ifdef HAVE_IPV6
-                 if (nexthop->rtype == NEXTHOP_TYPE_IPV6 
-                     || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX 
-                     || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME)
-                   addattr_l (&req.n, sizeof req, RTA_GATEWAY,
-                              &nexthop->rgate.ipv6, bytelen);
+                        (family == AF_INET) ? inet_ntoa (p->u.prefix4) :
+                        inet6_ntoa (p->u.prefix6),
+#else
+                        inet_ntoa (p->u.prefix4),
 #endif /* HAVE_IPV6 */
-                 if (nexthop->rtype == NEXTHOP_TYPE_IFINDEX
-                     || nexthop->rtype == NEXTHOP_TYPE_IFNAME
-                     || nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX
-                     || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX
-                     || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME)
-                   addattr32 (&req.n, sizeof req, RTA_OIF,
-                              nexthop->rifindex);
-               }
-             else
-               {
-                 if (nexthop->type == NEXTHOP_TYPE_IPV4 
-                     || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX)
-                   addattr_l (&req.n, sizeof req, RTA_GATEWAY,
-                              &nexthop->gate.ipv4, bytelen);
+                        
+                        p->prefixlen, nexthop_types_desc[nexthop->rtype]);
+                    }
+
+                  if (nexthop->rtype == NEXTHOP_TYPE_IPV4
+                      || nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX)
+                   {
+                     addattr_l (&req.n, sizeof req, RTA_GATEWAY,
+                                &nexthop->rgate.ipv4, bytelen);
+
+                     if (IS_ZEBRA_DEBUG_KERNEL)
+                       zlog_debug("netlink_route_multipath() (recursive, "
+                                  "1 hop): nexthop via %s if %u",
+                                  inet_ntoa (nexthop->rgate.ipv4),
+                                  nexthop->rifindex);
+                   }
 #ifdef HAVE_IPV6
-                 if (nexthop->type == NEXTHOP_TYPE_IPV6 
-                     || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME
-                     || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX)
-                   addattr_l (&req.n, sizeof req, RTA_GATEWAY,
-                              &nexthop->gate.ipv6, bytelen);
+                  if (nexthop->rtype == NEXTHOP_TYPE_IPV6
+                      || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX
+                      || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME)
+                   {
+                     addattr_l (&req.n, sizeof req, RTA_GATEWAY,
+                                &nexthop->rgate.ipv6, bytelen);
+
+                     if (IS_ZEBRA_DEBUG_KERNEL)
+                       zlog_debug("netlink_route_multipath() (recursive, "
+                                  "1 hop): nexthop via %s if %u",
+                                  inet6_ntoa (nexthop->rgate.ipv6),
+                                  nexthop->rifindex);
+                   }
 #endif /* HAVE_IPV6 */
-                 if (nexthop->type == NEXTHOP_TYPE_IFINDEX
-                     || nexthop->type == NEXTHOP_TYPE_IFNAME
-                     || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX
-                     || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX
-                     || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME)
-                   addattr32 (&req.n, sizeof req, RTA_OIF, nexthop->ifindex);
-               }
-
-             if (cmd == RTM_NEWROUTE)
-               SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
-
-             nexthop_num++;
-             break;
-           }
-       }
+                  if (nexthop->rtype == NEXTHOP_TYPE_IFINDEX
+                      || nexthop->rtype == NEXTHOP_TYPE_IFNAME
+                      || nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX
+                      || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX
+                      || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME)
+                   {
+                     addattr32 (&req.n, sizeof req, RTA_OIF,
+                                nexthop->rifindex);
+
+                     if (IS_ZEBRA_DEBUG_KERNEL)
+                       zlog_debug("netlink_route_multipath() (recursive, "
+                                  "1 hop): nexthop via if %u",
+                                  nexthop->rifindex);
+                   }
+                }
+              else
+                {
+                  if (IS_ZEBRA_DEBUG_KERNEL)
+                    {
+                      zlog_debug
+                        ("netlink_route_multipath() (single hop): "
+                         "%s %s/%d, type %s", lookup (nlmsg_str, cmd),
+#ifdef HAVE_IPV6
+                        (family == AF_INET) ? inet_ntoa (p->u.prefix4) :
+                        inet6_ntoa (p->u.prefix6),
+#else
+                        inet_ntoa (p->u.prefix4),
+#endif /* HAVE_IPV6 */
+                        p->prefixlen, nexthop_types_desc[nexthop->type]);
+                    }
+
+                  if (nexthop->type == NEXTHOP_TYPE_IPV4
+                      || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX)
+                   {
+                     addattr_l (&req.n, sizeof req, RTA_GATEWAY,
+                                &nexthop->gate.ipv4, bytelen);
+
+                     if (IS_ZEBRA_DEBUG_KERNEL)
+                       zlog_debug("netlink_route_multipath() (single hop): "
+                                  "nexthop via %s if %u",
+                                  inet_ntoa (nexthop->gate.ipv4),
+                                  nexthop->ifindex);
+                   }
+#ifdef HAVE_IPV6
+                  if (nexthop->type == NEXTHOP_TYPE_IPV6
+                      || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME
+                      || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX)
+                   {
+                     addattr_l (&req.n, sizeof req, RTA_GATEWAY,
+                                &nexthop->gate.ipv6, bytelen);
+
+                     if (IS_ZEBRA_DEBUG_KERNEL)
+                       zlog_debug("netlink_route_multipath() (single hop): "
+                                  "nexthop via %s if %u",
+                                  inet6_ntoa (nexthop->gate.ipv6),
+                                  nexthop->ifindex);
+                   }
+#endif /* HAVE_IPV6 */
+                  if (nexthop->type == NEXTHOP_TYPE_IFINDEX
+                      || nexthop->type == NEXTHOP_TYPE_IFNAME
+                      || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX
+                      || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX
+                      || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME)
+                   {
+                     addattr32 (&req.n, sizeof req, RTA_OIF, nexthop->ifindex);
+
+                     if (IS_ZEBRA_DEBUG_KERNEL)
+                       zlog_debug("netlink_route_multipath() (single hop): "
+                                  "nexthop via if %u", nexthop->ifindex);
+                   }
+                }
+
+              if (cmd == RTM_NEWROUTE)
+                SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
+
+              nexthop_num++;
+              break;
+            }
+        }
     }
   else
     {
@@ -1342,111 +1603,176 @@ netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib,
       struct rtnexthop *rtnh;
 
       rta->rta_type = RTA_MULTIPATH;
-      rta->rta_len = RTA_LENGTH(0);
-      rtnh = RTA_DATA(rta);
+      rta->rta_len = RTA_LENGTH (0);
+      rtnh = RTA_DATA (rta);
 
       nexthop_num = 0;
       for (nexthop = rib->nexthop;
-          nexthop && (MULTIPATH_NUM == 0 || nexthop_num < MULTIPATH_NUM);
-          nexthop = nexthop->next)
-       {
-         if ((cmd == RTM_NEWROUTE 
-              && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))
-             || (cmd == RTM_DELROUTE
-                 && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)))
-           {
-             nexthop_num++;
-
-             rtnh->rtnh_len = sizeof (*rtnh);
-             rtnh->rtnh_flags = 0;
-             rtnh->rtnh_hops = 0;
-             rta->rta_len += rtnh->rtnh_len;
-
-             if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
-               {
-                 if (nexthop->rtype == NEXTHOP_TYPE_IPV4
-                     || nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX)
+           nexthop && (MULTIPATH_NUM == 0 || nexthop_num < MULTIPATH_NUM);
+           nexthop = nexthop->next)
+        {
+          if ((cmd == RTM_NEWROUTE
+               && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))
+              || (cmd == RTM_DELROUTE
+                  && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)))
+            {
+              nexthop_num++;
+
+              rtnh->rtnh_len = sizeof (*rtnh);
+              rtnh->rtnh_flags = 0;
+              rtnh->rtnh_hops = 0;
+              rta->rta_len += rtnh->rtnh_len;
+
+              if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
+                {
+                  if (IS_ZEBRA_DEBUG_KERNEL)
+                    {
+                      zlog_debug ("netlink_route_multipath() "
+                         "(recursive, multihop): %s %s/%d type %s",
+                        lookup (nlmsg_str, cmd),
+#ifdef HAVE_IPV6
+                        (family == AF_INET) ? inet_ntoa (p->u.prefix4) :
+                        inet6_ntoa (p->u.prefix6),
+#else
+                        inet_ntoa (p->u.prefix4),
+#endif /* HAVE_IPV6 */
+                         p->prefixlen, nexthop_types_desc[nexthop->rtype]);
+                    }
+                  if (nexthop->rtype == NEXTHOP_TYPE_IPV4
+                      || nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX)
+                    {
+                      rta_addattr_l (rta, 4096, RTA_GATEWAY,
+                                     &nexthop->rgate.ipv4, bytelen);
+                      rtnh->rtnh_len += sizeof (struct rtattr) + 4;
+
+                     if (IS_ZEBRA_DEBUG_KERNEL)
+                       zlog_debug("netlink_route_multipath() (recursive, "
+                                  "multihop): nexthop via %s if %u",
+                                  inet_ntoa (nexthop->rgate.ipv4),
+                                  nexthop->rifindex);
+                    }
+#ifdef HAVE_IPV6
+                  if (nexthop->rtype == NEXTHOP_TYPE_IPV6
+                      || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME
+                      || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX)
                    {
                      rta_addattr_l (rta, 4096, RTA_GATEWAY,
-                                    &nexthop->rgate.ipv4, bytelen);
-                     rtnh->rtnh_len += sizeof (struct rtattr) + 4;
+                                    &nexthop->rgate.ipv6, bytelen);
+
+                     if (IS_ZEBRA_DEBUG_KERNEL)
+                       zlog_debug("netlink_route_multipath() (recursive, "
+                                  "multihop): nexthop via %s if %u",
+                                  inet6_ntoa (nexthop->rgate.ipv6),
+                                  nexthop->rifindex);
                    }
-#ifdef HAVE_IPV6
-                 if (nexthop->rtype == NEXTHOP_TYPE_IPV6
-                     || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME
-                     || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX)
-                   rta_addattr_l (rta, 4096, RTA_GATEWAY,
-                                  &nexthop->rgate.ipv6, bytelen);
 #endif /* HAVE_IPV6 */
-                 /* ifindex */
-                 if (nexthop->rtype == NEXTHOP_TYPE_IFINDEX
-                     || nexthop->rtype == NEXTHOP_TYPE_IFNAME
-                     || nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX
-                     || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX
-                     || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME)
-                   rtnh->rtnh_ifindex = nexthop->rifindex;
-                 else
-                   rtnh->rtnh_ifindex = 0;
-               }
-             else
-               {
-                 if (nexthop->type == NEXTHOP_TYPE_IPV4
-                     || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX)
+                  /* ifindex */
+                  if (nexthop->rtype == NEXTHOP_TYPE_IFINDEX
+                      || nexthop->rtype == NEXTHOP_TYPE_IFNAME
+                      || nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX
+                      || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX
+                      || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME)
                    {
+                     rtnh->rtnh_ifindex = nexthop->rifindex;
+
+                     if (IS_ZEBRA_DEBUG_KERNEL)
+                       zlog_debug("netlink_route_multipath() (recursive, "
+                                  "multihop): nexthop via if %u",
+                                  nexthop->rifindex);
+                   }
+                  else
+                   {
+                     rtnh->rtnh_ifindex = 0;
+                   }
+                }
+              else
+                {
+                  if (IS_ZEBRA_DEBUG_KERNEL)
+                    {
+                      zlog_debug ("netlink_route_multipath() (multihop): "
+                         "%s %s/%d, type %s", lookup (nlmsg_str, cmd),
+#ifdef HAVE_IPV6
+                        (family == AF_INET) ? inet_ntoa (p->u.prefix4) :
+                        inet6_ntoa (p->u.prefix6),
+#else
+                        inet_ntoa (p->u.prefix4),
+#endif /* HAVE_IPV6 */
+                        p->prefixlen, nexthop_types_desc[nexthop->type]);
+                    }
+                  if (nexthop->type == NEXTHOP_TYPE_IPV4
+                      || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX)
+                    {
                      rta_addattr_l (rta, 4096, RTA_GATEWAY,
                                     &nexthop->gate.ipv4, bytelen);
                      rtnh->rtnh_len += sizeof (struct rtattr) + 4;
-                   }
+
+                      if (IS_ZEBRA_DEBUG_KERNEL)
+                       zlog_debug("netlink_route_multipath() (multihop): "
+                                  "nexthop via %s if %u",
+                                  inet_ntoa (nexthop->gate.ipv4),
+                                  nexthop->ifindex);
+                    }
 #ifdef HAVE_IPV6
-                 if (nexthop->type == NEXTHOP_TYPE_IPV6
-                     || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME
-                     || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX)
-                   rta_addattr_l (rta, 4096, RTA_GATEWAY,
-                                  &nexthop->gate.ipv6, bytelen);
+                  if (nexthop->type == NEXTHOP_TYPE_IPV6
+                      || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME
+                      || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX)
+                   { 
+                     rta_addattr_l (rta, 4096, RTA_GATEWAY,
+                                    &nexthop->gate.ipv6, bytelen);
+
+                     if (IS_ZEBRA_DEBUG_KERNEL)
+                       zlog_debug("netlink_route_multipath() (multihop): "
+                                  "nexthop via %s if %u",
+                                  inet6_ntoa (nexthop->gate.ipv6),
+                                  nexthop->ifindex);
+                   }
 #endif /* HAVE_IPV6 */
-                 /* ifindex */
-                 if (nexthop->type == NEXTHOP_TYPE_IFINDEX
-                     || nexthop->type == NEXTHOP_TYPE_IFNAME
-                     || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX
-                     || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME
-                     || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX)
-                   rtnh->rtnh_ifindex = nexthop->ifindex;
-                 else
-                   rtnh->rtnh_ifindex = 0;
-               }
-             rtnh = RTNH_NEXT(rtnh);
-
-             if (cmd == RTM_NEWROUTE)
-               SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
-           }
-       }
+                  /* ifindex */
+                  if (nexthop->type == NEXTHOP_TYPE_IFINDEX
+                      || nexthop->type == NEXTHOP_TYPE_IFNAME
+                      || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX
+                      || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME
+                      || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX)
+                   {
+                     rtnh->rtnh_ifindex = nexthop->ifindex;
+
+                     if (IS_ZEBRA_DEBUG_KERNEL)
+                       zlog_debug("netlink_route_multipath() (multihop): "
+                                  "nexthop via if %u", nexthop->ifindex);
+                   }
+                  else
+                   {
+                     rtnh->rtnh_ifindex = 0;
+                   }
+                }
+              rtnh = RTNH_NEXT (rtnh);
+
+              if (cmd == RTM_NEWROUTE)
+                SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
+            }
+        }
 
       if (rta->rta_len > RTA_LENGTH (0))
-       addattr_l (&req.n, 1024, RTA_MULTIPATH, RTA_DATA(rta),
-                  RTA_PAYLOAD(rta));
+        addattr_l (&req.n, 1024, RTA_MULTIPATH, RTA_DATA (rta),
+                   RTA_PAYLOAD (rta));
     }
 
   /* If there is no useful nexthop then return. */
   if (nexthop_num == 0)
     {
       if (IS_ZEBRA_DEBUG_KERNEL)
-       zlog_info ("netlink_route_multipath(): No useful nexthop.");
+        zlog_debug ("netlink_route_multipath(): No useful nexthop.");
       return 0;
     }
 
- skip:
+skip:
 
   /* Destination netlink address. */
   memset (&snl, 0, sizeof snl);
   snl.nl_family = AF_NETLINK;
 
-  if (family == AF_INET)
-    nl = &netlink_cmd;
-  else
-    nl = &netlink;
-
   /* Talk to netlink socket. */
-  return netlink_talk (&req.n, nl);
+  return netlink_talk (&req.n, &netlink_cmd);
 }
 
 int
@@ -1477,22 +1803,22 @@ kernel_delete_ipv6 (struct prefix *p, struct rib *rib)
 /* Delete IPv6 route from the kernel. */
 int
 kernel_delete_ipv6_old (struct prefix_ipv6 *dest, struct in6_addr *gate,
-                       int index, int flags, int table)
+                        unsigned int index, int flags, int table)
 {
-  return netlink_route (RTM_DELROUTE, AF_INET6, &dest->prefix, dest->prefixlen,
-                       gate, index, flags, table);
+  return netlink_route (RTM_DELROUTE, AF_INET6, &dest->prefix,
+                        dest->prefixlen, gate, index, flags, table);
 }
 #endif /* HAVE_IPV6 */
 \f
 /* Interface address modification. */
 int
 netlink_address (int cmd, int family, struct interface *ifp,
-                struct connected *ifc)
+                 struct connected *ifc)
 {
   int bytelen;
   struct prefix *p;
 
-  struct 
+  struct
   {
     struct nlmsghdr n;
     struct ifaddrmsg ifa;
@@ -1504,7 +1830,7 @@ netlink_address (int cmd, int family, struct interface *ifp,
 
   bytelen = (family == AF_INET ? 4 : 16);
 
-  req.n.nlmsg_len = NLMSG_LENGTH (sizeof(struct ifaddrmsg));
+  req.n.nlmsg_len = NLMSG_LENGTH (sizeof (struct ifaddrmsg));
   req.n.nlmsg_flags = NLM_F_REQUEST;
   req.n.nlmsg_type = cmd;
   req.ifa.ifa_family = family;
@@ -1517,18 +1843,19 @@ netlink_address (int cmd, int family, struct interface *ifp,
   if (family == AF_INET && cmd == RTM_NEWADDR)
     {
       if (if_is_broadcast (ifp) && ifc->destination)
-       {
-         p = ifc->destination;
-         addattr_l(&req.n, sizeof req, IFA_BROADCAST, &p->u.prefix, bytelen);
-       }
+        {
+          p = ifc->destination;
+          addattr_l (&req.n, sizeof req, IFA_BROADCAST, &p->u.prefix,
+                     bytelen);
+        }
     }
 
   if (CHECK_FLAG (ifc->flags, ZEBRA_IFA_SECONDARY))
     SET_FLAG (req.ifa.ifa_flags, IFA_F_SECONDARY);
-    
+
   if (ifc->label)
     addattr_l (&req.n, sizeof req, IFA_LABEL, ifc->label,
-              strlen (ifc->label) + 1);
+               strlen (ifc->label) + 1);
 
   return netlink_talk (&req.n, &netlink_cmd);
 }
@@ -1557,7 +1884,7 @@ kernel_read (struct thread *thread)
 
   sock = THREAD_FD (thread);
   ret = netlink_parse_info (netlink_information_fetch, &netlink);
-  thread_add_read (master, kernel_read, NULL, netlink.sock);
+  thread_add_read (zebrad.master, kernel_read, NULL, netlink.sock);
 
   return 0;
 }
@@ -1565,18 +1892,18 @@ kernel_read (struct thread *thread)
 /* Exported interface function.  This function simply calls
    netlink_socket (). */
 void
-kernel_init ()
+kernel_init (void)
 {
   unsigned long groups;
 
-  groups = RTMGRP_LINK|RTMGRP_IPV4_ROUTE|RTMGRP_IPV4_IFADDR;
+  groups = RTMGRP_LINK | RTMGRP_IPV4_ROUTE | RTMGRP_IPV4_IFADDR;
 #ifdef HAVE_IPV6
-  groups |= RTMGRP_IPV6_ROUTE|RTMGRP_IPV6_IFADDR;
+  groups |= RTMGRP_IPV6_ROUTE | RTMGRP_IPV6_IFADDR;
 #endif /* HAVE_IPV6 */
   netlink_socket (&netlink, groups);
   netlink_socket (&netlink_cmd, 0);
 
   /* Register kernel socket. */
   if (netlink.sock > 0)
-    thread_add_read (master, kernel_read, NULL, netlink.sock);
+    thread_add_read (zebrad.master, kernel_read, NULL, netlink.sock);
 }