summaryrefslogtreecommitdiff
path: root/libopeniscsiusr
diff options
context:
space:
mode:
authorGris Ge <fge@redhat.com>2018-03-22 19:37:15 +0800
committerGris Ge <fge@redhat.com>2018-03-22 19:37:15 +0800
commit8c0eb3e6c34233fbecddd76c881bdcd0daaf91b9 (patch)
tree7d220c12f8c163f4f7252882dbf5e2aa05662b65 /libopeniscsiusr
parent76ed807df0dac8e7ffdf6ad450d8c2a9f5450e94 (diff)
downloadopen-iscsi-8c0eb3e6c34233fbecddd76c881bdcd0daaf91b9.tar.gz
libopeniscsiusr: Add full iscsi interface support.
* Improve existing iface query from sysfs. * Add new functions to query iSCSI interface information from /etc/iscsi/ifaces/ folder: * iscsi_ifaces_get()/iscsi_ifaces_free() * iscsi_iface_get()/iscsi_iface_free() * iscsi_iface_dump_config() * iscsi_iface_print_config() * iscsi_is_default_iface() * Use above functions in iscsiadm for iface query. * The `iscsiadm -m iface -P 1` command is untouched as it require node query. * Bump libopeniscsiusr library version to 0.1.1 due to API adding. * Tested on qla4xxx, cxgb4i, bnx2i, qedi and iscsi_tcp. Signed-off-by: Gris Ge <fge@redhat.com>
Diffstat (limited to 'libopeniscsiusr')
-rw-r--r--libopeniscsiusr/Makefile6
-rw-r--r--libopeniscsiusr/TODO3
-rw-r--r--libopeniscsiusr/context.c7
-rw-r--r--libopeniscsiusr/context.h5
-rw-r--r--libopeniscsiusr/idbm.c708
-rw-r--r--libopeniscsiusr/idbm.h46
-rw-r--r--libopeniscsiusr/idbm_fields.h98
-rw-r--r--libopeniscsiusr/iface.c934
-rw-r--r--libopeniscsiusr/iface.h120
-rw-r--r--libopeniscsiusr/libopeniscsiusr/libopeniscsiusr.h155
-rw-r--r--libopeniscsiusr/libopeniscsiusr/libopeniscsiusr_common.h9
-rw-r--r--libopeniscsiusr/libopeniscsiusr/libopeniscsiusr_iface.h53
-rw-r--r--libopeniscsiusr/misc.c207
-rw-r--r--libopeniscsiusr/misc.h29
-rw-r--r--libopeniscsiusr/session.c103
-rw-r--r--libopeniscsiusr/sysfs.c266
-rw-r--r--libopeniscsiusr/sysfs.h37
-rw-r--r--libopeniscsiusr/tests/test_iface.c112
-rw-r--r--libopeniscsiusr/version.h30
19 files changed, 2616 insertions, 312 deletions
diff --git a/libopeniscsiusr/Makefile b/libopeniscsiusr/Makefile
index 8b9b523..05a77da 100644
--- a/libopeniscsiusr/Makefile
+++ b/libopeniscsiusr/Makefile
@@ -26,7 +26,7 @@ PKGCONF_DIR ?= $(LIB_DIR)/pkgconfig
LIBISCSI_USR_DIR=$(TOPDIR)/libopeniscsiusr
LIBISCSI_USR_VERSION_MAJOR=0
-LIBISCSI_USR_VERSION=0.1.0
+LIBISCSI_USR_VERSION=0.1.1
SONAME=$(LIBISCSI_USR_VERSION)
DEVLIB = libopeniscsiusr.so
LIBS = $(DEVLIB).$(SONAME)
@@ -36,10 +36,10 @@ HEADERS = libopeniscsiusr/libopeniscsiusr.h \
libopeniscsiusr/libopeniscsiusr_common.h \
libopeniscsiusr/libopeniscsiusr_session.h \
libopeniscsiusr/libopeniscsiusr_iface.h
-TESTS = tests/test_context tests/test_session
+TESTS = tests/test_context tests/test_session tests/test_iface
EXTRA_MAN_FILES = libopeniscsiusr.h.3
-OBJS = context.o misc.o session.o sysfs.o iface.o
+OBJS = context.o misc.o session.o sysfs.o iface.o idbm.o
CFLAGS ?= -O2 -g
CFLAGS += -Wall -Werror -Wextra -fvisibility=hidden -fPIC
diff --git a/libopeniscsiusr/TODO b/libopeniscsiusr/TODO
new file mode 100644
index 0000000..21f3893
--- /dev/null
+++ b/libopeniscsiusr/TODO
@@ -0,0 +1,3 @@
+TODO:
+ * Add more debug message.
+ * Support of SYSFS_PATH environment.
diff --git a/libopeniscsiusr/context.c b/libopeniscsiusr/context.c
index 4c323b3..fe92155 100644
--- a/libopeniscsiusr/context.c
+++ b/libopeniscsiusr/context.c
@@ -44,12 +44,19 @@ struct iscsi_context *iscsi_context_new(void)
ctx->log_func = _iscsi_log_stderr;
ctx->log_priority = LIBISCSI_LOG_PRIORITY_DEFAULT;
ctx->userdata = NULL;
+ ctx->db = _idbm_new();
+ if (ctx->db == NULL) {
+ free(ctx);
+ return NULL;
+ }
return ctx;
}
void iscsi_context_free(struct iscsi_context *ctx)
{
+ if (ctx != NULL)
+ _idbm_free(ctx->db);
free(ctx);
}
diff --git a/libopeniscsiusr/context.h b/libopeniscsiusr/context.h
index df113f2..f37c90c 100644
--- a/libopeniscsiusr/context.h
+++ b/libopeniscsiusr/context.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 Red Hat, Inc.
+ * Copyright (C) 2017-2018 Red Hat, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -19,12 +19,15 @@
#ifndef __ISCSI_USR_CONTEXT_H__
#define __ISCSI_USR_CONTEXT_H__
+#include "idbm.h"
+
struct iscsi_context {
void (*log_func)(struct iscsi_context *ctx, int priority,
const char *file, int line, const char *func_name,
const char *format, va_list args);
int log_priority;
void *userdata;
+ struct idbm *db;
};
diff --git a/libopeniscsiusr/idbm.c b/libopeniscsiusr/idbm.c
new file mode 100644
index 0000000..a273fa8
--- /dev/null
+++ b/libopeniscsiusr/idbm.c
@@ -0,0 +1,708 @@
+/*
+ * Copyright (C) 2017-2018 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Gris Ge <fge@redhat.com>
+ */
+
+/* The code below is modified from usr/idbm.c which licensed like below:
+ *
+ * iSCSI Discovery Database Library
+ *
+ * Copyright (C) 2004 Dmitry Yusupov, Alex Aizman
+ * Copyright (C) 2006 Mike Christie
+ * Copyright (C) 2006 Red Hat, Inc. All rights reserved.
+ * maintained by open-iscsi@@googlegroups.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * See the file COPYING included with this distribution for more details.
+ *
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE /* For strerror_r() */
+#endif
+
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "libopeniscsiusr/libopeniscsiusr_common.h"
+
+#include "context.h"
+#include "idbm.h"
+#include "misc.h"
+#include "idbm_fields.h"
+#include "iface.h"
+#include "version.h"
+
+#define TYPE_STR 2
+#define TYPE_UINT8 3
+#define TYPE_UINT16 4
+#define TYPE_UINT32 5
+#define MAX_KEYS 256 /* number of keys total(including CNX_MAX) */
+#define NAME_MAXVAL 128 /* the maximum length of key name */
+#define VALUE_MAXVAL 256 /* the maximum length of 223 bytes in the RFC. */
+/* ^ MAX_KEYS, NAME_MAXVAL and VALUE_MAXVAL are copied from usr/idbm.h
+ * The RFC 3720 only said:
+ * If not otherwise specified, the maximum length of a simple-value (not
+ * its encoded representation) is 255 bytes, not including the delimiter
+ * (comma or zero byte).
+ */
+
+#define OPTS_MAXVAL 8
+
+#define IDBM_HIDE 0 /* Hide parameter when print. */
+#define IDBM_SHOW 1 /* Show parameter when print. */
+#define IDBM_MASKED 2 /* Show "stars" instead of real value when print */
+
+#define ISCSI_BEGIN_REC "# BEGIN RECORD "ISCSI_VERSION_STR
+#define ISCSI_END_REC "# END RECORD"
+
+#ifndef LOCK_DIR
+#define LOCK_DIR "/var/lock/iscsi"
+#endif
+#define LOCK_FILE LOCK_DIR"/lock"
+#define LOCK_WRITE_FILE LOCK_DIR"/lock.write"
+
+#define _rec_str(_key, _recs, _org, _name, _show, _n, _mod) \
+do { \
+ _recs[_n].type = TYPE_STR; \
+ _strncpy(_recs[_n].name, _key, NAME_MAXVAL); \
+ if (strlen((char*)_org->_name)) \
+ _strncpy((char*)_recs[_n].value, (char*)_org->_name, \
+ VALUE_MAXVAL); \
+ _recs[_n].data = &_org->_name; \
+ _recs[_n].data_len = sizeof(_org->_name); \
+ _recs[_n].visible = _show; \
+ _recs[_n].can_modify = _mod; \
+ _n++; \
+} while(0)
+
+#define _rec_uint8(_key, _recs, _org, _name, _show, _n, _mod) \
+do { \
+ _recs[_n].type = TYPE_UINT8; \
+ _strncpy(_recs[_n].name, _key, NAME_MAXVAL); \
+ snprintf(_recs[_n].value, VALUE_MAXVAL, "%" PRIu8, _org->_name); \
+ _recs[_n].data = &_org->_name; \
+ _recs[_n].data_len = sizeof(_org->_name); \
+ _recs[_n].visible = _show; \
+ _recs[_n].can_modify = _mod; \
+ _n++; \
+} while (0)
+
+#define _rec_uint16(_key, _recs, _org, _name, _show, _n, _mod) \
+do { \
+ _recs[_n].type = TYPE_UINT16; \
+ _strncpy(_recs[_n].name, _key, NAME_MAXVAL); \
+ snprintf(_recs[_n].value, VALUE_MAXVAL, "%" PRIu16, _org->_name); \
+ _recs[_n].data = &_org->_name; \
+ _recs[_n].data_len = sizeof(_org->_name); \
+ _recs[_n].visible = _show; \
+ _recs[_n].can_modify = _mod; \
+ _n++; \
+} while (0)
+
+#define _rec_uint32(_key, _recs, _org, _name, _show, _n, _mod) \
+do { \
+ _recs[_n].type = TYPE_UINT32; \
+ _strncpy(_recs[_n].name, _key, NAME_MAXVAL); \
+ snprintf(_recs[_n].value, VALUE_MAXVAL, "%" PRIu32, _org->_name); \
+ _recs[_n].data = &_org->_name; \
+ _recs[_n].data_len = sizeof(_org->_name); \
+ _recs[_n].visible = _show; \
+ _recs[_n].can_modify = _mod; \
+ _n++; \
+} while (0)
+
+enum modify_mode {
+ _CANNOT_MODIFY,
+ _CAN_MODIFY,
+};
+
+struct idbm_rec {
+ int type;
+ char name[NAME_MAXVAL];
+ char value[VALUE_MAXVAL];
+ void *data;
+ int data_len;
+ int visible;
+ /*
+ * TODO: make it a enum that can indicate whether it also requires
+ * a relogin to pick up if a session is running.
+ */
+ enum modify_mode can_modify;
+};
+
+int _idbm_lock(struct iscsi_context *ctx)
+{
+ int fd, i, ret;
+ struct idbm *db = NULL;
+ char strerr_buff[_STRERR_BUFF_LEN];
+ int errno_save = 0;
+
+ assert(ctx != NULL);
+
+ db = ctx->db;
+
+ if (db->refs > 0) {
+ db->refs++;
+ return 0;
+ }
+
+ if (access(LOCK_DIR, F_OK) != 0) {
+ if (mkdir(LOCK_DIR, 0660) != 0) {
+ _error(ctx, "Could not open %s: %d %s", LOCK_DIR, errno,
+ _strerror(errno, strerr_buff));
+ return LIBISCSI_ERR_IDBM;
+ }
+ }
+
+ fd = open(LOCK_FILE, O_RDWR | O_CREAT, 0666);
+ if (fd >= 0)
+ close(fd);
+
+ for (i = 0; i < 3000; i++) {
+ ret = link(LOCK_FILE, LOCK_WRITE_FILE);
+ if (ret == 0)
+ break;
+ errno_save = errno;
+
+ if (errno != EEXIST) {
+ _error(ctx, "Maybe you are not root? "
+ "Could not lock discovery DB: %s: %d %s",
+ LOCK_WRITE_FILE, errno_save,
+ _strerror(errno_save, strerr_buff));
+ return LIBISCSI_ERR_IDBM;
+ } else if (i == 0)
+ _debug(ctx, "Waiting for discovery DB lock on %s",
+ LOCK_WRITE_FILE);
+
+ usleep(10000);
+ }
+
+ if (ret != 0) {
+ _error(ctx, "Timeout on acquiring lock on DB: %s, errno: %d %s",
+ LOCK_WRITE_FILE, errno_save,
+ _strerror(errno_save, strerr_buff));
+ return LIBISCSI_ERR_IDBM;
+ }
+
+ db->refs = 1;
+ return 0;
+}
+
+void _idbm_unlock(struct iscsi_context *ctx)
+{
+ struct idbm *db = NULL;
+
+ assert(ctx != NULL);
+
+ db = ctx->db;
+
+ if (db->refs > 1) {
+ db->refs--;
+ return;
+ }
+
+ db->refs = 0;
+ unlink(LOCK_WRITE_FILE);
+}
+
+static struct idbm_rec* _idbm_recs_alloc(void)
+{
+ return calloc(MAX_KEYS, sizeof(struct idbm_rec));
+}
+
+static void _idbm_recs_free(struct idbm_rec* recs)
+{
+ free(recs);
+}
+
+static void _idbm_iface_rec_link(struct iscsi_iface *iface,
+ struct idbm_rec *recs)
+{
+ int num = 0;
+
+ _rec_str(IFACE_ISCSINAME, recs, iface, name, IDBM_SHOW, num,
+ _CANNOT_MODIFY);
+ _rec_str(IFACE_NETNAME, recs, iface, netdev, IDBM_SHOW, num,
+ _CAN_MODIFY);
+ _rec_str(IFACE_IPADDR, recs, iface, ipaddress, IDBM_SHOW, num,
+ _CAN_MODIFY);
+ _rec_str(IFACE_HWADDR, recs, iface, hwaddress, IDBM_SHOW, num,
+ _CAN_MODIFY);
+ _rec_str(IFACE_TRANSPORTNAME, recs, iface, transport_name, IDBM_SHOW,
+ num, _CAN_MODIFY);
+ _rec_str(IFACE_INAME, recs, iface, iname, IDBM_SHOW, num, _CAN_MODIFY);
+ _rec_str(IFACE_STATE, recs, iface, state, IDBM_SHOW, num, _CAN_MODIFY);
+ _rec_uint16(IFACE_VLAN_ID, recs, iface, vlan_id, IDBM_SHOW, num,
+ _CAN_MODIFY);
+ _rec_uint8(IFACE_VLAN_PRIORITY, recs, iface, vlan_priority, IDBM_SHOW,
+ num, _CAN_MODIFY);
+ _rec_str(IFACE_VLAN_STATE, recs, iface, vlan_state, IDBM_SHOW, num,
+ _CAN_MODIFY);
+ _rec_uint32(IFACE_NUM, recs, iface, iface_num, IDBM_SHOW, num,
+ _CAN_MODIFY);
+ _rec_uint16(IFACE_MTU, recs, iface, mtu, IDBM_SHOW, num, _CAN_MODIFY);
+ _rec_uint16(IFACE_PORT, recs, iface, port, IDBM_SHOW, num, _CAN_MODIFY);
+
+ if (! iface->is_ipv6) {
+ _rec_str(IFACE_BOOT_PROTO, recs, iface, bootproto, IDBM_SHOW,
+ num, _CAN_MODIFY);
+ _rec_str(IFACE_SUBNET_MASK, recs, iface, subnet_mask, IDBM_SHOW,
+ num, _CAN_MODIFY);
+ _rec_str(IFACE_GATEWAY, recs, iface, gateway, IDBM_SHOW, num,
+ _CAN_MODIFY);
+ _rec_str(IFACE_DHCP_ALT_CID, recs, iface,
+ dhcp_alt_client_id_state, IDBM_SHOW, num, _CAN_MODIFY);
+ _rec_str(IFACE_DHCP_ALT_CID_STR, recs, iface,
+ dhcp_alt_client_id, IDBM_SHOW, num, _CAN_MODIFY);
+ _rec_str(IFACE_DHCP_DNS, recs, iface, dhcp_dns, IDBM_SHOW, num,
+ _CAN_MODIFY);
+ _rec_str(IFACE_DHCP_LEARN_IQN, recs, iface, dhcp_learn_iqn,
+ IDBM_SHOW, num, _CAN_MODIFY);
+ _rec_str(IFACE_DHCP_REQ_VID, recs, iface,
+ dhcp_req_vendor_id_state, IDBM_SHOW, num, _CAN_MODIFY);
+ _rec_str(IFACE_DHCP_VID, recs, iface, dhcp_vendor_id_state,
+ IDBM_SHOW, num, _CAN_MODIFY);
+ _rec_str(IFACE_DHCP_VID_STR, recs, iface, dhcp_vendor_id,
+ IDBM_SHOW, num, _CAN_MODIFY);
+ _rec_str(IFACE_DHCP_SLP_DA, recs, iface, dhcp_slp_da, IDBM_SHOW,
+ num, _CAN_MODIFY);
+ _rec_str(IFACE_FRAGMENTATION, recs, iface, fragmentation,
+ IDBM_SHOW, num, _CAN_MODIFY);
+ _rec_str(IFACE_GRAT_ARP, recs, iface, gratuitous_arp, IDBM_SHOW,
+ num, _CAN_MODIFY);
+ _rec_str(IFACE_IN_FORWARD, recs, iface, incoming_forwarding,
+ IDBM_SHOW, num, _CAN_MODIFY);
+ _rec_str(IFACE_TOS_STATE, recs, iface, tos_state, IDBM_SHOW,
+ num, _CAN_MODIFY);
+ _rec_uint8(IFACE_TOS, recs, iface, tos, IDBM_SHOW, num,
+ _CAN_MODIFY);
+ _rec_uint8(IFACE_TTL, recs, iface, ttl, IDBM_SHOW, num,
+ _CAN_MODIFY);
+ } else {
+ _rec_str(IFACE_IPV6_AUTOCFG, recs, iface, ipv6_autocfg,
+ IDBM_SHOW, num, _CAN_MODIFY);
+ _rec_str(IFACE_LINKLOCAL_AUTOCFG, recs, iface,
+ linklocal_autocfg, IDBM_SHOW, num, _CAN_MODIFY);
+ _rec_str(IFACE_ROUTER_AUTOCFG, recs, iface, router_autocfg,
+ IDBM_SHOW, num, _CAN_MODIFY);
+ _rec_str(IFACE_LINKLOCAL, recs, iface, ipv6_linklocal,
+ IDBM_SHOW, num, _CAN_MODIFY);
+ _rec_str(IFACE_ROUTER, recs, iface, ipv6_router, IDBM_SHOW, num,
+ _CAN_MODIFY);
+ _rec_uint8(IFACE_DUP_ADDR_DETECT_CNT, recs, iface,
+ dup_addr_detect_cnt, IDBM_SHOW, num, _CAN_MODIFY);
+ _rec_uint32(IFACE_FLOW_LABEL, recs, iface, flow_label,
+ IDBM_SHOW, num, _CAN_MODIFY);
+ _rec_str(IFACE_GRAT_NEIGHBOR_ADV, recs, iface,
+ gratuitous_neighbor_adv, IDBM_SHOW, num, _CAN_MODIFY);
+ _rec_uint8(IFACE_HOP_LIMIT, recs, iface, hop_limit, IDBM_SHOW,
+ num, _CAN_MODIFY);
+ _rec_str(IFACE_MLD, recs, iface, mld, IDBM_SHOW, num,
+ _CAN_MODIFY);
+ _rec_uint32(IFACE_ND_REACHABLE_TMO, recs, iface,
+ nd_reachable_tmo, IDBM_SHOW, num, _CAN_MODIFY);
+ _rec_uint32(IFACE_ND_REXMIT_TIME, recs, iface, nd_rexmit_time,
+ IDBM_SHOW, num, _CAN_MODIFY);
+ _rec_uint32(IFACE_ND_STALE_TMO, recs, iface, nd_stale_tmo,
+ IDBM_SHOW, num, _CAN_MODIFY);
+ _rec_uint32(IFACE_RTR_ADV_LINK_MTU, recs, iface,
+ router_adv_link_mtu, IDBM_SHOW, num, _CAN_MODIFY);
+ _rec_uint8(IFACE_TRAFFIC_CLASS, recs, iface, traffic_class,
+ IDBM_SHOW, num, _CAN_MODIFY);
+ }
+
+ _rec_str(IFACE_DELAYED_ACK, recs, iface, delayed_ack, IDBM_SHOW, num,
+ _CAN_MODIFY);
+ _rec_str(IFACE_TCP_NAGLE, recs, iface, nagle, IDBM_SHOW, num,
+ _CAN_MODIFY);
+ _rec_str(IFACE_TCP_WSF_STATE, recs, iface, tcp_wsf_state, IDBM_SHOW,
+ num, _CAN_MODIFY);
+ _rec_uint8(IFACE_TCP_WSF, recs, iface, tcp_wsf, IDBM_SHOW, num,
+ _CAN_MODIFY);
+ _rec_uint8(IFACE_TCP_TIMER_SCALE, recs, iface, tcp_timer_scale,
+ IDBM_SHOW, num, _CAN_MODIFY);
+ _rec_str(IFACE_TCP_TIMESTAMP, recs, iface, tcp_timestamp, IDBM_SHOW,
+ num, _CAN_MODIFY);
+ _rec_str(IFACE_REDIRECT, recs, iface, redirect, IDBM_SHOW, num,
+ _CAN_MODIFY);
+ _rec_uint16(IFACE_DEF_TMF_TMO, recs, iface, def_task_mgmt_tmo,
+ IDBM_SHOW, num, _CAN_MODIFY);
+ _rec_str(IFACE_HDRDGST, recs, iface, header_digest, IDBM_SHOW, num,
+ _CAN_MODIFY);
+ _rec_str(IFACE_DATADGST, recs, iface, data_digest, IDBM_SHOW, num,
+ _CAN_MODIFY);
+ _rec_str(IFACE_IMM_DATA, recs, iface, immediate_data, IDBM_SHOW, num,
+ _CAN_MODIFY);
+ _rec_str(IFACE_INITIAL_R2T, recs, iface, initial_r2t, IDBM_SHOW, num,
+ _CAN_MODIFY);
+ _rec_str(IFACE_DSEQ_INORDER, recs, iface, data_seq_inorder, IDBM_SHOW,
+ num, _CAN_MODIFY);
+ _rec_str(IFACE_DPDU_INORDER, recs, iface, data_pdu_inorder, IDBM_SHOW,
+ num, _CAN_MODIFY);
+ _rec_uint8(IFACE_ERL, recs, iface, erl, IDBM_SHOW, num, _CAN_MODIFY);
+ _rec_uint32(IFACE_MAX_RECV_DLEN, recs, iface, max_recv_dlength,
+ IDBM_SHOW, num, _CAN_MODIFY);
+ _rec_uint32(IFACE_FIRST_BURST, recs, iface, first_burst_len, IDBM_SHOW,
+ num, _CAN_MODIFY);
+ _rec_uint16(IFACE_MAX_R2T, recs, iface, max_out_r2t, IDBM_SHOW, num,
+ _CAN_MODIFY);
+ _rec_uint32(IFACE_MAX_BURST, recs, iface, max_burst_len, IDBM_SHOW, num,
+ _CAN_MODIFY);
+ _rec_str(IFACE_CHAP_AUTH, recs, iface, chap_auth, IDBM_SHOW, num,
+ _CAN_MODIFY);
+ _rec_str(IFACE_BIDI_CHAP, recs, iface, bidi_chap, IDBM_SHOW, num,
+ _CAN_MODIFY);
+ _rec_str(IFACE_STRICT_LOGIN_COMP, recs, iface, strict_login_comp,
+ IDBM_SHOW, num, _CAN_MODIFY);
+ _rec_str(IFACE_DISCOVERY_AUTH, recs, iface, discovery_auth, IDBM_SHOW,
+ num, _CAN_MODIFY);
+ _rec_str(IFACE_DISCOVERY_LOGOUT, recs, iface, discovery_logout,
+ IDBM_SHOW, num, _CAN_MODIFY);
+}
+
+static void _idbm_recs_print(struct idbm_rec *recs, FILE *f, int show)
+{
+ int i;
+ fprintf(f, "%s\n", ISCSI_BEGIN_REC);
+ for (i = 0; i < MAX_KEYS; i++) {
+ if (recs[i].visible == IDBM_HIDE)
+ continue;
+ if (!show && recs[i].visible == IDBM_MASKED) {
+ if (*(char*)recs[i].data) {
+ fprintf(f, "%s = ********\n", recs[i].name);
+ continue;
+ }
+ /* fall through */
+ }
+
+ if (strlen(recs[i].value))
+ fprintf(f, "%s = %s\n", recs[i].name, recs[i].value);
+ else if (f == stdout)
+ fprintf(f, "%s = <empty>\n", recs[i].name);
+ }
+ fprintf(f, "%s\n", ISCSI_END_REC);
+}
+
+void _idbm_iface_print(struct iscsi_iface *iface, FILE *f)
+{
+ struct idbm_rec *recs = NULL;
+
+ recs = _idbm_recs_alloc();
+ if (recs == NULL)
+ return;
+
+ _idbm_iface_rec_link(iface, recs);
+
+ _idbm_recs_print(recs, f, IDBM_SHOW);
+
+ _idbm_recs_free(recs);
+}
+
+static int _idbm_rec_update_param(struct iscsi_context *ctx,
+ struct idbm_rec *recs, char *name,
+ char *value, int line_number)
+{
+ int rc = LIBISCSI_OK;
+ int i = 0;
+ int passwd_done = 0;
+ char passwd_len[8];
+
+ assert(ctx != NULL);
+ assert(recs != NULL);
+ assert(name != NULL);
+ assert(value != NULL);
+
+setup_passwd_len:
+ for (i = 0; i < MAX_KEYS; ++i) {
+ if (!strcmp(name, recs[i].name)) {
+ _debug(ctx, "updated '%s', '%s' => '%s'", name,
+ recs[i].value, value);
+ /* parse recinfo by type */
+ switch (recs[i].type) {
+ case TYPE_UINT8:
+ if (!recs[i].data)
+ continue;
+
+ *(uint8_t *)recs[i].data =
+ strtoul(value, NULL, 10);
+ goto updated;
+ case TYPE_UINT16:
+ if (!recs[i].data)
+ continue;
+
+ *(uint16_t *)recs[i].data =
+ strtoul(value, NULL, 10);
+ goto updated;
+ case TYPE_UINT32:
+ if (!recs[i].data)
+ continue;
+
+ *(uint32_t *)recs[i].data =
+ strtoul(value, NULL, 10);
+ goto updated;
+ case TYPE_STR:
+ if (!recs[i].data)
+ continue;
+
+ _strncpy((char*)recs[i].data,
+ value, recs[i].data_len);
+ goto updated;
+ default:
+ _error(ctx, "Got unknown data type %d "
+ "for name '%s', value '%s'",
+ recs[i].data, recs[i].name,
+ recs[i].value);
+ rc = LIBISCSI_ERR_BUG;
+ goto out;
+ }
+ if (line_number) {
+ _warn(ctx, "config file line %d contains "
+ "unknown value format '%s' for "
+ "parameter name '%s'",
+ line_number, value, name);
+ } else {
+ _error(ctx, "unknown value format '%s' for "
+ "parameter name '%s'", value, name);
+ rc = LIBISCSI_ERR_INVAL;
+ }
+ goto out;
+ }
+ }
+ _error(ctx, "Unknown parameter name %s", name);
+ rc = LIBISCSI_ERR_INVAL;
+ goto out;
+
+updated:
+ _strncpy((char*)recs[i].value, value, VALUE_MAXVAL);
+
+#define check_password_param(_param) \
+ if (!passwd_done && !strcmp(#_param, name)) { \
+ passwd_done = 1; \
+ name = #_param "_length"; \
+ snprintf(passwd_len, 8, "%d", (int)strlen(value)); \
+ value = passwd_len; \
+ goto setup_passwd_len; \
+ }
+
+ check_password_param(node.session.auth.password);
+ check_password_param(node.session.auth.password_in);
+ check_password_param(discovery.sendtargets.auth.password);
+ check_password_param(discovery.sendtargets.auth.password_in);
+ check_password_param(discovery.slp.auth.password);
+ check_password_param(discovery.slp.auth.password_in);
+ check_password_param(host.auth.password);
+ check_password_param(host.auth.password_in);
+
+out:
+ return rc;
+}
+
+/*
+ * from linux kernel
+ */
+static char *strstrip(char *s)
+{
+ size_t size;
+ char *end;
+
+ size = strlen(s);
+ if (!size)
+ return s;
+
+ end = s + size - 1;
+ while (end >= s && isspace(*end))
+ end--;
+ *(end + 1) = '\0';
+
+ while (*s && isspace(*s))
+ s++;
+
+ return s;
+}
+
+static int _idbm_recs_read(struct iscsi_context *ctx, struct idbm_rec *recs,
+ const char *conf_path)
+{
+ int rc = LIBISCSI_OK;
+ char name[NAME_MAXVAL];
+ char value[VALUE_MAXVAL];
+ char *line = NULL;
+ char *nl = NULL;
+ char buffer[2048];
+ int line_number = 0;
+ int c = 0;
+ int i = 0;
+ FILE *f = NULL;
+ int errno_save = 0;
+ char strerr_buff[_STRERR_BUFF_LEN];
+
+ assert(ctx != NULL);
+ assert(recs != NULL);
+ assert(conf_path != NULL);
+
+ f = fopen(conf_path, "r");
+ errno_save = errno;
+ if (!f) {
+ _error(ctx, "Failed to open %s using read mode: %d %s",
+ conf_path, errno_save,
+ _strerror(errno_save, strerr_buff));
+ rc = LIBISCSI_ERR_IDBM;
+ goto out;
+ }
+
+ _info(ctx, "Parsing iSCSI interface configuration %s", conf_path);
+ /* process the config file */
+ do {
+ line = fgets(buffer, sizeof (buffer), f);
+ line_number++;
+ if (!line)
+ continue;
+ if (strlen(line) == 0)
+ continue;
+
+ nl = line + strlen(line) - 1;
+ if (*nl != '\n') {
+ _warn(ctx, "Config file %s line %d too long.",
+ conf_path, line_number);
+ continue;
+ }
+
+ line = strstrip(line);
+ /* process any non-empty, non-comment lines */
+ if (!*line || *line == '\0' || *line == '\n' || *line == '#')
+ continue;
+
+ /* parse name */
+ i=0; nl = line; *name = 0;
+ while (*nl && !isspace(c = *nl) && *nl != '=') {
+ *(name+i) = *nl; i++; nl++;
+ }
+ if (!*nl) {
+ _warn(ctx, "config file %s line %d do not has value",
+ conf_path, line_number);
+ continue;
+ }
+ *(name+i)=0; nl++;
+ /* skip after-name traling spaces */
+ while (*nl && isspace(c = *nl)) nl++;
+ if (*nl && *nl != '=') {
+ _warn(ctx, "config file %s line %d has not '=' "
+ "separator", conf_path, line_number);
+ continue;
+ }
+ /* skip '=' sepa */
+ nl++;
+ /* skip after-sepa traling spaces */
+ while (*nl && isspace(c = *nl)) nl++;
+ if (!*nl) {
+ _warn(ctx, "config file %s line %d do not has value",
+ conf_path, line_number);
+ continue;
+ }
+ /* parse value */
+ i=0; *value = 0;
+ while (*nl) {
+ *(value+i) = *nl; i++; nl++;
+ }
+ *(value+i) = 0;
+
+ rc = _idbm_rec_update_param(ctx, recs, name, value,
+ line_number);
+ if (rc == LIBISCSI_ERR_INVAL) {
+ _error(ctx, "config file %s invalid.", conf_path);
+ goto out;
+ } else if (rc != LIBISCSI_OK)
+ goto out;
+ } while (line);
+
+out:
+ if (f != NULL)
+ fclose(f);
+ return rc;
+}
+
+int _idbm_iface_get(struct iscsi_context *ctx, const char *iface_name, struct
+ iscsi_iface **iface)
+{
+ int rc = LIBISCSI_OK;
+ char conf_path[PATH_MAX];
+ struct idbm_rec *recs = NULL;
+
+ assert(iface != NULL);
+ assert(ctx != NULL);
+
+ *iface = NULL;
+
+ if (iface_name == NULL)
+ goto out;
+
+ snprintf(conf_path, PATH_MAX, "%s/%s", IFACE_CONFIG_DIR, iface_name);
+
+ *iface = calloc(1, sizeof(struct iscsi_iface));
+ _alloc_null_check(ctx, *iface, rc, out);
+
+ snprintf((*iface)->name, sizeof((*iface)->name)/sizeof(char),
+ "%s", iface_name);
+
+ recs = _idbm_recs_alloc();
+ _alloc_null_check(ctx, recs, rc, out);
+
+ _idbm_iface_rec_link(*iface, recs);
+
+ _good(_idbm_recs_read(ctx, recs, conf_path), rc, out);
+
+ if (! _iface_is_valid(*iface)) {
+ _warn(ctx, "'%s' is not a valid iSCSI interface configuration "
+ "file", conf_path);
+ iscsi_iface_free(*iface);
+ *iface = NULL;
+ /* We still treat this as pass(no error) */
+ }
+
+out:
+ if (rc != LIBISCSI_OK) {
+ iscsi_iface_free(*iface);
+ *iface = NULL;
+ }
+ _idbm_recs_free(recs);
+ return rc;
+}
+struct idbm *_idbm_new(void)
+{
+ return calloc(1, sizeof(struct idbm));
+}
+
+void _idbm_free(struct idbm *db)
+{
+ free(db);
+}
diff --git a/libopeniscsiusr/idbm.h b/libopeniscsiusr/idbm.h
new file mode 100644
index 0000000..31cbe6b
--- /dev/null
+++ b/libopeniscsiusr/idbm.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2017-2018 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Gris Ge <fge@redhat.com>
+ */
+
+#ifndef __ISCSI_OPEN_USR_IDBM_H__
+#define __ISCSI_OPEN_USR_IDBM_H__
+
+#include <stdio.h>
+#include <stdbool.h>
+
+#include "libopeniscsiusr/libopeniscsiusr_common.h"
+
+#define ISCSI_CONFIG_ROOT "/etc/iscsi/"
+#define IFACE_CONFIG_DIR ISCSI_CONFIG_ROOT"ifaces"
+
+struct __DLL_LOCAL idbm;
+
+struct idbm {
+ int refs;
+};
+
+__DLL_LOCAL struct idbm *_idbm_new(void);
+__DLL_LOCAL void _idbm_free(struct idbm *db);
+__DLL_LOCAL int _idbm_lock(struct iscsi_context *ctx);
+__DLL_LOCAL void _idbm_unlock(struct iscsi_context *ctx);
+__DLL_LOCAL void _idbm_iface_print(struct iscsi_iface *iface, FILE *f);
+__DLL_LOCAL int _idbm_iface_get(struct iscsi_context *ctx,
+ const char *iface_name,
+ struct iscsi_iface **iface);
+
+#endif /* End of __ISCSI_OPEN_USR_IDBM_H__ */
diff --git a/libopeniscsiusr/idbm_fields.h b/libopeniscsiusr/idbm_fields.h
new file mode 100644
index 0000000..ce30ad9
--- /dev/null
+++ b/libopeniscsiusr/idbm_fields.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2018 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Gris Ge <fge@redhat.com>
+ */
+
+#ifndef __ISCSI_OPEN_USER_IDBM_FIELDS_H
+#define __ISCSI_OPEN_USER_IDBM_FIELDS_H
+
+/* iface fields */
+#define IFACE_HWADDR "iface.hwaddress"
+#define IFACE_ISCSINAME "iface.iscsi_ifacename"
+#define IFACE_NETNAME "iface.net_ifacename"
+#define IFACE_TRANSPORTNAME "iface.transport_name"
+#define IFACE_INAME "iface.initiatorname"
+#define IFACE_ISID "iface.isid"
+#define IFACE_BOOT_PROTO "iface.bootproto"
+#define IFACE_IPADDR "iface.ipaddress"
+#define IFACE_SUBNET_MASK "iface.subnet_mask"
+#define IFACE_GATEWAY "iface.gateway"
+#define IFACE_PRIMARY_DNS "iface.primary_dns"
+#define IFACE_SEC_DNS "iface.secondary_dns"
+#define IFACE_VLAN_ID "iface.vlan_id"
+#define IFACE_VLAN_PRIORITY "iface.vlan_priority"
+#define IFACE_VLAN_STATE "iface.vlan_state"
+#define IFACE_LINKLOCAL "iface.ipv6_linklocal"
+#define IFACE_ROUTER "iface.ipv6_router"
+#define IFACE_IPV6_AUTOCFG "iface.ipv6_autocfg"
+#define IFACE_LINKLOCAL_AUTOCFG "iface.linklocal_autocfg"
+#define IFACE_ROUTER_AUTOCFG "iface.router_autocfg"
+#define IFACE_STATE "iface.state"
+#define IFACE_NUM "iface.iface_num"
+#define IFACE_MTU "iface.mtu"
+#define IFACE_PORT "iface.port"
+#define IFACE_DELAYED_ACK "iface.delayed_ack"
+#define IFACE_TCP_NAGLE "iface.tcp_nagle"
+#define IFACE_TCP_WSF_STATE "iface.tcp_wsf_state"
+#define IFACE_TCP_WSF "iface.tcp_wsf"
+#define IFACE_TCP_TIMER_SCALE "iface.tcp_timer_scale"
+#define IFACE_TCP_TIMESTAMP "iface.tcp_timestamp"
+#define IFACE_DHCP_DNS "iface.dhcp_dns"
+#define IFACE_DHCP_SLP_DA "iface.dhcp_slp_da"
+#define IFACE_TOS_STATE "iface.tos_state"
+#define IFACE_TOS "iface.tos"
+#define IFACE_GRAT_ARP "iface.gratuitous_arp"
+#define IFACE_DHCP_ALT_CID "iface.dhcp_alt_client_id_state"
+#define IFACE_DHCP_ALT_CID_STR "iface.dhcp_alt_client_id"
+#define IFACE_DHCP_REQ_VID "iface.dhcp_req_vendor_id_state"
+#define IFACE_DHCP_VID "iface.dhcp_vendor_id_state"
+#define IFACE_DHCP_VID_STR "iface.dhcp_vendor_id"
+#define IFACE_DHCP_LEARN_IQN "iface.dhcp_learn_iqn"
+#define IFACE_FRAGMENTATION "iface.fragmentation"
+#define IFACE_IN_FORWARD "iface.incoming_forwarding"
+#define IFACE_TTL "iface.ttl"
+#define IFACE_GRAT_NEIGHBOR_ADV "iface.gratuitous_neighbor_adv"
+#define IFACE_REDIRECT "iface.redirect"
+#define IFACE_IGNORE_ICMP_ECHO_REQ "iface.ignore_icmp_echo_request"
+#define IFACE_MLD "iface.mld"
+#define IFACE_FLOW_LABEL "iface.flow_label"
+#define IFACE_TRAFFIC_CLASS "iface.traffic_class"
+#define IFACE_HOP_LIMIT "iface.hop_limit"
+#define IFACE_ND_REACHABLE_TMO "iface.nd_reachable_tmo"
+#define IFACE_ND_REXMIT_TIME "iface.nd_rexmit_time"
+#define IFACE_ND_STALE_TMO "iface.nd_stale_tmo"
+#define IFACE_DUP_ADDR_DETECT_CNT "iface.dup_addr_detect_cnt"
+#define IFACE_RTR_ADV_LINK_MTU "iface.router_adv_link_mtu"
+#define IFACE_DEF_TMF_TMO "iface.def_task_mgmt_timeout"
+#define IFACE_HDRDGST "iface.header_digest"
+#define IFACE_DATADGST "iface.data_digest"
+#define IFACE_IMM_DATA "iface.immediate_data"
+#define IFACE_INITIAL_R2T "iface.initial_r2t"
+#define IFACE_DSEQ_INORDER "iface.data_seq_inorder"
+#define IFACE_DPDU_INORDER "iface.data_pdu_inorder"
+#define IFACE_ERL "iface.erl"
+#define IFACE_MAX_RECV_DLEN "iface.max_receive_data_len"
+#define IFACE_FIRST_BURST "iface.first_burst_len"
+#define IFACE_MAX_R2T "iface.max_outstanding_r2t"
+#define IFACE_MAX_BURST "iface.max_burst_len"
+#define IFACE_CHAP_AUTH "iface.chap_auth"
+#define IFACE_BIDI_CHAP "iface.bidi_chap"
+#define IFACE_STRICT_LOGIN_COMP "iface.strict_login_compliance"
+#define IFACE_DISCOVERY_AUTH "iface.discovery_auth"
+#define IFACE_DISCOVERY_LOGOUT "iface.discovery_logout"
+
+#endif /* End of __ISCSI_OPEN_USER_IDBM_FIELDS_H */
diff --git a/libopeniscsiusr/iface.c b/libopeniscsiusr/iface.c
index 79898df..5cccbf2 100644
--- a/libopeniscsiusr/iface.c
+++ b/libopeniscsiusr/iface.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 Red Hat, Inc.
+ * Copyright (C) 2017-2018 Red Hat, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -27,117 +27,61 @@
#include <dirent.h>
#include <string.h>
#include <net/if.h>
-#include <netdb.h>
#include <assert.h>
#include <inttypes.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <stdbool.h>
+#ifdef USE_KMOD
+#include <libkmod.h>
+#endif
#include "libopeniscsiusr/libopeniscsiusr.h"
#include "misc.h"
#include "sysfs.h"
#include "iface.h"
-
-#define ISCSI_MAX_IFACE_LEN 65
-#define ISCSI_TRANSPORT_NAME_MAXLEN 16
-#define ISCSI_MAX_STR_LEN 80
-#define ISCSI_HWADDRESS_BUF_SIZE 18
-#define TARGET_NAME_MAXLEN 255
-/* ^ TODO(Gris Ge): Above 5 constants are copy from usr/config.h, need to
- * verify them in linux kernel code
- */
+#include "context.h"
+#include "idbm.h"
#define DEFAULT_IFACENAME "default"
#define DEFAULT_NETDEV "default"
#define DEFAULT_IPADDRESS "default"
#define DEFAULT_HWADDRESS "default"
+#define ISCSIUIO_PATH "/sbin/iscsiuio"
+#define _IFACE_DUMP_SIZE 8192
+struct _iscsi_net_drv {
+ const char *net_driver_name; // Ethernet driver.
+ const char *iscsi_driver_name; // iSCSI offload driver.
+ const char *transport_name; // iSCSI transport name.
+};
+
+static struct _iscsi_net_drv _ISCSI_NET_DRVS[] = {
+ {"cxgb3", "cxgb3i", "cxgb3i"},
+ {"cxgb4", "cxgb4i", "cxgb4i"},
+ {"bnx2", "bnx2i" , "bnx2i"},
+ {"bnx2x", "bnx2i", "bnx2i"},
+};
-/* Just copy from `struct iface_rec` from usr/config.h */
-struct iscsi_iface {
- /* iscsi iface record name */
- char name[ISCSI_MAX_IFACE_LEN];
- uint32_t iface_num;
- /* network layer iface name (eth0) */
- char netdev[IFNAMSIZ];
- char ipaddress[NI_MAXHOST];
- char subnet_mask[NI_MAXHOST];
- char gateway[NI_MAXHOST];
- char bootproto[ISCSI_MAX_STR_LEN];
- char ipv6_linklocal[NI_MAXHOST];
- char ipv6_router[NI_MAXHOST];
- char ipv6_autocfg[NI_MAXHOST];
- char linklocal_autocfg[NI_MAXHOST];
- char router_autocfg[NI_MAXHOST];
- uint16_t vlan_id;
- uint8_t vlan_priority;
- char vlan_state[ISCSI_MAX_STR_LEN];
- char state[ISCSI_MAX_STR_LEN]; /* 0 = disable,
- * 1 = enable */
- uint16_t mtu;
- uint16_t port;
- char delayed_ack[ISCSI_MAX_STR_LEN];
- char nagle[ISCSI_MAX_STR_LEN];
- char tcp_wsf_state[ISCSI_MAX_STR_LEN];
- uint8_t tcp_wsf;
- uint8_t tcp_timer_scale;
- char tcp_timestamp[ISCSI_MAX_STR_LEN];
- char dhcp_dns[ISCSI_MAX_STR_LEN];
- char dhcp_slp_da[ISCSI_MAX_STR_LEN];
- char tos_state[ISCSI_MAX_STR_LEN];
- uint8_t tos;
- char gratuitous_arp[ISCSI_MAX_STR_LEN];
- char dhcp_alt_client_id_state[ISCSI_MAX_STR_LEN];
- char dhcp_alt_client_id[ISCSI_MAX_STR_LEN];
- char dhcp_req_vendor_id_state[ISCSI_MAX_STR_LEN];
- char dhcp_vendor_id_state[ISCSI_MAX_STR_LEN];
- char dhcp_vendor_id[ISCSI_MAX_STR_LEN];
- char dhcp_learn_iqn[ISCSI_MAX_STR_LEN];
- char fragmentation[ISCSI_MAX_STR_LEN];
- char incoming_forwarding[ISCSI_MAX_STR_LEN];
- uint8_t ttl;
- char gratuitous_neighbor_adv[ISCSI_MAX_STR_LEN];
- char redirect[ISCSI_MAX_STR_LEN];
- char mld[ISCSI_MAX_STR_LEN];
- uint32_t flow_label;
- uint32_t traffic_class;
- uint8_t hop_limit;
- uint32_t nd_reachable_tmo;
- uint32_t nd_rexmit_time;
- uint32_t nd_stale_tmo;
- uint8_t dup_addr_detect_cnt;
- uint32_t router_adv_link_mtu;
- uint16_t def_task_mgmt_tmo;
- char header_digest[ISCSI_MAX_STR_LEN];
- char data_digest[ISCSI_MAX_STR_LEN];
- char immediate_data[ISCSI_MAX_STR_LEN];
- char initial_r2t[ISCSI_MAX_STR_LEN];
- char data_seq_inorder[ISCSI_MAX_STR_LEN];
- char data_pdu_inorder[ISCSI_MAX_STR_LEN];
- uint8_t erl;
- uint32_t max_recv_dlength;
- uint32_t first_burst_len;
- uint16_t max_out_r2t;
- uint32_t max_burst_len;
- char chap_auth[ISCSI_MAX_STR_LEN];
- char bidi_chap[ISCSI_MAX_STR_LEN];
- char strict_login_comp[ISCSI_MAX_STR_LEN];
- char discovery_auth[ISCSI_MAX_STR_LEN];
- char discovery_logout[ISCSI_MAX_STR_LEN];
- char port_state[ISCSI_MAX_STR_LEN];
- char port_speed[ISCSI_MAX_STR_LEN];
- /*
- * TODO: we may have to make this bigger and interconnect
- * specific for infiniband
- */
- char hwaddress[ISCSI_HWADDRESS_BUF_SIZE];
- char transport_name[ISCSI_TRANSPORT_NAME_MAXLEN];
- /*
- * This is only used for boot now, but the iser guys
- * can use this for their virtualization idea.
- */
- char alias[TARGET_NAME_MAXLEN + 1];
- char iname[TARGET_NAME_MAXLEN + 1];
+const struct iscsi_iface _DEFAULT_IFACES[] = {
+ {
+ .name = "default",
+ .transport_name = "tcp",
+ },
+ {
+ .name = "iser",
+ .transport_name = "iser",
+ },
};
+static int _load_kernel_module(struct iscsi_context *ctx, const char *drv_name);
+static int _iface_conf_write(struct iscsi_context *ctx,
+ struct iscsi_iface *iface);
+static int _fill_hw_iface_from_sys(struct iscsi_context *ctx,
+ struct iscsi_iface *iface,
+ const char *iface_kern_id);
+
_iscsi_getter_func_gen(iscsi_iface, hwaddress, const char *);
_iscsi_getter_func_gen(iscsi_iface, transport_name, const char *);
_iscsi_getter_func_gen(iscsi_iface, ipaddress, const char *);
@@ -147,36 +91,49 @@ _iscsi_getter_func_gen(iscsi_iface, port_state, const char *);
_iscsi_getter_func_gen(iscsi_iface, port_speed, const char *);
_iscsi_getter_func_gen(iscsi_iface, name, const char *);
-int _iscsi_iface_get(struct iscsi_context *ctx, uint32_t host_id, uint32_t sid,
- const char *iface_kern_id, struct iscsi_iface **iface)
+int _iscsi_iface_get_from_sysfs(struct iscsi_context *ctx, uint32_t host_id,
+ uint32_t sid, struct iscsi_iface **iface)
{
int rc = LIBISCSI_OK;
- char sysfs_se_dir_path[PATH_MAX];
- char sysfs_sh_dir_path[PATH_MAX];
- char sysfs_scsi_host_dir_path[PATH_MAX];
- char sysfs_iface_dir_path[PATH_MAX];
+ char *sysfs_se_dir_path = NULL;
+ char *sysfs_sh_dir_path = NULL;
+ char *sysfs_scsi_host_dir_path = NULL;
+ char *sysfs_iface_dir_path = NULL;
+ char iface_kern_id[PATH_MAX];
char proc_name[ISCSI_TRANSPORT_NAME_MAXLEN];
+ struct iscsi_iface **ifaces = NULL;
+ uint32_t iface_count = 0;
+ uint32_t i = 0;
+ struct iscsi_iface *tmp_iface = NULL;
+ bool bound_by_hwaddr = false;
+ bool bound_by_netdev = false;
+ bool matched = false;
assert(ctx != NULL);
assert(host_id != 0);
- assert(sid != 0);
- /* TODO(Gris Ge): Handle when sid == 0(ignored) */
assert(iface != NULL);
*iface = NULL;
- *iface = (struct iscsi_iface *) malloc(sizeof(struct iscsi_iface));
- _alloc_null_check(ctx, *iface, rc, out);
+ if (sid != 0) {
+ sysfs_se_dir_path = malloc(PATH_MAX);
+ _alloc_null_check(ctx, sysfs_se_dir_path, rc, out);
+ snprintf(sysfs_se_dir_path, PATH_MAX, "%s/session%" PRIu32,
+ _ISCSI_SYS_SESSION_DIR, sid);
+ }
- snprintf(sysfs_se_dir_path, PATH_MAX, "%s/session%" PRIu32,
- _ISCSI_SYS_SESSION_DIR, sid);
+ sysfs_sh_dir_path = malloc(PATH_MAX);
+ _alloc_null_check(ctx, sysfs_sh_dir_path, rc, out);
snprintf(sysfs_sh_dir_path, PATH_MAX, "%s/host%" PRIu32,
_ISCSI_SYS_HOST_DIR, host_id);
+
+ sysfs_scsi_host_dir_path = malloc(PATH_MAX);
+ _alloc_null_check(ctx, sysfs_scsi_host_dir_path, rc, out);
snprintf(sysfs_scsi_host_dir_path, PATH_MAX, "%s/host%" PRIu32,
_SCSI_SYS_HOST_DIR, host_id);
- if (iface_kern_id != NULL)
- snprintf(sysfs_iface_dir_path, PATH_MAX, "%s/%s",
- _ISCSI_SYS_IFACE_DIR, iface_kern_id);
+
+ *iface = (struct iscsi_iface *) calloc(1, sizeof(struct iscsi_iface));
+ _alloc_null_check(ctx, *iface, rc, out);
_good(_sysfs_prop_get_str(ctx, sysfs_scsi_host_dir_path, "proc_name",
proc_name, sizeof(proc_name) / sizeof(char),
@@ -184,10 +141,10 @@ int _iscsi_iface_get(struct iscsi_context *ctx, uint32_t host_id, uint32_t sid,
rc, out);
if (strncmp(proc_name, "iscsi_", strlen("iscsi_")) == 0)
- strncpy((*iface)->transport_name, proc_name + strlen("iscsi_"),
- sizeof((*iface)->transport_name) / sizeof(char));
+ _strncpy((*iface)->transport_name, proc_name + strlen("iscsi_"),
+ sizeof((*iface)->transport_name) / sizeof(char));
else
- strncpy((*iface)->transport_name, proc_name,
+ _strncpy((*iface)->transport_name, proc_name,
sizeof((*iface)->transport_name) / sizeof(char));
_good(_sysfs_prop_get_str(ctx, sysfs_sh_dir_path, "hwaddress",
@@ -195,41 +152,27 @@ int _iscsi_iface_get(struct iscsi_context *ctx, uint32_t host_id, uint32_t sid,
sizeof((*iface)->hwaddress) / sizeof(char),
DEFAULT_HWADDRESS),
rc, out);
-
- if (iface_kern_id != NULL)
- _good(_sysfs_prop_get_str(ctx, sysfs_iface_dir_path,
- "ipaddress",
- (*iface)->ipaddress,
- sizeof((*iface)->ipaddress) /
- sizeof(char), DEFAULT_IPADDRESS),
- rc, out);
- else
- _good(_sysfs_prop_get_str(ctx, sysfs_sh_dir_path, "ipaddress",
- (*iface)->ipaddress,
- sizeof((*iface)->ipaddress) /
- sizeof(char), DEFAULT_IPADDRESS),
- rc, out);
+ if (strcmp((*iface)->hwaddress, DEFAULT_HWADDRESS) != 0)
+ bound_by_hwaddr = true;
_good(_sysfs_prop_get_str(ctx, sysfs_sh_dir_path, "netdev",
(*iface)->netdev,
sizeof((*iface)->netdev) / sizeof(char),
DEFAULT_NETDEV),
rc, out);
+ if (strcmp((*iface)->netdev, DEFAULT_NETDEV) != 0)
+ bound_by_netdev = true;
- if (_sysfs_prop_get_str(NULL /* Ignore error */, sysfs_se_dir_path,
- "initiatorname",
- (*iface)->iname,
- sizeof((*iface)->iname) / sizeof(char),
- "")
- != LIBISCSI_OK) {
- _debug(ctx, "Failed to read initiatorname from %s folder",
- sysfs_se_dir_path);
+ if (sysfs_se_dir_path)
+ _sysfs_prop_get_str(ctx, sysfs_se_dir_path, "initiatorname",
+ (*iface)->iname,
+ sizeof((*iface)->iname) / sizeof(char), "");
+ if (strcmp((*iface)->iname, "") == 0)
_good(_sysfs_prop_get_str(ctx, sysfs_sh_dir_path,
"initiatorname", (*iface)->iname,
sizeof((*iface)->iname) /
sizeof(char), ""),
rc, out);
- }
_good(_sysfs_prop_get_str(ctx, sysfs_sh_dir_path, "port_state",
(*iface)->port_state,
@@ -238,8 +181,8 @@ int _iscsi_iface_get(struct iscsi_context *ctx, uint32_t host_id, uint32_t sid,
rc, out);
if (strcmp((*iface)->port_state, "Unknown!") == 0)
- strncpy((*iface)->port_state, "unknown",
- sizeof((*iface)->port_state) / sizeof(char));
+ _strncpy((*iface)->port_state, "unknown",
+ sizeof((*iface)->port_state) / sizeof(char));
_good(_sysfs_prop_get_str(ctx, sysfs_sh_dir_path, "port_speed",
(*iface)->port_speed,
@@ -248,42 +191,709 @@ int _iscsi_iface_get(struct iscsi_context *ctx, uint32_t host_id, uint32_t sid,
rc, out);
if (strncmp((*iface)->port_speed, "Unknown", strlen("Unknown")) == 0)
- strncpy((*iface)->port_speed, "unknown",
- sizeof((*iface)->port_speed) / sizeof(char));
-
- _good(_sysfs_prop_get_str(NULL /* Ignore error */, sysfs_se_dir_path,
- "ifacename",
- (*iface)->name,
- sizeof((*iface)->name)/sizeof(char),
- ""),
- rc, out);
+ _strncpy((*iface)->port_speed, "unknown",
+ sizeof((*iface)->port_speed) / sizeof(char));
+
+ if (sysfs_se_dir_path != NULL)
+ _sysfs_prop_get_str(ctx, sysfs_se_dir_path, "ifacename",
+ (*iface)->name,
+ sizeof((*iface)->name)/sizeof(char), "");
+
+ rc = _iscsi_iface_kern_id_of_host_id(ctx, host_id, iface_kern_id);
+ if (rc == LIBISCSI_OK) {
+ _good(_fill_hw_iface_from_sys(ctx, *iface, iface_kern_id),
+ rc, out);
+ } else {
+ _good(_sysfs_prop_get_str(ctx, sysfs_sh_dir_path, "ipaddress",
+ (*iface)->ipaddress,
+ sizeof((*iface)->ipaddress) /
+ sizeof(char), DEFAULT_IPADDRESS),
+ rc, out);
+ /* bnx2i does not create
+ * /sys/class/iscsi_iface/<iface_kernl_id>
+ * We need to use transport_name.hwaddress as iface name.
+ */
+ _debug(ctx, "HAHA: hwaddress %s", (*iface)->hwaddress);
+ if (bound_by_hwaddr)
+ snprintf((*iface)->name,
+ sizeof((*iface)->name)/sizeof(char),
+ "%s.%s", (*iface)->transport_name,
+ (*iface)->hwaddress);
+ }
+
if (strcmp((*iface)->name, "") == 0) {
- _debug(ctx, "Failed to query ifacename from %s folder",
- sysfs_se_dir_path);
/*
- * if the ifacename file is not there then we are
- * using a older kernel and can try to find the
- * binding by the net info which was used on these
- * older kernels.
+ * Before 2.0.870, we only could bind by netdeivce or hwaddress,
+ * so we did a simple reverse lookup to go from sysfs info to
+ * the iface name. After 2.0.870 we added a lot of options to
+ * the iface binding so we added the ifacename to the kernel.
+ *
+ * Below codes are for older kernels that do not export the
+ * ifacename. If the user was doing iscsi_tcp session binding
+ * we will find the iface by matching net info.
*/
- /*TODO(Gris Ge): need to parse /etc/iscsi/ifaces/<iface_name>
- * files to find a match. I will add the code later when
- * we expose more defiled information on iscsi_iface.
- */
- strncpy((*iface)->name, DEFAULT_IFACENAME,
- sizeof((*iface)->name) / sizeof(char));
+ _good(iscsi_ifaces_get(ctx, &ifaces, &iface_count), rc, out);
+
+ for (i = 0; i < iface_count; ++i) {
+ tmp_iface = ifaces[i];
+ if ((bound_by_hwaddr == true) &&
+ (strcmp(tmp_iface->hwaddress, (*iface)->hwaddress)
+ == 0)) {
+ _strncpy((*iface)->name, tmp_iface->name,
+ sizeof((*iface)->name)/sizeof(char));
+ matched = true;
+ break;
+ }
+ if ((bound_by_netdev == true) &&
+ (strcmp(tmp_iface->netdev, (*iface)->netdev)
+ == 0)) {
+ _strncpy((*iface)->name, tmp_iface->name,
+ sizeof((*iface)->name)/sizeof(char));
+ matched = true;
+ break;
+ }
+ }
+ if (!matched)
+ _strncpy((*iface)->name, DEFAULT_IFACENAME,
+ sizeof((*iface)->name) / sizeof(char));
}
out:
if (rc != LIBISCSI_OK) {
- _iscsi_iface_free(*iface);
+ iscsi_iface_free(*iface);
*iface = NULL;
}
+ free(sysfs_se_dir_path);
+ free(sysfs_sh_dir_path);
+ free(sysfs_scsi_host_dir_path);
+ free(sysfs_iface_dir_path);
+ iscsi_ifaces_free(ifaces, iface_count);
+ return rc;
+}
+
+int iscsi_default_iface_setup(struct iscsi_context *ctx)
+{
+ int rc = LIBISCSI_OK;
+ char strerr_buff[_STRERR_BUFF_LEN];
+ int errno_save = 0;
+ struct _eth_if **eifs = NULL;
+ uint32_t eif_count = 0;
+ uint32_t i = 0;
+ size_t j = 0;
+ struct _iscsi_net_drv *ind = NULL;
+ uint32_t *hids = NULL;
+ uint32_t hid_count = 0;
+ struct iscsi_iface *iface = NULL;
+ char path[PATH_MAX];
+
+ assert(ctx != NULL);
+
+ _good(_idbm_lock(ctx), rc, out);
+
+ if ((access(IFACE_CONFIG_DIR, F_OK) != 0) &&
+ (mkdir(IFACE_CONFIG_DIR, 0660) != 0)) {
+ errno_save = errno;
+ _idbm_unlock(ctx);
+ _error(ctx, "Could not make %s folder(%d %s). "
+ "HW/OFFLOAD iscsi may not be supported.",
+ IFACE_CONFIG_DIR, errno_save,
+ _strerror(errno_save, strerr_buff));
+ if (errno_save == EACCES)
+ return LIBISCSI_ERR_ACCESS;
+ return LIBISCSI_ERR_BUG;
+ }
+ _idbm_unlock(ctx);
+
+ /* Load kernel driver for iSCSI offload cards, like cxgb3i */
+ _good(_eth_ifs_get(ctx, &eifs, &eif_count), rc, out);
+
+ for (i = 0; i < eif_count; ++i) {
+ for (j = 0;
+ j < sizeof(_ISCSI_NET_DRVS)/sizeof(struct _iscsi_net_drv);
+ ++j) {
+ ind = &(_ISCSI_NET_DRVS[j]);
+ if ((ind->net_driver_name == NULL) ||
+ (strcmp(eifs[i]->driver_name,
+ ind->net_driver_name) != 0))
+ continue;
+ /*
+ * iSCSI hardware offload for bnx2{,x} is only supported
+ * if the iscsiuio executable is available.
+ */
+ if ((strcmp(eifs[i]->driver_name, "bnx2x") == 0) ||
+ (strcmp(eifs[i]->driver_name, "bnx2") == 0)) {
+ if (access(ISCSIUIO_PATH, F_OK) != 0) {
+ _debug(ctx, "iSCSI offload on %s(%s) "
+ "via %s is not supported due to "
+ "missing %s", eifs[i]->if_name,
+ eifs[i]->driver_name,
+ ind->iscsi_driver_name,
+ ISCSIUIO_PATH);
+ continue;
+ }
+ }
+
+ if (_iscsi_transport_is_loaded(ind->transport_name))
+ continue;
+
+ _debug(ctx, "Loading kernel module %s for iSCSI "
+ "offload on %s(%s)", ind->iscsi_driver_name,
+ eifs[i]->if_name, eifs[i]->driver_name);
+ _good(_load_kernel_module(ctx, ind->iscsi_driver_name),
+ rc, out);
+ }
+ }
+
+ _good(_iscsi_hids_get(ctx, &hids, &hid_count), rc, out);
+ for (i = 0; i < hid_count; ++i) {
+ /* Create /etc/iscsi/ifaces/<iface_name> file if not found
+ */
+ _good(_iscsi_iface_get_from_sysfs(ctx, hids[i], 0, &iface),
+ rc, out);
+ if ( ! iscsi_is_default_iface(iface)) {
+ snprintf(path, PATH_MAX, "%s/%s", IFACE_CONFIG_DIR,
+ iface->name);
+ if (access(path, F_OK) != 0)
+ rc = _iface_conf_write(ctx, iface);
+ }
+ iscsi_iface_free(iface);
+ if (rc != LIBISCSI_OK)
+ goto out;
+ }
+
+out:
+ _eth_ifs_free(eifs, eif_count);
+ free(hids);
+ return rc;
+}
+
+static int _load_kernel_module(struct iscsi_context *ctx, const char *drv_name)
+{
+#ifdef USE_KMOD
+ struct kmod_ctx *kctx = NULL;
+ struct kmod_module *mod = NULL;
+ int rc = LIBISCSI_OK;
+
+ kctx = kmod_new(NULL, NULL);
+ _alloc_null_check(ctx, kctx, rc, out);
+
+ kmod_load_resources(kctx);
+
+ if (kmod_module_new_from_name(kctx, drv_name, &mod)) {
+ _error(ctx, "Failed to load module %s.", drv_name);
+ rc = LIBISCSI_ERR_TRANS_NOT_FOUND;
+ goto out;
+ }
+
+ if (kmod_module_probe_insert_module(mod, KMOD_PROBE_APPLY_BLACKLIST,
+ NULL, NULL, NULL, NULL)) {
+ _error(ctx, "Could not insert module %s. Kmod error %d",
+ drv_name, rc);
+ rc = LIBISCSI_ERR_TRANS_NOT_FOUND;
+ }
+ kmod_module_unref(mod);
+
+out:
+ if (kctx != NULL)
+ kmod_unref(kctx);
+ return rc;
+
+#else
+ char *cmdline[4];
+ pid_t pid = 0;
+ char strerr_buff[_STRERR_BUFF_LEN];
+ int errno_save = 0;
+
+ cmdline[0] = "/sbin/modprobe";
+ cmdline[1] = "-qb";
+ cmdline[2] = (char *) drv_name;
+ cmdline[3] = NULL;
+
+ pid = fork();
+ if (pid == 0) {
+ if (execv("/sbin/modprobe", cmdline) < 0) {
+ errno_save = errno;
+ _error(ctx, "Failed to load module %s, error %d: %s",
+ drv_name, errno_save,
+ _strerror(errno_save, strerr_buff));
+ exit(-errno_save);
+ }
+ exit(0);
+ } else if (pid < 0) {
+ _error(ctx, "Failed to fork process to load module %s: %s",
+ drv_name, _strerror(errno_save, strerr_buff));
+ return LIBISCSI_ERR_TRANS_NOT_FOUND;
+ }
+
+ if (waitpid(pid, NULL, 0) < 0) {
+ _error(ctx, "Failed to load module %s", drv_name);
+ return LIBISCSI_ERR_TRANS_NOT_FOUND;
+ }
+
+ return LIBISCSI_OK;
+#endif
+}
+
+static int _iface_conf_write(struct iscsi_context *ctx,
+ struct iscsi_iface *iface)
+{
+ char conf_path[PATH_MAX];
+ char strerr_buff[_STRERR_BUFF_LEN];
+ int errno_save = 0;
+ FILE *f = NULL;
+ int rc = 0;
+
+ if (iscsi_is_default_iface(iface)) {
+ _error(ctx, "iface %s is not a special interface and "
+ "is not stored in %s", iface->name, IFACE_CONFIG_DIR);
+ return LIBISCSI_ERR_INVAL;
+ }
+
+ _good(_idbm_lock(ctx), rc, out);
+
+ snprintf(conf_path, PATH_MAX, "%s/%s", IFACE_CONFIG_DIR, iface->name);
+ _debug(ctx, "Creating iSCSI interface configuration file '%s' "
+ "using kernel information", conf_path);
+ f = fopen(conf_path, "w");
+ errno_save = errno;
+ if (!f) {
+ _error(ctx, "Failed to open %s using write mode: %d %s",
+ conf_path, errno_save,
+ _strerror(errno_save, strerr_buff));
+ rc = LIBISCSI_ERR_IDBM;
+ goto out;
+ }
+
+ _idbm_iface_print(iface, f);
+
+ _idbm_unlock(ctx);
+
+out:
+ if (f != NULL)
+ fclose(f);
+ return rc;
+}
+
+// mimic of iscsi_sysfs_read_iface() in iscsi_sysfs.c.
+static int _fill_hw_iface_from_sys(struct iscsi_context *ctx,
+ struct iscsi_iface *iface,
+ const char *iface_kern_id)
+{
+ int rc = LIBISCSI_OK;
+ char *sysfs_iface_dir_path = NULL;
+ uint32_t tmp_host_no = 0;
+ uint32_t iface_num = 0;
+ int iface_type = 0;
+
+
+ assert(ctx != NULL);
+ assert(iface != NULL);
+ assert(iface_kern_id != NULL);
+
+ sysfs_iface_dir_path = malloc(PATH_MAX);
+ _alloc_null_check(ctx, sysfs_iface_dir_path, rc, out);
+ snprintf(sysfs_iface_dir_path, PATH_MAX, "%s/%s",
+ _ISCSI_SYS_IFACE_DIR, iface_kern_id);
+
+ _good(_sysfs_prop_get_str(ctx, sysfs_iface_dir_path,
+ "ipaddress",
+ iface->ipaddress,
+ sizeof(iface->ipaddress) /
+ sizeof(char), DEFAULT_IPADDRESS),
+ rc, out);
+
+ if (strncmp(iface_kern_id, "ipv4", strlen("ipv4")) == 0) {
+ iface->is_ipv6 = false;
+ _sysfs_prop_get_str(ctx, sysfs_iface_dir_path,
+ "bootproto", iface->bootproto,
+ sizeof(iface->bootproto) / sizeof(char),
+ "");
+ _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, "gateway",
+ iface->gateway,
+ sizeof(iface->gateway) / sizeof(char),
+ "");
+ _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, "subnet",
+ iface->subnet_mask,
+ sizeof(iface->subnet_mask) / sizeof(char),
+ "");
+ _sysfs_prop_get_str(ctx, sysfs_iface_dir_path,
+ "dhcp_alt_client_id_en",
+ iface->dhcp_alt_client_id,
+ sizeof(iface->dhcp_alt_client_id) /
+ sizeof(char),
+ "");
+ _sysfs_prop_get_str(ctx, sysfs_iface_dir_path,
+ "dhcp_alt_client_id",
+ iface->dhcp_alt_client_id,
+ sizeof(iface->dhcp_alt_client_id) /
+ sizeof(char),
+ "");
+ _sysfs_prop_get_str(ctx, sysfs_iface_dir_path,
+ "dhcp_dns_address_en", iface->dhcp_dns,
+ sizeof(iface->dhcp_dns) / sizeof(char), "");
+ _sysfs_prop_get_str(ctx, sysfs_iface_dir_path,
+ "dhcp_learn_iqn_en", iface->dhcp_learn_iqn,
+ sizeof(iface->dhcp_learn_iqn) /
+ sizeof(char), "");
+ _sysfs_prop_get_str(ctx, sysfs_iface_dir_path,
+ "dhcp_req_vendor_id_en",
+ iface->dhcp_req_vendor_id_state,
+ sizeof(iface->dhcp_req_vendor_id_state) /
+ sizeof(char), "");
+ _sysfs_prop_get_str(ctx, sysfs_iface_dir_path,
+ "dhcp_use_vendor_id_en",
+ iface->dhcp_vendor_id_state,
+ sizeof(iface->dhcp_vendor_id_state) /
+ sizeof(char), "");
+ _sysfs_prop_get_str(ctx, sysfs_iface_dir_path,
+ "dhcp_vendor_id", iface->dhcp_vendor_id,
+ sizeof(iface->dhcp_vendor_id) /
+ sizeof(char), "");
+ _sysfs_prop_get_str(ctx, sysfs_iface_dir_path,
+ "dhcp_slp_da_info_en", iface->dhcp_slp_da,
+ sizeof(iface->dhcp_slp_da) / sizeof(char),
+ "");
+ _sysfs_prop_get_str(ctx, sysfs_iface_dir_path,
+ "fragment_disable", iface->fragmentation,
+ sizeof(iface->fragmentation) / sizeof(char),
+ "");
+ _sysfs_prop_get_str(ctx, sysfs_iface_dir_path,
+ "grat_arp_en", iface->gratuitous_arp,
+ sizeof(iface->gratuitous_arp) /
+ sizeof(char), "");
+ _sysfs_prop_get_str(ctx, sysfs_iface_dir_path,
+ "incoming_forwarding_en",
+ iface->incoming_forwarding,
+ sizeof(iface->incoming_forwarding) /
+ sizeof(char), "");
+ _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, "tos_en",
+ iface->tos_state, sizeof(iface->tos_state) /
+ sizeof(char), "");
+ _sysfs_prop_get_u8(ctx, sysfs_iface_dir_path, "tos",
+ &iface->tos, 0, true);
+ _sysfs_prop_get_u8(ctx, sysfs_iface_dir_path, "ttl",
+ &iface->ttl, 0, true);
+ } else {
+ iface->is_ipv6 = true;
+ _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, "ipaddr_autocfg",
+ iface->ipv6_autocfg,
+ sizeof(iface->ipv6_autocfg) / sizeof(char),
+ "");
+ _sysfs_prop_get_str(ctx, sysfs_iface_dir_path,
+ "link_local_addr", iface->ipv6_linklocal,
+ sizeof(iface->ipv6_linklocal) /
+ sizeof(char), "");
+ _sysfs_prop_get_str(ctx, sysfs_iface_dir_path,
+ "link_local_autocfg",
+ iface->linklocal_autocfg,
+ sizeof(iface->linklocal_autocfg) /
+ sizeof(char), "");
+ _sysfs_prop_get_str(ctx, sysfs_iface_dir_path,
+ "router_addr", iface->ipv6_router,
+ sizeof(iface->ipv6_router) / sizeof(char),
+ "");
+ _sysfs_prop_get_str(ctx, sysfs_iface_dir_path,
+ "router_state", iface->router_autocfg,
+ sizeof(iface->router_autocfg) /
+ sizeof(char), "");
+ _sysfs_prop_get_str(ctx, sysfs_iface_dir_path,
+ "grat_neighbor_adv_en",
+ iface->gratuitous_neighbor_adv,
+ sizeof(iface->gratuitous_neighbor_adv) /
+ sizeof(char), "");
+ _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, "mld_en",
+ iface->mld, sizeof(iface->mld) /
+ sizeof(char), "");
+ _sysfs_prop_get_u8(ctx, sysfs_iface_dir_path,
+ "dup_addr_detect_cnt",
+ &iface->dup_addr_detect_cnt, 0, true);
+ _sysfs_prop_get_u8(ctx, sysfs_iface_dir_path, "hop_limit",
+ &iface->hop_limit, 0, true);
+ _sysfs_prop_get_u32(ctx, sysfs_iface_dir_path,
+ "flow_label", &iface->flow_label, 0, true);
+ _sysfs_prop_get_u32(ctx, sysfs_iface_dir_path,
+ "nd_reachable_tmo",
+ &iface->nd_reachable_tmo, 0, true);
+ _sysfs_prop_get_u32(ctx, sysfs_iface_dir_path, "nd_rexmit_time",
+ &iface->nd_rexmit_time, 0, true);
+ _sysfs_prop_get_u32(ctx, sysfs_iface_dir_path, "nd_stale_tmo",
+ &iface->nd_stale_tmo, 0, true);
+ _sysfs_prop_get_u32(ctx, sysfs_iface_dir_path,
+ "router_adv_link_mtu",
+ &iface->router_adv_link_mtu, 0, true);
+ _sysfs_prop_get_u32(ctx, sysfs_iface_dir_path, "traffic_class",
+ &iface->traffic_class, 0, true);
+ }
+
+ _sysfs_prop_get_u16(ctx, sysfs_iface_dir_path, "port", &iface->port, 0,
+ true);
+ _sysfs_prop_get_u16(ctx, sysfs_iface_dir_path, "mtu", &iface->mtu, 0,
+ true);
+ _sysfs_prop_get_u16(ctx, sysfs_iface_dir_path, "vlan_id",
+ &iface->vlan_id, UINT16_MAX, true);
+ _sysfs_prop_get_u8(ctx, sysfs_iface_dir_path, "vlan_priority",
+ &iface->vlan_priority, UINT8_MAX, true);
+ _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, "vlan_enabled",
+ iface->vlan_state, sizeof(iface->vlan_state) /
+ sizeof(char), "");
+ _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, "enabled", iface->state,
+ sizeof(iface->state) / sizeof(char), "");
+ _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, "delayed_ack_en",
+ iface->delayed_ack,
+ sizeof(iface->delayed_ack) / sizeof(char), "");
+ _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, "tcp_nagle_disable",
+ iface->nagle, sizeof(iface->nagle) / sizeof(char),
+ "");
+ _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, "tcp_wsf_disable",
+ iface->tcp_wsf_state,
+ sizeof(iface->tcp_wsf_state) / sizeof(char), "");
+ _sysfs_prop_get_u8(ctx, sysfs_iface_dir_path, "tcp_wsf",
+ &iface->tcp_wsf, 0, true);
+ _sysfs_prop_get_u8(ctx, sysfs_iface_dir_path, "tcp_timer_scale",
+ &iface->tcp_timer_scale, 0, true);
+ _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, "tcp_timestamp_en",
+ iface->tcp_timestamp,
+ sizeof(iface->tcp_timestamp) / sizeof(char), "");
+ _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, "redirect_en",
+ iface->redirect,
+ sizeof(iface->redirect) / sizeof(char), "");
+ _sysfs_prop_get_u16(ctx, sysfs_iface_dir_path, "def_taskmgmt_tmo",
+ &iface->def_task_mgmt_tmo, 0, true);
+ _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, "header_digest",
+ iface->header_digest,
+ sizeof(iface->header_digest) / sizeof(char), "");
+ _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, "data_digest",
+ iface->data_digest,
+ sizeof(iface->data_digest) / sizeof(char), "");
+ _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, "immediate_data",
+ iface->immediate_data,
+ sizeof(iface->immediate_data) / sizeof(char), "");
+ _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, "initial_r2t",
+ iface->initial_r2t,
+ sizeof(iface->initial_r2t) / sizeof(char), "");
+ _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, "data_seq_in_order",
+ iface->data_seq_inorder,
+ sizeof(iface->data_seq_inorder) / sizeof(char),
+ "");
+ _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, "data_pdu_in_order",
+ iface->data_pdu_inorder,
+ sizeof(iface->data_pdu_inorder) / sizeof(char), "");
+ _sysfs_prop_get_u8(ctx, sysfs_iface_dir_path, "erl", &iface->erl, 0,
+ true);
+ _sysfs_prop_get_u32(ctx, sysfs_iface_dir_path, "max_recv_dlength",
+ &iface->max_recv_dlength, 0, true);
+ _sysfs_prop_get_u32(ctx, sysfs_iface_dir_path, "first_burst_len",
+ &iface->first_burst_len, 0, true);
+ _sysfs_prop_get_u16(ctx, sysfs_iface_dir_path, "max_outstanding_r2t",
+ &iface->max_out_r2t, 0, true);
+ _sysfs_prop_get_u32(ctx, sysfs_iface_dir_path, "max_burst_len",
+ &iface->max_burst_len, 0, true);
+ _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, "chap_auth",
+ iface->chap_auth,
+ sizeof(iface->chap_auth) / sizeof(char), "");
+ _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, "bidi_chap",
+ iface->bidi_chap,
+ sizeof(iface->bidi_chap) / sizeof(char), "");
+ _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, "strict_login_comp_en",
+ iface->strict_login_comp,
+ sizeof(iface->strict_login_comp) / sizeof(char),
+ "");
+ _sysfs_prop_get_str(ctx, sysfs_iface_dir_path,
+ "discovery_auth_optional",
+ iface->discovery_auth,
+ sizeof(iface->discovery_auth) / sizeof(char), "");
+ _sysfs_prop_get_str(ctx, sysfs_iface_dir_path,
+ "discovery_logout",
+ iface->discovery_logout,
+ sizeof(iface->discovery_logout) / sizeof(char), "");
+
+ if (sscanf(iface_kern_id, "ipv%d-iface-%" SCNu32 "-%" SCNu32,
+ &iface_type, &tmp_host_no, &iface_num) == 3)
+ iface->iface_num = iface_num;
+
+ snprintf(iface->name, sizeof(iface->name)/sizeof(char),
+ "%s.%s.%s.%u", iface->transport_name,
+ iface->hwaddress, iface->is_ipv6 ? "ipv6" : "ipv4",
+ iface->iface_num);
+
+out:
+ free(sysfs_iface_dir_path);
+ return rc;
+}
+
+int iscsi_ifaces_get(struct iscsi_context *ctx, struct iscsi_iface ***ifaces,
+ uint32_t *iface_count)
+{
+ int rc = LIBISCSI_OK;
+ struct dirent **namelist = NULL;
+ int n = 0;
+ size_t i = 0;
+ struct iscsi_iface *iface = NULL;
+ int j = 0;
+ uint32_t real_iface_count = 0;
+
+ assert(ctx != NULL);
+ assert(ifaces != NULL);
+ assert(iface_count != NULL);
+
+ *ifaces = NULL;
+ *iface_count = 0;
+
+ _good(_idbm_lock(ctx), rc, out);
+
+ _good(_scandir(ctx, IFACE_CONFIG_DIR, &namelist, &n), rc, out);
+ _debug(ctx, "Got %d iface from %s folder", *iface_count,
+ IFACE_CONFIG_DIR);
+ *iface_count = (n + sizeof(_DEFAULT_IFACES)/sizeof(struct iscsi_iface))
+ & UINT32_MAX;
+ *ifaces = (struct iscsi_iface **) calloc(*iface_count,
+ sizeof(struct iscsi_iface *));
+ _alloc_null_check(ctx, *ifaces, rc, out);
+
+ for (j = 0; j < n; ++j) {
+ _good(_idbm_iface_get(ctx, namelist[j]->d_name, &iface),
+ rc, out);
+ if (iface != NULL) {
+ (*ifaces)[real_iface_count++] = iface;
+ }
+ }
+
+ for (i = 0; i < sizeof(_DEFAULT_IFACES)/sizeof(struct iscsi_iface);
+ ++i) {
+ iface = calloc(1, sizeof(struct iscsi_iface));
+ _alloc_null_check(ctx, iface, rc, out);
+ (*ifaces)[real_iface_count++] = iface;
+ memcpy(iface, &_DEFAULT_IFACES[i], sizeof(struct iscsi_iface));
+ }
+
+ *iface_count = real_iface_count;
+
+out:
+ _scandir_free(namelist, n);
+ _idbm_unlock(ctx);
+ if (rc != LIBISCSI_OK) {
+ iscsi_ifaces_free(*ifaces, *iface_count);
+ *ifaces = NULL;
+ *iface_count = 0;
+ }
+ return rc;
+}
+
+void iscsi_ifaces_free(struct iscsi_iface **ifaces, uint32_t iface_count)
+{
+ uint32_t i = 0;
+
+ if ((ifaces == NULL) || (iface_count == 0))
+ return;
+
+ for (i = 0; i < iface_count; ++i)
+ iscsi_iface_free(ifaces[i]);
+ free (ifaces);
+}
+
+static bool _iface_is_bound_by_hwaddr(struct iscsi_iface *iface)
+{
+ if (iface && strlen(iface->hwaddress) &&
+ strcmp(iface->hwaddress, DEFAULT_HWADDRESS))
+ return true;
+ return false;
+}
+
+static bool _iface_is_bound_by_netdev(struct iscsi_iface *iface)
+{
+ if (iface && strlen(iface->netdev) &&
+ strcmp(iface->netdev, DEFAULT_NETDEV))
+ return true;
+ return false;
+}
+
+bool _iface_is_valid(struct iscsi_iface *iface)
+{
+ if (!iface)
+ return false;
+
+ if (strlen(iface->name) == 0)
+ return false;
+
+ if (strlen(iface->transport_name) == 0)
+ return false;
+
+ if (_iface_is_bound_by_hwaddr(iface))
+ return true;
+
+ if (_iface_is_bound_by_netdev(iface))
+ return true;
+
+ /* bound by transport name */
+ return true;
+}
+
+bool iscsi_is_default_iface(struct iscsi_iface *iface)
+{
+ size_t i = 0;
+ for (; i < sizeof(_DEFAULT_IFACES)/sizeof(struct iscsi_iface); ++i) {
+ if (strcmp(iface->name, _DEFAULT_IFACES[i].name) == 0)
+ return true;
+ }
+ return false;
+}
+
+const char *iscsi_iface_dump_config(struct iscsi_iface *iface)
+{
+ FILE *f = NULL;
+ char *buff = NULL;
+
+ assert(iface != NULL);
+
+ buff = calloc(1, _IFACE_DUMP_SIZE);
+ if (buff == NULL)
+ return NULL;
+
+ f = fmemopen(buff, _IFACE_DUMP_SIZE - 1, "w");
+ if (f == NULL) {
+ free(buff);
+ return NULL;
+ }
+
+ _idbm_iface_print(iface, f);
+
+ fclose(f);
+
+ return buff;
+}
+
+void iscsi_iface_print_config(struct iscsi_iface *iface)
+{
+ assert(iface != NULL);
+ _idbm_iface_print(iface, stdout);
+}
+
+int iscsi_iface_get(struct iscsi_context *ctx, const char *iface_name,
+ struct iscsi_iface **iface)
+{
+ int rc = LIBISCSI_OK;
+ assert(ctx != NULL);
+ assert(iface_name != NULL);
+ assert(strlen(iface_name) != 0);
+ assert(iface != NULL);
+
+ *iface = NULL;
+
+ rc = _idbm_lock(ctx);
+ if (rc != LIBISCSI_OK)
+ return rc;
+
+ rc = _idbm_iface_get(ctx, iface_name, iface);
+ if (*iface == NULL)
+ rc = LIBISCSI_ERR_IDBM;
+
+ _idbm_unlock(ctx);
+
return rc;
}
-void _iscsi_iface_free(struct iscsi_iface *iface)
+void iscsi_iface_free(struct iscsi_iface *iface)
{
free(iface);
}
diff --git a/libopeniscsiusr/iface.h b/libopeniscsiusr/iface.h
index 85aa97c..8a1e670 100644
--- a/libopeniscsiusr/iface.h
+++ b/libopeniscsiusr/iface.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 Red Hat, Inc.
+ * Copyright (C) 2017-2018 Red Hat, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -16,20 +16,126 @@
*
* Author: Gris Ge <fge@redhat.com>
*/
+
#ifndef __ISCSI_USR_IFACE_H__
#define __ISCSI_USR_IFACE_H__
#include "libopeniscsiusr/libopeniscsiusr_common.h"
#include <stdint.h>
+#include <netdb.h>
-/*
- * BUG(Gris Ge): Should include 'iface_kern_id' parameter.
+#define VALUE_MAXVAL 256 /* the maximum length of 223 bytes in the RFC. */
+/* ^ VALUE_MAXVAL is copied from usr/idbm.h
+ * The RFC 3720 only said:
+ * If not otherwise specified, the maximum length of a simple-value (not
+ * its encoded representation) is 255 bytes, not including the delimiter
+ * (comma or zero byte).
*/
-__DLL_LOCAL int _iscsi_iface_get(struct iscsi_context *ctx, uint32_t host_id,
- uint32_t sid, const char *iface_kern_id,
- struct iscsi_iface **iface);
-__DLL_LOCAL void _iscsi_iface_free(struct iscsi_iface *iface);
+#define ISCSI_MAX_IFACE_LEN 65
+#define ISCSI_TRANSPORT_NAME_MAXLEN 16
+#define ISCSI_MAX_STR_LEN 80
+#define ISCSI_HWADDRESS_BUF_SIZE 18
+#define TARGET_NAME_MAXLEN VALUE_MAXVAL
+/* ^ TODO(Gris Ge): Above 5 constants are copy from usr/config.h, need to
+ * verify them in linux kernel code
+ */
+
+struct iscsi_iface {
+ /* iscsi iface record name */
+ char name[ISCSI_MAX_IFACE_LEN];
+ uint32_t iface_num;
+ /* network layer iface name (eth0) */
+ char netdev[IFNAMSIZ];
+ char ipaddress[NI_MAXHOST];
+ char subnet_mask[NI_MAXHOST];
+ char gateway[NI_MAXHOST];
+ char bootproto[ISCSI_MAX_STR_LEN];
+ char ipv6_linklocal[NI_MAXHOST];
+ char ipv6_router[NI_MAXHOST];
+ char ipv6_autocfg[NI_MAXHOST];
+ char linklocal_autocfg[NI_MAXHOST];
+ char router_autocfg[NI_MAXHOST];
+// uint8_t prefix_len;
+ /* ^ prefix_len is removed, as linux kernel has no such sysfs property
+ * and there is no actual code in usr/ folder set this property
+ */
+ uint16_t vlan_id;
+ uint8_t vlan_priority;
+ char vlan_state[ISCSI_MAX_STR_LEN];
+ char state[ISCSI_MAX_STR_LEN]; /* 0 = disable,
+ * 1 = enable */
+ uint16_t mtu;
+ uint16_t port;
+ char delayed_ack[ISCSI_MAX_STR_LEN];
+ char nagle[ISCSI_MAX_STR_LEN];
+ char tcp_wsf_state[ISCSI_MAX_STR_LEN];
+ uint8_t tcp_wsf;
+ uint8_t tcp_timer_scale;
+ char tcp_timestamp[ISCSI_MAX_STR_LEN];
+ char dhcp_dns[ISCSI_MAX_STR_LEN];
+ char dhcp_slp_da[ISCSI_MAX_STR_LEN];
+ char tos_state[ISCSI_MAX_STR_LEN];
+ uint8_t tos;
+ char gratuitous_arp[ISCSI_MAX_STR_LEN];
+ char dhcp_alt_client_id_state[ISCSI_MAX_STR_LEN];
+ char dhcp_alt_client_id[ISCSI_MAX_STR_LEN];
+ char dhcp_req_vendor_id_state[ISCSI_MAX_STR_LEN];
+ char dhcp_vendor_id_state[ISCSI_MAX_STR_LEN];
+ char dhcp_vendor_id[ISCSI_MAX_STR_LEN];
+ char dhcp_learn_iqn[ISCSI_MAX_STR_LEN];
+ char fragmentation[ISCSI_MAX_STR_LEN];
+ char incoming_forwarding[ISCSI_MAX_STR_LEN];
+ uint8_t ttl;
+ char gratuitous_neighbor_adv[ISCSI_MAX_STR_LEN];
+ char redirect[ISCSI_MAX_STR_LEN];
+ char mld[ISCSI_MAX_STR_LEN];
+ uint32_t flow_label;
+ uint32_t traffic_class;
+ uint8_t hop_limit;
+ uint32_t nd_reachable_tmo;
+ uint32_t nd_rexmit_time;
+ uint32_t nd_stale_tmo;
+ uint8_t dup_addr_detect_cnt;
+ uint32_t router_adv_link_mtu;
+ uint16_t def_task_mgmt_tmo;
+ char header_digest[ISCSI_MAX_STR_LEN];
+ char data_digest[ISCSI_MAX_STR_LEN];
+ char immediate_data[ISCSI_MAX_STR_LEN];
+ char initial_r2t[ISCSI_MAX_STR_LEN];
+ char data_seq_inorder[ISCSI_MAX_STR_LEN];
+ char data_pdu_inorder[ISCSI_MAX_STR_LEN];
+ uint8_t erl;
+ uint32_t max_recv_dlength;
+ uint32_t first_burst_len;
+ uint16_t max_out_r2t;
+ uint32_t max_burst_len;
+ char chap_auth[ISCSI_MAX_STR_LEN];
+ char bidi_chap[ISCSI_MAX_STR_LEN];
+ char strict_login_comp[ISCSI_MAX_STR_LEN];
+ char discovery_auth[ISCSI_MAX_STR_LEN];
+ char discovery_logout[ISCSI_MAX_STR_LEN];
+ char port_state[ISCSI_MAX_STR_LEN];
+ char port_speed[ISCSI_MAX_STR_LEN];
+ /*
+ * TODO: we may have to make this bigger and interconnect
+ * specific for infiniband
+ */
+ char hwaddress[ISCSI_HWADDRESS_BUF_SIZE];
+ char transport_name[ISCSI_TRANSPORT_NAME_MAXLEN];
+ /*
+ * This is only used for boot now, but the iser guys
+ * can use this for their virtualization idea.
+ */
+ char alias[TARGET_NAME_MAXLEN + 1];
+ char iname[TARGET_NAME_MAXLEN + 1];
+ bool is_ipv6;
+};
+
+__DLL_LOCAL int _iscsi_iface_get_from_sysfs(struct iscsi_context *ctx,
+ uint32_t host_id, uint32_t sid,
+ struct iscsi_iface **iface);
+__DLL_LOCAL bool _iface_is_valid(struct iscsi_iface *iface);
#endif /* End of __ISCSI_USR_IFACE_H__ */
diff --git a/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr.h b/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr.h
index bf8a845..7b5a280 100644
--- a/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr.h
+++ b/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr.h
@@ -25,6 +25,7 @@ extern "C" {
#include <stdint.h>
#include <stdarg.h>
+#include <stdbool.h>
#include "libopeniscsiusr_common.h"
#include "libopeniscsiusr_session.h"
@@ -324,6 +325,160 @@ __DLL_EXPORT int iscsi_session_get(struct iscsi_context *ctx, uint32_t sid,
*/
__DLL_EXPORT void iscsi_session_free(struct iscsi_session *se);
+/**
+ * iscsi_default_iface_setup() - Setup default iSCSI interfaces.
+ *
+ * Setup default iSCSI interfaces for iSCSI TCP, iSER and iSCSI hardware offload
+ * cards. It is required after new iSCSI hardware offload card installed.
+ *
+ * Below kernel modules will be loaded when required by this function:
+ *
+ * * cxgb3i
+ * * cxgb4i
+ * * bnx2i
+ *
+ * It will also create configuration files for iSCSI hardware offload cards in
+ * /etc/iscsi/ifaces/<iface_name>.
+ *
+ * @ctx:
+ * Pointer of 'struct iscsi_context'.
+ *
+ * Return:
+ * int. Valid error codes are:
+ *
+ * * LIBISCSI_OK
+ *
+ * * LIBISCSI_ERR_BUG
+ *
+ * * LIBISCSI_ERR_NOMEM
+ *
+ * * LIBISCSI_ERR_ACCESS
+ *
+ * * LIBISCSI_ERR_SYSFS_LOOKUP
+ *
+ * * LIBISCSI_ERR_IDBM
+ *
+ * Error number could be converted to string by iscsi_strerror().
+ */
+__DLL_EXPORT int iscsi_default_iface_setup(struct iscsi_context *ctx);
+
+/**
+ * iscsi_ifaces_get() - Retrieve all iSCSI interfaces.
+ *
+ * Retrieves all iSCSI interfaces. For the properties of 'struct iscsi_iface',
+ * please refer to the functions defined in 'libopeniscsiusr_iface.h' file.
+ * The returned results contains default iSCSI interfaces(iser and iscsi_tcp)
+ * and iSCSI interfaces configured in "/etc/iscsi/ifaces/".
+ * Illegal configuration file will be skipped and warned.
+ * To generate iSCSI interface configuration when new card installed, please
+ * use iscsi_default_iface_setup().
+ *
+ * @ctx:
+ * Pointer of 'struct iscsi_context'.
+ * If this pointer is NULL, your program will be terminated by assert.
+ * @ifaces:
+ * Output pointer of 'struct iscsi_iface' pointer array. Its memory
+ * should be freed by iscsi_ifaces_free().
+ * If this pointer is NULL, your program will be terminated by assert.
+ * @iface_count:
+ * Output pointer of uint32_t. Will store the size of
+ * 'struct iscsi_iface' pointer array.
+ *
+ * Return:
+ * int. Valid error codes are:
+ *
+ * * LIBISCSI_OK
+ *
+ * * LIBISCSI_ERR_BUG
+ *
+ * * LIBISCSI_ERR_NOMEM
+ *
+ * * LIBISCSI_ERR_ACCESS
+ *
+ * * LIBISCSI_ERR_SYSFS_LOOKUP
+ *
+ * Error number could be converted to string by iscsi_strerror().
+ */
+__DLL_EXPORT int iscsi_ifaces_get(struct iscsi_context *ctx,
+ struct iscsi_iface ***ifaces,
+ uint32_t *iface_count);
+
+/**
+ * iscsi_ifaces_free() - Free the memory of 'struct iscsi_iface' pointer
+ * array
+ *
+ * Free the memory of 'iscsi_iface' pointer array generated by
+ * 'iscsi_ifaces_get()'.
+ * If provided 'ifaces' pointer is NULL or 'iface_count' is 0, do nothing.
+ *
+ * @ifaces:
+ * Pointer of 'struct iscsi_iface' pointer array.
+ * @iface_count:
+ * uint32_t, the size of 'struct iscsi_iface' pointer array.
+ *
+ * Return:
+ * void
+ */
+__DLL_EXPORT void iscsi_ifaces_free(struct iscsi_iface **ifaces,
+ uint32_t iface_count);
+
+/**
+ * iscsi_iface_get() - Retrieve specified iSCSI interface.
+ *
+ * Retrieves specified iSCSI interfaces by reading configuration from
+ * "/etc/iscsi/iface/<iface_name>".
+ * To generate iSCSI interface configuration when new card installed, please
+ * use iscsi_default_iface_setup().
+ * Illegal configuration file will be treated as error LIBISCSI_ERR_IDBM.
+ * Configuration file not found will be treated as error LIBISCSI_ERR_INVAL.
+ *
+ * @ctx:
+ * Pointer of 'struct iscsi_context'.
+ * If this pointer is NULL, your program will be terminated by assert.
+ * @iface_name:
+ * String. Name of iSCSI interface. Also the file name of configuration
+ * file "/etc/iscsi/iface/<iface_name>".
+ * If this pointer is NULL or empty string, your program will be terminated
+ * by assert.
+ * @iface:
+ * Output pointer of 'struct iscsi_iface'. Its memory should be freed by
+ * iscsi_iface_free().
+ * If this pointer is NULL, your program will be terminated by assert.
+ *
+ * Return:
+ * int. Valid error codes are:
+ *
+ * * LIBISCSI_OK
+ *
+ * * LIBISCSI_ERR_BUG
+ *
+ * * LIBISCSI_ERR_NOMEM
+ *
+ * * LIBISCSI_ERR_ACCESS
+ *
+ * * LIBISCSI_ERR_SYSFS_LOOKUP
+ *
+ * * LIBISCSI_ERR_IDBM
+ *
+ * Error number could be converted to string by iscsi_strerror().
+ */
+__DLL_EXPORT int iscsi_iface_get(struct iscsi_context *ctx,
+ const char *iface_name,
+ struct iscsi_iface **iface);
+
+/**
+ * iscsi_iface_free() - Free the memory of 'struct iscsi_iface' pointer.
+ *
+ * Free the memory of 'iscsi_iface' pointer generated by 'iscsi_iface_get()'.
+ * If provided 'iface' pointer is NULL, do nothing.
+ *
+ * @iface:
+ * Pointer of 'struct iscsi_iface' pointer.
+ *
+ * Return:
+ * void
+ */
+__DLL_EXPORT void iscsi_iface_free(struct iscsi_iface *iface);
#ifdef __cplusplus
} /* End of extern "C" */
diff --git a/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr_common.h b/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr_common.h
index 383ff19..334cc4e 100644
--- a/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr_common.h
+++ b/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr_common.h
@@ -36,6 +36,15 @@
#define LIBISCSI_ERR_NOMEM 3
/* ^ Could not allocate resource for operation */
+#define LIBISCSI_ERR_IDBM 6
+/* ^ Error accessing/managing iSCSI DB */
+
+#define LIBISCSI_ERR_INVAL 7
+/* ^ Invalid argument */
+
+#define LIBISCSI_ERR_TRANS_NOT_FOUND 12
+/* ^ iSCSI transport module not loaded in kernel or iscsid */
+
#define LIBISCSI_ERR_ACCESS 13
/* ^ Permission denied */
diff --git a/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr_iface.h b/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr_iface.h
index ffba1f7..23573bf 100644
--- a/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr_iface.h
+++ b/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr_iface.h
@@ -173,4 +173,57 @@ __DLL_EXPORT const char *iscsi_iface_port_speed_get(struct iscsi_iface *iface);
*/
__DLL_EXPORT const char *iscsi_iface_name_get(struct iscsi_iface *iface);
+/**
+ * iscsi_iface_dump_config() - Dump all configurations of specified iSCSI
+ * interface.
+ *
+ * Dump all configurations of specified iSCSI interface. Will skip empty
+ * configuration so that output string could be saved directly to
+ * /etc/iscsi/ifaces/<iface_name> file.
+ *
+ * @iface:
+ * Pointer of 'struct iscsi_iface'.
+ * If this pointer is NULL, your program will be terminated by assert.
+ *
+ * Return:
+ * const char *.
+ * Need to free this memory by free().
+ */
+__DLL_EXPORT const char *iscsi_iface_dump_config(struct iscsi_iface *iface);
+
+/**
+ * iscsi_iface_dump_config() - Print all configurations of specified iSCSI
+ * interface to STDOUT.
+ *
+ * Print all configurations of specified iSCSI interface.
+ * For empty configuration, it will be shown as "name = <empty>".
+ *
+ * @iface:
+ * Pointer of 'struct iscsi_iface'.
+ * If this pointer is NULL, your program will be terminated by assert.
+ *
+ * Return:
+ * void
+ */
+__DLL_EXPORT void iscsi_iface_print_config(struct iscsi_iface *iface);
+
+/**
+ * iscsi_is_default_iface() - Whether specified iSCSI interface is default
+ * interface.
+ *
+ * Check whether specified iSCSI interface is one of the default interfaces.
+ * Currently, default interfaces are :
+ *
+ * * Interface 'default' using 'iscsi_tcp' kernel module.
+ *
+ * * Interface 'iser' is using 'ib_iser' kernel module.
+ *
+ * @iface:
+ * Pointer of 'struct iscsi_iface'.
+ *
+ * Return:
+ * bool.
+ */
+__DLL_EXPORT bool iscsi_is_default_iface(struct iscsi_iface *iface);
+
#endif /* End of _LIB_OPEN_ISCSI_USR_IFACE_H_ */
diff --git a/libopeniscsiusr/misc.c b/libopeniscsiusr/misc.c
index 93db919..9111966 100644
--- a/libopeniscsiusr/misc.c
+++ b/libopeniscsiusr/misc.c
@@ -24,6 +24,12 @@
#include <dirent.h>
#include <string.h>
#include <unistd.h>
+#include <net/if.h>
+#include <sys/socket.h>
+#include <linux/ethtool.h>
+#include <sys/ioctl.h>
+#include <linux/sockios.h>
+#include <net/if_arp.h>
#include "libopeniscsiusr/libopeniscsiusr.h"
#include "misc.h"
@@ -63,6 +69,10 @@ static const struct _num_str_conv _ISCSI_RC_MSG_CONV[] = {
{LIBISCSI_ERR_ACCESS, "Permission deny"},
{LIBISCSI_ERR_NOMEM, "Out of memory"},
{LIBISCSI_ERR_SYSFS_LOOKUP, "Could not lookup object in sysfs"},
+ {LIBISCSI_ERR_IDBM, "Error accessing/managing iSCSI DB"},
+ {LIBISCSI_ERR_TRANS_NOT_FOUND,
+ "iSCSI transport module not loaded in kernel or iscsid"},
+ {LIBISCSI_ERR_INVAL, "Invalid argument"},
};
_iscsi_str_func_gen(iscsi_strerror, int, rc, _ISCSI_RC_MSG_CONV);
@@ -122,3 +132,200 @@ bool _file_exists(const char *path)
else
return false;
}
+
+static bool _is_eth(struct iscsi_context *ctx, const char *if_name)
+{
+ struct ifreq ifr;
+ int sockfd = -1;
+ char strerr_buff[_STRERR_BUFF_LEN];
+
+ assert(if_name != NULL);
+
+ memset(&ifr, 0, sizeof(ifr));
+
+ _strncpy(ifr.ifr_name, if_name, IFNAMSIZ);
+
+ sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sockfd < 0) {
+ _warn(ctx, "Failed to create SOCK_DGRAM AF_INET socket: %d %s",
+ errno, _strerror(errno, strerr_buff));
+ return false;
+ }
+
+ if (ioctl(sockfd, SIOCGIFHWADDR, &ifr) != 0) {
+ _warn(ctx, "IOCTL SIOCGIFHWADDR to %s failed: %d %s", if_name,
+ errno, _strerror(errno, strerr_buff));
+ close(sockfd);
+ return false;
+ }
+
+ close(sockfd);
+
+ if (ifr.ifr_hwaddr.sa_family == ARPHRD_ETHER)
+ return true;
+
+ return false;
+}
+
+/*
+ * driver_name should be char[_ETH_DRIVER_NAME_MAX_LEN]
+ */
+static int _eth_driver_get(struct iscsi_context *ctx, const char *if_name,
+ char *driver_name)
+{
+ int sockfd = -1;
+ struct ethtool_drvinfo drvinfo;
+ struct ifreq ifr;
+ char strerr_buff[_STRERR_BUFF_LEN];
+
+ assert(ctx != NULL);
+ assert(if_name != NULL);
+ assert(driver_name != NULL);
+
+ memset(&ifr, 0, sizeof(ifr));
+ memset(&drvinfo, 0, sizeof(drvinfo));
+
+ _strncpy(ifr.ifr_name, if_name, IFNAMSIZ);
+ drvinfo.cmd = ETHTOOL_GDRVINFO;
+ ifr.ifr_data = (caddr_t) &drvinfo;
+
+ sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sockfd < 0) {
+ _error(ctx, "Failed to create SOCK_DGRAM AF_INET socket: %d %s",
+ errno, _strerror(errno, strerr_buff));
+ return LIBISCSI_ERR_BUG;
+ }
+
+ if (ioctl(sockfd, SIOCETHTOOL, &ifr) != 0) {
+ _warn(ctx, "IOCTL SIOCETHTOOL to %s failed: %d %s", if_name,
+ errno, _strerror(errno, strerr_buff));
+ close(sockfd);
+ return LIBISCSI_ERR_BUG;
+ }
+ close(sockfd);
+ snprintf(driver_name, _ETH_DRIVER_NAME_MAX_LEN, "%s", drvinfo.driver);
+
+ return LIBISCSI_OK;
+}
+
+int _eth_ifs_get(struct iscsi_context *ctx, struct _eth_if ***eifs,
+ uint32_t *eif_count)
+{
+ int rc = LIBISCSI_OK;
+ struct if_nameindex *if_ni = NULL;
+ struct if_nameindex *if_i = NULL;
+ struct _eth_if *eif = NULL;
+ uint32_t tmp_count = 0;
+
+ assert(ctx != NULL);
+ assert(eifs != NULL);
+ assert(eif_count != NULL);
+
+ *eifs = NULL;
+ *eif_count = 0;
+
+ if_ni = if_nameindex();
+ _alloc_null_check(ctx, if_ni, rc, out);
+
+ for (if_i = if_ni; if_i && if_i->if_index && if_i->if_name; ++if_i)
+ tmp_count++;
+
+ if (tmp_count == 0)
+ goto out;
+
+ *eifs = calloc(tmp_count, sizeof(struct _eth_if *));
+ _alloc_null_check(ctx, *eifs, rc, out);
+
+ for (if_i = if_ni; if_i && if_i->if_index && if_i->if_name; ++if_i) {
+ if (! _is_eth(ctx, if_i->if_name))
+ continue;
+ eif = calloc(1, sizeof(struct _eth_if));
+ _alloc_null_check(ctx, eif, rc, out);
+ (*eifs)[(*eif_count)++] = eif;
+ snprintf(eif->if_name, sizeof(eif->if_name)/sizeof(char),
+ "%s", if_i->if_name);
+ _good(_eth_driver_get(ctx, eif->if_name, eif->driver_name),
+ rc, out);
+ }
+
+out:
+ if (rc != LIBISCSI_OK) {
+ _eth_ifs_free(*eifs, *eif_count);
+ *eifs = NULL;
+ *eif_count = 0;
+ }
+ if (if_ni != NULL)
+ if_freenameindex(if_ni);
+ return rc;
+}
+
+void _eth_ifs_free(struct _eth_if **eifs, uint32_t eif_count)
+{
+ uint32_t i = 0;
+
+ if ((eif_count == 0) || (eifs == NULL))
+ return;
+
+ for (; i < eif_count; ++i)
+ free(eifs[i]);
+ free(eifs);
+}
+
+void _scandir_free(struct dirent **namelist, int count)
+{
+ int i = 0;
+
+ if ((namelist == NULL) || (count == 0))
+ return;
+
+ for (i = count - 1; i >= 0; --i)
+ free(namelist[i]);
+ free(namelist);
+}
+
+int _scandir(struct iscsi_context *ctx, const char *dir_path,
+ struct dirent ***namelist, int *count)
+{
+ int rc = LIBISCSI_OK;
+ int errno_save = 0;
+
+ assert(ctx != NULL);
+ assert(dir_path != NULL);
+ assert(namelist != NULL);
+ assert(count != NULL);
+
+ *namelist = NULL;
+ *count = 0;
+
+ *count = scandir(dir_path, namelist, _scan_filter_skip_dot, alphasort);
+ if (*count < 0) {
+ errno_save = errno;
+ if (errno_save == ENOENT) {
+ *count = 0;
+ goto out;
+ }
+ if (errno_save == ENOMEM) {
+ rc = LIBISCSI_ERR_NOMEM;
+ goto out;
+ }
+ if (errno_save == ENOTDIR) {
+ rc = LIBISCSI_ERR_BUG;
+ _error(ctx, "Got ENOTDIR error when scandir %s",
+ dir_path);
+ goto out;
+ }
+ rc = LIBISCSI_ERR_BUG;
+ _error(ctx, "Got unexpected error %d when scandir %s",
+ errno_save, dir_path);
+ goto out;
+ }
+
+out:
+ if (rc != LIBISCSI_OK) {
+ _scandir_free(*namelist, *count);
+ *namelist = NULL;
+ *count = 0;
+ }
+
+ return rc;
+}
diff --git a/libopeniscsiusr/misc.h b/libopeniscsiusr/misc.h
index 1c5d696..9208bbd 100644
--- a/libopeniscsiusr/misc.h
+++ b/libopeniscsiusr/misc.h
@@ -24,6 +24,7 @@
#include <assert.h>
#include <stdarg.h>
#include <dirent.h>
+#include <net/if.h>
#include "libopeniscsiusr/libopeniscsiusr.h"
@@ -79,8 +80,36 @@ __DLL_LOCAL void _iscsi_log_stderr(struct iscsi_context *ctx, int priority,
} \
} while(0)
+#define _STRERR_BUFF_LEN 1024
+#define _strerror(err_no, buff) \
+ strerror_r(err_no, buff, _STRERR_BUFF_LEN)
+
+#define _strncpy(dst, src, size) \
+ do { \
+ strncpy(dst, src, size); \
+ * (char *) (dst + (size - 1)) = '\0'; \
+ } while(0)
+
__DLL_LOCAL int _scan_filter_skip_dot(const struct dirent *dir);
__DLL_LOCAL bool _file_exists(const char *path);
+
+#define _ETH_DRIVER_NAME_MAX_LEN 32
+/* ^ Defined in linux/ethtool.h `struct ethtool_drvinfo`. */
+
+struct _eth_if {
+ char driver_name[_ETH_DRIVER_NAME_MAX_LEN];
+ char if_name[IF_NAMESIZE];
+};
+
+__DLL_LOCAL int _eth_ifs_get(struct iscsi_context *ctx,
+ struct _eth_if ***eifs, uint32_t *eif_count);
+
+__DLL_LOCAL void _eth_ifs_free(struct _eth_if **eifs, uint32_t eif_count);
+
+__DLL_LOCAL int _scandir(struct iscsi_context *ctx, const char *dir_path,
+ struct dirent ***namelist, int *count);
+__DLL_LOCAL void _scandir_free(struct dirent **namelist, int count);
+
#endif /* End of __ISCSI_USR_MISC_H__ */
diff --git a/libopeniscsiusr/session.c b/libopeniscsiusr/session.c
index 0a04f89..4d9c57c 100644
--- a/libopeniscsiusr/session.c
+++ b/libopeniscsiusr/session.c
@@ -84,8 +84,6 @@ struct iscsi_session {
struct iscsi_iface *iface;
};
-static uint32_t session_str_to_sid(const char *session_str);
-
_iscsi_getter_func_gen(iscsi_session, sid, uint32_t);
_iscsi_getter_func_gen(iscsi_session, persistent_address, const char *);
_iscsi_getter_func_gen(iscsi_session, persistent_port, int32_t);
@@ -103,19 +101,6 @@ _iscsi_getter_func_gen(iscsi_session, address, const char *);
_iscsi_getter_func_gen(iscsi_session, port, int32_t);
_iscsi_getter_func_gen(iscsi_session, iface, struct iscsi_iface *);
-/*
- * The session string is "session%u" used by /sys/class/iscsi_session/session%u.
- * Return 0 if error parsing session string.
- */
-static uint32_t session_str_to_sid(const char *session_str)
-{
- uint32_t sid = 0;
-
- if (sscanf(session_str, "session%" SCNu32, &sid) != 1)
- return 0; /* error */
- return sid;
-}
-
int iscsi_session_get(struct iscsi_context *ctx, uint32_t sid,
struct iscsi_session **se)
{
@@ -189,25 +174,23 @@ int iscsi_session_get(struct iscsi_context *ctx, uint32_t sid,
rc, out);
_good(_sysfs_prop_get_i32(ctx, sysfs_se_dir_path, "recovery_tmo",
- &((*se)->recovery_tmo),
- -1),
+ &((*se)->recovery_tmo), -1, true),
rc, out);
_good(_sysfs_prop_get_i32(ctx, sysfs_se_dir_path, "lu_reset_tmo",
- &((*se)->lu_reset_tmo), -1),
+ &((*se)->lu_reset_tmo), -1, true),
rc, out);
- _good(_sysfs_prop_get_i32(ctx, sysfs_se_dir_path,
- "tgt_reset_tmo", &((*se)->tgt_reset_tmo), -1),
+ _good(_sysfs_prop_get_i32(ctx, sysfs_se_dir_path, "tgt_reset_tmo",
+ &((*se)->tgt_reset_tmo), -1, true),
rc, out);
_good(_sysfs_prop_get_i32(ctx, sysfs_se_dir_path, "abort_tmo",
- &((*se)->abort_tmo), -1),
+ &((*se)->abort_tmo), -1, true),
rc, out);
_good(_sysfs_prop_get_i32(ctx, sysfs_se_dir_path, "tpgt",
- &((*se)->tpgt),
- INT32_MAX /* raise error if not found */),
+ &((*se)->tpgt), -1, true),
rc, out);
_good(_sysfs_prop_get_str(ctx, sysfs_con_dir_path, "persistent_address",
@@ -217,27 +200,24 @@ int iscsi_session_get(struct iscsi_context *ctx, uint32_t sid,
rc, out);
_good(_sysfs_prop_get_i32(ctx, sysfs_con_dir_path, "persistent_port",
- &((*se)->persistent_port), -1),
+ &((*se)->persistent_port), -1, true),
rc, out);
- _good(_sysfs_prop_get_str(ctx, sysfs_con_dir_path, "address",
- (*se)->address,
- sizeof((*se)->address) / sizeof(char),
- ""),
- rc, out);
+ _sysfs_prop_get_str(ctx, sysfs_con_dir_path, "address", (*se)->address,
+ sizeof((*se)->address) / sizeof(char), "");
_good(_sysfs_prop_get_i32(ctx, sysfs_con_dir_path, "port",
- &((*se)->port), -1), rc, out);
+ &((*se)->port), -1, false), rc, out);
if ((strcmp((*se)->address, "") == 0) &&
(strcmp((*se)->persistent_address, "") != 0))
- strncpy((*se)->persistent_address, (*se)->address,
- sizeof((*se)->persistent_address) / sizeof(char));
+ _strncpy((*se)->persistent_address, (*se)->address,
+ sizeof((*se)->persistent_address) / sizeof(char));
if ((strcmp((*se)->address, "") != 0) &&
(strcmp((*se)->persistent_address, "") == 0))
- strncpy((*se)->address, (*se)->persistent_address,
- sizeof((*se)->address) / sizeof(char));
+ _strncpy((*se)->address, (*se)->persistent_address,
+ sizeof((*se)->address) / sizeof(char));
if (((*se)->persistent_port != -1) &&
((*se)->port == -1))
@@ -249,8 +229,7 @@ int iscsi_session_get(struct iscsi_context *ctx, uint32_t sid,
_good(_iscsi_host_id_of_session(ctx, sid, &host_id), rc, out);
- _good(_iscsi_iface_get(ctx, host_id, sid, NULL /*iface kernel id */,
- &((*se)->iface)),
+ _good(_iscsi_iface_get_from_sysfs(ctx, host_id, sid, &((*se)->iface)),
rc, out);
out:
@@ -265,13 +244,9 @@ int iscsi_sessions_get(struct iscsi_context *ctx,
struct iscsi_session ***sessions,
uint32_t *session_count)
{
- struct dirent **namelist = NULL;
- int n = 0;
int rc = LIBISCSI_OK;
- int errno_save = 0;
uint32_t i = 0;
- uint32_t sid = 0;
- int j = 0;
+ uint32_t *sids = NULL;
assert(ctx != NULL);
assert(sessions != NULL);
@@ -280,49 +255,19 @@ int iscsi_sessions_get(struct iscsi_context *ctx,
*sessions = NULL;
*session_count = 0;
- n = scandir(_ISCSI_SYS_SESSION_DIR, &namelist, _scan_filter_skip_dot,
- alphasort);
- if (n < 0) {
- errno_save = errno;
- if (errno_save == ENOENT)
- goto out;
- if (errno_save == ENOMEM) {
- rc = LIBISCSI_ERR_NOMEM;
- goto out;
- }
- if (errno_save == ENOTDIR) {
- rc = LIBISCSI_ERR_BUG;
- _error(ctx, "Got ENOTDIR error when scandir %s",
- _ISCSI_SYS_SESSION_DIR);
- goto out;
- }
- rc = LIBISCSI_ERR_BUG;
- _error(ctx, "Got unexpected error %d when scandir %s",
- errno_save, _ISCSI_SYS_SESSION_DIR);
- goto out;
- }
- _info(ctx, "Got %d iSCSI sessions", n);
- *sessions = (struct iscsi_session **)
- calloc (sizeof(struct iscsi_session *), n);
- _alloc_null_check(ctx, *sessions, rc, out);
+ _good(_iscsi_sids_get(ctx, &sids, session_count), rc ,out);
- *session_count = n & UINT32_MAX;
+ *sessions = calloc (*session_count, sizeof(struct iscsi_session *));
+ _alloc_null_check(ctx, *sessions, rc, out);
for (i = 0; i < *session_count; ++i) {
- sid = session_str_to_sid(namelist[i]->d_name);
- if (sid == 0) {
- _error(ctx, "Got illegal iscsi session string %s",
- namelist[i]->d_name);
- rc = LIBISCSI_ERR_BUG;
- goto out;
- }
- _good(iscsi_session_get(ctx, sid, &((*sessions)[i])), rc, out);
+ _debug(ctx, "sid %" PRIu32, sids[i]);
+ _good(iscsi_session_get(ctx, sids[i], &((*sessions)[i])),
+ rc, out);
}
out:
- for (j = n - 1; j >= 0; --j)
- free(namelist[j]);
- free(namelist);
+ free(sids);
if (rc != LIBISCSI_OK) {
iscsi_sessions_free(*sessions, *session_count);
*sessions = NULL;
@@ -334,7 +279,7 @@ out:
void iscsi_session_free(struct iscsi_session *se)
{
if (se != NULL)
- _iscsi_iface_free(se->iface);
+ iscsi_iface_free(se->iface);
free(se);
}
diff --git a/libopeniscsiusr/sysfs.c b/libopeniscsiusr/sysfs.c
index 70298f2..f568245 100644
--- a/libopeniscsiusr/sysfs.c
+++ b/libopeniscsiusr/sysfs.c
@@ -43,13 +43,45 @@
#define _SYS_NULL_STR "(null)"
+#define _sysfs_prop_get_int_func_gen(func_name, out_type, type_max_value) \
+ int func_name(struct iscsi_context *ctx, const char *dir_path, \
+ const char *prop_name, out_type *val, \
+ out_type default_value, bool ignore_error) \
+ { \
+ long long int tmp_val = 0; \
+ int rc = LIBISCSI_OK; \
+ long long int dv = default_value; \
+ rc = iscsi_sysfs_prop_get_ll(ctx, dir_path, prop_name, \
+ &tmp_val, (long long int) dv, \
+ ignore_error); \
+ if (rc == LIBISCSI_OK) \
+ *val = tmp_val & type_max_value; \
+ return rc; \
+ }
+
+
+enum _sysfs_dev_class {
+ _SYSFS_DEV_CLASS_ISCSI_SESSION,
+ _SYSFS_DEV_CLASS_ISCSI_HOST,
+};
+
static int sysfs_read_file(const char *path, uint8_t *buff, size_t buff_size);
static int iscsi_sysfs_prop_get_ll(struct iscsi_context *ctx,
const char *dir_path, const char *prop_name,
long long int *val,
- long long int default_value);
+ long long int default_value,
+ bool ignore_error);
+
+/*
+ * dev_path should be char[PATH_MAX]
+ */
static int sysfs_get_dev_path(struct iscsi_context *ctx, const char *path,
- char *dev_path);
+ enum _sysfs_dev_class class, char *dev_path);
+
+_sysfs_prop_get_int_func_gen(_sysfs_prop_get_u8, uint8_t, UINT8_MAX);
+_sysfs_prop_get_int_func_gen(_sysfs_prop_get_u16, uint16_t, UINT16_MAX);
+_sysfs_prop_get_int_func_gen(_sysfs_prop_get_i32, int32_t, INT32_MAX);
+_sysfs_prop_get_int_func_gen(_sysfs_prop_get_u32, uint32_t, UINT32_MAX);
/*
* dev_path should be char[PATH_MAX].
@@ -136,7 +168,13 @@ int _sysfs_prop_get_str(struct iscsi_context *ctx, const char *dir_path,
file_path, errno_save);
}
} else {
- _debug(ctx, "Open '%s', got '%s'", file_path, buff);
+ if ((buff[0] == '\0') && (default_value != NULL)) {
+ memcpy(buff, (void *) default_value,
+ strlen(default_value) + 1);
+ _debug(ctx, "Open '%s', got NULL, using default value",
+ file_path, default_value);
+ } else
+ _debug(ctx, "Open '%s', got '%s'", file_path, buff);
}
return rc;
}
@@ -144,7 +182,7 @@ int _sysfs_prop_get_str(struct iscsi_context *ctx, const char *dir_path,
static int iscsi_sysfs_prop_get_ll(struct iscsi_context *ctx,
const char *dir_path, const char *prop_name,
long long int *val,
- long long int default_value)
+ long long int default_value, bool ignore_error)
{
char file_path[PATH_MAX];
int rc = LIBISCSI_OK;
@@ -163,7 +201,7 @@ static int iscsi_sysfs_prop_get_ll(struct iscsi_context *ctx,
errno_save = sysfs_read_file(file_path, buff, _INT32_STR_MAX_LEN);
if (errno_save != 0) {
if (errno_save == ENOENT) {
- if (default_value == LLONG_MAX) {
+ if (! ignore_error) {
rc = LIBISCSI_ERR_SYSFS_LOOKUP;
_error(ctx, "Failed to read '%s': "
"file '%s' does not exists",
@@ -193,7 +231,7 @@ static int iscsi_sysfs_prop_get_ll(struct iscsi_context *ctx,
tmp_val = strtoll((const char *) buff, NULL, 10 /* base */);
errno_save = errno;
- if ((errno_save != 0) && (tmp_val == LONG_MAX)) {
+ if ((errno_save != 0) && (! ignore_error)) {
rc = LIBISCSI_ERR_BUG;
_error(ctx, "Sysfs: %s: Error when converting '%s' "
"to number", file_path, (char *) buff, errno_save);
@@ -207,45 +245,8 @@ static int iscsi_sysfs_prop_get_ll(struct iscsi_context *ctx,
return rc;
}
-int _sysfs_prop_get_u32(struct iscsi_context *ctx, const char *dir_path,
- const char *prop_name, uint32_t *val,
- uint32_t default_value)
-{
- long long int tmp_val = 0;
- int rc = LIBISCSI_OK;
- long long int dv = default_value;
-
- if (default_value == UINT32_MAX)
- dv = LLONG_MAX;
-
- rc = iscsi_sysfs_prop_get_ll(ctx, dir_path, prop_name, &tmp_val,
- (long long int) dv);
- if (rc == LIBISCSI_OK)
- *val = tmp_val & UINT32_MAX;
- return rc;
-}
-
-int _sysfs_prop_get_i32(struct iscsi_context *ctx, const char *dir_path,
- const char *prop_name, int32_t *val,
- int32_t default_value)
-{
- long long int tmp_val = 0;
- int rc = LIBISCSI_OK;
- long long int dv = default_value;
-
- if (default_value == INT32_MAX)
- dv = LLONG_MAX;
-
- rc = iscsi_sysfs_prop_get_ll(ctx, dir_path, prop_name, &tmp_val,
- (long long int) dv);
-
- if (rc == LIBISCSI_OK)
- *val = tmp_val & INT32_MAX;
- return rc;
-}
-
static int sysfs_get_dev_path(struct iscsi_context *ctx, const char *path,
- char *dev_path)
+ enum _sysfs_dev_class class, char *dev_path)
{
int rc = LIBISCSI_OK;
int errno_save = 0;
@@ -268,13 +269,27 @@ static int sysfs_get_dev_path(struct iscsi_context *ctx, const char *path,
goto out;
}
- reg_rc = regcomp(&regex,
- "\\(.\\{1,\\}/devices/.\\{1,\\}/host[0-9]\\{1,\\}\\)/"
- "session[0-9]\\{1,\\}/iscsi_session/",
- 0 /* no flag */);
- /* ^ BUG(Gris Ge): This is based on GUESS, should check linux kernel
- * code on this
- */
+ switch (class) {
+ case _SYSFS_DEV_CLASS_ISCSI_SESSION:
+ reg_rc = regcomp(&regex,
+ "\\(.\\{1,\\}/devices/.\\{1,\\}/"
+ "host[0-9]\\{1,\\}\\)/"
+ "session[0-9]\\{1,\\}/iscsi_session/",
+ 0 /* no flag */);
+ break;
+ case _SYSFS_DEV_CLASS_ISCSI_HOST:
+ reg_rc = regcomp(&regex,
+ "\\(.\\{1,\\}/devices/.\\{1,\\}/"
+ "host[0-9]\\{1,\\}\\)/"
+ "iscsi_host/",
+ 0 /* no flag */);
+ break;
+ default:
+ rc = LIBISCSI_ERR_BUG;
+ _error(ctx, "BUG: sysfs_get_dev_path(): got unknown class %d",
+ class);
+ goto out;
+ }
if (reg_rc != 0) {
rc = LIBISCSI_ERR_SYSFS_LOOKUP;
_error(ctx, "regcomp() failed %d", reg_rc);
@@ -310,7 +325,6 @@ int _iscsi_host_id_of_session(struct iscsi_context *ctx, uint32_t sid,
struct dirent **namelist = NULL;
int n = 0;
const char *host_id_str = NULL;
- int i = 0;
assert(ctx != NULL);
assert(sid != 0);
@@ -321,13 +335,15 @@ int _iscsi_host_id_of_session(struct iscsi_context *ctx, uint32_t sid,
*host_id = 0;
- _good(sysfs_get_dev_path(ctx, sys_se_dir_path, sys_dev_path), rc, out);
+ _good(sysfs_get_dev_path(ctx, sys_se_dir_path,
+ _SYSFS_DEV_CLASS_ISCSI_SESSION, sys_dev_path),
+ rc, out);
snprintf(sys_scsi_host_dir_path, PATH_MAX, "%s/iscsi_host/",
sys_dev_path);
- n = scandir(sys_scsi_host_dir_path, &namelist, _scan_filter_skip_dot,
- alphasort);
+ _good(_scandir(ctx, sys_scsi_host_dir_path, &namelist, &n), rc, out);
+
if (n != 1) {
rc = LIBISCSI_ERR_SYSFS_LOOKUP;
_error(ctx, "Got unexpected(should be 1) file in folder %s",
@@ -343,9 +359,143 @@ int _iscsi_host_id_of_session(struct iscsi_context *ctx, uint32_t sid,
}
out:
- for (i = n - 1; i >= 0; --i)
- free(namelist[i]);
- free(namelist);
+ _scandir_free(namelist, n);
+
+ return rc;
+}
+
+static int _iscsi_ids_get(struct iscsi_context *ctx,
+ uint32_t **ids, uint32_t *id_count,
+ const char *dir_path, const char *file_prefix)
+{
+ int rc = LIBISCSI_OK;
+ struct dirent **namelist = NULL;
+ int n = 0;
+ uint32_t i = 0;
+ const char *id_str = NULL;
+ char fmt_buff[128];
+
+ assert(ctx != NULL);
+ assert(ids != 0);
+ assert(id_count != NULL);
+
+ *ids = NULL;
+ *id_count = 0;
+
+ _good(_scandir(ctx, dir_path, &namelist, &n), rc, out);
+ _debug(ctx, "Got %d iSCSI %s", n, file_prefix);
+
+ *id_count = n & UINT32_MAX;
+
+ *ids = calloc(*id_count, sizeof(uint32_t));
+ _alloc_null_check(ctx, *ids, rc, out);
+
+ snprintf(fmt_buff, sizeof(fmt_buff)/sizeof(char), "%s%%" SCNu32,
+ file_prefix);
+
+ for (i = 0; i < *id_count; ++i) {
+ id_str = namelist[i]->d_name;
+ if (sscanf(id_str, fmt_buff, &((*ids)[i])) != 1) {
+ rc = LIBISCSI_ERR_SYSFS_LOOKUP;
+ _error(ctx, "sscanf() failed on string %s",
+ id_str);
+ goto out;
+ }
+ _debug(ctx, "Got iSCSI %s id %" PRIu32, file_prefix, (*ids)[i]);
+ }
+
+out:
+ _scandir_free(namelist, n);
+ if (rc != LIBISCSI_OK) {
+ free(*ids);
+ *ids = NULL;
+ *id_count = 0;
+ }
+ return rc;
+}
+
+int _iscsi_sids_get(struct iscsi_context *ctx, uint32_t **sids,
+ uint32_t *sid_count)
+{
+ return _iscsi_ids_get(ctx, sids, sid_count, _ISCSI_SYS_SESSION_DIR,
+ "session");
+}
+
+int _iscsi_hids_get(struct iscsi_context *ctx, uint32_t **hids,
+ uint32_t *hid_count)
+{
+ return _iscsi_ids_get(ctx, hids, hid_count, _ISCSI_SYS_HOST_DIR,
+ "host");
+}
+
+bool _iscsi_transport_is_loaded(const char *transport_name)
+{
+ char path[PATH_MAX];
+
+ if (transport_name == NULL)
+ return false;
+
+ snprintf(path, PATH_MAX, "%s/%s", _ISCSI_SYS_TRANSPORT_DIR,
+ transport_name);
+
+ if (access(path, F_OK) == 0)
+ return true;
+
+ return false;
+}
+
+int _iscsi_iface_kern_id_of_host_id(struct iscsi_context *ctx,
+ uint32_t host_id, char *iface_kern_id)
+{
+ char *sysfs_sh_path = NULL;
+ char *dev_path = NULL;
+ char *sysfs_iface_path = NULL;
+ int rc = LIBISCSI_OK;
+ struct dirent **namelist = NULL;
+ int n = 0;
+
+ sysfs_sh_path = malloc(PATH_MAX);
+ _alloc_null_check(ctx, sysfs_sh_path, rc, out);
+
+ dev_path = malloc(PATH_MAX);
+ _alloc_null_check(ctx, dev_path, rc, out);
+
+ sysfs_iface_path = malloc(PATH_MAX);
+ _alloc_null_check(ctx, sysfs_iface_path, rc, out);
+
+ snprintf(sysfs_sh_path, PATH_MAX, "%s/host%" PRIu32,
+ _ISCSI_SYS_HOST_DIR, host_id);
+
+ _good(sysfs_get_dev_path(ctx, sysfs_sh_path,
+ _SYSFS_DEV_CLASS_ISCSI_HOST, dev_path),
+ rc, out);
+
+ snprintf(sysfs_iface_path, PATH_MAX, "%s/iscsi_iface", dev_path);
+
+ _good(_scandir(ctx, sysfs_iface_path, &namelist, &n), rc, out);
+ if (n == 0) {
+ rc = LIBISCSI_ERR_SYSFS_LOOKUP;
+ _debug(ctx, "No iSCSI interface for iSCSI host %" PRIu32,
+ host_id);
+ goto out;
+ }
+
+ if (n != 1) {
+ rc = LIBISCSI_ERR_SYSFS_LOOKUP;
+ _debug(ctx, "Got unexpected(got %d, should be 1) file in "
+ "folder %s", n, sysfs_iface_path);
+ goto out;
+ }
+
+ snprintf(iface_kern_id, PATH_MAX, "%s", namelist[0]->d_name);
+ _debug(ctx, "Found iSCSI iface '%s' for iSCSI host %" PRIu32,
+ iface_kern_id, host_id);
+
+out:
+ _scandir_free(namelist, n);
+ free(sysfs_sh_path);
+ free(dev_path);
+ free(sysfs_iface_path);
return rc;
}
diff --git a/libopeniscsiusr/sysfs.h b/libopeniscsiusr/sysfs.h
index 3b501c2..e285537 100644
--- a/libopeniscsiusr/sysfs.h
+++ b/libopeniscsiusr/sysfs.h
@@ -20,6 +20,7 @@
#define __ISCSI_USR_SYSFS_H__
#include <stdint.h>
+#include <stdbool.h>
#include "libopeniscsiusr/libopeniscsiusr_common.h"
@@ -27,6 +28,7 @@
#define _ISCSI_SYS_CONNECTION_DIR "/sys/class/iscsi_connection"
#define _ISCSI_SYS_HOST_DIR "/sys/class/iscsi_host"
#define _ISCSI_SYS_IFACE_DIR "/sys/class/iscsi_iface"
+#define _ISCSI_SYS_TRANSPORT_DIR "/sys/class/iscsi_transport"
#define _SCSI_SYS_HOST_DIR "/sys/class/scsi_host"
/*
@@ -37,21 +39,52 @@ __DLL_LOCAL int _sysfs_prop_get_str(struct iscsi_context *ctx,
char *buff, size_t buff_size,
const char *default_value);
+int _sysfs_prop_get_u8(struct iscsi_context *ctx, const char *dir_path,
+ const char *prop_name, uint8_t *val,
+ uint8_t default_value, bool ignore_error);
+
+int _sysfs_prop_get_u16(struct iscsi_context *ctx, const char *dir_path,
+ const char *prop_name, uint16_t *val,
+ uint16_t default_value, bool ignore_error);
+
/*
* When default_value == UINT32_MAX, treat no such file as LIB_BUG.
*/
__DLL_LOCAL int _sysfs_prop_get_u32(struct iscsi_context *ctx,
const char *dir_path, const char *prop_name,
- uint32_t *val, uint32_t default_value);
+ uint32_t *val, uint32_t default_value,
+ bool ignore_error);
/*
* When default_value == INT32_MAX, treat no such file as LIB_BUG.
*/
__DLL_LOCAL int _sysfs_prop_get_i32(struct iscsi_context *ctx,
const char *dir_path, const char *prop_name,
- int32_t *val, int32_t default_value);
+ int32_t *val, int32_t default_value,
+ bool ignore_error);
__DLL_LOCAL int _iscsi_host_id_of_session(struct iscsi_context *ctx,
uint32_t sid, uint32_t *host_id);
+/*
+ * iface_kern_id should be char[PATH_MAX]
+ */
+__DLL_LOCAL int _iscsi_iface_kern_id_of_host_id(struct iscsi_context *ctx,
+ uint32_t host_id,
+ char *iface_kern_id);
+
+/*
+ * The memory of (uint32_t *sids) should be freed by free().
+ */
+__DLL_LOCAL int _iscsi_sids_get(struct iscsi_context *ctx,
+ uint32_t **sids, uint32_t *sid_count);
+
+/*
+ * The memory of (uint32_t *hids) should be freed by free().
+ */
+__DLL_LOCAL int _iscsi_hids_get(struct iscsi_context *ctx, uint32_t **hids,
+ uint32_t *hid_count);
+
+__DLL_LOCAL bool _iscsi_transport_is_loaded(const char *transport_name);
+
#endif /* End of __ISCSI_USR_SYSFS_H__ */
diff --git a/libopeniscsiusr/tests/test_iface.c b/libopeniscsiusr/tests/test_iface.c
new file mode 100644
index 0000000..f3ad8c6
--- /dev/null
+++ b/libopeniscsiusr/tests/test_iface.c
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenifaces/>.
+ *
+ * Author: Gris Ge <fge@redhat.com>
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <string.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <assert.h>
+
+#include <libopeniscsiusr/libopeniscsiusr.h>
+
+#define _assert_print_prop_str_can_empty(struct_name, obj, prop_name) \
+ do { \
+ assert(struct_name##_##prop_name##_get(obj) != NULL); \
+ printf("\t" # prop_name ": '%s'\n", \
+ struct_name##_##prop_name##_get(obj)); \
+ } while(0)
+
+#define _assert_print_prop_str_not_empty(struct_name, obj, prop_name) \
+ do { \
+ assert(struct_name##_##prop_name##_get(obj) != NULL); \
+ assert(strlen(struct_name##_##prop_name##_get(obj)) != 0); \
+ printf("\t" # prop_name ": '%s'\n", \
+ struct_name##_##prop_name##_get(obj)); \
+ } while(0)
+
+static void test_iface(struct iscsi_context *ctx, struct iscsi_iface *iface)
+{
+ struct iscsi_iface *tmp_iface = NULL;
+ const char *conf = NULL;
+
+ assert(iface != NULL);
+ printf("\t#### Interface info ####\n");
+ _assert_print_prop_str_not_empty(iscsi_iface, iface, name);
+ if (! iscsi_is_default_iface(iface)) {
+ assert(iscsi_iface_get(ctx, iscsi_iface_name_get(iface),
+ &tmp_iface) == LIBISCSI_OK);
+ _assert_print_prop_str_can_empty(iscsi_iface, tmp_iface,
+ ipaddress);
+ _assert_print_prop_str_can_empty(iscsi_iface, tmp_iface,
+ transport_name);
+ _assert_print_prop_str_can_empty(iscsi_iface, tmp_iface, iname);
+ _assert_print_prop_str_can_empty(iscsi_iface, tmp_iface,
+ hwaddress);
+ _assert_print_prop_str_can_empty(iscsi_iface, tmp_iface,
+ netdev);
+ _assert_print_prop_str_can_empty(iscsi_iface, tmp_iface,
+ port_state);
+ _assert_print_prop_str_can_empty(iscsi_iface, tmp_iface,
+ port_speed);
+ iscsi_iface_free(tmp_iface);
+ }
+ printf("\t########################\n");
+
+ conf = iscsi_iface_dump_config(iface);
+ assert(conf != NULL);
+ free((char *) conf);
+ iscsi_iface_print_config(iface);
+}
+
+int main()
+{
+ struct iscsi_context *ctx = NULL;
+ struct iscsi_iface **ifaces = NULL;
+ uint32_t iface_count = 0;
+ uint32_t i = 0;
+ int rc = EXIT_SUCCESS;
+
+ ctx = iscsi_context_new();
+ iscsi_context_log_priority_set(ctx, LIBISCSI_LOG_PRIORITY_DEBUG);
+
+ if (iscsi_default_iface_setup(ctx) != LIBISCSI_OK) {
+ printf("FAILED\n");
+ rc = EXIT_FAILURE;
+ }
+
+ if (iscsi_ifaces_get(ctx, &ifaces, &iface_count) != LIBISCSI_OK) {
+ printf("FAILED\n");
+ rc = EXIT_FAILURE;
+ } else {
+ assert(iface_count >= 2);
+ /* we will have at least the default ifaces:
+ * iser and iscsi_tcp
+ */
+ printf("\nGot %" PRIu32 " iSCSI ifaces\n", iface_count);
+ for (i = 0; i < iface_count; ++i) {
+ test_iface(ctx, ifaces[i]);
+ }
+ iscsi_ifaces_free(ifaces, iface_count);
+ }
+ iscsi_context_free(ctx);
+ exit(rc);
+}
diff --git a/libopeniscsiusr/version.h b/libopeniscsiusr/version.h
new file mode 100644
index 0000000..f05d159
--- /dev/null
+++ b/libopeniscsiusr/version.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017-2018 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Gris Ge <fge@redhat.com>
+ */
+
+#ifndef __ISCSI_OPEN_USR_VERSION_H__
+#define __ISCSI_OPEN_USR_VERSION_H__
+
+/*
+ * iSCSI tools version.
+ * This may not be the same value as the kernel versions because
+ * some other maintainer could merge a patch without going through us
+ */
+#define ISCSI_VERSION_STR "2.0-876"
+
+#endif /* End of __ISCSI_OPEN_USR_VERSION_H__ */