/*
 * iSCSI iface helpers
 *
 * Copyright (C) 2008 Mike Christie
 * Copyright (C) 2008 Red Hat, Inc. All rights reserved.
 * maintained by open-iscsi@@googlegroups.com
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published
 * by the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 *
 * See the file COPYING included with this distribution for more details.
 */
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <dirent.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <libopeniscsiusr/libopeniscsiusr.h>

#include "log.h"
#include "list.h"
#include "iscsi_sysfs.h"
#include "iscsi_settings.h"
#include "config.h"
#include "transport.h"
#include "idbm.h"
#include "iface.h"
#include "session_info.h"
#include "host.h"
#include "fw_context.h"
#include "sysdeps.h"
#include "iscsi_err.h"
#include "iscsi_netlink.h"

#define _unwrap(x) (x && strlen(x)) ? x : UNKNOWN_VALUE

/*
 * Default ifaces for use with transports that do not bind to hardware
 * by defaults (transports that let the interconnect layer to the routing
 * by defaults).
 */

/*
 * iSCSI over TCP/IP
 */
static struct iface_rec iface_default = {
	.name		= "default",
	.transport_name	= "tcp",
};

/*
 * iSER
 */
static struct iface_rec iface_iser = {
	.name		= "iser",
	.transport_name	= "iser",
};

static struct iface_rec *default_ifaces[] = {
	&iface_default,
	&iface_iser,
	NULL,
};

static struct iface_rec *iface_match_default(struct iface_rec *iface)
{
	struct iface_rec *def_iface;
	int i = 0;

	while ((def_iface = default_ifaces[i++])) {
		if (!strcmp(iface->name, def_iface->name))
			return def_iface;
	}
	return NULL;
}

static void iface_init(struct iface_rec *iface)
{
	if (!strlen(iface->name))
		sprintf(iface->name, DEFAULT_IFACENAME);
}

/*
 * default is to use tcp through whatever the network layer
 * selects for us with the /etc/iscsi/initiatorname.iscsi iname.
 */
void iface_setup_defaults(struct iface_rec *iface)
{
	sprintf(iface->transport_name, DEFAULT_TRANSPORT);
	iface_init(iface);
}

struct iface_rec *iface_alloc(char *ifname, int *err)
{
	struct iface_rec *iface;

	if (!strlen(ifname) || strlen(ifname) + 1 > ISCSI_MAX_IFACE_LEN) {
		*err = ISCSI_ERR_INVAL;
		return NULL;
	}

	iface = calloc(1, sizeof(*iface));
	if (!iface) {
		*err = ISCSI_ERR_NOMEM;
		return NULL;
	}

	strlcpy(iface->name, ifname, ISCSI_MAX_IFACE_LEN);
	INIT_LIST_HEAD(&iface->list);
	return iface;
}

static int __iface_conf_read(struct iface_rec *iface)
{
	char *iface_conf;
	recinfo_t *info;
	FILE *f;
	int rc = 0;

	iface_conf = calloc(1, PATH_MAX);
	if (!iface_conf)
		return ISCSI_ERR_NOMEM;

	info = idbm_recinfo_alloc(MAX_KEYS);
	if (!info) {
		rc = ISCSI_ERR_NOMEM;
		goto free_conf;
	}

	snprintf(iface_conf, PATH_MAX, "%s/%s", IFACE_CONFIG_DIR,
		 iface->name);

	log_debug(5, "looking for iface conf %s", iface_conf);
	f = fopen(iface_conf, "r");
	if (!f) {
		/*
		 * if someone passes in default but has not defined
		 * an iface with default then we do it for them
		 */
		if (!strcmp(iface->name, DEFAULT_IFACENAME)) {
			iface_setup_defaults(iface);
			rc = 0;
		} else
			rc = ISCSI_ERR_IDBM;
		goto free_info;
	}

	iface_init(iface);
	idbm_recinfo_iface(iface, info);
	idbm_recinfo_config(info, f);
	fclose(f);

free_info:
	free(info);
free_conf:
	free(iface_conf);
	return rc;
}

int iface_conf_read(struct iface_rec *iface)
{
	struct iface_rec *def_iface;
	int rc, retry = 0;

	def_iface = iface_match_default(iface);
	if (def_iface) {
		/*
		 * older tools allowed default to have different
		 * transport_names so we do not want to overwrite
		 * it.
		 */
		if (!strcmp(def_iface->name, DEFAULT_IFACENAME)) {
			if (!strlen(iface->name))
				strcpy(iface->name, def_iface->name);
			if (!strlen(iface->netdev))
				strcpy(iface->netdev, def_iface->netdev);
			if (!strlen(iface->hwaddress))
				strcpy(iface->hwaddress, def_iface->hwaddress);
			if (!strlen(iface->transport_name))
				strcpy(iface->transport_name,
				       def_iface->transport_name);
			if (!strlen(iface->iname))
				strcpy(iface->iname, def_iface->iname);
		} else {
			iface_init(iface);
			iface_copy(iface, def_iface);
		}
		return 0;
	}

retry_read:
	rc = idbm_lock();
	if (rc)
		return rc;

	rc = __iface_conf_read(iface);
	idbm_unlock();

	/*
	 * cmd was run before running -m iface, so force def bindings
	 * creation to see if that was the one requested
	 */
	if (retry < 1 && rc == ISCSI_ERR_IDBM) {
		iface_setup_host_bindings();
		retry++;
		goto retry_read;
	}

	return rc;
}

int iface_conf_delete(struct iface_rec *iface)
{
	struct iface_rec *def_iface;
	char *iface_conf;
	int rc = 0;

	def_iface = iface_match_default(iface);
	if (def_iface) {
		log_error("iface %s is a special interface and "
			  "cannot be deleted.", iface->name);
		return ISCSI_ERR_INVAL;
	}

	iface_conf = calloc(1, PATH_MAX);
	if (!iface_conf)
		return ISCSI_ERR_NOMEM;

	sprintf(iface_conf, "%s/%s", IFACE_CONFIG_DIR, iface->name);
	rc = idbm_lock();
	if (rc)
		goto free_conf;

	if (unlink(iface_conf))
		rc = ISCSI_ERR_IDBM;
	idbm_unlock();

free_conf:
	free(iface_conf);
	return rc;
}

int iface_conf_write(struct iface_rec *iface)
{
	struct iface_rec *def_iface;
	char *iface_conf;
	FILE *f;
	int rc = 0;

	def_iface = iface_match_default(iface);
	if (def_iface) {
		log_error("iface %s is a special interface and "
			  "is not stored in %s.", iface->name,
			  IFACE_CONFIG_DIR);
		return ISCSI_ERR_INVAL;
	}

	iface_conf = calloc(1, PATH_MAX);
	if (!iface_conf)
		return ISCSI_ERR_NOMEM;

	sprintf(iface_conf, "%s/%s", IFACE_CONFIG_DIR, iface->name);
	f = fopen(iface_conf, "w");
	if (!f) {
		rc = ISCSI_ERR_IDBM;
		goto free_conf;
	}

	rc = idbm_lock();
	if (rc)
		goto close_f;

	idbm_print(IDBM_PRINT_TYPE_IFACE, iface, 1, f);
	idbm_unlock();

close_f:
	fclose(f);
free_conf:
	free(iface_conf);
	return rc;
}

int iface_conf_update(struct list_head *params, struct iface_rec *iface)
{
	struct iface_rec *def_iface;
	recinfo_t *info;
	struct user_param *param;
	int rc = 0;

	def_iface = iface_match_default(iface);
	if (def_iface) {
		log_error("iface %s is a special interface and "
			  "cannot be modified.", iface->name);
		return ISCSI_ERR_INVAL;
	}

	info = idbm_recinfo_alloc(MAX_KEYS);
	if (!info)
		return ISCSI_ERR_NOMEM;

	idbm_recinfo_iface(iface, info);

	list_for_each_entry(param, params, list) {
		rc = idbm_verify_param(info, param->name);
		if (rc)
			goto free_info;
	}

	list_for_each_entry(param, params, list) {
		rc = idbm_rec_update_param(info, param->name, param->value, 0);
		if (rc)
			goto free_info;
	}

	rc = iface_conf_write(iface);
free_info:
	free(info);
	return rc;
}

#if 0 /* Unused */
static int iface_get_next_id(void)
{
	struct stat statb;
	char *iface_conf;
	int i, rc = ENOSPC;

	iface_conf = calloc(1, PATH_MAX);
	if (!iface_conf)
		return ENOMEM;

	for (i = 0; i < INT_MAX; i++) {
		memset(iface_conf, 0, PATH_MAX);
		/* check len */
		snprintf(iface_conf, PATH_MAX, "iface%d", i);
		if (strlen(iface_conf) > ISCSI_MAX_IFACE_LEN - 1) {
			log_error("iface namespace is full. Remove unused "
				  "iface definitions from %s or send mail "
				  "to open-iscsi@googlegroups.com to report "
				  "the problem", IFACE_CONFIG_DIR);
			rc = ENOSPC;
			break;
		}
		memset(iface_conf, 0, PATH_MAX);
		snprintf(iface_conf, PATH_MAX, "%s/iface%d", IFACE_CONFIG_DIR,
			i);

		if (!stat(iface_conf, &statb))
			continue;
		if (errno == ENOENT) {
			rc = i;
			break;
		}
	}

	free(iface_conf);
        return rc;
}
#endif /* Unused */

struct iface_search {
	struct iface_rec *pattern;
	struct iface_rec *found;
};

static int __iface_get_by_net_binding(void *data, struct iface_rec *iface)
{
	struct iface_search *search = data;

	if (!strcmp(search->pattern->name, iface->name)) {
		iface_copy(search->found, iface);
		return 1;
	}

	if (iface_is_bound_by_hwaddr(search->pattern)) {
		if (!strcasecmp(iface->hwaddress,
				search->pattern->hwaddress)) {
			iface_copy(search->found, iface);
			return 1;
		} else
			return 0;
	}

	if (iface_is_bound_by_netdev(search->pattern)) {
		if (!strcmp(iface->netdev, search->pattern->netdev)) {
			iface_copy(search->found, iface);
			return 1;
		} else
			return 0;
	}

/*
	if (iface_is_bound_by_ipaddr(search->pattern)) {
		if (!strcmp(iface->ipaddress, search->pattern->ipaddress)) {
			iface_copy(search->found, iface);
			return 1;
		} else
			return 0;
	}
*/
	return 0;
}

