1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-24 11:29:10 +00:00

Convert netinet6/ to use new routing API.

* Remove &ifpp from ip6_output() in favor of ri->ri_nh_info
* Provide different wrappers to in6_selectsrc:
  Currently it is used by 2 differenct type of customers:
  - socket-based one, which all are unsure about provided
   address scope and
  - in-kernel ones (ND code mostly), which don't have
    any sockets, options, crededentials, etc.
  So, we provide two different wrappers to in6_selectsrc()
  returning select source.
* Make different versions of selectroute():
  Currenly selectroute() is used in two scenarios:
  - SAS, via in6_selecsrc() -> in6_selectif() -> selectroute()
  - output, via in6_output -> wrapper -> selectroute()
  Provide different versions for each customer:
  - fib6_lookup_nh_basic()-based in6_selectif() which is
    capable of returning interface only, without MTU/NHOP/L2
    calculations
  - full-blown fib6_selectroute() with cached route/multipath/
    MTU/L2
* Stop using routing table for link-local address lookups
* Add in6_ifawithifp_lla() to make for-us check faster for link-local
* Add in6_splitscope / in6_setllascope for faster embed/deembed scopes
This commit is contained in:
Alexander V. Chernikov 2014-11-04 15:39:56 +00:00
parent 30514718e7
commit 257480b8ab
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/projects/routing/; revision=274089
34 changed files with 972 additions and 691 deletions

View File

@ -750,7 +750,7 @@ ipf_fastroute(m0, mpp, fin, fdp)
* currently "to <if>" and "to <if>:ip#" are not supported * currently "to <if>" and "to <if>:ip#" are not supported
* for IPv6 * for IPv6
*/ */
return ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL); return ip6_output(m, NULL, NULL, 0, NULL, NULL);
} }
#endif #endif

View File

@ -1140,7 +1140,7 @@ get_l2te_for_nexthop(struct port_info *pi, struct ifnet *ifp,
} }
/* TODO: Multipath */ /* TODO: Multipath */
if (fib6_lookup_nh_ext(inc->inc_fibnum, inc->inc6_faddr, if (fib6_lookup_nh_ext(inc->inc_fibnum, &inc->inc6_faddr,
0, 0, 0, &nhu.u.nh6) != 0) 0, 0, 0, &nhu.u.nh6) != 0)
return (NULL); return (NULL);
((struct sockaddr_in6 *)dst)->sin6_addr = nhu.u.nh6.nh_addr; ((struct sockaddr_in6 *)dst)->sin6_addr = nhu.u.nh6.nh_addr;

View File

