freebsd-dev/usr.sbin/ctld/isns.c
Alexander Motin 829603e21f Add basic iSNS client to the iSCSI target.
This makes ctld(8) register its iSCSI targets and portals on configured
iSNS servers to allow initiators find them without active discovery.

Fetching of allowed initiators from iSNS is not implemented now, so target
ACLs still should be configured manually.

Reviewed by:	trasz@
MFC after:	1 month
Sponsored by:	iXsystems, Inc.
2014-10-25 12:50:26 +00:00

259 lines
6.3 KiB
C

/*-
* Copyright (c) 2014 Alexander Motin <mav@FreeBSD.org>
* All rights reserved.
*
* 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/time.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/endian.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <netdb.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "ctld.h"
#include "isns.h"
struct isns_req *
isns_req_alloc(void)
{
struct isns_req *req;
req = calloc(sizeof(struct isns_req), 1);
if (req == NULL) {
log_err(1, "calloc");
return (NULL);
}
req->ir_buflen = sizeof(struct isns_hdr);
req->ir_usedlen = 0;
req->ir_buf = calloc(req->ir_buflen, 1);
if (req == NULL) {
free(req);
log_err(1, "calloc");
return (NULL);
}
return (req);
}
struct isns_req *
isns_req_create(uint16_t func, uint16_t flags)
{
struct isns_req *req;
struct isns_hdr *hdr;
req = isns_req_alloc();
req->ir_usedlen = sizeof(struct isns_hdr);
hdr = (struct isns_hdr *)req->ir_buf;
be16enc(hdr->ih_version, ISNS_VERSION);
be16enc(hdr->ih_function, func);
be16enc(hdr->ih_flags, flags);
return (req);
}
void
isns_req_free(struct isns_req *req)
{
free(req->ir_buf);
free(req);
}
static int
isns_req_getspace(struct isns_req *req, uint32_t len)
{
void *newbuf;
int newlen;
if (req->ir_usedlen + len <= req->ir_buflen)
return (0);
newlen = 1 << flsl(req->ir_usedlen + len);
newbuf = realloc(req->ir_buf, newlen);
if (newbuf == NULL) {
log_err(1, "realloc");
return (1);
}
req->ir_buf = newbuf;
req->ir_buflen = newlen;
return (0);
}
void
isns_req_add(struct isns_req *req, uint32_t tag, uint32_t len,
const void *value)
{
struct isns_tlv *tlv;
uint32_t vlen;
vlen = len + ((len & 3) ? (4 - (len & 3)) : 0);
isns_req_getspace(req, sizeof(*tlv) + vlen);
tlv = (struct isns_tlv *)&req->ir_buf[req->ir_usedlen];
be32enc(tlv->it_tag, tag);
be32enc(tlv->it_length, vlen);
memcpy(tlv->it_value, value, len);
if (vlen != len)
memset(&tlv->it_value[len], 0, vlen - len);
req->ir_usedlen += sizeof(*tlv) + vlen;
}
void
isns_req_add_delim(struct isns_req *req)
{
isns_req_add(req, 0, 0, NULL);
}
void
isns_req_add_str(struct isns_req *req, uint32_t tag, const char *value)
{
isns_req_add(req, tag, strlen(value) + 1, value);
}
void
isns_req_add_32(struct isns_req *req, uint32_t tag, uint32_t value)
{
uint32_t beval;
be32enc(&beval, value);
isns_req_add(req, tag, sizeof(value), &beval);
}
void
isns_req_add_addr(struct isns_req *req, uint32_t tag, struct addrinfo *ai)
{
struct sockaddr_in *in4;
struct sockaddr_in6 *in6;
uint8_t buf[16];
switch (ai->ai_addr->sa_family) {
case AF_INET:
in4 = (struct sockaddr_in *)(void *)ai->ai_addr;
memset(buf, 0, 10);
buf[10] = 0xff;
buf[11] = 0xff;
memcpy(&buf[12], &in4->sin_addr, sizeof(in4->sin_addr));
isns_req_add(req, tag, sizeof(buf), buf);
break;
case AF_INET6:
in6 = (struct sockaddr_in6 *)(void *)ai->ai_addr;
isns_req_add(req, tag, sizeof(in6->sin6_addr), &in6->sin6_addr);
break;
default:
log_errx(1, "Unsupported address family %d",
ai->ai_addr->sa_family);
}
}
void
isns_req_add_port(struct isns_req *req, uint32_t tag, struct addrinfo *ai)
{
struct sockaddr_in *in4;
struct sockaddr_in6 *in6;
uint32_t buf;
switch (ai->ai_addr->sa_family) {
case AF_INET:
in4 = (struct sockaddr_in *)(void *)ai->ai_addr;
be32enc(&buf, ntohs(in4->sin_port));
isns_req_add(req, tag, sizeof(buf), &buf);
break;
case AF_INET6:
in6 = (struct sockaddr_in6 *)(void *)ai->ai_addr;
be32enc(&buf, ntohs(in6->sin6_port));
isns_req_add(req, tag, sizeof(buf), &buf);
break;
default:
log_errx(1, "Unsupported address family %d",
ai->ai_addr->sa_family);
}
}
int
isns_req_send(int s, struct isns_req *req)
{
struct isns_hdr *hdr;
int res;
hdr = (struct isns_hdr *)req->ir_buf;
be16enc(hdr->ih_length, req->ir_usedlen - sizeof(*hdr));
be16enc(hdr->ih_flags, be16dec(hdr->ih_flags) |
ISNS_FLAG_LAST | ISNS_FLAG_FIRST);
be16enc(hdr->ih_transaction, 0);
be16enc(hdr->ih_sequence, 0);
res = write(s, req->ir_buf, req->ir_usedlen);
return ((res < 0) ? -1 : 0);
}
int
isns_req_receive(int s, struct isns_req *req)
{
struct isns_hdr *hdr;
ssize_t res, len;
req->ir_usedlen = 0;
isns_req_getspace(req, sizeof(*hdr));
res = read(s, req->ir_buf, sizeof(*hdr));
if (res < (ssize_t)sizeof(*hdr))
return (-1);
req->ir_usedlen = sizeof(*hdr);
hdr = (struct isns_hdr *)req->ir_buf;
if (be16dec(hdr->ih_version) != ISNS_VERSION)
return (-1);
if ((be16dec(hdr->ih_flags) & (ISNS_FLAG_LAST | ISNS_FLAG_FIRST)) !=
(ISNS_FLAG_LAST | ISNS_FLAG_FIRST))
return (-1);
len = be16dec(hdr->ih_length);
isns_req_getspace(req, len);
res = read(s, &req->ir_buf[req->ir_usedlen], len);
if (res < len)
return (-1);
req->ir_usedlen += len;
return (0);
}
uint32_t
isns_req_get_status(struct isns_req *req)
{
if (req->ir_usedlen < sizeof(struct isns_hdr) + 4)
return (-1);
return (be32dec(&req->ir_buf[sizeof(struct isns_hdr)]));
}