xref: /illumos-gate/usr/src/lib/libdscp/libdscp.c (revision 012e6ce759c490003aed29439cc47d3d73a99ad3)
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