Home | History | Annotate | Download | only in inet
      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, Version 1.0 only
      6  * (the "License").  You may not use this file except in compliance
      7  * with the License.
      8  *
      9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
     10  * or http://www.opensolaris.org/os/licensing.
     11  * See the License for the specific language governing permissions
     12  * and limitations under the License.
     13  *
     14  * When distributing Covered Code, include this CDDL HEADER in each
     15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     16  * If applicable, add the following below this CDDL HEADER, with the
     17  * fields enclosed by brackets "[]" replaced with your own identifying
     18  * information: Portions Copyright [yyyy] [name of copyright owner]
     19  *
     20  * CDDL HEADER END
     21  */
     22 /*
     23  * Copyright 2003 Sun Microsystems, Inc.
     24  * All rights reserved.  Use is subject to license terms.
     25  */
     26 
     27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     28 
     29 /*
     30  * This code is conformant to revision 7 of 2292bis.  Some of these functions
     31  * were provided (named inet6_rthdr_) in a very similar form in RFC 2292.
     32  * The RFC 2292 variants are not supported.
     33  */
     34 
     35 #include <stdio.h>
     36 #include <ctype.h>
     37 #include <string.h>
     38 #include <stdlib.h>
     39 #include <sys/types.h>
     40 #include <sys/socket.h>
     41 #include <netinet/in.h>
     42 #include <netinet/ip6.h>
     43 #include <unistd.h>
     44 #include <errno.h>
     45 
     46 #define	MAX_RTHDR0_SEGMENTS 127
     47 
     48 /*
     49  * Return amount of space needed to hold N segments for the specified
     50  * routing type. Does NOT include space for cmsghdr.
     51  */
     52 socklen_t
     53 inet6_rth_space(int type, int segments)
     54 {
     55 	if (type != IPV6_RTHDR_TYPE_0 || segments < 0 ||
     56 	    segments > MAX_RTHDR0_SEGMENTS)
     57 		return (0);
     58 
     59 	return (sizeof (struct ip6_rthdr0) +
     60 	    segments * sizeof (struct in6_addr));
     61 }
     62 
     63 /*
     64  * Initializes rthdr structure. Verifies the segments against the length of
     65  * the buffer.
     66  * Note that a routing header can only hold 127 segments since the length field
     67  * in the header is just a byte.
     68  */
     69 void *
     70 inet6_rth_init(void *bp, socklen_t bp_len, int type, int segments)
     71 {
     72 	struct ip6_rthdr0 *rthdr;
     73 
     74 	if (type != IPV6_RTHDR_TYPE_0 || segments < 0 ||
     75 	    segments > MAX_RTHDR0_SEGMENTS)
     76 		return (NULL);
     77 
     78 	if (bp_len < sizeof (struct ip6_rthdr0) +
     79 	    segments * sizeof (struct in6_addr))
     80 		return (NULL);
     81 
     82 	rthdr = (struct ip6_rthdr0 *)bp;
     83 	rthdr->ip6r0_nxt = 0;
     84 	rthdr->ip6r0_len = (segments * 2);
     85 	rthdr->ip6r0_type = type;
     86 	rthdr->ip6r0_segleft = 0;	/* Incremented by rthdr_add */
     87 	*(uint32_t *)&rthdr->ip6r0_reserved = 0;
     88 	return (bp);
     89 }
     90 
     91 /*
     92  * Add one more address to the routing header. Fails when there is no more
     93  * room.
     94  */
     95 int
     96 inet6_rth_add(void *bp, const struct in6_addr *addr)
     97 {
     98 	struct ip6_rthdr0 *rthdr;
     99 	struct in6_addr *addrs;
    100 
    101 	rthdr = (struct ip6_rthdr0 *)bp;
    102 	if ((rthdr->ip6r0_segleft + 1) * 2 > rthdr->ip6r0_len) {
    103 		/* Not room for one more */
    104 		return (-1);
    105 	}
    106 	addrs = (struct in6_addr *)((char *)rthdr + sizeof (*rthdr));
    107 	addrs[rthdr->ip6r0_segleft++] = *addr;
    108 	return (0);
    109 }
    110 
    111 /*
    112  * Reverse a source route. Both arguments can point to the same buffer.
    113  */
    114 int
    115 inet6_rth_reverse(const void *in, void *out)
    116 {
    117 	struct ip6_rthdr0 *rtin, *rtout;
    118 	int i, segments;
    119 	struct in6_addr tmp;
    120 	struct in6_addr *rtout_addrs;
    121 	struct in6_addr *rtin_addrs;
    122 
    123 	rtin = (struct ip6_rthdr0 *)in;
    124 	rtout = (struct ip6_rthdr0 *)out;
    125 
    126 	if (rtout->ip6r0_type != 0 || rtin->ip6r0_type != 0 ||
    127 	    rtout->ip6r0_len > MAX_RTHDR0_SEGMENTS * 2 ||
    128 	    rtin->ip6r0_len > MAX_RTHDR0_SEGMENTS * 2 ||
    129 	    rtout->ip6r0_len != rtin->ip6r0_len)
    130 		return (-1);
    131 
    132 	segments = rtin->ip6r0_len / 2;
    133 	rtout_addrs = (struct in6_addr *)((char *)rtout + sizeof (*rtout));
    134 	rtin_addrs = (struct in6_addr *)((char *)rtin + sizeof (*rtin));
    135 	for (i = 0; i < (segments + 1)/2; i++) {
    136 		tmp = rtin_addrs[i];
    137 		rtout_addrs[i] = rtin_addrs[segments - 1 - i];
    138 		rtout_addrs[segments - 1 - i] = tmp;
    139 	}
    140 	rtout->ip6r0_segleft = segments;
    141 	return (0);
    142 }
    143 
    144 /*
    145  * Return the number of segments in the routing header.
    146  */
    147 int
    148 inet6_rth_segments(const void *bp)
    149 {
    150 	struct ip6_rthdr0 *rthdr;
    151 
    152 	rthdr = (struct ip6_rthdr0 *)bp;
    153 	if (rthdr->ip6r0_type == 0) {
    154 		if (rthdr->ip6r0_len > MAX_RTHDR0_SEGMENTS * 2) {
    155 			return (-1);
    156 		} else {
    157 			return (rthdr->ip6r0_len / 2);
    158 		}
    159 	} else {
    160 		return (-1);
    161 	}
    162 }
    163 
    164 /*
    165  * Return a pointer to an element in the source route.
    166  * This uses the C convention for index [0, size-1].
    167  */
    168 struct in6_addr *
    169 inet6_rth_getaddr(const void *bp, int index)
    170 {
    171 	struct ip6_rthdr0 *rthdr;
    172 	struct in6_addr *rv;
    173 
    174 	rthdr = (struct ip6_rthdr0 *)bp;
    175 	if (index >= rthdr->ip6r0_len/2 || index < 0)
    176 		return (NULL);
    177 
    178 	rv = (struct in6_addr *)((char *)rthdr + sizeof (*rthdr));
    179 	return (&rv[index]);
    180 }
    181