1f4a6f97eSDan McDonald /*
2f4a6f97eSDan McDonald * This file and its contents are supplied under the terms of the
3f4a6f97eSDan McDonald * Common Development and Distribution License ("CDDL"), version 1.0.
4f4a6f97eSDan McDonald * You may only use this file in accordance with the terms of version
5f4a6f97eSDan McDonald * 1.0 of the CDDL.
6f4a6f97eSDan McDonald *
7f4a6f97eSDan McDonald * A full copy of the text of the CDDL should have accompanied this
8f4a6f97eSDan McDonald * source. A copy of the CDDL is also available via the Internet at
9f4a6f97eSDan McDonald * http://www.illumos.org/license/CDDL.
10f4a6f97eSDan McDonald */
11f4a6f97eSDan McDonald
12f4a6f97eSDan McDonald /*
13f4a6f97eSDan McDonald * Copyright 2017 Joyent, Inc.
14f4a6f97eSDan McDonald */
15f4a6f97eSDan McDonald
16f4a6f97eSDan McDonald #include <sys/socket.h>
17f4a6f97eSDan McDonald #include <net/pfkeyv2.h>
18f4a6f97eSDan McDonald #include <stdio.h>
19f4a6f97eSDan McDonald #include <errno.h>
20f4a6f97eSDan McDonald #include <err.h>
21f4a6f97eSDan McDonald #include <unistd.h>
22f4a6f97eSDan McDonald #include <stdlib.h>
23f4a6f97eSDan McDonald #include <string.h>
24*4c5582efSJason King #include <ipsec_util.h>
25f4a6f97eSDan McDonald
26f4a6f97eSDan McDonald #define COOKIE64 0xc0ffee4afee01deaULL
27f4a6f97eSDan McDonald #define COOKIE32 0x90125
28f4a6f97eSDan McDonald #define RESERVED 0xc0ffee
29f4a6f97eSDan McDonald
30cbc1abb4SDan McDonald #define EXIT_SETUP_FAIL -1
31cbc1abb4SDan McDonald #define EXIT_TEST_FAIL 1
32cbc1abb4SDan McDonald #define EXIT_SUCCESS 0
33cbc1abb4SDan McDonald
34f4a6f97eSDan McDonald /*
35f4a6f97eSDan McDonald * Exits app on failure.
36f4a6f97eSDan McDonald */
37f4a6f97eSDan McDonald static void
write_and_read(int s,sadb_msg_t * samsg,uint64_t * readbuf,int readlen,int expected,char * msgtypestr)38f4a6f97eSDan McDonald write_and_read(int s, sadb_msg_t *samsg, uint64_t *readbuf, int readlen,
39*4c5582efSJason King int expected, char *msgtypestr)
40f4a6f97eSDan McDonald {
41cbc1abb4SDan McDonald ssize_t rc;
42f4a6f97eSDan McDonald uint8_t msgtype = samsg->sadb_msg_type;
43f4a6f97eSDan McDonald pid_t pid = samsg->sadb_msg_pid;
44f4a6f97eSDan McDonald uint8_t seq = samsg->sadb_msg_seq;
45f4a6f97eSDan McDonald
46f4a6f97eSDan McDonald rc = write(s, samsg, SADB_64TO8(samsg->sadb_msg_len));
47f4a6f97eSDan McDonald if (rc == -1)
48cbc1abb4SDan McDonald err(EXIT_SETUP_FAIL, "%s write error", msgtypestr);
49f4a6f97eSDan McDonald
50f4a6f97eSDan McDonald /* Yes, parameter re-use, but we're done writing. */
51f4a6f97eSDan McDonald samsg = (sadb_msg_t *)readbuf;
52f4a6f97eSDan McDonald do {
53f4a6f97eSDan McDonald rc = read(s, readbuf, readlen);
54f4a6f97eSDan McDonald if (rc == -1)
55cbc1abb4SDan McDonald err(EXIT_SETUP_FAIL, "%s read reply error", msgtypestr);
56f4a6f97eSDan McDonald } while (samsg->sadb_msg_seq != seq || samsg->sadb_msg_pid != pid ||
57f4a6f97eSDan McDonald samsg->sadb_msg_type != msgtype);
58f4a6f97eSDan McDonald
59*4c5582efSJason King if (samsg->sadb_msg_errno != expected) {
60f4a6f97eSDan McDonald errno = samsg->sadb_msg_errno;
61*4c5582efSJason King err(EXIT_SETUP_FAIL, "%s reply has error (diag = %d, %s)",
62*4c5582efSJason King msgtypestr, samsg->sadb_x_msg_diagnostic,
63*4c5582efSJason King keysock_diag(samsg->sadb_x_msg_diagnostic));
64f4a6f97eSDan McDonald }
65f4a6f97eSDan McDonald }
66f4a6f97eSDan McDonald
67*4c5582efSJason King static void
usage(const char * progname)68*4c5582efSJason King usage(const char *progname)
69*4c5582efSJason King {
70*4c5582efSJason King (void) fprintf(stderr, "Usage: %s [-e expected_error] [-k kmc_value] "
71*4c5582efSJason King "[-p kmc_proto] <spi-value> [64]\n", progname);
72*4c5582efSJason King exit(EXIT_SETUP_FAIL);
73*4c5582efSJason King }
74*4c5582efSJason King
75f4a6f97eSDan McDonald int
main(int argc,char * const argv[])76*4c5582efSJason King main(int argc, char * const argv[])
77f4a6f97eSDan McDonald {
78f4a6f97eSDan McDonald uint32_t spi;
79f4a6f97eSDan McDonald sadb_ext_t *ext;
80f4a6f97eSDan McDonald sadb_sa_t *saext;
81f4a6f97eSDan McDonald sadb_msg_t *samsg;
82f4a6f97eSDan McDonald sadb_address_t *dstext, *srcext;
83f4a6f97eSDan McDonald sadb_x_kmc_t *kmcext;
84f4a6f97eSDan McDonald struct sockaddr_in *sin;
85f4a6f97eSDan McDonald uint64_t writebuf[20]; /* PF_KEY likes 64-bit alignment. */
86f4a6f97eSDan McDonald uint64_t readbuf[128];
87f4a6f97eSDan McDonald uint64_t *extptr, *endptr;
88*4c5582efSJason King const char *cookiestr = NULL;
89*4c5582efSJason King uint64_t cookie64 = COOKIE64;
90*4c5582efSJason King uint32_t cookie32 = COOKIE32;
91*4c5582efSJason King uint32_t reserved = RESERVED;
92*4c5582efSJason King uint32_t proto = 0;
93*4c5582efSJason King int experr = 0;
94f4a6f97eSDan McDonald pid_t pid = getpid();
95f4a6f97eSDan McDonald boolean_t do_64_test;
96f4a6f97eSDan McDonald int s;
97*4c5582efSJason King int c;
98f4a6f97eSDan McDonald
99*4c5582efSJason King while ((c = getopt(argc, argv, "e:k:p:")) != -1) {
100*4c5582efSJason King switch (c) {
101*4c5582efSJason King case 'e':
102*4c5582efSJason King errno = 0;
103*4c5582efSJason King experr = strtol(optarg, NULL, 0);
104*4c5582efSJason King if (errno != 0) {
105*4c5582efSJason King err(EXIT_SETUP_FAIL,
106*4c5582efSJason King "Expected error value '%s' is not a "
107*4c5582efSJason King "parsable number", optarg);
108*4c5582efSJason King }
109*4c5582efSJason King break;
110*4c5582efSJason King case 'k':
111*4c5582efSJason King cookiestr = optarg;
112*4c5582efSJason King break;
113*4c5582efSJason King case 'p':
114*4c5582efSJason King errno = 0;
115*4c5582efSJason King proto = strtoul(optarg, NULL, 0);
116*4c5582efSJason King if (errno != 0) {
117*4c5582efSJason King err(EXIT_SETUP_FAIL,
118*4c5582efSJason King "KMC Protocol value '%s' is not a parsable"
119*4c5582efSJason King " number", optarg);
120*4c5582efSJason King }
121*4c5582efSJason King break;
122*4c5582efSJason King case '?':
123*4c5582efSJason King (void) fprintf(stderr, "Invalid option -%c\n", optopt);
124*4c5582efSJason King usage(argv[0]);
125*4c5582efSJason King break;
126*4c5582efSJason King }
127f4a6f97eSDan McDonald }
128*4c5582efSJason King
129*4c5582efSJason King if (argc - optind != 1 && argc - optind != 2)
130*4c5582efSJason King usage(argv[0]);
131*4c5582efSJason King
132*4c5582efSJason King do_64_test = (argc - optind == 2);
133*4c5582efSJason King
134*4c5582efSJason King if (cookiestr != NULL) {
135*4c5582efSJason King errno = 0;
136*4c5582efSJason King
137*4c5582efSJason King if (do_64_test)
138*4c5582efSJason King cookie64 = strtoull(cookiestr, NULL, 0);
139*4c5582efSJason King else
140*4c5582efSJason King cookie32 = strtoul(cookiestr, NULL, 0);
141*4c5582efSJason King
142*4c5582efSJason King if (errno != 0) {
143*4c5582efSJason King err(EXIT_SETUP_FAIL,
144*4c5582efSJason King "KMC '%s' is not a parsable number",
145*4c5582efSJason King cookiestr);
146*4c5582efSJason King }
147*4c5582efSJason King }
148*4c5582efSJason King
149*4c5582efSJason King if (proto == 0)
150*4c5582efSJason King proto = do_64_test ? SADB_X_KMP_KINK : SADB_X_KMP_IKE;
151f4a6f97eSDan McDonald
152cbc1abb4SDan McDonald errno = 0; /* Clear for strtoul() call. */
153*4c5582efSJason King spi = strtoul(argv[optind], NULL, 0);
154f4a6f97eSDan McDonald if (spi == 0) {
155f4a6f97eSDan McDonald if (errno != 0) {
156cbc1abb4SDan McDonald err(EXIT_SETUP_FAIL,
157cbc1abb4SDan McDonald "Argument %s is not a parsable number:", argv[1]);
158f4a6f97eSDan McDonald } else {
159f4a6f97eSDan McDonald errno = EINVAL;
160cbc1abb4SDan McDonald err(EXIT_SETUP_FAIL, "Zero SPI not allowed:");
161f4a6f97eSDan McDonald }
162f4a6f97eSDan McDonald }
163f4a6f97eSDan McDonald
164f4a6f97eSDan McDonald s = socket(PF_KEY, SOCK_RAW, PF_KEY_V2);
165f4a6f97eSDan McDonald if (s == -1)
166cbc1abb4SDan McDonald err(EXIT_SETUP_FAIL, "socket(PF_KEY)");
167f4a6f97eSDan McDonald
168f4a6f97eSDan McDonald /* Base message. */
169f4a6f97eSDan McDonald samsg = (sadb_msg_t *)writebuf;
170f4a6f97eSDan McDonald samsg->sadb_msg_version = PF_KEY_V2;
171f4a6f97eSDan McDonald samsg->sadb_msg_type = SADB_UPDATE;
172f4a6f97eSDan McDonald samsg->sadb_msg_errno = 0;
173f4a6f97eSDan McDonald samsg->sadb_msg_satype = SADB_SATYPE_AH;
174f4a6f97eSDan McDonald samsg->sadb_msg_reserved = 0;
175f4a6f97eSDan McDonald samsg->sadb_msg_seq = 1;
176f4a6f97eSDan McDonald samsg->sadb_msg_pid = pid;
177f4a6f97eSDan McDonald samsg->sadb_msg_len = SADB_8TO64(sizeof (*samsg) + sizeof (*saext) +
178f4a6f97eSDan McDonald 2 * (sizeof (*dstext) + sizeof (*sin)) + sizeof (*kmcext));
179f4a6f97eSDan McDonald
180f4a6f97eSDan McDonald /* SA extension. Only used to set the SPI. */
181f4a6f97eSDan McDonald saext = (sadb_sa_t *)(samsg + 1);
182f4a6f97eSDan McDonald memset(saext, 0, sizeof (*saext));
183f4a6f97eSDan McDonald saext->sadb_sa_len = SADB_8TO64(sizeof (*saext));
184f4a6f97eSDan McDonald saext->sadb_sa_exttype = SADB_EXT_SA;
185f4a6f97eSDan McDonald saext->sadb_sa_spi = htonl(spi);
186f4a6f97eSDan McDonald saext->sadb_sa_state = SADB_SASTATE_MATURE;
187f4a6f97eSDan McDonald
188f4a6f97eSDan McDonald /* Destination IP, always 127.0.0.1 for this test. */
189f4a6f97eSDan McDonald dstext = (sadb_address_t *)(saext + 1);
190f4a6f97eSDan McDonald dstext->sadb_address_len = SADB_8TO64(sizeof (*dstext) + sizeof (*sin));
191f4a6f97eSDan McDonald dstext->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
192f4a6f97eSDan McDonald dstext->sadb_address_proto = 0;
193f4a6f97eSDan McDonald dstext->sadb_address_prefixlen = 0;
194f4a6f97eSDan McDonald dstext->sadb_address_reserved = 0;
195f4a6f97eSDan McDonald sin = (struct sockaddr_in *)(dstext + 1);
196f4a6f97eSDan McDonald sin->sin_family = AF_INET;
197f4a6f97eSDan McDonald sin->sin_port = 0;
198f4a6f97eSDan McDonald sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
199f4a6f97eSDan McDonald
200f4a6f97eSDan McDonald /* PF_KEY requires a source address, even if it's a wildcard. */
201f4a6f97eSDan McDonald srcext = (sadb_address_t *)(sin + 1);
202f4a6f97eSDan McDonald srcext->sadb_address_len = SADB_8TO64(sizeof (*srcext) + sizeof (*sin));
203f4a6f97eSDan McDonald srcext->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
204f4a6f97eSDan McDonald srcext->sadb_address_proto = 0;
205f4a6f97eSDan McDonald srcext->sadb_address_prefixlen = 0;
206f4a6f97eSDan McDonald srcext->sadb_address_reserved = 0;
207f4a6f97eSDan McDonald sin = (struct sockaddr_in *)(srcext + 1);
208f4a6f97eSDan McDonald sin->sin_family = AF_INET;
209f4a6f97eSDan McDonald sin->sin_port = 0;
210f4a6f97eSDan McDonald sin->sin_addr.s_addr = 0;
211f4a6f97eSDan McDonald
212f4a6f97eSDan McDonald /*
213f4a6f97eSDan McDonald * KM cookie. Depending, either make it IKEv1, 32-bit, AND store
214f4a6f97eSDan McDonald * garbage in the reserved field, or just put a big 64-bit cookie in.
215f4a6f97eSDan McDonald */
216f4a6f97eSDan McDonald kmcext = (sadb_x_kmc_t *)(sin + 1);
217f4a6f97eSDan McDonald kmcext->sadb_x_kmc_len = SADB_8TO64(sizeof (*kmcext));
218f4a6f97eSDan McDonald kmcext->sadb_x_kmc_exttype = SADB_X_EXT_KM_COOKIE;
219f4a6f97eSDan McDonald if (do_64_test) {
220f4a6f97eSDan McDonald /* 64-bit cookie test. KINK is non-zero, and non-IKEv1. */
221*4c5582efSJason King kmcext->sadb_x_kmc_proto = proto;
222*4c5582efSJason King kmcext->sadb_x_kmc_cookie64 = cookie64;
223f4a6f97eSDan McDonald } else {
224f4a6f97eSDan McDonald /* IKEv1 32-bit cookie test. */
225*4c5582efSJason King kmcext->sadb_x_kmc_proto = proto;
226*4c5582efSJason King kmcext->sadb_x_kmc_cookie = cookie32;
227*4c5582efSJason King kmcext->sadb_x_kmc_reserved = reserved;
228f4a6f97eSDan McDonald }
229f4a6f97eSDan McDonald
230*4c5582efSJason King write_and_read(s, samsg, readbuf, sizeof (readbuf), experr,
231*4c5582efSJason King "SADB_UPDATE");
232*4c5582efSJason King
233*4c5582efSJason King /* If we expected to fail, we shouldn't try to verify anything */
234*4c5582efSJason King if (experr != 0)
235*4c5582efSJason King exit(EXIT_SUCCESS);
236f4a6f97eSDan McDonald
237f4a6f97eSDan McDonald /*
238f4a6f97eSDan McDonald * Okay, it worked! Now let's find the KMC reported back from the
239f4a6f97eSDan McDonald * kernel.
240f4a6f97eSDan McDonald */
241f4a6f97eSDan McDonald samsg->sadb_msg_type = SADB_GET;
242f4a6f97eSDan McDonald samsg->sadb_msg_len -= SADB_8TO64(sizeof (*kmcext));
243f4a6f97eSDan McDonald
244f4a6f97eSDan McDonald /* Everything else in writebuf is good to go. */
245*4c5582efSJason King write_and_read(s, samsg, readbuf, sizeof (readbuf), 0, "SADB_GET");
246f4a6f97eSDan McDonald
247f4a6f97eSDan McDonald /* Actually find the KMC extension. (expand for loop for readability) */
248f4a6f97eSDan McDonald samsg = (sadb_msg_t *)readbuf;
249f4a6f97eSDan McDonald extptr = (uint64_t *)(samsg + 1);
250f4a6f97eSDan McDonald endptr = extptr + samsg->sadb_msg_len - SADB_8TO64(sizeof (*samsg));
251f4a6f97eSDan McDonald ext = (sadb_ext_t *)extptr;
252f4a6f97eSDan McDonald
253f4a6f97eSDan McDonald while ((extptr < endptr) &&
254f4a6f97eSDan McDonald (ext->sadb_ext_type != SADB_X_EXT_KM_COOKIE)) {
255f4a6f97eSDan McDonald extptr += ext->sadb_ext_len;
256f4a6f97eSDan McDonald ext = (sadb_ext_t *)extptr;
257f4a6f97eSDan McDonald }
258f4a6f97eSDan McDonald
259f4a6f97eSDan McDonald if (extptr == endptr) {
260f4a6f97eSDan McDonald (void) fprintf(stderr, "Can't find KMC extension in reply.\n");
261cbc1abb4SDan McDonald exit(EXIT_SETUP_FAIL);
262f4a6f97eSDan McDonald }
263f4a6f97eSDan McDonald kmcext = (sadb_x_kmc_t *)extptr;
264f4a6f97eSDan McDonald
265f4a6f97eSDan McDonald if (do_64_test) {
266*4c5582efSJason King if (kmcext->sadb_x_kmc_proto != proto ||
267*4c5582efSJason King kmcext->sadb_x_kmc_cookie64 != cookie64) {
268f4a6f97eSDan McDonald (void) fprintf(stderr, "Unexpected 64-bit results: "
269*4c5582efSJason King "KMC received was %" PRIu32
270*4c5582efSJason King ", expecting %" PRIu32 ",\n",
271*4c5582efSJason King kmcext->sadb_x_kmc_proto, proto);
272f4a6f97eSDan McDonald (void) fprintf(stderr, "64-bit cookie recevied was "
273f4a6f97eSDan McDonald "0x%"PRIx64", expecting 0x%"PRIx64"\n",
274*4c5582efSJason King kmcext->sadb_x_kmc_cookie64, cookie64);
275cbc1abb4SDan McDonald exit(EXIT_TEST_FAIL);
276f4a6f97eSDan McDonald }
277f4a6f97eSDan McDonald } else {
278*4c5582efSJason King if (kmcext->sadb_x_kmc_proto != proto ||
279*4c5582efSJason King kmcext->sadb_x_kmc_cookie != cookie32 ||
280f4a6f97eSDan McDonald kmcext->sadb_x_kmc_reserved != 0) {
281f4a6f97eSDan McDonald (void) fprintf(stderr, "Unexpected IKE/32-bit results:"
282*4c5582efSJason King " KMC received was %" PRIu32
283*4c5582efSJason King ", expecting %" PRIu32 ",\n",
284*4c5582efSJason King kmcext->sadb_x_kmc_proto, proto);
285f4a6f97eSDan McDonald (void) fprintf(stderr, "32-bit cookie recevied was "
286f4a6f97eSDan McDonald "0x%"PRIx32", expecting 0x%"PRIx32"\n",
287*4c5582efSJason King kmcext->sadb_x_kmc_cookie64, cookie32);
288f4a6f97eSDan McDonald (void) fprintf(stderr, "32-bit reserved recevied was "
289f4a6f97eSDan McDonald "0x%"PRIx32", expecting 0\n",
290f4a6f97eSDan McDonald kmcext->sadb_x_kmc_cookie64);
291cbc1abb4SDan McDonald exit(EXIT_TEST_FAIL);
292f4a6f97eSDan McDonald }
293f4a6f97eSDan McDonald }
294f4a6f97eSDan McDonald
295cbc1abb4SDan McDonald exit(EXIT_SUCCESS);
296f4a6f97eSDan McDonald }
297