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

Cache object: c8015d985a193087145b565d53df4655


[ 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.