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