summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Christie <michaelc@cs.wisc.edu>2010-01-12 23:59:59 -0600
committerMike Christie <michaelc@cs.wisc.edu>2010-01-12 23:59:59 -0600
commit39f4761231e2aa20b468cd5258785d6f56472772 (patch)
tree57a746742e8798014c6776c3198656c6bb87e268
parentda67e62ce691ff64e6ec45b9bbea8e21830f1c52 (diff)
downloadopen-iscsi-39f4761231e2aa20b468cd5258785d6f56472772.tar.gz
iscsistart option to bring up NICs using configuration in iBFT.
Patch and mail from Alex Zeffertt: For each target listed, iSCSI Boot Firmware Tables specify which NIC to use and how it should be configured. Until now this information has been ignored by open-iscsi. This patch enables iscsistart to apply the NIC configuration. The new command "iscsiadm -n" applies the NIC configuration specified in the iBFT for each valid target. The primary benefit of this is that it allows the initrd to extract networking information from the iBFT rather than hard code it. If the initrd uses the iBFT for networking info then when this info is modified via the BIOS it is not necessary to rebuild the initrd. Signed-off-by<alex.zeffertt@eu.citrix.com>
-rw-r--r--usr/iscsistart.c152
-rw-r--r--utils/fwparam_ibft/fwparam_ibft_sysfs.c67
2 files changed, 218 insertions, 1 deletions
diff --git a/usr/iscsistart.c b/usr/iscsistart.c
index 8482ad5..2ee2674 100644
--- a/usr/iscsistart.c
+++ b/usr/iscsistart.c
@@ -24,6 +24,7 @@
#include <getopt.h>
#include <stdlib.h>
#include <stdio.h>
+#include <stddef.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
@@ -32,6 +33,11 @@
#include <sys/signal.h>
#include <sys/types.h>
#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/route.h>
#include "initiator.h"
#include "iscsi_ipc.h"
@@ -73,6 +79,7 @@ static struct option const long_options[] = {
{"password_in", required_argument, NULL, 'W'},
{"debug", required_argument, NULL, 'd'},
{"fwparam_connect", no_argument, NULL, 'b'},
+ {"fwparam_network", no_argument, NULL, 'n'},
{"fwparam_print", no_argument, NULL, 'f'},
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'v'},
@@ -99,6 +106,7 @@ Open-iSCSI initiator.\n\
-W, --password_in=N set incoming password to N (optional\n\
-d, --debug debuglevel print debugging information \n\
-b, --fwparam_connect create a session to the target\n\
+ -n, --fwparam_network bring up the network as specified by iBFT\n\
-f, --fwparam_print print the iBFT to STDOUT \n\
-h, --help display this help and exit\n\
-v, --version display version and exit\n\
@@ -199,6 +207,140 @@ static int setup_session(void)
return rc;
}
+static int setup_nics(void)
+{
+ struct boot_context *context;
+ char *iface_prev = NULL;
+ int sock;
+ int ret;
+
+ /* Create socket for making networking changes */
+ if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
+ perror("socket(AF_INET, SOCK_DGRAM, 0)");
+ exit(1);
+ }
+
+ /*
+ * For each target in iBFT bring up required NIC and use routing
+ * to force iSCSI traffic through correct NIC
+ */
+ list_for_each_entry(context, &targets, list) {
+
+ /* Bring up NIC with correct address - unless it
+ * has already been handled (2 targets in IBFT may share one NIC)
+ */
+ struct sockaddr_in ipaddr = { .sin_family = AF_INET };
+ struct sockaddr_in netmask = { .sin_family = AF_INET };
+ struct sockaddr_in hostmask = { .sin_family = AF_INET };
+ struct sockaddr_in gateway = { .sin_family = AF_INET };
+ struct sockaddr_in tgt_ipaddr = { .sin_family = AF_INET };
+ struct rtentry rt;
+ struct ifreq ifr;
+
+ if (!strlen(context->iface)) {
+ printf("No iface in fw entry\n");
+ ret = -1;
+ continue;
+ }
+ if (!inet_aton(context->ipaddr, &ipaddr.sin_addr)) {
+ printf("Invalid or no ipaddr in fw entry\n");
+ ret = -1;
+ continue;
+ }
+
+ if (!inet_aton(context->mask, &netmask.sin_addr)) {
+ printf("Invalid or no netmask in fw entry\n");
+ ret = -1;
+ continue;
+ }
+ inet_aton("255.255.255.255", &hostmask.sin_addr);
+
+ if (!inet_aton(context->target_ipaddr, &tgt_ipaddr.sin_addr)) {
+ printf("Invalid or no target ipaddr in fw entry\n");
+ ret = -1;
+ continue;
+ }
+
+ /* Only set IP/NM if this is a new interface */
+ if (iface_prev == NULL || strcmp(context->iface, iface_prev)) {
+
+ /* Note: test above works because there is a maximum of two targets in the iBFT */
+ iface_prev = context->iface;
+
+ /* TODO: create vlan if strlen(context->vlan) */
+
+ /* Bring up interface */
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, context->iface, IFNAMSIZ);
+ ifr.ifr_flags = IFF_UP | IFF_RUNNING;
+ if (ioctl(sock, SIOCSIFFLAGS, &ifr) < 0) {
+ perror("ioctl(SIOCSIFFLAGS)");
+ ret = -1;
+ continue;
+ }
+ /* Set IP address */
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, context->iface, IFNAMSIZ);
+ memcpy(&ifr.ifr_addr, &ipaddr, sizeof(struct sockaddr));
+ if (ioctl(sock, SIOCSIFADDR, &ifr) < 0) {
+ perror("ioctl(SIOCSIFADDR)");
+ ret = -1;
+ continue;
+ }
+ /* Set netmask */
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, context->iface, IFNAMSIZ);
+ memcpy(&ifr.ifr_addr, &netmask, sizeof(struct sockaddr));
+ if (ioctl(sock, SIOCSIFNETMASK, &ifr) < 0) {
+ perror("ioctl(SIOCSIFNETMASK)");
+ ret = -1;
+ continue;
+ }
+ }
+
+ /* Set static route to target via this interface */
+ memset((char *) &rt, 0, sizeof(rt));
+ memcpy(&rt.rt_dst, &tgt_ipaddr, sizeof(tgt_ipaddr));
+ memcpy(&rt.rt_genmask, &hostmask, sizeof(hostmask));
+ rt.rt_flags = RTF_UP | RTF_HOST;
+ rt.rt_dev = context->iface;
+
+ if ((tgt_ipaddr.sin_addr.s_addr & netmask.sin_addr.s_addr) ==
+ ( ipaddr.sin_addr.s_addr & netmask.sin_addr.s_addr)) {
+ /* Same subnet */
+ if (ioctl(sock, SIOCADDRT, &rt) < 0) {
+ if (errno != EEXIST) {
+ perror("ioctl(SIOCADDRT)");
+ ret = -1;
+ continue;
+ }
+ }
+ } else {
+ /* Different subnet. Use gateway */
+ rt.rt_flags |= RTF_GATEWAY;
+ if (!inet_aton(context->gateway, &gateway.sin_addr)) {
+ printf("Invalid or no gateway in fw entry\n");
+ ret = -1;
+ continue;
+ }
+ memcpy(&rt.rt_gateway, &gateway, sizeof(gateway));
+ if (ioctl(sock, SIOCADDRT, &rt) < 0) {
+ if (errno != EEXIST) {
+ perror("ioctl(SIOCADDRT)");
+ ret = -1;
+ continue;
+ }
+ }
+ }
+ /* This target handled */
+ }
+
+ close(sock);
+ fw_free_targets(&targets);
+ return ret;
+}
+
+
static void catch_signal(int signo)
{
log_warning("pid %d caught signal -%d", getpid(), signo);
@@ -268,7 +410,7 @@ int main(int argc, char *argv[])
if (iscsi_sysfs_check_class_version())
exit(1);
- while ((ch = getopt_long(argc, argv, "i:t:g:a:p:d:u:w:U:W:bfvh",
+ while ((ch = getopt_long(argc, argv, "i:t:g:a:p:d:u:w:U:W:bnfvh",
long_options, &longindex)) >= 0) {
switch (ch) {
case 'i':
@@ -332,6 +474,14 @@ int main(int argc, char *argv[])
exit(1);
}
break;
+ case 'n':
+ ret = fw_get_targets(&targets);
+ if (ret || list_empty(&targets)) {
+ printf("Could not setup fw entries.\n");
+ exit(1);
+ }
+ ret = setup_nics();
+ exit(ret);
case 'f':
ret = fw_get_targets(&targets);
if (ret || list_empty(&targets)) {
diff --git a/utils/fwparam_ibft/fwparam_ibft_sysfs.c b/utils/fwparam_ibft/fwparam_ibft_sysfs.c
index 4f88083..1527485 100644
--- a/utils/fwparam_ibft/fwparam_ibft_sysfs.c
+++ b/utils/fwparam_ibft/fwparam_ibft_sysfs.c
@@ -19,6 +19,7 @@
*/
#define _XOPEN_SOURCE 500
+#define _SVID_SOURCE
#include <ftw.h>
#include <stdio.h>
#include <stdlib.h>
@@ -28,6 +29,7 @@
#include <errno.h>
#include <dirent.h>
#include <sys/types.h>
+#include <sys/stat.h>
#include "sysfs.h"
#include "fw_context.h"
@@ -36,6 +38,7 @@
#define IBFT_MAX 255
#define IBFT_SYSFS_ROOT "/sys/firmware/ibft/"
+#define NET_SYSFS_ROOT "/sys/class/net/"
#define IBFT_SUBSYS "ibft"
static char *target_list[IBFT_MAX];
@@ -156,6 +159,67 @@ static int get_iface_from_device(char *id, struct boot_context *context)
return rc;
}
+static int get_iface_from_mac(char *id, struct boot_context *context)
+{
+ char mac_fname[FILENAMESZ];
+ char mac[18] = {0};
+ int rc = ENODEV;
+ int fd;
+ int retval;
+ struct dirent **namelist;
+ int n, i;
+
+ memset(mac_fname, 0, FILENAMESZ);
+ snprintf(mac_fname, FILENAMESZ, IBFT_SYSFS_ROOT"/%s/mac", id);
+
+ if (!file_exist(mac_fname))
+ return 0;
+
+ fd = open(mac_fname, O_RDONLY);
+ if (fd == -1)
+ return errno;
+
+ retval = read(fd, mac, 17);
+ if (retval == -1)
+ return errno;
+ if (retval != 17) {
+ printf("Couldn't read whole mac address from %s\n", mac_fname);
+ return EINVAL;
+ }
+ close(fd);
+
+ n = scandir(NET_SYSFS_ROOT, &namelist, NULL, alphasort);
+ if (n <= 0)
+ return -n;
+
+ for (i = 0; i < n; i++) {
+ char name[256];
+ char *dir = namelist[i]->d_name;
+ char buf[18] = {0};
+ if (!strcmp(name, ".") || !strcmp(name, ".."))
+ continue;
+
+ snprintf(name, sizeof(name), "%s%s/address", NET_SYSFS_ROOT, dir);
+ fd = open(name, O_RDONLY);
+ if (fd == -1)
+ continue;
+ retval = read(fd, buf, 17);
+ close(fd);
+
+ if (strncasecmp(buf, mac, 17) == 0) {
+ strlcpy(context->iface, dir, sizeof(context->iface));
+ rc = 0;
+ break;
+ }
+ }
+
+ for (i = 0; i < n; i++)
+ free(namelist[i]);
+ free(namelist);
+
+ return rc;
+}
+
/*
* Routines to fill in the context values.
*/
@@ -170,6 +234,9 @@ static int fill_nic_context(char *id, struct boot_context *context)
rc = get_iface_from_device(id, context);
if (rc)
+ rc = get_iface_from_mac(id, context);
+
+ if (rc)
return rc;
sysfs_get_str(id, IBFT_SUBSYS, "ip-addr", context->ipaddr,