The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/netinet6/scope6.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (C) 2000 WIDE Project.
    3  * All rights reserved.
    4  *
    5  * Redistribution and use in source and binary forms, with or without
    6  * modification, are permitted provided that the following conditions
    7  * are met:
    8  * 1. Redistributions of source code must retain the above copyright
    9  *    notice, this list of conditions and the following disclaimer.
   10  * 2. Redistributions in binary form must reproduce the above copyright
   11  *    notice, this list of conditions and the following disclaimer in the
   12  *    documentation and/or other materials provided with the distribution.
   13  * 3. Neither the name of the project nor the names of its contributors
   14  *    may be used to endorse or promote products derived from this software
   15  *    without specific prior written permission.
   16  *
   17  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
   18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
   21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   27  * SUCH DAMAGE.
   28  *
   29  *      $KAME: scope6.c,v 1.10 2000/07/24 13:29:31 itojun Exp $
   30  */
   31 
   32 #include <sys/cdefs.h>
   33 __FBSDID("$FreeBSD: releng/8.0/sys/netinet6/scope6.c 196019 2009-08-01 19:26:27Z rwatson $");
   34 
   35 #include <sys/param.h>
   36 #include <sys/malloc.h>
   37 #include <sys/mbuf.h>
   38 #include <sys/socket.h>
   39 #include <sys/systm.h>
   40 #include <sys/queue.h>
   41 #include <sys/syslog.h>
   42 
   43 #include <net/if.h>
   44 #include <net/vnet.h>
   45 
   46 #include <netinet/in.h>
   47 
   48 #include <netinet/ip6.h>
   49 #include <netinet6/in6_var.h>
   50 #include <netinet6/ip6_var.h>
   51 #include <netinet6/scope6_var.h>
   52 
   53 
   54 /*
   55  * The scope6_lock protects the global sid default stored in
   56  * sid_default below.
   57  */
   58 static struct mtx scope6_lock;
   59 #define SCOPE6_LOCK_INIT()      mtx_init(&scope6_lock, "scope6_lock", NULL, MTX_DEF)
   60 #define SCOPE6_LOCK()           mtx_lock(&scope6_lock)
   61 #define SCOPE6_UNLOCK()         mtx_unlock(&scope6_lock)
   62 #define SCOPE6_LOCK_ASSERT()    mtx_assert(&scope6_lock, MA_OWNED)
   63 
   64 static VNET_DEFINE(struct scope6_id, sid_default);
   65 VNET_DEFINE(int, ip6_use_defzone);
   66 
   67 #define V_sid_default                   VNET(sid_default)
   68 
   69 #define SID(ifp) \
   70         (((struct in6_ifextra *)(ifp)->if_afdata[AF_INET6])->scope6_id)
   71 
   72 void
   73 scope6_init(void)
   74 {
   75 
   76 #ifdef ENABLE_DEFAULT_SCOPE
   77         V_ip6_use_defzone = 1;
   78 #else
   79         V_ip6_use_defzone = 0;
   80 #endif
   81         bzero(&V_sid_default, sizeof(V_sid_default));
   82 
   83         if (!IS_DEFAULT_VNET(curvnet))
   84                 return;
   85 
   86         SCOPE6_LOCK_INIT();
   87 }
   88 
   89 struct scope6_id *
   90 scope6_ifattach(struct ifnet *ifp)
   91 {
   92         struct scope6_id *sid;
   93 
   94         sid = (struct scope6_id *)malloc(sizeof(*sid), M_IFADDR, M_WAITOK);
   95         bzero(sid, sizeof(*sid));
   96 
   97         /*
   98          * XXX: IPV6_ADDR_SCOPE_xxx macros are not standard.
   99          * Should we rather hardcode here?
  100          */
  101         sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = ifp->if_index;
  102         sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = ifp->if_index;
  103 #ifdef MULTI_SCOPE
  104         /* by default, we don't care about scope boundary for these scopes. */
  105         sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL] = 1;
  106         sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL] = 1;
  107 #endif
  108 
  109         return sid;
  110 }
  111 
  112 void
  113 scope6_ifdetach(struct scope6_id *sid)
  114 {
  115 
  116         free(sid, M_IFADDR);
  117 }
  118 
  119 int
  120 scope6_set(struct ifnet *ifp, struct scope6_id *idlist)
  121 {
  122         int i;
  123         int error = 0;
  124         struct scope6_id *sid = NULL;
  125 
  126         IF_AFDATA_LOCK(ifp);
  127         sid = SID(ifp);
  128 
  129         if (!sid) {     /* paranoid? */
  130                 IF_AFDATA_UNLOCK(ifp);
  131                 return (EINVAL);
  132         }
  133 
  134         /*
  135          * XXX: We need more consistency checks of the relationship among
  136          * scopes (e.g. an organization should be larger than a site).
  137          */
  138 
  139         /*
  140          * TODO(XXX): after setting, we should reflect the changes to
  141          * interface addresses, routing table entries, PCB entries...
  142          */
  143 
  144         SCOPE6_LOCK();
  145         for (i = 0; i < 16; i++) {
  146                 if (idlist->s6id_list[i] &&
  147                     idlist->s6id_list[i] != sid->s6id_list[i]) {
  148                         /*
  149                          * An interface zone ID must be the corresponding
  150                          * interface index by definition.
  151                          */
  152                         if (i == IPV6_ADDR_SCOPE_INTFACELOCAL &&
  153                             idlist->s6id_list[i] != ifp->if_index) {
  154                                 IF_AFDATA_UNLOCK(ifp);
  155                                 SCOPE6_UNLOCK();
  156                                 return (EINVAL);
  157                         }
  158 
  159                         if (i == IPV6_ADDR_SCOPE_LINKLOCAL &&
  160                             idlist->s6id_list[i] > V_if_index) {
  161                                 /*
  162                                  * XXX: theoretically, there should be no
  163                                  * relationship between link IDs and interface
  164                                  * IDs, but we check the consistency for
  165                                  * safety in later use.
  166                                  */
  167                                 IF_AFDATA_UNLOCK(ifp);
  168                                 SCOPE6_UNLOCK();
  169                                 return (EINVAL);
  170                         }
  171 
  172                         /*
  173                          * XXX: we must need lots of work in this case,
  174                          * but we simply set the new value in this initial
  175                          * implementation.
  176                          */
  177                         sid->s6id_list[i] = idlist->s6id_list[i];
  178                 }
  179         }
  180         SCOPE6_UNLOCK();
  181         IF_AFDATA_UNLOCK(ifp);
  182 
  183         return (error);
  184 }
  185 
  186 int
  187 scope6_get(struct ifnet *ifp, struct scope6_id *idlist)
  188 {
  189         /* We only need to lock the interface's afdata for SID() to work. */
  190         IF_AFDATA_LOCK(ifp);
  191         struct scope6_id *sid = SID(ifp);
  192 
  193         if (sid == NULL) {      /* paranoid? */
  194                 IF_AFDATA_UNLOCK(ifp);
  195                 return (EINVAL);
  196         }
  197 
  198         SCOPE6_LOCK();
  199         *idlist = *sid;
  200         SCOPE6_UNLOCK();
  201 
  202         IF_AFDATA_UNLOCK(ifp);
  203         return (0);
  204 }
  205 
  206 
  207 /*
  208  * Get a scope of the address. Node-local, link-local, site-local or global.
  209  */
  210 int
  211 in6_addrscope(struct in6_addr *addr)
  212 {
  213         int scope;
  214 
  215         if (addr->s6_addr[0] == 0xfe) {
  216                 scope = addr->s6_addr[1] & 0xc0;
  217 
  218                 switch (scope) {
  219                 case 0x80:
  220                         return IPV6_ADDR_SCOPE_LINKLOCAL;
  221                         break;
  222                 case 0xc0:
  223                         return IPV6_ADDR_SCOPE_SITELOCAL;
  224                         break;
  225                 default:
  226                         return IPV6_ADDR_SCOPE_GLOBAL; /* just in case */
  227                         break;
  228                 }
  229         }
  230 
  231 
  232         if (addr->s6_addr[0] == 0xff) {
  233                 scope = addr->s6_addr[1] & 0x0f;
  234 
  235                 /*
  236                  * due to other scope such as reserved,
  237                  * return scope doesn't work.
  238                  */
  239                 switch (scope) {
  240                 case IPV6_ADDR_SCOPE_INTFACELOCAL:
  241                         return IPV6_ADDR_SCOPE_INTFACELOCAL;
  242                         break;
  243                 case IPV6_ADDR_SCOPE_LINKLOCAL:
  244                         return IPV6_ADDR_SCOPE_LINKLOCAL;
  245                         break;
  246                 case IPV6_ADDR_SCOPE_SITELOCAL:
  247                         return IPV6_ADDR_SCOPE_SITELOCAL;
  248                         break;
  249                 default:
  250                         return IPV6_ADDR_SCOPE_GLOBAL;
  251                         break;
  252                 }
  253         }
  254 
  255         /*
  256          * Regard loopback and unspecified addresses as global, since
  257          * they have no ambiguity.
  258          */
  259         if (bcmp(&in6addr_loopback, addr, sizeof(*addr) - 1) == 0) {
  260                 if (addr->s6_addr[15] == 1) /* loopback */
  261                         return IPV6_ADDR_SCOPE_LINKLOCAL;
  262                 if (addr->s6_addr[15] == 0) /* unspecified */
  263                         return IPV6_ADDR_SCOPE_GLOBAL; /* XXX: correct? */
  264         }
  265 
  266         return IPV6_ADDR_SCOPE_GLOBAL;
  267 }
  268 
  269 /*
  270  * ifp - note that this might be NULL
  271  */
  272 
  273 void
  274 scope6_setdefault(struct ifnet *ifp)
  275 {
  276 
  277         /*
  278          * Currently, this function just sets the default "interfaces"
  279          * and "links" according to the given interface.
  280          * We might eventually have to separate the notion of "link" from
  281          * "interface" and provide a user interface to set the default.
  282          */
  283         SCOPE6_LOCK();
  284         if (ifp) {
  285                 V_sid_default.s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] =
  286                         ifp->if_index;
  287                 V_sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] =
  288                         ifp->if_index;
  289         } else {
  290                 V_sid_default.s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = 0;
  291                 V_sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 0;
  292         }
  293         SCOPE6_UNLOCK();
  294 }
  295 
  296 int
  297 scope6_get_default(struct scope6_id *idlist)
  298 {
  299 
  300         SCOPE6_LOCK();
  301         *idlist = V_sid_default;
  302         SCOPE6_UNLOCK();
  303 
  304         return (0);
  305 }
  306 
  307 u_int32_t
  308 scope6_addr2default(struct in6_addr *addr)
  309 {
  310         u_int32_t id;
  311 
  312         /*
  313          * special case: The loopback address should be considered as
  314          * link-local, but there's no ambiguity in the syntax.
  315          */
  316         if (IN6_IS_ADDR_LOOPBACK(addr))
  317                 return (0);
  318 
  319         /*
  320          * XXX: 32-bit read is atomic on all our platforms, is it OK
  321          * not to lock here?
  322          */
  323         SCOPE6_LOCK();
  324         id = V_sid_default.s6id_list[in6_addrscope(addr)];
  325         SCOPE6_UNLOCK();
  326         return (id);
  327 }
  328 
  329 /*
  330  * Validate the specified scope zone ID in the sin6_scope_id field.  If the ID
  331  * is unspecified (=0), needs to be specified, and the default zone ID can be
  332  * used, the default value will be used.
  333  * This routine then generates the kernel-internal form: if the address scope
  334  * of is interface-local or link-local, embed the interface index in the
  335  * address.
  336  */
  337 int
  338 sa6_embedscope(struct sockaddr_in6 *sin6, int defaultok)
  339 {
  340         struct ifnet *ifp;
  341         u_int32_t zoneid;
  342 
  343         if ((zoneid = sin6->sin6_scope_id) == 0 && defaultok)
  344                 zoneid = scope6_addr2default(&sin6->sin6_addr);
  345 
  346         if (zoneid != 0 &&
  347             (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) ||
  348             IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr))) {
  349                 /*
  350                  * At this moment, we only check interface-local and
  351                  * link-local scope IDs, and use interface indices as the
  352                  * zone IDs assuming a one-to-one mapping between interfaces
  353                  * and links.
  354                  */
  355                 if (V_if_index < zoneid)
  356                         return (ENXIO);
  357                 ifp = ifnet_byindex(zoneid);
  358                 if (ifp == NULL) /* XXX: this can happen for some OS */
  359                         return (ENXIO);
  360 
  361                 /* XXX assignment to 16bit from 32bit variable */
  362                 sin6->sin6_addr.s6_addr16[1] = htons(zoneid & 0xffff);
  363 
  364                 sin6->sin6_scope_id = 0;
  365         }
  366 
  367         return 0;
  368 }
  369 
  370 /*
  371  * generate standard sockaddr_in6 from embedded form.
  372  */
  373 int
  374 sa6_recoverscope(struct sockaddr_in6 *sin6)
  375 {
  376         char ip6buf[INET6_ADDRSTRLEN];
  377         u_int32_t zoneid;
  378 
  379         if (sin6->sin6_scope_id != 0) {
  380                 log(LOG_NOTICE,
  381                     "sa6_recoverscope: assumption failure (non 0 ID): %s%%%d\n",
  382                     ip6_sprintf(ip6buf, &sin6->sin6_addr), sin6->sin6_scope_id);
  383                 /* XXX: proceed anyway... */
  384         }
  385         if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) ||
  386             IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr)) {
  387                 /*
  388                  * KAME assumption: link id == interface id
  389                  */
  390                 zoneid = ntohs(sin6->sin6_addr.s6_addr16[1]);
  391                 if (zoneid) {
  392                         /* sanity check */
  393                         if (zoneid < 0 || V_if_index < zoneid)
  394                                 return (ENXIO);
  395                         if (!ifnet_byindex(zoneid))
  396                                 return (ENXIO);
  397                         sin6->sin6_addr.s6_addr16[1] = 0;
  398                         sin6->sin6_scope_id = zoneid;
  399                 }
  400         }
  401 
  402         return 0;
  403 }
  404 
  405 /*
  406  * Determine the appropriate scope zone ID for in6 and ifp.  If ret_id is
  407  * non NULL, it is set to the zone ID.  If the zone ID needs to be embedded
  408  * in the in6_addr structure, in6 will be modified.
  409  *
  410  * ret_id - unnecessary?
  411  */
  412 int
  413 in6_setscope(struct in6_addr *in6, struct ifnet *ifp, u_int32_t *ret_id)
  414 {
  415         int scope;
  416         u_int32_t zoneid = 0;
  417         struct scope6_id *sid;
  418 
  419         IF_AFDATA_LOCK(ifp);
  420 
  421         sid = SID(ifp);
  422 
  423 #ifdef DIAGNOSTIC
  424         if (sid == NULL) { /* should not happen */
  425                 panic("in6_setscope: scope array is NULL");
  426                 /* NOTREACHED */
  427         }
  428 #endif
  429 
  430         /*
  431          * special case: the loopback address can only belong to a loopback
  432          * interface.
  433          */
  434         if (IN6_IS_ADDR_LOOPBACK(in6)) {
  435                 if (!(ifp->if_flags & IFF_LOOPBACK)) {
  436                         IF_AFDATA_UNLOCK(ifp);
  437                         return (EINVAL);
  438                 } else {
  439                         if (ret_id != NULL)
  440                                 *ret_id = 0; /* there's no ambiguity */
  441                         IF_AFDATA_UNLOCK(ifp);
  442                         return (0);
  443                 }
  444         }
  445 
  446         scope = in6_addrscope(in6);
  447 
  448         SCOPE6_LOCK();
  449         switch (scope) {
  450         case IPV6_ADDR_SCOPE_INTFACELOCAL: /* should be interface index */
  451                 zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL];
  452                 break;
  453 
  454         case IPV6_ADDR_SCOPE_LINKLOCAL:
  455                 zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL];
  456                 break;
  457 
  458         case IPV6_ADDR_SCOPE_SITELOCAL:
  459                 zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL];
  460                 break;
  461 
  462         case IPV6_ADDR_SCOPE_ORGLOCAL:
  463                 zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL];
  464                 break;
  465 
  466         default:
  467                 zoneid = 0;     /* XXX: treat as global. */
  468                 break;
  469         }
  470         SCOPE6_UNLOCK();
  471         IF_AFDATA_UNLOCK(ifp);
  472 
  473         if (ret_id != NULL)
  474                 *ret_id = zoneid;
  475 
  476         if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6))
  477                 in6->s6_addr16[1] = htons(zoneid & 0xffff); /* XXX */
  478 
  479         return (0);
  480 }
  481 
  482 /*
  483  * Just clear the embedded scope identifier.  Return 0 if the original address
  484  * is intact; return non 0 if the address is modified.
  485  */
  486 int
  487 in6_clearscope(struct in6_addr *in6)
  488 {
  489         int modified = 0;
  490 
  491         if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6)) {
  492                 if (in6->s6_addr16[1] != 0)
  493                         modified = 1;
  494                 in6->s6_addr16[1] = 0;
  495         }
  496 
  497         return (modified);
  498 }

Cache object: 173acf4a513795ff0806c92ff521121e


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.