diff options
Diffstat (limited to 'utils/open-isns/dd.c')
-rw-r--r-- | utils/open-isns/dd.c | 1306 |
1 files changed, 0 insertions, 1306 deletions
diff --git a/utils/open-isns/dd.c b/utils/open-isns/dd.c deleted file mode 100644 index c2dcd10..0000000 --- a/utils/open-isns/dd.c +++ /dev/null @@ -1,1306 +0,0 @@ -/* - * Handle DD registration/deregistration - * - * Discovery domains are weird, even in the context of - * iSNS. For once thing, all other objects have unique - * attributes; DDs attributes can appear several times. - * They should really have made each DD member an object - * in its own right. - * - * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com> - */ - -#include <stdlib.h> -#include <string.h> -#include "isns.h" -#include "attrs.h" -#include "objects.h" -#include "message.h" -#include "security.h" -#include "util.h" -#include "db.h" - -#define DD_DEBUG - -enum { - ISNS_DD_MEMBER_ISCSI_NODE = 1, - ISNS_DD_MEMBER_IFCP_NODE, - ISNS_DD_MEMBER_PORTAL, -}; -/* Must be zero/one: */ -enum { - NOTIFY_MEMBER_ADDED = 0, - NOTIFY_MEMBER_REMOVED = 1 -}; - -typedef struct isns_dd isns_dd_t; -typedef struct isns_dd_list isns_dd_list_t; -typedef struct isns_dd_member isns_dd_member_t; - -struct isns_dd { - uint32_t dd_id; - char * dd_name; - uint32_t dd_features; - isns_dd_member_t * dd_members; - - unsigned int dd_inserted : 1; - - isns_object_t * dd_object; -}; - -struct isns_dd_member { - isns_dd_member_t * ddm_next; - unsigned int ddm_type; - isns_object_ref_t ddm_object; - - unsigned int ddm_added : 1; - union { - uint32_t ddm_index; - - /* Index must be first in all structs below. - * Yeah, I know. Aliasing is bad. */ - struct isns_dd_portal { - uint32_t index; - isns_portal_info_t info; - } ddm_portal; - struct isns_dd_iscsi_node { - uint32_t index; - char * name; - } ddm_iscsi_node; - struct isns_dd_ifcp_node { - uint32_t index; - char * name; - } ddm_ifcp_node; - }; -}; - -struct isns_dd_list { - unsigned int ddl_count; - isns_dd_t ** ddl_data; -}; - -/* - * List of all discovery domains. - * This duplicates the DD information from the database, - * but unfortunately this can't be helped - we need to - * have fast algorithms to compute the membership of a - * node, and the relative visibility of two nodes. - */ -static int isns_dd_list_initialized = 0; -static isns_dd_list_t isns_dd_list; -static uint32_t isns_dd_next_id = 1; - -static isns_dd_t * isns_dd_alloc(void); -static isns_dd_t * isns_dd_clone(const isns_dd_t *); -static void isns_dd_release(isns_dd_t *); -static int isns_dd_parse_attrs(isns_dd_t *, - isns_db_t *, const isns_attr_list_t *, - const isns_dd_t *, int); -static int isns_dd_remove_members(isns_dd_t *, - isns_db_t *, - isns_dd_t *); -static void isns_dd_notify(const isns_dd_t *, - isns_dd_member_t *, - isns_dd_member_t *, - int); -static void isns_dd_add_members(isns_dd_t *, - isns_db_t *, - isns_dd_t *); -static void isns_dd_store(isns_db_t *, const isns_dd_t *, int); -static void isns_dd_destroy(isns_db_t *, isns_dd_t *); -static void isns_dd_insert(isns_dd_t *); -static isns_dd_t * isns_dd_by_id(uint32_t); -static isns_dd_t * isns_dd_by_name(const char *); -static isns_dd_member_t * isns_dd_create_member(isns_object_t *); -static inline void isns_dd_member_free(isns_dd_member_t *); -static int isns_dd_remove_member(isns_dd_t *, isns_object_t *); -static void isns_dd_list_resize(isns_dd_list_t *, unsigned int); -static void isns_dd_list_insert(isns_dd_list_t *, isns_dd_t *); -static void isns_dd_list_remove(isns_dd_list_t *, isns_dd_t *); - -static isns_object_t * isns_dd_get_member_object(isns_db_t *, - const isns_attr_t *, const isns_attr_t *, - int); - -/* - * Create DDReg messages - */ -isns_simple_t * -isns_create_dd_registration(isns_client_t *clnt, const isns_attr_list_t *attrs) -{ - isns_simple_t *msg; - isns_attr_t *id_attr; - - msg = isns_simple_create(ISNS_DD_REGISTER, clnt->ic_source, NULL); - if (msg == NULL) - return NULL; - - /* If the caller specified a DD_ID, use it in the - * message key. */ - if (isns_attr_list_get_attr(attrs, ISNS_TAG_DD_ID, &id_attr)) - isns_attr_list_append_attr(&msg->is_message_attrs, id_attr); - - isns_attr_list_copy(&msg->is_operating_attrs, attrs); - return msg; -} - -isns_simple_t * -isns_create_dd_deregistration(isns_client_t *clnt, - uint32_t dd_id, const isns_attr_list_t *attrs) -{ - isns_simple_t *msg; - - msg = isns_simple_create(ISNS_DD_DEREGISTER, clnt->ic_source, NULL); - if (msg == NULL) - return NULL; - - isns_attr_list_append_uint32(&msg->is_message_attrs, - ISNS_TAG_DD_ID, dd_id); - - isns_attr_list_copy(&msg->is_operating_attrs, attrs); - return msg; -} - -/* - * Process a DD registration - */ -int -isns_process_dd_registration(isns_server_t *srv, isns_simple_t *call, isns_simple_t **result) -{ - isns_simple_t *reply = NULL; - 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_dd_t *dd = NULL, *temp_dd = NULL; - isns_attr_t *attr; - uint32_t id = 0; - int status; - - /* - * 5.6.5.9. - * The Message Key, if used, contains the DD_ID of the Discovery - * Domain to be registered. If the Message Key contains a DD_ID - * of an existing DD entry in the iSNS database, then the DDReg - * message SHALL attempt to update the existing entry. If the - * DD_ID in the Message Key (if used) does not match an existing - * DD entry, then the iSNS server SHALL reject the DDReg message - * with a status code of 3 (Invalid Registration). - */ - switch (keys->ial_count) { - case 0: - /* Security: check if the client is allowed to - * create a discovery domain */ - if (!isns_policy_validate_object_creation(call->is_policy, - call->is_source, - &isns_dd_template, - keys, attrs, - call->is_function)) - goto unauthorized; - break; - - case 1: - attr = keys->ial_data[0]; - if (attr->ia_tag_id != ISNS_TAG_DD_ID) - goto reject; - if (ISNS_ATTR_IS_NIL(attr)) - break; - if (!ISNS_ATTR_IS_UINT32(attr)) - goto reject; - - id = attr->ia_value.iv_uint32; - if (id == 0) - goto reject; - - dd = isns_dd_by_id(id); - if (dd == NULL) { - isns_debug_state("DDReg for unknown ID=%u\n", id); - goto reject; - } - - /* Security: check if the client is allowed to - * mess with this DD. */ - isns_assert(dd->dd_object); - if (!isns_policy_validate_object_update(call->is_policy, - call->is_source, - dd->dd_object, attrs, - call->is_function)) - goto unauthorized; - - break; - - default: - goto reject; - } - - temp_dd = isns_dd_alloc(); - - /* Parse the attributes and build a DD object. */ - status = isns_dd_parse_attrs(temp_dd, db, attrs, dd, 1); - if (status != ISNS_SUCCESS) - goto out; - - if (dd == NULL) { - /* Create the DD, and copy the general information - * such asn features and symbolic name from temp_dd */ - dd = isns_dd_clone(temp_dd); - - /* Don't assign the attrs to the DD right away. - * First and foremost, they may be unsorted. Second, - * we really want to hand-pick through them due to - * the weird semantics mandated by the RFC. */ - dd->dd_object = isns_create_object(&isns_dd_template, NULL, NULL); - if (dd->dd_object == NULL) - goto reject; - - /* Insert new domain into database */ - isns_db_insert(db, dd->dd_object); - - /* Add it to the internal list. Assign DD_ID and - * symbolic name if none were given. - */ - isns_dd_insert(dd); - } else { - if (!dd->dd_id) - dd->dd_id = temp_dd->dd_id; - dd->dd_features = temp_dd->dd_features; - isns_assign_string(&dd->dd_name, temp_dd->dd_name); - } - - /* Send notifications. This must be done before merging - * the list of new members into the DD. - */ - isns_dd_notify(dd, dd->dd_members, temp_dd->dd_members, - NOTIFY_MEMBER_ADDED); - - /* Update the DD */ - isns_dd_add_members(dd, db, temp_dd); - - /* And add it to the database. */ - isns_dd_store(db, dd, 0); - - reply = isns_simple_create(ISNS_DD_REGISTER, srv->is_source, NULL); - isns_object_extract_all(dd->dd_object, &reply->is_operating_attrs); - - status = ISNS_SUCCESS; - -out: - isns_dd_release(temp_dd); - isns_dd_release(dd); - *result = reply; - return status; - -reject: - status = ISNS_INVALID_REGISTRATION; - goto out; - -unauthorized: - status = ISNS_SOURCE_UNAUTHORIZED; - goto out; -} - -/* - * Process a DD deregistration - */ -int -isns_process_dd_deregistration(isns_server_t *srv, isns_simple_t *call, isns_simple_t **result) -{ - isns_simple_t *reply = NULL; - 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_dd_t *dd = NULL, *temp_dd = NULL; - isns_attr_t *attr; - uint32_t id = 0; - int status; - - /* - * 5.6.5.10. - * The Message Key Attribute for a DDDereg message is the DD - * ID for the Discovery Domain being removed or having members - * removed. - */ - if (keys->ial_count != 1) - goto reject; - - attr = keys->ial_data[0]; - if (attr->ia_tag_id != ISNS_TAG_DD_ID - || ISNS_ATTR_IS_NIL(attr) - || !ISNS_ATTR_IS_UINT32(attr)) - goto reject; - - id = attr->ia_value.iv_uint32; - if (id == 0) - goto reject; - - dd = isns_dd_by_id(id); - if (dd == NULL) - goto reject; - - /* Security: check if the client is permitted to - * modify the DD object. - */ - if (!isns_policy_validate_object_update(call->is_policy, - call->is_source, - dd->dd_object, attrs, - call->is_function)) - goto unauthorized; - - /* - * 5.6.5.10. - * If the DD ID matches an existing DD and there are - * no Operating Attributes, then the DD SHALL be removed and a - * success Status Code returned. Any existing members of that - * DD SHALL remain in the iSNS database without membership in - * the just-removed DD. - */ - if (attrs->ial_count == 0) { - isns_dd_member_t *mp; - - /* Zap the membership bit */ - for (mp = dd->dd_members; mp; mp = mp->ddm_next) { - isns_object_t *obj = mp->ddm_object.obj; - - isns_object_clear_membership(obj, dd->dd_id); - } - - /* Notify all DD members that they will lose the other - * nodes. */ - isns_dd_notify(dd, NULL, dd->dd_members, NOTIFY_MEMBER_REMOVED); - - isns_dd_destroy(db, dd); - } else { - /* Parse the attributes and build a temporary DD object. */ - temp_dd = isns_dd_alloc(); - status = isns_dd_parse_attrs(temp_dd, db, attrs, dd, 0); - if (status != ISNS_SUCCESS) - goto out; - - /* Update the DD object */ - status = isns_dd_remove_members(dd, db, temp_dd); - if (status != ISNS_SUCCESS) - goto out; - - /* Send notifications. This must be done before after - * updating the DD. - */ - isns_dd_notify(dd, dd->dd_members, temp_dd->dd_members, - NOTIFY_MEMBER_REMOVED); - - /* Store it in the database. */ - isns_dd_store(db, dd, 1); - } - - reply = isns_simple_create(ISNS_DD_DEREGISTER, srv->is_source, NULL); - status = ISNS_SUCCESS; - -out: - isns_dd_release(temp_dd); - isns_dd_release(dd); - *result = reply; - return status; - -reject: - status = ISNS_INVALID_DEREGISTRATION; - goto out; - -unauthorized: - status = ISNS_SOURCE_UNAUTHORIZED; - goto out; -} - -static isns_dd_t * -isns_dd_alloc(void) -{ - return isns_calloc(1, sizeof(isns_dd_t)); -} - -/* - * Allocate a clone of the orig_dd, but without - * copying the members. - */ -static isns_dd_t * -isns_dd_clone(const isns_dd_t *orig_dd) -{ - isns_dd_t *dd; - - dd = isns_dd_alloc(); - - dd->dd_id = orig_dd->dd_id; - dd->dd_features = orig_dd->dd_features; - dd->dd_object = isns_object_get(orig_dd->dd_object); - isns_assign_string(&dd->dd_name, orig_dd->dd_name); - - return dd; -} - -static void -isns_dd_release(isns_dd_t *dd) -{ - isns_dd_member_t *member; - - if (dd == NULL || dd->dd_inserted) - return; - - while ((member = dd->dd_members) != NULL) { - dd->dd_members = member->ddm_next; - isns_dd_member_free(member); - } - - if (dd->dd_object) - isns_object_release(dd->dd_object); - - isns_free(dd->dd_name); - isns_free(dd); -} - -static isns_dd_member_t * -isns_dd_create_member(isns_object_t *obj) -{ - isns_dd_member_t *new; - - new = isns_calloc(1, sizeof(*new)); - new->ddm_added = 1; - - if (ISNS_IS_ISCSI_NODE(obj)) - new->ddm_type = ISNS_DD_MEMBER_ISCSI_NODE; - else if (ISNS_IS_PORTAL(obj)) - new->ddm_type = ISNS_DD_MEMBER_PORTAL; - else if (ISNS_IS_FC_NODE(obj)) - new->ddm_type = ISNS_DD_MEMBER_IFCP_NODE; - else { - isns_free(new); - return NULL; - } - - isns_object_reference_set(&new->ddm_object, obj); - return new; -} - -static inline void -isns_dd_member_free(isns_dd_member_t *member) -{ - switch (member->ddm_type) { - case ISNS_DD_MEMBER_ISCSI_NODE: - isns_free(member->ddm_iscsi_node.name); - break; - - case ISNS_DD_MEMBER_IFCP_NODE: - isns_free(member->ddm_ifcp_node.name); - break; - } - - isns_object_reference_drop(&member->ddm_object); - isns_free(member); -} - -void -isns_dd_get_members(uint32_t dd_id, isns_object_list_t *list, int active_only) -{ - isns_dd_t *dd; - isns_dd_member_t *mp; - - dd = isns_dd_by_id(dd_id); - if (dd == NULL) - return; - - for (mp = dd->dd_members; mp; mp = mp->ddm_next) { - isns_object_t *obj = mp->ddm_object.obj; - - if (active_only - && obj->ie_state != ISNS_OBJECT_STATE_MATURE) - continue; - - isns_object_list_append(list, obj); - } -} - -/* - * Helper function to remove a member referencing the given object - */ -static int -isns_dd_remove_member(isns_dd_t *dd, isns_object_t *obj) -{ - isns_dd_member_t *mp, **pos; - - pos = &dd->dd_members; - while ((mp = *pos) != NULL) { - if (mp->ddm_object.obj == obj) { - *pos = mp->ddm_next; - isns_dd_member_free(mp); - return 1; - } else { - pos = &mp->ddm_next; - } - } - - return 0; -} - -static void -isns_dd_insert(isns_dd_t *dd) -{ - if (dd->dd_inserted) - return; - - if (dd->dd_id == 0) { - uint32_t id = isns_dd_next_id; - unsigned int i; - - for (i = 0; i < isns_dd_list.ddl_count; ++i) { - isns_dd_t *cur = isns_dd_list.ddl_data[i]; - - if (cur->dd_id > id) - break; - if (cur->dd_id == id) - ++id; - } - isns_debug_state("Allocated new DD_ID %d\n", id); - dd->dd_id = id; - isns_dd_next_id = id + 1; - } - - /* - * When creating a new DD, if the DD_Symbolic_Name is - * not included in the Operating Attributes, or if it - * is included with a zero-length TLV, then the iSNS - * server SHALL provide a unique DD_Symbolic_Name value - * for the created DD. The assigned DD_Symbolic_Name - * value SHALL be returned in the DDRegRsp message. - */ - if (dd->dd_name == NULL) { - char namebuf[64]; - - snprintf(namebuf, sizeof(namebuf), "isns.dd%u", dd->dd_id); - isns_assign_string(&dd->dd_name, namebuf); - } - - isns_dd_list_insert(&isns_dd_list, dd); - dd->dd_inserted = 1; - -#ifdef DD_DEBUG - /* Safety first - make sure domains are sorted by DD_ID */ - { - unsigned int i, prev_id = 0; - - for (i = 0; i < isns_dd_list.ddl_count; ++i) { - isns_dd_t *cur = isns_dd_list.ddl_data[i]; - - isns_assert(cur->dd_id > prev_id); - prev_id = cur->dd_id; - } - } -#endif -} - -/* - * Resize the DD list - */ -#define LIST_SIZE(n) (((n) + 15) & ~15) -void -isns_dd_list_resize(isns_dd_list_t *list, unsigned int last_index) -{ - unsigned int new_size; - isns_dd_t **new_data; - - new_size = LIST_SIZE(last_index + 1); - if (new_size < list->ddl_count) - return; - - /* We don't use realloc here because we need - * to zero the new pointers anyway. */ - new_data = isns_calloc(new_size, sizeof(void *)); - isns_assert(new_data); - - memcpy(new_data, list->ddl_data, - list->ddl_count * sizeof(void *)); - isns_free(list->ddl_data); - - list->ddl_data = new_data; - list->ddl_count = last_index + 1; -} - -/* - * Find the insert position for a given DD ID. - * returns true iff the DD was found in the list. - */ -static int -__isns_dd_list_find_pos(isns_dd_list_t *list, unsigned int id, - unsigned int *where) -{ - unsigned int hi, lo, md; - - lo = 0; - hi = list->ddl_count; - - /* binary search */ - while (lo < hi) { - isns_dd_t *cur; - - md = (lo + hi) / 2; - cur = list->ddl_data[md]; - - if (id == cur->dd_id) { - *where = md; - return 1; - } - - if (id < cur->dd_id) { - hi = md; - } else { - lo = md + 1; - } - } - - *where = hi; - return 0; -} - -/* - * In-order insert - */ -static void -isns_dd_list_insert(isns_dd_list_t *list, isns_dd_t *dd) -{ - unsigned int pos; - - if (__isns_dd_list_find_pos(list, dd->dd_id, &pos)) { - isns_error("Internal error in %s: DD already listed\n", - __FUNCTION__); - return; - } - - isns_dd_list_resize(list, list->ddl_count); - /* Shift the tail of the list to make room for new entry. */ - memmove(list->ddl_data + pos + 1, - list->ddl_data + pos, - (list->ddl_count - pos - 1) * sizeof(void *)); - list->ddl_data[pos] = dd; -} - -/* - * Remove DD from list - */ -void -isns_dd_list_remove(isns_dd_list_t *list, isns_dd_t *dd) -{ - unsigned int pos; - - if (!__isns_dd_list_find_pos(list, dd->dd_id, &pos)) - return; - - /* Shift the tail of the list */ - memmove(list->ddl_data + pos, - list->ddl_data + pos + 1, - (list->ddl_count - pos - 1) * sizeof(void *)); - list->ddl_count -= 1; -} - -isns_dd_t * -isns_dd_by_id(uint32_t id) -{ - unsigned int i; - - for (i = 0; i < isns_dd_list.ddl_count; ++i) { - isns_dd_t *dd = isns_dd_list.ddl_data[i]; - - if (dd && dd->dd_id == id) - return dd; - } - - return NULL; -} - -static isns_dd_t * -isns_dd_by_name(const char *name) -{ - unsigned int i; - - for (i = 0; i < isns_dd_list.ddl_count; ++i) { - isns_dd_t *dd = isns_dd_list.ddl_data[i]; - - if (dd && !strcmp(dd->dd_name, name)) - return dd; - } - - return NULL; -} - -/* - * Validate the operating attributes, which is surprisingly - * tedious for DDs. It appears as if the whole DD/DDset - * stuff has been slapped onto iSNS as an afterthought. - * - * DDReg has some funky rules about how eg iSCSI nodes - * can be identified by either name or index, and how they - * relate to each other. Unfortunately, the RFC is very vague - * in describing how to treat DDReg message that mix these - * two types of identification, except by saying they - * need to be consistent. - */ -static int -isns_dd_parse_attrs(isns_dd_t *dd, isns_db_t *db, - const isns_attr_list_t *attrs, - const isns_dd_t *orig_dd, - int is_registration) -{ - isns_dd_member_t **tail; - const isns_dd_t *conflict; - unsigned int i; - int rv = ISNS_SUCCESS; - - if (orig_dd) { - dd->dd_id = orig_dd->dd_id; - dd->dd_features = orig_dd->dd_features; - isns_assign_string(&dd->dd_name, orig_dd->dd_name); - } - - isns_assert(dd->dd_members == NULL); - tail = &dd->dd_members; - - for (i = 0; i < attrs->ial_count; ++i) { - isns_object_t *obj = NULL; - isns_attr_t *attr, *next = NULL; - const char *name; - uint32_t id; - - attr = attrs->ial_data[i]; - - if (!isns_object_attr_valid(&isns_dd_template, attr->ia_tag_id)) - return ISNS_INVALID_REGISTRATION; - - switch (attr->ia_tag_id) { - case ISNS_TAG_DD_ID: - /* Ignore this attribute in DDDereg messages */ - if (!is_registration) - continue; - - /* - * 5.6.5.9. - * A DDReg message with no Message Key SHALL result - * in the attempted creation of a new Discovery Domain - * (DD). If the DD_ID attribute (with non-zero length) - * is included among the Operating Attributes in the - * DDReg message, then the new Discovery Domain SHALL be - * assigned the value contained in that DD_ID attribute. - * - * If the DD_ID is included in both the Message - * Key and Operating Attributes, then the DD_ID - * value in the Message Key MUST be the same as - * the DD_ID value in the Operating Attributes. - * - * Implementer's note: It's not clear why the standard - * makes an exception for the DD_ID, while all other - * index attributes are read-only. - */ - if (ISNS_ATTR_IS_NIL(attr)) - break; - - id = attr->ia_value.iv_uint32; - if (dd->dd_id != 0) { - if (dd->dd_id != id) - goto invalid; - } else if ((conflict = isns_dd_by_id(id)) != NULL) { - isns_debug_state("DDReg: requested ID %d " - "clashes with existing DD (%s)\n", - id, conflict->dd_name); - goto invalid; - } - dd->dd_id = id; - break; - - case ISNS_TAG_DD_SYMBOLIC_NAME: - /* Ignore this attribute in DDDereg messages */ - if (!is_registration) - continue; - - /* - * If the DD_Symbolic_Name is an operating - * attribute and its value is unique (i.e., it - * does not match the registered DD_Symbolic_Name - * for another DD), then the value SHALL be stored - * in the iSNS database as the DD_Symbolic_Name - * for the specified Discovery Domain. If the - * value for the DD_Symbolic_Name is not unique, - * then the iSNS server SHALL reject the attempted - * DD registration with a status code of 3 - * (Invalid Registration). - */ - if (ISNS_ATTR_IS_NIL(attr)) - break; - - name = attr->ia_value.iv_string; - if (dd->dd_name && strcmp(name, dd->dd_name)) { - isns_debug_state("DDReg: symbolic name conflict: " - "id=%d name=%s requested=%s\n", - dd->dd_id, dd->dd_name, name); - goto invalid; - } - if (dd->dd_name) - break; - - if ((conflict = isns_dd_by_name(name)) != NULL) { - isns_debug_state("DDReg: requested symbolic name (%s) " - "clashes with existing DD (id=%d)\n", - name, conflict->dd_id); - goto invalid; - } - isns_assign_string(&dd->dd_name, name); - break; - - case ISNS_TAG_DD_FEATURES: - /* Ignore this attribute in DDDereg messages */ - if (!is_registration) - continue; - - /* - * When creating a new DD, if the DD_Features - * attribute is not included in the Operating - * Attributes, then the iSNS server SHALL assign - * the default value. The default value for - * DD_Features is 0. - */ - if (ISNS_ATTR_IS_UINT32(attr)) - dd->dd_features = attr->ia_value.iv_uint32; - break; - - case ISNS_TAG_DD_MEMBER_PORTAL_IP_ADDR: - /* portal address must be followed by port */ - if (i + 1 >= attrs->ial_count) - goto invalid; - - next = attrs->ial_data[i + 1]; - if (next->ia_tag_id != ISNS_TAG_DD_MEMBER_PORTAL_TCP_UDP_PORT) - goto invalid; - i += 1; - /* fallthru to normal case */ - - case ISNS_TAG_DD_MEMBER_PORTAL_INDEX: - case ISNS_TAG_DD_MEMBER_ISCSI_INDEX: - case ISNS_TAG_DD_MEMBER_ISCSI_NAME: - case ISNS_TAG_DD_MEMBER_FC_PORT_NAME: - if (ISNS_ATTR_IS_NIL(attr)) - goto invalid; - - obj = isns_dd_get_member_object(db, - attr, next, - is_registration); - /* For a DD deregistration, it's okay if the - * object does not exist. */ - if (obj == NULL && is_registration) - goto invalid; - break; - - invalid: - rv = ISNS_INVALID_REGISTRATION; - continue; - - } - - if (obj) { - if (is_registration - && isns_object_test_membership(obj, dd->dd_id)) { - /* Duplicates are ignored */ - isns_debug_state("Ignoring duplicate DD registration " - "for %s %u\n", - obj->ie_template->iot_name, - obj->ie_index); - } else { - /* This just adds the member to the temporary DD object, - * without changing any state in the database. */ - isns_dd_member_t *new; - - new = isns_dd_create_member(obj); - if (new) { - *tail = new; - tail = &new->ddm_next; - } - } - isns_object_release(obj); - } - } - - return rv; -} - -/* - * Helper function: extract live nodes from the DD member list - */ -static inline void -isns_dd_get_member_nodes(isns_dd_member_t *members, isns_object_list_t *result) -{ - isns_dd_member_t *mp; - - /* Extract iSCSI nodes from both list. */ - for (mp = members; mp; mp = mp->ddm_next) { - isns_object_t *obj = mp->ddm_object.obj; - - if (ISNS_IS_ISCSI_NODE(obj) - && obj->ie_state == ISNS_OBJECT_STATE_MATURE) - isns_object_list_append(result, obj); - } -} - -void -isns_dd_notify(const isns_dd_t *dd, isns_dd_member_t *unchanged, - isns_dd_member_t *changed, int removed) -{ - isns_object_list_t dd_objects = ISNS_OBJECT_LIST_INIT; - isns_object_list_t changed_objects = ISNS_OBJECT_LIST_INIT; - unsigned int i, j, event; - - /* Extract iSCSI nodes from both list. */ - isns_dd_get_member_nodes(unchanged, &dd_objects); - isns_dd_get_member_nodes(changed, &changed_objects); - - /* Send a management SCN multicast to all - * control nodes that care. */ - event = removed? ISNS_SCN_DD_MEMBER_REMOVED_MASK : ISNS_SCN_DD_MEMBER_ADDED_MASK; - for (i = 0; i < changed_objects.iol_count; ++i) { - isns_object_t *obj = changed_objects.iol_data[i]; - - isns_object_event(obj, - event | ISNS_SCN_MANAGEMENT_REGISTRATION_MASK, - dd->dd_object); - } - -#ifdef notagoodidea - /* Not sure - it may be good to send OBJECT ADDED/REMOVED instead - * of the DD membership messages. However, right now the SCN code - * will nuke all SCN registrations for a node when it sees a - * REMOVE event for it. - */ - event = removed? ISNS_SCN_OBJECT_REMOVED_MASK : ISNS_SCN_OBJECT_ADDED_MASK; -#endif - - /* If we added an iscsi node, loop over all members - * and send unicast events to each iscsi node, - * informing them that a new member has been added/removed. - */ - for (j = 0; j < changed_objects.iol_count; ++j) { - isns_object_t *changed = changed_objects.iol_data[j]; - - for (i = 0; i < dd_objects.iol_count; ++i) { - isns_object_t *obj = dd_objects.iol_data[i]; - - /* For member removal, do not send notifications - * if the two nodes are still visible to each - * other through a different discovery domain */ - if (removed && isns_object_test_visibility(obj, changed)) - continue; - - /* Inform the old node that the new node was - * added/removed. */ - isns_unicast_event(obj, changed, event, NULL); - - /* Inform the new node that the old node became - * (in)accessible to it. */ - isns_unicast_event(changed, obj, event, NULL); - } - - /* Finally, inform each changed node of the other - * DD members that became (in)accessible to it. */ - for (i = 0; i < changed_objects.iol_count; ++i) { - isns_object_t *obj = changed_objects.iol_data[i]; - - if (obj == changed) - continue; - - if (removed && isns_object_test_visibility(obj, changed)) - continue; - - isns_unicast_event(changed, obj, event, NULL); - } - } -} - -void -isns_dd_add_members(isns_dd_t *dd, isns_db_t *db, isns_dd_t *new_dd) -{ - isns_dd_member_t *mp, **tail; - - for (mp = new_dd->dd_members; mp; mp = mp->ddm_next) { - const char *node_name; - isns_object_t *obj = mp->ddm_object.obj; - - /* - * If the Operating Attributes contain a DD - * Member iSCSI Name value for a Storage Node - * that is currently not registered in the iSNS - * database, then the iSNS server MUST allocate an - * unused iSCSI Node Index for that Storage Node. - * The assigned iSCSI Node Index SHALL be returned - * in the DDRegRsp message as the DD Member iSCSI - * Node Index. The allocated iSCSI Node Index - * value SHALL be assigned to the Storage Node - * if and when it registers in the iSNS database. - * [And likewise for portals] - */ - if (obj->ie_index == 0) - isns_db_insert_limbo(db, obj); - mp->ddm_index = obj->ie_index; - - /* Record the fact that the object is a member of - * this DD */ - isns_object_mark_membership(obj, dd->dd_id); - - switch (mp->ddm_type) { - case ISNS_DD_MEMBER_ISCSI_NODE: - if (isns_object_get_string(obj, ISNS_TAG_ISCSI_NAME, &node_name)) - isns_assign_string(&mp->ddm_iscsi_node.name, node_name); - - break; - - case ISNS_DD_MEMBER_IFCP_NODE: - if (isns_object_get_string(obj, ISNS_TAG_FC_PORT_NAME_WWPN, &node_name)) - isns_assign_string(&mp->ddm_ifcp_node.name, node_name); - - break; - - case ISNS_DD_MEMBER_PORTAL: - isns_portal_from_object(&mp->ddm_portal.info, - ISNS_TAG_PORTAL_IP_ADDRESS, - ISNS_TAG_PORTAL_TCP_UDP_PORT, - obj); - break; - } - } - - /* Find the tail of the DD member list */ - tail = &dd->dd_members; - while ((mp = *tail) != NULL) - tail = &mp->ddm_next; - - /* Append the new list of members */ - *tail = new_dd->dd_members; - new_dd->dd_members = NULL; -} - -/* - * Remove members from a DD - */ -int -isns_dd_remove_members(isns_dd_t *dd, isns_db_t *db, isns_dd_t *temp_dd) -{ - isns_dd_member_t *mp; - - for (mp = temp_dd->dd_members; mp; mp = mp->ddm_next) { - isns_object_t *obj = mp->ddm_object.obj; - - /* Clear the membership bit. If the object wasn't in this - * DD to begin with, bail out right away. */ - if (!isns_object_clear_membership(obj, dd->dd_id)) { - isns_debug_state("DD dereg: object %d is not in this DD\n", - obj->ie_index); - continue; - } - - if (!isns_dd_remove_member(dd, obj)) - isns_error("%s: DD member not found in internal list\n", - __FUNCTION__); - } - - return ISNS_SUCCESS; -} - -void -isns_dd_store(isns_db_t *db, const isns_dd_t *dd, int rewrite) -{ - isns_object_t *obj = dd->dd_object; - isns_dd_member_t *member; - - if (rewrite) - isns_object_prune_attrs(obj); - - isns_object_set_uint32(obj, ISNS_TAG_DD_ID, dd->dd_id); - isns_object_set_string(obj, ISNS_TAG_DD_SYMBOLIC_NAME, dd->dd_name); - isns_object_set_uint32(obj, ISNS_TAG_DD_FEATURES, dd->dd_features); - - for (member = dd->dd_members; member; member = member->ddm_next) { - struct isns_dd_iscsi_node *node; - struct isns_dd_portal *portal; - - if (!member->ddm_added && !rewrite) - continue; - - switch (member->ddm_type) { - case ISNS_DD_MEMBER_ISCSI_NODE: - node = &member->ddm_iscsi_node; - - isns_object_set_uint32(obj, - ISNS_TAG_DD_MEMBER_ISCSI_INDEX, - node->index); - if (node->name) - isns_object_set_string(obj, - ISNS_TAG_DD_MEMBER_ISCSI_NAME, - node->name); - break; - - case ISNS_DD_MEMBER_PORTAL: - portal = &member->ddm_portal; - - isns_object_set_uint32(obj, - ISNS_TAG_DD_MEMBER_PORTAL_INDEX, - portal->index); - if (portal->info.addr.sin6_family != AF_UNSPEC) { - isns_portal_to_object(&portal->info, - ISNS_TAG_DD_MEMBER_PORTAL_IP_ADDR, - ISNS_TAG_DD_MEMBER_PORTAL_TCP_UDP_PORT, - obj); - } - break; - } - - member->ddm_added = 0; - } -} - -/* - * Destroy a DD - * The caller should call isns_dd_release to free the DD object. - */ -void -isns_dd_destroy(isns_db_t *db, isns_dd_t *dd) -{ - isns_db_remove(db, dd->dd_object); - isns_dd_list_remove(&isns_dd_list, dd); - dd->dd_inserted = 0; -} - -int -isns_dd_load_all(isns_db_t *db) -{ - isns_object_list_t list = ISNS_OBJECT_LIST_INIT; - unsigned int i; - int rc; - - if (isns_dd_list_initialized) - return ISNS_SUCCESS; - - rc = isns_db_gang_lookup(db, &isns_dd_template, NULL, &list); - if (rc != ISNS_SUCCESS) - return rc; - - for (i = 0; i < list.iol_count; ++i) { - isns_object_t *obj = list.iol_data[i]; - isns_dd_t *dd = NULL, *temp_dd = NULL; - isns_dd_member_t *mp; - - temp_dd = isns_dd_alloc(); - - rc = isns_dd_parse_attrs(temp_dd, db, &obj->ie_attrs, NULL, 1); - if (rc) { - if (temp_dd->dd_id == 0) { - isns_error("Problem converting DD object (index 0x%x). No DD_ID\n", - obj->ie_index); - goto next; - } - isns_error("Problem converting DD %u. Proceeding anyway.\n", - temp_dd->dd_id); - } else { - isns_debug_state("Loaded DD %d from database\n", temp_dd->dd_id); - } - - dd = isns_dd_clone(temp_dd); - - dd->dd_object = isns_object_get(obj); - - isns_dd_insert(dd); - isns_dd_add_members(dd, db, temp_dd); - - /* Clear the ddm_added flag for all members, to - * prevent all information from being duplicated - * to the DB on the next DD modification. */ - for (mp = dd->dd_members; mp; mp = mp->ddm_next) - mp->ddm_added = 0; - -next: - isns_dd_release(temp_dd); - } - - isns_object_list_destroy(&list); - isns_dd_list_initialized = 1; - return ISNS_SUCCESS; -} - -isns_object_t * -isns_dd_get_member_object(isns_db_t *db, const isns_attr_t *key1, - const isns_attr_t *key2, - int create) -{ - isns_attr_list_t query = ISNS_ATTR_LIST_INIT; - isns_object_template_t *tmpl = NULL; - isns_object_t *obj; - isns_portal_info_t portal_info; - const char *key_string = NULL; - uint32_t key_index = 0; - - switch (key1->ia_tag_id) { - case ISNS_TAG_DD_MEMBER_ISCSI_INDEX: - key_index = key1->ia_value.iv_uint32; - isns_attr_list_append_uint32(&query, - ISNS_TAG_ISCSI_NODE_INDEX, - key_index); - tmpl = &isns_iscsi_node_template; - break; - - case ISNS_TAG_DD_MEMBER_ISCSI_NAME: - key_string = key1->ia_value.iv_string; - isns_attr_list_append_string(&query, - ISNS_TAG_ISCSI_NAME, - key_string); - tmpl = &isns_iscsi_node_template; - break; - - case ISNS_TAG_DD_MEMBER_FC_PORT_NAME: - key_string = key1->ia_value.iv_string; - isns_attr_list_append_string(&query, - ISNS_TAG_FC_PORT_NAME_WWPN, - key_string); - tmpl = &isns_fc_port_template; - break; - - case ISNS_TAG_DD_MEMBER_PORTAL_INDEX: - key_index = key1->ia_value.iv_uint32; - isns_attr_list_append_uint32(&query, - ISNS_TAG_PORTAL_INDEX, - key_index); - tmpl = &isns_portal_template; - break; - - case ISNS_TAG_DD_MEMBER_PORTAL_IP_ADDR: - if (!isns_portal_from_attr_pair(&portal_info, key1, key2) - || !isns_portal_to_attr_list(&portal_info, - ISNS_TAG_PORTAL_IP_ADDRESS, - ISNS_TAG_PORTAL_TCP_UDP_PORT, - &query)) - return NULL; - - key_string = isns_portal_string(&portal_info); - tmpl = &isns_portal_template; - break; - - default: - return NULL; - } - - obj = isns_db_lookup(db, tmpl, &query); - if (!obj && create) { - if (!key_string) { - isns_debug_state("Attempt to register %s DD member " - "with unknown index %u\n", - tmpl->iot_name, key_index); - goto out; - } - - obj = isns_create_object(tmpl, &query, NULL); - if (obj != NULL) - isns_debug_state("Created limbo object for " - "%s DD member %s\n", - tmpl->iot_name, key_string); - } - -out: - isns_attr_list_destroy(&query); - return obj; - -} |