Blame register.c

Packit 1c819f
/*
Packit 1c819f
 * Handle iSNS Device Attribute Registration
Packit 1c819f
 *
Packit 1c819f
 * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
Packit 1c819f
 */
Packit 1c819f
Packit 1c819f
#include <stdlib.h>
Packit 1c819f
#include <string.h>
Packit 1c819f
#include <time.h>
Packit 1c819f
#include "config.h"
Packit 1c819f
#include <libisns/isns.h>
Packit 1c819f
#include <libisns/attrs.h>
Packit 1c819f
#include "objects.h"
Packit 1c819f
#include <libisns/message.h>
Packit 1c819f
#include "security.h"
Packit 1c819f
#include <libisns/util.h>
Packit 1c819f
#include "db.h"
Packit 1c819f
Packit 1c819f
Packit 1c819f
static int	isns_create_default_pgs_for_object(isns_db_t *, isns_object_t *);
Packit 1c819f
Packit 1c819f
/*
Packit 1c819f
 * Create a registration, and set the source name
Packit 1c819f
 */
Packit 1c819f
static isns_simple_t *
Packit 1c819f
__isns_create_registration(isns_source_t *source, isns_object_t *key_obj)
Packit 1c819f
{
Packit 1c819f
	isns_simple_t	*reg;
Packit 1c819f
Packit 1c819f
	reg = isns_simple_create(ISNS_DEVICE_ATTRIBUTE_REGISTER, source, NULL);
Packit 1c819f
	if (reg == NULL)
Packit 1c819f
		return NULL;
Packit 1c819f
Packit 1c819f
	/*
Packit 1c819f
	 * When sending a registration, you can either specify
Packit 1c819f
	 * the object to be modified in the key attrs, or leave
Packit 1c819f
	 * the key empty.
Packit 1c819f
	 */
Packit 1c819f
	if (key_obj == NULL)
Packit 1c819f
		return reg;
Packit 1c819f
Packit 1c819f
	/* User gave us a key object. We need to put the key
Packit 1c819f
	 * attributes into the message attrs, and *all* attrs
Packit 1c819f
	 * into the operating attrs. */
Packit 1c819f
	if (!isns_object_extract_keys(key_obj, &reg->is_message_attrs)) {
Packit 1c819f
		/* bummer - seems the object is missing some
Packit 1c819f
		 * vital organs. */
Packit 1c819f
		isns_warning("%s: object not fully specified, key attrs missing\n",
Packit 1c819f
				__FUNCTION__);
Packit 1c819f
		goto failed;
Packit 1c819f
	}
Packit 1c819f
Packit 1c819f
	/*
Packit 1c819f
	 * The Message Key identifies the object the DevAttrReg message
Packit 1c819f
	 * acts upon.  [...] The key attribute(s) identifying this object
Packit 1c819f
	 * MUST also be included among the Operating Attributes.
Packit 1c819f
	 *
Packit 1c819f
	 * We do not enforce this here, we rely on the caller to get this
Packit 1c819f
	 * right.
Packit 1c819f
	 */
Packit 1c819f
#if 0
Packit 1c819f
	if (!isns_object_extract_all(key_obj, &reg->is_operating_attrs)) {
Packit 1c819f
		isns_warning("%s: unable to extract attrs from key objects\n",
Packit 1c819f
				__FUNCTION__);
Packit 1c819f
		goto failed;
Packit 1c819f
	}
Packit 1c819f
#endif
Packit 1c819f
Packit 1c819f
	return reg;
Packit 1c819f
Packit 1c819f
failed:
Packit 1c819f
	isns_simple_free(reg);
Packit 1c819f
	return NULL;
Packit 1c819f
}
Packit 1c819f
Packit 1c819f
isns_simple_t *
Packit 1c819f
isns_create_registration(isns_client_t *clnt, isns_object_t *key_obj)
Packit 1c819f
{
Packit 1c819f
	return __isns_create_registration(clnt->ic_source, key_obj);
Packit 1c819f
}
Packit 1c819f
Packit 1c819f
isns_simple_t *
Packit 1c819f
isns_create_registration2(isns_client_t *clnt, isns_object_t *key_obj,
Packit 1c819f
		isns_source_t *source)
Packit 1c819f
{
Packit 1c819f
	return __isns_create_registration(source?: clnt->ic_source, key_obj);
Packit 1c819f
}
Packit 1c819f
Packit 1c819f
/*
Packit 1c819f
 * Set the replace flag
Packit 1c819f
 */
Packit 1c819f
void
Packit 1c819f
isns_registration_set_replace(isns_simple_t *reg, int replace)
Packit 1c819f
{
Packit 1c819f
	reg->is_replace = !!replace;
Packit 1c819f
}
Packit 1c819f
Packit 1c819f
/*
Packit 1c819f
 * Add an object to the registration
Packit 1c819f
 */
Packit 1c819f
void
Packit 1c819f
isns_registration_add_object(isns_simple_t *reg, isns_object_t *obj)
Packit 1c819f
{
Packit 1c819f
	isns_object_extract_writable(obj, &reg->is_operating_attrs);
Packit 1c819f
}
Packit 1c819f
Packit 1c819f
void
Packit 1c819f
isns_registration_add_object_list(isns_simple_t *reg, isns_object_list_t *list)
Packit 1c819f
{
Packit 1c819f
	unsigned int i;
Packit 1c819f
Packit 1c819f
	for (i = 0; i < list->iol_count; ++i) {
Packit 1c819f
		isns_object_extract_writable(list->iol_data[i],
Packit 1c819f
				&reg->is_operating_attrs);
Packit 1c819f
	}
Packit 1c819f
}
Packit 1c819f
Packit 1c819f
/*
Packit 1c819f
 * Get the key object given in this message
Packit 1c819f
 *
Packit 1c819f
 * It doesn't say anywhere explicitly in the RFC, but
Packit 1c819f
 * the message key can contain both key and non-key
Packit 1c819f
 * attributes. For instance, you can search by
Packit 1c819f
 * Portal Group Index (section 3.4).
Packit 1c819f
 */
