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

Cache object: 9aee60760cbf77ac7cbf598fbbf411ad


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