/*
 * Before 2.0.870, we only could bind by netdeivce or hwaddress,
 * so we did a simple reverse lookup to go from sysfs info to
 * the iface name. After 2.0.870 we added a lot of options to the
 * iface binding so we added the ifacename to the kernel.
 *
 * This function is for older kernels that do not export the ifacename.
 * If the user was doing iscsi_tcp session binding we will find
 * the iface by matching net info.
 */
int iface_get_by_net_binding(struct iface_rec *pattern,
			     struct iface_rec *out_rec)
{
	int num_found = 0, rc;
	struct iface_search search;

	if (!iface_is_bound_by_hwaddr(pattern) &&
	    !iface_is_bound_by_netdev(pattern)) {
		sprintf(out_rec->name, DEFAULT_IFACENAME);
		return 0;
	}

	search.pattern = pattern;
	search.found = out_rec;

	rc = iface_for_each_iface(&search, 0, &num_found,
				  __iface_get_by_net_binding);
	if (rc == 1)
		return 0;
	return ISCSI_ERR_NO_OBJS_FOUND;
}

int iface_get_iptype(struct iface_rec *iface)
{
	/* address might not be set if user config with another tool */
	if (!strlen(iface->ipaddress) ||
	    !strcmp(UNKNOWN_VALUE, iface->ipaddress)) {
		/* try to figure out by name */
		if (strstr(iface->name, "ipv4"))
			return ISCSI_IFACE_TYPE_IPV4;
		else if (strstr(iface->name, "ipv6"))
			return ISCSI_IFACE_TYPE_IPV6;
		else	/* assume ipv4 by default */
			return ISCSI_IFACE_TYPE_IPV4;
	} else {
		if (strcmp(iface->bootproto, "dhcp") &&
		    !strstr(iface->ipaddress, "."))
			return ISCSI_IFACE_TYPE_IPV6;
		else
			return ISCSI_IFACE_TYPE_IPV4;
	}
}

static int iface_setup_binding_from_kern_iface(void *data,
					       struct iface_rec *kern_iface)
{
	struct host_info *hinfo = data;
	struct iface_rec iface;
	char iface_path[PATH_MAX];

	if (!strlen(hinfo->iface.hwaddress)) {
		log_error("Invalid offload iSCSI host %u. Missing "
			  "hwaddress. Try upgrading %s driver.",
			  hinfo->host_no, hinfo->iface.transport_name);
		return 0;
	}

	memset(&iface, 0, sizeof(struct iface_rec));
	if (kern_iface) {
		memcpy(&iface, kern_iface, sizeof(iface));

		snprintf(iface.name, sizeof(iface.name), "%s.%s.%s.%u",
			 kern_iface->transport_name,
			 kern_iface->hwaddress,
			 iface_get_iptype(kern_iface) == ISCSI_IFACE_TYPE_IPV4 ?
			 "ipv4" : "ipv6", kern_iface->iface_num);
	} else {
		snprintf(iface.name, sizeof(iface.name), "%s.%s",
			 hinfo->iface.transport_name, hinfo->iface.hwaddress);
	}

	strcpy(iface.hwaddress, hinfo->iface.hwaddress);
	strcpy(iface.transport_name, hinfo->iface.transport_name);

	memset(iface_path, 0, sizeof(iface_path));
	snprintf(iface_path, PATH_MAX, "%s/%s", IFACE_CONFIG_DIR,
		 iface.name);

	if (access(iface_path, F_OK) != 0) {
		/* not found so create it */
		if (iface_conf_write(&iface)) {
			log_error("Could not create default iface conf %s.",
				  iface.name);
			/* fall through - will not be persistent */
		}
	}

	return 0;
}

static int __iface_setup_host_bindings(void *data, struct host_info *hinfo)
{
	struct iface_rec *def_iface;
	struct iscsi_transport *t;
	int i = 0, nr_found;

	t = iscsi_sysfs_get_transport_by_hba(hinfo->host_no);
	if (!t)
		return 0;

	/* do not setup binding for hosts using non offload drivers */
	while ((def_iface = default_ifaces[i++])) {
		if (!strcmp(t->name, def_iface->transport_name))
			return 0;
	}

	nr_found = 0;
	iscsi_sysfs_for_each_iface_on_host(hinfo, hinfo->host_no,
					   &nr_found,
					   iface_setup_binding_from_kern_iface);
	if (!nr_found)
		iface_setup_binding_from_kern_iface(hinfo, NULL);
	return 0;
}

/*
 * Create a default iface for offload cards. We assume that we will
 * be able identify each host by MAC.
 */
void iface_setup_host_bindings(void)
{
	int nr_found = 0;

	if (idbm_lock())
		return;

	if (access(IFACE_CONFIG_DIR, F_OK) != 0) {
		if (mkdir(IFACE_CONFIG_DIR, 0660) != 0) {
			log_error("Could not make %s. HW/OFFLOAD iscsi "
				  "may not be supported", IFACE_CONFIG_DIR);
			idbm_unlock();
			return;
		}
	}
	idbm_unlock();

	transport_probe_for_offload();

	if (iscsi_sysfs_for_each_host(NULL, &nr_found,
				      __iface_setup_host_bindings))
		log_error("Could not scan scsi hosts. HW/OFFLOAD iscsi "
			  "operations may not be supported, or please "
			  "see README for instructions on setting up ifaces.");
}

void iface_copy(struct iface_rec *dst, struct iface_rec *src)
{
	if (strlen(src->name))
		strcpy(dst->name, src->name);
	if (src->iface_num)
		dst->iface_num = src->iface_num;
	if (strlen(src->netdev))
		strcpy(dst->netdev, src->netdev);
	if (strlen(src->ipaddress))
		strcpy(dst->ipaddress, src->ipaddress);
	if (strlen(src->subnet_mask))
		strcpy(dst->subnet_mask, src->subnet_mask);
	if (strlen(src->gateway))
		strcpy(dst->gateway, src->gateway);
	if (strlen(src->bootproto))
		strcpy(dst->bootproto, src->bootproto);
	if (strlen(src->ipv6_linklocal))
		strcpy(dst->ipv6_linklocal, src->ipv6_linklocal);
	if (strlen(src->ipv6_router))
		strcpy(dst->ipv6_router, src->ipv6_router);
	if (strlen(src->ipv6_autocfg))
		strcpy(dst->ipv6_autocfg, src->ipv6_autocfg);
	if (strlen(src->linklocal_autocfg))
		strcpy(dst->linklocal_autocfg, src->linklocal_autocfg);
	if (strlen(src->router_autocfg))
		strcpy(dst->router_autocfg, src->router_autocfg);
	if (src->vlan_id)
		dst->vlan_id = src->vlan_id;
	if (src->vlan_priority)
		dst->vlan_priority = src->vlan_priority;
	if (strlen(src->vlan_state))
		strcpy(dst->vlan_state, src->vlan_state);
	if (strlen(src->state))
		strcpy(dst->state, src->state);
	if (src->mtu)
		dst->mtu = src->mtu;
	if (src->port)
		dst->port = src->port;
	if (strlen(src->delayed_ack))
		strcpy(dst->delayed_ack, src->delayed_ack);
	if (strlen(src->nagle))
		strcpy(dst->nagle, src->nagle);
	if (strlen(src->tcp_wsf_state))
		strcpy(dst->tcp_wsf_state, src->tcp_wsf_state);
	if (src->tcp_wsf)
		dst->tcp_wsf = src->tcp_wsf;
	if (src->tcp_timer_scale)
		dst->tcp_timer_scale = src->tcp_timer_scale;
	if (strlen(src->tcp_timestamp))
		strcpy(dst->tcp_timestamp, src->tcp_timestamp);
	if (strlen(src->dhcp_dns))
		strcpy(dst->dhcp_dns, src->dhcp_dns);
	if (strlen(src->dhcp_slp_da))
		strcpy(dst->dhcp_slp_da, src->dhcp_slp_da);
	if (strlen(src->tos_state))
		strcpy(dst->tos_state, src->tos_state);
	if (src->tos)
		dst->tos = src->tos;
	if (strlen(src->gratuitous_arp))
		strcpy(dst->gratuitous_arp, src->gratuitous_arp);
	if (strlen(src->dhcp_alt_client_id_state))
		strcpy(dst->dhcp_alt_client_id_state,
		       src->dhcp_alt_client_id_state);
	if (strlen(src->dhcp_alt_client_id))
		strcpy(dst->dhcp_alt_client_id, src->dhcp_alt_client_id);
	if (strlen(src->dhcp_req_vendor_id_state))
		strcpy(dst->dhcp_req_vendor_id_state,
		       src->dhcp_req_vendor_id_state);
	if (strlen(src->dhcp_vendor_id_state))
		strcpy(dst->dhcp_vendor_id_state, src->dhcp_vendor_id_state);
	if (strlen(src->dhcp_vendor_id))
		strcpy(dst->dhcp_vendor_id, src->dhcp_vendor_id);
	if (strlen(src->dhcp_learn_iqn))
		strcpy(dst->dhcp_learn_iqn, src->dhcp_learn_iqn);
	if (strlen(src->fragmentation))
		strcpy(dst->fragmentation, src->fragmentation);
	if (strlen(src->incoming_forwarding))
		strcpy(dst->incoming_forwarding, src->incoming_forwarding);
	if (src->ttl)
		dst->ttl = src->ttl;
	if (strlen(src->gratuitous_neighbor_adv))
		strcpy(dst->gratuitous_neighbor_adv,
		       src->gratuitous_neighbor_adv);
	if (strlen(src->redirect))
		strcpy(dst->redirect, src->redirect);
	if (strlen(src->mld))
		strcpy(dst->mld, src->mld);
	if (src->flow_label)
		dst->flow_label = src->flow_label;
	if (src->traffic_class)
		dst->traffic_class = src->traffic_class;
	if (src->hop_limit)
		dst->hop_limit = src->hop_limit;
	if (src->nd_reachable_tmo)
		dst->nd_reachable_tmo = src->nd_reachable_tmo;
	if (src->nd_rexmit_time)
		dst->nd_rexmit_time = src->nd_rexmit_time;
	if (src->nd_stale_tmo)
		dst->nd_stale_tmo = src->nd_stale_tmo;
	if (src->dup_addr_detect_cnt)
		dst->dup_addr_detect_cnt = src->dup_addr_detect_cnt;
	if (src->router_adv_link_mtu)
		dst->router_adv_link_mtu = src->router_adv_link_mtu;
	if (src->def_task_mgmt_tmo)
		dst->def_task_mgmt_tmo = src->def_task_mgmt_tmo;
	if (strlen(src->header_digest))
		strcpy(dst->header_digest, src->header_digest);
	if (strlen(src->data_digest))
		strcpy(dst->data_digest, src->data_digest);
	if (strlen(src->immediate_data))
		strcpy(dst->immediate_data, src->immediate_data);
	if (strlen(src->initial_r2t))
		strcpy(dst->initial_r2t, src->initial_r2t);
	if (strlen(src->data_seq_inorder))
		strcpy(dst->data_seq_inorder, src->data_seq_inorder);
	if (strlen(src->data_pdu_inorder))
		strcpy(dst->data_pdu_inorder, src->data_pdu_inorder);
	if (src->erl)
		dst->erl = src->erl;
	if (src->max_recv_dlength)
		dst->max_recv_dlength = src->max_recv_dlength;
	if (src->first_burst_len)
		dst->first_burst_len = src->first_burst_len;
	if (src->max_out_r2t)
		dst->max_out_r2t = src->max_out_r2t;
	if (src->max_burst_len)
		dst->max_burst_len = src->max_burst_len;
	if (strlen(src->chap_auth))
		strcpy(dst->chap_auth, src->chap_auth);
	if (strlen(src->bidi_chap))
		strcpy(dst->bidi_chap, src->bidi_chap);
	if (strlen(src->strict_login_comp))
		strcpy(dst->strict_login_comp, src->strict_login_comp);
	if (strlen(src->discovery_auth))
		strcpy(dst->discovery_auth, src->discovery_auth);
	if (strlen(src->discovery_logout))
		strcpy(dst->discovery_logout, src->discovery_logout);
	if (strlen(src->hwaddress))
		strcpy(dst->hwaddress, src->hwaddress);
	if (strlen(src->transport_name))
		strcpy(dst->transport_name, src->transport_name);
	if (strlen(src->iname))
		strcpy(dst->iname, src->iname);
}

