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

Cache object: bc6e40f087503c786c566cf9b61c261f


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