static isns_policy_t * __isns_db_keystore_get_policy(isns_keystore_t *store_base, const char *name, size_t namelen) { isns_db_keystore_t *store = (isns_db_keystore_t *) store_base; isns_policy_t *policy; isns_object_t *obj; uint32_t intval; obj = __isns_db_keystore_lookup(store, name, namelen); if (obj == NULL) return NULL; policy = __isns_policy_alloc(name, namelen); /* retrieve policy bits from object */ #if 0 __isns_db_keystore_copy_policy_string(obj, OPENISNS_TAG_POLICY_SOURCE_NAME, &policy->ip_source); #endif __isns_db_keystore_copy_policy_string(obj, OPENISNS_TAG_POLICY_ENTITY, &policy->ip_entity); __isns_db_keystore_copy_policy_string(obj, OPENISNS_TAG_POLICY_DEFAULT_DD, &policy->ip_dd_default); __isns_db_keystore_copy_policy_strings(obj, OPENISNS_TAG_POLICY_NODE_NAME, &policy->ip_node_names); if (isns_object_get_uint32(obj, OPENISNS_TAG_POLICY_OBJECT_TYPE, &intval)) policy->ip_object_types = intval; if (isns_object_get_uint32(obj, OPENISNS_TAG_POLICY_NODE_TYPE, &intval)) policy->ip_node_types = intval; if (isns_object_get_uint32(obj, OPENISNS_TAG_POLICY_FUNCTIONS, &intval)) policy->ip_functions = intval; return policy; }
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; }
/* * 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); }