isns_object_t * isns_create_storage_node2(const isns_source_t *source, uint32_t type, isns_object_t *parent) { isns_attr_t *name_attr; isns_object_t *obj; if (parent && !ISNS_IS_ENTITY(parent)) { isns_warning("Invalid container type \"%s\" for storage node: " "should be \"%s\"\n", parent->ie_template->iot_name, isns_entity_template.iot_name); return NULL; } if ((name_attr = isns_source_attr(source)) == NULL) { isns_warning("No source attribute\n"); return NULL; } if (name_attr->ia_tag_id == ISNS_TAG_ISCSI_NAME) { obj = isns_create_object(&isns_iscsi_node_template, NULL, parent); isns_attr_list_update_attr(&obj->ie_attrs, name_attr); isns_object_set_uint32(obj, ISNS_TAG_ISCSI_NODE_TYPE, type); } else { /* No iFCP yet, sorry */ isns_warning("%s: source tag type %u not supported\n", __FUNCTION__); return NULL; } return obj; }
isns_object_t * isns_create_storage_node(const char *name, uint32_t type, isns_object_t *parent) { isns_object_t *obj; if (parent && !ISNS_IS_ENTITY(parent)) { isns_warning("Invalid container type \"%s\" for storage node: " "should be \"%s\"\n", parent->ie_template->iot_name, isns_entity_template.iot_name); return NULL; } obj = isns_create_object(&isns_iscsi_node_template, NULL, parent); isns_object_set_string(obj, ISNS_TAG_ISCSI_NAME, name); isns_object_set_uint32(obj, ISNS_TAG_ISCSI_NODE_TYPE, type); return obj; }
/* * 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; }
/* * This function is invoked whenever someone changes the * database. * * SCNs are another area where the RFC is fabulously wishy washy. * It is not entirely clear when DD/DDS information should be * included in a management SCN - one *reasonable* interpretation * would be that this happens for DDReg/DDDereg/DDSReg/DDSDereg * events only. But some sections make it sound as if DD * information is included for all management SCNs. */ void isns_scn_callback(const isns_db_event_t *ev, void *ptr) { isns_object_t *obj = ev->ie_object; isns_scn_t *scn, **pos; isns_attr_t *timestamp; uint32_t node_type; /* Never send out notifications for policy objects and the like. */ if (obj->ie_flags & ISNS_OBJECT_PRIVATE) return; /* When an entity is nuked, remove all SCNs to nodes * that registered from there */ if (ISNS_IS_ENTITY(obj) && (ev->ie_bits & ISNS_SCN_OBJECT_REMOVED_MASK)) { pos = &isns_scn_list; while ((scn = *pos) != NULL) { if (scn->scn_entity != obj) { pos = &scn->scn_next; continue; } isns_debug_scn("Deleting SCN registration for %s\n", scn->scn_name); *pos = scn->scn_next; isns_scn_free(scn); } return; } /* For now we handle iSCSI nodes only. Maybe later we'll * do iFC nodes as well. */ if (!ISNS_IS_ISCSI_NODE(obj)) return; if (!isns_object_get_uint32(obj, ISNS_TAG_ISCSI_NODE_TYPE, &node_type)) return; if (ev->ie_recipient) { isns_object_t *dst = ev->ie_recipient; isns_debug_scn("SCN unicast <%s %u, %s> -> %s %u\n", obj->ie_template->iot_name, obj->ie_index, isns_event_string(ev->ie_bits), dst->ie_template->iot_name, dst->ie_index); } else { isns_debug_scn("SCN multicast <%s %u, %s>\n", obj->ie_template->iot_name, obj->ie_index, isns_event_string(ev->ie_bits)); } timestamp = isns_create_timestamp_attr(); pos = &isns_scn_list; while ((scn = *pos) != NULL) { unsigned int scn_bits, management; isns_object_t *recipient, *dd = NULL; isns_simple_t *call; recipient = scn->scn_owner; /* Check if the node has gone away completely. */ if (recipient->ie_scn_mask == 0) { *pos = scn->scn_next; isns_scn_free(scn); continue; } if (recipient->ie_container == NULL) { isns_warning("Internal bug - SCN recipient without container\n"); /* Clear the bitmask and loop over - this will remove it */ recipient->ie_scn_mask = 0; continue; } /* See if portals were added/removed. * This does not catch updates that modified *just* * the SCN port */ if (recipient->ie_container->ie_mtime != scn->scn_last_update) { /* Rebuild the list of SCN portals */ isns_scn_release_funnels(scn); scn->scn_last_update = 0; } pos = &scn->scn_next; /* Check for unicast events (triggered for DD addition/removal). * For unicast events, we do not mask the SCN bits, so that * clients who have registered for non-management events * will see the membership events for their DDs nevertheless. */ if (ev->ie_recipient == NULL) { scn_bits = ev->ie_bits & recipient->ie_scn_mask; if (scn_bits == 0) continue; /* Management SCNs should not be delivered to nodes * that have not registered for them. */ if ((ev->ie_bits & ISNS_SCN_MANAGEMENT_REGISTRATION_MASK) && !(recipient->ie_scn_mask & ISNS_SCN_MANAGEMENT_REGISTRATION_MASK)) continue; } else if (recipient == ev->ie_recipient) { scn_bits = ev->ie_bits; } else { /* No match, skip this recipient */ continue; } if (scn->scn_last_update == 0) { scn->scn_last_update = recipient->ie_container->ie_mtime; isns_scn_setup(scn, recipient); } /* We check for SCN capable portals when processing * the SCN registration. But the portals may go away * in the meantime. */ if (scn->scn_funnels == NULL) continue; /* Check SCN bitmask. This will modify the event bits. */ scn_bits = isns_scn_match(scn, scn_bits, obj, node_type); if (scn_bits == 0) continue; management = !!(scn_bits & ISNS_SCN_MANAGEMENT_REGISTRATION_MASK); /* * 2.2.3 * A regular SCN registration indicates that the * Discovery Domain Service SHALL be used to control the * distribution of SCN messages. Receipt of regular * SCNs is limited to the discovery domains in which * the SCN-triggering event takes place. Regular SCNs * do not contain information about discovery domains. * * Implementer's note: We override check for unicast events. * The reason is that DDDereg will sever the * relationship, and we would never send an SCN for that * event. */ if (!management && !ev->ie_recipient) { if (!isns_object_test_visibility(obj, recipient)) continue; } isns_debug_scn("preparing to send SCN to %s\n", scn->scn_name); if ((call = scn->scn_message) == NULL) { call = isns_create_scn(isns_scn_server->is_source, scn->scn_attr, timestamp); if (call == NULL) continue; scn->scn_message = call; } /* * If the SCN is a Management SCN, then the SCN message * SHALL also list the DD_ID and/or DDS_ID of the * Discovery Domains and Discovery Domain Sets (if any) * that caused the change in state for that Storage Node. * These additional attributes (i.e., DD_ID and/or DDS_ID) * shall immediately follow the iSCSI Name or FC Port * Name and precede the next SCN bitmap for the next * notification message (if any). */ if (management && ev->ie_trigger) dd = ev->ie_trigger; isns_scn_add_event(call, scn_bits, obj, dd); } isns_attr_release(timestamp); }