Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F107314673
D34420.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
110 KB
Referenced Files
None
Subscribers
None
D34420.diff
View Options
Index: sys/dev/iscsi/iscsi.h
===================================================================
--- sys/dev/iscsi/iscsi.h
+++ sys/dev/iscsi/iscsi.h
@@ -36,6 +36,8 @@
struct iscsi_softc;
struct icl_conn;
+MALLOC_DECLARE(M_ISCSI);
+
#define ISCSI_NAME_LEN 224 /* 223 bytes, by RFC 3720, + '\0' */
#define ISCSI_ADDR_LEN 47 /* INET6_ADDRSTRLEN + '\0' */
#define ISCSI_SECRET_LEN 17 /* 16 + '\0' */
@@ -50,6 +52,8 @@
void *io_icl_prv;
};
+struct iscsi_rchap;
+
struct iscsi_session {
TAILQ_ENTRY(iscsi_session) is_next;
@@ -90,9 +94,19 @@
bool is_waiting_for_iscsid;
/*
+ * For boot session:
+ * Trigger kernel login thread now to perform login.
+ */
+ bool is_trigger_kern_login;
+
+ /*
+ * For userland-initiated sessions:
* Some iscsid(8) instance is handling the session;
* after login_timeout expires, kernel will wake up
* another iscsid(8) to handle the session.
+ *
+ * For boot sessions:
+ * A boot session kernel thread is handling the session.
*/
bool is_login_phase;
@@ -115,6 +129,7 @@
struct cv is_maintenance_cv;
struct iscsi_softc *is_softc;
unsigned int is_id;
+ bool is_boot_session;
struct iscsi_session_conf is_conf;
bool is_simq_frozen;
@@ -124,8 +139,53 @@
struct cv is_login_cv;
struct icl_pdu *is_login_pdu;
#endif
+
+ struct {
+ struct cv bl_login_cv;
+ struct thread *bl_login_thread;
+ struct iscsi_chap *bl_mutual_chap;
+ struct sockaddr_storage bl_from_ss;
+ struct sockaddr_storage bl_to_ss;
+ } is_boot_login;
};
+#define ISCSI_DEBUG(X, ...) \
+ do { \
+ if (iscsi_debug > 1) \
+ printf("%s: " X "\n", __func__, ## __VA_ARGS__);\
+ } while (0)
+
+#define ISCSI_WARN(X, ...) \
+ do { \
+ if (iscsi_debug > 0) { \
+ printf("WARNING: %s: " X "\n", \
+ __func__, ## __VA_ARGS__); \
+ } \
+ } while (0)
+
+#define ISCSI_SESSION_DEBUG(S, X, ...) \
+ do { \
+ if (iscsi_debug > 1) { \
+ printf("%s: %s (%s): " X "\n", \
+ __func__, S->is_conf.isc_target_addr, \
+ S->is_conf.isc_target, ## __VA_ARGS__); \
+ } \
+ } while (0)
+
+#define ISCSI_SESSION_WARN(S, X, ...) \
+ do { \
+ if (iscsi_debug > 0) { \
+ printf("WARNING: %s (%s): " X "\n", \
+ S->is_conf.isc_target_addr, \
+ S->is_conf.isc_target, ## __VA_ARGS__); \
+ } \
+ } while (0)
+
+#define ISCSI_SESSION_LOCK(X) mtx_lock(&X->is_lock)
+#define ISCSI_SESSION_UNLOCK(X) mtx_unlock(&X->is_lock)
+#define ISCSI_SESSION_LOCK_ASSERT(X) mtx_assert(&X->is_lock, MA_OWNED)
+#define ISCSI_SESSION_LOCK_ASSERT_NOT(X) mtx_assert(&X->is_lock, MA_NOTOWNED)
+
struct iscsi_softc {
device_t sc_dev;
struct sx sc_lock;
@@ -138,4 +198,88 @@
eventhandler_tag sc_shutdown_post_eh;
};
+#define ISCSI_KEYS_MAX 1024
+
+struct iscsi_keys {
+ char *ik_names[ISCSI_KEYS_MAX];
+ char *ik_values[ISCSI_KEYS_MAX];
+ char *ik_data;
+ size_t ik_data_len;
+};
+
+#define CHAP_CHALLENGE_LEN 1024
+#define CHAP_DIGEST_LEN 16 /* Equal to MD5 digest size. */
+
+struct iscsi_chap {
+ unsigned char chap_id;
+ char chap_challenge[CHAP_CHALLENGE_LEN];
+ char chap_response[CHAP_DIGEST_LEN];
+};
+
+struct iscsi_rchap {
+ char *rchap_secret;
+ unsigned char rchap_id;
+ void *rchap_challenge;
+ size_t rchap_challenge_len;
+};
+
+struct iscsi_kernel_login {
+ struct iscsi_session *ikl_is;
+ struct icl_drv_limits ikl_idl;
+};
+
+struct iscsi_kernel_handoff {
+ char ikh_target_alias[ISCSI_ALIAS_LEN];
+ int ikh_protocol_level;
+ int ikh_header_digest;
+ int ikh_data_digest;
+ int ikh_immediate_data;
+ int ikh_initial_r2t;
+ int ikh_max_recv_data_segment_length;
+ int ikh_max_send_data_segment_length;
+ int ikh_max_burst_length;
+ int ikh_first_burst_length;
+ int ikh_tsid;
+};
+
+extern int iscsi_debug;
+
+struct iscsi_chap *iscsi_chap_new(void);
+char *iscsi_chap_get_id(const struct iscsi_chap *chap);
+char *iscsi_chap_get_challenge(
+ const struct iscsi_chap *chap);
+int iscsi_chap_receive(struct iscsi_chap *chap,
+ const char *response);
+int iscsi_chap_authenticate(struct iscsi_chap *chap,
+ const char *secret);
+void iscsi_chap_delete(struct iscsi_chap *chap);
+
+struct iscsi_rchap *iscsi_rchap_new(const char *secret);
+int iscsi_rchap_receive(struct iscsi_rchap *rchap,
+ const char *id, const char *challenge);
+char *iscsi_rchap_get_response(struct iscsi_rchap *rchap);
+void iscsi_rchap_delete(struct iscsi_rchap *rchap);
+
+struct iscsi_keys *iscsi_keys_new(int mflags);
+void iscsi_keys_delete(struct iscsi_keys *ik);
+int iscsi_keys_load(struct iscsi_keys *ik,
+ struct icl_pdu *ip, int mflags);
+int iscsi_keys_save(struct iscsi_keys *ik,
+ struct icl_pdu *ip, int mflags);
+const char *iscsi_keys_find(struct iscsi_keys *ik,
+ const char *name);
+int iscsi_keys_add(struct iscsi_keys *ik,
+ const char *name, const char *value, int mflags);
+int iscsi_keys_add_int(struct iscsi_keys *ik,
+ const char *name, int value, int mflags);
+
+int iscsi_login(struct iscsi_kernel_login *login,
+ struct iscsi_kernel_handoff *handoff);
+
+/* iscsi_base64.c Should be moved to libkern */
+int iscsi_b64_ntop(u_char const *src, size_t srclength,
+ char *target, size_t targsize);
+int iscsi_b64_pton(const char *src, u_char *target,
+ size_t targsize);
+
#endif /* !ISCSI_H */
Index: sys/dev/iscsi/iscsi.c
===================================================================
--- sys/dev/iscsi/iscsi.c
+++ sys/dev/iscsi/iscsi.c
@@ -38,6 +38,7 @@
#include <sys/endian.h>
#include <sys/eventhandler.h>
#include <sys/file.h>
+#include <sys/jail.h>
#include <sys/kernel.h>
#include <sys/kthread.h>
#include <sys/lock.h>
@@ -45,7 +46,10 @@
#include <sys/mbuf.h>
#include <sys/mutex.h>
#include <sys/module.h>
+#include <sys/queue.h>
#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/sockio.h>
#include <sys/sockopt.h>
#include <sys/sysctl.h>
#include <sys/systm.h>
@@ -65,14 +69,41 @@
#include <dev/iscsi/icl.h>
#include <dev/iscsi/icl_wrappers.h>
+#include <dev/iscsi/iscsi_ibft.h>
#include <dev/iscsi/iscsi_ioctl.h>
#include <dev/iscsi/iscsi_proto.h>
#include <dev/iscsi/iscsi.h>
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+#include <net/route/route_ctl.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet6/nd6.h>
+
#ifdef ICL_KERNEL_PROXY
#include <sys/socketvar.h>
#endif
+/*
+ * Boot session configuration.
+ */
+struct iscsi_boot_session_conf {
+ char isc_initiator[ISCSI_NAME_LEN];
+ struct sockaddr_storage isc_initiator_sa;
+ char isc_initiator_alias[ISCSI_ALIAS_LEN];
+ char isc_target[ISCSI_NAME_LEN];
+ struct sockaddr_storage isc_target_sa;
+ int isc_auth_type;
+ char isc_user[ISCSI_NAME_LEN];
+ char isc_secret[ISCSI_SECRET_LEN];
+ char isc_mutual_user[ISCSI_NAME_LEN];
+ char isc_mutual_secret[ISCSI_SECRET_LEN];
+};
+
#ifdef ICL_KERNEL_PROXY
FEATURE(iscsi_kernel_proxy, "iSCSI initiator built with ICL_KERNEL_PROXY");
#endif
@@ -85,9 +116,9 @@
SYSCTL_NODE(_kern, OID_AUTO, iscsi, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
"iSCSI initiator");
-static int debug = 1;
+static int iscsi_debug = 1;
SYSCTL_INT(_kern_iscsi, OID_AUTO, debug, CTLFLAG_RWTUN,
- &debug, 0, "Enable debug messages");
+ &iscsi_debug, 0, "Enable debug messages");
static int ping_timeout = 5;
SYSCTL_INT(_kern_iscsi, OID_AUTO, ping_timeout, CTLFLAG_RWTUN, &ping_timeout,
@@ -114,43 +145,6 @@
#define CONN_SESSION(X) ((struct iscsi_session *)X->ic_prv0)
#define PDU_SESSION(X) (CONN_SESSION(X->ip_conn))
-#define ISCSI_DEBUG(X, ...) \
- do { \
- if (debug > 1) \
- printf("%s: " X "\n", __func__, ## __VA_ARGS__);\
- } while (0)
-
-#define ISCSI_WARN(X, ...) \
- do { \
- if (debug > 0) { \
- printf("WARNING: %s: " X "\n", \
- __func__, ## __VA_ARGS__); \
- } \
- } while (0)
-
-#define ISCSI_SESSION_DEBUG(S, X, ...) \
- do { \
- if (debug > 1) { \
- printf("%s: %s (%s): " X "\n", \
- __func__, S->is_conf.isc_target_addr, \
- S->is_conf.isc_target, ## __VA_ARGS__); \
- } \
- } while (0)
-
-#define ISCSI_SESSION_WARN(S, X, ...) \
- do { \
- if (debug > 0) { \
- printf("WARNING: %s (%s): " X "\n", \
- S->is_conf.isc_target_addr, \
- S->is_conf.isc_target, ## __VA_ARGS__); \
- } \
- } while (0)
-
-#define ISCSI_SESSION_LOCK(X) mtx_lock(&X->is_lock)
-#define ISCSI_SESSION_UNLOCK(X) mtx_unlock(&X->is_lock)
-#define ISCSI_SESSION_LOCK_ASSERT(X) mtx_assert(&X->is_lock, MA_OWNED)
-#define ISCSI_SESSION_LOCK_ASSERT_NOT(X) mtx_assert(&X->is_lock, MA_NOTOWNED)
-
static int iscsi_ioctl(struct cdev *dev, u_long cmd, caddr_t arg,
int mode, struct thread *td);
@@ -167,6 +161,7 @@
static void iscsi_pdu_handle_scsi_response(struct icl_pdu *response);
static void iscsi_pdu_handle_task_response(struct icl_pdu *response);
static void iscsi_pdu_handle_data_in(struct icl_pdu *response);
+static void iscsi_pdu_handle_login_response(struct icl_pdu *response);
static void iscsi_pdu_handle_logout_response(struct icl_pdu *response);
static void iscsi_pdu_handle_r2t(struct icl_pdu *response);
static void iscsi_pdu_handle_async_message(struct icl_pdu *response);
@@ -181,6 +176,7 @@
uint32_t *initiator_task_tagp);
static void iscsi_outstanding_remove(struct iscsi_session *is,
struct iscsi_outstanding *io);
+static void iscsi_boot_login_thread(void *);
static bool
iscsi_pdu_prepare(struct icl_pdu *request)
@@ -437,15 +433,25 @@
return;
}
- /*
- * Request immediate reconnection from iscsid(8).
- */
- //ISCSI_SESSION_DEBUG(is, "waking up iscsid(8)");
- is->is_waiting_for_iscsid = true;
- strlcpy(is->is_reason, "Waiting for iscsid(8)", sizeof(is->is_reason));
- is->is_timeout = 0;
- ISCSI_SESSION_UNLOCK(is);
- cv_signal(&is->is_softc->sc_cv);
+ if (!is->is_boot_session) {
+ /*
+ * Request immediate reconnection from iscsid(8).
+ */
+ //ISCSI_SESSION_DEBUG(is, "waking up iscsid(8)");
+ is->is_waiting_for_iscsid = true;
+ strlcpy(is->is_reason, "Waiting for iscsid(8)",
+ sizeof(is->is_reason));
+ is->is_timeout = 0;
+ ISCSI_SESSION_UNLOCK(is);
+ cv_signal(&is->is_softc->sc_cv);
+ } else {
+ is->is_trigger_kern_login = true;
+ strlcpy(is->is_reason, "Boot session connecting",
+ sizeof(is->is_reason));
+ is->is_timeout = 0;
+ ISCSI_SESSION_UNLOCK(is);
+ cv_signal(&is->is_boot_login.bl_login_cv);
+ }
}
static void
@@ -458,6 +464,13 @@
TAILQ_REMOVE(&sc->sc_sessions, is, is_next);
sx_xunlock(&sc->sc_lock);
+ ISCSI_SESSION_LOCK(is);
+ if (is->is_boot_session && is->is_boot_login.bl_login_thread != NULL) {
+ cv_signal(&is->is_boot_login.bl_login_cv);
+ cv_wait(&is->is_boot_login.bl_login_cv, &is->is_lock);
+ }
+ ISCSI_SESSION_UNLOCK(is);
+
icl_conn_close(is->is_conn);
callout_drain(&is->is_callout);
@@ -488,6 +501,8 @@
#ifdef ICL_KERNEL_PROXY
cv_destroy(&is->is_login_cv);
#endif
+ if (is->is_boot_session)
+ cv_destroy(&is->is_boot_login.bl_login_cv);
ISCSI_SESSION_DEBUG(is, "terminated");
free(is, M_ISCSI);
@@ -787,6 +802,10 @@
/* Session lock dropped inside. */
ISCSI_SESSION_LOCK_ASSERT_NOT(is);
break;
+ case ISCSI_BHS_OPCODE_LOGIN_RESPONSE:
+ iscsi_pdu_handle_login_response(response);
+ ISCSI_SESSION_UNLOCK(is);
+ break;
case ISCSI_BHS_OPCODE_LOGOUT_RESPONSE:
iscsi_pdu_handle_logout_response(response);
ISCSI_SESSION_UNLOCK(is);
@@ -1206,6 +1225,14 @@
icl_pdu_free(response);
}
+static void
+iscsi_pdu_handle_login_response(struct icl_pdu *response)
+{
+
+ ISCSI_SESSION_DEBUG(PDU_SESSION(response), "login response");
+ icl_pdu_free(response);
+}
+
static void
iscsi_pdu_handle_logout_response(struct icl_pdu *response)
{
@@ -1460,6 +1487,60 @@
}
}
+static int
+iscsi_prepare_sim_post_login(struct iscsi_session *is)
+{
+ int error;
+
+ if (is->is_sim != NULL) {
+ /*
+ * When reconnecting, there already is SIM allocated for the
+ * session.
+ */
+ KASSERT(is->is_simq_frozen, ("reconnect without frozen simq"));
+ ISCSI_SESSION_LOCK(is);
+ ISCSI_SESSION_DEBUG(is, "releasing");
+ is->is_simq_frozen = false;
+ xpt_release_simq(is->is_sim, 1);
+ ISCSI_SESSION_UNLOCK(is);
+ } else {
+ ISCSI_SESSION_LOCK(is);
+ is->is_devq = cam_simq_alloc(is->is_conn->ic_maxtags);
+ if (is->is_devq == NULL) {
+ ISCSI_SESSION_UNLOCK(is);
+ ISCSI_SESSION_WARN(is, "failed to allocate simq");
+ return (ENOMEM);
+ }
+
+ is->is_sim = cam_sim_alloc(iscsi_action, NULL, "iscsi",
+ is, is->is_id /* unit */, &is->is_lock,
+ 1, is->is_conn->ic_maxtags, is->is_devq);
+ if (is->is_sim == NULL) {
+ ISCSI_SESSION_UNLOCK(is);
+ ISCSI_SESSION_WARN(is, "failed to allocate SIM");
+ cam_simq_free(is->is_devq);
+ return (ENOMEM);
+ }
+
+ if (xpt_bus_register(is->is_sim, NULL, 0) != 0) {
+ ISCSI_SESSION_UNLOCK(is);
+ ISCSI_SESSION_WARN(is, "failed to register bus");
+ return (ENOMEM);
+ }
+
+ error = xpt_create_path(&is->is_path, /*periph*/NULL,
+ cam_sim_path(is->is_sim), CAM_TARGET_WILDCARD,
+ CAM_LUN_WILDCARD);
+ if (error != CAM_REQ_CMP) {
+ ISCSI_SESSION_UNLOCK(is);
+ ISCSI_SESSION_WARN(is, "failed to create path");
+ return (ENOMEM);
+ }
+ ISCSI_SESSION_UNLOCK(is);
+ }
+ return (0);
+}
+
static int
iscsi_ioctl_daemon_handoff(struct iscsi_softc *sc,
struct iscsi_daemon_handoff *handoff)
@@ -1557,55 +1638,10 @@
sx_sunlock(&sc->sc_lock);
- if (is->is_sim != NULL) {
- /*
- * When reconnecting, there already is SIM allocated for the session.
- */
- KASSERT(is->is_simq_frozen, ("reconnect without frozen simq"));
- ISCSI_SESSION_LOCK(is);
- ISCSI_SESSION_DEBUG(is, "releasing");
- is->is_simq_frozen = false;
- xpt_release_simq(is->is_sim, 1);
- ISCSI_SESSION_UNLOCK(is);
-
- } else {
- ISCSI_SESSION_LOCK(is);
- is->is_devq = cam_simq_alloc(ic->ic_maxtags);
- if (is->is_devq == NULL) {
- ISCSI_SESSION_UNLOCK(is);
- ISCSI_SESSION_WARN(is, "failed to allocate simq");
- iscsi_session_terminate(is);
- return (ENOMEM);
- }
-
- is->is_sim = cam_sim_alloc(iscsi_action, NULL, "iscsi",
- is, is->is_id /* unit */, &is->is_lock,
- 1, ic->ic_maxtags, is->is_devq);
- if (is->is_sim == NULL) {
- ISCSI_SESSION_UNLOCK(is);
- ISCSI_SESSION_WARN(is, "failed to allocate SIM");
- cam_simq_free(is->is_devq);
- iscsi_session_terminate(is);
- return (ENOMEM);
- }
-
- if (xpt_bus_register(is->is_sim, NULL, 0) != 0) {
- ISCSI_SESSION_UNLOCK(is);
- ISCSI_SESSION_WARN(is, "failed to register bus");
- iscsi_session_terminate(is);
- return (ENOMEM);
- }
-
- error = xpt_create_path(&is->is_path, /*periph*/NULL,
- cam_sim_path(is->is_sim), CAM_TARGET_WILDCARD,
- CAM_LUN_WILDCARD);
- if (error != CAM_REQ_CMP) {
- ISCSI_SESSION_UNLOCK(is);
- ISCSI_SESSION_WARN(is, "failed to create path");
- iscsi_session_terminate(is);
- return (ENOMEM);
- }
- ISCSI_SESSION_UNLOCK(is);
+ error = iscsi_prepare_sim_post_login(is);
+ if (error) {
+ iscsi_session_terminate(is);
+ return (error);
}
return (0);
@@ -1975,6 +2011,165 @@
return (0);
}
+static bool
+iscsi_is_addr_v4mapped(uint8_t *addr)
+{
+ uint32_t *addr4 = (uint32_t *)addr;
+
+ if (be32toh(addr4[0]) == 0 && be32toh(addr4[1]) == 0 &&
+ be32toh(addr4[2]) == 0x0000ffffU)
+ return (true);
+ return (false);
+}
+
+static void
+iscsi_sin2p(char *dst, const struct sockaddr_storage *sa, bool is_tgt __unused)
+{
+ const struct sockaddr_in *sin;
+ const struct sockaddr_in6 *sin6;
+
+ KASSERT(sa->ss_family == AF_INET || sa->ss_family == AF_INET6,
+ ("%s: Bogus %s address family", __func__,
+ is_tgt ? "target" : "initiator"));
+ if (sa->ss_family == AF_INET) {
+ KASSERT(sa->ss_len == sizeof(*sin),
+ ("%s: Bogus initiator sockaddr size", __func__));
+ sin = (const struct sockaddr_in *)sa;
+ inet_ntop(AF_INET, &sin->sin_addr, dst, ISCSI_ADDR_LEN);
+ } else {
+ KASSERT(sa->ss_len == sizeof(*sin6),
+ ("%s: Bogus initiator sockaddr size", __func__));
+ sin6 = (const struct sockaddr_in6 *)sa;
+ inet_ntop(AF_INET6, &sin6->sin6_addr, dst, ISCSI_ADDR_LEN);
+ }
+}
+
+static void
+iscsi_fill_is_conf(struct iscsi_session_conf *dst,
+ const struct iscsi_boot_session_conf *src)
+{
+ bzero(dst, sizeof(*dst));
+ strlcpy(dst->isc_initiator, src->isc_initiator,
+ sizeof(dst->isc_initiator));
+ iscsi_sin2p(dst->isc_initiator_addr, &src->isc_initiator_sa, false);
+ strlcpy(dst->isc_target, src->isc_target, sizeof(dst->isc_target));
+ iscsi_sin2p(dst->isc_target_addr, &src->isc_target_sa, true);
+ if (src->isc_auth_type == IBFT_CHAP_CHAP ||
+ src->isc_auth_type == IBFT_CHAP_MUTUAL) {
+ strlcpy(dst->isc_user, src->isc_user, sizeof(dst->isc_user));
+ strlcpy(dst->isc_secret, src->isc_secret,
+ sizeof(dst->isc_secret));
+ if (src->isc_auth_type == IBFT_CHAP_MUTUAL) {
+ strlcpy(dst->isc_mutual_user, src->isc_mutual_user,
+ sizeof(dst->isc_mutual_user));
+ strlcpy(dst->isc_mutual_secret, src->isc_mutual_secret,
+ sizeof(dst->isc_mutual_secret));
+ }
+ }
+ dst->isc_discovery = 0;
+ dst->isc_enable = 1;
+}
+
+static int
+iscsi_boot_session_add(struct iscsi_softc *sc,
+ struct iscsi_boot_session_conf *conf)
+{
+ struct iscsi_session *is, *is2;
+ int error;
+
+ is = malloc(sizeof(*is), M_ISCSI, M_ZERO | M_WAITOK);
+ iscsi_fill_is_conf(&is->is_conf, conf);
+
+ sx_xlock(&sc->sc_lock);
+
+ /*
+ * Prevent duplicates.
+ */
+ TAILQ_FOREACH(is2, &sc->sc_sessions, is_next) {
+ if (!!is->is_conf.isc_discovery !=
+ !!is2->is_conf.isc_discovery)
+ continue;
+
+ if (strcmp(is->is_conf.isc_target_addr,
+ is2->is_conf.isc_target_addr) != 0)
+ continue;
+
+ if (is->is_conf.isc_discovery == 0 &&
+ strcmp(is->is_conf.isc_target,
+ is2->is_conf.isc_target) != 0)
+ continue;
+
+ sx_xunlock(&sc->sc_lock);
+ free(is, M_ISCSI);
+ return (EBUSY);
+ }
+
+ is->is_boot_login.bl_from_ss = conf->isc_initiator_sa;
+ is->is_boot_login.bl_to_ss = conf->isc_target_sa;
+
+ /*
+ * XXX: There should be no offload and iSER for now.
+ * Offloading is actually desirable.
+ */
+ is->is_conn = icl_new_conn(is->is_conf.isc_offload,
+ is->is_conf.isc_iser, "iscsi", &is->is_lock);
+ if (is->is_conn == NULL) {
+ sx_xunlock(&sc->sc_lock);
+ free(is, M_ISCSI);
+ return (EINVAL);
+ }
+ is->is_conn->ic_receive = iscsi_receive_callback;
+ is->is_conn->ic_error = iscsi_error_callback;
+ is->is_conn->ic_prv0 = is;
+ TAILQ_INIT(&is->is_outstanding);
+ STAILQ_INIT(&is->is_postponed);
+ mtx_init(&is->is_lock, "iscsi_lock", NULL, MTX_DEF);
+ cv_init(&is->is_maintenance_cv, "iscsi_mt");
+#ifdef ICL_KERNEL_PROXY
+ cv_init(&is->is_login_cv, "iscsi_login");
+#endif
+ is->is_boot_session = true;
+ cv_init(&is->is_boot_login.bl_login_cv, "iscsi_boot_login");
+
+ /*
+ * Set some default values, from RFC 3720, section 12.
+ *
+ * These values are updated by the handoff IOCTL, but are
+ * needed prior to the handoff to support sending the ISER
+ * login PDU.
+ */
+ is->is_conn->ic_max_recv_data_segment_length = 8192;
+ is->is_conn->ic_max_send_data_segment_length = 8192;
+ is->is_max_burst_length = 262144;
+ is->is_first_burst_length = 65536;
+
+ is->is_softc = sc;
+ sc->sc_last_session_id++;
+ is->is_id = sc->sc_last_session_id;
+ is->is_isid[0] = 0x80; /* RFC 3720, 10.12.5: 10b, "Random" ISID. */
+ arc4rand(&is->is_isid[1], 5, 0);
+ is->is_tsih = 0;
+ callout_init(&is->is_callout, 1);
+
+ error = kthread_add(iscsi_maintenance_thread, is, NULL, NULL, 0, 0, "iscsimt");
+ if (error != 0) {
+ ISCSI_SESSION_WARN(is, "kthread_add(9) failed with error %d", error);
+ sx_xunlock(&sc->sc_lock);
+ return (error);
+ }
+
+ callout_reset(&is->is_callout, 1 * hz, iscsi_callout, is);
+ TAILQ_INSERT_TAIL(&sc->sc_sessions, is, is_next);
+
+ is->is_trigger_kern_login = true;
+ error = kthread_add(iscsi_boot_login_thread, is, NULL,
+ &is->is_boot_login.bl_login_thread, 0, 0, "iscsiblt");
+ if (error != 0)
+ ISCSI_SESSION_WARN(is, "kthread_add(9) failed with error %d", error);
+ sx_xunlock(&sc->sc_lock);
+ return (error);
+}
+
static bool
iscsi_session_conf_matches(unsigned int id1, const struct iscsi_session_conf *c1,
unsigned int id2, const struct iscsi_session_conf *c2)
@@ -2003,8 +2198,10 @@
sx_xlock(&sc->sc_lock);
TAILQ_FOREACH_SAFE(is, &sc->sc_sessions, is_next, tmp) {
ISCSI_SESSION_LOCK(is);
+ /* Do not remove boot sessions when wildcard matching */
if (iscsi_session_conf_matches(is->is_id, &is->is_conf,
- isr->isr_session_id, &isr->isr_conf)) {
+ isr->isr_session_id, &isr->isr_conf) &&
+ !(isr->isr_session_id == 0 && is->is_boot_session)) {
found = true;
iscsi_session_logout(is);
iscsi_session_terminate(is);
@@ -2616,6 +2813,430 @@
}
}
+static void
+iscsi_boot_login_thread(void *arg)
+{
+ struct sockaddr_storage from_ss, to_ss;
+ struct sockaddr *from_sap;
+ struct iscsi_kernel_handoff handoff;
+ struct iscsi_kernel_login login;
+ struct iscsi_session *is;
+ struct icl_conn *ic;
+ int error;
+
+ is = (struct iscsi_session *)arg;
+ KASSERT(is->is_boot_session, ("%s: launched on user session",
+ __func__));
+
+ ISCSI_SESSION_LOCK(is);
+ ic = is->is_conn;
+ do {
+ while (!is->is_trigger_kern_login && !is->is_terminating)
+ cv_wait(&is->is_boot_login.bl_login_cv, &is->is_lock);
+ if (is->is_terminating)
+ break;
+
+ is->is_trigger_kern_login = false;
+ is->is_login_phase = true;
+ memcpy(&from_ss, &is->is_boot_login.bl_from_ss,
+ sizeof(from_ss));
+ memcpy(&to_ss, &is->is_boot_login.bl_to_ss, sizeof(to_ss));
+ is->is_statsn = 0;
+ is->is_cmdsn = 0;
+ is->is_expcmdsn = 0;
+ is->is_maxcmdsn = 0;
+ is->is_timeout = 0;
+ is->is_reason[0] = '\0';
+
+ /*
+ * Digests are always disabled during login phase.
+ */
+ ic->ic_header_crc32c = false;
+ ic->ic_data_crc32c = false;
+
+ memset(&handoff, 0, sizeof(handoff));
+ handoff.ikh_header_digest = ISCSI_DIGEST_NONE;
+ handoff.ikh_data_digest = ISCSI_DIGEST_NONE;
+ handoff.ikh_immediate_data = true;
+ ic->ic_max_recv_data_segment_length =
+ handoff.ikh_max_recv_data_segment_length = 8192;
+ ic->ic_max_send_data_segment_length =
+ handoff.ikh_max_send_data_segment_length = 8192;
+ handoff.ikh_max_burst_length = 262144;
+ handoff.ikh_first_burst_length = 65536;
+ handoff.ikh_tsid = 0;
+ ISCSI_SESSION_UNLOCK(is);
+
+ if (from_ss.ss_len != 0)
+ from_sap = (struct sockaddr *)&from_ss;
+ else
+ from_sap = NULL;
+ error = icl_conn_connect(ic, to_ss.ss_family, SOCK_STREAM, 0,
+ from_sap, (struct sockaddr *)&to_ss);
+ if (error != 0) {
+ ISCSI_SESSION_LOCK(is);
+ snprintf(is->is_reason, sizeof(is->is_reason),
+ "Failed to connect to endpoint. error: %d", error);
+ ISCSI_SESSION_DEBUG(is, "%s", is->is_reason);
+ ISCSI_SESSION_UNLOCK(is);
+ goto notify;
+ }
+ login.ikl_is = is;
+ error = icl_limits(ic->ic_offload, ic->ic_iser, &login.ikl_idl);
+ if (error != 0) {
+ ISCSI_SESSION_LOCK(is);
+ snprintf(is->is_reason, sizeof(is->is_reason),
+ "icl_limits for offload \"%s\" "
+ "failed with error %d", is->is_conf.isc_offload,
+ error);
+ ISCSI_SESSION_UNLOCK(is);
+ goto notify;
+ }
+
+ ISCSI_SESSION_LOCK(is);
+ if (is->is_terminating)
+ break;
+ do {
+ error = iscsi_login(&login, &handoff);
+ } while (error == EAGAIN);
+ if (error != 0) {
+ ISCSI_SESSION_UNLOCK(is);
+ goto notify;
+ }
+
+ strlcpy(is->is_target_alias, handoff.ikh_target_alias,
+ sizeof(is->is_target_alias));
+ is->is_protocol_level = handoff.ikh_protocol_level;
+ is->is_initial_r2t = handoff.ikh_initial_r2t;
+ is->is_immediate_data = handoff.ikh_immediate_data;
+
+ ic->ic_max_recv_data_segment_length =
+ handoff.ikh_max_recv_data_segment_length;
+ ic->ic_max_send_data_segment_length =
+ handoff.ikh_max_send_data_segment_length;
+ is->is_max_burst_length = handoff.ikh_max_burst_length;
+ is->is_first_burst_length = handoff.ikh_first_burst_length;
+
+ if (handoff.ikh_header_digest == ISCSI_DIGEST_CRC32C)
+ ic->ic_header_crc32c = true;
+ else
+ ic->ic_header_crc32c = false;
+ if (handoff.ikh_data_digest == ISCSI_DIGEST_CRC32C)
+ ic->ic_data_crc32c = true;
+ else
+ ic->ic_data_crc32c = false;
+ ic->ic_maxtags = maxtags;
+
+ is->is_cmdsn = 0;
+ is->is_expcmdsn = 0;
+ is->is_maxcmdsn = 0;
+ is->is_waiting_for_iscsid = false;
+ is->is_login_phase = false;
+ is->is_timeout = 0;
+ is->is_connected = true;
+ is->is_reason[0] = '\0';
+ ISCSI_SESSION_UNLOCK(is);
+
+ error = iscsi_prepare_sim_post_login(is);
+ if (error) {
+ ISCSI_SESSION_LOCK(is);
+ iscsi_session_terminate(is);
+ break;
+ }
+
+notify:
+ ISCSI_SESSION_LOCK(is);
+ cv_signal(&is->is_boot_login.bl_login_cv);
+ } while (1);
+
+ is->is_boot_login.bl_login_thread = NULL;
+ cv_signal(&is->is_boot_login.bl_login_cv);
+ ISCSI_SESSION_UNLOCK(is);
+ kthread_exit();
+}
+
+static u_int
+iscsi_match_lladdr(void *arg, struct sockaddr_dl *sadl, u_int count)
+{
+ struct ibft_i_nic_s *nic;
+
+ nic = (struct ibft_i_nic_s *)arg;
+ if (bcmp(nic->ns_mac, LLADDR(sadl), sizeof(nic->ns_mac)) == 0)
+ return (1);
+ return (0);
+}
+
+static int
+iscsi_ibft_configure_nic(struct ibft_i_nic_s *nic)
+{
+ struct sockaddr_in gwsa, gwmasksa, zerodstsa;
+ struct sockaddr_in6 gwsa6, gwmasksa6, zerodstsa6;
+ struct sockaddr *gwsap, *gwmasksap, *zerodstsap;
+ struct in_aliasreq ifra;
+ struct in6_aliasreq ifra6;
+ struct rt_addrinfo info;
+ struct rib_cmd_info rc;
+ struct ifnet *ifp;
+ struct socket *so;
+ struct thread *td;
+ struct ifreq ifr;
+ void *ifrap;
+ int prefixlen;
+ u_long ioc;
+ bool is_v6;
+ int error;
+ int i;
+
+ td = curthread;
+ prefixlen = nic->ns_prefixlen;
+ ifrap = &ifra6;
+ ioc = SIOCAIFADDR_IN6;
+ is_v6 = !iscsi_is_addr_v4mapped(nic->ns_ip);
+ bzero(&gwsa, sizeof(gwsa));
+ bzero(&gwsa6, sizeof(gwsa6));
+ bzero(&gwmasksa, sizeof(gwmasksa));
+ bzero(&gwmasksa6, sizeof(gwmasksa6));
+ bzero(&zerodstsa, sizeof(zerodstsa));
+ bzero(&zerodstsa6, sizeof(zerodstsa6));
+ gwsap = sin6tosa(&gwsa6);
+ gwmasksap = sin6tosa(&gwmasksa6);
+ zerodstsap = sin6tosa(&zerodstsa6);
+ bzero(&ifra6, sizeof(ifra6));
+ bzero(&ifra, sizeof(ifra));
+ bzero(&ifr, sizeof(ifr));
+
+ CURVNET_SET(TD_TO_VNET(td));
+ IFNET_RLOCK();
+ CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link) {
+ if (if_foreach_lladdr(ifp, iscsi_match_lladdr, nic) != 0) {
+ strlcpy(ifr.ifr_name, ifp->if_xname,
+ sizeof(ifr.ifr_name));
+ break;
+ }
+ }
+ IFNET_RUNLOCK();
+ CURVNET_RESTORE();
+ if (ifr.ifr_name[0] == '\0') {
+ ISCSI_WARN("No matching MAC for NIC%hhu."
+ "MAC wanted: %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
+ nic->ns_idx, nic->ns_mac[0], nic->ns_mac[1], nic->ns_mac[2],
+ nic->ns_mac[3], nic->ns_mac[4], nic->ns_mac[5]);
+ return (ENODEV);
+ }
+
+ error = socreate(AF_INET, &so, SOCK_DGRAM, 0, td->td_ucred, td);
+ if (error != 0) {
+ ISCSI_WARN("Failed creating socket");
+ return (error);
+ }
+ ISCSI_DEBUG("Finished creating socket for addr setting");
+
+ /* Bring up the interface so we can set addr */
+ error = ifioctl(so, SIOCGIFFLAGS, (caddr_t)&ifr, td);
+ if (error) {
+ ISCSI_WARN("ifioctl SIOCGIFFLAGS\n");
+ goto out;
+ }
+ ISCSI_DEBUG("Finished getting interface flags");
+ ifr.ifr_flags |= IFF_UP;
+ error = ifioctl(so, SIOCSIFFLAGS, (caddr_t)&ifr, td);
+ if (error) {
+ ISCSI_WARN("ifioctl SIOCSIFFLAGS\n");
+ goto out;
+ }
+ ISCSI_DEBUG("Finished bringing up interface");
+
+ ifra6.ifra_addr.sin6_len = sizeof(ifra6.ifra_addr);
+ ifra6.ifra_addr.sin6_family = AF_INET6;
+ memcpy(&ifra6.ifra_addr.sin6_addr, nic->ns_ip,
+ sizeof(ifra6.ifra_addr.sin6_addr));
+ if (!is_v6) {
+ strlcpy(ifra.ifra_name, ifr.ifr_name,
+ sizeof(ifra.ifra_name));
+ in6_sin6_2_sin(&ifra.ifra_addr, &ifra6.ifra_addr);
+ ifra.ifra_mask.sin_len = sizeof(ifra.ifra_mask);
+ ifra.ifra_mask.sin_family = AF_INET;
+ if (prefixlen > 32) {
+ error = EINVAL;
+ goto out;
+ }
+ ifra.ifra_mask.sin_addr.s_addr =
+ htonl(~0 << (32 - prefixlen));
+ bcopy(&ifra.ifra_addr, &ifra.ifra_broadaddr,
+ sizeof(ifra.ifra_broadaddr));
+ ifra.ifra_broadaddr.sin_addr.s_addr |=
+ ~ifra.ifra_mask.sin_addr.s_addr;
+ ifrap = &ifra;
+ ioc = SIOCAIFADDR;
+ ISCSI_DEBUG("Interface %s - IP: %hhu.%hhu.%hhu.%hhu. "
+ "Netmask: %hhu.%hhu.%hhu.%hhu",
+ ifra.ifra_name,
+ ((char *)&ifra.ifra_addr.sin_addr.s_addr)[0],
+ ((char *)&ifra.ifra_addr.sin_addr.s_addr)[1],
+ ((char *)&ifra.ifra_addr.sin_addr.s_addr)[2],
+ ((char *)&ifra.ifra_addr.sin_addr.s_addr)[3],
+ ((char *)&ifra.ifra_mask.sin_addr.s_addr)[0],
+ ((char *)&ifra.ifra_mask.sin_addr.s_addr)[1],
+ ((char *)&ifra.ifra_mask.sin_addr.s_addr)[2],
+ ((char *)&ifra.ifra_mask.sin_addr.s_addr)[3]);
+ } else {
+ strlcpy(ifra6.ifra_name, ifr.ifr_name,
+ sizeof(ifra6.ifra_name));
+ ifra6.ifra_prefixmask.sin6_len = sizeof(ifra6.ifra_prefixmask);
+ ifra6.ifra_prefixmask.sin6_family = AF_INET6;
+ if (prefixlen > 128) {
+ error = EINVAL;
+ goto out;
+ }
+ for (i = 0; i < prefixlen / 8; i++)
+ ifra6.ifra_prefixmask.sin6_addr.s6_addr[i] = 0xff;
+ if (prefixlen % 8 != 0) {
+ ifra6.ifra_prefixmask.sin6_addr.s6_addr[i] =
+ ~0 << (8 - (prefixlen % 8));
+ }
+ ifra6.ifra_lifetime.ia6t_expire = 0;
+ ifra6.ifra_lifetime.ia6t_preferred = 0;
+ ifra6.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
+ ifra6.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
+ }
+ error = ifioctl(so, ioc, ifrap, td);
+ if (error != 0) {
+ ISCSI_WARN("Failed setting interface addr. error %d",
+ error);
+ goto out;
+ }
+ ISCSI_DEBUG("Finished addr setting");
+
+ if (bcmp(nic->ns_gateway, &in6addr_any, sizeof(in6addr_any))) {
+ /*
+ * Gateway is set in the NIC
+ */
+
+ gwsa6.sin6_len = sizeof(gwsa);
+ gwsa6.sin6_family = AF_INET6;
+ memcpy(&gwsa6.sin6_addr, nic->ns_ip, sizeof(gwsa6.sin6_addr));
+ gwmasksa6.sin6_len = sizeof(gwsa6);
+ gwmasksa6.sin6_family = AF_INET6;
+ zerodstsa6.sin6_len = sizeof(zerodstsa6);
+ zerodstsa6.sin6_family = AF_INET6;
+ if (!is_v6) {
+ in6_sin6_2_sin(&gwsa, &gwsa6);
+ in6_sin6_2_sin(&gwmasksa, &gwmasksa6);
+ in6_sin6_2_sin(&zerodstsa, &zerodstsa6);
+
+ gwsap = sintosa(&gwsa);
+ gwmasksap = sintosa(&gwmasksa);
+ zerodstsap = sintosa(&zerodstsa);
+ }
+
+ info.rti_flags = RTF_UP | RTF_GATEWAY;
+ info.rti_info[RTAX_DST] = zerodstsap;
+ info.rti_info[RTAX_GATEWAY] = gwsap;
+ info.rti_info[RTAX_NETMASK] = gwmasksap;
+ error = rib_action(RT_DEFAULT_FIB, RTM_ADD, &info, &rc);
+ if (error != 0) {
+ ISCSI_WARN("Failed setting up routing table. error %d",
+ error);
+ goto out;
+ }
+ }
+out:
+ soclose(so);
+ return (error);
+}
+
+static int
+iscsi_ibft_add_target(struct ibft_i_tgt_s *target)
+{
+ struct iscsi_boot_session_conf *conf;
+ struct sockaddr_in6 srcsa6, dstsa6;
+ struct ibft_i_nic_s *nic;
+ bool is_v4;
+ int error;
+
+ conf = (struct iscsi_boot_session_conf *)malloc(sizeof(*conf), M_ISCSI,
+ M_ZERO | M_NOWAIT);
+ if (conf == NULL)
+ return (ENOMEM);
+ nic = &ibft_nics[target->ts_nic_idx];
+ if (!nic->ns_present) {
+ ISCSI_WARN("No target presented in the IBFT table");
+ error = ENODEV;
+ goto out;
+ }
+ is_v4 = iscsi_is_addr_v4mapped(target->ts_ip);
+
+ error = iscsi_ibft_configure_nic(nic);
+ if (error != 0) {
+ ISCSI_WARN("Failed to configure NIC%hhu for iSCSI target%hhu",
+ target->ts_nic_idx, target->ts_idx);
+ goto out;
+ }
+ ISCSI_DEBUG("Finished configuring NIC%hhu for iSCSI target%hhu",
+ target->ts_nic_idx, target->ts_idx);
+
+ bzero(&srcsa6, sizeof(srcsa6));
+ srcsa6.sin6_len = sizeof(srcsa6);
+ srcsa6.sin6_family = AF_INET6;
+ memcpy(&srcsa6.sin6_addr, nic->ns_ip, sizeof(nic->ns_ip));
+ if (is_v4) {
+ in6_sin6_2_sin((struct sockaddr_in *)&conf->isc_initiator_sa,
+ &srcsa6);
+ } else {
+ memcpy(&conf->isc_initiator_sa, &srcsa6, sizeof(srcsa6));
+ }
+
+ iscsi_ibft_getstr(conf->isc_initiator,
+ sizeof(conf->isc_initiator), ibft_initiator->is_name,
+ ibft_initiator->is_name_len);
+ iscsi_ibft_getstr(conf->isc_target,
+ sizeof(conf->isc_target), target->ts_tgt_name,
+ target->ts_tgt_name_len);
+ bzero(&dstsa6, sizeof(dstsa6));
+ dstsa6.sin6_len = sizeof(dstsa6);
+ dstsa6.sin6_family = AF_INET6;
+ dstsa6.sin6_port = htons(target->ts_port);
+ memcpy(&dstsa6.sin6_addr.s6_addr, target->ts_ip, sizeof(target->ts_ip));
+ if (is_v4) {
+ in6_sin6_2_sin((struct sockaddr_in *)&conf->isc_target_sa,
+ &dstsa6);
+ } else {
+ memcpy(&conf->isc_target_sa, &dstsa6, sizeof(dstsa6));
+ }
+ conf->isc_auth_type = target->ts_chap_type;
+ iscsi_ibft_getstr(conf->isc_user, sizeof(conf->isc_user),
+ target->ts_chap_name, target->ts_chap_name_len);
+ iscsi_ibft_getstr(conf->isc_secret, sizeof(conf->isc_secret),
+ target->ts_chap_secret, target->ts_chap_secret_len);
+ iscsi_ibft_getstr(conf->isc_mutual_user, sizeof(conf->isc_mutual_user),
+ target->ts_rchap_name, target->ts_rchap_name_len);
+ iscsi_ibft_getstr(conf->isc_mutual_secret,
+ sizeof(conf->isc_mutual_secret), target->ts_rchap_secret,
+ target->ts_rchap_secret_len);
+
+ error = iscsi_boot_session_add(sc, conf);
+ if (error)
+ ISCSI_WARN("Failed to add iSCSI target %s", conf->isc_target);
+ ISCSI_DEBUG("Finished adding iSCSI target %s", conf->isc_target);
+
+out:
+ free(conf, M_ISCSI);
+ return (error);
+}
+
+static void
+iscsi_ibft_sysinit(void *arg __unused)
+{
+ struct ibft_i_tgt_s *target;
+
+ iscsi_ibft_init();
+ TAILQ_FOREACH(target, &ibft_targets_list, ts_entry) {
+ iscsi_ibft_add_target(target);
+ }
+}
+SYSINIT(iscsi_ibft_sysinit, SI_SUB_LAST, SI_ORDER_ANY, iscsi_ibft_sysinit,
+ NULL);
+
static int
iscsi_load(void)
{
@@ -2656,6 +3277,8 @@
iscsi_unload(void)
{
+ iscsi_ibft_fini();
+
/* Awaken any threads asleep in iscsi_ioctl(). */
sx_xlock(&sc->sc_lock);
sc->sc_unloading = true;
Index: sys/dev/iscsi/iscsi_base64.c
===================================================================
--- /dev/null
+++ sys/dev/iscsi/iscsi_base64.c
@@ -0,0 +1,320 @@
+/*
+ * Copyright (c) 1996, 1998 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
+ * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+/*
+ * Portions Copyright (c) 1995 by International Business Machines, Inc.
+ *
+ * International Business Machines, Inc. (hereinafter called IBM) grants
+ * permission under its copyrights to use, copy, modify, and distribute this
+ * Software with or without fee, provided that the above copyright notice and
+ * all paragraphs of this notice appear in all copies, and that the name of IBM
+ * not be used in connection with the marketing of any product incorporating
+ * the Software or modifications thereof, without specific, written prior
+ * permission.
+ *
+ * To the extent it has a right to do so, IBM grants an immunity from suit
+ * under its patents, if any, for the use, sale or manufacture of products to
+ * the extent that such products are used for performing Domain Name System
+ * dynamic updates in TCP/IP networks by means of the Software. No immunity is
+ * granted for any product per se or for any other function of any product.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL,
+ * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN
+ * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/ctype.h>
+#include <sys/eventhandler.h>
+#include <sys/malloc.h>
+
+#include <netinet/in.h>
+
+#include "icl.h"
+#include "icl_wrappers.h"
+#include "iscsi_ioctl.h"
+#include "iscsi_proto.h"
+#include "iscsi.h"
+
+#define Assert(Cond) if (!(Cond)) panic(#Cond)
+
+static const char Base64[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static const char Pad64 = '=';
+
+/* (From RFC1521 and draft-ietf-dnssec-secext-03.txt)
+ The following encoding technique is taken from RFC 1521 by Borenstein
+ and Freed. It is reproduced here in a slightly edited form for
+ convenience.
+
+ A 65-character subset of US-ASCII is used, enabling 6 bits to be
+ represented per printable character. (The extra 65th character, "=",
+ is used to signify a special processing function.)
+
+ The encoding process represents 24-bit groups of input bits as output
+ strings of 4 encoded characters. Proceeding from left to right, a
+ 24-bit input group is formed by concatenating 3 8-bit input groups.
+ These 24 bits are then treated as 4 concatenated 6-bit groups, each
+ of which is translated into a single digit in the base64 alphabet.
+
+ Each 6-bit group is used as an index into an array of 64 printable
+ characters. The character referenced by the index is placed in the
+ output string.
+
+ Table 1: The Base64 Alphabet
+
+ Value Encoding Value Encoding Value Encoding Value Encoding
+ 0 A 17 R 34 i 51 z
+ 1 B 18 S 35 j 52 0
+ 2 C 19 T 36 k 53 1
+ 3 D 20 U 37 l 54 2
+ 4 E 21 V 38 m 55 3
+ 5 F 22 W 39 n 56 4
+ 6 G 23 X 40 o 57 5
+ 7 H 24 Y 41 p 58 6
+ 8 I 25 Z 42 q 59 7
+ 9 J 26 a 43 r 60 8
+ 10 K 27 b 44 s 61 9
+ 11 L 28 c 45 t 62 +
+ 12 M 29 d 46 u 63 /
+ 13 N 30 e 47 v
+ 14 O 31 f 48 w (pad) =
+ 15 P 32 g 49 x
+ 16 Q 33 h 50 y
+
+ Special processing is performed if fewer than 24 bits are available
+ at the end of the data being encoded. A full encoding quantum is
+ always completed at the end of a quantity. When fewer than 24 input
+ bits are available in an input group, zero bits are added (on the
+ right) to form an integral number of 6-bit groups. Padding at the
+ end of the data is performed using the '=' character.
+
+ Since all base64 input is an integral number of octets, only the
+ -------------------------------------------------
+ following cases can arise:
+
+ (1) the final quantum of encoding input is an integral
+ multiple of 24 bits; here, the final unit of encoded
+ output will be an integral multiple of 4 characters
+ with no "=" padding,
+ (2) the final quantum of encoding input is exactly 8 bits;
+ here, the final unit of encoded output will be two
+ characters followed by two "=" padding characters, or
+ (3) the final quantum of encoding input is exactly 16 bits;
+ here, the final unit of encoded output will be three
+ characters followed by one "=" padding character.
+ */
+
+int
+iscsi_b64_ntop(u_char const *src, size_t srclength, char *target, size_t targsize) {
+ size_t datalength = 0;
+ u_char input[3];
+ u_char output[4];
+ size_t i;
+
+ while (2 < srclength) {
+ input[0] = *src++;
+ input[1] = *src++;
+ input[2] = *src++;
+ srclength -= 3;
+
+ output[0] = input[0] >> 2;
+ output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
+ output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
+ output[3] = input[2] & 0x3f;
+ Assert(output[0] < 64);
+ Assert(output[1] < 64);
+ Assert(output[2] < 64);
+ Assert(output[3] < 64);
+
+ if (datalength + 4 > targsize)
+ return (-1);
+ target[datalength++] = Base64[output[0]];
+ target[datalength++] = Base64[output[1]];
+ target[datalength++] = Base64[output[2]];
+ target[datalength++] = Base64[output[3]];
+ }
+
+ /* Now we worry about padding. */
+ if (0 != srclength) {
+ /* Get what's left. */
+ input[0] = input[1] = input[2] = '\0';
+ for (i = 0; i < srclength; i++)
+ input[i] = *src++;
+
+ output[0] = input[0] >> 2;
+ output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
+ output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
+ Assert(output[0] < 64);
+ Assert(output[1] < 64);
+ Assert(output[2] < 64);
+
+ if (datalength + 4 > targsize)
+ return (-1);
+ target[datalength++] = Base64[output[0]];
+ target[datalength++] = Base64[output[1]];
+ if (srclength == 1)
+ target[datalength++] = Pad64;
+ else
+ target[datalength++] = Base64[output[2]];
+ target[datalength++] = Pad64;
+ }
+ if (datalength >= targsize)
+ return (-1);
+ target[datalength] = '\0'; /* Returned value doesn't count \0. */
+ return (datalength);
+}
+
+/* skips all whitespace anywhere.
+ converts characters, four at a time, starting at (or after)
+ src from base - 64 numbers into three 8 bit bytes in the target area.
+ it returns the number of data bytes stored at the target, or -1 on error.
+ */
+
+int
+iscsi_b64_pton(const char *src, u_char *target, size_t targsize)
+{
+ int tarindex, state, ch;
+ u_char nextbyte;
+ char *pos;
+
+ state = 0;
+ tarindex = 0;
+
+ while ((ch = *src++) != '\0') {
+ if (isspace((unsigned char)ch)) /* Skip whitespace anywhere. */
+ continue;
+
+ if (ch == Pad64)
+ break;
+
+ pos = strchr(Base64, ch);
+ if (pos == NULL) /* A non-base64 character. */
+ return (-1);
+
+ switch (state) {
+ case 0:
+ if (target) {
+ if ((size_t)tarindex >= targsize)
+ return (-1);
+ target[tarindex] = (pos - Base64) << 2;
+ }
+ state = 1;
+ break;
+ case 1:
+ if (target) {
+ if ((size_t)tarindex >= targsize)
+ return (-1);
+ target[tarindex] |= (pos - Base64) >> 4;
+ nextbyte = ((pos - Base64) & 0x0f) << 4;
+ if ((size_t)tarindex + 1 < targsize)
+ target[tarindex + 1] = nextbyte;
+ else if (nextbyte)
+ return (-1);
+ }
+ tarindex++;
+ state = 2;
+ break;
+ case 2:
+ if (target) {
+ if ((size_t)tarindex >= targsize)
+ return (-1);
+ target[tarindex] |= (pos - Base64) >> 2;
+ nextbyte = ((pos - Base64) & 0x03) << 6;
+ if ((size_t)tarindex + 1 < targsize)
+ target[tarindex + 1] = nextbyte;
+ else if (nextbyte)
+ return (-1);
+ }
+ tarindex++;
+ state = 3;
+ break;
+ case 3:
+ if (target) {
+ if ((size_t)tarindex >= targsize)
+ return (-1);
+ target[tarindex] |= (pos - Base64);
+ }
+ tarindex++;
+ state = 0;
+ break;
+ default:
+ __assert_unreachable();
+ }
+ }
+
+ /*
+ * We are done decoding Base-64 chars. Let's see if we ended
+ * on a byte boundary, and/or with erroneous trailing characters.
+ */
+
+ if (ch == Pad64) { /* We got a pad char. */
+ ch = *src++; /* Skip it, get next. */
+ switch (state) {
+ case 0: /* Invalid = in first position */
+ case 1: /* Invalid = in second position */
+ return (-1);
+
+ case 2: /* Valid, means one byte of info */
+ /* Skip any number of spaces. */
+ for ((void)NULL; ch != '\0'; ch = *src++)
+ if (!isspace((unsigned char)ch))
+ break;
+ /* Make sure there is another trailing = sign. */
+ if (ch != Pad64)
+ return (-1);
+ ch = *src++; /* Skip the = */
+ /* Fall through to "single trailing =" case. */
+ /* FALLTHROUGH */
+
+ case 3: /* Valid, means two bytes of info */
+ /*
+ * We know this char is an =. Is there anything but
+ * whitespace after it?
+ */
+ for ((void)NULL; ch != '\0'; ch = *src++)
+ if (!isspace((unsigned char)ch))
+ return (-1);
+
+ /*
+ * Now make sure for cases 2 and 3 that the "extra"
+ * bits that slopped past the last full byte were
+ * zeros. If we don't check them, they become a
+ * subliminal channel.
+ */
+ if (target && (size_t)tarindex < targsize &&
+ target[tarindex] != 0)
+ return (-1);
+ }
+ } else {
+ /*
+ * We ended by seeing the end of the string. Make sure we
+ * have no partial bytes lying around.
+ */
+ if (state != 0)
+ return (-1);
+ }
+
+ return (tarindex);
+}
Index: sys/dev/iscsi/iscsi_chap.c
===================================================================
--- /dev/null
+++ sys/dev/iscsi/iscsi_chap.c
@@ -0,0 +1,442 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2014, 2021 The FreeBSD Foundation
+ *
+ * This software was developed by Edward Tomasz Napierala under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Portions of this software was developed by Ka Ho Ng under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/eventhandler.h>
+#include <sys/malloc.h>
+#include <sys/md5.h>
+
+#include <netinet/in.h>
+
+#include "icl.h"
+#include "icl_wrappers.h"
+#include "iscsi_ioctl.h"
+#include "iscsi_proto.h"
+#include "iscsi.h"
+
+#define b64_pton iscsi_b64_pton
+
+static void
+chap_compute_md5(const char id, const char *secret,
+ const void *challenge, size_t challenge_len, void *response,
+ size_t response_len)
+{
+ MD5_CTX ctx;
+
+ KASSERT(response_len == CHAP_DIGEST_LEN,
+ ("%s: response length mismatch", __func__));
+
+ MD5Init(&ctx);
+ MD5Update(&ctx, &id, sizeof(id));
+ MD5Update(&ctx, secret, strlen(secret));
+ MD5Update(&ctx, challenge, challenge_len);
+ MD5Final(response, &ctx);
+}
+
+static int
+chap_hex2int(const char hex)
+{
+ switch (hex) {
+ case '0':
+ return (0x00);
+ case '1':
+ return (0x01);
+ case '2':
+ return (0x02);
+ case '3':
+ return (0x03);
+ case '4':
+ return (0x04);
+ case '5':
+ return (0x05);
+ case '6':
+ return (0x06);
+ case '7':
+ return (0x07);
+ case '8':
+ return (0x08);
+ case '9':
+ return (0x09);
+ case 'a':
+ case 'A':
+ return (0x0a);
+ case 'b':
+ case 'B':
+ return (0x0b);
+ case 'c':
+ case 'C':
+ return (0x0c);
+ case 'd':
+ case 'D':
+ return (0x0d);
+ case 'e':
+ case 'E':
+ return (0x0e);
+ case 'f':
+ case 'F':
+ return (0x0f);
+ default:
+ return (-1);
+ }
+}
+
+static int
+chap_b642bin(const char *b64, void **binp, size_t *bin_lenp)
+{
+ char *bin;
+ int b64_len, bin_len;
+
+ b64_len = strlen(b64);
+ bin_len = (b64_len + 3) / 4 * 3;
+ bin = malloc(bin_len, M_ISCSI, M_NOWAIT | M_ZERO);
+ if (bin == NULL)
+ return (ENOMEM);
+
+ bin_len = b64_pton(b64, bin, bin_len);
+ if (bin_len < 0) {
+ ISCSI_WARN("malformed base64 variable");
+ free(bin, M_ISCSI);
+ return (-1);
+ }
+ *binp = bin;
+ *bin_lenp = bin_len;
+ return (0);
+}
+
+/*
+ * XXX: Review this _carefully_.
+ */
+static int
+chap_hex2bin(const char *hex, void **binp, size_t *bin_lenp)
+{
+ int i, hex_len, nibble;
+ bool lo = true; /* As opposed to 'hi'. */
+ char *bin;
+ size_t bin_off, bin_len;
+
+ if (strncasecmp(hex, "0b", strlen("0b")) == 0)
+ return (chap_b642bin(hex + 2, binp, bin_lenp));
+
+ if (strncasecmp(hex, "0x", strlen("0x")) != 0) {
+ ISCSI_WARN("malformed variable, should start with \"0x\""
+ " or \"0b\"");
+ return (-1);
+ }
+
+ hex += strlen("0x");
+ hex_len = strlen(hex);
+ if (hex_len < 1) {
+ ISCSI_WARN("malformed variable; doesn't contain anything "
+ "but \"0x\"");
+ return (-1);
+ }
+
+ bin_len = hex_len / 2 + hex_len % 2;
+ bin = malloc(bin_len, M_ISCSI, M_NOWAIT | M_ZERO);
+ if (bin == NULL) {
+ return (-1);
+ }
+
+ bin_off = bin_len - 1;
+ for (i = hex_len - 1; i >= 0; i--) {
+ nibble = chap_hex2int(hex[i]);
+ if (nibble < 0) {
+ ISCSI_WARN("malformed variable, invalid char \"%c\"",
+ hex[i]);
+ free(bin, M_ISCSI);
+ return (-1);
+ }
+
+ KASSERT(bin_off < bin_len, ("%s: bin_off < bin_len failed",
+ __func__));
+ if (lo) {
+ bin[bin_off] = nibble;
+ lo = false;
+ } else {
+ bin[bin_off] |= nibble << 4;
+ bin_off--;
+ lo = true;
+ }
+ }
+
+ *binp = bin;
+ *bin_lenp = bin_len;
+ return (0);
+}
+
+#ifdef USE_BASE64
+static char *
+chap_bin2hex(const char *bin, size_t bin_len)
+{
+ unsigned char *b64, *tmp;
+ size_t b64_len;
+
+ b64_len = (bin_len + 2) / 3 * 4 + 3; /* +2 for "0b", +1 for '\0'. */
+ b64 = malloc(b64_len);
+ if (b64 == NULL)
+ log_err(1, "malloc");
+
+ tmp = b64;
+ tmp += sprintf(tmp, "0b");
+ b64_ntop(bin, bin_len, tmp, b64_len - 2);
+
+ return (b64);
+}
+#else
+static char *
+chap_bin2hex(const char *bin, size_t bin_len)
+{
+ unsigned char *hex, *tmp, ch;
+ size_t hex_len;
+ size_t i;
+
+ hex_len = bin_len * 2 + 3; /* +2 for "0x", +1 for '\0'. */
+ hex = malloc(hex_len, M_ISCSI, M_NOWAIT | M_ZERO);
+ if (hex == NULL)
+ return (NULL);
+
+ tmp = hex;
+ tmp += sprintf(tmp, "0x");
+ for (i = 0; i < bin_len; i++) {
+ ch = bin[i];
+ tmp += sprintf(tmp, "%02x", ch);
+ }
+
+ return (hex);
+}
+#endif /* !USE_BASE64 */
+
+struct iscsi_chap *
+iscsi_chap_new(void)
+{
+ struct iscsi_chap *chap;
+
+ chap = malloc(sizeof(*chap), M_ISCSI, M_NOWAIT | M_ZERO);
+ if (chap == NULL)
+ return (NULL);
+
+ /*
+ * Generate the challenge.
+ */
+ arc4random_buf(chap->chap_challenge, sizeof(chap->chap_challenge));
+ arc4random_buf(&chap->chap_id, sizeof(chap->chap_id));
+
+ return (chap);
+}
+
+char *
+iscsi_chap_get_id(const struct iscsi_chap *chap)
+{
+ char *chap_i;
+ int ret;
+
+ ret = asprintf(&chap_i, M_ISCSI, "%d", chap->chap_id);
+ if (ret < 0)
+ return (NULL);
+
+ return (chap_i);
+}
+
+char *
+iscsi_chap_get_challenge(const struct iscsi_chap *chap)
+{
+ char *chap_c;
+
+ chap_c = chap_bin2hex(chap->chap_challenge,
+ sizeof(chap->chap_challenge));
+
+ return (chap_c);
+}
+
+static int
+chap_receive_bin(struct iscsi_chap *chap, void *response, size_t response_len)
+{
+
+ if (response_len != sizeof(chap->chap_response)) {
+ //log_debugx("got CHAP response with invalid length; "
+ // "got %zd, should be %zd",
+ // response_len, sizeof(chap->chap_response));
+ return (1);
+ }
+
+ memcpy(chap->chap_response, response, response_len);
+ return (0);
+}
+
+int
+iscsi_chap_receive(struct iscsi_chap *chap, const char *response)
+{
+ void *response_bin;
+ size_t response_bin_len;
+ int error;
+
+ error = chap_hex2bin(response, &response_bin, &response_bin_len);
+ if (error != 0) {
+ ISCSI_DEBUG("got incorrectly encoded CHAP response \"%s\"",
+ response);
+ return (1);
+ }
+
+ error = chap_receive_bin(chap, response_bin, response_bin_len);
+ free(response_bin, M_ISCSI);
+
+ return (error);
+}
+
+int
+iscsi_chap_authenticate(struct iscsi_chap *chap, const char *secret)
+{
+ char expected_response[CHAP_DIGEST_LEN];
+
+ chap_compute_md5(chap->chap_id, secret,
+ chap->chap_challenge, sizeof(chap->chap_challenge),
+ expected_response, sizeof(expected_response));
+
+ if (memcmp(chap->chap_response,
+ expected_response, sizeof(expected_response)) != 0) {
+ return (-1);
+ }
+
+ return (0);
+}
+
+void
+iscsi_chap_delete(struct iscsi_chap *chap)
+{
+
+ free(chap, M_ISCSI);
+}
+
+struct iscsi_rchap *
+iscsi_rchap_new(const char *secret)
+{
+ struct iscsi_rchap *rchap;
+
+ rchap = malloc(sizeof(*rchap), M_ISCSI, M_NOWAIT | M_ZERO);
+ if (rchap == NULL)
+ return (NULL);
+
+ rchap->rchap_secret = strdup_flags(secret, M_ISCSI, M_NOWAIT);
+
+ return (rchap);
+}
+
+static int
+rchap_receive_bin(struct iscsi_rchap *rchap, const unsigned char id,
+ const void *challenge, size_t challenge_len)
+{
+
+ rchap->rchap_id = id;
+ rchap->rchap_challenge = malloc(challenge_len, M_ISCSI,
+ M_NOWAIT | M_ZERO);
+ if (rchap->rchap_challenge == NULL)
+ return (1);
+ memcpy(rchap->rchap_challenge, challenge, challenge_len);
+ rchap->rchap_challenge_len = challenge_len;
+ return (0);
+}
+
+int
+iscsi_rchap_receive(struct iscsi_rchap *rchap, const char *id,
+ const char *challenge)
+{
+ unsigned char id_bin;
+ void *challenge_bin;
+ size_t challenge_bin_len;
+
+ int error;
+
+ id_bin = strtoul(id, NULL, 10);
+
+ error = chap_hex2bin(challenge, &challenge_bin, &challenge_bin_len);
+ if (error != 0) {
+ //log_debugx("got incorrectly encoded CHAP challenge \"%s\"",
+ // challenge);
+ return (1);
+ }
+
+ error = rchap_receive_bin(rchap, id_bin, challenge_bin,
+ challenge_bin_len);
+ free(challenge_bin, M_ISCSI);
+
+ return (error);
+}
+
+static int
+rchap_get_response_bin(struct iscsi_rchap *rchap,
+ void **responsep, size_t *response_lenp)
+{
+ void *response_bin;
+ size_t response_bin_len = CHAP_DIGEST_LEN;
+
+ response_bin = malloc(response_bin_len, M_ISCSI, M_NOWAIT | M_ZERO);
+ if (response_bin == NULL)
+ return (1);
+
+ chap_compute_md5(rchap->rchap_id, rchap->rchap_secret,
+ rchap->rchap_challenge, rchap->rchap_challenge_len,
+ response_bin, response_bin_len);
+
+ *responsep = response_bin;
+ *response_lenp = response_bin_len;
+ return (0);
+}
+
+char *
+iscsi_rchap_get_response(struct iscsi_rchap *rchap)
+{
+ void *response;
+ size_t response_len;
+ char *chap_r;
+
+ if (rchap_get_response_bin(rchap, &response, &response_len) != 0)
+ return (NULL);
+ chap_r = chap_bin2hex(response, response_len);
+ free(response, M_ISCSI);
+
+ return (chap_r);
+}
+
+void
+iscsi_rchap_delete(struct iscsi_rchap *rchap)
+{
+
+ free(rchap->rchap_secret, M_ISCSI);
+ free(rchap->rchap_challenge, M_ISCSI);
+ free(rchap, M_ISCSI);
+}
Index: sys/dev/iscsi/iscsi_ibft.h
===================================================================
--- /dev/null
+++ sys/dev/iscsi/iscsi_ibft.h
@@ -0,0 +1,276 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2021 The FreeBSD Foundation
+ *
+ * This software was developed by Ka Ho Ng under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef ISCSI_IBFT_H
+#define ISCSI_IBFT_H
+
+#define IBFT_STRUCT_ALIGN 8
+ /* Structure alignment in bytes */
+
+#define IBFT_SCAN_START_PA 545288
+ /* Scan start physical address */
+#define IBFT_SCAN_END_PA 1048576
+ /* Scan end physical address */
+#define IBFT_SCAN_ALIGN 16
+ /* Scan alignment in bytes */
+
+#define IBFT_S_ID_RESERVED 0 /* Reserved */
+#define IBFT_S_ID_CONTROL 1 /* Control structure */
+#define IBFT_S_ID_INITIATOR 2 /* Initiator structure */
+#define IBFT_S_ID_NIC 3 /* NIC structure */
+#define IBFT_S_ID_TARGET 4 /* Target structure */
+#define IBFT_S_ID_EXTENSION 5 /* Extension structure */
+
+#define IBFT_CHAP_NONE 0 /* No auth */
+#define IBFT_CHAP_CHAP 1 /* CHAP auth */
+#define IBFT_CHAP_MUTUAL 2 /* Mutual CHAP auth */
+
+#define IBFT_OFF_UNUSED 0
+ /* Unused offset field should be zero */
+
+#define IBFT_S_CTRL_MIN_LEN 18
+#define IBFT_S_INITIATOR_LEN 74
+#define IBFT_S_NIC_LEN 102
+#define IBFT_S_TGT_LEN 54
+
+#define IBFT_TABLE_SIGNATURE "iBFT"
+
+/*
+ * 3.2 iBFT Standard Structure Header
+ */
+struct ibft_std_s_hdr {
+ uint8_t sh_s_id;
+ uint8_t sh_version;
+ uint16_t sh_length;
+ uint8_t sh_index;
+ uint8_t sh_flags;
+};
+
+/*
+ * 3.3 iBFT Table Header
+ */
+struct ibft_tbl_hdr {
+ uint8_t th_signature[4];
+ uint32_t th_length;
+ uint8_t th_revision;
+ uint8_t th_checksum;
+ uint8_t th_oemid[6];
+ uint8_t th_oem_tbl_id[8];
+ uint8_t th_reserved0[24];
+};
+_Static_assert(sizeof(struct ibft_tbl_hdr) == 48,
+ "iBFT Table Header must be 48 bytes in size");
+
+/*
+ * 3.4 Control Structure
+ *
+ * Structure ID : control structure
+ * Structure Version : 1
+ * Structure Length : >=18
+ * Structure Index : 0
+ * Structure Flags :
+ * * Bit0 - Boot Failover Flag
+ */
+struct ibft_ctrl_s {
+ struct ibft_std_s_hdr cs_hdr;
+ uint16_t cs_extensions;
+ uint16_t cs_initiator_off;
+ uint16_t cs_nic0_off;
+ uint16_t cs_tgt0_off;
+ uint16_t cs_nic1_off;
+ uint16_t cs_tgt1_off;
+};
+
+/*
+ * 3.5 Initiator Structure
+ *
+ * Structure ID : initiator structure
+ * Structure Version : 1
+ * Structure Length : 74
+ * Structure Index : 0
+ * Structure Flags :
+ * - Bit0 - Block Valid Flag
+ * - Bit1 - Firmware Boot Selected Flag
+ */
+struct ibft_initiator_s {
+ struct ibft_std_s_hdr is_hdr;
+ uint8_t is_isns[16];
+ uint8_t is_slp[16];
+ uint8_t is_pri_radius[16];
+ uint8_t is_sec_radius[16];
+ uint16_t is_initiator_name_len;
+ uint16_t is_initiator_name_off;
+};
+
+/*
+ * 3.6 NIC Structure
+ *
+ * Structure ID : NIC structure
+ * Structure Version : 1
+ * Structure Length : 102
+ * Structure Index : 0...255
+ * Structure Flags :
+ * - Bit0 - Block Valid Flag
+ * - Bit1 - Firmware Boot Selected Flag
+ * - Bit2 - Global/Link Local
+ */
+struct ibft_nic_s {
+ struct ibft_std_s_hdr ns_hdr;
+ uint8_t ns_ip[16];
+ uint8_t ns_prefixlen;
+ uint8_t ns_origin;
+ uint8_t ns_gateway[16];
+ uint8_t ns_pri_dns[16];
+ uint8_t ns_sec_dns[16];
+ uint8_t ns_dhcp_dns[16];
+ uint16_t ns_vlan;
+ uint8_t ns_mac[6];
+ uint16_t ns_pci_bdf;
+ uint16_t ns_hostname_len;
+ uint16_t ns_hostname_off;
+};
+
+/*
+ * 3.7 Target Structure
+ *
+ * Structure ID : target structure
+ * Structure Version : 1
+ * Structure Length : 54
+ * Structure Index : 0...255
+ * Structure Flags :
+ * - Bit0 - Block Valid Flag
+ * - Bit1 - Firmware Boot Selected Flag
+ * - Bit2 - Use Radius CHAP
+ * - Bit3 - Use Radius rCHAP
+ */
+struct ibft_tgt_s {
+ struct ibft_std_s_hdr ts_hdr;
+ uint8_t ts_ip[16];
+ uint16_t ts_port;
+ uint64_t ts_lun;
+ uint8_t ts_chap_type;
+ uint8_t ts_nic_idx;
+ uint16_t ts_tgt_name_len;
+ uint16_t ts_tgt_name_off;
+ uint16_t ts_chap_name_len;
+ uint16_t ts_chap_name_off;
+ uint16_t ts_chap_secret_len;
+ uint16_t ts_chap_secret_off;
+ uint16_t ts_rchap_name_len;
+ uint16_t ts_rchap_name_off;
+ uint16_t ts_rchap_secret_len;
+ uint16_t ts_rchap_secret_off;
+};
+
+/*
+ * Below are in-memory representation of the above structures.
+ */
+
+struct ibft_i_initiator_s {
+ struct ibft_initiator_s *is_rawptr;
+ bool is_present;
+ unsigned int is_flags;
+ uint8_t is_isns[16];
+ uint8_t is_slp[16];
+ uint8_t is_pri_radius[16];
+ uint8_t is_sec_radius[16];
+ size_t is_name_len;
+ const char *is_name;
+};
+
+struct ibft_i_nic_s {
+ struct ibft_nic_s *ns_rawptr;
+ bool ns_present;
+ TAILQ_ENTRY(ibft_i_nic_s) ns_entry;
+ uint8_t ns_idx;
+ unsigned int ns_flags;
+ uint8_t ns_ip[16];
+ uint8_t ns_prefixlen;
+ uint8_t ns_origin;
+ uint8_t ns_gateway[16];
+ uint8_t ns_pri_dns[16];
+ uint8_t ns_sec_dns[16];
+ uint8_t ns_dhcp_dns[16];
+ uint16_t ns_vlan;
+ uint8_t ns_mac[6];
+ uint16_t ns_pci_bdf;
+ size_t ns_hostname_len;
+ const char *ns_hostname;
+};
+
+struct ibft_i_tgt_s {
+ struct ibft_tgt_s *ts_rawptr;
+ bool ts_present;
+ TAILQ_ENTRY(ibft_i_tgt_s) ts_entry;
+ uint8_t ts_idx;
+ unsigned int ts_flags;
+ uint8_t ts_ip[16];
+ uint16_t ts_port;
+ uint64_t ts_lun;
+ uint8_t ts_chap_type;
+ uint8_t ts_nic_idx;
+ size_t ts_tgt_name_len;
+ const char *ts_tgt_name;
+ size_t ts_chap_name_len;
+ const char *ts_chap_name;
+ size_t ts_chap_secret_len;
+ const char *ts_chap_secret;
+ size_t ts_rchap_name_len;
+ const char *ts_rchap_name;
+ size_t ts_rchap_secret_len;
+ const char *ts_rchap_secret;
+};
+
+#define IBFT_MAX_IDX (255)
+#define IBFT_MAX_N_STRUCTS (IBFT_MAX_IDX + 1)
+
+TAILQ_HEAD(ibft_i_nics_head, ibft_i_nic_s);
+TAILQ_HEAD(ibft_i_tgts_head, ibft_i_tgt_s);
+
+extern struct ibft_i_initiator_s *ibft_initiator;
+extern struct ibft_i_nics_head ibft_nics_list;
+extern struct ibft_i_tgts_head ibft_targets_list;
+extern struct ibft_i_nic_s *ibft_nics;
+extern struct ibft_i_tgt_s *ibft_targets;
+
+int iscsi_ibft_init(void);
+void iscsi_ibft_fini(void);
+
+static inline void
+iscsi_ibft_getstr(char *dst, size_t dstsz, const char *src, size_t srclen)
+{
+ bzero(dst, dstsz);
+ if (src == NULL)
+ return;
+ memcpy(dst, src, min(dstsz - 1, srclen));
+}
+
+#endif
\ No newline at end of file
Index: sys/dev/iscsi/iscsi_ibft.c
===================================================================
--- /dev/null
+++ sys/dev/iscsi/iscsi_ibft.c
@@ -0,0 +1,275 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012, 2021 The FreeBSD Foundation
+ *
+ * This software was developed by Edward Tomasz Napierala under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Portions of this software was developed by Ka Ho Ng under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/endian.h>
+#include <sys/ctype.h>
+#include <sys/eventhandler.h>
+#include <sys/malloc.h>
+#include <sys/queue.h>
+
+#include <contrib/dev/acpica/include/acpi.h>
+
+#include "icl.h"
+#include "icl_wrappers.h"
+#include "iscsi_ibft.h"
+#include "iscsi_ioctl.h"
+#include "iscsi_proto.h"
+#include "iscsi.h"
+
+static char *ibft_region;
+static const struct ibft_ctrl_s *ibft_ctl;
+
+struct ibft_i_initiator_s *ibft_initiator;
+struct ibft_i_nic_s *ibft_nics;
+struct ibft_i_tgt_s *ibft_targets;
+static size_t ibft_num_nics, ibft_num_targets;
+
+struct ibft_i_nics_head ibft_nics_list;
+struct ibft_i_tgts_head ibft_targets_list;
+
+static int iscsi_verify_ibft(const struct ibft_tbl_hdr *ibftp);
+
+static int
+iscsi_verify_ibft(const struct ibft_tbl_hdr *ibftp)
+{
+ const struct ibft_ctrl_s *ctrlp;
+
+ if (le16toh(ibftp->th_length) < sizeof(*ibftp))
+ return (1);
+ if (ibftp->th_revision != 1)
+ return (1);
+
+ ctrlp = (const struct ibft_ctrl_s *)(ibftp + 1);
+ if (ctrlp->cs_hdr.sh_s_id != IBFT_S_ID_CONTROL)
+ return (1);
+ if (ctrlp->cs_hdr.sh_version != 1)
+ return (1);
+ if (le16toh(ctrlp->cs_hdr.sh_length) < sizeof(*ctrlp))
+ return (1);
+ if (ctrlp->cs_hdr.sh_index != 0)
+ return (1);
+
+ return (0);
+}
+
+#define IBFTSTRFETCH(name, dest, raw) { \
+ if (le16toh((raw)->name##_off) != 0) \
+ (dest)->name = ibft_region + \
+ le16toh((raw)->name##_off); \
+}
+static void
+iscsi_ibft_extract_initiator(struct ibft_initiator_s *raw,
+ struct ibft_i_initiator_s *initiator)
+{
+ bzero(initiator, sizeof(*initiator));
+ initiator->is_rawptr = raw;
+ initiator->is_present = true;
+ initiator->is_flags = raw->is_hdr.sh_flags;
+ memcpy(initiator->is_isns, raw->is_isns, sizeof(raw->is_isns));
+ memcpy(initiator->is_slp, raw->is_slp, sizeof(initiator->is_slp));
+ memcpy(initiator->is_pri_radius, raw->is_pri_radius,
+ sizeof(initiator->is_pri_radius));
+ memcpy(initiator->is_sec_radius, raw->is_sec_radius,
+ sizeof(initiator->is_sec_radius));
+ initiator->is_name_len = le16toh(raw->is_initiator_name_len);
+ initiator->is_name = ibft_region + le16toh(raw->is_initiator_name_off);
+}
+
+static void
+iscsi_ibft_extract_nic(struct ibft_nic_s *raw,
+ struct ibft_i_nic_s *nic)
+{
+ bzero(nic, sizeof(*nic));
+ nic->ns_rawptr = raw;
+ nic->ns_present = true;
+ nic->ns_idx = raw->ns_hdr.sh_index;
+ nic->ns_flags = raw->ns_hdr.sh_flags;
+ memcpy(nic->ns_ip, raw->ns_ip, sizeof(raw->ns_ip));
+ nic->ns_prefixlen = raw->ns_prefixlen;
+ nic->ns_origin = raw->ns_origin;
+ memcpy(nic->ns_gateway, raw->ns_gateway, sizeof(raw->ns_gateway));
+ memcpy(nic->ns_pri_dns, raw->ns_pri_dns, sizeof(raw->ns_pri_dns));
+ memcpy(nic->ns_sec_dns, raw->ns_sec_dns, sizeof(raw->ns_sec_dns));
+ memcpy(nic->ns_dhcp_dns, raw->ns_dhcp_dns, sizeof(raw->ns_dhcp_dns));
+ nic->ns_vlan = le16toh(raw->ns_vlan);
+ memcpy(nic->ns_mac, raw->ns_mac, sizeof(raw->ns_mac));
+ nic->ns_pci_bdf = le16toh(raw->ns_pci_bdf);
+ nic->ns_hostname_len = le16toh(raw->ns_hostname_len);
+ IBFTSTRFETCH(ns_hostname, nic, raw);
+}
+
+static void
+iscsi_ibft_extract_target(struct ibft_tgt_s *raw,
+ struct ibft_i_tgt_s *tgt)
+{
+ bzero(tgt, sizeof(*tgt));
+ tgt->ts_rawptr = raw;
+ tgt->ts_present = true;
+ tgt->ts_idx = raw->ts_hdr.sh_index;
+ tgt->ts_flags = raw->ts_hdr.sh_flags;
+ memcpy(tgt->ts_ip, raw->ts_ip, sizeof(raw->ts_ip));
+ tgt->ts_port = le16toh(raw->ts_port);
+ tgt->ts_lun = le64toh(raw->ts_lun);
+ tgt->ts_chap_type = raw->ts_chap_type;
+ tgt->ts_nic_idx = raw->ts_nic_idx;
+ tgt->ts_tgt_name_len = le16toh(raw->ts_tgt_name_len);
+ IBFTSTRFETCH(ts_tgt_name, tgt, raw);
+ tgt->ts_chap_name_len = le16toh(raw->ts_chap_name_len);
+ IBFTSTRFETCH(ts_chap_name, tgt, raw);
+ tgt->ts_chap_secret_len = le16toh(raw->ts_chap_secret_len);
+ IBFTSTRFETCH(ts_chap_secret, tgt, raw);
+ tgt->ts_rchap_name_len = le16toh(raw->ts_rchap_name_len);
+ IBFTSTRFETCH(ts_rchap_name, tgt, raw);
+ tgt->ts_rchap_secret_len = le16toh(raw->ts_rchap_secret_len);
+ IBFTSTRFETCH(ts_rchap_secret, tgt, raw);
+}
+#undef IBFTSTRFETCH
+
+int
+iscsi_ibft_init(void)
+{
+ struct ibft_std_s_hdr *hdr;
+ const uint16_t *offs;
+ int n, noffs;
+ uint16_t off;
+ struct ibft_tbl_hdr *ibft;
+ ACPI_STATUS r;
+
+ TAILQ_INIT(&ibft_nics_list);
+ TAILQ_INIT(&ibft_targets_list);
+
+ r = AcpiGetTable(ACPI_SIG_IBFT, 1, (ACPI_TABLE_HEADER **)&ibft);
+ if (ACPI_FAILURE(r)) {
+ r = AcpiGetTable(IBFT_TABLE_SIGNATURE, 1,
+ (ACPI_TABLE_HEADER **)&ibft);
+ if (ACPI_FAILURE(r)) {
+ ISCSI_DEBUG("Cannot find IBFT table");
+ return (1);
+ }
+ }
+ if (iscsi_verify_ibft((struct ibft_tbl_hdr *)ibft) != 0) {
+ ISCSI_WARN("Failed verifying IBFT table");
+ return (1);
+ }
+
+ ibft_region = (char *)ibft;
+ ibft_ctl = (const struct ibft_ctrl_s *)(ibft + 1);
+
+ ibft_initiator = (struct ibft_i_initiator_s *)malloc(
+ sizeof(*ibft_initiator), M_ISCSI, M_NOWAIT);
+ if (ibft_initiator == NULL)
+ goto fail;
+ ibft_nics = mallocarray(IBFT_MAX_N_STRUCTS, sizeof(*ibft_nics), M_ISCSI,
+ M_ZERO | M_NOWAIT);
+ if (ibft_nics == NULL)
+ goto fail;
+ ibft_targets = mallocarray(IBFT_MAX_N_STRUCTS, sizeof(*ibft_targets),
+ M_ISCSI, M_ZERO | M_NOWAIT);
+ if (ibft_targets == NULL)
+ goto fail;
+
+ /*
+ * Parse Initiator, NICs and Targets.
+ *
+ * Per 3.4.3 Optional Structure Expansion
+ */
+ offs = &ibft_ctl->cs_initiator_off;
+ noffs = (le16toh(ibft_ctl->cs_hdr.sh_length) -
+ offsetof(struct ibft_ctrl_s, cs_initiator_off)) / 2;
+ for (n = 0; n < noffs; n++) {
+ off = le16toh(offs[n]);
+ if (off == 0)
+ continue;
+ if (off >= le32toh(ibft->th_length)) {
+ ISCSI_WARN("Invalid optional offset in IBFT table. "
+ "Index in Optional Structure Expansion: %d. off: %hu. noff: %d",
+ n, off, noffs);
+ continue;
+ }
+ hdr = (struct ibft_std_s_hdr *)(ibft_region + off);
+ switch (hdr->sh_s_id) {
+ case IBFT_S_ID_INITIATOR:
+ iscsi_ibft_extract_initiator(
+ (struct ibft_initiator_s *)hdr,
+ ibft_initiator);
+ break;
+ case IBFT_S_ID_NIC:
+ iscsi_ibft_extract_nic((struct ibft_nic_s *)hdr,
+ &ibft_nics[hdr->sh_index]);
+ TAILQ_INSERT_TAIL(&ibft_nics_list,
+ &ibft_nics[hdr->sh_index], ns_entry);
+ ibft_num_nics++;
+ break;
+ case IBFT_S_ID_TARGET:
+ iscsi_ibft_extract_target((struct ibft_tgt_s *)hdr,
+ &ibft_targets[hdr->sh_index]);
+ TAILQ_INSERT_TAIL(&ibft_targets_list,
+ &ibft_targets[hdr->sh_index], ts_entry);
+ ibft_num_targets++;
+ break;
+ default:
+ ISCSI_WARN("Unexpected id in Optional Structure Expansion: %hhu",
+ hdr->sh_s_id);
+ }
+ }
+
+ ISCSI_DEBUG("Done parsing IBFT table. NICs: %zu, Targets: %zu",
+ ibft_num_nics, ibft_num_targets);
+ return (0);
+fail:
+ iscsi_ibft_fini();
+ return (1);
+}
+
+void
+iscsi_ibft_fini(void)
+{
+ TAILQ_INIT(&ibft_nics_list);
+ TAILQ_INIT(&ibft_targets_list);
+
+ free(ibft_initiator, M_ISCSI);
+ ibft_initiator = NULL;
+ free(ibft_nics, M_ISCSI);
+ ibft_nics = NULL;
+ free(ibft_targets, M_ISCSI);
+ ibft_targets = NULL;
+
+ ibft_region = NULL;
+ ibft_num_nics = ibft_num_targets = 0;
+}
\ No newline at end of file
Index: sys/dev/iscsi/iscsi_keys.c
===================================================================
--- /dev/null
+++ sys/dev/iscsi/iscsi_keys.c
@@ -0,0 +1,257 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012, 2021 The FreeBSD Foundation
+ *
+ * This software was developed by Edward Tomasz Napierala under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Portions of this software was developed by Ka Ho Ng under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/eventhandler.h>
+#include <sys/kdb.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/stddef.h>
+#include <sys/socket.h>
+
+#include "icl.h"
+#include "icl_wrappers.h"
+#include "iscsi_ioctl.h"
+#include "iscsi.h"
+
+struct iscsi_keys *
+iscsi_keys_new(int mflags)
+{
+ struct iscsi_keys *ik;
+
+ ik = malloc(sizeof(*ik), M_ISCSI, mflags | M_ZERO);
+ if (ik == NULL)
+ ISCSI_WARN("keys_new malloc failed");
+
+ return (ik);
+}
+
+static bool
+str_out_of_data(struct iscsi_keys *ik, const char *str)
+{
+ ptrdiff_t diff;
+
+ diff = str - ik->ik_data;
+ if (diff < 0 || diff >= ik->ik_data_len)
+ return (true);
+ return (false);
+}
+
+void
+iscsi_keys_delete(struct iscsi_keys *ik)
+{
+ int i;
+
+ for (i = 0; i < ISCSI_KEYS_MAX; i++) {
+ if (ik->ik_names[i] == NULL)
+ break;
+ if (str_out_of_data(ik, ik->ik_names[i]))
+ free(ik->ik_names[i], M_ISCSI);
+ KASSERT(ik->ik_values[i] != NULL,
+ ("iscsi: keys_delete encountered null value"));
+ if (str_out_of_data(ik, ik->ik_values[i]))
+ free(ik->ik_values[i], M_ISCSI);
+ }
+ free(ik->ik_data, M_ISCSI);
+ free(ik, M_ISCSI);
+}
+
+int
+iscsi_keys_load(struct iscsi_keys *ik, struct icl_pdu *ip, int mflags)
+{
+ int i;
+ char *pair;
+ size_t data_len, pair_len;
+ int error;
+
+ i = 0;
+
+ if (ip->ip_data_len == 0)
+ return (0);
+
+ KASSERT(ik->ik_data == NULL, ("iscsi: ik_data non-null"));
+ KASSERT(ik->ik_names[0] == NULL, ("iscsi: added kv pairs exists"));
+ data_len = ip->ip_data_len;
+ ik->ik_data = malloc(data_len, M_ISCSI, mflags);
+ if (ik->ik_data == NULL) {
+ ISCSI_WARN("keys_load out of memory for ik_data");
+ return (ENOMEM);
+ }
+ ik->ik_data_len = data_len;
+ icl_pdu_get_data(ip, 0, ik->ik_data, ik->ik_data_len);
+ if (ik->ik_data[data_len - 1] != '\0') {
+ ISCSI_WARN("protocol error: key not NULL-terminated. data_len : %zu",
+ data_len);
+ //hexdump(ik->ik_data, data_len, "iscsi_keys_load pdu: ", 0);
+ error = EINTEGRITY;
+ goto fail;
+ }
+
+ pair = ik->ik_data;
+ for (; i < ISCSI_KEYS_MAX; i++) {
+ pair_len = strlen(pair);
+
+ ik->ik_values[i] = pair;
+ ik->ik_names[i] = strsep(&ik->ik_values[i], "=");
+ if (ik->ik_names[i] == NULL || ik->ik_values[i] == NULL) {
+ ISCSI_WARN("malformed keys");
+ error = EINTEGRITY;
+ goto fail;
+ }
+ ISCSI_DEBUG("key received: \"%s=%s\"", ik->ik_names[i],
+ ik->ik_values[i]);
+
+ pair += pair_len + 1; /* +1 to skip the terminating '\0'. */
+ if (pair == ik->ik_data + ik->ik_data_len)
+ break;
+ KASSERT(pair < ik->ik_data + ik->ik_data_len,
+ ("iscsi: keys_load pair out of bound"));
+ }
+ if (i >= ISCSI_KEYS_MAX) {
+ ISCSI_WARN("too many keys received.");
+ error = EINTEGRITY;
+ goto fail;
+ }
+
+ return (0);
+fail:
+ free(ik->ik_data, M_ISCSI);
+ ik->ik_data = NULL;
+ ik->ik_data_len = 0;
+ while (i-- > 0)
+ ik->ik_names[i] = ik->ik_values[i] = NULL;
+ return (error);
+}
+
+int
+iscsi_keys_save(struct iscsi_keys *ik, struct icl_pdu *ip, int mflags)
+{
+ size_t len;
+ int i, error;
+
+ error = 0;
+
+ for (i = 0; i < ISCSI_KEYS_MAX; i++) {
+ if (ik->ik_names[i] == NULL)
+ break;
+ len = strlen(ik->ik_names[i]);
+ error = icl_pdu_append_data(ip, ik->ik_names[i], len, mflags);
+ if (error)
+ break;
+ error = icl_pdu_append_data(ip, "=", 1, mflags);
+ if (error)
+ break;
+ len = strlen(ik->ik_values[i]);
+ error = icl_pdu_append_data(ip, ik->ik_values[i], len, mflags);
+ if (error)
+ break;
+ error = icl_pdu_append_data(ip, "\0", 1, mflags);
+ if (error)
+ break;
+ }
+
+ return (error);
+}
+
+const char *
+iscsi_keys_find(struct iscsi_keys *ik, const char *name)
+{
+ int i;
+
+ /*
+ * Note that we don't handle duplicated key names here,
+ * as they are not supposed to happen in requests, and if they do,
+ * it's an initiator error.
+ */
+ for (i = 0; i < ISCSI_KEYS_MAX; i++) {
+ if (ik->ik_names[i] == NULL)
+ return (NULL);
+ if (strcmp(ik->ik_names[i], name) == 0)
+ return (ik->ik_values[i]);
+ }
+ return (NULL);
+}
+
+int
+iscsi_keys_add(struct iscsi_keys *ik, const char *name, const char *value,
+ int mflags)
+{
+ int i;
+
+ KASSERT(name != NULL, ("iscsi: keys_add with null name"));
+ KASSERT(value != NULL, ("iscsi: keys_add with null value"));
+ ISCSI_DEBUG("key to send: \"%s=%s\"", name, value);
+
+ /*
+ * Note that we don't check for duplicates here, as they are perfectly
+ * fine in responses, e.g. the "TargetName" keys in discovery sesion
+ * response.
+ */
+ for (i = 0; i < ISCSI_KEYS_MAX; i++) {
+ if (ik->ik_names[i] == NULL) {
+ ik->ik_names[i] = strdup_flags(name, M_ISCSI, mflags);
+ if (ik->ik_names[i] == NULL)
+ return (ENOMEM);
+ ik->ik_values[i] = strdup_flags(value, M_ISCSI, mflags);
+ if (ik->ik_values[i] == NULL) {
+ free(ik->ik_names[i], M_ISCSI);
+ return (ENOMEM);
+ }
+ return (0);
+ }
+ }
+ panic("iscsi: keys_add with too many keys.\n");
+}
+
+int
+iscsi_keys_add_int(struct iscsi_keys *ik, const char *name, int value,
+ int mflags)
+{
+ char *str;
+ int ret, error;
+
+ ret = asprintf(&str, M_ISCSI, "%d", value);
+ if (ret <= 0) {
+ ISCSI_WARN("keys_add_int runs out of memory.");
+ return (ENOMEM);
+ }
+
+ error = iscsi_keys_add(ik, name, str, mflags);
+ free(str, M_ISCSI);
+ return (error);
+}
\ No newline at end of file
Index: sys/dev/iscsi/iscsi_login.c
===================================================================
--- /dev/null
+++ sys/dev/iscsi/iscsi_login.c
@@ -0,0 +1,1123 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012, 2021 The FreeBSD Foundation
+ *
+ * This software was developed by Edward Tomasz Napierala under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Portions of this software was developed by Ka Ho Ng under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/ctype.h>
+#include <sys/eventhandler.h>
+#include <sys/malloc.h>
+
+#include <netinet/in.h>
+
+#include "icl.h"
+#include "icl_wrappers.h"
+#include "iscsi_ioctl.h"
+#include "iscsi_proto.h"
+#include "iscsi.h"
+
+static int
+login_nsg(const struct icl_pdu *response)
+{
+ struct iscsi_bhs_login_response *bhslr;
+
+ bhslr = (struct iscsi_bhs_login_response *)response->ip_bhs;
+
+ return (bhslr->bhslr_flags & 0x03);
+}
+
+static void
+login_set_nsg(struct icl_pdu *request, int nsg)
+{
+ struct iscsi_bhs_login_request *bhslr;
+
+ KASSERT(nsg == BHSLR_STAGE_SECURITY_NEGOTIATION ||
+ nsg == BHSLR_STAGE_OPERATIONAL_NEGOTIATION ||
+ nsg == BHSLR_STAGE_FULL_FEATURE_PHASE,
+ ("%s: invalid nsg 0x%x", __func__, nsg));
+
+ bhslr = (struct iscsi_bhs_login_request *)request->ip_bhs;
+
+ bhslr->bhslr_flags &= 0xFC;
+ bhslr->bhslr_flags |= nsg;
+}
+
+static void
+login_set_csg(struct icl_pdu *request, int csg)
+{
+ struct iscsi_bhs_login_request *bhslr;
+
+ KASSERT(csg == BHSLR_STAGE_SECURITY_NEGOTIATION ||
+ csg == BHSLR_STAGE_OPERATIONAL_NEGOTIATION ||
+ csg == BHSLR_STAGE_FULL_FEATURE_PHASE,
+ ("%s: invalid csg 0x%x", __func__, csg));
+
+ bhslr = (struct iscsi_bhs_login_request *)request->ip_bhs;
+
+ bhslr->bhslr_flags &= 0xF3;
+ bhslr->bhslr_flags |= csg << 2;
+}
+
+static const char *
+login_target_error_str(int class, int detail)
+{
+ /*
+ * RFC 3270, 10.13.5. Status-Class and Status-Detail
+ */
+ switch (class) {
+ case 0x01:
+ switch (detail) {
+ case 0x01:
+ return ("Target moved temporarily");
+ case 0x02:
+ return ("Target moved permanently");
+ default:
+ return ("unknown redirection Status-Detail");
+ }
+ case 0x02:
+ switch (detail) {
+ case 0x00:
+ return ("Initiator error");
+ case 0x01:
+ return ("Authentication failure");
+ case 0x02:
+ return ("Authorization failure");
+ case 0x03:
+ return ("Not found");
+ case 0x04:
+ return ("Target removed");
+ case 0x05:
+ return ("Unsupported version");
+ case 0x06:
+ return ("Too many connections");
+ case 0x07:
+ return ("Missing parameter");
+ case 0x08:
+ return ("Can't include in session");
+ case 0x09:
+ return ("Session type not supported");
+ case 0x0a:
+ return ("Session does not exist");
+ case 0x0b:
+ return ("Invalid during login");
+ default:
+ return ("unknown initiator error Status-Detail");
+ }
+ case 0x03:
+ switch (detail) {
+ case 0x00:
+ return ("Target error");
+ case 0x01:
+ return ("Service unavailable");
+ case 0x02:
+ return ("Out of resources");
+ default:
+ return ("unknown target error Status-Detail");
+ }
+ default:
+ return ("unknown target error Status-Class");
+ }
+}
+
+/*
+ * XXX: Currently redirection is not supported
+ */
+static int
+login_handle_redirection(struct iscsi_session *is, struct icl_pdu *response)
+{
+ return (EOPNOTSUPP);
+}
+
+static void
+login_send(struct iscsi_session *is, struct icl_pdu *ip)
+{
+ ISCSI_SESSION_LOCK_ASSERT(is);
+ icl_pdu_queue(ip);
+}
+
+static int
+login_receive(struct iscsi_session *is, struct icl_pdu **ip)
+{
+ struct iscsi_bhs_login_response *bhslr;
+ struct icl_pdu *response;
+ const char *errorstr;
+ int error;
+
+ ISCSI_SESSION_LOCK_ASSERT(is);
+
+ while (is->is_login_pdu == NULL &&
+ !is->is_terminating && !is->is_reconnecting) {
+ error = cv_wait_sig(&is->is_login_cv, &is->is_lock);
+ if (error != 0)
+ break;
+ }
+ if (is->is_terminating || is->is_reconnecting)
+ return (EPIPE);
+ response = is->is_login_pdu;
+ is->is_login_pdu = NULL;
+
+ bhslr = (struct iscsi_bhs_login_response *)response->ip_bhs;
+ error = EINTEGRITY;
+
+ if (bhslr->bhslr_opcode != ISCSI_BHS_OPCODE_LOGIN_RESPONSE) {
+ ISCSI_SESSION_WARN(is,
+ "protocol error: received invalid opcode 0x%x",
+ bhslr->bhslr_opcode);
+ goto out;
+ }
+ /*
+ * XXX: Implement the C flag some day.
+ */
+ if ((bhslr->bhslr_flags & BHSLR_FLAGS_CONTINUE) != 0) {
+ ISCSI_SESSION_WARN(is,
+ "received Login PDU with unsupported \"C\" flag");
+ goto out;
+ }
+ if (bhslr->bhslr_version_max != 0x00) {
+ ISCSI_SESSION_WARN(is, "received Login PDU with unsupported "
+ "Version-max 0x%x", bhslr->bhslr_version_max);
+ goto out;
+ }
+ if (bhslr->bhslr_version_active != 0x00) {
+ ISCSI_SESSION_WARN(is, "received Login PDU with unsupported "
+ "Version-active 0x%x", bhslr->bhslr_version_active);
+ goto out;
+ }
+ if (bhslr->bhslr_status_class == 1) {
+ error = login_handle_redirection(is, response);
+ if (error == 0) {
+ ISCSI_DEBUG("redirection handled");
+ error = EAGAIN;
+ }
+ goto out;
+ }
+ if (bhslr->bhslr_status_class != 0) {
+ errorstr = login_target_error_str(bhslr->bhslr_status_class,
+ bhslr->bhslr_status_detail);
+ ISCSI_SESSION_WARN(is, "target returned error: %s", errorstr);
+ goto out;
+ }
+ is->is_tsih = ntohs(bhslr->bhslr_tsih);
+ error = 0;
+
+out:
+ *ip = response;
+ return (error);
+}
+
+static struct icl_pdu *
+login_new_request(struct iscsi_session *is, int csg)
+{
+ struct icl_pdu *request;
+ struct iscsi_bhs_login_request *bhslr;
+ int nsg;
+
+ request = icl_pdu_new(is->is_conn, M_NOWAIT);
+ if (request == NULL)
+ return (NULL);
+ bhslr = (struct iscsi_bhs_login_request *)request->ip_bhs;
+ bhslr->bhslr_opcode = ISCSI_BHS_OPCODE_LOGIN_REQUEST |
+ ISCSI_BHS_OPCODE_IMMEDIATE;
+
+ bhslr->bhslr_flags = BHSLR_FLAGS_TRANSIT;
+ switch (csg) {
+ case BHSLR_STAGE_SECURITY_NEGOTIATION:
+ nsg = BHSLR_STAGE_OPERATIONAL_NEGOTIATION;
+ break;
+ case BHSLR_STAGE_OPERATIONAL_NEGOTIATION:
+ nsg = BHSLR_STAGE_FULL_FEATURE_PHASE;
+ break;
+ default:
+ panic("%s: invalid csg %d", __func__, csg);
+ }
+ login_set_csg(request, csg);
+ login_set_nsg(request, nsg);
+
+ memcpy(bhslr->bhslr_isid, is->is_isid, sizeof(bhslr->bhslr_isid));
+ bhslr->bhslr_tsih = htons(is->is_tsih);
+ bhslr->bhslr_initiator_task_tag = 0;
+ bhslr->bhslr_cmdsn = 0;
+ bhslr->bhslr_expstatsn = htonl(is->is_statsn + 1);
+
+ return (request);
+}
+
+static int
+login_list_prefers(const char *list,
+ const char *choice1, const char *choice2)
+{
+ char *tofree, *str, *token;
+
+ tofree = str = strdup_flags(list, M_ISCSI, M_NOWAIT);
+
+ while ((token = strsep(&str, ",")) != NULL) {
+ if (strcmp(token, choice1) == 0) {
+ free(tofree, M_ISCSI);
+ return (1);
+ }
+ if (strcmp(token, choice2) == 0) {
+ free(tofree, M_ISCSI);
+ return (2);
+ }
+ }
+ free(tofree, M_ISCSI);
+ return (-1);
+}
+
+static int
+login_negotiate_key(struct iscsi_session *is, struct icl_drv_limits *idl,
+ const char *name, const char *value, struct iscsi_kernel_handoff *handoff)
+{
+ struct icl_conn *ic;
+ int which, tmp;
+
+ ic = is->is_conn;
+
+ if (strcmp(name, "TargetAlias") == 0) {
+ strlcpy(handoff->ikh_target_alias, value,
+ sizeof(handoff->ikh_target_alias));
+ } else if (strcmp(value, "Irrelevant") == 0) {
+ /* Ignore. */
+ } else if (strcmp(name, "iSCSIProtocolLevel") == 0) {
+ tmp = strtoul(value, NULL, 10);
+ if (tmp < 0 || tmp > 31) {
+ ISCSI_SESSION_WARN(is,
+ "received invalid iSCSIProtocolLevel");
+ return (EINVAL);
+ }
+ handoff->ikh_protocol_level = tmp;
+ } else if (strcmp(name, "HeaderDigest") == 0) {
+ which = login_list_prefers(value, "CRC32C", "None");
+ switch (which) {
+ case 1:
+ handoff->ikh_header_digest = ISCSI_DIGEST_CRC32C;
+ break;
+ case 2:
+ handoff->ikh_header_digest = ISCSI_DIGEST_NONE;
+ break;
+ default:
+ ISCSI_SESSION_WARN(is, "target sent unrecognized "
+ "HeaderDigest value \"%s\"; will use None", value);
+ handoff->ikh_header_digest = ISCSI_DIGEST_NONE;
+ break;
+ }
+ } else if (strcmp(name, "DataDigest") == 0) {
+ which = login_list_prefers(value, "CRC32C", "None");
+ switch (which) {
+ case 1:
+ handoff->ikh_data_digest = ISCSI_DIGEST_CRC32C;
+ break;
+ case 2:
+ handoff->ikh_data_digest = ISCSI_DIGEST_NONE;
+ break;
+ default:
+ ISCSI_SESSION_WARN(is, "target sent unrecognized "
+ "DataDigest value \"%s\"; will use None", value);
+ handoff->ikh_data_digest = ISCSI_DIGEST_NONE;
+ break;
+ }
+ } else if (strcmp(name, "MaxConnections") == 0) {
+ /* Ignore. */
+ } else if (strcmp(name, "InitialR2T") == 0) {
+ if (strcmp(value, "Yes") == 0)
+ handoff->ikh_initial_r2t = 1;
+ else
+ handoff->ikh_initial_r2t = 0;
+ } else if (strcmp(name, "ImmediateData") == 0) {
+ if (strcmp(value, "Yes") == 0)
+ handoff->ikh_immediate_data = 1;
+ else
+ handoff->ikh_immediate_data = 0;
+ } else if (strcmp(name, "MaxRecvDataSegmentLength") == 0) {
+ tmp = strtoul(value, NULL, 10);
+ if (tmp <= 0) {
+ ISCSI_SESSION_WARN(is,
+ "received invalid MaxRecvDataSegmentLength");
+ return (EINVAL);
+ }
+ if (tmp > idl->idl_max_send_data_segment_length) {
+ ISCSI_SESSION_DEBUG(is,
+ "capping max_send_data_segment_length "
+ "from %d to %d", tmp,
+ idl->idl_max_send_data_segment_length);
+ tmp = idl->idl_max_send_data_segment_length;
+ }
+ ic->ic_max_send_data_segment_length =
+ handoff->ikh_max_send_data_segment_length = tmp;
+ } else if (strcmp(name, "MaxBurstLength") == 0) {
+ tmp = strtoul(value, NULL, 10);
+ if (tmp <= 0) {
+ ISCSI_SESSION_WARN(is,
+ "received invalid MaxBurstLength");
+ return (EINVAL);
+ }
+ if (tmp > idl->idl_max_burst_length) {
+ ISCSI_SESSION_DEBUG(is,
+ "capping MaxBurstLength "
+ "from %d to %d", tmp, idl->idl_max_burst_length);
+ tmp = idl->idl_max_burst_length;
+ }
+ handoff->ikh_max_burst_length = tmp;
+ } else if (strcmp(name, "FirstBurstLength") == 0) {
+ tmp = strtoul(value, NULL, 10);
+ if (tmp <= 0) {
+ ISCSI_SESSION_WARN(is,
+ "received invalid FirstBurstLength");
+ return (EINVAL);
+ }
+ if (tmp > idl->idl_first_burst_length) {
+ ISCSI_SESSION_DEBUG(is,
+ "capping FirstBurstLength "
+ "from %d to %d", tmp,
+ idl->idl_first_burst_length);
+ tmp = idl->idl_first_burst_length;
+ }
+ handoff->ikh_first_burst_length = tmp;
+ } else if (strcmp(name, "DefaultTime2Wait") == 0) {
+ /* Ignore */
+ } else if (strcmp(name, "DefaultTime2Retain") == 0) {
+ /* Ignore */
+ } else if (strcmp(name, "MaxOutstandingR2T") == 0) {
+ /* Ignore */
+ } else if (strcmp(name, "DataPDUInOrder") == 0) {
+ /* Ignore */
+ } else if (strcmp(name, "DataSequenceInOrder") == 0) {
+ /* Ignore */
+ } else if (strcmp(name, "ErrorRecoveryLevel") == 0) {
+ /* Ignore */
+ } else if (strcmp(name, "OFMarker") == 0) {
+ /* Ignore */
+ } else if (strcmp(name, "IFMarker") == 0) {
+ /* Ignore */
+ } else if (strcmp(name, "RDMAExtensions") == 0) {
+ if (is->is_conf.isc_iser == 1 &&
+ strcmp(value, "Yes") != 0) {
+ ISCSI_SESSION_WARN(is,
+ "received unsupported RDMAExtensions");
+ return (EINVAL);
+ }
+ } else if (strcmp(name, "InitiatorRecvDataSegmentLength") == 0) {
+ tmp = strtoul(value, NULL, 10);
+ if (tmp <= 0) {
+ ISCSI_SESSION_WARN(is, "received invalid "
+ "InitiatorRecvDataSegmentLength");
+ return (EINVAL);
+ }
+ if ((int)tmp > idl->idl_max_recv_data_segment_length) {
+ ISCSI_SESSION_DEBUG(is,
+ "capping InitiatorRecvDataSegmentLength "
+ "from %d to %d", tmp,
+ idl->idl_max_recv_data_segment_length);
+ tmp = idl->idl_max_recv_data_segment_length;
+ }
+ ic->ic_max_recv_data_segment_length =
+ handoff->ikh_max_recv_data_segment_length = tmp;
+ } else if (strcmp(name, "TargetPortalGroupTag") == 0) {
+ /* Ignore */
+ } else if (strcmp(name, "TargetRecvDataSegmentLength") == 0) {
+ tmp = strtoul(value, NULL, 10);
+ if (tmp <= 0) {
+ ISCSI_SESSION_WARN(is,
+ "%s: received invalid "
+ "TargetRecvDataSegmentLength", __func__);
+ return (EINVAL);
+ }
+ if (tmp > idl->idl_max_send_data_segment_length) {
+ ISCSI_SESSION_WARN(is,
+ "capping TargetRecvDataSegmentLength "
+ "from %d to %d", tmp,
+ idl->idl_max_send_data_segment_length);
+ tmp = idl->idl_max_send_data_segment_length;
+ }
+ ic->ic_max_send_data_segment_length =
+ handoff->ikh_max_send_data_segment_length = tmp;
+ } else {
+ ISCSI_SESSION_WARN(is, "unknown key \"%s\"; ignoring", name);
+ }
+ return (0);
+}
+
+static int
+login_negotiate(struct iscsi_session *is, struct icl_drv_limits *idl,
+ struct iscsi_kernel_handoff *handoff)
+{
+ struct icl_pdu *request, *response;
+ struct iscsi_bhs_login_response *bhslr;
+ struct iscsi_keys *request_keys, *response_keys;
+ struct icl_conn *ic;
+ int i, nrequests = 0;
+ int error;
+
+ ISCSI_SESSION_DEBUG(is, "beginning operational parameter negotiation");
+ ic = is->is_conn;
+ error = ENOMEM;
+ request_keys = NULL;
+ response_keys = NULL;
+ response = NULL;
+ request = login_new_request(is, BHSLR_STAGE_OPERATIONAL_NEGOTIATION);
+ if (request == NULL)
+ return (ENOMEM);
+ request_keys = iscsi_keys_new(M_NOWAIT);
+ if (request_keys == NULL)
+ goto out;
+
+ ISCSI_SESSION_DEBUG(is, "Limits for offload \"%s\" are "
+ "MaxRecvDataSegment=%d, max_send_dsl=%d, "
+ "MaxBurstLength=%d, FirstBurstLength=%d",
+ ic->ic_offload, idl->idl_max_recv_data_segment_length,
+ idl->idl_max_send_data_segment_length, idl->idl_max_burst_length,
+ idl->idl_first_burst_length);
+
+ /*
+ * The following keys are irrelevant for discovery sessions.
+ */
+ if (is->is_conf.isc_discovery == 0) {
+ error = iscsi_keys_add(request_keys, "iSCSIProtocolLevel", "2",
+ M_NOWAIT);
+ if (ic->ic_header_crc32c) {
+ error |= iscsi_keys_add(request_keys, "HeaderDigest",
+ "CRC32C", M_NOWAIT);
+ } else {
+ error |= iscsi_keys_add(request_keys, "HeaderDigest",
+ "None", M_NOWAIT);
+ }
+ if (ic->ic_data_crc32c) {
+ error |= iscsi_keys_add(request_keys, "DataDigest",
+ "CRC32C", M_NOWAIT);
+ } else {
+ error |= iscsi_keys_add(request_keys, "DataDigest",
+ "None", M_NOWAIT);
+ }
+
+ error |= iscsi_keys_add(request_keys, "ImmediateData", "Yes",
+ M_NOWAIT);
+ error |= iscsi_keys_add_int(request_keys, "MaxBurstLength",
+ idl->idl_max_burst_length, M_NOWAIT);
+ error |= iscsi_keys_add_int(request_keys, "FirstBurstLength",
+ idl->idl_first_burst_length, M_NOWAIT);
+ error |= iscsi_keys_add(request_keys, "InitialR2T", "Yes",
+ M_NOWAIT);
+ error |= iscsi_keys_add(request_keys, "MaxOutstandingR2T", "1",
+ M_NOWAIT);
+ if (ic->ic_iser) {
+ error |= iscsi_keys_add_int(request_keys,
+ "InitiatorRecvDataSegmentLength",
+ idl->idl_max_recv_data_segment_length, M_NOWAIT);
+ error |= iscsi_keys_add_int(request_keys,
+ "TargetRecvDataSegmentLength",
+ idl->idl_max_send_data_segment_length, M_NOWAIT);
+ error |= iscsi_keys_add(request_keys, "RDMAExtensions",
+ "Yes", M_NOWAIT);
+ } else {
+ error |= iscsi_keys_add_int(request_keys,
+ "MaxRecvDataSegmentLength",
+ idl->idl_max_recv_data_segment_length, M_NOWAIT);
+ }
+ } else {
+ error = iscsi_keys_add(request_keys, "HeaderDigest", "None",
+ M_NOWAIT);
+ error |= iscsi_keys_add(request_keys, "DataDigest", "None",
+ M_NOWAIT);
+ error |= iscsi_keys_add_int(request_keys,
+ "MaxRecvDataSegmentLength",
+ idl->idl_max_recv_data_segment_length, M_NOWAIT);
+ }
+
+ ic->ic_max_recv_data_segment_length =
+ handoff->ikh_max_recv_data_segment_length =
+ idl->idl_max_recv_data_segment_length;
+
+ error |= iscsi_keys_add(request_keys, "DefaultTime2Wait", "0",
+ M_NOWAIT);
+ error |= iscsi_keys_add(request_keys, "DefaultTime2Retain", "0",
+ M_NOWAIT);
+ error |= iscsi_keys_add(request_keys, "ErrorRecoveryLevel", "0",
+ M_NOWAIT);
+ if (error != 0) {
+ ISCSI_SESSION_WARN(is, "failed to add keys into request");
+ error = ENOMEM;
+ goto out;
+ }
+ error = iscsi_keys_save(request_keys, request, M_NOWAIT);
+ if (error != 0) {
+ ISCSI_SESSION_WARN(is, "failed to save keys to request");
+ goto out;
+ }
+ iscsi_keys_delete(request_keys);
+ request_keys = NULL;
+
+ login_send(is, request);
+ request = NULL;
+ error = login_receive(is, &response);
+ if (error != 0)
+ goto out;
+
+ response_keys = iscsi_keys_new(M_NOWAIT);
+ iscsi_keys_load(response_keys, response, M_NOWAIT);
+ for (i = 0; i < ISCSI_KEYS_MAX; i++) {
+ if (response_keys->ik_names[i] == NULL)
+ break;
+
+ login_negotiate_key(is, idl, response_keys->ik_names[i],
+ response_keys->ik_values[i], handoff);
+ }
+
+ iscsi_keys_delete(response_keys);
+ response_keys = NULL;
+
+ for (;;) {
+ bhslr = (struct iscsi_bhs_login_response *)response->ip_bhs;
+ if ((bhslr->bhslr_flags & BHSLR_FLAGS_TRANSIT) != 0)
+ break;
+
+ nrequests++;
+ if (nrequests > 5) {
+ ISCSI_SESSION_WARN(is, "received login response "
+ "without the \"T\" flag too many times; giving up");
+ break;
+ }
+
+ ISCSI_SESSION_DEBUG(is, "received login response "
+ "without the \"T\" flag; sending another request");
+
+ icl_pdu_free(response);
+ response = NULL;
+
+ request = login_new_request(is,
+ BHSLR_STAGE_OPERATIONAL_NEGOTIATION);
+ login_send(is, request);
+ request = NULL;
+ error = login_receive(is, &response);
+ if (error != 0)
+ goto out;
+ }
+
+ if (login_nsg(response) != BHSLR_STAGE_FULL_FEATURE_PHASE) {
+ ISCSI_SESSION_WARN(is,
+ "received final login response with wrong NSG 0x%x",
+ login_nsg(response));
+ }
+ icl_pdu_free(response);
+ response = NULL;
+
+ ISCSI_SESSION_DEBUG(is, "operational parameter negotiation done; "
+ "transitioning to Full Feature phase");
+out:
+ if (request_keys != NULL)
+ iscsi_keys_delete(request_keys);
+ if (response_keys != NULL)
+ iscsi_keys_delete(response_keys);
+ if (request != NULL)
+ icl_pdu_free(request);
+ if (response != NULL)
+ icl_pdu_free(response);
+ return (error);
+}
+
+static void
+login_send_chap_a(struct iscsi_session *is)
+{
+ struct icl_pdu *request;
+ struct iscsi_keys *request_keys;
+
+ request = login_new_request(is, BHSLR_STAGE_SECURITY_NEGOTIATION);
+ request_keys = iscsi_keys_new(M_NOWAIT);
+ iscsi_keys_add(request_keys, "CHAP_A", "5", M_NOWAIT);
+ iscsi_keys_save(request_keys, request, M_NOWAIT);
+ iscsi_keys_delete(request_keys);
+ login_send(is, request);
+}
+
+static int
+login_send_chap_r(struct iscsi_session *is, struct icl_pdu *response)
+{
+ struct icl_pdu *request;
+ struct iscsi_keys *request_keys, *response_keys;
+ struct iscsi_rchap *rchap;
+ const char *chap_a, *chap_c, *chap_i;
+ char *chap_r;
+ int error;
+ char *mutual_chap_c, *mutual_chap_i;
+
+ /*
+ * As in the rest of the initiator, 'request' means
+ * 'initiator -> target', and 'response' means 'target -> initiator',
+ *
+ * So, here the 'response' from the target is the packet that contains
+ * CHAP challenge; our CHAP response goes into 'request'.
+ */
+
+ request = NULL;
+ request_keys = NULL;
+ rchap = NULL;
+ chap_r = NULL;
+ mutual_chap_c = mutual_chap_i = NULL;
+
+ response_keys = iscsi_keys_new(M_NOWAIT);
+ if (response_keys == NULL)
+ return (ENOMEM);
+ error = iscsi_keys_load(response_keys, response, M_NOWAIT);
+ if (error != 0) {
+ iscsi_keys_delete(response_keys);
+ return (error);
+ }
+
+ /*
+ * First, compute the response.
+ */
+ chap_a = iscsi_keys_find(response_keys, "CHAP_A");
+ if (chap_a == NULL) {
+ ISCSI_SESSION_WARN(is, "received CHAP packet without CHAP_A");
+ error = EPROTO;
+ goto out;
+ }
+ chap_c = iscsi_keys_find(response_keys, "CHAP_C");
+ if (chap_c == NULL) {
+ ISCSI_SESSION_WARN(is, "received CHAP packet without CHAP_C");
+ error = EPROTO;
+ goto out;
+ }
+ chap_i = iscsi_keys_find(response_keys, "CHAP_I");
+ if (chap_i == NULL) {
+ ISCSI_SESSION_WARN(is, "received CHAP packet without CHAP_I");
+ error = EPROTO;
+ goto out;
+ }
+
+ if (strcmp(chap_a, "5") != 0) {
+ ISCSI_SESSION_WARN(is, "received CHAP packet "
+ "with unsupported CHAP_A \"%s\"", chap_a);
+ error = EPROTO;
+ goto out;
+ }
+
+ rchap = iscsi_rchap_new(is->is_conf.isc_secret);
+ if (rchap == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+ error = iscsi_rchap_receive(rchap, chap_i, chap_c);
+ if (error != 0) {
+ ISCSI_SESSION_WARN(is, "received CHAP packet "
+ "with malformed CHAP_I or CHAP_C");
+ error = EPROTO;
+ goto out;
+ }
+ chap_r = iscsi_rchap_get_response(rchap);
+ iscsi_rchap_delete(rchap);
+ rchap = NULL;
+
+ iscsi_keys_delete(response_keys);
+ response_keys = NULL;
+
+ request = login_new_request(is, BHSLR_STAGE_SECURITY_NEGOTIATION);
+ if (request == NULL) {
+ ISCSI_SESSION_WARN(is, "login_new_request out of memory");
+ error = ENOMEM;
+ goto out;
+ }
+ request_keys = iscsi_keys_new(M_NOWAIT);
+ if (request_keys == NULL) {
+ ISCSI_SESSION_WARN(is, "iscsi_keys_new out of memory");
+ error = ENOMEM;
+ goto out;
+ }
+ error = iscsi_keys_add(request_keys, "CHAP_N", is->is_conf.isc_user,
+ M_NOWAIT);
+ error |= iscsi_keys_add(request_keys, "CHAP_R", chap_r, M_NOWAIT);
+ if (error != 0) {
+ ISCSI_SESSION_WARN(is, "iscsi_keys_add out of memory");
+ error = ENOMEM;
+ goto out;
+ }
+ free(chap_r, M_ISCSI);
+ chap_r = NULL;
+
+ /*
+ * If we want mutual authentication, we're expected to send
+ * our CHAP_I/CHAP_C now.
+ */
+ if (is->is_conf.isc_mutual_user[0] != '\0') {
+ ISCSI_SESSION_DEBUG(is, "requesting mutual authentication; "
+ "binary challenge size is %zd bytes",
+ sizeof(is->is_boot_login.bl_mutual_chap->chap_challenge));
+
+ KASSERT(is->is_boot_login.bl_mutual_chap == NULL,
+ ("%s: is->is_boot_login.bl_mutual_chap non-null\n",
+ __func__));
+ is->is_boot_login.bl_mutual_chap = iscsi_chap_new();
+ mutual_chap_i = iscsi_chap_get_id(
+ is->is_boot_login.bl_mutual_chap);
+ if (mutual_chap_i == NULL)
+ goto out;
+ mutual_chap_c = iscsi_chap_get_challenge(
+ is->is_boot_login.bl_mutual_chap);
+ if (mutual_chap_c == NULL)
+ goto out;
+ error = iscsi_keys_add(request_keys, "CHAP_I", mutual_chap_i,
+ M_NOWAIT);
+ if (error != 0)
+ goto out;
+ error = iscsi_keys_add(request_keys, "CHAP_C", mutual_chap_c,
+ M_NOWAIT);
+ if (error != 0)
+ goto out;
+ free(mutual_chap_i, M_ISCSI);
+ free(mutual_chap_c, M_ISCSI);
+ mutual_chap_i = mutual_chap_c = NULL;
+ }
+
+ iscsi_keys_save(request_keys, request, M_NOWAIT);
+ iscsi_keys_delete(request_keys);
+ request_keys = NULL;
+ login_send(is, request);
+ request = NULL;
+
+out:
+ if (request != NULL)
+ icl_pdu_free(request);
+ if (request_keys != NULL)
+ iscsi_keys_delete(request_keys);
+ if (response_keys != NULL)
+ iscsi_keys_delete(response_keys);
+ if (rchap != NULL)
+ iscsi_rchap_delete(rchap);
+ free(chap_r, M_ISCSI);
+ free(mutual_chap_c, M_ISCSI);
+ free(mutual_chap_i, M_ISCSI);
+ return (error);
+}
+
+static int
+login_verify_mutual(struct iscsi_session *is,
+ struct icl_pdu *response)
+{
+ struct iscsi_keys *response_keys;
+ const char *chap_n, *chap_r;
+ int error;
+
+ KASSERT(is->is_boot_login.bl_mutual_chap != NULL,
+ ("%s: without mutual_chap.", __func__));
+
+ response_keys = iscsi_keys_new(M_NOWAIT);
+ if (response_keys == NULL)
+ return (ENOMEM);
+ error = iscsi_keys_load(response_keys, response, M_NOWAIT);
+ if (error != 0)
+ goto out;
+
+ chap_n = iscsi_keys_find(response_keys, "CHAP_N");
+ if (chap_n == NULL) {
+ ISCSI_SESSION_WARN(is,
+ "received CHAP Response PDU without CHAP_N");
+ error = EPROTO;
+ goto out;
+ }
+ chap_r = iscsi_keys_find(response_keys, "CHAP_R");
+ if (chap_r == NULL) {
+ ISCSI_SESSION_WARN(is,
+ "received CHAP Response PDU without CHAP_R");
+ error = EPROTO;
+ goto out;
+ }
+
+ error = iscsi_chap_receive(is->is_boot_login.bl_mutual_chap, chap_r);
+ if (error != 0) {
+ ISCSI_SESSION_WARN(is,
+ "received CHAP Response PDU with invalid CHAP_R");
+ error = EPROTO;
+ goto out;
+ }
+
+ if (strcmp(chap_n, is->is_conf.isc_mutual_user) != 0) {
+ ISCSI_SESSION_WARN(is,
+ "mutual CHAP authentication failed: wrong user");
+ error = EAUTH;
+ goto out;
+ }
+
+ error = iscsi_chap_authenticate(is->is_boot_login.bl_mutual_chap,
+ is->is_conf.isc_mutual_secret);
+ if (error != 0) {
+ ISCSI_SESSION_WARN(is,
+ "mutual CHAP authentication failed: wrong secret");
+ error = EAUTH;
+ goto out;
+ }
+ ISCSI_SESSION_DEBUG(is, "mutual CHAP authentication succeeded");
+
+out:
+ iscsi_keys_delete(response_keys);
+ iscsi_chap_delete(is->is_boot_login.bl_mutual_chap);
+ is->is_boot_login.bl_mutual_chap = NULL;
+ return (error);
+}
+
+static int
+login_chap(struct iscsi_session *is)
+{
+ struct icl_pdu *response;
+ int error;
+
+ ISCSI_SESSION_DEBUG(is, "beginning CHAP authentication; "
+ "sending CHAP_A");
+ login_send_chap_a(is);
+
+ ISCSI_SESSION_DEBUG(is, "waiting for CHAP_A/CHAP_C/CHAP_I");
+ error = login_receive(is, &response);
+ if (error != 0)
+ return (error);
+
+ ISCSI_SESSION_DEBUG(is, "sending CHAP_N/CHAP_R");
+ error = login_send_chap_r(is, response);
+ if (error != 0)
+ goto out;
+ icl_pdu_free(response);
+ response = NULL;
+
+ /*
+ * XXX: Make sure this is not susceptible to MITM.
+ */
+
+ ISCSI_SESSION_DEBUG(is, "waiting for CHAP result");
+ error = login_receive(is, &response);
+ if (error != 0)
+ goto out;
+ if (is->is_conf.isc_mutual_user[0] != '\0') {
+ error = login_verify_mutual(is, response);
+ if (error != 0)
+ goto out;
+ }
+ icl_pdu_free(response);
+ response = NULL;
+
+ ISCSI_SESSION_DEBUG(is, "CHAP authentication done");
+
+out:
+ if (response != NULL)
+ icl_pdu_free(response);
+ return (error);
+}
+
+int
+iscsi_login(struct iscsi_kernel_login *login, struct iscsi_kernel_handoff *handoff)
+{
+ struct iscsi_session *is;
+ struct icl_drv_limits *idl;
+ struct icl_pdu *request, *response;
+ struct iscsi_keys *request_keys, *response_keys;
+ struct iscsi_bhs_login_response *bhslr2;
+ const char *auth_method;
+ int error;
+ int i;
+
+ ISCSI_SESSION_LOCK_ASSERT(login->ikl_is);
+ is = login->ikl_is;
+ idl = &login->ikl_idl;
+ response = NULL;
+ request_keys = response_keys = NULL;
+ error = 0;
+ is->is_tsih = handoff->ikh_tsid;
+
+ ISCSI_SESSION_DEBUG(is, "beginning Login phase; sending Login PDU");
+ request = login_new_request(is, BHSLR_STAGE_SECURITY_NEGOTIATION);
+ if (request == NULL)
+ return (ENOMEM);
+ request_keys = iscsi_keys_new(M_NOWAIT);
+ if (request_keys == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+ if (is->is_conf.isc_mutual_user[0] != '\0') {
+ error = iscsi_keys_add(request_keys, "AuthMethod", "CHAP",
+ M_NOWAIT);
+ } else if (is->is_conf.isc_user[0] != '\0') {
+ /*
+ * Give target a chance to skip authentication if it
+ * doesn't feel like it.
+ *
+ * None is first, CHAP second; this is to work around
+ * what seems to be LIO (Linux target) bug: otherwise,
+ * if target is configured with no authentication,
+ * and we are configured to authenticate, the target
+ * will erroneously respond with AuthMethod=CHAP
+ * instead of AuthMethod=None, and will subsequently
+ * fail the connection. This usually happens with
+ * Discovery sessions, which default to no authentication.
+ */
+ error = iscsi_keys_add(request_keys, "AuthMethod", "None,CHAP",
+ M_NOWAIT);
+ } else {
+ error = iscsi_keys_add(request_keys, "AuthMethod", "None",
+ M_NOWAIT);
+ }
+ error |= iscsi_keys_add(request_keys, "InitiatorName",
+ is->is_conf.isc_initiator, M_NOWAIT);
+ if (is->is_conf.isc_initiator_alias[0] != '\0') {
+ error |= iscsi_keys_add(request_keys, "InitiatorAlias",
+ is->is_conf.isc_initiator_alias, M_NOWAIT);
+ }
+ if (is->is_conf.isc_discovery == 0) {
+ error |= iscsi_keys_add(request_keys, "SessionType", "Normal", M_NOWAIT);
+ error |= iscsi_keys_add(request_keys,
+ "TargetName", is->is_conf.isc_target, M_NOWAIT);
+ } else {
+ error |= iscsi_keys_add(request_keys, "SessionType", "Discovery",
+ M_NOWAIT);
+ }
+ if (error != 0) {
+ error = ENOMEM;
+ goto out;
+ }
+ error = iscsi_keys_save(request_keys, request, M_NOWAIT);
+ if (error != 0)
+ goto out;
+ iscsi_keys_delete(request_keys);
+ request_keys = NULL;
+
+ login_send(is, request);
+ request = NULL;
+ error = login_receive(is, &response);
+ if (error != 0)
+ goto out;
+
+ response_keys = iscsi_keys_new(M_NOWAIT);
+ iscsi_keys_load(response_keys, response, M_NOWAIT);
+
+ for (i = 0; i < ISCSI_KEYS_MAX; i++) {
+ if (response_keys->ik_names[i] == NULL)
+ break;
+
+ /*
+ * Not interested in AuthMethod at this point; we only need
+ * to parse things such as TargetAlias.
+ *
+ * XXX: This is somewhat ugly. We should have a way to apply
+ * all the keys to the session and use that by default
+ * instead of discarding them.
+ */
+ if (strcmp(response_keys->ik_names[i], "AuthMethod") == 0)
+ continue;
+
+ error = login_negotiate_key(is, idl, response_keys->ik_names[i],
+ response_keys->ik_values[i], handoff);
+ if (error != 0)
+ goto out;
+ }
+
+ bhslr2 = (struct iscsi_bhs_login_response *)response->ip_bhs;
+ if ((bhslr2->bhslr_flags & BHSLR_FLAGS_TRANSIT) != 0 &&
+ login_nsg(response) == BHSLR_STAGE_OPERATIONAL_NEGOTIATION) {
+ if (is->is_conf.isc_mutual_user[0] != '\0') {
+ ISCSI_SESSION_WARN(is, "target requested transition "
+ "to operational parameter negotiation, "
+ "but we require mutual CHAP");
+ error = EPROTO;
+ goto out;
+ }
+
+ ISCSI_SESSION_DEBUG(is, "target requested transition "
+ "to operational parameter negotiation");
+ iscsi_keys_delete(response_keys);
+ response_keys = NULL;
+ icl_pdu_free(response);
+ response = NULL;
+ error = login_negotiate(is, idl, handoff);
+ goto out;
+ }
+
+ auth_method = iscsi_keys_find(response_keys, "AuthMethod");
+ if (auth_method == NULL) {
+ ISCSI_SESSION_WARN(is, "received response without AuthMethod");
+ error = EPROTO;
+ goto out;
+ }
+ if (strcmp(auth_method, "None") == 0) {
+ if (is->is_conf.isc_mutual_user[0] != '\0') {
+ ISCSI_SESSION_WARN(is,
+ "target does not require authantication, "
+ "but we require mutual CHAP");
+ error = EAUTH;
+ goto out;
+ }
+
+ ISCSI_SESSION_DEBUG(is,
+ "target does not require authentication");
+ iscsi_keys_delete(response_keys);
+ response_keys = NULL;
+ icl_pdu_free(response);
+ error = login_negotiate(is, idl, handoff);
+ goto out;
+ }
+
+ if (strcmp(auth_method, "CHAP") != 0) {
+ ISCSI_SESSION_WARN(is,
+ "received response with unsupported AuthMethod \"%s\"",
+ auth_method);
+ error = EAUTH;
+ goto out;
+ }
+
+ if (is->is_conf.isc_user[0] == '\0' ||
+ is->is_conf.isc_secret[0] == '\0') {
+ ISCSI_SESSION_WARN(is,
+ "target requests CHAP authentication, but we don't "
+ "have user and secret");
+ error = EAUTH;
+ goto out;
+ }
+
+ iscsi_keys_delete(response_keys);
+ response_keys = NULL;
+ icl_pdu_free(response);
+ response = NULL;
+
+ error = login_chap(is);
+ if (error != 0)
+ goto out;
+ error = login_negotiate(is, idl, handoff);
+
+out:
+ if (request != NULL)
+ icl_pdu_free(request);
+ if (response != NULL)
+ icl_pdu_free(response);
+ if (request_keys != NULL)
+ iscsi_keys_delete(request_keys);
+ if (response_keys != NULL)
+ iscsi_keys_delete(response_keys);
+ return (error);
+}
Index: sys/modules/iscsi/Makefile
===================================================================
--- sys/modules/iscsi/Makefile
+++ sys/modules/iscsi/Makefile
@@ -15,6 +15,13 @@
SRCS+= device_if.h
SRCS+= icl_conn_if.c
SRCS+= icl_conn_if.h
+SRCS+= iscsi_login.c
+SRCS+= iscsi_keys.c
+SRCS+= iscsi_chap.c
+SRCS+= iscsi_base64.c
+SRCS+= iscsi_ibft.c
+
+SRCS+= opt_acpi.h
.if ${MK_OFED} != "no" || defined(ALL_MODULES)
CFLAGS+=-DICL_KERNEL_PROXY
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Jan 13, 10:03 AM (21 h, 25 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
15780662
Default Alt Text
D34420.diff (110 KB)
Attached To
Mode
D34420: iscsi: squashed
Attached
Detach File
Event Timeline
Log In to Comment