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
dscpBind(int domain_id,int sockfd,int port)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
dscpSecure(int domain_id,int sockfd)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
dscpAuth(int domain_id,struct sockaddr * saddr,int len)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
dscpAddr(int domain_id,int which,struct sockaddr * saddr,int * lenp)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
dscpIdent(struct sockaddr * saddr,int len,int * domainp)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
get_ifname(char * ifname)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
convert_ipv6(struct sockaddr_in6 * addr6,uint32_t * addrp)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
convert_ipv4(struct sockaddr_in * addr,struct sockaddr_in6 * addr6,int * lenp)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