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.
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.
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]
22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #pragma ident "%Z%%M% %I% %E% SMI"
35 #include <sys/types.h>
37 #include <sys/ioctl.h>
38 #include <sys/socket.h>
39 #include <sys/sockio.h>
41 #include <net/pfkeyv2.h>
42 #include <netinet/in.h>
43 #include <arpa/inet.h>
47 * Define the file containing the configured DSCP interface name
49 #define DSCP_CONFIGFILE "/var/run/dscp.ifname"
52 * Forward declarations
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 *);
62 * Properly bind a socket to the local DSCP address.
63 * Optionally bind it to a specific port.
66 dscpBind(int domain_id
, int sockfd
, int port
)
71 struct sockaddr_in addr
;
72 struct sockaddr_in6 addr6
;
75 if ((sockfd
< 0) || (port
>= IPPORT_RESERVED
)) {
76 return (DSCP_ERROR_INVALID
);
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
);
83 if (error
!= DSCP_OK
) {
88 * If the caller specified a port, then update the socket address
89 * to also specify the same port.
92 addr
.sin_port
= htons(port
);
98 * EINVAL means it is already bound.
99 * EAFNOSUPPORT means try again using IPv6.
101 if (bind(sockfd
, (struct sockaddr
*)&addr
, len
) < 0) {
103 if (errno
== EINVAL
) {
104 return (DSCP_ERROR_ALREADY
);
107 if (errno
!= EAFNOSUPPORT
) {
111 if (convert_ipv4(&addr
, &addr6
, &len6
) < 0) {
115 if (bind(sockfd
, (struct sockaddr
*)&addr6
, len6
) < 0) {
116 if (errno
== EINVAL
) {
117 return (DSCP_ERROR_ALREADY
);
129 * Enable DSCP security mechanisms on a socket.
131 * DSCP uses the IPSec AH (Authentication Headers) protocol with
132 * the SHA-1 algorithm.
136 dscpSecure(int domain_id
, int sockfd
)
140 /* Check arguments */
142 return (DSCP_ERROR_INVALID
);
146 * Construct a socket option argument that specifies the protocols
147 * and algorithms required for DSCP's use of IPSec.
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
;
156 * Set the socket option that enables IPSec usage upon the socket,
157 * using the socket option argument constructed above.
159 if (setsockopt(sockfd
, IPPROTO_IP
, IP_SEC_OPT
, (const char *)&opt
,
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.
176 dscpAuth(int domain_id
, struct sockaddr
*saddr
, int len
)
179 struct sockaddr daddr
;
180 struct sockaddr_in
*sin
;
181 struct sockaddr_in6
*sin6
;
185 /* Check arguments */
187 return (DSCP_ERROR_INVALID
);
191 * Get the remote IP address associated with the SP.
193 if (dscpAddr(0, DSCP_ADDR_REMOTE
, &daddr
, &dlen
) != DSCP_OK
) {
194 return (DSCP_ERROR_DB
);
198 * Convert the request's address to a 32-bit integer.
200 * This may require a conversion if the caller is
201 * using an IPv6 socket.
203 switch (saddr
->sa_family
) {
205 /* LINTED E_BAD_PTR_CAST_ALIGN */
206 sin
= (struct sockaddr_in
*)saddr
;
207 reqaddr
= ntohl(*((uint32_t *)&(sin
->sin_addr
)));
210 /* LINTED E_BAD_PTR_CAST_ALIGN */
211 sin6
= (struct sockaddr_in6
*)saddr
;
212 if (convert_ipv6(sin6
, &reqaddr
) < 0) {
221 * Convert the SP's address to a 32-bit integer.
223 /* LINTED E_BAD_PTR_CAST_ALIGN */
224 sin
= (struct sockaddr_in
*)&daddr
;
225 spaddr
= ntohl(*((uint32_t *)&(sin
->sin_addr
)));
228 * Compare the addresses. Reject if they don't match.
230 if (reqaddr
!= spaddr
) {
231 return (DSCP_ERROR_REJECT
);
240 * Get the addresses associated with a specific DSCP link.
244 dscpAddr(int domain_id
, int which
, struct sockaddr
*saddr
, int *lenp
)
249 char ifname
[LIFNAMSIZ
];
252 /* Check arguments */
253 if (((saddr
== NULL
) || (lenp
== NULL
)) ||
254 ((which
!= DSCP_ADDR_LOCAL
) && (which
!= DSCP_ADDR_REMOTE
))) {
255 return (DSCP_ERROR_INVALID
);
259 * Get the DSCP interface name.
261 if (get_ifname(ifname
) != 0) {
262 return (DSCP_ERROR_DB
);
268 if ((sockfd
= socket(PF_INET
, SOCK_DGRAM
, 0)) < 0) {
269 return (DSCP_ERROR_DB
);
273 * Get the interface flags.
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
);
281 flags
= lifr
.lifr_flags
;
284 * The interface must be a PPP link using IPv4.
286 if (((flags
& IFF_IPV4
) == 0) ||
287 ((flags
& IFF_POINTOPOINT
) == 0)) {
288 (void) close(sockfd
);
289 return (DSCP_ERROR_DB
);
293 * Get the local or remote address, depending upon 'which'.
295 (void) strncpy(lifr
.lifr_name
, ifname
, sizeof (lifr
.lifr_name
));
296 if (which
== DSCP_ADDR_LOCAL
) {
297 error
= ioctl(sockfd
, SIOCGLIFADDR
, (char *)&lifr
);
299 error
= ioctl(sockfd
, SIOCGLIFDSTADDR
, (char *)&lifr
);
302 (void) close(sockfd
);
303 return (DSCP_ERROR_DB
);
307 * Copy the sockaddr value back to the caller.
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
);
313 (void) close(sockfd
);
320 * Determine the domain of origin associated with a sockaddr.
321 * (Map a sockaddr to a domain ID.)
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.
328 dscpIdent(struct sockaddr
*saddr
, int len
, int *domainp
)
332 /* Check arguments */
333 if ((saddr
== NULL
) || (domainp
== NULL
)) {
334 return (DSCP_ERROR_INVALID
);
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
) {
346 *domainp
= DSCP_IDENT_SP
;
353 * Retrieve the interface name used by DSCP.
354 * It should be available from a file in /var/run.
356 * Returns: 0 upon success, -1 upon failure.
359 get_ifname(char *ifname
)
371 * Initialize the interface name.
373 (void) memset(ifname
, 0, LIFNAMSIZ
);
376 * Test for a a valid configuration file.
378 if ((stat(DSCP_CONFIGFILE
, &stbuf
) < 0) ||
379 (S_ISREG(stbuf
.st_mode
) == 0) ||
380 (stbuf
.st_size
> LIFNAMSIZ
)) {
385 * Open the configuration file and read its contents
388 if ((fd
= open(DSCP_CONFIGFILE
, O_RDONLY
)) < 0) {
393 size
= stbuf
.st_size
;
395 i
= read(fd
, &ifname
[count
], size
- count
);
401 } while (count
< size
);
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.
412 /* Detect the beginning of the interface name */
413 for (begin
= -1, i
= 0; i
< size
; i
++) {
414 if (isalnum(ifname
[i
]) != 0) {
420 /* Fail if no such beginning was found */
425 /* Detect the end of the interface name */
426 for (end
= size
- 1, i
= begin
; i
< size
; i
++) {
427 if (isalnum(ifname
[i
]) == 0) {
433 /* Compute the length of the name */
436 /* Remove leading whitespace */
438 (void) memmove(ifname
, &ifname
[begin
], len
);
441 /* Clear out any remaining garbage */
443 (void) memset(&ifname
[len
], 0, size
- len
);
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.
456 * The IPv4 address is additionally converted from network
457 * byte order to host byte order.
459 * Returns: 0 upon success, with 'addrp' updated.
460 * -1 upon failure, with 'addrp' undefined.
463 convert_ipv6(struct sockaddr_in6
*addr6
, uint32_t *addrp
)
467 char ipv6str
[INET6_ADDRSTRLEN
];
470 * Convert the IPv6 address into a string.
472 if (inet_ntop(AF_INET6
, &addr6
->sin6_addr
, ipv6str
,
473 sizeof (ipv6str
)) == NULL
) {
478 * Use the IPv6 string to construct an IPv4 string.
480 if ((ipv4str
= strrchr(ipv6str
, ':')) != NULL
) {
487 * Convert the IPv4 string into a 32-bit integer.
489 if (inet_pton(AF_INET
, ipv4str
, &addr
) <= 0) {
493 *addrp
= ntohl(addr
);
500 * Convert an IPv4 socket address into an equivalent IPv6 address.
502 * Returns: 0 upon success, with 'addr6' and 'lenp' updated.
503 * -1 upon failure, with 'addr6' and 'lenp' undefined.
506 convert_ipv4(struct sockaddr_in
*addr
, struct sockaddr_in6
*addr6
, int *lenp
)
510 char ipv4str
[INET_ADDRSTRLEN
];
511 char ipv6str
[INET6_ADDRSTRLEN
];
514 * Convert the IPv4 socket address into a string.
516 ipv4addr
= *((uint32_t *)&(addr
->sin_addr
));
517 if (inet_ntop(AF_INET
, &ipv4addr
, ipv4str
, sizeof (ipv4str
)) == NULL
) {
522 * Use the IPv4 string to construct an IPv6 string.
524 len
= snprintf(ipv6str
, INET6_ADDRSTRLEN
, "::ffff:%s", ipv4str
);
525 if (len
>= INET6_ADDRSTRLEN
) {
530 * Convert the IPv6 string to an IPv6 socket address.
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) {
539 *lenp
= sizeof (struct sockaddr_in6
);