/* * Get the key object given in this message * * It doesn't say anywhere explicitly in the RFC, but * the message key can contain both key and non-key * attributes. For instance, you can search by * Portal Group Index (section 3.4). */ static int isns_registration_get_key(isns_simple_t *reg, isns_db_t *db, isns_object_t **key_obj) { isns_attr_list_t *keys = ®->is_message_attrs; isns_attr_list_t dummy_keys = ISNS_ATTR_LIST_INIT; isns_attr_t *attr; isns_object_t *obj = NULL; const char *eid = NULL; char eidbuf[128]; int status = ISNS_SUCCESS; int obj_must_exist = 0; /* * 5.6.5.1 * If the Message Key is not present, then the DevAttrReg message * implicitly registers a new Network Entity. In this case, * the replace bit SHALL be ignored; a new Network Entity SHALL * be created. * * Note that some clients seem to leave the message key * empty, but hide the entity identifier in the operating * attrs. */ if (keys->ial_count != 0) { attr = keys->ial_data[0]; /* * 5.6.5.1 * If the Message Key does not contain an EID, and no * pre-existing objects match the Message Key, then the * DevAttrReg message SHALL be rejected with a status * code of 3 (Invalid Registration). */ if (keys->ial_count != 1 || attr->ia_tag_id != ISNS_TAG_ENTITY_IDENTIFIER) obj_must_exist = 1; } else { /* Empty message key. But the client may have hidden * the EID in the operating attrs :-/ */ if (reg->is_operating_attrs.ial_count == 0) goto create_entity; attr = reg->is_operating_attrs.ial_data[0]; if (attr->ia_tag_id != ISNS_TAG_ENTITY_IDENTIFIER) goto create_entity; isns_attr_list_append_attr(&dummy_keys, attr); keys = &dummy_keys; } /* If the caller specifies an EID, extract it while * we know what we're doing :-) */ if (attr->ia_tag_id == ISNS_TAG_ENTITY_IDENTIFIER && ISNS_ATTR_IS_STRING(attr)) eid = attr->ia_value.iv_string; /* Look up the object identified by the keys. * We do not scope the lookup, as the client * may want to add nodes to an entity that's * currently empty - and hence not visible to * any DD. */ if (!ISNS_ATTR_IS_NIL(attr)) obj = isns_db_lookup(db, NULL, keys); if (obj == NULL && obj_must_exist) goto err_invalid; if (obj != NULL) { /* * Policy: verify that the client is permitted * to access this object. * * This includes * - the client node must be the object owner, * or a control node. * - the policy must allow modification of * this object type. */ if (!isns_policy_validate_object_access(reg->is_policy, reg->is_source, obj, reg->is_function)) goto err_unauthorized; found_object: if (reg->is_replace) { isns_object_t *container = NULL; if (!ISNS_IS_ENTITY(obj)) { container = isns_object_get_entity(obj); if (container == NULL) { isns_error("Trying to replace %s (id %u) " "which has no container\n", obj->ie_template->iot_name, obj->ie_index); goto err_invalid; } } isns_debug_state("Replacing %s (id %u)\n", obj->ie_template->iot_name, obj->ie_index); isns_db_remove(db, obj); isns_object_release(obj); /* Purge the deleted objects from the database now */ isns_db_purge(db); /* We need to flush pending SCNs because the * objects may be resurrected from limbo, * and we might be looking at stale data. */ isns_scn_transmit_all(); /* It's an entity. Nuke it and create * a new one. */ if (container == NULL) { isns_source_set_entity(reg->is_source, NULL); goto create_entity; } obj = isns_object_get(container); } goto out; } /* * If the Message Key contains an EID and no pre-existing objects * match the Message Key, then the DevAttrReg message SHALL create a * new Entity with the specified EID and any new object(s) specified * by the Operating Attributes. The replace bit SHALL be ignored. * * Implementer's note: the EID attribute may be empty, in which case * we also create a new entity. */ create_entity: if (!isns_policy_validate_object_creation(reg->is_policy, reg->is_source, &isns_entity_template, keys, NULL, reg->is_function)) goto err_unauthorized; /* * 5.6.5.1 * A registration message that creates a new Network Entity object * MUST contain at least one Portal or one Storage Node. If the * message does not, then it SHALL be considered invalid and result * in a response with Status Code of 3 (Invalid Registration). */ /* FIXME: Implement this check */ /* We try to play nice with lazy clients and attempt to * look up the network entity given the source name. * But we don't do this if a non-NULL EID was given, * because the client may explicitly want to specify more * than one Network Entity. */ if (eid == NULL) { obj = reg->is_source->is_entity; if (obj != NULL) { isns_object_get(obj); goto found_object; } /* The policy may define a default entity name. * If that is the case, use it. */ eid = isns_policy_default_entity(reg->is_policy); if (eid) { obj = isns_db_vlookup(db, &isns_entity_template, ISNS_TAG_ENTITY_IDENTIFIER, eid, 0); if (obj) { reg->is_source->is_entity = isns_object_get(obj); goto found_object; } } } /* * 5.6.5.1 * If the Message Key and Operating Attributes do not contain * an EID attribute, or if the EID attribute has a length of 0, * then a new Network Entity object SHALL be created and the iSNS * server SHALL supply a unique EID value for it. */ if (eid == NULL) eid = isns_db_generate_eid(db, eidbuf, sizeof(eidbuf)); /* * 6.2.2. Entity Protocol * * This attribute is required during initial registration of * the Network Entity. * * Implementer's note: we don't rely on this. Instead, the * Entity Protocol is selected based on the source type. * If the client specifies the protocol, the auto-selected * value is overwritten. */ obj = isns_create_entity_for_source(reg->is_source, eid); if (obj == NULL) goto err_invalid; isns_source_set_entity(reg->is_source, obj); /* * 6.2.6 * If a Registration Period is not requested by the iSNS * client and Entity Status Inquiry (ESI) messages are not * enabled for that client, then the Registration Period * SHALL be set to a non-zero value by the iSNS server. * This implementation-specific value for the Registration * Period SHALL be returned in the registration response to the * iSNS client. The Registration Period may be set to zero, * indicating its non-use, only if ESI messages are enabled for * that Network Entity. * * Implementer's note: we diverge from this in two ways: * - the admin may choose to disable registration timeout, * by setting RegistrationPeriod=0 in the config file * * - When a new entity is created, we always set the * registration interval because we cannot know yet * whether the client will subsequently enable ESI or * not. * * - The control entity (holding policy objects) will * not expire. */ if (isns_config.ic_registration_period && strcasecmp(eid, ISNS_ENTITY_CONTROL)) { isns_object_set_uint32(obj, ISNS_TAG_REGISTRATION_PERIOD, isns_config.ic_registration_period); isns_object_set_uint64(obj, ISNS_TAG_TIMESTAMP, time(NULL)); } /* Insert into database, and set the object's owner */ isns_db_insert(db, obj); reg->is_replace = 0; out: *key_obj = obj; isns_attr_list_destroy(&dummy_keys); return ISNS_SUCCESS; error: if (obj) isns_object_release(obj); isns_attr_list_destroy(&dummy_keys); return status; err_unauthorized: status = ISNS_SOURCE_UNAUTHORIZED; goto error; err_invalid: status = ISNS_INVALID_REGISTRATION; goto error; }
/* * Process a SCNDereg message */ int isns_process_scn_deregistration(isns_server_t *srv, isns_simple_t *call, isns_simple_t **result) { isns_attr_list_t *keys = &call->is_message_attrs; isns_db_t *db = srv->is_db; isns_attr_t *attr; isns_object_t *node = NULL; int status = ISNS_SUCCESS; /* * 5.6.5.6 * The SCNDereg request message PDU Payload contains a Source Attribute * and Message Key Attribute(s). Valid Message Key Attributes for a * SCNDereg are shown below: * * Valid Message Key Attributes for SCNDereg * ----------------------------------------- * iSCSI Name * FC Port Name WWPN * * There are no Operating Attributes in the SCNDereg message. */ if (keys->ial_count != 1) return ISNS_SCN_REGISTRATION_REJECTED; attr = keys->ial_data[0]; if (attr->ia_tag_id != ISNS_TAG_ISCSI_NAME && attr->ia_tag_id != ISNS_TAG_FC_PORT_NAME_WWPN) return ISNS_SCN_REGISTRATION_REJECTED; /* Look up the storage node for this source. If it does * not exist, reject the message. */ node = isns_db_lookup(db, NULL, keys); if (node == NULL) return ISNS_SUCCESS; /* * Policy: verify that the client is permitted * to access this entity. * * This includes * - the client node must be the object owner, * or a control node. * - the policy must allow monitoring of * this object type. */ if (!isns_policy_validate_object_access(call->is_policy, call->is_source, node, call->is_function)) goto unauthorized; isns_object_set_scn_mask(node, 0); isns_scn_delete_scn(node); *result = isns_simple_create(ISNS_SCN_DEREGISTER, srv->is_source, NULL); status = ISNS_SUCCESS; out: if (node) isns_object_release(node); return status; unauthorized: status = ISNS_SOURCE_UNAUTHORIZED; goto out; }
/* * Get the list of objects matching this query */ static int isns_query_get_objects(isns_simple_t *qry, isns_db_t *db, isns_object_list_t *result) { isns_scope_t *scope = NULL; isns_object_list_t matching = ISNS_OBJECT_LIST_INIT; isns_attr_list_t *keys = &qry->is_message_attrs; isns_object_template_t *query_type = NULL; unsigned int i, qry_mask = 0; int status; /* 5.6.5.2 * If multiple attributes are used as the Message Key, then they * MUST all be from the same object type (e.g., IP address and * TCP/UDP Port are attributes of the Portal object type). */ for (i = 0; i < keys->ial_count; ++i) { isns_object_template_t *tmpl; uint32_t tag = keys->ial_data[i]->ia_tag_id; tmpl = isns_object_template_for_tag(tag); if (tmpl == NULL) return ISNS_ATTRIBUTE_NOT_IMPLEMENTED; if (query_type == NULL) query_type = tmpl; else if (tmpl != query_type) return ISNS_INVALID_QUERY; } /* * 5.6.5.2 * An empty Message Key field indicates the query is scoped to * the entire database accessible by the source Node. */ if (keys->ial_count == 0) { query_type = &isns_entity_template; keys = NULL; } /* Policy: check whether the client is allowed to * query this type of object. */ if (!isns_policy_validate_object_type(qry->is_policy, query_type, qry->is_function)) return ISNS_SOURCE_UNAUTHORIZED; /* No scope means that the source is not part of * any discovery domain, and there's no default DD. * Just return an empty reply. */ scope = isns_scope_for_call(db, qry); if (scope == NULL) return ISNS_SUCCESS; status = isns_scope_gang_lookup(scope, query_type, keys, &matching); if (status != ISNS_SUCCESS) goto out; /* Extract the mask of requested objects */ qry_mask = isns_query_get_requested_types(&qry->is_operating_attrs); /* * 5.6.5.2 * The DevAttrQry response message returns attributes of objects * listed in the Operating Attributes that are related to the * Message Key of the original DevAttrQry message. */ for (i = 0; i < matching.iol_count; ++i) { isns_object_t *obj = matching.iol_data[i]; if (!isns_policy_validate_object_access(qry->is_policy, qry->is_source, obj, qry->is_function)) continue; if (obj->ie_container) isns_object_list_append(result, obj->ie_container); isns_object_list_append(result, obj); isns_scope_get_related(scope, obj, qry_mask, result); } out: isns_object_list_destroy(&matching); isns_scope_release(scope); return status; }
/* * Process a SCN registration */ int isns_process_scn_register(isns_server_t *srv, isns_simple_t *call, isns_simple_t **result) { isns_attr_list_t *keys = &call->is_message_attrs; isns_attr_list_t *attrs = &call->is_operating_attrs; isns_db_t *db = srv->is_db; isns_attr_t *attr; isns_object_t *node = NULL; uint32_t scn_bitmap; isns_scn_t *scn; int status = ISNS_SUCCESS; /* * 5.6.5.5 * The SCNReg request PDU Payload contains a Source Attribute, a Message * Key Attribute, and an Operating Attribute. Valid Message Key * Attributes for a SCNReg are shown below: * * Valid Message Key Attributes for SCNReg * --------------------------------------- * iSCSI Name * FC Port Name WWPN */ if (keys->ial_count != 1 || attrs->ial_count != 1) return ISNS_SCN_REGISTRATION_REJECTED; attr = keys->ial_data[0]; if (attr->ia_tag_id != ISNS_TAG_ISCSI_NAME && attr->ia_tag_id != ISNS_TAG_FC_PORT_NAME_WWPN) return ISNS_SCN_REGISTRATION_REJECTED; /* Look up the storage node for this source. If it does * not exist, reject the message. */ node = isns_db_lookup(db, NULL, keys); if (node == NULL) return ISNS_SOURCE_UNKNOWN; /* * Policy: verify that the client is permitted * to access this entity. * * This includes * - the client node must be the object owner, * or a control node. * - the policy must allow monitoring of * this object type. */ if (!isns_policy_validate_object_access(call->is_policy, call->is_source, node, call->is_function)) goto unauthorized; /* * 5.6.5.5 * The SCN Bitmap is the only operating attribute of this message * [...] * Control Nodes MAY conduct registrations for management SCNs; * iSNS clients that are not supporting Control Nodes MUST NOT * conduct registrations for management SCNs. * * Implementer's note: for iFCP sources, we should check for * ISNS_TAG_IFCP_SCN_BITMAP. */ attr = attrs->ial_data[0]; if (attr->ia_tag_id != ISNS_TAG_ISCSI_SCN_BITMAP || !ISNS_ATTR_IS_UINT32(attr)) goto rejected; scn_bitmap = attr->ia_value.iv_uint32; if (!isns_policy_validate_scn_bitmap(call->is_policy, scn_bitmap)) goto unauthorized; /* * 5.6.5.5 * If no SCN Port fields of any Portals of the Storage Node are * registered to receive SCN messages, then the SCNReg message SHALL * be rejected with Status Code 17 (SCN Registration Rejected). */ if (!(scn = isns_scn_create_scn(node, scn_bitmap, db))) goto rejected; *result = isns_simple_create(ISNS_SCN_REGISTER, srv->is_source, NULL); status = ISNS_SUCCESS; out: if (node) isns_object_release(node); return status; rejected: status = ISNS_SCN_REGISTRATION_REJECTED; goto out; unauthorized: status = ISNS_SOURCE_UNAUTHORIZED; goto out; }
/* * Process a deregistration * * Normally, you would expect that a deregistration removes the * object from the database, and that's the end of the story. * Unfortunately, someone added Discovery Domains to the protocol, * requiring _some_ information to survive as long as an object * is referenced by a discovery domain. Specifically, we need to * retain the relationship between key attributes (eg iscsi node * name) and the object index. * * Thus, deregistration consists of the following steps * - the object is removed from the database's global scope, * so that it's no longer visible to DB lookups. * * - the object is detached from its containing Network * Entity. * * - all attributes except the key attr(s) and the index * attribute are removed. */ int isns_process_deregistration(isns_server_t *srv, isns_simple_t *call, isns_simple_t **result) { isns_object_list_t objects = ISNS_OBJECT_LIST_INIT; isns_simple_t *reply = NULL; isns_db_t *db = srv->is_db; int status, dereg_status; unsigned int i; /* Get the objects to deregister */ status = isns_deregistration_get_objects(call, db, &objects); if (status != ISNS_SUCCESS) goto done; /* * 5.6.5.4 * * For messages that change the contents of the iSNS database, * the iSNS server MUST verify that the Source Attribute * identifies either a Control Node or a Storage Node that is * a part of the Network Entity containing the added, deleted, * or modified objects. */ /* * Implementation note: this can be implemented either by * explicitly checking the object's owner in isns_db_remove * (which is what we do right now), or by matching only * those objects that have the right owner anyway. * * The latter sounds like a better choice if the client * uses NIL attributes, because it limits the scope of * the operation; but then the RFC doesn't say whether * this kind of deregistration would be valid at all. */ /* Success: create a new simple message, and * send it in our reply. */ reply = __isns_create_deregistration(srv->is_source, NULL); if (reply == NULL) { status = ISNS_INTERNAL_ERROR; goto done; } dereg_status = ISNS_SUCCESS; for (i = 0; i < objects.iol_count; ++i) { isns_object_t *obj = objects.iol_data[i]; /* Policy: check that the client is permitted * to deregister this object */ if (!isns_policy_validate_object_access(call->is_policy, call->is_source, obj, call->is_function)) status = ISNS_SOURCE_UNAUTHORIZED; if (status == ISNS_SUCCESS) status = isns_db_remove(db, obj); if (status != ISNS_SUCCESS) { /* * 5.7.5.4 * * In the event of an error, this response message * contains the appropriate status code as well * as a list of objects from the original DevDereg * message that were not successfully deregistered * from the iSNS database. This list of objects * is contained in the Operating Attributes * of the DevDeregRsp message. Note that an * attempted deregistration of a non-existent * object does not constitute an isns_error, and * non-existent entries SHALL not be returned * in the DevDeregRsp message. */ /* * Implementation: right now this doesn't work * at all, because isns_msg_set_error will * discard the entire message except for the * status word. */ isns_debug_message("Failed to deregister object: %s (0x%04x)\n", isns_strerror(status), status); isns_object_extract_all(obj, &reply->is_operating_attrs); dereg_status = status; continue; } /* * 5.7.5.4 * If all Nodes and Portals associated with a Network * Entity are deregistered, then the Network Entity * SHALL also be removed. * [...] * If both the Portal and iSCSI Storage Node objects * associated with a Portal Group object are removed, * then that Portal Group object SHALL also be removed. * The Portal Group object SHALL remain registered * as long as either of its associated Portal or * iSCSI Storage Node objects remain registered. If a * deleted Storage Node or Portal object is subsequently * re-registered, then a relationship between the re- * registered object and an existing Portal or Storage * Node object registration, indicated by the PG object, * SHALL be restored. */ /* isns_db_remove takes care of removing dead entities, * and dead portal groups. */ } if (status == ISNS_SUCCESS) status = dereg_status; done: isns_object_list_destroy(&objects); *result = reply; return status; }