int iface_is_valid(struct iface_rec *iface)
{
	if (!iface)
		return 0;

	if (!strlen(iface->name))
		return 0;

	if (!strlen(iface->transport_name))
		return 0;

	if (iface_is_bound_by_hwaddr(iface))
		return 1;

	if (iface_is_bound_by_netdev(iface))
		return 1;
//	if (iface_is_bound_by_ipaddr(iface))
//		return 1;

	/* bound by transport name */
	return 1;
}

int iface_match(struct iface_rec *pattern, struct iface_rec *iface)
{
	if (!pattern || !iface)
		return 1;

	if (!strlen(pattern->name))
		return 1;

	if (!strcmp(pattern->name, iface->name)) {
		if (!strcmp(pattern->name, DEFAULT_IFACENAME))
			return 1;
		/*
		 * For default we allow the same name, but different
		 * transports.
		 */
		if (!strlen(pattern->transport_name))
			return 1;

		if (!strcmp(pattern->transport_name, iface->transport_name))
			return 1;
		/* fall through */
	}
	return 0;
}

int iface_is_bound_by_hwaddr(struct iface_rec *iface)
{
	if (iface && strlen(iface->hwaddress) &&
	    strcmp(iface->hwaddress, DEFAULT_HWADDRESS))
		return 1;
	return 0;
}

int iface_is_bound_by_netdev(struct iface_rec *iface)
{
	if (iface && strlen(iface->netdev) &&
	   strcmp(iface->netdev, DEFAULT_NETDEV))
		return 1;
	return 0;
}

int iface_is_bound_by_ipaddr(struct iface_rec *iface)
{
	if (iface && strlen(iface->ipaddress) &&
	   strcmp(iface->ipaddress, DEFAULT_IPADDRESS))
		return 1;
	return 0;
}

void iface_print(struct iscsi_iface *iface, char *prefix)
{
	const char *ipaddress = iscsi_iface_ipaddress_get(iface);

	printf("%sIface Name: %s\n", prefix,
	       (strlen(iscsi_iface_name_get(iface)) > 0) ?
	       iscsi_iface_name_get(iface) : UNKNOWN_VALUE);

	printf("%sIface Transport: %s\n", prefix,
	       (strlen(iscsi_iface_transport_name_get(iface)) > 0) ?
	       iscsi_iface_transport_name_get(iface) : UNKNOWN_VALUE);

	printf("%sIface Initiatorname: %s\n", prefix,
	       (strlen(iscsi_iface_iname_get(iface)) > 0) ?
	       iscsi_iface_iname_get(iface) : UNKNOWN_VALUE);

	if (!strlen(ipaddress))
		printf("%sIface IPaddress: %s\n", prefix, UNKNOWN_VALUE);
	else if (strchr(ipaddress, '.'))
		printf("%sIface IPaddress: %s\n", prefix, ipaddress);
	else
		printf("%sIface IPaddress: [%s]\n", prefix, ipaddress);

	printf("%sIface HWaddress: %s\n", prefix,
	       (strlen(iscsi_iface_hwaddress_get(iface)) > 0) ?
	       iscsi_iface_hwaddress_get(iface) : UNKNOWN_VALUE);

	printf("%sIface Netdev: %s\n", prefix,
	       (strlen(iscsi_iface_netdev_get(iface)) > 0) ?
	       iscsi_iface_netdev_get(iface) : UNKNOWN_VALUE);
}

struct iface_print_node_data {
	struct node_rec *last_rec;
	struct iface_rec *match_iface;
};

static int iface_print_nodes(void *data, node_rec_t *rec)
{
	struct iface_print_node_data *print_data = data;

	if (!iface_match(print_data->match_iface, &rec->iface))
		return -1;

	idbm_print_node_tree(print_data->last_rec, rec, "\t");
	return 0;
}

/**
 * iface_print_tree - print out binding info
 * @iface: iface to print out
 *
 * Currently this looks like the iface conf print, because we only
 * have the binding info. When we store the iface specific node settings
 * in the iface record then it will look different.
 */
int iface_print_tree(void *data, struct iface_rec *iface)
{
	struct node_rec last_rec;
	struct iface_print_node_data print_data;
	int num_found = 0;

	printf("Iface: %s\n", iface->name);

	memset(&last_rec, 0, sizeof(struct node_rec ));

	print_data.match_iface = iface;
	print_data.last_rec = &last_rec;

	idbm_for_each_rec(&num_found, &print_data, iface_print_nodes);
	return 0;
}

void iface_print_flat(struct iscsi_iface *iface)
{
	printf("%s %s,%s,%s,%s,%s\n",
	       _unwrap(iscsi_iface_name_get(iface)),
	       _unwrap(iscsi_iface_transport_name_get(iface)),
	       _unwrap(iscsi_iface_hwaddress_get(iface)),
	       _unwrap(iscsi_iface_ipaddress_get(iface)),
	       _unwrap(iscsi_iface_netdev_get(iface)),
	       _unwrap(iscsi_iface_iname_get(iface)));
}

int iface_for_each_iface(void *data, int skip_def, int *nr_found,
			 iface_op_fn *fn)
{
	DIR *iface_dirfd;
	struct dirent *iface_dent;
	struct iface_rec *iface, *def_iface;
	int err = 0, i = 0;

	if (!skip_def) {
		while ((def_iface = default_ifaces[i++])) {
			iface = iface_alloc(def_iface->name, &err);
			if (!iface) {
				log_error("Could not add iface %s.",
					  def_iface->name);
				continue;
			}
			iface_copy(iface, def_iface);
			err = fn(data, iface);
			free(iface);
			if (err)
				return err;
			(*nr_found)++;
		}
	}

	iface_dirfd = opendir(IFACE_CONFIG_DIR);
	if (!iface_dirfd)
		return errno;

	while ((iface_dent = readdir(iface_dirfd))) {
		if (!strcmp(iface_dent->d_name, ".") ||
		    !strcmp(iface_dent->d_name, ".."))
			continue;

		log_debug(5, "iface_for_each_iface found %s",
			 iface_dent->d_name);
		iface = iface_alloc(iface_dent->d_name, &err);
		if (!iface || err) {
			if (err == ISCSI_ERR_INVAL)
				log_error("Invalid iface name %s. Must be "
					  "from 1 to %d characters.",
					   iface_dent->d_name,
					   ISCSI_MAX_IFACE_LEN - 1);
			else
				log_error("Could not add iface %s.",
					  iface_dent->d_name);
			continue;
		}

		err = idbm_lock();
		if (err) {
			free(iface);
			continue;
		}

		err = __iface_conf_read(iface);
		idbm_unlock();
		if (err) {
			log_error("Could not read def iface %s (err %d)",
				  iface->name, err);
			free(iface);
			continue;
		}

		if (!iface_is_valid(iface)) {
			log_debug(5, "iface is not valid "
				  "Iface settings " iface_fmt,
				  iface_str(iface));
			free(iface);
			continue;
		}

		err = fn(data, iface);
		free(iface);
		if (err)
			break;
		(*nr_found)++;
	}

	closedir(iface_dirfd);
	return err;
}

static int iface_link(void *data, struct iface_rec *iface)
{
	struct list_head *ifaces = data;
	struct iface_rec *iface_copy;

	iface_copy = calloc(1, sizeof(*iface_copy));
	if (!iface_copy)
		return ISCSI_ERR_NOMEM;

	memcpy(iface_copy, iface, sizeof(*iface_copy));
	INIT_LIST_HEAD(&iface_copy->list);
	list_add_tail(&iface_copy->list, ifaces);
	return 0;
}

/**
 * iface_link_ifaces - link non default ifaces
 * @ifaces: list to add ifaces to
 *
 * This will return a list of the ifaces created by iscsiadm
 * or the user. It does not return the static default ones.
 */
void iface_link_ifaces(struct list_head *ifaces)
{
	int nr_found = 0;

	iface_for_each_iface(ifaces, 1, &nr_found, iface_link);
}

/**
 * iface_setup_from_boot_context - setup iface from boot context info
 * @iface: iface t setup
 * @context: boot context info
 *
 * Returns 1 if setup for offload.
 */
int iface_setup_from_boot_context(struct iface_rec *iface,
				   struct boot_context *context)
{
	struct iscsi_transport *t = NULL;
	uint32_t hostno;

