/*
* Define all iSNS tags with their types, etc.
*
* Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
*/
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include "config.h"
#include <libisns/isns-proto.h>
#include "vendor.h"
#include <libisns/attrs.h>
#include "security.h"
#include "objects.h"
#include <libisns/util.h>
#define ISNS_MAX_BUILTIN_TAG 4096
static void print_bitfield(unsigned long, char **, char *, size_t);
static int parse_bitfield( char **, const char *, uint32_t *);
static const char *help_bitfield(char **);
#define DECLARE_VALIDATOR(name) \
static int isns_##name##_validate(const isns_value_t *, const isns_policy_t *);
#define DECLARE_ACCESSORS(name) \
static int isns_##name##_parse(isns_value_t *, const char *buf); \
static void isns_##name##_print(const isns_value_t *, char *buf, size_t size); \
static const char * isns_##name##_help(void)
#define USE_VALIDATOR(name) \
.it_validate = isns_##name##_validate
#define USE_ACCESSORS(name) \
.it_parse = isns_##name##_parse, \
.it_print = isns_##name##_print, \
.it_help = isns_##name##_help
DECLARE_VALIDATOR(entity_protocol);
DECLARE_ACCESSORS(entity_protocol);
DECLARE_ACCESSORS(tcpudp_port);
DECLARE_VALIDATOR(iscsi_node_type);
DECLARE_ACCESSORS(iscsi_node_type);
DECLARE_ACCESSORS(timestamp);
DECLARE_ACCESSORS(portal_secbitmap);
DECLARE_ACCESSORS(scn_bitmap);
DECLARE_ACCESSORS(dd_features);
DECLARE_ACCESSORS(policy_object_type);
DECLARE_ACCESSORS(policy_function);
static const char *isns_authmethod_help(void);
#define TAG(ID, name, type, args...) \
[ISNS_TAG_##ID] = { \
.it_id = ISNS_TAG_##ID, \
.it_name = name, \
.it_type = &isns_attr_type_##type, \
args \
}
static isns_tag_type_t isns_tags[ISNS_MAX_BUILTIN_TAG] = {
TAG(DELIMITER, "Delimiter", nil),
TAG(ENTITY_IDENTIFIER, "Entity identifier", string),
TAG(ENTITY_PROTOCOL, "Entity protocol", uint32,
USE_VALIDATOR(entity_protocol),
USE_ACCESSORS(entity_protocol)),
TAG(MGMT_IP_ADDRESS, "Mgmt IP address", ipaddr),
TAG(TIMESTAMP, "Timestamp", uint64,
USE_ACCESSORS(timestamp),
.it_readonly = 1),
TAG(PROTOCOL_VERSION_RANGE, "Protocol version range", range16),
TAG(REGISTRATION_PERIOD, "Registration Period", uint32),
TAG(ENTITY_INDEX, "Entity index", uint32,
.it_readonly = 1),
TAG(ENTITY_NEXT_INDEX, "Entity next index", uint32,
.it_readonly = 1),
TAG(PORTAL_IP_ADDRESS, "Portal IP address", ipaddr),
TAG(PORTAL_TCP_UDP_PORT, "Portal TCP/UDP port", uint32,
USE_ACCESSORS(tcpudp_port)),
TAG(ESI_INTERVAL, "ESI interval", uint32),
TAG(ESI_PORT, "ESI port", uint32,
USE_ACCESSORS(tcpudp_port)),
TAG(PORTAL_SYMBOLIC_NAME, "Portal name", string),
TAG(PORTAL_INDEX, "Portal index", uint32),
TAG(SCN_PORT, "SCN port", uint32,
USE_ACCESSORS(tcpudp_port)),
TAG(PORTAL_SECURITY_BITMAP, "Portal security bitmap", uint32,
USE_ACCESSORS(portal_secbitmap)),
TAG(PORTAL_NEXT_INDEX, "Portal next index", uint32,
.it_readonly = 1),
TAG(ISCSI_NAME, "iSCSI name", string),
TAG(ISCSI_NODE_TYPE, "iSCSI node type", uint32,
USE_VALIDATOR(iscsi_node_type),
USE_ACCESSORS(iscsi_node_type)),
TAG(ISCSI_ALIAS, "iSCSI alias", string),
TAG(ISCSI_SCN_BITMAP, "iSCSI SCN bitmap", uint32,
USE_ACCESSORS(scn_bitmap)),
TAG(ISCSI_NODE_INDEX, "iSCSI node index", uint32,
.it_readonly = 1),
TAG(WWNN_TOKEN, "WWNN token", uint64),
TAG(ISCSI_NODE_NEXT_INDEX, "iSCSI node next index",uint32,
.it_readonly = 1),
TAG(ISCSI_AUTHMETHOD, "iSCSI auth method", string,
.it_help = isns_authmethod_help),
TAG(PG_ISCSI_NAME, "Portal group name", string),
TAG(PG_PORTAL_IP_ADDR, "Portal group address", ipaddr),
TAG(PG_PORTAL_TCP_UDP_PORT, "Portal group port", uint32,
USE_ACCESSORS(tcpudp_port)),
TAG(PG_TAG, "Portal group tag", uint32),
TAG(PG_INDEX, "Portal group index", uint32,
.it_readonly = 1),
TAG(PG_NEXT_INDEX, "Portal group next index",uint32,
.it_readonly = 1),
/* FC Port */
TAG(FC_PORT_NAME_WWPN, "FC port name WWPN", uint64),
TAG(PORT_ID, "FC port ID", uint32),
TAG(FC_PORT_TYPE, "FC port type", uint32),
TAG(SYMBOLIC_PORT_NAME, "FC symbolic port name",string),
TAG(FABRIC_PORT_NAME, "FC fabric port name", uint64),
TAG(HARD_ADDRESS, "FC hard", uint32),
TAG(PORT_IP_ADDRESS, "FC Port IP address", ipaddr),
TAG(CLASS_OF_SERVICE, "FC service class", uint32),
TAG(FC4_TYPES, "FC4 types", opaque),
TAG(FC4_DESCRIPTOR, "FC4 descriptor", string),
TAG(FC4_FEATURES, "FC4 features", opaque),
TAG(IFCP_SCN_BITMAP, "iFCP SCN bitmap", uint32,
USE_ACCESSORS(scn_bitmap)),
TAG(PORT_ROLE, "FC port role", uint32),
TAG(PERMANENT_PORT_NAME, "FC permanent port name",uint64),
TAG(FC4_TYPE_CODE, "FC4 type code", uint32),
/* FC Node */
TAG(FC_NODE_NAME_WWNN, "FC node name", uint64),
TAG(SYMBOLIC_NODE_NAME, "FC symbolic node name",string),
TAG(NODE_IP_ADDRESS, "FC node IP address", ipaddr),
TAG(NODE_IPA, "FC node IPA", uint64),
TAG(PROXY_ISCSI_NAME, "FC node proxy iSCSI name",string),
/* Other FC tags to go here */
/* Discovery domain set */
TAG(DD_SET_ID, "DD set ID", uint32),
TAG(DD_SET_SYMBOLIC_NAME, "DD set name", string),
TAG(DD_SET_STATUS, "DD set status", uint32),
TAG(DD_SET_NEXT_ID, "DD set next ID", uint32,
.it_readonly = 1),
/* Discovery domain */
TAG(DD_ID, "DD ID", uint32),
TAG(DD_SYMBOLIC_NAME, "DD name", string),
TAG(DD_MEMBER_ISCSI_INDEX, "DD member iSCSI index",uint32,
.it_multiple = 1),
TAG(DD_MEMBER_ISCSI_NAME, "DD member iSCSI name", string,
.it_multiple = 1),
TAG(DD_MEMBER_FC_PORT_NAME, "DD member FC WWPN", string,
.it_multiple = 1),
TAG(DD_MEMBER_PORTAL_INDEX, "DD member portal index",uint32,
.it_multiple = 1),
TAG(DD_MEMBER_PORTAL_IP_ADDR, "DD member portal addr",ipaddr,
.it_multiple = 1),
TAG(DD_MEMBER_PORTAL_TCP_UDP_PORT,"DD member portal port",uint32,
USE_ACCESSORS(tcpudp_port),
.it_multiple = 1),
TAG(DD_FEATURES, "DD features", uint32,
USE_ACCESSORS(dd_features)),
TAG(DD_NEXT_ID, "DD next ID", uint32,
.it_readonly = 1),
};
/*
* End of RFC defined tags
*/
#undef TAG
/*
* Open-iSNS vendor specific tags
*/
#define TAG(ID, name, type, args...) \
{ \
.it_id = OPENISNS_TAG_##ID, \
.it_name = name, \
.it_type = &isns_attr_type_##type, \
args \
}
static isns_tag_type_t isns_vendor_tags[] = {
TAG(POLICY_SPI, "Security Policy Index", string),
TAG(POLICY_KEY, "DSA security key", opaque),
TAG(POLICY_ENTITY, "Policy allowed entity name", string),
TAG(POLICY_OBJECT_TYPE, "Policy allowed object types", uint32,
USE_ACCESSORS(policy_object_type)),
TAG(POLICY_NODE_NAME, "Policy allowed node name", string,
.it_multiple = 1),
TAG(POLICY_NODE_TYPE, "Policy allowed node type", uint32,
USE_VALIDATOR(iscsi_node_type),
USE_ACCESSORS(iscsi_node_type)),
TAG(POLICY_FUNCTIONS, "Policy allowed functions", uint32,
USE_ACCESSORS(policy_function)),
TAG(POLICY_VISIBLE_DD, "Visible Discovery Domain", string,
.it_multiple = 1),
TAG(POLICY_DEFAULT_DD, "Default Discovery Domain", string),
{ 0 }
};
/*
* End of vendor-specific tags
*/
static isns_tag_type_t isns_unknown_tag = {
.it_id = 0xffff,
.it_name = "unknown",
.it_type = &isns_attr_type_opaque,
};
/*
* Map iSNS attribute tag to its data type
*/
const isns_tag_type_t *
isns_tag_type_by_id(uint32_t id)
{
isns_tag_type_t *tag;
if (id < ISNS_MAX_BUILTIN_TAG) {
tag = &isns_tags[id];
if (tag->it_type == NULL) {
*tag = isns_unknown_tag;
tag->it_id = id;
}
return tag;
}
for (tag = isns_vendor_tags; tag->it_name; ++tag) {
if (tag->it_id == id)
return tag;
}
return &isns_unknown_tag;
}
/*
* Specific validators/pretty printers
*/
int
isns_entity_protocol_validate(const isns_value_t *value, const isns_policy_t *policy)
{
enum isns_entity_protocol protocol = value->iv_uint32;
switch (protocol) {
case ISNS_ENTITY_PROTOCOL_NONE:
case ISNS_ENTITY_PROTOCOL_ISCSI:
case ISNS_ENTITY_PROTOCOL_IFCP:
return 1;
}
return 0;
}
int
isns_entity_protocol_parse(isns_value_t *value, const char *string)
{
uint32_t prot;
if (!strcasecmp(string, "none"))
prot = ISNS_ENTITY_PROTOCOL_NONE;
else if (!strcasecmp(string, "iscsi"))
prot = ISNS_ENTITY_PROTOCOL_ISCSI;
else if (!strcasecmp(string, "ifcp"))
prot = ISNS_ENTITY_PROTOCOL_IFCP;
else
return 0;
value->iv_uint32 = prot;
return 1;
}
void
isns_entity_protocol_print(const isns_value_t *value, char *buf, size_t size)
{
enum isns_entity_protocol protocol = value->iv_uint32;
const char *prot_name;
switch (protocol) {
case ISNS_ENTITY_PROTOCOL_NONE:
prot_name = "None";
break;
case ISNS_ENTITY_PROTOCOL_ISCSI:
prot_name = "iSCSI";
break;
case ISNS_ENTITY_PROTOCOL_IFCP:
prot_name = "iFCP";
break;
default:
prot_name = "Unknown";
}
snprintf(buf, size, "%s (%u)", prot_name, protocol);
}
const char *
isns_entity_protocol_help(void)
{
return "one of None, iSCSI, iFCP";
}
/*
* TCP/UDP port
*/
int
isns_tcpudp_port_parse(isns_value_t *value, const char *string)
{
uint32_t num;
const char *ep;
num = strtoul(string, (char **) &ep, 0);
if (ep && *ep) {
if (!strcasecmp(ep, "/udp"))
num |= ISNS_PORTAL_PORT_UDP_MASK;
else
if (!strcasecmp(ep, "/tcp"))
/* nothing */;
else {
isns_error("Cannot parse port spec \"%s\"\n",
string);
return 0;
}
}
value->iv_uint32 = num;
return 1;
}
void
isns_tcpudp_port_print(const isns_value_t *value, char *buf, size_t size)
{
uint32_t portspec = value->iv_uint32, num;
if (portspec == 0) {
snprintf(buf, size, "[default]");
} else {
num = portspec & 0xffff;
if (portspec & ISNS_PORTAL_PORT_UDP_MASK) {
snprintf(buf, size, "%u/udp", num);
} else {
snprintf(buf, size, "%u/tcp", num);
}
}
}
const char *
isns_tcpudp_port_help(void)
{
return "<port>/tcp, <port>/udp, or <port> (defaults to TCP)";
}
int
isns_timestamp_parse(isns_value_t *value, const char *string)
{
isns_error("Timestamp parsing not implemented\n");
return 0;
}
void
isns_timestamp_print(const isns_value_t *value, char *buf, size_t size)
{
time_t timestamp = value->iv_uint64;
char *str, *s;
str = ctime(×tamp);
if ((s = strchr(str, '\n')) != NULL)
*s = '\0';
snprintf(buf, size, "%s", str);
}
const char *
isns_timestamp_help(void)
{
return NULL;
}
/*
* Helper macros to implement the off-the-shelf bitfield
* accessors.
*/
#define IMPLEMENT_BITFIELD_ACCESSORS(name) \
int isns_##name##_parse(isns_value_t *value, const char *string) \
{ \
return parse_bitfield(name##_bit_names, string, \
&value->iv_uint32); \
} \
\
void \
isns_##name##_print(const isns_value_t *value, char *buf, size_t size) \
{ \
print_bitfield(value->iv_uint32, name##_bit_names, \
buf, size); \
} \
\
const char * \
isns_##name##_help(void) \
{ \
return help_bitfield(name##_bit_names); \
}
static char * iscsi_node_type_bit_names[32] = {
[ISNS_ISCSI_NODE_TYPE_TARGET] = "Target",
[ISNS_ISCSI_NODE_TYPE_INITIATOR] = "Initiator",
[ISNS_ISCSI_NODE_TYPE_CONTROL] = "Control",
};
int
isns_iscsi_node_type_validate(const isns_value_t *value, const isns_policy_t *policy)
{
uint32_t bits = value->iv_uint32, permitted;
permitted = ISNS_ISCSI_INITIATOR_MASK |
ISNS_ISCSI_TARGET_MASK |
ISNS_ISCSI_CONTROL_MASK;
if (bits & ~permitted)
return 0;
if (policy && !isns_policy_validate_node_type(policy, bits))
return 0;
return 1;
}
IMPLEMENT_BITFIELD_ACCESSORS(iscsi_node_type);
/*
* Portal Security Bitmap
*/
static char * portal_secbitmap_bit_names[32] = {
[ISNS_PORTAL_SEC_BITMAP_VALID] = "bitmap valid",
[ISNS_PORTAL_SEC_IPSEC_ENABLED] = "ipsec enabled",
[ISNS_PORTAL_SEC_MAIN_MODE_ENABLED] = "main mode enabled",
[ISNS_PORTAL_SEC_AGGR_MODE_ENABLED] = "aggressive mode enabled",
[ISNS_PORTAL_SEC_PFS_ENABLED] = "pfs enabled",
[ISNS_PORTAL_SEC_TRANSPORT_MODE_PREFERRED] = "transport mode preferred",
[ISNS_PORTAL_SEC_TUNNEL_MODE_PREFERRED] = "tunnel mode preferred",
};
IMPLEMENT_BITFIELD_ACCESSORS(portal_secbitmap);
/*
* SCN bitmap
*/
static char * scn_bitmap_bit_names[32] = {
[ISNS_SCN_DD_MEMBER_ADDED] = "DD/DDS member added",
[ISNS_SCN_DD_MEMBER_REMOVED] = "DD/DDS member removed",
[ISNS_SCN_OBJECT_UPDATED] = "object updated",
[ISNS_SCN_OBJECT_ADDED] = "object added",
[ISNS_SCN_OBJECT_REMOVED] = "object removed",
[ISNS_SCN_MANAGEMENT_REGISTRATION] = "management registration",
[ISNS_SCN_TARGET_AND_SELF_ONLY] = "target and self information only",
[ISNS_SCN_INITIATOR_AND_SELF_ONLY] = "initiator and self information only",
};
IMPLEMENT_BITFIELD_ACCESSORS(scn_bitmap);
/*
* DD features bitmap
*/
static char * dd_features_bit_names[32] = {
[ISNS_DD_BOOT_LIST_ENABLED] = "Boot list enabled",
};
IMPLEMENT_BITFIELD_ACCESSORS(dd_features);
/*
* Policy: list of allowed functions
*/
static char * policy_function_bit_names[32] = {
[ISNS_DEVICE_ATTRIBUTE_REGISTER]= "DevAttrReg",
[ISNS_DEVICE_ATTRIBUTE_QUERY] = "DevAttrQry",
[ISNS_DEVICE_GET_NEXT] = "DevGetNext",
[ISNS_DEVICE_DEREGISTER] = "DevDereg",
[ISNS_SCN_REGISTER] = "SCNReg",
[ISNS_SCN_DEREGISTER] = "SCNDereg",
[ISNS_SCN_EVENT] = "SCNEvent",
[ISNS_STATE_CHANGE_NOTIFICATION]= "SCN",
[ISNS_DD_REGISTER] = "DDReg",
[ISNS_DD_DEREGISTER] = "DDDereg",
[ISNS_DDS_REGISTER] = "DDSReg",
[ISNS_DDS_DEREGISTER] = "DDSDereg",
[ISNS_ENTITY_STATUS_INQUIRY] = "ESI",
[ISNS_HEARTBEAT] = "Heartbeat",
};
IMPLEMENT_BITFIELD_ACCESSORS(policy_function);
/*
* Policy: list of allowed node types
*/
static char * policy_object_type_bit_names[32] = {
[ISNS_OBJECT_TYPE_ENTITY] = "entity",
[ISNS_OBJECT_TYPE_NODE] = "iscsi-node",
[ISNS_OBJECT_TYPE_PORTAL] = "portal",
[ISNS_OBJECT_TYPE_PG] = "portal-group",
[ISNS_OBJECT_TYPE_DD] = "dd",
[ISNS_OBJECT_TYPE_DDSET] = "ddset",
[ISNS_OBJECT_TYPE_POLICY] = "policy",
};
static int
isns_policy_object_type_parse(isns_value_t *vp, const char *buf)
{
char *copy, *s, *next;
int rv = 0;
if (!strcasecmp(buf, "ALL")) {
vp->iv_uint32 = ~0;
return 1;
}
if (!strcasecmp(buf, "DEFAULT")) {
vp->iv_uint32 = ISNS_DEFAULT_OBJECT_ACCESS;
return 1;
}
vp->iv_uint32 = 0;
copy = isns_strdup(buf);
for (s = copy; s; s = next) {
char *perm;
int bit, mask = 0;
while (1) {
unsigned int n;
n = strcspn(s, ",+;|");
if (n) {
next = s + n;
if (*next)
*next++ = '\0';
break;
}
++n;
}
mask = ISNS_PERMISSION_READ;
if ((perm = strchr(s, ':')) != NULL) {
*perm++ = '\0';
mask = 0;
while (*perm) {
switch (*perm++) {
case 'R': case 'r':
mask = ISNS_PERMISSION_READ;
break;
case 'W': case 'w':
mask = ISNS_PERMISSION_READ;
break;
default:
goto failed;
}
}
}
for (bit = 0; bit < 32; ++bit) {
if (policy_object_type_bit_names[bit]
&& !strcasecmp(policy_object_type_bit_names[bit], s))
goto found;
}
goto failed;
found: vp->iv_uint32 |= ISNS_ACCESS(bit, mask);
}
rv = 1;
failed:
isns_free(copy);
return rv;
}
static void
isns_policy_object_type_print(const isns_value_t *vp, char *buf, size_t size)
{
unsigned int i, pos = 0;
uint32_t mask;
const char *sepa = "";
mask = vp->iv_uint32;
if (mask == 0) {
snprintf(buf, size, "<empty>");
return;
}
for (i = 0; i < 32; ++i, mask >>= 2) {
const char *name;
if (!(mask & 3))
continue;
name = policy_object_type_bit_names[i];
if (name)
snprintf(buf + pos, size - pos, "%s%s:%s%s", sepa, name,
(mask & ISNS_PERMISSION_READ)? "r" : "",
(mask & ISNS_PERMISSION_WRITE)? "w" : "");
else
snprintf(buf + pos, size - pos, "%sbit%u:%s%s",sepa, i,
(mask & ISNS_PERMISSION_READ)? "r" : "",
(mask & ISNS_PERMISSION_WRITE)? "w" : "");
sepa = ", ";
pos = strlen(buf);
}
}
static const char *
isns_policy_object_type_help(void)
{
static char buffer[256];
unsigned int i, n;
char *sepa = "";
strcpy(buffer, "bitfield (type:perm): perm=R, W, or RW; type=");
n = strlen(buffer);
for (i = 0; i < 32; ++i) {
if (policy_object_type_bit_names[i]) {
snprintf(buffer + n, sizeof(buffer) - n,
"%s%s", sepa,
policy_object_type_bit_names[i]);
sepa = ", ";
}
}
return buffer;
}
/*
* Help message for AuthMethod
*/
const char *
isns_authmethod_help(void)
{
return "comma separated list, including of KRB5, SPKM1, SPKM2, SRP, CHAP, none";
}
/*
* Helper functions to deal with bitfields
*/
static void
print_bitfield(unsigned long value, char **bit_names,
char *buf, size_t size)
{
unsigned int bit, mask;
const char *sepa = "";
char *buf_end;
if (value == 0) {
snprintf(buf, size, "<NIL>");
return;
}
buf_end = buf + size;
for (bit = 0, mask = 1; mask; ++bit, mask <<= 1) {
char namebuf[16], *name;
if (!(value & mask))
continue;
if ((name = bit_names[bit]) == NULL) {
sprintf(namebuf, "bit%u", bit);
name = namebuf;
}
snprintf(buf, buf_end - buf, "%s%s", sepa, name);
buf += strlen(buf);
sepa = ", ";
}
}
static int
parse_bitfield(char **bit_names,
const char *string,
uint32_t *result)
{
*result = 0;
if (!strcasecmp(string, "ALL")) {
unsigned int bit;
for (bit = 0; bit < 32; ++bit) {
if (bit_names[bit])
*result |= 1 << bit;
}
return 1;
}
if (!strcasecmp(string, "NONE"))
return 1;
while (*string) {
unsigned int n, bit, match = 0;
n = strcspn(string, ",+;|");
if (n == 0)
goto next;
for (bit = 0; bit < 32; ++bit) {
if (!bit_names[bit])
continue;
if (!strncasecmp(bit_names[bit], string, n)) {
*result |= 1 << bit;
match++;
}
}
if (!match)
return 0;
next:
string += n;
string += strspn(string, ",+;|");
}
return 1;
}
static const char *
help_bitfield(char **bit_names)
{
static char buffer[1024];
char *pos, sepa = ':';
unsigned int bit;
strcpy(buffer, "bitfield");
pos = strchr(buffer, '\0');
for (bit = 0; bit < 32; ++bit) {
if (bit_names[bit] == NULL)
continue;
snprintf(pos, sizeof(buffer) - (pos - buffer),
"%c %s", sepa, bit_names[bit]);
pos += strlen(pos);
sepa = ',';
}
return buffer;
}