Page MenuHomeFreeBSD

D34420.diff
No OneTemporary

D34420.diff

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

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)

Event Timeline