Packit 1c819f
static int
Packit 1c819f
isns_registration_get_key(isns_simple_t *reg, isns_db_t *db, isns_object_t **key_obj)
Packit 1c819f
{
Packit 1c819f
	isns_attr_list_t *keys = &reg->is_message_attrs;
Packit 1c819f
	isns_attr_list_t dummy_keys = ISNS_ATTR_LIST_INIT;
Packit 1c819f
	isns_attr_t	*attr;
Packit 1c819f
	isns_object_t	*obj = NULL;
Packit 1c819f
	const char	*eid = NULL;
Packit 1c819f
	char		eidbuf[128];
Packit 1c819f
	int		status = ISNS_SUCCESS;
Packit 1c819f
	int		obj_must_exist = 0;
Packit 1c819f
Packit 1c819f
	/*
Packit 1c819f
	 * 5.6.5.1
Packit 1c819f
	 * If the Message Key is not present, then the DevAttrReg message
Packit 1c819f
	 * implicitly registers a new Network Entity.  In this case,
Packit 1c819f
	 * the replace bit SHALL be ignored; a new Network Entity SHALL
Packit 1c819f
	 * be created.
Packit 1c819f
	 *
Packit 1c819f
	 * Note that some clients seem to leave the message key
Packit 1c819f
	 * empty, but hide the entity identifier in the operating
Packit 1c819f
	 * attrs.
Packit 1c819f
	 */
Packit 1c819f
	if (keys->ial_count != 0) {
Packit 1c819f
		attr = keys->ial_data[0];
Packit 1c819f
Packit 1c819f
		/*
Packit 1c819f
		 * 5.6.5.1
Packit 1c819f
		 * If the Message Key does not contain an EID, and no
Packit 1c819f
		 * pre-existing objects match the Message Key, then the
Packit 1c819f
		 * DevAttrReg message SHALL be rejected with a status
Packit 1c819f
		 * code of 3 (Invalid Registration).
Packit 1c819f
		 */
Packit 1c819f
		if (keys->ial_count != 1
Packit 1c819f
		 || attr->ia_tag_id != ISNS_TAG_ENTITY_IDENTIFIER)
Packit 1c819f
			obj_must_exist = 1;
Packit 1c819f
	} else {
Packit 1c819f
		/* Empty message key. But the client may have hidden
Packit 1c819f
		 * the EID in the operating attrs :-/
Packit 1c819f
		 */
Packit 1c819f
		if (reg->is_operating_attrs.ial_count == 0)
Packit 1c819f
			goto create_entity;
Packit 1c819f
Packit 1c819f
		attr = reg->is_operating_attrs.ial_data[0];
Packit 1c819f
		if (attr->ia_tag_id != ISNS_TAG_ENTITY_IDENTIFIER)
Packit 1c819f
			goto create_entity;
Packit 1c819f
Packit 1c819f
		isns_attr_list_append_attr(&dummy_keys, attr);
Packit 1c819f
		keys = &dummy_keys;
Packit 1c819f
	}
Packit 1c819f
Packit 1c819f
	/* If the caller specifies an EID, extract it while
Packit 1c819f
	 * we know what we're doing :-) */
Packit 1c819f
	if (attr->ia_tag_id == ISNS_TAG_ENTITY_IDENTIFIER
Packit 1c819f
	 && ISNS_ATTR_IS_STRING(attr))
Packit 1c819f
		eid = attr->ia_value.iv_string;
Packit 1c819f
Packit 1c819f
	/* Look up the object identified by the keys.
Packit 1c819f
	 * We do not scope the lookup, as the client
Packit 1c819f
	 * may want to add nodes to an entity that's
Packit 1c819f
	 * currently empty - and hence not visible to
Packit 1c819f
	 * any DD. */
Packit 1c819f
	if (!ISNS_ATTR_IS_NIL(attr))
Packit 1c819f
		obj = isns_db_lookup(db, NULL, keys);
Packit 1c819f
Packit 1c819f
	if (obj == NULL && obj_must_exist)
Packit 1c819f
		goto err_invalid;
Packit 1c819f
Packit 1c819f
	if (obj != NULL) {
Packit 1c819f
		/*
Packit 1c819f
		 * Policy: verify that the client is permitted
Packit 1c819f
		 * to access this object.
Packit 1c819f
		 *
Packit 1c819f
		 * This includes
Packit 1c819f
		 *  -	the client node must be the object owner,
Packit 1c819f
		 *	or a control node.
Packit 1c819f
		 *  -	the policy must allow modification of
Packit 1c819f
		 *	this object type.
Packit 1c819f
		 */
Packit 1c819f
		if (!isns_policy_validate_object_access(reg->is_policy,
Packit 1c819f
					reg->is_source,
Packit 1c819f
					obj, reg->is_function))
Packit 1c819f
			goto err_unauthorized;
Packit 1c819f
Packit 1c819f
found_object:
Packit 1c819f
		if (reg->is_replace) {
Packit 1c819f
			isns_object_t *container = NULL;
Packit 1c819f
Packit 1c819f
			if (!ISNS_IS_ENTITY(obj)) {
Packit 1c819f
				container = isns_object_get_entity(obj);
Packit 1c819f
				if (container == NULL) {
Packit 1c819f
					isns_error("Trying to replace %s (id %u) "
Packit 1c819f
						   "which has no container\n",
Packit 1c819f
						obj->ie_template->iot_name,
Packit 1c819f
						obj->ie_index);
Packit 1c819f
					goto err_invalid;
Packit 1c819f
				}
Packit 1c819f
			}
Packit 1c819f
Packit 1c819f
			isns_debug_state("Replacing %s (id %u)\n",
Packit 1c819f
				obj->ie_template->iot_name,
Packit 1c819f
				obj->ie_index);
Packit 1c819f
			isns_db_remove(db, obj);
Packit 1c819f
			isns_object_release(obj);
Packit 1c819f
Packit 1c819f
			/* Purge the deleted objects from the database now */
Packit 1c819f
			isns_db_purge(db);
Packit 1c819f
Packit 1c819f
			/* We need to flush pending SCNs because the
Packit 1c819f
			 * objects may be resurrected from limbo,
Packit 1c819f
			 * and we might be looking at stale data. */
Packit 1c819f
			isns_scn_transmit_all();
Packit 1c819f
Packit 1c819f
			/* It's an entity. Nuke it and create
Packit 1c819f
			 * a new one. */
Packit 1c819f
			if (container == NULL) {
Packit 1c819f
				isns_source_set_entity(reg->is_source, NULL);
Packit 1c819f
				goto create_entity;
Packit 1c819f
			}
Packit 1c819f
Packit 1c819f
			obj = isns_object_get(container);
Packit 1c819f
		}
Packit 1c819f
Packit 1c819f
		goto out;
Packit 1c819f
	}
Packit 1c819f
Packit 1c819f
	/*
Packit 1c819f
	 * If the Message Key contains an EID and no pre-existing objects
Packit 1c819f
	 * match the Message Key, then the DevAttrReg message SHALL create a
Packit 1c819f
	 * new Entity with the specified EID and any new object(s) specified
Packit 1c819f
	 * by the Operating Attributes.  The replace bit SHALL be ignored.
Packit 1c819f
	 *
Packit 1c819f
	 * Implementer's note: the EID attribute may be empty, in which case
Packit 1c819f
	 * we also create a new entity.
Packit 1c819f
	 */
Packit 1c819f
Packit 1c819f
create_entity:
Packit 1c819f
	if (!isns_policy_validate_object_creation(reg->is_policy,
Packit 1c819f
				reg->is_source,
Packit 1c819f
				&isns_entity_template, keys, NULL,
Packit 1c819f
				reg->is_function))
Packit 1c819f
		goto err_unauthorized;
Packit 1c819f
Packit 1c819f
	/*
Packit 1c819f
	 * 5.6.5.1
Packit 1c819f
	 * A registration message that creates a new Network Entity object
Packit 1c819f
	 * MUST contain at least one Portal or one Storage Node.  If the
Packit 1c819f
	 * message does not, then it SHALL be considered invalid and result
Packit 1c819f
	 * in a response with Status Code of 3 (Invalid Registration).
Packit 1c819f
	 */
Packit 1c819f
	/* FIXME: Implement this check */
Packit 1c819f
Packit 1c819f
	/* We try to play nice with lazy clients and attempt to
Packit 1c819f
	 * look up the network entity given the source name.
Packit 1c819f
	 * But we don't do this if a non-NULL EID was given,
Packit 1c819f
	 * because the client may explicitly want to specify more
Packit 1c819f
	 * than one Network Entity.
Packit 1c819f
	 */
Packit 1c819f
	if (eid == NULL) {
Packit 1c819f
		obj = reg->is_source->is_entity;
Packit 1c819f
		if (obj != NULL) {
Packit 1c819f
			isns_object_get(obj);
Packit 1c819f
			goto found_object;
Packit 1c819f
		}
Packit 1c819f
Packit 1c819f
		/* The policy may define a default entity name.
Packit 1c819f
		 * If that is the case, use it.
Packit 1c819f
		 */
Packit 1c819f
		eid = isns_policy_default_entity(reg->is_policy);
Packit 1c819f
		if (eid) {
Packit 1c819f
			obj = isns_db_vlookup(db, &isns_entity_template,
Packit 1c819f
					ISNS_TAG_ENTITY_IDENTIFIER, eid,
Packit 1c819f
					0);
Packit 1c819f
			if (obj) {
Packit 1c819f
				reg->is_source->is_entity = isns_object_get(obj);
Packit 1c819f
				goto found_object;
Packit 1c819f
			}
Packit 1c819f
		}
Packit 1c819f
	}
Packit 1c819f
Packit 1c819f
	/*
Packit 1c819f
	 * 5.6.5.1
Packit 1c819f
	 * If the Message Key and Operating Attributes do not contain
Packit 1c819f
	 * an EID attribute, or if the EID attribute has a length of 0,
Packit 1c819f
	 * then a new Network Entity object SHALL be created and the iSNS
Packit 1c819f
	 * server SHALL supply a unique EID value for it.
Packit 1c819f
	 */
Packit 1c819f
	if (eid == NULL)
Packit 1c819f
		eid = isns_db_generate_eid(db, eidbuf, sizeof(eidbuf));
Packit 1c819f
Packit 1c819f
	/*
Packit 1c819f
	 * 6.2.2.  Entity Protocol
Packit 1c819f
	 *
Packit 1c819f
	 * This attribute is required during initial registration of
Packit 1c819f
	 * the Network Entity.
Packit 1c819f
	 *
Packit 1c819f
	 * Implementer's note: we don't rely on this. Instead, the
Packit 1c819f
	 * Entity Protocol is selected based on the source type.
Packit 1c819f
	 * If the client specifies the protocol, the auto-selected
Packit 1c819f
	 * value is overwritten.
Packit 1c819f
	 */
Packit 1c819f
	obj = isns_create_entity_for_source(reg->is_source, eid);
Packit 1c819f
	if (obj == NULL)
Packit 1c819f
		goto err_invalid;
Packit 1c819f
Packit 1c819f
	isns_source_set_entity(reg->is_source, obj);
Packit 1c819f
Packit 1c819f
	/*
Packit 1c819f
	 * 6.2.6
Packit 1c819f
	 * If a Registration Period is not requested by the iSNS
Packit 1c819f
	 * client and Entity Status Inquiry (ESI) messages are not
Packit 1c819f
	 * enabled for that client, then the Registration Period
Packit 1c819f
	 * SHALL be set to a non-zero value by the iSNS server.
Packit 1c819f
	 * This implementation-specific value for the Registration
Packit 1c819f
	 * Period SHALL be returned in the registration response to the
Packit 1c819f
	 * iSNS client.  The Registration Period may be set to zero,
Packit 1c819f
	 * indicating its non-use, only if ESI messages are enabled for
Packit 1c819f
	 * that Network Entity.
Packit 1c819f
	 *
Packit 1c819f
	 * Implementer's note: we diverge from this in two ways:
Packit 1c819f
	 *  -	the admin may choose to disable registration timeout,
Packit 1c819f
	 *	by setting RegistrationPeriod=0 in the config file
Packit 1c819f
	 *
Packit 1c819f
	 *  -	When a new entity is created, we always set the
Packit 1c819f
	 *	registration interval because we cannot know yet
Packit 1c819f
	 *	whether the client will subsequently enable ESI or
Packit 1c819f
	 *	not.
Packit 1c819f
	 *
Packit 1c819f
	 *  -	The control entity (holding policy objects) will
Packit 1c819f
	 *	not expire.
Packit 1c819f
	 */
Packit 1c819f
	if (isns_config.ic_registration_period
Packit 1c819f
	 && strcasecmp(eid, ISNS_ENTITY_CONTROL)) {
Packit 1c819f
		isns_object_set_uint32(obj,
Packit 1c819f
				ISNS_TAG_REGISTRATION_PERIOD,
Packit 1c819f
				isns_config.ic_registration_period);
Packit 1c819f
		isns_object_set_uint64(obj,
Packit 1c819f
				ISNS_TAG_TIMESTAMP,
Packit 1c819f
				time(NULL));
Packit 1c819f
	}
Packit 1c819f
Packit 1c819f
	/* Insert into database, and set the object's owner */
Packit 1c819f
	isns_db_insert(db, obj);
Packit 1c819f
Packit 1c819f
	reg->is_replace = 0;
Packit 1c819f
Packit 1c819f
out:
Packit 1c819f
	*key_obj = obj;
Packit 1c819f
	isns_attr_list_destroy(&dummy_keys);
Packit 1c819f
	return ISNS_SUCCESS;
Packit 1c819f
Packit 1c819f
error:
Packit 1c819f
	if (obj)
Packit 1c819f
		isns_object_release(obj);
Packit 1c819f
	isns_attr_list_destroy(&dummy_keys);
Packit 1c819f
	return status;
Packit 1c819f
Packit 1c819f
err_unauthorized:
Packit 1c819f
	status = ISNS_SOURCE_UNAUTHORIZED;
Packit 1c819f
	goto error;
Packit 1c819f
Packit 1c819f
err_invalid:
Packit 1c819f
	status = ISNS_INVALID_REGISTRATION;
Packit 1c819f
	goto error;
Packit 1c819f
}
Packit 1c819f
Packit 1c819f
static int
Packit 1c819f
isns_registration_get_next_object(isns_db_t *db,
Packit 1c819f
				struct isns_attr_list_scanner *st,
Packit 1c819f
				isns_object_list_t *result)