	if (strlen(context->initiatorname))
		strlcpy(iface->iname, context->initiatorname,
			sizeof(iface->iname));

	if (strlen(context->scsi_host_name)) {
		if (sscanf(context->scsi_host_name,
			   "iscsi_boot%u", &hostno) != 1) {
			log_error("Could not parse %s's host no.",
				  context->scsi_host_name);
			return 0;
		}
	} else if (strlen(context->iface)) {
/* this ifdef is only temp until distros and firmwares are updated */
#ifdef OFFLOAD_BOOT_SUPPORTED
		char transport_name[ISCSI_TRANSPORT_NAME_MAXLEN];
		int rc;

		memset(transport_name, 0, ISCSI_TRANSPORT_NAME_MAXLEN);
		/* make sure offload driver is loaded */
		if (!net_get_transport_name_from_netdev(context->iface,
							transport_name))
			t = iscsi_sysfs_get_transport_by_name(transport_name);

		if (net_ifup_netdev(context->iface))
			log_warning("Could not bring up netdev %s for boot",
				    context->iface);

		hostno = iscsi_sysfs_get_host_no_from_hwaddress(context->mac,
								&rc);
		if (rc) {
			/*
			 * If the MAC in the boot info does not match an iscsi
			 * host then the MAC must be for network card, so boot
			 * is not going to be offloaded.
			 */
			log_debug(3, "Could not match %s to host",
				  context->mac);
			return 0;
		}

		strlcpy(iface->netdev, context->iface, sizeof(iface->netdev));
#else
		return 0;
#endif
	} else
		return 0;

	/*
	 * set up for access through a offload card.
	 */
	if (!t)
		t = iscsi_sysfs_get_transport_by_hba(hostno);
	if (!t) {
		log_error("Could not get transport for host%u. "
			  "Make sure the iSCSI driver is loaded.",
			  hostno);
		return 0;
	}
	strcpy(iface->transport_name, t->name);

	memset(iface->name, 0, sizeof(iface->name));
	snprintf(iface->name, sizeof(iface->name), "%s.%s",
		 iface->transport_name, context->mac);
	strlcpy(iface->hwaddress, context->mac,
		sizeof(iface->hwaddress));
	strlcpy(iface->ipaddress, context->ipaddr,
		sizeof(iface->ipaddress));
	iface->vlan_id = atoi(context->vlan);
	strlcpy(iface->subnet_mask, context->mask,
		sizeof(iface->subnet_mask));
	strlcpy(iface->gateway, context->gateway,
		sizeof(iface->gateway));
	log_debug(1, "iface " iface_fmt "", iface_str(iface));
	return 1;
}

/**
 * iface_create_ifaces_from_boot_contexts - create ifaces based on boot info
 * @ifaces: list to store ifaces in
 * @targets: list of targets to create ifaces from
 *
 * This function will create an iface struct based on the boot info
 * and it will create (or update if existing already) an iface rec in
 * the ifaces dir based on the info.
 */
int iface_create_ifaces_from_boot_contexts(struct list_head *ifaces,
					   struct list_head *targets)
{
	struct boot_context *context;
	struct iface_rec *iface, *tmp_iface;
	int rc = 0;

	list_for_each_entry(context, targets, list) {
		rc = 0;
		/* use dummy name. If valid it will get overwritten below */
		iface = iface_alloc(DEFAULT_IFACENAME, &rc);
		if (!iface) {
			log_error("Could not setup iface %s for boot",
				  context->iface);
			goto fail;
		}
		if (!iface_setup_from_boot_context(iface, context)) {
			/* no offload so forget it */
			free(iface);
			continue;
		}

		rc = iface_conf_write(iface);
		if (rc) {
			log_error("Could not setup default iface conf "
				  "for %s.", iface->name);
			free(iface);
			goto fail;
		}
		list_add_tail(&iface->list, ifaces);
	}

	return 0;
fail:
	list_for_each_entry_safe(iface, tmp_iface, ifaces, list) {
		list_del(&iface->list);
		free(iface);
	}
	return rc;
}

struct iface_param_count {
	struct iface_rec *primary;
	int count;
};

#define IFACE_NET_PARAM_EN_CNT(param_val, cnt) {	\
	if (!strcmp(param_val, "disable") ||		\
	    !strcmp(param_val, "enable"))		\
		(*cnt)++;				\
}

/**
 * iface_get_common_param_count - Gets common parameters count for given iface
 * @iface: iface to setup
 * @count: number of parameters to set
 */
static void iface_get_common_param_count(struct iface_rec *iface, int *count)
{
	if (strcmp(iface->vlan_state, "disable")) {
		/* vlan_state enabled */
		(*count)++;

		if (iface->vlan_id)
			/* For vlan value */
			(*count)++;
	} else {
		/* vlan_state disabled */
		(*count)++;
	}

	if (iface->mtu)
		(*count)++;

	if (iface->port)
		(*count)++;

	IFACE_NET_PARAM_EN_CNT(iface->delayed_ack, count);
	IFACE_NET_PARAM_EN_CNT(iface->nagle, count);
	IFACE_NET_PARAM_EN_CNT(iface->tcp_wsf_state, count);
	IFACE_NET_PARAM_EN_CNT(iface->tcp_timestamp, count);
	IFACE_NET_PARAM_EN_CNT(iface->redirect, count);
	IFACE_NET_PARAM_EN_CNT(iface->header_digest, count);
	IFACE_NET_PARAM_EN_CNT(iface->data_digest, count);
	IFACE_NET_PARAM_EN_CNT(iface->immediate_data, count);
	IFACE_NET_PARAM_EN_CNT(iface->initial_r2t, count);
	IFACE_NET_PARAM_EN_CNT(iface->data_seq_inorder, count);
	IFACE_NET_PARAM_EN_CNT(iface->data_pdu_inorder, count);
	IFACE_NET_PARAM_EN_CNT(iface->chap_auth, count);
	IFACE_NET_PARAM_EN_CNT(iface->bidi_chap, count);
	IFACE_NET_PARAM_EN_CNT(iface->strict_login_comp, count);
	IFACE_NET_PARAM_EN_CNT(iface->discovery_auth, count);
	IFACE_NET_PARAM_EN_CNT(iface->discovery_logout, count);

	if (iface->tcp_wsf)
		(*count)++;

	if (iface->tcp_timer_scale)
		(*count)++;

	if (iface->def_task_mgmt_tmo)
		(*count)++;

	if (iface->erl)
		(*count)++;

	if (iface->max_recv_dlength)
		(*count)++;

	if (iface->first_burst_len)
		(*count)++;

	if (iface->max_burst_len)
		(*count)++;

	if (iface->max_out_r2t)
		(*count)++;
}

/**
 * __iface_get_param_count - Gets netconfig parameter count for given iface
 * @data: iface_param_count structure
 * @iface: iface to setup
 */
static int __iface_get_param_count(void *data, struct iface_rec *iface)
{
	struct iface_param_count *iface_params = data;
	int iptype = ISCSI_IFACE_TYPE_IPV4;
	int count = 0;

	if (strcmp(iface_params->primary->hwaddress, iface->hwaddress))
		return 0;

	iptype = iface_get_iptype(iface);
	if (iptype == ISCSI_IFACE_TYPE_IPV4) {

		if (strcmp(iface->state, "disable")) {
			if (strstr(iface->bootproto, "dhcp")) {
				/* DHCP enabled */
				count++;
			} else {
				/* DHCP disabled */
				count++;

				if (strstr(iface->ipaddress, ".")) {
					/* User configured IPv4 Address */
					count++;

					if (strstr(iface->subnet_mask, "."))
						/* User configured Subnet */
						count++;

					if (strstr(iface->gateway, "."))
						/* User configured Gateway */
						count++;
				} else {
					/*
					 * IPv4 Address not valid, decrement
					 * count of DHCP
					 */
					count--;
				}
			}

			/*
			 * If IPv4 configuration in iface file is valid,
			 * enable state and other parameters (if any)
			 */
			if (count) {
				/* iface state */
				count++;

				IFACE_NET_PARAM_EN_CNT(iface->dhcp_dns,
						       &count);

				IFACE_NET_PARAM_EN_CNT(iface->dhcp_slp_da,
						       &count);

				IFACE_NET_PARAM_EN_CNT(iface->tos_state,
						       &count);

				IFACE_NET_PARAM_EN_CNT(iface->gratuitous_arp,
						       &count);

				IFACE_NET_PARAM_EN_CNT(
						iface->dhcp_alt_client_id_state,
						&count);

				if (iface->dhcp_alt_client_id[0])
					count++;

				IFACE_NET_PARAM_EN_CNT(
						iface->dhcp_req_vendor_id_state,
						&count);

				IFACE_NET_PARAM_EN_CNT(
						iface->dhcp_vendor_id_state,
						&count);

				if (iface->dhcp_vendor_id[0])
					count++;

				IFACE_NET_PARAM_EN_CNT(iface->dhcp_learn_iqn,
						       &count);

				IFACE_NET_PARAM_EN_CNT(iface->fragmentation,
						       &count);

				IFACE_NET_PARAM_EN_CNT(
						     iface->incoming_forwarding,
						     &count);

				if (iface->tos)
					count++;

				if (iface->ttl)
					count++;

				iface_get_common_param_count(iface, &count);
			}
		} else {
			/* IPv4 is disabled, iface state */
			count++;
		}
	} else if (iptype == ISCSI_IFACE_TYPE_IPV6) {

		if (strcmp(iface->state, "disable")) {

			/* IPv6 Address */
			if (strstr(iface->ipv6_autocfg, "nd") ||
			    strstr(iface->ipv6_autocfg, "dhcpv6")) {
				/* Autocfg enabled */
				count++;
			} else {
				/* Autocfg disabled */
				count++;

				if (strstr(iface->ipaddress, ":"))
					/* User configured IPv6 Address */
					count++;
				else
					/*
					 * IPv6 Address not valid, decrement
					 * count of IPv6 Autocfg
					 */
					count--;
			}

			/* IPv6 LinkLocal Address */
			if (strstr(iface->linklocal_autocfg, "auto"))
				/* Autocfg enabled */
				count++;
			else {
				/* Autocfg disabled */
				count++;

				if (strstr(iface->ipv6_linklocal, ":"))
					/* User configured LinkLocal Address */
					count++;
				else
					/*
					 * LinkLocal Address not valid,
					 * decrement count of LinkLocal Autocfg
					 */
					count--;
			}

			/* IPv6 Router Address */
			if (strstr(iface->router_autocfg, "auto"))
				/* Autocfg enabled */
				count++;
			else {
				/* Autocfg disabled */
				count++;

				if (strstr(iface->ipv6_router, ":"))
					/* User configured Router Address */
					count++;
				else
					/*
					 * Router Address not valid,
					 * decrement count of Router Autocfg
					 */
					count--;
			}

			/*
			 * If IPv6 configuration in iface file is valid,
			 * enable state and other parameters (if any)
			 */
			if (count) {
				/* iface state */
				count++;

				IFACE_NET_PARAM_EN_CNT(
						 iface->gratuitous_neighbor_adv,
						 &count);

				IFACE_NET_PARAM_EN_CNT(iface->mld, &count);

				if (iface->flow_label)
					count++;

				if (iface->traffic_class)
					count++;

				if (iface->hop_limit)
					count++;

				if (iface->nd_reachable_tmo)
					count++;

				if (iface->nd_rexmit_time)
					count++;

				if (iface->nd_stale_tmo)
					count++;

				if (iface->dup_addr_detect_cnt)
					count++;

				if (iface->router_adv_link_mtu)
					count++;

				iface_get_common_param_count(iface, &count);
			}
		} else {
			/* IPv6 is disabled, iface state */
			count++;
		}
	}

	iface_params->count += count;
	return 0;
}

