Blame iscsiuio/src/unix/iscsid_ipc.c

Packit eace71
/*
Packit eace71
 * Copyright (c) 2009-2011, Broadcom Corporation
Packit eace71
 * Copyright (c) 2014, QLogic Corporation
Packit eace71
 *
Packit eace71
 * Written by:  Benjamin Li  (benli@broadcom.com)
Packit eace71
 *
Packit eace71
 * All rights reserved.
Packit eace71
 *
Packit eace71
 * Redistribution and use in source and binary forms, with or without
Packit eace71
 * modification, are permitted provided that the following conditions
Packit eace71
 * are met:
Packit eace71
 * 1. Redistributions of source code must retain the above copyright
Packit eace71
 *    notice, this list of conditions and the following disclaimer.
Packit eace71
 * 2. Redistributions in binary form must reproduce the above copyright
Packit eace71
 *    notice, this list of conditions and the following disclaimer in the
Packit eace71
 *    documentation and/or other materials provided with the distribution.
Packit eace71
 * 3. All advertising materials mentioning features or use of this software
Packit eace71
 *    must display the following acknowledgement:
Packit eace71
 *      This product includes software developed by Adam Dunkels.
Packit eace71
 * 4. The name of the author may not be used to endorse or promote
Packit eace71
 *    products derived from this software without specific prior
Packit eace71
 *    written permission.
Packit eace71
 *
Packit eace71
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
Packit eace71
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
Packit eace71
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
Packit eace71
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
Packit eace71
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
Packit eace71
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
Packit eace71
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
Packit eace71
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
Packit eace71
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
Packit eace71
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
Packit eace71
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Packit eace71
 *
Packit eace71
 * iscsi_ipc.c - Generic NIC management/utility functions
Packit eace71
 *
Packit eace71
 */
Packit eace71
Packit eace71
#define _GNU_SOURCE
Packit eace71
Packit eace71
#include <errno.h>
Packit eace71
#include <pthread.h>
Packit eace71
#include <signal.h>
Packit eace71
#include <string.h>
Packit eace71
#include <stdio.h>
Packit eace71
#include <unistd.h>
Packit eace71
#include <arpa/inet.h>
Packit eace71
#include <sys/socket.h>
Packit eace71
#include <sys/time.h>
Packit eace71
#include <sys/un.h>
Packit eace71
#include <sys/types.h>
Packit eace71
#include <pwd.h>
Packit eace71
Packit eace71
#define PFX "iscsi_ipc "
Packit eace71
Packit eace71
/* TODO fix me */
Packit eace71
#define IFNAMSIZ 15
Packit eace71
Packit eace71
#include "nic.h"
Packit eace71
#include "nic_utils.h"
Packit eace71
#include "nic_vlan.h"
Packit eace71
#include "options.h"
Packit eace71
#include "mgmt_ipc.h"
Packit eace71
#include "iscsid_ipc.h"
Packit eace71
#include "uip.h"
Packit eace71
#include "uip_mgmt_ipc.h"
Packit eace71
#include "sysdeps.h"
Packit eace71
Packit eace71
#include "logger.h"
Packit eace71
#include "uip.h"
Packit eace71
#include "ping.h"
Packit eace71
Packit eace71
/*  private iscsid options stucture */
Packit eace71
struct iscsid_options {
Packit eace71
	int fd;
Packit eace71
	pthread_t thread;
Packit eace71
};
Packit eace71
Packit eace71
struct iface_rec_decode {
Packit eace71
	/* General */
Packit eace71
	int32_t			iface_num;
Packit eace71
	uint32_t		ip_type;
Packit eace71
Packit eace71
	/* IPv4 */
Packit eace71
	struct in_addr		ipv4_addr;
Packit eace71
	struct in_addr		ipv4_subnet_mask;
Packit eace71
	struct in_addr		ipv4_gateway;
Packit eace71
Packit eace71
	/* IPv6 */
Packit eace71
	struct in6_addr		ipv6_addr;
Packit eace71
	struct in6_addr		ipv6_subnet_mask;
Packit eace71
	uint32_t		prefix_len;
Packit eace71
	struct in6_addr		ipv6_linklocal;
Packit eace71
	struct in6_addr		ipv6_router;
Packit eace71
Packit eace71
	uint8_t			ipv6_autocfg;
Packit eace71
	uint8_t                 linklocal_autocfg;
Packit eace71
	uint8_t                 router_autocfg;
Packit eace71
Packit eace71
	uint8_t			vlan_state;
Packit eace71
	uint8_t			vlan_priority;
Packit eace71
	uint16_t		vlan_id;
Packit eace71
Packit eace71
#define MIN_MTU_SUPPORT		46
Packit eace71
#define MAX_MTU_SUPPORT		9000
Packit eace71
	uint16_t		mtu;
Packit eace71
};
Packit eace71
Packit eace71
#define PEERUSER_MAX	64
Packit eace71
Packit eace71
/******************************************************************************
Packit eace71
 *  Globals
Packit eace71
 *****************************************************************************/
Packit eace71
static struct iscsid_options iscsid_opts = {
Packit eace71
	.fd = INVALID_FD,
Packit eace71
	.thread = INVALID_THREAD,
Packit eace71
};
Packit eace71
Packit eace71
/******************************************************************************
Packit eace71
 *  iscsid Functions
Packit eace71
 *****************************************************************************/
