netdev: Allow recv, recv_wait, drain, send, send_wait to be null.
[sliver-openvswitch.git] / lib / netdev-linux.c
index f2a6a5d..736b588 100644 (file)
@@ -75,7 +75,7 @@
 #endif
 
 static struct rtnetlink_notifier netdev_linux_cache_notifier;
-static struct shash cache_map = SHASH_INITIALIZER(&cache_map);
+static int cache_notifier_refcount;
 
 enum {
     VALID_IFINDEX = 1 << 0,
@@ -123,10 +123,12 @@ struct gre_config {
     uint32_t remote_ip;
     uint32_t in_key;
     uint32_t out_key;
+    uint8_t tos;
     bool have_in_key;
     bool have_out_key;
     bool in_csum;
     bool out_csum;
+    bool pmtud;
 };
 
 static struct {
@@ -216,28 +218,34 @@ netdev_linux_wait(void)
 
 static void
 netdev_linux_cache_cb(const struct rtnetlink_change *change,
-                      void *aux UNUSED)
+                      void *aux OVS_UNUSED)
 {
     struct netdev_dev_linux *dev;
     if (change) {
-        dev = shash_find_data(&cache_map, change->ifname);
-        if (dev) {
+        struct netdev_dev *base_dev = netdev_dev_from_name(change->ifname);
+        if (base_dev) {
+            dev = netdev_dev_linux_cast(base_dev);
             dev->cache_valid = 0;
         }
     } else {
+        struct shash device_shash;
         struct shash_node *node;
-        SHASH_FOR_EACH (node, &cache_map) {
+
+        shash_init(&device_shash);
+        netdev_dev_get_devices(&netdev_linux_class, &device_shash);
+        SHASH_FOR_EACH (node, &device_shash) {
             dev = node->data;
             dev->cache_valid = 0;
         }
+        shash_destroy(&device_shash);
     }
 }
 
 /* The arguments are marked as unused to prevent warnings on platforms where
  * the Netlink interface isn't supported. */
 static int
-setup_gre_netlink(const char *name UNUSED, struct gre_config *config UNUSED,
-                  bool create UNUSED)
+setup_gre_netlink(const char *name OVS_UNUSED,
+                  struct gre_config *config OVS_UNUSED, bool create OVS_UNUSED)
 {
 #ifdef GRE_IOCTL_ONLY
     return EOPNOTSUPP;
@@ -250,7 +258,6 @@ setup_gre_netlink(const char *name UNUSED, struct gre_config *config UNUSED,
     struct nlattr *info_data_hdr;
     uint16_t iflags = 0;
     uint16_t oflags = 0;
-    uint8_t pmtudisc = 0;
 
     VLOG_DBG("%s: attempting to create gre device using netlink", name);
 
@@ -309,9 +316,9 @@ setup_gre_netlink(const char *name UNUSED, struct gre_config *config UNUSED,
     nl_msg_put_u16(&request, IFLA_GRE_OFLAGS, oflags);
     nl_msg_put_u32(&request, IFLA_GRE_LOCAL, config->local_ip);
     nl_msg_put_u32(&request, IFLA_GRE_REMOTE, config->remote_ip);
-    nl_msg_put_u8(&request, IFLA_GRE_PMTUDISC, pmtudisc);
-    nl_msg_put_u8(&request, IFLA_GRE_TTL, 0);
-    nl_msg_put_u8(&request, IFLA_GRE_TOS, 0);
+    nl_msg_put_u8(&request, IFLA_GRE_PMTUDISC, config->pmtud);
+    nl_msg_put_u8(&request, IFLA_GRE_TTL, IPDEFTTL);
+    nl_msg_put_u8(&request, IFLA_GRE_TOS, config->tos);
 
     info_data_hdr->nla_len = (char *)ofpbuf_tail(&request)
                                 - (char *)info_data_hdr;
@@ -350,6 +357,8 @@ setup_gre_ioctl(const char *name, struct gre_config *config, bool create)
     p.iph.protocol = IPPROTO_GRE;
     p.iph.saddr = config->local_ip;
     p.iph.daddr = config->remote_ip;
+    p.iph.ttl = IPDEFTTL;
+    p.iph.tos = config->tos;
 
     if (config->have_in_key) {
         p.i_flags |= GRE_KEY;
@@ -367,6 +376,10 @@ setup_gre_ioctl(const char *name, struct gre_config *config, bool create)
         p.o_flags |= GRE_CSUM;
     }
 
+    if (config->pmtud) {
+        p.iph.frag_off = htons(IP_DONT_FRAGMENT);
+    }
+
     strncpy(ifr.ifr_name, create ? GRE_IOCTL_DEVICE : name, IFNAMSIZ);
     ifr.ifr_ifru.ifru_data = (void *)&p;
 
@@ -391,7 +404,7 @@ setup_gre_ioctl(const char *name, struct gre_config *config, bool create)
 /* The arguments are marked as unused to prevent warnings on platforms where
  * the Netlink interface isn't supported. */
 static bool
-check_gre_device_netlink(const char *name UNUSED)
+check_gre_device_netlink(const char *name OVS_UNUSED)
 {
 #ifdef GRE_IOCTL_ONLY
     return false;
@@ -480,6 +493,7 @@ setup_gre(const char *name, const struct shash *args, bool create)
     memset(&config, 0, sizeof config);
     config.in_csum = true;
     config.out_csum = true;
+    config.pmtud = true;
 
     SHASH_FOR_EACH (node, args) {
         if (!strcmp(node->name, "remote_ip")) {
@@ -505,11 +519,17 @@ setup_gre(const char *name, const struct shash *args, bool create)
         } else if (!strcmp(node->name, "out_key")) {
             config.have_out_key = true;
             config.out_key = htonl(atoi(node->data));
+        } else if (!strcmp(node->name, "tos")) {
+            config.tos = atoi(node->data);
         } else if (!strcmp(node->name, "csum")) {
             if (!strcmp(node->data, "false")) {
                 config.in_csum = false;
                 config.out_csum = false;
             }
+        } else if (!strcmp(node->name, "pmtud")) {
+            if (!strcmp(node->data, "false")) {
+                config.pmtud = false;
+            }
         } else {
             VLOG_WARN("unknown gre argument '%s'", node->name);
         }
@@ -563,7 +583,7 @@ error:
 
 /* Creates the netdev device of 'type' with 'name'. */
 static int
-netdev_linux_create_system(const char *name, const char *type UNUSED,
+netdev_linux_create_system(const char *name, const char *type OVS_UNUSED,
                     const struct shash *args, struct netdev_dev **netdev_devp)
 {
     struct netdev_dev_linux *netdev_dev;
@@ -573,18 +593,18 @@ netdev_linux_create_system(const char *name, const char *type UNUSED,
         VLOG_WARN("%s: arguments for system devices should be empty", name);
     }
 
-    if (shash_is_empty(&cache_map)) {
+    if (!cache_notifier_refcount) {
         error = rtnetlink_notifier_register(&netdev_linux_cache_notifier,
                                             netdev_linux_cache_cb, NULL);
         if (error) {
             return error;
         }
     }
+    cache_notifier_refcount++;
 
     netdev_dev = xzalloc(sizeof *netdev_dev);
-    netdev_dev->shash_node = shash_add(&cache_map, name, netdev_dev);
-
     netdev_dev_init(&netdev_dev->netdev_dev, name, &netdev_linux_class);
+
     *netdev_devp = &netdev_dev->netdev_dev;
     return 0;
 }
@@ -596,7 +616,7 @@ netdev_linux_create_system(const char *name, const char *type UNUSED,
  * buffers, across all readers.  Therefore once data is read it will
  * be unavailable to other reads for tap devices. */
 static int
-netdev_linux_create_tap(const char *name, const char *type UNUSED,
+netdev_linux_create_tap(const char *name, const char *type OVS_UNUSED,
                     const struct shash *args, struct netdev_dev **netdev_devp)
 {
     struct netdev_dev_linux *netdev_dev;
@@ -663,7 +683,7 @@ if_up(const char *name)
 }
 
 static int
-netdev_linux_create_gre(const char *name, const char *type UNUSED,
+netdev_linux_create_gre(const char *name, const char *type OVS_UNUSED,
                     const struct shash *args, struct netdev_dev **netdev_devp)
 {
     struct netdev_dev_linux *netdev_dev;
@@ -702,7 +722,7 @@ netdev_linux_reconfigure_gre(struct netdev_dev *netdev_dev_,
 /* The arguments are marked as unused to prevent warnings on platforms where
  * the Netlink interface isn't supported. */
 static int
-destroy_gre_netlink(const char *name UNUSED)
+destroy_gre_netlink(const char *name OVS_UNUSED)
 {
 #ifdef GRE_IOCTL_ONLY
     return EOPNOTSUPP;
@@ -787,9 +807,9 @@ netdev_linux_destroy(struct netdev_dev *netdev_dev_)
     const char *type = netdev_dev_get_type(netdev_dev_);
 
     if (!strcmp(type, "system")) {
-        shash_delete(&cache_map, netdev_dev->shash_node);
+        cache_notifier_refcount--;
 
-        if (shash_is_empty(&cache_map)) {
+        if (!cache_notifier_refcount) {
             rtnetlink_notifier_unregister(&netdev_linux_cache_notifier);
         }
     } else if (!strcmp(type, "tap")) {
@@ -1217,9 +1237,7 @@ netdev_linux_get_stats(const struct netdev *netdev_,
     COVERAGE_INC(netdev_get_stats);
 
     if (!(netdev_dev->cache_valid & VALID_IS_INTERNAL)) {
-        netdev_dev->is_internal = !strcmp(netdev_get_type(netdev_),
-                                                "tap");
-
+        netdev_dev->is_internal = !strcmp(netdev_get_type(netdev_), "tap");
         if (!netdev_dev->is_internal) {
             struct ethtool_drvinfo drvinfo;
 
@@ -1260,7 +1278,7 @@ netdev_linux_get_stats(const struct netdev *netdev_,
      * will appear to be swapped relative to the other ports since we are the
      * one sending the data, not a remote computer.  For consistency, we swap
      * them back here. */
-    if (netdev_dev->is_internal) {
+    if (!error && netdev_dev->is_internal) {
         stats->rx_packets = raw_stats.tx_packets;
         stats->tx_packets = raw_stats.rx_packets;
         stats->rx_bytes = raw_stats.tx_bytes;
@@ -1703,7 +1721,7 @@ do_set_addr(struct netdev *netdev,
 
 /* Adds 'router' as a default IP gateway. */
 static int
-netdev_linux_add_router(struct netdev *netdev UNUSED, struct in_addr router)
+netdev_linux_add_router(struct netdev *netdev OVS_UNUSED, struct in_addr router)
 {
     struct in_addr any = { INADDR_ANY };
     struct rtentry rt;
@@ -1870,7 +1888,7 @@ poll_notify(struct list *list)
 
 static void
 netdev_linux_poll_cb(const struct rtnetlink_change *change,
-                     void *aux UNUSED)
+                     void *aux OVS_UNUSED)
 {
     if (change) {
         struct list *list = shash_find_data(&netdev_linux_notifiers,