1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 /*
  27  * DL_IPV6 MAC Type plugin for the Nemo mac module
  28  */
  29 
  30 #include <sys/types.h>
  31 #include <sys/modctl.h>
  32 #include <sys/dlpi.h>
  33 #include <sys/mac.h>
  34 #include <sys/mac_ipv6.h>
  35 #include <sys/mac_ipv4_impl.h>
  36 #include <sys/byteorder.h>
  37 #include <sys/strsun.h>
  38 #include <netinet/ip6.h>
  39 #include <inet/common.h>
  40 #include <inet/mib2.h>
  41 #include <inet/ip.h>
  42 #include <inet/ip6.h>
  43 #include <inet/iptun.h>
  44 
  45 static struct modlmisc mac_ipv6_modlmisc = {
  46         &mod_miscops,
  47         "IPv6 tunneling MAC plugin"
  48 };
  49 
  50 static struct modlinkage mac_ipv6_modlinkage = {
  51         MODREV_1,
  52         {   &mac_ipv6_modlmisc,
  53             NULL }
  54 };
  55 
  56 static mactype_ops_t mac_ipv6_type_ops;
  57 
  58 int
  59 _init(void)
  60 {
  61         mactype_register_t *mtrp;
  62         int     err;
  63 
  64         if ((mtrp = mactype_alloc(MACTYPE_VERSION)) == NULL)
  65                 return (EINVAL);
  66         mtrp->mtr_ident = MAC_PLUGIN_IDENT_IPV6;
  67         mtrp->mtr_ops = &mac_ipv6_type_ops;
  68         mtrp->mtr_mactype = DL_IPV6;
  69         mtrp->mtr_nativetype = DL_IPV6;
  70         mtrp->mtr_addrlen = sizeof (in6_addr_t);
  71         if ((err = mactype_register(mtrp)) == 0) {
  72                 if ((err = mod_install(&mac_ipv6_modlinkage)) != 0)
  73                         (void) mactype_unregister(MAC_PLUGIN_IDENT_IPV6);
  74         }
  75         mactype_free(mtrp);
  76         return (err);
  77 }
  78 
  79 int
  80 _fini(void)
  81 {
  82         int     err;
  83         if ((err = mactype_unregister(MAC_PLUGIN_IDENT_IPV6)) != 0)
  84                 return (err);
  85         return (mod_remove(&mac_ipv6_modlinkage));
  86 }
  87 
  88 int
  89 _info(struct modinfo *modinfop)
  90 {
  91         return (mod_info(&mac_ipv6_modlinkage, modinfop));
  92 }
  93 
  94 
  95 /*
  96  * MAC Type plugin operations
  97  */
  98 
  99 /* ARGSUSED */
 100 int
 101 mac_ipv6_unicst_verify(const void *addr, void *pdata)
 102 {
 103         const in6_addr_t *in6addr = addr;
 104         if (IN6_IS_ADDR_UNSPECIFIED(in6addr) ||
 105             IN6_IS_ADDR_LOOPBACK(in6addr) ||
 106             IN6_IS_ADDR_MULTICAST(in6addr) ||
 107             IN6_IS_ADDR_V4MAPPED(in6addr) ||
 108             IN6_IS_ADDR_V4COMPAT(in6addr)) {
 109                 return (EINVAL);
 110         }
 111         return (0);
 112 }
 113 
 114 /*
 115  * Build an IPv6 link-layer header for tunneling.  If provided, the
 116  * template header provided by the driver supplies the traffic class, flow
 117  * label, hop limit, and potential options.  The template's payload length
 118  * must either be 0 if there are no extension headers, or reflect the size
 119  * of the extension headers if present.  The template's next header value
 120  * must either be IPPROTO_NONE if no extension headers are present, or
 121  * reflect the type of extension header that follows (the same is true for
 122  * the field values of the extension headers themselves.)
 123  */
 124 /* ARGSUSED */
 125 mblk_t *
 126 mac_ipv6_header(const void *saddr, const void *daddr, uint32_t sap, void *pdata,
 127     mblk_t *payload, size_t extra_len)
 128 {
 129         ip6_t   *ip6hp;
 130         ip6_t   *tmpl_ip6hp = pdata;
 131         mblk_t  *mp;
 132         size_t  hdr_len = sizeof (ip6_t);
 133         uint8_t *nxt_proto;
 134 
 135         if (!mac_ipv4_sap_verify(sap, NULL, NULL))
 136                 return (NULL);
 137 
 138         if (tmpl_ip6hp != NULL)
 139                 hdr_len = sizeof (ip6_t) + tmpl_ip6hp->ip6_plen;
 140 
 141         if ((mp = allocb(hdr_len + extra_len, BPRI_HI)) == NULL)
 142                 return (NULL);
 143 
 144         ip6hp = (ip6_t *)mp->b_rptr;
 145 
 146         bzero(ip6hp, hdr_len + extra_len);
 147         if (tmpl_ip6hp != NULL) {
 148                 bcopy(tmpl_ip6hp, ip6hp, hdr_len);
 149         } else {
 150                 ip6hp->ip6_nxt = IPPROTO_NONE;
 151                 ip6hp->ip6_hlim = IPTUN_DEFAULT_HOPLIMIT;
 152         }
 153 
 154         ip6hp->ip6_vcf = IPV6_DEFAULT_VERS_AND_FLOW;
 155         ip6hp->ip6_plen = 0;
 156 
 157         nxt_proto = &ip6hp->ip6_nxt;
 158         if (*nxt_proto != IPPROTO_NONE) {
 159                 ip6_dest_t *hdrptr = (ip6_dest_t *)(ip6hp + 1);
 160                 nxt_proto = &hdrptr->ip6d_nxt;
 161                 while (*nxt_proto != IPPROTO_NONE) {
 162                         hdrptr = (ip6_dest_t *)((uint8_t *)hdrptr +
 163                             (8 * (hdrptr->ip6d_len + 1)));
 164                         nxt_proto = &hdrptr->ip6d_nxt;
 165                 }
 166         }
 167         *nxt_proto = (uint8_t)sap;
 168         bcopy(saddr, &(ip6hp->ip6_src), sizeof (in6_addr_t));
 169         bcopy(daddr, &(ip6hp->ip6_dst), sizeof (in6_addr_t));
 170 
 171         mp->b_wptr += hdr_len;
 172         return (mp);
 173 }
 174 
 175 /* ARGSUSED */
 176 int
 177 mac_ipv6_header_info(mblk_t *mp, void *pdata, mac_header_info_t *hdr_info)
 178 {
 179         ip6_t   *ip6hp;
 180         uint8_t *whereptr, *endptr;
 181         uint8_t nexthdr;
 182 
 183         if (MBLKL(mp) < sizeof (ip6_t))
 184                 return (EINVAL);
 185 
 186         ip6hp = (ip6_t *)mp->b_rptr;
 187 
 188         /*
 189          * IPv6 tunnels don't have a concept of link-layer multicast since
 190          * they have fixed unicast endpoints.
 191          */
 192         if (mac_ipv6_unicst_verify(&ip6hp->ip6_dst, NULL) != 0)
 193                 return (EINVAL);
 194 
 195         nexthdr = ip6hp->ip6_nxt;
 196         whereptr = (uint8_t *)(ip6hp + 1);
 197         endptr = mp->b_wptr;
 198         while (nexthdr != IPPROTO_ENCAP && nexthdr != IPPROTO_IPV6) {
 199                 ip6_dest_t      *exthdrptr = (ip6_dest_t *)whereptr;
 200 
 201                 if (whereptr + sizeof (ip6_dest_t) >= endptr)
 202                         return (EINVAL);
 203 
 204                 nexthdr = exthdrptr->ip6d_nxt;
 205                 whereptr += 8 * (exthdrptr->ip6d_len + 1);
 206 
 207                 if (whereptr > endptr)
 208                         return (EINVAL);
 209         }
 210 
 211         hdr_info->mhi_hdrsize = whereptr - mp->b_rptr;
 212         hdr_info->mhi_pktsize = 0;
 213         hdr_info->mhi_daddr = (const uint8_t *)&(ip6hp->ip6_dst);
 214         hdr_info->mhi_saddr = (const uint8_t *)&(ip6hp->ip6_src);
 215         hdr_info->mhi_bindsap = hdr_info->mhi_origsap = nexthdr;
 216         hdr_info->mhi_dsttype = MAC_ADDRTYPE_UNICAST;
 217         return (0);
 218 }
 219 
 220 /*
 221  * This plugin's MAC plugin data is a template IPv6 header followed by
 222  * optional extension headers.  The chain of headers must be terminated by
 223  * a header with a next header value of IPPROTO_NONE.  The payload length
 224  * of the IPv6 header must be 0 if there are no extension headers, or must
 225  * reflect the total size of extension headers present.
 226  */
 227 boolean_t
 228 mac_ipv6_pdata_verify(void *pdata, size_t pdata_size)
 229 {
 230         ip6_t   *ip6hp = pdata;
 231         uint8_t *whereptr, *endptr;
 232         uint8_t nexthdr;
 233 
 234         /*
 235          * Since the plugin does not require plugin data, it is acceptable
 236          * for drivers to pass in NULL plugin data as long as the plugin
 237          * data size is consistent.
 238          */
 239         if (pdata == NULL)
 240                 return (pdata_size == 0);
 241 
 242         /* First verify that we have enough data to hold an IPv6 header. */
 243         if (pdata_size < sizeof (ip6_t))
 244                 return (B_FALSE);
 245         /* Make sure that pdata_size is consistent with the payload length. */
 246         if (pdata_size != sizeof (ip6_t) + ip6hp->ip6_plen)
 247                 return (B_FALSE);
 248 
 249         /*
 250          * Make sure that the header chain is terminated by a header with a
 251          * next header value of IPPROTO_NONE.
 252          */
 253         nexthdr = ip6hp->ip6_nxt;
 254         if (nexthdr == IPPROTO_NONE)
 255                 return (ip6hp->ip6_plen == 0);
 256         whereptr = (uint8_t *)(ip6hp + 1);
 257         endptr = (uint8_t *)pdata + pdata_size;
 258 
 259         while (nexthdr != IPPROTO_NONE && whereptr < endptr) {
 260                 ip6_dest_t *hdrptr = (ip6_dest_t *)whereptr;
 261 
 262                 /* make sure we're pointing at a complete header */
 263                 if (whereptr + sizeof (ip6_dest_t) > endptr)
 264                         break;
 265                 nexthdr = hdrptr->ip6d_nxt;
 266                 whereptr += 8 * (hdrptr->ip6d_len + 1);
 267         }
 268 
 269         return (nexthdr == IPPROTO_NONE && whereptr == endptr);
 270 }
 271 
 272 static mactype_ops_t mac_ipv6_type_ops = {
 273         MTOPS_PDATA_VERIFY,
 274         mac_ipv6_unicst_verify,
 275         mac_ipv4_multicst_verify, /* neither plugin supports multicast */
 276         mac_ipv4_sap_verify,    /* same set of legal SAP values */
 277         mac_ipv6_header,
 278         mac_ipv6_header_info,
 279         mac_ipv6_pdata_verify,
 280         NULL,
 281         NULL,
 282         NULL
 283 };