Packit eace71
Packit eace71
static void *enable_nic_thread(void *data)
Packit eace71
{
Packit eace71
	nic_t *nic = (nic_t *) data;
Packit eace71
Packit eace71
	prepare_nic_thread(nic);
Packit eace71
	LOG_INFO(PFX "%s: started NIC enable thread state: 0x%x",
Packit eace71
		 nic->log_name, nic->state)
Packit eace71
Packit eace71
	/*  Enable the NIC */
Packit eace71
	nic_enable(nic);
Packit eace71
Packit eace71
	nic->enable_thread = INVALID_THREAD;
Packit eace71
Packit eace71
	pthread_exit(NULL);
Packit eace71
}
Packit eace71
Packit eace71
static int decode_cidr(char *in_ipaddr_str, struct iface_rec_decode *ird)
Packit eace71
{
Packit eace71
	int rc = 0, i;
Packit eace71
	char *tmp, *tok;
Packit eace71
	char ipaddr_str[NI_MAXHOST];
Packit eace71
	char str[INET6_ADDRSTRLEN];
Packit eace71
	unsigned long keepbits = 0;
Packit eace71
	struct in_addr ia;
Packit eace71
	struct in6_addr ia6;
Packit eace71
Packit eace71
	strlcpy(ipaddr_str, in_ipaddr_str, NI_MAXHOST);
Packit eace71
Packit eace71
	/* Find the CIDR if any */
Packit eace71
	tmp = strchr(ipaddr_str, '/');
Packit eace71
	if (tmp) {
Packit eace71
		/* CIDR found, now decode, tmpbuf = ip, tmp = netmask */
Packit eace71
		tmp = ipaddr_str;
Packit eace71
		tok = strsep(&tmp, "/");
Packit eace71
		LOG_INFO(PFX "in cidr: bitmask '%s' ip '%s'", tmp, tok);
Packit eace71
		keepbits = strtoull(tmp, NULL, 10);
Packit eace71
	}
Packit eace71
Packit eace71
	/*  Determine if the IP address passed from the iface file is
Packit eace71
	 *  an IPv4 or IPv6 address */
Packit eace71
	rc = inet_pton(AF_INET, ipaddr_str, &ird->ipv6_addr);
Packit eace71
	if (rc == 0) {
Packit eace71
		/* Test to determine if the addres is an IPv6 address */
Packit eace71
		rc = inet_pton(AF_INET6, ipaddr_str, &ird->ipv6_addr);
Packit eace71
		if (rc == 0) {
Packit eace71
			LOG_ERR(PFX "Could not parse IP address: '%s'",
Packit eace71
				ipaddr_str);
Packit eace71
			goto out;
Packit eace71
		}
Packit eace71
		ird->ip_type = AF_INET6;
Packit eace71
		if (keepbits > 128) {
Packit eace71
			LOG_ERR(PFX "CIDR netmask > 128 for IPv6: %d(%s)",
Packit eace71
				keepbits, tmp);
Packit eace71
			goto out;
Packit eace71
		}
Packit eace71
		if (!keepbits) {
Packit eace71
			/* Default prefix mask to 64 */
Packit eace71
			memcpy(&ird->ipv6_subnet_mask.s6_addr, all_zeroes_addr6,
Packit eace71
			       sizeof(struct in6_addr));
Packit eace71
			ird->prefix_len = 64;
Packit eace71
			for (i = 0; i < 2; i++)
Packit eace71
				ird->ipv6_subnet_mask.s6_addr32[i] = 0xffffffff;
Packit eace71
			goto out;
Packit eace71
		}
Packit eace71
		ird->prefix_len = keepbits;
Packit eace71
		memcpy(&ia6.s6_addr, all_zeroes_addr6, sizeof(struct in6_addr));
Packit eace71
		for (i = 0; i < 4; i++) {
Packit eace71
			if (keepbits < 32) {
Packit eace71
				ia6.s6_addr32[i] = keepbits > 0 ?
Packit eace71
				    0x00 - (1 << (32 - keepbits)) : 0;
Packit eace71
				ia6.s6_addr32[i] = htonl(ia6.s6_addr32[i]);
Packit eace71
				break;
Packit eace71
			} else
Packit eace71
				ia6.s6_addr32[i] = 0xFFFFFFFF;
Packit eace71
			keepbits -= 32;
Packit eace71
		}
Packit eace71
		ird->ipv6_subnet_mask = ia6;
Packit eace71
		if (inet_ntop(AF_INET6, &ia6, str, sizeof(str)))
Packit eace71
			LOG_INFO(PFX "Using netmask: %s", str);
Packit eace71
	} else {
Packit eace71
		ird->ip_type = AF_INET;
Packit eace71
		rc = inet_pton(AF_INET, ipaddr_str, &ird->ipv4_addr);
Packit eace71
Packit eace71
		if (keepbits > 32) {
Packit eace71
			LOG_ERR(PFX "CIDR netmask > 32 for IPv4: %d(%s)",
Packit eace71
				keepbits, tmp);
Packit eace71
			goto out;
Packit eace71
		}
Packit eace71
		ia.s_addr = keepbits > 0 ? 0x00 - (1 << (32 - keepbits)) : 0;
Packit eace71
		ird->ipv4_subnet_mask.s_addr = htonl(ia.s_addr);
Packit eace71
		LOG_INFO(PFX "Using netmask: %s",
Packit eace71
			 inet_ntoa(ird->ipv4_subnet_mask));
Packit eace71
	}
Packit eace71
out:
Packit eace71
	return rc;
Packit eace71
}
Packit eace71
Packit eace71
static int decode_iface(struct iface_rec_decode *ird, struct iface_rec *rec)
Packit eace71
{
Packit eace71
	int rc = 0;
Packit eace71
	char ipaddr_str[NI_MAXHOST];
Packit eace71
Packit eace71
	/* Decodes the rec contents */
Packit eace71
	memset(ird, 0, sizeof(struct iface_rec_decode));
Packit eace71
Packit eace71
	/*  Detect for CIDR notation and strip off the netmask if present */
Packit eace71
	rc = decode_cidr(rec->ipaddress, ird);
Packit eace71
	if (rc && !ird->ip_type) {
Packit eace71
		LOG_ERR(PFX "cidr decode err: rc=%d, ip_type=%d",
Packit eace71
			rc, ird->ip_type);
Packit eace71
		/* Can't decode address, just exit */
Packit eace71
		return rc;
Packit eace71
	}
Packit eace71
	rc = 0;
Packit eace71
	ird->iface_num = rec->iface_num;
Packit eace71
	ird->vlan_id = rec->vlan_id;
Packit eace71
	if (rec->iface_num != IFACE_NUM_INVALID) {
Packit eace71
		ird->mtu = rec->mtu;
Packit eace71
		if (rec->vlan_id && strcmp(rec->vlan_state, "disable")) {
Packit eace71
			ird->vlan_state = 1;
Packit eace71
			ird->vlan_priority = rec->vlan_priority;
Packit eace71
			ird->vlan_id = rec->vlan_id;
Packit eace71
		}
Packit eace71
		if (ird->ip_type == AF_INET6) {
Packit eace71
			if (!strcmp(rec->ipv6_autocfg, "dhcpv6"))
Packit eace71
				ird->ipv6_autocfg = IPV6_AUTOCFG_DHCPV6;
Packit eace71
			else if (!strcmp(rec->ipv6_autocfg, "nd"))
Packit eace71
				ird->ipv6_autocfg = IPV6_AUTOCFG_ND;
Packit eace71
			else
Packit eace71
				ird->ipv6_autocfg = IPV6_AUTOCFG_NOTSPEC;
Packit eace71
Packit eace71
			if (!strcmp(rec->linklocal_autocfg, "auto"))
Packit eace71
				ird->linklocal_autocfg = IPV6_LL_AUTOCFG_ON;
Packit eace71
			else if (!strcmp(rec->linklocal_autocfg, "off"))
Packit eace71
				ird->linklocal_autocfg = IPV6_LL_AUTOCFG_OFF;
Packit eace71
			else /* default */
Packit eace71
				ird->linklocal_autocfg = IPV6_LL_AUTOCFG_ON;
Packit eace71
Packit eace71
			if (!strcmp(rec->router_autocfg, "auto"))
Packit eace71
				ird->router_autocfg = IPV6_RTR_AUTOCFG_ON;
Packit eace71
			else if (!strcmp(rec->router_autocfg, "off"))
Packit eace71
				ird->router_autocfg = IPV6_RTR_AUTOCFG_OFF;
Packit eace71
			else /* default */
Packit eace71
				ird->router_autocfg = IPV6_RTR_AUTOCFG_ON;
Packit eace71
Packit eace71
			/* Decode the addresses based on the control flags */
Packit eace71
			/* For DHCP, ignore the IPv6 addr in the iface */
Packit eace71
			if (ird->ipv6_autocfg == IPV6_AUTOCFG_DHCPV6)
Packit eace71
				memcpy(&ird->ipv6_addr, all_zeroes_addr6,
Packit eace71
				       sizeof(struct in6_addr));
Packit eace71
			/* Subnet mask priority: CIDR, then rec */
Packit eace71
			if (!ird->ipv6_subnet_mask.s6_addr)
Packit eace71
				inet_pton(AF_INET6, rec->subnet_mask,
Packit eace71
					  &ird->ipv6_subnet_mask);
Packit eace71
Packit eace71
			/* For LL on, ignore the IPv6 addr in the iface */
Packit eace71
			if (ird->linklocal_autocfg == IPV6_LL_AUTOCFG_OFF) {
Packit eace71
				strlcpy(ipaddr_str, rec->ipv6_linklocal,
Packit eace71
					NI_MAXHOST);
Packit eace71
				inet_pton(AF_INET6, ipaddr_str,
Packit eace71
					  &ird->ipv6_linklocal);
Packit eace71
			}
Packit eace71
Packit eace71
			/* For RTR on, ignore the IPv6 addr in the iface */
Packit eace71
			if (ird->router_autocfg == IPV6_RTR_AUTOCFG_OFF) {
Packit eace71
				strlcpy(ipaddr_str, rec->ipv6_router,
Packit eace71
					NI_MAXHOST);
Packit eace71
				inet_pton(AF_INET6, ipaddr_str,
Packit eace71
					  &ird->ipv6_router);
Packit eace71
			}
Packit eace71
		} else {
Packit eace71
			/* Subnet mask priority: CIDR, rec, default */
Packit eace71
			if (!ird->ipv4_subnet_mask.s_addr)
Packit eace71
				inet_pton(AF_INET, rec->subnet_mask,
Packit eace71
					  &ird->ipv4_subnet_mask);
Packit eace71
			if (!ird->ipv4_subnet_mask.s_addr)
Packit eace71
				ird->ipv4_subnet_mask.s_addr =
Packit eace71
					calculate_default_netmask(
Packit eace71
							ird->ipv4_addr.s_addr);
Packit eace71
Packit eace71
			strlcpy(ipaddr_str, rec->gateway, NI_MAXHOST);
Packit eace71
			inet_pton(AF_INET, ipaddr_str, &ird->ipv4_gateway);
Packit eace71
		}
Packit eace71
	} else {
Packit eace71
		ird->ipv6_autocfg = IPV6_AUTOCFG_NOTUSED;
Packit eace71
		ird->linklocal_autocfg = IPV6_LL_AUTOCFG_NOTUSED;
Packit eace71
		ird->router_autocfg = IPV6_RTR_AUTOCFG_NOTUSED;
Packit eace71
	}
Packit eace71
	return rc;
Packit eace71
}
Packit eace71
Packit eace71
static void *perform_ping(void *arg)
Packit eace71
{
Packit eace71
	struct ping_conf *png_c = (struct ping_conf *)arg;
Packit eace71
	nic_interface_t *nic_iface = png_c->nic_iface;
Packit eace71
	nic_t *nic = nic_iface->parent;
Packit eace71
	iscsid_uip_broadcast_t *data;
Packit eace71
	struct sockaddr_in *addr;
Packit eace71
	struct sockaddr_in6 *addr6;
Packit eace71
	uip_ip6addr_t dst_addr;
Packit eace71
	int rc = 0;
Packit eace71
	int datalen;
Packit eace71
	struct timespec ts = {.tv_sec = 5,
Packit eace71
			      .tv_nsec = 0};
Packit eace71
Packit eace71
	data = (iscsid_uip_broadcast_t *)png_c->data;
Packit eace71
	datalen = data->u.ping_rec.datalen;
Packit eace71
	if ((datalen > STD_MTU_SIZE) || (datalen < 0)) {
Packit eace71
		LOG_ERR(PFX "Ping datalen invalid: %d", datalen);
Packit eace71
		rc = -EINVAL;
Packit eace71
		goto ping_done;
Packit eace71
	}
Packit eace71
Packit eace71
	memset(dst_addr, 0, sizeof(uip_ip6addr_t));
Packit eace71
	if (nic_iface->protocol == AF_INET) {
Packit eace71
		/* IPv4 */
Packit eace71
		addr = (struct sockaddr_in *)&data->u.ping_rec.ipaddr;
Packit eace71
		memcpy(dst_addr, &addr->sin_addr.s_addr, sizeof(uip_ip4addr_t));
Packit eace71
	} else {
Packit eace71
		/* IPv6 */
Packit eace71
		addr6 = (struct sockaddr_in6 *)&data->u.ping_rec.ipaddr;
Packit eace71
		memcpy(dst_addr, &addr6->sin6_addr.s6_addr,
Packit eace71
		       sizeof(uip_ip6addr_t));
Packit eace71
	}
Packit eace71
Packit eace71
	/*  Ensure that the NIC is RUNNING */
Packit eace71
	if ((nic->state != NIC_RUNNING) || !(nic->flags & NIC_ENABLED)) {
Packit eace71
		pthread_mutex_lock(&nic->nic_mutex);
Packit eace71
		rc = pthread_cond_timedwait(&nic->enable_done_cond,
Packit eace71
					    &nic->nic_mutex, &ts);
Packit eace71
		if ((rc == 0) && (nic->state == NIC_RUNNING)) {
Packit eace71
			LOG_DEBUG(PFX "%s: nic running", nic->log_name);
Packit eace71
		} else if (rc) {
Packit eace71
			LOG_DEBUG(PFX "%s: err %d", nic->log_name, rc);
Packit eace71
			rc = -EAGAIN;
Packit eace71
		}
Packit eace71
		pthread_mutex_unlock(&nic->nic_mutex);
Packit eace71
	}
Packit eace71
Packit eace71
	if (rc || nic->state != NIC_RUNNING) {
Packit eace71
		png_c->state = rc;
Packit eace71
		goto ping_done;
Packit eace71
	}
Packit eace71
Packit eace71
	ping_init(png_c, dst_addr, nic_iface->protocol, datalen);
Packit eace71
Packit eace71
	rc = do_ping_from_nic_iface(png_c);
Packit eace71
	if (png_c->state == -1)
Packit eace71
		png_c->state = rc;
Packit eace71
Packit eace71
ping_done:
Packit eace71
	LOG_INFO(PFX "ping thread end");
Packit eace71
	nic->ping_thread = INVALID_THREAD;
Packit eace71
	pthread_exit(NULL);
Packit eace71
}
Packit eace71
Packit eace71
static int parse_iface(void *arg, int do_ping)
Packit eace71
{
Packit eace71
	int rc, i;
Packit eace71
	nic_t *nic = NULL;
Packit eace71
	nic_interface_t *nic_iface;
Packit eace71
	char *transport_name;
Packit eace71
	size_t transport_name_size;
Packit eace71
	nic_lib_handle_t *handle;
Packit eace71
	iscsid_uip_broadcast_t *data;
Packit eace71
	char ipv6_buf_str[INET6_ADDRSTRLEN];
Packit eace71
	int request_type = 0;
Packit eace71
	struct iface_rec *rec;
Packit eace71
	struct iface_rec_decode ird;
Packit eace71
	struct in_addr src_match, dst_match;
Packit eace71
	pthread_attr_t attr;
Packit eace71
	struct ping_conf *png_c;
Packit eace71
Packit eace71
	data = (iscsid_uip_broadcast_t *) arg;
Packit eace71
	if (do_ping)
Packit eace71
		rec = &data->u.ping_rec.ifrec;
Packit eace71
	else
Packit eace71
		rec = &data->u.iface_rec.rec;
Packit eace71
Packit eace71
	LOG_INFO(PFX "Received request for '%s' to set IP address: '%s' "
Packit eace71
		 "VLAN: '%d'",
Packit eace71
		 rec->netdev,
Packit eace71
		 rec->ipaddress,
Packit eace71
		 rec->vlan_id);
Packit eace71
Packit eace71
	rc = decode_iface(&ird, rec);
Packit eace71
	if (ird.vlan_id && valid_vlan(ird.vlan_id) == 0) {
Packit eace71
		LOG_ERR(PFX "Invalid VLAN tag: %d", ird.vlan_id);
Packit eace71
		rc = -EIO;
Packit eace71
		goto early_exit;
Packit eace71
	}
Packit eace71
	if (rc && !ird.ip_type) {
Packit eace71
		LOG_ERR(PFX "iface err: rc=%d, ip_type=%d", rc, ird.ip_type);
Packit eace71
		goto early_exit;
Packit eace71
	}
Packit eace71
Packit eace71
	for (i = 0; i < 10; i++) {
Packit eace71
		struct timespec sleep_req, sleep_rem;
Packit eace71
Packit eace71
		if (pthread_mutex_trylock(&nic_list_mutex) == 0)
Packit eace71
			break;
Packit eace71
Packit eace71
		sleep_req.tv_sec = 0;
Packit eace71
		sleep_req.tv_nsec = 100000;
Packit eace71
		nanosleep(&sleep_req, &sleep_rem);
Packit eace71
	}
Packit eace71
Packit eace71
	if (i >= 10) {
Packit eace71
		LOG_WARN(PFX "Could not acquire nic_list_mutex lock");
Packit eace71
		rc = -EIO;
Packit eace71
		goto early_exit;
Packit eace71
	}
Packit eace71
Packit eace71
	/* nic_list_mutex locked */
Packit eace71
Packit eace71
	/*  Check if we can find the NIC device using the netdev
Packit eace71
	 *  name */
Packit eace71
	rc = from_netdev_name_find_nic(rec->netdev, &nic;;
Packit eace71
Packit eace71
	if (rc != 0) {
Packit eace71
		LOG_WARN(PFX "Couldn't find NIC: %s, creating an instance",
Packit eace71
			 rec->netdev);
Packit eace71
Packit eace71
		nic = nic_init();
Packit eace71
		if (nic == NULL) {
Packit eace71
			LOG_ERR(PFX "Couldn't allocate space for NIC %s",
Packit eace71
				rec->netdev);
Packit eace71
Packit eace71
			rc = -ENOMEM;
Packit eace71
			goto done;
Packit eace71
		}
Packit eace71
Packit eace71
		strncpy(nic->eth_device_name,
Packit eace71
			rec->netdev,
Packit eace71
			sizeof(nic->eth_device_name));
Packit eace71
		nic->config_device_name = nic->eth_device_name;
Packit eace71
		nic->log_name = nic->eth_device_name;
Packit eace71
Packit eace71
		if (nic_fill_name(nic) != 0) {
Packit eace71
			free(nic);
Packit eace71
			rc = -EIO;
Packit eace71
			goto done;
Packit eace71
		}
Packit eace71
Packit eace71
		nic_add(nic);
Packit eace71
	} else {
Packit eace71
		LOG_INFO(PFX " %s, using existing NIC",
Packit eace71
			 rec->netdev);
Packit eace71
	}
Packit eace71
Packit eace71
	pthread_mutex_lock(&nic->nic_mutex);
Packit eace71
	if (nic->flags & NIC_GOING_DOWN) {
Packit eace71
		pthread_mutex_unlock(&nic->nic_mutex);
Packit eace71
		rc = -EIO;
Packit eace71
		LOG_INFO(PFX "nic->flags GOING DOWN");
Packit eace71
		goto done;
Packit eace71
	}
Packit eace71
Packit eace71
	/*  If we retry too many times allow iscsid to timeout */
Packit eace71
	if (nic->pending_count > 1000) {
Packit eace71
		nic->pending_count = 0;
Packit eace71
		nic->flags &= ~NIC_ENABLED_PENDING;
Packit eace71
		pthread_mutex_unlock(&nic->nic_mutex);
Packit eace71
Packit eace71
		LOG_WARN(PFX "%s: pending count exceeded 1000", nic->log_name);
Packit eace71
Packit eace71
		rc = 0;
Packit eace71
		goto done;
Packit eace71
	}
Packit eace71
Packit eace71
	if (nic->flags & NIC_ENABLED_PENDING) {
Packit eace71
		struct timespec sleep_req, sleep_rem;
Packit eace71
Packit eace71
		nic->pending_count++;
Packit eace71
		pthread_mutex_unlock(&nic->nic_mutex);
Packit eace71
Packit eace71
		sleep_req.tv_sec = 2;
Packit eace71
		sleep_req.tv_nsec = 0;
Packit eace71
		nanosleep(&sleep_req, &sleep_rem);
Packit eace71
Packit eace71
		pthread_mutex_lock(&nic->nic_mutex);
Packit eace71
		if (!(nic->flags & NIC_ENABLED) ||
Packit eace71
		    nic->state != NIC_RUNNING) {
Packit eace71
			pthread_mutex_unlock(&nic->nic_mutex);
Packit eace71
			LOG_INFO(PFX "%s: enabled pending", nic->log_name);
Packit eace71
			rc = -EAGAIN;
Packit eace71
			goto done;
Packit eace71
		}
Packit eace71
	}
Packit eace71
	pthread_mutex_unlock(&nic->nic_mutex);
Packit eace71
Packit eace71
	prepare_library(nic);
Packit eace71
Packit eace71
	/*  Sanity Check to ensure the transport names are the same */
Packit eace71
	handle = nic->nic_library;
Packit eace71
	if (handle != NULL) {
Packit eace71
		(*handle->ops->lib_ops.get_transport_name) (&transport_name,
Packit eace71
							  &transport_name_size);
Packit eace71
Packit eace71
		if (strncmp(transport_name,
Packit eace71
			    rec->transport_name,
Packit eace71
			    transport_name_size) != 0) {
Packit eace71
			LOG_ERR(PFX "%s Transport name is not equal "
Packit eace71
				"expected: %s got: %s",
Packit eace71
				nic->log_name,
Packit eace71
				rec->transport_name,
Packit eace71
				transport_name);
Packit eace71
		}
Packit eace71
	} else {
Packit eace71
		LOG_ERR(PFX "%s Couldn't find nic library ", nic->log_name);
Packit eace71
		rc = -EIO;
Packit eace71
		goto done;
Packit eace71
	}
Packit eace71
Packit eace71
	LOG_INFO(PFX "%s library set using transport_name %s",
Packit eace71
		 nic->log_name, transport_name);
Packit eace71
Packit eace71
	/*  Determine how to configure the IP address */
Packit eace71
	if (ird.ip_type == AF_INET) {
Packit eace71
		if (memcmp(&ird.ipv4_addr,
Packit eace71
			   all_zeroes_addr4, sizeof(uip_ip4addr_t)) == 0) {
Packit eace71
			LOG_INFO(PFX "%s: requesting configuration using DHCP",
Packit eace71
				 nic->log_name);
Packit eace71
			request_type = IPV4_CONFIG_DHCP;
Packit eace71
		} else {
Packit eace71
			LOG_INFO(PFX "%s: requesting configuration using "
Packit eace71
				 "static IP address", nic->log_name);
Packit eace71
			request_type = IPV4_CONFIG_STATIC;
Packit eace71
		}
Packit eace71
	} else if (ird.ip_type == AF_INET6) {
Packit eace71
		/* For the new 872_22, check ipv6_autocfg for DHCPv6 instead */
Packit eace71
		switch (ird.ipv6_autocfg) {
Packit eace71
		case IPV6_AUTOCFG_DHCPV6:
Packit eace71
			request_type = IPV6_CONFIG_DHCP;
Packit eace71
			break;
Packit eace71
		case IPV6_AUTOCFG_ND:
Packit eace71
			request_type = IPV6_CONFIG_STATIC;
Packit eace71
			break;
Packit eace71
		case IPV6_AUTOCFG_NOTSPEC:
Packit eace71
			/* Treat NOTSPEC the same as NOTUSED for now */
Packit eace71
		case IPV6_AUTOCFG_NOTUSED:
Packit eace71
			/* For 871 */
Packit eace71
		default:
Packit eace71
			/* Just the IP address to determine */
Packit eace71
			if (memcmp(&ird.ipv6_addr,
Packit eace71
				   all_zeroes_addr6,
Packit eace71
				   sizeof(struct in6_addr)) == 0)
Packit eace71
				request_type = IPV6_CONFIG_DHCP;
Packit eace71
			else
Packit eace71
				request_type = IPV6_CONFIG_STATIC;
Packit eace71
		}
Packit eace71
	} else {
Packit eace71
		LOG_ERR(PFX "%s: unknown ip_type to configure: 0x%x",
Packit eace71
			nic->log_name, ird.ip_type);
Packit eace71
Packit eace71
		rc = -EIO;
Packit eace71
		goto done;
Packit eace71
	}
Packit eace71
Packit eace71
	pthread_mutex_lock(&nic->nic_mutex);
Packit eace71
Packit eace71
	nic_iface = nic_find_nic_iface(nic, ird.ip_type, ird.vlan_id,
Packit eace71
				       ird.iface_num, request_type);
Packit eace71
Packit eace71
	if (nic->flags & NIC_PATHREQ_WAIT) {
Packit eace71
		if (!nic_iface ||
Packit eace71
		    !(nic_iface->flags & NIC_IFACE_PATHREQ_WAIT)) {
Packit eace71
			int pathreq_wait;
Packit eace71
Packit eace71
			if (nic_iface &&
Packit eace71
			    (nic_iface->flags & NIC_IFACE_PATHREQ_WAIT2))
Packit eace71
				pathreq_wait = 12;
Packit eace71
			else
Packit eace71
				pathreq_wait = 10;
Packit eace71
Packit eace71
			if (nic->pathreq_pending_count < pathreq_wait) {
Packit eace71
				struct timespec sleep_req, sleep_rem;
Packit eace71
Packit eace71
				pthread_mutex_unlock(&nic->nic_mutex);
Packit eace71
Packit eace71
				nic->pathreq_pending_count++;
Packit eace71
				sleep_req.tv_sec = 0;
Packit eace71
				sleep_req.tv_nsec = 100000;
Packit eace71
				nanosleep(&sleep_req, &sleep_rem);
Packit eace71
				/* Somebody else is waiting for PATH_REQ */
Packit eace71
				LOG_INFO(PFX "%s: path req pending cnt=%d",
Packit eace71
					 nic->log_name,
Packit eace71
					 nic->pathreq_pending_count);
Packit eace71
				rc = -EAGAIN;
Packit eace71
				goto done;
Packit eace71
			} else {
Packit eace71
				nic->pathreq_pending_count = 0;
Packit eace71
				LOG_DEBUG(PFX "%s: path req pending cnt "
Packit eace71
					  "exceeded!", nic->log_name);
Packit eace71
				/* Allow to fall thru */
Packit eace71
			}
Packit eace71
		}
Packit eace71
	}
Packit eace71
Packit eace71
	nic->flags |= NIC_PATHREQ_WAIT;
Packit eace71
Packit eace71
	/* Create the network interface if it doesn't exist */
Packit eace71
	if (nic_iface == NULL) {
Packit eace71
		LOG_DEBUG(PFX "%s couldn't find interface with "
Packit eace71
			  "ip_type: 0x%x creating it",
Packit eace71
			  nic->log_name, ird.ip_type);
Packit eace71
		nic_iface = nic_iface_init();
Packit eace71
Packit eace71
		if (nic_iface == NULL) {
Packit eace71
			pthread_mutex_unlock(&nic->nic_mutex);
Packit eace71
			LOG_ERR(PFX "%s Couldn't allocate "
Packit eace71
				"interface with ip_type: 0x%x",
Packit eace71
				nic->log_name, ird.ip_type);
Packit eace71
			goto done;
Packit eace71
		}
Packit eace71
		nic_iface->protocol = ird.ip_type;
Packit eace71
		nic_iface->vlan_id = ird.vlan_id;
Packit eace71
		nic_iface->vlan_priority = ird.vlan_priority;
Packit eace71
		if (ird.mtu >= MIN_MTU_SUPPORT && ird.mtu <= MAX_MTU_SUPPORT)
Packit eace71
			nic_iface->mtu = ird.mtu;
Packit eace71
		nic_iface->iface_num = ird.iface_num;
Packit eace71
		nic_iface->request_type = request_type;
Packit eace71
		nic_add_nic_iface(nic, nic_iface);
Packit eace71
Packit eace71
		persist_all_nic_iface(nic);
Packit eace71
Packit eace71
		LOG_INFO(PFX "%s: created network interface",
Packit eace71
			 nic->log_name);
Packit eace71
	} else {
Packit eace71
		/* Move the nic_iface to the front */
Packit eace71
		set_nic_iface(nic, nic_iface);
Packit eace71
		LOG_INFO(PFX "%s: using existing network interface",
Packit eace71
			 nic->log_name);
Packit eace71
	}
Packit eace71
Packit eace71
	nic_iface->flags |= NIC_IFACE_PATHREQ_WAIT1;
Packit eace71
	if (nic->nl_process_thread == INVALID_THREAD) {
Packit eace71
		pthread_attr_init(&attr);
Packit eace71
		pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
Packit eace71
		rc = pthread_create(&nic->nl_process_thread, &attr,
Packit eace71
				    nl_process_handle_thread, nic);
Packit eace71
		if (rc != 0) {
Packit eace71
			LOG_ERR(PFX "%s: Could not create NIC NL "
Packit eace71
				"processing thread [%s]", nic->log_name,
Packit eace71
				strerror(rc));
Packit eace71
			nic->nl_process_thread = INVALID_THREAD;
Packit eace71
			/* Reset both WAIT flags */
Packit eace71
			nic_iface->flags &= ~NIC_IFACE_PATHREQ_WAIT;
Packit eace71
			nic->flags &= ~NIC_PATHREQ_WAIT;
Packit eace71
		}
Packit eace71
	}
Packit eace71
Packit eace71
	pthread_mutex_unlock(&nic->nic_mutex);
Packit eace71
Packit eace71
	if (nic_iface->ustack.ip_config == request_type) {
Packit eace71
		/* Same request_type, check for STATIC address change */
Packit eace71
		if (request_type == IPV4_CONFIG_STATIC) {
Packit eace71
			if (memcmp(nic_iface->ustack.hostaddr, &ird.ipv4_addr,
Packit eace71
				   sizeof(struct in_addr)))
Packit eace71
				goto reacquire;
Packit eace71
		} else if (request_type == IPV6_CONFIG_STATIC) {
Packit eace71
			if (memcmp(nic_iface->ustack.hostaddr6, &ird.ipv6_addr,
Packit eace71
				   sizeof(struct in6_addr)))
Packit eace71
				goto reacquire;
Packit eace71
			else
Packit eace71
				inet_ntop(AF_INET6, &ird.ipv6_addr,
Packit eace71
					  ipv6_buf_str,
Packit eace71
					  sizeof(ipv6_buf_str));
Packit eace71
		}
Packit eace71
		LOG_INFO(PFX "%s: IP configuration didn't change using 0x%x",
Packit eace71
			 nic->log_name, nic_iface->ustack.ip_config);
Packit eace71
		/* No need to acquire the IP address */
Packit eace71
		inet_ntop(AF_INET6, &ird.ipv6_addr, ipv6_buf_str,
Packit eace71
			  sizeof(ipv6_buf_str));
Packit eace71
Packit eace71
		goto enable_nic;
Packit eace71
	}
Packit eace71
reacquire:
Packit eace71
	/* Config needs to re-acquire for this nic_iface */
Packit eace71
	pthread_mutex_lock(&nic->nic_mutex);
Packit eace71
	nic_iface->flags |= NIC_IFACE_ACQUIRE;
Packit eace71
	pthread_mutex_unlock(&nic->nic_mutex);
Packit eace71
Packit eace71
	/* Disable the nic loop from further processing, upon returned,
Packit eace71
	   the nic_iface should be cleared */
Packit eace71
	nic_disable(nic, 0);
Packit eace71
Packit eace71
	/*  Check to see if this is using DHCP or if this is
Packit eace71
	 *  a static IPv4 address.  This is done by checking
Packit eace71
	 *  if the IP address is equal to 0.0.0.0.  If it is
Packit eace71
	 *  then the user has specified to use DHCP.  If not
Packit eace71
	 *  then the user has spcicied to use a static IP address
Packit eace71
	 *  an the default netmask will be used */
Packit eace71
	switch (request_type) {
Packit eace71
	case IPV4_CONFIG_DHCP:
Packit eace71
		memset(nic_iface->ustack.hostaddr, 0, sizeof(struct in_addr));
Packit eace71
		LOG_INFO(PFX "%s: configuring using DHCP", nic->log_name);
Packit eace71
		nic_iface->ustack.ip_config = IPV4_CONFIG_DHCP;
Packit eace71
		break;
Packit eace71
Packit eace71
	case IPV4_CONFIG_STATIC:
Packit eace71
		memcpy(nic_iface->ustack.hostaddr, &ird.ipv4_addr,
Packit eace71
		       sizeof(struct in_addr));
Packit eace71
		LOG_INFO(PFX "%s: configuring using static IP "
Packit eace71
			 "IPv4 address :%s ",
Packit eace71
			 nic->log_name, inet_ntoa(ird.ipv4_addr));
Packit eace71
Packit eace71
		if (ird.ipv4_subnet_mask.s_addr)
Packit eace71
			memcpy(nic_iface->ustack.netmask,
Packit eace71
			       &ird.ipv4_subnet_mask, sizeof(struct in_addr));
Packit eace71
		LOG_INFO(PFX " netmask: %s", inet_ntoa(ird.ipv4_subnet_mask));
Packit eace71
Packit eace71
		/* Default route */
Packit eace71
		if (ird.ipv4_gateway.s_addr) {
Packit eace71
			/* Check for validity */
Packit eace71
			src_match.s_addr = ird.ipv4_addr.s_addr &
Packit eace71
					   ird.ipv4_subnet_mask.s_addr;
Packit eace71
			dst_match.s_addr = ird.ipv4_gateway.s_addr &
Packit eace71
					   ird.ipv4_subnet_mask.s_addr;
Packit eace71
			if (src_match.s_addr == dst_match.s_addr)
Packit eace71
				memcpy(nic_iface->ustack.default_route_addr,
Packit eace71
				       &ird.ipv4_gateway,
Packit eace71
				       sizeof(struct in_addr));
Packit eace71
		}
Packit eace71
		nic_iface->ustack.ip_config = IPV4_CONFIG_STATIC;
Packit eace71
		break;
Packit eace71
Packit eace71
	case IPV6_CONFIG_DHCP:
Packit eace71
		memset(nic_iface->ustack.hostaddr6, 0,
Packit eace71
		       sizeof(struct in6_addr));
Packit eace71
		nic_iface->ustack.prefix_len = ird.prefix_len;
Packit eace71
		nic_iface->ustack.ipv6_autocfg = ird.ipv6_autocfg;
Packit eace71
		nic_iface->ustack.linklocal_autocfg = ird.linklocal_autocfg;
Packit eace71
		nic_iface->ustack.router_autocfg = ird.router_autocfg;
Packit eace71
Packit eace71
		if (memcmp(&ird.ipv6_subnet_mask, all_zeroes_addr6,
Packit eace71
			   sizeof(struct in6_addr)))
Packit eace71
			memcpy(nic_iface->ustack.netmask6,
Packit eace71
			       &ird.ipv6_subnet_mask, sizeof(struct in6_addr));
Packit eace71
		if (ird.linklocal_autocfg == IPV6_LL_AUTOCFG_OFF)
Packit eace71
			memcpy(nic_iface->ustack.linklocal6,
Packit eace71
			       &ird.ipv6_linklocal, sizeof(struct in6_addr));
Packit eace71
		if (ird.router_autocfg == IPV6_RTR_AUTOCFG_OFF)
Packit eace71
			memcpy(nic_iface->ustack.default_route_addr6,
Packit eace71
			       &ird.ipv6_router, sizeof(struct in6_addr));
Packit eace71
		inet_ntop(AF_INET6, &ird.ipv6_addr, ipv6_buf_str,
Packit eace71
			  sizeof(ipv6_buf_str));
Packit eace71
		LOG_INFO(PFX "%s: configuring using DHCPv6",
Packit eace71
			 nic->log_name);
Packit eace71
		nic_iface->ustack.ip_config = IPV6_CONFIG_DHCP;
Packit eace71
		break;
Packit eace71
Packit eace71
	case IPV6_CONFIG_STATIC:
Packit eace71
		memcpy(nic_iface->ustack.hostaddr6, &ird.ipv6_addr,
Packit eace71
		       sizeof(struct in6_addr));
Packit eace71
		nic_iface->ustack.prefix_len = ird.prefix_len;
Packit eace71
		nic_iface->ustack.ipv6_autocfg = ird.ipv6_autocfg;
Packit eace71
		nic_iface->ustack.linklocal_autocfg = ird.linklocal_autocfg;
Packit eace71
		nic_iface->ustack.router_autocfg = ird.router_autocfg;
Packit eace71
Packit eace71
		if (memcmp(&ird.ipv6_subnet_mask, all_zeroes_addr6,
Packit eace71
			   sizeof(struct in6_addr)))
Packit eace71
			memcpy(nic_iface->ustack.netmask6,
Packit eace71
			       &ird.ipv6_subnet_mask, sizeof(struct in6_addr));
Packit eace71
		if (ird.linklocal_autocfg == IPV6_LL_AUTOCFG_OFF)
Packit eace71
			memcpy(nic_iface->ustack.linklocal6,
Packit eace71
			       &ird.ipv6_linklocal, sizeof(struct in6_addr));
Packit eace71
		if (ird.router_autocfg == IPV6_RTR_AUTOCFG_OFF)
Packit eace71
			memcpy(nic_iface->ustack.default_route_addr6,
Packit eace71
			       &ird.ipv6_router, sizeof(struct in6_addr));
Packit eace71
Packit eace71
		inet_ntop(AF_INET6, &ird.ipv6_addr, ipv6_buf_str,
Packit eace71
			  sizeof(ipv6_buf_str));
Packit eace71
		LOG_INFO(PFX "%s: configuring using static IP "
Packit eace71
			 "IPv6 address: '%s'", nic->log_name, ipv6_buf_str);
Packit eace71
Packit eace71
		nic_iface->ustack.ip_config = IPV6_CONFIG_STATIC;
Packit eace71
		break;
Packit eace71
Packit eace71
	default:
Packit eace71
		LOG_INFO(PFX "%s: Unknown request type: 0x%x",
Packit eace71
			 nic->log_name, request_type);
Packit eace71
Packit eace71
	}
Packit eace71
Packit eace71
enable_nic:
Packit eace71
	switch (nic->state) {
Packit eace71
	case NIC_STOPPED:
Packit eace71
		/* This thread will be thrown away when completed */
Packit eace71
		if (nic->enable_thread != INVALID_THREAD) {
Packit eace71
			rc = pthread_cancel(nic->enable_thread);
Packit eace71
			if (rc != 0) {
Packit eace71
				LOG_INFO(PFX "%s: failed to cancel enable NIC "
Packit eace71
					 "thread\n", nic->log_name);
Packit eace71
				goto eagain;
Packit eace71
			}
Packit eace71
		}
Packit eace71
		pthread_attr_init(&attr);
Packit eace71
		pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
Packit eace71
		rc = pthread_create(&nic->enable_thread, &attr,
Packit eace71
				    enable_nic_thread, (void *)nic);
Packit eace71
		if (rc != 0)
Packit eace71
			LOG_WARN(PFX "%s: failed starting enable NIC thread\n",
Packit eace71
				 nic->log_name);
Packit eace71
eagain:
Packit eace71
		rc = -EAGAIN;
Packit eace71
		break;
Packit eace71
Packit eace71
	case NIC_RUNNING:
Packit eace71
		LOG_INFO(PFX "%s: NIC already enabled "
Packit eace71
			 "flags: 0x%x state: 0x%x\n",
Packit eace71
			 nic->log_name, nic->flags, nic->state);
Packit eace71
		rc = 0;
Packit eace71
		break;
Packit eace71
	default:
Packit eace71
		LOG_INFO(PFX "%s: NIC enable still in progress "
Packit eace71
			 "flags: 0x%x state: 0x%x\n",
Packit eace71
			 nic->log_name, nic->flags, nic->state);
Packit eace71
		rc = -EAGAIN;
Packit eace71
	}
Packit eace71
Packit eace71
	LOG_INFO(PFX "ISCSID_UIP_IPC_GET_IFACE: command: %x "
Packit eace71
		 "name: %s, netdev: %s ipaddr: %s vlan: %d transport_name:%s",
Packit eace71
		 data->header.command, rec->name, rec->netdev,
Packit eace71
		 (ird.ip_type == AF_INET) ? inet_ntoa(ird.ipv4_addr) :
Packit eace71
					     ipv6_buf_str,
Packit eace71
		 ird.vlan_id, rec->transport_name);
Packit eace71
Packit eace71
	if (do_ping) {
Packit eace71
		if (nic->ping_thread != INVALID_THREAD) {
Packit eace71
			rc = pthread_cancel(nic->ping_thread);
Packit eace71
			if (rc != 0) {
Packit eace71
				LOG_INFO(PFX "%s: failed to cancel ping thread",
Packit eace71
					 nic->log_name);
Packit eace71
				rc = -EAGAIN;
Packit eace71
				goto done;
Packit eace71
			}
Packit eace71
		}
Packit eace71
Packit eace71
		png_c = malloc(sizeof(struct ping_conf));
Packit eace71
		if (!png_c) {
Packit eace71
			LOG_ERR(PFX "Memory alloc failed for ping conf");
Packit eace71
			rc = -ENOMEM;
Packit eace71
			goto done;
Packit eace71
		}
Packit eace71
Packit eace71
		memset(png_c, 0, sizeof(struct ping_conf));
Packit eace71
		png_c->nic_iface = nic_iface;
Packit eace71
		png_c->data = arg;
Packit eace71
		nic_iface->ustack.ping_conf = png_c;
Packit eace71
Packit eace71
		/* Spawn a thread to perform ping operation.
Packit eace71
		 * This thread will exit when done.
Packit eace71
		 */
Packit eace71
		rc = pthread_create(&nic->ping_thread, NULL,
Packit eace71
				    perform_ping, (void *)png_c);
Packit eace71
		if (rc != 0) {
Packit eace71
			LOG_WARN(PFX "%s: failed starting ping thread\n",
Packit eace71
				 nic->log_name);
Packit eace71
		} else {
Packit eace71
			pthread_join(nic->ping_thread, NULL);
Packit eace71
			rc = png_c->state;
Packit eace71
			if (rc == -EAGAIN)
Packit eace71
				png_c->state = 0;
Packit eace71
		}
Packit eace71
		free(png_c);
Packit eace71
		nic_iface->ustack.ping_conf = NULL;
Packit eace71
	}
Packit eace71
Packit eace71
done:
Packit eace71
	pthread_mutex_unlock(&nic_list_mutex);
Packit eace71
Packit eace71
early_exit:
Packit eace71
	return rc;
Packit eace71
}
Packit eace71
Packit eace71
/**
Packit eace71
 *  process_iscsid_broadcast() - This function is used to process the
Packit eace71
 *                               broadcast messages from iscsid
Packit eace71
 *
Packit eace71
 *                               s2 is an open file descriptor, which
Packit eace71
 *                               must not be left open upon return
Packit eace71
 */
Packit eace71
int process_iscsid_broadcast(int s2)
Packit eace71
{
Packit eace71
	int rc = 0;
Packit eace71
	iscsid_uip_broadcast_t *data;
Packit eace71
	iscsid_uip_rsp_t rsp;
Packit eace71
	FILE *fd;
Packit eace71
	size_t size;
Packit eace71
	iscsid_uip_cmd_e cmd;
Packit eace71
	uint32_t payload_len;
Packit eace71
Packit eace71
	fd = fdopen(s2, "r+");
Packit eace71
	if (fd == NULL) {
Packit eace71
		LOG_ERR(PFX "Couldn't open file descriptor: %d(%s)",
Packit eace71
			errno, strerror(errno));
Packit eace71
		close(s2);
Packit eace71
		return -EIO;
Packit eace71
	}
Packit eace71
Packit eace71
	/*  This will be freed by parse_iface_thread() */
Packit eace71
	data = (iscsid_uip_broadcast_t *) calloc(1, sizeof(*data));
Packit eace71
	if (data == NULL) {
Packit eace71
		LOG_ERR(PFX "Couldn't allocate memory for iface data");
Packit eace71
		rc = -ENOMEM;
Packit eace71
		goto error;
Packit eace71
	}
Packit eace71
	memset(data, 0, sizeof(*data));
Packit eace71
Packit eace71
	size = fread(data, sizeof(iscsid_uip_broadcast_header_t), 1, fd);
Packit eace71
	if (!size) {
Packit eace71
		LOG_ERR(PFX "Could not read request: %d(%s)",
Packit eace71
			errno, strerror(errno));
Packit eace71
		rc = ferror(fd);
Packit eace71
		goto error;
Packit eace71
	}
Packit eace71
Packit eace71
	cmd = data->header.command;
Packit eace71
	payload_len = data->header.payload_len;
Packit eace71
	if (payload_len > sizeof(data->u)) {
Packit eace71
		LOG_ERR(PFX "Data payload length too large (%d). Corrupt payload?",
Packit eace71
				payload_len);
Packit eace71
		rc = -EINVAL;
Packit eace71
		goto error;
Packit eace71
	}
Packit eace71
Packit eace71
	LOG_DEBUG(PFX "recv iscsid request: cmd: %d, payload_len: %d",
Packit eace71
		  cmd, payload_len);
Packit eace71
Packit eace71
	memset(&rsp, 0, sizeof(rsp));
Packit eace71
Packit eace71
	switch (cmd) {
Packit eace71
	case ISCSID_UIP_IPC_GET_IFACE:
Packit eace71
		size = fread(&data->u.iface_rec, payload_len, 1, fd);
Packit eace71
		if (!size) {
Packit eace71
			LOG_ERR(PFX "Could not read data: %d(%s)",
Packit eace71
				errno, strerror(errno));
Packit eace71
			goto error;
Packit eace71
		}
Packit eace71
Packit eace71
		rc = parse_iface(data, 0);
Packit eace71
		switch (rc) {
Packit eace71
		case 0:
Packit eace71
			rsp.command = cmd;
Packit eace71
			rsp.err = ISCSID_UIP_MGMT_IPC_DEVICE_UP;
Packit eace71
			break;
Packit eace71
		case -EAGAIN:
Packit eace71
			rsp.command = cmd;
Packit eace71
			rsp.err = ISCSID_UIP_MGMT_IPC_DEVICE_INITIALIZING;
Packit eace71
			break;
Packit eace71
		default:
Packit eace71
			rsp.command = cmd;
Packit eace71
			rsp.err = ISCSID_UIP_MGMT_IPC_ERR;
Packit eace71
		}
Packit eace71
Packit eace71
		break;
Packit eace71
	case ISCSID_UIP_IPC_PING:
Packit eace71
		size = fread(&data->u.ping_rec, payload_len, 1, fd);
Packit eace71
		if (!size) {
Packit eace71
			LOG_ERR(PFX "Could not read data: %d(%s)",
Packit eace71
				errno, strerror(errno));
Packit eace71
			goto error;
Packit eace71
		}
Packit eace71
Packit eace71
		rc = parse_iface(data, 1);
Packit eace71
		rsp.command = cmd;
Packit eace71
		rsp.ping_sc = rc;
Packit eace71
Packit eace71
		switch (rc) {
Packit eace71
		case 0:
Packit eace71
			rsp.err = ISCSID_UIP_MGMT_IPC_DEVICE_UP;
Packit eace71
			break;
Packit eace71
		case -EAGAIN:
Packit eace71
			rsp.err = ISCSID_UIP_MGMT_IPC_DEVICE_INITIALIZING;
Packit eace71
			break;
Packit eace71
		default:
Packit eace71
			rsp.err = ISCSID_UIP_MGMT_IPC_ERR;
Packit eace71
		}
Packit eace71
Packit eace71
		break;
Packit eace71
	default:
Packit eace71
		LOG_WARN(PFX "Unknown iscsid broadcast command: %x",
Packit eace71
			 data->header.command);
Packit eace71
Packit eace71
		/*  Send a response back to iscsid to tell it the
Packit eace71
		   operation succeeded */
Packit eace71
		rsp.command = cmd;
Packit eace71
		rsp.err = ISCSID_UIP_MGMT_IPC_OK;
Packit eace71
		break;
Packit eace71
	}
Packit eace71
Packit eace71
	size = fwrite(&rsp, sizeof(rsp), 1, fd);
Packit eace71
	if (size == -1) {
Packit eace71
		LOG_ERR(PFX "Could not send response: %d(%s)",
Packit eace71
			errno, strerror(errno));
Packit eace71
		rc = ferror(fd);
Packit eace71
	}
Packit eace71
Packit eace71
error:
Packit eace71
	if (data)
Packit eace71
		free(data);
Packit eace71
	fclose(fd);
Packit eace71
Packit eace71
	return rc;
Packit eace71
}
Packit eace71
Packit eace71
static void iscsid_loop_close(void *arg)
Packit eace71
{
Packit eace71
	close(iscsid_opts.fd);
Packit eace71
Packit eace71
	LOG_INFO(PFX "iSCSI daemon socket closed");
Packit eace71
}
Packit eace71
Packit eace71
/*
Packit eace71
 * check that the peer user is privilidged
Packit eace71
 *
Packit eace71
 * return 1 if peer is ok else 0
Packit eace71
 *
Packit eace71
 * XXX: this function is copied from iscsid_ipc.c and should be
Packit eace71
 * moved into a common library
Packit eace71
 */
Packit eace71
static int
Packit eace71
mgmt_peeruser(int sock, char *user)
Packit eace71
{
Packit eace71
	struct ucred peercred;
Packit eace71
	socklen_t so_len = sizeof(peercred);
Packit eace71
	struct passwd *pass;
Packit eace71
Packit eace71
	errno = 0;
Packit eace71
	if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &peercred,
Packit eace71
		&so_len) != 0 || so_len != sizeof(peercred)) {
Packit eace71
		/* We didn't get a valid credentials struct. */
Packit eace71
		LOG_ERR(PFX "peeruser_unux: error receiving credentials: %m");
Packit eace71
		return 0;
Packit eace71
	}