Packit 1c819f
{
Packit 1c819f
	isns_object_t	*current;
Packit 1c819f
	int		status, esi = 0;
Packit 1c819f
Packit 1c819f
	status = isns_attr_list_scanner_next(st);
Packit 1c819f
	/* We get here if the registration has a trailing PGT */
Packit 1c819f
	if (status == ISNS_NO_SUCH_ENTRY)
Packit 1c819f
		return ISNS_SUCCESS;
Packit 1c819f
	if (status)
Packit 1c819f
		return status;
Packit 1c819f
Packit 1c819f
	/*
Packit 1c819f
	 * Validate the attrlist.
Packit 1c819f
	 * This makes sure the client does not include
Packit 1c819f
	 * duplicate attributes, readonly attributes
Packit 1c819f
	 * such as Registration Timestamp, Index and Next Index,
Packit 1c819f
	 * or privileged data (such as marking a storage node as
Packit 1c819f
	 * control node).
Packit 1c819f
	 */
Packit 1c819f
	status = isns_attr_list_validate(&st->attrs,
Packit 1c819f
			st->policy,
Packit 1c819f
			ISNS_DEVICE_ATTRIBUTE_REGISTER);
Packit 1c819f
	if (status) {
Packit 1c819f
		isns_debug_protocol("invalid attr in message\n");
Packit 1c819f
		return status;
Packit 1c819f
	}
Packit 1c819f
Packit 1c819f
	/*
Packit 1c819f
	 * 6.3.4.  Entity Status Inquiry Interval
Packit 1c819f
	 *
Packit 1c819f
	 * If the iSNS server is unable to support ESI messages
Packit 1c819f
	 * or the ESI Interval requested, it SHALL [...] reject
Packit 1c819f
	 * the ESI request by returning an "ESI Not Available"
Packit 1c819f
	 * Status Code [...]
Packit 1c819f
	 *
Packit 1c819f
	 * Implementer's note: In section 5.7.5.1, the RFC talks
Packit 1c819f
	 * about modifying the requested ESI interval; so it seems
Packit 1c819f
	 * it's okay to be liberal about the ESI intervals we accept,
Packit 1c819f
	 * and update them quietly.
Packit 1c819f
	 */
Packit 1c819f
	if (isns_attr_list_contains(&st->attrs, ISNS_TAG_ESI_PORT)) {
Packit 1c819f
		if (!isns_esi_enabled) {
Packit 1c819f
			isns_debug_esi("Refusing to accept portal "
Packit 1c819f
					"registration with ESI port\n");
Packit 1c819f
			return ISNS_ESI_NOT_AVAILABLE;
Packit 1c819f
		}
Packit 1c819f
		esi = 1;
Packit 1c819f
	}
Packit 1c819f
Packit 1c819f
	/*
Packit 1c819f
	 * Override any registration period specified by the client.
Packit 1c819f
	 */
Packit 1c819f
	if (isns_attr_list_contains(&st->attrs, ISNS_TAG_REGISTRATION_PERIOD)) {
Packit 1c819f
		isns_value_t value = ISNS_VALUE_INIT(uint32,
Packit 1c819f
					isns_config.ic_registration_period);
Packit 1c819f
Packit 1c819f
		isns_attr_list_update_value(&st->attrs,
Packit 1c819f
				ISNS_TAG_REGISTRATION_PERIOD, NULL,
Packit 1c819f
				&value);
Packit 1c819f
	}
Packit 1c819f
Packit 1c819f
	if (st->tmpl == &isns_entity_template) {
Packit 1c819f
		/*
Packit 1c819f
		 * 5.6.5.1.
Packit 1c819f
		 * A maximum of one Network Entity object can be
Packit 1c819f
		 * created or updated with a single DevAttrReg
Packit 1c819f
		 * message.  Consequently, the Operating Attributes
Packit 1c819f
		 * MUST NOT contain more than one Network Entity
Packit 1c819f
		 * object.
Packit 1c819f
		 */
Packit 1c819f
		if (st->entities++) {
Packit 1c819f
			isns_debug_protocol("More than one entity in DevAttrReg msg\n");
Packit 1c819f
			return ISNS_INVALID_REGISTRATION;
Packit 1c819f
		}
Packit 1c819f
Packit 1c819f
		/* This should be the key object.
Packit 1c819f
		 * The EID specified by by the client may be
Packit 1c819f
		 * empty, so don't overwrite the value we
Packit 1c819f
		 * assigned with something else.
Packit 1c819f
		 */
Packit 1c819f
		if (!isns_object_match(st->key_obj, &st->keys)) {
Packit 1c819f
			isns_debug_protocol("Entity mismatch in message vs. operating attrs\n");
Packit 1c819f
			return ISNS_INVALID_REGISTRATION;
Packit 1c819f
		}
Packit 1c819f
		current = isns_object_get(st->key_obj);
Packit 1c819f
	} else
Packit 1c819f
	if (st->tmpl == &isns_dd_template || st->tmpl == &isns_ddset_template) {
Packit 1c819f
		isns_debug_protocol("DevAttrReg of type %s not allowed\n",
Packit 1c819f
				st->tmpl->iot_name);
Packit 1c819f
		return ISNS_INVALID_REGISTRATION;
Packit 1c819f
	} else {
Packit 1c819f
		/* This will also catch objects in limbo. */
Packit 1c819f
		current = isns_db_lookup(db, st->tmpl, &st->keys);
Packit 1c819f
	}
Packit 1c819f
Packit 1c819f
	if (current != NULL) {
Packit 1c819f
		/* 
Packit 1c819f
		 * If the replace bit is not set, then the message updates
Packit 1c819f
		 * the attributes of the object identified by the Message Key
Packit 1c819f
		 * and its subordinate objects.  Existing object containment
Packit 1c819f
		 * relationships MUST NOT be changed.  For existing objects,
Packit 1c819f
		 * key attributes MUST NOT be modified, but new subordinate
Packit 1c819f
		 * objects MAY be added.
Packit 1c819f
		 */
Packit 1c819f
Packit 1c819f
		/*
Packit 1c819f
		 * [...]
Packit 1c819f
		 * If the Node identified by the Source Attribute is
Packit 1c819f
		 * not a Control Node, then the objects in the operating
Packit 1c819f
		 * attributes MUST be members of the same Network Entity
Packit 1c819f
		 * as the Source Node.
Packit 1c819f
		 */
Packit 1c819f
		if (!isns_policy_validate_object_update(st->policy,
Packit 1c819f
					st->source, current, &st->attrs,
Packit 1c819f
					ISNS_DEVICE_ATTRIBUTE_REGISTER)) {
Packit 1c819f
			isns_object_release(current);
Packit 1c819f
			return ISNS_SOURCE_UNAUTHORIZED;
Packit 1c819f
		}
Packit 1c819f
Packit 1c819f
		/* We shouldn't allow messages affecting one Entity
Packit 1c819f
		 * to modify objects owned by a different Entity.
Packit 1c819f
		 *
Packit 1c819f
		 * However, there may be orphan objects (created
Packit 1c819f
		 * while populating discovery domains). These will
Packit 1c819f
		 * not be associated with any Network Entity, so
Packit 1c819f
		 * they're up for grabs.
Packit 1c819f
		 */
Packit 1c819f
		if (st->key_obj == current
Packit 1c819f
		 || st->key_obj == current->ie_container) {
Packit 1c819f
			/* All is well. The current object is the
Packit 1c819f
			 * key object itself, or a direct descendant of the
Packit 1c819f
			 * key object. */
Packit 1c819f
			/* FIXME: with FC we can get deeper nesting;
Packit 1c819f
			 * this needs work. */
Packit 1c819f
		} else
Packit 1c819f
		if (!isns_object_is_valid_container(st->key_obj, st->tmpl)) {
Packit 1c819f
			isns_error("Client attempts to add %s object to a %s - tsk tsk.\n",
Packit 1c819f
					st->tmpl->iot_name,
Packit 1c819f
					st->key_obj->ie_template->iot_name);
Packit 1c819f
			goto invalid_registration;
Packit 1c819f
		} else if (current->ie_container) {
Packit 1c819f
			/* We shouldn't get here in authenticated mode,
Packit 1c819f
			 * but in insecure mode we still may. */
Packit 1c819f
			isns_error("Client attempts to move %s %u to a different %s\n",
Packit 1c819f
					current->ie_template->iot_name,
Packit 1c819f
					current->ie_index,
Packit 1c819f
					st->key_obj->ie_template->iot_name);
Packit 1c819f
			goto invalid_registration;
Packit 1c819f
		}
Packit 1c819f
	} else {
Packit 1c819f
		if (!isns_object_is_valid_container(st->key_obj, st->tmpl)) {
Packit 1c819f
			isns_error("Client attempts to add %s object to a %s - tsk tsk.\n",
Packit 1c819f
					st->tmpl->iot_name,
Packit 1c819f
					st->key_obj->ie_template->iot_name);
Packit 1c819f
			goto invalid_registration;
Packit 1c819f
		}
Packit 1c819f
Packit 1c819f
		if (!isns_policy_validate_object_creation(st->policy,
Packit 1c819f
					st->source, st->tmpl,
Packit 1c819f
					&st->keys, &st->attrs,
Packit 1c819f
					ISNS_DEVICE_ATTRIBUTE_REGISTER)) {
Packit 1c819f
			return ISNS_SOURCE_UNAUTHORIZED;
Packit 1c819f
		}
Packit 1c819f
		current = isns_create_object(st->tmpl, &st->keys,
Packit 1c819f
				isns_object_get_entity(st->key_obj));
Packit 1c819f
Packit 1c819f
		/* We do not insert the new object into the database yet.
Packit 1c819f
		 * That happens after we're done with parsing *all*
Packit 1c819f
		 * objects. */
Packit 1c819f
	}
Packit 1c819f
Packit 1c819f
	if (!isns_object_set_attrlist(current, &st->attrs)) {
Packit 1c819f
		isns_debug_state("Error updating object's attrlist\n");
Packit 1c819f
		isns_object_release(current);
Packit 1c819f
		return ISNS_INTERNAL_ERROR;
Packit 1c819f
	}
Packit 1c819f
Packit 1c819f
	/* If the client specifies an ESI port, make sure the
Packit 1c819f
	 * ESI interval is set and within bounds. */
Packit 1c819f
	if (esi) {
Packit 1c819f
		uint32_t	esi_interval;
Packit 1c819f
Packit 1c819f
		if (!isns_object_get_uint32(current,
Packit 1c819f
					ISNS_TAG_ESI_INTERVAL, &esi_interval)) {
Packit 1c819f
			esi_interval = isns_config.ic_esi_min_interval;
Packit 1c819f
		} else
Packit 1c819f
		if (esi_interval < isns_config.ic_esi_min_interval) {
Packit 1c819f
			esi_interval = isns_config.ic_esi_min_interval;
Packit 1c819f
		} else
Packit 1c819f
		if (esi_interval > isns_config.ic_esi_max_interval) {
Packit 1c819f
			esi_interval = isns_config.ic_esi_max_interval;
Packit 1c819f
		} else {
Packit 1c819f
			esi_interval = 0;
Packit 1c819f
		}
Packit 1c819f
Packit 1c819f
		if (esi_interval)
Packit 1c819f
			isns_object_set_uint32(current,
Packit 1c819f
					ISNS_TAG_ESI_INTERVAL, esi_interval);
Packit 1c819f
	}
Packit 1c819f
Packit 1c819f
	/* Append it to the result list.
Packit 1c819f
	 * We do not return the key object, otherwise
Packit 1c819f
	 * we end up putting it into the response twice.
Packit 1c819f
	 */
Packit 1c819f
	if (current != st->key_obj)
Packit 1c819f
		isns_object_list_append(result, current);
Packit 1c819f
Packit 1c819f
	/*
Packit 1c819f
	 * When a Portal is registered, the Portal attributes MAY immediately be
Packit 1c819f
	 * followed by a PGT attribute. 
Packit 1c819f
	 * [...]
Packit 1c819f
	 * When an iSCSI Storage Node is registered, the Storage Node attributes
Packit 1c819f
	 * MAY immediately be followed by a PGT attribute.
Packit 1c819f
	 */
Packit 1c819f
	if (st->tmpl == &isns_portal_template
Packit 1c819f
	 || st->tmpl == &isns_iscsi_node_template) {
Packit 1c819f
		st->pgt_next_attr = ISNS_TAG_PG_TAG;
Packit 1c819f
		st->pgt_base_object = current;
Packit 1c819f
	} else if (st->tmpl != &isns_iscsi_pg_template) {
Packit 1c819f
		st->pgt_next_attr = 0;
Packit 1c819f
		st->pgt_base_object = NULL;
Packit 1c819f
	}
Packit 1c819f
Packit 1c819f
	isns_object_release(current);
Packit 1c819f
	return ISNS_SUCCESS;
Packit 1c819f
Packit 1c819f
invalid_registration:
Packit 1c819f
	if (current)
Packit 1c819f
		isns_object_release(current);
Packit 1c819f
	return ISNS_INVALID_REGISTRATION;
Packit 1c819f
}
Packit 1c819f
Packit 1c819f
/*
Packit 1c819f
 * Extract the list of objects to be registered from
Packit 1c819f
 * the list of operating attributes.
Packit 1c819f
 */
