+/*
+ * Validate the specified scope zone ID in the sin6_scope_id field. If the ID
+ * is unspecified (=0), needs to be specified, and the default zone ID can be
+ * used, the default value will be used.
+ * This routine then generates the kernel-internal form: if the address scope
+ * of is interface-local or link-local, embed the interface index in the
+ * address.
+ */
+int
+sa6_embedscope(struct sockaddr_in6 *sin6, int defaultok)
+{
+ struct ifnet *ifp;
+ u_int32_t zoneid;
+
+ if ((zoneid = sin6->sin6_scope_id) == 0 && defaultok)
+ zoneid = scope6_addr2default(&sin6->sin6_addr);
+
+ if (zoneid != 0 &&
+ (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) ||
+ IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr))) {
+ /*
+ * At this moment, we only check interface-local and
+ * link-local scope IDs, and use interface indices as the
+ * zone IDs assuming a one-to-one mapping between interfaces
+ * and links.
+ */
+ if (if_index < zoneid)
+ return (ENXIO);
+ ifnet_head_lock_shared();
+ ifp = ifindex2ifnet[zoneid];
+ if (ifp == NULL) {/* XXX: this can happen for some OS */
+ ifnet_head_done();
+ return (ENXIO);
+ }
+ ifnet_head_done();
+ /* XXX assignment to 16bit from 32bit variable */
+ sin6->sin6_addr.s6_addr16[1] = htons(zoneid & 0xffff);
+
+ sin6->sin6_scope_id = 0;
+ }
+
+ return 0;
+}
+
+void
+rtkey_to_sa6(struct rtentry *rt, struct sockaddr_in6 *sin6)
+{
+ VERIFY(rt_key(rt)->sa_family == AF_INET6);
+
+ *sin6 = *((struct sockaddr_in6 *)rt_key(rt));
+ sin6->sin6_scope_id = 0;
+}
+
+void
+rtgw_to_sa6(struct rtentry *rt, struct sockaddr_in6 *sin6)
+{
+ VERIFY(rt->rt_flags & RTF_GATEWAY);
+
+ *sin6 = *((struct sockaddr_in6 *)rt->rt_gateway);
+ sin6->sin6_scope_id = 0;
+}
+
+/*
+ * generate standard sockaddr_in6 from embedded form.
+ */
+int
+sa6_recoverscope(struct sockaddr_in6 *sin6)
+{
+ u_int32_t zoneid;
+
+ if (sin6->sin6_scope_id != 0) {
+ log(LOG_NOTICE,
+ "sa6_recoverscope: assumption failure (non 0 ID): %s%%%d\n",
+ ip6_sprintf(&sin6->sin6_addr), sin6->sin6_scope_id);
+ /* XXX: proceed anyway... */
+ }
+ if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) ||
+ IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr)) {
+ /*
+ * KAME assumption: link id == interface id
+ */
+ zoneid = ntohs(sin6->sin6_addr.s6_addr16[1]);
+ if (zoneid) {
+ /* sanity check */
+ if (if_index < zoneid)
+ return (ENXIO);
+ ifnet_head_lock_shared();
+ if (ifindex2ifnet[zoneid] == NULL) {
+ ifnet_head_done();
+ return (ENXIO);
+ }
+ ifnet_head_done();
+ sin6->sin6_addr.s6_addr16[1] = 0;
+ sin6->sin6_scope_id = zoneid;
+ }
+ }
+
+ return 0;
+}
+