Packit eace71
Packit eace71
	pass = getpwuid(peercred.uid);
Packit eace71
	if (pass == NULL) {
Packit eace71
		LOG_ERR(PFX "peeruser_unix: unknown local user with uid %d",
Packit eace71
				(int) peercred.uid);
Packit eace71
		return 0;
Packit eace71
	}
Packit eace71
Packit eace71
	strlcpy(user, pass->pw_name, PEERUSER_MAX);
Packit eace71
	return 1;
Packit eace71
}
Packit eace71
Packit eace71
/**
Packit eace71
 *  iscsid_loop() - This is the function which will process the broadcast
Packit eace71
 *                  messages from iscsid
Packit eace71
 *
Packit eace71
 */
Packit eace71
static void *iscsid_loop(void *arg)
Packit eace71
{
Packit eace71
	int rc;
Packit eace71
	sigset_t set;
Packit eace71
	char user[PEERUSER_MAX];
Packit eace71
Packit eace71
	pthread_cleanup_push(iscsid_loop_close, arg);
Packit eace71
Packit eace71
	sigfillset(&set);
Packit eace71
	rc = pthread_sigmask(SIG_BLOCK, &set, NULL);
Packit eace71
	if (rc != 0) {
Packit eace71
		LOG_ERR(PFX
Packit eace71
			"Couldn't set signal mask for the iscisd listening "
Packit eace71
			"thread");
Packit eace71
	}
Packit eace71
Packit eace71
	LOG_DEBUG(PFX "Started iscsid listening thread");
Packit eace71
Packit eace71
	while (1) {
Packit eace71
		struct sockaddr_un remote;
Packit eace71
		socklen_t sock_len;
Packit eace71
		int s2;
Packit eace71
Packit eace71
		LOG_DEBUG(PFX "Waiting for iscsid command");
Packit eace71
Packit eace71
		sock_len = sizeof(remote);
Packit eace71
		s2 = accept(iscsid_opts.fd,
Packit eace71
			    (struct sockaddr *)&remote, &sock_len);
Packit eace71
		if (s2 == -1) {
Packit eace71
			if (errno == EAGAIN) {
Packit eace71
				LOG_DEBUG("Got EAGAIN from accept");
Packit eace71
				sleep(1);
Packit eace71
				continue;
Packit eace71
			} else if (errno == EINTR) {
Packit eace71
				LOG_DEBUG("Got EINTR from accept");
Packit eace71
				/*  The program is terminating, time to exit */
Packit eace71
				break;
Packit eace71
			}
Packit eace71
Packit eace71
			LOG_ERR(PFX "Could not accept: %d(%s)",
Packit eace71
				s2, strerror(errno));
Packit eace71
			continue;
Packit eace71
		}
Packit eace71
Packit eace71
		if (!mgmt_peeruser(iscsid_opts.fd, user) || strncmp(user, "root", PEERUSER_MAX)) {
Packit eace71
			close(s2);
Packit eace71
			LOG_ERR(PFX "Access error: non-administrative connection rejected");
Packit eace71
			break;
Packit eace71
		}
Packit eace71
Packit eace71
		/* this closes the file descriptor s2 */
Packit eace71
		process_iscsid_broadcast(s2);
Packit eace71
	}
Packit eace71
Packit eace71
	pthread_cleanup_pop(0);
Packit eace71
Packit eace71
	LOG_ERR(PFX "exit iscsid listening thread");
Packit eace71
Packit eace71
	pthread_exit(NULL);
Packit eace71
}
Packit eace71
Packit eace71
#define SD_SOCKET_FDS_START 3
Packit eace71
Packit eace71
static int ipc_systemd(void)
Packit eace71
{
Packit eace71
	char *env;
Packit eace71
Packit eace71
	env = getenv("LISTEN_PID");
Packit eace71
Packit eace71
	if (!env || (strtoul(env, NULL, 10) != getpid()))
Packit eace71
		return -EINVAL;
Packit eace71
Packit eace71
	env = getenv("LISTEN_FDS");
Packit eace71
Packit eace71
	if (!env)
Packit eace71
		return -EINVAL;
Packit eace71
Packit eace71
	if (strtoul(env, NULL, 10) != 1) {
Packit eace71
		LOG_ERR("Did not receive exactly one IPC socket from systemd");
Packit eace71
		return -EINVAL;
Packit eace71
	}
Packit eace71
Packit eace71
	return SD_SOCKET_FDS_START;
Packit eace71
}
Packit eace71
Packit eace71
/******************************************************************************
Packit eace71
 *  Initialize/Cleanup routines
Packit eace71
 ******************************************************************************/
