mirror of
https://github.com/SCST-project/scst.git
synced 2026-05-14 09:11:27 +00:00
git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@7396 d57e44dd-8a1f-0410-8b47-8ef2f437770f
1229 lines
27 KiB
C
1229 lines
27 KiB
C
/*
|
|
* iSNS functions
|
|
*
|
|
* Copyright (C) 2006 FUJITA Tomonori <tomof@acm.org>
|
|
* Copyright (C) 2007 - 2018 Vladislav Bolkhovitin
|
|
* Copyright (C) 2007 - 2018 Western Digital Corporation
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program.
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <netdb.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <netinet/in.h>
|
|
#include <netinet/tcp.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/types.h>
|
|
|
|
#include "iscsid.h"
|
|
#include "isns_proto.h"
|
|
#include "misc.h"
|
|
|
|
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
|
#define BUFSIZE (1 << 18)
|
|
|
|
struct isns_io {
|
|
char *buf;
|
|
int offset;
|
|
};
|
|
|
|
struct isns_qry_mgmt {
|
|
char name[ISCSI_NAME_LEN];
|
|
uint16_t transaction;
|
|
struct __qelem qlist;
|
|
};
|
|
|
|
struct isns_initiator {
|
|
char name[ISCSI_NAME_LEN];
|
|
struct __qelem ilist;
|
|
};
|
|
|
|
char *isns_server;
|
|
int isns_access_control;
|
|
char isns_entity_target_name[ISCSI_NAME_LEN];
|
|
int isns_timeout = -1;
|
|
|
|
static LIST_HEAD(qry_list);
|
|
static uint16_t scn_listen_port;
|
|
static int isns_fd, scn_listen_fd, scn_fd;
|
|
static struct isns_io isns_rx, scn_rx;
|
|
static char *rxbuf;
|
|
static uint16_t transaction;
|
|
static uint32_t current_timeout = 30; /* seconds */
|
|
static char eid[ISCSI_NAME_LEN];
|
|
static uint8_t ip[16]; /* SCST iSCSI supports only one portal */
|
|
static struct sockaddr_storage ss;
|
|
|
|
int isns_scn_access_allowed(uint32_t tid, char *name)
|
|
{
|
|
struct isns_initiator *ini;
|
|
struct target *target = target_find_by_id(tid);
|
|
|
|
if ((isns_server == NULL) || !isns_access_control)
|
|
return 1;
|
|
|
|
if (!target)
|
|
return 0;
|
|
|
|
list_for_each_entry(ini, &target->isns_head, ilist) {
|
|
if (!strcmp(ini->name, name))
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int isns_get_ip(int fd)
|
|
{
|
|
int err, i;
|
|
uint32_t addr;
|
|
union {
|
|
struct sockaddr sa;
|
|
struct sockaddr_in sin;
|
|
struct sockaddr_in6 sin6;
|
|
} lss;
|
|
socklen_t slen = sizeof(lss);
|
|
|
|
err = getsockname(fd, &lss.sa, &slen);
|
|
if (err) {
|
|
log_error("getsockname error: %s!", strerror(errno));
|
|
return err;
|
|
}
|
|
|
|
err = getnameinfo(&lss.sa, sizeof(lss),
|
|
eid, sizeof(eid), NULL, 0, 0);
|
|
if (err == EAI_AGAIN)
|
|
err = getnameinfo(&lss.sa, sizeof(lss),
|
|
eid, sizeof(eid), NULL, 0, NI_NUMERICHOST);
|
|
if (err) {
|
|
log_error("getnameinfo error: %s!", get_error_str(err));
|
|
return err;
|
|
}
|
|
|
|
switch (lss.sa.sa_family) {
|
|
case AF_INET:
|
|
addr = lss.sin.sin_addr.s_addr;
|
|
|
|
ip[10] = ip[11] = 0xff;
|
|
ip[15] = 0xff & (addr >> 24);
|
|
ip[14] = 0xff & (addr >> 16);
|
|
ip[13] = 0xff & (addr >> 8);
|
|
ip[12] = 0xff & addr;
|
|
break;
|
|
case AF_INET6:
|
|
for (i = 0; i < ARRAY_SIZE(ip); i++)
|
|
ip[i] = lss.sin6.sin6_addr.s6_addr[i];
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int isns_connect(void)
|
|
{
|
|
int fd, err;
|
|
|
|
log_debug(1, "Going to connect to iSNS server %s", isns_server);
|
|
|
|
fd = socket(ss.ss_family, SOCK_STREAM, IPPROTO_TCP);
|
|
if (fd < 0) {
|
|
log_error("unable to create (%s) %d!", strerror(errno),
|
|
ss.ss_family);
|
|
return -errno;
|
|
}
|
|
|
|
/*
|
|
* ToDo: must be made non-blocking, otherwise for an unreacheable
|
|
* server it blocks all other events processing until timeout (30 secs).
|
|
*/
|
|
err = connect(fd, (struct sockaddr *)&ss, sizeof(ss));
|
|
if (err < 0) {
|
|
log_error("unable to connect (%s) %d!", strerror(errno),
|
|
ss.ss_family);
|
|
close(fd);
|
|
return -errno;
|
|
}
|
|
|
|
log_info("%s %d: new connection %d", __func__, __LINE__, fd);
|
|
|
|
if (!strlen(eid)) {
|
|
err = isns_get_ip(fd);
|
|
if (err) {
|
|
close(fd);
|
|
return err;
|
|
}
|
|
}
|
|
|
|
isns_fd = fd;
|
|
isns_set_fd(fd, scn_listen_fd, scn_fd);
|
|
|
|
return fd;
|
|
}
|
|
|
|
static void isns_hdr_init(struct isns_hdr *hdr, uint16_t function,
|
|
uint16_t length, uint16_t flags,
|
|
uint16_t trans, uint16_t sequence)
|
|
{
|
|
hdr->version = htons(0x0001);
|
|
hdr->function = htons(function);
|
|
hdr->length = htons(length);
|
|
hdr->flags = htons(flags);
|
|
hdr->transaction = htons(trans);
|
|
hdr->sequence = htons(sequence);
|
|
}
|
|
|
|
static int isns_tlv_set(struct isns_tlv **tlv, int max_tlv_buflen,
|
|
uint32_t tag, uint32_t length, void *value)
|
|
{
|
|
int l = length;
|
|
int res;
|
|
|
|
if (l % ISNS_ALIGN)
|
|
l += (ISNS_ALIGN - (l % ISNS_ALIGN));
|
|
|
|
if (sizeof(struct isns_tlv) + l > max_tlv_buflen) {
|
|
log_error("Too big tlv len %d (max allowed %d)", l,
|
|
max_tlv_buflen);
|
|
res = -EOVERFLOW;
|
|
goto out;
|
|
}
|
|
|
|
(*tlv)->tag = htonl(tag);
|
|
(*tlv)->length = htonl(l);
|
|
|
|
if (length)
|
|
memcpy((*tlv)->value, value, length);
|
|
|
|
l += sizeof(struct isns_tlv);
|
|
*tlv = (struct isns_tlv *)((char *)*tlv + l);
|
|
|
|
res = l;
|
|
|
|
out:
|
|
return res;
|
|
}
|
|
|
|
static int isns_scn_deregister(char *name)
|
|
{
|
|
int err;
|
|
uint16_t flags, length = 0;
|
|
char buf[2048];
|
|
struct isns_hdr *hdr = (struct isns_hdr *)buf;
|
|
struct isns_tlv *tlv;
|
|
int max_buf;
|
|
|
|
if (!isns_fd) {
|
|
err = isns_connect();
|
|
if (err < 0)
|
|
goto out;
|
|
}
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
tlv = (struct isns_tlv *)hdr->pdu;
|
|
max_buf = sizeof(buf) - offsetof(struct isns_hdr, pdu);
|
|
|
|
err = isns_tlv_set(&tlv, max_buf - length, ISNS_ATTR_ISCSI_NAME,
|
|
strlen(name) + 1, name);
|
|
if (err < 0)
|
|
goto out;
|
|
length += err;
|
|
|
|
err = isns_tlv_set(&tlv, max_buf - length, ISNS_ATTR_ISCSI_NAME,
|
|
strlen(name) + 1, name);
|
|
if (err < 0)
|
|
goto out;
|
|
length += err;
|
|
|
|
flags = ISNS_FLAG_CLIENT | ISNS_FLAG_LAST_PDU | ISNS_FLAG_FIRST_PDU;
|
|
isns_hdr_init(hdr, ISNS_FUNC_SCN_DEREG, length, flags,
|
|
++transaction, 0);
|
|
|
|
err = write(isns_fd, buf, length + sizeof(struct isns_hdr));
|
|
if (err < 0)
|
|
log_error("%s %d: %s", __func__, __LINE__, strerror(errno));
|
|
|
|
out:
|
|
return err;
|
|
}
|
|
|
|
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
|
#define correct_scn_flag_endiannes(x) \
|
|
{ \
|
|
x = (x & 0x55555555) << 1 | (x & 0xaaaaaaaa) >> 1; \
|
|
x = (x & 0x33333333) << 2 | (x & 0xcccccccc) >> 2; \
|
|
x = (x & 0x0f0f0f0f) << 4 | (x & 0xf0f0f0f0) >> 4; \
|
|
x = (x & 0x00ff00ff) << 8 | (x & 0xff00ff00) >> 8; \
|
|
x = (x & 0x0000ffff) << 16 | (x & 0xffff0000) >> 16; \
|
|
}
|
|
#else
|
|
#define correct_scn_flag_endiannes(x) { }
|
|
#endif
|
|
|
|
static int isns_scn_register(void)
|
|
{
|
|
int err;
|
|
uint16_t flags, length = 0;
|
|
uint32_t scn_flags;
|
|
char buf[4096];
|
|
struct isns_hdr *hdr = (struct isns_hdr *)buf;
|
|
int max_buf;
|
|
struct isns_tlv *tlv;
|
|
struct target *target;
|
|
|
|
if (list_empty(&targets_list))
|
|
return 0;
|
|
|
|
if (!isns_fd) {
|
|
err = isns_connect();
|
|
if (err < 0)
|
|
goto out;
|
|
}
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
tlv = (struct isns_tlv *)hdr->pdu;
|
|
max_buf = sizeof(buf) - offsetof(struct isns_hdr, pdu);
|
|
|
|
if (strlen(isns_entity_target_name) < 1) {
|
|
target = list_entry(targets_list.q_forw, struct target, tlist);
|
|
err = isns_tlv_set(&tlv, max_buf - length, ISNS_ATTR_ISCSI_NAME,
|
|
strlen(target->name) + 1, target->name);
|
|
} else {
|
|
err = isns_tlv_set(&tlv, max_buf - length, ISNS_ATTR_ISCSI_NAME,
|
|
strlen(isns_entity_target_name) + 1, isns_entity_target_name);
|
|
}
|
|
|
|
if (err < 0)
|
|
goto out;
|
|
length += err;
|
|
|
|
err = isns_tlv_set(&tlv, max_buf - length, 0, 0, 0);
|
|
if (err < 0)
|
|
goto out;
|
|
length += err;
|
|
|
|
scn_flags = ISNS_SCN_FLAG_INITIATOR | ISNS_SCN_FLAG_OBJECT_REMOVE |
|
|
ISNS_SCN_FLAG_OBJECT_ADDED | ISNS_SCN_FLAG_OBJECT_UPDATED;
|
|
correct_scn_flag_endiannes(scn_flags);
|
|
scn_flags = htonl(scn_flags);
|
|
|
|
err = isns_tlv_set(&tlv, max_buf - length, ISNS_ATTR_ISCSI_SCN_BITMAP,
|
|
sizeof(scn_flags), &scn_flags);
|
|
if (err < 0)
|
|
goto out;
|
|
length += err;
|
|
|
|
flags = ISNS_FLAG_CLIENT | ISNS_FLAG_LAST_PDU | ISNS_FLAG_FIRST_PDU;
|
|
isns_hdr_init(hdr, ISNS_FUNC_SCN_REG, length, flags, ++transaction, 0);
|
|
|
|
err = write(isns_fd, buf, length + sizeof(struct isns_hdr));
|
|
if (err < 0)
|
|
log_error("%s %d: %s", __func__, __LINE__, strerror(errno));
|
|
|
|
out:
|
|
return err;
|
|
}
|
|
|
|
static int isns_attr_query(char *name)
|
|
{
|
|
int err;
|
|
uint16_t flags, length = 0;
|
|
char buf[4096];
|
|
struct isns_hdr *hdr = (struct isns_hdr *)buf;
|
|
struct isns_tlv *tlv;
|
|
struct target *target;
|
|
uint32_t node = htonl(ISNS_NODE_INITIATOR);
|
|
struct isns_qry_mgmt *mgmt;
|
|
int max_buf;
|
|
|
|
if (list_empty(&targets_list))
|
|
return 0;
|
|
|
|
if (!isns_fd) {
|
|
err = isns_connect();
|
|
if (err < 0)
|
|
goto out;
|
|
}
|
|
|
|
mgmt = malloc(sizeof(*mgmt));
|
|
if (!mgmt)
|
|
return 0;
|
|
list_add_tail(&mgmt->qlist, &qry_list);
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
tlv = (struct isns_tlv *)hdr->pdu;
|
|
max_buf = sizeof(buf) - offsetof(struct isns_hdr, pdu);
|
|
|
|
if (name)
|
|
snprintf(mgmt->name, sizeof(mgmt->name), "%s", name);
|
|
else {
|
|
mgmt->name[0] = '\0';
|
|
target = list_entry(targets_list.q_forw, struct target, tlist);
|
|
name = target->name;
|
|
}
|
|
|
|
err = isns_tlv_set(&tlv, max_buf - length, ISNS_ATTR_ISCSI_NAME,
|
|
strlen(name) + 1, name);
|
|
if (err < 0)
|
|
goto out;
|
|
length += err;
|
|
|
|
err = isns_tlv_set(&tlv, max_buf - length, ISNS_ATTR_ISCSI_NODE_TYPE,
|
|
sizeof(node), &node);
|
|
if (err < 0)
|
|
goto out;
|
|
length += err;
|
|
|
|
err = isns_tlv_set(&tlv, max_buf - length, 0, 0, 0);
|
|
if (err < 0)
|
|
goto out;
|
|
length += err;
|
|
|
|
err = isns_tlv_set(&tlv, max_buf - length, ISNS_ATTR_ISCSI_NAME, 0, 0);
|
|
if (err < 0)
|
|
goto out;
|
|
length += err;
|
|
|
|
err = isns_tlv_set(&tlv, max_buf - length, ISNS_ATTR_ISCSI_NODE_TYPE, 0, 0);
|
|
if (err < 0)
|
|
goto out;
|
|
length += err;
|
|
|
|
err = isns_tlv_set(&tlv, max_buf - length, ISNS_ATTR_PORTAL_IP_ADDRESS, 0, 0);
|
|
if (err < 0)
|
|
goto out;
|
|
length += err;
|
|
|
|
flags = ISNS_FLAG_CLIENT | ISNS_FLAG_LAST_PDU | ISNS_FLAG_FIRST_PDU;
|
|
isns_hdr_init(hdr, ISNS_FUNC_DEV_ATTR_QRY, length, flags,
|
|
++transaction, 0);
|
|
mgmt->transaction = transaction;
|
|
|
|
err = write(isns_fd, buf, length + sizeof(struct isns_hdr));
|
|
if (err < 0)
|
|
log_error("%s %d: %s", __func__, __LINE__, strerror(errno));
|
|
|
|
out:
|
|
return err;
|
|
}
|
|
|
|
static int isns_deregister(void)
|
|
{
|
|
int err;
|
|
uint16_t flags, length = 0;
|
|
char buf[4096];
|
|
struct isns_hdr *hdr = (struct isns_hdr *)buf;
|
|
struct isns_tlv *tlv;
|
|
struct target *target;
|
|
int max_buf;
|
|
|
|
if (list_empty(&targets_list))
|
|
return 0;
|
|
|
|
if (!isns_fd) {
|
|
err = isns_connect();
|
|
if (err < 0)
|
|
goto out;
|
|
}
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
tlv = (struct isns_tlv *)hdr->pdu;
|
|
max_buf = sizeof(buf) - offsetof(struct isns_hdr, pdu);
|
|
|
|
target = list_entry(targets_list.q_forw, struct target, tlist);
|
|
|
|
err = isns_tlv_set(&tlv, max_buf - length, ISNS_ATTR_ISCSI_NAME,
|
|
strlen(target->name) + 1, target->name);
|
|
if (err < 0)
|
|
goto out;
|
|
length += err;
|
|
|
|
err = isns_tlv_set(&tlv, max_buf - length, 0, 0, 0);
|
|
if (err < 0)
|
|
goto out;
|
|
length += err;
|
|
|
|
err = isns_tlv_set(&tlv, max_buf - length, ISNS_ATTR_ENTITY_IDENTIFIER,
|
|
strlen(eid) + 1, eid);
|
|
if (err < 0)
|
|
goto out;
|
|
length += err;
|
|
|
|
flags = ISNS_FLAG_CLIENT | ISNS_FLAG_LAST_PDU | ISNS_FLAG_FIRST_PDU;
|
|
isns_hdr_init(hdr, ISNS_FUNC_DEV_DEREG, length, flags,
|
|
++transaction, 0);
|
|
|
|
err = write(isns_fd, buf, length + sizeof(struct isns_hdr));
|
|
if (err < 0)
|
|
log_error("%s %d: %s", __func__, __LINE__, strerror(errno));
|
|
out:
|
|
return err;
|
|
}
|
|
|
|
int isns_target_register(char *name)
|
|
{
|
|
char buf[4096];
|
|
uint16_t flags = 0, length = 0;
|
|
struct isns_hdr *hdr = (struct isns_hdr *)buf;
|
|
struct isns_tlv *tlv;
|
|
uint32_t port = htonl(server_port);
|
|
uint32_t node = htonl(ISNS_NODE_TARGET);
|
|
uint32_t type = htonl(2);
|
|
struct target *target;
|
|
int err, initial = list_length_is_one(&targets_list);
|
|
int max_buf;
|
|
|
|
if (isns_server == NULL)
|
|
return 0;
|
|
|
|
if (!isns_fd) {
|
|
err = isns_connect();
|
|
if (err < 0)
|
|
return err;
|
|
}
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
tlv = (struct isns_tlv *)hdr->pdu;
|
|
max_buf = sizeof(buf) - offsetof(struct isns_hdr, pdu);
|
|
|
|
if (strlen(isns_entity_target_name) < 1) {
|
|
target = list_entry(targets_list.q_forw, struct target, tlist);
|
|
err = isns_tlv_set(&tlv, max_buf - length, ISNS_ATTR_ISCSI_NAME,
|
|
strlen(target->name) + 1, target->name);
|
|
} else {
|
|
err = isns_tlv_set(&tlv, max_buf - length, ISNS_ATTR_ISCSI_NAME,
|
|
strlen(isns_entity_target_name) + 1, isns_entity_target_name);
|
|
}
|
|
if (err < 0)
|
|
goto out;
|
|
length += err;
|
|
|
|
err = isns_tlv_set(&tlv, max_buf - length, ISNS_ATTR_ENTITY_IDENTIFIER,
|
|
strlen(eid) + 1, eid);
|
|
if (err < 0)
|
|
goto out;
|
|
length += err;
|
|
|
|
err = isns_tlv_set(&tlv, max_buf - length, 0, 0, 0);
|
|
if (err < 0)
|
|
goto out;
|
|
length += err;
|
|
|
|
err = isns_tlv_set(&tlv, max_buf - length, ISNS_ATTR_ENTITY_IDENTIFIER,
|
|
strlen(eid) + 1, eid);
|
|
if (err < 0)
|
|
goto out;
|
|
length += err;
|
|
|
|
if (initial) {
|
|
err = isns_tlv_set(&tlv, max_buf - length, ISNS_ATTR_ENTITY_PROTOCOL,
|
|
sizeof(type), &type);
|
|
if (err < 0)
|
|
goto out;
|
|
length += err;
|
|
|
|
err = isns_tlv_set(&tlv, max_buf - length, ISNS_ATTR_PORTAL_IP_ADDRESS,
|
|
sizeof(ip), &ip);
|
|
if (err < 0)
|
|
goto out;
|
|
length += err;
|
|
|
|
err = isns_tlv_set(&tlv, max_buf - length, ISNS_ATTR_PORTAL_PORT,
|
|
sizeof(port), &port);
|
|
if (err < 0)
|
|
goto out;
|
|
length += err;
|
|
|
|
flags = ISNS_FLAG_REPLACE;
|
|
|
|
if (scn_listen_port) {
|
|
uint32_t sport = htonl(scn_listen_port);
|
|
|
|
err = isns_tlv_set(&tlv, max_buf - length, ISNS_ATTR_SCN_PORT,
|
|
sizeof(sport), &sport);
|
|
if (err < 0)
|
|
goto out;
|
|
length += err;
|
|
}
|
|
}
|
|
|
|
err = isns_tlv_set(&tlv, max_buf - length, ISNS_ATTR_ISCSI_NAME,
|
|
strlen(name) + 1, name);
|
|
if (err < 0)
|
|
goto out;
|
|
length += err;
|
|
|
|
err = isns_tlv_set(&tlv, max_buf - length, ISNS_ATTR_ISCSI_NODE_TYPE,
|
|
sizeof(node), &node);
|
|
if (err < 0)
|
|
goto out;
|
|
length += err;
|
|
|
|
flags |= ISNS_FLAG_CLIENT | ISNS_FLAG_LAST_PDU | ISNS_FLAG_FIRST_PDU;
|
|
isns_hdr_init(hdr, ISNS_FUNC_DEV_ATTR_REG, length, flags,
|
|
++transaction, 0);
|
|
|
|
err = write(isns_fd, buf, length + sizeof(struct isns_hdr));
|
|
if (err < 0)
|
|
log_error("%s %d: %s", __func__, __LINE__, strerror(errno));
|
|
|
|
if (scn_listen_port)
|
|
isns_scn_register();
|
|
|
|
isns_attr_query(name);
|
|
|
|
out:
|
|
return err;
|
|
}
|
|
|
|
static void free_all_acl(struct target *target)
|
|
{
|
|
struct isns_initiator *ini;
|
|
|
|
while (!list_empty(&target->isns_head)) {
|
|
ini = list_entry(target->isns_head.q_forw, typeof(*ini), ilist);
|
|
list_del(&ini->ilist);
|
|
free(ini);
|
|
}
|
|
}
|
|
|
|
int isns_target_deregister(char *name)
|
|
{
|
|
char buf[4096];
|
|
uint16_t flags, length = 0;
|
|
struct isns_hdr *hdr = (struct isns_hdr *)buf;
|
|
struct isns_tlv *tlv;
|
|
int err, last = list_empty(&targets_list);
|
|
struct target *target;
|
|
int max_buf;
|
|
|
|
target = target_find_by_name(name);
|
|
if (target)
|
|
free_all_acl(target);
|
|
|
|
if (isns_server == NULL)
|
|
return 0;
|
|
|
|
if (!isns_fd) {
|
|
err = isns_connect();
|
|
if (err < 0)
|
|
goto out;
|
|
}
|
|
|
|
isns_scn_deregister(name);
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
tlv = (struct isns_tlv *)hdr->pdu;
|
|
max_buf = sizeof(buf) - offsetof(struct isns_hdr, pdu);
|
|
|
|
err = isns_tlv_set(&tlv, max_buf - length, ISNS_ATTR_ISCSI_NAME,
|
|
strlen(name) + 1, name);
|
|
if (err < 0)
|
|
goto out;
|
|
length += err;
|
|
|
|
err = isns_tlv_set(&tlv, max_buf - length, 0, 0, 0);
|
|
if (err < 0)
|
|
goto out;
|
|
length += err;
|
|
|
|
if (last) {
|
|
err = isns_tlv_set(&tlv, max_buf - length, ISNS_ATTR_ENTITY_IDENTIFIER,
|
|
strlen(eid) + 1, eid);
|
|
if (err < 0)
|
|
goto out;
|
|
length += err;
|
|
} else {
|
|
err = isns_tlv_set(&tlv, max_buf - length, ISNS_ATTR_ISCSI_NAME,
|
|
strlen(name) + 1, name);
|
|
if (err < 0)
|
|
goto out;
|
|
length += err;
|
|
}
|
|
|
|
flags = ISNS_FLAG_CLIENT | ISNS_FLAG_LAST_PDU | ISNS_FLAG_FIRST_PDU;
|
|
isns_hdr_init(hdr, ISNS_FUNC_DEV_DEREG, length, flags,
|
|
++transaction, 0);
|
|
|
|
err = write(isns_fd, buf, length + sizeof(struct isns_hdr));
|
|
if (err < 0)
|
|
log_error("%s %d: %s", __func__, __LINE__, strerror(errno));
|
|
|
|
out:
|
|
return err;
|
|
}
|
|
|
|
static int recv_hdr(int fd, struct isns_io *rx, struct isns_hdr *hdr)
|
|
{
|
|
int err;
|
|
|
|
if (rx->offset < sizeof(*hdr)) {
|
|
err = read(fd, rx->buf + rx->offset,
|
|
sizeof(*hdr) - rx->offset);
|
|
if (err < 0) {
|
|
if (errno == EAGAIN || errno == EINTR)
|
|
return -EAGAIN;
|
|
log_error("header read error %d %d %s %d",
|
|
fd, err, strerror(errno), rx->offset);
|
|
return -1;
|
|
} else if (err == 0)
|
|
return -1;
|
|
|
|
log_debug(1, "header %d %d bytes!", fd, err);
|
|
rx->offset += err;
|
|
|
|
if (rx->offset < sizeof(*hdr)) {
|
|
log_debug(1, "header wait %d %d", rx->offset, err);
|
|
return -EAGAIN;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define get_hdr_param(hdr, function, length, flags, transaction, sequence) \
|
|
{ \
|
|
function = ntohs(hdr->function); \
|
|
length = ntohs(hdr->length); \
|
|
flags = ntohs(hdr->flags); \
|
|
transaction = ntohs(hdr->transaction); \
|
|
sequence = ntohs(hdr->sequence); \
|
|
}
|
|
|
|
static int recv_pdu(int fd, struct isns_io *rx, struct isns_hdr *hdr)
|
|
{
|
|
uint16_t function, length, flags, transaction, sequence;
|
|
int err;
|
|
|
|
err = recv_hdr(fd, rx, hdr);
|
|
if (err)
|
|
return err;
|
|
|
|
/* Now we got a complete header */
|
|
get_hdr_param(hdr, function, length, flags, transaction, sequence);
|
|
log_debug(1, "got a header %x %u %x %u %u", function, length, flags,
|
|
transaction, sequence);
|
|
|
|
if (length + sizeof(*hdr) > BUFSIZE) {
|
|
log_error("ToDo: we cannot handle this yet %u!", length);
|
|
return -1;
|
|
}
|
|
|
|
if (rx->offset < length + sizeof(*hdr)) {
|
|
err = read(fd, rx->buf + rx->offset,
|
|
length + sizeof(*hdr) - rx->offset);
|
|
if (err < 0) {
|
|
if (errno == EAGAIN || errno == EINTR)
|
|
return -EAGAIN;
|
|
log_error("pdu read error %d %d %s %d",
|
|
fd, err, strerror(errno), rx->offset);
|
|
return -1;
|
|
} else if (err == 0)
|
|
return -1;
|
|
|
|
log_debug(1, "pdu %u %u", fd, err);
|
|
rx->offset += err;
|
|
|
|
if (rx->offset < length + sizeof(*hdr)) {
|
|
log_error("pdu wait %d %d", rx->offset, err);
|
|
return -EAGAIN;
|
|
}
|
|
}
|
|
|
|
/* Now we got everything. */
|
|
rx->offset = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define print_unknown_pdu(hdr) \
|
|
{ \
|
|
uint16_t function, length, flags, transaction, sequence; \
|
|
get_hdr_param(hdr, function, length, flags, transaction, \
|
|
sequence) \
|
|
log_error("%s %d: unknown function %x %u %x %u %u", \
|
|
__func__, __LINE__, \
|
|
function, length, flags, transaction, sequence); \
|
|
}
|
|
|
|
static char *print_scn_pdu(struct isns_hdr *hdr)
|
|
{
|
|
struct isns_tlv *tlv = (struct isns_tlv *)hdr->pdu;
|
|
uint16_t function __attribute__((unused));
|
|
uint16_t length;
|
|
uint16_t flags __attribute__((unused));
|
|
uint16_t transaction __attribute__((unused));
|
|
uint16_t sequence __attribute__((unused));
|
|
char *name = NULL;
|
|
|
|
get_hdr_param(hdr, function, length, flags, transaction, sequence);
|
|
|
|
while (length) {
|
|
uint32_t vlen = ntohl(tlv->length);
|
|
|
|
if (vlen + sizeof(*tlv) > length)
|
|
vlen = length - sizeof(*tlv);
|
|
|
|
if (vlen < 4)
|
|
goto next;
|
|
|
|
switch (ntohl(tlv->tag)) {
|
|
case ISNS_ATTR_ISCSI_NAME:
|
|
((char *)tlv->value)[vlen-1] = '\0';
|
|
log_debug(3, "scn name: %u, %s", vlen, (char *)tlv->value);
|
|
if (!name)
|
|
name = (char *)tlv->value;
|
|
break;
|
|
case ISNS_ATTR_TIMESTAMP:
|
|
if (vlen < 8)
|
|
goto next;
|
|
/* log_debug(3, "%u : %u : %" PRIx64, ntohl(tlv->tag), vlen, */
|
|
/* *((uint64_t *)tlv->value)); */
|
|
break;
|
|
case ISNS_ATTR_ISCSI_SCN_BITMAP:
|
|
log_debug(3, "scn bitmap : %x", *((uint32_t *)tlv->value));
|
|
break;
|
|
}
|
|
|
|
next:
|
|
length -= (sizeof(*tlv) + vlen);
|
|
tlv = (struct isns_tlv *)((char *)tlv->value + vlen);
|
|
}
|
|
|
|
return name;
|
|
}
|
|
|
|
static void qry_rsp_handle(struct isns_hdr *hdr)
|
|
{
|
|
struct isns_tlv *tlv;
|
|
uint16_t function __attribute__((unused));
|
|
uint16_t flags __attribute__((unused));
|
|
uint16_t sequence __attribute__((unused));
|
|
uint16_t length, transaction;
|
|
uint32_t status = (uint32_t) (*hdr->pdu);
|
|
struct isns_qry_mgmt *mgmt, *n;
|
|
struct target *target;
|
|
struct isns_initiator *ini;
|
|
char *name = NULL;
|
|
|
|
get_hdr_param(hdr, function, length, flags, transaction, sequence);
|
|
|
|
list_for_each_entry_safe(mgmt, n, &qry_list, qlist) {
|
|
if (mgmt->transaction == transaction) {
|
|
list_del(&mgmt->qlist);
|
|
goto found;
|
|
}
|
|
}
|
|
|
|
log_error("%s %d: transaction not found %u",
|
|
__func__, __LINE__, transaction);
|
|
|
|
return;
|
|
|
|
found:
|
|
if (status) {
|
|
log_error("%s %d: error response %u",
|
|
__func__, __LINE__, status);
|
|
|
|
goto free_qry_mgmt;
|
|
}
|
|
|
|
if (!strlen(mgmt->name)) {
|
|
log_debug(1, "%s %d: skip %u",
|
|
__func__, __LINE__, transaction);
|
|
goto free_qry_mgmt;
|
|
}
|
|
|
|
target = target_find_by_name(mgmt->name);
|
|
if (!target) {
|
|
log_error("%s %d: invalid tid %s",
|
|
__func__, __LINE__, mgmt->name);
|
|
goto free_qry_mgmt;
|
|
}
|
|
|
|
free_all_acl(target);
|
|
|
|
/* skip status */
|
|
if (length < 4)
|
|
goto free_qry_mgmt;
|
|
tlv = (struct isns_tlv *)((char *)hdr->pdu + 4);
|
|
length -= 4;
|
|
|
|
while (length) {
|
|
uint32_t vlen = ntohl(tlv->length);
|
|
|
|
if (vlen + sizeof(*tlv) > length)
|
|
vlen = length - sizeof(*tlv);
|
|
|
|
if (vlen < 4)
|
|
goto next;
|
|
|
|
switch (ntohl(tlv->tag)) {
|
|
case ISNS_ATTR_ISCSI_NAME:
|
|
((char *)tlv->value)[vlen-1] = '\0';
|
|
name = (char *)tlv->value;
|
|
break;
|
|
case ISNS_ATTR_ISCSI_NODE_TYPE:
|
|
if (ntohl(*(tlv->value)) == ISNS_NODE_INITIATOR && name) {
|
|
log_debug(3, "%s %d: %s", __func__, __LINE__,
|
|
(char *)name);
|
|
ini = malloc(sizeof(*ini));
|
|
if (!ini)
|
|
goto free_qry_mgmt;
|
|
snprintf(ini->name, sizeof(ini->name), "%s",
|
|
name);
|
|
list_add_tail(&ini->ilist, &target->isns_head);
|
|
} else
|
|
name = NULL;
|
|
break;
|
|
default:
|
|
name = NULL;
|
|
break;
|
|
}
|
|
|
|
next:
|
|
length -= (sizeof(*tlv) + vlen);
|
|
tlv = (struct isns_tlv *)((char *)tlv->value + vlen);
|
|
}
|
|
|
|
free_qry_mgmt:
|
|
free(mgmt);
|
|
}
|
|
|
|
int isns_handle(int is_timeout)
|
|
{
|
|
int err;
|
|
struct isns_io *rx = &isns_rx;
|
|
struct isns_hdr *hdr = (struct isns_hdr *)rx->buf;
|
|
uint16_t function;
|
|
uint16_t length __attribute__((unused));
|
|
uint16_t flags __attribute__((unused));
|
|
uint16_t transaction __attribute__((unused));
|
|
uint16_t sequence __attribute__((unused));
|
|
char *name = NULL;
|
|
|
|
if (isns_server == NULL)
|
|
return 0;
|
|
|
|
if (is_timeout)
|
|
return isns_attr_query(NULL);
|
|
|
|
err = recv_pdu(isns_fd, rx, hdr);
|
|
if (err) {
|
|
if (err == -EAGAIN)
|
|
return err;
|
|
log_debug(1, "%s %d: close connection %d", __func__, __LINE__,
|
|
isns_fd);
|
|
close(isns_fd);
|
|
isns_fd = 0;
|
|
isns_set_fd(0, scn_listen_fd, scn_fd);
|
|
return err;
|
|
}
|
|
|
|
get_hdr_param(hdr, function, length, flags, transaction, sequence);
|
|
|
|
switch (function) {
|
|
case ISNS_FUNC_DEV_ATTR_REG_RSP:
|
|
break;
|
|
case ISNS_FUNC_DEV_ATTR_QRY_RSP:
|
|
qry_rsp_handle(hdr);
|
|
break;
|
|
case ISNS_FUNC_DEV_DEREG_RSP:
|
|
case ISNS_FUNC_SCN_REG_RSP:
|
|
case ISNS_FUNC_SCN_DEREG_RSP:
|
|
break;
|
|
case ISNS_FUNC_SCN:
|
|
name = print_scn_pdu(hdr);
|
|
if (name) {
|
|
log_debug(3, "%s %d: %s", __func__, __LINE__, name);
|
|
isns_attr_query(name);
|
|
}
|
|
break;
|
|
default:
|
|
print_unknown_pdu(hdr);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int scn_accept_connection(void)
|
|
{
|
|
union {
|
|
struct sockaddr sa;
|
|
struct sockaddr_in sin;
|
|
struct sockaddr_in6 sin6;
|
|
} from;
|
|
socklen_t slen;
|
|
int fd, err, opt = 1;
|
|
|
|
if (isns_server == NULL) {
|
|
/*
|
|
* Sometimes we have (leftover?) events after disable iSNS
|
|
* server, so ignore them
|
|
*/
|
|
goto out;
|
|
}
|
|
|
|
slen = sizeof(from);
|
|
fd = accept(scn_listen_fd, &from.sa, &slen);
|
|
if (fd < 0) {
|
|
log_error("%s %d: accept error: %s", __func__, __LINE__,
|
|
strerror(errno));
|
|
return -errno;
|
|
}
|
|
log_info("Accept scn connection %d", fd);
|
|
|
|
err = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt));
|
|
if (err)
|
|
log_error("%s %d: %s\n", __func__, __LINE__,
|
|
strerror(errno));
|
|
/* not critical, so ignore. */
|
|
|
|
scn_fd = fd;
|
|
isns_set_fd(isns_fd, scn_listen_fd, scn_fd);
|
|
|
|
out:
|
|
return 0;
|
|
}
|
|
|
|
static int send_scn_rsp(char *name, uint16_t transaction)
|
|
{
|
|
char buf[1024];
|
|
struct isns_hdr *hdr = (struct isns_hdr *)buf;
|
|
struct isns_tlv *tlv;
|
|
uint16_t flags, length = 0;
|
|
int err, max_buf;
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
*((uint32_t *)hdr->pdu) = 0;
|
|
max_buf = sizeof(buf) - offsetof(struct isns_hdr, pdu);
|
|
tlv = (struct isns_tlv *)((char *)hdr->pdu + 4);
|
|
length += 4;
|
|
|
|
err = isns_tlv_set(&tlv, max_buf - length, ISNS_ATTR_ISCSI_NAME,
|
|
strlen(name) + 1, name);
|
|
if (err < 0)
|
|
goto out;
|
|
length += err;
|
|
|
|
flags = ISNS_FLAG_CLIENT | ISNS_FLAG_LAST_PDU | ISNS_FLAG_FIRST_PDU;
|
|
isns_hdr_init(hdr, ISNS_FUNC_SCN_RSP, length, flags, transaction, 0);
|
|
|
|
err = write(scn_fd, buf, length + sizeof(struct isns_hdr));
|
|
if (err < 0)
|
|
log_error("%s %d: %s", __func__, __LINE__, strerror(errno));
|
|
|
|
out:
|
|
return err;
|
|
}
|
|
|
|
int isns_scn_handle(int is_accept)
|
|
{
|
|
int err;
|
|
struct isns_io *rx = &scn_rx;
|
|
struct isns_hdr *hdr = (struct isns_hdr *)rx->buf;
|
|
uint16_t function, transaction;
|
|
uint16_t length __attribute__((unused));
|
|
uint16_t flags __attribute__((unused));
|
|
uint16_t sequence __attribute__((unused));
|
|
char *name = NULL;
|
|
|
|
log_debug(3, "%s %d: %d", __func__, __LINE__, is_accept);
|
|
|
|
if (is_accept)
|
|
return scn_accept_connection();
|
|
|
|
err = recv_pdu(scn_fd, rx, hdr);
|
|
if (err) {
|
|
if (err == -EAGAIN)
|
|
return err;
|
|
log_debug(1, "%s %d: close connection %d", __func__, __LINE__,
|
|
scn_fd);
|
|
close(scn_fd);
|
|
scn_fd = 0;
|
|
isns_set_fd(isns_fd, scn_listen_fd, 0);
|
|
return err;
|
|
}
|
|
|
|
get_hdr_param(hdr, function, length, flags, transaction, sequence);
|
|
|
|
switch (function) {
|
|
case ISNS_FUNC_SCN:
|
|
name = print_scn_pdu(hdr);
|
|
break;
|
|
default:
|
|
print_unknown_pdu(hdr);
|
|
}
|
|
|
|
if (name) {
|
|
send_scn_rsp(name, transaction);
|
|
isns_attr_query(name);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int scn_init(void)
|
|
{
|
|
int fd, opt, err;
|
|
union {
|
|
struct sockaddr sa;
|
|
struct sockaddr_in sin;
|
|
struct sockaddr_in6 sin6;
|
|
} lss;
|
|
socklen_t slen;
|
|
|
|
fd = socket(ss.ss_family, SOCK_STREAM, IPPROTO_TCP);
|
|
if (fd < 0) {
|
|
log_error("%s %d: %s\n", __func__, __LINE__, strerror(errno));
|
|
err = -errno;
|
|
goto out;
|
|
}
|
|
|
|
opt = 1;
|
|
if (ss.ss_family == AF_INET6) {
|
|
err = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt));
|
|
if (err) {
|
|
log_error("%s %d: %s\n", __func__, __LINE__,
|
|
strerror(errno));
|
|
goto out_close;
|
|
}
|
|
}
|
|
|
|
err = listen(fd, 5);
|
|
if (err) {
|
|
log_error("%s %d: %s\n", __func__, __LINE__, strerror(errno));
|
|
goto out_close;
|
|
}
|
|
|
|
slen = sizeof(lss);
|
|
err = getsockname(fd, (struct sockaddr *)&lss, &slen);
|
|
if (err) {
|
|
log_error("%s %d: %s\n", __func__, __LINE__, strerror(errno));
|
|
goto out_close;
|
|
}
|
|
|
|
/* protocol independent way ? */
|
|
if (lss.sa.sa_family == AF_INET6)
|
|
scn_listen_port = ntohs(lss.sin6.sin6_port);
|
|
else
|
|
scn_listen_port = ntohs(lss.sin.sin_port);
|
|
|
|
log_info("scn listen port %u %d %d\n", scn_listen_port, fd, err);
|
|
|
|
out_close:
|
|
if (err)
|
|
close(fd);
|
|
else {
|
|
scn_listen_fd = fd;
|
|
isns_set_fd(isns_fd, scn_listen_fd, scn_fd);
|
|
}
|
|
|
|
out:
|
|
return err;
|
|
}
|
|
|
|
int isns_init(void)
|
|
{
|
|
int err;
|
|
char port[8];
|
|
struct addrinfo hints, *res;
|
|
|
|
snprintf(port, sizeof(port), "%d", ISNS_PORT);
|
|
memset(&hints, 0, sizeof(hints));
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
err = getaddrinfo(isns_server, (char *)&port, &hints, &res);
|
|
if (err) {
|
|
log_error("getaddrinfo error: %s, %s", get_error_str(err),
|
|
isns_server);
|
|
goto out;
|
|
}
|
|
memcpy(&ss, res->ai_addr, sizeof(*res->ai_addr));
|
|
freeaddrinfo(res);
|
|
|
|
rxbuf = calloc(2, BUFSIZE);
|
|
if (!rxbuf) {
|
|
log_error("oom!");
|
|
err = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
err = scn_init();
|
|
if (err != 0)
|
|
goto out_free;
|
|
|
|
isns_rx.buf = rxbuf;
|
|
isns_rx.offset = 0;
|
|
scn_rx.buf = rxbuf + BUFSIZE;
|
|
scn_rx.offset = 0;
|
|
|
|
isns_timeout = current_timeout * 1000;
|
|
|
|
err = isns_connect();
|
|
if (err > 0)
|
|
err = 0;
|
|
|
|
out:
|
|
return err;
|
|
|
|
out_free:
|
|
free(rxbuf);
|
|
goto out;
|
|
}
|
|
|
|
void isns_exit(void)
|
|
{
|
|
struct target *target;
|
|
|
|
if (isns_server == NULL)
|
|
goto out;
|
|
|
|
if (!isns_fd)
|
|
goto close;
|
|
|
|
list_for_each_entry(target, &targets_list, tlist)
|
|
isns_scn_deregister(target->name);
|
|
|
|
isns_deregister();
|
|
/* we can't receive events any more. */
|
|
isns_set_fd(0, 0, 0);
|
|
|
|
close:
|
|
if (isns_fd) {
|
|
close(isns_fd);
|
|
isns_fd = 0;
|
|
}
|
|
if (scn_listen_fd) {
|
|
close(scn_listen_fd);
|
|
scn_listen_fd = 0;
|
|
}
|
|
if (scn_fd) {
|
|
close(scn_fd);
|
|
scn_fd = 0;
|
|
}
|
|
|
|
free(rxbuf);
|
|
rxbuf = NULL;
|
|
|
|
free(isns_server);
|
|
isns_server = NULL;
|
|
|
|
isns_timeout = -1;
|
|
|
|
out:
|
|
return;
|
|
}
|