@ -66,6 +66,10 @@
#include <netinet/ip_mroute.h> #include <netinet/ip_mroute.h>
#include <netinet/ip6.h> #include <netinet/ip6.h>
#include <netinet6/in6_var.h> #include <netinet6/in6_var.h>
#include <netinet6/nd6.h>
#include <netinet6/scope6_var.h>
#include <net/if_llatbl.h>
#include <net/if_types.h> #include <net/if_types.h>
#include <netinet/if_ether.h> #include <netinet/if_ether.h>
@ -117,11 +121,20 @@ static void fib4_rte_to_nh_extended(struct rtentry *rte, struct in_addr dst,
static void fib4_rte_to_nh_basic(struct rtentry *rte, struct in_addr dst, static void fib4_rte_to_nh_basic(struct rtentry *rte, struct in_addr dst,
struct nhop4_basic *pnh4); struct nhop4_basic *pnh4);
#endif #endif
#ifdef INET #ifdef INET6
static void fib6_rte_to_nh_extended(struct rtentry *rte, struct in6_addr dst, static void fib6_rte_to_nh_extended(struct rtentry *rte, struct in6_addr *dst,
struct nhop6_extended *pnh6); struct nhop6_extended *pnh6);
static void fib6_rte_to_nh_basic(struct rtentry *rte, struct in6_addr dst, static void fib6_rte_to_nh_basic(struct rtentry *rte, struct in6_addr *dst,
struct nhop6_basic *pnh6); struct nhop6_basic *pnh6);
static int fib6_storelladdr(struct ifnet *ifp, struct in6_addr *dst,
int mm_flags, u_char *desten);
static uint16_t fib6_get_ifa(struct rtentry *rte);
static int fib6_lla_to_nh_basic(struct in6_addr *dst, uint32_t scopeid,
struct nhop6_basic *pnh6);
static int fib6_lla_to_nh_extended(struct in6_addr *dst, uint32_t scopeid,
struct nhop6_extended *pnh6);
static int fib6_lla_to_nh(struct in6_addr *dst, uint32_t scopeid,
struct nhop_prepend *nh, struct ifnet **lifp);
#endif #endif
MALLOC_DEFINE(M_RTFIB, "rtfib", "routing fwd"); MALLOC_DEFINE(M_RTFIB, "rtfib", "routing fwd");
@ -292,8 +305,11 @@ fib4_lookup_prepend(uint32_t fibnum, struct in_addr dst, struct mbuf *m,
* Currently all we have is rte ifp. * Currently all we have is rte ifp.
* Simply use it. * Simply use it.
*/ */
lifp = rte->rt_ifp; /* Save interface address ifp */
lifp = rte->rt_ifa->ifa_ifp;
nh->aifp_idx = lifp->if_index;
/* Save both logical and transmit interface indexes */ /* Save both logical and transmit interface indexes */
lifp = rte->rt_ifp;
nh->lifp_idx = lifp->if_index; nh->lifp_idx = lifp->if_index;
nh->i.ifp_idx = nh->lifp_idx; nh->i.ifp_idx = nh->lifp_idx;
@ -407,6 +423,7 @@ fib4_rte_to_nh_basic(struct rtentry *rte, struct in_addr dst,
gw = (struct sockaddr_in *)rt_key(rte); gw = (struct sockaddr_in *)rt_key(rte);
if (gw->sin_addr.s_addr == 0) if (gw->sin_addr.s_addr == 0)
pnh4->nh_flags |= NHF_DEFAULT; pnh4->nh_flags |= NHF_DEFAULT;
/* XXX: Set RTF_BROADCAST if GW address is broadcast */
} }
static void static void
@ -428,6 +445,7 @@ fib4_rte_to_nh_extended(struct rtentry *rte, struct in_addr dst,
gw = (struct sockaddr_in *)rt_key(rte); gw = (struct sockaddr_in *)rt_key(rte);
if (gw->sin_addr.s_addr == 0) if (gw->sin_addr.s_addr == 0)
pnh4->nh_flags |= NHF_DEFAULT; pnh4->nh_flags |= NHF_DEFAULT;
/* XXX: Set RTF_BROADCAST if GW address is broadcast */
ia = ifatoia(rte->rt_ifa); ia = ifatoia(rte->rt_ifa);
pnh4->nh_src = IA_SIN(ia)->sin_addr; pnh4->nh_src = IA_SIN(ia)->sin_addr;
@ -561,19 +579,335 @@ fib6_choose_prepend(uint32_t fibnum, struct nhop_prepend *nh_src,
*/ */
} }
/*
* Temporary function to copy ethernet address from valid lle
*/
static int
fib6_storelladdr(struct ifnet *ifp, struct in6_addr *dst, int mm_flags,
u_char *desten)
{
struct llentry *ln;
struct sockaddr_in6 dst_sa;
if (mm_flags & M_MCAST) {
ETHER_MAP_IPV6_MULTICAST(&dst, desten);
return (0);
}
memset(&dst_sa, 0, sizeof(dst_sa));
dst_sa.sin6_family = AF_INET6;
dst_sa.sin6_len = sizeof(dst_sa);
dst_sa.sin6_addr = *dst;
dst_sa.sin6_scope_id = ifp->if_index;
/*
* the entry should have been created in nd6_store_lladdr
*/
IF_AFDATA_RLOCK(ifp);
ln = lla_lookup(LLTABLE6(ifp), 0, (struct sockaddr *)&dst_sa);
/*
* Perform fast path for the following cases:
* 1) lle state is REACHABLE
* 2) lle state is DELAY (NS message sentNS message sent)
*
* Every other case involves lle modification, so we handle
* them separately.
*/
if (ln == NULL || (ln->ln_state != ND6_LLINFO_REACHABLE &&
ln->ln_state != ND6_LLINFO_DELAY)) {
if (ln != NULL)
LLE_RUNLOCK(ln);
IF_AFDATA_RUNLOCK(ifp);
return (1);
}
bcopy(&ln->ll_addr, desten, ifp->if_addrlen);
LLE_RUNLOCK(ln);
IF_AFDATA_RUNLOCK(ifp);
return (0);
}
int
fib6_lookup_prepend(uint32_t fibnum, struct in6_addr *dst, uint32_t scopeid,
struct mbuf *m, struct nhop_prepend *nh, struct nhop6_extended *nh_ext)
{
struct radix_node_head *rnh;
struct radix_node *rn;
struct sockaddr_in6 sin6, *gw_sa;
struct in6_addr gw6;
struct rtentry *rte;
struct ifnet *lifp;
struct ether_header *eh;
uint32_t flags;
int error;
if (IN6_IS_SCOPE_LINKLOCAL(dst)) {
/* Do not lookup link-local addresses in rtable */
error = fib6_lla_to_nh(dst, scopeid, nh, &lifp);
if (error != 0)
return (error);
/* */
gw6 = *dst;
goto do_l2;
}
KASSERT((fibnum < rt_numfibs), ("fib6_lookup_prepend: bad fibnum"));
rnh = rt_tables_get_rnh(fibnum, AF_INET6);
if (rnh == NULL)
return (ENOENT);
/* Prepare lookup key */
memset(&sin6, 0, sizeof(sin6));
sin6.sin6_len = sizeof(struct sockaddr_in6);
sin6.sin6_addr = *dst;
sin6.sin6_scope_id = scopeid;
sa6_embedscope(&sin6, 0);
RADIX_NODE_HEAD_RLOCK(rnh);
rn = rnh->rnh_matchaddr((void *)&sin6, rnh);
rte = RNTORT(rn);
if (rn == NULL || ((rn->rn_flags & RNF_ROOT) != 0) ||
RT_LINK_IS_UP(rte->rt_ifp) == 0) {
RADIX_NODE_HEAD_RUNLOCK(rnh);
return (EHOSTUNREACH);
}
/* Explicitly zero nexthop */
memset(nh, 0, sizeof(*nh));
flags = 0;
nh->nh_mtu = min(rte->rt_mtu, IN6_LINKMTU(rte->rt_ifp));
if (rte->rt_flags & RTF_GATEWAY) {
gw_sa = (struct sockaddr_in6 *)rte->rt_gateway;
gw6 = gw_sa->sin6_addr;
in6_clearscope(&gw6);
} else
gw6 = *dst;
/* Set flags */
flags = fib_rte_to_nh_flags(rte->rt_flags);
gw_sa = (struct sockaddr_in6 *)rt_key(rte);
if (IN6_IS_ADDR_UNSPECIFIED(&gw_sa->sin6_addr))
flags |= NHF_DEFAULT;
/*
* TODO: nh L2/L3 resolve.
* Currently all we have is rte ifp.
* Simply use it.
*/
/* Save interface address ifp */
nh->aifp_idx = fib6_get_ifa(rte);
/* Save both logical and transmit interface indexes */
lifp = rte->rt_ifp;
nh->lifp_idx = lifp->if_index;
nh->i.ifp_idx = nh->lifp_idx;
RADIX_NODE_HEAD_RUNLOCK(rnh);
nh->nh_flags = flags;
do_l2:
/*
* Try to lookup L2 info.
* Do this using separate LLE locks.
* TODO: move this under radix lock.
*/
if (lifp->if_type == IFT_ETHER) {
eh = (struct ether_header *)nh->d.data;
/*
* Fill in ethernet header.
* It should be already presented if we're
* sending data via known gateway.
*/
error = fib6_storelladdr(lifp, &gw6, m ? m->m_flags : 0,
eh->ether_dhost);
if (error == 0) {
memcpy(&eh->ether_shost, IF_LLADDR(lifp), ETHER_ADDR_LEN);
eh->ether_type = htons(ETHERTYPE_IPV6);
nh->nh_count = ETHER_HDR_LEN;
return (0);
}
}
/* Notify caller that no L2 info is linked */
nh->nh_count = 0;
nh->nh_flags |= NHF_L2_INCOMPLETE;
/* ..And save gateway address */
nh->d.gw6 = gw6;
return (0);
}
int
fib6_sendmbuf(struct ifnet *ifp, struct ifnet *origifp, struct mbuf *m,
struct nhop_prepend *nh)
{
int error;
if (nh != NULL && (nh->nh_flags & NHF_L2_INCOMPLETE) == 0) {
/*
* Fast path case. Most packets should
* be sent from here.
* TODO: Make special ifnet
* 'if_output_frame' handler for that.
*/
struct route_compat rc;
struct ether_header *eh;
rc.ro_flags = AF_INET6 << 8 | RT_NHOP;
rc.ro_nh = nh;
M_PREPEND(m, nh->nh_count, M_NOWAIT);
if (m == NULL)
return (ENOBUFS);
eh = mtod(m, struct ether_header *);
memcpy(eh, nh->d.data, nh->nh_count);
error = (*ifp->if_output)(ifp, m,
NULL, (struct route *)&rc);
} else {
/* We need to perform ND lookup */
struct sockaddr_in6 gw_out;
memset(&gw_out, 0, sizeof(gw_out));
gw_out.sin6_family = AF_INET6;
gw_out.sin6_len = sizeof(gw_out);
gw_out.sin6_addr = nh->d.gw6;
gw_out.sin6_scope_id = ifp->if_index;
sa6_embedscope(&gw_out, 0);
error = nd6_output(ifp, origifp, m, &gw_out, NULL);
}
return (error);
}
static uint16_t
fib6_get_ifa(struct rtentry *rte)
{
struct ifnet *ifp;
struct sockaddr_dl *sdl;
ifp = rte->rt_ifp;
if ((ifp->if_flags & IFF_LOOPBACK) &&
rte->rt_gateway->sa_family == AF_LINK) {
sdl = (struct sockaddr_dl *)rte->rt_gateway;
return (sdl->sdl_index);
}
return (ifp->if_index);
#if 0
/* IPv6 case */
/* Alternative way to get interface address ifp */
/*
* Adjust the "outgoing" interface. If we're going to loop
* the packet back to ourselves, the ifp would be the loopback
* interface. However, we'd rather know the interface associated
* to the destination address (which should probably be one of
* our own addresses.)
*/
if (rt) {
if ((rt->rt_ifp->if_flags & IFF_LOOPBACK) &&
(rt->rt_gateway->sa_family == AF_LINK))
*retifp =
ifnet_byindex(((struct sockaddr_dl *)
rt->rt_gateway)->sdl_index);
}
/* IPv4 case */
//pnh6->nh_ifp = rte->rt_ifa->ifa_ifp;
#endif
}
static int
fib6_lla_to_nh_basic(struct in6_addr *dst, uint32_t scopeid,
struct nhop6_basic *pnh6)
{
struct ifnet *ifp;
ifp = ifnet_byindex_locked(scopeid);
if (ifp == NULL)
return (ENOENT);
/* Do explicit nexthop zero unless we're copying it */
memset(pnh6, 0, sizeof(*pnh6));
pnh6->nh_ifp = ifp;
pnh6->nh_mtu = IN6_LINKMTU(ifp);
/* No flags set */
pnh6->nh_addr = *dst;
return (0);
}
static int
fib6_lla_to_nh_extended(struct in6_addr *dst, uint32_t scopeid,
struct nhop6_extended *pnh6)
{
struct ifnet *ifp;
ifp = ifnet_byindex_locked(scopeid);
if (ifp == NULL)
return (ENOENT);
/* Do explicit nexthop zero unless we're copying it */
memset(pnh6, 0, sizeof(*pnh6));
pnh6->nh_ifp = ifp;
pnh6->nh_mtu = IN6_LINKMTU(ifp);
/* No flags set */
pnh6->nh_addr = *dst;
return (0);
}
static int
fib6_lla_to_nh(struct in6_addr *dst, uint32_t scopeid,
struct nhop_prepend *nh, struct ifnet **lifp)
{
struct ifnet *ifp;
ifp = ifnet_byindex_locked(scopeid);
if (ifp == NULL)
return (ENOENT);
/* Do explicit nexthop zero unless we're copying it */
memset(nh, 0, sizeof(*nh));
/* No flags set */
nh->nh_mtu = IN6_LINKMTU(ifp);
/* Save lifp */
*lifp = ifp;
nh->aifp_idx = scopeid;
nh->lifp_idx = scopeid;
/* Check id this is for-us address */
if (in6_ifawithifp_lla(ifp, dst)) {
if ((ifp = V_loif) != NULL)
nh->lifp_idx = ifp->if_index;
}
return (0);
}
static void static void
fib6_rte_to_nh_basic(struct rtentry *rte, struct in6_addr dst, fib6_rte_to_nh_basic(struct rtentry *rte, struct in6_addr *dst,
struct nhop6_basic *pnh6) struct nhop6_basic *pnh6)
{ {
struct sockaddr_in6 *gw; struct sockaddr_in6 *gw;
pnh6->nh_ifp = rte->rt_ifa->ifa_ifp; /* Do explicit nexthop zero unless we're copying it */
pnh6->nh_mtu = min(rte->rt_mtu, rte->rt_ifp->if_mtu); memset(pnh6, 0, sizeof(*pnh6));
pnh6->nh_ifp = ifnet_byindex(fib6_get_ifa(rte));
pnh6->nh_mtu = min(rte->rt_mtu, IN6_LINKMTU(rte->rt_ifp));
if (rte->rt_flags & RTF_GATEWAY) { if (rte->rt_flags & RTF_GATEWAY) {
gw = (struct sockaddr_in6 *)rte->rt_gateway; gw = (struct sockaddr_in6 *)rte->rt_gateway;
pnh6->nh_addr = gw->sin6_addr; pnh6->nh_addr = gw->sin6_addr;
in6_clearscope(&pnh6->nh_addr);
} else } else
pnh6->nh_addr = dst; pnh6->nh_addr = *dst;
/* Set flags */ /* Set flags */
pnh6->nh_flags = fib_rte_to_nh_flags(rte->rt_flags); pnh6->nh_flags = fib_rte_to_nh_flags(rte->rt_flags);
gw = (struct sockaddr_in6 *)rt_key(rte); gw = (struct sockaddr_in6 *)rt_key(rte);
@ -582,19 +916,23 @@ fib6_rte_to_nh_basic(struct rtentry *rte, struct in6_addr dst,
} }
static void static void
fib6_rte_to_nh_extended(struct rtentry *rte, struct in6_addr dst, fib6_rte_to_nh_extended(struct rtentry *rte, struct in6_addr *dst,
struct nhop6_extended *pnh6) struct nhop6_extended *pnh6)
{ {
struct sockaddr_in6 *gw; struct sockaddr_in6 *gw;
struct in6_ifaddr *ia; struct in6_ifaddr *ia;
pnh6->nh_ifp = rte->rt_ifa->ifa_ifp; /* Do explicit nexthop zero unless we're copying it */
pnh6->nh_mtu = min(rte->rt_mtu, rte->rt_ifp->if_mtu); memset(pnh6, 0, sizeof(*pnh6));
pnh6->nh_ifp = ifnet_byindex(fib6_get_ifa(rte));
pnh6->nh_mtu = min(rte->rt_mtu, IN6_LINKMTU(rte->rt_ifp));
if (rte->rt_flags & RTF_GATEWAY) { if (rte->rt_flags & RTF_GATEWAY) {
gw = (struct sockaddr_in6 *)rte->rt_gateway; gw = (struct sockaddr_in6 *)rte->rt_gateway;
pnh6->nh_addr = gw->sin6_addr; pnh6->nh_addr = gw->sin6_addr;
in6_clearscope(&pnh6->nh_addr);
} else } else
pnh6->nh_addr = dst; pnh6->nh_addr = *dst;
/* Set flags */ /* Set flags */
pnh6->nh_flags = fib_rte_to_nh_flags(rte->rt_flags); pnh6->nh_flags = fib_rte_to_nh_flags(rte->rt_flags);
gw = (struct sockaddr_in6 *)rt_key(rte); gw = (struct sockaddr_in6 *)rt_key(rte);
@ -602,18 +940,22 @@ fib6_rte_to_nh_extended(struct rtentry *rte, struct in6_addr dst,
pnh6->nh_flags |= NHF_DEFAULT; pnh6->nh_flags |= NHF_DEFAULT;
ia = ifatoia6(rte->rt_ifa); ia = ifatoia6(rte->rt_ifa);
pnh6->nh_src = IA6_SIN6(ia)->sin6_addr;
} }
int int
fib6_lookup_nh_basic(uint32_t fibnum, struct in6_addr dst, uint32_t flowid, fib6_lookup_nh_basic(uint32_t fibnum, struct in6_addr *dst, uint32_t scopeid,
struct nhop6_basic *pnh6) uint32_t flowid, struct nhop6_basic *pnh6)
{ {
struct radix_node_head *rnh; struct radix_node_head *rnh;
struct radix_node *rn; struct radix_node *rn;
struct sockaddr_in6 sin6; struct sockaddr_in6 sin6;
struct rtentry *rte; struct rtentry *rte;
if (IN6_IS_SCOPE_LINKLOCAL(dst)) {
/* Do not lookup link-local addresses in rtable */
return (fib6_lla_to_nh_basic(dst, scopeid, pnh6));
}
KASSERT((fibnum < rt_numfibs), ("fib6_lookup_nh_basic: bad fibnum")); KASSERT((fibnum < rt_numfibs), ("fib6_lookup_nh_basic: bad fibnum"));
rnh = rt_tables_get_rnh(fibnum, AF_INET6); rnh = rt_tables_get_rnh(fibnum, AF_INET6);
if (rnh == NULL) if (rnh == NULL)
@ -621,7 +963,9 @@ fib6_lookup_nh_basic(uint32_t fibnum, struct in6_addr dst, uint32_t flowid,
/* Prepare lookup key */ /* Prepare lookup key */
memset(&sin6, 0, sizeof(sin6)); memset(&sin6, 0, sizeof(sin6));
sin6.sin6_addr = dst; sin6.sin6_addr = *dst;
sin6.sin6_scope_id = scopeid;
sa6_embedscope(&sin6, 0);
RADIX_NODE_HEAD_RLOCK(rnh); RADIX_NODE_HEAD_RLOCK(rnh);
rn = rnh->rnh_matchaddr((void *)&sin6, rnh); rn = rnh->rnh_matchaddr((void *)&sin6, rnh);
@ -649,7 +993,7 @@ fib6_lookup_nh_basic(uint32_t fibnum, struct in6_addr dst, uint32_t flowid,
* - mtu from logical transmit interface will be returned. * - mtu from logical transmit interface will be returned.
*/ */
int int
fib6_lookup_nh_ext(uint32_t fibnum, struct in6_addr dst, uint32_t scopeid, fib6_lookup_nh_ext(uint32_t fibnum, struct in6_addr *dst, uint32_t scopeid,
uint32_t flowid, uint32_t flags, struct nhop6_extended *pnh6) uint32_t flowid, uint32_t flags, struct nhop6_extended *pnh6)
{ {
struct radix_node_head *rnh; struct radix_node_head *rnh;
@ -657,6 +1001,12 @@ fib6_lookup_nh_ext(uint32_t fibnum, struct in6_addr dst, uint32_t scopeid,
struct sockaddr_in6 sin6; struct sockaddr_in6 sin6;
struct rtentry *rte; struct rtentry *rte;
if (IN6_IS_SCOPE_LINKLOCAL(dst)) {
/* Do not lookup link-local addresses in rtable */
/* XXX: Do lwref on egress ifp */
return (fib6_lla_to_nh_extended(dst, scopeid, pnh6));
}
KASSERT((fibnum < rt_numfibs), ("fib4_lookup_nh_ext: bad fibnum")); KASSERT((fibnum < rt_numfibs), ("fib4_lookup_nh_ext: bad fibnum"));
rnh = rt_tables_get_rnh(fibnum, AF_INET6); rnh = rt_tables_get_rnh(fibnum, AF_INET6);
if (rnh == NULL) if (rnh == NULL)
@ -665,7 +1015,9 @@ fib6_lookup_nh_ext(uint32_t fibnum, struct in6_addr dst, uint32_t scopeid,
/* Prepare lookup key */ /* Prepare lookup key */
memset(&sin6, 0, sizeof(sin6)); memset(&sin6, 0, sizeof(sin6));
sin6.sin6_len = sizeof(struct sockaddr_in6); sin6.sin6_len = sizeof(struct sockaddr_in6);
sin6.sin6_addr = dst; sin6.sin6_addr = *dst;
sin6.sin6_scope_id = scopeid;
sa6_embedscope(&sin6, 0);
RADIX_NODE_HEAD_RLOCK(rnh); RADIX_NODE_HEAD_RLOCK(rnh);
rn = rnh->rnh_matchaddr((void *)&sin6, rnh); rn = rnh->rnh_matchaddr((void *)&sin6, rnh);

View File

@ -74,7 +74,8 @@ struct nhop_prepend {
uint16_t ifp_idx; /* Transmit interface index */ uint16_t ifp_idx; /* Transmit interface index */
uint16_t nhop_idx; /* L2 multipath nhop index */ uint16_t nhop_idx; /* L2 multipath nhop index */
} i; } i;
uint16_t spare1[3]; uint16_t aifp_idx; /* Interface address index */
uint16_t spare1[2];
union { union {
char data[MAX_PREPEND_LEN]; /* data to prepend */ char data[MAX_PREPEND_LEN]; /* data to prepend */
#ifdef INET #ifdef INET
@ -102,6 +103,7 @@ struct nhop_prepend {
#define NH_LIFP(nh) ifnet_byindex_locked((nh)->lifp_idx) #define NH_LIFP(nh) ifnet_byindex_locked((nh)->lifp_idx)
#define NH_TIFP(nh) ifnet_byindex_locked((nh)->i.ifp_idx) #define NH_TIFP(nh) ifnet_byindex_locked((nh)->i.ifp_idx)
#define NH_AIFP(nh) ifnet_byindex_locked((nh)->aifp_idx)
/* L2/L3 recursive nexthop */ /* L2/L3 recursive nexthop */
struct nhop_multi { struct nhop_multi {
@ -173,7 +175,6 @@ struct nhop6_extended {
uint16_t nh_flags; /* nhop flags */ uint16_t nh_flags; /* nhop flags */
uint8_t spare[4]; uint8_t spare[4];
struct in6_addr nh_addr; /* GW/DST IPv6 address */ struct in6_addr nh_addr; /* GW/DST IPv6 address */
struct in6_addr nh_src; /* default source IPv6 address */
uint64_t spare2[2]; uint64_t spare2[2];
}; };
@ -186,9 +187,10 @@ struct nhopu_extended {
struct route_info { struct route_info {
struct nhop_prepend *ri_nh; /* Desired nexthop to use */ struct nhop_prepend *ri_nh; /* Desired nexthop to use */
struct nhop64_basic *ri_nh_info; /* Get selected route info */ struct nhopu_basic *ri_nh_info; /* Get selected route info */
uint16_t ri_mtu; uint16_t ri_mtu; /* Get selected route MTU */
uint16_t spare[3]; uint16_t spare;
uint32_t scopeid; /* Desired scope id to use */
}; };
struct route_compat { struct route_compat {
@ -206,9 +208,9 @@ void fib4_free_nh_ext(uint32_t fibnum, struct nhop4_extended *pnh4);
#define NHOP_LOOKUP_REF 0x01 #define NHOP_LOOKUP_REF 0x01
int fib6_lookup_nh_basic(uint32_t fibnum, struct in6_addr dst, uint32_t flowid, int fib6_lookup_nh_basic(uint32_t fibnum, struct in6_addr *dst,
struct nhop6_basic *pnh6); uint32_t scopeid, uint32_t flowid, struct nhop6_basic *pnh6);
int fib6_lookup_nh_ext(uint32_t fibnum, struct in6_addr dst, int fib6_lookup_nh_ext(uint32_t fibnum, struct in6_addr *dst,
uint32_t scopeid, uint32_t flowid, uint32_t flags, uint32_t scopeid, uint32_t flowid, uint32_t flags,
struct nhop6_extended *pnh6); struct nhop6_extended *pnh6);
void fib6_free_nh_ext(uint32_t fibnum, struct nhop6_extended *pnh6); void fib6_free_nh_ext(uint32_t fibnum, struct nhop6_extended *pnh6);
@ -228,6 +230,11 @@ int fib4_sendmbuf(struct ifnet *ifp, struct mbuf *m, struct nhop_prepend *nh,
void fib6_free_nh_prepend(uint32_t fibnum, struct nhop_prepend *nh); void fib6_free_nh_prepend(uint32_t fibnum, struct nhop_prepend *nh);
void fib6_choose_prepend(uint32_t fibnum, struct nhop_prepend *nh_src, void fib6_choose_prepend(uint32_t fibnum, struct nhop_prepend *nh_src,
uint32_t flowid, struct nhop_prepend *nh, struct nhop6_extended *nh_ext); uint32_t flowid, struct nhop_prepend *nh, struct nhop6_extended *nh_ext);
int fib6_lookup_prepend(uint32_t fibnum, struct in6_addr *dst, uint32_t scopeid,
struct mbuf *m, struct nhop_prepend *nh, struct nhop6_extended *nh_ext);
int fib6_sendmbuf(struct ifnet *ifp, struct ifnet *origifp, struct mbuf *m,
struct nhop_prepend *nh);
#define FWD_INET 0 #define FWD_INET 0
#define FWD_INET6 1 #define FWD_INET6 1

View File

@ -271,8 +271,7 @@ ng_ipfw_rcvdata(hook_p hook, item_p item)
#endif #endif
#ifdef INET6 #ifdef INET6
case IPV6_VERSION >> 4: case IPV6_VERSION >> 4:
return (ip6_output(m, NULL, NULL, 0, NULL, return (ip6_output(m, NULL, NULL, 0, NULL, NULL));
NULL, NULL));
#endif #endif
} }
} }

View File

@ -930,7 +930,7 @@ carp_send_ad_locked(struct carp_softc *sc)
CARPSTATS_INC(carps_opackets6); CARPSTATS_INC(carps_opackets6);
carp_send_ad_error(sc, ip6_output(m, NULL, NULL, 0, carp_send_ad_error(sc, ip6_output(m, NULL, NULL, 0,
&sc->sc_carpdev->if_carp->cif_im6o, NULL, NULL)); &sc->sc_carpdev->if_carp->cif_im6o, NULL));
} }
#endif /* INET6 */ #endif /* INET6 */

View File

@ -456,7 +456,7 @@ div_output(struct socket *so, struct mbuf *m, struct sockaddr_in *sin,
break; break;
#ifdef INET6 #ifdef INET6
case IPV6_VERSION >> 4: case IPV6_VERSION >> 4:
error = ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL); error = ip6_output(m, NULL, NULL, 0, NULL, NULL);
break; break;
#endif #endif
} }

View File

@ -110,9 +110,13 @@ extern struct protosw inetsw[];
* header (with len, off, ttl, proto, tos, src, dst). * header (with len, off, ttl, proto, tos, src, dst).
* The mbuf chain containing the packet will be freed. * The mbuf chain containing the packet will be freed.
* The mbuf opt, if present, will not be freed. * The mbuf opt, if present, will not be freed.
* If route ro is present and has ro_rt initialized, route lookup would be *
* skipped and ro->ro_rt would be used. If ro is present but ro->ro_rt is NULL, * If @ri is present:
* then result of route lookup is stored in ro->ro_rt. * - if ri->ri_nh is not null, route will be calculated using ri_nh.
* - if ri->ri_nh_info is set, nhop4_basic route info will be stored on
* successful transmit (error=0).
* - ri->ri_mtu will be set if packet fails to be transmitted due to MTU
* issues
* *
* In the IP forwarding case, the packet will arrive with options already * In the IP forwarding case, the packet will arrive with options already
* inserted, so must have a NULL opt pointer. * inserted, so must have a NULL opt pointer.
@ -364,21 +368,12 @@ ip_output(struct mbuf *m, struct mbuf *opt, struct route_info *ri, int flags,
nh = &local_nh; nh = &local_nh;
ifp = NH_LIFP(nh); ifp = NH_LIFP(nh);
mtu = nh->nh_mtu; mtu = nh->nh_mtu;
if (nh->nh_flags & (RTF_HOST|RTF_GATEWAY)) { if (nh->nh_flags & (RTF_HOST|RTF_GATEWAY))
/* XXX: Set RTF_BROADCAST if GW address is broadcast */
isbroadcast = (nh->nh_flags & RTF_BROADCAST); isbroadcast = (nh->nh_flags & RTF_BROADCAST);
} else else
isbroadcast = in_broadcast(dst, ifp); isbroadcast = in_broadcast(dst, ifp);
} }
/*
* XXX: Move somewhere to sendit
*/
if (ri != NULL) {
ri->ri_mtu = mtu;
}
/* Catch a possible divide by zero later. */ /* Catch a possible divide by zero later. */
KASSERT(mtu > 0, ("%s: mtu %d <= 0, rte=%p (rt_flags=0x%08x) ifp=%p", KASSERT(mtu > 0, ("%s: mtu %d <= 0, rte=%p (rt_flags=0x%08x) ifp=%p",
__func__, mtu, nh, (nh != NULL) ? nh->nh_flags : 0, ifp)); __func__, mtu, nh, (nh != NULL) ? nh->nh_flags : 0, ifp));
@ -607,6 +602,20 @@ ip_output(struct mbuf *m, struct mbuf *opt, struct route_info *ri, int flags,
goto again; goto again;
} }
if (ri != NULL) {
ri->ri_mtu = mtu;
if (ri->ri_nh_info != NULL) {
struct nhop4_basic *pnh4;
pnh4 = &ri->ri_nh_info->u.nh4;
pnh4->nh_ifp = ifp;
pnh4->nh_flags = nh ? nh->nh_flags : 0;
pnh4->nh_mtu = mtu;
/* XXX: This is not always correct. */
pnh4->nh_addr = dst;
}
}
passout: passout:
/* 127/8 must not appear on wire - RFC1122. */ /* 127/8 must not appear on wire - RFC1122. */
if ((ntohl(ip->ip_dst.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET || if ((ntohl(ip->ip_dst.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET ||

View File

@ -452,9 +452,9 @@ typedef struct rtentry sctp_rtentry_t;
if (local_stcb && local_stcb->sctp_ep) \ if (local_stcb && local_stcb->sctp_ep) \
result = ip6_output(o_pak, \ result = ip6_output(o_pak, \
((struct in6pcb *)(local_stcb->sctp_ep))->in6p_outputopts, \ ((struct in6pcb *)(local_stcb->sctp_ep))->in6p_outputopts, \
(ro), 0, 0, ifp, NULL); \ NULL, 0, NULL, NULL); \
else \ else \
result = ip6_output(o_pak, NULL, (ro), 0, 0, ifp, NULL); \ result = ip6_output(o_pak, NULL, NULL, 0, NULL, NULL); \
} }
struct mbuf * struct mbuf *

View File

@ -48,6 +48,7 @@ __FBSDID("$FreeBSD$");
#define TCPOUTFLAGS #define TCPOUTFLAGS
#include <netinet/tcp_fsm.h> #include <netinet/tcp_fsm.h>
#include <netinet/toecore.h> #include <netinet/toecore.h>
#include <netinet6/scope6_var.h>
int registered_toedevs; int registered_toedevs;
@ -86,11 +87,13 @@ tcp_offload_connect(struct socket *so, struct sockaddr *nam)
goto done; goto done;
} else if (af == AF_INET6) { } else if (af == AF_INET6) {
struct sockaddr_in6 *sin6; struct sockaddr_in6 *sin6;
struct in6_addr dst;
uint32_t scopeid;
sin6 = (struct sockaddr_in6 *)nam; sin6 = (struct sockaddr_in6 *)nam;
in6_splitscope(&sin6->sin6_addr, &dst, &scopeid);
if (fib6_lookup_nh_ext(fibnum, if (fib6_lookup_nh_ext(fibnum, &dst, scopeid,
sin6->sin6_addr, sin6->sin6_scope_id,
0, NHOP_LOOKUP_REF, &nhu_ext.u.nh6) != 0) 0, NHOP_LOOKUP_REF, &nhu_ext.u.nh6) != 0)
return (EHOSTUNREACH); return (EHOSTUNREACH);

View File

@ -1257,9 +1257,9 @@ tcp_output(struct tcpcb *tp)
*/ */
#ifdef INET6 #ifdef INET6
if (isipv6) { if (isipv6) {
struct route_in6 ro; struct route_info ri;
bzero(&ro, sizeof(ro)); bzero(&ri, sizeof(ri));
/* /*
* we separately set hoplimit for every segment, since the * we separately set hoplimit for every segment, since the
* user might want to change the value via setsockopt. * user might want to change the value via setsockopt.
@ -1281,13 +1281,12 @@ tcp_output(struct tcpcb *tp)
TCP_PROBE5(send, NULL, tp, ip6, tp, th); TCP_PROBE5(send, NULL, tp, ip6, tp, th);
/* TODO: IPv6 IP6TOS_ECT bit on */ /* TODO: IPv6 IP6TOS_ECT bit on */
error = ip6_output(m, tp->t_inpcb->in6p_outputopts, &ro, error = ip6_output(m, tp->t_inpcb->in6p_outputopts, &ri,
((so->so_options & SO_DONTROUTE) ? IP_ROUTETOIF : 0), ((so->so_options & SO_DONTROUTE) ? IP_ROUTETOIF : 0),
NULL, NULL, tp->t_inpcb); NULL, tp->t_inpcb);
if (error == EMSGSIZE && ro.ro_rt != NULL) if (error == EMSGSIZE)
mtu = ro.ro_rt->rt_mtu; mtu = ri.ri_mtu;
RO_RTFREE(&ro);
} }
#endif /* INET6 */ #endif /* INET6 */
#if defined(INET) && defined(INET6) #if defined(INET) && defined(INET6)
@ -1324,7 +1323,7 @@ tcp_output(struct tcpcb *tp)
TCP_PROBE5(send, NULL, tp, ip, tp, th); TCP_PROBE5(send, NULL, tp, ip, tp, th);
error = ip_output(m, tp->t_inpcb->inp_options, &ri, error = ip_output(m, tp->t_inpcb->inp_options, &ri,
((so->so_options & SO_DONTROUTE) ? IP_ROUTETOIF : 0), 0, ((so->so_options & SO_DONTROUTE) ? IP_ROUTETOIF : 0), NULL,
tp->t_inpcb); tp->t_inpcb);
if (error == EMSGSIZE) if (error == EMSGSIZE)

View File

@ -725,7 +725,7 @@ tcp_respond(struct tcpcb *tp, void *ipgen, struct tcphdr *th, struct mbuf *m,
TCP_PROBE5(send, NULL, tp, mtod(m, const char *), tp, nth); TCP_PROBE5(send, NULL, tp, mtod(m, const char *), tp, nth);
#ifdef INET6 #ifdef INET6
if (isipv6) if (isipv6)
(void) ip6_output(m, NULL, NULL, ipflags, NULL, NULL, inp); (void) ip6_output(m, NULL, NULL, ipflags, NULL, inp);
#endif /* INET6 */ #endif /* INET6 */
#if defined(INET) && defined(INET6) #if defined(INET) && defined(INET6)
else else

View File

@ -1571,7 +1571,7 @@ syncache_respond(struct syncache *sc, struct syncache_head *sch, int locked)
return (error); return (error);
} }
#endif #endif
error = ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL); error = ip6_output(m, NULL, NULL, 0, NULL, NULL);
} }
#endif #endif
#if defined(INET6) && defined(INET) #if defined(INET6) && defined(INET)

View File

@ -596,7 +596,7 @@ tcp_twrespond(struct tcptw *tw, int flags)
sizeof(struct tcphdr) + optlen, IPPROTO_TCP, 0); sizeof(struct tcphdr) + optlen, IPPROTO_TCP, 0);
ip6->ip6_hlim = in6_selecthlim(inp, NULL); ip6->ip6_hlim = in6_selecthlim(inp, NULL);
error = ip6_output(m, inp->in6p_outputopts, NULL, error = ip6_output(m, inp->in6p_outputopts, NULL,
(tw->tw_so_options & SO_DONTROUTE), NULL, NULL, inp); (tw->tw_so_options & SO_DONTROUTE), NULL, inp);
} }
#endif #endif
#if defined(INET6) && defined(INET) #if defined(INET6) && defined(INET)

View File

@ -2142,7 +2142,12 @@ icmp6_reflect(struct mbuf *m, size_t off)
int plen; int plen;
int type, code; int type, code;
struct ifnet *outif = NULL; struct ifnet *outif = NULL;
struct in6_addr origdst, src, *srcp = NULL; struct in6_addr origdst, src, dst;
struct route_info ri;
struct nhop6_basic nh6;
uint32_t scopeid;
int e;
/* too short to reflect */ /* too short to reflect */
if (off < sizeof(struct ip6_hdr)) { if (off < sizeof(struct ip6_hdr)) {
@ -2206,11 +2211,13 @@ icmp6_reflect(struct mbuf *m, size_t off)
* procedure of an outgoing packet of our own, in which case we need * procedure of an outgoing packet of our own, in which case we need
* to search in the ifaddr list. * to search in the ifaddr list.
*/ */
memset(&src, 0, sizeof(src));
if (!IN6_IS_ADDR_MULTICAST(&origdst)) { if (!IN6_IS_ADDR_MULTICAST(&origdst)) {
if ((ia = ip6_getdstifaddr(m))) { if ((ia = ip6_getdstifaddr(m))) {
if (!(ia->ia6_flags & if (!(ia->ia6_flags &
(IN6_IFF_ANYCAST|IN6_IFF_NOTREADY))) (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY)))
srcp = &ia->ia_addr.sin6_addr; src = ia->ia_addr.sin6_addr;
ifa_free(&ia->ia_ifa);
} else { } else {
struct sockaddr_in6 d; struct sockaddr_in6 d;
@ -2223,42 +2230,14 @@ icmp6_reflect(struct mbuf *m, size_t off)
if (ia && if (ia &&
!(ia->ia6_flags & !(ia->ia6_flags &
(IN6_IFF_ANYCAST|IN6_IFF_NOTREADY))) { (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY))) {
srcp = &ia->ia_addr.sin6_addr; src = ia->ia_addr.sin6_addr;
ifa_free(&ia->ia_ifa);
} }
} }
} }
if (srcp == NULL) {
int e;
struct sockaddr_in6 sin6;
struct route_in6 ro;
/* ip6->ip6_src = src;
* This case matches to multicasts, our anycast, or unicasts
* that we do not own. Select a source address based on the
* source address of the erroneous packet.
*/
bzero(&sin6, sizeof(sin6));
sin6.sin6_family = AF_INET6;
sin6.sin6_len = sizeof(sin6);
sin6.sin6_addr = ip6->ip6_dst; /* zone ID should be embedded */
bzero(&ro, sizeof(ro));
e = in6_selectsrc(&sin6, NULL, NULL, &ro, NULL, &outif, &src);
if (ro.ro_rt)
RTFREE(ro.ro_rt); /* XXX: we could use this */
if (e) {
char ip6buf[INET6_ADDRSTRLEN];
nd6log((LOG_DEBUG,
"icmp6_reflect: source can't be determined: "
"dst=%s, error=%d\n",
ip6_sprintf(ip6buf, &sin6.sin6_addr), e));
goto bad;
}
srcp = &src;
}
ip6->ip6_src = *srcp;
ip6->ip6_flow = 0; ip6->ip6_flow = 0;
ip6->ip6_vfc &= ~IPV6_VERSION_MASK; ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
ip6->ip6_vfc |= IPV6_VERSION; ip6->ip6_vfc |= IPV6_VERSION;
@ -2271,6 +2250,33 @@ icmp6_reflect(struct mbuf *m, size_t off)
} else } else
ip6->ip6_hlim = V_ip6_defhlim; ip6->ip6_hlim = V_ip6_defhlim;
/*
* Deembed scope
*/
in6_splitscope(&ip6->ip6_dst, &dst, &scopeid);
if (IN6_IS_ADDR_UNSPECIFIED(&src)) {
/*
* This case matches to multicasts, our anycast, or unicasts
* that we do not own. Select a source address based on the
* source address of the erroneous packet.
*/
e = in6_selectsrc_addr(M_GETFIB(m), &dst, scopeid, &src);
if (e) {
char ip6buf[INET6_ADDRSTRLEN];
nd6log((LOG_DEBUG,
"icmp6_reflect: source can't be determined: "
"dst=%s, error=%d\n",
ip6_sprintf(ip6buf, &dst), e));
goto bad;
}
ip6->ip6_src = src;
}
/* finalize header */
icmp6->icmp6_cksum = 0; icmp6->icmp6_cksum = 0;
icmp6->icmp6_cksum = in6_cksum(m, IPPROTO_ICMPV6, icmp6->icmp6_cksum = in6_cksum(m, IPPROTO_ICMPV6,
sizeof(struct ip6_hdr), plen); sizeof(struct ip6_hdr), plen);
@ -2281,17 +2287,20 @@ icmp6_reflect(struct mbuf *m, size_t off)
m->m_flags &= ~(M_BCAST|M_MCAST); m->m_flags &= ~(M_BCAST|M_MCAST);
ip6_output(m, NULL, NULL, 0, NULL, &outif, NULL); memset(&ri, 0, sizeof(ri));
if (outif) ri.ri_nh_info = (struct nhopu_basic *)&nh6;
icmp6_ifoutstat_inc(outif, type, code); ri.scopeid = scopeid;
e = ip6_output(m, NULL, &ri, 0, NULL, NULL);
if (e == 0) {
/* XXX: Possible use after free */
outif = nh6.nh_ifp;
//icmp6_ifoutstat_inc(outif, type, code);
}
if (ia != NULL)
ifa_free(&ia->ia_ifa);
return; return;
bad: bad:
if (ia != NULL)
ifa_free(&ia->ia_ifa);
m_freem(m); m_freem(m);
return; return;
} }
@ -2387,11 +2396,11 @@ icmp6_redirect_input(struct mbuf *m, int off)
} }
{ {
/* ip6->ip6_src must be equal to gw for icmp6->icmp6_reddst */ /* ip6->ip6_src must be equal to gw for icmp6->icmp6_reddst */
struct nhop6_extended nh_ext; struct nhop6_basic nh6;
if (fib6_lookup_nh_ext(RT_DEFAULT_FIB, reddst6, 0, 0, 0, &nh_ext) == 0){ if (fib6_lookup_nh_basic(RT_DEFAULT_FIB, &reddst6, 0, 0, &nh6)==0) {
/* XXX: Think about AF_LINK GW */ /* XXX: Think about AF_LINK GW */
if ((nh_ext.nh_flags & NHF_GATEWAY) == 0) { if ((nh6.nh_flags & NHF_GATEWAY) == 0) {
nd6log((LOG_ERR, nd6log((LOG_ERR,
"ICMP6 redirect rejected; no route " "ICMP6 redirect rejected; no route "
"with inet6 gateway found for redirect dst: %s\n", "with inet6 gateway found for redirect dst: %s\n",
@ -2399,12 +2408,12 @@ icmp6_redirect_input(struct mbuf *m, int off)
goto bad; goto bad;
} }
if (bcmp(&src6, &nh_ext.nh_addr, sizeof(struct in6_addr)) != 0){ if (IN6_ARE_ADDR_EQUAL(&src6, &nh6.nh_addr) == 0) {
nd6log((LOG_ERR, nd6log((LOG_ERR,
"ICMP6 redirect rejected; " "ICMP6 redirect rejected; "
"not equal to gw-for-src=%s (must be same): " "not equal to gw-for-src=%s (must be same): "
"%s\n", "%s\n",
ip6_sprintf(ip6buf, &nh_ext.nh_addr), ip6_sprintf(ip6buf, &nh6.nh_addr),
icmp6_redirect_diag(&src6, &reddst6, &redtgt6))); icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
goto bad; goto bad;
} }
@ -2526,6 +2535,9 @@ icmp6_redirect_output(struct mbuf *m0, struct rtentry *rt)
u_char *p; u_char *p;
struct ifnet *outif = NULL; struct ifnet *outif = NULL;
struct sockaddr_in6 src_sa; struct sockaddr_in6 src_sa;
struct route_info ri;
struct nhop6_basic nh6;
int e;
icmp6_errcount(ND_REDIRECT, 0); icmp6_errcount(ND_REDIRECT, 0);
@ -2784,9 +2796,14 @@ noredhdropt:;
m_tag_prepend(m, mtag); m_tag_prepend(m, mtag);
} }
memset(&ri, 0, sizeof(ri));
memset(&nh6, 0, sizeof(nh6));
ri.ri_nh_info = (struct nhopu_basic *)&nh6;
/* send the packet to outside... */ /* send the packet to outside... */
ip6_output(m, NULL, NULL, 0, NULL, &outif, NULL); e = ip6_output(m, NULL, &ri, 0, NULL, NULL);
if (outif) { if (e == 0) {
outif = nh6.nh_ifp;
icmp6_ifstat_inc(outif, ifs6_out_msg); icmp6_ifstat_inc(outif, ifs6_out_msg);
icmp6_ifstat_inc(outif, ifs6_out_redirect); icmp6_ifstat_inc(outif, ifs6_out_redirect);
} }

View File

@ -2000,6 +2000,33 @@ in6_prefixlen2mask(struct in6_addr *maskp, int len)
maskp->s6_addr[bytelen] = maskarray[bitlen - 1]; maskp->s6_addr[bytelen] = maskarray[bitlen - 1];
} }
int
in6_ifawithifp_lla(struct ifnet *ifp, struct in6_addr *dst)
{
struct ifaddr *ifa;
struct in6_ifaddr *ifa6;
struct in6_addr a6;
KASSERT(IN6_IS_SCOPE_LINKLOCAL(dst), ("Non-linklocal address"));
a6 = *dst;
in6_setllascope(&a6, ifp);
IF_ADDR_RLOCK(ifp);
TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
if (ifa->ifa_addr->sa_family != AF_INET6)
continue;
ifa6 = (struct in6_ifaddr *)ifa;
if (IN6_ARE_ADDR_EQUAL(&a6, &ifa6->ia_addr.sin6_addr)) {
IF_ADDR_RUNLOCK(ifp);
return (1);
}
}
IF_ADDR_RUNLOCK(ifp);
return (0);
}
/* /*
* return the best address out of the same scope. if no address was * return the best address out of the same scope. if no address was
* found, return the first valid address from designated IF. * found, return the first valid address from designated IF.

View File

@ -653,6 +653,7 @@ int in6_localaddr(struct in6_addr *);
int in6_localip(struct in6_addr *); int in6_localip(struct in6_addr *);
int in6_addrscope(const struct in6_addr *); int in6_addrscope(const struct in6_addr *);
struct in6_ifaddr *in6_ifawithifp(struct ifnet *, struct in6_addr *); struct in6_ifaddr *in6_ifawithifp(struct ifnet *, struct in6_addr *);
int in6_ifawithifp_lla(struct ifnet *, struct in6_addr *);
extern void in6_if_up(struct ifnet *); extern void in6_if_up(struct ifnet *);
struct sockaddr; struct sockaddr;
extern u_char ip6_protox[]; extern u_char ip6_protox[];

View File

@ -273,9 +273,9 @@ in6_gif_output(struct ifnet *ifp,
* it is too painful to ask for resend of inner packet, to achieve * it is too painful to ask for resend of inner packet, to achieve
* path MTU discovery for encapsulated packets. * path MTU discovery for encapsulated packets.
*/ */
error = ip6_output(m, 0, &sc->gif_ro6, IPV6_MINMTU, 0, NULL, NULL); error = ip6_output(m, NULL, NULL, IPV6_MINMTU, NULL, NULL);
#else #else
error = ip6_output(m, 0, &sc->gif_ro6, 0, 0, NULL, NULL); error = ip6_output(m, NULL, NULL, 0, NULL, NULL);
#endif #endif
if (!(GIF2IFP(sc)->if_flags & IFF_LINK0) && if (!(GIF2IFP(sc)->if_flags & IFF_LINK0) &&

View File

@ -328,7 +328,6 @@ in6_pcbladdr(register struct inpcb *inp, struct sockaddr *nam,
{ {
register struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)nam; register struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)nam;
int error = 0; int error = 0;
struct ifnet *ifp = NULL;
int scope_ambiguous = 0; int scope_ambiguous = 0;
struct in6_addr in6a; struct in6_addr in6a;
@ -358,16 +357,11 @@ in6_pcbladdr(register struct inpcb *inp, struct sockaddr *nam,
if ((error = prison_remote_ip6(inp->inp_cred, &sin6->sin6_addr)) != 0) if ((error = prison_remote_ip6(inp->inp_cred, &sin6->sin6_addr)) != 0)
return (error); return (error);
error = in6_selectsrc(sin6, inp->in6p_outputopts, error = in6_selectsrc_scope(sin6, inp->in6p_outputopts,
inp, NULL, inp->inp_cred, &ifp, &in6a); inp, inp->inp_cred, scope_ambiguous, &in6a);
if (error) if (error)
return (error); return (error);
if (ifp && scope_ambiguous &&
(error = in6_setscope(&sin6->sin6_addr, ifp, NULL)) != 0) {
return(error);
}
/* /*
* Do not update this earlier, in case we return with an error. * Do not update this earlier, in case we return with an error.
* *

View File

@ -130,12 +130,11 @@ static VNET_DEFINE(struct in6_addrpolicy, defaultaddrpolicy);
VNET_DEFINE(int, ip6_prefer_tempaddr) = 0; VNET_DEFINE(int, ip6_prefer_tempaddr) = 0;
static int selectroute(struct sockaddr_in6 *, struct ip6_pktopts *, static int in6_selectsrc(uint32_t fibnum, struct sockaddr_in6 *dstsock,
struct ip6_moptions *, struct route_in6 *, struct ifnet **, struct ip6_pktopts *opts, struct inpcb *inp, struct ucred *cred,
struct rtentry **, int, u_int); struct ifnet **ifpp, struct in6_addr *srcp);
static int in6_selectif(struct sockaddr_in6 *, struct ip6_pktopts *, static int in6_selectif(uint32_t fibnum, struct sockaddr_in6 *,
struct ip6_moptions *, struct route_in6 *ro, struct ifnet **, struct ip6_pktopts *, struct ip6_moptions *, struct ifnet **);
struct ifnet *, u_int);
static struct in6_addrpolicy *lookup_addrsel_policy(struct sockaddr_in6 *); static struct in6_addrpolicy *lookup_addrsel_policy(struct sockaddr_in6 *);
@ -147,6 +146,74 @@ static int walk_addrsel_policy(int (*)(struct in6_addrpolicy *, void *),
static int dump_addrsel_policyent(struct in6_addrpolicy *, void *); static int dump_addrsel_policyent(struct in6_addrpolicy *, void *);
static struct in6_addrpolicy *match_addrsel_policy(struct sockaddr_in6 *); static struct in6_addrpolicy *match_addrsel_policy(struct sockaddr_in6 *);
/*
* Selects source address.
* Alters destination scope address if @update_scope is not 0.
* Stores selected address to @srcp.
* Returns 0 on success.
*
* Used by socket-based consumers.
*/
int
in6_selectsrc_scope(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
struct inpcb *inp, struct ucred *cred, int update_scope,
struct in6_addr *srcp)
{
struct ifnet *retifp;
uint32_t fibnum;
int error;
fibnum = (inp != NULL) ? inp->inp_inc.inc_fibnum : RT_DEFAULT_FIB;
retifp = NULL;
error = in6_selectsrc(fibnum, dstsock, opts, inp, cred, &retifp, srcp);
if (error != 0)
return (error);
if (retifp == NULL || update_scope == 0)
return (0);
/*
* 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, retifp, NULL);
return (error);
}
/*
* Select source address based on @fibnum, @dst and @scopeid.
* Stores selected address to @srcp.
* Returns 0 on success.
*
* Used by non-socket based consumers (ND code mostly).
*/
int
in6_selectsrc_addr(uint32_t fibnum, struct in6_addr *dst, uint32_t scopeid,
struct in6_addr *srcp)
{
struct ifnet *retifp;
struct sockaddr_in6 dst_sa;
int error;
retifp = NULL;
memset(&dst_sa, 0, sizeof(dst_sa));
dst_sa.sin6_family = AF_INET6;
dst_sa.sin6_len = sizeof(dst_sa);
dst_sa.sin6_addr = *dst;
dst_sa.sin6_scope_id = scopeid;
sa6_embedscope(&dst_sa, 0);
error = in6_selectsrc(fibnum, &dst_sa, NULL, NULL, NULL, &retifp, srcp);
return (error);
}
/* /*
* Return an IPv6 address, which is the most appropriate for a given * Return an IPv6 address, which is the most appropriate for a given
* destination and user specified options. * destination and user specified options.
@ -175,9 +242,9 @@ static struct in6_addrpolicy *match_addrsel_policy(struct sockaddr_in6 *);
goto out; /* XXX: we can't use 'break' here */ \ goto out; /* XXX: we can't use 'break' here */ \
} while(0) } while(0)
int static int
in6_selectsrc(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, in6_selectsrc(uint32_t fibnum, struct sockaddr_in6 *dstsock,
struct inpcb *inp, struct route_in6 *ro, struct ucred *cred, struct ip6_pktopts *opts, struct inpcb *inp, struct ucred *cred,
struct ifnet **ifpp, struct in6_addr *srcp) struct ifnet **ifpp, struct in6_addr *srcp)
{ {
struct in6_addr dst, tmp; struct in6_addr dst, tmp;
@ -210,9 +277,8 @@ in6_selectsrc(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
if (inp != NULL) { if (inp != NULL) {
INP_LOCK_ASSERT(inp); INP_LOCK_ASSERT(inp);
mopts = inp->in6p_moptions; mopts = inp->in6p_moptions;
} else { } else
mopts = NULL; mopts = NULL;
}
/* /*
* If the source address is explicitly specified by the caller, * If the source address is explicitly specified by the caller,
@ -226,9 +292,8 @@ in6_selectsrc(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
struct in6_ifaddr *ia6; struct in6_ifaddr *ia6;
/* get the outgoing interface */ /* get the outgoing interface */
if ((error = in6_selectif(dstsock, opts, mopts, ro, &ifp, oifp, error = in6_selectif(fibnum, dstsock, opts, mopts, &ifp);
(inp != NULL) ? inp->inp_inc.inc_fibnum : RT_DEFAULT_FIB)) if (error != 0)
!= 0)
return (error); return (error);
/* /*
@ -292,8 +357,7 @@ in6_selectsrc(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
* the outgoing interface and the destination address. * the outgoing interface and the destination address.
*/ */
/* get the outgoing interface */ /* get the outgoing interface */
if ((error = in6_selectif(dstsock, opts, mopts, ro, &ifp, oifp, if ((error = in6_selectif(fibnum, dstsock, opts, mopts, &ifp)) != 0)
(inp != NULL) ? inp->inp_inc.inc_fibnum : RT_DEFAULT_FIB)) != 0)
return (error); return (error);
#ifdef DIAGNOSTIC #ifdef DIAGNOSTIC
@ -544,45 +608,30 @@ in6_selectsrc(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
} }
/* /*
* clone - meaningful only for bsdi and freebsd * Selects route based on fib/dst and numerous forwarding altering options.
*
*/ */
static int int
selectroute(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, fib6_selectroute(uint32_t fibnum, struct in6_addr *dst, uint32_t scopeid,
struct ip6_moptions *mopts, struct route_in6 *ro, struct nhop_prepend *nh_src, struct mbuf *m,
struct ifnet **retifp, struct rtentry **retrt, int norouteok, u_int fibnum) struct ip6_pktopts *opts, struct ip6_moptions *mopts,
struct nhop_prepend *nh)
{ {
int error = 0; int error = 0;
int fill_nhop;
struct ifnet *ifp = NULL; struct ifnet *ifp = NULL;
struct rtentry *rt = NULL;
struct sockaddr_in6 *sin6_next; struct sockaddr_in6 *sin6_next;
struct in6_pktinfo *pi = NULL; struct in6_pktinfo *pi = NULL;
struct in6_addr *dst = &dstsock->sin6_addr;
#if 0
char ip6buf[INET6_ADDRSTRLEN];
if (dstsock->sin6_addr.s6_addr32[0] == 0 &&
dstsock->sin6_addr.s6_addr32[1] == 0 &&
!IN6_IS_ADDR_LOOPBACK(&dstsock->sin6_addr)) {
printf("in6_selectroute: strange destination %s\n",
ip6_sprintf(ip6buf, &dstsock->sin6_addr));
} else {
printf("in6_selectroute: destination = %s%%%d\n",
ip6_sprintf(ip6buf, &dstsock->sin6_addr),
dstsock->sin6_scope_id); /* for debug */
}
#endif
fill_nhop = 0;
/* If the caller specify the outgoing interface explicitly, use it. */ /* If the caller specify the outgoing interface explicitly, use it. */
if (opts && (pi = opts->ip6po_pktinfo) != NULL && pi->ipi6_ifindex) { if (opts && (pi = opts->ip6po_pktinfo) != NULL && pi->ipi6_ifindex) {
/* XXX boundary check is assumed to be already done. */ /* XXX boundary check is assumed to be already done. */
ifp = ifnet_byindex(pi->ipi6_ifindex); ifp = ifnet_byindex(pi->ipi6_ifindex);
if (ifp != NULL && if (ifp != NULL && IN6_IS_ADDR_MULTICAST(dst)) {
(norouteok || retrt == NULL || fill_nhop = 1;
IN6_IS_ADDR_MULTICAST(dst))) {
/*
* we do not have to check or get the route for
* multicast.
*/
goto done; goto done;
} else } else
goto getroute; goto getroute;
@ -594,6 +643,7 @@ selectroute(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
*/ */
if (IN6_IS_ADDR_MULTICAST(dst) && if (IN6_IS_ADDR_MULTICAST(dst) &&
mopts != NULL && (ifp = mopts->im6o_multicast_ifp) != NULL) { mopts != NULL && (ifp = mopts->im6o_multicast_ifp) != NULL) {
fill_nhop = 1;
goto done; /* we do not need a route for multicast. */ goto done; /* we do not need a route for multicast. */
} }
@ -603,8 +653,6 @@ selectroute(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
* use it as the gateway. * use it as the gateway.
*/ */
if (opts && opts->ip6po_nexthop) { if (opts && opts->ip6po_nexthop) {
struct route_in6 *ron;
struct llentry *la;
sin6_next = satosin6(opts->ip6po_nexthop); sin6_next = satosin6(opts->ip6po_nexthop);
@ -614,268 +662,197 @@ selectroute(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
goto done; goto done;
} }
in6_splitscope(&sin6_next->sin6_addr, dst, &scopeid);
if (fib6_lookup_prepend(fibnum, dst, scopeid, m, nh, NULL) != 0) {
error = EHOSTUNREACH;
goto done;
}
/*
* If the next hop is an IPv6 address, then the node identified
* by that address must be a neighbor of the sending host.
*/
if ((nh->nh_flags & (NHF_GATEWAY|NHF_BLACKHOLE|NHF_REJECT))
!= 0) {
error = EHOSTUNREACH;
goto done;
}
goto done;
}
/* Do route lookup */
if (nh_src == NULL) {
error = fib6_lookup_prepend(fibnum, dst, scopeid, m, nh, NULL);
if (error != 0) {
error = EHOSTUNREACH;
goto done;
}
} else
fib6_choose_prepend(fibnum, nh_src, m->m_pkthdr.flowid, nh,
NULL);
/* Explicitly free nhop here to be consistent with returned results */
fib6_free_nh_prepend(fibnum, nh);
/*
* Check if the outgoing interface conflicts with
* the interface specified by ipi6_ifindex (if specified).
* Note that loopback interface is always okay.
* (this may happen when we are sending a packet to one of
* our own addresses.)
* XXX-ME: this can be simplified by using aifp index.
*/
if (pi != NULL && pi->ipi6_ifindex != 0) {
ifp = NH_LIFP(nh);
if (!(ifp->if_flags & IFF_LOOPBACK) &&
ifp->if_index != pi->ipi6_ifindex) {
error = EHOSTUNREACH;
goto done;
}
}
done:
if (error == EHOSTUNREACH)
IP6STAT_INC(ip6s_noroute);
if (error != 0)
return (error);
if (fill_nhop == 0)
return (0);
/*
* we do not have to check or get the route for
* multicast. However, we need to fill in @nh info
*/
memset(nh, 0, sizeof(*nh));
nh->nh_flags = NHF_L2_INCOMPLETE;
nh->nh_count = 0;
nh->spare0 = 0;
nh->nh_mtu = IN6_LINKMTU(ifp);
nh->lifp_idx = ifp->if_index;
nh->i.ifp_idx = ifp->if_index;
nh->aifp_idx = ifp->if_index;
nh->d.gw6 = *dst;
/* In future, we will need to do some sort of refcounting */
return (0);
}
static int
in6_selectif(uint32_t fibnum, struct sockaddr_in6 *dstsock,
struct ip6_pktopts *opts, struct ip6_moptions *mopts,
struct ifnet **retifp)
{
int error = 0;
struct ifnet *ifp = NULL;
struct sockaddr_in6 *sin6_next;
struct in6_pktinfo *pi = NULL;
struct in6_addr dst;
uint32_t scopeid;
struct nhop6_basic nh6;
/* If the caller specify the outgoing interface explicitly, use it. */
if (opts && (pi = opts->ip6po_pktinfo) != NULL && pi->ipi6_ifindex) {
/* XXX boundary check is assumed to be already done. */
ifp = ifnet_byindex(pi->ipi6_ifindex);
if (ifp != NULL)
goto done;
else
goto getroute;
}
in6_splitscope(&dstsock->sin6_addr, &dst, &scopeid);
/*
* If the destination address is a multicast address and the outgoing
* interface for the address is specified by the caller, use it.
*/
if (IN6_IS_ADDR_MULTICAST(&dst) &&
mopts != NULL && (ifp = mopts->im6o_multicast_ifp) != NULL) {
goto done; /* we do not need a route for multicast. */
}
getroute:
/*
* If the next hop address for the packet is specified by the caller,
* use it as the gateway.
*/
if (opts && opts->ip6po_nexthop) {
sin6_next = satosin6(opts->ip6po_nexthop);
/* at this moment, we only support AF_INET6 next hops */
if (sin6_next->sin6_family != AF_INET6) {
error = EAFNOSUPPORT; /* or should we proceed? */
goto done;
}
/* /*
* If the next hop is an IPv6 address, then the node identified * If the next hop is an IPv6 address, then the node identified
* by that address must be a neighbor of the sending host. * by that address must be a neighbor of the sending host.
* XXX: Embedded form?
*/ */
ron = &opts->ip6po_nextroute; in6_splitscope(&sin6_next->sin6_addr, &dst, &scopeid);
/* if (fib6_lookup_nh_basic(fibnum, &dst, scopeid, 0, &nh6) != 0) {
* XXX what do we do here?
* PLZ to be fixing
*/
if (ron->ro_rt == NULL) {
in6_rtalloc(ron, fibnum); /* multi path case? */
if (ron->ro_rt == NULL) {
/* XXX-BZ WT.? */
if (ron->ro_rt) {
RTFREE(ron->ro_rt);
ron->ro_rt = NULL;
}
error = EHOSTUNREACH;
goto done;
}
}
rt = ron->ro_rt;
ifp = rt->rt_ifp;
IF_AFDATA_RLOCK(ifp);
la = lla_lookup(LLTABLE6(ifp), 0, (struct sockaddr *)sin6_next);
IF_AFDATA_RUNLOCK(ifp);
if (la != NULL)
LLE_RUNLOCK(la);
else {
error = EHOSTUNREACH; error = EHOSTUNREACH;
goto done; goto done;
} }
#if 0
if ((ron->ro_rt && if ((nh6.nh_flags & (NHF_GATEWAY|NHF_BLACKHOLE|NHF_REJECT))
(ron->ro_rt->rt_flags & (RTF_UP | RTF_LLINFO)) != != 0) {
(RTF_UP | RTF_LLINFO)) || error = EHOSTUNREACH;
!IN6_ARE_ADDR_EQUAL(&satosin6(&ron->ro_dst)->sin6_addr, goto done;
&sin6_next->sin6_addr)) {
if (ron->ro_rt) {
RTFREE(ron->ro_rt);
ron->ro_rt = NULL;
}
*satosin6(&ron->ro_dst) = *sin6_next;
} }
if (ron->ro_rt == NULL) {
in6_rtalloc(ron, fibnum); /* multi path case? */
if (ron->ro_rt == NULL ||
!(ron->ro_rt->rt_flags & RTF_LLINFO)) {
if (ron->ro_rt) {
RTFREE(ron->ro_rt);
ron->ro_rt = NULL;
}
error = EHOSTUNREACH;
goto done;
}
}
#endif
/* /*
* When cloning is required, try to allocate a route to the * XXX-ME shouldn't we check ip6po_pktinfo index here as well?
* destination so that the caller can store path MTU
* information.
*/ */
goto done; goto done;
} }
/* /* Do route lookup */
* Use a cached route if it exists and is valid, else try to allocate if (fib6_lookup_nh_basic(fibnum, &dst, scopeid, 0, &nh6) != 0) {
* a new one. Note that we should check the address family of the
* cached destination, in case of sharing the cache with IPv4.
*/
if (ro) {
if (ro->ro_rt &&
(!(ro->ro_rt->rt_flags & RTF_UP) ||
((struct sockaddr *)(&ro->ro_dst))->sa_family != AF_INET6 ||
!IN6_ARE_ADDR_EQUAL(&satosin6(&ro->ro_dst)->sin6_addr,
dst))) {
RTFREE(ro->ro_rt);
ro->ro_rt = (struct rtentry *)NULL;
}
if (ro->ro_rt == (struct rtentry *)NULL) {
struct sockaddr_in6 *sa6;
/* No route yet, so try to acquire one */
bzero(&ro->ro_dst, sizeof(struct sockaddr_in6));
sa6 = (struct sockaddr_in6 *)&ro->ro_dst;
*sa6 = *dstsock;
sa6->sin6_scope_id = 0;
#ifdef RADIX_MPATH
rtalloc_mpath_fib((struct route *)ro,
ntohl(sa6->sin6_addr.s6_addr32[3]), fibnum);
#else
ro->ro_rt = in6_rtalloc1((struct sockaddr *)
&ro->ro_dst, 0, 0UL, fibnum);
if (ro->ro_rt)
RT_UNLOCK(ro->ro_rt);
#endif
}
/*
* do not care about the result if we have the nexthop
* explicitly specified.
*/
if (opts && opts->ip6po_nexthop)
goto done;
if (ro->ro_rt) {
ifp = ro->ro_rt->rt_ifp;
if (ifp == NULL) { /* can this really happen? */
RTFREE(ro->ro_rt);
ro->ro_rt = NULL;
}
}
if (ro->ro_rt == NULL)
error = EHOSTUNREACH;
rt = ro->ro_rt;
/*
* Check if the outgoing interface conflicts with
* the interface specified by ipi6_ifindex (if specified).
* Note that loopback interface is always okay.
* (this may happen when we are sending a packet to one of
* our own addresses.)
*/
if (ifp && opts && opts->ip6po_pktinfo &&
opts->ip6po_pktinfo->ipi6_ifindex) {
if (!(ifp->if_flags & IFF_LOOPBACK) &&
ifp->if_index !=
opts->ip6po_pktinfo->ipi6_ifindex) {
error = EHOSTUNREACH;
goto done;
}
}
}
done:
if (ifp == NULL && rt == NULL) {
/*
* This can happen if the caller did not pass a cached route
* nor any other hints. We treat this case an error.
*/
error = EHOSTUNREACH; error = EHOSTUNREACH;
goto done;
} }
ifp = nh6.nh_ifp;
/*
* Check if the outgoing interface conflicts with
* the interface specified by ipi6_ifindex (if specified).
* Note that loopback interface is always okay.
* (this may happen when we are sending a packet to one of
* our own addresses.)
*
* XXX: basic_ means we return "proper" interface address.
*
*/
if (opts && opts->ip6po_pktinfo && opts->ip6po_pktinfo->ipi6_ifindex) {
if (!(ifp->if_flags & IFF_LOOPBACK) &&
ifp->if_index != opts->ip6po_pktinfo->ipi6_ifindex) {
error = EHOSTUNREACH;
goto done;
}
}
/* do not use a rejected or black hole route. */
if ((nh6.nh_flags & (NHF_BLACKHOLE|NHF_REJECT)) != 0)
error = EHOSTUNREACH;
done:
if (error == EHOSTUNREACH) if (error == EHOSTUNREACH)
IP6STAT_INC(ip6s_noroute); IP6STAT_INC(ip6s_noroute);
if (retifp != NULL) { *retifp = ifp;
*retifp = ifp;
/*
* Adjust the "outgoing" interface. If we're going to loop
* the packet back to ourselves, the ifp would be the loopback
* interface. However, we'd rather know the interface associated
* to the destination address (which should probably be one of
* our own addresses.)
*/
if (rt) {
if ((rt->rt_ifp->if_flags & IFF_LOOPBACK) &&
(rt->rt_gateway->sa_family == AF_LINK))
*retifp =
ifnet_byindex(((struct sockaddr_dl *)
rt->rt_gateway)->sdl_index);
}
}
if (retrt != NULL)
*retrt = rt; /* rt may be NULL */
return (error); return (error);
} }
static int
in6_selectif(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
struct ip6_moptions *mopts, struct route_in6 *ro, struct ifnet **retifp,
struct ifnet *oifp, u_int fibnum)
{
int error;
struct route_in6 sro;
struct rtentry *rt = NULL;
KASSERT(retifp != NULL, ("%s: retifp is NULL", __func__));
if (ro == NULL) {
bzero(&sro, sizeof(sro));
ro = &sro;
}
if ((error = selectroute(dstsock, opts, mopts, ro, retifp,
&rt, 1, fibnum)) != 0) {
if (ro == &sro && rt && rt == sro.ro_rt)
RTFREE(rt);
/* Help ND. See oifp comment in in6_selectsrc(). */
if (oifp != NULL && fibnum == RT_DEFAULT_FIB) {
*retifp = oifp;
error = 0;
}
return (error);
}
/*
* do not use a rejected or black hole route.
* XXX: this check should be done in the L2 output routine.
* However, if we skipped this check here, we'd see the following
* scenario:
* - install a rejected route for a scoped address prefix
* (like fe80::/10)
* - send a packet to a destination that matches the scoped prefix,
* with ambiguity about the scope zone.
* - pick the outgoing interface from the route, and disambiguate the
* scope zone with the interface.
* - ip6_output() would try to get another route with the "new"
* destination, which may be valid.
* - we'd see no error on output.
* Although this may not be very harmful, it should still be confusing.
* We thus reject the case here.
*/
if (rt && (rt->rt_flags & (RTF_REJECT | RTF_BLACKHOLE))) {
int flags = (rt->rt_flags & RTF_HOST ? EHOSTUNREACH : ENETUNREACH);
if (ro == &sro && rt && rt == sro.ro_rt)
RTFREE(rt);
return (flags);
}
if (ro == &sro && rt && rt == sro.ro_rt)
RTFREE(rt);
return (0);
}
/*
* Public wrapper function to selectroute().
*
* XXX-BZ in6_selectroute() should and will grow the FIB argument. The
* in6_selectroute_fib() function is only there for backward compat on stable.
*/
int
in6_selectroute(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
struct ip6_moptions *mopts, struct route_in6 *ro,
struct ifnet **retifp, struct rtentry **retrt)
{
return (selectroute(dstsock, opts, mopts, ro, retifp,
retrt, 0, RT_DEFAULT_FIB));
}
#ifndef BURN_BRIDGES
int
in6_selectroute_fib(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
struct ip6_moptions *mopts, struct route_in6 *ro,
struct ifnet **retifp, struct rtentry **retrt, u_int fibnum)
{
return (selectroute(dstsock, opts, mopts, ro, retifp,
retrt, 0, fibnum));
}
#endif
/* /*
* Default hop limit selection. The precedence is as follows: * Default hop limit selection. The precedence is as follows:
* 1. Hoplimit value specified via ioctl. * 1. Hoplimit value specified via ioctl.
@ -898,7 +875,7 @@ in6_selecthlim(struct inpcb *in6p, struct ifnet *ifp)
fibnum = in6p->inp_inc.inc_fibnum; fibnum = in6p->inp_inc.inc_fibnum;
if (fib6_lookup_nh_ext(fibnum, in6p->in6p_faddr, 0, 0, if (fib6_lookup_nh_ext(fibnum, &in6p->in6p_faddr, 0, 0,
NHOP_LOOKUP_REF, &nh_ext) == 0) { NHOP_LOOKUP_REF, &nh_ext) == 0) {
hlim = ND_IFINFO(nh_ext.nh_ifp)->chlim; hlim = ND_IFINFO(nh_ext.nh_ifp)->chlim;
fib6_free_nh_ext(fibnum, &nh_ext); fib6_free_nh_ext(fibnum, &nh_ext);

View File

@ -1550,7 +1550,7 @@ phyint_send(struct ip6_hdr *ip6, struct mif6 *mifp, struct mbuf *m)
im6o.im6o_multicast_hlim = ip6->ip6_hlim; im6o.im6o_multicast_hlim = ip6->ip6_hlim;
im6o.im6o_multicast_loop = 1; im6o.im6o_multicast_loop = 1;
error = ip6_output(mb_copy, NULL, NULL, IPV6_FORWARDING, &im6o, error = ip6_output(mb_copy, NULL, NULL, IPV6_FORWARDING, &im6o,
NULL, NULL); NULL);
MRT6_DLOG(DEBUG_XMIT, "mif %u err %d", MRT6_DLOG(DEBUG_XMIT, "mif %u err %d",
(uint16_t)(mifp - mif6table), error); (uint16_t)(mifp - mif6table), error);
@ -1568,7 +1568,7 @@ phyint_send(struct ip6_hdr *ip6, struct mif6 *mifp, struct mbuf *m)
dst6.sin6_len = sizeof(struct sockaddr_in6); dst6.sin6_len = sizeof(struct sockaddr_in6);
dst6.sin6_family = AF_INET6; dst6.sin6_family = AF_INET6;
dst6.sin6_addr = ip6->ip6_dst; dst6.sin6_addr = ip6->ip6_dst;
ip6_mloopback(ifp, m, &dst6); ip6_mloopback(ifp, m, AF_INET6);
} }
/* /*

View File

@ -105,6 +105,8 @@ __FBSDID("$FreeBSD$");
#include <netinet6/nd6.h> #include <netinet6/nd6.h>
#include <netinet/in_rss.h> #include <netinet/in_rss.h>
#include <net/rt_nhops.h>
#ifdef IPSEC #ifdef IPSEC
#include <netipsec/ipsec.h> #include <netipsec/ipsec.h>
#include <netipsec/ipsec6.h> #include <netipsec/ipsec6.h>
@ -146,8 +148,6 @@ static int ip6_insertfraghdr(struct mbuf *, struct mbuf *, int,
struct ip6_frag **); struct ip6_frag **);
static int ip6_insert_jumboopt(struct ip6_exthdrs *, u_int32_t); static int ip6_insert_jumboopt(struct ip6_exthdrs *, u_int32_t);
static int ip6_splithdr(struct mbuf *, struct ip6_exthdrs *); static int ip6_splithdr(struct mbuf *, struct ip6_exthdrs *);
static int ip6_getpmtu(struct route_in6 *, struct route_in6 *,
struct ifnet *, struct in6_addr *, u_long *, int *, u_int);
static int copypktopts(struct ip6_pktopts *, struct ip6_pktopts *, int); static int copypktopts(struct ip6_pktopts *, struct ip6_pktopts *, int);
@ -224,41 +224,38 @@ in6_delayed_cksum(struct mbuf *m, uint32_t plen, u_short offset)
* type of "mtu": rt_mtu is u_long, ifnet.ifr_mtu is int, and * type of "mtu": rt_mtu is u_long, ifnet.ifr_mtu is int, and
* nd_ifinfo.linkmtu is u_int32_t. so we use u_long to hold largest one, * nd_ifinfo.linkmtu is u_int32_t. so we use u_long to hold largest one,
* which is rt_mtu. * which is rt_mtu.
*
* ifpp - XXX: just for statistics
*/ */
/* /*
* XXX TODO: no flowid is assigned for outbound flows? * XXX TODO: no flowid is assigned for outbound flows?
*/ */
int int
ip6_output(struct mbuf *m0, struct ip6_pktopts *opt, ip6_output(struct mbuf *m0, struct ip6_pktopts *opt,
struct route_in6 *ro, int flags, struct ip6_moptions *im6o, struct route_info *ri, int flags, struct ip6_moptions *im6o,
struct ifnet **ifpp, struct inpcb *inp) struct inpcb *inp)
{ {
struct ip6_hdr *ip6, *mhip6; struct ip6_hdr *ip6, *mhip6;
struct ifnet *ifp, *origifp; struct ifnet *ifp, *origifp;
struct mbuf *m = m0; struct mbuf *m = m0;
struct mbuf *mprev = NULL; struct mbuf *mprev = NULL;
int hlen, tlen, len, off; int hlen, tlen, len, off;
struct route_in6 ip6route; struct nhop_prepend local_nh, *nh;
struct rtentry *rt = NULL; struct sockaddr_in6 src_sa, dst_sa;
struct sockaddr_in6 *dst, src_sa, dst_sa; struct in6_addr dst, odst;
struct in6_addr odst;
int error = 0; int error = 0;
struct in6_ifaddr *ia = NULL;
u_long mtu; u_long mtu;
int alwaysfrag, dontfrag; int alwaysfrag, dontfrag;
u_int32_t optlen = 0, plen = 0, unfragpartlen = 0; u_int32_t optlen = 0, plen = 0, unfragpartlen = 0;
struct ip6_exthdrs exthdrs; struct ip6_exthdrs exthdrs;
struct in6_addr finaldst, src0, dst0; struct in6_addr src0, dst0;
u_int32_t zone; u_int32_t zone;
struct route_in6 *ro_pmtu = NULL;
int hdrsplit = 0; int hdrsplit = 0;
int sw_csum, tso; int sw_csum, tso;
uint32_t scopeid;
int needfiblookup; int needfiblookup;
uint32_t fibnum; uint32_t fibnum;
struct m_tag *fwd_tag = NULL; struct m_tag *fwd_tag = NULL;
nh = NULL;
ip6 = mtod(m, struct ip6_hdr *); ip6 = mtod(m, struct ip6_hdr *);
if (ip6 == NULL) { if (ip6 == NULL) {
printf ("ip6 is NULL"); printf ("ip6 is NULL");
@ -274,7 +271,6 @@ ip6_output(struct mbuf *m0, struct ip6_pktopts *opt,
} }
} }
finaldst = ip6->ip6_dst;
bzero(&exthdrs, sizeof(exthdrs)); bzero(&exthdrs, sizeof(exthdrs));
if (opt) { if (opt) {
/* Hop-by-Hop options header */ /* Hop-by-Hop options header */
@ -435,6 +431,7 @@ ip6_output(struct mbuf *m0, struct ip6_pktopts *opt,
IP6STAT_INC(ip6s_localout); IP6STAT_INC(ip6s_localout);
#if 0
/* /*
* Route packet. * Route packet.
*/ */
@ -443,14 +440,15 @@ ip6_output(struct mbuf *m0, struct ip6_pktopts *opt,
bzero((caddr_t)ro, sizeof(*ro)); bzero((caddr_t)ro, sizeof(*ro));
} }
ro_pmtu = ro; ro_pmtu = ro;
if (opt && opt->ip6po_rthdr) #endif
ro = &opt->ip6po_route;
dst = (struct sockaddr_in6 *)&ro->ro_dst;
#ifdef FLOWTABLE #ifdef FLOWTABLE
if (ro->ro_rt == NULL) if (ro->ro_rt == NULL)
(void )flowtable_lookup(AF_INET6, m, (struct route *)ro); (void )flowtable_lookup(AF_INET6, m, (struct route *)ro);
#endif #endif
fibnum = (inp != NULL) ? inp->inp_inc.inc_fibnum : M_GETFIB(m); fibnum = (inp != NULL) ? inp->inp_inc.inc_fibnum : M_GETFIB(m);
/* Make compiler happy */
memset(&dst, 0, sizeof(dst));
scopeid = 0;
again: again:
/* /*
* if specified, try to fill in the traffic class field. * if specified, try to fill in the traffic class field.
@ -481,32 +479,28 @@ ip6_output(struct mbuf *m0, struct ip6_pktopts *opt,
/* adjust pointer */ /* adjust pointer */
ip6 = mtod(m, struct ip6_hdr *); ip6 = mtod(m, struct ip6_hdr *);
if (ro->ro_rt && fwd_tag == NULL) { if (fwd_tag == NULL) {
rt = ro->ro_rt; in6_splitscope(&ip6->ip6_dst, &dst, &scopeid);
ifp = ro->ro_rt->rt_ifp;
} else {
if (fwd_tag == NULL) {
bzero(&dst_sa, sizeof(dst_sa));
dst_sa.sin6_family = AF_INET6;
dst_sa.sin6_len = sizeof(dst_sa);
dst_sa.sin6_addr = ip6->ip6_dst;
}
error = in6_selectroute_fib(&dst_sa, opt, im6o, ro, &ifp,
&rt, fibnum);
if (error != 0) {
if (ifp != NULL)
in6_ifstat_inc(ifp, ifs6_out_discard);
goto bad;
}
}
if (rt == NULL) {
/*
* If in6_selectroute() does not return a route entry,
* dst may not have been updated.
*/
*dst = dst_sa; /* XXX */
} }
error = fib6_selectroute(fibnum, &dst, scopeid, ri ? ri->ri_nh : NULL,
m, opt, im6o, &local_nh);
if (error != 0)
goto bad;
nh = &local_nh;
ifp = NH_LIFP(nh); /* logical transmit interface */
origifp = NH_AIFP(nh); /* ifp of address associated with route */
mtu = nh->nh_mtu;
/*
* Note the difference between @origifp and @ifp
* is in transmit-via-loopback case: if destination
* is our local address, @originifp will point to 'lo0' interface
* while @ifp will point to ifp which given address
* belongs to.
*/
/* /*
* then rt (for unicast) and ifp must be non-NULL valid values. * then rt (for unicast) and ifp must be non-NULL valid values.
*/ */
@ -514,17 +508,11 @@ ip6_output(struct mbuf *m0, struct ip6_pktopts *opt,
/* XXX: the FORWARDING flag can be set for mrouting. */ /* XXX: the FORWARDING flag can be set for mrouting. */
in6_ifstat_inc(ifp, ifs6_out_request); in6_ifstat_inc(ifp, ifs6_out_request);
} }
if (rt != NULL) {
ia = (struct in6_ifaddr *)(rt->rt_ifa);
counter_u64_add(rt->rt_pksent, 1);
}
/* /*
* The outgoing interface must be in the zone of source and * The outgoing interface must be in the zone of source and
* destination addresses. * destination addresses.
*/ */
origifp = ifp;
src0 = ip6->ip6_src; src0 = ip6->ip6_src;
if (in6_setscope(&src0, origifp, &zone)) if (in6_setscope(&src0, origifp, &zone))
@ -548,12 +536,6 @@ ip6_output(struct mbuf *m0, struct ip6_pktopts *opt,
goto badscope; goto badscope;
} }
/* We should use ia_ifp to support the case of
* sending packets to an address of our own.
*/
if (ia != NULL && ia->ia_ifp)
ifp = ia->ia_ifp;
/* scope check is done. */ /* scope check is done. */
goto routefound; goto routefound;
@ -565,19 +547,6 @@ ip6_output(struct mbuf *m0, struct ip6_pktopts *opt,
goto bad; goto bad;
routefound: routefound:
if (rt && !IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
if (opt && opt->ip6po_nextroute.ro_rt) {
/*
* The nexthop is explicitly specified by the
* application. We assume the next hop is an IPv6
* address.
*/
dst = (struct sockaddr_in6 *)opt->ip6po_nexthop;
}
else if ((rt->rt_flags & RTF_GATEWAY))
dst = (struct sockaddr_in6 *)rt->rt_gateway;
}
if (!IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { if (!IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
m->m_flags &= ~(M_BCAST | M_MCAST); /* just in case */ m->m_flags &= ~(M_BCAST | M_MCAST); /* just in case */
} else { } else {
@ -601,7 +570,7 @@ ip6_output(struct mbuf *m0, struct ip6_pktopts *opt,
* thus deferring a hash lookup and lock acquisition * thus deferring a hash lookup and lock acquisition
* at the expense of an m_copym(). * at the expense of an m_copym().
*/ */
ip6_mloopback(ifp, m, dst); ip6_mloopback(ifp, m, AF_INET6);
} else { } else {
/* /*
* If we are acting as a multicast router, perform * If we are acting as a multicast router, perform
@ -641,19 +610,35 @@ ip6_output(struct mbuf *m0, struct ip6_pktopts *opt,
m_freem(m); m_freem(m);
goto done; goto done;
} }
/* XXX: Check path MTU */
} }
/* /*
* Fill the outgoing inteface to tell the upper layer * Fill the outgoing inteface to tell the upper layer
* to increment per-interface statistics. * to increment per-interface statistics.
*/ */
#if 0
if (ifpp) if (ifpp)
*ifpp = ifp; *ifpp = ifp;
#endif
/* Determine path MTU. */
if ((error = ip6_getpmtu(ro_pmtu, ro, ifp, &finaldst, &mtu, /* Check path MTU. */
&alwaysfrag, fibnum)) != 0) alwaysfrag = 0;
goto bad; if (mtu < IPV6_MMTU) {
/*
* RFC2460 section 5, last paragraph:
* if we record ICMPv6 too big message with
* mtu < IPV6_MMTU, transmit packets sized IPV6_MMTU
* or smaller, with framgent header attached.
* (fragment header is needed regardless from the
* packet size, for translators to identify packets)
*/
alwaysfrag = 1;
mtu = IPV6_MMTU;
}
/* /*
* The caller of this function may specify to use the minimum MTU * The caller of this function may specify to use the minimum MTU
@ -756,12 +741,18 @@ ip6_output(struct mbuf *m0, struct ip6_pktopts *opt,
/* See if fib was changed by packet filter. */ /* See if fib was changed by packet filter. */
if (fibnum != M_GETFIB(m)) { if (fibnum != M_GETFIB(m)) {
m->m_flags |= M_SKIP_FIREWALL; m->m_flags |= M_SKIP_FIREWALL;
fibnum = M_GETFIB(m);
RO_RTFREE(ro);
needfiblookup = 1; needfiblookup = 1;
} }
if (needfiblookup) if (needfiblookup) {
if (nh != NULL) {
fib6_free_nh_prepend(fibnum, nh);
nh = NULL;
}
if (ri != NULL)
ri->ri_nh = NULL;
fibnum = M_GETFIB(m);
goto again; goto again;
}
/* See if local, if yes, send it to netisr. */ /* See if local, if yes, send it to netisr. */
if (m->m_flags & M_FASTFWD_OURS) { if (m->m_flags & M_FASTFWD_OURS) {
@ -782,11 +773,18 @@ ip6_output(struct mbuf *m0, struct ip6_pktopts *opt,
/* Or forward to some other address? */ /* Or forward to some other address? */
if ((m->m_flags & M_IP6_NEXTHOP) && if ((m->m_flags & M_IP6_NEXTHOP) &&
(fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL)) != NULL) { (fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL)) != NULL) {
dst = (struct sockaddr_in6 *)&ro->ro_dst;
bcopy((fwd_tag+1), &dst_sa, sizeof(struct sockaddr_in6)); bcopy((fwd_tag+1), &dst_sa, sizeof(struct sockaddr_in6));
in6_splitscope(&dst_sa.sin6_addr, &dst, &scopeid);
m->m_flags |= M_SKIP_FIREWALL; m->m_flags |= M_SKIP_FIREWALL;
m->m_flags &= ~M_IP6_NEXTHOP; m->m_flags &= ~M_IP6_NEXTHOP;
m_tag_delete(m, fwd_tag); m_tag_delete(m, fwd_tag);
if (nh != NULL) {
fib6_free_nh_prepend(fibnum, nh);
nh = NULL;
}
if (ri != NULL)
ri->ri_nh = NULL;
goto again; goto again;
} }
@ -835,6 +833,10 @@ ip6_output(struct mbuf *m0, struct ip6_pktopts *opt,
m->m_pkthdr.csum_flags &= ifp->if_hwassist; m->m_pkthdr.csum_flags &= ifp->if_hwassist;
tlen = m->m_pkthdr.len; tlen = m->m_pkthdr.len;
/* Save MTU */
if (ri != NULL)
ri->ri_mtu = mtu;
if ((opt && (opt->ip6po_flags & IP6PO_DONTFRAG)) || tso) if ((opt && (opt->ip6po_flags & IP6PO_DONTFRAG)) || tso)
dontfrag = 1; dontfrag = 1;
else else
@ -859,7 +861,9 @@ ip6_output(struct mbuf *m0, struct ip6_pktopts *opt,
mtu32 = (u_int32_t)mtu; mtu32 = (u_int32_t)mtu;
bzero(&ip6cp, sizeof(ip6cp)); bzero(&ip6cp, sizeof(ip6cp));
ip6cp.ip6c_cmdarg = (void *)&mtu32; ip6cp.ip6c_cmdarg = (void *)&mtu32;
pfctlinput2(PRC_MSGSIZE, (struct sockaddr *)&ro_pmtu->ro_dst, /* XXX-ME: what destination should we pass here ? */
dst_sa.sin6_addr = ip6->ip6_dst;
pfctlinput2(PRC_MSGSIZE, (struct sockaddr *)&dst_sa,
(void *)&ip6cp); (void *)&ip6cp);
error = EMSGSIZE; error = EMSGSIZE;
@ -881,7 +885,8 @@ ip6_output(struct mbuf *m0, struct ip6_pktopts *opt,
m->m_pkthdr.len); m->m_pkthdr.len);
ifa_free(&ia6->ia_ifa); ifa_free(&ia6->ia_ifa);
} }
error = nd6_output(ifp, origifp, m, dst, ro->ro_rt);
error = fib6_sendmbuf(ifp, origifp, m, nh);
goto done; goto done;
} }
@ -912,8 +917,6 @@ ip6_output(struct mbuf *m0, struct ip6_pktopts *opt,
* Must be able to put at least 8 bytes per fragment. * Must be able to put at least 8 bytes per fragment.
*/ */
hlen = unfragpartlen; hlen = unfragpartlen;
if (mtu > IPV6_MAXPACKET)
mtu = IPV6_MAXPACKET;
len = (mtu - hlen - sizeof(struct ip6_frag)) & ~7; len = (mtu - hlen - sizeof(struct ip6_frag)) & ~7;
if (len < 8) { if (len < 8) {
@ -1031,13 +1034,15 @@ ip6_output(struct mbuf *m0, struct ip6_pktopts *opt,
m0 = m->m_nextpkt; m0 = m->m_nextpkt;
m->m_nextpkt = 0; m->m_nextpkt = 0;
if (error == 0) { if (error == 0) {
#if 0
/* Record statistics for this interface address. */ /* Record statistics for this interface address. */
if (ia) { if (ia) {
counter_u64_add(ia->ia_ifa.ifa_opackets, 1); counter_u64_add(ia->ia_ifa.ifa_opackets, 1);
counter_u64_add(ia->ia_ifa.ifa_obytes, counter_u64_add(ia->ia_ifa.ifa_obytes,
m->m_pkthdr.len); m->m_pkthdr.len);
} }
error = nd6_output(ifp, origifp, m, dst, ro->ro_rt); #endif
error = fib6_sendmbuf(ifp, origifp, m, nh);
} else } else
m_freem(m); m_freem(m);
} }
@ -1046,10 +1051,8 @@ ip6_output(struct mbuf *m0, struct ip6_pktopts *opt,
IP6STAT_INC(ip6s_fragmented); IP6STAT_INC(ip6s_fragmented);
done: done:
if (ro == &ip6route) if (nh != NULL)
RO_RTFREE(ro); fib6_free_nh_prepend(fibnum, nh);
if (ro_pmtu == &ip6route)
RO_RTFREE(ro_pmtu);
return (error); return (error);
freehdrs: freehdrs:
@ -1218,86 +1221,6 @@ ip6_insertfraghdr(struct mbuf *m0, struct mbuf *m, int hlen,
return (0); return (0);
} }
static int
ip6_getpmtu(struct route_in6 *ro_pmtu, struct route_in6 *ro,
struct ifnet *ifp, struct in6_addr *dst, u_long *mtup,
int *alwaysfragp, u_int fibnum)
{
u_int32_t mtu = 0;
int alwaysfrag = 0;
int error = 0;
if (ro_pmtu != ro) {
/* The first hop and the final destination may differ. */
struct sockaddr_in6 *sa6_dst =
(struct sockaddr_in6 *)&ro_pmtu->ro_dst;
if (ro_pmtu->ro_rt &&
((ro_pmtu->ro_rt->rt_flags & RTF_UP) == 0 ||
!IN6_ARE_ADDR_EQUAL(&sa6_dst->sin6_addr, dst))) {
RTFREE(ro_pmtu->ro_rt);
ro_pmtu->ro_rt = (struct rtentry *)NULL;
}
if (ro_pmtu->ro_rt == NULL) {
bzero(sa6_dst, sizeof(*sa6_dst));
sa6_dst->sin6_family = AF_INET6;
sa6_dst->sin6_len = sizeof(struct sockaddr_in6);
sa6_dst->sin6_addr = *dst;
in6_rtalloc(ro_pmtu, fibnum);
}
}
if (ro_pmtu->ro_rt) {
u_int32_t ifmtu;
struct in_conninfo inc;
bzero(&inc, sizeof(inc));
inc.inc_flags |= INC_ISIPV6;
inc.inc6_faddr = *dst;
if (ifp == NULL)
ifp = ro_pmtu->ro_rt->rt_ifp;
ifmtu = IN6_LINKMTU(ifp);
mtu = tcp_hc_getmtu(&inc);
if (mtu)
mtu = min(mtu, ro_pmtu->ro_rt->rt_mtu);
else
mtu = ro_pmtu->ro_rt->rt_mtu;
if (mtu == 0)
mtu = ifmtu;
else if (mtu < IPV6_MMTU) {
/*
* RFC2460 section 5, last paragraph:
* if we record ICMPv6 too big message with
* mtu < IPV6_MMTU, transmit packets sized IPV6_MMTU
* or smaller, with framgent header attached.
* (fragment header is needed regardless from the
* packet size, for translators to identify packets)
*/
alwaysfrag = 1;
mtu = IPV6_MMTU;
} else if (mtu > ifmtu) {
/*
* The MTU on the route is larger than the MTU on
* the interface! This shouldn't happen, unless the
* MTU of the interface has been changed after the
* interface was brought up. Change the MTU in the
* route to match the interface MTU (as long as the
* field isn't locked).
*/
mtu = ifmtu;
ro_pmtu->ro_rt->rt_mtu = mtu;
}
} else if (ifp) {
mtu = IN6_LINKMTU(ifp);
} else
error = EHOSTUNREACH; /* XXX */
*mtup = mtu;
if (alwaysfragp)
*alwaysfragp = alwaysfrag;
return (error);
}
/* /*
* IP6 socket option processing. * IP6 socket option processing.
*/ */
@ -1935,9 +1858,7 @@ do { \
{ {
u_long pmtu = 0; u_long pmtu = 0;
struct ip6_mtuinfo mtuinfo; struct ip6_mtuinfo mtuinfo;
struct route_in6 sro; struct nhop6_basic nh6;
bzero(&sro, sizeof(sro));
if (!(so->so_state & SS_ISCONNECTED)) if (!(so->so_state & SS_ISCONNECTED))
return (ENOTCONN); return (ENOTCONN);
@ -1945,14 +1866,15 @@ do { \
* XXX: we dot not consider the case of source * XXX: we dot not consider the case of source
* routing, or optional information to specify * routing, or optional information to specify
* the outgoing interface. * the outgoing interface.
* TODO: embedded, Multipath
*/ */
error = ip6_getpmtu(&sro, NULL, NULL, if (fib6_lookup_nh_basic(so->so_fibnum,
&in6p->in6p_faddr, &pmtu, NULL, &in6p->in6p_faddr, 0, 0, &nh6) != 0) {
so->so_fibnum); error = EHOSTUNREACH;
if (sro.ro_rt)
RTFREE(sro.ro_rt);
if (error)
break; break;
}
pmtu = nh6.nh_mtu;
if (pmtu > IPV6_MAXPACKET) if (pmtu > IPV6_MAXPACKET)
pmtu = IPV6_MAXPACKET; pmtu = IPV6_MAXPACKET;
@ -2313,10 +2235,6 @@ ip6_clearpktopts(struct ip6_pktopts *pktopt, int optname)
if (optname == -1 || optname == IPV6_TCLASS) if (optname == -1 || optname == IPV6_TCLASS)
pktopt->ip6po_tclass = -1; pktopt->ip6po_tclass = -1;
if (optname == -1 || optname == IPV6_NEXTHOP) { if (optname == -1 || optname == IPV6_NEXTHOP) {
if (pktopt->ip6po_nextroute.ro_rt) {
RTFREE(pktopt->ip6po_nextroute.ro_rt);
pktopt->ip6po_nextroute.ro_rt = NULL;
}
if (pktopt->ip6po_nexthop) if (pktopt->ip6po_nexthop)
free(pktopt->ip6po_nexthop, M_IP6OPT); free(pktopt->ip6po_nexthop, M_IP6OPT);
pktopt->ip6po_nexthop = NULL; pktopt->ip6po_nexthop = NULL;
@ -2335,10 +2253,6 @@ ip6_clearpktopts(struct ip6_pktopts *pktopt, int optname)
if (pktopt->ip6po_rhinfo.ip6po_rhi_rthdr) if (pktopt->ip6po_rhinfo.ip6po_rhi_rthdr)
free(pktopt->ip6po_rhinfo.ip6po_rhi_rthdr, M_IP6OPT); free(pktopt->ip6po_rhinfo.ip6po_rhi_rthdr, M_IP6OPT);
pktopt->ip6po_rhinfo.ip6po_rhi_rthdr = NULL; pktopt->ip6po_rhinfo.ip6po_rhi_rthdr = NULL;
if (pktopt->ip6po_route.ro_rt) {
RTFREE(pktopt->ip6po_route.ro_rt);
pktopt->ip6po_route.ro_rt = NULL;
}
} }
if (optname == -1 || optname == IPV6_DSTOPTS) { if (optname == -1 || optname == IPV6_DSTOPTS) {
if (pktopt->ip6po_dest2) if (pktopt->ip6po_dest2)
@ -2904,7 +2818,7 @@ ip6_setpktopt(int optname, u_char *buf, int len, struct ip6_pktopts *opt,
* pointer that might NOT be &loif -- easier than replicating that code here. * pointer that might NOT be &loif -- easier than replicating that code here.
*/ */
void void
ip6_mloopback(struct ifnet *ifp, struct mbuf *m, struct sockaddr_in6 *dst) ip6_mloopback(struct ifnet *ifp, struct mbuf *m, int family)
{ {
struct mbuf *copym; struct mbuf *copym;
struct ip6_hdr *ip6; struct ip6_hdr *ip6;
@ -2940,7 +2854,7 @@ ip6_mloopback(struct ifnet *ifp, struct mbuf *m, struct sockaddr_in6 *dst)
in6_clearscope(&ip6->ip6_src); in6_clearscope(&ip6->ip6_src);
in6_clearscope(&ip6->ip6_dst); in6_clearscope(&ip6->ip6_dst);
(void)if_simloop(ifp, copym, dst->sin6_family, 0); (void)if_simloop(ifp, copym, family, 0);
} }
/* /*

View File

@ -372,6 +372,8 @@ VNET_DECLARE(int, ip6stealth);
extern struct pr_usrreqs rip6_usrreqs; extern struct pr_usrreqs rip6_usrreqs;
struct sockopt; struct sockopt;
struct route_info;
struct nhop_prepend;
struct inpcb; struct inpcb;
@ -412,11 +414,11 @@ int ip6_sysctl(int *, u_int, void *, size_t *, void *, size_t);
void ip6_forward(struct mbuf *, int); void ip6_forward(struct mbuf *, int);
void ip6_mloopback(struct ifnet *, struct mbuf *, struct sockaddr_in6 *); void ip6_mloopback(struct ifnet *, struct mbuf *, int family);
int ip6_output(struct mbuf *, struct ip6_pktopts *, int ip6_output(struct mbuf *, struct ip6_pktopts *,
struct route_in6 *, struct route_info *,
int, int,
struct ip6_moptions *, struct ifnet **, struct ip6_moptions *,
struct inpcb *); struct inpcb *);
int ip6_ctloutput(struct socket *, struct sockopt *); int ip6_ctloutput(struct socket *, struct sockopt *);
int ip6_raw_ctloutput(struct socket *, struct sockopt *); int ip6_raw_ctloutput(struct socket *, struct sockopt *);
@ -445,15 +447,14 @@ int rip6_usrreq(struct socket *,
int dest6_input(struct mbuf **, int *, int); int dest6_input(struct mbuf **, int *, int);
int none_input(struct mbuf **, int *, int); int none_input(struct mbuf **, int *, int);
int in6_selectsrc(struct sockaddr_in6 *, struct ip6_pktopts *, int in6_selectsrc_addr(uint32_t, struct in6_addr *, uint32_t,
struct inpcb *inp, struct route_in6 *, struct ucred *cred, struct in6_addr *);
struct ifnet **, struct in6_addr *); int in6_selectsrc_scope(struct sockaddr_in6 *, struct ip6_pktopts *,
int in6_selectroute(struct sockaddr_in6 *, struct ip6_pktopts *, struct inpcb *, struct ucred *, int, struct in6_addr *);
struct ip6_moptions *, struct route_in6 *, struct ifnet **, int fib6_selectroute(uint32_t fibnum, struct in6_addr *dst,
struct rtentry **); uint32_t scopeid, struct nhop_prepend *nh_src, struct mbuf *m,
int in6_selectroute_fib(struct sockaddr_in6 *, struct ip6_pktopts *, struct ip6_pktopts *opts, struct ip6_moptions *mopts,
struct ip6_moptions *, struct route_in6 *, struct ifnet **, struct nhop_prepend *nh);
struct rtentry **, u_int);
u_int32_t ip6_randomid(void); u_int32_t ip6_randomid(void);
u_int32_t ip6_randomflowlabel(void); u_int32_t ip6_randomflowlabel(void);
void in6_delayed_cksum(struct mbuf *m, uint32_t plen, u_short offset); void in6_delayed_cksum(struct mbuf *m, uint32_t plen, u_short offset);

View File

@ -3120,14 +3120,14 @@ mld_dispatch_packet(struct mbuf *m)
mld = (struct mld_hdr *)(mtod(md, uint8_t *) + off); mld = (struct mld_hdr *)(mtod(md, uint8_t *) + off);
type = mld->mld_type; type = mld->mld_type;
error = ip6_output(m0, &mld_po, NULL, IPV6_UNSPECSRC, &im6o, error = ip6_output(m0, &mld_po, NULL, IPV6_UNSPECSRC, &im6o, NULL);
&oifp, NULL);
if (error) { if (error) {
CTR3(KTR_MLD, "%s: ip6_output(%p) = %d", __func__, m0, error); CTR3(KTR_MLD, "%s: ip6_output(%p) = %d", __func__, m0, error);
goto out; goto out;
} }
ICMP6STAT_INC(icp6s_outhist[type]); ICMP6STAT_INC(icp6s_outhist[type]);
if (oifp != NULL) { if (error == 0) {
oifp = ifp;
icmp6_ifstat_inc(oifp, ifs6_out_msg); icmp6_ifstat_inc(oifp, ifs6_out_msg);
switch (type) { switch (type) {
case MLD_LISTENER_REPORT: case MLD_LISTENER_REPORT:

View File

@ -392,7 +392,6 @@ nd6_ns_output(struct ifnet *ifp, const struct in6_addr *daddr6,
int icmp6len; int icmp6len;
int maxlen; int maxlen;
caddr_t mac; caddr_t mac;
struct route_in6 ro;
if (IN6_IS_ADDR_MULTICAST(taddr6)) if (IN6_IS_ADDR_MULTICAST(taddr6))
return; return;
@ -415,8 +414,6 @@ nd6_ns_output(struct ifnet *ifp, const struct in6_addr *daddr6,
if (m == NULL) if (m == NULL)
return; return;
bzero(&ro, sizeof(ro));
if (daddr6 == NULL || IN6_IS_ADDR_MULTICAST(daddr6)) { if (daddr6 == NULL || IN6_IS_ADDR_MULTICAST(daddr6)) {
m->m_flags |= M_MCAST; m->m_flags |= M_MCAST;
im6o.im6o_multicast_ifp = ifp; im6o.im6o_multicast_ifp = ifp;
@ -493,24 +490,20 @@ nd6_ns_output(struct ifnet *ifp, const struct in6_addr *daddr6,
ifa_free(ifa); ifa_free(ifa);
} else { } else {
int error; int error;
struct sockaddr_in6 dst_sa; struct in6_addr dst_in, src_in;
struct in6_addr src_in; uint32_t scopeid;
struct ifnet *oifp;
bzero(&dst_sa, sizeof(dst_sa)); in6_splitscope(&ip6->ip6_dst, &dst_in, &scopeid);
dst_sa.sin6_family = AF_INET6;
dst_sa.sin6_len = sizeof(dst_sa);
dst_sa.sin6_addr = ip6->ip6_dst;
oifp = ifp; error = in6_selectsrc_addr(ifp->if_fib, &dst_in,
error = in6_selectsrc(&dst_sa, NULL, scopeid, &src_in);
NULL, &ro, NULL, &oifp, &src_in);
if (error) { if (error) {
char ip6buf[INET6_ADDRSTRLEN]; char ip6buf[INET6_ADDRSTRLEN];
nd6log((LOG_DEBUG, nd6log((LOG_DEBUG,
"nd6_ns_output: source can't be " "nd6_ns_output: source can't be "
"determined: dst=%s, error=%d\n", "determined: dst=%s, error=%d\n",
ip6_sprintf(ip6buf, &dst_sa.sin6_addr), ip6_sprintf(ip6buf, &dst_in),
error)); error));
goto bad; goto bad;
} }
@ -574,20 +567,14 @@ nd6_ns_output(struct ifnet *ifp, const struct in6_addr *daddr6,
m_tag_prepend(m, mtag); m_tag_prepend(m, mtag);
} }
ip6_output(m, NULL, &ro, dad ? IPV6_UNSPECSRC : 0, &im6o, NULL, NULL); ip6_output(m, NULL, NULL, dad ? IPV6_UNSPECSRC : 0, &im6o, NULL);
icmp6_ifstat_inc(ifp, ifs6_out_msg); icmp6_ifstat_inc(ifp, ifs6_out_msg);
icmp6_ifstat_inc(ifp, ifs6_out_neighborsolicit); icmp6_ifstat_inc(ifp, ifs6_out_neighborsolicit);
ICMP6STAT_INC(icp6s_outhist[ND_NEIGHBOR_SOLICIT]); ICMP6STAT_INC(icp6s_outhist[ND_NEIGHBOR_SOLICIT]);
/* We don't cache this route. */
RO_RTFREE(&ro);
return; return;
bad: bad:
if (ro.ro_rt) {
RTFREE(ro.ro_rt);
}
m_freem(m); m_freem(m);
return; return;
} }
@ -956,13 +943,12 @@ nd6_na_output_fib(struct ifnet *ifp, const struct in6_addr *daddr6_0,
{ {
struct mbuf *m; struct mbuf *m;
struct m_tag *mtag; struct m_tag *mtag;
struct ifnet *oifp;
struct ip6_hdr *ip6; struct ip6_hdr *ip6;
struct nd_neighbor_advert *nd_na; struct nd_neighbor_advert *nd_na;
struct ip6_moptions im6o; struct ip6_moptions im6o;
struct in6_addr src, daddr6; struct in6_addr dst, src, daddr6;
struct sockaddr_in6 dst_sa;
int icmp6len, maxlen, error; int icmp6len, maxlen, error;
uint32_t scopeid;
caddr_t mac = NULL; caddr_t mac = NULL;
struct route_in6 ro; struct route_in6 ro;
@ -1020,22 +1006,18 @@ nd6_na_output_fib(struct ifnet *ifp, const struct in6_addr *daddr6_0,
flags &= ~ND_NA_FLAG_SOLICITED; flags &= ~ND_NA_FLAG_SOLICITED;
} }
ip6->ip6_dst = daddr6; ip6->ip6_dst = daddr6;
bzero(&dst_sa, sizeof(struct sockaddr_in6));
dst_sa.sin6_family = AF_INET6;
dst_sa.sin6_len = sizeof(struct sockaddr_in6);
dst_sa.sin6_addr = daddr6;
/* /*
* Select a source whose scope is the same as that of the dest. * Select a source whose scope is the same as that of the dest.
*/ */
bcopy(&dst_sa, &ro.ro_dst, sizeof(dst_sa)); in6_splitscope(&daddr6, &dst, &scopeid);
oifp = ifp;
error = in6_selectsrc(&dst_sa, NULL, NULL, &ro, NULL, &oifp, &src); error = in6_selectsrc_addr(ifp->if_fib, &dst, scopeid, &src);
if (error) { if (error) {
char ip6buf[INET6_ADDRSTRLEN]; char ip6buf[INET6_ADDRSTRLEN];
nd6log((LOG_DEBUG, "nd6_na_output: source can't be " nd6log((LOG_DEBUG, "nd6_na_output: source can't be "
"determined: dst=%s, error=%d\n", "determined: dst=%s, error=%d\n",
ip6_sprintf(ip6buf, &dst_sa.sin6_addr), error)); ip6_sprintf(ip6buf, &dst), error));
goto bad; goto bad;
} }
ip6->ip6_src = src; ip6->ip6_src = src;
@ -1101,20 +1083,14 @@ nd6_na_output_fib(struct ifnet *ifp, const struct in6_addr *daddr6_0,
m_tag_prepend(m, mtag); m_tag_prepend(m, mtag);
} }
ip6_output(m, NULL, &ro, 0, &im6o, NULL, NULL); ip6_output(m, NULL, NULL, 0, &im6o, NULL);
icmp6_ifstat_inc(ifp, ifs6_out_msg); icmp6_ifstat_inc(ifp, ifs6_out_msg);
icmp6_ifstat_inc(ifp, ifs6_out_neighboradvert); icmp6_ifstat_inc(ifp, ifs6_out_neighboradvert);
ICMP6STAT_INC(icp6s_outhist[ND_NEIGHBOR_ADVERT]); ICMP6STAT_INC(icp6s_outhist[ND_NEIGHBOR_ADVERT]);
/* We don't cache this route. */
RO_RTFREE(&ro);
return; return;
bad: bad:
if (ro.ro_rt) {
RTFREE(ro.ro_rt);
}
m_freem(m); m_freem(m);
return; return;
} }

View File

@ -401,7 +401,6 @@ rip6_output(struct mbuf *m, struct socket *so, ...)
u_int plen = m->m_pkthdr.len; u_int plen = m->m_pkthdr.len;
int error = 0; int error = 0;
struct ip6_pktopts opt, *optp; struct ip6_pktopts opt, *optp;
struct ifnet *oifp = NULL;
int type = 0, code = 0; /* for ICMPv6 output statistics only */ int type = 0, code = 0; /* for ICMPv6 output statistics only */
int scope_ambiguous = 0; int scope_ambiguous = 0;
int use_defzone = 0; int use_defzone = 0;
@ -468,8 +467,8 @@ rip6_output(struct mbuf *m, struct socket *so, ...)
/* /*
* Source address selection. * Source address selection.
*/ */
error = in6_selectsrc(dstsock, optp, in6p, NULL, so->so_cred, error = in6_selectsrc_scope(dstsock, optp, in6p, so->so_cred,
&oifp, &in6a); scope_ambiguous, &in6a);
if (error) if (error)
goto bad; goto bad;
error = prison_check_ip6(in6p->inp_cred, &in6a); error = prison_check_ip6(in6p->inp_cred, &in6a);
@ -477,19 +476,6 @@ rip6_output(struct mbuf *m, struct socket *so, ...)
goto bad; goto bad;
ip6->ip6_src = in6a; ip6->ip6_src = in6a;
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; ip6->ip6_dst = dstsock->sin6_addr;
/* /*
@ -504,7 +490,8 @@ rip6_output(struct mbuf *m, struct socket *so, ...)
* ip6_plen will be filled in ip6_output, so not fill it here. * ip6_plen will be filled in ip6_output, so not fill it here.
*/ */
ip6->ip6_nxt = in6p->inp_ip_p; ip6->ip6_nxt = in6p->inp_ip_p;
ip6->ip6_hlim = in6_selecthlim(in6p, oifp); /* XXX: Get proper HLIM from selectsrc */
ip6->ip6_hlim = in6_selecthlim(in6p, NULL); /* oifp */
if (so->so_proto->pr_protocol == IPPROTO_ICMPV6 || if (so->so_proto->pr_protocol == IPPROTO_ICMPV6 ||
in6p->in6p_cksum != -1) { in6p->in6p_cksum != -1) {
@ -552,10 +539,13 @@ rip6_output(struct mbuf *m, struct socket *so, ...)
} }
} }
error = ip6_output(m, optp, NULL, 0, in6p->in6p_moptions, &oifp, in6p); error = ip6_output(m, optp, NULL, 0, in6p->in6p_moptions, in6p);
if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) { if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) {
/*
* XXX: think about better way of doing interface statistics
if (oifp) if (oifp)
icmp6_ifoutstat_inc(oifp, type, code); icmp6_ifoutstat_inc(oifp, type, code);
*/
ICMP6STAT_INC(icp6s_outhist[type]); ICMP6STAT_INC(icp6s_outhist[type]);
} else } else
RIP6STAT_INC(rip6s_opackets); RIP6STAT_INC(rip6s_opackets);
@ -792,7 +782,6 @@ rip6_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
struct inpcb *inp; struct inpcb *inp;
struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam; struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam;
struct in6_addr in6a; struct in6_addr in6a;
struct ifnet *ifp = NULL;
int error = 0, scope_ambiguous = 0; int error = 0, scope_ambiguous = 0;
inp = sotoinpcb(so); inp = sotoinpcb(so);
@ -821,21 +810,14 @@ rip6_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
INP_INFO_WLOCK(&V_ripcbinfo); INP_INFO_WLOCK(&V_ripcbinfo);
INP_WLOCK(inp); INP_WLOCK(inp);
/* Source address selection. XXX: need pcblookup? */ /* Source address selection. XXX: need pcblookup? */
error = in6_selectsrc(addr, inp->in6p_outputopts, error = in6_selectsrc_scope(addr, inp->in6p_outputopts,
inp, NULL, so->so_cred, &ifp, &in6a); inp, so->so_cred, scope_ambiguous, &in6a);
if (error) { if (error) {
INP_WUNLOCK(inp); INP_WUNLOCK(inp);
INP_INFO_WUNLOCK(&V_ripcbinfo); INP_INFO_WUNLOCK(&V_ripcbinfo);
return (error); return (error);
} }
/* XXX: see above */
if (ifp && scope_ambiguous &&
(error = in6_setscope(&addr->sin6_addr, ifp, NULL)) != 0) {
INP_WUNLOCK(inp);
INP_INFO_WUNLOCK(&V_ripcbinfo);
return (error);
}
inp->in6p_faddr = addr->sin6_addr; inp->in6p_faddr = addr->sin6_addr;
inp->in6p_laddr = in6a; inp->in6p_laddr = in6a;
soisconnected(so); soisconnected(so);

View File

@ -383,6 +383,20 @@ sa6_recoverscope(struct sockaddr_in6 *sin6)
return 0; return 0;
} }
/*
* Embed interface index for link-local addresses
*
*/
void
in6_setllascope(struct in6_addr *in6, struct ifnet *ifp)
{
uint32_t zoneid;
KASSERT(IN6_IS_SCOPE_LINKLOCAL(in6), ("Non-linklocal address"));
zoneid = ifp->if_index;
in6->s6_addr16[1] = htons(zoneid & 0xffff);
}
/* /*
* Determine the appropriate scope zone ID for in6 and ifp. If ret_id is * Determine the appropriate scope zone ID for in6 and ifp. If ret_id is
* non NULL, it is set to the zone ID. If the zone ID needs to be embedded * non NULL, it is set to the zone ID. If the zone ID needs to be embedded
@ -460,6 +474,17 @@ in6_getscope(struct in6_addr *in6)
return (0); return (0);
} }
void
in6_splitscope(struct in6_addr *src, struct in6_addr *dst, uint32_t *scopeid)
{
uint32_t zoneid;
*dst = *src;
zoneid = ntohs(in6_getscope(dst));
in6_clearscope(dst);
*scopeid = zoneid;
}
/* /*
* Return pointer to ifnet structure, corresponding to the zone id of * Return pointer to ifnet structure, corresponding to the zone id of
* link-local scope. * link-local scope.

View File

@ -59,10 +59,13 @@ int sa6_embedscope(struct sockaddr_in6 *, int);
int sa6_recoverscope(struct sockaddr_in6 *); int sa6_recoverscope(struct sockaddr_in6 *);
int sa6_checkzone(struct sockaddr_in6 *); int sa6_checkzone(struct sockaddr_in6 *);
int in6_setscope(struct in6_addr *, struct ifnet *, u_int32_t *); int in6_setscope(struct in6_addr *, struct ifnet *, u_int32_t *);
void in6_setllascope(struct in6_addr *in6, struct ifnet *ifp);
int in6_clearscope(struct in6_addr *); int in6_clearscope(struct in6_addr *);
uint16_t in6_getscope(struct in6_addr *); uint16_t in6_getscope(struct in6_addr *);
uint32_t in6_getscopezone(const struct ifnet *, int); uint32_t in6_getscopezone(const struct ifnet *, int);
struct ifnet* in6_getlinkifnet(uint32_t); struct ifnet* in6_getlinkifnet(uint32_t);
void in6_splitscope(struct in6_addr *src, struct in6_addr *dst,
uint32_t *scopeid);
#endif /* _KERNEL */ #endif /* _KERNEL */
#endif /* _NETINET6_SCOPE6_VAR_H_ */ #endif /* _NETINET6_SCOPE6_VAR_H_ */

View File

@ -631,7 +631,6 @@ udp6_output(struct inpcb *inp, struct mbuf *m, struct sockaddr *addr6,
struct udphdr *udp6; struct udphdr *udp6;
struct in6_addr *laddr, *faddr, in6a; struct in6_addr *laddr, *faddr, in6a;
struct sockaddr_in6 *sin6 = NULL; struct sockaddr_in6 *sin6 = NULL;
struct ifnet *oifp = NULL;
int cscov_partial = 0; int cscov_partial = 0;
int scope_ambiguous = 0; int scope_ambiguous = 0;
u_short fport; u_short fport;
@ -729,15 +728,10 @@ udp6_output(struct inpcb *inp, struct mbuf *m, struct sockaddr *addr6,
} }
if (!IN6_IS_ADDR_V4MAPPED(faddr)) { if (!IN6_IS_ADDR_V4MAPPED(faddr)) {
error = in6_selectsrc(sin6, optp, inp, NULL, error = in6_selectsrc_scope(sin6, optp, inp,
td->td_ucred, &oifp, &in6a); td->td_ucred, scope_ambiguous, &in6a);
if (error) if (error)
goto release; goto release;
if (oifp && scope_ambiguous &&
(error = in6_setscope(&sin6->sin6_addr,
oifp, NULL))) {
goto release;
}
laddr = &in6a; laddr = &in6a;
} else } else
laddr = &inp->in6p_laddr; /* XXX */ laddr = &inp->in6p_laddr; /* XXX */
@ -867,7 +861,7 @@ udp6_output(struct inpcb *inp, struct mbuf *m, struct sockaddr *addr6,
UDP_PROBE(send, NULL, inp, ip6, inp, udp6); UDP_PROBE(send, NULL, inp, ip6, inp, udp6);
UDPSTAT_INC(udps_opackets); UDPSTAT_INC(udps_opackets);
error = ip6_output(m, optp, NULL, flags, inp->in6p_moptions, error = ip6_output(m, optp, NULL, flags, inp->in6p_moptions,
NULL, inp); inp);
break; break;
case AF_INET: case AF_INET:
error = EAFNOSUPPORT; error = EAFNOSUPPORT;

View File

@ -254,7 +254,7 @@ ipsec_process_done(struct mbuf *m, struct ipsecrequest *isr)
* We don't need massage, IPv6 header fields are always in * We don't need massage, IPv6 header fields are always in
* net endian. * net endian.
*/ */
return ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL); return ip6_output(m, NULL, NULL, 0, NULL, NULL);
#endif /* INET6 */ #endif /* INET6 */
} }
panic("ipsec_process_done"); panic("ipsec_process_done");

View File

@ -777,7 +777,7 @@ dummynet_send(struct mbuf *m)
break; break;
case DIR_OUT | PROTO_IPV6: case DIR_OUT | PROTO_IPV6:
ip6_output(m, NULL, NULL, IPV6_FORWARDING, NULL, NULL, NULL); ip6_output(m, NULL, NULL, IPV6_FORWARDING, NULL, NULL);
break; break;
#endif #endif

View File

@ -515,7 +515,8 @@ verify_path6(struct in6_addr *src, struct ifnet *ifp, u_int fib)
{ {
struct nhop6_basic nh6; struct nhop6_basic nh6;
if (fib6_lookup_nh_basic(fib, *src, 0, &nh6) != 0) /* XXX: unembed scope? */
if (fib6_lookup_nh_basic(fib, src, 0, 0, &nh6) != 0)
return (0); return (0);
/* If ifp is provided, check for equality with route table. */ /* If ifp is provided, check for equality with route table. */
@ -563,8 +564,7 @@ send_reject6(struct ip_fw_args *args, int code, u_int hlen, struct ip6_hdr *ip6)
ntohl(tcp->th_seq), ntohl(tcp->th_ack), ntohl(tcp->th_seq), ntohl(tcp->th_ack),
tcp->th_flags | TH_RST); tcp->th_flags | TH_RST);
if (m0 != NULL) if (m0 != NULL)
ip6_output(m0, NULL, NULL, 0, NULL, NULL, ip6_output(m0, NULL, NULL, 0, NULL, NULL);
NULL);
} }
FREE_PKT(m); FREE_PKT(m);
} else if (code != ICMP6_UNREACH_RST) { /* Send an ICMPv6 unreach. */ } else if (code != ICMP6_UNREACH_RST) { /* Send an ICMPv6 unreach. */

View File

@ -1309,7 +1309,7 @@ check_dyn_rules(struct ip_fw_chain *chain, ipfw_range_tlv *rt,
ip_output(m, NULL, NULL, 0, NULL, NULL); ip_output(m, NULL, NULL, 0, NULL, NULL);
#ifdef INET6 #ifdef INET6
else else
ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL); ip6_output(m, NULL, NULL, 0, NULL, NULL);
#endif #endif
} }

View File

@ -1378,8 +1378,7 @@ pf_intr(void *v)
#endif /* INET */ #endif /* INET */
#ifdef INET6 #ifdef INET6
case PFSE_IP6: case PFSE_IP6:
ip6_output(pfse->pfse_m, NULL, NULL, 0, NULL, NULL, ip6_output(pfse->pfse_m, NULL, NULL, 0, NULL, NULL);
NULL);
break; break;
case PFSE_ICMP6: case PFSE_ICMP6:
icmp6_error(pfse->pfse_m, pfse->pfse_icmp_type, icmp6_error(pfse->pfse_m, pfse->pfse_icmp_type,
@ -2923,7 +2922,8 @@ pf_calc_mss(struct pf_addr *addr, sa_family_t af, int rtableid, u_int16_t offer)
#ifdef INET6 #ifdef INET6
case AF_INET6: case AF_INET6:
hlen = sizeof(struct ip6_hdr); hlen = sizeof(struct ip6_hdr);
if (fib6_lookup_nh_basic(rtableid, addr->v6, 0, &nh.u.nh6) == 0) if (fib6_lookup_nh_basic(rtableid, &addr->v6, 0, 0, &nh.u.nh6)
== 0)
mss = nh.u.nh6.nh_mtu - hlen - sizeof(struct tcphdr); mss = nh.u.nh6.nh_mtu - hlen - sizeof(struct tcphdr);
break; break;
#endif /* INET6 */ #endif /* INET6 */
@ -5100,7 +5100,8 @@ pf_routable(struct pf_addr *addr, sa_family_t af, struct pfi_kif *kif,
*/ */
if (IN6_IS_SCOPE_EMBED(&addr->v6)) if (IN6_IS_SCOPE_EMBED(&addr->v6))
return (1); return (1);
if (fib6_lookup_nh_basic(rtableid, addr->v6, 0, &nh.u.nh6) != 0) if (fib6_lookup_nh_basic(rtableid, &addr->v6, 0, 0, &nh.u.nh6)
!= 0)
return (0); return (0);
break; break;
#endif #endif
@ -5360,7 +5361,7 @@ pf_route6(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp,
if (s) if (s)
PF_STATE_UNLOCK(s); PF_STATE_UNLOCK(s);
m0->m_flags |= M_SKIP_FIREWALL; m0->m_flags |= M_SKIP_FIREWALL;
ip6_output(m0, NULL, NULL, 0, NULL, NULL, NULL); ip6_output(m0, NULL, NULL, 0, NULL, NULL);
return; return;
} }