--- src/sys/netinet6/scope6.c 2006/10/24 06:18:42 1.9 +++ src/sys/netinet6/scope6.c 2007/08/16 20:03:58 1.10 @@ -296,3 +296,76 @@ scope6_addr2default(struct in6_addr *add return (sid_default.s6id_list[in6_addrscope(addr)]); } + +/* + * 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 + * in the in6_addr structure, in6 will be modified. + */ +int +in6_setscope(struct in6_addr *in6, struct ifnet *ifp, u_int32_t *ret_id) +{ + int scope; + u_int32_t zoneid = 0; + struct scope6_id *sid; + + lwkt_serialize_enter(ifp->if_serializer); + + sid = SID(ifp); + +#ifdef DIAGNOSTIC + if (sid == NULL) { /* should not happen */ + panic("in6_setscope: scope array is NULL"); + /* NOTREACHED */ + } +#endif + + /* + * special case: the loopback address can only belong to a loopback + * interface. + */ + if (IN6_IS_ADDR_LOOPBACK(in6)) { + if (!(ifp->if_flags & IFF_LOOPBACK)) { + lwkt_serialize_exit(ifp->if_serializer); + return (EINVAL); + } else { + if (ret_id != NULL) + *ret_id = 0; /* there's no ambiguity */ + lwkt_serialize_exit(ifp->if_serializer); + return (0); + } + } + + scope = in6_addrscope(in6); + + switch (scope) { + case IPV6_ADDR_SCOPE_NODELOCAL: /* should be interface index */ + zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_NODELOCAL]; + break; + + case IPV6_ADDR_SCOPE_LINKLOCAL: + zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL]; + break; + + case IPV6_ADDR_SCOPE_SITELOCAL: + zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL]; + break; + + case IPV6_ADDR_SCOPE_ORGLOCAL: + zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL]; + break; + + default: + zoneid = 0; /* XXX: treat as global. */ + break; + } + lwkt_serialize_exit(ifp->if_serializer); + + if (ret_id != NULL) + *ret_id = zoneid; + + if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_NODELOCAL(in6) ) + in6->s6_addr16[1] = htons(zoneid & 0xffff); /* XXX */ + + return (0); +}