Packit 1c819f
static int
Packit 1c819f
isns_registration_get_objects(isns_simple_t *reg, isns_db_t *db,
Packit 1c819f
					isns_object_t *key_obj,
Packit 1c819f
					isns_object_list_t *result)
Packit 1c819f
{
Packit 1c819f
	struct isns_attr_list_scanner state;
Packit 1c819f
	int		status = ISNS_SUCCESS;
Packit 1c819f
Packit 1c819f
	isns_attr_list_scanner_init(&state, key_obj, &reg->is_operating_attrs);
Packit 1c819f
	state.source = reg->is_source;
Packit 1c819f
	state.policy = reg->is_policy;
Packit 1c819f
Packit 1c819f
	/*
Packit 1c819f
	 * 5.6.4.
Packit 1c819f
	 * The ordering of Operating Attributes in the message is
Packit 1c819f
	 * important for determining the relationships among objects
Packit 1c819f
	 * and their ownership of non-key attributes.  iSNS protocol
Packit 1c819f
	 * messages that violate these ordering rules SHALL be rejected
Packit 1c819f
	 * with the Status Code of 2 (Message Format Error).
Packit 1c819f
	 */
Packit 1c819f
	/* FIXME: Implement this check */
Packit 1c819f
Packit 1c819f
	while (state.pos < state.orig_attrs.ial_count) {
Packit 1c819f
		status = isns_registration_get_next_object(db,
Packit 1c819f
				&state, result);
Packit 1c819f
Packit 1c819f
		if (status)
Packit 1c819f
			break;
Packit 1c819f
	}
Packit 1c819f
Packit 1c819f
	isns_attr_list_scanner_destroy(&state);
Packit 1c819f
	return status;
Packit 1c819f
}
Packit 1c819f
Packit 1c819f
/*
Packit 1c819f
 * 5.6.5.1
Packit 1c819f
 * New PG objects are registered when an associated Portal or
Packit 1c819f
 * iSCSI Node object is registered.  An explicit PG object
Packit 1c819f
 * registration MAY follow a Portal or iSCSI Node object
Packit 1c819f
 * registration in a DevAttrReg message.
Packit 1c819f
 * [...]
Packit 1c819f
 * If the PGT value is not included in the Storage Node or
Packit 1c819f
 * Portal object registration, and if a PGT value was not
Packit 1c819f
 * previously registered for the relationship, then the PGT for
Packit 1c819f
 * the corresponding PG object SHALL be registered with a value
Packit 1c819f
 * of 0x00000001.
Packit 1c819f
 */
