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 };
284