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