netdev-linux: Open AF_PACKET socket only when it is needed.
authorBen Pfaff <blp@nicira.com>
Mon, 2 May 2011 16:53:31 +0000 (09:53 -0700)
committerBen Pfaff <blp@nicira.com>
Fri, 13 May 2011 21:45:53 +0000 (14:45 -0700)
Only a privileged process can open a raw AF_PACKET socket, so netdev-linux
will fail to initialize if run as non-root and you get a cascade of error
messages, like this:

netdev_linux|ERR|failed to create packet socket: Operation not permitted
netdev|ERR|failed to initialize system network device class: Operation not permitted
netdev|ERR|failed to initialize internal network device class: Operation not permitted
netdev|ERR|failed to initialize tap network device class: Operation not permitted

But in fact the AF_PACKET socket is not needed for most operations (only
for sending packets) and it is never needed for testing with the "dummy"
datapath and network device, so we can avoid logging all of these errors
by opening the packet socket only on demand, as this commit does.

Reviewed-by: Simon Horman <horms@verge.net.au>
lib/netdev-linux.c

index 1c5ddc53bf311baa321c03f08d05e659b9568e41..f6f2fc819f3df1257baf784e94cf721d5e3a2712 100644 (file)
@@ -373,7 +373,6 @@ struct netdev_linux {
 
 /* Sockets used for ioctl operations. */
 static int af_inet_sock = -1;   /* AF_INET, SOCK_DGRAM. */
-static int af_packet_sock = -1; /* AF_PACKET, SOCK_RAW. */
 
 /* A Netlink routing socket that is not subscribed to any multicast groups. */
 static struct nl_sock *rtnl_sock;
@@ -411,6 +410,7 @@ static int set_etheraddr(const char *netdev_name, int hwaddr_family,
                          const uint8_t[ETH_ADDR_LEN]);
 static int get_stats_via_netlink(int ifindex, struct netdev_stats *stats);
 static int get_stats_via_proc(const char *netdev_name, struct netdev_stats *stats);
+static int af_packet_sock(void);
 
 static bool
 is_netdev_linux_class(const struct netdev_class *netdev_class)
@@ -447,16 +447,6 @@ netdev_linux_init(void)
         status = af_inet_sock >= 0 ? 0 : errno;
         if (status) {
             VLOG_ERR("failed to create inet socket: %s", strerror(status));
-        } else {
-            /* Create AF_PACKET socket. */
-            af_packet_sock = socket(AF_PACKET, SOCK_RAW, 0);
-            status = af_packet_sock >= 0 ? 0 : errno;
-            if (!status) {
-                set_nonblocking(af_packet_sock);
-            } else {
-                VLOG_ERR("failed to create packet socket: %s",
-                         strerror(status));
-            }
         }
 
         /* Create rtnetlink socket. */
@@ -844,6 +834,12 @@ netdev_linux_send(struct netdev *netdev_, const void *data, size_t size)
             struct iovec iov;
             int ifindex;
             int error;
+            int sock;
+
+            sock = af_packet_sock();
+            if (sock < 0) {
+                return sock;
+            }
 
             error = get_ifindex(netdev_, &ifindex);
             if (error) {
@@ -867,7 +863,7 @@ netdev_linux_send(struct netdev *netdev_, const void *data, size_t size)
             msg.msg_controllen = 0;
             msg.msg_flags = 0;
 
-            retval = sendmsg(af_packet_sock, &msg, 0);
+            retval = sendmsg(sock, &msg, 0);
         } else {
             /* Use the netdev's own fd to send to this device.  This is
              * essential for tap devices, because packets sent to a tap device
@@ -4239,3 +4235,22 @@ netdev_linux_get_ipv4(const struct netdev *netdev, struct in_addr *ip,
     }
     return error;
 }
+
+/* Returns an AF_PACKET raw socket or a negative errno value. */
+static int
+af_packet_sock(void)
+{
+    static int sock = INT_MIN;
+
+    if (sock == INT_MIN) {
+        sock = socket(AF_PACKET, SOCK_RAW, 0);
+        if (sock >= 0) {
+            set_nonblocking(sock);
+        } else {
+            sock = -errno;
+            VLOG_ERR("failed to create packet socket: %s", strerror(errno));
+        }
+    }
+
+    return sock;
+}