]> git.proxmox.com Git - mirror_frr.git/blobdiff - zebra/rt_netlink.c
zebra: fix IPv6 RA wrt interface removal (BZ#480)
[mirror_frr.git] / zebra / rt_netlink.c
index 592dfe2e1f263b8d661a0d5021fcc339f778f3b5..eb9eae3aab0ac5b1a70cf8ae62b4855f9693b87b 100644 (file)
@@ -112,73 +112,61 @@ set_ifindex(struct interface *ifp, unsigned int ifi_index)
   ifp->ifindex = ifi_index;
 }
 
-/* Make socket for Linux netlink interface. */
 static int
-netlink_socket (struct nlsock *nl, unsigned long groups)
+netlink_recvbuf (struct nlsock *nl, uint32_t newsize)
 {
+  u_int32_t oldsize;
+  socklen_t newlen = sizeof(newsize);
+  socklen_t oldlen = sizeof(oldsize);
   int ret;
-  struct sockaddr_nl snl;
-  int sock;
-  int namelen;
-  int save_errno;
 
-  sock = socket (AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
-  if (sock < 0)
+  ret = getsockopt(nl->sock, SOL_SOCKET, SO_RCVBUF, &oldsize, &oldlen);
+  if (ret < 0)
     {
-      zlog (NULL, LOG_ERR, "Can't open %s socket: %s", nl->name,
-            safe_strerror (errno));
+      zlog (NULL, LOG_ERR, "Can't get %s receive buffer size: %s", nl->name,
+           safe_strerror (errno));
       return -1;
     }
 
-  ret = fcntl (sock, F_SETFL, O_NONBLOCK);
+  ret = setsockopt(nl->sock, SOL_SOCKET, SO_RCVBUF, &nl_rcvbufsize,
+                  sizeof(nl_rcvbufsize));
   if (ret < 0)
     {
-      zlog (NULL, LOG_ERR, "Can't set %s socket flags: %s", nl->name,
-            safe_strerror (errno));
-      close (sock);
+      zlog (NULL, LOG_ERR, "Can't set %s receive buffer size: %s", nl->name,
+           safe_strerror (errno));
       return -1;
     }
 
-  /* Set receive buffer size if it's set from command line */
-  if (nl_rcvbufsize)
+  ret = getsockopt(nl->sock, SOL_SOCKET, SO_RCVBUF, &newsize, &newlen);
+  if (ret < 0)
     {
-      u_int32_t oldsize, oldlen;
-      u_int32_t newsize, newlen;
+      zlog (NULL, LOG_ERR, "Can't get %s receive buffer size: %s", nl->name,
+           safe_strerror (errno));
+      return -1;
+    }
 
-      oldlen = sizeof(oldsize);
-      newlen = sizeof(newsize);
+  zlog (NULL, LOG_INFO,
+       "Setting netlink socket receive buffer size: %u -> %u",
+       oldsize, newsize);
+  return 0;
+}
 
-      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;
-       }
+/* Make socket for Linux netlink interface. */
+static int
+netlink_socket (struct nlsock *nl, unsigned long groups)
+{
+  int ret;
+  struct sockaddr_nl snl;
+  int sock;
+  int namelen;
+  int save_errno;
 
-      zlog (NULL, LOG_INFO,
-           "Setting netlink socket receive buffer size: %u -> %u",
-           oldsize, newsize);
+  sock = socket (AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+  if (sock < 0)
+    {
+      zlog (NULL, LOG_ERR, "Can't open %s socket: %s", nl->name,
+            safe_strerror (errno));
+      return -1;
     }
 
   memset (&snl, 0, sizeof snl);
@@ -221,41 +209,6 @@ netlink_socket (struct nlsock *nl, unsigned long groups)
   return ret;
 }
 
-static 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)
-    {
-      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)
-    {
-      zlog (NULL, LOG_ERR, "%s:%i F_SETFL error: %s",
-            __FUNCTION__, __LINE__, safe_strerror (errno));
-      return -1;
-    }
-  return 0;
-}
-
-static 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)
-    {
-      zlog (NULL, LOG_ERR, "%s:%i F_SETFL error: %s",
-            __FUNCTION__, __LINE__, safe_strerror (errno));
-      return -1;
-    }
-  return 0;
-}
-
 /* Get type specified information from netlink. */
 static int
 netlink_request (int family, int type, struct nlsock *nl)
@@ -332,25 +285,16 @@ netlink_parse_info (int (*filter) (struct sockaddr_nl *, struct nlmsghdr *),
       struct sockaddr_nl snl;
       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 (save_errno == EINTR)
+          if (errno == EINTR)
             continue;
-          if (save_errno == EWOULDBLOCK || save_errno == EAGAIN)
+          if (errno == EWOULDBLOCK || errno == EAGAIN)
             break;
           zlog (NULL, LOG_ERR, "%s recvmsg overrun: %s",
-               nl->name, safe_strerror(save_errno));
+               nl->name, safe_strerror(errno));
           continue;
         }
 
