xref: /titanic_44/usr/src/uts/common/inet/ip/conn_opt.c (revision ac3e5be0db2e7e4fabda0a139e10da4702077a9d)
1bd670b35SErik Nordmark /*
2bd670b35SErik Nordmark  * CDDL HEADER START
3bd670b35SErik Nordmark  *
4bd670b35SErik Nordmark  * The contents of this file are subject to the terms of the
5bd670b35SErik Nordmark  * Common Development and Distribution License (the "License").
6bd670b35SErik Nordmark  * You may not use this file except in compliance with the License.
7bd670b35SErik Nordmark  *
8bd670b35SErik Nordmark  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9bd670b35SErik Nordmark  * or http://www.opensolaris.org/os/licensing.
10bd670b35SErik Nordmark  * See the License for the specific language governing permissions
11bd670b35SErik Nordmark  * and limitations under the License.
12bd670b35SErik Nordmark  *
13bd670b35SErik Nordmark  * When distributing Covered Code, include this CDDL HEADER in each
14bd670b35SErik Nordmark  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15bd670b35SErik Nordmark  * If applicable, add the following below this CDDL HEADER, with the
16bd670b35SErik Nordmark  * fields enclosed by brackets "[]" replaced with your own identifying
17bd670b35SErik Nordmark  * information: Portions Copyright [yyyy] [name of copyright owner]
18bd670b35SErik Nordmark  *
19bd670b35SErik Nordmark  * CDDL HEADER END
20bd670b35SErik Nordmark  */
21bd670b35SErik Nordmark 
22bd670b35SErik Nordmark /*
23*ac3e5be0SMarcel Telka  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
24bd670b35SErik Nordmark  */
25bd670b35SErik Nordmark /* Copyright (c) 1990 Mentat Inc. */
26bd670b35SErik Nordmark 
27bd670b35SErik Nordmark #include <sys/types.h>
28bd670b35SErik Nordmark #include <sys/stream.h>
29bd670b35SErik Nordmark #include <sys/strsun.h>
30bd670b35SErik Nordmark #define	_SUN_TPI_VERSION 2
31bd670b35SErik Nordmark #include <sys/tihdr.h>
32bd670b35SErik Nordmark #include <sys/xti_inet.h>
33bd670b35SErik Nordmark #include <sys/ucred.h>
34bd670b35SErik Nordmark #include <sys/zone.h>
35bd670b35SErik Nordmark #include <sys/ddi.h>
36bd670b35SErik Nordmark #include <sys/sunddi.h>
37bd670b35SErik Nordmark #include <sys/cmn_err.h>
38bd670b35SErik Nordmark #include <sys/debug.h>
39bd670b35SErik Nordmark #include <sys/atomic.h>
40bd670b35SErik Nordmark #include <sys/policy.h>
41bd670b35SErik Nordmark 
42bd670b35SErik Nordmark #include <sys/systm.h>
43bd670b35SErik Nordmark #include <sys/param.h>
44bd670b35SErik Nordmark #include <sys/kmem.h>
45bd670b35SErik Nordmark #include <sys/sdt.h>
46bd670b35SErik Nordmark #include <sys/socket.h>
47bd670b35SErik Nordmark #include <sys/ethernet.h>
48bd670b35SErik Nordmark #include <sys/mac.h>
49bd670b35SErik Nordmark #include <net/if.h>
50bd670b35SErik Nordmark #include <net/if_types.h>
51bd670b35SErik Nordmark #include <net/if_arp.h>
52bd670b35SErik Nordmark #include <net/route.h>
53bd670b35SErik Nordmark #include <sys/sockio.h>
54bd670b35SErik Nordmark #include <netinet/in.h>
55bd670b35SErik Nordmark #include <net/if_dl.h>
56bd670b35SErik Nordmark 
57bd670b35SErik Nordmark #include <inet/common.h>
58bd670b35SErik Nordmark #include <inet/mi.h>
59bd670b35SErik Nordmark #include <inet/mib2.h>
60bd670b35SErik Nordmark #include <inet/nd.h>
61bd670b35SErik Nordmark #include <inet/arp.h>
62bd670b35SErik Nordmark #include <inet/snmpcom.h>
63bd670b35SErik Nordmark #include <inet/kstatcom.h>
64bd670b35SErik Nordmark 
65bd670b35SErik Nordmark #include <netinet/igmp_var.h>
66bd670b35SErik Nordmark #include <netinet/ip6.h>
67bd670b35SErik Nordmark #include <netinet/icmp6.h>
68bd670b35SErik Nordmark #include <netinet/sctp.h>
69bd670b35SErik Nordmark 
70bd670b35SErik Nordmark #include <inet/ip.h>
71bd670b35SErik Nordmark #include <inet/ip_impl.h>
72bd670b35SErik Nordmark #include <inet/ip6.h>
73bd670b35SErik Nordmark #include <inet/ip6_asp.h>
74bd670b35SErik Nordmark #include <inet/tcp.h>
75bd670b35SErik Nordmark #include <inet/ip_multi.h>
76bd670b35SErik Nordmark #include <inet/ip_if.h>
77bd670b35SErik Nordmark #include <inet/ip_ire.h>
78bd670b35SErik Nordmark #include <inet/ip_ftable.h>
79bd670b35SErik Nordmark #include <inet/ip_rts.h>
80bd670b35SErik Nordmark #include <inet/optcom.h>
81bd670b35SErik Nordmark #include <inet/ip_ndp.h>
82bd670b35SErik Nordmark #include <inet/ip_listutils.h>
83bd670b35SErik Nordmark #include <netinet/igmp.h>
84bd670b35SErik Nordmark #include <netinet/ip_mroute.h>
85bd670b35SErik Nordmark #include <netinet/udp.h>
86bd670b35SErik Nordmark #include <inet/ipp_common.h>
87bd670b35SErik Nordmark 
88bd670b35SErik Nordmark #include <net/pfkeyv2.h>
89bd670b35SErik Nordmark #include <inet/sadb.h>
90bd670b35SErik Nordmark #include <inet/ipsec_impl.h>
91bd670b35SErik Nordmark #include <inet/ipdrop.h>
92bd670b35SErik Nordmark #include <inet/ip_netinfo.h>
93bd670b35SErik Nordmark 
94bd670b35SErik Nordmark #include <inet/ipclassifier.h>
95bd670b35SErik Nordmark #include <inet/sctp_ip.h>
96bd670b35SErik Nordmark #include <inet/sctp/sctp_impl.h>
97bd670b35SErik Nordmark #include <inet/udp_impl.h>
98bd670b35SErik Nordmark #include <sys/sunddi.h>
99bd670b35SErik Nordmark 
100bd670b35SErik Nordmark #include <sys/tsol/label.h>
101bd670b35SErik Nordmark #include <sys/tsol/tnet.h>
102bd670b35SErik Nordmark 
103bd670b35SErik Nordmark /*
104bd670b35SErik Nordmark  * Return how much size is needed for the different ancillary data items
105bd670b35SErik Nordmark  */
106bd670b35SErik Nordmark uint_t
conn_recvancillary_size(conn_t * connp,crb_t recv_ancillary,ip_recv_attr_t * ira,mblk_t * mp,ip_pkt_t * ipp)107bd670b35SErik Nordmark conn_recvancillary_size(conn_t *connp, crb_t recv_ancillary,
108bd670b35SErik Nordmark     ip_recv_attr_t *ira, mblk_t *mp, ip_pkt_t *ipp)
109bd670b35SErik Nordmark {
110bd670b35SErik Nordmark 	uint_t		ancil_size;
111bd670b35SErik Nordmark 	ip_stack_t	*ipst = connp->conn_netstack->netstack_ip;
112bd670b35SErik Nordmark 
113bd670b35SErik Nordmark 	/*
114bd670b35SErik Nordmark 	 * If IP_RECVDSTADDR is set we include the destination IP
115bd670b35SErik Nordmark 	 * address as an option. With IP_RECVOPTS we include all
116bd670b35SErik Nordmark 	 * the IP options.
117bd670b35SErik Nordmark 	 */
118bd670b35SErik Nordmark 	ancil_size = 0;
119bd670b35SErik Nordmark 	if (recv_ancillary.crb_recvdstaddr &&
120bd670b35SErik Nordmark 	    (ira->ira_flags & IRAF_IS_IPV4)) {
121bd670b35SErik Nordmark 		ancil_size += sizeof (struct T_opthdr) +
122bd670b35SErik Nordmark 		    sizeof (struct in_addr);
123bd670b35SErik Nordmark 		IP_STAT(ipst, conn_in_recvdstaddr);
124bd670b35SErik Nordmark 	}
125bd670b35SErik Nordmark 
126bd670b35SErik Nordmark 	/*
127bd670b35SErik Nordmark 	 * ip_recvpktinfo is used for both AF_INET and AF_INET6 but
128bd670b35SErik Nordmark 	 * are different
129bd670b35SErik Nordmark 	 */
130bd670b35SErik Nordmark 	if (recv_ancillary.crb_ip_recvpktinfo &&
131bd670b35SErik Nordmark 	    connp->conn_family == AF_INET) {
132bd670b35SErik Nordmark 		ancil_size += sizeof (struct T_opthdr) +
133bd670b35SErik Nordmark 		    sizeof (struct in_pktinfo);
134bd670b35SErik Nordmark 		IP_STAT(ipst, conn_in_recvpktinfo);
135bd670b35SErik Nordmark 	}
136bd670b35SErik Nordmark 
137bd670b35SErik Nordmark 	if ((recv_ancillary.crb_recvopts) &&
138bd670b35SErik Nordmark 	    (ipp->ipp_fields & IPPF_IPV4_OPTIONS)) {
139bd670b35SErik Nordmark 		ancil_size += sizeof (struct T_opthdr) +
140bd670b35SErik Nordmark 		    ipp->ipp_ipv4_options_len;
141bd670b35SErik Nordmark 		IP_STAT(ipst, conn_in_recvopts);
142bd670b35SErik Nordmark 	}
143bd670b35SErik Nordmark 
144bd670b35SErik Nordmark 	if (recv_ancillary.crb_recvslla) {
145bd670b35SErik Nordmark 		ip_stack_t *ipst = connp->conn_netstack->netstack_ip;
146bd670b35SErik Nordmark 		ill_t *ill;
147bd670b35SErik Nordmark 
148bd670b35SErik Nordmark 		/* Make sure ira_l2src is setup if not already */
149bd670b35SErik Nordmark 		if (!(ira->ira_flags & IRAF_L2SRC_SET)) {
150bd670b35SErik Nordmark 			ill = ill_lookup_on_ifindex(ira->ira_rifindex, B_FALSE,
151bd670b35SErik Nordmark 			    ipst);
152bd670b35SErik Nordmark 			if (ill != NULL) {
153bd670b35SErik Nordmark 				ip_setl2src(mp, ira, ill);
154bd670b35SErik Nordmark 				ill_refrele(ill);
155bd670b35SErik Nordmark 			}
156bd670b35SErik Nordmark 		}
157bd670b35SErik Nordmark 		ancil_size += sizeof (struct T_opthdr) +
158bd670b35SErik Nordmark 		    sizeof (struct sockaddr_dl);
159bd670b35SErik Nordmark 		IP_STAT(ipst, conn_in_recvslla);
160bd670b35SErik Nordmark 	}
161bd670b35SErik Nordmark 
162bd670b35SErik Nordmark 	if (recv_ancillary.crb_recvif) {
163bd670b35SErik Nordmark 		ancil_size += sizeof (struct T_opthdr) + sizeof (uint_t);
164bd670b35SErik Nordmark 		IP_STAT(ipst, conn_in_recvif);
165bd670b35SErik Nordmark 	}
166bd670b35SErik Nordmark 
167bd670b35SErik Nordmark 	/*
168bd670b35SErik Nordmark 	 * ip_recvpktinfo is used for both AF_INET and AF_INET6 but
169bd670b35SErik Nordmark 	 * are different
170bd670b35SErik Nordmark 	 */
171bd670b35SErik Nordmark 	if (recv_ancillary.crb_ip_recvpktinfo &&
172bd670b35SErik Nordmark 	    connp->conn_family == AF_INET6) {
173bd670b35SErik Nordmark 		ancil_size += sizeof (struct T_opthdr) +
174bd670b35SErik Nordmark 		    sizeof (struct in6_pktinfo);
175bd670b35SErik Nordmark 		IP_STAT(ipst, conn_in_recvpktinfo);
176bd670b35SErik Nordmark 	}
177bd670b35SErik Nordmark 
178bd670b35SErik Nordmark 	if (recv_ancillary.crb_ipv6_recvhoplimit) {
179bd670b35SErik Nordmark 		ancil_size += sizeof (struct T_opthdr) + sizeof (int);
180bd670b35SErik Nordmark 		IP_STAT(ipst, conn_in_recvhoplimit);
181bd670b35SErik Nordmark 	}
182bd670b35SErik Nordmark 
183bd670b35SErik Nordmark 	if (recv_ancillary.crb_ipv6_recvtclass) {
184bd670b35SErik Nordmark 		ancil_size += sizeof (struct T_opthdr) + sizeof (int);
185bd670b35SErik Nordmark 		IP_STAT(ipst, conn_in_recvtclass);
186bd670b35SErik Nordmark 	}
187bd670b35SErik Nordmark 
188bd670b35SErik Nordmark 	if (recv_ancillary.crb_ipv6_recvhopopts &&
189bd670b35SErik Nordmark 	    (ipp->ipp_fields & IPPF_HOPOPTS)) {
190bd670b35SErik Nordmark 		ancil_size += sizeof (struct T_opthdr) + ipp->ipp_hopoptslen;
191bd670b35SErik Nordmark 		IP_STAT(ipst, conn_in_recvhopopts);
192bd670b35SErik Nordmark 	}
193bd670b35SErik Nordmark 	/*
194bd670b35SErik Nordmark 	 * To honor RFC3542 when an application asks for both IPV6_RECVDSTOPTS
195bd670b35SErik Nordmark 	 * and IPV6_RECVRTHDR, we pass up the item rthdrdstopts (the destination
196bd670b35SErik Nordmark 	 * options that appear before a routing header.
197bd670b35SErik Nordmark 	 * We also pass them up if IPV6_RECVRTHDRDSTOPTS is set.
198bd670b35SErik Nordmark 	 */
199bd670b35SErik Nordmark 	if (ipp->ipp_fields & IPPF_RTHDRDSTOPTS) {
200bd670b35SErik Nordmark 		if (recv_ancillary.crb_ipv6_recvrthdrdstopts ||
201bd670b35SErik Nordmark 		    (recv_ancillary.crb_ipv6_recvdstopts &&
202bd670b35SErik Nordmark 		    recv_ancillary.crb_ipv6_recvrthdr)) {
203bd670b35SErik Nordmark 			ancil_size += sizeof (struct T_opthdr) +
204bd670b35SErik Nordmark 			    ipp->ipp_rthdrdstoptslen;
205bd670b35SErik Nordmark 			IP_STAT(ipst, conn_in_recvrthdrdstopts);
206bd670b35SErik Nordmark 		}
207bd670b35SErik Nordmark 	}
208bd670b35SErik Nordmark 	if ((recv_ancillary.crb_ipv6_recvrthdr) &&
209bd670b35SErik Nordmark 	    (ipp->ipp_fields & IPPF_RTHDR)) {
210bd670b35SErik Nordmark 		ancil_size += sizeof (struct T_opthdr) + ipp->ipp_rthdrlen;
211bd670b35SErik Nordmark 		IP_STAT(ipst, conn_in_recvrthdr);
212bd670b35SErik Nordmark 	}
213bd670b35SErik Nordmark 	if ((recv_ancillary.crb_ipv6_recvdstopts ||
214bd670b35SErik Nordmark 	    recv_ancillary.crb_old_ipv6_recvdstopts) &&
215bd670b35SErik Nordmark 	    (ipp->ipp_fields & IPPF_DSTOPTS)) {
216bd670b35SErik Nordmark 		ancil_size += sizeof (struct T_opthdr) + ipp->ipp_dstoptslen;
217bd670b35SErik Nordmark 		IP_STAT(ipst, conn_in_recvdstopts);
218bd670b35SErik Nordmark 	}
219bd670b35SErik Nordmark 	if (recv_ancillary.crb_recvucred && ira->ira_cred != NULL) {
22067dbe2beSCasper H.S. Dik 		ancil_size += sizeof (struct T_opthdr) +
22167dbe2beSCasper H.S. Dik 		    ucredminsize(ira->ira_cred);
222bd670b35SErik Nordmark 		IP_STAT(ipst, conn_in_recvucred);
223bd670b35SErik Nordmark 	}
224bd670b35SErik Nordmark 
225bd670b35SErik Nordmark 	/*
226bd670b35SErik Nordmark 	 * If SO_TIMESTAMP is set allocate the appropriate sized
227bd670b35SErik Nordmark 	 * buffer. Since gethrestime() expects a pointer aligned
228bd670b35SErik Nordmark 	 * argument, we allocate space necessary for extra
229bd670b35SErik Nordmark 	 * alignment (even though it might not be used).
230bd670b35SErik Nordmark 	 */
231bd670b35SErik Nordmark 	if (recv_ancillary.crb_timestamp) {
232bd670b35SErik Nordmark 		ancil_size += sizeof (struct T_opthdr) +
233bd670b35SErik Nordmark 		    sizeof (timestruc_t) + _POINTER_ALIGNMENT;
234bd670b35SErik Nordmark 		IP_STAT(ipst, conn_in_timestamp);
235bd670b35SErik Nordmark 	}
236bd670b35SErik Nordmark 
237bd670b35SErik Nordmark 	/*
238bd670b35SErik Nordmark 	 * If IP_RECVTTL is set allocate the appropriate sized buffer
239bd670b35SErik Nordmark 	 */
240bd670b35SErik Nordmark 	if (recv_ancillary.crb_recvttl &&
241bd670b35SErik Nordmark 	    (ira->ira_flags & IRAF_IS_IPV4)) {
242bd670b35SErik Nordmark 		ancil_size += sizeof (struct T_opthdr) + sizeof (uint8_t);
243bd670b35SErik Nordmark 		IP_STAT(ipst, conn_in_recvttl);
244bd670b35SErik Nordmark 	}
245bd670b35SErik Nordmark 
246bd670b35SErik Nordmark 	return (ancil_size);
247bd670b35SErik Nordmark }
248bd670b35SErik Nordmark 
249bd670b35SErik Nordmark /*
250bd670b35SErik Nordmark  * Lay down the ancillary data items at "ancil_buf".
251bd670b35SErik Nordmark  * Assumes caller has used conn_recvancillary_size to allocate a sufficiently
252bd670b35SErik Nordmark  * large buffer - ancil_size.
253bd670b35SErik Nordmark  */
254bd670b35SErik Nordmark void
conn_recvancillary_add(conn_t * connp,crb_t recv_ancillary,ip_recv_attr_t * ira,ip_pkt_t * ipp,uchar_t * ancil_buf,uint_t ancil_size)255bd670b35SErik Nordmark conn_recvancillary_add(conn_t *connp, crb_t recv_ancillary,
256bd670b35SErik Nordmark     ip_recv_attr_t *ira, ip_pkt_t *ipp, uchar_t *ancil_buf, uint_t ancil_size)
257bd670b35SErik Nordmark {
258bd670b35SErik Nordmark 	/*
259bd670b35SErik Nordmark 	 * Copy in destination address before options to avoid
260bd670b35SErik Nordmark 	 * any padding issues.
261bd670b35SErik Nordmark 	 */
262bd670b35SErik Nordmark 	if (recv_ancillary.crb_recvdstaddr &&
263bd670b35SErik Nordmark 	    (ira->ira_flags & IRAF_IS_IPV4)) {
264bd670b35SErik Nordmark 		struct T_opthdr *toh;
265bd670b35SErik Nordmark 		ipaddr_t *dstptr;
266bd670b35SErik Nordmark 
267bd670b35SErik Nordmark 		toh = (struct T_opthdr *)ancil_buf;
268bd670b35SErik Nordmark 		toh->level = IPPROTO_IP;
269bd670b35SErik Nordmark 		toh->name = IP_RECVDSTADDR;
270bd670b35SErik Nordmark 		toh->len = sizeof (struct T_opthdr) + sizeof (ipaddr_t);
271bd670b35SErik Nordmark 		toh->status = 0;
272bd670b35SErik Nordmark 		ancil_buf += sizeof (struct T_opthdr);
273bd670b35SErik Nordmark 		dstptr = (ipaddr_t *)ancil_buf;
274bd670b35SErik Nordmark 		*dstptr = ipp->ipp_addr_v4;
275bd670b35SErik Nordmark 		ancil_buf += sizeof (ipaddr_t);
276bd670b35SErik Nordmark 		ancil_size -= toh->len;
277bd670b35SErik Nordmark 	}
278bd670b35SErik Nordmark 
279bd670b35SErik Nordmark 	/*
280bd670b35SErik Nordmark 	 * ip_recvpktinfo is used for both AF_INET and AF_INET6 but
281bd670b35SErik Nordmark 	 * are different
282bd670b35SErik Nordmark 	 */
283bd670b35SErik Nordmark 	if (recv_ancillary.crb_ip_recvpktinfo &&
284bd670b35SErik Nordmark 	    connp->conn_family == AF_INET) {
285bd670b35SErik Nordmark 		ip_stack_t *ipst = connp->conn_netstack->netstack_ip;
286bd670b35SErik Nordmark 		struct T_opthdr *toh;
287bd670b35SErik Nordmark 		struct in_pktinfo *pktinfop;
288bd670b35SErik Nordmark 		ill_t *ill;
289bd670b35SErik Nordmark 		ipif_t *ipif;
290bd670b35SErik Nordmark 
291bd670b35SErik Nordmark 		toh = (struct T_opthdr *)ancil_buf;
292bd670b35SErik Nordmark 		toh->level = IPPROTO_IP;
293bd670b35SErik Nordmark 		toh->name = IP_PKTINFO;
294bd670b35SErik Nordmark 		toh->len = sizeof (struct T_opthdr) + sizeof (*pktinfop);
295bd670b35SErik Nordmark 		toh->status = 0;
296bd670b35SErik Nordmark 		ancil_buf += sizeof (struct T_opthdr);
297bd670b35SErik Nordmark 		pktinfop = (struct in_pktinfo *)ancil_buf;
298bd670b35SErik Nordmark 
299bd670b35SErik Nordmark 		pktinfop->ipi_ifindex = ira->ira_ruifindex;
300bd670b35SErik Nordmark 		pktinfop->ipi_spec_dst.s_addr = INADDR_ANY;
301bd670b35SErik Nordmark 
302bd670b35SErik Nordmark 		/* Find a good address to report */
303bd670b35SErik Nordmark 		ill = ill_lookup_on_ifindex(ira->ira_ruifindex, B_FALSE, ipst);
304bd670b35SErik Nordmark 		if (ill != NULL) {
305bd670b35SErik Nordmark 			ipif = ipif_good_addr(ill, IPCL_ZONEID(connp));
306bd670b35SErik Nordmark 			if (ipif != NULL) {
307bd670b35SErik Nordmark 				pktinfop->ipi_spec_dst.s_addr =
308bd670b35SErik Nordmark 				    ipif->ipif_lcl_addr;
309bd670b35SErik Nordmark 				ipif_refrele(ipif);
310bd670b35SErik Nordmark 			}
311bd670b35SErik Nordmark 			ill_refrele(ill);
312bd670b35SErik Nordmark 		}
313bd670b35SErik Nordmark 		pktinfop->ipi_addr.s_addr = ipp->ipp_addr_v4;
314bd670b35SErik Nordmark 		ancil_buf += sizeof (struct in_pktinfo);
315bd670b35SErik Nordmark 		ancil_size -= toh->len;
316bd670b35SErik Nordmark 	}
317bd670b35SErik Nordmark 
318bd670b35SErik Nordmark 	if ((recv_ancillary.crb_recvopts) &&
319bd670b35SErik Nordmark 	    (ipp->ipp_fields & IPPF_IPV4_OPTIONS)) {
320bd670b35SErik Nordmark 		struct T_opthdr *toh;
321bd670b35SErik Nordmark 
322bd670b35SErik Nordmark 		toh = (struct T_opthdr *)ancil_buf;
323bd670b35SErik Nordmark 		toh->level = IPPROTO_IP;
324bd670b35SErik Nordmark 		toh->name = IP_RECVOPTS;
325bd670b35SErik Nordmark 		toh->len = sizeof (struct T_opthdr) + ipp->ipp_ipv4_options_len;
326bd670b35SErik Nordmark 		toh->status = 0;
327bd670b35SErik Nordmark 		ancil_buf += sizeof (struct T_opthdr);
328bd670b35SErik Nordmark 		bcopy(ipp->ipp_ipv4_options, ancil_buf,
329bd670b35SErik Nordmark 		    ipp->ipp_ipv4_options_len);
330bd670b35SErik Nordmark 		ancil_buf += ipp->ipp_ipv4_options_len;
331bd670b35SErik Nordmark 		ancil_size -= toh->len;
332bd670b35SErik Nordmark 	}
333bd670b35SErik Nordmark 
334bd670b35SErik Nordmark 	if (recv_ancillary.crb_recvslla) {
335bd670b35SErik Nordmark 		ip_stack_t *ipst = connp->conn_netstack->netstack_ip;
336bd670b35SErik Nordmark 		struct T_opthdr *toh;
337bd670b35SErik Nordmark 		struct sockaddr_dl *dstptr;
338bd670b35SErik Nordmark 		ill_t *ill;
339bd670b35SErik Nordmark 		int alen = 0;
340bd670b35SErik Nordmark 
341bd670b35SErik Nordmark 		ill = ill_lookup_on_ifindex(ira->ira_rifindex, B_FALSE, ipst);
342bd670b35SErik Nordmark 		if (ill != NULL)
343bd670b35SErik Nordmark 			alen = ill->ill_phys_addr_length;
344bd670b35SErik Nordmark 
345bd670b35SErik Nordmark 		/*
346bd670b35SErik Nordmark 		 * For loopback multicast and broadcast the packet arrives
347bd670b35SErik Nordmark 		 * with ira_ruifdex being the physical interface, but
348bd670b35SErik Nordmark 		 * ira_l2src is all zero since ip_postfrag_loopback doesn't
349bd670b35SErik Nordmark 		 * know our l2src. We don't report the address in that case.
350bd670b35SErik Nordmark 		 */
351bd670b35SErik Nordmark 		if (ira->ira_flags & IRAF_LOOPBACK)
352bd670b35SErik Nordmark 			alen = 0;
353bd670b35SErik Nordmark 
354bd670b35SErik Nordmark 		toh = (struct T_opthdr *)ancil_buf;
355bd670b35SErik Nordmark 		toh->level = IPPROTO_IP;
356bd670b35SErik Nordmark 		toh->name = IP_RECVSLLA;
357bd670b35SErik Nordmark 		toh->len = sizeof (struct T_opthdr) +
358bd670b35SErik Nordmark 		    sizeof (struct sockaddr_dl);
359bd670b35SErik Nordmark 		toh->status = 0;
360bd670b35SErik Nordmark 		ancil_buf += sizeof (struct T_opthdr);
361bd670b35SErik Nordmark 		dstptr = (struct sockaddr_dl *)ancil_buf;
362bd670b35SErik Nordmark 		dstptr->sdl_family = AF_LINK;
363bd670b35SErik Nordmark 		dstptr->sdl_index = ira->ira_ruifindex;
364bd670b35SErik Nordmark 		if (ill != NULL)
365bd670b35SErik Nordmark 			dstptr->sdl_type = ill->ill_type;
366bd670b35SErik Nordmark 		else
367bd670b35SErik Nordmark 			dstptr->sdl_type = 0;
368bd670b35SErik Nordmark 		dstptr->sdl_nlen = 0;
369bd670b35SErik Nordmark 		dstptr->sdl_alen = alen;
370bd670b35SErik Nordmark 		dstptr->sdl_slen = 0;
371bd670b35SErik Nordmark 		bcopy(ira->ira_l2src, dstptr->sdl_data, alen);
372bd670b35SErik Nordmark 		ancil_buf += sizeof (struct sockaddr_dl);
373bd670b35SErik Nordmark 		ancil_size -= toh->len;
374bd670b35SErik Nordmark 		if (ill != NULL)
375bd670b35SErik Nordmark 			ill_refrele(ill);
376bd670b35SErik Nordmark 	}
377bd670b35SErik Nordmark 
378bd670b35SErik Nordmark 	if (recv_ancillary.crb_recvif) {
379bd670b35SErik Nordmark 		struct T_opthdr *toh;
380bd670b35SErik Nordmark 		uint_t		*dstptr;
381bd670b35SErik Nordmark 
382bd670b35SErik Nordmark 		toh = (struct T_opthdr *)ancil_buf;
383bd670b35SErik Nordmark 		toh->level = IPPROTO_IP;
384bd670b35SErik Nordmark 		toh->name = IP_RECVIF;
385bd670b35SErik Nordmark 		toh->len = sizeof (struct T_opthdr) + sizeof (uint_t);
386bd670b35SErik Nordmark 		toh->status = 0;
387bd670b35SErik Nordmark 		ancil_buf += sizeof (struct T_opthdr);
388bd670b35SErik Nordmark 		dstptr = (uint_t *)ancil_buf;
389bd670b35SErik Nordmark 		*dstptr = ira->ira_ruifindex;
390bd670b35SErik Nordmark 		ancil_buf += sizeof (uint_t);
391bd670b35SErik Nordmark 		ancil_size -= toh->len;
392bd670b35SErik Nordmark 	}
393bd670b35SErik Nordmark 
394bd670b35SErik Nordmark 	/*
395bd670b35SErik Nordmark 	 * ip_recvpktinfo is used for both AF_INET and AF_INET6 but
396bd670b35SErik Nordmark 	 * are different
397bd670b35SErik Nordmark 	 */
398bd670b35SErik Nordmark 	if (recv_ancillary.crb_ip_recvpktinfo &&
399bd670b35SErik Nordmark 	    connp->conn_family == AF_INET6) {
400bd670b35SErik Nordmark 		struct T_opthdr *toh;
401bd670b35SErik Nordmark 		struct in6_pktinfo *pkti;
402bd670b35SErik Nordmark 
403bd670b35SErik Nordmark 		toh = (struct T_opthdr *)ancil_buf;
404bd670b35SErik Nordmark 		toh->level = IPPROTO_IPV6;
405bd670b35SErik Nordmark 		toh->name = IPV6_PKTINFO;
406bd670b35SErik Nordmark 		toh->len = sizeof (struct T_opthdr) + sizeof (*pkti);
407bd670b35SErik Nordmark 		toh->status = 0;
408bd670b35SErik Nordmark 		ancil_buf += sizeof (struct T_opthdr);
409bd670b35SErik Nordmark 		pkti = (struct in6_pktinfo *)ancil_buf;
410bd670b35SErik Nordmark 		if (ira->ira_flags & IRAF_IS_IPV4) {
411bd670b35SErik Nordmark 			IN6_IPADDR_TO_V4MAPPED(ipp->ipp_addr_v4,
412bd670b35SErik Nordmark 			    &pkti->ipi6_addr);
413bd670b35SErik Nordmark 		} else {
414bd670b35SErik Nordmark 			pkti->ipi6_addr = ipp->ipp_addr;
415bd670b35SErik Nordmark 		}
416bd670b35SErik Nordmark 		pkti->ipi6_ifindex = ira->ira_ruifindex;
417bd670b35SErik Nordmark 
418bd670b35SErik Nordmark 		ancil_buf += sizeof (*pkti);
419bd670b35SErik Nordmark 		ancil_size -= toh->len;
420bd670b35SErik Nordmark 	}
421bd670b35SErik Nordmark 	if (recv_ancillary.crb_ipv6_recvhoplimit) {
422bd670b35SErik Nordmark 		struct T_opthdr *toh;
423bd670b35SErik Nordmark 
424bd670b35SErik Nordmark 		toh = (struct T_opthdr *)ancil_buf;
425bd670b35SErik Nordmark 		toh->level = IPPROTO_IPV6;
426bd670b35SErik Nordmark 		toh->name = IPV6_HOPLIMIT;
427bd670b35SErik Nordmark 		toh->len = sizeof (struct T_opthdr) + sizeof (uint_t);
428bd670b35SErik Nordmark 		toh->status = 0;
429bd670b35SErik Nordmark 		ancil_buf += sizeof (struct T_opthdr);
430bd670b35SErik Nordmark 		*(uint_t *)ancil_buf = ipp->ipp_hoplimit;
431bd670b35SErik Nordmark 		ancil_buf += sizeof (uint_t);
432bd670b35SErik Nordmark 		ancil_size -= toh->len;
433bd670b35SErik Nordmark 	}
434bd670b35SErik Nordmark 	if (recv_ancillary.crb_ipv6_recvtclass) {
435bd670b35SErik Nordmark 		struct T_opthdr *toh;
436bd670b35SErik Nordmark 
437bd670b35SErik Nordmark 		toh = (struct T_opthdr *)ancil_buf;
438bd670b35SErik Nordmark 		toh->level = IPPROTO_IPV6;
439bd670b35SErik Nordmark 		toh->name = IPV6_TCLASS;
440bd670b35SErik Nordmark 		toh->len = sizeof (struct T_opthdr) + sizeof (uint_t);
441bd670b35SErik Nordmark 		toh->status = 0;
442bd670b35SErik Nordmark 		ancil_buf += sizeof (struct T_opthdr);
443bd670b35SErik Nordmark 
444bd670b35SErik Nordmark 		if (ira->ira_flags & IRAF_IS_IPV4)
445bd670b35SErik Nordmark 			*(uint_t *)ancil_buf = ipp->ipp_type_of_service;
446bd670b35SErik Nordmark 		else
447bd670b35SErik Nordmark 			*(uint_t *)ancil_buf = ipp->ipp_tclass;
448bd670b35SErik Nordmark 		ancil_buf += sizeof (uint_t);
449bd670b35SErik Nordmark 		ancil_size -= toh->len;
450bd670b35SErik Nordmark 	}
451bd670b35SErik Nordmark 	if (recv_ancillary.crb_ipv6_recvhopopts &&
452bd670b35SErik Nordmark 	    (ipp->ipp_fields & IPPF_HOPOPTS)) {
453bd670b35SErik Nordmark 		struct T_opthdr *toh;
454bd670b35SErik Nordmark 
455bd670b35SErik Nordmark 		toh = (struct T_opthdr *)ancil_buf;
456bd670b35SErik Nordmark 		toh->level = IPPROTO_IPV6;
457bd670b35SErik Nordmark 		toh->name = IPV6_HOPOPTS;
458bd670b35SErik Nordmark 		toh->len = sizeof (struct T_opthdr) + ipp->ipp_hopoptslen;
459bd670b35SErik Nordmark 		toh->status = 0;
460bd670b35SErik Nordmark 		ancil_buf += sizeof (struct T_opthdr);
461bd670b35SErik Nordmark 		bcopy(ipp->ipp_hopopts, ancil_buf, ipp->ipp_hopoptslen);
462bd670b35SErik Nordmark 		ancil_buf += ipp->ipp_hopoptslen;
463bd670b35SErik Nordmark 		ancil_size -= toh->len;
464bd670b35SErik Nordmark 	}
465bd670b35SErik Nordmark 	/*
466bd670b35SErik Nordmark 	 * To honor RFC3542 when an application asks for both IPV6_RECVDSTOPTS
467bd670b35SErik Nordmark 	 * and IPV6_RECVRTHDR, we pass up the item rthdrdstopts (the destination
468bd670b35SErik Nordmark 	 * options that appear before a routing header.
469bd670b35SErik Nordmark 	 * We also pass them up if IPV6_RECVRTHDRDSTOPTS is set.
470bd670b35SErik Nordmark 	 */
471bd670b35SErik Nordmark 	if (ipp->ipp_fields & IPPF_RTHDRDSTOPTS) {
472bd670b35SErik Nordmark 		if (recv_ancillary.crb_ipv6_recvrthdrdstopts ||
473bd670b35SErik Nordmark 		    (recv_ancillary.crb_ipv6_recvdstopts &&
474bd670b35SErik Nordmark 		    recv_ancillary.crb_ipv6_recvrthdr)) {
475bd670b35SErik Nordmark 			struct T_opthdr *toh;
476bd670b35SErik Nordmark 
477bd670b35SErik Nordmark 			toh = (struct T_opthdr *)ancil_buf;
478bd670b35SErik Nordmark 			toh->level = IPPROTO_IPV6;
479bd670b35SErik Nordmark 			toh->name = IPV6_DSTOPTS;
480bd670b35SErik Nordmark 			toh->len = sizeof (struct T_opthdr) +
481bd670b35SErik Nordmark 			    ipp->ipp_rthdrdstoptslen;
482bd670b35SErik Nordmark 			toh->status = 0;
483bd670b35SErik Nordmark 			ancil_buf += sizeof (struct T_opthdr);
484bd670b35SErik Nordmark 			bcopy(ipp->ipp_rthdrdstopts, ancil_buf,
485bd670b35SErik Nordmark 			    ipp->ipp_rthdrdstoptslen);
486bd670b35SErik Nordmark 			ancil_buf += ipp->ipp_rthdrdstoptslen;
487bd670b35SErik Nordmark 			ancil_size -= toh->len;
488bd670b35SErik Nordmark 		}
489bd670b35SErik Nordmark 	}
490bd670b35SErik Nordmark 	if (recv_ancillary.crb_ipv6_recvrthdr &&
491bd670b35SErik Nordmark 	    (ipp->ipp_fields & IPPF_RTHDR)) {
492bd670b35SErik Nordmark 		struct T_opthdr *toh;
493bd670b35SErik Nordmark 
494bd670b35SErik Nordmark 		toh = (struct T_opthdr *)ancil_buf;
495bd670b35SErik Nordmark 		toh->level = IPPROTO_IPV6;
496bd670b35SErik Nordmark 		toh->name = IPV6_RTHDR;
497bd670b35SErik Nordmark 		toh->len = sizeof (struct T_opthdr) + ipp->ipp_rthdrlen;
498bd670b35SErik Nordmark 		toh->status = 0;
499bd670b35SErik Nordmark 		ancil_buf += sizeof (struct T_opthdr);
500bd670b35SErik Nordmark 		bcopy(ipp->ipp_rthdr, ancil_buf, ipp->ipp_rthdrlen);
501bd670b35SErik Nordmark 		ancil_buf += ipp->ipp_rthdrlen;
502bd670b35SErik Nordmark 		ancil_size -= toh->len;
503bd670b35SErik Nordmark 	}
504bd670b35SErik Nordmark 	if ((recv_ancillary.crb_ipv6_recvdstopts ||
505bd670b35SErik Nordmark 	    recv_ancillary.crb_old_ipv6_recvdstopts) &&
506bd670b35SErik Nordmark 	    (ipp->ipp_fields & IPPF_DSTOPTS)) {
507bd670b35SErik Nordmark 		struct T_opthdr *toh;
508bd670b35SErik Nordmark 
509bd670b35SErik Nordmark 		toh = (struct T_opthdr *)ancil_buf;
510bd670b35SErik Nordmark 		toh->level = IPPROTO_IPV6;
511bd670b35SErik Nordmark 		toh->name = IPV6_DSTOPTS;
512bd670b35SErik Nordmark 		toh->len = sizeof (struct T_opthdr) + ipp->ipp_dstoptslen;
513bd670b35SErik Nordmark 		toh->status = 0;
514bd670b35SErik Nordmark 		ancil_buf += sizeof (struct T_opthdr);
515bd670b35SErik Nordmark 		bcopy(ipp->ipp_dstopts, ancil_buf, ipp->ipp_dstoptslen);
516bd670b35SErik Nordmark 		ancil_buf += ipp->ipp_dstoptslen;
517bd670b35SErik Nordmark 		ancil_size -= toh->len;
518bd670b35SErik Nordmark 	}
519bd670b35SErik Nordmark 
520bd670b35SErik Nordmark 	if (recv_ancillary.crb_recvucred && ira->ira_cred != NULL) {
521bd670b35SErik Nordmark 		struct T_opthdr *toh;
522bd670b35SErik Nordmark 		cred_t		*rcr = connp->conn_cred;
523bd670b35SErik Nordmark 
524bd670b35SErik Nordmark 		toh = (struct T_opthdr *)ancil_buf;
525bd670b35SErik Nordmark 		toh->level = SOL_SOCKET;
526bd670b35SErik Nordmark 		toh->name = SCM_UCRED;
52767dbe2beSCasper H.S. Dik 		toh->len = sizeof (struct T_opthdr) +
52867dbe2beSCasper H.S. Dik 		    ucredminsize(ira->ira_cred);
529bd670b35SErik Nordmark 		toh->status = 0;
530bd670b35SErik Nordmark 		(void) cred2ucred(ira->ira_cred, ira->ira_cpid, &toh[1], rcr);
531bd670b35SErik Nordmark 		ancil_buf += toh->len;
532bd670b35SErik Nordmark 		ancil_size -= toh->len;
533bd670b35SErik Nordmark 	}
534bd670b35SErik Nordmark 	if (recv_ancillary.crb_timestamp) {
535bd670b35SErik Nordmark 		struct	T_opthdr *toh;
536bd670b35SErik Nordmark 
537bd670b35SErik Nordmark 		toh = (struct T_opthdr *)ancil_buf;
538bd670b35SErik Nordmark 		toh->level = SOL_SOCKET;
539bd670b35SErik Nordmark 		toh->name = SCM_TIMESTAMP;
540bd670b35SErik Nordmark 		toh->len = sizeof (struct T_opthdr) +
541bd670b35SErik Nordmark 		    sizeof (timestruc_t) + _POINTER_ALIGNMENT;
542bd670b35SErik Nordmark 		toh->status = 0;
543bd670b35SErik Nordmark 		ancil_buf += sizeof (struct T_opthdr);
544bd670b35SErik Nordmark 		/* Align for gethrestime() */
545bd670b35SErik Nordmark 		ancil_buf = (uchar_t *)P2ROUNDUP((intptr_t)ancil_buf,
546bd670b35SErik Nordmark 		    sizeof (intptr_t));
547bd670b35SErik Nordmark 		gethrestime((timestruc_t *)ancil_buf);
548bd670b35SErik Nordmark 		ancil_buf = (uchar_t *)toh + toh->len;
549bd670b35SErik Nordmark 		ancil_size -= toh->len;
550bd670b35SErik Nordmark 	}
551bd670b35SErik Nordmark 
552bd670b35SErik Nordmark 	/*
553bd670b35SErik Nordmark 	 * CAUTION:
554bd670b35SErik Nordmark 	 * Due to aligment issues
555bd670b35SErik Nordmark 	 * Processing of IP_RECVTTL option
556bd670b35SErik Nordmark 	 * should always be the last. Adding
557bd670b35SErik Nordmark 	 * any option processing after this will
558bd670b35SErik Nordmark 	 * cause alignment panic.
559bd670b35SErik Nordmark 	 */
560bd670b35SErik Nordmark 	if (recv_ancillary.crb_recvttl &&
561bd670b35SErik Nordmark 	    (ira->ira_flags & IRAF_IS_IPV4)) {
562bd670b35SErik Nordmark 		struct	T_opthdr *toh;
563bd670b35SErik Nordmark 		uint8_t	*dstptr;
564bd670b35SErik Nordmark 
565bd670b35SErik Nordmark 		toh = (struct T_opthdr *)ancil_buf;
566bd670b35SErik Nordmark 		toh->level = IPPROTO_IP;
567bd670b35SErik Nordmark 		toh->name = IP_RECVTTL;
568bd670b35SErik Nordmark 		toh->len = sizeof (struct T_opthdr) + sizeof (uint8_t);
569bd670b35SErik Nordmark 		toh->status = 0;
570bd670b35SErik Nordmark 		ancil_buf += sizeof (struct T_opthdr);
571bd670b35SErik Nordmark 		dstptr = (uint8_t *)ancil_buf;
572bd670b35SErik Nordmark 		*dstptr = ipp->ipp_hoplimit;
573bd670b35SErik Nordmark 		ancil_buf += sizeof (uint8_t);
574bd670b35SErik Nordmark 		ancil_size -= toh->len;
575bd670b35SErik Nordmark 	}
576bd670b35SErik Nordmark 
577bd670b35SErik Nordmark 	/* Consumed all of allocated space */
578bd670b35SErik Nordmark 	ASSERT(ancil_size == 0);
579bd670b35SErik Nordmark 
580bd670b35SErik Nordmark }
581bd670b35SErik Nordmark 
582bd670b35SErik Nordmark /*
583bd670b35SErik Nordmark  * This routine retrieves the current status of socket options.
584bd670b35SErik Nordmark  * It returns the size of the option retrieved, or -1.
585bd670b35SErik Nordmark  */
586bd670b35SErik Nordmark int
conn_opt_get(conn_opt_arg_t * coa,t_scalar_t level,t_scalar_t name,uchar_t * ptr)587bd670b35SErik Nordmark conn_opt_get(conn_opt_arg_t *coa, t_scalar_t level, t_scalar_t name,
588bd670b35SErik Nordmark     uchar_t *ptr)
589bd670b35SErik Nordmark {
590bd670b35SErik Nordmark 	int		*i1 = (int *)ptr;
591bd670b35SErik Nordmark 	conn_t		*connp = coa->coa_connp;
592bd670b35SErik Nordmark 	ip_xmit_attr_t	*ixa = coa->coa_ixa;
593bd670b35SErik Nordmark 	ip_pkt_t	*ipp = coa->coa_ipp;
594bd670b35SErik Nordmark 	ip_stack_t	*ipst = ixa->ixa_ipst;
595bd670b35SErik Nordmark 	uint_t		len;
596bd670b35SErik Nordmark 
597bd670b35SErik Nordmark 	ASSERT(MUTEX_HELD(&coa->coa_connp->conn_lock));
598bd670b35SErik Nordmark 
599bd670b35SErik Nordmark 	switch (level) {
600bd670b35SErik Nordmark 	case SOL_SOCKET:
601bd670b35SErik Nordmark 		switch (name) {
602bd670b35SErik Nordmark 		case SO_DEBUG:
603bd670b35SErik Nordmark 			*i1 = connp->conn_debug ? SO_DEBUG : 0;
604bd670b35SErik Nordmark 			break;	/* goto sizeof (int) option return */
605bd670b35SErik Nordmark 		case SO_KEEPALIVE:
606bd670b35SErik Nordmark 			*i1 = connp->conn_keepalive ? SO_KEEPALIVE : 0;
607bd670b35SErik Nordmark 			break;
608bd670b35SErik Nordmark 		case SO_LINGER:	{
609bd670b35SErik Nordmark 			struct linger *lgr = (struct linger *)ptr;
610bd670b35SErik Nordmark 
611bd670b35SErik Nordmark 			lgr->l_onoff = connp->conn_linger ? SO_LINGER : 0;
612bd670b35SErik Nordmark 			lgr->l_linger = connp->conn_lingertime;
613bd670b35SErik Nordmark 			}
614bd670b35SErik Nordmark 			return (sizeof (struct linger));
615bd670b35SErik Nordmark 
616bd670b35SErik Nordmark 		case SO_OOBINLINE:
617bd670b35SErik Nordmark 			*i1 = connp->conn_oobinline ? SO_OOBINLINE : 0;
618bd670b35SErik Nordmark 			break;
619bd670b35SErik Nordmark 		case SO_REUSEADDR:
620bd670b35SErik Nordmark 			*i1 = connp->conn_reuseaddr ? SO_REUSEADDR : 0;
621bd670b35SErik Nordmark 			break;	/* goto sizeof (int) option return */
622bd670b35SErik Nordmark 		case SO_TYPE:
623bd670b35SErik Nordmark 			*i1 = connp->conn_so_type;
624bd670b35SErik Nordmark 			break;	/* goto sizeof (int) option return */
625bd670b35SErik Nordmark 		case SO_DONTROUTE:
626bd670b35SErik Nordmark 			*i1 = (ixa->ixa_flags & IXAF_DONTROUTE) ?
627bd670b35SErik Nordmark 			    SO_DONTROUTE : 0;
628bd670b35SErik Nordmark 			break;	/* goto sizeof (int) option return */
629bd670b35SErik Nordmark 		case SO_USELOOPBACK:
630bd670b35SErik Nordmark 			*i1 = connp->conn_useloopback ? SO_USELOOPBACK : 0;
631bd670b35SErik Nordmark 			break;	/* goto sizeof (int) option return */
632bd670b35SErik Nordmark 		case SO_BROADCAST:
633bd670b35SErik Nordmark 			*i1 = connp->conn_broadcast ? SO_BROADCAST : 0;
634bd670b35SErik Nordmark 			break;	/* goto sizeof (int) option return */
635bd670b35SErik Nordmark 
636bd670b35SErik Nordmark 		case SO_SNDBUF:
637bd670b35SErik Nordmark 			*i1 = connp->conn_sndbuf;
638bd670b35SErik Nordmark 			break;	/* goto sizeof (int) option return */
639bd670b35SErik Nordmark 		case SO_RCVBUF:
640bd670b35SErik Nordmark 			*i1 = connp->conn_rcvbuf;
641bd670b35SErik Nordmark 			break;	/* goto sizeof (int) option return */
642bd670b35SErik Nordmark 		case SO_RCVTIMEO:
643bd670b35SErik Nordmark 		case SO_SNDTIMEO:
644bd670b35SErik Nordmark 			/*
645bd670b35SErik Nordmark 			 * Pass these two options in order for third part
646bd670b35SErik Nordmark 			 * protocol usage. Here just return directly.
647bd670b35SErik Nordmark 			 */
648bd670b35SErik Nordmark 			*i1 = 0;
649bd670b35SErik Nordmark 			break;
650bd670b35SErik Nordmark 		case SO_DGRAM_ERRIND:
651bd670b35SErik Nordmark 			*i1 = connp->conn_dgram_errind ? SO_DGRAM_ERRIND : 0;
652bd670b35SErik Nordmark 			break;	/* goto sizeof (int) option return */
653bd670b35SErik Nordmark 		case SO_RECVUCRED:
654bd670b35SErik Nordmark 			*i1 = connp->conn_recv_ancillary.crb_recvucred;
655bd670b35SErik Nordmark 			break;	/* goto sizeof (int) option return */
656bd670b35SErik Nordmark 		case SO_TIMESTAMP:
657bd670b35SErik Nordmark 			*i1 = connp->conn_recv_ancillary.crb_timestamp;
658bd670b35SErik Nordmark 			break;	/* goto sizeof (int) option return */
659bd670b35SErik Nordmark 		case SO_VRRP:
660bd670b35SErik Nordmark 			*i1 = connp->conn_isvrrp;
661bd670b35SErik Nordmark 			break;	/* goto sizeof (int) option return */
662bd670b35SErik Nordmark 		case SO_ANON_MLP:
663bd670b35SErik Nordmark 			*i1 = connp->conn_anon_mlp;
664bd670b35SErik Nordmark 			break;	/* goto sizeof (int) option return */
665bd670b35SErik Nordmark 		case SO_MAC_EXEMPT:
666bd670b35SErik Nordmark 			*i1 = (connp->conn_mac_mode == CONN_MAC_AWARE);
667bd670b35SErik Nordmark 			break;	/* goto sizeof (int) option return */
668bd670b35SErik Nordmark 		case SO_MAC_IMPLICIT:
669bd670b35SErik Nordmark 			*i1 = (connp->conn_mac_mode == CONN_MAC_IMPLICIT);
670bd670b35SErik Nordmark 			break;	/* goto sizeof (int) option return */
671bd670b35SErik Nordmark 		case SO_ALLZONES:
672bd670b35SErik Nordmark 			*i1 = connp->conn_allzones;
673bd670b35SErik Nordmark 			break;	/* goto sizeof (int) option return */
674bd670b35SErik Nordmark 		case SO_EXCLBIND:
675bd670b35SErik Nordmark 			*i1 = connp->conn_exclbind ? SO_EXCLBIND : 0;
676bd670b35SErik Nordmark 			break;
677bd670b35SErik Nordmark 		case SO_PROTOTYPE:
678bd670b35SErik Nordmark 			*i1 = connp->conn_proto;
679bd670b35SErik Nordmark 			break;
680bd670b35SErik Nordmark 
681bd670b35SErik Nordmark 		case SO_DOMAIN:
682bd670b35SErik Nordmark 			*i1 = connp->conn_family;
683bd670b35SErik Nordmark 			break;
684bd670b35SErik Nordmark 		default:
685bd670b35SErik Nordmark 			return (-1);
686bd670b35SErik Nordmark 		}
687bd670b35SErik Nordmark 		break;
688bd670b35SErik Nordmark 	case IPPROTO_IP:
689bd670b35SErik Nordmark 		if (connp->conn_family != AF_INET)
690bd670b35SErik Nordmark 			return (-1);
691bd670b35SErik Nordmark 		switch (name) {
692bd670b35SErik Nordmark 		case IP_OPTIONS:
693bd670b35SErik Nordmark 		case T_IP_OPTIONS:
694bd670b35SErik Nordmark 			if (!(ipp->ipp_fields & IPPF_IPV4_OPTIONS))
695bd670b35SErik Nordmark 				return (0);
696bd670b35SErik Nordmark 
697bd670b35SErik Nordmark 			len = ipp->ipp_ipv4_options_len;
698bd670b35SErik Nordmark 			if (len > 0) {
699bd670b35SErik Nordmark 				bcopy(ipp->ipp_ipv4_options, ptr, len);
700bd670b35SErik Nordmark 			}
701bd670b35SErik Nordmark 			return (len);
702bd670b35SErik Nordmark 
703bd670b35SErik Nordmark 		case IP_PKTINFO: {
704bd670b35SErik Nordmark 			/*
705bd670b35SErik Nordmark 			 * This also handles IP_RECVPKTINFO.
706bd670b35SErik Nordmark 			 * IP_PKTINFO and IP_RECVPKTINFO have same value.
707bd670b35SErik Nordmark 			 * Differentiation is based on the size of the
708bd670b35SErik Nordmark 			 * argument passed in.
709bd670b35SErik Nordmark 			 */
710bd670b35SErik Nordmark 			struct in_pktinfo *pktinfo;
711bd670b35SErik Nordmark 
712bd670b35SErik Nordmark #ifdef notdef
713bd670b35SErik Nordmark 			/* optcom doesn't provide a length with "get" */
714bd670b35SErik Nordmark 			if (inlen == sizeof (int)) {
715bd670b35SErik Nordmark 				/* This is IP_RECVPKTINFO option. */
716bd670b35SErik Nordmark 				*i1 = connp->conn_recv_ancillary.
717bd670b35SErik Nordmark 				    crb_ip_recvpktinfo;
718bd670b35SErik Nordmark 				return (sizeof (int));
719bd670b35SErik Nordmark 			}
720bd670b35SErik Nordmark #endif
721bd670b35SErik Nordmark 			/* XXX assumes that caller has room for max size! */
722bd670b35SErik Nordmark 
723bd670b35SErik Nordmark 			pktinfo = (struct in_pktinfo *)ptr;
724bd670b35SErik Nordmark 			pktinfo->ipi_ifindex = ixa->ixa_ifindex;
725bd670b35SErik Nordmark 			if (ipp->ipp_fields & IPPF_ADDR)
726bd670b35SErik Nordmark 				pktinfo->ipi_spec_dst.s_addr = ipp->ipp_addr_v4;
727bd670b35SErik Nordmark 			else
728bd670b35SErik Nordmark 				pktinfo->ipi_spec_dst.s_addr = INADDR_ANY;
729bd670b35SErik Nordmark 			return (sizeof (struct in_pktinfo));
730bd670b35SErik Nordmark 		}
731bd670b35SErik Nordmark 		case IP_DONTFRAG:
732bd670b35SErik Nordmark 			*i1 = (ixa->ixa_flags & IXAF_DONTFRAG) != 0;
733bd670b35SErik Nordmark 			return (sizeof (int));
734bd670b35SErik Nordmark 		case IP_TOS:
735bd670b35SErik Nordmark 		case T_IP_TOS:
736bd670b35SErik Nordmark 			*i1 = (int)ipp->ipp_type_of_service;
737bd670b35SErik Nordmark 			break;	/* goto sizeof (int) option return */
738bd670b35SErik Nordmark 		case IP_TTL:
739bd670b35SErik Nordmark 			*i1 = (int)ipp->ipp_unicast_hops;
740bd670b35SErik Nordmark 			break;	/* goto sizeof (int) option return */
741bd670b35SErik Nordmark 		case IP_DHCPINIT_IF:
742bd670b35SErik Nordmark 			return (-1);
743bd670b35SErik Nordmark 		case IP_NEXTHOP:
744bd670b35SErik Nordmark 			if (ixa->ixa_flags & IXAF_NEXTHOP_SET) {
745bd670b35SErik Nordmark 				*(ipaddr_t *)ptr = ixa->ixa_nexthop_v4;
746bd670b35SErik Nordmark 				return (sizeof (ipaddr_t));
747bd670b35SErik Nordmark 			} else {
748bd670b35SErik Nordmark 				return (0);
749bd670b35SErik Nordmark 			}
750bd670b35SErik Nordmark 
751bd670b35SErik Nordmark 		case IP_MULTICAST_IF:
752bd670b35SErik Nordmark 			/* 0 address if not set */
753bd670b35SErik Nordmark 			*(ipaddr_t *)ptr = ixa->ixa_multicast_ifaddr;
754bd670b35SErik Nordmark 			return (sizeof (ipaddr_t));
755bd670b35SErik Nordmark 		case IP_MULTICAST_TTL:
756bd670b35SErik Nordmark 			*(uchar_t *)ptr = ixa->ixa_multicast_ttl;
757bd670b35SErik Nordmark 			return (sizeof (uchar_t));
758bd670b35SErik Nordmark 		case IP_MULTICAST_LOOP:
759bd670b35SErik Nordmark 			*ptr = (ixa->ixa_flags & IXAF_MULTICAST_LOOP) ? 1 : 0;
760bd670b35SErik Nordmark 			return (sizeof (uint8_t));
761bd670b35SErik Nordmark 		case IP_RECVOPTS:
762bd670b35SErik Nordmark 			*i1 = connp->conn_recv_ancillary.crb_recvopts;
763bd670b35SErik Nordmark 			break;	/* goto sizeof (int) option return */
764bd670b35SErik Nordmark 		case IP_RECVDSTADDR:
765bd670b35SErik Nordmark 			*i1 = connp->conn_recv_ancillary.crb_recvdstaddr;
766bd670b35SErik Nordmark 			break;	/* goto sizeof (int) option return */
767bd670b35SErik Nordmark 		case IP_RECVIF:
768bd670b35SErik Nordmark 			*i1 = connp->conn_recv_ancillary.crb_recvif;
769bd670b35SErik Nordmark 			break;	/* goto sizeof (int) option return */
770bd670b35SErik Nordmark 		case IP_RECVSLLA:
771bd670b35SErik Nordmark 			*i1 = connp->conn_recv_ancillary.crb_recvslla;
772bd670b35SErik Nordmark 			break;	/* goto sizeof (int) option return */
773bd670b35SErik Nordmark 		case IP_RECVTTL:
774bd670b35SErik Nordmark 			*i1 = connp->conn_recv_ancillary.crb_recvttl;
775bd670b35SErik Nordmark 			break;	/* goto sizeof (int) option return */
776bd670b35SErik Nordmark 		case IP_ADD_MEMBERSHIP:
777bd670b35SErik Nordmark 		case IP_DROP_MEMBERSHIP:
778bd670b35SErik Nordmark 		case MCAST_JOIN_GROUP:
779bd670b35SErik Nordmark 		case MCAST_LEAVE_GROUP:
780bd670b35SErik Nordmark 		case IP_BLOCK_SOURCE:
781bd670b35SErik Nordmark 		case IP_UNBLOCK_SOURCE:
782bd670b35SErik Nordmark 		case IP_ADD_SOURCE_MEMBERSHIP:
783bd670b35SErik Nordmark 		case IP_DROP_SOURCE_MEMBERSHIP:
784bd670b35SErik Nordmark 		case MCAST_BLOCK_SOURCE:
785bd670b35SErik Nordmark 		case MCAST_UNBLOCK_SOURCE:
786bd670b35SErik Nordmark 		case MCAST_JOIN_SOURCE_GROUP:
787bd670b35SErik Nordmark 		case MCAST_LEAVE_SOURCE_GROUP:
788bd670b35SErik Nordmark 		case MRT_INIT:
789bd670b35SErik Nordmark 		case MRT_DONE:
790bd670b35SErik Nordmark 		case MRT_ADD_VIF:
791bd670b35SErik Nordmark 		case MRT_DEL_VIF:
792bd670b35SErik Nordmark 		case MRT_ADD_MFC:
793bd670b35SErik Nordmark 		case MRT_DEL_MFC:
794bd670b35SErik Nordmark 			/* cannot "get" the value for these */
795bd670b35SErik Nordmark 			return (-1);
796bd670b35SErik Nordmark 		case MRT_VERSION:
797bd670b35SErik Nordmark 		case MRT_ASSERT:
798bd670b35SErik Nordmark 			(void) ip_mrouter_get(name, connp, ptr);
799bd670b35SErik Nordmark 			return (sizeof (int));
800bd670b35SErik Nordmark 		case IP_SEC_OPT:
801bd670b35SErik Nordmark 			return (ipsec_req_from_conn(connp, (ipsec_req_t	*)ptr,
802bd670b35SErik Nordmark 			    IPSEC_AF_V4));
803bd670b35SErik Nordmark 		case IP_BOUND_IF:
804bd670b35SErik Nordmark 			/* Zero if not set */
805bd670b35SErik Nordmark 			*i1 = connp->conn_bound_if;
806bd670b35SErik Nordmark 			break;	/* goto sizeof (int) option return */
807bd670b35SErik Nordmark 		case IP_UNSPEC_SRC:
808bd670b35SErik Nordmark 			*i1 = connp->conn_unspec_src;
809bd670b35SErik Nordmark 			break;	/* goto sizeof (int) option return */
810bd670b35SErik Nordmark 		case IP_BROADCAST_TTL:
811bd670b35SErik Nordmark 			if (ixa->ixa_flags & IXAF_BROADCAST_TTL_SET)
812bd670b35SErik Nordmark 				*(uchar_t *)ptr = ixa->ixa_broadcast_ttl;
813bd670b35SErik Nordmark 			else
814bd670b35SErik Nordmark 				*(uchar_t *)ptr = ipst->ips_ip_broadcast_ttl;
815bd670b35SErik Nordmark 			return (sizeof (uchar_t));
816bd670b35SErik Nordmark 		default:
817bd670b35SErik Nordmark 			return (-1);
818bd670b35SErik Nordmark 		}
819bd670b35SErik Nordmark 		break;
820bd670b35SErik Nordmark 	case IPPROTO_IPV6:
821bd670b35SErik Nordmark 		if (connp->conn_family != AF_INET6)
822bd670b35SErik Nordmark 			return (-1);
823bd670b35SErik Nordmark 		switch (name) {
824bd670b35SErik Nordmark 		case IPV6_UNICAST_HOPS:
825bd670b35SErik Nordmark 			*i1 = (int)ipp->ipp_unicast_hops;
826bd670b35SErik Nordmark 			break;	/* goto sizeof (int) option return */
827bd670b35SErik Nordmark 		case IPV6_MULTICAST_IF:
828bd670b35SErik Nordmark 			/* 0 index if not set */
829bd670b35SErik Nordmark 			*i1 = ixa->ixa_multicast_ifindex;
830bd670b35SErik Nordmark 			break;	/* goto sizeof (int) option return */
831bd670b35SErik Nordmark 		case IPV6_MULTICAST_HOPS:
832bd670b35SErik Nordmark 			*i1 = ixa->ixa_multicast_ttl;
833bd670b35SErik Nordmark 			break;	/* goto sizeof (int) option return */
834bd670b35SErik Nordmark 		case IPV6_MULTICAST_LOOP:
835bd670b35SErik Nordmark 			*i1 = (ixa->ixa_flags & IXAF_MULTICAST_LOOP) ? 1 : 0;
836bd670b35SErik Nordmark 			break;	/* goto sizeof (int) option return */
837bd670b35SErik Nordmark 		case IPV6_JOIN_GROUP:
838bd670b35SErik Nordmark 		case IPV6_LEAVE_GROUP:
839bd670b35SErik Nordmark 		case MCAST_JOIN_GROUP:
840bd670b35SErik Nordmark 		case MCAST_LEAVE_GROUP:
841bd670b35SErik Nordmark 		case MCAST_BLOCK_SOURCE:
842bd670b35SErik Nordmark 		case MCAST_UNBLOCK_SOURCE:
843bd670b35SErik Nordmark 		case MCAST_JOIN_SOURCE_GROUP:
844bd670b35SErik Nordmark 		case MCAST_LEAVE_SOURCE_GROUP:
845bd670b35SErik Nordmark 			/* cannot "get" the value for these */
846bd670b35SErik Nordmark 			return (-1);
847bd670b35SErik Nordmark 		case IPV6_BOUND_IF:
848bd670b35SErik Nordmark 			/* Zero if not set */
849bd670b35SErik Nordmark 			*i1 = connp->conn_bound_if;
850bd670b35SErik Nordmark 			break;	/* goto sizeof (int) option return */
851bd670b35SErik Nordmark 		case IPV6_UNSPEC_SRC:
852bd670b35SErik Nordmark 			*i1 = connp->conn_unspec_src;
853bd670b35SErik Nordmark 			break;	/* goto sizeof (int) option return */
854bd670b35SErik Nordmark 		case IPV6_RECVPKTINFO:
855bd670b35SErik Nordmark 			*i1 = connp->conn_recv_ancillary.crb_ip_recvpktinfo;
856bd670b35SErik Nordmark 			break;	/* goto sizeof (int) option return */
857bd670b35SErik Nordmark 		case IPV6_RECVTCLASS:
858bd670b35SErik Nordmark 			*i1 = connp->conn_recv_ancillary.crb_ipv6_recvtclass;
859bd670b35SErik Nordmark 			break;	/* goto sizeof (int) option return */
860bd670b35SErik Nordmark 		case IPV6_RECVPATHMTU:
861bd670b35SErik Nordmark 			*i1 = connp->conn_ipv6_recvpathmtu;
862bd670b35SErik Nordmark 			break;	/* goto sizeof (int) option return */
863bd670b35SErik Nordmark 		case IPV6_RECVHOPLIMIT:
864bd670b35SErik Nordmark 			*i1 = connp->conn_recv_ancillary.crb_ipv6_recvhoplimit;
865bd670b35SErik Nordmark 			break;	/* goto sizeof (int) option return */
866bd670b35SErik Nordmark 		case IPV6_RECVHOPOPTS:
867bd670b35SErik Nordmark 			*i1 = connp->conn_recv_ancillary.crb_ipv6_recvhopopts;
868bd670b35SErik Nordmark 			break;	/* goto sizeof (int) option return */
869bd670b35SErik Nordmark 		case IPV6_RECVDSTOPTS:
870bd670b35SErik Nordmark 			*i1 = connp->conn_recv_ancillary.crb_ipv6_recvdstopts;
871bd670b35SErik Nordmark 			break;	/* goto sizeof (int) option return */
872bd670b35SErik Nordmark 		case _OLD_IPV6_RECVDSTOPTS:
873bd670b35SErik Nordmark 			*i1 =
874bd670b35SErik Nordmark 			    connp->conn_recv_ancillary.crb_old_ipv6_recvdstopts;
875bd670b35SErik Nordmark 			break;	/* goto sizeof (int) option return */
876bd670b35SErik Nordmark 		case IPV6_RECVRTHDRDSTOPTS:
877bd670b35SErik Nordmark 			*i1 = connp->conn_recv_ancillary.
878bd670b35SErik Nordmark 			    crb_ipv6_recvrthdrdstopts;
879bd670b35SErik Nordmark 			break;	/* goto sizeof (int) option return */
880bd670b35SErik Nordmark 		case IPV6_RECVRTHDR:
881bd670b35SErik Nordmark 			*i1 = connp->conn_recv_ancillary.crb_ipv6_recvrthdr;
882bd670b35SErik Nordmark 			break;	/* goto sizeof (int) option return */
883bd670b35SErik Nordmark 		case IPV6_PKTINFO: {
884bd670b35SErik Nordmark 			/* XXX assumes that caller has room for max size! */
885bd670b35SErik Nordmark 			struct in6_pktinfo *pkti;
886bd670b35SErik Nordmark 
887bd670b35SErik Nordmark 			pkti = (struct in6_pktinfo *)ptr;
888bd670b35SErik Nordmark 			pkti->ipi6_ifindex = ixa->ixa_ifindex;
889bd670b35SErik Nordmark 			if (ipp->ipp_fields & IPPF_ADDR)
890bd670b35SErik Nordmark 				pkti->ipi6_addr = ipp->ipp_addr;
891bd670b35SErik Nordmark 			else
892bd670b35SErik Nordmark 				pkti->ipi6_addr = ipv6_all_zeros;
893bd670b35SErik Nordmark 			return (sizeof (struct in6_pktinfo));
894bd670b35SErik Nordmark 		}
895bd670b35SErik Nordmark 		case IPV6_TCLASS:
896bd670b35SErik Nordmark 			*i1 = ipp->ipp_tclass;
897bd670b35SErik Nordmark 			break;	/* goto sizeof (int) option return */
898bd670b35SErik Nordmark 		case IPV6_NEXTHOP: {
899bd670b35SErik Nordmark 			sin6_t *sin6 = (sin6_t *)ptr;
900bd670b35SErik Nordmark 
901bd670b35SErik Nordmark 			if (ixa->ixa_flags & IXAF_NEXTHOP_SET)
902bd670b35SErik Nordmark 				return (0);
903bd670b35SErik Nordmark 
904bd670b35SErik Nordmark 			*sin6 = sin6_null;
905bd670b35SErik Nordmark 			sin6->sin6_family = AF_INET6;
906bd670b35SErik Nordmark 			sin6->sin6_addr = ixa->ixa_nexthop_v6;
907bd670b35SErik Nordmark 
908bd670b35SErik Nordmark 			return (sizeof (sin6_t));
909bd670b35SErik Nordmark 		}
910bd670b35SErik Nordmark 		case IPV6_HOPOPTS:
911bd670b35SErik Nordmark 			if (!(ipp->ipp_fields & IPPF_HOPOPTS))
912bd670b35SErik Nordmark 				return (0);
913bd670b35SErik Nordmark 			bcopy(ipp->ipp_hopopts, ptr,
914bd670b35SErik Nordmark 			    ipp->ipp_hopoptslen);
915bd670b35SErik Nordmark 			return (ipp->ipp_hopoptslen);
916bd670b35SErik Nordmark 		case IPV6_RTHDRDSTOPTS:
917bd670b35SErik Nordmark 			if (!(ipp->ipp_fields & IPPF_RTHDRDSTOPTS))
918bd670b35SErik Nordmark 				return (0);
919bd670b35SErik Nordmark 			bcopy(ipp->ipp_rthdrdstopts, ptr,
920bd670b35SErik Nordmark 			    ipp->ipp_rthdrdstoptslen);
921bd670b35SErik Nordmark 			return (ipp->ipp_rthdrdstoptslen);
922bd670b35SErik Nordmark 		case IPV6_RTHDR:
923bd670b35SErik Nordmark 			if (!(ipp->ipp_fields & IPPF_RTHDR))
924bd670b35SErik Nordmark 				return (0);
925bd670b35SErik Nordmark 			bcopy(ipp->ipp_rthdr, ptr, ipp->ipp_rthdrlen);
926bd670b35SErik Nordmark 			return (ipp->ipp_rthdrlen);
927bd670b35SErik Nordmark 		case IPV6_DSTOPTS:
928bd670b35SErik Nordmark 			if (!(ipp->ipp_fields & IPPF_DSTOPTS))
929bd670b35SErik Nordmark 				return (0);
930bd670b35SErik Nordmark 			bcopy(ipp->ipp_dstopts, ptr, ipp->ipp_dstoptslen);
931bd670b35SErik Nordmark 			return (ipp->ipp_dstoptslen);
932bd670b35SErik Nordmark 		case IPV6_PATHMTU:
933bd670b35SErik Nordmark 			return (ip_fill_mtuinfo(connp, ixa,
934bd670b35SErik Nordmark 			    (struct ip6_mtuinfo *)ptr));
935bd670b35SErik Nordmark 		case IPV6_SEC_OPT:
936bd670b35SErik Nordmark 			return (ipsec_req_from_conn(connp, (ipsec_req_t	*)ptr,
937bd670b35SErik Nordmark 			    IPSEC_AF_V6));
938bd670b35SErik Nordmark 		case IPV6_SRC_PREFERENCES:
939bd670b35SErik Nordmark 			return (ip6_get_src_preferences(ixa, (uint32_t *)ptr));
940bd670b35SErik Nordmark 		case IPV6_DONTFRAG:
941bd670b35SErik Nordmark 			*i1 = (ixa->ixa_flags & IXAF_DONTFRAG) != 0;
942bd670b35SErik Nordmark 			return (sizeof (int));
943bd670b35SErik Nordmark 		case IPV6_USE_MIN_MTU:
944bd670b35SErik Nordmark 			if (ixa->ixa_flags & IXAF_USE_MIN_MTU)
945bd670b35SErik Nordmark 				*i1 = ixa->ixa_use_min_mtu;
946bd670b35SErik Nordmark 			else
947bd670b35SErik Nordmark 				*i1 = IPV6_USE_MIN_MTU_MULTICAST;
948bd670b35SErik Nordmark 			break;
949bd670b35SErik Nordmark 		case IPV6_V6ONLY:
950bd670b35SErik Nordmark 			*i1 = connp->conn_ipv6_v6only;
951bd670b35SErik Nordmark 			return (sizeof (int));
952bd670b35SErik Nordmark 		default:
953bd670b35SErik Nordmark 			return (-1);
954bd670b35SErik Nordmark 		}
955bd670b35SErik Nordmark 		break;
956bd670b35SErik Nordmark 	case IPPROTO_UDP:
957bd670b35SErik Nordmark 		switch (name) {
958bd670b35SErik Nordmark 		case UDP_ANONPRIVBIND:
959bd670b35SErik Nordmark 			*i1 = connp->conn_anon_priv_bind;
960bd670b35SErik Nordmark 			break;
961bd670b35SErik Nordmark 		case UDP_EXCLBIND:
962bd670b35SErik Nordmark 			*i1 = connp->conn_exclbind ? UDP_EXCLBIND : 0;
963bd670b35SErik Nordmark 			break;
964bd670b35SErik Nordmark 		default:
965bd670b35SErik Nordmark 			return (-1);
966bd670b35SErik Nordmark 		}
967bd670b35SErik Nordmark 		break;
968bd670b35SErik Nordmark 	case IPPROTO_TCP:
969bd670b35SErik Nordmark 		switch (name) {
970bd670b35SErik Nordmark 		case TCP_RECVDSTADDR:
971bd670b35SErik Nordmark 			*i1 = connp->conn_recv_ancillary.crb_recvdstaddr;
972bd670b35SErik Nordmark 			break;
973bd670b35SErik Nordmark 		case TCP_ANONPRIVBIND:
974bd670b35SErik Nordmark 			*i1 = connp->conn_anon_priv_bind;
975bd670b35SErik Nordmark 			break;
976bd670b35SErik Nordmark 		case TCP_EXCLBIND:
977bd670b35SErik Nordmark 			*i1 = connp->conn_exclbind ? TCP_EXCLBIND : 0;
978bd670b35SErik Nordmark 			break;
979bd670b35SErik Nordmark 		default:
980bd670b35SErik Nordmark 			return (-1);
981bd670b35SErik Nordmark 		}
982bd670b35SErik Nordmark 		break;
983bd670b35SErik Nordmark 	default:
984bd670b35SErik Nordmark 		return (-1);
985bd670b35SErik Nordmark 	}
986bd670b35SErik Nordmark 	return (sizeof (int));
987bd670b35SErik Nordmark }
988bd670b35SErik Nordmark 
989bd670b35SErik Nordmark static int conn_opt_set_socket(conn_opt_arg_t *coa, t_scalar_t name,
990bd670b35SErik Nordmark     uint_t inlen, uchar_t *invalp, boolean_t checkonly, cred_t *cr);
991bd670b35SErik Nordmark static int conn_opt_set_ip(conn_opt_arg_t *coa, t_scalar_t name,
992bd670b35SErik Nordmark     uint_t inlen, uchar_t *invalp, boolean_t checkonly, cred_t *cr);
993bd670b35SErik Nordmark static int conn_opt_set_ipv6(conn_opt_arg_t *coa, t_scalar_t name,
994bd670b35SErik Nordmark     uint_t inlen, uchar_t *invalp, boolean_t checkonly, cred_t *cr);
995bd670b35SErik Nordmark static int conn_opt_set_udp(conn_opt_arg_t *coa, t_scalar_t name,
996bd670b35SErik Nordmark     uint_t inlen, uchar_t *invalp, boolean_t checkonly, cred_t *cr);
997bd670b35SErik Nordmark static int conn_opt_set_tcp(conn_opt_arg_t *coa, t_scalar_t name,
998bd670b35SErik Nordmark     uint_t inlen, uchar_t *invalp, boolean_t checkonly, cred_t *cr);
999bd670b35SErik Nordmark 
1000bd670b35SErik Nordmark /*
1001bd670b35SErik Nordmark  * This routine sets the most common socket options including some
1002bd670b35SErik Nordmark  * that are transport/ULP specific.
1003bd670b35SErik Nordmark  * It returns errno or zero.
1004bd670b35SErik Nordmark  *
1005bd670b35SErik Nordmark  * For fixed length options, there is no sanity check
1006bd670b35SErik Nordmark  * of passed in length is done. It is assumed *_optcom_req()
1007bd670b35SErik Nordmark  * routines do the right thing.
1008bd670b35SErik Nordmark  */
1009bd670b35SErik Nordmark int
conn_opt_set(conn_opt_arg_t * coa,t_scalar_t level,t_scalar_t name,uint_t inlen,uchar_t * invalp,boolean_t checkonly,cred_t * cr)1010bd670b35SErik Nordmark conn_opt_set(conn_opt_arg_t *coa, t_scalar_t level, t_scalar_t name,
1011bd670b35SErik Nordmark     uint_t inlen, uchar_t *invalp, boolean_t checkonly, cred_t *cr)
1012bd670b35SErik Nordmark {
1013bd670b35SErik Nordmark 	ASSERT(MUTEX_NOT_HELD(&coa->coa_connp->conn_lock));
1014bd670b35SErik Nordmark 
1015bd670b35SErik Nordmark 	/* We have different functions for different levels */
1016bd670b35SErik Nordmark 	switch (level) {
1017bd670b35SErik Nordmark 	case SOL_SOCKET:
1018bd670b35SErik Nordmark 		return (conn_opt_set_socket(coa, name, inlen, invalp,
1019bd670b35SErik Nordmark 		    checkonly, cr));
1020bd670b35SErik Nordmark 	case IPPROTO_IP:
1021bd670b35SErik Nordmark 		return (conn_opt_set_ip(coa, name, inlen, invalp,
1022bd670b35SErik Nordmark 		    checkonly, cr));
1023bd670b35SErik Nordmark 	case IPPROTO_IPV6:
1024bd670b35SErik Nordmark 		return (conn_opt_set_ipv6(coa, name, inlen, invalp,
1025bd670b35SErik Nordmark 		    checkonly, cr));
1026bd670b35SErik Nordmark 	case IPPROTO_UDP:
1027bd670b35SErik Nordmark 		return (conn_opt_set_udp(coa, name, inlen, invalp,
1028bd670b35SErik Nordmark 		    checkonly, cr));
1029bd670b35SErik Nordmark 	case IPPROTO_TCP:
1030bd670b35SErik Nordmark 		return (conn_opt_set_tcp(coa, name, inlen, invalp,
1031bd670b35SErik Nordmark 		    checkonly, cr));
1032bd670b35SErik Nordmark 	default:
1033bd670b35SErik Nordmark 		return (0);
1034bd670b35SErik Nordmark 	}
1035bd670b35SErik Nordmark }
1036bd670b35SErik Nordmark 
1037bd670b35SErik Nordmark /*
1038bd670b35SErik Nordmark  * Handle SOL_SOCKET
1039bd670b35SErik Nordmark  * Note that we do not handle SO_PROTOTYPE here. The ULPs that support
1040bd670b35SErik Nordmark  * it implement their own checks and setting of conn_proto.
1041bd670b35SErik Nordmark  */
1042bd670b35SErik Nordmark /* ARGSUSED1 */
1043bd670b35SErik Nordmark static int
conn_opt_set_socket(conn_opt_arg_t * coa,t_scalar_t name,uint_t inlen,uchar_t * invalp,boolean_t checkonly,cred_t * cr)1044bd670b35SErik Nordmark conn_opt_set_socket(conn_opt_arg_t *coa, t_scalar_t name, uint_t inlen,
1045bd670b35SErik Nordmark     uchar_t *invalp, boolean_t checkonly, cred_t *cr)
1046bd670b35SErik Nordmark {
1047bd670b35SErik Nordmark 	conn_t		*connp = coa->coa_connp;
1048bd670b35SErik Nordmark 	ip_xmit_attr_t	*ixa = coa->coa_ixa;
1049bd670b35SErik Nordmark 	int		*i1 = (int *)invalp;
1050bd670b35SErik Nordmark 	boolean_t	onoff = (*i1 == 0) ? 0 : 1;
1051bd670b35SErik Nordmark 
1052bd670b35SErik Nordmark 	switch (name) {
1053bd670b35SErik Nordmark 	case SO_ALLZONES:
1054bd670b35SErik Nordmark 		if (IPCL_IS_BOUND(connp))
1055bd670b35SErik Nordmark 			return (EINVAL);
1056bd670b35SErik Nordmark 		break;
1057bd670b35SErik Nordmark 	case SO_VRRP:
1058bd670b35SErik Nordmark 		if (secpolicy_ip_config(cr, checkonly) != 0)
1059bd670b35SErik Nordmark 			return (EACCES);
1060bd670b35SErik Nordmark 		break;
1061bd670b35SErik Nordmark 	case SO_MAC_EXEMPT:
1062bd670b35SErik Nordmark 		if (secpolicy_net_mac_aware(cr) != 0)
1063bd670b35SErik Nordmark 			return (EACCES);
1064bd670b35SErik Nordmark 		if (IPCL_IS_BOUND(connp))
1065bd670b35SErik Nordmark 			return (EINVAL);
1066bd670b35SErik Nordmark 		break;
1067bd670b35SErik Nordmark 	case SO_MAC_IMPLICIT:
1068bd670b35SErik Nordmark 		if (secpolicy_net_mac_implicit(cr) != 0)
1069bd670b35SErik Nordmark 			return (EACCES);
1070bd670b35SErik Nordmark 		break;
1071bd670b35SErik Nordmark 	}
1072bd670b35SErik Nordmark 	if (checkonly)
1073bd670b35SErik Nordmark 		return (0);
1074bd670b35SErik Nordmark 
1075bd670b35SErik Nordmark 	mutex_enter(&connp->conn_lock);
1076bd670b35SErik Nordmark 	/* Here we set the actual option value */
1077bd670b35SErik Nordmark 	switch (name) {
1078bd670b35SErik Nordmark 	case SO_DEBUG:
1079bd670b35SErik Nordmark 		connp->conn_debug = onoff;
1080bd670b35SErik Nordmark 		break;
1081bd670b35SErik Nordmark 	case SO_KEEPALIVE:
1082bd670b35SErik Nordmark 		connp->conn_keepalive = onoff;
1083bd670b35SErik Nordmark 		break;
1084bd670b35SErik Nordmark 	case SO_LINGER: {
1085bd670b35SErik Nordmark 		struct linger *lgr = (struct linger *)invalp;
1086bd670b35SErik Nordmark 
1087bd670b35SErik Nordmark 		if (lgr->l_onoff) {
1088bd670b35SErik Nordmark 			connp->conn_linger = 1;
1089bd670b35SErik Nordmark 			connp->conn_lingertime = lgr->l_linger;
1090bd670b35SErik Nordmark 		} else {
1091bd670b35SErik Nordmark 			connp->conn_linger = 0;
1092bd670b35SErik Nordmark 			connp->conn_lingertime = 0;
1093bd670b35SErik Nordmark 		}
1094bd670b35SErik Nordmark 		break;
1095bd670b35SErik Nordmark 	}
1096bd670b35SErik Nordmark 	case SO_OOBINLINE:
1097bd670b35SErik Nordmark 		connp->conn_oobinline = onoff;
1098bd670b35SErik Nordmark 		coa->coa_changed |= COA_OOBINLINE_CHANGED;
1099bd670b35SErik Nordmark 		break;
1100bd670b35SErik Nordmark 	case SO_REUSEADDR:
1101bd670b35SErik Nordmark 		connp->conn_reuseaddr = onoff;
1102bd670b35SErik Nordmark 		break;
1103bd670b35SErik Nordmark 	case SO_DONTROUTE:
1104bd670b35SErik Nordmark 		if (onoff)
1105bd670b35SErik Nordmark 			ixa->ixa_flags |= IXAF_DONTROUTE;
1106bd670b35SErik Nordmark 		else
1107bd670b35SErik Nordmark 			ixa->ixa_flags &= ~IXAF_DONTROUTE;
1108bd670b35SErik Nordmark 		coa->coa_changed |= COA_ROUTE_CHANGED;
1109bd670b35SErik Nordmark 		break;
1110bd670b35SErik Nordmark 	case SO_USELOOPBACK:
1111bd670b35SErik Nordmark 		connp->conn_useloopback = onoff;
1112bd670b35SErik Nordmark 		break;
1113bd670b35SErik Nordmark 	case SO_BROADCAST:
1114bd670b35SErik Nordmark 		connp->conn_broadcast = onoff;
1115bd670b35SErik Nordmark 		break;
1116bd670b35SErik Nordmark 	case SO_SNDBUF:
1117bd670b35SErik Nordmark 		/* ULP has range checked the value */
1118bd670b35SErik Nordmark 		connp->conn_sndbuf = *i1;
1119bd670b35SErik Nordmark 		coa->coa_changed |= COA_SNDBUF_CHANGED;
1120bd670b35SErik Nordmark 		break;
1121bd670b35SErik Nordmark 	case SO_RCVBUF:
1122bd670b35SErik Nordmark 		/* ULP has range checked the value */
1123bd670b35SErik Nordmark 		connp->conn_rcvbuf = *i1;
1124bd670b35SErik Nordmark 		coa->coa_changed |= COA_RCVBUF_CHANGED;
1125bd670b35SErik Nordmark 		break;
1126bd670b35SErik Nordmark 	case SO_RCVTIMEO:
1127bd670b35SErik Nordmark 	case SO_SNDTIMEO:
1128bd670b35SErik Nordmark 		/*
1129bd670b35SErik Nordmark 		 * Pass these two options in order for third part
1130bd670b35SErik Nordmark 		 * protocol usage.
1131bd670b35SErik Nordmark 		 */
1132bd670b35SErik Nordmark 		break;
1133bd670b35SErik Nordmark 	case SO_DGRAM_ERRIND:
1134bd670b35SErik Nordmark 		connp->conn_dgram_errind = onoff;
1135bd670b35SErik Nordmark 		break;
1136bd670b35SErik Nordmark 	case SO_RECVUCRED:
1137bd670b35SErik Nordmark 		connp->conn_recv_ancillary.crb_recvucred = onoff;
1138bd670b35SErik Nordmark 		break;
1139bd670b35SErik Nordmark 	case SO_ALLZONES:
1140bd670b35SErik Nordmark 		connp->conn_allzones = onoff;
1141bd670b35SErik Nordmark 		coa->coa_changed |= COA_ROUTE_CHANGED;
1142bd670b35SErik Nordmark 		if (onoff)
1143bd670b35SErik Nordmark 			ixa->ixa_zoneid = ALL_ZONES;
1144bd670b35SErik Nordmark 		else
1145bd670b35SErik Nordmark 			ixa->ixa_zoneid = connp->conn_zoneid;
1146bd670b35SErik Nordmark 		break;
1147bd670b35SErik Nordmark 	case SO_TIMESTAMP:
1148bd670b35SErik Nordmark 		connp->conn_recv_ancillary.crb_timestamp = onoff;
1149bd670b35SErik Nordmark 		break;
1150bd670b35SErik Nordmark 	case SO_VRRP:
1151bd670b35SErik Nordmark 		connp->conn_isvrrp = onoff;
1152bd670b35SErik Nordmark 		break;
1153bd670b35SErik Nordmark 	case SO_ANON_MLP:
1154bd670b35SErik Nordmark 		connp->conn_anon_mlp = onoff;
1155bd670b35SErik Nordmark 		break;
1156bd670b35SErik Nordmark 	case SO_MAC_EXEMPT:
1157bd670b35SErik Nordmark 		connp->conn_mac_mode = onoff ?
1158bd670b35SErik Nordmark 		    CONN_MAC_AWARE : CONN_MAC_DEFAULT;
1159bd670b35SErik Nordmark 		break;
1160bd670b35SErik Nordmark 	case SO_MAC_IMPLICIT:
1161bd670b35SErik Nordmark 		connp->conn_mac_mode = onoff ?
1162bd670b35SErik Nordmark 		    CONN_MAC_IMPLICIT : CONN_MAC_DEFAULT;
1163bd670b35SErik Nordmark 		break;
1164bd670b35SErik Nordmark 	case SO_EXCLBIND:
1165bd670b35SErik Nordmark 		connp->conn_exclbind = onoff;
1166bd670b35SErik Nordmark 		break;
1167bd670b35SErik Nordmark 	}
1168bd670b35SErik Nordmark 	mutex_exit(&connp->conn_lock);
1169bd670b35SErik Nordmark 	return (0);
1170bd670b35SErik Nordmark }
1171bd670b35SErik Nordmark 
1172bd670b35SErik Nordmark /* Handle IPPROTO_IP */
1173bd670b35SErik Nordmark static int
conn_opt_set_ip(conn_opt_arg_t * coa,t_scalar_t name,uint_t inlen,uchar_t * invalp,boolean_t checkonly,cred_t * cr)1174bd670b35SErik Nordmark conn_opt_set_ip(conn_opt_arg_t *coa, t_scalar_t name, uint_t inlen,
1175bd670b35SErik Nordmark     uchar_t *invalp, boolean_t checkonly, cred_t *cr)
1176bd670b35SErik Nordmark {
1177bd670b35SErik Nordmark 	conn_t		*connp = coa->coa_connp;
1178bd670b35SErik Nordmark 	ip_xmit_attr_t	*ixa = coa->coa_ixa;
1179bd670b35SErik Nordmark 	ip_pkt_t	*ipp = coa->coa_ipp;
1180bd670b35SErik Nordmark 	int		*i1 = (int *)invalp;
1181bd670b35SErik Nordmark 	boolean_t	onoff = (*i1 == 0) ? 0 : 1;
1182bd670b35SErik Nordmark 	ipaddr_t	addr = (ipaddr_t)*i1;
1183bd670b35SErik Nordmark 	uint_t		ifindex;
1184bd670b35SErik Nordmark 	zoneid_t	zoneid = IPCL_ZONEID(connp);
1185bd670b35SErik Nordmark 	ipif_t		*ipif;
1186bd670b35SErik Nordmark 	ip_stack_t	*ipst = connp->conn_netstack->netstack_ip;
1187bd670b35SErik Nordmark 	int		error;
1188bd670b35SErik Nordmark 
1189bd670b35SErik Nordmark 	if (connp->conn_family != AF_INET)
1190bd670b35SErik Nordmark 		return (EINVAL);
1191bd670b35SErik Nordmark 
1192bd670b35SErik Nordmark 	switch (name) {
1193bd670b35SErik Nordmark 	case IP_TTL:
1194bd670b35SErik Nordmark 		/* Don't allow zero */
1195bd670b35SErik Nordmark 		if (*i1 < 1 || *i1 > 255)
1196bd670b35SErik Nordmark 			return (EINVAL);
1197bd670b35SErik Nordmark 		break;
1198bd670b35SErik Nordmark 	case IP_MULTICAST_IF:
1199bd670b35SErik Nordmark 		if (addr == INADDR_ANY) {
1200bd670b35SErik Nordmark 			/* Clear */
1201bd670b35SErik Nordmark 			ifindex = 0;
1202bd670b35SErik Nordmark 			break;
1203bd670b35SErik Nordmark 		}
1204bd670b35SErik Nordmark 		ipif = ipif_lookup_addr(addr, NULL, zoneid, ipst);
1205bd670b35SErik Nordmark 		if (ipif == NULL)
1206bd670b35SErik Nordmark 			return (EHOSTUNREACH);
1207bd670b35SErik Nordmark 		/* not supported by the virtual network iface */
1208bd670b35SErik Nordmark 		if (IS_VNI(ipif->ipif_ill)) {
1209bd670b35SErik Nordmark 			ipif_refrele(ipif);
1210bd670b35SErik Nordmark 			return (EINVAL);
1211bd670b35SErik Nordmark 		}
1212bd670b35SErik Nordmark 		ifindex = ipif->ipif_ill->ill_phyint->phyint_ifindex;
1213bd670b35SErik Nordmark 		ipif_refrele(ipif);
1214bd670b35SErik Nordmark 		break;
1215bd670b35SErik Nordmark 	case IP_NEXTHOP: {
1216bd670b35SErik Nordmark 		ire_t	*ire;
1217bd670b35SErik Nordmark 
1218bd670b35SErik Nordmark 		if (addr == INADDR_ANY) {
1219bd670b35SErik Nordmark 			/* Clear */
1220bd670b35SErik Nordmark 			break;
1221bd670b35SErik Nordmark 		}
1222bd670b35SErik Nordmark 		/* Verify that the next-hop is on-link */
1223bd670b35SErik Nordmark 		ire = ire_ftable_lookup_v4(addr, 0, 0, IRE_ONLINK, NULL, zoneid,
1224bd670b35SErik Nordmark 		    NULL, MATCH_IRE_TYPE, 0, ipst, NULL);
1225bd670b35SErik Nordmark 		if (ire == NULL)
1226bd670b35SErik Nordmark 			return (EHOSTUNREACH);
1227bd670b35SErik Nordmark 		ire_refrele(ire);
1228bd670b35SErik Nordmark 		break;
1229bd670b35SErik Nordmark 	}
1230bd670b35SErik Nordmark 	case IP_OPTIONS:
1231bd670b35SErik Nordmark 	case T_IP_OPTIONS: {
1232bd670b35SErik Nordmark 		uint_t newlen;
1233bd670b35SErik Nordmark 
1234bd670b35SErik Nordmark 		if (ipp->ipp_fields & IPPF_LABEL_V4)
1235bd670b35SErik Nordmark 			newlen = inlen + (ipp->ipp_label_len_v4 + 3) & ~3;
1236bd670b35SErik Nordmark 		else
1237bd670b35SErik Nordmark 			newlen = inlen;
1238bd670b35SErik Nordmark 		if ((inlen & 0x3) || newlen > IP_MAX_OPT_LENGTH) {
1239bd670b35SErik Nordmark 			return (EINVAL);
1240bd670b35SErik Nordmark 		}
1241bd670b35SErik Nordmark 		break;
1242bd670b35SErik Nordmark 	}
1243bd670b35SErik Nordmark 	case IP_PKTINFO: {
1244bd670b35SErik Nordmark 		struct in_pktinfo *pktinfo;
1245bd670b35SErik Nordmark 
1246bd670b35SErik Nordmark 		/* Two different valid lengths */
1247bd670b35SErik Nordmark 		if (inlen != sizeof (int) &&
1248bd670b35SErik Nordmark 		    inlen != sizeof (struct in_pktinfo))
1249bd670b35SErik Nordmark 			return (EINVAL);
1250bd670b35SErik Nordmark 		if (inlen == sizeof (int))
1251bd670b35SErik Nordmark 			break;
1252bd670b35SErik Nordmark 
1253bd670b35SErik Nordmark 		pktinfo = (struct in_pktinfo *)invalp;
1254bd670b35SErik Nordmark 		if (pktinfo->ipi_spec_dst.s_addr != INADDR_ANY) {
1255bd670b35SErik Nordmark 			switch (ip_laddr_verify_v4(pktinfo->ipi_spec_dst.s_addr,
1256bd670b35SErik Nordmark 			    zoneid, ipst, B_FALSE)) {
1257bd670b35SErik Nordmark 			case IPVL_UNICAST_UP:
1258bd670b35SErik Nordmark 			case IPVL_UNICAST_DOWN:
1259bd670b35SErik Nordmark 				break;
1260bd670b35SErik Nordmark 			default:
1261bd670b35SErik Nordmark 				return (EADDRNOTAVAIL);
1262bd670b35SErik Nordmark 			}
1263bd670b35SErik Nordmark 		}
1264b1b66e09SErik Nordmark 		if (!ip_xmit_ifindex_valid(pktinfo->ipi_ifindex, zoneid,
1265b1b66e09SErik Nordmark 		    B_FALSE, ipst))
1266bd670b35SErik Nordmark 			return (ENXIO);
1267bd670b35SErik Nordmark 		break;
1268bd670b35SErik Nordmark 	}
1269bd670b35SErik Nordmark 	case IP_BOUND_IF:
1270bd670b35SErik Nordmark 		ifindex = *(uint_t *)i1;
1271bd670b35SErik Nordmark 
1272bd670b35SErik Nordmark 		/* Just check it is ok. */
1273b1b66e09SErik Nordmark 		if (!ip_xmit_ifindex_valid(ifindex, zoneid, B_FALSE, ipst))
1274bd670b35SErik Nordmark 			return (ENXIO);
1275bd670b35SErik Nordmark 		break;
1276bd670b35SErik Nordmark 	}
1277bd670b35SErik Nordmark 	if (checkonly)
1278bd670b35SErik Nordmark 		return (0);
1279bd670b35SErik Nordmark 
1280bd670b35SErik Nordmark 	/* Here we set the actual option value */
1281bd670b35SErik Nordmark 	/*
1282bd670b35SErik Nordmark 	 * conn_lock protects the bitfields, and is used to
1283bd670b35SErik Nordmark 	 * set the fields atomically. Not needed for ixa settings since
1284bd670b35SErik Nordmark 	 * the caller has an exclusive copy of the ixa.
1285bd670b35SErik Nordmark 	 * We can not hold conn_lock across the multicast options though.
1286bd670b35SErik Nordmark 	 */
1287bd670b35SErik Nordmark 	switch (name) {
1288bd670b35SErik Nordmark 	case IP_OPTIONS:
1289bd670b35SErik Nordmark 	case T_IP_OPTIONS:
1290bd670b35SErik Nordmark 		/* Save options for use by IP. */
1291bd670b35SErik Nordmark 		mutex_enter(&connp->conn_lock);
1292bd670b35SErik Nordmark 		error = optcom_pkt_set(invalp, inlen,
1293bd670b35SErik Nordmark 		    (uchar_t **)&ipp->ipp_ipv4_options,
1294bd670b35SErik Nordmark 		    &ipp->ipp_ipv4_options_len);
1295bd670b35SErik Nordmark 		if (error != 0) {
1296bd670b35SErik Nordmark 			mutex_exit(&connp->conn_lock);
1297bd670b35SErik Nordmark 			return (error);
1298bd670b35SErik Nordmark 		}
1299bd670b35SErik Nordmark 		if (ipp->ipp_ipv4_options_len == 0) {
1300bd670b35SErik Nordmark 			ipp->ipp_fields &= ~IPPF_IPV4_OPTIONS;
1301bd670b35SErik Nordmark 		} else {
1302bd670b35SErik Nordmark 			ipp->ipp_fields |= IPPF_IPV4_OPTIONS;
1303bd670b35SErik Nordmark 		}
1304bd670b35SErik Nordmark 		mutex_exit(&connp->conn_lock);
1305bd670b35SErik Nordmark 		coa->coa_changed |= COA_HEADER_CHANGED;
1306bd670b35SErik Nordmark 		coa->coa_changed |= COA_WROFF_CHANGED;
1307bd670b35SErik Nordmark 		break;
1308bd670b35SErik Nordmark 
1309bd670b35SErik Nordmark 	case IP_TTL:
1310bd670b35SErik Nordmark 		mutex_enter(&connp->conn_lock);
1311bd670b35SErik Nordmark 		ipp->ipp_unicast_hops = *i1;
1312bd670b35SErik Nordmark 		mutex_exit(&connp->conn_lock);
1313bd670b35SErik Nordmark 		coa->coa_changed |= COA_HEADER_CHANGED;
1314bd670b35SErik Nordmark 		break;
1315bd670b35SErik Nordmark 	case IP_TOS:
1316bd670b35SErik Nordmark 	case T_IP_TOS:
1317bd670b35SErik Nordmark 		mutex_enter(&connp->conn_lock);
1318bd670b35SErik Nordmark 		if (*i1 == -1) {
1319bd670b35SErik Nordmark 			ipp->ipp_type_of_service = 0;
1320bd670b35SErik Nordmark 		} else {
1321bd670b35SErik Nordmark 			ipp->ipp_type_of_service = *i1;
1322bd670b35SErik Nordmark 		}
1323bd670b35SErik Nordmark 		mutex_exit(&connp->conn_lock);
1324bd670b35SErik Nordmark 		coa->coa_changed |= COA_HEADER_CHANGED;
1325bd670b35SErik Nordmark 		break;
1326bd670b35SErik Nordmark 	case IP_MULTICAST_IF:
1327bd670b35SErik Nordmark 		ixa->ixa_multicast_ifindex = ifindex;
1328bd670b35SErik Nordmark 		ixa->ixa_multicast_ifaddr = addr;
1329bd670b35SErik Nordmark 		coa->coa_changed |= COA_ROUTE_CHANGED;
1330bd670b35SErik Nordmark 		break;
1331bd670b35SErik Nordmark 	case IP_MULTICAST_TTL:
1332bd670b35SErik Nordmark 		ixa->ixa_multicast_ttl = *invalp;
1333bd670b35SErik Nordmark 		/* Handled automatically by ip_output */
1334bd670b35SErik Nordmark 		break;
1335bd670b35SErik Nordmark 	case IP_MULTICAST_LOOP:
1336bd670b35SErik Nordmark 		if (*invalp != 0)
1337bd670b35SErik Nordmark 			ixa->ixa_flags |= IXAF_MULTICAST_LOOP;
1338bd670b35SErik Nordmark 		else
1339bd670b35SErik Nordmark 			ixa->ixa_flags &= ~IXAF_MULTICAST_LOOP;
1340bd670b35SErik Nordmark 		/* Handled automatically by ip_output */
1341bd670b35SErik Nordmark 		break;
1342bd670b35SErik Nordmark 	case IP_RECVOPTS:
1343bd670b35SErik Nordmark 		mutex_enter(&connp->conn_lock);
1344bd670b35SErik Nordmark 		connp->conn_recv_ancillary.crb_recvopts = onoff;
1345bd670b35SErik Nordmark 		mutex_exit(&connp->conn_lock);
1346bd670b35SErik Nordmark 		break;
1347bd670b35SErik Nordmark 	case IP_RECVDSTADDR:
1348bd670b35SErik Nordmark 		mutex_enter(&connp->conn_lock);
1349bd670b35SErik Nordmark 		connp->conn_recv_ancillary.crb_recvdstaddr = onoff;
1350bd670b35SErik Nordmark 		mutex_exit(&connp->conn_lock);
1351bd670b35SErik Nordmark 		break;
1352bd670b35SErik Nordmark 	case IP_RECVIF:
1353bd670b35SErik Nordmark 		mutex_enter(&connp->conn_lock);
1354bd670b35SErik Nordmark 		connp->conn_recv_ancillary.crb_recvif = onoff;
1355bd670b35SErik Nordmark 		mutex_exit(&connp->conn_lock);
1356bd670b35SErik Nordmark 		break;
1357bd670b35SErik Nordmark 	case IP_RECVSLLA:
1358bd670b35SErik Nordmark 		mutex_enter(&connp->conn_lock);
1359bd670b35SErik Nordmark 		connp->conn_recv_ancillary.crb_recvslla = onoff;
1360bd670b35SErik Nordmark 		mutex_exit(&connp->conn_lock);
1361bd670b35SErik Nordmark 		break;
1362bd670b35SErik Nordmark 	case IP_RECVTTL:
1363bd670b35SErik Nordmark 		mutex_enter(&connp->conn_lock);
1364bd670b35SErik Nordmark 		connp->conn_recv_ancillary.crb_recvttl = onoff;
1365bd670b35SErik Nordmark 		mutex_exit(&connp->conn_lock);
1366bd670b35SErik Nordmark 		break;
1367bd670b35SErik Nordmark 	case IP_PKTINFO: {
1368bd670b35SErik Nordmark 		/*
1369bd670b35SErik Nordmark 		 * This also handles IP_RECVPKTINFO.
1370bd670b35SErik Nordmark 		 * IP_PKTINFO and IP_RECVPKTINFO have same value.
1371bd670b35SErik Nordmark 		 * Differentiation is based on the size of the
1372bd670b35SErik Nordmark 		 * argument passed in.
1373bd670b35SErik Nordmark 		 */
1374bd670b35SErik Nordmark 		struct in_pktinfo *pktinfo;
1375bd670b35SErik Nordmark 
1376bd670b35SErik Nordmark 		if (inlen == sizeof (int)) {
1377bd670b35SErik Nordmark 			/* This is IP_RECVPKTINFO option. */
1378bd670b35SErik Nordmark 			mutex_enter(&connp->conn_lock);
1379bd670b35SErik Nordmark 			connp->conn_recv_ancillary.crb_ip_recvpktinfo =
1380bd670b35SErik Nordmark 			    onoff;
1381bd670b35SErik Nordmark 			mutex_exit(&connp->conn_lock);
1382bd670b35SErik Nordmark 			break;
1383bd670b35SErik Nordmark 		}
1384bd670b35SErik Nordmark 
1385bd670b35SErik Nordmark 		/* This is IP_PKTINFO option. */
1386bd670b35SErik Nordmark 		mutex_enter(&connp->conn_lock);
1387bd670b35SErik Nordmark 		pktinfo = (struct in_pktinfo *)invalp;
1388*ac3e5be0SMarcel Telka 		if (pktinfo->ipi_spec_dst.s_addr != INADDR_ANY) {
1389bd670b35SErik Nordmark 			ipp->ipp_fields |= IPPF_ADDR;
1390bd670b35SErik Nordmark 			IN6_INADDR_TO_V4MAPPED(&pktinfo->ipi_spec_dst,
1391bd670b35SErik Nordmark 			    &ipp->ipp_addr);
1392bd670b35SErik Nordmark 		} else {
1393bd670b35SErik Nordmark 			ipp->ipp_fields &= ~IPPF_ADDR;
1394bd670b35SErik Nordmark 			ipp->ipp_addr = ipv6_all_zeros;
1395bd670b35SErik Nordmark 		}
1396bd670b35SErik Nordmark 		mutex_exit(&connp->conn_lock);
1397bd670b35SErik Nordmark 		ixa->ixa_ifindex = pktinfo->ipi_ifindex;
1398bd670b35SErik Nordmark 		coa->coa_changed |= COA_ROUTE_CHANGED;
1399bd670b35SErik Nordmark 		coa->coa_changed |= COA_HEADER_CHANGED;
1400bd670b35SErik Nordmark 		break;
1401bd670b35SErik Nordmark 	}
1402bd670b35SErik Nordmark 	case IP_DONTFRAG:
1403bd670b35SErik Nordmark 		if (onoff) {
1404bd670b35SErik Nordmark 			ixa->ixa_flags |= (IXAF_DONTFRAG | IXAF_PMTU_IPV4_DF);
1405bd670b35SErik Nordmark 			ixa->ixa_flags &= ~IXAF_PMTU_DISCOVERY;
1406bd670b35SErik Nordmark 		} else {
1407bd670b35SErik Nordmark 			ixa->ixa_flags &= ~(IXAF_DONTFRAG | IXAF_PMTU_IPV4_DF);
1408bd670b35SErik Nordmark 			ixa->ixa_flags |= IXAF_PMTU_DISCOVERY;
1409bd670b35SErik Nordmark 		}
1410bd670b35SErik Nordmark 		/* Need to redo ip_attr_connect */
1411bd670b35SErik Nordmark 		coa->coa_changed |= COA_ROUTE_CHANGED;
1412bd670b35SErik Nordmark 		break;
1413bd670b35SErik Nordmark 	case IP_ADD_MEMBERSHIP:
1414bd670b35SErik Nordmark 	case IP_DROP_MEMBERSHIP:
1415bd670b35SErik Nordmark 	case MCAST_JOIN_GROUP:
1416bd670b35SErik Nordmark 	case MCAST_LEAVE_GROUP:
1417bd670b35SErik Nordmark 		return (ip_opt_set_multicast_group(connp, name,
1418bd670b35SErik Nordmark 		    invalp, B_FALSE, checkonly));
1419bd670b35SErik Nordmark 
1420bd670b35SErik Nordmark 	case IP_BLOCK_SOURCE:
1421bd670b35SErik Nordmark 	case IP_UNBLOCK_SOURCE:
1422bd670b35SErik Nordmark 	case IP_ADD_SOURCE_MEMBERSHIP:
1423bd670b35SErik Nordmark 	case IP_DROP_SOURCE_MEMBERSHIP:
1424bd670b35SErik Nordmark 	case MCAST_BLOCK_SOURCE:
1425bd670b35SErik Nordmark 	case MCAST_UNBLOCK_SOURCE:
1426bd670b35SErik Nordmark 	case MCAST_JOIN_SOURCE_GROUP:
1427bd670b35SErik Nordmark 	case MCAST_LEAVE_SOURCE_GROUP:
1428bd670b35SErik Nordmark 		return (ip_opt_set_multicast_sources(connp, name,
1429bd670b35SErik Nordmark 		    invalp, B_FALSE, checkonly));
1430bd670b35SErik Nordmark 
1431bd670b35SErik Nordmark 	case IP_SEC_OPT:
1432bd670b35SErik Nordmark 		mutex_enter(&connp->conn_lock);
1433bd670b35SErik Nordmark 		error = ipsec_set_req(cr, connp, (ipsec_req_t *)invalp);
1434bd670b35SErik Nordmark 		mutex_exit(&connp->conn_lock);
1435bd670b35SErik Nordmark 		if (error != 0) {
1436bd670b35SErik Nordmark 			return (error);
1437bd670b35SErik Nordmark 		}
1438bd670b35SErik Nordmark 		/* This is an IPsec policy change - redo ip_attr_connect */
1439bd670b35SErik Nordmark 		coa->coa_changed |= COA_ROUTE_CHANGED;
1440bd670b35SErik Nordmark 		break;
1441bd670b35SErik Nordmark 	case IP_NEXTHOP:
1442bd670b35SErik Nordmark 		ixa->ixa_nexthop_v4 = addr;
1443bd670b35SErik Nordmark 		if (addr != INADDR_ANY)
1444bd670b35SErik Nordmark 			ixa->ixa_flags |= IXAF_NEXTHOP_SET;
1445bd670b35SErik Nordmark 		else
1446bd670b35SErik Nordmark 			ixa->ixa_flags &= ~IXAF_NEXTHOP_SET;
1447bd670b35SErik Nordmark 		coa->coa_changed |= COA_ROUTE_CHANGED;
1448bd670b35SErik Nordmark 		break;
1449bd670b35SErik Nordmark 
1450bd670b35SErik Nordmark 	case IP_BOUND_IF:
1451bd670b35SErik Nordmark 		ixa->ixa_ifindex = ifindex;		/* Send */
1452bd670b35SErik Nordmark 		mutex_enter(&connp->conn_lock);
1453bd670b35SErik Nordmark 		connp->conn_incoming_ifindex = ifindex;	/* Receive */
1454bd670b35SErik Nordmark 		connp->conn_bound_if = ifindex;		/* getsockopt */
1455bd670b35SErik Nordmark 		mutex_exit(&connp->conn_lock);
1456bd670b35SErik Nordmark 		coa->coa_changed |= COA_ROUTE_CHANGED;
1457bd670b35SErik Nordmark 		break;
1458bd670b35SErik Nordmark 	case IP_UNSPEC_SRC:
1459bd670b35SErik Nordmark 		mutex_enter(&connp->conn_lock);
1460bd670b35SErik Nordmark 		connp->conn_unspec_src = onoff;
1461bd670b35SErik Nordmark 		if (onoff)
1462bd670b35SErik Nordmark 			ixa->ixa_flags &= ~IXAF_VERIFY_SOURCE;
1463bd670b35SErik Nordmark 		else
1464bd670b35SErik Nordmark 			ixa->ixa_flags |= IXAF_VERIFY_SOURCE;
1465bd670b35SErik Nordmark 
1466bd670b35SErik Nordmark 		mutex_exit(&connp->conn_lock);
1467bd670b35SErik Nordmark 		break;
1468bd670b35SErik Nordmark 	case IP_BROADCAST_TTL:
1469bd670b35SErik Nordmark 		ixa->ixa_broadcast_ttl = *invalp;
1470bd670b35SErik Nordmark 		ixa->ixa_flags |= IXAF_BROADCAST_TTL_SET;
1471bd670b35SErik Nordmark 		/* Handled automatically by ip_output */
1472bd670b35SErik Nordmark 		break;
1473bd670b35SErik Nordmark 	case MRT_INIT:
1474bd670b35SErik Nordmark 	case MRT_DONE:
1475bd670b35SErik Nordmark 	case MRT_ADD_VIF:
1476bd670b35SErik Nordmark 	case MRT_DEL_VIF:
1477bd670b35SErik Nordmark 	case MRT_ADD_MFC:
1478bd670b35SErik Nordmark 	case MRT_DEL_MFC:
1479bd670b35SErik Nordmark 	case MRT_ASSERT:
1480bd670b35SErik Nordmark 		if ((error = secpolicy_ip_config(cr, B_FALSE)) != 0) {
1481bd670b35SErik Nordmark 			return (error);
1482bd670b35SErik Nordmark 		}
1483bd670b35SErik Nordmark 		error = ip_mrouter_set((int)name, connp, checkonly,
1484bd670b35SErik Nordmark 		    (uchar_t *)invalp, inlen);
1485bd670b35SErik Nordmark 		if (error) {
1486bd670b35SErik Nordmark 			return (error);
1487bd670b35SErik Nordmark 		}
1488bd670b35SErik Nordmark 		return (0);
1489bd670b35SErik Nordmark 
1490bd670b35SErik Nordmark 	}
1491bd670b35SErik Nordmark 	return (0);
1492bd670b35SErik Nordmark }
1493bd670b35SErik Nordmark 
1494bd670b35SErik Nordmark /* Handle IPPROTO_IPV6 */
1495bd670b35SErik Nordmark static int
conn_opt_set_ipv6(conn_opt_arg_t * coa,t_scalar_t name,uint_t inlen,uchar_t * invalp,boolean_t checkonly,cred_t * cr)1496bd670b35SErik Nordmark conn_opt_set_ipv6(conn_opt_arg_t *coa, t_scalar_t name, uint_t inlen,
1497bd670b35SErik Nordmark     uchar_t *invalp, boolean_t checkonly, cred_t *cr)
1498bd670b35SErik Nordmark {
1499bd670b35SErik Nordmark 	conn_t		*connp = coa->coa_connp;
1500bd670b35SErik Nordmark 	ip_xmit_attr_t	*ixa = coa->coa_ixa;
1501bd670b35SErik Nordmark 	ip_pkt_t	*ipp = coa->coa_ipp;
1502bd670b35SErik Nordmark 	int		*i1 = (int *)invalp;
1503bd670b35SErik Nordmark 	boolean_t	onoff = (*i1 == 0) ? 0 : 1;
1504bd670b35SErik Nordmark 	uint_t		ifindex;
1505bd670b35SErik Nordmark 	zoneid_t	zoneid = IPCL_ZONEID(connp);
1506bd670b35SErik Nordmark 	ip_stack_t	*ipst = connp->conn_netstack->netstack_ip;
1507bd670b35SErik Nordmark 	int		error;
1508bd670b35SErik Nordmark 
1509bd670b35SErik Nordmark 	if (connp->conn_family != AF_INET6)
1510bd670b35SErik Nordmark 		return (EINVAL);
1511bd670b35SErik Nordmark 
1512bd670b35SErik Nordmark 	switch (name) {
1513bd670b35SErik Nordmark 	case IPV6_MULTICAST_IF:
1514bd670b35SErik Nordmark 		/*
1515bd670b35SErik Nordmark 		 * The only possible error is EINVAL.
1516bd670b35SErik Nordmark 		 * We call this option on both V4 and V6
1517bd670b35SErik Nordmark 		 * If both fail, then this call returns
1518bd670b35SErik Nordmark 		 * EINVAL. If at least one of them succeeds we
1519bd670b35SErik Nordmark 		 * return success.
1520bd670b35SErik Nordmark 		 */
1521bd670b35SErik Nordmark 		ifindex = *(uint_t *)i1;
1522bd670b35SErik Nordmark 
1523b1b66e09SErik Nordmark 		if (!ip_xmit_ifindex_valid(ifindex, zoneid, B_TRUE, ipst) &&
1524b1b66e09SErik Nordmark 		    !ip_xmit_ifindex_valid(ifindex, zoneid, B_FALSE, ipst))
1525bd670b35SErik Nordmark 			return (EINVAL);
1526bd670b35SErik Nordmark 		break;
1527bd670b35SErik Nordmark 	case IPV6_UNICAST_HOPS:
1528bd670b35SErik Nordmark 		/* Don't allow zero. -1 means to use default */
1529bd670b35SErik Nordmark 		if (*i1 < -1 || *i1 == 0 || *i1 > IPV6_MAX_HOPS)
1530bd670b35SErik Nordmark 			return (EINVAL);
1531bd670b35SErik Nordmark 		break;
1532bd670b35SErik Nordmark 	case IPV6_MULTICAST_HOPS:
1533bd670b35SErik Nordmark 		/* -1 means use default */
1534bd670b35SErik Nordmark 		if (*i1 < -1 || *i1 > IPV6_MAX_HOPS)
1535bd670b35SErik Nordmark 			return (EINVAL);
1536bd670b35SErik Nordmark 		break;
1537bd670b35SErik Nordmark 	case IPV6_MULTICAST_LOOP:
1538bd670b35SErik Nordmark 		if (*i1 != 0 && *i1 != 1)
1539bd670b35SErik Nordmark 			return (EINVAL);
1540bd670b35SErik Nordmark 		break;
1541bd670b35SErik Nordmark 	case IPV6_BOUND_IF:
1542bd670b35SErik Nordmark 		ifindex = *(uint_t *)i1;
1543bd670b35SErik Nordmark 
1544b1b66e09SErik Nordmark 		if (!ip_xmit_ifindex_valid(ifindex, zoneid, B_TRUE, ipst))
1545bd670b35SErik Nordmark 			return (ENXIO);
1546bd670b35SErik Nordmark 		break;
1547bd670b35SErik Nordmark 	case IPV6_PKTINFO: {
1548bd670b35SErik Nordmark 		struct in6_pktinfo *pkti;
1549bd670b35SErik Nordmark 		boolean_t isv6;
1550bd670b35SErik Nordmark 
1551bd670b35SErik Nordmark 		if (inlen != 0 && inlen != sizeof (struct in6_pktinfo))
1552bd670b35SErik Nordmark 			return (EINVAL);
1553bd670b35SErik Nordmark 		if (inlen == 0)
1554bd670b35SErik Nordmark 			break;	/* Clear values below */
1555bd670b35SErik Nordmark 
1556bd670b35SErik Nordmark 		/*
1557bd670b35SErik Nordmark 		 * Verify the source address and ifindex. Privileged users
1558bd670b35SErik Nordmark 		 * can use any source address.
1559bd670b35SErik Nordmark 		 */
1560bd670b35SErik Nordmark 		pkti = (struct in6_pktinfo *)invalp;
1561bd670b35SErik Nordmark 
1562bd670b35SErik Nordmark 		/*
1563bd670b35SErik Nordmark 		 * For link-local addresses we use the ipi6_ifindex when
1564bd670b35SErik Nordmark 		 * we verify the local address.
1565bd670b35SErik Nordmark 		 * If net_rawaccess then any source address can be used.
1566bd670b35SErik Nordmark 		 */
1567bd670b35SErik Nordmark 		if (!IN6_IS_ADDR_UNSPECIFIED(&pkti->ipi6_addr) &&
1568bd670b35SErik Nordmark 		    secpolicy_net_rawaccess(cr) != 0) {
1569bd670b35SErik Nordmark 			uint_t scopeid = 0;
1570bd670b35SErik Nordmark 			in6_addr_t *v6src = &pkti->ipi6_addr;
1571bd670b35SErik Nordmark 			ipaddr_t v4src;
1572bd670b35SErik Nordmark 			ip_laddr_t laddr_type = IPVL_UNICAST_UP;
1573bd670b35SErik Nordmark 
1574bd670b35SErik Nordmark 			if (IN6_IS_ADDR_V4MAPPED(v6src)) {
1575bd670b35SErik Nordmark 				IN6_V4MAPPED_TO_IPADDR(v6src, v4src);
1576bd670b35SErik Nordmark 				if (v4src != INADDR_ANY) {
1577bd670b35SErik Nordmark 					laddr_type = ip_laddr_verify_v4(v4src,
1578bd670b35SErik Nordmark 					    zoneid, ipst, B_FALSE);
1579bd670b35SErik Nordmark 				}
1580bd670b35SErik Nordmark 			} else {
1581bd670b35SErik Nordmark 				if (IN6_IS_ADDR_LINKSCOPE(v6src))
1582bd670b35SErik Nordmark 					scopeid = pkti->ipi6_ifindex;
1583bd670b35SErik Nordmark 
1584bd670b35SErik Nordmark 				laddr_type = ip_laddr_verify_v6(v6src, zoneid,
1585bd670b35SErik Nordmark 				    ipst, B_FALSE, scopeid);
1586bd670b35SErik Nordmark 			}
1587bd670b35SErik Nordmark 			switch (laddr_type) {
1588bd670b35SErik Nordmark 			case IPVL_UNICAST_UP:
1589bd670b35SErik Nordmark 			case IPVL_UNICAST_DOWN:
1590bd670b35SErik Nordmark 				break;
1591bd670b35SErik Nordmark 			default:
1592bd670b35SErik Nordmark 				return (EADDRNOTAVAIL);
1593bd670b35SErik Nordmark 			}
1594bd670b35SErik Nordmark 			ixa->ixa_flags |= IXAF_VERIFY_SOURCE;
1595bd670b35SErik Nordmark 		} else if (!IN6_IS_ADDR_UNSPECIFIED(&pkti->ipi6_addr)) {
1596bd670b35SErik Nordmark 			/* Allow any source */
1597bd670b35SErik Nordmark 			ixa->ixa_flags &= ~IXAF_VERIFY_SOURCE;
1598bd670b35SErik Nordmark 		}
1599bd670b35SErik Nordmark 		isv6 = !(IN6_IS_ADDR_V4MAPPED(&pkti->ipi6_addr));
1600b1b66e09SErik Nordmark 		if (!ip_xmit_ifindex_valid(pkti->ipi6_ifindex, zoneid, isv6,
1601b1b66e09SErik Nordmark 		    ipst))
1602bd670b35SErik Nordmark 			return (ENXIO);
1603bd670b35SErik Nordmark 		break;
1604bd670b35SErik Nordmark 	}
1605bd670b35SErik Nordmark 	case IPV6_HOPLIMIT:
1606bd670b35SErik Nordmark 		/* It is only allowed as ancilary data */
1607bd670b35SErik Nordmark 		if (!coa->coa_ancillary)
1608bd670b35SErik Nordmark 			return (EINVAL);
1609bd670b35SErik Nordmark 
1610bd670b35SErik Nordmark 		if (inlen != 0 && inlen != sizeof (int))
1611bd670b35SErik Nordmark 			return (EINVAL);
1612bd670b35SErik Nordmark 		if (inlen == sizeof (int)) {
1613bd670b35SErik Nordmark 			if (*i1 > 255 || *i1 < -1 || *i1 == 0)
1614bd670b35SErik Nordmark 				return (EINVAL);
1615bd670b35SErik Nordmark 		}
1616bd670b35SErik Nordmark 		break;
1617bd670b35SErik Nordmark 	case IPV6_TCLASS:
1618bd670b35SErik Nordmark 		if (inlen != 0 && inlen != sizeof (int))
1619bd670b35SErik Nordmark 			return (EINVAL);
1620bd670b35SErik Nordmark 		if (inlen == sizeof (int)) {
1621bd670b35SErik Nordmark 			if (*i1 > 255 || *i1 < -1)
1622bd670b35SErik Nordmark 				return (EINVAL);
1623bd670b35SErik Nordmark 		}
1624bd670b35SErik Nordmark 		break;
1625bd670b35SErik Nordmark 	case IPV6_NEXTHOP:
1626bd670b35SErik Nordmark 		if (inlen != 0 && inlen != sizeof (sin6_t))
1627bd670b35SErik Nordmark 			return (EINVAL);
1628bd670b35SErik Nordmark 		if (inlen == sizeof (sin6_t)) {
1629bd670b35SErik Nordmark 			sin6_t *sin6 = (sin6_t *)invalp;
1630bd670b35SErik Nordmark 			ire_t	*ire;
1631bd670b35SErik Nordmark 
1632bd670b35SErik Nordmark 			if (sin6->sin6_family != AF_INET6)
1633bd670b35SErik Nordmark 				return (EAFNOSUPPORT);
1634bd670b35SErik Nordmark 			if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
1635bd670b35SErik Nordmark 				return (EADDRNOTAVAIL);
1636bd670b35SErik Nordmark 
1637bd670b35SErik Nordmark 			/* Verify that the next-hop is on-link */
1638bd670b35SErik Nordmark 			ire = ire_ftable_lookup_v6(&sin6->sin6_addr,
1639bd670b35SErik Nordmark 			    0, 0, IRE_ONLINK, NULL, zoneid,
1640bd670b35SErik Nordmark 			    NULL, MATCH_IRE_TYPE, 0, ipst, NULL);
1641bd670b35SErik Nordmark 			if (ire == NULL)
1642bd670b35SErik Nordmark 				return (EHOSTUNREACH);
1643bd670b35SErik Nordmark 			ire_refrele(ire);
1644bd670b35SErik Nordmark 			break;
1645bd670b35SErik Nordmark 		}
1646bd670b35SErik Nordmark 		break;
1647bd670b35SErik Nordmark 	case IPV6_RTHDR:
1648bd670b35SErik Nordmark 	case IPV6_DSTOPTS:
1649bd670b35SErik Nordmark 	case IPV6_RTHDRDSTOPTS:
1650bd670b35SErik Nordmark 	case IPV6_HOPOPTS: {
1651bd670b35SErik Nordmark 		/* All have the length field in the same place */
1652bd670b35SErik Nordmark 		ip6_hbh_t *hopts = (ip6_hbh_t *)invalp;
1653bd670b35SErik Nordmark 		/*
1654bd670b35SErik Nordmark 		 * Sanity checks - minimum size, size a multiple of
1655bd670b35SErik Nordmark 		 * eight bytes, and matching size passed in.
1656bd670b35SErik Nordmark 		 */
1657bd670b35SErik Nordmark 		if (inlen != 0 &&
1658bd670b35SErik Nordmark 		    inlen != (8 * (hopts->ip6h_len + 1)))
1659bd670b35SErik Nordmark 			return (EINVAL);
1660bd670b35SErik Nordmark 		break;
1661bd670b35SErik Nordmark 	}
1662bd670b35SErik Nordmark 	case IPV6_PATHMTU:
1663bd670b35SErik Nordmark 		/* Can't be set */
1664bd670b35SErik Nordmark 		return (EINVAL);
1665bd670b35SErik Nordmark 
1666bd670b35SErik Nordmark 	case IPV6_USE_MIN_MTU:
1667bd670b35SErik Nordmark 		if (inlen != sizeof (int))
1668bd670b35SErik Nordmark 			return (EINVAL);
1669bd670b35SErik Nordmark 		if (*i1 < -1 || *i1 > 1)
1670bd670b35SErik Nordmark 			return (EINVAL);
1671bd670b35SErik Nordmark 		break;
1672bd670b35SErik Nordmark 	case IPV6_SRC_PREFERENCES:
1673bd670b35SErik Nordmark 		if (inlen != sizeof (uint32_t))
1674bd670b35SErik Nordmark 			return (EINVAL);
1675bd670b35SErik Nordmark 		break;
1676bd670b35SErik Nordmark 	case IPV6_V6ONLY:
1677bd670b35SErik Nordmark 		if (*i1 < 0 || *i1 > 1) {
1678bd670b35SErik Nordmark 			return (EINVAL);
1679bd670b35SErik Nordmark 		}
1680bd670b35SErik Nordmark 		break;
1681bd670b35SErik Nordmark 	}
1682bd670b35SErik Nordmark 	if (checkonly)
1683bd670b35SErik Nordmark 		return (0);
1684bd670b35SErik Nordmark 
1685bd670b35SErik Nordmark 	/* Here we set the actual option value */
1686bd670b35SErik Nordmark 	/*
1687bd670b35SErik Nordmark 	 * conn_lock protects the bitfields, and is used to
1688bd670b35SErik Nordmark 	 * set the fields atomically. Not needed for ixa settings since
1689bd670b35SErik Nordmark 	 * the caller has an exclusive copy of the ixa.
1690bd670b35SErik Nordmark 	 * We can not hold conn_lock across the multicast options though.
1691bd670b35SErik Nordmark 	 */
1692bd670b35SErik Nordmark 	ASSERT(MUTEX_NOT_HELD(&coa->coa_connp->conn_lock));
1693bd670b35SErik Nordmark 	switch (name) {
1694bd670b35SErik Nordmark 	case IPV6_MULTICAST_IF:
1695bd670b35SErik Nordmark 		ixa->ixa_multicast_ifindex = ifindex;
1696bd670b35SErik Nordmark 		/* Need to redo ip_attr_connect */
1697bd670b35SErik Nordmark 		coa->coa_changed |= COA_ROUTE_CHANGED;
1698bd670b35SErik Nordmark 		break;
1699bd670b35SErik Nordmark 	case IPV6_UNICAST_HOPS:
1700bd670b35SErik Nordmark 		/* -1 means use default */
1701bd670b35SErik Nordmark 		mutex_enter(&connp->conn_lock);
1702bd670b35SErik Nordmark 		if (*i1 == -1) {
1703bd670b35SErik Nordmark 			ipp->ipp_unicast_hops = connp->conn_default_ttl;
1704bd670b35SErik Nordmark 		} else {
1705bd670b35SErik Nordmark 			ipp->ipp_unicast_hops = (uint8_t)*i1;
1706bd670b35SErik Nordmark 		}
1707bd670b35SErik Nordmark 		mutex_exit(&connp->conn_lock);
1708bd670b35SErik Nordmark 		coa->coa_changed |= COA_HEADER_CHANGED;
1709bd670b35SErik Nordmark 		break;
1710bd670b35SErik Nordmark 	case IPV6_MULTICAST_HOPS:
1711bd670b35SErik Nordmark 		/* -1 means use default */
1712bd670b35SErik Nordmark 		if (*i1 == -1) {
1713bd670b35SErik Nordmark 			ixa->ixa_multicast_ttl = IP_DEFAULT_MULTICAST_TTL;
1714bd670b35SErik Nordmark 		} else {
1715bd670b35SErik Nordmark 			ixa->ixa_multicast_ttl = (uint8_t)*i1;
1716bd670b35SErik Nordmark 		}
1717bd670b35SErik Nordmark 		/* Handled automatically by ip_output */
1718bd670b35SErik Nordmark 		break;
1719bd670b35SErik Nordmark 	case IPV6_MULTICAST_LOOP:
1720bd670b35SErik Nordmark 		if (*i1 != 0)
1721bd670b35SErik Nordmark 			ixa->ixa_flags |= IXAF_MULTICAST_LOOP;
1722bd670b35SErik Nordmark 		else
1723bd670b35SErik Nordmark 			ixa->ixa_flags &= ~IXAF_MULTICAST_LOOP;
1724bd670b35SErik Nordmark 		/* Handled automatically by ip_output */
1725bd670b35SErik Nordmark 		break;
1726bd670b35SErik Nordmark 	case IPV6_JOIN_GROUP:
1727bd670b35SErik Nordmark 	case IPV6_LEAVE_GROUP:
1728bd670b35SErik Nordmark 	case MCAST_JOIN_GROUP:
1729bd670b35SErik Nordmark 	case MCAST_LEAVE_GROUP:
1730bd670b35SErik Nordmark 		return (ip_opt_set_multicast_group(connp, name,
1731bd670b35SErik Nordmark 		    invalp, B_TRUE, checkonly));
1732bd670b35SErik Nordmark 
1733bd670b35SErik Nordmark 	case MCAST_BLOCK_SOURCE:
1734bd670b35SErik Nordmark 	case MCAST_UNBLOCK_SOURCE:
1735bd670b35SErik Nordmark 	case MCAST_JOIN_SOURCE_GROUP:
1736bd670b35SErik Nordmark 	case MCAST_LEAVE_SOURCE_GROUP:
1737bd670b35SErik Nordmark 		return (ip_opt_set_multicast_sources(connp, name,
1738bd670b35SErik Nordmark 		    invalp, B_TRUE, checkonly));
1739bd670b35SErik Nordmark 
1740bd670b35SErik Nordmark 	case IPV6_BOUND_IF:
1741bd670b35SErik Nordmark 		ixa->ixa_ifindex = ifindex;		/* Send */
1742bd670b35SErik Nordmark 		mutex_enter(&connp->conn_lock);
1743bd670b35SErik Nordmark 		connp->conn_incoming_ifindex = ifindex;	/* Receive */
1744bd670b35SErik Nordmark 		connp->conn_bound_if = ifindex;		/* getsockopt */
1745bd670b35SErik Nordmark 		mutex_exit(&connp->conn_lock);
1746bd670b35SErik Nordmark 		coa->coa_changed |= COA_ROUTE_CHANGED;
1747bd670b35SErik Nordmark 		break;
1748bd670b35SErik Nordmark 	case IPV6_UNSPEC_SRC:
1749bd670b35SErik Nordmark 		mutex_enter(&connp->conn_lock);
1750bd670b35SErik Nordmark 		connp->conn_unspec_src = onoff;
1751bd670b35SErik Nordmark 		if (onoff)
1752bd670b35SErik Nordmark 			ixa->ixa_flags &= ~IXAF_VERIFY_SOURCE;
1753bd670b35SErik Nordmark 		else
1754bd670b35SErik Nordmark 			ixa->ixa_flags |= IXAF_VERIFY_SOURCE;
1755bd670b35SErik Nordmark 		mutex_exit(&connp->conn_lock);
1756bd670b35SErik Nordmark 		break;
1757bd670b35SErik Nordmark 	case IPV6_RECVPKTINFO:
1758bd670b35SErik Nordmark 		mutex_enter(&connp->conn_lock);
1759bd670b35SErik Nordmark 		connp->conn_recv_ancillary.crb_ip_recvpktinfo = onoff;
1760bd670b35SErik Nordmark 		mutex_exit(&connp->conn_lock);
1761bd670b35SErik Nordmark 		break;
1762bd670b35SErik Nordmark 	case IPV6_RECVTCLASS:
1763bd670b35SErik Nordmark 		mutex_enter(&connp->conn_lock);
1764bd670b35SErik Nordmark 		connp->conn_recv_ancillary.crb_ipv6_recvtclass = onoff;
1765bd670b35SErik Nordmark 		mutex_exit(&connp->conn_lock);
1766bd670b35SErik Nordmark 		break;
1767bd670b35SErik Nordmark 	case IPV6_RECVPATHMTU:
1768bd670b35SErik Nordmark 		mutex_enter(&connp->conn_lock);
1769bd670b35SErik Nordmark 		connp->conn_ipv6_recvpathmtu = onoff;
1770bd670b35SErik Nordmark 		mutex_exit(&connp->conn_lock);
1771bd670b35SErik Nordmark 		break;
1772bd670b35SErik Nordmark 	case IPV6_RECVHOPLIMIT:
1773bd670b35SErik Nordmark 		mutex_enter(&connp->conn_lock);
1774bd670b35SErik Nordmark 		connp->conn_recv_ancillary.crb_ipv6_recvhoplimit =
1775bd670b35SErik Nordmark 		    onoff;
1776bd670b35SErik Nordmark 		mutex_exit(&connp->conn_lock);
1777bd670b35SErik Nordmark 		break;
1778bd670b35SErik Nordmark 	case IPV6_RECVHOPOPTS:
1779bd670b35SErik Nordmark 		mutex_enter(&connp->conn_lock);
1780bd670b35SErik Nordmark 		connp->conn_recv_ancillary.crb_ipv6_recvhopopts = onoff;
1781bd670b35SErik Nordmark 		mutex_exit(&connp->conn_lock);
1782bd670b35SErik Nordmark 		break;
1783bd670b35SErik Nordmark 	case IPV6_RECVDSTOPTS:
1784bd670b35SErik Nordmark 		mutex_enter(&connp->conn_lock);
1785bd670b35SErik Nordmark 		connp->conn_recv_ancillary.crb_ipv6_recvdstopts = onoff;
1786bd670b35SErik Nordmark 		mutex_exit(&connp->conn_lock);
1787bd670b35SErik Nordmark 		break;
1788bd670b35SErik Nordmark 	case _OLD_IPV6_RECVDSTOPTS:
1789bd670b35SErik Nordmark 		mutex_enter(&connp->conn_lock);
1790bd670b35SErik Nordmark 		connp->conn_recv_ancillary.crb_old_ipv6_recvdstopts =
1791bd670b35SErik Nordmark 		    onoff;
1792bd670b35SErik Nordmark 		mutex_exit(&connp->conn_lock);
1793bd670b35SErik Nordmark 		break;
1794bd670b35SErik Nordmark 	case IPV6_RECVRTHDRDSTOPTS:
1795bd670b35SErik Nordmark 		mutex_enter(&connp->conn_lock);
1796bd670b35SErik Nordmark 		connp->conn_recv_ancillary.crb_ipv6_recvrthdrdstopts =
1797bd670b35SErik Nordmark 		    onoff;
1798bd670b35SErik Nordmark 		mutex_exit(&connp->conn_lock);
1799bd670b35SErik Nordmark 		break;
1800bd670b35SErik Nordmark 	case IPV6_RECVRTHDR:
1801bd670b35SErik Nordmark 		mutex_enter(&connp->conn_lock);
1802bd670b35SErik Nordmark 		connp->conn_recv_ancillary.crb_ipv6_recvrthdr = onoff;
1803bd670b35SErik Nordmark 		mutex_exit(&connp->conn_lock);
1804bd670b35SErik Nordmark 		break;
1805bd670b35SErik Nordmark 	case IPV6_PKTINFO:
1806bd670b35SErik Nordmark 		mutex_enter(&connp->conn_lock);
1807bd670b35SErik Nordmark 		if (inlen == 0) {
1808bd670b35SErik Nordmark 			ipp->ipp_fields &= ~IPPF_ADDR;
1809bd670b35SErik Nordmark 			ipp->ipp_addr = ipv6_all_zeros;
1810bd670b35SErik Nordmark 			ixa->ixa_ifindex = 0;
1811bd670b35SErik Nordmark 		} else {
1812bd670b35SErik Nordmark 			struct in6_pktinfo *pkti;
1813bd670b35SErik Nordmark 
1814bd670b35SErik Nordmark 			pkti = (struct in6_pktinfo *)invalp;
1815bd670b35SErik Nordmark 			ipp->ipp_addr = pkti->ipi6_addr;
1816bd670b35SErik Nordmark 			if (!IN6_IS_ADDR_UNSPECIFIED(&ipp->ipp_addr))
1817bd670b35SErik Nordmark 				ipp->ipp_fields |= IPPF_ADDR;
1818bd670b35SErik Nordmark 			else
1819bd670b35SErik Nordmark 				ipp->ipp_fields &= ~IPPF_ADDR;
1820bd670b35SErik Nordmark 			ixa->ixa_ifindex = pkti->ipi6_ifindex;
1821bd670b35SErik Nordmark 		}
1822bd670b35SErik Nordmark 		mutex_exit(&connp->conn_lock);
1823bd670b35SErik Nordmark 		/* Source and ifindex might have changed */
1824bd670b35SErik Nordmark 		coa->coa_changed |= COA_HEADER_CHANGED;
1825bd670b35SErik Nordmark 		coa->coa_changed |= COA_ROUTE_CHANGED;
1826bd670b35SErik Nordmark 		break;
1827bd670b35SErik Nordmark 	case IPV6_HOPLIMIT:
1828bd670b35SErik Nordmark 		mutex_enter(&connp->conn_lock);
1829bd670b35SErik Nordmark 		if (inlen == 0 || *i1 == -1) {
1830bd670b35SErik Nordmark 			/* Revert to default */
1831bd670b35SErik Nordmark 			ipp->ipp_fields &= ~IPPF_HOPLIMIT;
1832bd670b35SErik Nordmark 			ixa->ixa_flags &= ~IXAF_NO_TTL_CHANGE;
1833bd670b35SErik Nordmark 		} else {
1834bd670b35SErik Nordmark 			ipp->ipp_hoplimit = *i1;
1835bd670b35SErik Nordmark 			ipp->ipp_fields |= IPPF_HOPLIMIT;
1836bd670b35SErik Nordmark 			/* Ensure that it sticks for multicast packets */
1837bd670b35SErik Nordmark 			ixa->ixa_flags |= IXAF_NO_TTL_CHANGE;
1838bd670b35SErik Nordmark 		}
1839bd670b35SErik Nordmark 		mutex_exit(&connp->conn_lock);
1840bd670b35SErik Nordmark 		coa->coa_changed |= COA_HEADER_CHANGED;
1841bd670b35SErik Nordmark 		break;
1842bd670b35SErik Nordmark 	case IPV6_TCLASS:
1843bd670b35SErik Nordmark 		/*
1844bd670b35SErik Nordmark 		 * IPV6_TCLASS accepts -1 as use kernel default
1845bd670b35SErik Nordmark 		 * and [0, 255] as the actualy traffic class.
1846bd670b35SErik Nordmark 		 */
1847bd670b35SErik Nordmark 		mutex_enter(&connp->conn_lock);
1848bd670b35SErik Nordmark 		if (inlen == 0 || *i1 == -1) {
1849bd670b35SErik Nordmark 			ipp->ipp_tclass = 0;
1850bd670b35SErik Nordmark 			ipp->ipp_fields &= ~IPPF_TCLASS;
1851bd670b35SErik Nordmark 		} else {
1852bd670b35SErik Nordmark 			ipp->ipp_tclass = *i1;
1853bd670b35SErik Nordmark 			ipp->ipp_fields |= IPPF_TCLASS;
1854bd670b35SErik Nordmark 		}
1855bd670b35SErik Nordmark 		mutex_exit(&connp->conn_lock);
1856bd670b35SErik Nordmark 		coa->coa_changed |= COA_HEADER_CHANGED;
1857bd670b35SErik Nordmark 		break;
1858bd670b35SErik Nordmark 	case IPV6_NEXTHOP:
1859bd670b35SErik Nordmark 		if (inlen == 0) {
1860bd670b35SErik Nordmark 			ixa->ixa_flags &= ~IXAF_NEXTHOP_SET;
1861bd670b35SErik Nordmark 		} else {
1862bd670b35SErik Nordmark 			sin6_t *sin6 = (sin6_t *)invalp;
1863bd670b35SErik Nordmark 
1864bd670b35SErik Nordmark 			ixa->ixa_nexthop_v6 = sin6->sin6_addr;
1865bd670b35SErik Nordmark 			if (!IN6_IS_ADDR_UNSPECIFIED(&ixa->ixa_nexthop_v6))
1866bd670b35SErik Nordmark 				ixa->ixa_flags |= IXAF_NEXTHOP_SET;
1867bd670b35SErik Nordmark 			else
1868bd670b35SErik Nordmark 				ixa->ixa_flags &= ~IXAF_NEXTHOP_SET;
1869bd670b35SErik Nordmark 		}
1870bd670b35SErik Nordmark 		coa->coa_changed |= COA_ROUTE_CHANGED;
1871bd670b35SErik Nordmark 		break;
1872bd670b35SErik Nordmark 	case IPV6_HOPOPTS:
1873bd670b35SErik Nordmark 		mutex_enter(&connp->conn_lock);
1874bd670b35SErik Nordmark 		error = optcom_pkt_set(invalp, inlen,
1875bd670b35SErik Nordmark 		    (uchar_t **)&ipp->ipp_hopopts, &ipp->ipp_hopoptslen);
1876bd670b35SErik Nordmark 		if (error != 0) {
1877bd670b35SErik Nordmark 			mutex_exit(&connp->conn_lock);
1878bd670b35SErik Nordmark 			return (error);
1879bd670b35SErik Nordmark 		}
1880bd670b35SErik Nordmark 		if (ipp->ipp_hopoptslen == 0) {
1881bd670b35SErik Nordmark 			ipp->ipp_fields &= ~IPPF_HOPOPTS;
1882bd670b35SErik Nordmark 		} else {
1883bd670b35SErik Nordmark 			ipp->ipp_fields |= IPPF_HOPOPTS;
1884bd670b35SErik Nordmark 		}
1885bd670b35SErik Nordmark 		mutex_exit(&connp->conn_lock);
1886bd670b35SErik Nordmark 		coa->coa_changed |= COA_HEADER_CHANGED;
1887bd670b35SErik Nordmark 		coa->coa_changed |= COA_WROFF_CHANGED;
1888bd670b35SErik Nordmark 		break;
1889bd670b35SErik Nordmark 	case IPV6_RTHDRDSTOPTS:
1890bd670b35SErik Nordmark 		mutex_enter(&connp->conn_lock);
1891bd670b35SErik Nordmark 		error = optcom_pkt_set(invalp, inlen,
1892bd670b35SErik Nordmark 		    (uchar_t **)&ipp->ipp_rthdrdstopts,
1893bd670b35SErik Nordmark 		    &ipp->ipp_rthdrdstoptslen);
1894bd670b35SErik Nordmark 		if (error != 0) {
1895bd670b35SErik Nordmark 			mutex_exit(&connp->conn_lock);
1896bd670b35SErik Nordmark 			return (error);
1897bd670b35SErik Nordmark 		}
1898bd670b35SErik Nordmark 		if (ipp->ipp_rthdrdstoptslen == 0) {
1899bd670b35SErik Nordmark 			ipp->ipp_fields &= ~IPPF_RTHDRDSTOPTS;
1900bd670b35SErik Nordmark 		} else {
1901bd670b35SErik Nordmark 			ipp->ipp_fields |= IPPF_RTHDRDSTOPTS;
1902bd670b35SErik Nordmark 		}
1903bd670b35SErik Nordmark 		mutex_exit(&connp->conn_lock);
1904bd670b35SErik Nordmark 		coa->coa_changed |= COA_HEADER_CHANGED;
1905bd670b35SErik Nordmark 		coa->coa_changed |= COA_WROFF_CHANGED;
1906bd670b35SErik Nordmark 		break;
1907bd670b35SErik Nordmark 	case IPV6_DSTOPTS:
1908bd670b35SErik Nordmark 		mutex_enter(&connp->conn_lock);
1909bd670b35SErik Nordmark 		error = optcom_pkt_set(invalp, inlen,
1910bd670b35SErik Nordmark 		    (uchar_t **)&ipp->ipp_dstopts, &ipp->ipp_dstoptslen);
1911bd670b35SErik Nordmark 		if (error != 0) {
1912bd670b35SErik Nordmark 			mutex_exit(&connp->conn_lock);
1913bd670b35SErik Nordmark 			return (error);
1914bd670b35SErik Nordmark 		}
1915bd670b35SErik Nordmark 		if (ipp->ipp_dstoptslen == 0) {
1916bd670b35SErik Nordmark 			ipp->ipp_fields &= ~IPPF_DSTOPTS;
1917bd670b35SErik Nordmark 		} else {
1918bd670b35SErik Nordmark 			ipp->ipp_fields |= IPPF_DSTOPTS;
1919bd670b35SErik Nordmark 		}
1920bd670b35SErik Nordmark 		mutex_exit(&connp->conn_lock);
1921bd670b35SErik Nordmark 		coa->coa_changed |= COA_HEADER_CHANGED;
1922bd670b35SErik Nordmark 		coa->coa_changed |= COA_WROFF_CHANGED;
1923bd670b35SErik Nordmark 		break;
1924bd670b35SErik Nordmark 	case IPV6_RTHDR:
1925bd670b35SErik Nordmark 		mutex_enter(&connp->conn_lock);
1926bd670b35SErik Nordmark 		error = optcom_pkt_set(invalp, inlen,
1927bd670b35SErik Nordmark 		    (uchar_t **)&ipp->ipp_rthdr, &ipp->ipp_rthdrlen);
1928bd670b35SErik Nordmark 		if (error != 0) {
1929bd670b35SErik Nordmark 			mutex_exit(&connp->conn_lock);
1930bd670b35SErik Nordmark 			return (error);
1931bd670b35SErik Nordmark 		}
1932bd670b35SErik Nordmark 		if (ipp->ipp_rthdrlen == 0) {
1933bd670b35SErik Nordmark 			ipp->ipp_fields &= ~IPPF_RTHDR;
1934bd670b35SErik Nordmark 		} else {
1935bd670b35SErik Nordmark 			ipp->ipp_fields |= IPPF_RTHDR;
1936bd670b35SErik Nordmark 		}
1937bd670b35SErik Nordmark 		mutex_exit(&connp->conn_lock);
1938bd670b35SErik Nordmark 		coa->coa_changed |= COA_HEADER_CHANGED;
1939bd670b35SErik Nordmark 		coa->coa_changed |= COA_WROFF_CHANGED;
1940bd670b35SErik Nordmark 		break;
1941bd670b35SErik Nordmark 
1942bd670b35SErik Nordmark 	case IPV6_DONTFRAG:
1943bd670b35SErik Nordmark 		if (onoff) {
1944bd670b35SErik Nordmark 			ixa->ixa_flags |= IXAF_DONTFRAG;
1945bd670b35SErik Nordmark 			ixa->ixa_flags &= ~IXAF_PMTU_DISCOVERY;
1946bd670b35SErik Nordmark 		} else {
1947bd670b35SErik Nordmark 			ixa->ixa_flags &= ~IXAF_DONTFRAG;
1948bd670b35SErik Nordmark 			ixa->ixa_flags |= IXAF_PMTU_DISCOVERY;
1949bd670b35SErik Nordmark 		}
1950bd670b35SErik Nordmark 		/* Need to redo ip_attr_connect */
1951bd670b35SErik Nordmark 		coa->coa_changed |= COA_ROUTE_CHANGED;
1952bd670b35SErik Nordmark 		break;
1953bd670b35SErik Nordmark 
1954bd670b35SErik Nordmark 	case IPV6_USE_MIN_MTU:
1955bd670b35SErik Nordmark 		ixa->ixa_flags |= IXAF_USE_MIN_MTU;
1956bd670b35SErik Nordmark 		ixa->ixa_use_min_mtu = *i1;
1957bd670b35SErik Nordmark 		/* Need to redo ip_attr_connect */
1958bd670b35SErik Nordmark 		coa->coa_changed |= COA_ROUTE_CHANGED;
1959bd670b35SErik Nordmark 		break;
1960bd670b35SErik Nordmark 
1961bd670b35SErik Nordmark 	case IPV6_SEC_OPT:
1962bd670b35SErik Nordmark 		mutex_enter(&connp->conn_lock);
1963bd670b35SErik Nordmark 		error = ipsec_set_req(cr, connp, (ipsec_req_t *)invalp);
1964bd670b35SErik Nordmark 		mutex_exit(&connp->conn_lock);
1965bd670b35SErik Nordmark 		if (error != 0) {
1966bd670b35SErik Nordmark 			return (error);
1967bd670b35SErik Nordmark 		}
1968bd670b35SErik Nordmark 		/* This is an IPsec policy change - redo ip_attr_connect */
1969bd670b35SErik Nordmark 		coa->coa_changed |= COA_ROUTE_CHANGED;
1970bd670b35SErik Nordmark 		break;
1971bd670b35SErik Nordmark 	case IPV6_SRC_PREFERENCES:
1972bd670b35SErik Nordmark 		/*
1973bd670b35SErik Nordmark 		 * This socket option only affects connected
1974bd670b35SErik Nordmark 		 * sockets that haven't already bound to a specific
1975bd670b35SErik Nordmark 		 * IPv6 address.  In other words, sockets that
1976bd670b35SErik Nordmark 		 * don't call bind() with an address other than the
1977bd670b35SErik Nordmark 		 * unspecified address and that call connect().
1978bd670b35SErik Nordmark 		 * ip_set_destination_v6() passes these preferences
1979bd670b35SErik Nordmark 		 * to the ipif_select_source_v6() function.
1980bd670b35SErik Nordmark 		 */
1981bd670b35SErik Nordmark 		mutex_enter(&connp->conn_lock);
1982bd670b35SErik Nordmark 		error = ip6_set_src_preferences(ixa, *(uint32_t *)invalp);
1983bd670b35SErik Nordmark 		mutex_exit(&connp->conn_lock);
1984bd670b35SErik Nordmark 		if (error != 0) {
1985bd670b35SErik Nordmark 			return (error);
1986bd670b35SErik Nordmark 		}
1987bd670b35SErik Nordmark 		break;
1988bd670b35SErik Nordmark 	case IPV6_V6ONLY:
1989bd670b35SErik Nordmark 		mutex_enter(&connp->conn_lock);
1990bd670b35SErik Nordmark 		connp->conn_ipv6_v6only = onoff;
1991bd670b35SErik Nordmark 		mutex_exit(&connp->conn_lock);
1992bd670b35SErik Nordmark 		break;
1993bd670b35SErik Nordmark 	}
1994bd670b35SErik Nordmark 	return (0);
1995bd670b35SErik Nordmark }
1996bd670b35SErik Nordmark 
1997bd670b35SErik Nordmark /* Handle IPPROTO_UDP */
1998bd670b35SErik Nordmark /* ARGSUSED1 */
1999bd670b35SErik Nordmark static int
conn_opt_set_udp(conn_opt_arg_t * coa,t_scalar_t name,uint_t inlen,uchar_t * invalp,boolean_t checkonly,cred_t * cr)2000bd670b35SErik Nordmark conn_opt_set_udp(conn_opt_arg_t *coa, t_scalar_t name, uint_t inlen,
2001bd670b35SErik Nordmark     uchar_t *invalp, boolean_t checkonly, cred_t *cr)
2002bd670b35SErik Nordmark {
2003bd670b35SErik Nordmark 	conn_t		*connp = coa->coa_connp;
2004bd670b35SErik Nordmark 	int		*i1 = (int *)invalp;
2005bd670b35SErik Nordmark 	boolean_t	onoff = (*i1 == 0) ? 0 : 1;
2006bd670b35SErik Nordmark 	int		error;
2007bd670b35SErik Nordmark 
2008bd670b35SErik Nordmark 	switch (name) {
2009bd670b35SErik Nordmark 	case UDP_ANONPRIVBIND:
2010bd670b35SErik Nordmark 		if ((error = secpolicy_net_privaddr(cr, 0, IPPROTO_UDP)) != 0) {
2011bd670b35SErik Nordmark 			return (error);
2012bd670b35SErik Nordmark 		}
2013bd670b35SErik Nordmark 		break;
2014bd670b35SErik Nordmark 	}
2015bd670b35SErik Nordmark 	if (checkonly)
2016bd670b35SErik Nordmark 		return (0);
2017bd670b35SErik Nordmark 
2018bd670b35SErik Nordmark 	/* Here we set the actual option value */
2019bd670b35SErik Nordmark 	mutex_enter(&connp->conn_lock);
2020bd670b35SErik Nordmark 	switch (name) {
2021bd670b35SErik Nordmark 	case UDP_ANONPRIVBIND:
2022bd670b35SErik Nordmark 		connp->conn_anon_priv_bind = onoff;
2023bd670b35SErik Nordmark 		break;
2024bd670b35SErik Nordmark 	case UDP_EXCLBIND:
2025bd670b35SErik Nordmark 		connp->conn_exclbind = onoff;
2026bd670b35SErik Nordmark 		break;
2027bd670b35SErik Nordmark 	}
2028bd670b35SErik Nordmark 	mutex_exit(&connp->conn_lock);
2029bd670b35SErik Nordmark 	return (0);
2030bd670b35SErik Nordmark }
2031bd670b35SErik Nordmark 
2032bd670b35SErik Nordmark /* Handle IPPROTO_TCP */
2033bd670b35SErik Nordmark /* ARGSUSED1 */
2034bd670b35SErik Nordmark static int
conn_opt_set_tcp(conn_opt_arg_t * coa,t_scalar_t name,uint_t inlen,uchar_t * invalp,boolean_t checkonly,cred_t * cr)2035bd670b35SErik Nordmark conn_opt_set_tcp(conn_opt_arg_t *coa, t_scalar_t name, uint_t inlen,
2036bd670b35SErik Nordmark     uchar_t *invalp, boolean_t checkonly, cred_t *cr)
2037bd670b35SErik Nordmark {
2038bd670b35SErik Nordmark 	conn_t		*connp = coa->coa_connp;
2039bd670b35SErik Nordmark 	int		*i1 = (int *)invalp;
2040bd670b35SErik Nordmark 	boolean_t	onoff = (*i1 == 0) ? 0 : 1;
2041bd670b35SErik Nordmark 	int		error;
2042bd670b35SErik Nordmark 
2043bd670b35SErik Nordmark 	switch (name) {
2044bd670b35SErik Nordmark 	case TCP_ANONPRIVBIND:
2045bd670b35SErik Nordmark 		if ((error = secpolicy_net_privaddr(cr, 0, IPPROTO_TCP)) != 0) {
2046bd670b35SErik Nordmark 			return (error);
2047bd670b35SErik Nordmark 		}
2048bd670b35SErik Nordmark 		break;
2049bd670b35SErik Nordmark 	}
2050bd670b35SErik Nordmark 	if (checkonly)
2051bd670b35SErik Nordmark 		return (0);
2052bd670b35SErik Nordmark 
2053bd670b35SErik Nordmark 	/* Here we set the actual option value */
2054bd670b35SErik Nordmark 	mutex_enter(&connp->conn_lock);
2055bd670b35SErik Nordmark 	switch (name) {
2056bd670b35SErik Nordmark 	case TCP_ANONPRIVBIND:
2057bd670b35SErik Nordmark 		connp->conn_anon_priv_bind = onoff;
2058bd670b35SErik Nordmark 		break;
2059bd670b35SErik Nordmark 	case TCP_EXCLBIND:
2060bd670b35SErik Nordmark 		connp->conn_exclbind = onoff;
2061bd670b35SErik Nordmark 		break;
2062bd670b35SErik Nordmark 	case TCP_RECVDSTADDR:
2063bd670b35SErik Nordmark 		connp->conn_recv_ancillary.crb_recvdstaddr = onoff;
2064bd670b35SErik Nordmark 		break;
2065bd670b35SErik Nordmark 	}
2066bd670b35SErik Nordmark 	mutex_exit(&connp->conn_lock);
2067bd670b35SErik Nordmark 	return (0);
2068bd670b35SErik Nordmark }
2069bd670b35SErik Nordmark 
2070bd670b35SErik Nordmark int
conn_getsockname(conn_t * connp,struct sockaddr * sa,uint_t * salenp)2071bd670b35SErik Nordmark conn_getsockname(conn_t *connp, struct sockaddr *sa, uint_t *salenp)
2072bd670b35SErik Nordmark {
2073bd670b35SErik Nordmark 	sin_t		*sin;
2074bd670b35SErik Nordmark 	sin6_t		*sin6;
2075bd670b35SErik Nordmark 
2076bd670b35SErik Nordmark 	if (connp->conn_family == AF_INET) {
2077bd670b35SErik Nordmark 		if (*salenp < sizeof (sin_t))
2078bd670b35SErik Nordmark 			return (EINVAL);
2079bd670b35SErik Nordmark 
2080bd670b35SErik Nordmark 		*salenp = sizeof (sin_t);
2081bd670b35SErik Nordmark 		/* Fill zeroes and then initialize non-zero fields */
2082bd670b35SErik Nordmark 		sin = (sin_t *)sa;
2083bd670b35SErik Nordmark 		*sin = sin_null;
2084bd670b35SErik Nordmark 		sin->sin_family = AF_INET;
2085bd670b35SErik Nordmark 		if (!IN6_IS_ADDR_V4MAPPED_ANY(&connp->conn_saddr_v6) &&
2086bd670b35SErik Nordmark 		    !IN6_IS_ADDR_UNSPECIFIED(&connp->conn_saddr_v6)) {
2087bd670b35SErik Nordmark 			sin->sin_addr.s_addr = connp->conn_saddr_v4;
2088bd670b35SErik Nordmark 		} else {
2089bd670b35SErik Nordmark 			/*
2090bd670b35SErik Nordmark 			 * INADDR_ANY
2091bd670b35SErik Nordmark 			 * conn_saddr is not set, we might be bound to
2092bd670b35SErik Nordmark 			 * broadcast/multicast. Use conn_bound_addr as
2093bd670b35SErik Nordmark 			 * local address instead (that could
2094bd670b35SErik Nordmark 			 * also still be INADDR_ANY)
2095bd670b35SErik Nordmark 			 */
2096bd670b35SErik Nordmark 			sin->sin_addr.s_addr = connp->conn_bound_addr_v4;
2097bd670b35SErik Nordmark 		}
2098bd670b35SErik Nordmark 		sin->sin_port = connp->conn_lport;
2099bd670b35SErik Nordmark 	} else {
2100bd670b35SErik Nordmark 		if (*salenp < sizeof (sin6_t))
2101bd670b35SErik Nordmark 			return (EINVAL);
2102bd670b35SErik Nordmark 
2103bd670b35SErik Nordmark 		*salenp = sizeof (sin6_t);
2104bd670b35SErik Nordmark 		/* Fill zeroes and then initialize non-zero fields */
2105bd670b35SErik Nordmark 		sin6 = (sin6_t *)sa;
2106bd670b35SErik Nordmark 		*sin6 = sin6_null;
2107bd670b35SErik Nordmark 		sin6->sin6_family = AF_INET6;
2108bd670b35SErik Nordmark 		if (!IN6_IS_ADDR_UNSPECIFIED(&connp->conn_saddr_v6)) {
2109bd670b35SErik Nordmark 			sin6->sin6_addr = connp->conn_saddr_v6;
2110bd670b35SErik Nordmark 		} else {
2111bd670b35SErik Nordmark 			/*
2112bd670b35SErik Nordmark 			 * conn_saddr is not set, we might be bound to
2113bd670b35SErik Nordmark 			 * broadcast/multicast. Use conn_bound_addr as
2114bd670b35SErik Nordmark 			 * local address instead (which could
2115bd670b35SErik Nordmark 			 * also still be unspecified)
2116bd670b35SErik Nordmark 			 */
2117bd670b35SErik Nordmark 			sin6->sin6_addr = connp->conn_bound_addr_v6;
2118bd670b35SErik Nordmark 		}
2119bd670b35SErik Nordmark 		sin6->sin6_port = connp->conn_lport;
2120bd670b35SErik Nordmark 		if (IN6_IS_ADDR_LINKSCOPE(&sin6->sin6_addr) &&
2121bd670b35SErik Nordmark 		    (connp->conn_ixa->ixa_flags & IXAF_SCOPEID_SET))
2122bd670b35SErik Nordmark 			sin6->sin6_scope_id = connp->conn_ixa->ixa_scopeid;
2123bd670b35SErik Nordmark 	}
2124bd670b35SErik Nordmark 	return (0);
2125bd670b35SErik Nordmark }
2126bd670b35SErik Nordmark 
2127bd670b35SErik Nordmark int
conn_getpeername(conn_t * connp,struct sockaddr * sa,uint_t * salenp)2128bd670b35SErik Nordmark conn_getpeername(conn_t *connp, struct sockaddr *sa, uint_t *salenp)
2129bd670b35SErik Nordmark {
2130bd670b35SErik Nordmark 	struct sockaddr_in	*sin;
2131bd670b35SErik Nordmark 	struct sockaddr_in6	*sin6;
2132bd670b35SErik Nordmark 
2133bd670b35SErik Nordmark 	if (connp->conn_family == AF_INET) {
2134bd670b35SErik Nordmark 		if (*salenp < sizeof (sin_t))
2135bd670b35SErik Nordmark 			return (EINVAL);
2136bd670b35SErik Nordmark 
2137bd670b35SErik Nordmark 		*salenp = sizeof (sin_t);
2138bd670b35SErik Nordmark 		/* initialize */
2139bd670b35SErik Nordmark 		sin = (sin_t *)sa;
2140bd670b35SErik Nordmark 		*sin = sin_null;
2141bd670b35SErik Nordmark 		sin->sin_family = AF_INET;
2142bd670b35SErik Nordmark 		sin->sin_addr.s_addr = connp->conn_faddr_v4;
2143bd670b35SErik Nordmark 		sin->sin_port = connp->conn_fport;
2144bd670b35SErik Nordmark 	} else {
2145bd670b35SErik Nordmark 		if (*salenp < sizeof (sin6_t))
2146bd670b35SErik Nordmark 			return (EINVAL);
2147bd670b35SErik Nordmark 
2148bd670b35SErik Nordmark 		*salenp = sizeof (sin6_t);
2149bd670b35SErik Nordmark 		/* initialize */
2150bd670b35SErik Nordmark 		sin6 = (sin6_t *)sa;
2151bd670b35SErik Nordmark 		*sin6 = sin6_null;
2152bd670b35SErik Nordmark 		sin6->sin6_family = AF_INET6;
2153bd670b35SErik Nordmark 		sin6->sin6_addr = connp->conn_faddr_v6;
2154bd670b35SErik Nordmark 		sin6->sin6_port =  connp->conn_fport;
2155bd670b35SErik Nordmark 		sin6->sin6_flowinfo = connp->conn_flowinfo;
2156bd670b35SErik Nordmark 		if (IN6_IS_ADDR_LINKSCOPE(&sin6->sin6_addr) &&
2157bd670b35SErik Nordmark 		    (connp->conn_ixa->ixa_flags & IXAF_SCOPEID_SET))
2158bd670b35SErik Nordmark 			sin6->sin6_scope_id = connp->conn_ixa->ixa_scopeid;
2159bd670b35SErik Nordmark 	}
2160bd670b35SErik Nordmark 	return (0);
2161bd670b35SErik Nordmark }
2162bd670b35SErik Nordmark 
2163bd670b35SErik Nordmark static uint32_t	cksum_massage_options_v4(ipha_t *, netstack_t *);
2164bd670b35SErik Nordmark static uint32_t cksum_massage_options_v6(ip6_t *, uint_t, netstack_t *);
2165bd670b35SErik Nordmark 
2166bd670b35SErik Nordmark /*
2167bd670b35SErik Nordmark  * Allocate and fill in conn_ht_iphc based on the current information
2168bd670b35SErik Nordmark  * in the conn.
2169bd670b35SErik Nordmark  * Normally used when we bind() and connect().
2170bd670b35SErik Nordmark  * Returns failure if can't allocate memory, or if there is a problem
2171bd670b35SErik Nordmark  * with a routing header/option.
2172bd670b35SErik Nordmark  *
2173bd670b35SErik Nordmark  * We allocate space for the transport header (ulp_hdr_len + extra) and
2174bd670b35SErik Nordmark  * indicate the offset of the ulp header by setting ixa_ip_hdr_length.
2175bd670b35SErik Nordmark  * The extra is there for transports that want some spare room for future
2176bd670b35SErik Nordmark  * options. conn_ht_iphc_allocated is what was allocated; conn_ht_iphc_len
2177bd670b35SErik Nordmark  * excludes the extra part.
2178bd670b35SErik Nordmark  *
2179bd670b35SErik Nordmark  * We massage an routing option/header and store the ckecksum difference
2180bd670b35SErik Nordmark  * in conn_sum.
2181bd670b35SErik Nordmark  *
2182bd670b35SErik Nordmark  * Caller needs to update conn_wroff if desired.
2183bd670b35SErik Nordmark  */
2184bd670b35SErik Nordmark int
conn_build_hdr_template(conn_t * connp,uint_t ulp_hdr_length,uint_t extra,const in6_addr_t * v6src,const in6_addr_t * v6dst,uint32_t flowinfo)2185bd670b35SErik Nordmark conn_build_hdr_template(conn_t *connp, uint_t ulp_hdr_length, uint_t extra,
2186bd670b35SErik Nordmark     const in6_addr_t *v6src, const in6_addr_t *v6dst, uint32_t flowinfo)
2187bd670b35SErik Nordmark {
2188bd670b35SErik Nordmark 	ip_xmit_attr_t	*ixa = connp->conn_ixa;
2189bd670b35SErik Nordmark 	ip_pkt_t	*ipp = &connp->conn_xmit_ipp;
2190bd670b35SErik Nordmark 	uint_t		ip_hdr_length;
2191bd670b35SErik Nordmark 	uchar_t		*hdrs;
2192bd670b35SErik Nordmark 	uint_t		hdrs_len;
2193bd670b35SErik Nordmark 
2194bd670b35SErik Nordmark 	ASSERT(MUTEX_HELD(&connp->conn_lock));
2195bd670b35SErik Nordmark 
2196bd670b35SErik Nordmark 	if (ixa->ixa_flags & IXAF_IS_IPV4) {
2197bd670b35SErik Nordmark 		ip_hdr_length = ip_total_hdrs_len_v4(ipp);
2198bd670b35SErik Nordmark 		/* In case of TX label and IP options it can be too much */
2199bd670b35SErik Nordmark 		if (ip_hdr_length > IP_MAX_HDR_LENGTH) {
2200bd670b35SErik Nordmark 			/* Preserves existing TX errno for this */
2201bd670b35SErik Nordmark 			return (EHOSTUNREACH);
2202bd670b35SErik Nordmark 		}
2203bd670b35SErik Nordmark 	} else {
2204bd670b35SErik Nordmark 		ip_hdr_length = ip_total_hdrs_len_v6(ipp);
2205bd670b35SErik Nordmark 	}
2206bd670b35SErik Nordmark 	ixa->ixa_ip_hdr_length = ip_hdr_length;
2207bd670b35SErik Nordmark 	hdrs_len = ip_hdr_length + ulp_hdr_length + extra;
2208bd670b35SErik Nordmark 	ASSERT(hdrs_len != 0);
2209bd670b35SErik Nordmark 
2210bd670b35SErik Nordmark 	if (hdrs_len != connp->conn_ht_iphc_allocated) {
2211bd670b35SErik Nordmark 		/* Allocate new before we free any old */
2212bd670b35SErik Nordmark 		hdrs = kmem_alloc(hdrs_len, KM_NOSLEEP);
2213bd670b35SErik Nordmark 		if (hdrs == NULL)
2214bd670b35SErik Nordmark 			return (ENOMEM);
2215bd670b35SErik Nordmark 
2216bd670b35SErik Nordmark 		if (connp->conn_ht_iphc != NULL) {
2217bd670b35SErik Nordmark 			kmem_free(connp->conn_ht_iphc,
2218bd670b35SErik Nordmark 			    connp->conn_ht_iphc_allocated);
2219bd670b35SErik Nordmark 		}
2220bd670b35SErik Nordmark 		connp->conn_ht_iphc = hdrs;
2221bd670b35SErik Nordmark 		connp->conn_ht_iphc_allocated = hdrs_len;
2222bd670b35SErik Nordmark 	} else {
2223bd670b35SErik Nordmark 		hdrs = connp->conn_ht_iphc;
2224bd670b35SErik Nordmark 	}
2225bd670b35SErik Nordmark 	hdrs_len -= extra;
2226bd670b35SErik Nordmark 	connp->conn_ht_iphc_len = hdrs_len;
2227bd670b35SErik Nordmark 
2228bd670b35SErik Nordmark 	connp->conn_ht_ulp = hdrs + ip_hdr_length;
2229bd670b35SErik Nordmark 	connp->conn_ht_ulp_len = ulp_hdr_length;
2230bd670b35SErik Nordmark 
2231bd670b35SErik Nordmark 	if (ixa->ixa_flags & IXAF_IS_IPV4) {
2232bd670b35SErik Nordmark 		ipha_t	*ipha = (ipha_t *)hdrs;
2233bd670b35SErik Nordmark 
2234bd670b35SErik Nordmark 		IN6_V4MAPPED_TO_IPADDR(v6src, ipha->ipha_src);
2235bd670b35SErik Nordmark 		IN6_V4MAPPED_TO_IPADDR(v6dst, ipha->ipha_dst);
2236bd670b35SErik Nordmark 		ip_build_hdrs_v4(hdrs, ip_hdr_length, ipp, connp->conn_proto);
2237bd670b35SErik Nordmark 		ipha->ipha_length = htons(hdrs_len);
2238bd670b35SErik Nordmark 		if (ixa->ixa_flags & IXAF_PMTU_IPV4_DF)
2239bd670b35SErik Nordmark 			ipha->ipha_fragment_offset_and_flags |= IPH_DF_HTONS;
2240bd670b35SErik Nordmark 		else
2241bd670b35SErik Nordmark 			ipha->ipha_fragment_offset_and_flags &= ~IPH_DF_HTONS;
2242bd670b35SErik Nordmark 
2243bd670b35SErik Nordmark 		if (ipp->ipp_fields & IPPF_IPV4_OPTIONS) {
2244bd670b35SErik Nordmark 			connp->conn_sum = cksum_massage_options_v4(ipha,
2245bd670b35SErik Nordmark 			    connp->conn_netstack);
2246bd670b35SErik Nordmark 		} else {
2247bd670b35SErik Nordmark 			connp->conn_sum = 0;
2248bd670b35SErik Nordmark 		}
2249bd670b35SErik Nordmark 	} else {
2250bd670b35SErik Nordmark 		ip6_t	*ip6h = (ip6_t *)hdrs;
2251bd670b35SErik Nordmark 
2252bd670b35SErik Nordmark 		ip6h->ip6_src = *v6src;
2253bd670b35SErik Nordmark 		ip6h->ip6_dst = *v6dst;
2254bd670b35SErik Nordmark 		ip_build_hdrs_v6(hdrs, ip_hdr_length, ipp, connp->conn_proto,
2255bd670b35SErik Nordmark 		    flowinfo);
2256bd670b35SErik Nordmark 		ip6h->ip6_plen = htons(hdrs_len - IPV6_HDR_LEN);
2257bd670b35SErik Nordmark 
2258bd670b35SErik Nordmark 		if (ipp->ipp_fields & IPPF_RTHDR) {
2259bd670b35SErik Nordmark 			connp->conn_sum = cksum_massage_options_v6(ip6h,
2260bd670b35SErik Nordmark 			    ip_hdr_length, connp->conn_netstack);
2261bd670b35SErik Nordmark 
2262bd670b35SErik Nordmark 			/*
2263bd670b35SErik Nordmark 			 * Verify that the first hop isn't a mapped address.
2264bd670b35SErik Nordmark 			 * Routers along the path need to do this verification
2265bd670b35SErik Nordmark 			 * for subsequent hops.
2266bd670b35SErik Nordmark 			 */
2267bd670b35SErik Nordmark 			if (IN6_IS_ADDR_V4MAPPED(&ip6h->ip6_dst))
2268bd670b35SErik Nordmark 				return (EADDRNOTAVAIL);
2269bd670b35SErik Nordmark 
2270bd670b35SErik Nordmark 		} else {
2271bd670b35SErik Nordmark 			connp->conn_sum = 0;
2272bd670b35SErik Nordmark 		}
2273bd670b35SErik Nordmark 	}
2274bd670b35SErik Nordmark 	return (0);
2275bd670b35SErik Nordmark }
2276bd670b35SErik Nordmark 
2277bd670b35SErik Nordmark /*
2278bd670b35SErik Nordmark  * Prepend a header template to data_mp based on the ip_pkt_t
2279bd670b35SErik Nordmark  * and the passed in source, destination and protocol.
2280bd670b35SErik Nordmark  *
2281bd670b35SErik Nordmark  * Returns failure if can't allocate memory, in which case data_mp is freed.
2282bd670b35SErik Nordmark  * We allocate space for the transport header (ulp_hdr_len) and
2283bd670b35SErik Nordmark  * indicate the offset of the ulp header by setting ixa_ip_hdr_length.
2284bd670b35SErik Nordmark  *
2285bd670b35SErik Nordmark  * We massage an routing option/header and return the ckecksum difference
2286bd670b35SErik Nordmark  * in *sump. This is in host byte order.
2287bd670b35SErik Nordmark  *
2288bd670b35SErik Nordmark  * Caller needs to update conn_wroff if desired.
2289bd670b35SErik Nordmark  */
2290bd670b35SErik Nordmark mblk_t *
conn_prepend_hdr(ip_xmit_attr_t * ixa,const ip_pkt_t * ipp,const in6_addr_t * v6src,const in6_addr_t * v6dst,uint8_t protocol,uint32_t flowinfo,uint_t ulp_hdr_length,mblk_t * data_mp,uint_t data_length,uint_t wroff_extra,uint32_t * sump,int * errorp)2291bd670b35SErik Nordmark conn_prepend_hdr(ip_xmit_attr_t *ixa, const ip_pkt_t *ipp,
2292bd670b35SErik Nordmark     const in6_addr_t *v6src, const in6_addr_t *v6dst,
2293bd670b35SErik Nordmark     uint8_t protocol, uint32_t flowinfo, uint_t ulp_hdr_length, mblk_t *data_mp,
2294bd670b35SErik Nordmark     uint_t data_length, uint_t wroff_extra, uint32_t *sump, int *errorp)
2295bd670b35SErik Nordmark {
2296bd670b35SErik Nordmark 	uint_t		ip_hdr_length;
2297bd670b35SErik Nordmark 	uchar_t		*hdrs;
2298bd670b35SErik Nordmark 	uint_t		hdrs_len;
2299bd670b35SErik Nordmark 	mblk_t		*mp;
2300bd670b35SErik Nordmark 
2301bd670b35SErik Nordmark 	if (ixa->ixa_flags & IXAF_IS_IPV4) {
2302bd670b35SErik Nordmark 		ip_hdr_length = ip_total_hdrs_len_v4(ipp);
2303bd670b35SErik Nordmark 		ASSERT(ip_hdr_length <= IP_MAX_HDR_LENGTH);
2304bd670b35SErik Nordmark 	} else {
2305bd670b35SErik Nordmark 		ip_hdr_length = ip_total_hdrs_len_v6(ipp);
2306bd670b35SErik Nordmark 	}
2307bd670b35SErik Nordmark 	hdrs_len = ip_hdr_length + ulp_hdr_length;
2308bd670b35SErik Nordmark 	ASSERT(hdrs_len != 0);
2309bd670b35SErik Nordmark 
2310bd670b35SErik Nordmark 	ixa->ixa_ip_hdr_length = ip_hdr_length;
2311bd670b35SErik Nordmark 
2312bd670b35SErik Nordmark 	/* Can we prepend to data_mp? */
2313bd670b35SErik Nordmark 	if (data_mp != NULL &&
2314bd670b35SErik Nordmark 	    data_mp->b_rptr - data_mp->b_datap->db_base >= hdrs_len &&
2315bd670b35SErik Nordmark 	    data_mp->b_datap->db_ref == 1) {
2316bd670b35SErik Nordmark 		hdrs = data_mp->b_rptr - hdrs_len;
2317bd670b35SErik Nordmark 		data_mp->b_rptr = hdrs;
2318bd670b35SErik Nordmark 		mp = data_mp;
2319bd670b35SErik Nordmark 	} else {
2320bd670b35SErik Nordmark 		mp = allocb(hdrs_len + wroff_extra, BPRI_MED);
2321bd670b35SErik Nordmark 		if (mp == NULL) {
2322bd670b35SErik Nordmark 			freemsg(data_mp);
2323bd670b35SErik Nordmark 			*errorp = ENOMEM;
2324bd670b35SErik Nordmark 			return (NULL);
2325bd670b35SErik Nordmark 		}
2326bd670b35SErik Nordmark 		mp->b_wptr = mp->b_datap->db_lim;
2327bd670b35SErik Nordmark 		hdrs = mp->b_rptr = mp->b_wptr - hdrs_len;
2328bd670b35SErik Nordmark 		mp->b_cont = data_mp;
2329bd670b35SErik Nordmark 	}
2330bd670b35SErik Nordmark 
2331bd670b35SErik Nordmark 	/*
2332bd670b35SErik Nordmark 	 * Set the source in the header. ip_build_hdrs_v4/v6 will overwrite it
2333bd670b35SErik Nordmark 	 * if PKTINFO (aka IPPF_ADDR) was set.
2334bd670b35SErik Nordmark 	 */
2335bd670b35SErik Nordmark 	if (ixa->ixa_flags & IXAF_IS_IPV4) {
2336bd670b35SErik Nordmark 		ipha_t *ipha = (ipha_t *)hdrs;
2337bd670b35SErik Nordmark 
2338bd670b35SErik Nordmark 		ASSERT(IN6_IS_ADDR_V4MAPPED(v6dst));
2339bd670b35SErik Nordmark 		IN6_V4MAPPED_TO_IPADDR(v6src, ipha->ipha_src);
2340bd670b35SErik Nordmark 		IN6_V4MAPPED_TO_IPADDR(v6dst, ipha->ipha_dst);
2341bd670b35SErik Nordmark 		ip_build_hdrs_v4(hdrs, ip_hdr_length, ipp, protocol);
2342bd670b35SErik Nordmark 		ipha->ipha_length = htons(hdrs_len + data_length);
2343bd670b35SErik Nordmark 		if (ixa->ixa_flags & IXAF_PMTU_IPV4_DF)
2344bd670b35SErik Nordmark 			ipha->ipha_fragment_offset_and_flags |= IPH_DF_HTONS;
2345bd670b35SErik Nordmark 		else
2346bd670b35SErik Nordmark 			ipha->ipha_fragment_offset_and_flags &= ~IPH_DF_HTONS;
2347bd670b35SErik Nordmark 
2348bd670b35SErik Nordmark 		if (ipp->ipp_fields & IPPF_IPV4_OPTIONS) {
2349bd670b35SErik Nordmark 			*sump = cksum_massage_options_v4(ipha,
2350bd670b35SErik Nordmark 			    ixa->ixa_ipst->ips_netstack);
2351bd670b35SErik Nordmark 		} else {
2352bd670b35SErik Nordmark 			*sump = 0;
2353bd670b35SErik Nordmark 		}
2354bd670b35SErik Nordmark 	} else {
2355bd670b35SErik Nordmark 		ip6_t *ip6h = (ip6_t *)hdrs;
2356bd670b35SErik Nordmark 
2357bd670b35SErik Nordmark 		ip6h->ip6_src = *v6src;
2358bd670b35SErik Nordmark 		ip6h->ip6_dst = *v6dst;
2359bd670b35SErik Nordmark 		ip_build_hdrs_v6(hdrs, ip_hdr_length, ipp, protocol, flowinfo);
2360bd670b35SErik Nordmark 		ip6h->ip6_plen = htons(hdrs_len + data_length - IPV6_HDR_LEN);
2361bd670b35SErik Nordmark 
2362bd670b35SErik Nordmark 		if (ipp->ipp_fields & IPPF_RTHDR) {
2363bd670b35SErik Nordmark 			*sump = cksum_massage_options_v6(ip6h,
2364bd670b35SErik Nordmark 			    ip_hdr_length, ixa->ixa_ipst->ips_netstack);
2365bd670b35SErik Nordmark 
2366bd670b35SErik Nordmark 			/*
2367bd670b35SErik Nordmark 			 * Verify that the first hop isn't a mapped address.
2368bd670b35SErik Nordmark 			 * Routers along the path need to do this verification
2369bd670b35SErik Nordmark 			 * for subsequent hops.
2370bd670b35SErik Nordmark 			 */
2371bd670b35SErik Nordmark 			if (IN6_IS_ADDR_V4MAPPED(&ip6h->ip6_dst)) {
2372bd670b35SErik Nordmark 				*errorp = EADDRNOTAVAIL;
2373bd670b35SErik Nordmark 				freemsg(mp);
2374bd670b35SErik Nordmark 				return (NULL);
2375bd670b35SErik Nordmark 			}
2376bd670b35SErik Nordmark 		} else {
2377bd670b35SErik Nordmark 			*sump = 0;
2378bd670b35SErik Nordmark 		}
2379bd670b35SErik Nordmark 	}
2380bd670b35SErik Nordmark 	return (mp);
2381bd670b35SErik Nordmark }
2382bd670b35SErik Nordmark 
2383bd670b35SErik Nordmark /*
2384bd670b35SErik Nordmark  * Massage a source route if any putting the first hop
2385bd670b35SErik Nordmark  * in ipha_dst. Compute a starting value for the checksum which
2386bd670b35SErik Nordmark  * takes into account that the original ipha_dst should be
2387bd670b35SErik Nordmark  * included in the checksum but that IP will include the
2388bd670b35SErik Nordmark  * first hop from the source route in the tcp checksum.
2389bd670b35SErik Nordmark  */
2390bd670b35SErik Nordmark static uint32_t
cksum_massage_options_v4(ipha_t * ipha,netstack_t * ns)2391bd670b35SErik Nordmark cksum_massage_options_v4(ipha_t *ipha, netstack_t *ns)
2392bd670b35SErik Nordmark {
2393bd670b35SErik Nordmark 	in_addr_t	dst;
2394bd670b35SErik Nordmark 	uint32_t	cksum;
2395bd670b35SErik Nordmark 
2396bd670b35SErik Nordmark 	/* Get last hop then diff against first hop */
2397bd670b35SErik Nordmark 	cksum = ip_massage_options(ipha, ns);
2398bd670b35SErik Nordmark 	cksum = (cksum & 0xFFFF) + (cksum >> 16);
2399bd670b35SErik Nordmark 	dst = ipha->ipha_dst;
2400bd670b35SErik Nordmark 	cksum -= ((dst >> 16) + (dst & 0xffff));
2401bd670b35SErik Nordmark 	if ((int)cksum < 0)
2402bd670b35SErik Nordmark 		cksum--;
2403bd670b35SErik Nordmark 	cksum = (cksum & 0xFFFF) + (cksum >> 16);
2404bd670b35SErik Nordmark 	cksum = (cksum & 0xFFFF) + (cksum >> 16);
2405bd670b35SErik Nordmark 	ASSERT(cksum < 0x10000);
2406bd670b35SErik Nordmark 	return (ntohs(cksum));
2407bd670b35SErik Nordmark }
2408bd670b35SErik Nordmark 
2409bd670b35SErik Nordmark static uint32_t
cksum_massage_options_v6(ip6_t * ip6h,uint_t ip_hdr_len,netstack_t * ns)2410bd670b35SErik Nordmark cksum_massage_options_v6(ip6_t *ip6h, uint_t ip_hdr_len, netstack_t *ns)
2411bd670b35SErik Nordmark {
2412bd670b35SErik Nordmark 	uint8_t		*end;
2413bd670b35SErik Nordmark 	ip6_rthdr_t	*rth;
2414bd670b35SErik Nordmark 	uint32_t	cksum;
2415bd670b35SErik Nordmark 
2416bd670b35SErik Nordmark 	end = (uint8_t *)ip6h + ip_hdr_len;
2417bd670b35SErik Nordmark 	rth = ip_find_rthdr_v6(ip6h, end);
2418bd670b35SErik Nordmark 	if (rth == NULL)
2419bd670b35SErik Nordmark 		return (0);
2420bd670b35SErik Nordmark 
2421bd670b35SErik Nordmark 	cksum = ip_massage_options_v6(ip6h, rth, ns);
2422bd670b35SErik Nordmark 	cksum = (cksum & 0xFFFF) + (cksum >> 16);
2423bd670b35SErik Nordmark 	ASSERT(cksum < 0x10000);
2424bd670b35SErik Nordmark 	return (ntohs(cksum));
2425bd670b35SErik Nordmark }
2426bd670b35SErik Nordmark 
2427bd670b35SErik Nordmark /*
2428bd670b35SErik Nordmark  * ULPs that change the destination address need to call this for each
2429bd670b35SErik Nordmark  * change to discard any state about a previous destination that might
2430bd670b35SErik Nordmark  * have been multicast or multirt.
2431bd670b35SErik Nordmark  */
2432bd670b35SErik Nordmark void
ip_attr_newdst(ip_xmit_attr_t * ixa)2433bd670b35SErik Nordmark ip_attr_newdst(ip_xmit_attr_t *ixa)
2434bd670b35SErik Nordmark {
2435bd670b35SErik Nordmark 	ixa->ixa_flags &= ~(IXAF_LOOPBACK_COPY | IXAF_NO_HW_CKSUM |
2436bd670b35SErik Nordmark 	    IXAF_NO_TTL_CHANGE | IXAF_IPV6_ADD_FRAGHDR |
2437bd670b35SErik Nordmark 	    IXAF_NO_LOOP_ZONEID_SET);
2438bd670b35SErik Nordmark }
2439bd670b35SErik Nordmark 
2440bd670b35SErik Nordmark /*
2441bd670b35SErik Nordmark  * Determine the nexthop which will be used.
2442bd670b35SErik Nordmark  * Normally this is just the destination, but if a IPv4 source route, or
2443bd670b35SErik Nordmark  * IPv6 routing header, is in the ip_pkt_t then we extract the nexthop from
2444bd670b35SErik Nordmark  * there.
2445bd670b35SErik Nordmark  */
2446bd670b35SErik Nordmark void
ip_attr_nexthop(const ip_pkt_t * ipp,const ip_xmit_attr_t * ixa,const in6_addr_t * dst,in6_addr_t * nexthop)2447bd670b35SErik Nordmark ip_attr_nexthop(const ip_pkt_t *ipp, const ip_xmit_attr_t *ixa,
2448bd670b35SErik Nordmark     const in6_addr_t *dst, in6_addr_t *nexthop)
2449bd670b35SErik Nordmark {
2450188e1664SErik Nordmark 	if (!(ipp->ipp_fields & (IPPF_IPV4_OPTIONS|IPPF_RTHDR))) {
2451188e1664SErik Nordmark 		*nexthop = *dst;
2452188e1664SErik Nordmark 		return;
2453188e1664SErik Nordmark 	}
2454bd670b35SErik Nordmark 	if (ixa->ixa_flags & IXAF_IS_IPV4) {
2455bd670b35SErik Nordmark 		ipaddr_t v4dst;
2456bd670b35SErik Nordmark 		ipaddr_t v4nexthop;
2457bd670b35SErik Nordmark 
2458bd670b35SErik Nordmark 		IN6_V4MAPPED_TO_IPADDR(dst, v4dst);
2459bd670b35SErik Nordmark 		v4nexthop = ip_pkt_source_route_v4(ipp);
2460bd670b35SErik Nordmark 		if (v4nexthop == INADDR_ANY)
2461bd670b35SErik Nordmark 			v4nexthop = v4dst;
2462bd670b35SErik Nordmark 
2463bd670b35SErik Nordmark 		IN6_IPADDR_TO_V4MAPPED(v4nexthop, nexthop);
2464bd670b35SErik Nordmark 	} else {
2465bd670b35SErik Nordmark 		const in6_addr_t *v6nexthop;
2466bd670b35SErik Nordmark 
2467bd670b35SErik Nordmark 		v6nexthop = ip_pkt_source_route_v6(ipp);
2468bd670b35SErik Nordmark 		if (v6nexthop == NULL)
2469bd670b35SErik Nordmark 			v6nexthop = dst;
2470bd670b35SErik Nordmark 
2471bd670b35SErik Nordmark 		*nexthop = *v6nexthop;
2472bd670b35SErik Nordmark 	}
2473bd670b35SErik Nordmark }
2474bd670b35SErik Nordmark 
2475bd670b35SErik Nordmark /*
2476bd670b35SErik Nordmark  * Update the ip_xmit_attr_t based the addresses, conn_xmit_ipp and conn_ixa.
2477bd670b35SErik Nordmark  * If IPDF_IPSEC is set we cache the IPsec policy to handle the unconnected
2478bd670b35SErik Nordmark  * case (connected latching is done in conn_connect).
2479bd670b35SErik Nordmark  * Note that IPsec policy lookup requires conn_proto and conn_laddr to be
2480bd670b35SErik Nordmark  * set, but doesn't otherwise use the conn_t.
2481bd670b35SErik Nordmark  *
2482bd670b35SErik Nordmark  * Caller must set/clear IXAF_IS_IPV4 as appropriately.
2483bd670b35SErik Nordmark  * Caller must use ip_attr_nexthop() to determine the nexthop argument.
2484bd670b35SErik Nordmark  *
2485bd670b35SErik Nordmark  * The caller must NOT hold conn_lock (to avoid problems with ill_refrele
2486bd670b35SErik Nordmark  * causing the squeue to run doing ipcl_walk grabbing conn_lock.)
2487bd670b35SErik Nordmark  *
2488bd670b35SErik Nordmark  * Updates laddrp and uinfo if they are non-NULL.
2489bd670b35SErik Nordmark  *
2490bd670b35SErik Nordmark  * TSOL notes: The callers if ip_attr_connect must check if the destination
2491bd670b35SErik Nordmark  * is different than before and in that case redo conn_update_label.
2492bd670b35SErik Nordmark  * The callers of conn_connect do not need that since conn_connect
2493bd670b35SErik Nordmark  * performs the conn_update_label.
2494bd670b35SErik Nordmark  */
2495bd670b35SErik Nordmark int
ip_attr_connect(const conn_t * connp,ip_xmit_attr_t * ixa,const in6_addr_t * v6src,const in6_addr_t * v6dst,const in6_addr_t * v6nexthop,in_port_t dstport,in6_addr_t * laddrp,iulp_t * uinfo,uint32_t flags)2496bd670b35SErik Nordmark ip_attr_connect(const conn_t *connp, ip_xmit_attr_t *ixa,
2497bd670b35SErik Nordmark     const in6_addr_t *v6src, const in6_addr_t *v6dst,
2498bd670b35SErik Nordmark     const in6_addr_t *v6nexthop, in_port_t dstport, in6_addr_t *laddrp,
2499bd670b35SErik Nordmark     iulp_t *uinfo, uint32_t flags)
2500bd670b35SErik Nordmark {
2501bd670b35SErik Nordmark 	in6_addr_t		laddr = *v6src;
2502bd670b35SErik Nordmark 	int			error;
2503bd670b35SErik Nordmark 
2504bd670b35SErik Nordmark 	ASSERT(MUTEX_NOT_HELD(&connp->conn_lock));
2505bd670b35SErik Nordmark 
2506bd670b35SErik Nordmark 	if (connp->conn_zone_is_global)
2507bd670b35SErik Nordmark 		flags |= IPDF_ZONE_IS_GLOBAL;
2508bd670b35SErik Nordmark 	else
2509bd670b35SErik Nordmark 		flags &= ~IPDF_ZONE_IS_GLOBAL;
2510bd670b35SErik Nordmark 
2511bd670b35SErik Nordmark 	/*
2512bd670b35SErik Nordmark 	 * Lookup the route to determine a source address and the uinfo.
2513bd670b35SErik Nordmark 	 * If the ULP has a source route option then the caller will
2514bd670b35SErik Nordmark 	 * have set v6nexthop to be the first hop.
2515bd670b35SErik Nordmark 	 */
2516bd670b35SErik Nordmark 	if (ixa->ixa_flags & IXAF_IS_IPV4) {
2517bd670b35SErik Nordmark 		ipaddr_t v4dst;
2518bd670b35SErik Nordmark 		ipaddr_t v4src, v4nexthop;
2519bd670b35SErik Nordmark 
2520bd670b35SErik Nordmark 		IN6_V4MAPPED_TO_IPADDR(v6dst, v4dst);
2521bd670b35SErik Nordmark 		IN6_V4MAPPED_TO_IPADDR(v6nexthop, v4nexthop);
2522bd670b35SErik Nordmark 		IN6_V4MAPPED_TO_IPADDR(v6src, v4src);
2523bd670b35SErik Nordmark 
2524bd670b35SErik Nordmark 		if (connp->conn_unspec_src || v4src != INADDR_ANY)
2525bd670b35SErik Nordmark 			flags &= ~IPDF_SELECT_SRC;
2526bd670b35SErik Nordmark 		else
2527bd670b35SErik Nordmark 			flags |= IPDF_SELECT_SRC;
2528bd670b35SErik Nordmark 
2529bd670b35SErik Nordmark 		error = ip_set_destination_v4(&v4src, v4dst, v4nexthop, ixa,
2530bd670b35SErik Nordmark 		    uinfo, flags, connp->conn_mac_mode);
2531bd670b35SErik Nordmark 		IN6_IPADDR_TO_V4MAPPED(v4src, &laddr);
2532bd670b35SErik Nordmark 	} else {
2533bd670b35SErik Nordmark 		if (connp->conn_unspec_src || !IN6_IS_ADDR_UNSPECIFIED(v6src))
2534bd670b35SErik Nordmark 			flags &= ~IPDF_SELECT_SRC;
2535bd670b35SErik Nordmark 		else
2536bd670b35SErik Nordmark 			flags |= IPDF_SELECT_SRC;
2537bd670b35SErik Nordmark 
2538bd670b35SErik Nordmark 		error = ip_set_destination_v6(&laddr, v6dst, v6nexthop, ixa,
2539bd670b35SErik Nordmark 		    uinfo, flags, connp->conn_mac_mode);
2540bd670b35SErik Nordmark 	}
2541bd670b35SErik Nordmark 	/* Pass out some address even if we hit a RTF_REJECT etc */
2542bd670b35SErik Nordmark 	if (laddrp != NULL)
2543bd670b35SErik Nordmark 		*laddrp = laddr;
2544bd670b35SErik Nordmark 
2545bd670b35SErik Nordmark 	if (error != 0)
2546bd670b35SErik Nordmark 		return (error);
2547bd670b35SErik Nordmark 
2548bd670b35SErik Nordmark 	if (flags & IPDF_IPSEC) {
2549bd670b35SErik Nordmark 		/*
2550bd670b35SErik Nordmark 		 * Set any IPsec policy in ixa. Routine also looks at ULP
2551bd670b35SErik Nordmark 		 * ports.
2552bd670b35SErik Nordmark 		 */
2553bd670b35SErik Nordmark 		ipsec_cache_outbound_policy(connp, v6src, v6dst, dstport, ixa);
2554bd670b35SErik Nordmark 	}
2555bd670b35SErik Nordmark 	return (0);
2556bd670b35SErik Nordmark }
2557bd670b35SErik Nordmark 
2558bd670b35SErik Nordmark /*
2559bd670b35SErik Nordmark  * Connect the conn based on the addresses, conn_xmit_ipp and conn_ixa.
2560bd670b35SErik Nordmark  * Assumes that conn_faddr and conn_fport are already set. As such it is not
2561bd670b35SErik Nordmark  * usable for SCTP, since SCTP has multiple faddrs.
2562bd670b35SErik Nordmark  *
2563bd670b35SErik Nordmark  * Caller must hold conn_lock to provide atomic constency between the
2564bd670b35SErik Nordmark  * conn_t's addresses and the ixa.
2565bd670b35SErik Nordmark  * NOTE: this function drops and reaquires conn_lock since it can't be
2566bd670b35SErik Nordmark  * held across ip_attr_connect/ip_set_destination.
2567bd670b35SErik Nordmark  *
2568bd670b35SErik Nordmark  * The caller needs to handle inserting in the receive-side fanout when
2569bd670b35SErik Nordmark  * appropriate after conn_connect returns.
2570bd670b35SErik Nordmark  */
2571bd670b35SErik Nordmark int
conn_connect(conn_t * connp,iulp_t * uinfo,uint32_t flags)2572bd670b35SErik Nordmark conn_connect(conn_t *connp, iulp_t *uinfo, uint32_t flags)
2573bd670b35SErik Nordmark {
2574bd670b35SErik Nordmark 	ip_xmit_attr_t	*ixa = connp->conn_ixa;
2575bd670b35SErik Nordmark 	in6_addr_t	nexthop;
2576bd670b35SErik Nordmark 	in6_addr_t	saddr, faddr;
2577bd670b35SErik Nordmark 	in_port_t	fport;
2578bd670b35SErik Nordmark 	int		error;
2579bd670b35SErik Nordmark 
2580bd670b35SErik Nordmark 	ASSERT(MUTEX_HELD(&connp->conn_lock));
2581bd670b35SErik Nordmark 
2582bd670b35SErik Nordmark 	if (connp->conn_ipversion == IPV4_VERSION)
2583bd670b35SErik Nordmark 		ixa->ixa_flags |= IXAF_IS_IPV4;
2584bd670b35SErik Nordmark 	else
2585bd670b35SErik Nordmark 		ixa->ixa_flags &= ~IXAF_IS_IPV4;
2586bd670b35SErik Nordmark 
2587bd670b35SErik Nordmark 	/* We do IPsec latching below - hence no caching in ip_attr_connect */
2588bd670b35SErik Nordmark 	flags &= ~IPDF_IPSEC;
2589bd670b35SErik Nordmark 
2590bd670b35SErik Nordmark 	/* In case we had previously done an ip_attr_connect */
2591bd670b35SErik Nordmark 	ip_attr_newdst(ixa);
2592bd670b35SErik Nordmark 
2593bd670b35SErik Nordmark 	/*
2594bd670b35SErik Nordmark 	 * Determine the nexthop and copy the addresses before dropping
2595bd670b35SErik Nordmark 	 * conn_lock.
2596bd670b35SErik Nordmark 	 */
2597bd670b35SErik Nordmark 	ip_attr_nexthop(&connp->conn_xmit_ipp, connp->conn_ixa,
2598bd670b35SErik Nordmark 	    &connp->conn_faddr_v6, &nexthop);
2599bd670b35SErik Nordmark 	saddr = connp->conn_saddr_v6;
2600bd670b35SErik Nordmark 	faddr = connp->conn_faddr_v6;
2601bd670b35SErik Nordmark 	fport = connp->conn_fport;
2602bd670b35SErik Nordmark 
2603bd670b35SErik Nordmark 	mutex_exit(&connp->conn_lock);
2604bd670b35SErik Nordmark 	error = ip_attr_connect(connp, ixa, &saddr, &faddr, &nexthop, fport,
2605bd670b35SErik Nordmark 	    &saddr, uinfo, flags | IPDF_VERIFY_DST);
2606bd670b35SErik Nordmark 	mutex_enter(&connp->conn_lock);
2607bd670b35SErik Nordmark 
2608bd670b35SErik Nordmark 	/* Could have changed even if an error */
2609bd670b35SErik Nordmark 	connp->conn_saddr_v6 = saddr;
2610bd670b35SErik Nordmark 	if (error != 0)
2611bd670b35SErik Nordmark 		return (error);
2612bd670b35SErik Nordmark 
2613bd670b35SErik Nordmark 	/*
2614bd670b35SErik Nordmark 	 * Check whether Trusted Solaris policy allows communication with this
2615bd670b35SErik Nordmark 	 * host, and pretend that the destination is unreachable if not.
2616bd670b35SErik Nordmark 	 * Compute any needed label and place it in ipp_label_v4/v6.
2617bd670b35SErik Nordmark 	 *
2618bd670b35SErik Nordmark 	 * Later conn_build_hdr_template() takes ipp_label_v4/v6 to form
2619bd670b35SErik Nordmark 	 * the packet.
2620bd670b35SErik Nordmark 	 *
2621bd670b35SErik Nordmark 	 * TSOL Note: Any concurrent threads would pick a different ixa
2622bd670b35SErik Nordmark 	 * (and ipp if they are to change the ipp)  so we
2623bd670b35SErik Nordmark 	 * don't have to worry about concurrent threads.
2624bd670b35SErik Nordmark 	 */
2625bd670b35SErik Nordmark 	if (is_system_labeled()) {
2626bd670b35SErik Nordmark 		if (connp->conn_mlp_type != mlptSingle)
2627bd670b35SErik Nordmark 			return (ECONNREFUSED);
2628bd670b35SErik Nordmark 
2629bd670b35SErik Nordmark 		/*
2630bd670b35SErik Nordmark 		 * conn_update_label will set ipp_label* which will later
2631bd670b35SErik Nordmark 		 * be used by conn_build_hdr_template.
2632bd670b35SErik Nordmark 		 */
2633bd670b35SErik Nordmark 		error = conn_update_label(connp, ixa,
2634bd670b35SErik Nordmark 		    &connp->conn_faddr_v6, &connp->conn_xmit_ipp);
2635bd670b35SErik Nordmark 		if (error != 0)
2636bd670b35SErik Nordmark 			return (error);
2637bd670b35SErik Nordmark 	}
2638bd670b35SErik Nordmark 
2639bd670b35SErik Nordmark 	/*
2640bd670b35SErik Nordmark 	 * Ensure that we match on the selected local address.
2641bd670b35SErik Nordmark 	 * This overrides conn_laddr in the case we had earlier bound to a
2642bd670b35SErik Nordmark 	 * multicast or broadcast address.
2643bd670b35SErik Nordmark 	 */
2644bd670b35SErik Nordmark 	connp->conn_laddr_v6 = connp->conn_saddr_v6;
2645bd670b35SErik Nordmark 
2646bd670b35SErik Nordmark 	/*
2647bd670b35SErik Nordmark 	 * Allow setting new policies.
2648bd670b35SErik Nordmark 	 * The addresses/ports are already set, thus the IPsec policy calls
2649bd670b35SErik Nordmark 	 * can handle their passed-in conn's.
2650bd670b35SErik Nordmark 	 */
2651bd670b35SErik Nordmark 	connp->conn_policy_cached = B_FALSE;
2652bd670b35SErik Nordmark 
2653bd670b35SErik Nordmark 	/*
2654bd670b35SErik Nordmark 	 * Cache IPsec policy in this conn.  If we have per-socket policy,
2655bd670b35SErik Nordmark 	 * we'll cache that.  If we don't, we'll inherit global policy.
2656bd670b35SErik Nordmark 	 *
2657bd670b35SErik Nordmark 	 * This is done before the caller inserts in the receive-side fanout.
2658bd670b35SErik Nordmark 	 * Note that conn_policy_cached is set by ipsec_conn_cache_policy() even
2659bd670b35SErik Nordmark 	 * for connections where we don't have a policy. This is to prevent
2660bd670b35SErik Nordmark 	 * global policy lookups in the inbound path.
2661bd670b35SErik Nordmark 	 *
2662bd670b35SErik Nordmark 	 * If we insert before we set conn_policy_cached,
2663bd670b35SErik Nordmark 	 * CONN_INBOUND_POLICY_PRESENT() check can still evaluate true
2664bd670b35SErik Nordmark 	 * because global policy cound be non-empty. We normally call
2665bd670b35SErik Nordmark 	 * ipsec_check_policy() for conn_policy_cached connections only if
2666bd670b35SErik Nordmark 	 * conn_in_enforce_policy is set. But in this case,
2667bd670b35SErik Nordmark 	 * conn_policy_cached can get set anytime since we made the
2668bd670b35SErik Nordmark 	 * CONN_INBOUND_POLICY_PRESENT() check and ipsec_check_policy() is
2669bd670b35SErik Nordmark 	 * called, which will make the above assumption false.  Thus, we
2670bd670b35SErik Nordmark 	 * need to insert after we set conn_policy_cached.
2671bd670b35SErik Nordmark 	 */
2672bd670b35SErik Nordmark 	error = ipsec_conn_cache_policy(connp,
2673bd670b35SErik Nordmark 	    connp->conn_ipversion == IPV4_VERSION);
2674bd670b35SErik Nordmark 	if (error != 0)
2675bd670b35SErik Nordmark 		return (error);
2676bd670b35SErik Nordmark 
2677bd670b35SErik Nordmark 	/*
2678bd670b35SErik Nordmark 	 * We defer to do LSO check until here since now we have better idea
2679bd670b35SErik Nordmark 	 * whether IPsec is present. If the underlying ill is LSO capable,
2680bd670b35SErik Nordmark 	 * copy its capability in so the ULP can decide whether to enable LSO
2681bd670b35SErik Nordmark 	 * on this connection. So far, only TCP/IPv4 is implemented, so won't
2682bd670b35SErik Nordmark 	 * claim LSO for IPv6.
2683bd670b35SErik Nordmark 	 *
2684bd670b35SErik Nordmark 	 * Currently, won't enable LSO for IRE_LOOPBACK or IRE_LOCAL, because
2685bd670b35SErik Nordmark 	 * the receiver can not handle it. Also not to enable LSO for MULTIRT.
2686bd670b35SErik Nordmark 	 */
2687bd670b35SErik Nordmark 	ixa->ixa_flags &= ~IXAF_LSO_CAPAB;
2688bd670b35SErik Nordmark 
2689bd670b35SErik Nordmark 	ASSERT(ixa->ixa_ire != NULL);
2690bd670b35SErik Nordmark 	if (ixa->ixa_ipst->ips_ip_lso_outbound && (flags & IPDF_LSO) &&
2691bd670b35SErik Nordmark 	    !(ixa->ixa_flags & IXAF_IPSEC_SECURE) &&
2692bd670b35SErik Nordmark 	    !(ixa->ixa_ire->ire_type & (IRE_LOCAL | IRE_LOOPBACK)) &&
2693bd670b35SErik Nordmark 	    !(ixa->ixa_ire->ire_flags & RTF_MULTIRT) &&
2694bd670b35SErik Nordmark 	    (ixa->ixa_nce != NULL) &&
2695bd670b35SErik Nordmark 	    ((ixa->ixa_flags & IXAF_IS_IPV4) ?
2696bd670b35SErik Nordmark 	    ILL_LSO_TCP_IPV4_USABLE(ixa->ixa_nce->nce_ill) :
2697bd670b35SErik Nordmark 	    ILL_LSO_TCP_IPV6_USABLE(ixa->ixa_nce->nce_ill))) {
2698bd670b35SErik Nordmark 		ixa->ixa_lso_capab = *ixa->ixa_nce->nce_ill->ill_lso_capab;
2699bd670b35SErik Nordmark 		ixa->ixa_flags |= IXAF_LSO_CAPAB;
2700bd670b35SErik Nordmark 	}
2701bd670b35SErik Nordmark 
2702bd670b35SErik Nordmark 	/* Check whether ZEROCOPY capability is usable for this connection. */
2703bd670b35SErik Nordmark 	ixa->ixa_flags &= ~IXAF_ZCOPY_CAPAB;
2704bd670b35SErik Nordmark 
2705bd670b35SErik Nordmark 	if ((flags & IPDF_ZCOPY) &&
2706bd670b35SErik Nordmark 	    !(ixa->ixa_flags & IXAF_IPSEC_SECURE) &&
2707bd670b35SErik Nordmark 	    !(ixa->ixa_ire->ire_type & (IRE_LOCAL | IRE_LOOPBACK)) &&
2708bd670b35SErik Nordmark 	    !(ixa->ixa_ire->ire_flags & RTF_MULTIRT) &&
2709bd670b35SErik Nordmark 	    (ixa->ixa_nce != NULL) &&
2710bd670b35SErik Nordmark 	    ILL_ZCOPY_USABLE(ixa->ixa_nce->nce_ill)) {
2711bd670b35SErik Nordmark 		ixa->ixa_flags |= IXAF_ZCOPY_CAPAB;
2712bd670b35SErik Nordmark 	}
2713bd670b35SErik Nordmark 	return (0);
2714bd670b35SErik Nordmark }
2715bd670b35SErik Nordmark 
2716bd670b35SErik Nordmark /*
2717bd670b35SErik Nordmark  * Predicates to check if the addresses match conn_last*
2718bd670b35SErik Nordmark  */
2719bd670b35SErik Nordmark 
2720bd670b35SErik Nordmark /*
2721bd670b35SErik Nordmark  * Compare the conn against an address.
2722bd670b35SErik Nordmark  * If using mapped addresses on AF_INET6 sockets, use the _v6 function
2723bd670b35SErik Nordmark  */
2724bd670b35SErik Nordmark boolean_t
conn_same_as_last_v4(conn_t * connp,sin_t * sin)2725bd670b35SErik Nordmark conn_same_as_last_v4(conn_t *connp, sin_t *sin)
2726bd670b35SErik Nordmark {
2727bd670b35SErik Nordmark 	ASSERT(connp->conn_family == AF_INET);
2728bd670b35SErik Nordmark 	return (sin->sin_addr.s_addr == connp->conn_v4lastdst &&
2729bd670b35SErik Nordmark 	    sin->sin_port == connp->conn_lastdstport);
2730bd670b35SErik Nordmark }
2731bd670b35SErik Nordmark 
2732bd670b35SErik Nordmark /*
2733bd670b35SErik Nordmark  * Compare, including for mapped addresses
2734bd670b35SErik Nordmark  */
2735bd670b35SErik Nordmark boolean_t
conn_same_as_last_v6(conn_t * connp,sin6_t * sin6)2736bd670b35SErik Nordmark conn_same_as_last_v6(conn_t *connp, sin6_t *sin6)
2737bd670b35SErik Nordmark {
2738bd670b35SErik Nordmark 	return (IN6_ARE_ADDR_EQUAL(&connp->conn_v6lastdst, &sin6->sin6_addr) &&
2739bd670b35SErik Nordmark 	    sin6->sin6_port == connp->conn_lastdstport &&
2740bd670b35SErik Nordmark 	    sin6->sin6_flowinfo == connp->conn_lastflowinfo &&
2741bd670b35SErik Nordmark 	    sin6->sin6_scope_id == connp->conn_lastscopeid);
2742bd670b35SErik Nordmark }
2743bd670b35SErik Nordmark 
2744bd670b35SErik Nordmark /*
2745bd670b35SErik Nordmark  * Compute a label and place it in the ip_packet_t.
2746bd670b35SErik Nordmark  * Handles IPv4 and IPv6.
2747bd670b35SErik Nordmark  * The caller should have a correct ixa_tsl and ixa_zoneid and have
2748bd670b35SErik Nordmark  * already called conn_connect or ip_attr_connect to ensure that tsol_check_dest
2749bd670b35SErik Nordmark  * has been called.
2750bd670b35SErik Nordmark  */
2751bd670b35SErik Nordmark int
conn_update_label(const conn_t * connp,const ip_xmit_attr_t * ixa,const in6_addr_t * v6dst,ip_pkt_t * ipp)2752bd670b35SErik Nordmark conn_update_label(const conn_t *connp, const ip_xmit_attr_t *ixa,
2753bd670b35SErik Nordmark     const in6_addr_t *v6dst, ip_pkt_t *ipp)
2754bd670b35SErik Nordmark {
2755bd670b35SErik Nordmark 	int		err;
2756bd670b35SErik Nordmark 	ipaddr_t	v4dst;
2757bd670b35SErik Nordmark 
2758bd670b35SErik Nordmark 	if (IN6_IS_ADDR_V4MAPPED(v6dst)) {
2759bd670b35SErik Nordmark 		uchar_t		opt_storage[IP_MAX_OPT_LENGTH];
2760bd670b35SErik Nordmark 
2761bd670b35SErik Nordmark 		IN6_V4MAPPED_TO_IPADDR(v6dst, v4dst);
2762bd670b35SErik Nordmark 
2763bd670b35SErik Nordmark 		err = tsol_compute_label_v4(ixa->ixa_tsl, ixa->ixa_zoneid,
2764bd670b35SErik Nordmark 		    v4dst, opt_storage, ixa->ixa_ipst);
2765bd670b35SErik Nordmark 		if (err == 0) {
2766bd670b35SErik Nordmark 			/* Length contained in opt_storage[IPOPT_OLEN] */
2767bd670b35SErik Nordmark 			err = optcom_pkt_set(opt_storage,
2768bd670b35SErik Nordmark 			    opt_storage[IPOPT_OLEN],
2769bd670b35SErik Nordmark 			    (uchar_t **)&ipp->ipp_label_v4,
2770bd670b35SErik Nordmark 			    &ipp->ipp_label_len_v4);
2771bd670b35SErik Nordmark 		}
2772bd670b35SErik Nordmark 		if (err != 0) {
2773bd670b35SErik Nordmark 			DTRACE_PROBE4(tx__ip__log__info__updatelabel,
2774bd670b35SErik Nordmark 			    char *, "conn(1) failed to update options(2) "
2775bd670b35SErik Nordmark 			    "on ixa(3)",
2776bd670b35SErik Nordmark 			    conn_t *, connp, char *, opt_storage,
2777bd670b35SErik Nordmark 			    ip_xmit_attr_t *, ixa);
2778bd670b35SErik Nordmark 		}
2779bd670b35SErik Nordmark 		if (ipp->ipp_label_len_v4 != 0)
2780bd670b35SErik Nordmark 			ipp->ipp_fields |= IPPF_LABEL_V4;
2781bd670b35SErik Nordmark 		else
2782bd670b35SErik Nordmark 			ipp->ipp_fields &= ~IPPF_LABEL_V4;
2783bd670b35SErik Nordmark 	} else {
2784bd670b35SErik Nordmark 		uchar_t		opt_storage[TSOL_MAX_IPV6_OPTION];
2785bd670b35SErik Nordmark 		uint_t		optlen;
2786bd670b35SErik Nordmark 
2787bd670b35SErik Nordmark 		err = tsol_compute_label_v6(ixa->ixa_tsl, ixa->ixa_zoneid,
2788bd670b35SErik Nordmark 		    v6dst, opt_storage, ixa->ixa_ipst);
2789bd670b35SErik Nordmark 		if (err == 0) {
2790bd670b35SErik Nordmark 			/*
2791bd670b35SErik Nordmark 			 * Note that ipp_label_v6 is just the option - not
2792bd670b35SErik Nordmark 			 * the hopopts extension header.
2793bd670b35SErik Nordmark 			 *
2794bd670b35SErik Nordmark 			 * Length contained in opt_storage[IPOPT_OLEN], but
2795bd670b35SErik Nordmark 			 * that doesn't include the two byte options header.
2796bd670b35SErik Nordmark 			 */
2797bd670b35SErik Nordmark 			optlen = opt_storage[IPOPT_OLEN];
2798bd670b35SErik Nordmark 			if (optlen != 0)
2799bd670b35SErik Nordmark 				optlen += 2;
2800bd670b35SErik Nordmark 
2801bd670b35SErik Nordmark 			err = optcom_pkt_set(opt_storage, optlen,
2802bd670b35SErik Nordmark 			    (uchar_t **)&ipp->ipp_label_v6,
2803bd670b35SErik Nordmark 			    &ipp->ipp_label_len_v6);
2804bd670b35SErik Nordmark 		}
2805bd670b35SErik Nordmark 		if (err != 0) {
2806bd670b35SErik Nordmark 			DTRACE_PROBE4(tx__ip__log__info__updatelabel,
2807bd670b35SErik Nordmark 			    char *, "conn(1) failed to update options(2) "
2808bd670b35SErik Nordmark 			    "on ixa(3)",
2809bd670b35SErik Nordmark 			    conn_t *, connp, char *, opt_storage,
2810bd670b35SErik Nordmark 			    ip_xmit_attr_t *, ixa);
2811bd670b35SErik Nordmark 		}
2812bd670b35SErik Nordmark 		if (ipp->ipp_label_len_v6 != 0)
2813bd670b35SErik Nordmark 			ipp->ipp_fields |= IPPF_LABEL_V6;
2814bd670b35SErik Nordmark 		else
2815bd670b35SErik Nordmark 			ipp->ipp_fields &= ~IPPF_LABEL_V6;
2816bd670b35SErik Nordmark 	}
2817bd670b35SErik Nordmark 	return (err);
2818bd670b35SErik Nordmark }
2819bd670b35SErik Nordmark 
2820bd670b35SErik Nordmark /*
2821bd670b35SErik Nordmark  * Inherit all options settings from the parent/listener to the eager.
2822bd670b35SErik Nordmark  * Returns zero on success; ENOMEM if memory allocation failed.
2823bd670b35SErik Nordmark  *
2824bd670b35SErik Nordmark  * We assume that the eager has not had any work done i.e., the conn_ixa
2825bd670b35SErik Nordmark  * and conn_xmit_ipp are all zero.
2826bd670b35SErik Nordmark  * Furthermore we assume that no other thread can access the eager (because
2827bd670b35SErik Nordmark  * it isn't inserted in any fanout list).
2828bd670b35SErik Nordmark  */
2829bd670b35SErik Nordmark int
conn_inherit_parent(conn_t * lconnp,conn_t * econnp)2830bd670b35SErik Nordmark conn_inherit_parent(conn_t *lconnp, conn_t *econnp)
2831bd670b35SErik Nordmark {
2832bd670b35SErik Nordmark 	cred_t	*credp;
2833bd670b35SErik Nordmark 	int	err;
2834bd670b35SErik Nordmark 	void	*notify_cookie;
28355fbcfb69SRao Shoaib 	uint32_t xmit_hint;
2836bd670b35SErik Nordmark 
2837bd670b35SErik Nordmark 	econnp->conn_family = lconnp->conn_family;
2838bd670b35SErik Nordmark 	econnp->conn_ipv6_v6only = lconnp->conn_ipv6_v6only;
2839bd670b35SErik Nordmark 	econnp->conn_wq = lconnp->conn_wq;
2840bd670b35SErik Nordmark 	econnp->conn_rq = lconnp->conn_rq;
2841bd670b35SErik Nordmark 
2842bd670b35SErik Nordmark 	/*
2843bd670b35SErik Nordmark 	 * Make a safe copy of the transmit attributes.
2844bd670b35SErik Nordmark 	 * conn_connect will later be used by the caller to setup the ire etc.
2845bd670b35SErik Nordmark 	 */
2846bd670b35SErik Nordmark 	ASSERT(econnp->conn_ixa->ixa_refcnt == 1);
2847bd670b35SErik Nordmark 	ASSERT(econnp->conn_ixa->ixa_ire == NULL);
2848bd670b35SErik Nordmark 	ASSERT(econnp->conn_ixa->ixa_dce == NULL);
2849bd670b35SErik Nordmark 	ASSERT(econnp->conn_ixa->ixa_nce == NULL);
2850bd670b35SErik Nordmark 
28515fbcfb69SRao Shoaib 	/* Preserve ixa_notify_cookie and xmit_hint */
2852bd670b35SErik Nordmark 	notify_cookie = econnp->conn_ixa->ixa_notify_cookie;
28535fbcfb69SRao Shoaib 	xmit_hint = econnp->conn_ixa->ixa_xmit_hint;
2854bd670b35SErik Nordmark 	ixa_safe_copy(lconnp->conn_ixa, econnp->conn_ixa);
2855bd670b35SErik Nordmark 	econnp->conn_ixa->ixa_notify_cookie = notify_cookie;
28565fbcfb69SRao Shoaib 	econnp->conn_ixa->ixa_xmit_hint = xmit_hint;
2857bd670b35SErik Nordmark 
2858bd670b35SErik Nordmark 	econnp->conn_bound_if = lconnp->conn_bound_if;
2859bd670b35SErik Nordmark 	econnp->conn_incoming_ifindex = lconnp->conn_incoming_ifindex;
2860bd670b35SErik Nordmark 
2861bd670b35SErik Nordmark 	/* Inherit all RECV options */
2862bd670b35SErik Nordmark 	econnp->conn_recv_ancillary = lconnp->conn_recv_ancillary;
2863bd670b35SErik Nordmark 
2864bd670b35SErik Nordmark 	err = ip_pkt_copy(&lconnp->conn_xmit_ipp, &econnp->conn_xmit_ipp,
2865bd670b35SErik Nordmark 	    KM_NOSLEEP);
2866bd670b35SErik Nordmark 	if (err != 0)
2867bd670b35SErik Nordmark 		return (err);
2868bd670b35SErik Nordmark 
2869bd670b35SErik Nordmark 	econnp->conn_zoneid = lconnp->conn_zoneid;
2870bd670b35SErik Nordmark 	econnp->conn_allzones = lconnp->conn_allzones;
2871bd670b35SErik Nordmark 
2872bd670b35SErik Nordmark 	/* This is odd. Pick a flowlabel for each connection instead? */
2873bd670b35SErik Nordmark 	econnp->conn_flowinfo = lconnp->conn_flowinfo;
2874bd670b35SErik Nordmark 
2875bd670b35SErik Nordmark 	econnp->conn_default_ttl = lconnp->conn_default_ttl;
2876bd670b35SErik Nordmark 
2877bd670b35SErik Nordmark 	/*
2878bd670b35SErik Nordmark 	 * TSOL: tsol_input_proc() needs the eager's cred before the
2879bd670b35SErik Nordmark 	 * eager is accepted
2880bd670b35SErik Nordmark 	 */
2881bd670b35SErik Nordmark 	ASSERT(lconnp->conn_cred != NULL);
2882bd670b35SErik Nordmark 	econnp->conn_cred = credp = lconnp->conn_cred;
2883bd670b35SErik Nordmark 	crhold(credp);
2884bd670b35SErik Nordmark 	econnp->conn_cpid = lconnp->conn_cpid;
2885d3d50737SRafael Vanoni 	econnp->conn_open_time = ddi_get_lbolt64();
2886bd670b35SErik Nordmark 
2887bd670b35SErik Nordmark 	/*
2888bd670b35SErik Nordmark 	 * Cache things in the ixa without any refhold.
2889bd670b35SErik Nordmark 	 * Listener might not have set up ixa_cred
2890bd670b35SErik Nordmark 	 */
2891be4c8f74SErik Nordmark 	ASSERT(!(econnp->conn_ixa->ixa_free_flags & IXA_FREE_CRED));
2892bd670b35SErik Nordmark 	econnp->conn_ixa->ixa_cred = econnp->conn_cred;
2893bd670b35SErik Nordmark 	econnp->conn_ixa->ixa_cpid = econnp->conn_cpid;
2894bd670b35SErik Nordmark 	if (is_system_labeled())
2895bd670b35SErik Nordmark 		econnp->conn_ixa->ixa_tsl = crgetlabel(econnp->conn_cred);
2896bd670b35SErik Nordmark 
2897bd670b35SErik Nordmark 	/*
2898bd670b35SErik Nordmark 	 * If the caller has the process-wide flag set, then default to MAC
2899bd670b35SErik Nordmark 	 * exempt mode.  This allows read-down to unlabeled hosts.
2900bd670b35SErik Nordmark 	 */
2901bd670b35SErik Nordmark 	if (getpflags(NET_MAC_AWARE, credp) != 0)
2902bd670b35SErik Nordmark 		econnp->conn_mac_mode = CONN_MAC_AWARE;
2903bd670b35SErik Nordmark 
2904bd670b35SErik Nordmark 	econnp->conn_zone_is_global = lconnp->conn_zone_is_global;
2905bd670b35SErik Nordmark 
2906bd670b35SErik Nordmark 	/*
2907bd670b35SErik Nordmark 	 * We eliminate the need for sockfs to send down a T_SVR4_OPTMGMT_REQ
2908bd670b35SErik Nordmark 	 * via soaccept()->soinheritoptions() which essentially applies
2909bd670b35SErik Nordmark 	 * all the listener options to the new connection. The options that we
2910bd670b35SErik Nordmark 	 * need to take care of are:
2911bd670b35SErik Nordmark 	 * SO_DEBUG, SO_REUSEADDR, SO_KEEPALIVE, SO_DONTROUTE, SO_BROADCAST,
2912bd670b35SErik Nordmark 	 * SO_USELOOPBACK, SO_OOBINLINE, SO_DGRAM_ERRIND, SO_LINGER,
2913bd670b35SErik Nordmark 	 * SO_SNDBUF, SO_RCVBUF.
2914bd670b35SErik Nordmark 	 *
2915bd670b35SErik Nordmark 	 * SO_RCVBUF:	conn_rcvbuf is set.
2916bd670b35SErik Nordmark 	 * SO_SNDBUF:	conn_sndbuf is set.
2917bd670b35SErik Nordmark 	 */
2918bd670b35SErik Nordmark 
2919188e1664SErik Nordmark 	/* Could we define a struct and use a struct copy for this? */
2920bd670b35SErik Nordmark 	econnp->conn_sndbuf = lconnp->conn_sndbuf;
2921bd670b35SErik Nordmark 	econnp->conn_rcvbuf = lconnp->conn_rcvbuf;
2922bd670b35SErik Nordmark 	econnp->conn_sndlowat = lconnp->conn_sndlowat;
2923bd670b35SErik Nordmark 	econnp->conn_rcvlowat = lconnp->conn_rcvlowat;
2924bd670b35SErik Nordmark 	econnp->conn_dgram_errind = lconnp->conn_dgram_errind;
2925bd670b35SErik Nordmark 	econnp->conn_oobinline = lconnp->conn_oobinline;
2926bd670b35SErik Nordmark 	econnp->conn_debug = lconnp->conn_debug;
2927bd670b35SErik Nordmark 	econnp->conn_keepalive = lconnp->conn_keepalive;
2928bd670b35SErik Nordmark 	econnp->conn_linger = lconnp->conn_linger;
2929bd670b35SErik Nordmark 	econnp->conn_lingertime = lconnp->conn_lingertime;
2930bd670b35SErik Nordmark 
2931bd670b35SErik Nordmark 	/* Set the IP options */
2932bd670b35SErik Nordmark 	econnp->conn_broadcast = lconnp->conn_broadcast;
2933bd670b35SErik Nordmark 	econnp->conn_useloopback = lconnp->conn_useloopback;
2934bd670b35SErik Nordmark 	econnp->conn_reuseaddr = lconnp->conn_reuseaddr;
2935bd670b35SErik Nordmark 	return (0);
2936bd670b35SErik Nordmark }
2937