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-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-2  -  FREEBSD-11-1  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-4  -  FREEBSD-10-3  -  FREEBSD-10-2  -  FREEBSD-10-1  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-3  -  FREEBSD-9-2  -  FREEBSD-9-1  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-4  -  FREEBSD-8-3  -  FREEBSD-8-2  -  FREEBSD-8-1  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-4  -  FREEBSD-7-3  -  FREEBSD-7-2  -  FREEBSD-7-1  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-4  -  FREEBSD-6-3  -  FREEBSD-6-2  -  FREEBSD-6-1  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-5  -  FREEBSD-5-4  -  FREEBSD-5-3  -  FREEBSD-5-2  -  FREEBSD-5-1  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  linux-2.6  -  linux-2.4.22  -  MK83  -  MK84  -  PLAN9  -  DFBSD  -  NETBSD  -  NETBSD5  -  NETBSD4  -  NETBSD3  -  NETBSD20  -  OPENBSD  -  xnu-517  -  xnu-792  -  xnu-792.6.70  -  xnu-1228  -  xnu-1456.1.26  -  xnu-1699.24.8  -  xnu-2050.18.24  -  OPENSOLARIS  -  minix-3-1-1 
SearchContext: -  none  -  3  -  10 

    1 /*      $FreeBSD: releng/6.2/sys/netinet6/scope6.c 152060 2005-11-04 20:26:16Z ume $    */
    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()
   71 {
   72 
   73         SCOPE6_LOCK_INIT();
   74         bzero(&sid_default, sizeof(sid_default));
   75 }
   76 
   77 struct scope6_id *
   78 scope6_ifattach(ifp)
   79         struct ifnet *ifp;
   80 {
   81         struct scope6_id *sid;
   82 
   83         sid = (struct scope6_id *)malloc(sizeof(*sid), M_IFADDR, M_WAITOK);
   84         bzero(sid, sizeof(*sid));
   85 
   86         /*
   87          * XXX: IPV6_ADDR_SCOPE_xxx macros are not standard.
   88          * Should we rather hardcode here?
   89          */
   90         sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = ifp->if_index;
   91         sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = ifp->if_index;
   92 #ifdef MULTI_SCOPE
   93         /* by default, we don't care about scope boundary for these scopes. */
   94         sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL] = 1;
   95         sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL] = 1;
   96 #endif
   97 
   98         return sid;
   99 }
  100 
  101 void
  102 scope6_ifdetach(sid)
  103         struct scope6_id *sid;
  104 {
  105 
  106         free(sid, M_IFADDR);
  107 }
  108 
  109 int
  110 scope6_set(ifp, idlist)
  111         struct ifnet *ifp;
  112         struct scope6_id *idlist;
  113 {
  114         int i;
  115         int error = 0;
  116         struct scope6_id *sid = NULL;
  117 
  118         IF_AFDATA_LOCK(ifp);
  119         sid = SID(ifp);
  120 
  121         if (!sid) {     /* paranoid? */
  122                 IF_AFDATA_UNLOCK(ifp);
  123                 return (EINVAL);
  124         }
  125 
  126         /*
  127          * XXX: We need more consistency checks of the relationship among
  128          * scopes (e.g. an organization should be larger than a site).
  129          */
  130 
  131         /*
  132          * TODO(XXX): after setting, we should reflect the changes to
  133          * interface addresses, routing table entries, PCB entries...
  134          */
  135 
  136         SCOPE6_LOCK();
  137         for (i = 0; i < 16; i++) {
  138                 if (idlist->s6id_list[i] &&
  139                     idlist->s6id_list[i] != sid->s6id_list[i]) {
  140                         /*
  141                          * An interface zone ID must be the corresponding
  142                          * interface index by definition.
  143                          */
  144                         if (i == IPV6_ADDR_SCOPE_INTFACELOCAL &&
  145                             idlist->s6id_list[i] != ifp->if_index) {
  146                                 IF_AFDATA_UNLOCK(ifp);
  147                                 SCOPE6_UNLOCK();
  148                                 return (EINVAL);
  149                         }
  150 
  151                         if (i == IPV6_ADDR_SCOPE_LINKLOCAL &&
  152                             idlist->s6id_list[i] > if_index) {
  153                                 /*
  154                                  * XXX: theoretically, there should be no
  155                                  * relationship between link IDs and interface
  156                                  * IDs, but we check the consistency for
  157                                  * safety in later use.
  158                                  */
  159                                 IF_AFDATA_UNLOCK(ifp);
  160                                 SCOPE6_UNLOCK();
  161                                 return (EINVAL);
  162                         }
  163 
  164                         /*
  165                          * XXX: we must need lots of work in this case,
  166                          * but we simply set the new value in this initial
  167                          * implementation.
  168                          */
  169                         sid->s6id_list[i] = idlist->s6id_list[i];
  170                 }
  171         }
  172         SCOPE6_UNLOCK();
  173         IF_AFDATA_UNLOCK(ifp);
  174 
  175         return (error);
  176 }
  177 
  178 int
  179 scope6_get(ifp, idlist)
  180         struct ifnet *ifp;
  181         struct scope6_id *idlist;
  182 {
  183         /* We only need to lock the interface's afdata for SID() to work. */
  184         IF_AFDATA_LOCK(ifp);
  185         struct scope6_id *sid = SID(ifp);
  186 
  187         if (sid == NULL) {      /* paranoid? */
  188                 IF_AFDATA_UNLOCK(ifp);
  189                 return (EINVAL);
  190         }
  191 
  192         SCOPE6_LOCK();
  193         *idlist = *sid;
  194         SCOPE6_UNLOCK();
  195 
  196         IF_AFDATA_UNLOCK(ifp);
  197         return (0);
  198 }
  199 
  200 
  201 /*
  202  * Get a scope of the address. Node-local, link-local, site-local or global.
  203  */
  204 int
  205 in6_addrscope(addr)
  206         struct in6_addr *addr;
  207 {
  208         int scope;
  209 
  210         if (addr->s6_addr[0] == 0xfe) {
  211                 scope = addr->s6_addr[1] & 0xc0;
  212 
  213                 switch (scope) {
  214                 case 0x80:
  215                         return IPV6_ADDR_SCOPE_LINKLOCAL;
  216                         break;
  217                 case 0xc0:
  218                         return IPV6_ADDR_SCOPE_SITELOCAL;
  219                         break;
  220                 default:
  221                         return IPV6_ADDR_SCOPE_GLOBAL; /* just in case */
  222                         break;
  223                 }
  224         }
  225 
  226 
  227         if (addr->s6_addr[0] == 0xff) {
  228                 scope = addr->s6_addr[1] & 0x0f;
  229 
  230                 /*
  231                  * due to other scope such as reserved,
  232                  * return scope doesn't work.
  233                  */
  234                 switch (scope) {
  235                 case IPV6_ADDR_SCOPE_INTFACELOCAL:
  236                         return IPV6_ADDR_SCOPE_INTFACELOCAL;
  237                         break;
  238                 case IPV6_ADDR_SCOPE_LINKLOCAL:
  239                         return IPV6_ADDR_SCOPE_LINKLOCAL;
  240                         break;
  241                 case IPV6_ADDR_SCOPE_SITELOCAL:
  242                         return IPV6_ADDR_SCOPE_SITELOCAL;
  243                         break;
  244                 default:
  245                         return IPV6_ADDR_SCOPE_GLOBAL;
  246                         break;
  247                 }
  248         }
  249 
  250         /*
  251          * Regard loopback and unspecified addresses as global, since
  252          * they have no ambiguity.
  253          */
  254         if (bcmp(&in6addr_loopback, addr, sizeof(*addr) - 1) == 0) {
  255                 if (addr->s6_addr[15] == 1) /* loopback */
  256                         return IPV6_ADDR_SCOPE_LINKLOCAL;
  257                 if (addr->s6_addr[15] == 0) /* unspecified */
  258                         return IPV6_ADDR_SCOPE_GLOBAL; /* XXX: correct? */
  259         }
  260 
  261         return IPV6_ADDR_SCOPE_GLOBAL;
  262 }
  263 
  264 void
  265 scope6_setdefault(ifp)
  266         struct ifnet *ifp;      /* note that this might be NULL */
  267 {
  268         /*
  269          * Currently, this function just sets the default "interfaces"
  270          * and "links" according to the given interface.
  271          * We might eventually have to separate the notion of "link" from
  272          * "interface" and provide a user interface to set the default.
  273          */
  274         SCOPE6_LOCK();
  275         if (ifp) {
  276                 sid_default.s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] =
  277                         ifp->if_index;
  278                 sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] =
  279                         ifp->if_index;
  280         } else {
  281                 sid_default.s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = 0;
  282                 sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 0;
  283         }
  284         SCOPE6_UNLOCK();
  285 }
  286 
  287 int
  288 scope6_get_default(idlist)
  289         struct scope6_id *idlist;
  290 {
  291 
  292         SCOPE6_LOCK();
  293         *idlist = sid_default;
  294         SCOPE6_UNLOCK();
  295 
  296         return (0);
  297 }
  298 
  299 u_int32_t
  300 scope6_addr2default(addr)
  301         struct in6_addr *addr;
  302 {
  303         u_int32_t id;
  304 
  305         /*
  306          * special case: The loopback address should be considered as
  307          * link-local, but there's no ambiguity in the syntax.
  308          */
  309         if (IN6_IS_ADDR_LOOPBACK(addr))
  310                 return (0);
  311 
  312         /*
  313          * XXX: 32-bit read is atomic on all our platforms, is it OK
  314          * not to lock here?
  315          */
  316         SCOPE6_LOCK();
  317         id = sid_default.s6id_list[in6_addrscope(addr)];
  318         SCOPE6_UNLOCK();
  319         return (id);
  320 }
  321 
  322 /*
  323  * Validate the specified scope zone ID in the sin6_scope_id field.  If the ID
  324  * is unspecified (=0), needs to be specified, and the default zone ID can be
  325  * used, the default value will be used.
  326  * This routine then generates the kernel-internal form: if the address scope
  327  * of is interface-local or link-local, embed the interface index in the
  328  * address.
  329  */
  330 int
  331 sa6_embedscope(sin6, defaultok)
  332         struct sockaddr_in6 *sin6;
  333         int defaultok;
  334 {
  335         struct ifnet *ifp;
  336         u_int32_t zoneid;
  337 
  338         if ((zoneid = sin6->sin6_scope_id) == 0 && defaultok)
  339                 zoneid = scope6_addr2default(&sin6->sin6_addr);
  340 
  341         if (zoneid != 0 &&
  342             (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) ||
  343             IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr))) {
  344                 /*
  345                  * At this moment, we only check interface-local and
  346                  * link-local scope IDs, and use interface indices as the
  347                  * zone IDs assuming a one-to-one mapping between interfaces
  348                  * and links.
  349                  */
  350                 if (if_index < zoneid)
  351                         return (ENXIO);
  352                 ifp = ifnet_byindex(zoneid);
  353                 if (ifp == NULL) /* XXX: this can happen for some OS */
  354                         return (ENXIO);
  355 
  356                 /* XXX assignment to 16bit from 32bit variable */
  357                 sin6->sin6_addr.s6_addr16[1] = htons(zoneid & 0xffff);
  358 
  359                 sin6->sin6_scope_id = 0;
  360         }
  361 
  362         return 0;
  363 }
  364 
  365 /*
  366  * generate standard sockaddr_in6 from embedded form.
  367  */
  368 int
  369 sa6_recoverscope(sin6)
  370         struct sockaddr_in6 *sin6;
  371 {
  372         u_int32_t zoneid;
  373 
  374         if (sin6->sin6_scope_id != 0) {
  375                 log(LOG_NOTICE,
  376                     "sa6_recoverscope: assumption failure (non 0 ID): %s%%%d\n",
  377                     ip6_sprintf(&sin6->sin6_addr), sin6->sin6_scope_id);
  378                 /* XXX: proceed anyway... */
  379         }
  380         if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) ||
  381             IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr)) {
  382                 /*
  383                  * KAME assumption: link id == interface id
  384                  */
  385                 zoneid = ntohs(sin6->sin6_addr.s6_addr16[1]);
  386                 if (zoneid) {
  387                         /* sanity check */
  388                         if (zoneid < 0 || if_index < zoneid)
  389                                 return (ENXIO);
  390                         if (!ifnet_byindex(zoneid))
  391                                 return (ENXIO);
  392                         sin6->sin6_addr.s6_addr16[1] = 0;
  393                         sin6->sin6_scope_id = zoneid;
  394                 }
  395         }
  396 
  397         return 0;
  398 }
  399 
  400 /*
  401  * Determine the appropriate scope zone ID for in6 and ifp.  If ret_id is
  402  * non NULL, it is set to the zone ID.  If the zone ID needs to be embedded
  403  * in the in6_addr structure, in6 will be modified.
  404  */
  405 int
  406 in6_setscope(in6, ifp, ret_id)
  407         struct in6_addr *in6;
  408         struct ifnet *ifp;
  409         u_int32_t *ret_id;      /* unnecessary? */
  410 {
  411         int scope;
  412         u_int32_t zoneid = 0;
  413         struct scope6_id *sid;
  414 
  415         IF_AFDATA_LOCK(ifp);
  416 
  417         sid = SID(ifp);
  418 
  419 #ifdef DIAGNOSTIC
  420         if (sid == NULL) { /* should not happen */
  421                 panic("in6_setscope: scope array is NULL");
  422                 /* NOTREACHED */
  423         }
  424 #endif
  425 
  426         /*
  427          * special case: the loopback address can only belong to a loopback
  428          * interface.
  429          */
  430         if (IN6_IS_ADDR_LOOPBACK(in6)) {
  431                 if (!(ifp->if_flags & IFF_LOOPBACK)) {
  432                         IF_AFDATA_UNLOCK(ifp);
  433                         return (EINVAL);
  434                 } else {
  435                         if (ret_id != NULL)
  436                                 *ret_id = 0; /* there's no ambiguity */
  437                         IF_AFDATA_UNLOCK(ifp);
  438                         return (0);
  439                 }
  440         }
  441 
  442         scope = in6_addrscope(in6);
  443 
  444         SCOPE6_LOCK();
  445         switch (scope) {
  446         case IPV6_ADDR_SCOPE_INTFACELOCAL: /* should be interface index */
  447                 zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL];
  448                 break;
  449 
  450         case IPV6_ADDR_SCOPE_LINKLOCAL:
  451                 zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL];
  452                 break;
  453 
  454         case IPV6_ADDR_SCOPE_SITELOCAL:
  455                 zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_SITELOCAL];
  456                 break;
  457 
  458         case IPV6_ADDR_SCOPE_ORGLOCAL:
  459                 zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL];
  460                 break;
  461 
  462         default:
  463                 zoneid = 0;     /* XXX: treat as global. */
  464                 break;
  465         }
  466         SCOPE6_UNLOCK();
  467         IF_AFDATA_UNLOCK(ifp);
  468 
  469         if (ret_id != NULL)
  470                 *ret_id = zoneid;
  471 
  472         if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6))
  473                 in6->s6_addr16[1] = htons(zoneid & 0xffff); /* XXX */
  474 
  475         return (0);
  476 }
  477 
  478 /*
  479  * Just clear the embedded scope identifier.  Return 0 if the original address
  480  * is intact; return non 0 if the address is modified.
  481  */
  482 int
  483 in6_clearscope(in6)
  484         struct in6_addr *in6;
  485 {
  486         int modified = 0;
  487 
  488         if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6)) {
  489                 if (in6->s6_addr16[1] != 0)
  490                         modified = 1;
  491                 in6->s6_addr16[1] = 0;
  492         }
  493 
  494         return (modified);
  495 }

Cache object: 14b071f2d840115f66faf6d25bfd33ca


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