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
_init(void)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
_fini(void)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
_info(struct modinfo * modinfop)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
mac_ipv6_unicst_verify(const void * addr,void * pdata)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 *
mac_ipv6_header(const void * saddr,const void * daddr,uint32_t sap,void * pdata,mblk_t * payload,size_t extra_len)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
mac_ipv6_header_info(mblk_t * mp,void * pdata,mac_header_info_t * hdr_info)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
mac_ipv6_pdata_verify(void * pdata,size_t pdata_size)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 };
284