Pull configuration information from DMI.
authorJustin Pettit <jpettit@nicira.com>
Thu, 30 Oct 2008 23:23:27 +0000 (16:23 -0700)
committerJustin Pettit <jpettit@nicira.com>
Thu, 30 Oct 2008 23:23:27 +0000 (16:23 -0700)
Pull configuration information from DMI when available.  In its current form,
it will only use DMI information set by Nicira.  Otherwise, it will use default
values.  This change also makes DPIDs the same as the MAC address of the
local OpenFlow device.

datapath/datapath.c
datapath/dp_dev.c
datapath/linux-2.4/compat-2.4/include/linux/dmi.h [new file with mode: 0644]
include/openflow/nicira-ext.h

index 3acf243..9e81179 100644 (file)
@@ -31,7 +31,9 @@
 #include <linux/list.h>
 #include <linux/rculist.h>
 #include <linux/workqueue.h>
+#include <linux/dmi.h>
 
+#include "openflow/nicira-ext.h"
 #include "openflow/openflow-netlink.h"
 #include "datapath.h"
 #include "nx_act_snat.h"
@@ -46,7 +48,7 @@
 
 /* Strings to describe the manufacturer, hardware, and software.  This data 
  * is queriable through the switch description stats message. */
-static char mfr_desc[DESC_STR_LEN] = "Nicira Networks";
+static char mfr_desc[DESC_STR_LEN] = "Nicira Networks, Inc.";
 static char hw_desc[DESC_STR_LEN] = "Reference Linux Kernel Module";
 static char sw_desc[DESC_STR_LEN] = VERSION;
 static char serial_num[SERIAL_NUM_LEN] = "None";
@@ -220,37 +222,15 @@ send_openflow_skb(struct sk_buff *skb, const struct sender *sender)
                : genlmsg_multicast(skb, 0, mc_group.id, GFP_ATOMIC));
 }
 
-/* Generates a unique datapath id.  It incorporates the datapath index
- * and a hardware address, if available.  If not, it generates a random
- * one.
- */
+/* Retrieves the datapath id, which is the MAC address of the "of" device. */
 static 
-uint64_t gen_datapath_id(uint16_t dp_idx)
+uint64_t get_datapath_id(struct net_device *dev)
 {
-       uint64_t id;
+       uint64_t id = 0;
        int i;
-       struct net_device *dev;
-
-       /* The top 16 bits are used to identify the datapath.  The lower 48 bits
-        * use an interface address.  */
-       id = (uint64_t)dp_idx << 48;
-       if ((dev = dev_get_by_name(&init_net, "ctl0")) 
-                       || (dev = dev_get_by_name(&init_net, "eth0"))) {
-               for (i=0; i<ETH_ALEN; i++) {
-                       id |= (uint64_t)dev->dev_addr[i] << (8*(ETH_ALEN-1 - i));
-               }
-               dev_put(dev);
-       } else {
-               /* Randomly choose the lower 48 bits if we cannot find an
-                * address and mark the most significant bit to indicate that
-                * this was randomly generated. */
-               uint8_t rand[ETH_ALEN];
-               get_random_bytes(rand, ETH_ALEN);
-               id |= (uint64_t)1 << 63;
-               for (i=0; i<ETH_ALEN; i++) {
-                       id |= (uint64_t)rand[i] << (8*(ETH_ALEN-1 - i));
-               }
-       }
+
+       for (i=0; i<ETH_ALEN; i++) 
+               id |= (uint64_t)dev->dev_addr[i] << (8*(ETH_ALEN-1 - i));
 
        return id;
 }
@@ -285,7 +265,7 @@ static int new_dp(int dp_idx)
                goto err_free_dp;
 
        dp->dp_idx = dp_idx;
-       dp->id = gen_datapath_id(dp_idx);
+       dp->id = get_datapath_id(dp->netdev);
        dp->chain = chain_create(dp);
        if (dp->chain == NULL)
                goto err_destroy_dp_dev;
@@ -1803,6 +1783,33 @@ static void dp_uninit_netlink(void)
        genl_unregister_family(&dp_genl_family);
 }
 
+/* Set the description strings if appropriate values are available from
+ * the DMI. */
+static void set_desc(void)
+{
+       const char *uuid = dmi_get_system_info(DMI_PRODUCT_UUID);
+       const char *uptr = uuid + 24;
+
+       if (!uuid || *uuid == '\0' || strlen(uuid) != 36) 
+               return;
+
+       /* We are only interested version 1 UUIDs, since the last six bytes
+        * are an IEEE 802 MAC address. */
+       if (uuid[14] != '1') 
+               return;
+
+       /* Only set if the UUID is from Nicira. */
+       if (strncmp(uptr, NICIRA_OUI_STR, strlen(NICIRA_OUI_STR)))
+               return;
+
+       strlcpy(mfr_desc, dmi_get_system_info(DMI_SYS_VENDOR), sizeof(mfr_desc));
+       snprintf(hw_desc, sizeof(hw_desc), "%s %s", 
+                       dmi_get_system_info(DMI_PRODUCT_NAME), 
+                       dmi_get_system_info(DMI_PRODUCT_VERSION));
+       strlcpy(serial_num, dmi_get_system_info(DMI_PRODUCT_SERIAL), 
+                       sizeof(serial_num));
+}
+
 static int __init dp_init(void)
 {
        int err;
@@ -1822,6 +1829,10 @@ static int __init dp_init(void)
        if (err)
                goto error_unreg_notifier;
 
+       /* Check if better descriptions of the switch are available than the
+        * defaults. */
+       set_desc();
+
        /* Hook into callback used by the bridge to intercept packets.
         * Parasites we are. */
        if (br_handle_frame_hook)
index 195accb..ee66638 100644 (file)
@@ -4,6 +4,7 @@
 #include <linux/rcupdate.h>
 #include <linux/skbuff.h>
 #include <linux/workqueue.h>
+#include <linux/dmi.h>
 
 #include "datapath.h"
 #include "forward.h"
@@ -107,6 +108,44 @@ static int dp_dev_stop(struct net_device *netdev)
        return 0;
 }
 