Packit eace71
/**
Packit eace71
 *  iscsid_init() - This function will setup the thread used to listen for
Packit eace71
 *                  the iscsid broadcast messages
Packit eace71
 *  @return 0 on success, <0 on failure
Packit eace71
 */
Packit eace71
int iscsid_init()
Packit eace71
{
Packit eace71
	int rc, addr_len;
Packit eace71
	struct sockaddr_un addr;
Packit eace71
Packit eace71
	iscsid_opts.fd = ipc_systemd();
Packit eace71
	if (iscsid_opts.fd >= 0)
Packit eace71
		return 0;
Packit eace71
Packit eace71
	iscsid_opts.fd = socket(AF_LOCAL, SOCK_STREAM, 0);
Packit eace71
	if (iscsid_opts.fd < 0) {
Packit eace71
		LOG_ERR(PFX "Can not create IPC socket");
Packit eace71
		return iscsid_opts.fd;
Packit eace71
	}
Packit eace71
Packit eace71
	addr_len = offsetof(struct sockaddr_un, sun_path) + strlen(ISCSID_UIP_NAMESPACE) + 1;
Packit eace71
Packit eace71
	memset(&addr, 0, sizeof(addr));
Packit eace71
	addr.sun_family = AF_LOCAL;
Packit eace71
	memcpy((char *)&addr.sun_path + 1, ISCSID_UIP_NAMESPACE,
Packit eace71
	       strlen(ISCSID_UIP_NAMESPACE));
Packit eace71
Packit eace71
	rc = bind(iscsid_opts.fd, (struct sockaddr *)&addr, addr_len);
Packit eace71
	if (rc < 0) {
Packit eace71
		LOG_ERR(PFX "Can not bind IPC socket: %s", strerror(errno));
Packit eace71
		goto error;
Packit eace71
	}
Packit eace71
Packit eace71
	rc = listen(iscsid_opts.fd, 32);
Packit eace71
	if (rc < 0) {
Packit eace71
		LOG_ERR(PFX "Can not listen IPC socket: %s", strerror(errno));
Packit eace71
		goto error;
Packit eace71
	}
Packit eace71
Packit eace71
	return 0;
Packit eace71
error:
Packit eace71
	close(iscsid_opts.fd);
Packit eace71
	iscsid_opts.fd = INVALID_FD;
Packit eace71
Packit eace71
	return rc;
Packit eace71
}
Packit eace71
Packit eace71
/**
Packit eace71
 *  iscsid_start() - This function will start the thread used to listen for
Packit eace71
 *                  the iscsid broadcast messages
Packit eace71
 *  @return 0 on success, <0 on failure
Packit eace71
 */