/**
 * iface_get_param_count - Gets netconfig parameter count from iface
 * @iface: iface to setup
 * @iface_all: Flag for number of ifaces to traverse (1 for all)
 *
 * Returns netconfig parameter count.
 */
int iface_get_param_count(struct iface_rec *iface, int iface_all)
{
	int num_found = 0, rc;
	struct iface_param_count iface_params;

	log_debug(8, "In iface_get_param_count");

	iface_params.primary = iface;
	iface_params.count = 0;

	if (iface_all)
		rc = iface_for_each_iface(&iface_params, 0, &num_found,
					  __iface_get_param_count);
	else
		rc = __iface_get_param_count(&iface_params, iface);

	log_debug(8, "iface_get_param_count: rc = %d, count = %d",
		  rc, iface_params.count);
	return iface_params.count;
}

/* write integer parameter value */
static int iface_fill_int_param_val(struct iovec *iov, uint32_t iface_num,
				    uint8_t iface_type, uint16_t param,
				    uint8_t param_type, uint32_t param_len,
				    uint32_t param_val)
{
	int len;
	struct iscsi_iface_param_info *net_param;
	struct nlattr *attr;
	uint8_t val8 = 0;
	uint16_t val16 = 0;
	uint32_t val32 = 0;
	char *val = NULL;

	len = sizeof(struct iscsi_iface_param_info) + param_len;
	iov->iov_base = iscsi_nla_alloc(param, len);
	if (!(iov->iov_base))
		return 1;

	attr = iov->iov_base;
	iov->iov_len = NLA_ALIGN(attr->nla_len);
	net_param = (struct iscsi_iface_param_info *)ISCSI_NLA_DATA(attr);
	net_param->iface_num = iface_num;
	net_param->len = param_len;
	net_param->param = param;
	net_param->iface_type = iface_type;
	net_param->param_type = param_type;
	switch (param_len) {
	case 1:
		val8 = (uint8_t)param_val;
		val = (char *)&val8;
		break;

	case 2:
		val16 = (uint16_t)param_val;
		val = (char *)&val16;
		break;

	case 4:
		val32 = (uint32_t)param_val;
		val = (char *)&val32;
		break;

	default:
		goto free;
	}
	memcpy(net_param->value, val, param_len);
	return 0;
free:
	free(iov->iov_base);
	iov->iov_base = NULL;
	iov->iov_len = 0;
	return 1;
}

#define IFACE_SET_PARAM_INTVAL(iov, inum, itype, param, ptype, plen,	\
			       ival, gcnt, lcnt) {			\
	if (ival && !iface_fill_int_param_val(iov, inum, itype, param,	\
					      ptype, plen, ival)) {	\
		(*gcnt)++;						\
		(*lcnt)++;						\
	}								\
}

/* IPv4/IPv6 VLAN_ID: decimal value <= 4095 */
static int iface_fill_vlan_id(struct iovec *iov, struct iface_rec *iface,
			      uint32_t iface_type)
{
	int len;
	struct iscsi_iface_param_info *net_param;
	uint16_t vlan = 0;
	struct nlattr *attr;

	len = sizeof(struct iscsi_iface_param_info) + 2;
	iov->iov_base = iscsi_nla_alloc(ISCSI_NET_PARAM_VLAN_TAG, len);
	if (!(iov->iov_base))
		return 1;

	attr = iov->iov_base;
	iov->iov_len = NLA_ALIGN(attr->nla_len);
	net_param = (struct iscsi_iface_param_info *)ISCSI_NLA_DATA(attr);
	net_param->param = ISCSI_NET_PARAM_VLAN_TAG;
	net_param->iface_type = iface_type;
	net_param->iface_num = iface->iface_num;
	net_param->param_type = ISCSI_NET_PARAM;
	net_param->len = 2;
	if (iface->vlan_id <= ISCSI_MAX_VLAN_ID &&
	    iface->vlan_priority <= ISCSI_MAX_VLAN_PRIORITY)
		/*
		 * Bit 15-13: User Priority of VLAN
		 * Bit 11-00: VLAN ID
		 */
		vlan = (iface->vlan_priority << 13) |
		       (iface->vlan_id & ISCSI_MAX_VLAN_ID);
	memcpy(net_param->value, &vlan, net_param->len);
	return 0;
}

/* disable/enable parameters */
static int iface_fill_param_state(struct iovec *iov, uint32_t iface_num,
				  uint8_t iface_type, uint16_t param,
				  uint8_t param_type, char *param_val)
{
	int len;
	struct iscsi_iface_param_info *net_param;
	struct nlattr *attr;

	if (!param_val[0])
		return 1;

	len = sizeof(struct iscsi_iface_param_info) + 1;
	iov->iov_base = iscsi_nla_alloc(param, len);
	if (!(iov->iov_base))
		return 1;

	attr = iov->iov_base;
	iov->iov_len = NLA_ALIGN(attr->nla_len);
	net_param = (struct iscsi_iface_param_info *)ISCSI_NLA_DATA(attr);
	net_param->iface_num = iface_num;
	net_param->len = 1;
	net_param->param = param;
	net_param->iface_type = iface_type;
	net_param->param_type = param_type;
	if (!strcmp(param_val, "disable"))
		net_param->value[0] = ISCSI_NET_PARAM_DISABLE;
	else if (!strcmp(param_val, "enable"))
		net_param->value[0] = ISCSI_NET_PARAM_ENABLE;
	else
		goto free;
	return 0;
free:
	free(iov->iov_base);
	iov->iov_base = NULL;
	iov->iov_len = 0;
	return 1;
}

#define IFACE_SET_PARAM_STATE(iov, inum, itype, param, ptype, ival,	\
			      gcnt, lcnt) {				\
	if (!iface_fill_param_state(iov, inum, itype, param, ptype,	\
				    ival)) {				\
		(*gcnt)++;						\
		(*lcnt)++;						\
	}								\
}

/* IPv4 Bootproto: DHCP/static */
static int iface_fill_net_bootproto(struct iovec *iov, struct iface_rec *iface)
{
	int len;
	struct iscsi_iface_param_info *net_param;
	struct nlattr *attr;

	len = sizeof(struct iscsi_iface_param_info) + 1;
	iov->iov_base = iscsi_nla_alloc(ISCSI_NET_PARAM_IPV4_BOOTPROTO, len);
	if (!(iov->iov_base))
		return 1;

	attr = iov->iov_base;
	iov->iov_len = NLA_ALIGN(attr->nla_len);
	net_param = (struct iscsi_iface_param_info *)ISCSI_NLA_DATA(attr);
	net_param->param = ISCSI_NET_PARAM_IPV4_BOOTPROTO;
	net_param->iface_type = ISCSI_IFACE_TYPE_IPV4;
	net_param->iface_num = iface->iface_num;
	net_param->param_type = ISCSI_NET_PARAM;
	net_param->len = 1;
	if (!strcmp(iface->bootproto, "dhcp"))
		net_param->value[0] = ISCSI_BOOTPROTO_DHCP;
	else
		net_param->value[0] = ISCSI_BOOTPROTO_STATIC;
	return 0;
}

/* IPv6 IPAddress Autocfg: nd/dhcpv6/disable */
static int iface_fill_net_autocfg(struct iovec *iov, struct iface_rec *iface)
{
	int len;
	struct iscsi_iface_param_info *net_param;
	struct nlattr *attr;

	len = sizeof(struct iscsi_iface_param_info) + 1;
	iov->iov_base = iscsi_nla_alloc(ISCSI_NET_PARAM_IPV6_ADDR_AUTOCFG, len);
	if (!(iov->iov_base))
		return 1;

	attr = iov->iov_base;
	iov->iov_len = NLA_ALIGN(attr->nla_len);
	net_param = (struct iscsi_iface_param_info *)ISCSI_NLA_DATA(attr);
	net_param->param = ISCSI_NET_PARAM_IPV6_ADDR_AUTOCFG;
	net_param->iface_type = ISCSI_IFACE_TYPE_IPV6;
	net_param->param_type = ISCSI_NET_PARAM;
	net_param->len = 1;

	if (!strcmp(iface->ipv6_autocfg, "nd"))
		net_param->value[0] = ISCSI_IPV6_AUTOCFG_ND_ENABLE;
	else if (!strcmp(iface->ipv6_autocfg, "dhcpv6"))
		net_param->value[0] = ISCSI_IPV6_AUTOCFG_DHCPV6_ENABLE;
	else
		net_param->value[0] = ISCSI_IPV6_AUTOCFG_DISABLE;

	return 0;
}

/* IPv6 LinkLocal Autocfg: enable/disable */
static int iface_fill_linklocal_autocfg(struct iovec *iov,
					struct iface_rec *iface)
{
	int len;
	struct iscsi_iface_param_info *net_param;
	struct nlattr *attr;