Packit 1c819f
static int
Packit 1c819f
isns_create_registration_pgs(isns_db_t *db,
Packit 1c819f
		const isns_object_list_t *list)
Packit 1c819f
{
Packit 1c819f
	unsigned int	i, num_created = 0;
Packit 1c819f
Packit 1c819f
	for (i = 0; i < list->iol_count; ++i) {
Packit 1c819f
		isns_object_t	*obj = list->iol_data[i];
Packit 1c819f
Packit 1c819f
		if (ISNS_IS_ISCSI_NODE(obj) || ISNS_IS_PORTAL(obj))
Packit 1c819f
			num_created += isns_create_default_pgs_for_object(db, obj);
Packit 1c819f
	}
Packit 1c819f
	return num_created;
Packit 1c819f
}
Packit 1c819f
Packit 1c819f
static int
Packit 1c819f
isns_create_default_pgs_for_object(isns_db_t *db, isns_object_t *this)
Packit 1c819f
{
Packit 1c819f
	isns_object_template_t *match_tmpl;
Packit 1c819f
	isns_object_t	*entity;
Packit 1c819f
	unsigned int	i, num_created = 0;
Packit 1c819f
Packit 1c819f
	if (ISNS_IS_ISCSI_NODE(this))
Packit 1c819f
		match_tmpl = &isns_portal_template;
Packit 1c819f
	else
Packit 1c819f
		match_tmpl = &isns_iscsi_node_template;
Packit 1c819f
Packit 1c819f
	entity = isns_object_get_entity(this);
Packit 1c819f
	for (i = 0; i < entity->ie_children.iol_count; ++i) {
Packit 1c819f
		isns_object_t	*that = entity->ie_children.iol_data[i], *pg;
Packit 1c819f
Packit 1c819f
		if (that->ie_template != match_tmpl)
Packit 1c819f
			continue;
Packit 1c819f
Packit 1c819f
		/* Create the portal group if it does not
Packit 1c819f
		 * exist. 
Packit 1c819f
		 * Note: we do not return these implicitly
Packit 1c819f
		 * created portal groups - that's a matter
Packit 1c819f
		 * of sheer laziness. We would have to
Packit 1c819f
		 * splice these into the list in the
Packit 1c819f
		 * appropriate location, and I guess it's
Packit 1c819f
		 * not really worth the hassle.
Packit 1c819f
		 */
Packit 1c819f
		if (ISNS_IS_ISCSI_NODE(this))
Packit 1c819f
			pg = isns_create_default_portal_group(db, that, this);
Packit 1c819f
		else
Packit 1c819f
			pg = isns_create_default_portal_group(db, this, that);
Packit 1c819f
Packit 1c819f
		/* There already is a PG linking these two
Packit 1c819f
		 * objects. */
Packit 1c819f
		if (pg == NULL)
Packit 1c819f
			continue;
Packit 1c819f
Packit 1c819f
		isns_db_insert(db, pg);
Packit 1c819f
Packit 1c819f
		isns_debug_message("--Created default PG:--\n");
Packit 1c819f
		isns_object_print(pg, isns_debug_message);
Packit 1c819f
Packit 1c819f
		isns_object_release(pg);
Packit 1c819f
		num_created++;
Packit 1c819f
	}
Packit 1c819f
Packit 1c819f
	return num_created;
Packit 1c819f
}
Packit 1c819f
Packit 1c819f
/*
Packit 1c819f
 * Commit all changes to the DB
Packit 1c819f
 */