Packit eace71
int iscsid_start()
Packit eace71
{
Packit eace71
	pthread_attr_t attr;
Packit eace71
	int rc;
Packit eace71
Packit eace71
	pthread_attr_init(&attr);
Packit eace71
	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
Packit eace71
	rc = pthread_create(&iscsid_opts.thread, &attr, iscsid_loop, NULL);
Packit eace71
	if (rc != 0) {
Packit eace71
		LOG_ERR(PFX "Could not start iscsid listening thread rc=%d",
Packit eace71
			rc);
Packit eace71
		goto error;
Packit eace71
	}
Packit eace71
Packit eace71
	return 0;
Packit eace71
Packit eace71
error:
Packit eace71
	close(iscsid_opts.fd);
Packit eace71
	iscsid_opts.fd = INVALID_FD;
Packit eace71
Packit eace71
	return rc;
Packit eace71
}
Packit eace71
Packit eace71
/**
Packit eace71
 *  iscsid_cleanup() - This is called when stoping the thread listening
Packit eace71
 *                     for the iscsid broadcast messages
Packit eace71
 */
Packit eace71
void iscsid_cleanup()
Packit eace71
{
Packit eace71
	int rc;
Packit eace71
Packit eace71
	if (iscsid_opts.fd != INVALID_FD &&
Packit eace71
	    iscsid_opts.thread != INVALID_THREAD) {
Packit eace71
		rc = pthread_cancel(iscsid_opts.thread);
Packit eace71
		if (rc != 0) {
Packit eace71
			LOG_ERR("Could not cancel iscsid listening thread: %s",
Packit eace71
				strerror(rc));
Packit eace71
		}
Packit eace71
	}
Packit eace71
Packit eace71
	LOG_INFO(PFX "iscsid listening thread has shutdown");
Packit eace71
}