	len = sizeof(struct iscsi_iface_param_info) + 1;
	iov->iov_base = iscsi_nla_alloc(ISCSI_NET_PARAM_IPV6_LINKLOCAL_AUTOCFG,
					len);
	if (!(iov->iov_base))
		return 1;

	attr = iov->iov_base;
	iov->iov_len = NLA_ALIGN(attr->nla_len);
	net_param = (struct iscsi_iface_param_info *)ISCSI_NLA_DATA(attr);
	net_param->param = ISCSI_NET_PARAM_IPV6_LINKLOCAL_AUTOCFG;
	net_param->iface_type = ISCSI_IFACE_TYPE_IPV6;
	net_param->param_type = ISCSI_NET_PARAM;
	net_param->len = 1;

	if (strstr(iface->linklocal_autocfg, "auto"))
		net_param->value[0] = ISCSI_IPV6_LINKLOCAL_AUTOCFG_ENABLE;
	else
		net_param->value[0] = ISCSI_IPV6_LINKLOCAL_AUTOCFG_DISABLE;

	return 0;
}

/* IPv6 Router Autocfg: enable/disable */
static int iface_fill_router_autocfg(struct iovec *iov, struct iface_rec *iface)
{
	int len;
	struct iscsi_iface_param_info *net_param;
	struct nlattr *attr;

	len = sizeof(struct iscsi_iface_param_info) + 1;
	iov->iov_base = iscsi_nla_alloc(ISCSI_NET_PARAM_IPV6_ROUTER_AUTOCFG,
					len);
	if (!(iov->iov_base))
		return 1;

	attr = iov->iov_base;
	iov->iov_len = NLA_ALIGN(attr->nla_len);
	net_param = (struct iscsi_iface_param_info *)ISCSI_NLA_DATA(attr);
	net_param->param = ISCSI_NET_PARAM_IPV6_ROUTER_AUTOCFG;
	net_param->iface_type = ISCSI_IFACE_TYPE_IPV6;
	net_param->param_type = ISCSI_NET_PARAM;
	net_param->len = 1;

	if (strstr(iface->router_autocfg, "auto"))
		net_param->value[0] = ISCSI_IPV6_ROUTER_AUTOCFG_ENABLE;
	else
		net_param->value[0] = ISCSI_IPV6_ROUTER_AUTOCFG_DISABLE;

	return 0;
}

/* IPv4 IPAddress/Subnet Mask/Gateway: 4 bytes */
static int iface_fill_net_ipv4_addr(struct iovec *iov, uint32_t iface_num,
				    uint16_t param, char *param_val)
{
	int rc = 1;
	int len;
	struct iscsi_iface_param_info *net_param;
	struct nlattr *attr;

	len = sizeof(struct iscsi_iface_param_info) + 4;
	iov->iov_base = iscsi_nla_alloc(param, len);
	if (!(iov->iov_base))
		return 1;

	attr = iov->iov_base;
	iov->iov_len = NLA_ALIGN(attr->nla_len);
	net_param = (struct iscsi_iface_param_info *)ISCSI_NLA_DATA(attr);
	net_param->param = param;
	net_param->iface_type = ISCSI_IFACE_TYPE_IPV4;
	net_param->iface_num = iface_num;
	net_param->len = 4;
	net_param->param_type = ISCSI_NET_PARAM;
	rc = inet_pton(AF_INET, param_val, net_param->value);
	if (rc <= 0)
		goto free;

	/* validate */
	if (!net_param->value[0] && !net_param->value[1] &&
	    !net_param->value[2] && !net_param->value[3])
		goto free;

	return 0;
free:
	free(iov->iov_base);
	iov->iov_base = NULL;
	iov->iov_len = 0;
	return 1;
}

#define IFACE_SET_NET_PARAM_IPV4_ADDR(iov, inum, param, ival, gcnt,	\
				      lcnt) {				\
	if (strstr(ival, ".")) {					\
		if (!iface_fill_net_ipv4_addr(iov, inum, param, ival)) {\
			(*gcnt)++;					\
			(*lcnt)++;					\
		}							\
	}								\
}

/* IPv6 IPAddress/LinkLocal/Router: 16 bytes */
static int iface_fill_net_ipv6_addr(struct iovec *iov, uint32_t iface_num,
				    uint16_t param, char *param_val)
{
	int rc;
	int len;
	struct iscsi_iface_param_info *net_param;
	struct nlattr *attr;

	len = sizeof(struct iscsi_iface_param_info) + 16;
	iov->iov_base = iscsi_nla_alloc(param, len);
	if (!(iov->iov_base))
		return 1;

	attr = iov->iov_base;
	iov->iov_len = NLA_ALIGN(attr->nla_len);
	net_param = (struct iscsi_iface_param_info *)ISCSI_NLA_DATA(attr);
	net_param->param = param;
	net_param->iface_type = ISCSI_IFACE_TYPE_IPV6;
	net_param->iface_num = iface_num;
	net_param->param_type = ISCSI_NET_PARAM;
	net_param->len = 16;
	rc = inet_pton(AF_INET6, param_val, net_param->value);
	if (rc <= 0)
		goto free;

	return 0;
free:
	free(iov->iov_base);
	iov->iov_base = NULL;
	iov->iov_len = 0;
	return 1;
}

#define IFACE_SET_NET_PARAM_IPV6_ADDR(iov, inum, param, ival, gcnt,	\
				      lcnt) {				\
	if (strstr(ival, ":")) {					\
		if (!iface_fill_net_ipv6_addr(iov, inum, param, ival)) {\
			(*gcnt)++;					\
			(*lcnt)++;					\
		}							\
	}								\
}

/* write string parameter value */
static int iface_fill_str_param_val(struct iovec *iov, uint32_t iface_num,
				    uint8_t iface_type, uint16_t param,
				    uint32_t param_len, char *param_val)
{
	int len;
	struct iscsi_iface_param_info *net_param;
	struct nlattr *attr;

	if (!param_val[0])
		return 1;

	len = sizeof(struct iscsi_iface_param_info) + param_len;
	iov->iov_base = iscsi_nla_alloc(param, len);
	if (!(iov->iov_base))
		return 1;

	attr = iov->iov_base;
	iov->iov_len = NLA_ALIGN(attr->nla_len);
	net_param = (struct iscsi_iface_param_info *)ISCSI_NLA_DATA(attr);
	net_param->iface_num = iface_num;
	net_param->len = param_len;
	net_param->param = param;
	net_param->iface_type = iface_type;
	net_param->param_type = ISCSI_NET_PARAM;
	memcpy(net_param->value, param_val, param_len);
	return 0;
}

#define IFACE_SET_NET_PARAM_STRVAL(iov, inum, itype, param, plen,	\
				   ival, gcnt, lcnt) {			\
	if (!iface_fill_str_param_val(iov, inum, itype, param, plen,	\
				      ival)) {				\
		(*gcnt)++;						\
		(*lcnt)++;						\
	}								\
}

struct iface_net_config {
	struct iface_rec *primary;
	struct iovec *iovs;
	int count;
};