Packit 1c819f
static int
Packit 1c819f
isns_commit_registration(isns_db_t *db, isns_object_t *key_obj, isns_object_list_t *list)
Packit 1c819f
{
Packit 1c819f
	unsigned int		i;
Packit 1c819f
Packit 1c819f
	/*
Packit 1c819f
	 * If there are any Portal Groups in this registration, build
Packit 1c819f
	 * the relationship handle:
Packit 1c819f
	 *
Packit 1c819f
	 * 3.4
Packit 1c819f
	 * A new PG object can only be registered by referencing
Packit 1c819f
	 * its associated iSCSI Storage Node or Portal object.
Packit 1c819f
	 * A pre-existing PG object can be modified or queried
Packit 1c819f
	 * by using its Portal Group Index as message key, or
Packit 1c819f
	 * by referencing its associated iSCSI Storage Node or
Packit 1c819f
	 * Portal object.
Packit 1c819f
	 *
Packit 1c819f
	 * Implementation note: isns_db_create_pg_relation
Packit 1c819f
	 * checks whether the referenced node and portal exist,
Packit 1c819f
	 * and belong to the same entity as the PG. If this is
Packit 1c819f
	 * not the case, NULL is returned, and no relation is
Packit 1c819f
	 * defined.
Packit 1c819f
	 */
Packit 1c819f
	for (i = 0; i < list->iol_count; ++i) {
Packit 1c819f
		isns_object_t *obj = list->iol_data[i];
Packit 1c819f
		isns_object_template_t *tmpl;
Packit 1c819f
Packit 1c819f
		tmpl = obj->ie_template;
Packit 1c819f
		if (tmpl->iot_build_relation && !obj->ie_relation
Packit 1c819f
		 && !tmpl->iot_build_relation(db, obj, list)) {
Packit 1c819f
			isns_debug_protocol("Unable to build relation for new %s\n",
Packit 1c819f
					tmpl->iot_name);
Packit 1c819f
			return ISNS_INVALID_REGISTRATION;
Packit 1c819f
		}
Packit 1c819f
	}
Packit 1c819f
Packit 1c819f
	for (i = 0; i < list->iol_count; ++i) {
Packit 1c819f
		isns_object_t *obj = list->iol_data[i];
Packit 1c819f
		isns_object_template_t *tmpl;
Packit 1c819f
Packit 1c819f
		tmpl = obj->ie_template;
Packit 1c819f
		if (key_obj != obj && !obj->ie_container) {
Packit 1c819f
			if (!isns_object_attach(obj, key_obj)) {
Packit 1c819f
				/* This should not fail any longer */
Packit 1c819f
				isns_debug_protocol("Unable to attach %s %u to %s\n",
Packit 1c819f
					tmpl->iot_name, obj->ie_index,
Packit 1c819f
					key_obj->ie_template->iot_name);
Packit 1c819f
				return ISNS_INVALID_REGISTRATION;
Packit 1c819f
			}
Packit 1c819f
		}
Packit 1c819f
Packit 1c819f
		if (obj->ie_state != ISNS_OBJECT_STATE_MATURE)
Packit 1c819f
			isns_db_insert(db, obj);
Packit 1c819f
	}
Packit 1c819f
Packit 1c819f
	return ISNS_SUCCESS;
Packit 1c819f
}
Packit 1c819f
Packit 1c819f
/*
Packit 1c819f
 * Process a registration
Packit 1c819f
 */
