/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Copyright (c) 1982, 1986, 1988, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)raw_ip.c 8.2 (Berkeley) 1/4/94
*/
sockaddr_in6_init(&rip6src, &ip6->ip6_src, 0, 0, 0);
if (sa6_recoverscope(&rip6src) != 0) {
/* XXX: should be impossible. */
m_freem(m);
return IPPROTO_DONE;
}
TAILQ_FOREACH(inp, &raw6cbtable.inpt_queue, inp_queue) {
if (inp->inp_af != AF_INET6)
continue;
if (in6p_ip6(inp).ip6_nxt &&
in6p_ip6(inp).ip6_nxt != proto)
continue;
if (!IN6_IS_ADDR_UNSPECIFIED(&in6p_laddr(inp)) &&
!IN6_ARE_ADDR_EQUAL(&in6p_laddr(inp), &ip6->ip6_dst))
continue;
if (!IN6_IS_ADDR_UNSPECIFIED(&in6p_faddr(inp)) &&
!IN6_ARE_ADDR_EQUAL(&in6p_faddr(inp), &ip6->ip6_src))
continue;
if (in6p_cksum(inp) != -1) {
RIP6_STATINC(RIP6_STAT_ISUM);
/*
* Although in6_cksum() does not need the position of
* the checksum field for verification, enforce that it
* is located within the packet. Userland has given
* a checksum offset, a packet too short for that is
* invalid. Avoid overflow with user supplied offset.
*/
if (m->m_pkthdr.len < *offp + 2 ||
m->m_pkthdr.len - *offp - 2 < in6p_cksum(inp) ||
in6_cksum(m, proto, *offp,
m->m_pkthdr.len - *offp)) {
RIP6_STATINC(RIP6_STAT_BADSUM);
continue;
}
}
if (last == NULL) {
;
}
#ifdef IPSEC
else if (ipsec_used && ipsec_in_reject(m, last)) {
/* do not inject data into pcb */
}
#endif
else if ((n = m_copypacket(m, M_DONTWAIT)) != NULL) {
rip6_sbappendaddr(last, ip6, sin6tosa(&rip6src),
*offp, n);
}
last = inp;
}
#ifdef IPSEC
if (ipsec_used && last && ipsec_in_reject(m, last)) {
m_freem(m);
IP6_STATDEC(IP6_STAT_DELIVERED);
/* do not inject data into pcb */
} else
#endif
if (last != NULL) {
rip6_sbappendaddr(last, ip6, sin6tosa(&rip6src), *offp, m);
} else {
RIP6_STATINC(RIP6_STAT_NOSOCK);
if (m->m_flags & M_MCAST)
RIP6_STATINC(RIP6_STAT_NOSOCKMCAST);
if (proto == IPPROTO_NONE)
m_freem(m);
else {
int s;
struct ifnet *rcvif = m_get_rcvif(m, &s);
const int prvnxt = ip6_get_prevhdr(m, *offp);
in6_ifstat_inc(rcvif, ifs6_in_protounknown);
m_put_rcvif(rcvif, &s);
icmp6_error(m, ICMP6_PARAM_PROB,
ICMP6_PARAMPROB_NEXTHEADER,
prvnxt);
}
IP6_STATDEC(IP6_STAT_DELIVERED);
}
return IPPROTO_DONE;
}
if ((unsigned)cmd >= PRC_NCMDS)
return NULL;
if (PRC_IS_REDIRECT(cmd))
notify = in6pcb_rtchange, d = NULL;
else if (cmd == PRC_HOSTDEAD)
d = NULL;
else if (cmd == PRC_MSGSIZE)
; /* special code is present, see below */
else if (inet6ctlerrmap[cmd] == 0)
return NULL;
/* if the parameter is from icmp6, decode it. */
if (d != NULL) {
ip6cp = (struct ip6ctlparam *)d;
ip6 = ip6cp->ip6c_ip6;
cmdarg = ip6cp->ip6c_cmdarg;
sa6_src = ip6cp->ip6c_src;
nxt = ip6cp->ip6c_nxt;
} else {
ip6 = NULL;
cmdarg = NULL;
sa6_src = &sa6_any;
nxt = -1;
}
/*
* Check to see if we have a valid raw IPv6 socket
* corresponding to the address in the ICMPv6 message
* payload, and the protocol (ip6_nxt) meets the socket.
* XXX chase extension headers, or pass final nxt value
* from icmp6_notify_error()
*/
inp = NULL;
inp = in6pcb_lookup(&raw6cbtable, &sa6->sin6_addr, 0,
(const struct in6_addr *)&sa6_src->sin6_addr, 0, 0, 0);
#if 0
if (!inp) {
/*
* As the use of sendto(2) is fairly popular,
* we may want to allow non-connected pcb too.
* But it could be too weak against attacks...
* We should at least check if the local
* address (= s) is really ours.
*/
inp = in6pcb_lookup_bound(&raw6cbtable,
&sa6->sin6_addr, 0, 0);
}
#endif
if (inp && in6p_ip6(inp).ip6_nxt &&
in6p_ip6(inp).ip6_nxt == nxt)
valid++;
/*
* Depending on the value of "valid" and routing table
* size (mtudisc_{hi,lo}wat), we will:
* - recalculate the new MTU and create the
* corresponding routing entry, or
* - ignore the MTU change notification.
*/
icmp6_mtudisc_update((struct ip6ctlparam *)d, valid);
/*
* regardless of if we called icmp6_mtudisc_update(),
* we need to call in6pcb_notify(), to notify path MTU
* change to the userland (RFC3542), because some
* unconnected sockets may share the same destination
* and want to know the path MTU.
*/
}
/*
* Check and convert scope zone ID into internal form.
* XXX: we may still need to determine the zone later.
*/
if (!(so->so_state & SS_ISCONNECTED)) {
if (dstsock->sin6_scope_id == 0 && !ip6_use_defzone)
scope_ambiguous = 1;
if ((error = sa6_embedscope(dstsock, ip6_use_defzone)) != 0)
goto bad;
}
/*
* For an ICMPv6 packet, we should know its type and code
* to update statistics.
*/
if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) {
struct icmp6_hdr *icmp6;
if (m->m_len < sizeof(struct icmp6_hdr) &&
(m = m_pullup(m, sizeof(struct icmp6_hdr))) == NULL) {
error = ENOBUFS;
goto bad;
}
icmp6 = mtod(m, struct icmp6_hdr *);
type = icmp6->icmp6_type;
code = icmp6->icmp6_code;
} else {
type = 0;
code = 0;
}
if (oifp && scope_ambiguous) {
/*
* Application should provide a proper zone ID or the use of
* default zone IDs should be enabled. Unfortunately, some
* applications do not behave as it should, so we need a
* workaround. Even if an appropriate ID is not determined
* (when it's required), if we can determine the outgoing
* interface. determine the zone ID based on the interface.
*/
error = in6_setscope(&dstsock->sin6_addr, oifp, NULL);
if (error != 0)
goto bad;
}
ip6->ip6_dst = dstsock->sin6_addr;
/* fill in the rest of the IPv6 header fields */
ip6->ip6_flow = in6p_flowinfo(inp) & IPV6_FLOWINFO_MASK;
ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
ip6->ip6_vfc |= IPV6_VERSION;
/* ip6_plen will be filled in ip6_output, so not fill it here. */
ip6->ip6_nxt = in6p_ip6(inp).ip6_nxt;
ip6->ip6_hlim = in6pcb_selecthlim(inp, oifp);
if_put(oifp, &psref);
oifp = NULL;
if (so->so_proto->pr_protocol == IPPROTO_ICMPV6 ||
in6p_cksum(inp) != -1) {
const uint8_t nxt = ip6->ip6_nxt;
int off;
u_int16_t sum;
/* compute checksum */
if (so->so_proto->pr_protocol == IPPROTO_ICMPV6)
off = offsetof(struct icmp6_hdr, icmp6_cksum);
else
off = in6p_cksum(inp);
if (plen < 2 || plen - 2 < off) {
error = EINVAL;
goto bad;
}
off += sizeof(struct ip6_hdr);
sum = 0;
m = m_copyback_cow(m, off, sizeof(sum), (void *)&sum,
M_DONTWAIT);
if (m == NULL) {
error = ENOBUFS;
goto bad;
}
sum = in6_cksum(m, nxt, sizeof(*ip6), plen);
m = m_copyback_cow(m, off, sizeof(sum), (void *)&sum,
M_DONTWAIT);
if (m == NULL) {
error = ENOBUFS;
goto bad;
}
}
/*
* Raw IPv6 socket option processing.
*/
int
rip6_ctloutput(int op, struct socket *so, struct sockopt *sopt)
{
int error = 0;
if (sopt->sopt_level == SOL_SOCKET && sopt->sopt_name == SO_NOHEADER) {
int optval;
/* need to fiddle w/ opt(IPPROTO_IPV6, IPV6_CHECKSUM)? */
if (op == PRCO_GETOPT) {
optval = 1;
error = sockopt_set(sopt, &optval, sizeof(optval));
} else if (op == PRCO_SETOPT) {
error = sockopt_getint(sopt, &optval);
if (error)
goto out;
if (optval == 0)
error = EINVAL;
}
goto out;
} else if (sopt->sopt_level != IPPROTO_IPV6)
return ip6_ctloutput(op, so, sopt);
switch (sopt->sopt_name) {
case MRT6_INIT:
case MRT6_DONE:
case MRT6_ADD_MIF:
case MRT6_DEL_MIF:
case MRT6_ADD_MFC:
case MRT6_DEL_MFC:
case MRT6_PIM:
if (op == PRCO_SETOPT)
error = ip6_mrouter_set(so, sopt);
else if (op == PRCO_GETOPT)
error = ip6_mrouter_get(so, sopt);
else
error = EINVAL;
break;
case IPV6_CHECKSUM:
return ip6_raw_ctloutput(op, so, sopt);
default:
return ip6_ctloutput(op, so, sopt);
}
out:
return error;
}
if (IFNET_READER_EMPTY())
return EADDRNOTAVAIL;
if (addr->sin6_family != AF_INET6)
return EAFNOSUPPORT;
if (addr->sin6_len != sizeof(*addr))
return EINVAL;
/*
* Application should provide a proper zone ID or the use of
* default zone IDs should be enabled. Unfortunately, some
* applications do not behave as it should, so we need a
* workaround. Even if an appropriate ID is not determined,
* we'll see if we can determine the outgoing interface. If we
* can, determine the zone ID based on the interface below.
*/
if (addr->sin6_scope_id == 0 && !ip6_use_defzone)
scope_ambiguous = 1;
if ((error = sa6_embedscope(addr, ip6_use_defzone)) != 0)
return error;