#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip6.h>
#include <unistd.h>
#include <errno.h>
#define MAX_RTHDR0_SEGMENTS 127
socklen_t
inet6_rth_space(int type, int segments)
{
if (type != IPV6_RTHDR_TYPE_0 || segments < 0 ||
segments > MAX_RTHDR0_SEGMENTS)
return (0);
return (sizeof (struct ip6_rthdr0) +
segments * sizeof (struct in6_addr));
}
void *
inet6_rth_init(void *bp, socklen_t bp_len, int type, int segments)
{
struct ip6_rthdr0 *rthdr;
if (type != IPV6_RTHDR_TYPE_0 || segments < 0 ||
segments > MAX_RTHDR0_SEGMENTS)
return (NULL);
if (bp_len < sizeof (struct ip6_rthdr0) +
segments * sizeof (struct in6_addr))
return (NULL);
rthdr = (struct ip6_rthdr0 *)bp;
rthdr->ip6r0_nxt = 0;
rthdr->ip6r0_len = (segments * 2);
rthdr->ip6r0_type = type;
rthdr->ip6r0_segleft = 0;
*(uint32_t *)&rthdr->ip6r0_reserved = 0;
return (bp);
}
int
inet6_rth_add(void *bp, const struct in6_addr *addr)
{
struct ip6_rthdr0 *rthdr;
struct in6_addr *addrs;
rthdr = (struct ip6_rthdr0 *)bp;
if ((rthdr->ip6r0_segleft + 1) * 2 > rthdr->ip6r0_len) {
return (-1);
}
addrs = (struct in6_addr *)((char *)rthdr + sizeof (*rthdr));
addrs[rthdr->ip6r0_segleft++] = *addr;
return (0);
}
int
inet6_rth_reverse(const void *in, void *out)
{
struct ip6_rthdr0 *rtin, *rtout;
int i, segments;
struct in6_addr tmp;
struct in6_addr *rtout_addrs;
struct in6_addr *rtin_addrs;
rtin = (struct ip6_rthdr0 *)in;
rtout = (struct ip6_rthdr0 *)out;
if (rtout->ip6r0_type != 0 || rtin->ip6r0_type != 0 ||
rtout->ip6r0_len > MAX_RTHDR0_SEGMENTS * 2 ||
rtin->ip6r0_len > MAX_RTHDR0_SEGMENTS * 2 ||
rtout->ip6r0_len != rtin->ip6r0_len)
return (-1);
segments = rtin->ip6r0_len / 2;
rtout_addrs = (struct in6_addr *)((char *)rtout + sizeof (*rtout));
rtin_addrs = (struct in6_addr *)((char *)rtin + sizeof (*rtin));
for (i = 0; i < (segments + 1)/2; i++) {
tmp = rtin_addrs[i];
rtout_addrs[i] = rtin_addrs[segments - 1 - i];
rtout_addrs[segments - 1 - i] = tmp;
}
rtout->ip6r0_segleft = segments;
return (0);
}
int
inet6_rth_segments(const void *bp)
{
struct ip6_rthdr0 *rthdr;
rthdr = (struct ip6_rthdr0 *)bp;
if (rthdr->ip6r0_type == 0) {
if (rthdr->ip6r0_len > MAX_RTHDR0_SEGMENTS * 2) {
return (-1);
} else {
return (rthdr->ip6r0_len / 2);
}
} else {
return (-1);
}
}
struct in6_addr *
inet6_rth_getaddr(const void *bp, int index)
{
struct ip6_rthdr0 *rthdr;
struct in6_addr *rv;
rthdr = (struct ip6_rthdr0 *)bp;
if (index >= rthdr->ip6r0_len/2 || index < 0)
return (NULL);
rv = (struct in6_addr *)((char *)rthdr + sizeof (*rthdr));
return (&rv[index]);
}