int dhcpv6_send_packet(int index, struct dhcpv6_packet *dhcp_pkt, int len)
{
	struct msghdr m;
	struct iovec v;
	struct in6_pktinfo *pktinfo;
	struct cmsghdr *cmsg;
	int fd, ret, opt = 1;
	struct sockaddr_in6 src;
	struct sockaddr_in6 dst;
	void *control_buf;
	size_t control_buf_len;

	fd = socket(PF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP);
	if (fd < 0)
		return -errno;

	if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
		int err = errno;
		close(fd);
		return -err;
	}

	memset(&src, 0, sizeof(src));
	src.sin6_family = AF_INET6;
	src.sin6_port = htons(DHCPV6_CLIENT_PORT);

	if (bind(fd, (struct sockaddr *) &src, sizeof(src)) <0) {
		int err = errno;
		close(fd);
		return -err;
	}

	memset(&dst, 0, sizeof(dst));
	dst.sin6_family = AF_INET6;
	dst.sin6_port = htons(DHCPV6_SERVER_PORT);

	dst.sin6_addr = in6addr_all_dhcp_relay_agents_and_servers_mc;

	control_buf_len = CMSG_SPACE(sizeof(struct in6_pktinfo));
	control_buf = g_try_malloc0(control_buf_len);
	if (!control_buf) {
		close(fd);
		return -ENOMEM;
	}

	memset(&m, 0, sizeof(m));
	memset(&v, 0, sizeof(v));

	m.msg_name = &dst;
	m.msg_namelen = sizeof(dst);

	v.iov_base = (char *)dhcp_pkt;
	v.iov_len = len;
	m.msg_iov = &v;
	m.msg_iovlen = 1;

	m.msg_control = control_buf;
	m.msg_controllen = control_buf_len;
	cmsg = CMSG_FIRSTHDR(&m);
	cmsg->cmsg_level = IPPROTO_IPV6;
	cmsg->cmsg_type = IPV6_PKTINFO;
	cmsg->cmsg_len = CMSG_LEN(sizeof(*pktinfo));

	pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg);
	memset(pktinfo, 0, sizeof(*pktinfo));
	pktinfo->ipi6_ifindex = index;
	m.msg_controllen = cmsg->cmsg_len;

	ret = sendmsg(fd, &m, 0);
	if (ret < 0) {
		char *msg = "DHCPv6 msg send failed";

		if (errno == EADDRNOTAVAIL) {
			char *str = g_strdup_printf("%s (index %d)",
					msg, index);
			perror(str);
			g_free(str);
		} else
			perror(msg);
	}

	g_free(control_buf);
	close(fd);

	return ret;
}
int dhcpv6_send_packet(int index, struct dhcpv6_packet *dhcp_pkt, int len)
{
	struct msghdr m;
	struct iovec v;
	struct in6_pktinfo *pktinfo;
	struct cmsghdr *cmsg;
	int fd, ret, opt = 1;
	struct sockaddr_in6 src;
	struct sockaddr_in6 dst;
	void *control_buf;
	size_t control_buf_len;

	fd = socket(PF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP);
	if (fd < 0)
		return -errno;

	if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
		int err = errno;
		close(fd);
		return -err;
	}

	memset(&src, 0, sizeof(src));
	src.sin6_family = AF_INET6;
	src.sin6_port = htons(DHCPV6_CLIENT_PORT);

	if (bind(fd, (struct sockaddr *) &src, sizeof(src)) <0) {
		int err = errno;
		close(fd);
		return -err;
	}

	memset(&dst, 0, sizeof(dst));
	dst.sin6_family = AF_INET6;
	dst.sin6_port = htons(DHCPV6_SERVER_PORT);

	dst.sin6_addr = in6addr_all_dhcp_relay_agents_and_servers_mc;

	control_buf_len = CMSG_SPACE(sizeof(struct in6_pktinfo));
	control_buf = g_try_malloc0(control_buf_len);
	if (!control_buf) {
		close(fd);
		return -ENOMEM;
	}

	memset(&m, 0, sizeof(m));
	memset(&v, 0, sizeof(v));

	m.msg_name = &dst;
	m.msg_namelen = sizeof(dst);

	v.iov_base = (char *)dhcp_pkt;
	v.iov_len = len;
	m.msg_iov = &v;
	m.msg_iovlen = 1;

	m.msg_control = control_buf;
	m.msg_controllen = control_buf_len;
	cmsg = CMSG_FIRSTHDR(&m);
	cmsg->cmsg_level = IPPROTO_IPV6;
	cmsg->cmsg_type = IPV6_PKTINFO;
	cmsg->cmsg_len = CMSG_LEN(sizeof(*pktinfo));

	pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg);
	memset(pktinfo, 0, sizeof(*pktinfo));
	pktinfo->ipi6_ifindex = index;
	m.msg_controllen = cmsg->cmsg_len;

	ret = sendmsg(fd, &m, 0);
	if (ret < 0) {
		char *msg = "DHCPv6 msg send failed";

		if (errno == EADDRNOTAVAIL) {
			char *str = g_strdup_printf("%s (index %d)",
					msg, index);
			perror(str);
			g_free(str);
		} else
			perror(msg);
	}

	g_free(control_buf);
	close(fd);

	return ret;
}