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

Cache object: 9798ec48cb4906992ddbfdba084f7a5d


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