Home | History | Annotate | Download | only in libdscp
      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