1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo/*
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo * CDDL HEADER START
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo *
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo * The contents of this file are subject to the terms of the
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo * Common Development and Distribution License (the "License").
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo * You may not use this file except in compliance with the License.
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo *
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo * or http://www.opensolaris.org/os/licensing.
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo * See the License for the specific language governing permissions
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo * and limitations under the License.
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo *
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo * When distributing Covered Code, include this CDDL HEADER in each
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo * If applicable, add the following below this CDDL HEADER, with the
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo * fields enclosed by brackets "[]" replaced with your own identifying
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo * information: Portions Copyright [yyyy] [name of copyright owner]
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo *
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo * CDDL HEADER END
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo */
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo
28b1e50e4eed7be353f9778497714aab53ef2a0dSriharsha Basavapatna/*
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo */
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo/*
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo * This code is conformant to revision 7 of 2292bis. Some of these functions
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo * were provided (named inet6_rthdr_) in a very similar form in RFC 2292.
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo * The RFC 2292 variants are not supported.
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo */
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo#include <stdio.h>
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo#include <ctype.h>
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo#include <string.h>
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo#include <stdlib.h>
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo#include <sys/types.h>
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo#include <sys/socket.h>
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo#include <netinet/in.h>
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo#include <netinet/ip6.h>
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo#include <unistd.h>
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo#include <errno.h>
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo#define MAX_RTHDR0_SEGMENTS 127
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo/*
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo * Return amount of space needed to hold N segments for the specified
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo * routing type. Does NOT include space for cmsghdr.
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo */
1ae0874509b6811fdde1dfd46f0d93fd09867a3fhepposocklen_t
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppoinet6_rth_space(int type, int segments)
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo{
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo if (type != IPV6_RTHDR_TYPE_0 || segments < 0 ||
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo segments > MAX_RTHDR0_SEGMENTS)
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo return (0);
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo return (sizeof (struct ip6_rthdr0) +
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo segments * sizeof (struct in6_addr));
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo}
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo/*
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo * Initializes rthdr structure. Verifies the segments against the length of
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo * the buffer.
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo * Note that a routing header can only hold 127 segments since the length field
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo * in the header is just a byte.
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo */
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppovoid *
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppoinet6_rth_init(void *bp, socklen_t bp_len, int type, int segments)
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo{
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo struct ip6_rthdr0 *rthdr;
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo if (type != IPV6_RTHDR_TYPE_0 || segments < 0 ||
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo segments > MAX_RTHDR0_SEGMENTS)
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo return (NULL);
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo if (bp_len < sizeof (struct ip6_rthdr0) +
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo segments * sizeof (struct in6_addr))
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo return (NULL);
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo rthdr = (struct ip6_rthdr0 *)bp;
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo rthdr->ip6r0_nxt = 0;
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo rthdr->ip6r0_len = (segments * 2);
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo rthdr->ip6r0_type = type;
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo rthdr->ip6r0_segleft = 0; /* Incremented by rthdr_add */
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo *(uint32_t *)&rthdr->ip6r0_reserved = 0;
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo return (bp);
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo}
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo/*
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo * Add one more address to the routing header. Fails when there is no more
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo * room.
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo */
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppoint
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppoinet6_rth_add(void *bp, const struct in6_addr *addr)
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo{
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo struct ip6_rthdr0 *rthdr;
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo struct in6_addr *addrs;
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo rthdr = (struct ip6_rthdr0 *)bp;
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo if ((rthdr->ip6r0_segleft + 1) * 2 > rthdr->ip6r0_len) {
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo /* Not room for one more */
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo return (-1);
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo }
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo /* LINTED alignment */
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo addrs = (struct in6_addr *)((char *)rthdr + sizeof (*rthdr));
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo addrs[rthdr->ip6r0_segleft++] = *addr;
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo return (0);
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo}
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo/*
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo * Reverse a source route. Both arguments can point to the same buffer.
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo */
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppoint
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppoinet6_rth_reverse(const void *in, void *out)
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo{
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo struct ip6_rthdr0 *rtin, *rtout;
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo int i, segments;
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo struct in6_addr tmp;
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo struct in6_addr *rtout_addrs;
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo struct in6_addr *rtin_addrs;
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo rtin = (struct ip6_rthdr0 *)in;
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo rtout = (struct ip6_rthdr0 *)out;
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo if (rtout->ip6r0_type != 0 || rtin->ip6r0_type != 0 ||
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo rtout->ip6r0_len > MAX_RTHDR0_SEGMENTS * 2 ||
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo rtin->ip6r0_len > MAX_RTHDR0_SEGMENTS * 2 ||
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo rtout->ip6r0_len != rtin->ip6r0_len)
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo return (-1);
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo segments = rtin->ip6r0_len / 2;
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo /* LINTED alignment */
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo rtout_addrs = (struct in6_addr *)((char *)rtout + sizeof (*rtout));
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo /* LINTED alignment */
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo rtin_addrs = (struct in6_addr *)((char *)rtin + sizeof (*rtin));
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo for (i = 0; i < (segments + 1)/2; i++) {
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo tmp = rtin_addrs[i];
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo rtout_addrs[i] = rtin_addrs[segments - 1 - i];
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo rtout_addrs[segments - 1 - i] = tmp;
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo }
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo rtout->ip6r0_segleft = segments;
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo return (0);
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo}
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo/*
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo * Return the number of segments in the routing header.
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo */
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppoint
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppoinet6_rth_segments(const void *bp)
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo{
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo struct ip6_rthdr0 *rthdr;
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo rthdr = (struct ip6_rthdr0 *)bp;
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo if (rthdr->ip6r0_type == 0) {
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo if (rthdr->ip6r0_len > MAX_RTHDR0_SEGMENTS * 2) {
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo return (-1);
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo } else {
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo return (rthdr->ip6r0_len / 2);
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo }
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo } else {
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo return (-1);
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo }
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo}
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo/*
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo * Return a pointer to an element in the source route.
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo * This uses the C convention for index [0, size-1].
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo */
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppostruct in6_addr *
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppoinet6_rth_getaddr(const void *bp, int index)
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo{
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo struct ip6_rthdr0 *rthdr;
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo struct in6_addr *rv;
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo rthdr = (struct ip6_rthdr0 *)bp;
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo if (index >= rthdr->ip6r0_len/2 || index < 0)
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo return (NULL);
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo /* LINTED alignment */
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo rv = (struct in6_addr *)((char *)rthdr + sizeof (*rthdr));
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo return (&rv[index]);
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo}
1ae0874509b6811fdde1dfd46f0d93fd09867a3fheppo