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 */
54static int get_ifname(char *);
55static int convert_ipv6(struct sockaddr_in6 *, uint32_t *);
56static 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 */
65int
66dscpBind(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*/
135int
136dscpSecure(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*/
175int
176dscpAuth(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*/
243int
244dscpAddr(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 */
327int
328dscpIdent(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 */
358static int
359get_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 */
462static int
463convert_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 */
505static int
506convert_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