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