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); }
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); }