summaryrefslogtreecommitdiff
path: root/utils/open-isns/message.c
diff options
context:
space:
mode:
Diffstat (limited to 'utils/open-isns/message.c')
-rw-r--r--utils/open-isns/message.c681
1 files changed, 681 insertions, 0 deletions
diff --git a/utils/open-isns/message.c b/utils/open-isns/message.c
new file mode 100644
index 0000000..4cd40c3
--- /dev/null
+++ b/utils/open-isns/message.c
@@ -0,0 +1,681 @@
+/*
+ * iSNS message handling functions
+ *
+ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
+ *
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h> /* for timercmp */
+#include <unistd.h> /* gethostname */
+#include <ctype.h>
+#include "isns.h"
+#include "attrs.h"
+#include "message.h"
+#include "socket.h"
+#include "util.h"
+
+/* iSCSI qualified names include the year and
+ * month in which the domain was assigned.
+ * See RFC 3720, section 3.2.6.3.1.
+ * That's one of these wonderful committee
+ * type of ideas that makes it hard for everyone,
+ * from coder to sysadmin.
+ * Since we have no way of finding out here,
+ * we fake it by assigning a date before the
+ * dawn of time.
+ */
+#define DUMMY_IQN_PREFIX "iqn.1967-12."
+
+static uint32_t isns_xid = 1;
+
+/*
+ * Initialize a message object
+ */
+isns_message_t *
+__isns_alloc_message(uint32_t xid, size_t size, void (*destroy)(isns_message_t *))
+{
+ isns_message_t *msg;
+
+ isns_assert(size >= sizeof(*msg));
+ msg = isns_calloc(1, size);
+
+ isns_list_init(&msg->im_list);
+ msg->im_users = 1;
+ msg->im_xid = xid;
+ msg->im_destroy = destroy;
+
+ return msg;
+}
+
+static int
+__isns_message_init(isns_message_t *msg,
+ uint16_t function, uint16_t flags,
+ size_t payload_len)
+{
+ struct isns_hdr *hdr = &msg->im_header;
+
+ /* Pad to multiple of 4 octets */
+ payload_len = (payload_len + 3) & ~3UL;
+
+ /* For now, we don't do segmentation */
+ if (payload_len > ISNS_MAX_PDU_SIZE)
+ return 0;
+
+ /* msg->im_header is in host byte order */
+ hdr->i_version = ISNS_VERSION;
+ hdr->i_function = function;
+ hdr->i_flags = flags;
+ hdr->i_length = payload_len;
+ hdr->i_xid = msg->im_xid;
+ hdr->i_seq = 0;
+
+ /* Allocate buffer and reserve room for header */
+ msg->im_payload = buf_alloc(sizeof(*hdr) + payload_len);
+ buf_push(msg->im_payload, sizeof(*hdr));
+
+ return 1;
+}
+
+/*
+ * Allocate a message object.
+ */
+static isns_message_t *
+__isns_create_message(uint32_t xid, uint16_t function, uint16_t flags)
+{
+ isns_message_t *msg;
+
+ msg = __isns_alloc_message(xid, sizeof(*msg), NULL);
+ __isns_message_init(msg, function, flags, ISNS_MAX_MESSAGE);
+
+ return msg;
+}
+
+/*
+ * Allocate a request message
+ */
+isns_message_t *
+isns_create_message(uint16_t function, uint16_t flags)
+{
+ return __isns_create_message(isns_xid++, function, flags);
+}
+
+/*
+ * Allocate a response message
+ */
+isns_message_t *
+isns_create_reply(const isns_message_t *msg)
+{
+ uint16_t function = msg->im_header.i_function;;
+ isns_message_t *resp;
+
+ resp = __isns_create_message(msg->im_xid, function | 0x8000, ISNS_F_SERVER);
+ resp->im_addr = msg->im_addr;
+ resp->im_addrlen = msg->im_addrlen;
+
+ /* Default to ISNS_SUCCESS */
+ buf_put32(resp->im_payload, ISNS_SUCCESS);
+
+ return resp;
+}
+
+/*
+ * Delete a message
+ */
+void
+isns_message_release(isns_message_t *msg)
+{
+ if (msg == NULL)
+ return;
+
+ isns_assert(msg->im_users);
+ if (--(msg->im_users))
+ return;
+
+ if (msg->im_destroy)
+ msg->im_destroy(msg);
+ if (msg->im_payload)
+ buf_free(msg->im_payload);
+ isns_principal_free(msg->im_security);
+
+ isns_list_del(&msg->im_list);
+ isns_free(msg);
+}
+
+/*
+ * Extract the status from a reply message
+ */
+int
+isns_message_status(isns_message_t *msg)
+{
+ uint32_t status;
+
+ if (!(msg->im_header.i_function & 0x8000)
+ || !buf_get32(msg->im_payload, &status))
+ return ISNS_MESSAGE_FORMAT_ERROR;
+ return status;
+}
+
+/*
+ * Obtain the socket on which the message was received.
+ */
+isns_socket_t *
+isns_message_socket(const isns_message_t *msg)
+{
+ return msg->im_socket;
+}
+
+/*
+ * Obtain the message's security context
+ */
+isns_security_t *
+isns_message_security(const isns_message_t *msg)
+{
+ if (!msg->im_socket)
+ return NULL;
+ return msg->im_socket->is_security;
+}
+
+unsigned int
+isns_message_function(const isns_message_t *msg)
+{
+ return msg->im_header.i_function;
+}
+
+/*
+ * Reset the response message, and encode isns_error
+ * status
+ */
+void
+isns_message_set_error(isns_message_t *msg, uint32_t status)
+{
+ /* Clear the buffer. This just resets head + tail */
+ buf_clear(msg->im_payload);
+
+ /* Now move past the header, and overwrite the
+ * status word. */
+ buf_push(msg->im_payload, sizeof(struct isns_hdr));
+ buf_put32(msg->im_payload, status);
+}
+
+/*
+ * Message queue handling. Most related functions are
+ * in message.h
+ */
+void
+isns_message_queue_move(isns_message_queue_t *dstq,
+ isns_message_t *msg)
+{
+ unsigned int src_ref = 0;
+
+ /* If the message was on a different queue,
+ * the source queue will hold a reference
+ * to it. Account for that and fix up the
+ * refcount after we've appended it to the
+ * destination queue. */
+ if (isns_message_unlink(msg))
+ src_ref = 1;
+
+ isns_message_queue_append(dstq, msg);
+ msg->im_users -= src_ref;
+}
+
+/*
+ * Insert a messsage into a queue sorted by resend timeout
+ */
+void
+isns_message_queue_insert_sorted(isns_message_queue_t *q,
+ int sort, isns_message_t *msg)
+{
+ isns_list_t *pos;
+ isns_message_t *__m;
+
+ isns_assert(msg->im_queue == NULL);
+ if (sort == ISNS_MQ_SORT_RESEND_TIMEOUT) {
+ isns_message_queue_foreach(q, pos, __m) {
+ if (timercmp(&msg->im_resend_timeout,
+ &__m->im_resend_timeout, <))
+ break;
+ }
+ } else {
+ isns_message_queue_append(q, msg);
+ return;
+ }
+
+ /* Insert before pos */
+ __isns_list_insert(pos->prev, &msg->im_list, pos);
+ q->imq_count++;
+
+ msg->im_queue = q;
+ msg->im_users++;
+}
+
+/*
+ * Message queue handling
+ */
+void
+isns_message_queue_destroy(isns_message_queue_t *q)
+{
+ isns_message_t *msg;
+
+ while ((msg = isns_message_dequeue(q)) != NULL)
+ isns_message_release(msg);
+}
+
+/*
+ * Find a message with matching xid and address.
+ * (address, alen) may be NULL.
+ */
+isns_message_t *
+isns_message_queue_find(isns_message_queue_t *q, uint32_t xid,
+ const struct sockaddr_storage *addr, socklen_t alen)
+{
+ isns_message_t *msg;
+ isns_list_t *pos;
+
+ isns_message_queue_foreach(q, pos, msg) {
+ if (msg->im_xid != xid)
+ continue;
+ if (alen == 0)
+ return msg;
+
+ if (msg->im_addrlen == alen
+ && !memcmp(&msg->im_addr, addr, alen))
+ return msg;
+ }
+
+ return NULL;
+}
+
+/*
+ * Convert a hostname into an iSCSI qualified name
+ * We omit the dismbiguating YYYY-MM infix because
+ * we have no way of finding out, short of bothering
+ * whois.
+ */
+static char *
+__revert_fqdn(const char *prefix, const char *__fqdn, const char *suffix)
+{
+ static char namebuf[1024] = { '\0' };
+ char *fqdn, *result = NULL;
+ int pos, count = 0;
+
+ if (prefix)
+ strcpy(namebuf, prefix);
+ pos = strlen(namebuf);
+
+ fqdn = isns_strdup(__fqdn);
+ while (1) {
+ char *dot, *comp;
+ int comp_len;
+
+ if ((dot = strrchr(fqdn, '.')) != NULL) {
+ *dot++ = '\0';
+ comp = dot;
+ } else {
+ comp = fqdn;
+ }
+
+ if (*comp == '\0')
+ continue;
+ comp_len = strlen(comp);
+ if (pos + comp_len + 2 > sizeof(namebuf)) {
+ isns_error("%s: FQDN too long\n", __FUNCTION__);
+ goto out;
+ }
+ if (count++)
+ namebuf[pos++] = '.';
+ strcpy(namebuf + pos, comp);
+ pos += comp_len;
+
+ if (dot == NULL)
+ break;
+ }
+
+ if (suffix) {
+ int sfx_len = strlen(suffix);
+
+ if (pos + sfx_len + 2 > sizeof(namebuf)) {
+ isns_error("%s: name too long\n", __FUNCTION__);
+ goto out;
+ }
+ namebuf[pos++] = ':';
+ strcpy(namebuf + pos, suffix);
+ pos += sfx_len;
+ }
+
+ result = isns_strdup(namebuf);
+
+out: isns_free(fqdn);
+ return result;
+}
+
+/*
+ * Initialize all names
+ */
+int
+isns_init_names(void)
+{
+ if (isns_config.ic_host_name == NULL) {
+ char namebuf[1024], *fqdn;
+
+ if (gethostname(namebuf, sizeof(namebuf)) < 0) {
+ isns_error("gehostname: %m\n");
+ return 0;
+ }
+ fqdn = isns_get_canon_name(namebuf);
+ if (fqdn == NULL) {
+ /* FIXME: we could get some unique value here
+ * such as the IP address, and concat that
+ * with iqn.2005-01.org.open-iscsi.ip for the
+ * source name.
+ */
+ isns_error("Unable to get fully qualified hostname\n");
+ return 0;
+ }
+ isns_config.ic_host_name = fqdn;
+ }
+
+ if (isns_config.ic_auth_name == NULL) {
+ isns_config.ic_auth_name = isns_config.ic_host_name;
+ }
+
+ if (isns_config.ic_entity_name == NULL) {
+ isns_config.ic_entity_name = isns_config.ic_auth_name;
+ }
+
+ if (isns_config.ic_source_name == NULL) {
+ isns_config.ic_source_name = __revert_fqdn(DUMMY_IQN_PREFIX,
+ isns_config.ic_host_name,
+ isns_config.ic_source_suffix);
+ if (isns_config.ic_source_name == NULL) {
+ isns_error("Unable to build source name\n");
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/*
+ * Match a source name to a pattern (which is really just
+ * the entity identifier, usually).
+ *
+ * If the pattern is of the form "match:rev-fqdn", the
+ * source name must match
+ * iqn.[YYYY-MM.]<rev-fqdn>
+ * optionally followed by dot, colon or hyphen and arbitrary
+ * text.
+ *
+ * If the pattern does not start with "match:", the source name
+ * must match the pattern literally (case insensitively).
+ */
+int
+isns_source_pattern_match(const char *pattern, const char *source)
+{
+ unsigned int rev_len;
+
+ isns_debug_message("%s(%s, %s)\n",
+ __FUNCTION__, pattern, source);
+
+ if (!strcmp(pattern, "*"))
+ return 1;
+
+ if (strncmp(pattern, "match:", 6))
+ return !strcasecmp(pattern, source);
+ pattern += 6;
+
+ if (strncasecmp(source, "iqn.", 4))
+ return 0;
+ source += 4;
+
+ rev_len = strlen(pattern);
+ if (strncasecmp(source, pattern, rev_len)) {
+ /* See if the next component is YYYY-MM */
+ if (!(isdigit(source[0])
+ && isdigit(source[1])
+ && isdigit(source[2])
+ && isdigit(source[3])
+ && source[4] == '-'
+ && isdigit(source[5])
+ && isdigit(source[6])
+ && source[7] == '.'))
+ return 0;
+ source += 8;
+
+ if (strncasecmp(source, pattern, rev_len))
+ return 0;
+ }
+
+ source += rev_len;
+ if (source[0] != '.'
+ && source[0] != ':'
+ && source[0] != '-'
+ && source[0] != '\0')
+ return 0;
+
+ return 1;
+}
+
+/*
+ * This really just reverts the FQDN so it can
+ * be used in isns_source_entity_match
+ */
+char *
+isns_build_source_pattern(const char *fqdn)
+{
+ return __revert_fqdn("match:", fqdn, NULL);
+}
+
+/*
+ * Manage source objects
+ */
+static isns_source_t *
+__isns_source_create(isns_attr_t *name_attr)
+{
+ isns_source_t *source = isns_calloc(1, sizeof(*source));
+
+ source->is_users = 1;
+ source->is_attr = name_attr;
+ return source;
+}
+
+isns_source_t *
+isns_source_create(isns_attr_t *name_attr)
+{
+ if (name_attr->ia_tag_id != ISNS_TAG_ISCSI_NAME
+ && name_attr->ia_tag_id != ISNS_TAG_FC_PORT_NAME_WWPN)
+ return NULL;
+
+ name_attr->ia_users++;
+ return __isns_source_create(name_attr);
+}
+
+isns_source_t *
+isns_source_from_object(const isns_object_t *node)
+{
+ isns_attr_t *attr;
+
+ if (!(attr = isns_storage_node_key_attr(node)))
+ return NULL;
+ return isns_source_create(attr);
+}
+
+isns_source_t *
+isns_source_create_iscsi(const char *name)
+{
+ isns_value_t var = ISNS_VALUE_INIT(string, (char *) name);
+ isns_attr_t *attr;
+
+ attr = isns_attr_alloc(ISNS_TAG_ISCSI_NAME, NULL, &var);
+ return __isns_source_create(attr);
+}
+
+/*
+ * This is used to attach a dummy source to iSNS responses
+ * until I fixed up all the code that relies on msg->is_source
+ * to be valid all the time.
+ */
+isns_source_t *
+isns_source_dummy(void)
+{
+ static isns_source_t *dummy = NULL;
+
+ if (!dummy)
+ dummy = isns_source_create_iscsi(".dummy.");
+ return isns_source_get(dummy);
+}
+
+uint32_t
+isns_source_type(const isns_source_t *source)
+{
+ return source->is_attr->ia_tag_id;
+}
+
+const char *
+isns_source_name(const isns_source_t *source)
+{
+ return source->is_attr->ia_value.iv_string;
+}
+
+isns_attr_t *
+isns_source_attr(const isns_source_t *source)
+{
+ return source->is_attr;
+}
+
+/*
+ * Obtain an additional reference on the source object
+ */
+isns_source_t *
+isns_source_get(isns_source_t *source)
+{
+ if (source)
+ source->is_users++;
+ return source;
+}
+
+/*
+ * Look up the node corresponding to this source name
+ * When we get here, we have already verified that the
+ * client is permitted (by policy) to use this source node.
+ */
+int
+isns_source_set_node(isns_source_t *source, isns_db_t *db)
+{
+ isns_object_t *node, *entity;
+ uint32_t node_type;
+
+ if (source->is_node)
+ return 1;
+
+ if (db == NULL)
+ return 0;
+
+ node = isns_db_lookup_source_node(db, source);
+ if (node == NULL)
+ return 0;
+
+ if (!isns_object_get_uint32(node, ISNS_TAG_ISCSI_NODE_TYPE, &node_type))
+ node_type = 0;
+
+ source->is_node = node;
+ source->is_node_type = node_type;
+
+ if ((entity = isns_object_get_entity(node)) != NULL)
+ source->is_entity = isns_object_get(entity);
+ return 1;
+}
+
+void
+isns_source_set_entity(isns_source_t *source, isns_object_t *obj)
+{
+ if (obj)
+ isns_object_get(obj);
+ isns_object_release(source->is_entity);
+ source->is_entity = obj;
+}
+
+/*
+ * Release a reference on the source object
+ */
+void
+isns_source_release(isns_source_t *source)
+{
+ if (source && --source->is_users == 0) {
+ isns_attr_release(source->is_attr);
+ isns_object_release(source->is_node);
+ isns_object_release(source->is_entity);
+ memset(source, 0xa5, sizeof(*source));
+ isns_free(source);
+ }
+}
+
+/*
+ * Compare two source objects
+ */
+int
+isns_source_match(const isns_source_t *a,
+ const isns_source_t *b)
+{
+ if (a && b)
+ return isns_attr_match(a->is_attr, b->is_attr);
+ return 0;
+}
+
+/*
+ * Encode/decode source object
+ */
+int
+isns_source_encode(buf_t *bp, const isns_source_t *source)
+{
+ if (source == NULL) {
+ isns_attr_t nil = ISNS_ATTR_INIT(ISNS_TAG_DELIMITER, nil, 0);
+
+ return isns_attr_encode(bp, &nil);
+ }
+ return isns_attr_encode(bp, source->is_attr);
+}
+
+int
+isns_source_decode(buf_t *bp, isns_source_t **result)
+{
+ isns_attr_t *attr;
+ int status;
+
+ status = isns_attr_decode(bp, &attr);
+ if (status == ISNS_SUCCESS) {
+ /*
+ * 5.6.1
+ * The Source Attribute uniquely identifies the source of the
+ * message. Valid Source Attribute types are shown below.
+ *
+ * Valid Source Attributes
+ * -----------------------
+ * iSCSI Name
+ * FC Port Name WWPN
+ */
+ switch (attr->ia_tag_id) {
+#if 0
+ case ISNS_TAG_DELIMITER:
+ *result = NULL;
+ break;
+#endif
+
+ case ISNS_TAG_ISCSI_NAME:
+ *result = __isns_source_create(attr);
+ break;
+
+ case ISNS_TAG_FC_PORT_NAME_WWPN:
+ *result = __isns_source_create(attr);
+ break;
+
+ default:
+ isns_attr_release(attr);
+ return ISNS_SOURCE_UNKNOWN;
+ }
+ }
+ return status;
+}