From 7f1bdbd3733225229fa1f07a0a424ffdafb2ff5e Mon Sep 17 00:00:00 2001 From: mlelstv Date: Sat, 25 Nov 2023 08:06:02 +0000 Subject: [PATCH] Parse IPv6 targets and handle IPv6 addresses. --- sbin/iscsictl/iscsic_parse.c | 78 +++++++------ sbin/iscsid/iscsid_driverif.c | 212 ++++++++++++++++++---------------- 2 files changed, 157 insertions(+), 133 deletions(-) diff --git a/sbin/iscsictl/iscsic_parse.c b/sbin/iscsictl/iscsic_parse.c index 8a6f21310e82..e7d9f5f9e754 100644 --- a/sbin/iscsictl/iscsic_parse.c +++ b/sbin/iscsictl/iscsic_parse.c @@ -1,4 +1,4 @@ -/* $NetBSD: iscsic_parse.c,v 1.4 2021/12/03 13:27:38 andvar Exp $ */ +/* $NetBSD: iscsic_parse.c,v 1.5 2023/11/25 08:06:02 mlelstv Exp $ */ /*- * Copyright (c) 2005,2006,2011 The NetBSD Foundation, Inc. @@ -48,50 +48,62 @@ STATIC void get_address(iscsi_portal_address_t * portal, char *str, char *arg) { - char *sp, *sp2; + char *sp; int val; if (!str || !*str) arg_error(arg, "Address is missing"); - /* is there a port? don't check inside square brackets (IPv6 addr) */ - for (sp = str + 1, val = 0; *sp && (*sp != ':' || val); sp++) { - if (*sp == '[') - val = 1; - else if (*sp == ']') - val = 0; - } - - /* */ - if (*sp) { - for (sp2 = sp + 1; *sp2 && *sp2 != ':'; sp2++); - /* if there's a second colon, assume it's an unbracketed IPv6 address */ - if (!*sp2) { - /* truncate source, that's the address */ - *sp++ = '\0'; - if (sscanf(sp, "%d", &val) != 1) - arg_error(arg, "Bad address format: Expected port number"); - if (val < 0 || val > 0xffff) - arg_error(arg, "Bad address format: Port number out of range"); - portal->port = (uint16_t) val; - } - /* is there a group tag? */ - for (; isdigit((unsigned char)*sp); sp++); - if (*sp && *sp != ',') - arg_error(arg, "Bad address format: Extra character(s) '%c'", *sp); - } else - for (sp = str + 1; *sp && *sp != ','; sp++); - - if (*sp) { + /* Parse and strip trailing group tag */ + sp = strrchr(str, ','); + if (sp != NULL) { if (sscanf(sp + 1, "%d", &val) != 1) arg_error(arg, "Bad address format: Expected group tag"); if (val < 0 || val > 0xffff) arg_error(arg, "Bad address format: Group tag out of range"); portal->group_tag = (uint16_t) val; - /* truncate source, that's the address */ *sp = '\0'; } - /* only check length, don't verify correct format (too many possibilities) */ + + /* Skip over bracketed IPv6 address */ + sp = strchr(str, ']'); + if (sp != NULL) + sp++; + else + sp = str; + + /* Parse and strip trailing port number */ + sp = strchr(sp, ':'); + if (sp != NULL) { + if (strchr(sp + 1, ':') != NULL) { + /* + * If there's a second colon, assume + * it's an unbracketed IPv6 address + */ + portal->port = 0; + } else { + if (sscanf(sp + 1, "%d", &val) != 1) + arg_error(arg, "Bad address format: Expected port number"); + if (val < 0 || val > 0xffff) + arg_error(arg, "Bad address format: Port number ut of range"); + portal->port = (uint16_t) val; + *sp = '\0'; + } + } + + /* Remove brackets */ + if (*str == '[') { + sp = strchr(str, ']'); + if (sp != NULL && !*(sp+1)) { + str = str + 1; + *sp = '\0'; + } + } + + /* + * only check length, don't verify correct format + * (too many possibilities) + */ if (strlen(str) >= sizeof(portal->address)) arg_error(arg, "Bad address format: Address string too long"); diff --git a/sbin/iscsid/iscsid_driverif.c b/sbin/iscsid/iscsid_driverif.c index 5f9917547fda..987a9c2b0d51 100644 --- a/sbin/iscsid/iscsid_driverif.c +++ b/sbin/iscsid/iscsid_driverif.c @@ -1,4 +1,4 @@ -/* $NetBSD: iscsid_driverif.c,v 1.8 2016/05/29 13:35:45 mlelstv Exp $ */ +/* $NetBSD: iscsid_driverif.c,v 1.9 2023/11/25 08:06:02 mlelstv Exp $ */ /*- * Copyright (c) 2005,2006,2011 The NetBSD Foundation, Inc. @@ -98,23 +98,29 @@ set_node_name(iscsid_set_node_name_req_t * par) static int bind_socket(int sock, uint8_t * addr) { - struct sockaddr_in serverAddress; - struct hostent *host; + struct addrinfo hints, *ai, *ai0; + int ret = FALSE; DEB(8, ("Binding to <%s>", addr)); - (void) memset(&serverAddress, 0x0, sizeof(serverAddress)); - host = gethostbyname((char *)addr); - if (host == NULL) - return FALSE; - if (host->h_length > (int)sizeof(serverAddress.sin_addr)) - return FALSE; - serverAddress.sin_family = host->h_addrtype; - serverAddress.sin_port = 0; - serverAddress.sin_len = host->h_length; - memcpy(&serverAddress.sin_addr, host->h_addr_list[0], host->h_length); + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + if (getaddrinfo((char *)addr, NULL, &hints, &ai0)) + return ret; - return bind(sock, (struct sockaddr *)(void *)&serverAddress, - (socklen_t)sizeof(serverAddress)) >= 0; + for (ai = ai0; ai; ai = ai->ai_next) { + if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) + continue; + + listen(sock, 5); + ret = TRUE; + break; + } + freeaddrinfo(ai0); + + return ret; } @@ -183,14 +189,13 @@ make_connection(session_t * sess, iscsid_login_req_t * req, target_t *target; portal_t *portal = NULL; iscsi_portal_address_t *addr; - struct sockaddr_in serverAddress; - struct hostent *host; + struct addrinfo hints, *ai, *ai0; + char portnum[6]; initiator_t *init; DEB(9, ("Make Connection sess=%p, req=%p, res=%p, stid=%p", sess, req, res, stid)); (void) memset(&loginp, 0x0, sizeof(loginp)); - (void) memset(&serverAddress, 0x0, sizeof(serverAddress)); /* find the target portal */ if (stid != NULL) { @@ -277,69 +282,72 @@ make_connection(session_t * sess, iscsid_login_req_t * req, /* translate target address */ DEB(8, ("Connecting to <%s>, port %d", addr->address, addr->port)); - host = gethostbyname((char *)addr->address); - if (host == NULL) { - switch (h_errno) { - case HOST_NOT_FOUND: - res->status = ISCSID_STATUS_HOST_NOT_FOUND; - break; - case TRY_AGAIN: - res->status = ISCSID_STATUS_HOST_TRY_AGAIN; - break; - default: - res->status = ISCSID_STATUS_HOST_ERROR; - break; - } - return NULL; - } - if (host->h_length > (int)sizeof(serverAddress.sin_addr)) { + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + snprintf(portnum, sizeof(portnum), "%u", addr->port); + ret = getaddrinfo((char *)addr->address, portnum, &hints, &ai0); + switch (ret) { + case 0: + break; + case EAI_NODATA: + res->status = ISCSID_STATUS_HOST_NOT_FOUND; + break; + case EAI_AGAIN: + res->status = ISCSID_STATUS_HOST_TRY_AGAIN; + break; + default: res->status = ISCSID_STATUS_HOST_ERROR; - return NULL; + break; } - DEB(8, ("Gethostbyname OK, addrtype %d, len %d, addr %x", - host->h_addrtype, host->h_length, *((int *) host->h_addr_list[0]))); - serverAddress.sin_family = host->h_addrtype; - serverAddress.sin_port = htons((addr->port) - ? addr->port : ISCSI_DEFAULT_PORT); - serverAddress.sin_len = host->h_length; - memcpy(&serverAddress.sin_addr, host->h_addr_list[0], host->h_length); /* alloc the connection structure */ conn = calloc(1, sizeof(*conn)); if (conn == NULL) { + freeaddrinfo(ai0); res->status = ISCSID_STATUS_NO_RESOURCES; return NULL; } - /* create and connect the socket */ - sock = socket(AF_INET, SOCK_STREAM, 0); + + res->status = ISCSID_STATUS_HOST_ERROR; + sock = -1; + for (ai = ai0; ai; ai = ai->ai_next) { + /* create and connect the socket */ + sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (sock < 0) { + res->status = ISCSID_STATUS_SOCKET_ERROR; + break; + } + + if (init) { + if (!bind_socket(sock, init->address)) { + close(sock); + res->status = ISCSID_STATUS_INITIATOR_BIND_ERROR; + break; + } + } + + DEB(8, ("Connecting socket")); + if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) { + close(sock); + res->status = ISCSID_STATUS_CONNECT_ERROR; + continue; + } + + res->status = ISCSID_STATUS_SUCCESS; + break; + } + freeaddrinfo(ai0); + if (sock < 0) { free(conn); - res->status = ISCSID_STATUS_SOCKET_ERROR; - return NULL; - } - - if (init) { - if (!bind_socket(sock, init->address)) { - close(sock); - free(conn); - res->status = ISCSID_STATUS_INITIATOR_BIND_ERROR; - return NULL; - } - } - - DEB(8, ("Connecting socket")); - if (connect(sock, (struct sockaddr *)(void *)&serverAddress, - (socklen_t)sizeof(serverAddress)) < 0) { - close(sock); - free(conn); - res->status = ISCSID_STATUS_CONNECT_ERROR; DEB(1, ("Connecting to socket failed (error %d), returning %d", errno, res->status)); return NULL; } + /* speed up socket processing */ setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &yes, (socklen_t)sizeof(yes)); - /* setup login parameter structure */ loginp.socket = sock; if (target->TargetName[0]) { @@ -506,11 +514,10 @@ event_recover_connection(uint32_t sid, uint32_t cid) portal_t *portal; initiator_t *init; iscsi_portal_address_t *addr; - struct sockaddr_in serverAddress; - struct hostent *host; + struct addrinfo hints, *ai, *ai0; + char portnum[6]; DEB(1, ("Event_Recover_Connection sid=%d, cid=%d", sid, cid)); - (void) memset(&serverAddress, 0x0, sizeof(serverAddress)); LOCK_SESSIONS; @@ -543,47 +550,53 @@ event_recover_connection(uint32_t sid, uint32_t cid) DEB(1, ("Event_Recover_Connection Connecting to <%s>, port %d", addr->address, addr->port)); - if ((host = gethostbyname((char *)addr->address)) == NULL) { - DEB(1, ("GetHostByName failed (error %d)", h_errno)); - return; - } - if (host->h_length > (int)sizeof(serverAddress.sin_addr)) { - DEB(1, ("Host address length invalid (%d)", host->h_length)); + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + snprintf(portnum, sizeof(portnum), "%u", addr->port); + ret = getaddrinfo((char *)addr->address, portnum, &hints, &ai0); + if (ret) { + DEB(1, ("getaddrinfo failed (%s)", gai_strerror(ret))); return; } - serverAddress.sin_family = host->h_addrtype; - serverAddress.sin_port = htons((addr->port) - ? addr->port : ISCSI_DEFAULT_PORT); - serverAddress.sin_len = host->h_length; - memcpy(&serverAddress.sin_addr, host->h_addr_list[0], host->h_length); + sock = -1; + for (ai = ai0; ai; ai = ai->ai_next) { - /* create and connect the socket */ - sock = socket(AF_INET, SOCK_STREAM, 0); - if (sock < 0) { - DEB(1, ("Creating socket failed (error %d)", errno)); - return; - } - - DEB(1, ("recover_connection: Socket = %d", sock)); - - if (init) { - if (!bind_socket(sock, init->address)) { - DEB(1, ("Binding to interface failed (error %d)", errno)); - close(sock); - return; + /* create and connect the socket */ + sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (sock < 0) { + DEB(1, ("Creating socket failed (error %d)", errno)); + break; } - } - if (connect(sock, (struct sockaddr *)(void *)&serverAddress, - (socklen_t)sizeof(serverAddress)) < 0) { - DEB(1, ("Connecting to socket failed (error %d)", errno)); - close(sock); - return; + DEB(1, ("recover_connection: Socket = %d", sock)); + + if (init) { + if (!bind_socket(sock, init->address)) { + DEB(1, ("Binding to interface failed (error %d)", errno)); + close(sock); + sock = -1; + continue; + } + } + + if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) { + close(sock); + sock = -1; + DEB(1, ("Connecting to socket failed (error %d)", errno)); + continue; + } + + break; } + freeaddrinfo(ai0); + + if (sock < 0) + return; + /* speed up socket processing */ setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &yes, (socklen_t)sizeof(yes)); - conn->loginp.socket = sock; conn->loginp.status = 0; ret = ioctl(driver, ISCSI_RESTORE_CONNECTION, &conn->loginp); @@ -631,7 +644,6 @@ log_in(iscsid_login_req_t * req, iscsid_response_t * res) UNLOCK_SESSIONS; } - /* * add_connection: * Handle ADD_CONNECTION request: Log secondary connection into given portal.