netdev-dpdk: fix mbuf leaks
[cascardo/ovs.git] / lib / route-table-bsd.c
index 6d5fe5c..b5ff3cc 100644 (file)
 #include <net/if_dl.h>
 #include <netinet/in.h>
 
+#include <errno.h>
+#include <poll.h>
 #include <string.h>
 #include <unistd.h>
 
 #include "ovs-router.h"
+#include "packets.h"
+#include "openvswitch/vlog.h"
 #include "util.h"
 
+VLOG_DEFINE_THIS_MODULE(route_table_bsd);
+
 bool
-ovs_router_lookup(ovs_be32 ip, char name[], ovs_be32 *gw)
+route_table_fallback_lookup(ovs_be32 ip, char name[], ovs_be32 *gw)
 {
     struct {
         struct rt_msghdr rtm;
@@ -45,15 +51,21 @@ ovs_router_lookup(ovs_be32 ip, char name[], ovs_be32 *gw)
     struct sockaddr_in *sin;
     struct sockaddr *sa;
     static int seq;
-    int i, len, namelen, rtsock;
+    int i, namelen, rtsock;
+    ssize_t len;
     const pid_t pid = getpid();
+    bool got_ifp = false;
+    unsigned int retry_count = 5;  /* arbitrary */
+
+    VLOG_DBG("looking route up for " IP_FMT " pid %" PRIuMAX,
+        IP_ARGS(ip), (uintmax_t)pid);
 
     rtsock = socket(PF_ROUTE, SOCK_RAW, 0);
     if (rtsock < 0)
         return false;
 
+retry:
     memset(&rtmsg, 0, sizeof(rtmsg));
-
     rtm->rtm_msglen = sizeof(struct rt_msghdr) + sizeof(struct sockaddr_in);
     rtm->rtm_version = RTM_VERSION;
     rtm->rtm_type = RTM_GET;
@@ -65,22 +77,67 @@ ovs_router_lookup(ovs_be32 ip, char name[], ovs_be32 *gw)
     sin->sin_family = AF_INET;
     sin->sin_addr.s_addr = ip;
 
-    if ((write(rtsock, (char *)&rtmsg, rtm->rtm_msglen)) < 0) {
+    len = write(rtsock, (char *)&rtmsg, rtm->rtm_msglen);
+    if (len == -1) {
+        if (errno == ENOBUFS && retry_count-- > 0) {
+            VLOG_INFO("Recoverable error writing to routing socket: %s",
+                      ovs_strerror(errno));
+            usleep(500 * 1000);  /* arbitrary */
+            goto retry;
+        }
+        VLOG_ERR("Error writing to routing socket: %s", ovs_strerror(errno));
+        close(rtsock);
+        return false;
+    }
+    if (len != rtm->rtm_msglen) {
+        VLOG_ERR("Short write to routing socket");
         close(rtsock);
         return false;
     }
 
     do {
+        struct pollfd pfd;
+        int ret;
+
+        memset(&pfd, 0, sizeof(pfd));
+        pfd.fd = rtsock;
+        pfd.events = POLLIN;
+        /*
+         * The timeout value below is somehow arbitrary.
+         * It's to detect the lost of routing messages due to
+         * buffer exhaustion etc.  The routing socket is not
+         * reliable.
+         */
+        ret = poll(&pfd, 1, 500);
+        if (ret == -1) {
+            VLOG_ERR("Error polling on routing socket: %s",
+                     ovs_strerror(errno));
+            close(rtsock);
+            return false;
+        }
+        if (ret == 0) {
+            if (retry_count-- > 0) {
+                VLOG_INFO("Timeout; resending routing message");
+                goto retry;
+            }
+            close(rtsock);
+            return false;
+        }
         len = read(rtsock, (char *)&rtmsg, sizeof(rtmsg));
+        if (len > 0) {
+            VLOG_DBG("got rtmsg pid %" PRIuMAX " seq %d",
+                (uintmax_t)rtmsg.rtm.rtm_pid,
+                rtmsg.rtm.rtm_seq);
+        }
     } while (len > 0 && (rtmsg.rtm.rtm_seq != seq ||
         rtmsg.rtm.rtm_pid != pid));
-
     close(rtsock);
-
-    if (len < 0) {
+    if (len == -1) {
+        VLOG_ERR("Error reading from routing socket: %s", ovs_strerror(errno));
         return false;
     }
 
+    *gw = 0;
     sa = (struct sockaddr *)(rtm + 1);
     for (i = 1; i; i <<= 1) {
         if (rtm->rtm_addrs & i) {
@@ -92,8 +149,14 @@ ovs_router_lookup(ovs_be32 ip, char name[], ovs_be32 *gw)
                     namelen = IFNAMSIZ - 1;
                 memcpy(name, ifp->sdl_data, namelen);
                 name[namelen] = '\0';
-                *gw = 0;
-                return true;
+                VLOG_DBG("got ifp %s", name);
+                got_ifp = true;
+            } else if (i == RTA_GATEWAY && sa->sa_family == AF_INET) {
+                const struct sockaddr_in *sin_dst =
+                    ALIGNED_CAST(struct sockaddr_in *, sa);
+
+                *gw = sin_dst->sin_addr.s_addr;
+                VLOG_DBG("got gateway " IP_FMT, IP_ARGS(*gw));
             }
 #if defined(__FreeBSD__)
             sa = (struct sockaddr *)((char *)sa + SA_SIZE(sa));
@@ -104,7 +167,7 @@ ovs_router_lookup(ovs_be32 ip, char name[], ovs_be32 *gw)
 #endif
         }
     }
-    return false;
+    return got_ifp;
 }
 
 uint64_t
@@ -116,6 +179,7 @@ route_table_get_change_seq(void)
 void
 route_table_init(void)
 {
+    ovs_router_init();
 }
 
 void
@@ -127,8 +191,3 @@ void
 route_table_wait(void)
 {
 }
-
-void
-ovs_router_init(void)
-{
-}