static int __iface_build_net_config(void *data, struct iface_rec *iface)
{
	struct iface_net_config *net_config = data;
	struct iovec *iov;
	int iptype = ISCSI_IFACE_TYPE_IPV4;
	int count = 0;

	if (strcmp(net_config->primary->hwaddress, iface->hwaddress))
		return 0;

	/* start at 2, because 0 is for nlmsghdr and 1 for event */
	iov = net_config->iovs + 2;

	if (!iface->port)
		iface->port = 3260;

	iptype = iface_get_iptype(iface);
	switch (iptype) {
	case ISCSI_IFACE_TYPE_IPV4:
		if (!strcmp(iface->state, "disable")) {
			IFACE_SET_PARAM_STATE(&iov[net_config->count],
					      iface->iface_num,
					      ISCSI_IFACE_TYPE_IPV4,
					      ISCSI_NET_PARAM_IFACE_ENABLE,
					      ISCSI_NET_PARAM,
					      iface->state,
					      &net_config->count,
					      &count);
			return 0;
		}

		if (strstr(iface->bootproto, "dhcp")) {
			if (!iface_fill_net_bootproto(&iov[net_config->count],
						      iface)) {
				net_config->count++;
				count++;
			}
		} else if (strstr(iface->ipaddress, ".")) {
			if (!iface_fill_net_bootproto(&iov[net_config->count],
						      iface)) {
				net_config->count++;
				count++;
			}

			IFACE_SET_NET_PARAM_IPV4_ADDR(&iov[net_config->count],
						      iface->iface_num,
						      ISCSI_NET_PARAM_IPV4_ADDR,
						      iface->ipaddress,
						      &net_config->count,
						      &count);

			IFACE_SET_NET_PARAM_IPV4_ADDR(&iov[net_config->count],
						    iface->iface_num,
						    ISCSI_NET_PARAM_IPV4_SUBNET,
						    iface->subnet_mask,
						    &net_config->count,
						    &count);

			IFACE_SET_NET_PARAM_IPV4_ADDR(&iov[net_config->count],
						      iface->iface_num,
						      ISCSI_NET_PARAM_IPV4_GW,
						      iface->gateway,
						      &net_config->count,
						      &count);
		}

		/*
		 * If IPv4 configuration in iface file is valid,
		 * fill state and other parameters (if any)
		 */
		if (count) {
			IFACE_SET_PARAM_STATE(&iov[net_config->count],
					  iface->iface_num,
					  ISCSI_IFACE_TYPE_IPV4,
					  ISCSI_NET_PARAM_IPV4_DHCP_DNS_ADDR_EN,
					  ISCSI_NET_PARAM,
					  iface->dhcp_dns,
					  &net_config->count,
					  &count);

			IFACE_SET_PARAM_STATE(&iov[net_config->count],
					    iface->iface_num,
					    ISCSI_IFACE_TYPE_IPV4,
					    ISCSI_NET_PARAM_IPV4_DHCP_SLP_DA_EN,
					    ISCSI_NET_PARAM,
					    iface->dhcp_slp_da,
					    &net_config->count,
					    &count);

			IFACE_SET_PARAM_STATE(&iov[net_config->count],
					      iface->iface_num,
					      ISCSI_IFACE_TYPE_IPV4,
					      ISCSI_NET_PARAM_IPV4_TOS_EN,
					      ISCSI_NET_PARAM,
					      iface->tos_state,
					      &net_config->count,
					      &count);

			IFACE_SET_PARAM_INTVAL(&iov[net_config->count],
					      iface->iface_num,
					      ISCSI_IFACE_TYPE_IPV4,
					      ISCSI_NET_PARAM_IPV4_TOS,
					      ISCSI_NET_PARAM,
					      1,
					      iface->tos,
					      &net_config->count,
					      &count);

			IFACE_SET_PARAM_STATE(&iov[net_config->count],
					      iface->iface_num,
					      ISCSI_IFACE_TYPE_IPV4,
					      ISCSI_NET_PARAM_IPV4_GRAT_ARP_EN,
					      ISCSI_NET_PARAM,
					      iface->gratuitous_arp,
					      &net_config->count,
					      &count);

			IFACE_SET_PARAM_STATE(&iov[net_config->count],
				     iface->iface_num,
				     ISCSI_IFACE_TYPE_IPV4,
				     ISCSI_NET_PARAM_IPV4_DHCP_ALT_CLIENT_ID_EN,
				     ISCSI_NET_PARAM,
				     iface->dhcp_alt_client_id_state,
				     &net_config->count,
				     &count);

			IFACE_SET_NET_PARAM_STRVAL(&iov[net_config->count],
					iface->iface_num,
					ISCSI_IFACE_TYPE_IPV4,
					ISCSI_NET_PARAM_IPV4_DHCP_ALT_CLIENT_ID,
					 strlen(iface->dhcp_alt_client_id),
					iface->dhcp_alt_client_id,
					&net_config->count,
					&count);

			IFACE_SET_PARAM_STATE(&iov[net_config->count],
				     iface->iface_num,
				     ISCSI_IFACE_TYPE_IPV4,
				     ISCSI_NET_PARAM_IPV4_DHCP_REQ_VENDOR_ID_EN,
				     ISCSI_NET_PARAM,
				     iface->dhcp_req_vendor_id_state,
				     &net_config->count,
				     &count);

			IFACE_SET_PARAM_STATE(&iov[net_config->count],
				     iface->iface_num,
				     ISCSI_IFACE_TYPE_IPV4,
				     ISCSI_NET_PARAM_IPV4_DHCP_USE_VENDOR_ID_EN,
				     ISCSI_NET_PARAM,
				     iface->dhcp_vendor_id_state,
				     &net_config->count,
				     &count);

			IFACE_SET_NET_PARAM_STRVAL(&iov[net_config->count],
					iface->iface_num,
					ISCSI_IFACE_TYPE_IPV4,
					ISCSI_NET_PARAM_IPV4_DHCP_VENDOR_ID,
					strlen(iface->dhcp_vendor_id),
					iface->dhcp_vendor_id,
					&net_config->count,
					&count);

			IFACE_SET_PARAM_STATE(&iov[net_config->count],
					 iface->iface_num,
					 ISCSI_IFACE_TYPE_IPV4,
					 ISCSI_NET_PARAM_IPV4_DHCP_LEARN_IQN_EN,
					 ISCSI_NET_PARAM,
					 iface->dhcp_learn_iqn,
					 &net_config->count,
					 &count);

			IFACE_SET_PARAM_STATE(&iov[net_config->count],
					  iface->iface_num,
					  ISCSI_IFACE_TYPE_IPV4,
					  ISCSI_NET_PARAM_IPV4_FRAGMENT_DISABLE,
					  ISCSI_NET_PARAM,
					  iface->fragmentation,
					  &net_config->count,
					  &count);

			IFACE_SET_PARAM_STATE(&iov[net_config->count],
					     iface->iface_num,
					     ISCSI_IFACE_TYPE_IPV4,
					     ISCSI_NET_PARAM_IPV4_IN_FORWARD_EN,
					     ISCSI_NET_PARAM,
					     iface->incoming_forwarding,
					     &net_config->count,
					     &count);

			IFACE_SET_PARAM_INTVAL(&iov[net_config->count],
					       iface->iface_num,
					       ISCSI_IFACE_TYPE_IPV4,
					       ISCSI_NET_PARAM_IPV4_TTL,
					       ISCSI_NET_PARAM,
					       1,
					       iface->ttl,
					       &net_config->count,
					       &count);
		}
		break;

	case ISCSI_IFACE_TYPE_IPV6:
		if (!strcmp(iface->state, "disable")) {
			IFACE_SET_PARAM_STATE(&iov[net_config->count],
					      iface->iface_num,
					      ISCSI_IFACE_TYPE_IPV6,
					      ISCSI_NET_PARAM_IFACE_ENABLE,
					      ISCSI_NET_PARAM,
					      iface->state,
					      &net_config->count,
					      &count);
			return 0;
		}

		/* For IPv6 Address */
		if (strstr(iface->ipv6_autocfg, "nd") ||
		    strstr(iface->ipv6_autocfg, "dhcpv6")) {
			if (!iface_fill_net_autocfg(&iov[net_config->count],
						    iface)) {
				net_config->count++;
				count++;
			}
		} else if (strstr(iface->ipaddress, ":")) {
			if (!iface_fill_net_autocfg(&iov[net_config->count],
						    iface)) {
				net_config->count++;
				count++;
			}
			/* User provided IPv6 Address */
			IFACE_SET_NET_PARAM_IPV6_ADDR(&iov[net_config->count],
						      iface->iface_num,
						      ISCSI_NET_PARAM_IPV6_ADDR,
						      iface->ipaddress,
						      &net_config->count,
						      &count);
		}

		/* For LinkLocal Address */
		if (strstr(iface->linklocal_autocfg, "auto")) {
			if (!iface_fill_linklocal_autocfg(
						&iov[net_config->count],
						iface)) {
				net_config->count++;
				count++;
			}
		} else if (strstr(iface->ipv6_linklocal, ":")) {
			if (!iface_fill_linklocal_autocfg(
						&iov[net_config->count],
						iface)) {
				net_config->count++;
				count++;
			}
			/* User provided Link Local Address */
			IFACE_SET_NET_PARAM_IPV6_ADDR(&iov[net_config->count],
						 iface->iface_num,
						 ISCSI_NET_PARAM_IPV6_LINKLOCAL,
						 iface->ipv6_linklocal,
						 &net_config->count,
						 &count);
		}

		/* For Router Address */
		if (strstr(iface->router_autocfg, "auto")) {
			if (!iface_fill_router_autocfg(&iov[net_config->count],
						       iface)) {
				net_config->count++;
				count++;
			}
		} else if (strstr(iface->ipv6_router, ":")) {
			if (!iface_fill_router_autocfg(&iov[net_config->count],
						       iface)) {
				net_config->count++;
				count++;
			}
			/* User provided Router Address */
			IFACE_SET_NET_PARAM_IPV6_ADDR(&iov[net_config->count],
						    iface->iface_num,
						    ISCSI_NET_PARAM_IPV6_ROUTER,
						    iface->ipv6_router,
						    &net_config->count,
						    &count);
		}

		/*
		 * If IPv6 configuration in iface file is valid,
		 * fill state and other parameters
		 */
		if (count) {
			IFACE_SET_PARAM_STATE(&iov[net_config->count],
				      iface->iface_num,
				      ISCSI_IFACE_TYPE_IPV6,
				      ISCSI_NET_PARAM_IPV6_GRAT_NEIGHBOR_ADV_EN,
				      ISCSI_NET_PARAM,
				      iface->gratuitous_neighbor_adv,
				      &net_config->count,
				      &count);

			IFACE_SET_PARAM_STATE(&iov[net_config->count],
					      iface->iface_num,
					      ISCSI_IFACE_TYPE_IPV6,
					      ISCSI_NET_PARAM_IPV6_MLD_EN,
					      ISCSI_NET_PARAM,
					      iface->mld,
					      &net_config->count,
					      &count);

			IFACE_SET_PARAM_INTVAL(&iov[net_config->count],
					       iface->iface_num,
					       ISCSI_IFACE_TYPE_IPV6,
					       ISCSI_NET_PARAM_IPV6_FLOW_LABEL,
					       ISCSI_NET_PARAM,
					       4,
					       iface->flow_label,
					       &net_config->count,
					       &count);

			IFACE_SET_PARAM_INTVAL(&iov[net_config->count],
					     iface->iface_num,
					     ISCSI_IFACE_TYPE_IPV6,
					     ISCSI_NET_PARAM_IPV6_TRAFFIC_CLASS,
					     ISCSI_NET_PARAM,
					     1,
					     iface->traffic_class,
					     &net_config->count,
					     &count);

			IFACE_SET_PARAM_INTVAL(&iov[net_config->count],
					       iface->iface_num,
					       ISCSI_IFACE_TYPE_IPV6,
					       ISCSI_NET_PARAM_IPV6_HOP_LIMIT,
					       ISCSI_NET_PARAM,
					       1,
					       iface->hop_limit,
					       &net_config->count,
					       &count);

			IFACE_SET_PARAM_INTVAL(&iov[net_config->count],
					  iface->iface_num,
					  ISCSI_IFACE_TYPE_IPV6,
					  ISCSI_NET_PARAM_IPV6_ND_REACHABLE_TMO,
					  ISCSI_NET_PARAM,
					  4,
					  iface->nd_reachable_tmo,
					  &net_config->count,
					  &count);

			IFACE_SET_PARAM_INTVAL(&iov[net_config->count],
					    iface->iface_num,
					    ISCSI_IFACE_TYPE_IPV6,
					    ISCSI_NET_PARAM_IPV6_ND_REXMIT_TIME,
					    ISCSI_NET_PARAM,
					    4,
					    iface->nd_rexmit_time,
					    &net_config->count,
					    &count);

			IFACE_SET_PARAM_INTVAL(&iov[net_config->count],
					      iface->iface_num,
					      ISCSI_IFACE_TYPE_IPV6,
					      ISCSI_NET_PARAM_IPV6_ND_STALE_TMO,
					      ISCSI_NET_PARAM,
					      4,
					      iface->nd_stale_tmo,
					      &net_config->count,
					      &count);

			IFACE_SET_PARAM_INTVAL(&iov[net_config->count],
				       iface->iface_num,
				       ISCSI_IFACE_TYPE_IPV6,
				       ISCSI_NET_PARAM_IPV6_DUP_ADDR_DETECT_CNT,
				       ISCSI_NET_PARAM,
				       1,
				       iface->dup_addr_detect_cnt,
				       &net_config->count,
				       &count);

			IFACE_SET_PARAM_INTVAL(&iov[net_config->count],
					  iface->iface_num,
					  ISCSI_IFACE_TYPE_IPV6,
					  ISCSI_NET_PARAM_IPV6_RTR_ADV_LINK_MTU,
					  ISCSI_NET_PARAM,
					  4,
					  iface->router_adv_link_mtu,
					  &net_config->count,
					  &count);
		}
		break;
	}

	/* Fill parameters common to IPv4 and IPv6 ifaces */
	if (count) {
		IFACE_SET_PARAM_STATE(&iov[net_config->count],
				      iface->iface_num,
				      iptype,
				      ISCSI_NET_PARAM_IFACE_ENABLE,
				      ISCSI_NET_PARAM,
				      iface->state,
				      &net_config->count,
				      &count);

		IFACE_SET_PARAM_STATE(&iov[net_config->count],
				      iface->iface_num,
				      iptype,
				      ISCSI_NET_PARAM_VLAN_ENABLED,
				      ISCSI_NET_PARAM,
				      iface->vlan_state,
				      &net_config->count,
				      &count);

		if (strcmp(iface->vlan_state, "disable") && iface->vlan_id) {
			if (!iface_fill_vlan_id(&iov[net_config->count], iface,
						iptype)) {
				net_config->count++;
				count++;
			}
		}

		IFACE_SET_PARAM_INTVAL(&iov[net_config->count],
				       iface->iface_num,
				       iptype,
				       ISCSI_NET_PARAM_MTU,
				       ISCSI_NET_PARAM,
				       2,
				       iface->mtu,
				       &net_config->count,
				       &count);

		IFACE_SET_PARAM_INTVAL(&iov[net_config->count],
				       iface->iface_num,
				       iptype,
				       ISCSI_NET_PARAM_PORT,
				       ISCSI_NET_PARAM,
				       2,
				       iface->port,
				       &net_config->count,
				       &count);

		IFACE_SET_PARAM_STATE(&iov[net_config->count],
				      iface->iface_num,
				      iptype,
				      ISCSI_NET_PARAM_DELAYED_ACK_EN,
				      ISCSI_NET_PARAM,
				      iface->delayed_ack,
				      &net_config->count,
				      &count);

		IFACE_SET_PARAM_STATE(&iov[net_config->count],
				      iface->iface_num,
				      iptype,
				      ISCSI_NET_PARAM_TCP_NAGLE_DISABLE,
				      ISCSI_NET_PARAM,
				      iface->nagle,
				      &net_config->count,
				      &count);

		IFACE_SET_PARAM_STATE(&iov[net_config->count],
				      iface->iface_num,
				      iptype,
				      ISCSI_NET_PARAM_TCP_WSF_DISABLE,
				      ISCSI_NET_PARAM,
				      iface->tcp_wsf_state,
				      &net_config->count,
				      &count);

		IFACE_SET_PARAM_INTVAL(&iov[net_config->count],
				       iface->iface_num,
				       iptype,
				       ISCSI_NET_PARAM_TCP_WSF,
				       ISCSI_NET_PARAM,
				       1,
				       iface->tcp_wsf,
				       &net_config->count,
				       &count);

		IFACE_SET_PARAM_INTVAL(&iov[net_config->count],
				       iface->iface_num,
				       iptype,
				       ISCSI_NET_PARAM_TCP_TIMER_SCALE,
				       ISCSI_NET_PARAM,
				       1,
				       iface->tcp_timer_scale,
				       &net_config->count,
				       &count);

		IFACE_SET_PARAM_STATE(&iov[net_config->count],
				      iface->iface_num,
				      iptype,
				      ISCSI_NET_PARAM_TCP_TIMESTAMP_EN,
				      ISCSI_NET_PARAM,
				      iface->tcp_timestamp,
				      &net_config->count,
				      &count);

		IFACE_SET_PARAM_STATE(&iov[net_config->count],
				      iface->iface_num,
				      iptype,
				      ISCSI_NET_PARAM_REDIRECT_EN,
				      ISCSI_NET_PARAM,
				      iface->redirect,
				      &net_config->count,
				      &count);

		IFACE_SET_PARAM_INTVAL(&iov[net_config->count],
				     iface->iface_num,
				     iptype,
				     ISCSI_IFACE_PARAM_DEF_TASKMGMT_TMO,
				     ISCSI_IFACE_PARAM,
				     2,
				     iface->def_task_mgmt_tmo,
				     &net_config->count,
				     &count);

		IFACE_SET_PARAM_STATE(&iov[net_config->count],
				      iface->iface_num,
				      iptype,
				      ISCSI_IFACE_PARAM_HDRDGST_EN,
				      ISCSI_IFACE_PARAM,
				      iface->header_digest,
				      &net_config->count,
				      &count);

		IFACE_SET_PARAM_STATE(&iov[net_config->count],
				      iface->iface_num,
				      iptype,
				      ISCSI_IFACE_PARAM_DATADGST_EN,
				      ISCSI_IFACE_PARAM,
				      iface->data_digest,
				      &net_config->count,
				      &count);

		IFACE_SET_PARAM_STATE(&iov[net_config->count],
				      iface->iface_num,
				      iptype,
				      ISCSI_IFACE_PARAM_IMM_DATA_EN,
				      ISCSI_IFACE_PARAM,
				      iface->immediate_data,
				      &net_config->count,
				      &count);

		IFACE_SET_PARAM_STATE(&iov[net_config->count],
				      iface->iface_num,
				      iptype,
				      ISCSI_IFACE_PARAM_INITIAL_R2T_EN,
				      ISCSI_IFACE_PARAM,
				      iface->initial_r2t,
				      &net_config->count,
				      &count);

		IFACE_SET_PARAM_STATE(&iov[net_config->count],
				      iface->iface_num,
				      iptype,
				      ISCSI_IFACE_PARAM_DATASEQ_INORDER_EN,
				      ISCSI_IFACE_PARAM,
				      iface->data_seq_inorder,
				      &net_config->count,
				      &count);

		IFACE_SET_PARAM_STATE(&iov[net_config->count],
				      iface->iface_num,
				      iptype,
				      ISCSI_IFACE_PARAM_PDU_INORDER_EN,
				      ISCSI_IFACE_PARAM,
				      iface->data_pdu_inorder,
				      &net_config->count,
				      &count);

		IFACE_SET_PARAM_INTVAL(&iov[net_config->count],
				       iface->iface_num,
				       iptype,
				       ISCSI_IFACE_PARAM_ERL,
				       ISCSI_IFACE_PARAM,
				       1,
				       iface->erl,
				       &net_config->count,
				       &count);

		IFACE_SET_PARAM_INTVAL(&iov[net_config->count],
				       iface->iface_num,
				       iptype,
				       ISCSI_IFACE_PARAM_MAX_RECV_DLENGTH,
				       ISCSI_IFACE_PARAM,
				       4,
				       iface->max_recv_dlength,
				       &net_config->count,
				       &count);

		IFACE_SET_PARAM_INTVAL(&iov[net_config->count],
				       iface->iface_num,
				       iptype,
				       ISCSI_IFACE_PARAM_FIRST_BURST,
				       ISCSI_IFACE_PARAM,
				       4,
				       iface->first_burst_len,
				       &net_config->count,
				       &count);

		IFACE_SET_PARAM_INTVAL(&iov[net_config->count],
				       iface->iface_num,
				       iptype,
				       ISCSI_IFACE_PARAM_MAX_R2T,
				       ISCSI_IFACE_PARAM,
				       2,
				       iface->max_out_r2t,
				       &net_config->count,
				       &count);

		IFACE_SET_PARAM_INTVAL(&iov[net_config->count],
				       iface->iface_num,
				       iptype,
				       ISCSI_IFACE_PARAM_MAX_BURST,
				       ISCSI_IFACE_PARAM,
				       4,
				       iface->max_burst_len,
				       &net_config->count,
				       &count);

		IFACE_SET_PARAM_STATE(&iov[net_config->count],
				      iface->iface_num,
				      iptype,
				      ISCSI_IFACE_PARAM_CHAP_AUTH_EN,
				      ISCSI_IFACE_PARAM,
				      iface->chap_auth,
				      &net_config->count,
				      &count);

		IFACE_SET_PARAM_STATE(&iov[net_config->count],
				      iface->iface_num,
				      iptype,
				      ISCSI_IFACE_PARAM_BIDI_CHAP_EN,
				      ISCSI_IFACE_PARAM,
				      iface->bidi_chap,
				      &net_config->count,
				      &count);

		IFACE_SET_PARAM_STATE(&iov[net_config->count],
				      iface->iface_num,
				      iptype,
				      ISCSI_IFACE_PARAM_STRICT_LOGIN_COMP_EN,
				      ISCSI_IFACE_PARAM,
				      iface->strict_login_comp,
				      &net_config->count,
				      &count);

		IFACE_SET_PARAM_STATE(&iov[net_config->count],
				      iface->iface_num,
				      iptype,
				      ISCSI_IFACE_PARAM_DISCOVERY_AUTH_OPTIONAL,
				      ISCSI_IFACE_PARAM,
				      iface->discovery_auth,
				      &net_config->count,
				      &count);

		IFACE_SET_PARAM_STATE(&iov[net_config->count],
				      iface->iface_num,
				      iptype,
				      ISCSI_IFACE_PARAM_DISCOVERY_LOGOUT_EN,
				      ISCSI_IFACE_PARAM,
				      iface->discovery_logout,
				      &net_config->count,
				      &count);
	}
	return 0;
}

/**
 * iface_build_net_config - Setup neconfig parameter buffers
 * @iface: iface to setup
 * @iface_all: Flag for number of ifaces to traverse (1 for all)
 * @iovs: iovec buffer for netconfig parameters
 *
 * Returns total number of netconfig parameter buffers used.
 */
int iface_build_net_config(struct iface_rec *iface, int iface_all,
			   struct iovec *iovs)
{
	int num_found = 0, rc;
	struct iface_net_config net_config;

	log_debug(8, "In iface_build_net_config");

	net_config.primary = iface;
	net_config.iovs = iovs;
	net_config.count = 0;

	if (iface_all)
		rc = iface_for_each_iface(&net_config, 0, &num_found,
					  __iface_build_net_config);
	else
		rc = __iface_build_net_config(&net_config, iface);

	log_debug(8, "iface_build_net_config: rc = %d, count = %d",
		  rc, net_config.count);
	return net_config.count;
}
