#include <sys/param.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/queue.h>
#include <string.h>
#include <net/if.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet6/in6_var.h>
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#include <netinet/icmp6.h>
#if IP6_RTHDR0_ALLOWED
static int ip6_rthdr0(struct mbuf *, struct ip6_hdr *,
struct ip6_rthdr0 *);
#endif
int
route6_input(struct mbuf **mp, int *offp)
{
struct ip6_hdr *ip6;
struct mbuf *m = *mp;
struct ip6_rthdr *rh;
int off = *offp, rhlen;
struct ip6aux *ip6a;
ip6a = ip6_findaux(m);
if (ip6a) {
if (ip6a->ip6a_flags & IP6A_SWAP) {
ip6stat.ip6s_badoptions++;
m_freem(m);
return IPPROTO_DONE;
}
}
#ifndef PULLDOWN_TEST
IP6_EXTHDR_CHECK(m, off, sizeof(*rh), return IPPROTO_DONE);
ip6 = mtod(m, struct ip6_hdr *);
rh = (struct ip6_rthdr *)((caddr_t)ip6 + off);
#else
ip6 = mtod(m, struct ip6_hdr *);
IP6_EXTHDR_GET(rh, struct ip6_rthdr *, m, off, sizeof(*rh));
if (rh == NULL) {
ip6stat.ip6s_tooshort++;
return IPPROTO_DONE;
}
#endif
switch (rh->ip6r_type) {
#if IP6_RTHDR0_ALLOWED
case IPV6_RTHDR_TYPE_0:
rhlen = (rh->ip6r_len + 1) << 3;
#ifndef PULLDOWN_TEST
IP6_EXTHDR_CHECK(m, off, rhlen, return IPPROTO_DONE);
#else
IP6_EXTHDR_GET(rh, struct ip6_rthdr *, m, off, rhlen);
if (rh == NULL) {
ip6stat.ip6s_tooshort++;
return IPPROTO_DONE;
}
#endif
if (ip6_rthdr0(m, ip6, (struct ip6_rthdr0 *)rh))
return(IPPROTO_DONE);
break;
#endif
default:
if (rh->ip6r_segleft == 0) {
rhlen = (rh->ip6r_len + 1) << 3;
break;
}
ip6stat.ip6s_badoptions++;
icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER,
(caddr_t)&rh->ip6r_type - (caddr_t)ip6);
return(IPPROTO_DONE);
}
*offp += rhlen;
return(rh->ip6r_nxt);
}
#if IP6_RTHDR0_ALLOWED
static int
ip6_rthdr0(m, ip6, rh0)
struct mbuf *m;
struct ip6_hdr *ip6;
struct ip6_rthdr0 *rh0;
{
int addrs, index;
struct in6_addr *nextaddr, tmpaddr;
struct route_in6 ip6forward_rt;
if (rh0->ip6r0_segleft == 0)
return(0);
if (rh0->ip6r0_len % 2
#if COMPAT_RFC1883
|| rh0->ip6r0_len > 46
#endif
) {
ip6stat.ip6s_badoptions++;
icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER,
(caddr_t)&rh0->ip6r0_len - (caddr_t)ip6);
return(-1);
}
if ((addrs = rh0->ip6r0_len / 2) < rh0->ip6r0_segleft) {
ip6stat.ip6s_badoptions++;
icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER,
(caddr_t)&rh0->ip6r0_segleft - (caddr_t)ip6);
return(-1);
}
index = addrs - rh0->ip6r0_segleft;
rh0->ip6r0_segleft--;
nextaddr = rh0->ip6r0_addr + index;
if (IN6_IS_ADDR_MULTICAST(nextaddr) ||
IN6_IS_ADDR_UNSPECIFIED(nextaddr) ||
IN6_IS_ADDR_V4MAPPED(nextaddr) ||
IN6_IS_ADDR_V4COMPAT(nextaddr)) {
ip6stat.ip6s_badoptions++;
m_freem(m);
return(-1);
}
if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) ||
IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_dst) ||
IN6_IS_ADDR_V4MAPPED(&ip6->ip6_dst) ||
IN6_IS_ADDR_V4COMPAT(&ip6->ip6_dst)) {
ip6stat.ip6s_badoptions++;
m_freem(m);
return(-1);
}
tmpaddr = *nextaddr;
*nextaddr = ip6->ip6_dst;
if (IN6_IS_ADDR_LINKLOCAL(nextaddr))
nextaddr->s6_addr16[1] = 0;
ip6->ip6_dst = tmpaddr;
if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst))
ip6->ip6_dst.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index);
bzero(&ip6forward_rt, sizeof (ip6forward_rt));
#if COMPAT_RFC1883
if (rh0->ip6r0_slmap[index / 8] & (1 << (7 - (index % 8))))
ip6_forward(m, &ip6forward_rt, IPV6_SRCRT_NEIGHBOR, 0);
else
ip6_forward(m, &ip6forward_rt, IPV6_SRCRT_NOTNEIGHBOR, 0);
#else
ip6_forward(m, &ip6forward_rt, 1, 0);
#endif
if (ip6forward_rt.ro_rt != NULL) {
rtfree(ip6forward_rt.ro_rt);
ip6forward_rt.ro_rt = NULL;
}
return(-1);
}
#endif