diff options
author | Hajimu UMEMOTO <ume@FreeBSD.org> | 2005-07-25 12:31:43 +0000 |
---|---|---|
committer | Hajimu UMEMOTO <ume@FreeBSD.org> | 2005-07-25 12:31:43 +0000 |
commit | a1f7e5f8ee7fee62414af8d3a3008313cfd2cb17 (patch) | |
tree | 5a678f63b25976c30f74f3bad9edb6f708c52930 /sys/netinet6/in6_src.c | |
parent | 869de95743d527dda9d81d06b0e74cd5e95f8f9c (diff) | |
download | src-a1f7e5f8ee7fee62414af8d3a3008313cfd2cb17.tar.gz src-a1f7e5f8ee7fee62414af8d3a3008313cfd2cb17.zip |
scope cleanup. with this change
- most of the kernel code will not care about the actual encoding of
scope zone IDs and won't touch "s6_addr16[1]" directly.
- similarly, most of the kernel code will not care about link-local
scoped addresses as a special case.
- scope boundary check will be stricter. For example, the current
*BSD code allows a packet with src=::1 and dst=(some global IPv6
address) to be sent outside of the node, if the application do:
s = socket(AF_INET6);
bind(s, "::1");
sendto(s, some_global_IPv6_addr);
This is clearly wrong, since ::1 is only meaningful within a single
node, but the current implementation of the *BSD kernel cannot
reject this attempt.
Submitted by: JINMEI Tatuya <jinmei__at__isl.rdc.toshiba.co.jp>
Obtained from: KAME
Notes
Notes:
svn path=/head/; revision=148385
Diffstat (limited to 'sys/netinet6/in6_src.c')
-rw-r--r-- | sys/netinet6/in6_src.c | 346 |
1 files changed, 115 insertions, 231 deletions
diff --git a/sys/netinet6/in6_src.c b/sys/netinet6/in6_src.c index 812b03d66650..78e18cde76c0 100644 --- a/sys/netinet6/in6_src.c +++ b/sys/netinet6/in6_src.c @@ -89,6 +89,7 @@ #include <netinet/ip6.h> #include <netinet6/in6_pcb.h> #include <netinet6/ip6_var.h> +#include <netinet6/scope6_var.h> #include <netinet6/nd6.h> #ifdef ENABLE_DEFAULT_SCOPE #include <netinet6/scope6_var.h> @@ -107,6 +108,9 @@ struct in6_addrpolicy defaultaddrpolicy; int ip6_prefer_tempaddr = 0; +static int selectroute __P((struct sockaddr_in6 *, struct ip6_pktopts *, + struct ip6_moptions *, struct route_in6 *, struct ifnet **, + struct rtentry **, int, int)); static int in6_selectif __P((struct sockaddr_in6 *, struct ip6_pktopts *, struct ip6_moptions *, struct route_in6 *ro, struct ifnet **)); @@ -148,15 +152,16 @@ static struct in6_addrpolicy *match_addrsel_policy __P((struct sockaddr_in6 *)); } while(0) struct in6_addr * -in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp) +in6_selectsrc(dstsock, opts, mopts, ro, laddr, ifpp, errorp) struct sockaddr_in6 *dstsock; struct ip6_pktopts *opts; struct ip6_moptions *mopts; struct route_in6 *ro; struct in6_addr *laddr; + struct ifnet **ifpp; int *errorp; { - struct in6_addr *dst; + struct in6_addr dst; struct ifnet *ifp = NULL; struct in6_ifaddr *ia = NULL, *ia_best = NULL; struct in6_pktinfo *pi = NULL; @@ -164,30 +169,11 @@ in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp) struct in6_addrpolicy *dst_policy = NULL, *best_policy = NULL; u_int32_t odstzone; int prefer_tempaddr; - struct sockaddr_in6 dstsock0; - dstsock0 = *dstsock; - if (IN6_IS_SCOPE_LINKLOCAL(&dstsock0.sin6_addr) || - IN6_IS_ADDR_MC_INTFACELOCAL(&dstsock0.sin6_addr)) { - /* KAME assumption: link id == interface id */ - if (opts && opts->ip6po_pktinfo && - opts->ip6po_pktinfo->ipi6_ifindex) { - ifp = ifnet_byindex(opts->ip6po_pktinfo->ipi6_ifindex); - dstsock0.sin6_addr.s6_addr16[1] = - htons(opts->ip6po_pktinfo->ipi6_ifindex); - } else if (mopts && - IN6_IS_ADDR_MULTICAST(&dstsock0.sin6_addr) && - mopts->im6o_multicast_ifp) { - ifp = mopts->im6o_multicast_ifp; - dstsock0.sin6_addr.s6_addr16[1] = htons(ifp->if_index); - } else if ((*errorp = in6_embedscope(&dstsock0.sin6_addr, - &dstsock0, NULL, NULL)) != 0) - return (NULL); - } - dstsock = &dstsock0; - - dst = &dstsock->sin6_addr; + dst = dstsock->sin6_addr; /* make a copy for local operation */ *errorp = 0; + if (ifpp) + *ifpp = NULL; /* * If the source address is explicitly specified by the caller, @@ -209,23 +195,20 @@ in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp) /* * determine the appropriate zone id of the source based on * the zone of the destination and the outgoing interface. + * If the specified address is ambiguous wrt the scope zone, + * the interface must be specified; otherwise, ifa_ifwithaddr() + * will fail matching the address. */ bzero(&srcsock, sizeof(srcsock)); srcsock.sin6_family = AF_INET6; srcsock.sin6_len = sizeof(srcsock); srcsock.sin6_addr = pi->ipi6_addr; if (ifp) { - if (in6_addr2zoneid(ifp, &pi->ipi6_addr, - &srcsock.sin6_scope_id)) { - *errorp = EINVAL; /* XXX */ + *errorp = in6_setscope(&srcsock.sin6_addr, ifp, NULL); + if (*errorp != 0) return (NULL); - } - } - if ((*errorp = in6_embedscope(&srcsock.sin6_addr, &srcsock, - NULL, NULL)) != 0) { - return (NULL); } - srcsock.sin6_scope_id = 0; /* XXX: ifa_ifwithaddr expects 0 */ + ia6 = (struct in6_ifaddr *)ifa_ifwithaddr((struct sockaddr *)(&srcsock)); if (ia6 == NULL || (ia6->ia6_flags & (IN6_IFF_ANYCAST | IN6_IFF_NOTREADY))) { @@ -233,6 +216,8 @@ in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp) return (NULL); } pi->ipi6_addr = srcsock.sin6_addr; /* XXX: this overrides pi */ + if (ifpp) + *ifpp = ifp; return (&ia6->ia_addr.sin6_addr); } @@ -254,14 +239,15 @@ in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp) if (ifp == NULL) /* this should not happen */ panic("in6_selectsrc: NULL ifp"); #endif - if (in6_addr2zoneid(ifp, dst, &odstzone)) { /* impossible */ - *errorp = EIO; /* XXX */ + *errorp = in6_setscope(&dst, ifp, &odstzone); + if (*errorp != 0) return (NULL); - } + for (ia = in6_ifaddr; ia; ia = ia->ia_next) { int new_scope = -1, new_matchlen = -1; struct in6_addrpolicy *new_policy = NULL; u_int32_t srczone, osrczone, dstzone; + struct in6_addr src; struct ifnet *ifp1 = ia->ia_ifp; /* @@ -270,12 +256,13 @@ in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp) * does not contain the outgoing interface. * XXX: we should probably use sin6_scope_id here. */ - if (in6_addr2zoneid(ifp1, dst, &dstzone) || + if (in6_setscope(&dst, ifp1, &dstzone) || odstzone != dstzone) { continue; } - if (in6_addr2zoneid(ifp, &ia->ia_addr.sin6_addr, &osrczone) || - in6_addr2zoneid(ifp1, &ia->ia_addr.sin6_addr, &srczone) || + src = ia->ia_addr.sin6_addr; + if (in6_setscope(&src, ifp, &osrczone) || + in6_setscope(&src, ifp1, &srczone) || osrczone != srczone) { continue; } @@ -289,7 +276,7 @@ in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp) continue; /* Rule 1: Prefer same address */ - if (IN6_ARE_ADDR_EQUAL(dst, &ia->ia_addr.sin6_addr)) { + if (IN6_ARE_ADDR_EQUAL(&dst, &ia->ia_addr.sin6_addr)) { ia_best = ia; BREAK(1); /* there should be no better candidate */ } @@ -299,7 +286,7 @@ in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp) /* Rule 2: Prefer appropriate scope */ if (dst_scope < 0) - dst_scope = in6_addrscope(dst); + dst_scope = in6_addrscope(&dst); new_scope = in6_addrscope(&ia->ia_addr.sin6_addr); if (IN6_ARE_SCOPE_CMP(best_scope, new_scope) < 0) { if (IN6_ARE_SCOPE_CMP(best_scope, dst_scope) < 0) @@ -396,7 +383,7 @@ in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp) * a large number so that it is easy to assign smaller numbers * to more preferred rules. */ - new_matchlen = in6_matchlen(&ia->ia_addr.sin6_addr, dst); + new_matchlen = in6_matchlen(&ia->ia_addr.sin6_addr, &dst); if (best_matchlen < new_matchlen) REPLACE(14); if (new_matchlen < best_matchlen) @@ -418,7 +405,7 @@ in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp) lookup_addrsel_policy(&ia_best->ia_addr)); best_matchlen = (new_matchlen >= 0 ? new_matchlen : in6_matchlen(&ia_best->ia_addr.sin6_addr, - dst)); + &dst)); next: continue; @@ -432,75 +419,14 @@ in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp) return (NULL); } + if (ifpp) + *ifpp = ifp; + return (&ia->ia_addr.sin6_addr); } static int -in6_selectif(dstsock, opts, mopts, ro, retifp) - struct sockaddr_in6 *dstsock; - struct ip6_pktopts *opts; - struct ip6_moptions *mopts; - struct route_in6 *ro; - struct ifnet **retifp; -{ - int error; - struct route_in6 sro; - struct rtentry *rt = NULL; - - if (ro == NULL) { - bzero(&sro, sizeof(sro)); - ro = &sro; - } - - if ((error = in6_selectroute(dstsock, opts, mopts, ro, retifp, - &rt, 0)) != 0) { - if (rt && rt == sro.ro_rt) - RTFREE(rt); - 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 (rt && rt == sro.ro_rt) - RTFREE(rt); - return (flags); - } - - /* - * 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 && rt->rt_ifa && rt->rt_ifa->ifa_ifp) - *retifp = rt->rt_ifa->ifa_ifp; - - if (rt && rt == sro.ro_rt) - RTFREE(rt); - return (0); -} - -int -in6_selectroute(dstsock, opts, mopts, ro, retifp, retrt, clone) +selectroute(dstsock, opts, mopts, ro, retifp, retrt, clone, norouteok) struct sockaddr_in6 *dstsock; struct ip6_pktopts *opts; struct ip6_moptions *mopts; @@ -508,6 +434,7 @@ in6_selectroute(dstsock, opts, mopts, ro, retifp, retrt, clone) struct ifnet **retifp; struct rtentry **retrt; int clone; /* meaningful only for bsdi and freebsd. */ + int norouteok; { int error = 0; struct ifnet *ifp = NULL; @@ -534,7 +461,8 @@ in6_selectroute(dstsock, opts, mopts, ro, retifp, retrt, clone) /* XXX boundary check is assumed to be already done. */ ifp = ifnet_byindex(pi->ipi6_ifindex); if (ifp != NULL && - (retrt == NULL || IN6_IS_ADDR_MULTICAST(dst))) { + (norouteok || retrt == NULL || + IN6_IS_ADDR_MULTICAST(dst))) { /* * we do not have to check nor get the route for * multicast. @@ -697,6 +625,84 @@ in6_selectroute(dstsock, opts, mopts, ro, retifp, retrt, clone) return (error); } +static int +in6_selectif(dstsock, opts, mopts, ro, retifp) + struct sockaddr_in6 *dstsock; + struct ip6_pktopts *opts; + struct ip6_moptions *mopts; + struct route_in6 *ro; + struct ifnet **retifp; +{ + int error; + struct route_in6 sro; + struct rtentry *rt = NULL; + + if (ro == NULL) { + bzero(&sro, sizeof(sro)); + ro = &sro; + } + + if ((error = selectroute(dstsock, opts, mopts, ro, retifp, + &rt, 0, 1)) != 0) { + if (rt && rt == sro.ro_rt) + RTFREE(rt); + 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 (rt && rt == sro.ro_rt) + RTFREE(rt); + return (flags); + } + + /* + * 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 && rt->rt_ifa && rt->rt_ifa->ifa_ifp) + *retifp = rt->rt_ifa->ifa_ifp; + + if (rt && rt == sro.ro_rt) + RTFREE(rt); + return (0); +} + +int +in6_selectroute(dstsock, opts, mopts, ro, retifp, retrt, clone) + struct sockaddr_in6 *dstsock; + struct ip6_pktopts *opts; + struct ip6_moptions *mopts; + struct route_in6 *ro; + struct ifnet **retifp; + struct rtentry **retrt; + int clone; /* meaningful only for bsdi and freebsd. */ +{ + return (selectroute(dstsock, opts, mopts, ro, retifp, + retrt, clone, 0)); +} + /* * Default hop limit selection. The precedence is as follows: * 1. Hoplimit value specified via ioctl. @@ -830,128 +836,6 @@ in6_pcbsetport(laddr, inp, cred) return (0); } -/* - * Generate kernel-internal form (scopeid embedded into s6_addr16[1]). - * If the address scope of is link-local, embed the interface index in the - * address. The routine determines our precedence - * between advanced API scope/interface specification and basic API - * specification. - * - * This function should be nuked in the future, when we get rid of embedded - * scopeid thing. - * - * XXX actually, it is over-specification to return ifp against sin6_scope_id. - * there can be multiple interfaces that belong to a particular scope zone - * (in specification, we have 1:N mapping between a scope zone and interfaces). - * we may want to change the function to return something other than ifp. - */ -int -in6_embedscope(in6, sin6, in6p, ifpp) - struct in6_addr *in6; - const struct sockaddr_in6 *sin6; - struct in6pcb *in6p; - struct ifnet **ifpp; -{ - struct ifnet *ifp = NULL; - u_int32_t zoneid = sin6->sin6_scope_id; - - *in6 = sin6->sin6_addr; - if (ifpp) - *ifpp = NULL; - - /* - * don't try to read sin6->sin6_addr beyond here, since the caller may - * ask us to overwrite existing sockaddr_in6 - */ - -#ifdef ENABLE_DEFAULT_SCOPE - if (zoneid == 0) - zoneid = scope6_addr2default(in6); -#endif - - if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6)) { - struct in6_pktinfo *pi; - - /* KAME assumption: link id == interface id */ - if (in6p && in6p->in6p_outputopts && - (pi = in6p->in6p_outputopts->ip6po_pktinfo) && - pi->ipi6_ifindex) { - ifp = ifnet_byindex(pi->ipi6_ifindex); - in6->s6_addr16[1] = htons(pi->ipi6_ifindex); - } else if (in6p && IN6_IS_ADDR_MULTICAST(in6) && - in6p->in6p_moptions && - in6p->in6p_moptions->im6o_multicast_ifp) { - ifp = in6p->in6p_moptions->im6o_multicast_ifp; - in6->s6_addr16[1] = htons(ifp->if_index); - } else if (zoneid) { - if (if_index < zoneid) - return (ENXIO); /* XXX EINVAL? */ - ifp = ifnet_byindex(zoneid); - - /* XXX assignment to 16bit from 32bit variable */ - in6->s6_addr16[1] = htons(zoneid & 0xffff); - } - - if (ifpp) - *ifpp = ifp; - } - - return 0; -} - -/* - * generate standard sockaddr_in6 from embedded form. - * touches sin6_addr and sin6_scope_id only. - * - * this function should be nuked in the future, when we get rid of - * embedded scopeid thing. - */ -int -in6_recoverscope(sin6, in6, ifp) - struct sockaddr_in6 *sin6; - const struct in6_addr *in6; - struct ifnet *ifp; -{ - u_int32_t zoneid; - - sin6->sin6_addr = *in6; - - /* - * don't try to read *in6 beyond here, since the caller may - * ask us to overwrite existing sockaddr_in6 - */ - - sin6->sin6_scope_id = 0; - if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6)) { - /* - * KAME assumption: link id == interface id - */ - zoneid = ntohs(sin6->sin6_addr.s6_addr16[1]); - if (zoneid) { - /* sanity check */ - if (zoneid < 0 || if_index < zoneid) - return ENXIO; - if (ifp && ifp->if_index != zoneid) - return ENXIO; - sin6->sin6_addr.s6_addr16[1] = 0; - sin6->sin6_scope_id = zoneid; - } - } - - return 0; -} - -/* - * just clear the embedded scope identifier. - */ -void -in6_clearscope(addr) - struct in6_addr *addr; -{ - if (IN6_IS_SCOPE_LINKLOCAL(addr) || IN6_IS_ADDR_MC_INTFACELOCAL(addr)) - addr->s6_addr16[1] = 0; -} - void addrsel_policy_init() { |