+/* Check if the DMI UUID contains a Nicira mac address that should be 
+ * used for this interface.  The UUID is assumed to be RFC 4122
+ * compliant. */
+static void
+set_uuid_mac(struct net_device *netdev)
+{
+       const char *uuid = dmi_get_system_info(DMI_PRODUCT_UUID);
+       const char *uptr = uuid + 24;
+       uint8_t mac[ETH_ALEN];
+       int i;
+
+       if (!uuid || *uuid == '\0' || strlen(uuid) != 36)
+               return;
+
+       /* We are only interested version 1 UUIDs, since the last six bytes
+        * are an IEEE 802 MAC address. */
+       if (uuid[14] != '1') 
+               return;
+
+       /* Pull out the embedded MAC address.  The kernel's sscanf doesn't
+        * support field widths on hex digits, so we use this hack. */
+       for (i=0; i<ETH_ALEN; i++) {
+               unsigned char d[3];
+               
+               d[0] = *uptr++;
+               d[1] = *uptr++;
+               d[2] = '\0';
+               
+               mac[i] = simple_strtoul(d, NULL, 16);
+       }
+
+       /* If this is a Nicira one, then use it. */
+       if (mac[0] != 0x00 || mac[1] != 0x23 || mac[2] != 0x20) 
+               return;
+
+       memcpy(netdev->dev_addr, mac, ETH_ALEN);
+}
+
 static void
 do_setup(struct net_device *netdev)
 {
@@ -122,9 +161,14 @@ do_setup(struct net_device *netdev)
        netdev->flags = IFF_BROADCAST | IFF_MULTICAST;
 
        random_ether_addr(netdev->dev_addr);
+
+       /* Set the OUI to the Nicira one. */
        netdev->dev_addr[0] = 0x00;
        netdev->dev_addr[1] = 0x23;
        netdev->dev_addr[2] = 0x20;
+
+       /* Set the top bits to indicate random Nicira address. */
+       netdev->dev_addr[3] |= 0xc0;
 }
 
 
@@ -146,6 +190,11 @@ int dp_dev_setup(struct datapath *dp)
                return err;
        }
 
+       /* For "of0", we check the DMI UUID to see if a Nicira mac address
+        * is available to use instead of the random one just generated. */
+       if (dp->dp_idx == 0) 
+               set_uuid_mac(netdev);
+
        dp_dev = dp_dev_priv(netdev);
        dp_dev->dp = dp;
        skb_queue_head_init(&dp_dev->xmit_queue);
diff --git a/datapath/linux-2.4/compat-2.4/include/linux/dmi.h b/datapath/linux-2.4/compat-2.4/include/linux/dmi.h
new file mode 100644 (file)
index 0000000..bf51e25
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef __LINUX_DMI_H
+#define __LINUX_DMI_H 1
+
+enum dmi_field {
+       DMI_NONE,
+       DMI_BIOS_VENDOR,
+       DMI_BIOS_VERSION,
+       DMI_BIOS_DATE,
+       DMI_SYS_VENDOR,
+       DMI_PRODUCT_NAME,
+       DMI_PRODUCT_VERSION,
+       DMI_PRODUCT_SERIAL,
+       DMI_PRODUCT_UUID,
+       DMI_BOARD_VENDOR,
+       DMI_BOARD_NAME,
+       DMI_BOARD_VERSION,
+       DMI_BOARD_SERIAL,
+       DMI_BOARD_ASSET_TAG,
+       DMI_CHASSIS_VENDOR,
+       DMI_CHASSIS_TYPE,
+       DMI_CHASSIS_VERSION,
+       DMI_CHASSIS_SERIAL,
+       DMI_CHASSIS_ASSET_TAG,
+       DMI_STRING_MAX
+};
+
+/* 2.4 kernels didn't provide a general ability to query DMI.  We'll just
+ * always return NULL, since this functionality isn't critical. */
+static inline 
+const char *dmi_get_system_info(int field) {
+       return NULL;
+}
+
+#endif 
index 821ad6f..da71be3 100644 (file)
@@ -8,6 +8,8 @@
 
 #include "openflow/openflow.h"
 
+#define NICIRA_OUI_STR "002320"
+
 /* The following vendor extensions, proposed by Nicira Networks, are not yet
  * ready for standardization (and may never be), so they are not included in
  * openflow.h. */