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