@@ -378,6 +322,8 @@ netlink_parse_info (int (*filter) (struct sockaddr_nl *, struct nlmsghdr *),
           if (h->nlmsg_type == NLMSG_ERROR)
             {
               struct nlmsgerr *err = (struct nlmsgerr *) NLMSG_DATA (h);
+             int errnum = err->error;
+             int msg_type = err->msg.nlmsg_type;
 
               /* If the error field is zero, then this is an ACK */
               if (err->error == 0)
@@ -406,27 +352,24 @@ netlink_parse_info (int (*filter) (struct sockaddr_nl *, struct nlmsghdr *),
                   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;
-               */
+              /* Deal with errors that occur because of races in link handling */
+             if (nl == &netlink_cmd
+                 && ((msg_type == RTM_DELROUTE &&
+                      (-errnum == ENODEV || -errnum == ESRCH))
+                     || (msg_type == RTM_NEWROUTE && -errnum == EEXIST)))
+               {
+                 if (IS_ZEBRA_DEBUG_KERNEL)
+                   zlog_debug ("%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);
+                 return 0;
+               }
+
+             zlog_err ("%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);
               return -1;
             }
 
@@ -526,7 +469,7 @@ netlink_interface (struct sockaddr_nl *snl, struct nlmsghdr *h)
   ifp = if_get_by_name (name);
   set_ifindex(ifp, ifi->ifi_index);
   ifp->flags = ifi->ifi_flags & 0x0000fffff;
-  ifp->mtu6 = ifp->mtu = *(int *) RTA_DATA (tb[IFLA_MTU]);
+  ifp->mtu6 = ifp->mtu = *(uint32_t *) RTA_DATA (tb[IFLA_MTU]);
   ifp->metric = 1;
 
   /* Hardware type and address. */
@@ -825,6 +768,8 @@ netlink_route_change (struct sockaddr_nl *snl, struct nlmsghdr *h)
 
   int index;
   int table;
+  int metric;
+
   void *dest;
   void *gate;
   void *src;
@@ -882,6 +827,7 @@ netlink_route_change (struct sockaddr_nl *snl, struct nlmsghdr *h)
     }
 
   index = 0;
+  metric = 0;
   dest = NULL;
   gate = NULL;
   src = NULL;
@@ -900,6 +846,9 @@ netlink_route_change (struct sockaddr_nl *snl, struct nlmsghdr *h)
   if (tb[RTA_PREFSRC])
     src = RTA_DATA (tb[RTA_PREFSRC]);
 
+  if (h->nlmsg_type == RTM_NEWROUTE && tb[RTA_PRIORITY])
+    metric = *(int *) RTA_DATA(tb[RTA_PRIORITY]);
+
   if (rtm->rtm_family == AF_INET)
     {
       struct prefix_ipv4 p;
@@ -918,7 +867,7 @@ netlink_route_change (struct sockaddr_nl *snl, struct nlmsghdr *h)
         }
 
       if (h->nlmsg_type == RTM_NEWROUTE)
-        rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, src, index, table, 0, 0);
+        rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, src, index, table, metric, 0);
       else
         rib_delete_ipv4 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, index, table);
     }
@@ -946,9 +895,9 @@ netlink_route_change (struct sockaddr_nl *snl, struct nlmsghdr *h)
         }
 
       if (h->nlmsg_type == RTM_NEWROUTE)
-        rib_add_ipv6 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, index, 0, 0, 0);
+        rib_add_ipv6 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, index, table, metric, 0);
       else
-        rib_delete_ipv6 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, index, 0);
+        rib_delete_ipv6 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, index, table);
     }
 #endif /* HAVE_IPV6 */
 
@@ -1098,18 +1047,6 @@ int
 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__);
 
   /* Get interface information. */
   ret = netlink_request (AF_PACKET, RTM_GETLINK, &netlink_cmd);
@@ -1137,9 +1074,6 @@ interface_lookup_netlink (void)
     return ret;
 #endif /* HAVE_IPV6 */
 
-  /* restore socket flags */
-  if (snb_ret == 0)
-    set_netlink_nonblocking (&netlink_cmd, &flags);
   return 0;
 }
 
@@ -1149,18 +1083,6 @@ int
 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__);
 
   /* Get IPv4 routing table. */
   ret = netlink_request (AF_INET, RTM_GETROUTE, &netlink_cmd);
@@ -1180,9 +1102,6 @@ netlink_route_read (void)
     return ret;
 #endif /* HAVE_IPV6 */
 
-  /* restore flags */
-  if (snb_ret == 0)
-    set_netlink_nonblocking (&netlink_cmd, &flags);
   return 0;
 }
 
@@ -1265,8 +1184,6 @@ netlink_talk (struct nlmsghdr *n, struct nlsock *nl)
   struct sockaddr_nl snl;
   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);
@@ -1297,27 +1214,12 @@ netlink_talk (struct nlmsghdr *n, struct nlsock *nl)
       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__);
 
   /* 
    * 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);
-
-  return status;
+  return netlink_parse_info (netlink_talk_filter, nl);
 }
 
 /* Routing table change via netlink interface. */
@@ -1983,6 +1885,15 @@ kernel_init (void)
   /* Register kernel socket. */
   if (netlink.sock > 0)
     {
+      /* Only want non-blocking on the netlink event socket */
+      if (fcntl (netlink.sock, F_SETFL, O_NONBLOCK) < 0)
+       zlog (NULL, LOG_ERR, "Can't set %s socket flags: %s", netlink.name,
+               safe_strerror (errno));
+
+      /* Set receive buffer size if it's set from command line */
+      if (nl_rcvbufsize)
+       netlink_recvbuf (&netlink, nl_rcvbufsize);
+
       netlink_install_filter (netlink.sock, netlink_cmd.snl.nl_pid);
       thread_add_read (zebrad.master, kernel_read, NULL, netlink.sock);
     }