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