/* * When creating a portal group, it must not connect nodes and * portals from other entities. However, it is perfectly fine to * link objects in limbo. */ static inline int __isns_pg_may_relate(isns_object_t *entity, isns_object_t *subordinate) { isns_object_t *other; other = isns_object_get_entity(subordinate); return other == NULL || other == entity; }
void __isns_db_keystore_change_notify(const isns_db_event_t *ev, void *handle) { isns_db_keystore_t *store = handle; isns_object_t *obj = ev->ie_object; if (isns_object_get_entity(obj) == store->sd_control) { isns_debug_auth("DB keystore: policy data was modified\n"); store->sd_base.ic_generation++; } }
/* * Set up the SCN object. */ static isns_scn_t * isns_scn_setup(isns_scn_t *scn, isns_object_t *node) { isns_object_list_t portals = ISNS_OBJECT_LIST_INIT; isns_object_t *entity; unsigned int i; entity = isns_object_get_entity(node); if (entity == NULL || !isns_object_find_descendants(entity, &isns_portal_template, NULL, &portals)) return NULL; for (i = 0; i < portals.iol_count; ++i) { isns_object_t *portal = portals.iol_data[i]; isns_portal_info_t info; isns_scn_funnel_t *funnel; /* Extract address and SCN port from portal */ if (!isns_portal_from_object(&info, ISNS_TAG_PORTAL_IP_ADDRESS, ISNS_TAG_SCN_PORT, portal)) continue; /* We know where to send our notifications! */ if (scn == NULL) { isns_attr_t *attr; if (!isns_object_get_attr(node, ISNS_TAG_ISCSI_NAME, &attr) && !isns_object_get_attr(node, ISNS_TAG_FC_PORT_NAME_WWPN, &attr)) { isns_error("Attempt to set up SCN for strange node type\n"); return NULL; } scn = isns_calloc(1, sizeof(*scn)); scn->scn_entity = isns_object_get(entity); scn->scn_owner = isns_object_get(node); scn->scn_attr = isns_attr_get(attr); scn->scn_name = isns_strdup(attr->ia_value.iv_string); } funnel = isns_calloc(1, sizeof(*funnel)); funnel->scn_portal = info; funnel->scn_next = scn->scn_funnels; scn->scn_funnels = funnel; } isns_object_list_destroy(&portals); return scn; }
/* * Portal Group */ static isns_object_t * __isns_pg_create(const isns_attr_list_t *attrs, uint32_t pg_tag, isns_object_t *portal, isns_object_t *node) { isns_object_t *obj; obj = isns_create_object(&isns_iscsi_pg_template, attrs, isns_object_get_entity(portal)); /* * 3.4 * * Each Portal and iSCSI Storage Node registered in an Entity can * be associated using a Portal Group (PG) object. The PG Tag * (PGT), if non-NULL, indicates that the associated Portal * provides access to the associated iSCSI Storage Node in * the Entity. All Portals that have the same PGT value for * a specific iSCSI Storage Node allow coordinated access to * that node. * * 5.6.5.2 * * If the PGT of the Portal Group is not NULL, then a relationship * exists between the indicated Storage Node and Portal; if the * PGT is NULL, then no relationship exists. */ if (pg_tag != 0) { isns_object_set_uint32(obj, ISNS_TAG_PG_TAG, pg_tag); } else { /* A NULL PGT indicates that the * storage node cannot be accessed through * this portal. */ isns_object_set_nil(obj, ISNS_TAG_PG_TAG); } /* This object represents a relationship between portal and storage node. Create a relation. */ obj->ie_relation = isns_create_relation(obj, ISNS_RELATION_PORTAL_GROUP, portal, node); return obj; }
/* * Given a portal group object, create the relationship */ isns_relation_t * isns_db_build_pg_relation(isns_db_t *db, isns_object_t *pg, const isns_object_list_t *extra_objs) { isns_object_t *entity, *node = NULL, *portal = NULL; entity = isns_object_get_entity(pg); node = __isns_pg_find_node(db, pg, extra_objs); if (node == NULL) { isns_error("Trying to register PG for non-existant node\n"); goto failed; } if (!__isns_pg_may_relate(entity, node)) { isns_error("Trying to register PG for node in other entity\n"); goto failed; } portal = __isns_pg_find_portal(db, pg, extra_objs); if (portal == NULL) { isns_error("Trying to register PG for non-existant portal\n"); goto failed; } if (!__isns_pg_may_relate(entity, portal)) { isns_error("Trying to register PG for portal in other entity\n"); goto failed; } pg->ie_relation = isns_create_relation(pg, ISNS_RELATION_PORTAL_GROUP, node, portal); isns_object_release(portal); isns_object_release(node); return pg->ie_relation; failed: if (portal) isns_object_release(portal); if (node) isns_object_release(node); return NULL; }
static int isns_registration_get_next_object(isns_db_t *db, struct isns_attr_list_scanner *st, isns_object_list_t *result) { isns_object_t *current; int status, esi = 0; status = isns_attr_list_scanner_next(st); /* We get here if the registration has a trailing PGT */ if (status == ISNS_NO_SUCH_ENTRY) return ISNS_SUCCESS; if (status) return status; /* * Validate the attrlist. * This makes sure the client does not include * duplicate attributes, readonly attributes * such as Registration Timestamp, Index and Next Index, * or privileged data (such as marking a storage node as * control node). */ status = isns_attr_list_validate(&st->attrs, st->policy, ISNS_DEVICE_ATTRIBUTE_REGISTER); if (status) { isns_debug_protocol("invalid attr in message\n"); return status; } /* * 6.3.4. Entity Status Inquiry Interval * * If the iSNS server is unable to support ESI messages * or the ESI Interval requested, it SHALL [...] reject * the ESI request by returning an "ESI Not Available" * Status Code [...] * * Implementer's note: In section 5.7.5.1, the RFC talks * about modifying the requested ESI interval; so it seems * it's okay to be liberal about the ESI intervals we accept, * and update them quietly. */ if (isns_attr_list_contains(&st->attrs, ISNS_TAG_ESI_PORT)) { if (!isns_esi_enabled) { isns_debug_esi("Refusing to accept portal " "registration with ESI port\n"); return ISNS_ESI_NOT_AVAILABLE; } esi = 1; } /* * Override any registration period specified by the client. */ if (isns_attr_list_contains(&st->attrs, ISNS_TAG_REGISTRATION_PERIOD)) { isns_value_t value = ISNS_VALUE_INIT(uint32, isns_config.ic_registration_period); isns_attr_list_update_value(&st->attrs, ISNS_TAG_REGISTRATION_PERIOD, NULL, &value); } if (st->tmpl == &isns_entity_template) { /* * 5.6.5.1. * A maximum of one Network Entity object can be * created or updated with a single DevAttrReg * message. Consequently, the Operating Attributes * MUST NOT contain more than one Network Entity * object. */ if (st->entities++) { isns_debug_protocol("More than one entity in DevAttrReg msg\n"); return ISNS_INVALID_REGISTRATION; } /* This should be the key object. * The EID specified by by the client may be * empty, so don't overwrite the value we * assigned with something else. */ if (!isns_object_match(st->key_obj, &st->keys)) { isns_debug_protocol("Entity mismatch in message vs. operating attrs\n"); return ISNS_INVALID_REGISTRATION; } current = isns_object_get(st->key_obj); } else if (st->tmpl == &isns_dd_template || st->tmpl == &isns_ddset_template) { isns_debug_protocol("DevAttrReg of type %s not allowed\n", st->tmpl->iot_name); return ISNS_INVALID_REGISTRATION; } else { /* This will also catch objects in limbo. */ current = isns_db_lookup(db, st->tmpl, &st->keys); } if (current != NULL) { /* * If the replace bit is not set, then the message updates * the attributes of the object identified by the Message Key * and its subordinate objects. Existing object containment * relationships MUST NOT be changed. For existing objects, * key attributes MUST NOT be modified, but new subordinate * objects MAY be added. */ /* * [...] * If the Node identified by the Source Attribute is * not a Control Node, then the objects in the operating * attributes MUST be members of the same Network Entity * as the Source Node. */ if (!isns_policy_validate_object_update(st->policy, st->source, current, &st->attrs, ISNS_DEVICE_ATTRIBUTE_REGISTER)) { isns_object_release(current); return ISNS_SOURCE_UNAUTHORIZED; } /* We shouldn't allow messages affecting one Entity * to modify objects owned by a different Entity. * * However, there may be orphan objects (created * while populating discovery domains). These will * not be associated with any Network Entity, so * they're up for grabs. */ if (st->key_obj == current || st->key_obj == current->ie_container) { /* All is well. The current object is the * key object itself, or a direct descendant of the * key object. */ /* FIXME: with FC we can get deeper nesting; * this needs work. */ } else if (!isns_object_is_valid_container(st->key_obj, st->tmpl)) { isns_error("Client attempts to add %s object to a %s - tsk tsk.\n", st->tmpl->iot_name, st->key_obj->ie_template->iot_name); goto invalid_registration; } else if (current->ie_container) { /* We shouldn't get here in authenticated mode, * but in insecure mode we still may. */ isns_error("Client attempts to move %s %u to a different %s\n", current->ie_template->iot_name, current->ie_index, st->key_obj->ie_template->iot_name); goto invalid_registration; } } else { if (!isns_object_is_valid_container(st->key_obj, st->tmpl)) { isns_error("Client attempts to add %s object to a %s - tsk tsk.\n", st->tmpl->iot_name, st->key_obj->ie_template->iot_name); goto invalid_registration; } if (!isns_policy_validate_object_creation(st->policy, st->source, st->tmpl, &st->keys, &st->attrs, ISNS_DEVICE_ATTRIBUTE_REGISTER)) { return ISNS_SOURCE_UNAUTHORIZED; } current = isns_create_object(st->tmpl, &st->keys, isns_object_get_entity(st->key_obj)); /* We do not insert the new object into the database yet. * That happens after we're done with parsing *all* * objects. */ } if (!isns_object_set_attrlist(current, &st->attrs)) { isns_debug_state("Error updating object's attrlist\n"); isns_object_release(current); return ISNS_INTERNAL_ERROR; } /* If the client specifies an ESI port, make sure the * ESI interval is set and within bounds. */ if (esi) { uint32_t esi_interval; if (!isns_object_get_uint32(current, ISNS_TAG_ESI_INTERVAL, &esi_interval)) { esi_interval = isns_config.ic_esi_min_interval; } else if (esi_interval < isns_config.ic_esi_min_interval) { esi_interval = isns_config.ic_esi_min_interval; } else if (esi_interval > isns_config.ic_esi_max_interval) { esi_interval = isns_config.ic_esi_max_interval; } else { esi_interval = 0; } if (esi_interval) isns_object_set_uint32(current, ISNS_TAG_ESI_INTERVAL, esi_interval); } /* Append it to the result list. * We do not return the key object, otherwise * we end up putting it into the response twice. */ if (current != st->key_obj) isns_object_list_append(result, current); /* * When a Portal is registered, the Portal attributes MAY immediately be * followed by a PGT attribute. * [...] * When an iSCSI Storage Node is registered, the Storage Node attributes * MAY immediately be followed by a PGT attribute. */ if (st->tmpl == &isns_portal_template || st->tmpl == &isns_iscsi_node_template) { st->pgt_next_attr = ISNS_TAG_PG_TAG; st->pgt_base_object = current; } else if (st->tmpl != &isns_iscsi_pg_template) { st->pgt_next_attr = 0; st->pgt_base_object = NULL; } isns_object_release(current); return ISNS_SUCCESS; invalid_registration: if (current) isns_object_release(current); return ISNS_INVALID_REGISTRATION; }
/* * 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; }