1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <stdlib.h> 27 #include <stdio.h> 28 #include <unistd.h> 29 #include <string.h> 30 #include <ctype.h> 31 #include <errno.h> 32 #include <fcntl.h> 33 #include <sys/types.h> 34 #include <sys/stat.h> 35 #include <sys/ioctl.h> 36 #include <sys/socket.h> 37 #include <sys/sockio.h> 38 #include <net/if.h> 39 #include <net/pfkeyv2.h> 40 #include <netinet/in.h> 41 #include <arpa/inet.h> 42 #include <libdscp.h> 43 44 /* 45 * Define the file containing the configured DSCP interface name 46 */ 47 #define DSCP_CONFIGFILE "/var/run/dscp.ifname" 48 49 /* 50 * Forward declarations 51 */ 52 static int get_ifname(char *); 53 static int convert_ipv6(struct sockaddr_in6 *, uint32_t *); 54 static int convert_ipv4(struct sockaddr_in *, 55 struct sockaddr_in6 *, int *); 56 57 /* 58 * dscpBind() 59 * 60 * Properly bind a socket to the local DSCP address. 61 * Optionally bind it to a specific port. 62 */ 63 int 64 dscpBind(int domain_id, int sockfd, int port) 65 { 66 int len; 67 int len6; 68 int error; 69 struct sockaddr_in addr; 70 struct sockaddr_in6 addr6; 71 72 /* Check arguments */ 73 if ((sockfd < 0) || (port >= IPPORT_RESERVED)) { 74 return (DSCP_ERROR_INVALID); 75 } 76 77 /* Get the local DSCP address used to communicate with the SP */ 78 error = dscpAddr(domain_id, DSCP_ADDR_LOCAL, 79 (struct sockaddr *)&addr, &len); 80 81 if (error != DSCP_OK) { 82 return (error); 83 } 84 85 /* 86 * If the caller specified a port, then update the socket address 87 * to also specify the same port. 88 */ 89 if (port != 0) { 90 addr.sin_port = htons(port); 91 } 92 93 /* 94 * Bind the socket. 95 * 96 * EINVAL means it is already bound. 97 * EAFNOSUPPORT means try again using IPv6. 98 */ 99 if (bind(sockfd, (struct sockaddr *)&addr, len) < 0) { 100 101 if (errno == EINVAL) { 102 return (DSCP_ERROR_ALREADY); 103 } 104 105 if (errno != EAFNOSUPPORT) { 106 return (DSCP_ERROR); 107 } 108 109 if (convert_ipv4(&addr, &addr6, &len6) < 0) { 110 return (DSCP_ERROR); 111 } 112 113 if (bind(sockfd, (struct sockaddr *)&addr6, len6) < 0) { 114 if (errno == EINVAL) { 115 return (DSCP_ERROR_ALREADY); 116 } 117 return (DSCP_ERROR); 118 } 119 } 120 121 return (DSCP_OK); 122 } 123 124 /* 125 * dscpSecure() 126 * 127 * Enable DSCP security mechanisms on a socket. 128 * 129 * DSCP uses the IPSec AH (Authentication Headers) protocol with 130 * the SHA-1 algorithm. 131 */ 132 /*ARGSUSED*/ 133 int 134 dscpSecure(int domain_id, int sockfd) 135 { 136 ipsec_req_t opt; 137 138 /* Check arguments */ 139 if (sockfd < 0) { 140 return (DSCP_ERROR_INVALID); 141 } 142 143 /* 144 * Construct a socket option argument that specifies the protocols 145 * and algorithms required for DSCP's use of IPSec. 146 */ 147 (void) memset(&opt, 0, sizeof (opt)); 148 opt.ipsr_ah_req = IPSEC_PREF_REQUIRED; 149 opt.ipsr_esp_req = IPSEC_PREF_NEVER; 150 opt.ipsr_self_encap_req = IPSEC_PREF_NEVER; 151 opt.ipsr_auth_alg = SADB_AALG_MD5HMAC; 152 153 /* 154 * Set the socket option that enables IPSec usage upon the socket, 155 * using the socket option argument constructed above. 156 */ 157 if (setsockopt(sockfd, IPPROTO_IP, IP_SEC_OPT, (const char *)&opt, 158 sizeof (opt)) < 0) { 159 return (DSCP_ERROR); 160 } 161 162 return (DSCP_OK); 163 } 164 165 /* 166 * dscpAuth() 167 * 168 * Test whether a connection should be accepted or refused. 169 * The address of the connection request is compared against 170 * the remote address of the specified DSCP link. 171 */ 172 /*ARGSUSED*/ 173 int 174 dscpAuth(int domain_id, struct sockaddr *saddr, int len) 175 { 176 int dlen; 177 struct sockaddr daddr; 178 struct sockaddr_in *sin; 179 struct sockaddr_in6 *sin6; 180 uint32_t spaddr; 181 uint32_t reqaddr; 182 183 /* Check arguments */ 184 if (saddr == NULL) { 185 return (DSCP_ERROR_INVALID); 186 } 187 188 /* 189 * Get the remote IP address associated with the SP. 190 */ 191 if (dscpAddr(0, DSCP_ADDR_REMOTE, &daddr, &dlen) != DSCP_OK) { 192 return (DSCP_ERROR_DB); 193 } 194 195 /* 196 * Convert the request's address to a 32-bit integer. 197 * 198 * This may require a conversion if the caller is 199 * using an IPv6 socket. 200 */ 201 switch (saddr->sa_family) { 202 case AF_INET: 203 /* LINTED E_BAD_PTR_CAST_ALIGN */ 204 sin = (struct sockaddr_in *)saddr; 205 reqaddr = ntohl(*((uint32_t *)&(sin->sin_addr))); 206 break; 207 case AF_INET6: 208 /* LINTED E_BAD_PTR_CAST_ALIGN */ 209 sin6 = (struct sockaddr_in6 *)saddr; 210 if (convert_ipv6(sin6, &reqaddr) < 0) { 211 return (DSCP_ERROR); 212 } 213 break; 214 default: 215 return (DSCP_ERROR); 216 } 217 218 /* 219 * Convert the SP's address to a 32-bit integer. 220 */ 221 /* LINTED E_BAD_PTR_CAST_ALIGN */ 222 sin = (struct sockaddr_in *)&daddr; 223 spaddr = ntohl(*((uint32_t *)&(sin->sin_addr))); 224 225 /* 226 * Compare the addresses. Reject if they don't match. 227 */ 228 if (reqaddr != spaddr) { 229 return (DSCP_ERROR_REJECT); 230 } 231 232 return (DSCP_OK); 233 } 234 235 /* 236 * dscpAddr() 237 * 238 * Get the addresses associated with a specific DSCP link. 239 */ 240 /*ARGSUSED*/ 241 int 242 dscpAddr(int domain_id, int which, struct sockaddr *saddr, int *lenp) 243 { 244 int error; 245 int sockfd; 246 uint64_t flags; 247 char ifname[LIFNAMSIZ]; 248 struct lifreq lifr; 249 250 /* Check arguments */ 251 if (((saddr == NULL) || (lenp == NULL)) || 252 ((which != DSCP_ADDR_LOCAL) && (which != DSCP_ADDR_REMOTE))) { 253 return (DSCP_ERROR_INVALID); 254 } 255 256 /* 257 * Get the DSCP interface name. 258 */ 259 if (get_ifname(ifname) != 0) { 260 return (DSCP_ERROR_DB); 261 } 262 263 /* 264 * Open a socket. 265 */ 266 if ((sockfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { 267 return (DSCP_ERROR_DB); 268 } 269 270 /* 271 * Get the interface flags. 272 */ 273 (void) memset(&lifr, 0, sizeof (lifr)); 274 (void) strncpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name)); 275 if (ioctl(sockfd, SIOCGLIFFLAGS, (char *)&lifr) < 0) { 276 (void) close(sockfd); 277 return (DSCP_ERROR_DB); 278 } 279 flags = lifr.lifr_flags; 280 281 /* 282 * The interface must be a PPP link using IPv4. 283 */ 284 if (((flags & IFF_IPV4) == 0) || 285 ((flags & IFF_POINTOPOINT) == 0)) { 286 (void) close(sockfd); 287 return (DSCP_ERROR_DB); 288 } 289 290 /* 291 * Get the local or remote address, depending upon 'which'. 292 */ 293 (void) strncpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name)); 294 if (which == DSCP_ADDR_LOCAL) { 295 error = ioctl(sockfd, SIOCGLIFADDR, (char *)&lifr); 296 } else { 297 error = ioctl(sockfd, SIOCGLIFDSTADDR, (char *)&lifr); 298 } 299 if (error < 0) { 300 (void) close(sockfd); 301 return (DSCP_ERROR_DB); 302 } 303 304 /* 305 * Copy the sockaddr value back to the caller. 306 */ 307 (void) memset(saddr, 0, sizeof (struct sockaddr)); 308 (void) memcpy(saddr, &lifr.lifr_addr, sizeof (struct sockaddr_in)); 309 *lenp = sizeof (struct sockaddr_in); 310 311 (void) close(sockfd); 312 return (DSCP_OK); 313 } 314 315 /* 316 * dscpIdent() 317 * 318 * Determine the domain of origin associated with a sockaddr. 319 * (Map a sockaddr to a domain ID.) 320 * 321 * In the Solaris version, the remote socket address should always 322 * be the SP. A call to dscpAuth() is used to confirm this, and 323 * then DSCP_IDENT_SP is returned as a special domain ID. 324 */ 325 int 326 dscpIdent(struct sockaddr *saddr, int len, int *domainp) 327 { 328 int error; 329 330 /* Check arguments */ 331 if ((saddr == NULL) || (domainp == NULL)) { 332 return (DSCP_ERROR_INVALID); 333 } 334 335 /* Confirm that the address is the SP */ 336 error = dscpAuth(0, saddr, len); 337 if (error != DSCP_OK) { 338 if (error == DSCP_ERROR_REJECT) { 339 return (DSCP_ERROR); 340 } 341 return (error); 342 } 343 344 *domainp = DSCP_IDENT_SP; 345 return (DSCP_OK); 346 } 347 348 /* 349 * get_ifname() 350 * 351 * Retrieve the interface name used by DSCP. 352 * It should be available from a file in /var/run. 353 * 354 * Returns: 0 upon success, -1 upon failure. 355 */ 356 static int 357 get_ifname(char *ifname) 358 { 359 int i; 360 int fd; 361 int len; 362 int size; 363 int count; 364 int end; 365 int begin; 366 struct stat stbuf; 367 368 /* 369 * Initialize the interface name. 370 */ 371 (void) memset(ifname, 0, LIFNAMSIZ); 372 373 /* 374 * Test for a a valid configuration file. 375 */ 376 if ((stat(DSCP_CONFIGFILE, &stbuf) < 0) || 377 (S_ISREG(stbuf.st_mode) == 0) || 378 (stbuf.st_size > LIFNAMSIZ)) { 379 return (-1); 380 } 381 382 /* 383 * Open the configuration file and read its contents 384 */ 385 386 if ((fd = open(DSCP_CONFIGFILE, O_RDONLY)) < 0) { 387 return (-1); 388 } 389 390 count = 0; 391 size = stbuf.st_size; 392 do { 393 i = read(fd, &ifname[count], size - count); 394 if (i <= 0) { 395 (void) close(fd); 396 return (-1); 397 } 398 count += i; 399 } while (count < size); 400 401 (void) close(fd); 402 403 /* 404 * Analyze the interface name that was just read, 405 * and clean it up as necessary. The result should 406 * be a simple NULL terminated string such as "sppp0" 407 * with no extra whitespace or other characters. 408 */ 409 410 /* Detect the beginning of the interface name */ 411 for (begin = -1, i = 0; i < size; i++) { 412 if (isalnum(ifname[i]) != 0) { 413 begin = i; 414 break; 415 } 416 } 417 418 /* Fail if no such beginning was found */ 419 if (begin < 0) { 420 return (-1); 421 } 422 423 /* Detect the end of the interface name */ 424 for (end = size - 1, i = begin; i < size; i++) { 425 if (isalnum(ifname[i]) == 0) { 426 end = i; 427 break; 428 } 429 } 430 431 /* Compute the length of the name */ 432 len = end - begin; 433 434 /* Remove leading whitespace */ 435 if (begin > 0) { 436 (void) memmove(ifname, &ifname[begin], len); 437 } 438 439 /* Clear out any remaining garbage */ 440 if (len < size) { 441 (void) memset(&ifname[len], 0, size - len); 442 } 443 444 return (0); 445 } 446 447 /* 448 * convert_ipv6() 449 * 450 * Converts an IPv6 socket address into an equivalent IPv4 451 * address. The conversion is to a 32-bit integer because 452 * that is sufficient for how libdscp uses IPv4 addresses. 453 * 454 * The IPv4 address is additionally converted from network 455 * byte order to host byte order. 456 * 457 * Returns: 0 upon success, with 'addrp' updated. 458 * -1 upon failure, with 'addrp' undefined. 459 */ 460 static int 461 convert_ipv6(struct sockaddr_in6 *addr6, uint32_t *addrp) 462 { 463 uint32_t addr; 464 char *ipv4str; 465 char ipv6str[INET6_ADDRSTRLEN]; 466 467 /* 468 * Convert the IPv6 address into a string. 469 */ 470 if (inet_ntop(AF_INET6, &addr6->sin6_addr, ipv6str, 471 sizeof (ipv6str)) == NULL) { 472 return (-1); 473 } 474 475 /* 476 * Use the IPv6 string to construct an IPv4 string. 477 */ 478 if ((ipv4str = strrchr(ipv6str, ':')) != NULL) { 479 ipv4str++; 480 } else { 481 return (-1); 482 } 483 484 /* 485 * Convert the IPv4 string into a 32-bit integer. 486 */ 487 if (inet_pton(AF_INET, ipv4str, &addr) <= 0) { 488 return (-1); 489 } 490 491 *addrp = ntohl(addr); 492 return (0); 493 } 494 495 /* 496 * convert_ipv4() 497 * 498 * Convert an IPv4 socket address into an equivalent IPv6 address. 499 * 500 * Returns: 0 upon success, with 'addr6' and 'lenp' updated. 501 * -1 upon failure, with 'addr6' and 'lenp' undefined. 502 */ 503 static int 504 convert_ipv4(struct sockaddr_in *addr, struct sockaddr_in6 *addr6, int *lenp) 505 { 506 int len; 507 uint32_t ipv4addr; 508 char ipv4str[INET_ADDRSTRLEN]; 509 char ipv6str[INET6_ADDRSTRLEN]; 510 511 /* 512 * Convert the IPv4 socket address into a string. 513 */ 514 ipv4addr = *((uint32_t *)&(addr->sin_addr)); 515 if (inet_ntop(AF_INET, &ipv4addr, ipv4str, sizeof (ipv4str)) == NULL) { 516 return (-1); 517 } 518 519 /* 520 * Use the IPv4 string to construct an IPv6 string. 521 */ 522 len = snprintf(ipv6str, INET6_ADDRSTRLEN, "::ffff:%s", ipv4str); 523 if (len >= INET6_ADDRSTRLEN) { 524 return (-1); 525 } 526 527 /* 528 * Convert the IPv6 string to an IPv6 socket address. 529 */ 530 (void) memset(addr6, 0, sizeof (*addr6)); 531 addr6->sin6_family = AF_INET6; 532 addr6->sin6_port = addr->sin_port; 533 if (inet_pton(AF_INET6, ipv6str, &addr6->sin6_addr) <= 0) { 534 return (-1); 535 } 536 537 *lenp = sizeof (struct sockaddr_in6); 538 539 return (0); 540 } 541