Packit 1c819f
int
Packit 1c819f
isns_process_registration(isns_server_t *srv, isns_simple_t *call, isns_simple_t **result)
Packit 1c819f
{
Packit 1c819f
	isns_object_list_t	objects = ISNS_OBJECT_LIST_INIT;
Packit 1c819f
	isns_simple_t		*reply = NULL;
Packit 1c819f
	isns_object_t		*key_obj = NULL;
Packit 1c819f
	isns_db_t		*db = srv->is_db;
Packit 1c819f
	int			status;
Packit 1c819f
	unsigned int		i;
Packit 1c819f
Packit 1c819f
	/*
Packit 1c819f
	 * 5.6.1
Packit 1c819f
	 * For messages that change the contents of the iSNS database,
Packit 1c819f
	 * the iSNS server MUST verify that the Source Attribute
Packit 1c819f
	 * identifies either a Control Node or a Storage Node that is
Packit 1c819f
	 * a part of the Network Entity containing the added, deleted,
Packit 1c819f
	 * or modified objects.
Packit 1c819f
	 *
Packit 1c819f
	 * This check happens in isns_registration_get_key by calling
Packit 1c819f
	 * isns_policy_validate_object_access.
Packit 1c819f
	 */
Packit 1c819f
Packit 1c819f
	/* Get the key object (usually a Network Entity) */
Packit 1c819f
	status = isns_registration_get_key(call, db, &key_obj);
Packit 1c819f
	if (status)
Packit 1c819f
		goto done;
Packit 1c819f
Packit 1c819f
	/* Get the objects to register */
Packit 1c819f
	status = isns_registration_get_objects(call, db, key_obj, &objects);
Packit 1c819f
	if (status != ISNS_SUCCESS)
Packit 1c819f
		goto done;
Packit 1c819f
Packit 1c819f
	/* We parsed the request alright; all semantic checks passed.
Packit 1c819f
	 * Now insert the modified/new objects.
Packit 1c819f
	 * We do this in two passes, by first committing all nodes and
Packit 1c819f
	 * portals, and then committing the portal groups.
Packit 1c819f
	 */
Packit 1c819f
	status = isns_commit_registration(db, key_obj, &objects);
Packit 1c819f
	if (status != ISNS_SUCCESS)
Packit 1c819f
		goto done;
Packit 1c819f
Packit 1c819f
	/* The client may have registered a bunch of storage nodes,
Packit 1c819f
	 * and created an entity in the process. However, there's the
Packit 1c819f
	 * odd chance that the source node name it used was not
Packit 1c819f
	 * registered. However, we need to be able to later find
Packit 1c819f
	 * the entity it registered based on its source name.
Packit 1c819f
	 * So we implicitly create a dummy storage node with the given
Packit 1c819f
	 * source name and attach it.
Packit 1c819f
	 */
Packit 1c819f
#if 1
Packit 1c819f
	if (ISNS_IS_ENTITY(key_obj)
Packit 1c819f
	 && !isns_source_set_node(call->is_source, db)) {
Packit 1c819f
		isns_attr_list_t attrs = ISNS_ATTR_LIST_INIT;
Packit 1c819f
		isns_source_t *source = call->is_source;
Packit 1c819f
		isns_object_t *obj;
Packit 1c819f
Packit 1c819f
		isns_attr_list_append_attr(&attrs, isns_source_attr(source));
Packit 1c819f
		isns_attr_list_append_uint32(&attrs, 
Packit 1c819f
				ISNS_TAG_ISCSI_NODE_TYPE,
Packit 1c819f
				0);
Packit 1c819f
		obj = isns_create_object(&isns_iscsi_node_template,
Packit 1c819f
				&attrs, key_obj);
Packit 1c819f
		if (obj) {
Packit 1c819f
			isns_db_insert(db, obj);
Packit 1c819f
		} else {
Packit 1c819f
			isns_warning("Unable to create dummy storage node "
Packit 1c819f
					"for source %s\n",
Packit 1c819f
					isns_source_name(source));
Packit 1c819f
		}
Packit 1c819f
		isns_attr_list_destroy(&attrs);
Packit 1c819f
	}
Packit 1c819f
#endif
Packit 1c819f
Packit 1c819f
	/*
Packit 1c819f
	 * 5.6.5.1
Packit 1c819f
	 * New PG objects are registered when an associated Portal or
Packit 1c819f
	 * iSCSI Node object is registered.  An explicit PG object
Packit 1c819f
	 * registration MAY follow a Portal or iSCSI Node object
Packit 1c819f
	 * registration in a DevAttrReg message.
Packit 1c819f
	 * [...]
Packit 1c819f
	 * If the PGT value is not included in the Storage Node or
Packit 1c819f
	 * Portal object registration, and if a PGT value was not
Packit 1c819f
	 * previously registered for the relationship, then the PGT for
Packit 1c819f
	 * the corresponding PG object SHALL be registered with a value
Packit 1c819f
	 * of 0x00000001.
Packit 1c819f
	 */
Packit 1c819f
	isns_create_registration_pgs(db, &objects);
Packit 1c819f
Packit 1c819f
	/* Success: create a new registration message, and
Packit 1c819f
	 * send it in our reply. */
Packit 1c819f
	reply = __isns_create_registration(srv->is_source, key_obj);
Packit 1c819f
	if (reply == NULL) {
Packit 1c819f
		status = ISNS_INTERNAL_ERROR;
Packit 1c819f
		goto done;
Packit 1c819f
	}
Packit 1c819f
Packit 1c819f
	/* If the key object was modified (or created)
Packit 1c819f
	 * include it in the response.
Packit 1c819f
	 * We really ought to restrict ourselves to the
Packit 1c819f
	 * key attrs plus those that were modified by this
Packit 1c819f
	 * registration. But right now have no way of
Packit 1c819f
	 * finding out.
Packit 1c819f
	 */
Packit 1c819f
	if (key_obj->ie_flags & ISNS_OBJECT_DIRTY)
Packit 1c819f
		isns_registration_add_object(reply, key_obj);
Packit 1c819f
Packit 1c819f
	for (i = 0; i < objects.iol_count; ++i) {
Packit 1c819f
		isns_registration_add_object(reply,
Packit 1c819f
				objects.iol_data[i]);
Packit 1c819f
	}
Packit 1c819f
Packit 1c819f
Packit 1c819f
done:
Packit 1c819f
	isns_object_list_destroy(&objects);
Packit 1c819f
	isns_object_release(key_obj);
Packit 1c819f
	*result = reply;
Packit 1c819f
	return status;
Packit 1c819f
}
Packit 1c819f
Packit 1c819f
/*
Packit 1c819f
 * Extract the list of objects from the DevAttrReg response
Packit 1c819f
 */
Packit 1c819f
int
Packit 1c819f
isns_registration_response_get_objects(isns_simple_t *reg,
Packit 1c819f
		isns_object_list_t *result)
Packit 1c819f
{
Packit 1c819f
	return isns_simple_response_get_objects(reg, result);
Packit 1c819f
}