#include <sys/socket.h>
#include <net/pfkeyv2.h>
#include <stdio.h>
#include <errno.h>
#include <err.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <ipsec_util.h>
#define COOKIE64 0xc0ffee4afee01deaULL
#define COOKIE32 0x90125
#define RESERVED 0xc0ffee
#define EXIT_SETUP_FAIL -1
#define EXIT_TEST_FAIL 1
#define EXIT_SUCCESS 0
static void
write_and_read(int s, sadb_msg_t *samsg, uint64_t *readbuf, int readlen,
int expected, char *msgtypestr)
{
ssize_t rc;
uint8_t msgtype = samsg->sadb_msg_type;
pid_t pid = samsg->sadb_msg_pid;
uint8_t seq = samsg->sadb_msg_seq;
rc = write(s, samsg, SADB_64TO8(samsg->sadb_msg_len));
if (rc == -1)
err(EXIT_SETUP_FAIL, "%s write error", msgtypestr);
samsg = (sadb_msg_t *)readbuf;
do {
rc = read(s, readbuf, readlen);
if (rc == -1)
err(EXIT_SETUP_FAIL, "%s read reply error", msgtypestr);
} while (samsg->sadb_msg_seq != seq || samsg->sadb_msg_pid != pid ||
samsg->sadb_msg_type != msgtype);
if (samsg->sadb_msg_errno != expected) {
errno = samsg->sadb_msg_errno;
err(EXIT_SETUP_FAIL, "%s reply has error (diag = %d, %s)",
msgtypestr, samsg->sadb_x_msg_diagnostic,
keysock_diag(samsg->sadb_x_msg_diagnostic));
}
}
static void
usage(const char *progname)
{
(void) fprintf(stderr, "Usage: %s [-e expected_error] [-k kmc_value] "
"[-p kmc_proto] <spi-value> [64]\n", progname);
exit(EXIT_SETUP_FAIL);
}
int
main(int argc, char * const argv[])
{
uint32_t spi;
sadb_ext_t *ext;
sadb_sa_t *saext;
sadb_msg_t *samsg;
sadb_address_t *dstext, *srcext;
sadb_x_kmc_t *kmcext;
struct sockaddr_in *sin;
uint64_t writebuf[20];
uint64_t readbuf[128];
uint64_t *extptr, *endptr;
const char *cookiestr = NULL;
uint64_t cookie64 = COOKIE64;
uint32_t cookie32 = COOKIE32;
uint32_t reserved = RESERVED;
uint32_t proto = 0;
int experr = 0;
pid_t pid = getpid();
boolean_t do_64_test;
int s;
int c;
while ((c = getopt(argc, argv, "e:k:p:")) != -1) {
switch (c) {
case 'e':
errno = 0;
experr = strtol(optarg, NULL, 0);
if (errno != 0) {
err(EXIT_SETUP_FAIL,
"Expected error value '%s' is not a "
"parsable number", optarg);
}
break;
case 'k':
cookiestr = optarg;
break;
case 'p':
errno = 0;
proto = strtoul(optarg, NULL, 0);
if (errno != 0) {
err(EXIT_SETUP_FAIL,
"KMC Protocol value '%s' is not a parsable"
" number", optarg);
}
break;
case '?':
(void) fprintf(stderr, "Invalid option -%c\n", optopt);
usage(argv[0]);
break;
}
}
if (argc - optind != 1 && argc - optind != 2)
usage(argv[0]);
do_64_test = (argc - optind == 2);
if (cookiestr != NULL) {
errno = 0;
if (do_64_test)
cookie64 = strtoull(cookiestr, NULL, 0);
else
cookie32 = strtoul(cookiestr, NULL, 0);
if (errno != 0) {
err(EXIT_SETUP_FAIL,
"KMC '%s' is not a parsable number",
cookiestr);
}
}
if (proto == 0)
proto = do_64_test ? SADB_X_KMP_KINK : SADB_X_KMP_IKE;
errno = 0;
spi = strtoul(argv[optind], NULL, 0);
if (spi == 0) {
if (errno != 0) {
err(EXIT_SETUP_FAIL,
"Argument %s is not a parsable number:", argv[1]);
} else {
errno = EINVAL;
err(EXIT_SETUP_FAIL, "Zero SPI not allowed:");
}
}
s = socket(PF_KEY, SOCK_RAW, PF_KEY_V2);
if (s == -1)
err(EXIT_SETUP_FAIL, "socket(PF_KEY)");
samsg = (sadb_msg_t *)writebuf;
samsg->sadb_msg_version = PF_KEY_V2;
samsg->sadb_msg_type = SADB_UPDATE;
samsg->sadb_msg_errno = 0;
samsg->sadb_msg_satype = SADB_SATYPE_AH;
samsg->sadb_msg_reserved = 0;
samsg->sadb_msg_seq = 1;
samsg->sadb_msg_pid = pid;
samsg->sadb_msg_len = SADB_8TO64(sizeof (*samsg) + sizeof (*saext) +
2 * (sizeof (*dstext) + sizeof (*sin)) + sizeof (*kmcext));
saext = (sadb_sa_t *)(samsg + 1);
memset(saext, 0, sizeof (*saext));
saext->sadb_sa_len = SADB_8TO64(sizeof (*saext));
saext->sadb_sa_exttype = SADB_EXT_SA;
saext->sadb_sa_spi = htonl(spi);
saext->sadb_sa_state = SADB_SASTATE_MATURE;
dstext = (sadb_address_t *)(saext + 1);
dstext->sadb_address_len = SADB_8TO64(sizeof (*dstext) + sizeof (*sin));
dstext->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
dstext->sadb_address_proto = 0;
dstext->sadb_address_prefixlen = 0;
dstext->sadb_address_reserved = 0;
sin = (struct sockaddr_in *)(dstext + 1);
sin->sin_family = AF_INET;
sin->sin_port = 0;
sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
srcext = (sadb_address_t *)(sin + 1);
srcext->sadb_address_len = SADB_8TO64(sizeof (*srcext) + sizeof (*sin));
srcext->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
srcext->sadb_address_proto = 0;
srcext->sadb_address_prefixlen = 0;
srcext->sadb_address_reserved = 0;
sin = (struct sockaddr_in *)(srcext + 1);
sin->sin_family = AF_INET;
sin->sin_port = 0;
sin->sin_addr.s_addr = 0;
kmcext = (sadb_x_kmc_t *)(sin + 1);
kmcext->sadb_x_kmc_len = SADB_8TO64(sizeof (*kmcext));
kmcext->sadb_x_kmc_exttype = SADB_X_EXT_KM_COOKIE;
if (do_64_test) {
kmcext->sadb_x_kmc_proto = proto;
kmcext->sadb_x_kmc_cookie64 = cookie64;
} else {
kmcext->sadb_x_kmc_proto = proto;
kmcext->sadb_x_kmc_cookie = cookie32;
kmcext->sadb_x_kmc_reserved = reserved;
}
write_and_read(s, samsg, readbuf, sizeof (readbuf), experr,
"SADB_UPDATE");
if (experr != 0)
exit(EXIT_SUCCESS);
samsg->sadb_msg_type = SADB_GET;
samsg->sadb_msg_len -= SADB_8TO64(sizeof (*kmcext));
write_and_read(s, samsg, readbuf, sizeof (readbuf), 0, "SADB_GET");
samsg = (sadb_msg_t *)readbuf;
extptr = (uint64_t *)(samsg + 1);
endptr = extptr + samsg->sadb_msg_len - SADB_8TO64(sizeof (*samsg));
ext = (sadb_ext_t *)extptr;
while ((extptr < endptr) &&
(ext->sadb_ext_type != SADB_X_EXT_KM_COOKIE)) {
extptr += ext->sadb_ext_len;
ext = (sadb_ext_t *)extptr;
}
if (extptr == endptr) {
(void) fprintf(stderr, "Can't find KMC extension in reply.\n");
exit(EXIT_SETUP_FAIL);
}
kmcext = (sadb_x_kmc_t *)extptr;
if (do_64_test) {
if (kmcext->sadb_x_kmc_proto != proto ||
kmcext->sadb_x_kmc_cookie64 != cookie64) {
(void) fprintf(stderr, "Unexpected 64-bit results: "
"KMC received was %" PRIu32
", expecting %" PRIu32 ",\n",
kmcext->sadb_x_kmc_proto, proto);
(void) fprintf(stderr, "64-bit cookie recevied was "
"0x%"PRIx64", expecting 0x%"PRIx64"\n",
kmcext->sadb_x_kmc_cookie64, cookie64);
exit(EXIT_TEST_FAIL);
}
} else {
if (kmcext->sadb_x_kmc_proto != proto ||
kmcext->sadb_x_kmc_cookie != cookie32 ||
kmcext->sadb_x_kmc_reserved != 0) {
(void) fprintf(stderr, "Unexpected IKE/32-bit results:"
" KMC received was %" PRIu32
", expecting %" PRIu32 ",\n",
kmcext->sadb_x_kmc_proto, proto);
(void) fprintf(stderr, "32-bit cookie recevied was "
"0x%"PRIx32", expecting 0x%"PRIx32"\n",
kmcext->sadb_x_kmc_cookie64, cookie32);
(void) fprintf(stderr, "32-bit reserved recevied was "
"0x%"PRIx32", expecting 0\n",
kmcext->sadb_x_kmc_cookie64);
exit(EXIT_TEST_FAIL);
}
}
exit(EXIT_SUCCESS);
}