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

Cache object: 287cf508aa212872b0bd8a38fd7f8c8a


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