Beispiel #1
0
static int
pfkey_send(int sd, uint8_t satype, uint8_t mtype, uint8_t dir,
    int af, union ldpd_addr *src, union ldpd_addr *dst, uint32_t spi,
    uint8_t aalg, int alen, char *akey, uint8_t ealg, int elen, char *ekey,
    uint16_t sport, uint16_t dport)
{
	struct sadb_msg		smsg;
	struct sadb_sa		sa;
	struct sadb_address	sa_src, sa_dst;
	struct sadb_key		sa_akey, sa_ekey;
	struct sadb_spirange	sa_spirange;
	struct iovec		iov[IOV_CNT];
	ssize_t			n;
	int			len = 0;
	int			iov_cnt;
	struct sockaddr_storage	smask, dmask;
	union sockunion		su_src, su_dst;

	if (!pid)
		pid = getpid();

	/* we need clean sockaddr... no ports set */
	memset(&smask, 0, sizeof(smask));

	addr2sa(af, src, 0, &su_src);

	switch (af) {
	case AF_INET:
		memset(&((struct sockaddr_in *)&smask)->sin_addr, 0xff, 32/8);
		break;
	case AF_INET6:
		memset(&((struct sockaddr_in6 *)&smask)->sin6_addr, 0xff,
		    128/8);
		break;
	default:
		return (-1);
	}
	smask.ss_family = su_src.sa.sa_family;
	smask.ss_len = sockaddr_len(&su_src.sa);

	memset(&dmask, 0, sizeof(dmask));

	addr2sa(af, dst, 0, &su_dst);

	switch (af) {
	case AF_INET:
		memset(&((struct sockaddr_in *)&dmask)->sin_addr, 0xff, 32/8);
		break;
	case AF_INET6:
		memset(&((struct sockaddr_in6 *)&dmask)->sin6_addr, 0xff,
		    128/8);
		break;
	default:
		return (-1);
	}
	dmask.ss_family = su_dst.sa.sa_family;
	dmask.ss_len = sockaddr_len(&su_dst.sa);

	memset(&smsg, 0, sizeof(smsg));
	smsg.sadb_msg_version = PF_KEY_V2;
	smsg.sadb_msg_seq = ++sadb_msg_seq;
	smsg.sadb_msg_pid = pid;
	smsg.sadb_msg_len = sizeof(smsg) / 8;
	smsg.sadb_msg_type = mtype;
	smsg.sadb_msg_satype = satype;

	switch (mtype) {
	case SADB_GETSPI:
		memset(&sa_spirange, 0, sizeof(sa_spirange));
		sa_spirange.sadb_spirange_exttype = SADB_EXT_SPIRANGE;
		sa_spirange.sadb_spirange_len = sizeof(sa_spirange) / 8;
		sa_spirange.sadb_spirange_min = 0x100;
		sa_spirange.sadb_spirange_max = 0xffffffff;
		sa_spirange.sadb_spirange_reserved = 0;
		break;
	case SADB_ADD:
	case SADB_UPDATE:
	case SADB_DELETE:
		memset(&sa, 0, sizeof(sa));
		sa.sadb_sa_exttype = SADB_EXT_SA;
		sa.sadb_sa_len = sizeof(sa) / 8;
		sa.sadb_sa_replay = 0;
		sa.sadb_sa_spi = htonl(spi);
		sa.sadb_sa_state = SADB_SASTATE_MATURE;
		break;
	}

	memset(&sa_src, 0, sizeof(sa_src));
	sa_src.sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
	sa_src.sadb_address_len =
		(sizeof(sa_src) + ROUNDUP(sockaddr_len(&su_src.sa))) / 8;

	memset(&sa_dst, 0, sizeof(sa_dst));
	sa_dst.sadb_address_exttype = SADB_EXT_ADDRESS_DST;
	sa_dst.sadb_address_len =
		(sizeof(sa_dst) + ROUNDUP(sockaddr_len(&su_dst.sa))) / 8;

	sa.sadb_sa_auth = aalg;
	sa.sadb_sa_encrypt = SADB_X_EALG_AES; /* XXX */

	switch (mtype) {
	case SADB_ADD:
	case SADB_UPDATE:
		memset(&sa_akey, 0, sizeof(sa_akey));
		sa_akey.sadb_key_exttype = SADB_EXT_KEY_AUTH;
		sa_akey.sadb_key_len = (sizeof(sa_akey) +
		    ((alen + 7) / 8) * 8) / 8;
		sa_akey.sadb_key_bits = 8 * alen;

		memset(&sa_ekey, 0, sizeof(sa_ekey));
		sa_ekey.sadb_key_exttype = SADB_EXT_KEY_ENCRYPT;
		sa_ekey.sadb_key_len = (sizeof(sa_ekey) +
		    ((elen + 7) / 8) * 8) / 8;
		sa_ekey.sadb_key_bits = 8 * elen;

		break;
	}

	iov_cnt = 0;

	/* msghdr */
	iov[iov_cnt].iov_base = &smsg;
	iov[iov_cnt].iov_len = sizeof(smsg);
	iov_cnt++;

	switch (mtype) {
	case SADB_ADD:
	case SADB_UPDATE:
	case SADB_DELETE:
		/* SA hdr */
		iov[iov_cnt].iov_base = &sa;
		iov[iov_cnt].iov_len = sizeof(sa);
		smsg.sadb_msg_len += sa.sadb_sa_len;
		iov_cnt++;
		break;
	case SADB_GETSPI:
		/* SPI range */
		iov[iov_cnt].iov_base = &sa_spirange;
		iov[iov_cnt].iov_len = sizeof(sa_spirange);
		smsg.sadb_msg_len += sa_spirange.sadb_spirange_len;
		iov_cnt++;
		break;
	}

	/* dest addr */
	iov[iov_cnt].iov_base = &sa_dst;
	iov[iov_cnt].iov_len = sizeof(sa_dst);
	iov_cnt++;
	iov[iov_cnt].iov_base = &su_dst;
	iov[iov_cnt].iov_len = ROUNDUP(sockaddr_len(&su_dst.sa));
	smsg.sadb_msg_len += sa_dst.sadb_address_len;
	iov_cnt++;

	/* src addr */
	iov[iov_cnt].iov_base = &sa_src;
	iov[iov_cnt].iov_len = sizeof(sa_src);
	iov_cnt++;
	iov[iov_cnt].iov_base = &su_src;
	iov[iov_cnt].iov_len = ROUNDUP(sockaddr_len(&su_src.sa));
	smsg.sadb_msg_len += sa_src.sadb_address_len;
	iov_cnt++;

	switch (mtype) {
	case SADB_ADD:
	case SADB_UPDATE:
		if (alen) {
			/* auth key */
			iov[iov_cnt].iov_base = &sa_akey;
			iov[iov_cnt].iov_len = sizeof(sa_akey);
			iov_cnt++;
			iov[iov_cnt].iov_base = akey;
			iov[iov_cnt].iov_len = ((alen + 7) / 8) * 8;
			smsg.sadb_msg_len += sa_akey.sadb_key_len;
			iov_cnt++;
		}
		if (elen) {
			/* encryption key */
			iov[iov_cnt].iov_base = &sa_ekey;
			iov[iov_cnt].iov_len = sizeof(sa_ekey);
			iov_cnt++;
			iov[iov_cnt].iov_base = ekey;
			iov[iov_cnt].iov_len = ((elen + 7) / 8) * 8;
			smsg.sadb_msg_len += sa_ekey.sadb_key_len;
			iov_cnt++;
		}
		break;
	}

	len = smsg.sadb_msg_len * 8;
	do {
		n = writev(sd, iov, iov_cnt);
	} while (n == -1 && (errno == EAGAIN || errno == EINTR));

	if (n == -1) {
		log_warn("writev (%d/%d)", iov_cnt, len);
		return (-1);
	}

	return (0);
}
Beispiel #2
0
int
ldp_create_socket(int af, enum socket_type type)
{
	int			 fd, domain, proto;
	union ldpd_addr		 addr;
	struct sockaddr_storage	 local_sa;
	int			 opt;

	/* create socket */
	switch (type) {
	case LDP_SOCKET_DISC:
	case LDP_SOCKET_EDISC:
		domain = SOCK_DGRAM;
		proto = IPPROTO_UDP;
		break;
	case LDP_SOCKET_SESSION:
		domain = SOCK_STREAM;
		proto = IPPROTO_TCP;
		break;
	default:
		fatalx("ldp_create_socket: unknown socket type");
	}
	fd = socket(af, domain | SOCK_NONBLOCK | SOCK_CLOEXEC, proto);
	if (fd == -1) {
		log_warn("%s: error creating socket", __func__);
		return (-1);
	}

	/* bind to a local address/port */
	switch (type) {
	case LDP_SOCKET_DISC:
		/* listen on all addresses */
		memset(&addr, 0, sizeof(addr));
		memcpy(&local_sa, addr2sa(af, &addr, LDP_PORT),
		    sizeof(local_sa));
		break;
	case LDP_SOCKET_EDISC:
	case LDP_SOCKET_SESSION:
		addr = (ldp_af_conf_get(ldpd_conf, af))->trans_addr;
		memcpy(&local_sa, addr2sa(af, &addr, LDP_PORT),
		    sizeof(local_sa));
		if (sock_set_bindany(fd, 1) == -1) {
			close(fd);
			return (-1);
		}
		break;
	}
	if (sock_set_reuse(fd, 1) == -1) {
		close(fd);
		return (-1);
	}
	if (bind(fd, (struct sockaddr *)&local_sa, local_sa.ss_len) == -1) {
		log_warn("%s: error binding socket", __func__);
		close(fd);
		return (-1);
	}

	/* set options */
	switch (af) {
	case AF_INET:
		if (sock_set_ipv4_tos(fd, IPTOS_PREC_INTERNETCONTROL) == -1) {
			close(fd);
			return (-1);
		}
		if (type == LDP_SOCKET_DISC) {
			if (sock_set_ipv4_mcast_ttl(fd,
			    IP_DEFAULT_MULTICAST_TTL) == -1) {
				close(fd);
				return (-1);
			}
			if (sock_set_ipv4_mcast_loop(fd) == -1) {
				close(fd);
				return (-1);
			}
		}
		if (type == LDP_SOCKET_DISC || type == LDP_SOCKET_EDISC) {
			if (sock_set_ipv4_recvif(fd, 1) == -1) {
				close(fd);
				return (-1);
			}
		}
		if (type == LDP_SOCKET_SESSION) {
			if (sock_set_ipv4_ucast_ttl(fd, 255) == -1) {
				close(fd);
				return (-1);
			}
		}
		break;
	case AF_INET6:
		if (sock_set_ipv6_dscp(fd, IPTOS_PREC_INTERNETCONTROL) == -1) {
			close(fd);
			return (-1);
		}
		if (type == LDP_SOCKET_DISC) {
			if (sock_set_ipv6_mcast_loop(fd) == -1) {
				close(fd);
				return (-1);
			}
			if (sock_set_ipv6_mcast_hops(fd, 255) == -1) {
				close(fd);
				return (-1);
			}
			if (!(ldpd_conf->ipv6.flags & F_LDPD_AF_NO_GTSM)) {
				if (sock_set_ipv6_minhopcount(fd, 255) == -1) {
					close(fd);
					return (-1);
				}
			}
		}
		if (type == LDP_SOCKET_DISC || type == LDP_SOCKET_EDISC) {
			if (sock_set_ipv6_pktinfo(fd, 1) == -1) {
				close(fd);
				return (-1);
			}
		}
		if (type == LDP_SOCKET_SESSION) {
			if (sock_set_ipv6_ucast_hops(fd, 255) == -1) {
				close(fd);
				return (-1);
			}
		}
		break;
	}
	switch (type) {
	case LDP_SOCKET_DISC:
	case LDP_SOCKET_EDISC:
		sock_set_recvbuf(fd);
		break;
	case LDP_SOCKET_SESSION:
		if (listen(fd, LDP_BACKLOG) == -1)
			log_warn("%s: error listening on socket", __func__);

		opt = 1;
		if (setsockopt(fd, IPPROTO_TCP, TCP_MD5SIG, &opt,
		    sizeof(opt)) == -1) {
			if (errno == ENOPROTOOPT) {	/* system w/o md5sig */
				log_warnx("md5sig not available, disabling");
				sysdep.no_md5sig = 1;
			} else {
				close(fd);
				return (-1);
			}
		}
		break;
	}

	return (fd);
}