xref: /onnv-gate/usr/src/uts/common/inet/ip/conn_opt.c (revision 11849:90b0a05a5e2c)
111042SErik.Nordmark@Sun.COM /*
211042SErik.Nordmark@Sun.COM  * CDDL HEADER START
311042SErik.Nordmark@Sun.COM  *
411042SErik.Nordmark@Sun.COM  * The contents of this file are subject to the terms of the
511042SErik.Nordmark@Sun.COM  * Common Development and Distribution License (the "License").
611042SErik.Nordmark@Sun.COM  * You may not use this file except in compliance with the License.
711042SErik.Nordmark@Sun.COM  *
811042SErik.Nordmark@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
911042SErik.Nordmark@Sun.COM  * or http://www.opensolaris.org/os/licensing.
1011042SErik.Nordmark@Sun.COM  * See the License for the specific language governing permissions
1111042SErik.Nordmark@Sun.COM  * and limitations under the License.
1211042SErik.Nordmark@Sun.COM  *
1311042SErik.Nordmark@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
1411042SErik.Nordmark@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1511042SErik.Nordmark@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
1611042SErik.Nordmark@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
1711042SErik.Nordmark@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
1811042SErik.Nordmark@Sun.COM  *
1911042SErik.Nordmark@Sun.COM  * CDDL HEADER END
2011042SErik.Nordmark@Sun.COM  */
2111042SErik.Nordmark@Sun.COM 
2211042SErik.Nordmark@Sun.COM /*
2311430SErik.Nordmark@Sun.COM  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
2411042SErik.Nordmark@Sun.COM  * Use is subject to license terms.
2511042SErik.Nordmark@Sun.COM  */
2611042SErik.Nordmark@Sun.COM /* Copyright (c) 1990 Mentat Inc. */
2711042SErik.Nordmark@Sun.COM 
2811042SErik.Nordmark@Sun.COM #include <sys/types.h>
2911042SErik.Nordmark@Sun.COM #include <sys/stream.h>
3011042SErik.Nordmark@Sun.COM #include <sys/strsun.h>
3111042SErik.Nordmark@Sun.COM #define	_SUN_TPI_VERSION 2
3211042SErik.Nordmark@Sun.COM #include <sys/tihdr.h>
3311042SErik.Nordmark@Sun.COM #include <sys/xti_inet.h>
3411042SErik.Nordmark@Sun.COM #include <sys/ucred.h>
3511042SErik.Nordmark@Sun.COM #include <sys/zone.h>
3611042SErik.Nordmark@Sun.COM #include <sys/ddi.h>
3711042SErik.Nordmark@Sun.COM #include <sys/sunddi.h>
3811042SErik.Nordmark@Sun.COM #include <sys/cmn_err.h>
3911042SErik.Nordmark@Sun.COM #include <sys/debug.h>
4011042SErik.Nordmark@Sun.COM #include <sys/atomic.h>
4111042SErik.Nordmark@Sun.COM #include <sys/policy.h>
4211042SErik.Nordmark@Sun.COM 
4311042SErik.Nordmark@Sun.COM #include <sys/systm.h>
4411042SErik.Nordmark@Sun.COM #include <sys/param.h>
4511042SErik.Nordmark@Sun.COM #include <sys/kmem.h>
4611042SErik.Nordmark@Sun.COM #include <sys/sdt.h>
4711042SErik.Nordmark@Sun.COM #include <sys/socket.h>
4811042SErik.Nordmark@Sun.COM #include <sys/ethernet.h>
4911042SErik.Nordmark@Sun.COM #include <sys/mac.h>
5011042SErik.Nordmark@Sun.COM #include <net/if.h>
5111042SErik.Nordmark@Sun.COM #include <net/if_types.h>
5211042SErik.Nordmark@Sun.COM #include <net/if_arp.h>
5311042SErik.Nordmark@Sun.COM #include <net/route.h>
5411042SErik.Nordmark@Sun.COM #include <sys/sockio.h>
5511042SErik.Nordmark@Sun.COM #include <netinet/in.h>
5611042SErik.Nordmark@Sun.COM #include <net/if_dl.h>
5711042SErik.Nordmark@Sun.COM 
5811042SErik.Nordmark@Sun.COM #include <inet/common.h>
5911042SErik.Nordmark@Sun.COM #include <inet/mi.h>
6011042SErik.Nordmark@Sun.COM #include <inet/mib2.h>
6111042SErik.Nordmark@Sun.COM #include <inet/nd.h>
6211042SErik.Nordmark@Sun.COM #include <inet/arp.h>
6311042SErik.Nordmark@Sun.COM #include <inet/snmpcom.h>
6411042SErik.Nordmark@Sun.COM #include <inet/kstatcom.h>
6511042SErik.Nordmark@Sun.COM 
6611042SErik.Nordmark@Sun.COM #include <netinet/igmp_var.h>
6711042SErik.Nordmark@Sun.COM #include <netinet/ip6.h>
6811042SErik.Nordmark@Sun.COM #include <netinet/icmp6.h>
6911042SErik.Nordmark@Sun.COM #include <netinet/sctp.h>
7011042SErik.Nordmark@Sun.COM 
7111042SErik.Nordmark@Sun.COM #include <inet/ip.h>
7211042SErik.Nordmark@Sun.COM #include <inet/ip_impl.h>
7311042SErik.Nordmark@Sun.COM #include <inet/ip6.h>
7411042SErik.Nordmark@Sun.COM #include <inet/ip6_asp.h>
7511042SErik.Nordmark@Sun.COM #include <inet/tcp.h>
7611042SErik.Nordmark@Sun.COM #include <inet/ip_multi.h>
7711042SErik.Nordmark@Sun.COM #include <inet/ip_if.h>
7811042SErik.Nordmark@Sun.COM #include <inet/ip_ire.h>
7911042SErik.Nordmark@Sun.COM #include <inet/ip_ftable.h>
8011042SErik.Nordmark@Sun.COM #include <inet/ip_rts.h>
8111042SErik.Nordmark@Sun.COM #include <inet/optcom.h>
8211042SErik.Nordmark@Sun.COM #include <inet/ip_ndp.h>
8311042SErik.Nordmark@Sun.COM #include <inet/ip_listutils.h>
8411042SErik.Nordmark@Sun.COM #include <netinet/igmp.h>
8511042SErik.Nordmark@Sun.COM #include <netinet/ip_mroute.h>
8611042SErik.Nordmark@Sun.COM #include <netinet/udp.h>
8711042SErik.Nordmark@Sun.COM #include <inet/ipp_common.h>
8811042SErik.Nordmark@Sun.COM 
8911042SErik.Nordmark@Sun.COM #include <net/pfkeyv2.h>
9011042SErik.Nordmark@Sun.COM #include <inet/sadb.h>
9111042SErik.Nordmark@Sun.COM #include <inet/ipsec_impl.h>
9211042SErik.Nordmark@Sun.COM #include <inet/ipdrop.h>
9311042SErik.Nordmark@Sun.COM #include <inet/ip_netinfo.h>
9411042SErik.Nordmark@Sun.COM 
9511042SErik.Nordmark@Sun.COM #include <inet/ipclassifier.h>
9611042SErik.Nordmark@Sun.COM #include <inet/sctp_ip.h>
9711042SErik.Nordmark@Sun.COM #include <inet/sctp/sctp_impl.h>
9811042SErik.Nordmark@Sun.COM #include <inet/udp_impl.h>
9911042SErik.Nordmark@Sun.COM #include <sys/sunddi.h>
10011042SErik.Nordmark@Sun.COM 
10111042SErik.Nordmark@Sun.COM #include <sys/tsol/label.h>
10211042SErik.Nordmark@Sun.COM #include <sys/tsol/tnet.h>
10311042SErik.Nordmark@Sun.COM 
10411042SErik.Nordmark@Sun.COM /*
10511042SErik.Nordmark@Sun.COM  * Return how much size is needed for the different ancillary data items
10611042SErik.Nordmark@Sun.COM  */
10711042SErik.Nordmark@Sun.COM uint_t
10811042SErik.Nordmark@Sun.COM conn_recvancillary_size(conn_t *connp, crb_t recv_ancillary,
10911042SErik.Nordmark@Sun.COM     ip_recv_attr_t *ira, mblk_t *mp, ip_pkt_t *ipp)
11011042SErik.Nordmark@Sun.COM {
11111042SErik.Nordmark@Sun.COM 	uint_t		ancil_size;
11211042SErik.Nordmark@Sun.COM 	ip_stack_t	*ipst = connp->conn_netstack->netstack_ip;
11311042SErik.Nordmark@Sun.COM 
11411042SErik.Nordmark@Sun.COM 	/*
11511042SErik.Nordmark@Sun.COM 	 * If IP_RECVDSTADDR is set we include the destination IP
11611042SErik.Nordmark@Sun.COM 	 * address as an option. With IP_RECVOPTS we include all
11711042SErik.Nordmark@Sun.COM 	 * the IP options.
11811042SErik.Nordmark@Sun.COM 	 */
11911042SErik.Nordmark@Sun.COM 	ancil_size = 0;
12011042SErik.Nordmark@Sun.COM 	if (recv_ancillary.crb_recvdstaddr &&
12111042SErik.Nordmark@Sun.COM 	    (ira->ira_flags & IRAF_IS_IPV4)) {
12211042SErik.Nordmark@Sun.COM 		ancil_size += sizeof (struct T_opthdr) +
12311042SErik.Nordmark@Sun.COM 		    sizeof (struct in_addr);
12411042SErik.Nordmark@Sun.COM 		IP_STAT(ipst, conn_in_recvdstaddr);
12511042SErik.Nordmark@Sun.COM 	}
12611042SErik.Nordmark@Sun.COM 
12711042SErik.Nordmark@Sun.COM 	/*
12811042SErik.Nordmark@Sun.COM 	 * ip_recvpktinfo is used for both AF_INET and AF_INET6 but
12911042SErik.Nordmark@Sun.COM 	 * are different
13011042SErik.Nordmark@Sun.COM 	 */
13111042SErik.Nordmark@Sun.COM 	if (recv_ancillary.crb_ip_recvpktinfo &&
13211042SErik.Nordmark@Sun.COM 	    connp->conn_family == AF_INET) {
13311042SErik.Nordmark@Sun.COM 		ancil_size += sizeof (struct T_opthdr) +
13411042SErik.Nordmark@Sun.COM 		    sizeof (struct in_pktinfo);
13511042SErik.Nordmark@Sun.COM 		IP_STAT(ipst, conn_in_recvpktinfo);
13611042SErik.Nordmark@Sun.COM 	}
13711042SErik.Nordmark@Sun.COM 
13811042SErik.Nordmark@Sun.COM 	if ((recv_ancillary.crb_recvopts) &&
13911042SErik.Nordmark@Sun.COM 	    (ipp->ipp_fields & IPPF_IPV4_OPTIONS)) {
14011042SErik.Nordmark@Sun.COM 		ancil_size += sizeof (struct T_opthdr) +
14111042SErik.Nordmark@Sun.COM 		    ipp->ipp_ipv4_options_len;
14211042SErik.Nordmark@Sun.COM 		IP_STAT(ipst, conn_in_recvopts);
14311042SErik.Nordmark@Sun.COM 	}
14411042SErik.Nordmark@Sun.COM 
14511042SErik.Nordmark@Sun.COM 	if (recv_ancillary.crb_recvslla) {
14611042SErik.Nordmark@Sun.COM 		ip_stack_t *ipst = connp->conn_netstack->netstack_ip;
14711042SErik.Nordmark@Sun.COM 		ill_t *ill;
14811042SErik.Nordmark@Sun.COM 
14911042SErik.Nordmark@Sun.COM 		/* Make sure ira_l2src is setup if not already */
15011042SErik.Nordmark@Sun.COM 		if (!(ira->ira_flags & IRAF_L2SRC_SET)) {
15111042SErik.Nordmark@Sun.COM 			ill = ill_lookup_on_ifindex(ira->ira_rifindex, B_FALSE,
15211042SErik.Nordmark@Sun.COM 			    ipst);
15311042SErik.Nordmark@Sun.COM 			if (ill != NULL) {
15411042SErik.Nordmark@Sun.COM 				ip_setl2src(mp, ira, ill);
15511042SErik.Nordmark@Sun.COM 				ill_refrele(ill);
15611042SErik.Nordmark@Sun.COM 			}
15711042SErik.Nordmark@Sun.COM 		}
15811042SErik.Nordmark@Sun.COM 		ancil_size += sizeof (struct T_opthdr) +
15911042SErik.Nordmark@Sun.COM 		    sizeof (struct sockaddr_dl);
16011042SErik.Nordmark@Sun.COM 		IP_STAT(ipst, conn_in_recvslla);
16111042SErik.Nordmark@Sun.COM 	}
16211042SErik.Nordmark@Sun.COM 
16311042SErik.Nordmark@Sun.COM 	if (recv_ancillary.crb_recvif) {
16411042SErik.Nordmark@Sun.COM 		ancil_size += sizeof (struct T_opthdr) + sizeof (uint_t);
16511042SErik.Nordmark@Sun.COM 		IP_STAT(ipst, conn_in_recvif);
16611042SErik.Nordmark@Sun.COM 	}
16711042SErik.Nordmark@Sun.COM 
16811042SErik.Nordmark@Sun.COM 	/*
16911042SErik.Nordmark@Sun.COM 	 * ip_recvpktinfo is used for both AF_INET and AF_INET6 but
17011042SErik.Nordmark@Sun.COM 	 * are different
17111042SErik.Nordmark@Sun.COM 	 */
17211042SErik.Nordmark@Sun.COM 	if (recv_ancillary.crb_ip_recvpktinfo &&
17311042SErik.Nordmark@Sun.COM 	    connp->conn_family == AF_INET6) {
17411042SErik.Nordmark@Sun.COM 		ancil_size += sizeof (struct T_opthdr) +
17511042SErik.Nordmark@Sun.COM 		    sizeof (struct in6_pktinfo);
17611042SErik.Nordmark@Sun.COM 		IP_STAT(ipst, conn_in_recvpktinfo);
17711042SErik.Nordmark@Sun.COM 	}
17811042SErik.Nordmark@Sun.COM 
17911042SErik.Nordmark@Sun.COM 	if (recv_ancillary.crb_ipv6_recvhoplimit) {
18011042SErik.Nordmark@Sun.COM 		ancil_size += sizeof (struct T_opthdr) + sizeof (int);
18111042SErik.Nordmark@Sun.COM 		IP_STAT(ipst, conn_in_recvhoplimit);
18211042SErik.Nordmark@Sun.COM 	}
18311042SErik.Nordmark@Sun.COM 
18411042SErik.Nordmark@Sun.COM 	if (recv_ancillary.crb_ipv6_recvtclass) {
18511042SErik.Nordmark@Sun.COM 		ancil_size += sizeof (struct T_opthdr) + sizeof (int);
18611042SErik.Nordmark@Sun.COM 		IP_STAT(ipst, conn_in_recvtclass);
18711042SErik.Nordmark@Sun.COM 	}
18811042SErik.Nordmark@Sun.COM 
18911042SErik.Nordmark@Sun.COM 	if (recv_ancillary.crb_ipv6_recvhopopts &&
19011042SErik.Nordmark@Sun.COM 	    (ipp->ipp_fields & IPPF_HOPOPTS)) {
19111042SErik.Nordmark@Sun.COM 		ancil_size += sizeof (struct T_opthdr) + ipp->ipp_hopoptslen;
19211042SErik.Nordmark@Sun.COM 		IP_STAT(ipst, conn_in_recvhopopts);
19311042SErik.Nordmark@Sun.COM 	}
19411042SErik.Nordmark@Sun.COM 	/*
19511042SErik.Nordmark@Sun.COM 	 * To honor RFC3542 when an application asks for both IPV6_RECVDSTOPTS
19611042SErik.Nordmark@Sun.COM 	 * and IPV6_RECVRTHDR, we pass up the item rthdrdstopts (the destination
19711042SErik.Nordmark@Sun.COM 	 * options that appear before a routing header.
19811042SErik.Nordmark@Sun.COM 	 * We also pass them up if IPV6_RECVRTHDRDSTOPTS is set.
19911042SErik.Nordmark@Sun.COM 	 */
20011042SErik.Nordmark@Sun.COM 	if (ipp->ipp_fields & IPPF_RTHDRDSTOPTS) {
20111042SErik.Nordmark@Sun.COM 		if (recv_ancillary.crb_ipv6_recvrthdrdstopts ||
20211042SErik.Nordmark@Sun.COM 		    (recv_ancillary.crb_ipv6_recvdstopts &&
20311042SErik.Nordmark@Sun.COM 		    recv_ancillary.crb_ipv6_recvrthdr)) {
20411042SErik.Nordmark@Sun.COM 			ancil_size += sizeof (struct T_opthdr) +
20511042SErik.Nordmark@Sun.COM 			    ipp->ipp_rthdrdstoptslen;
20611042SErik.Nordmark@Sun.COM 			IP_STAT(ipst, conn_in_recvrthdrdstopts);
20711042SErik.Nordmark@Sun.COM 		}
20811042SErik.Nordmark@Sun.COM 	}
20911042SErik.Nordmark@Sun.COM 	if ((recv_ancillary.crb_ipv6_recvrthdr) &&
21011042SErik.Nordmark@Sun.COM 	    (ipp->ipp_fields & IPPF_RTHDR)) {
21111042SErik.Nordmark@Sun.COM 		ancil_size += sizeof (struct T_opthdr) + ipp->ipp_rthdrlen;
21211042SErik.Nordmark@Sun.COM 		IP_STAT(ipst, conn_in_recvrthdr);
21311042SErik.Nordmark@Sun.COM 	}
21411042SErik.Nordmark@Sun.COM 	if ((recv_ancillary.crb_ipv6_recvdstopts ||
21511042SErik.Nordmark@Sun.COM 	    recv_ancillary.crb_old_ipv6_recvdstopts) &&
21611042SErik.Nordmark@Sun.COM 	    (ipp->ipp_fields & IPPF_DSTOPTS)) {
21711042SErik.Nordmark@Sun.COM 		ancil_size += sizeof (struct T_opthdr) + ipp->ipp_dstoptslen;
21811042SErik.Nordmark@Sun.COM 		IP_STAT(ipst, conn_in_recvdstopts);
21911042SErik.Nordmark@Sun.COM 	}
22011042SErik.Nordmark@Sun.COM 	if (recv_ancillary.crb_recvucred && ira->ira_cred != NULL) {
22111134SCasper.Dik@Sun.COM 		ancil_size += sizeof (struct T_opthdr) +
22211134SCasper.Dik@Sun.COM 		    ucredminsize(ira->ira_cred);
22311042SErik.Nordmark@Sun.COM 		IP_STAT(ipst, conn_in_recvucred);
22411042SErik.Nordmark@Sun.COM 	}
22511042SErik.Nordmark@Sun.COM 
22611042SErik.Nordmark@Sun.COM 	/*
22711042SErik.Nordmark@Sun.COM 	 * If SO_TIMESTAMP is set allocate the appropriate sized
22811042SErik.Nordmark@Sun.COM 	 * buffer. Since gethrestime() expects a pointer aligned
22911042SErik.Nordmark@Sun.COM 	 * argument, we allocate space necessary for extra
23011042SErik.Nordmark@Sun.COM 	 * alignment (even though it might not be used).
23111042SErik.Nordmark@Sun.COM 	 */
23211042SErik.Nordmark@Sun.COM 	if (recv_ancillary.crb_timestamp) {
23311042SErik.Nordmark@Sun.COM 		ancil_size += sizeof (struct T_opthdr) +
23411042SErik.Nordmark@Sun.COM 		    sizeof (timestruc_t) + _POINTER_ALIGNMENT;
23511042SErik.Nordmark@Sun.COM 		IP_STAT(ipst, conn_in_timestamp);
23611042SErik.Nordmark@Sun.COM 	}
23711042SErik.Nordmark@Sun.COM 
23811042SErik.Nordmark@Sun.COM 	/*
23911042SErik.Nordmark@Sun.COM 	 * If IP_RECVTTL is set allocate the appropriate sized buffer
24011042SErik.Nordmark@Sun.COM 	 */
24111042SErik.Nordmark@Sun.COM 	if (recv_ancillary.crb_recvttl &&
24211042SErik.Nordmark@Sun.COM 	    (ira->ira_flags & IRAF_IS_IPV4)) {
24311042SErik.Nordmark@Sun.COM 		ancil_size += sizeof (struct T_opthdr) + sizeof (uint8_t);
24411042SErik.Nordmark@Sun.COM 		IP_STAT(ipst, conn_in_recvttl);
24511042SErik.Nordmark@Sun.COM 	}
24611042SErik.Nordmark@Sun.COM 
24711042SErik.Nordmark@Sun.COM 	return (ancil_size);
24811042SErik.Nordmark@Sun.COM }
24911042SErik.Nordmark@Sun.COM 
25011042SErik.Nordmark@Sun.COM /*
25111042SErik.Nordmark@Sun.COM  * Lay down the ancillary data items at "ancil_buf".
25211042SErik.Nordmark@Sun.COM  * Assumes caller has used conn_recvancillary_size to allocate a sufficiently
25311042SErik.Nordmark@Sun.COM  * large buffer - ancil_size.
25411042SErik.Nordmark@Sun.COM  */
25511042SErik.Nordmark@Sun.COM void
25611042SErik.Nordmark@Sun.COM conn_recvancillary_add(conn_t *connp, crb_t recv_ancillary,
25711042SErik.Nordmark@Sun.COM     ip_recv_attr_t *ira, ip_pkt_t *ipp, uchar_t *ancil_buf, uint_t ancil_size)
25811042SErik.Nordmark@Sun.COM {
25911042SErik.Nordmark@Sun.COM 	/*
26011042SErik.Nordmark@Sun.COM 	 * Copy in destination address before options to avoid
26111042SErik.Nordmark@Sun.COM 	 * any padding issues.
26211042SErik.Nordmark@Sun.COM 	 */
26311042SErik.Nordmark@Sun.COM 	if (recv_ancillary.crb_recvdstaddr &&
26411042SErik.Nordmark@Sun.COM 	    (ira->ira_flags & IRAF_IS_IPV4)) {
26511042SErik.Nordmark@Sun.COM 		struct T_opthdr *toh;
26611042SErik.Nordmark@Sun.COM 		ipaddr_t *dstptr;
26711042SErik.Nordmark@Sun.COM 
26811042SErik.Nordmark@Sun.COM 		toh = (struct T_opthdr *)ancil_buf;
26911042SErik.Nordmark@Sun.COM 		toh->level = IPPROTO_IP;
27011042SErik.Nordmark@Sun.COM 		toh->name = IP_RECVDSTADDR;
27111042SErik.Nordmark@Sun.COM 		toh->len = sizeof (struct T_opthdr) + sizeof (ipaddr_t);
27211042SErik.Nordmark@Sun.COM 		toh->status = 0;
27311042SErik.Nordmark@Sun.COM 		ancil_buf += sizeof (struct T_opthdr);
27411042SErik.Nordmark@Sun.COM 		dstptr = (ipaddr_t *)ancil_buf;
27511042SErik.Nordmark@Sun.COM 		*dstptr = ipp->ipp_addr_v4;
27611042SErik.Nordmark@Sun.COM 		ancil_buf += sizeof (ipaddr_t);
27711042SErik.Nordmark@Sun.COM 		ancil_size -= toh->len;
27811042SErik.Nordmark@Sun.COM 	}
27911042SErik.Nordmark@Sun.COM 
28011042SErik.Nordmark@Sun.COM 	/*
28111042SErik.Nordmark@Sun.COM 	 * ip_recvpktinfo is used for both AF_INET and AF_INET6 but
28211042SErik.Nordmark@Sun.COM 	 * are different
28311042SErik.Nordmark@Sun.COM 	 */
28411042SErik.Nordmark@Sun.COM 	if (recv_ancillary.crb_ip_recvpktinfo &&
28511042SErik.Nordmark@Sun.COM 	    connp->conn_family == AF_INET) {
28611042SErik.Nordmark@Sun.COM 		ip_stack_t *ipst = connp->conn_netstack->netstack_ip;
28711042SErik.Nordmark@Sun.COM 		struct T_opthdr *toh;
28811042SErik.Nordmark@Sun.COM 		struct in_pktinfo *pktinfop;
28911042SErik.Nordmark@Sun.COM 		ill_t *ill;
29011042SErik.Nordmark@Sun.COM 		ipif_t *ipif;
29111042SErik.Nordmark@Sun.COM 
29211042SErik.Nordmark@Sun.COM 		toh = (struct T_opthdr *)ancil_buf;
29311042SErik.Nordmark@Sun.COM 		toh->level = IPPROTO_IP;
29411042SErik.Nordmark@Sun.COM 		toh->name = IP_PKTINFO;
29511042SErik.Nordmark@Sun.COM 		toh->len = sizeof (struct T_opthdr) + sizeof (*pktinfop);
29611042SErik.Nordmark@Sun.COM 		toh->status = 0;
29711042SErik.Nordmark@Sun.COM 		ancil_buf += sizeof (struct T_opthdr);
29811042SErik.Nordmark@Sun.COM 		pktinfop = (struct in_pktinfo *)ancil_buf;
29911042SErik.Nordmark@Sun.COM 
30011042SErik.Nordmark@Sun.COM 		pktinfop->ipi_ifindex = ira->ira_ruifindex;
30111042SErik.Nordmark@Sun.COM 		pktinfop->ipi_spec_dst.s_addr = INADDR_ANY;
30211042SErik.Nordmark@Sun.COM 
30311042SErik.Nordmark@Sun.COM 		/* Find a good address to report */
30411042SErik.Nordmark@Sun.COM 		ill = ill_lookup_on_ifindex(ira->ira_ruifindex, B_FALSE, ipst);
30511042SErik.Nordmark@Sun.COM 		if (ill != NULL) {
30611042SErik.Nordmark@Sun.COM 			ipif = ipif_good_addr(ill, IPCL_ZONEID(connp));
30711042SErik.Nordmark@Sun.COM 			if (ipif != NULL) {
30811042SErik.Nordmark@Sun.COM 				pktinfop->ipi_spec_dst.s_addr =
30911042SErik.Nordmark@Sun.COM 				    ipif->ipif_lcl_addr;
31011042SErik.Nordmark@Sun.COM 				ipif_refrele(ipif);
31111042SErik.Nordmark@Sun.COM 			}
31211042SErik.Nordmark@Sun.COM 			ill_refrele(ill);
31311042SErik.Nordmark@Sun.COM 		}
31411042SErik.Nordmark@Sun.COM 		pktinfop->ipi_addr.s_addr = ipp->ipp_addr_v4;
31511042SErik.Nordmark@Sun.COM 		ancil_buf += sizeof (struct in_pktinfo);
31611042SErik.Nordmark@Sun.COM 		ancil_size -= toh->len;
31711042SErik.Nordmark@Sun.COM 	}
31811042SErik.Nordmark@Sun.COM 
31911042SErik.Nordmark@Sun.COM 	if ((recv_ancillary.crb_recvopts) &&
32011042SErik.Nordmark@Sun.COM 	    (ipp->ipp_fields & IPPF_IPV4_OPTIONS)) {
32111042SErik.Nordmark@Sun.COM 		struct T_opthdr *toh;
32211042SErik.Nordmark@Sun.COM 
32311042SErik.Nordmark@Sun.COM 		toh = (struct T_opthdr *)ancil_buf;
32411042SErik.Nordmark@Sun.COM 		toh->level = IPPROTO_IP;
32511042SErik.Nordmark@Sun.COM 		toh->name = IP_RECVOPTS;
32611042SErik.Nordmark@Sun.COM 		toh->len = sizeof (struct T_opthdr) + ipp->ipp_ipv4_options_len;
32711042SErik.Nordmark@Sun.COM 		toh->status = 0;
32811042SErik.Nordmark@Sun.COM 		ancil_buf += sizeof (struct T_opthdr);
32911042SErik.Nordmark@Sun.COM 		bcopy(ipp->ipp_ipv4_options, ancil_buf,
33011042SErik.Nordmark@Sun.COM 		    ipp->ipp_ipv4_options_len);
33111042SErik.Nordmark@Sun.COM 		ancil_buf += ipp->ipp_ipv4_options_len;
33211042SErik.Nordmark@Sun.COM 		ancil_size -= toh->len;
33311042SErik.Nordmark@Sun.COM 	}
33411042SErik.Nordmark@Sun.COM 
33511042SErik.Nordmark@Sun.COM 	if (recv_ancillary.crb_recvslla) {
33611042SErik.Nordmark@Sun.COM 		ip_stack_t *ipst = connp->conn_netstack->netstack_ip;
33711042SErik.Nordmark@Sun.COM 		struct T_opthdr *toh;
33811042SErik.Nordmark@Sun.COM 		struct sockaddr_dl *dstptr;
33911042SErik.Nordmark@Sun.COM 		ill_t *ill;
34011042SErik.Nordmark@Sun.COM 		int alen = 0;
34111042SErik.Nordmark@Sun.COM 
34211042SErik.Nordmark@Sun.COM 		ill = ill_lookup_on_ifindex(ira->ira_rifindex, B_FALSE, ipst);
34311042SErik.Nordmark@Sun.COM 		if (ill != NULL)
34411042SErik.Nordmark@Sun.COM 			alen = ill->ill_phys_addr_length;
34511042SErik.Nordmark@Sun.COM 
34611042SErik.Nordmark@Sun.COM 		/*
34711042SErik.Nordmark@Sun.COM 		 * For loopback multicast and broadcast the packet arrives
34811042SErik.Nordmark@Sun.COM 		 * with ira_ruifdex being the physical interface, but
34911042SErik.Nordmark@Sun.COM 		 * ira_l2src is all zero since ip_postfrag_loopback doesn't
35011042SErik.Nordmark@Sun.COM 		 * know our l2src. We don't report the address in that case.
35111042SErik.Nordmark@Sun.COM 		 */
35211042SErik.Nordmark@Sun.COM 		if (ira->ira_flags & IRAF_LOOPBACK)
35311042SErik.Nordmark@Sun.COM 			alen = 0;
35411042SErik.Nordmark@Sun.COM 
35511042SErik.Nordmark@Sun.COM 		toh = (struct T_opthdr *)ancil_buf;
35611042SErik.Nordmark@Sun.COM 		toh->level = IPPROTO_IP;
35711042SErik.Nordmark@Sun.COM 		toh->name = IP_RECVSLLA;
35811042SErik.Nordmark@Sun.COM 		toh->len = sizeof (struct T_opthdr) +
35911042SErik.Nordmark@Sun.COM 		    sizeof (struct sockaddr_dl);
36011042SErik.Nordmark@Sun.COM 		toh->status = 0;
36111042SErik.Nordmark@Sun.COM 		ancil_buf += sizeof (struct T_opthdr);
36211042SErik.Nordmark@Sun.COM 		dstptr = (struct sockaddr_dl *)ancil_buf;
36311042SErik.Nordmark@Sun.COM 		dstptr->sdl_family = AF_LINK;
36411042SErik.Nordmark@Sun.COM 		dstptr->sdl_index = ira->ira_ruifindex;
36511042SErik.Nordmark@Sun.COM 		if (ill != NULL)
36611042SErik.Nordmark@Sun.COM 			dstptr->sdl_type = ill->ill_type;
36711042SErik.Nordmark@Sun.COM 		else
36811042SErik.Nordmark@Sun.COM 			dstptr->sdl_type = 0;
36911042SErik.Nordmark@Sun.COM 		dstptr->sdl_nlen = 0;
37011042SErik.Nordmark@Sun.COM 		dstptr->sdl_alen = alen;
37111042SErik.Nordmark@Sun.COM 		dstptr->sdl_slen = 0;
37211042SErik.Nordmark@Sun.COM 		bcopy(ira->ira_l2src, dstptr->sdl_data, alen);
37311042SErik.Nordmark@Sun.COM 		ancil_buf += sizeof (struct sockaddr_dl);
37411042SErik.Nordmark@Sun.COM 		ancil_size -= toh->len;
37511042SErik.Nordmark@Sun.COM 		if (ill != NULL)
37611042SErik.Nordmark@Sun.COM 			ill_refrele(ill);
37711042SErik.Nordmark@Sun.COM 	}
37811042SErik.Nordmark@Sun.COM 
37911042SErik.Nordmark@Sun.COM 	if (recv_ancillary.crb_recvif) {
38011042SErik.Nordmark@Sun.COM 		struct T_opthdr *toh;
38111042SErik.Nordmark@Sun.COM 		uint_t		*dstptr;
38211042SErik.Nordmark@Sun.COM 
38311042SErik.Nordmark@Sun.COM 		toh = (struct T_opthdr *)ancil_buf;
38411042SErik.Nordmark@Sun.COM 		toh->level = IPPROTO_IP;
38511042SErik.Nordmark@Sun.COM 		toh->name = IP_RECVIF;
38611042SErik.Nordmark@Sun.COM 		toh->len = sizeof (struct T_opthdr) + sizeof (uint_t);
38711042SErik.Nordmark@Sun.COM 		toh->status = 0;
38811042SErik.Nordmark@Sun.COM 		ancil_buf += sizeof (struct T_opthdr);
38911042SErik.Nordmark@Sun.COM 		dstptr = (uint_t *)ancil_buf;
39011042SErik.Nordmark@Sun.COM 		*dstptr = ira->ira_ruifindex;
39111042SErik.Nordmark@Sun.COM 		ancil_buf += sizeof (uint_t);
39211042SErik.Nordmark@Sun.COM 		ancil_size -= toh->len;
39311042SErik.Nordmark@Sun.COM 	}
39411042SErik.Nordmark@Sun.COM 
39511042SErik.Nordmark@Sun.COM 	/*
39611042SErik.Nordmark@Sun.COM 	 * ip_recvpktinfo is used for both AF_INET and AF_INET6 but
39711042SErik.Nordmark@Sun.COM 	 * are different
39811042SErik.Nordmark@Sun.COM 	 */
39911042SErik.Nordmark@Sun.COM 	if (recv_ancillary.crb_ip_recvpktinfo &&
40011042SErik.Nordmark@Sun.COM 	    connp->conn_family == AF_INET6) {
40111042SErik.Nordmark@Sun.COM 		struct T_opthdr *toh;
40211042SErik.Nordmark@Sun.COM 		struct in6_pktinfo *pkti;
40311042SErik.Nordmark@Sun.COM 
40411042SErik.Nordmark@Sun.COM 		toh = (struct T_opthdr *)ancil_buf;
40511042SErik.Nordmark@Sun.COM 		toh->level = IPPROTO_IPV6;
40611042SErik.Nordmark@Sun.COM 		toh->name = IPV6_PKTINFO;
40711042SErik.Nordmark@Sun.COM 		toh->len = sizeof (struct T_opthdr) + sizeof (*pkti);
40811042SErik.Nordmark@Sun.COM 		toh->status = 0;
40911042SErik.Nordmark@Sun.COM 		ancil_buf += sizeof (struct T_opthdr);
41011042SErik.Nordmark@Sun.COM 		pkti = (struct in6_pktinfo *)ancil_buf;
41111042SErik.Nordmark@Sun.COM 		if (ira->ira_flags & IRAF_IS_IPV4) {
41211042SErik.Nordmark@Sun.COM 			IN6_IPADDR_TO_V4MAPPED(ipp->ipp_addr_v4,
41311042SErik.Nordmark@Sun.COM 			    &pkti->ipi6_addr);
41411042SErik.Nordmark@Sun.COM 		} else {
41511042SErik.Nordmark@Sun.COM 			pkti->ipi6_addr = ipp->ipp_addr;
41611042SErik.Nordmark@Sun.COM 		}
41711042SErik.Nordmark@Sun.COM 		pkti->ipi6_ifindex = ira->ira_ruifindex;
41811042SErik.Nordmark@Sun.COM 
41911042SErik.Nordmark@Sun.COM 		ancil_buf += sizeof (*pkti);
42011042SErik.Nordmark@Sun.COM 		ancil_size -= toh->len;
42111042SErik.Nordmark@Sun.COM 	}
42211042SErik.Nordmark@Sun.COM 	if (recv_ancillary.crb_ipv6_recvhoplimit) {
42311042SErik.Nordmark@Sun.COM 		struct T_opthdr *toh;
42411042SErik.Nordmark@Sun.COM 
42511042SErik.Nordmark@Sun.COM 		toh = (struct T_opthdr *)ancil_buf;
42611042SErik.Nordmark@Sun.COM 		toh->level = IPPROTO_IPV6;
42711042SErik.Nordmark@Sun.COM 		toh->name = IPV6_HOPLIMIT;
42811042SErik.Nordmark@Sun.COM 		toh->len = sizeof (struct T_opthdr) + sizeof (uint_t);
42911042SErik.Nordmark@Sun.COM 		toh->status = 0;
43011042SErik.Nordmark@Sun.COM 		ancil_buf += sizeof (struct T_opthdr);
43111042SErik.Nordmark@Sun.COM 		*(uint_t *)ancil_buf = ipp->ipp_hoplimit;
43211042SErik.Nordmark@Sun.COM 		ancil_buf += sizeof (uint_t);
43311042SErik.Nordmark@Sun.COM 		ancil_size -= toh->len;
43411042SErik.Nordmark@Sun.COM 	}
43511042SErik.Nordmark@Sun.COM 	if (recv_ancillary.crb_ipv6_recvtclass) {
43611042SErik.Nordmark@Sun.COM 		struct T_opthdr *toh;
43711042SErik.Nordmark@Sun.COM 
43811042SErik.Nordmark@Sun.COM 		toh = (struct T_opthdr *)ancil_buf;
43911042SErik.Nordmark@Sun.COM 		toh->level = IPPROTO_IPV6;
44011042SErik.Nordmark@Sun.COM 		toh->name = IPV6_TCLASS;
44111042SErik.Nordmark@Sun.COM 		toh->len = sizeof (struct T_opthdr) + sizeof (uint_t);
44211042SErik.Nordmark@Sun.COM 		toh->status = 0;
44311042SErik.Nordmark@Sun.COM 		ancil_buf += sizeof (struct T_opthdr);
44411042SErik.Nordmark@Sun.COM 
44511042SErik.Nordmark@Sun.COM 		if (ira->ira_flags & IRAF_IS_IPV4)
44611042SErik.Nordmark@Sun.COM 			*(uint_t *)ancil_buf = ipp->ipp_type_of_service;
44711042SErik.Nordmark@Sun.COM 		else
44811042SErik.Nordmark@Sun.COM 			*(uint_t *)ancil_buf = ipp->ipp_tclass;
44911042SErik.Nordmark@Sun.COM 		ancil_buf += sizeof (uint_t);
45011042SErik.Nordmark@Sun.COM 		ancil_size -= toh->len;
45111042SErik.Nordmark@Sun.COM 	}
45211042SErik.Nordmark@Sun.COM 	if (recv_ancillary.crb_ipv6_recvhopopts &&
45311042SErik.Nordmark@Sun.COM 	    (ipp->ipp_fields & IPPF_HOPOPTS)) {
45411042SErik.Nordmark@Sun.COM 		struct T_opthdr *toh;
45511042SErik.Nordmark@Sun.COM 
45611042SErik.Nordmark@Sun.COM 		toh = (struct T_opthdr *)ancil_buf;
45711042SErik.Nordmark@Sun.COM 		toh->level = IPPROTO_IPV6;
45811042SErik.Nordmark@Sun.COM 		toh->name = IPV6_HOPOPTS;
45911042SErik.Nordmark@Sun.COM 		toh->len = sizeof (struct T_opthdr) + ipp->ipp_hopoptslen;
46011042SErik.Nordmark@Sun.COM 		toh->status = 0;
46111042SErik.Nordmark@Sun.COM 		ancil_buf += sizeof (struct T_opthdr);
46211042SErik.Nordmark@Sun.COM 		bcopy(ipp->ipp_hopopts, ancil_buf, ipp->ipp_hopoptslen);
46311042SErik.Nordmark@Sun.COM 		ancil_buf += ipp->ipp_hopoptslen;
46411042SErik.Nordmark@Sun.COM 		ancil_size -= toh->len;
46511042SErik.Nordmark@Sun.COM 	}
46611042SErik.Nordmark@Sun.COM 	/*
46711042SErik.Nordmark@Sun.COM 	 * To honor RFC3542 when an application asks for both IPV6_RECVDSTOPTS
46811042SErik.Nordmark@Sun.COM 	 * and IPV6_RECVRTHDR, we pass up the item rthdrdstopts (the destination
46911042SErik.Nordmark@Sun.COM 	 * options that appear before a routing header.
47011042SErik.Nordmark@Sun.COM 	 * We also pass them up if IPV6_RECVRTHDRDSTOPTS is set.
47111042SErik.Nordmark@Sun.COM 	 */
47211042SErik.Nordmark@Sun.COM 	if (ipp->ipp_fields & IPPF_RTHDRDSTOPTS) {
47311042SErik.Nordmark@Sun.COM 		if (recv_ancillary.crb_ipv6_recvrthdrdstopts ||
47411042SErik.Nordmark@Sun.COM 		    (recv_ancillary.crb_ipv6_recvdstopts &&
47511042SErik.Nordmark@Sun.COM 		    recv_ancillary.crb_ipv6_recvrthdr)) {
47611042SErik.Nordmark@Sun.COM 			struct T_opthdr *toh;
47711042SErik.Nordmark@Sun.COM 
47811042SErik.Nordmark@Sun.COM 			toh = (struct T_opthdr *)ancil_buf;
47911042SErik.Nordmark@Sun.COM 			toh->level = IPPROTO_IPV6;
48011042SErik.Nordmark@Sun.COM 			toh->name = IPV6_DSTOPTS;
48111042SErik.Nordmark@Sun.COM 			toh->len = sizeof (struct T_opthdr) +
48211042SErik.Nordmark@Sun.COM 			    ipp->ipp_rthdrdstoptslen;
48311042SErik.Nordmark@Sun.COM 			toh->status = 0;
48411042SErik.Nordmark@Sun.COM 			ancil_buf += sizeof (struct T_opthdr);
48511042SErik.Nordmark@Sun.COM 			bcopy(ipp->ipp_rthdrdstopts, ancil_buf,
48611042SErik.Nordmark@Sun.COM 			    ipp->ipp_rthdrdstoptslen);
48711042SErik.Nordmark@Sun.COM 			ancil_buf += ipp->ipp_rthdrdstoptslen;
48811042SErik.Nordmark@Sun.COM 			ancil_size -= toh->len;
48911042SErik.Nordmark@Sun.COM 		}
49011042SErik.Nordmark@Sun.COM 	}
49111042SErik.Nordmark@Sun.COM 	if (recv_ancillary.crb_ipv6_recvrthdr &&
49211042SErik.Nordmark@Sun.COM 	    (ipp->ipp_fields & IPPF_RTHDR)) {
49311042SErik.Nordmark@Sun.COM 		struct T_opthdr *toh;
49411042SErik.Nordmark@Sun.COM 
49511042SErik.Nordmark@Sun.COM 		toh = (struct T_opthdr *)ancil_buf;
49611042SErik.Nordmark@Sun.COM 		toh->level = IPPROTO_IPV6;
49711042SErik.Nordmark@Sun.COM 		toh->name = IPV6_RTHDR;
49811042SErik.Nordmark@Sun.COM 		toh->len = sizeof (struct T_opthdr) + ipp->ipp_rthdrlen;
49911042SErik.Nordmark@Sun.COM 		toh->status = 0;
50011042SErik.Nordmark@Sun.COM 		ancil_buf += sizeof (struct T_opthdr);
50111042SErik.Nordmark@Sun.COM 		bcopy(ipp->ipp_rthdr, ancil_buf, ipp->ipp_rthdrlen);
50211042SErik.Nordmark@Sun.COM 		ancil_buf += ipp->ipp_rthdrlen;
50311042SErik.Nordmark@Sun.COM 		ancil_size -= toh->len;
50411042SErik.Nordmark@Sun.COM 	}
50511042SErik.Nordmark@Sun.COM 	if ((recv_ancillary.crb_ipv6_recvdstopts ||
50611042SErik.Nordmark@Sun.COM 	    recv_ancillary.crb_old_ipv6_recvdstopts) &&
50711042SErik.Nordmark@Sun.COM 	    (ipp->ipp_fields & IPPF_DSTOPTS)) {
50811042SErik.Nordmark@Sun.COM 		struct T_opthdr *toh;
50911042SErik.Nordmark@Sun.COM 
51011042SErik.Nordmark@Sun.COM 		toh = (struct T_opthdr *)ancil_buf;
51111042SErik.Nordmark@Sun.COM 		toh->level = IPPROTO_IPV6;
51211042SErik.Nordmark@Sun.COM 		toh->name = IPV6_DSTOPTS;
51311042SErik.Nordmark@Sun.COM 		toh->len = sizeof (struct T_opthdr) + ipp->ipp_dstoptslen;
51411042SErik.Nordmark@Sun.COM 		toh->status = 0;
51511042SErik.Nordmark@Sun.COM 		ancil_buf += sizeof (struct T_opthdr);
51611042SErik.Nordmark@Sun.COM 		bcopy(ipp->ipp_dstopts, ancil_buf, ipp->ipp_dstoptslen);
51711042SErik.Nordmark@Sun.COM 		ancil_buf += ipp->ipp_dstoptslen;
51811042SErik.Nordmark@Sun.COM 		ancil_size -= toh->len;
51911042SErik.Nordmark@Sun.COM 	}
52011042SErik.Nordmark@Sun.COM 
52111042SErik.Nordmark@Sun.COM 	if (recv_ancillary.crb_recvucred && ira->ira_cred != NULL) {
52211042SErik.Nordmark@Sun.COM 		struct T_opthdr *toh;
52311042SErik.Nordmark@Sun.COM 		cred_t		*rcr = connp->conn_cred;
52411042SErik.Nordmark@Sun.COM 
52511042SErik.Nordmark@Sun.COM 		toh = (struct T_opthdr *)ancil_buf;
52611042SErik.Nordmark@Sun.COM 		toh->level = SOL_SOCKET;
52711042SErik.Nordmark@Sun.COM 		toh->name = SCM_UCRED;
52811134SCasper.Dik@Sun.COM 		toh->len = sizeof (struct T_opthdr) +
52911134SCasper.Dik@Sun.COM 		    ucredminsize(ira->ira_cred);
53011042SErik.Nordmark@Sun.COM 		toh->status = 0;
53111042SErik.Nordmark@Sun.COM 		(void) cred2ucred(ira->ira_cred, ira->ira_cpid, &toh[1], rcr);
53211042SErik.Nordmark@Sun.COM 		ancil_buf += toh->len;
53311042SErik.Nordmark@Sun.COM 		ancil_size -= toh->len;
53411042SErik.Nordmark@Sun.COM 	}
53511042SErik.Nordmark@Sun.COM 	if (recv_ancillary.crb_timestamp) {
53611042SErik.Nordmark@Sun.COM 		struct	T_opthdr *toh;
53711042SErik.Nordmark@Sun.COM 
53811042SErik.Nordmark@Sun.COM 		toh = (struct T_opthdr *)ancil_buf;
53911042SErik.Nordmark@Sun.COM 		toh->level = SOL_SOCKET;
54011042SErik.Nordmark@Sun.COM 		toh->name = SCM_TIMESTAMP;
54111042SErik.Nordmark@Sun.COM 		toh->len = sizeof (struct T_opthdr) +
54211042SErik.Nordmark@Sun.COM 		    sizeof (timestruc_t) + _POINTER_ALIGNMENT;
54311042SErik.Nordmark@Sun.COM 		toh->status = 0;
54411042SErik.Nordmark@Sun.COM 		ancil_buf += sizeof (struct T_opthdr);
54511042SErik.Nordmark@Sun.COM 		/* Align for gethrestime() */
54611042SErik.Nordmark@Sun.COM 		ancil_buf = (uchar_t *)P2ROUNDUP((intptr_t)ancil_buf,
54711042SErik.Nordmark@Sun.COM 		    sizeof (intptr_t));
54811042SErik.Nordmark@Sun.COM 		gethrestime((timestruc_t *)ancil_buf);
54911042SErik.Nordmark@Sun.COM 		ancil_buf = (uchar_t *)toh + toh->len;
55011042SErik.Nordmark@Sun.COM 		ancil_size -= toh->len;
55111042SErik.Nordmark@Sun.COM 	}
55211042SErik.Nordmark@Sun.COM 
55311042SErik.Nordmark@Sun.COM 	/*
55411042SErik.Nordmark@Sun.COM 	 * CAUTION:
55511042SErik.Nordmark@Sun.COM 	 * Due to aligment issues
55611042SErik.Nordmark@Sun.COM 	 * Processing of IP_RECVTTL option
55711042SErik.Nordmark@Sun.COM 	 * should always be the last. Adding
55811042SErik.Nordmark@Sun.COM 	 * any option processing after this will
55911042SErik.Nordmark@Sun.COM 	 * cause alignment panic.
56011042SErik.Nordmark@Sun.COM 	 */
56111042SErik.Nordmark@Sun.COM 	if (recv_ancillary.crb_recvttl &&
56211042SErik.Nordmark@Sun.COM 	    (ira->ira_flags & IRAF_IS_IPV4)) {
56311042SErik.Nordmark@Sun.COM 		struct	T_opthdr *toh;
56411042SErik.Nordmark@Sun.COM 		uint8_t	*dstptr;
56511042SErik.Nordmark@Sun.COM 
56611042SErik.Nordmark@Sun.COM 		toh = (struct T_opthdr *)ancil_buf;
56711042SErik.Nordmark@Sun.COM 		toh->level = IPPROTO_IP;
56811042SErik.Nordmark@Sun.COM 		toh->name = IP_RECVTTL;
56911042SErik.Nordmark@Sun.COM 		toh->len = sizeof (struct T_opthdr) + sizeof (uint8_t);
57011042SErik.Nordmark@Sun.COM 		toh->status = 0;
57111042SErik.Nordmark@Sun.COM 		ancil_buf += sizeof (struct T_opthdr);
57211042SErik.Nordmark@Sun.COM 		dstptr = (uint8_t *)ancil_buf;
57311042SErik.Nordmark@Sun.COM 		*dstptr = ipp->ipp_hoplimit;
57411042SErik.Nordmark@Sun.COM 		ancil_buf += sizeof (uint8_t);
57511042SErik.Nordmark@Sun.COM 		ancil_size -= toh->len;
57611042SErik.Nordmark@Sun.COM 	}
57711042SErik.Nordmark@Sun.COM 
57811042SErik.Nordmark@Sun.COM 	/* Consumed all of allocated space */
57911042SErik.Nordmark@Sun.COM 	ASSERT(ancil_size == 0);
58011042SErik.Nordmark@Sun.COM 
58111042SErik.Nordmark@Sun.COM }
58211042SErik.Nordmark@Sun.COM 
58311042SErik.Nordmark@Sun.COM /*
58411042SErik.Nordmark@Sun.COM  * This routine retrieves the current status of socket options.
58511042SErik.Nordmark@Sun.COM  * It returns the size of the option retrieved, or -1.
58611042SErik.Nordmark@Sun.COM  */
58711042SErik.Nordmark@Sun.COM int
58811042SErik.Nordmark@Sun.COM conn_opt_get(conn_opt_arg_t *coa, t_scalar_t level, t_scalar_t name,
58911042SErik.Nordmark@Sun.COM     uchar_t *ptr)
59011042SErik.Nordmark@Sun.COM {
59111042SErik.Nordmark@Sun.COM 	int		*i1 = (int *)ptr;
59211042SErik.Nordmark@Sun.COM 	conn_t		*connp = coa->coa_connp;
59311042SErik.Nordmark@Sun.COM 	ip_xmit_attr_t	*ixa = coa->coa_ixa;
59411042SErik.Nordmark@Sun.COM 	ip_pkt_t	*ipp = coa->coa_ipp;
59511042SErik.Nordmark@Sun.COM 	ip_stack_t	*ipst = ixa->ixa_ipst;
59611042SErik.Nordmark@Sun.COM 	uint_t		len;
59711042SErik.Nordmark@Sun.COM 
59811042SErik.Nordmark@Sun.COM 	ASSERT(MUTEX_HELD(&coa->coa_connp->conn_lock));
59911042SErik.Nordmark@Sun.COM 
60011042SErik.Nordmark@Sun.COM 	switch (level) {
60111042SErik.Nordmark@Sun.COM 	case SOL_SOCKET:
60211042SErik.Nordmark@Sun.COM 		switch (name) {
60311042SErik.Nordmark@Sun.COM 		case SO_DEBUG:
60411042SErik.Nordmark@Sun.COM 			*i1 = connp->conn_debug ? SO_DEBUG : 0;
60511042SErik.Nordmark@Sun.COM 			break;	/* goto sizeof (int) option return */
60611042SErik.Nordmark@Sun.COM 		case SO_KEEPALIVE:
60711042SErik.Nordmark@Sun.COM 			*i1 = connp->conn_keepalive ? SO_KEEPALIVE : 0;
60811042SErik.Nordmark@Sun.COM 			break;
60911042SErik.Nordmark@Sun.COM 		case SO_LINGER:	{
61011042SErik.Nordmark@Sun.COM 			struct linger *lgr = (struct linger *)ptr;
61111042SErik.Nordmark@Sun.COM 
61211042SErik.Nordmark@Sun.COM 			lgr->l_onoff = connp->conn_linger ? SO_LINGER : 0;
61311042SErik.Nordmark@Sun.COM 			lgr->l_linger = connp->conn_lingertime;
61411042SErik.Nordmark@Sun.COM 			}
61511042SErik.Nordmark@Sun.COM 			return (sizeof (struct linger));
61611042SErik.Nordmark@Sun.COM 
61711042SErik.Nordmark@Sun.COM 		case SO_OOBINLINE:
61811042SErik.Nordmark@Sun.COM 			*i1 = connp->conn_oobinline ? SO_OOBINLINE : 0;
61911042SErik.Nordmark@Sun.COM 			break;
62011042SErik.Nordmark@Sun.COM 		case SO_REUSEADDR:
62111042SErik.Nordmark@Sun.COM 			*i1 = connp->conn_reuseaddr ? SO_REUSEADDR : 0;
62211042SErik.Nordmark@Sun.COM 			break;	/* goto sizeof (int) option return */
62311042SErik.Nordmark@Sun.COM 		case SO_TYPE:
62411042SErik.Nordmark@Sun.COM 			*i1 = connp->conn_so_type;
62511042SErik.Nordmark@Sun.COM 			break;	/* goto sizeof (int) option return */
62611042SErik.Nordmark@Sun.COM 		case SO_DONTROUTE:
62711042SErik.Nordmark@Sun.COM 			*i1 = (ixa->ixa_flags & IXAF_DONTROUTE) ?
62811042SErik.Nordmark@Sun.COM 			    SO_DONTROUTE : 0;
62911042SErik.Nordmark@Sun.COM 			break;	/* goto sizeof (int) option return */
63011042SErik.Nordmark@Sun.COM 		case SO_USELOOPBACK:
63111042SErik.Nordmark@Sun.COM 			*i1 = connp->conn_useloopback ? SO_USELOOPBACK : 0;
63211042SErik.Nordmark@Sun.COM 			break;	/* goto sizeof (int) option return */
63311042SErik.Nordmark@Sun.COM 		case SO_BROADCAST:
63411042SErik.Nordmark@Sun.COM 			*i1 = connp->conn_broadcast ? SO_BROADCAST : 0;
63511042SErik.Nordmark@Sun.COM 			break;	/* goto sizeof (int) option return */
63611042SErik.Nordmark@Sun.COM 
63711042SErik.Nordmark@Sun.COM 		case SO_SNDBUF:
63811042SErik.Nordmark@Sun.COM 			*i1 = connp->conn_sndbuf;
63911042SErik.Nordmark@Sun.COM 			break;	/* goto sizeof (int) option return */
64011042SErik.Nordmark@Sun.COM 		case SO_RCVBUF:
64111042SErik.Nordmark@Sun.COM 			*i1 = connp->conn_rcvbuf;
64211042SErik.Nordmark@Sun.COM 			break;	/* goto sizeof (int) option return */
64311042SErik.Nordmark@Sun.COM 		case SO_RCVTIMEO:
64411042SErik.Nordmark@Sun.COM 		case SO_SNDTIMEO:
64511042SErik.Nordmark@Sun.COM 			/*
64611042SErik.Nordmark@Sun.COM 			 * Pass these two options in order for third part
64711042SErik.Nordmark@Sun.COM 			 * protocol usage. Here just return directly.
64811042SErik.Nordmark@Sun.COM 			 */
64911042SErik.Nordmark@Sun.COM 			*i1 = 0;
65011042SErik.Nordmark@Sun.COM 			break;
65111042SErik.Nordmark@Sun.COM 		case SO_DGRAM_ERRIND:
65211042SErik.Nordmark@Sun.COM 			*i1 = connp->conn_dgram_errind ? SO_DGRAM_ERRIND : 0;
65311042SErik.Nordmark@Sun.COM 			break;	/* goto sizeof (int) option return */
65411042SErik.Nordmark@Sun.COM 		case SO_RECVUCRED:
65511042SErik.Nordmark@Sun.COM 			*i1 = connp->conn_recv_ancillary.crb_recvucred;
65611042SErik.Nordmark@Sun.COM 			break;	/* goto sizeof (int) option return */
65711042SErik.Nordmark@Sun.COM 		case SO_TIMESTAMP:
65811042SErik.Nordmark@Sun.COM 			*i1 = connp->conn_recv_ancillary.crb_timestamp;
65911042SErik.Nordmark@Sun.COM 			break;	/* goto sizeof (int) option return */
66011042SErik.Nordmark@Sun.COM 		case SO_VRRP:
66111042SErik.Nordmark@Sun.COM 			*i1 = connp->conn_isvrrp;
66211042SErik.Nordmark@Sun.COM 			break;	/* goto sizeof (int) option return */
66311042SErik.Nordmark@Sun.COM 		case SO_ANON_MLP:
66411042SErik.Nordmark@Sun.COM 			*i1 = connp->conn_anon_mlp;
66511042SErik.Nordmark@Sun.COM 			break;	/* goto sizeof (int) option return */
66611042SErik.Nordmark@Sun.COM 		case SO_MAC_EXEMPT:
66711042SErik.Nordmark@Sun.COM 			*i1 = (connp->conn_mac_mode == CONN_MAC_AWARE);
66811042SErik.Nordmark@Sun.COM 			break;	/* goto sizeof (int) option return */
66911042SErik.Nordmark@Sun.COM 		case SO_MAC_IMPLICIT:
67011042SErik.Nordmark@Sun.COM 			*i1 = (connp->conn_mac_mode == CONN_MAC_IMPLICIT);
67111042SErik.Nordmark@Sun.COM 			break;	/* goto sizeof (int) option return */
67211042SErik.Nordmark@Sun.COM 		case SO_ALLZONES:
67311042SErik.Nordmark@Sun.COM 			*i1 = connp->conn_allzones;
67411042SErik.Nordmark@Sun.COM 			break;	/* goto sizeof (int) option return */
67511042SErik.Nordmark@Sun.COM 		case SO_EXCLBIND:
67611042SErik.Nordmark@Sun.COM 			*i1 = connp->conn_exclbind ? SO_EXCLBIND : 0;
67711042SErik.Nordmark@Sun.COM 			break;
67811042SErik.Nordmark@Sun.COM 		case SO_PROTOTYPE:
67911042SErik.Nordmark@Sun.COM 			*i1 = connp->conn_proto;
68011042SErik.Nordmark@Sun.COM 			break;
68111042SErik.Nordmark@Sun.COM 
68211042SErik.Nordmark@Sun.COM 		case SO_DOMAIN:
68311042SErik.Nordmark@Sun.COM 			*i1 = connp->conn_family;
68411042SErik.Nordmark@Sun.COM 			break;
68511042SErik.Nordmark@Sun.COM 		default:
68611042SErik.Nordmark@Sun.COM 			return (-1);
68711042SErik.Nordmark@Sun.COM 		}
68811042SErik.Nordmark@Sun.COM 		break;
68911042SErik.Nordmark@Sun.COM 	case IPPROTO_IP:
69011042SErik.Nordmark@Sun.COM 		if (connp->conn_family != AF_INET)
69111042SErik.Nordmark@Sun.COM 			return (-1);
69211042SErik.Nordmark@Sun.COM 		switch (name) {
69311042SErik.Nordmark@Sun.COM 		case IP_OPTIONS:
69411042SErik.Nordmark@Sun.COM 		case T_IP_OPTIONS:
69511042SErik.Nordmark@Sun.COM 			if (!(ipp->ipp_fields & IPPF_IPV4_OPTIONS))
69611042SErik.Nordmark@Sun.COM 				return (0);
69711042SErik.Nordmark@Sun.COM 
69811042SErik.Nordmark@Sun.COM 			len = ipp->ipp_ipv4_options_len;
69911042SErik.Nordmark@Sun.COM 			if (len > 0) {
70011042SErik.Nordmark@Sun.COM 				bcopy(ipp->ipp_ipv4_options, ptr, len);
70111042SErik.Nordmark@Sun.COM 			}
70211042SErik.Nordmark@Sun.COM 			return (len);
70311042SErik.Nordmark@Sun.COM 
70411042SErik.Nordmark@Sun.COM 		case IP_PKTINFO: {
70511042SErik.Nordmark@Sun.COM 			/*
70611042SErik.Nordmark@Sun.COM 			 * This also handles IP_RECVPKTINFO.
70711042SErik.Nordmark@Sun.COM 			 * IP_PKTINFO and IP_RECVPKTINFO have same value.
70811042SErik.Nordmark@Sun.COM 			 * Differentiation is based on the size of the
70911042SErik.Nordmark@Sun.COM 			 * argument passed in.
71011042SErik.Nordmark@Sun.COM 			 */
71111042SErik.Nordmark@Sun.COM 			struct in_pktinfo *pktinfo;
71211042SErik.Nordmark@Sun.COM 
71311042SErik.Nordmark@Sun.COM #ifdef notdef
71411042SErik.Nordmark@Sun.COM 			/* optcom doesn't provide a length with "get" */
71511042SErik.Nordmark@Sun.COM 			if (inlen == sizeof (int)) {
71611042SErik.Nordmark@Sun.COM 				/* This is IP_RECVPKTINFO option. */
71711042SErik.Nordmark@Sun.COM 				*i1 = connp->conn_recv_ancillary.
71811042SErik.Nordmark@Sun.COM 				    crb_ip_recvpktinfo;
71911042SErik.Nordmark@Sun.COM 				return (sizeof (int));
72011042SErik.Nordmark@Sun.COM 			}
72111042SErik.Nordmark@Sun.COM #endif
72211042SErik.Nordmark@Sun.COM 			/* XXX assumes that caller has room for max size! */
72311042SErik.Nordmark@Sun.COM 
72411042SErik.Nordmark@Sun.COM 			pktinfo = (struct in_pktinfo *)ptr;
72511042SErik.Nordmark@Sun.COM 			pktinfo->ipi_ifindex = ixa->ixa_ifindex;
72611042SErik.Nordmark@Sun.COM 			if (ipp->ipp_fields & IPPF_ADDR)
72711042SErik.Nordmark@Sun.COM 				pktinfo->ipi_spec_dst.s_addr = ipp->ipp_addr_v4;
72811042SErik.Nordmark@Sun.COM 			else
72911042SErik.Nordmark@Sun.COM 				pktinfo->ipi_spec_dst.s_addr = INADDR_ANY;
73011042SErik.Nordmark@Sun.COM 			return (sizeof (struct in_pktinfo));
73111042SErik.Nordmark@Sun.COM 		}
73211042SErik.Nordmark@Sun.COM 		case IP_DONTFRAG:
73311042SErik.Nordmark@Sun.COM 			*i1 = (ixa->ixa_flags & IXAF_DONTFRAG) != 0;
73411042SErik.Nordmark@Sun.COM 			return (sizeof (int));
73511042SErik.Nordmark@Sun.COM 		case IP_TOS:
73611042SErik.Nordmark@Sun.COM 		case T_IP_TOS:
73711042SErik.Nordmark@Sun.COM 			*i1 = (int)ipp->ipp_type_of_service;
73811042SErik.Nordmark@Sun.COM 			break;	/* goto sizeof (int) option return */
73911042SErik.Nordmark@Sun.COM 		case IP_TTL:
74011042SErik.Nordmark@Sun.COM 			*i1 = (int)ipp->ipp_unicast_hops;
74111042SErik.Nordmark@Sun.COM 			break;	/* goto sizeof (int) option return */
74211042SErik.Nordmark@Sun.COM 		case IP_DHCPINIT_IF:
74311042SErik.Nordmark@Sun.COM 			return (-1);
74411042SErik.Nordmark@Sun.COM 		case IP_NEXTHOP:
74511042SErik.Nordmark@Sun.COM 			if (ixa->ixa_flags & IXAF_NEXTHOP_SET) {
74611042SErik.Nordmark@Sun.COM 				*(ipaddr_t *)ptr = ixa->ixa_nexthop_v4;
74711042SErik.Nordmark@Sun.COM 				return (sizeof (ipaddr_t));
74811042SErik.Nordmark@Sun.COM 			} else {
74911042SErik.Nordmark@Sun.COM 				return (0);
75011042SErik.Nordmark@Sun.COM 			}
75111042SErik.Nordmark@Sun.COM 
75211042SErik.Nordmark@Sun.COM 		case IP_MULTICAST_IF:
75311042SErik.Nordmark@Sun.COM 			/* 0 address if not set */
75411042SErik.Nordmark@Sun.COM 			*(ipaddr_t *)ptr = ixa->ixa_multicast_ifaddr;
75511042SErik.Nordmark@Sun.COM 			return (sizeof (ipaddr_t));
75611042SErik.Nordmark@Sun.COM 		case IP_MULTICAST_TTL:
75711042SErik.Nordmark@Sun.COM 			*(uchar_t *)ptr = ixa->ixa_multicast_ttl;
75811042SErik.Nordmark@Sun.COM 			return (sizeof (uchar_t));
75911042SErik.Nordmark@Sun.COM 		case IP_MULTICAST_LOOP:
76011042SErik.Nordmark@Sun.COM 			*ptr = (ixa->ixa_flags & IXAF_MULTICAST_LOOP) ? 1 : 0;
76111042SErik.Nordmark@Sun.COM 			return (sizeof (uint8_t));
76211042SErik.Nordmark@Sun.COM 		case IP_RECVOPTS:
76311042SErik.Nordmark@Sun.COM 			*i1 = connp->conn_recv_ancillary.crb_recvopts;
76411042SErik.Nordmark@Sun.COM 			break;	/* goto sizeof (int) option return */
76511042SErik.Nordmark@Sun.COM 		case IP_RECVDSTADDR:
76611042SErik.Nordmark@Sun.COM 			*i1 = connp->conn_recv_ancillary.crb_recvdstaddr;
76711042SErik.Nordmark@Sun.COM 			break;	/* goto sizeof (int) option return */
76811042SErik.Nordmark@Sun.COM 		case IP_RECVIF:
76911042SErik.Nordmark@Sun.COM 			*i1 = connp->conn_recv_ancillary.crb_recvif;
77011042SErik.Nordmark@Sun.COM 			break;	/* goto sizeof (int) option return */
77111042SErik.Nordmark@Sun.COM 		case IP_RECVSLLA:
77211042SErik.Nordmark@Sun.COM 			*i1 = connp->conn_recv_ancillary.crb_recvslla;
77311042SErik.Nordmark@Sun.COM 			break;	/* goto sizeof (int) option return */
77411042SErik.Nordmark@Sun.COM 		case IP_RECVTTL:
77511042SErik.Nordmark@Sun.COM 			*i1 = connp->conn_recv_ancillary.crb_recvttl;
77611042SErik.Nordmark@Sun.COM 			break;	/* goto sizeof (int) option return */
77711042SErik.Nordmark@Sun.COM 		case IP_ADD_MEMBERSHIP:
77811042SErik.Nordmark@Sun.COM 		case IP_DROP_MEMBERSHIP:
77911042SErik.Nordmark@Sun.COM 		case MCAST_JOIN_GROUP:
78011042SErik.Nordmark@Sun.COM 		case MCAST_LEAVE_GROUP:
78111042SErik.Nordmark@Sun.COM 		case IP_BLOCK_SOURCE:
78211042SErik.Nordmark@Sun.COM 		case IP_UNBLOCK_SOURCE:
78311042SErik.Nordmark@Sun.COM 		case IP_ADD_SOURCE_MEMBERSHIP:
78411042SErik.Nordmark@Sun.COM 		case IP_DROP_SOURCE_MEMBERSHIP:
78511042SErik.Nordmark@Sun.COM 		case MCAST_BLOCK_SOURCE:
78611042SErik.Nordmark@Sun.COM 		case MCAST_UNBLOCK_SOURCE:
78711042SErik.Nordmark@Sun.COM 		case MCAST_JOIN_SOURCE_GROUP:
78811042SErik.Nordmark@Sun.COM 		case MCAST_LEAVE_SOURCE_GROUP:
78911042SErik.Nordmark@Sun.COM 		case MRT_INIT:
79011042SErik.Nordmark@Sun.COM 		case MRT_DONE:
79111042SErik.Nordmark@Sun.COM 		case MRT_ADD_VIF:
79211042SErik.Nordmark@Sun.COM 		case MRT_DEL_VIF:
79311042SErik.Nordmark@Sun.COM 		case MRT_ADD_MFC:
79411042SErik.Nordmark@Sun.COM 		case MRT_DEL_MFC:
79511042SErik.Nordmark@Sun.COM 			/* cannot "get" the value for these */
79611042SErik.Nordmark@Sun.COM 			return (-1);
79711042SErik.Nordmark@Sun.COM 		case MRT_VERSION:
79811042SErik.Nordmark@Sun.COM 		case MRT_ASSERT:
79911042SErik.Nordmark@Sun.COM 			(void) ip_mrouter_get(name, connp, ptr);
80011042SErik.Nordmark@Sun.COM 			return (sizeof (int));
80111042SErik.Nordmark@Sun.COM 		case IP_SEC_OPT:
80211042SErik.Nordmark@Sun.COM 			return (ipsec_req_from_conn(connp, (ipsec_req_t	*)ptr,
80311042SErik.Nordmark@Sun.COM 			    IPSEC_AF_V4));
80411042SErik.Nordmark@Sun.COM 		case IP_BOUND_IF:
80511042SErik.Nordmark@Sun.COM 			/* Zero if not set */
80611042SErik.Nordmark@Sun.COM 			*i1 = connp->conn_bound_if;
80711042SErik.Nordmark@Sun.COM 			break;	/* goto sizeof (int) option return */
80811042SErik.Nordmark@Sun.COM 		case IP_UNSPEC_SRC:
80911042SErik.Nordmark@Sun.COM 			*i1 = connp->conn_unspec_src;
81011042SErik.Nordmark@Sun.COM 			break;	/* goto sizeof (int) option return */
81111042SErik.Nordmark@Sun.COM 		case IP_BROADCAST_TTL:
81211042SErik.Nordmark@Sun.COM 			if (ixa->ixa_flags & IXAF_BROADCAST_TTL_SET)
81311042SErik.Nordmark@Sun.COM 				*(uchar_t *)ptr = ixa->ixa_broadcast_ttl;
81411042SErik.Nordmark@Sun.COM 			else
81511042SErik.Nordmark@Sun.COM 				*(uchar_t *)ptr = ipst->ips_ip_broadcast_ttl;
81611042SErik.Nordmark@Sun.COM 			return (sizeof (uchar_t));
81711042SErik.Nordmark@Sun.COM 		default:
81811042SErik.Nordmark@Sun.COM 			return (-1);
81911042SErik.Nordmark@Sun.COM 		}
82011042SErik.Nordmark@Sun.COM 		break;
82111042SErik.Nordmark@Sun.COM 	case IPPROTO_IPV6:
82211042SErik.Nordmark@Sun.COM 		if (connp->conn_family != AF_INET6)
82311042SErik.Nordmark@Sun.COM 			return (-1);
82411042SErik.Nordmark@Sun.COM 		switch (name) {
82511042SErik.Nordmark@Sun.COM 		case IPV6_UNICAST_HOPS:
82611042SErik.Nordmark@Sun.COM 			*i1 = (int)ipp->ipp_unicast_hops;
82711042SErik.Nordmark@Sun.COM 			break;	/* goto sizeof (int) option return */
82811042SErik.Nordmark@Sun.COM 		case IPV6_MULTICAST_IF:
82911042SErik.Nordmark@Sun.COM 			/* 0 index if not set */
83011042SErik.Nordmark@Sun.COM 			*i1 = ixa->ixa_multicast_ifindex;
83111042SErik.Nordmark@Sun.COM 			break;	/* goto sizeof (int) option return */
83211042SErik.Nordmark@Sun.COM 		case IPV6_MULTICAST_HOPS:
83311042SErik.Nordmark@Sun.COM 			*i1 = ixa->ixa_multicast_ttl;
83411042SErik.Nordmark@Sun.COM 			break;	/* goto sizeof (int) option return */
83511042SErik.Nordmark@Sun.COM 		case IPV6_MULTICAST_LOOP:
83611042SErik.Nordmark@Sun.COM 			*i1 = (ixa->ixa_flags & IXAF_MULTICAST_LOOP) ? 1 : 0;
83711042SErik.Nordmark@Sun.COM 			break;	/* goto sizeof (int) option return */
83811042SErik.Nordmark@Sun.COM 		case IPV6_JOIN_GROUP:
83911042SErik.Nordmark@Sun.COM 		case IPV6_LEAVE_GROUP:
84011042SErik.Nordmark@Sun.COM 		case MCAST_JOIN_GROUP:
84111042SErik.Nordmark@Sun.COM 		case MCAST_LEAVE_GROUP:
84211042SErik.Nordmark@Sun.COM 		case MCAST_BLOCK_SOURCE:
84311042SErik.Nordmark@Sun.COM 		case MCAST_UNBLOCK_SOURCE:
84411042SErik.Nordmark@Sun.COM 		case MCAST_JOIN_SOURCE_GROUP:
84511042SErik.Nordmark@Sun.COM 		case MCAST_LEAVE_SOURCE_GROUP:
84611042SErik.Nordmark@Sun.COM 			/* cannot "get" the value for these */
84711042SErik.Nordmark@Sun.COM 			return (-1);
84811042SErik.Nordmark@Sun.COM 		case IPV6_BOUND_IF:
84911042SErik.Nordmark@Sun.COM 			/* Zero if not set */
85011042SErik.Nordmark@Sun.COM 			*i1 = connp->conn_bound_if;
85111042SErik.Nordmark@Sun.COM 			break;	/* goto sizeof (int) option return */
85211042SErik.Nordmark@Sun.COM 		case IPV6_UNSPEC_SRC:
85311042SErik.Nordmark@Sun.COM 			*i1 = connp->conn_unspec_src;
85411042SErik.Nordmark@Sun.COM 			break;	/* goto sizeof (int) option return */
85511042SErik.Nordmark@Sun.COM 		case IPV6_RECVPKTINFO:
85611042SErik.Nordmark@Sun.COM 			*i1 = connp->conn_recv_ancillary.crb_ip_recvpktinfo;
85711042SErik.Nordmark@Sun.COM 			break;	/* goto sizeof (int) option return */
85811042SErik.Nordmark@Sun.COM 		case IPV6_RECVTCLASS:
85911042SErik.Nordmark@Sun.COM 			*i1 = connp->conn_recv_ancillary.crb_ipv6_recvtclass;
86011042SErik.Nordmark@Sun.COM 			break;	/* goto sizeof (int) option return */
86111042SErik.Nordmark@Sun.COM 		case IPV6_RECVPATHMTU:
86211042SErik.Nordmark@Sun.COM 			*i1 = connp->conn_ipv6_recvpathmtu;
86311042SErik.Nordmark@Sun.COM 			break;	/* goto sizeof (int) option return */
86411042SErik.Nordmark@Sun.COM 		case IPV6_RECVHOPLIMIT:
86511042SErik.Nordmark@Sun.COM 			*i1 = connp->conn_recv_ancillary.crb_ipv6_recvhoplimit;
86611042SErik.Nordmark@Sun.COM 			break;	/* goto sizeof (int) option return */
86711042SErik.Nordmark@Sun.COM 		case IPV6_RECVHOPOPTS:
86811042SErik.Nordmark@Sun.COM 			*i1 = connp->conn_recv_ancillary.crb_ipv6_recvhopopts;
86911042SErik.Nordmark@Sun.COM 			break;	/* goto sizeof (int) option return */
87011042SErik.Nordmark@Sun.COM 		case IPV6_RECVDSTOPTS:
87111042SErik.Nordmark@Sun.COM 			*i1 = connp->conn_recv_ancillary.crb_ipv6_recvdstopts;
87211042SErik.Nordmark@Sun.COM 			break;	/* goto sizeof (int) option return */
87311042SErik.Nordmark@Sun.COM 		case _OLD_IPV6_RECVDSTOPTS:
87411042SErik.Nordmark@Sun.COM 			*i1 =
87511042SErik.Nordmark@Sun.COM 			    connp->conn_recv_ancillary.crb_old_ipv6_recvdstopts;
87611042SErik.Nordmark@Sun.COM 			break;	/* goto sizeof (int) option return */
87711042SErik.Nordmark@Sun.COM 		case IPV6_RECVRTHDRDSTOPTS:
87811042SErik.Nordmark@Sun.COM 			*i1 = connp->conn_recv_ancillary.
87911042SErik.Nordmark@Sun.COM 			    crb_ipv6_recvrthdrdstopts;
88011042SErik.Nordmark@Sun.COM 			break;	/* goto sizeof (int) option return */
88111042SErik.Nordmark@Sun.COM 		case IPV6_RECVRTHDR:
88211042SErik.Nordmark@Sun.COM 			*i1 = connp->conn_recv_ancillary.crb_ipv6_recvrthdr;
88311042SErik.Nordmark@Sun.COM 			break;	/* goto sizeof (int) option return */
88411042SErik.Nordmark@Sun.COM 		case IPV6_PKTINFO: {
88511042SErik.Nordmark@Sun.COM 			/* XXX assumes that caller has room for max size! */
88611042SErik.Nordmark@Sun.COM 			struct in6_pktinfo *pkti;
88711042SErik.Nordmark@Sun.COM 
88811042SErik.Nordmark@Sun.COM 			pkti = (struct in6_pktinfo *)ptr;
88911042SErik.Nordmark@Sun.COM 			pkti->ipi6_ifindex = ixa->ixa_ifindex;
89011042SErik.Nordmark@Sun.COM 			if (ipp->ipp_fields & IPPF_ADDR)
89111042SErik.Nordmark@Sun.COM 				pkti->ipi6_addr = ipp->ipp_addr;
89211042SErik.Nordmark@Sun.COM 			else
89311042SErik.Nordmark@Sun.COM 				pkti->ipi6_addr = ipv6_all_zeros;
89411042SErik.Nordmark@Sun.COM 			return (sizeof (struct in6_pktinfo));
89511042SErik.Nordmark@Sun.COM 		}
89611042SErik.Nordmark@Sun.COM 		case IPV6_TCLASS:
89711042SErik.Nordmark@Sun.COM 			*i1 = ipp->ipp_tclass;
89811042SErik.Nordmark@Sun.COM 			break;	/* goto sizeof (int) option return */
89911042SErik.Nordmark@Sun.COM 		case IPV6_NEXTHOP: {
90011042SErik.Nordmark@Sun.COM 			sin6_t *sin6 = (sin6_t *)ptr;
90111042SErik.Nordmark@Sun.COM 
90211042SErik.Nordmark@Sun.COM 			if (ixa->ixa_flags & IXAF_NEXTHOP_SET)
90311042SErik.Nordmark@Sun.COM 				return (0);
90411042SErik.Nordmark@Sun.COM 
90511042SErik.Nordmark@Sun.COM 			*sin6 = sin6_null;
90611042SErik.Nordmark@Sun.COM 			sin6->sin6_family = AF_INET6;
90711042SErik.Nordmark@Sun.COM 			sin6->sin6_addr = ixa->ixa_nexthop_v6;
90811042SErik.Nordmark@Sun.COM 
90911042SErik.Nordmark@Sun.COM 			return (sizeof (sin6_t));
91011042SErik.Nordmark@Sun.COM 		}
91111042SErik.Nordmark@Sun.COM 		case IPV6_HOPOPTS:
91211042SErik.Nordmark@Sun.COM 			if (!(ipp->ipp_fields & IPPF_HOPOPTS))
91311042SErik.Nordmark@Sun.COM 				return (0);
91411042SErik.Nordmark@Sun.COM 			bcopy(ipp->ipp_hopopts, ptr,
91511042SErik.Nordmark@Sun.COM 			    ipp->ipp_hopoptslen);
91611042SErik.Nordmark@Sun.COM 			return (ipp->ipp_hopoptslen);
91711042SErik.Nordmark@Sun.COM 		case IPV6_RTHDRDSTOPTS:
91811042SErik.Nordmark@Sun.COM 			if (!(ipp->ipp_fields & IPPF_RTHDRDSTOPTS))
91911042SErik.Nordmark@Sun.COM 				return (0);
92011042SErik.Nordmark@Sun.COM 			bcopy(ipp->ipp_rthdrdstopts, ptr,
92111042SErik.Nordmark@Sun.COM 			    ipp->ipp_rthdrdstoptslen);
92211042SErik.Nordmark@Sun.COM 			return (ipp->ipp_rthdrdstoptslen);
92311042SErik.Nordmark@Sun.COM 		case IPV6_RTHDR:
92411042SErik.Nordmark@Sun.COM 			if (!(ipp->ipp_fields & IPPF_RTHDR))
92511042SErik.Nordmark@Sun.COM 				return (0);
92611042SErik.Nordmark@Sun.COM 			bcopy(ipp->ipp_rthdr, ptr, ipp->ipp_rthdrlen);
92711042SErik.Nordmark@Sun.COM 			return (ipp->ipp_rthdrlen);
92811042SErik.Nordmark@Sun.COM 		case IPV6_DSTOPTS:
92911042SErik.Nordmark@Sun.COM 			if (!(ipp->ipp_fields & IPPF_DSTOPTS))
93011042SErik.Nordmark@Sun.COM 				return (0);
93111042SErik.Nordmark@Sun.COM 			bcopy(ipp->ipp_dstopts, ptr, ipp->ipp_dstoptslen);
93211042SErik.Nordmark@Sun.COM 			return (ipp->ipp_dstoptslen);
93311042SErik.Nordmark@Sun.COM 		case IPV6_PATHMTU:
93411042SErik.Nordmark@Sun.COM 			return (ip_fill_mtuinfo(connp, ixa,
93511042SErik.Nordmark@Sun.COM 			    (struct ip6_mtuinfo *)ptr));
93611042SErik.Nordmark@Sun.COM 		case IPV6_SEC_OPT:
93711042SErik.Nordmark@Sun.COM 			return (ipsec_req_from_conn(connp, (ipsec_req_t	*)ptr,
93811042SErik.Nordmark@Sun.COM 			    IPSEC_AF_V6));
93911042SErik.Nordmark@Sun.COM 		case IPV6_SRC_PREFERENCES:
94011042SErik.Nordmark@Sun.COM 			return (ip6_get_src_preferences(ixa, (uint32_t *)ptr));
94111042SErik.Nordmark@Sun.COM 		case IPV6_DONTFRAG:
94211042SErik.Nordmark@Sun.COM 			*i1 = (ixa->ixa_flags & IXAF_DONTFRAG) != 0;
94311042SErik.Nordmark@Sun.COM 			return (sizeof (int));
94411042SErik.Nordmark@Sun.COM 		case IPV6_USE_MIN_MTU:
94511042SErik.Nordmark@Sun.COM 			if (ixa->ixa_flags & IXAF_USE_MIN_MTU)
94611042SErik.Nordmark@Sun.COM 				*i1 = ixa->ixa_use_min_mtu;
94711042SErik.Nordmark@Sun.COM 			else
94811042SErik.Nordmark@Sun.COM 				*i1 = IPV6_USE_MIN_MTU_MULTICAST;
94911042SErik.Nordmark@Sun.COM 			break;
95011042SErik.Nordmark@Sun.COM 		case IPV6_V6ONLY:
95111042SErik.Nordmark@Sun.COM 			*i1 = connp->conn_ipv6_v6only;
95211042SErik.Nordmark@Sun.COM 			return (sizeof (int));
95311042SErik.Nordmark@Sun.COM 		default:
95411042SErik.Nordmark@Sun.COM 			return (-1);
95511042SErik.Nordmark@Sun.COM 		}
95611042SErik.Nordmark@Sun.COM 		break;
95711042SErik.Nordmark@Sun.COM 	case IPPROTO_UDP:
95811042SErik.Nordmark@Sun.COM 		switch (name) {
95911042SErik.Nordmark@Sun.COM 		case UDP_ANONPRIVBIND:
96011042SErik.Nordmark@Sun.COM 			*i1 = connp->conn_anon_priv_bind;
96111042SErik.Nordmark@Sun.COM 			break;
96211042SErik.Nordmark@Sun.COM 		case UDP_EXCLBIND:
96311042SErik.Nordmark@Sun.COM 			*i1 = connp->conn_exclbind ? UDP_EXCLBIND : 0;
96411042SErik.Nordmark@Sun.COM 			break;
96511042SErik.Nordmark@Sun.COM 		default:
96611042SErik.Nordmark@Sun.COM 			return (-1);
96711042SErik.Nordmark@Sun.COM 		}
96811042SErik.Nordmark@Sun.COM 		break;
96911042SErik.Nordmark@Sun.COM 	case IPPROTO_TCP:
97011042SErik.Nordmark@Sun.COM 		switch (name) {
97111042SErik.Nordmark@Sun.COM 		case TCP_RECVDSTADDR:
97211042SErik.Nordmark@Sun.COM 			*i1 = connp->conn_recv_ancillary.crb_recvdstaddr;
97311042SErik.Nordmark@Sun.COM 			break;
97411042SErik.Nordmark@Sun.COM 		case TCP_ANONPRIVBIND:
97511042SErik.Nordmark@Sun.COM 			*i1 = connp->conn_anon_priv_bind;
97611042SErik.Nordmark@Sun.COM 			break;
97711042SErik.Nordmark@Sun.COM 		case TCP_EXCLBIND:
97811042SErik.Nordmark@Sun.COM 			*i1 = connp->conn_exclbind ? TCP_EXCLBIND : 0;
97911042SErik.Nordmark@Sun.COM 			break;
98011042SErik.Nordmark@Sun.COM 		default:
98111042SErik.Nordmark@Sun.COM 			return (-1);
98211042SErik.Nordmark@Sun.COM 		}
98311042SErik.Nordmark@Sun.COM 		break;
98411042SErik.Nordmark@Sun.COM 	default:
98511042SErik.Nordmark@Sun.COM 		return (-1);
98611042SErik.Nordmark@Sun.COM 	}
98711042SErik.Nordmark@Sun.COM 	return (sizeof (int));
98811042SErik.Nordmark@Sun.COM }
98911042SErik.Nordmark@Sun.COM 
99011042SErik.Nordmark@Sun.COM static int conn_opt_set_socket(conn_opt_arg_t *coa, t_scalar_t name,
99111042SErik.Nordmark@Sun.COM     uint_t inlen, uchar_t *invalp, boolean_t checkonly, cred_t *cr);
99211042SErik.Nordmark@Sun.COM static int conn_opt_set_ip(conn_opt_arg_t *coa, t_scalar_t name,
99311042SErik.Nordmark@Sun.COM     uint_t inlen, uchar_t *invalp, boolean_t checkonly, cred_t *cr);
99411042SErik.Nordmark@Sun.COM static int conn_opt_set_ipv6(conn_opt_arg_t *coa, t_scalar_t name,
99511042SErik.Nordmark@Sun.COM     uint_t inlen, uchar_t *invalp, boolean_t checkonly, cred_t *cr);
99611042SErik.Nordmark@Sun.COM static int conn_opt_set_udp(conn_opt_arg_t *coa, t_scalar_t name,
99711042SErik.Nordmark@Sun.COM     uint_t inlen, uchar_t *invalp, boolean_t checkonly, cred_t *cr);
99811042SErik.Nordmark@Sun.COM static int conn_opt_set_tcp(conn_opt_arg_t *coa, t_scalar_t name,
99911042SErik.Nordmark@Sun.COM     uint_t inlen, uchar_t *invalp, boolean_t checkonly, cred_t *cr);
100011042SErik.Nordmark@Sun.COM 
100111042SErik.Nordmark@Sun.COM /*
100211042SErik.Nordmark@Sun.COM  * This routine sets the most common socket options including some
100311042SErik.Nordmark@Sun.COM  * that are transport/ULP specific.
100411042SErik.Nordmark@Sun.COM  * It returns errno or zero.
100511042SErik.Nordmark@Sun.COM  *
100611042SErik.Nordmark@Sun.COM  * For fixed length options, there is no sanity check
100711042SErik.Nordmark@Sun.COM  * of passed in length is done. It is assumed *_optcom_req()
100811042SErik.Nordmark@Sun.COM  * routines do the right thing.
100911042SErik.Nordmark@Sun.COM  */
101011042SErik.Nordmark@Sun.COM int
101111042SErik.Nordmark@Sun.COM conn_opt_set(conn_opt_arg_t *coa, t_scalar_t level, t_scalar_t name,
101211042SErik.Nordmark@Sun.COM     uint_t inlen, uchar_t *invalp, boolean_t checkonly, cred_t *cr)
101311042SErik.Nordmark@Sun.COM {
101411042SErik.Nordmark@Sun.COM 	ASSERT(MUTEX_NOT_HELD(&coa->coa_connp->conn_lock));
101511042SErik.Nordmark@Sun.COM 
101611042SErik.Nordmark@Sun.COM 	/* We have different functions for different levels */
101711042SErik.Nordmark@Sun.COM 	switch (level) {
101811042SErik.Nordmark@Sun.COM 	case SOL_SOCKET:
101911042SErik.Nordmark@Sun.COM 		return (conn_opt_set_socket(coa, name, inlen, invalp,
102011042SErik.Nordmark@Sun.COM 		    checkonly, cr));
102111042SErik.Nordmark@Sun.COM 	case IPPROTO_IP:
102211042SErik.Nordmark@Sun.COM 		return (conn_opt_set_ip(coa, name, inlen, invalp,
102311042SErik.Nordmark@Sun.COM 		    checkonly, cr));
102411042SErik.Nordmark@Sun.COM 	case IPPROTO_IPV6:
102511042SErik.Nordmark@Sun.COM 		return (conn_opt_set_ipv6(coa, name, inlen, invalp,
102611042SErik.Nordmark@Sun.COM 		    checkonly, cr));
102711042SErik.Nordmark@Sun.COM 	case IPPROTO_UDP:
102811042SErik.Nordmark@Sun.COM 		return (conn_opt_set_udp(coa, name, inlen, invalp,
102911042SErik.Nordmark@Sun.COM 		    checkonly, cr));
103011042SErik.Nordmark@Sun.COM 	case IPPROTO_TCP:
103111042SErik.Nordmark@Sun.COM 		return (conn_opt_set_tcp(coa, name, inlen, invalp,
103211042SErik.Nordmark@Sun.COM 		    checkonly, cr));
103311042SErik.Nordmark@Sun.COM 	default:
103411042SErik.Nordmark@Sun.COM 		return (0);
103511042SErik.Nordmark@Sun.COM 	}
103611042SErik.Nordmark@Sun.COM }
103711042SErik.Nordmark@Sun.COM 
103811042SErik.Nordmark@Sun.COM /*
103911042SErik.Nordmark@Sun.COM  * Handle SOL_SOCKET
104011042SErik.Nordmark@Sun.COM  * Note that we do not handle SO_PROTOTYPE here. The ULPs that support
104111042SErik.Nordmark@Sun.COM  * it implement their own checks and setting of conn_proto.
104211042SErik.Nordmark@Sun.COM  */
104311042SErik.Nordmark@Sun.COM /* ARGSUSED1 */
104411042SErik.Nordmark@Sun.COM static int
104511042SErik.Nordmark@Sun.COM conn_opt_set_socket(conn_opt_arg_t *coa, t_scalar_t name, uint_t inlen,
104611042SErik.Nordmark@Sun.COM     uchar_t *invalp, boolean_t checkonly, cred_t *cr)
104711042SErik.Nordmark@Sun.COM {
104811042SErik.Nordmark@Sun.COM 	conn_t		*connp = coa->coa_connp;
104911042SErik.Nordmark@Sun.COM 	ip_xmit_attr_t	*ixa = coa->coa_ixa;
105011042SErik.Nordmark@Sun.COM 	int		*i1 = (int *)invalp;
105111042SErik.Nordmark@Sun.COM 	boolean_t	onoff = (*i1 == 0) ? 0 : 1;
105211042SErik.Nordmark@Sun.COM 
105311042SErik.Nordmark@Sun.COM 	switch (name) {
105411042SErik.Nordmark@Sun.COM 	case SO_ALLZONES:
105511042SErik.Nordmark@Sun.COM 		if (IPCL_IS_BOUND(connp))
105611042SErik.Nordmark@Sun.COM 			return (EINVAL);
105711042SErik.Nordmark@Sun.COM 		break;
105811042SErik.Nordmark@Sun.COM 	case SO_VRRP:
105911042SErik.Nordmark@Sun.COM 		if (secpolicy_ip_config(cr, checkonly) != 0)
106011042SErik.Nordmark@Sun.COM 			return (EACCES);
106111042SErik.Nordmark@Sun.COM 		break;
106211042SErik.Nordmark@Sun.COM 	case SO_MAC_EXEMPT:
106311042SErik.Nordmark@Sun.COM 		if (secpolicy_net_mac_aware(cr) != 0)
106411042SErik.Nordmark@Sun.COM 			return (EACCES);
106511042SErik.Nordmark@Sun.COM 		if (IPCL_IS_BOUND(connp))
106611042SErik.Nordmark@Sun.COM 			return (EINVAL);
106711042SErik.Nordmark@Sun.COM 		break;
106811042SErik.Nordmark@Sun.COM 	case SO_MAC_IMPLICIT:
106911042SErik.Nordmark@Sun.COM 		if (secpolicy_net_mac_implicit(cr) != 0)
107011042SErik.Nordmark@Sun.COM 			return (EACCES);
107111042SErik.Nordmark@Sun.COM 		break;
107211042SErik.Nordmark@Sun.COM 	}
107311042SErik.Nordmark@Sun.COM 	if (checkonly)
107411042SErik.Nordmark@Sun.COM 		return (0);
107511042SErik.Nordmark@Sun.COM 
107611042SErik.Nordmark@Sun.COM 	mutex_enter(&connp->conn_lock);
107711042SErik.Nordmark@Sun.COM 	/* Here we set the actual option value */
107811042SErik.Nordmark@Sun.COM 	switch (name) {
107911042SErik.Nordmark@Sun.COM 	case SO_DEBUG:
108011042SErik.Nordmark@Sun.COM 		connp->conn_debug = onoff;
108111042SErik.Nordmark@Sun.COM 		break;
108211042SErik.Nordmark@Sun.COM 	case SO_KEEPALIVE:
108311042SErik.Nordmark@Sun.COM 		connp->conn_keepalive = onoff;
108411042SErik.Nordmark@Sun.COM 		break;
108511042SErik.Nordmark@Sun.COM 	case SO_LINGER: {
108611042SErik.Nordmark@Sun.COM 		struct linger *lgr = (struct linger *)invalp;
108711042SErik.Nordmark@Sun.COM 
108811042SErik.Nordmark@Sun.COM 		if (lgr->l_onoff) {
108911042SErik.Nordmark@Sun.COM 			connp->conn_linger = 1;
109011042SErik.Nordmark@Sun.COM 			connp->conn_lingertime = lgr->l_linger;
109111042SErik.Nordmark@Sun.COM 		} else {
109211042SErik.Nordmark@Sun.COM 			connp->conn_linger = 0;
109311042SErik.Nordmark@Sun.COM 			connp->conn_lingertime = 0;
109411042SErik.Nordmark@Sun.COM 		}
109511042SErik.Nordmark@Sun.COM 		break;
109611042SErik.Nordmark@Sun.COM 	}
109711042SErik.Nordmark@Sun.COM 	case SO_OOBINLINE:
109811042SErik.Nordmark@Sun.COM 		connp->conn_oobinline = onoff;
109911042SErik.Nordmark@Sun.COM 		coa->coa_changed |= COA_OOBINLINE_CHANGED;
110011042SErik.Nordmark@Sun.COM 		break;
110111042SErik.Nordmark@Sun.COM 	case SO_REUSEADDR:
110211042SErik.Nordmark@Sun.COM 		connp->conn_reuseaddr = onoff;
110311042SErik.Nordmark@Sun.COM 		break;
110411042SErik.Nordmark@Sun.COM 	case SO_DONTROUTE:
110511042SErik.Nordmark@Sun.COM 		if (onoff)
110611042SErik.Nordmark@Sun.COM 			ixa->ixa_flags |= IXAF_DONTROUTE;
110711042SErik.Nordmark@Sun.COM 		else
110811042SErik.Nordmark@Sun.COM 			ixa->ixa_flags &= ~IXAF_DONTROUTE;
110911042SErik.Nordmark@Sun.COM 		coa->coa_changed |= COA_ROUTE_CHANGED;
111011042SErik.Nordmark@Sun.COM 		break;
111111042SErik.Nordmark@Sun.COM 	case SO_USELOOPBACK:
111211042SErik.Nordmark@Sun.COM 		connp->conn_useloopback = onoff;
111311042SErik.Nordmark@Sun.COM 		break;
111411042SErik.Nordmark@Sun.COM 	case SO_BROADCAST:
111511042SErik.Nordmark@Sun.COM 		connp->conn_broadcast = onoff;
111611042SErik.Nordmark@Sun.COM 		break;
111711042SErik.Nordmark@Sun.COM 	case SO_SNDBUF:
111811042SErik.Nordmark@Sun.COM 		/* ULP has range checked the value */
111911042SErik.Nordmark@Sun.COM 		connp->conn_sndbuf = *i1;
112011042SErik.Nordmark@Sun.COM 		coa->coa_changed |= COA_SNDBUF_CHANGED;
112111042SErik.Nordmark@Sun.COM 		break;
112211042SErik.Nordmark@Sun.COM 	case SO_RCVBUF:
112311042SErik.Nordmark@Sun.COM 		/* ULP has range checked the value */
112411042SErik.Nordmark@Sun.COM 		connp->conn_rcvbuf = *i1;
112511042SErik.Nordmark@Sun.COM 		coa->coa_changed |= COA_RCVBUF_CHANGED;
112611042SErik.Nordmark@Sun.COM 		break;
112711042SErik.Nordmark@Sun.COM 	case SO_RCVTIMEO:
112811042SErik.Nordmark@Sun.COM 	case SO_SNDTIMEO:
112911042SErik.Nordmark@Sun.COM 		/*
113011042SErik.Nordmark@Sun.COM 		 * Pass these two options in order for third part
113111042SErik.Nordmark@Sun.COM 		 * protocol usage.
113211042SErik.Nordmark@Sun.COM 		 */
113311042SErik.Nordmark@Sun.COM 		break;
113411042SErik.Nordmark@Sun.COM 	case SO_DGRAM_ERRIND:
113511042SErik.Nordmark@Sun.COM 		connp->conn_dgram_errind = onoff;
113611042SErik.Nordmark@Sun.COM 		break;
113711042SErik.Nordmark@Sun.COM 	case SO_RECVUCRED:
113811042SErik.Nordmark@Sun.COM 		connp->conn_recv_ancillary.crb_recvucred = onoff;
113911042SErik.Nordmark@Sun.COM 		break;
114011042SErik.Nordmark@Sun.COM 	case SO_ALLZONES:
114111042SErik.Nordmark@Sun.COM 		connp->conn_allzones = onoff;
114211042SErik.Nordmark@Sun.COM 		coa->coa_changed |= COA_ROUTE_CHANGED;
114311042SErik.Nordmark@Sun.COM 		if (onoff)
114411042SErik.Nordmark@Sun.COM 			ixa->ixa_zoneid = ALL_ZONES;
114511042SErik.Nordmark@Sun.COM 		else
114611042SErik.Nordmark@Sun.COM 			ixa->ixa_zoneid = connp->conn_zoneid;
114711042SErik.Nordmark@Sun.COM 		break;
114811042SErik.Nordmark@Sun.COM 	case SO_TIMESTAMP:
114911042SErik.Nordmark@Sun.COM 		connp->conn_recv_ancillary.crb_timestamp = onoff;
115011042SErik.Nordmark@Sun.COM 		break;
115111042SErik.Nordmark@Sun.COM 	case SO_VRRP:
115211042SErik.Nordmark@Sun.COM 		connp->conn_isvrrp = onoff;
115311042SErik.Nordmark@Sun.COM 		break;
115411042SErik.Nordmark@Sun.COM 	case SO_ANON_MLP:
115511042SErik.Nordmark@Sun.COM 		connp->conn_anon_mlp = onoff;
115611042SErik.Nordmark@Sun.COM 		break;
115711042SErik.Nordmark@Sun.COM 	case SO_MAC_EXEMPT:
115811042SErik.Nordmark@Sun.COM 		connp->conn_mac_mode = onoff ?
115911042SErik.Nordmark@Sun.COM 		    CONN_MAC_AWARE : CONN_MAC_DEFAULT;
116011042SErik.Nordmark@Sun.COM 		break;
116111042SErik.Nordmark@Sun.COM 	case SO_MAC_IMPLICIT:
116211042SErik.Nordmark@Sun.COM 		connp->conn_mac_mode = onoff ?
116311042SErik.Nordmark@Sun.COM 		    CONN_MAC_IMPLICIT : CONN_MAC_DEFAULT;
116411042SErik.Nordmark@Sun.COM 		break;
116511042SErik.Nordmark@Sun.COM 	case SO_EXCLBIND:
116611042SErik.Nordmark@Sun.COM 		connp->conn_exclbind = onoff;
116711042SErik.Nordmark@Sun.COM 		break;
116811042SErik.Nordmark@Sun.COM 	}
116911042SErik.Nordmark@Sun.COM 	mutex_exit(&connp->conn_lock);
117011042SErik.Nordmark@Sun.COM 	return (0);
117111042SErik.Nordmark@Sun.COM }
117211042SErik.Nordmark@Sun.COM 
117311042SErik.Nordmark@Sun.COM /* Handle IPPROTO_IP */
117411042SErik.Nordmark@Sun.COM static int
117511042SErik.Nordmark@Sun.COM conn_opt_set_ip(conn_opt_arg_t *coa, t_scalar_t name, uint_t inlen,
117611042SErik.Nordmark@Sun.COM     uchar_t *invalp, boolean_t checkonly, cred_t *cr)
117711042SErik.Nordmark@Sun.COM {
117811042SErik.Nordmark@Sun.COM 	conn_t		*connp = coa->coa_connp;
117911042SErik.Nordmark@Sun.COM 	ip_xmit_attr_t	*ixa = coa->coa_ixa;
118011042SErik.Nordmark@Sun.COM 	ip_pkt_t	*ipp = coa->coa_ipp;
118111042SErik.Nordmark@Sun.COM 	int		*i1 = (int *)invalp;
118211042SErik.Nordmark@Sun.COM 	boolean_t	onoff = (*i1 == 0) ? 0 : 1;
118311042SErik.Nordmark@Sun.COM 	ipaddr_t	addr = (ipaddr_t)*i1;
118411042SErik.Nordmark@Sun.COM 	uint_t		ifindex;
118511042SErik.Nordmark@Sun.COM 	zoneid_t	zoneid = IPCL_ZONEID(connp);
118611042SErik.Nordmark@Sun.COM 	ipif_t		*ipif;
118711042SErik.Nordmark@Sun.COM 	ip_stack_t	*ipst = connp->conn_netstack->netstack_ip;
118811042SErik.Nordmark@Sun.COM 	int		error;
118911042SErik.Nordmark@Sun.COM 
119011042SErik.Nordmark@Sun.COM 	if (connp->conn_family != AF_INET)
119111042SErik.Nordmark@Sun.COM 		return (EINVAL);
119211042SErik.Nordmark@Sun.COM 
119311042SErik.Nordmark@Sun.COM 	switch (name) {
119411042SErik.Nordmark@Sun.COM 	case IP_TTL:
119511042SErik.Nordmark@Sun.COM 		/* Don't allow zero */
119611042SErik.Nordmark@Sun.COM 		if (*i1 < 1 || *i1 > 255)
119711042SErik.Nordmark@Sun.COM 			return (EINVAL);
119811042SErik.Nordmark@Sun.COM 		break;
119911042SErik.Nordmark@Sun.COM 	case IP_MULTICAST_IF:
120011042SErik.Nordmark@Sun.COM 		if (addr == INADDR_ANY) {
120111042SErik.Nordmark@Sun.COM 			/* Clear */
120211042SErik.Nordmark@Sun.COM 			ifindex = 0;
120311042SErik.Nordmark@Sun.COM 			break;
120411042SErik.Nordmark@Sun.COM 		}
120511042SErik.Nordmark@Sun.COM 		ipif = ipif_lookup_addr(addr, NULL, zoneid, ipst);
120611042SErik.Nordmark@Sun.COM 		if (ipif == NULL)
120711042SErik.Nordmark@Sun.COM 			return (EHOSTUNREACH);
120811042SErik.Nordmark@Sun.COM 		/* not supported by the virtual network iface */
120911042SErik.Nordmark@Sun.COM 		if (IS_VNI(ipif->ipif_ill)) {
121011042SErik.Nordmark@Sun.COM 			ipif_refrele(ipif);
121111042SErik.Nordmark@Sun.COM 			return (EINVAL);
121211042SErik.Nordmark@Sun.COM 		}
121311042SErik.Nordmark@Sun.COM 		ifindex = ipif->ipif_ill->ill_phyint->phyint_ifindex;
121411042SErik.Nordmark@Sun.COM 		ipif_refrele(ipif);
121511042SErik.Nordmark@Sun.COM 		break;
121611042SErik.Nordmark@Sun.COM 	case IP_NEXTHOP: {
121711042SErik.Nordmark@Sun.COM 		ire_t	*ire;
121811042SErik.Nordmark@Sun.COM 
121911042SErik.Nordmark@Sun.COM 		if (addr == INADDR_ANY) {
122011042SErik.Nordmark@Sun.COM 			/* Clear */
122111042SErik.Nordmark@Sun.COM 			break;
122211042SErik.Nordmark@Sun.COM 		}
122311042SErik.Nordmark@Sun.COM 		/* Verify that the next-hop is on-link */
122411042SErik.Nordmark@Sun.COM 		ire = ire_ftable_lookup_v4(addr, 0, 0, IRE_ONLINK, NULL, zoneid,
122511042SErik.Nordmark@Sun.COM 		    NULL, MATCH_IRE_TYPE, 0, ipst, NULL);
122611042SErik.Nordmark@Sun.COM 		if (ire == NULL)
122711042SErik.Nordmark@Sun.COM 			return (EHOSTUNREACH);
122811042SErik.Nordmark@Sun.COM 		ire_refrele(ire);
122911042SErik.Nordmark@Sun.COM 		break;
123011042SErik.Nordmark@Sun.COM 	}
123111042SErik.Nordmark@Sun.COM 	case IP_OPTIONS:
123211042SErik.Nordmark@Sun.COM 	case T_IP_OPTIONS: {
123311042SErik.Nordmark@Sun.COM 		uint_t newlen;
123411042SErik.Nordmark@Sun.COM 
123511042SErik.Nordmark@Sun.COM 		if (ipp->ipp_fields & IPPF_LABEL_V4)
123611042SErik.Nordmark@Sun.COM 			newlen = inlen + (ipp->ipp_label_len_v4 + 3) & ~3;
123711042SErik.Nordmark@Sun.COM 		else
123811042SErik.Nordmark@Sun.COM 			newlen = inlen;
123911042SErik.Nordmark@Sun.COM 		if ((inlen & 0x3) || newlen > IP_MAX_OPT_LENGTH) {
124011042SErik.Nordmark@Sun.COM 			return (EINVAL);
124111042SErik.Nordmark@Sun.COM 		}
124211042SErik.Nordmark@Sun.COM 		break;
124311042SErik.Nordmark@Sun.COM 	}
124411042SErik.Nordmark@Sun.COM 	case IP_PKTINFO: {
124511042SErik.Nordmark@Sun.COM 		struct in_pktinfo *pktinfo;
124611042SErik.Nordmark@Sun.COM 
124711042SErik.Nordmark@Sun.COM 		/* Two different valid lengths */
124811042SErik.Nordmark@Sun.COM 		if (inlen != sizeof (int) &&
124911042SErik.Nordmark@Sun.COM 		    inlen != sizeof (struct in_pktinfo))
125011042SErik.Nordmark@Sun.COM 			return (EINVAL);
125111042SErik.Nordmark@Sun.COM 		if (inlen == sizeof (int))
125211042SErik.Nordmark@Sun.COM 			break;
125311042SErik.Nordmark@Sun.COM 
125411042SErik.Nordmark@Sun.COM 		pktinfo = (struct in_pktinfo *)invalp;
125511042SErik.Nordmark@Sun.COM 		if (pktinfo->ipi_spec_dst.s_addr != INADDR_ANY) {
125611042SErik.Nordmark@Sun.COM 			switch (ip_laddr_verify_v4(pktinfo->ipi_spec_dst.s_addr,
125711042SErik.Nordmark@Sun.COM 			    zoneid, ipst, B_FALSE)) {
125811042SErik.Nordmark@Sun.COM 			case IPVL_UNICAST_UP:
125911042SErik.Nordmark@Sun.COM 			case IPVL_UNICAST_DOWN:
126011042SErik.Nordmark@Sun.COM 				break;
126111042SErik.Nordmark@Sun.COM 			default:
126211042SErik.Nordmark@Sun.COM 				return (EADDRNOTAVAIL);
126311042SErik.Nordmark@Sun.COM 			}
126411042SErik.Nordmark@Sun.COM 		}
126511430SErik.Nordmark@Sun.COM 		if (!ip_xmit_ifindex_valid(pktinfo->ipi_ifindex, zoneid,
126611430SErik.Nordmark@Sun.COM 		    B_FALSE, ipst))
126711042SErik.Nordmark@Sun.COM 			return (ENXIO);
126811042SErik.Nordmark@Sun.COM 		break;
126911042SErik.Nordmark@Sun.COM 	}
127011042SErik.Nordmark@Sun.COM 	case IP_BOUND_IF:
127111042SErik.Nordmark@Sun.COM 		ifindex = *(uint_t *)i1;
127211042SErik.Nordmark@Sun.COM 
127311042SErik.Nordmark@Sun.COM 		/* Just check it is ok. */
127411430SErik.Nordmark@Sun.COM 		if (!ip_xmit_ifindex_valid(ifindex, zoneid, B_FALSE, ipst))
127511042SErik.Nordmark@Sun.COM 			return (ENXIO);
127611042SErik.Nordmark@Sun.COM 		break;
127711042SErik.Nordmark@Sun.COM 	}
127811042SErik.Nordmark@Sun.COM 	if (checkonly)
127911042SErik.Nordmark@Sun.COM 		return (0);
128011042SErik.Nordmark@Sun.COM 
128111042SErik.Nordmark@Sun.COM 	/* Here we set the actual option value */
128211042SErik.Nordmark@Sun.COM 	/*
128311042SErik.Nordmark@Sun.COM 	 * conn_lock protects the bitfields, and is used to
128411042SErik.Nordmark@Sun.COM 	 * set the fields atomically. Not needed for ixa settings since
128511042SErik.Nordmark@Sun.COM 	 * the caller has an exclusive copy of the ixa.
128611042SErik.Nordmark@Sun.COM 	 * We can not hold conn_lock across the multicast options though.
128711042SErik.Nordmark@Sun.COM 	 */
128811042SErik.Nordmark@Sun.COM 	switch (name) {
128911042SErik.Nordmark@Sun.COM 	case IP_OPTIONS:
129011042SErik.Nordmark@Sun.COM 	case T_IP_OPTIONS:
129111042SErik.Nordmark@Sun.COM 		/* Save options for use by IP. */
129211042SErik.Nordmark@Sun.COM 		mutex_enter(&connp->conn_lock);
129311042SErik.Nordmark@Sun.COM 		error = optcom_pkt_set(invalp, inlen,
129411042SErik.Nordmark@Sun.COM 		    (uchar_t **)&ipp->ipp_ipv4_options,
129511042SErik.Nordmark@Sun.COM 		    &ipp->ipp_ipv4_options_len);
129611042SErik.Nordmark@Sun.COM 		if (error != 0) {
129711042SErik.Nordmark@Sun.COM 			mutex_exit(&connp->conn_lock);
129811042SErik.Nordmark@Sun.COM 			return (error);
129911042SErik.Nordmark@Sun.COM 		}
130011042SErik.Nordmark@Sun.COM 		if (ipp->ipp_ipv4_options_len == 0) {
130111042SErik.Nordmark@Sun.COM 			ipp->ipp_fields &= ~IPPF_IPV4_OPTIONS;
130211042SErik.Nordmark@Sun.COM 		} else {
130311042SErik.Nordmark@Sun.COM 			ipp->ipp_fields |= IPPF_IPV4_OPTIONS;
130411042SErik.Nordmark@Sun.COM 		}
130511042SErik.Nordmark@Sun.COM 		mutex_exit(&connp->conn_lock);
130611042SErik.Nordmark@Sun.COM 		coa->coa_changed |= COA_HEADER_CHANGED;
130711042SErik.Nordmark@Sun.COM 		coa->coa_changed |= COA_WROFF_CHANGED;
130811042SErik.Nordmark@Sun.COM 		break;
130911042SErik.Nordmark@Sun.COM 
131011042SErik.Nordmark@Sun.COM 	case IP_TTL:
131111042SErik.Nordmark@Sun.COM 		mutex_enter(&connp->conn_lock);
131211042SErik.Nordmark@Sun.COM 		ipp->ipp_unicast_hops = *i1;
131311042SErik.Nordmark@Sun.COM 		mutex_exit(&connp->conn_lock);
131411042SErik.Nordmark@Sun.COM 		coa->coa_changed |= COA_HEADER_CHANGED;
131511042SErik.Nordmark@Sun.COM 		break;
131611042SErik.Nordmark@Sun.COM 	case IP_TOS:
131711042SErik.Nordmark@Sun.COM 	case T_IP_TOS:
131811042SErik.Nordmark@Sun.COM 		mutex_enter(&connp->conn_lock);
131911042SErik.Nordmark@Sun.COM 		if (*i1 == -1) {
132011042SErik.Nordmark@Sun.COM 			ipp->ipp_type_of_service = 0;
132111042SErik.Nordmark@Sun.COM 		} else {
132211042SErik.Nordmark@Sun.COM 			ipp->ipp_type_of_service = *i1;
132311042SErik.Nordmark@Sun.COM 		}
132411042SErik.Nordmark@Sun.COM 		mutex_exit(&connp->conn_lock);
132511042SErik.Nordmark@Sun.COM 		coa->coa_changed |= COA_HEADER_CHANGED;
132611042SErik.Nordmark@Sun.COM 		break;
132711042SErik.Nordmark@Sun.COM 	case IP_MULTICAST_IF:
132811042SErik.Nordmark@Sun.COM 		ixa->ixa_multicast_ifindex = ifindex;
132911042SErik.Nordmark@Sun.COM 		ixa->ixa_multicast_ifaddr = addr;
133011042SErik.Nordmark@Sun.COM 		coa->coa_changed |= COA_ROUTE_CHANGED;
133111042SErik.Nordmark@Sun.COM 		break;
133211042SErik.Nordmark@Sun.COM 	case IP_MULTICAST_TTL:
133311042SErik.Nordmark@Sun.COM 		ixa->ixa_multicast_ttl = *invalp;
133411042SErik.Nordmark@Sun.COM 		/* Handled automatically by ip_output */
133511042SErik.Nordmark@Sun.COM 		break;
133611042SErik.Nordmark@Sun.COM 	case IP_MULTICAST_LOOP:
133711042SErik.Nordmark@Sun.COM 		if (*invalp != 0)
133811042SErik.Nordmark@Sun.COM 			ixa->ixa_flags |= IXAF_MULTICAST_LOOP;
133911042SErik.Nordmark@Sun.COM 		else
134011042SErik.Nordmark@Sun.COM 			ixa->ixa_flags &= ~IXAF_MULTICAST_LOOP;
134111042SErik.Nordmark@Sun.COM 		/* Handled automatically by ip_output */
134211042SErik.Nordmark@Sun.COM 		break;
134311042SErik.Nordmark@Sun.COM 	case IP_RECVOPTS:
134411042SErik.Nordmark@Sun.COM 		mutex_enter(&connp->conn_lock);
134511042SErik.Nordmark@Sun.COM 		connp->conn_recv_ancillary.crb_recvopts = onoff;
134611042SErik.Nordmark@Sun.COM 		mutex_exit(&connp->conn_lock);
134711042SErik.Nordmark@Sun.COM 		break;
134811042SErik.Nordmark@Sun.COM 	case IP_RECVDSTADDR:
134911042SErik.Nordmark@Sun.COM 		mutex_enter(&connp->conn_lock);
135011042SErik.Nordmark@Sun.COM 		connp->conn_recv_ancillary.crb_recvdstaddr = onoff;
135111042SErik.Nordmark@Sun.COM 		mutex_exit(&connp->conn_lock);
135211042SErik.Nordmark@Sun.COM 		break;
135311042SErik.Nordmark@Sun.COM 	case IP_RECVIF:
135411042SErik.Nordmark@Sun.COM 		mutex_enter(&connp->conn_lock);
135511042SErik.Nordmark@Sun.COM 		connp->conn_recv_ancillary.crb_recvif = onoff;
135611042SErik.Nordmark@Sun.COM 		mutex_exit(&connp->conn_lock);
135711042SErik.Nordmark@Sun.COM 		break;
135811042SErik.Nordmark@Sun.COM 	case IP_RECVSLLA:
135911042SErik.Nordmark@Sun.COM 		mutex_enter(&connp->conn_lock);
136011042SErik.Nordmark@Sun.COM 		connp->conn_recv_ancillary.crb_recvslla = onoff;
136111042SErik.Nordmark@Sun.COM 		mutex_exit(&connp->conn_lock);
136211042SErik.Nordmark@Sun.COM 		break;
136311042SErik.Nordmark@Sun.COM 	case IP_RECVTTL:
136411042SErik.Nordmark@Sun.COM 		mutex_enter(&connp->conn_lock);
136511042SErik.Nordmark@Sun.COM 		connp->conn_recv_ancillary.crb_recvttl = onoff;
136611042SErik.Nordmark@Sun.COM 		mutex_exit(&connp->conn_lock);
136711042SErik.Nordmark@Sun.COM 		break;
136811042SErik.Nordmark@Sun.COM 	case IP_PKTINFO: {
136911042SErik.Nordmark@Sun.COM 		/*
137011042SErik.Nordmark@Sun.COM 		 * This also handles IP_RECVPKTINFO.
137111042SErik.Nordmark@Sun.COM 		 * IP_PKTINFO and IP_RECVPKTINFO have same value.
137211042SErik.Nordmark@Sun.COM 		 * Differentiation is based on the size of the
137311042SErik.Nordmark@Sun.COM 		 * argument passed in.
137411042SErik.Nordmark@Sun.COM 		 */
137511042SErik.Nordmark@Sun.COM 		struct in_pktinfo *pktinfo;
137611042SErik.Nordmark@Sun.COM 
137711042SErik.Nordmark@Sun.COM 		if (inlen == sizeof (int)) {
137811042SErik.Nordmark@Sun.COM 			/* This is IP_RECVPKTINFO option. */
137911042SErik.Nordmark@Sun.COM 			mutex_enter(&connp->conn_lock);
138011042SErik.Nordmark@Sun.COM 			connp->conn_recv_ancillary.crb_ip_recvpktinfo =
138111042SErik.Nordmark@Sun.COM 			    onoff;
138211042SErik.Nordmark@Sun.COM 			mutex_exit(&connp->conn_lock);
138311042SErik.Nordmark@Sun.COM 			break;
138411042SErik.Nordmark@Sun.COM 		}
138511042SErik.Nordmark@Sun.COM 
138611042SErik.Nordmark@Sun.COM 		/* This is IP_PKTINFO option. */
138711042SErik.Nordmark@Sun.COM 		mutex_enter(&connp->conn_lock);
138811042SErik.Nordmark@Sun.COM 		pktinfo = (struct in_pktinfo *)invalp;
138911042SErik.Nordmark@Sun.COM 		if (ipp->ipp_addr_v4 != INADDR_ANY) {
139011042SErik.Nordmark@Sun.COM 			ipp->ipp_fields |= IPPF_ADDR;
139111042SErik.Nordmark@Sun.COM 			IN6_INADDR_TO_V4MAPPED(&pktinfo->ipi_spec_dst,
139211042SErik.Nordmark@Sun.COM 			    &ipp->ipp_addr);
139311042SErik.Nordmark@Sun.COM 		} else {
139411042SErik.Nordmark@Sun.COM 			ipp->ipp_fields &= ~IPPF_ADDR;
139511042SErik.Nordmark@Sun.COM 			ipp->ipp_addr = ipv6_all_zeros;
139611042SErik.Nordmark@Sun.COM 		}
139711042SErik.Nordmark@Sun.COM 		mutex_exit(&connp->conn_lock);
139811042SErik.Nordmark@Sun.COM 		ixa->ixa_ifindex = pktinfo->ipi_ifindex;
139911042SErik.Nordmark@Sun.COM 		coa->coa_changed |= COA_ROUTE_CHANGED;
140011042SErik.Nordmark@Sun.COM 		coa->coa_changed |= COA_HEADER_CHANGED;
140111042SErik.Nordmark@Sun.COM 		break;
140211042SErik.Nordmark@Sun.COM 	}
140311042SErik.Nordmark@Sun.COM 	case IP_DONTFRAG:
140411042SErik.Nordmark@Sun.COM 		if (onoff) {
140511042SErik.Nordmark@Sun.COM 			ixa->ixa_flags |= (IXAF_DONTFRAG | IXAF_PMTU_IPV4_DF);
140611042SErik.Nordmark@Sun.COM 			ixa->ixa_flags &= ~IXAF_PMTU_DISCOVERY;
140711042SErik.Nordmark@Sun.COM 		} else {
140811042SErik.Nordmark@Sun.COM 			ixa->ixa_flags &= ~(IXAF_DONTFRAG | IXAF_PMTU_IPV4_DF);
140911042SErik.Nordmark@Sun.COM 			ixa->ixa_flags |= IXAF_PMTU_DISCOVERY;
141011042SErik.Nordmark@Sun.COM 		}
141111042SErik.Nordmark@Sun.COM 		/* Need to redo ip_attr_connect */
141211042SErik.Nordmark@Sun.COM 		coa->coa_changed |= COA_ROUTE_CHANGED;
141311042SErik.Nordmark@Sun.COM 		break;
141411042SErik.Nordmark@Sun.COM 	case IP_ADD_MEMBERSHIP:
141511042SErik.Nordmark@Sun.COM 	case IP_DROP_MEMBERSHIP:
141611042SErik.Nordmark@Sun.COM 	case MCAST_JOIN_GROUP:
141711042SErik.Nordmark@Sun.COM 	case MCAST_LEAVE_GROUP:
141811042SErik.Nordmark@Sun.COM 		return (ip_opt_set_multicast_group(connp, name,
141911042SErik.Nordmark@Sun.COM 		    invalp, B_FALSE, checkonly));
142011042SErik.Nordmark@Sun.COM 
142111042SErik.Nordmark@Sun.COM 	case IP_BLOCK_SOURCE:
142211042SErik.Nordmark@Sun.COM 	case IP_UNBLOCK_SOURCE:
142311042SErik.Nordmark@Sun.COM 	case IP_ADD_SOURCE_MEMBERSHIP:
142411042SErik.Nordmark@Sun.COM 	case IP_DROP_SOURCE_MEMBERSHIP:
142511042SErik.Nordmark@Sun.COM 	case MCAST_BLOCK_SOURCE:
142611042SErik.Nordmark@Sun.COM 	case MCAST_UNBLOCK_SOURCE:
142711042SErik.Nordmark@Sun.COM 	case MCAST_JOIN_SOURCE_GROUP:
142811042SErik.Nordmark@Sun.COM 	case MCAST_LEAVE_SOURCE_GROUP:
142911042SErik.Nordmark@Sun.COM 		return (ip_opt_set_multicast_sources(connp, name,
143011042SErik.Nordmark@Sun.COM 		    invalp, B_FALSE, checkonly));
143111042SErik.Nordmark@Sun.COM 
143211042SErik.Nordmark@Sun.COM 	case IP_SEC_OPT:
143311042SErik.Nordmark@Sun.COM 		mutex_enter(&connp->conn_lock);
143411042SErik.Nordmark@Sun.COM 		error = ipsec_set_req(cr, connp, (ipsec_req_t *)invalp);
143511042SErik.Nordmark@Sun.COM 		mutex_exit(&connp->conn_lock);
143611042SErik.Nordmark@Sun.COM 		if (error != 0) {
143711042SErik.Nordmark@Sun.COM 			return (error);
143811042SErik.Nordmark@Sun.COM 		}
143911042SErik.Nordmark@Sun.COM 		/* This is an IPsec policy change - redo ip_attr_connect */
144011042SErik.Nordmark@Sun.COM 		coa->coa_changed |= COA_ROUTE_CHANGED;
144111042SErik.Nordmark@Sun.COM 		break;
144211042SErik.Nordmark@Sun.COM 	case IP_NEXTHOP:
144311042SErik.Nordmark@Sun.COM 		ixa->ixa_nexthop_v4 = addr;
144411042SErik.Nordmark@Sun.COM 		if (addr != INADDR_ANY)
144511042SErik.Nordmark@Sun.COM 			ixa->ixa_flags |= IXAF_NEXTHOP_SET;
144611042SErik.Nordmark@Sun.COM 		else
144711042SErik.Nordmark@Sun.COM 			ixa->ixa_flags &= ~IXAF_NEXTHOP_SET;
144811042SErik.Nordmark@Sun.COM 		coa->coa_changed |= COA_ROUTE_CHANGED;
144911042SErik.Nordmark@Sun.COM 		break;
145011042SErik.Nordmark@Sun.COM 
145111042SErik.Nordmark@Sun.COM 	case IP_BOUND_IF:
145211042SErik.Nordmark@Sun.COM 		ixa->ixa_ifindex = ifindex;		/* Send */
145311042SErik.Nordmark@Sun.COM 		mutex_enter(&connp->conn_lock);
145411042SErik.Nordmark@Sun.COM 		connp->conn_incoming_ifindex = ifindex;	/* Receive */
145511042SErik.Nordmark@Sun.COM 		connp->conn_bound_if = ifindex;		/* getsockopt */
145611042SErik.Nordmark@Sun.COM 		mutex_exit(&connp->conn_lock);
145711042SErik.Nordmark@Sun.COM 		coa->coa_changed |= COA_ROUTE_CHANGED;
145811042SErik.Nordmark@Sun.COM 		break;
145911042SErik.Nordmark@Sun.COM 	case IP_UNSPEC_SRC:
146011042SErik.Nordmark@Sun.COM 		mutex_enter(&connp->conn_lock);
146111042SErik.Nordmark@Sun.COM 		connp->conn_unspec_src = onoff;
146211042SErik.Nordmark@Sun.COM 		if (onoff)
146311042SErik.Nordmark@Sun.COM 			ixa->ixa_flags &= ~IXAF_VERIFY_SOURCE;
146411042SErik.Nordmark@Sun.COM 		else
146511042SErik.Nordmark@Sun.COM 			ixa->ixa_flags |= IXAF_VERIFY_SOURCE;
146611042SErik.Nordmark@Sun.COM 
146711042SErik.Nordmark@Sun.COM 		mutex_exit(&connp->conn_lock);
146811042SErik.Nordmark@Sun.COM 		break;
146911042SErik.Nordmark@Sun.COM 	case IP_BROADCAST_TTL:
147011042SErik.Nordmark@Sun.COM 		ixa->ixa_broadcast_ttl = *invalp;
147111042SErik.Nordmark@Sun.COM 		ixa->ixa_flags |= IXAF_BROADCAST_TTL_SET;
147211042SErik.Nordmark@Sun.COM 		/* Handled automatically by ip_output */
147311042SErik.Nordmark@Sun.COM 		break;
147411042SErik.Nordmark@Sun.COM 	case MRT_INIT:
147511042SErik.Nordmark@Sun.COM 	case MRT_DONE:
147611042SErik.Nordmark@Sun.COM 	case MRT_ADD_VIF:
147711042SErik.Nordmark@Sun.COM 	case MRT_DEL_VIF:
147811042SErik.Nordmark@Sun.COM 	case MRT_ADD_MFC:
147911042SErik.Nordmark@Sun.COM 	case MRT_DEL_MFC:
148011042SErik.Nordmark@Sun.COM 	case MRT_ASSERT:
148111042SErik.Nordmark@Sun.COM 		if ((error = secpolicy_ip_config(cr, B_FALSE)) != 0) {
148211042SErik.Nordmark@Sun.COM 			return (error);
148311042SErik.Nordmark@Sun.COM 		}
148411042SErik.Nordmark@Sun.COM 		error = ip_mrouter_set((int)name, connp, checkonly,
148511042SErik.Nordmark@Sun.COM 		    (uchar_t *)invalp, inlen);
148611042SErik.Nordmark@Sun.COM 		if (error) {
148711042SErik.Nordmark@Sun.COM 			return (error);
148811042SErik.Nordmark@Sun.COM 		}
148911042SErik.Nordmark@Sun.COM 		return (0);
149011042SErik.Nordmark@Sun.COM 
149111042SErik.Nordmark@Sun.COM 	}
149211042SErik.Nordmark@Sun.COM 	return (0);
149311042SErik.Nordmark@Sun.COM }
149411042SErik.Nordmark@Sun.COM 
149511042SErik.Nordmark@Sun.COM /* Handle IPPROTO_IPV6 */
149611042SErik.Nordmark@Sun.COM static int
149711042SErik.Nordmark@Sun.COM conn_opt_set_ipv6(conn_opt_arg_t *coa, t_scalar_t name, uint_t inlen,
149811042SErik.Nordmark@Sun.COM     uchar_t *invalp, boolean_t checkonly, cred_t *cr)
149911042SErik.Nordmark@Sun.COM {
150011042SErik.Nordmark@Sun.COM 	conn_t		*connp = coa->coa_connp;
150111042SErik.Nordmark@Sun.COM 	ip_xmit_attr_t	*ixa = coa->coa_ixa;
150211042SErik.Nordmark@Sun.COM 	ip_pkt_t	*ipp = coa->coa_ipp;
150311042SErik.Nordmark@Sun.COM 	int		*i1 = (int *)invalp;
150411042SErik.Nordmark@Sun.COM 	boolean_t	onoff = (*i1 == 0) ? 0 : 1;
150511042SErik.Nordmark@Sun.COM 	uint_t		ifindex;
150611042SErik.Nordmark@Sun.COM 	zoneid_t	zoneid = IPCL_ZONEID(connp);
150711042SErik.Nordmark@Sun.COM 	ip_stack_t	*ipst = connp->conn_netstack->netstack_ip;
150811042SErik.Nordmark@Sun.COM 	int		error;
150911042SErik.Nordmark@Sun.COM 
151011042SErik.Nordmark@Sun.COM 	if (connp->conn_family != AF_INET6)
151111042SErik.Nordmark@Sun.COM 		return (EINVAL);
151211042SErik.Nordmark@Sun.COM 
151311042SErik.Nordmark@Sun.COM 	switch (name) {
151411042SErik.Nordmark@Sun.COM 	case IPV6_MULTICAST_IF:
151511042SErik.Nordmark@Sun.COM 		/*
151611042SErik.Nordmark@Sun.COM 		 * The only possible error is EINVAL.
151711042SErik.Nordmark@Sun.COM 		 * We call this option on both V4 and V6
151811042SErik.Nordmark@Sun.COM 		 * If both fail, then this call returns
151911042SErik.Nordmark@Sun.COM 		 * EINVAL. If at least one of them succeeds we
152011042SErik.Nordmark@Sun.COM 		 * return success.
152111042SErik.Nordmark@Sun.COM 		 */
152211042SErik.Nordmark@Sun.COM 		ifindex = *(uint_t *)i1;
152311042SErik.Nordmark@Sun.COM 
152411430SErik.Nordmark@Sun.COM 		if (!ip_xmit_ifindex_valid(ifindex, zoneid, B_TRUE, ipst) &&
152511430SErik.Nordmark@Sun.COM 		    !ip_xmit_ifindex_valid(ifindex, zoneid, B_FALSE, ipst))
152611042SErik.Nordmark@Sun.COM 			return (EINVAL);
152711042SErik.Nordmark@Sun.COM 		break;
152811042SErik.Nordmark@Sun.COM 	case IPV6_UNICAST_HOPS:
152911042SErik.Nordmark@Sun.COM 		/* Don't allow zero. -1 means to use default */
153011042SErik.Nordmark@Sun.COM 		if (*i1 < -1 || *i1 == 0 || *i1 > IPV6_MAX_HOPS)
153111042SErik.Nordmark@Sun.COM 			return (EINVAL);
153211042SErik.Nordmark@Sun.COM 		break;
153311042SErik.Nordmark@Sun.COM 	case IPV6_MULTICAST_HOPS:
153411042SErik.Nordmark@Sun.COM 		/* -1 means use default */
153511042SErik.Nordmark@Sun.COM 		if (*i1 < -1 || *i1 > IPV6_MAX_HOPS)
153611042SErik.Nordmark@Sun.COM 			return (EINVAL);
153711042SErik.Nordmark@Sun.COM 		break;
153811042SErik.Nordmark@Sun.COM 	case IPV6_MULTICAST_LOOP:
153911042SErik.Nordmark@Sun.COM 		if (*i1 != 0 && *i1 != 1)
154011042SErik.Nordmark@Sun.COM 			return (EINVAL);
154111042SErik.Nordmark@Sun.COM 		break;
154211042SErik.Nordmark@Sun.COM 	case IPV6_BOUND_IF:
154311042SErik.Nordmark@Sun.COM 		ifindex = *(uint_t *)i1;
154411042SErik.Nordmark@Sun.COM 
154511430SErik.Nordmark@Sun.COM 		if (!ip_xmit_ifindex_valid(ifindex, zoneid, B_TRUE, ipst))
154611042SErik.Nordmark@Sun.COM 			return (ENXIO);
154711042SErik.Nordmark@Sun.COM 		break;
154811042SErik.Nordmark@Sun.COM 	case IPV6_PKTINFO: {
154911042SErik.Nordmark@Sun.COM 		struct in6_pktinfo *pkti;
155011042SErik.Nordmark@Sun.COM 		boolean_t isv6;
155111042SErik.Nordmark@Sun.COM 
155211042SErik.Nordmark@Sun.COM 		if (inlen != 0 && inlen != sizeof (struct in6_pktinfo))
155311042SErik.Nordmark@Sun.COM 			return (EINVAL);
155411042SErik.Nordmark@Sun.COM 		if (inlen == 0)
155511042SErik.Nordmark@Sun.COM 			break;	/* Clear values below */
155611042SErik.Nordmark@Sun.COM 
155711042SErik.Nordmark@Sun.COM 		/*
155811042SErik.Nordmark@Sun.COM 		 * Verify the source address and ifindex. Privileged users
155911042SErik.Nordmark@Sun.COM 		 * can use any source address.
156011042SErik.Nordmark@Sun.COM 		 */
156111042SErik.Nordmark@Sun.COM 		pkti = (struct in6_pktinfo *)invalp;
156211042SErik.Nordmark@Sun.COM 
156311042SErik.Nordmark@Sun.COM 		/*
156411042SErik.Nordmark@Sun.COM 		 * For link-local addresses we use the ipi6_ifindex when
156511042SErik.Nordmark@Sun.COM 		 * we verify the local address.
156611042SErik.Nordmark@Sun.COM 		 * If net_rawaccess then any source address can be used.
156711042SErik.Nordmark@Sun.COM 		 */
156811042SErik.Nordmark@Sun.COM 		if (!IN6_IS_ADDR_UNSPECIFIED(&pkti->ipi6_addr) &&
156911042SErik.Nordmark@Sun.COM 		    secpolicy_net_rawaccess(cr) != 0) {
157011042SErik.Nordmark@Sun.COM 			uint_t scopeid = 0;
157111042SErik.Nordmark@Sun.COM 			in6_addr_t *v6src = &pkti->ipi6_addr;
157211042SErik.Nordmark@Sun.COM 			ipaddr_t v4src;
157311042SErik.Nordmark@Sun.COM 			ip_laddr_t laddr_type = IPVL_UNICAST_UP;
157411042SErik.Nordmark@Sun.COM 
157511042SErik.Nordmark@Sun.COM 			if (IN6_IS_ADDR_V4MAPPED(v6src)) {
157611042SErik.Nordmark@Sun.COM 				IN6_V4MAPPED_TO_IPADDR(v6src, v4src);
157711042SErik.Nordmark@Sun.COM 				if (v4src != INADDR_ANY) {
157811042SErik.Nordmark@Sun.COM 					laddr_type = ip_laddr_verify_v4(v4src,
157911042SErik.Nordmark@Sun.COM 					    zoneid, ipst, B_FALSE);
158011042SErik.Nordmark@Sun.COM 				}
158111042SErik.Nordmark@Sun.COM 			} else {
158211042SErik.Nordmark@Sun.COM 				if (IN6_IS_ADDR_LINKSCOPE(v6src))
158311042SErik.Nordmark@Sun.COM 					scopeid = pkti->ipi6_ifindex;
158411042SErik.Nordmark@Sun.COM 
158511042SErik.Nordmark@Sun.COM 				laddr_type = ip_laddr_verify_v6(v6src, zoneid,
158611042SErik.Nordmark@Sun.COM 				    ipst, B_FALSE, scopeid);
158711042SErik.Nordmark@Sun.COM 			}
158811042SErik.Nordmark@Sun.COM 			switch (laddr_type) {
158911042SErik.Nordmark@Sun.COM 			case IPVL_UNICAST_UP:
159011042SErik.Nordmark@Sun.COM 			case IPVL_UNICAST_DOWN:
159111042SErik.Nordmark@Sun.COM 				break;
159211042SErik.Nordmark@Sun.COM 			default:
159311042SErik.Nordmark@Sun.COM 				return (EADDRNOTAVAIL);
159411042SErik.Nordmark@Sun.COM 			}
159511042SErik.Nordmark@Sun.COM 			ixa->ixa_flags |= IXAF_VERIFY_SOURCE;
159611042SErik.Nordmark@Sun.COM 		} else if (!IN6_IS_ADDR_UNSPECIFIED(&pkti->ipi6_addr)) {
159711042SErik.Nordmark@Sun.COM 			/* Allow any source */
159811042SErik.Nordmark@Sun.COM 			ixa->ixa_flags &= ~IXAF_VERIFY_SOURCE;
159911042SErik.Nordmark@Sun.COM 		}
160011042SErik.Nordmark@Sun.COM 		isv6 = !(IN6_IS_ADDR_V4MAPPED(&pkti->ipi6_addr));
160111430SErik.Nordmark@Sun.COM 		if (!ip_xmit_ifindex_valid(pkti->ipi6_ifindex, zoneid, isv6,
160211430SErik.Nordmark@Sun.COM 		    ipst))
160311042SErik.Nordmark@Sun.COM 			return (ENXIO);
160411042SErik.Nordmark@Sun.COM 		break;
160511042SErik.Nordmark@Sun.COM 	}
160611042SErik.Nordmark@Sun.COM 	case IPV6_HOPLIMIT:
160711042SErik.Nordmark@Sun.COM 		/* It is only allowed as ancilary data */
160811042SErik.Nordmark@Sun.COM 		if (!coa->coa_ancillary)
160911042SErik.Nordmark@Sun.COM 			return (EINVAL);
161011042SErik.Nordmark@Sun.COM 
161111042SErik.Nordmark@Sun.COM 		if (inlen != 0 && inlen != sizeof (int))
161211042SErik.Nordmark@Sun.COM 			return (EINVAL);
161311042SErik.Nordmark@Sun.COM 		if (inlen == sizeof (int)) {
161411042SErik.Nordmark@Sun.COM 			if (*i1 > 255 || *i1 < -1 || *i1 == 0)
161511042SErik.Nordmark@Sun.COM 				return (EINVAL);
161611042SErik.Nordmark@Sun.COM 		}
161711042SErik.Nordmark@Sun.COM 		break;
161811042SErik.Nordmark@Sun.COM 	case IPV6_TCLASS:
161911042SErik.Nordmark@Sun.COM 		if (inlen != 0 && inlen != sizeof (int))
162011042SErik.Nordmark@Sun.COM 			return (EINVAL);
162111042SErik.Nordmark@Sun.COM 		if (inlen == sizeof (int)) {
162211042SErik.Nordmark@Sun.COM 			if (*i1 > 255 || *i1 < -1)
162311042SErik.Nordmark@Sun.COM 				return (EINVAL);
162411042SErik.Nordmark@Sun.COM 		}
162511042SErik.Nordmark@Sun.COM 		break;
162611042SErik.Nordmark@Sun.COM 	case IPV6_NEXTHOP:
162711042SErik.Nordmark@Sun.COM 		if (inlen != 0 && inlen != sizeof (sin6_t))
162811042SErik.Nordmark@Sun.COM 			return (EINVAL);
162911042SErik.Nordmark@Sun.COM 		if (inlen == sizeof (sin6_t)) {
163011042SErik.Nordmark@Sun.COM 			sin6_t *sin6 = (sin6_t *)invalp;
163111042SErik.Nordmark@Sun.COM 			ire_t	*ire;
163211042SErik.Nordmark@Sun.COM 
163311042SErik.Nordmark@Sun.COM 			if (sin6->sin6_family != AF_INET6)
163411042SErik.Nordmark@Sun.COM 				return (EAFNOSUPPORT);
163511042SErik.Nordmark@Sun.COM 			if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
163611042SErik.Nordmark@Sun.COM 				return (EADDRNOTAVAIL);
163711042SErik.Nordmark@Sun.COM 
163811042SErik.Nordmark@Sun.COM 			/* Verify that the next-hop is on-link */
163911042SErik.Nordmark@Sun.COM 			ire = ire_ftable_lookup_v6(&sin6->sin6_addr,
164011042SErik.Nordmark@Sun.COM 			    0, 0, IRE_ONLINK, NULL, zoneid,
164111042SErik.Nordmark@Sun.COM 			    NULL, MATCH_IRE_TYPE, 0, ipst, NULL);
164211042SErik.Nordmark@Sun.COM 			if (ire == NULL)
164311042SErik.Nordmark@Sun.COM 				return (EHOSTUNREACH);
164411042SErik.Nordmark@Sun.COM 			ire_refrele(ire);
164511042SErik.Nordmark@Sun.COM 			break;
164611042SErik.Nordmark@Sun.COM 		}
164711042SErik.Nordmark@Sun.COM 		break;
164811042SErik.Nordmark@Sun.COM 	case IPV6_RTHDR:
164911042SErik.Nordmark@Sun.COM 	case IPV6_DSTOPTS:
165011042SErik.Nordmark@Sun.COM 	case IPV6_RTHDRDSTOPTS:
165111042SErik.Nordmark@Sun.COM 	case IPV6_HOPOPTS: {
165211042SErik.Nordmark@Sun.COM 		/* All have the length field in the same place */
165311042SErik.Nordmark@Sun.COM 		ip6_hbh_t *hopts = (ip6_hbh_t *)invalp;
165411042SErik.Nordmark@Sun.COM 		/*
165511042SErik.Nordmark@Sun.COM 		 * Sanity checks - minimum size, size a multiple of
165611042SErik.Nordmark@Sun.COM 		 * eight bytes, and matching size passed in.
165711042SErik.Nordmark@Sun.COM 		 */
165811042SErik.Nordmark@Sun.COM 		if (inlen != 0 &&
165911042SErik.Nordmark@Sun.COM 		    inlen != (8 * (hopts->ip6h_len + 1)))
166011042SErik.Nordmark@Sun.COM 			return (EINVAL);
166111042SErik.Nordmark@Sun.COM 		break;
166211042SErik.Nordmark@Sun.COM 	}
166311042SErik.Nordmark@Sun.COM 	case IPV6_PATHMTU:
166411042SErik.Nordmark@Sun.COM 		/* Can't be set */
166511042SErik.Nordmark@Sun.COM 		return (EINVAL);
166611042SErik.Nordmark@Sun.COM 
166711042SErik.Nordmark@Sun.COM 	case IPV6_USE_MIN_MTU:
166811042SErik.Nordmark@Sun.COM 		if (inlen != sizeof (int))
166911042SErik.Nordmark@Sun.COM 			return (EINVAL);
167011042SErik.Nordmark@Sun.COM 		if (*i1 < -1 || *i1 > 1)
167111042SErik.Nordmark@Sun.COM 			return (EINVAL);
167211042SErik.Nordmark@Sun.COM 		break;
167311042SErik.Nordmark@Sun.COM 	case IPV6_SRC_PREFERENCES:
167411042SErik.Nordmark@Sun.COM 		if (inlen != sizeof (uint32_t))
167511042SErik.Nordmark@Sun.COM 			return (EINVAL);
167611042SErik.Nordmark@Sun.COM 		break;
167711042SErik.Nordmark@Sun.COM 	case IPV6_V6ONLY:
167811042SErik.Nordmark@Sun.COM 		if (*i1 < 0 || *i1 > 1) {
167911042SErik.Nordmark@Sun.COM 			return (EINVAL);
168011042SErik.Nordmark@Sun.COM 		}
168111042SErik.Nordmark@Sun.COM 		break;
168211042SErik.Nordmark@Sun.COM 	}
168311042SErik.Nordmark@Sun.COM 	if (checkonly)
168411042SErik.Nordmark@Sun.COM 		return (0);
168511042SErik.Nordmark@Sun.COM 
168611042SErik.Nordmark@Sun.COM 	/* Here we set the actual option value */
168711042SErik.Nordmark@Sun.COM 	/*
168811042SErik.Nordmark@Sun.COM 	 * conn_lock protects the bitfields, and is used to
168911042SErik.Nordmark@Sun.COM 	 * set the fields atomically. Not needed for ixa settings since
169011042SErik.Nordmark@Sun.COM 	 * the caller has an exclusive copy of the ixa.
169111042SErik.Nordmark@Sun.COM 	 * We can not hold conn_lock across the multicast options though.
169211042SErik.Nordmark@Sun.COM 	 */
169311042SErik.Nordmark@Sun.COM 	ASSERT(MUTEX_NOT_HELD(&coa->coa_connp->conn_lock));
169411042SErik.Nordmark@Sun.COM 	switch (name) {
169511042SErik.Nordmark@Sun.COM 	case IPV6_MULTICAST_IF:
169611042SErik.Nordmark@Sun.COM 		ixa->ixa_multicast_ifindex = ifindex;
169711042SErik.Nordmark@Sun.COM 		/* Need to redo ip_attr_connect */
169811042SErik.Nordmark@Sun.COM 		coa->coa_changed |= COA_ROUTE_CHANGED;
169911042SErik.Nordmark@Sun.COM 		break;
170011042SErik.Nordmark@Sun.COM 	case IPV6_UNICAST_HOPS:
170111042SErik.Nordmark@Sun.COM 		/* -1 means use default */
170211042SErik.Nordmark@Sun.COM 		mutex_enter(&connp->conn_lock);
170311042SErik.Nordmark@Sun.COM 		if (*i1 == -1) {
170411042SErik.Nordmark@Sun.COM 			ipp->ipp_unicast_hops = connp->conn_default_ttl;
170511042SErik.Nordmark@Sun.COM 		} else {
170611042SErik.Nordmark@Sun.COM 			ipp->ipp_unicast_hops = (uint8_t)*i1;
170711042SErik.Nordmark@Sun.COM 		}
170811042SErik.Nordmark@Sun.COM 		mutex_exit(&connp->conn_lock);
170911042SErik.Nordmark@Sun.COM 		coa->coa_changed |= COA_HEADER_CHANGED;
171011042SErik.Nordmark@Sun.COM 		break;
171111042SErik.Nordmark@Sun.COM 	case IPV6_MULTICAST_HOPS:
171211042SErik.Nordmark@Sun.COM 		/* -1 means use default */
171311042SErik.Nordmark@Sun.COM 		if (*i1 == -1) {
171411042SErik.Nordmark@Sun.COM 			ixa->ixa_multicast_ttl = IP_DEFAULT_MULTICAST_TTL;
171511042SErik.Nordmark@Sun.COM 		} else {
171611042SErik.Nordmark@Sun.COM 			ixa->ixa_multicast_ttl = (uint8_t)*i1;
171711042SErik.Nordmark@Sun.COM 		}
171811042SErik.Nordmark@Sun.COM 		/* Handled automatically by ip_output */
171911042SErik.Nordmark@Sun.COM 		break;
172011042SErik.Nordmark@Sun.COM 	case IPV6_MULTICAST_LOOP:
172111042SErik.Nordmark@Sun.COM 		if (*i1 != 0)
172211042SErik.Nordmark@Sun.COM 			ixa->ixa_flags |= IXAF_MULTICAST_LOOP;
172311042SErik.Nordmark@Sun.COM 		else
172411042SErik.Nordmark@Sun.COM 			ixa->ixa_flags &= ~IXAF_MULTICAST_LOOP;
172511042SErik.Nordmark@Sun.COM 		/* Handled automatically by ip_output */
172611042SErik.Nordmark@Sun.COM 		break;
172711042SErik.Nordmark@Sun.COM 	case IPV6_JOIN_GROUP:
172811042SErik.Nordmark@Sun.COM 	case IPV6_LEAVE_GROUP:
172911042SErik.Nordmark@Sun.COM 	case MCAST_JOIN_GROUP:
173011042SErik.Nordmark@Sun.COM 	case MCAST_LEAVE_GROUP:
173111042SErik.Nordmark@Sun.COM 		return (ip_opt_set_multicast_group(connp, name,
173211042SErik.Nordmark@Sun.COM 		    invalp, B_TRUE, checkonly));
173311042SErik.Nordmark@Sun.COM 
173411042SErik.Nordmark@Sun.COM 	case MCAST_BLOCK_SOURCE:
173511042SErik.Nordmark@Sun.COM 	case MCAST_UNBLOCK_SOURCE:
173611042SErik.Nordmark@Sun.COM 	case MCAST_JOIN_SOURCE_GROUP:
173711042SErik.Nordmark@Sun.COM 	case MCAST_LEAVE_SOURCE_GROUP:
173811042SErik.Nordmark@Sun.COM 		return (ip_opt_set_multicast_sources(connp, name,
173911042SErik.Nordmark@Sun.COM 		    invalp, B_TRUE, checkonly));
174011042SErik.Nordmark@Sun.COM 
174111042SErik.Nordmark@Sun.COM 	case IPV6_BOUND_IF:
174211042SErik.Nordmark@Sun.COM 		ixa->ixa_ifindex = ifindex;		/* Send */
174311042SErik.Nordmark@Sun.COM 		mutex_enter(&connp->conn_lock);
174411042SErik.Nordmark@Sun.COM 		connp->conn_incoming_ifindex = ifindex;	/* Receive */
174511042SErik.Nordmark@Sun.COM 		connp->conn_bound_if = ifindex;		/* getsockopt */
174611042SErik.Nordmark@Sun.COM 		mutex_exit(&connp->conn_lock);
174711042SErik.Nordmark@Sun.COM 		coa->coa_changed |= COA_ROUTE_CHANGED;
174811042SErik.Nordmark@Sun.COM 		break;
174911042SErik.Nordmark@Sun.COM 	case IPV6_UNSPEC_SRC:
175011042SErik.Nordmark@Sun.COM 		mutex_enter(&connp->conn_lock);
175111042SErik.Nordmark@Sun.COM 		connp->conn_unspec_src = onoff;
175211042SErik.Nordmark@Sun.COM 		if (onoff)
175311042SErik.Nordmark@Sun.COM 			ixa->ixa_flags &= ~IXAF_VERIFY_SOURCE;
175411042SErik.Nordmark@Sun.COM 		else
175511042SErik.Nordmark@Sun.COM 			ixa->ixa_flags |= IXAF_VERIFY_SOURCE;
175611042SErik.Nordmark@Sun.COM 		mutex_exit(&connp->conn_lock);
175711042SErik.Nordmark@Sun.COM 		break;
175811042SErik.Nordmark@Sun.COM 	case IPV6_RECVPKTINFO:
175911042SErik.Nordmark@Sun.COM 		mutex_enter(&connp->conn_lock);
176011042SErik.Nordmark@Sun.COM 		connp->conn_recv_ancillary.crb_ip_recvpktinfo = onoff;
176111042SErik.Nordmark@Sun.COM 		mutex_exit(&connp->conn_lock);
176211042SErik.Nordmark@Sun.COM 		break;
176311042SErik.Nordmark@Sun.COM 	case IPV6_RECVTCLASS:
176411042SErik.Nordmark@Sun.COM 		mutex_enter(&connp->conn_lock);
176511042SErik.Nordmark@Sun.COM 		connp->conn_recv_ancillary.crb_ipv6_recvtclass = onoff;
176611042SErik.Nordmark@Sun.COM 		mutex_exit(&connp->conn_lock);
176711042SErik.Nordmark@Sun.COM 		break;
176811042SErik.Nordmark@Sun.COM 	case IPV6_RECVPATHMTU:
176911042SErik.Nordmark@Sun.COM 		mutex_enter(&connp->conn_lock);
177011042SErik.Nordmark@Sun.COM 		connp->conn_ipv6_recvpathmtu = onoff;
177111042SErik.Nordmark@Sun.COM 		mutex_exit(&connp->conn_lock);
177211042SErik.Nordmark@Sun.COM 		break;
177311042SErik.Nordmark@Sun.COM 	case IPV6_RECVHOPLIMIT:
177411042SErik.Nordmark@Sun.COM 		mutex_enter(&connp->conn_lock);
177511042SErik.Nordmark@Sun.COM 		connp->conn_recv_ancillary.crb_ipv6_recvhoplimit =
177611042SErik.Nordmark@Sun.COM 		    onoff;
177711042SErik.Nordmark@Sun.COM 		mutex_exit(&connp->conn_lock);
177811042SErik.Nordmark@Sun.COM 		break;
177911042SErik.Nordmark@Sun.COM 	case IPV6_RECVHOPOPTS:
178011042SErik.Nordmark@Sun.COM 		mutex_enter(&connp->conn_lock);
178111042SErik.Nordmark@Sun.COM 		connp->conn_recv_ancillary.crb_ipv6_recvhopopts = onoff;
178211042SErik.Nordmark@Sun.COM 		mutex_exit(&connp->conn_lock);
178311042SErik.Nordmark@Sun.COM 		break;
178411042SErik.Nordmark@Sun.COM 	case IPV6_RECVDSTOPTS:
178511042SErik.Nordmark@Sun.COM 		mutex_enter(&connp->conn_lock);
178611042SErik.Nordmark@Sun.COM 		connp->conn_recv_ancillary.crb_ipv6_recvdstopts = onoff;
178711042SErik.Nordmark@Sun.COM 		mutex_exit(&connp->conn_lock);
178811042SErik.Nordmark@Sun.COM 		break;
178911042SErik.Nordmark@Sun.COM 	case _OLD_IPV6_RECVDSTOPTS:
179011042SErik.Nordmark@Sun.COM 		mutex_enter(&connp->conn_lock);
179111042SErik.Nordmark@Sun.COM 		connp->conn_recv_ancillary.crb_old_ipv6_recvdstopts =
179211042SErik.Nordmark@Sun.COM 		    onoff;
179311042SErik.Nordmark@Sun.COM 		mutex_exit(&connp->conn_lock);
179411042SErik.Nordmark@Sun.COM 		break;
179511042SErik.Nordmark@Sun.COM 	case IPV6_RECVRTHDRDSTOPTS:
179611042SErik.Nordmark@Sun.COM 		mutex_enter(&connp->conn_lock);
179711042SErik.Nordmark@Sun.COM 		connp->conn_recv_ancillary.crb_ipv6_recvrthdrdstopts =
179811042SErik.Nordmark@Sun.COM 		    onoff;
179911042SErik.Nordmark@Sun.COM 		mutex_exit(&connp->conn_lock);
180011042SErik.Nordmark@Sun.COM 		break;
180111042SErik.Nordmark@Sun.COM 	case IPV6_RECVRTHDR:
180211042SErik.Nordmark@Sun.COM 		mutex_enter(&connp->conn_lock);
180311042SErik.Nordmark@Sun.COM 		connp->conn_recv_ancillary.crb_ipv6_recvrthdr = onoff;
180411042SErik.Nordmark@Sun.COM 		mutex_exit(&connp->conn_lock);
180511042SErik.Nordmark@Sun.COM 		break;
180611042SErik.Nordmark@Sun.COM 	case IPV6_PKTINFO:
180711042SErik.Nordmark@Sun.COM 		mutex_enter(&connp->conn_lock);
180811042SErik.Nordmark@Sun.COM 		if (inlen == 0) {
180911042SErik.Nordmark@Sun.COM 			ipp->ipp_fields &= ~IPPF_ADDR;
181011042SErik.Nordmark@Sun.COM 			ipp->ipp_addr = ipv6_all_zeros;
181111042SErik.Nordmark@Sun.COM 			ixa->ixa_ifindex = 0;
181211042SErik.Nordmark@Sun.COM 		} else {
181311042SErik.Nordmark@Sun.COM 			struct in6_pktinfo *pkti;
181411042SErik.Nordmark@Sun.COM 
181511042SErik.Nordmark@Sun.COM 			pkti = (struct in6_pktinfo *)invalp;
181611042SErik.Nordmark@Sun.COM 			ipp->ipp_addr = pkti->ipi6_addr;
181711042SErik.Nordmark@Sun.COM 			if (!IN6_IS_ADDR_UNSPECIFIED(&ipp->ipp_addr))
181811042SErik.Nordmark@Sun.COM 				ipp->ipp_fields |= IPPF_ADDR;
181911042SErik.Nordmark@Sun.COM 			else
182011042SErik.Nordmark@Sun.COM 				ipp->ipp_fields &= ~IPPF_ADDR;
182111042SErik.Nordmark@Sun.COM 			ixa->ixa_ifindex = pkti->ipi6_ifindex;
182211042SErik.Nordmark@Sun.COM 		}
182311042SErik.Nordmark@Sun.COM 		mutex_exit(&connp->conn_lock);
182411042SErik.Nordmark@Sun.COM 		/* Source and ifindex might have changed */
182511042SErik.Nordmark@Sun.COM 		coa->coa_changed |= COA_HEADER_CHANGED;
182611042SErik.Nordmark@Sun.COM 		coa->coa_changed |= COA_ROUTE_CHANGED;
182711042SErik.Nordmark@Sun.COM 		break;
182811042SErik.Nordmark@Sun.COM 	case IPV6_HOPLIMIT:
182911042SErik.Nordmark@Sun.COM 		mutex_enter(&connp->conn_lock);
183011042SErik.Nordmark@Sun.COM 		if (inlen == 0 || *i1 == -1) {
183111042SErik.Nordmark@Sun.COM 			/* Revert to default */
183211042SErik.Nordmark@Sun.COM 			ipp->ipp_fields &= ~IPPF_HOPLIMIT;
183311042SErik.Nordmark@Sun.COM 			ixa->ixa_flags &= ~IXAF_NO_TTL_CHANGE;
183411042SErik.Nordmark@Sun.COM 		} else {
183511042SErik.Nordmark@Sun.COM 			ipp->ipp_hoplimit = *i1;
183611042SErik.Nordmark@Sun.COM 			ipp->ipp_fields |= IPPF_HOPLIMIT;
183711042SErik.Nordmark@Sun.COM 			/* Ensure that it sticks for multicast packets */
183811042SErik.Nordmark@Sun.COM 			ixa->ixa_flags |= IXAF_NO_TTL_CHANGE;
183911042SErik.Nordmark@Sun.COM 		}
184011042SErik.Nordmark@Sun.COM 		mutex_exit(&connp->conn_lock);
184111042SErik.Nordmark@Sun.COM 		coa->coa_changed |= COA_HEADER_CHANGED;
184211042SErik.Nordmark@Sun.COM 		break;
184311042SErik.Nordmark@Sun.COM 	case IPV6_TCLASS:
184411042SErik.Nordmark@Sun.COM 		/*
184511042SErik.Nordmark@Sun.COM 		 * IPV6_TCLASS accepts -1 as use kernel default
184611042SErik.Nordmark@Sun.COM 		 * and [0, 255] as the actualy traffic class.
184711042SErik.Nordmark@Sun.COM 		 */
184811042SErik.Nordmark@Sun.COM 		mutex_enter(&connp->conn_lock);
184911042SErik.Nordmark@Sun.COM 		if (inlen == 0 || *i1 == -1) {
185011042SErik.Nordmark@Sun.COM 			ipp->ipp_tclass = 0;
185111042SErik.Nordmark@Sun.COM 			ipp->ipp_fields &= ~IPPF_TCLASS;
185211042SErik.Nordmark@Sun.COM 		} else {
185311042SErik.Nordmark@Sun.COM 			ipp->ipp_tclass = *i1;
185411042SErik.Nordmark@Sun.COM 			ipp->ipp_fields |= IPPF_TCLASS;
185511042SErik.Nordmark@Sun.COM 		}
185611042SErik.Nordmark@Sun.COM 		mutex_exit(&connp->conn_lock);
185711042SErik.Nordmark@Sun.COM 		coa->coa_changed |= COA_HEADER_CHANGED;
185811042SErik.Nordmark@Sun.COM 		break;
185911042SErik.Nordmark@Sun.COM 	case IPV6_NEXTHOP:
186011042SErik.Nordmark@Sun.COM 		if (inlen == 0) {
186111042SErik.Nordmark@Sun.COM 			ixa->ixa_flags &= ~IXAF_NEXTHOP_SET;
186211042SErik.Nordmark@Sun.COM 		} else {
186311042SErik.Nordmark@Sun.COM 			sin6_t *sin6 = (sin6_t *)invalp;
186411042SErik.Nordmark@Sun.COM 
186511042SErik.Nordmark@Sun.COM 			ixa->ixa_nexthop_v6 = sin6->sin6_addr;
186611042SErik.Nordmark@Sun.COM 			if (!IN6_IS_ADDR_UNSPECIFIED(&ixa->ixa_nexthop_v6))
186711042SErik.Nordmark@Sun.COM 				ixa->ixa_flags |= IXAF_NEXTHOP_SET;
186811042SErik.Nordmark@Sun.COM 			else
186911042SErik.Nordmark@Sun.COM 				ixa->ixa_flags &= ~IXAF_NEXTHOP_SET;
187011042SErik.Nordmark@Sun.COM 		}
187111042SErik.Nordmark@Sun.COM 		coa->coa_changed |= COA_ROUTE_CHANGED;
187211042SErik.Nordmark@Sun.COM 		break;
187311042SErik.Nordmark@Sun.COM 	case IPV6_HOPOPTS:
187411042SErik.Nordmark@Sun.COM 		mutex_enter(&connp->conn_lock);
187511042SErik.Nordmark@Sun.COM 		error = optcom_pkt_set(invalp, inlen,
187611042SErik.Nordmark@Sun.COM 		    (uchar_t **)&ipp->ipp_hopopts, &ipp->ipp_hopoptslen);
187711042SErik.Nordmark@Sun.COM 		if (error != 0) {
187811042SErik.Nordmark@Sun.COM 			mutex_exit(&connp->conn_lock);
187911042SErik.Nordmark@Sun.COM 			return (error);
188011042SErik.Nordmark@Sun.COM 		}
188111042SErik.Nordmark@Sun.COM 		if (ipp->ipp_hopoptslen == 0) {
188211042SErik.Nordmark@Sun.COM 			ipp->ipp_fields &= ~IPPF_HOPOPTS;
188311042SErik.Nordmark@Sun.COM 		} else {
188411042SErik.Nordmark@Sun.COM 			ipp->ipp_fields |= IPPF_HOPOPTS;
188511042SErik.Nordmark@Sun.COM 		}
188611042SErik.Nordmark@Sun.COM 		mutex_exit(&connp->conn_lock);
188711042SErik.Nordmark@Sun.COM 		coa->coa_changed |= COA_HEADER_CHANGED;
188811042SErik.Nordmark@Sun.COM 		coa->coa_changed |= COA_WROFF_CHANGED;
188911042SErik.Nordmark@Sun.COM 		break;
189011042SErik.Nordmark@Sun.COM 	case IPV6_RTHDRDSTOPTS:
189111042SErik.Nordmark@Sun.COM 		mutex_enter(&connp->conn_lock);
189211042SErik.Nordmark@Sun.COM 		error = optcom_pkt_set(invalp, inlen,
189311042SErik.Nordmark@Sun.COM 		    (uchar_t **)&ipp->ipp_rthdrdstopts,
189411042SErik.Nordmark@Sun.COM 		    &ipp->ipp_rthdrdstoptslen);
189511042SErik.Nordmark@Sun.COM 		if (error != 0) {
189611042SErik.Nordmark@Sun.COM 			mutex_exit(&connp->conn_lock);
189711042SErik.Nordmark@Sun.COM 			return (error);
189811042SErik.Nordmark@Sun.COM 		}
189911042SErik.Nordmark@Sun.COM 		if (ipp->ipp_rthdrdstoptslen == 0) {
190011042SErik.Nordmark@Sun.COM 			ipp->ipp_fields &= ~IPPF_RTHDRDSTOPTS;
190111042SErik.Nordmark@Sun.COM 		} else {
190211042SErik.Nordmark@Sun.COM 			ipp->ipp_fields |= IPPF_RTHDRDSTOPTS;
190311042SErik.Nordmark@Sun.COM 		}
190411042SErik.Nordmark@Sun.COM 		mutex_exit(&connp->conn_lock);
190511042SErik.Nordmark@Sun.COM 		coa->coa_changed |= COA_HEADER_CHANGED;
190611042SErik.Nordmark@Sun.COM 		coa->coa_changed |= COA_WROFF_CHANGED;
190711042SErik.Nordmark@Sun.COM 		break;
190811042SErik.Nordmark@Sun.COM 	case IPV6_DSTOPTS:
190911042SErik.Nordmark@Sun.COM 		mutex_enter(&connp->conn_lock);
191011042SErik.Nordmark@Sun.COM 		error = optcom_pkt_set(invalp, inlen,
191111042SErik.Nordmark@Sun.COM 		    (uchar_t **)&ipp->ipp_dstopts, &ipp->ipp_dstoptslen);
191211042SErik.Nordmark@Sun.COM 		if (error != 0) {
191311042SErik.Nordmark@Sun.COM 			mutex_exit(&connp->conn_lock);
191411042SErik.Nordmark@Sun.COM 			return (error);
191511042SErik.Nordmark@Sun.COM 		}
191611042SErik.Nordmark@Sun.COM 		if (ipp->ipp_dstoptslen == 0) {
191711042SErik.Nordmark@Sun.COM 			ipp->ipp_fields &= ~IPPF_DSTOPTS;
191811042SErik.Nordmark@Sun.COM 		} else {
191911042SErik.Nordmark@Sun.COM 			ipp->ipp_fields |= IPPF_DSTOPTS;
192011042SErik.Nordmark@Sun.COM 		}
192111042SErik.Nordmark@Sun.COM 		mutex_exit(&connp->conn_lock);
192211042SErik.Nordmark@Sun.COM 		coa->coa_changed |= COA_HEADER_CHANGED;
192311042SErik.Nordmark@Sun.COM 		coa->coa_changed |= COA_WROFF_CHANGED;
192411042SErik.Nordmark@Sun.COM 		break;
192511042SErik.Nordmark@Sun.COM 	case IPV6_RTHDR:
192611042SErik.Nordmark@Sun.COM 		mutex_enter(&connp->conn_lock);
192711042SErik.Nordmark@Sun.COM 		error = optcom_pkt_set(invalp, inlen,
192811042SErik.Nordmark@Sun.COM 		    (uchar_t **)&ipp->ipp_rthdr, &ipp->ipp_rthdrlen);
192911042SErik.Nordmark@Sun.COM 		if (error != 0) {
193011042SErik.Nordmark@Sun.COM 			mutex_exit(&connp->conn_lock);
193111042SErik.Nordmark@Sun.COM 			return (error);
193211042SErik.Nordmark@Sun.COM 		}
193311042SErik.Nordmark@Sun.COM 		if (ipp->ipp_rthdrlen == 0) {
193411042SErik.Nordmark@Sun.COM 			ipp->ipp_fields &= ~IPPF_RTHDR;
193511042SErik.Nordmark@Sun.COM 		} else {
193611042SErik.Nordmark@Sun.COM 			ipp->ipp_fields |= IPPF_RTHDR;
193711042SErik.Nordmark@Sun.COM 		}
193811042SErik.Nordmark@Sun.COM 		mutex_exit(&connp->conn_lock);
193911042SErik.Nordmark@Sun.COM 		coa->coa_changed |= COA_HEADER_CHANGED;
194011042SErik.Nordmark@Sun.COM 		coa->coa_changed |= COA_WROFF_CHANGED;
194111042SErik.Nordmark@Sun.COM 		break;
194211042SErik.Nordmark@Sun.COM 
194311042SErik.Nordmark@Sun.COM 	case IPV6_DONTFRAG:
194411042SErik.Nordmark@Sun.COM 		if (onoff) {
194511042SErik.Nordmark@Sun.COM 			ixa->ixa_flags |= IXAF_DONTFRAG;
194611042SErik.Nordmark@Sun.COM 			ixa->ixa_flags &= ~IXAF_PMTU_DISCOVERY;
194711042SErik.Nordmark@Sun.COM 		} else {
194811042SErik.Nordmark@Sun.COM 			ixa->ixa_flags &= ~IXAF_DONTFRAG;
194911042SErik.Nordmark@Sun.COM 			ixa->ixa_flags |= IXAF_PMTU_DISCOVERY;
195011042SErik.Nordmark@Sun.COM 		}
195111042SErik.Nordmark@Sun.COM 		/* Need to redo ip_attr_connect */
195211042SErik.Nordmark@Sun.COM 		coa->coa_changed |= COA_ROUTE_CHANGED;
195311042SErik.Nordmark@Sun.COM 		break;
195411042SErik.Nordmark@Sun.COM 
195511042SErik.Nordmark@Sun.COM 	case IPV6_USE_MIN_MTU:
195611042SErik.Nordmark@Sun.COM 		ixa->ixa_flags |= IXAF_USE_MIN_MTU;
195711042SErik.Nordmark@Sun.COM 		ixa->ixa_use_min_mtu = *i1;
195811042SErik.Nordmark@Sun.COM 		/* Need to redo ip_attr_connect */
195911042SErik.Nordmark@Sun.COM 		coa->coa_changed |= COA_ROUTE_CHANGED;
196011042SErik.Nordmark@Sun.COM 		break;
196111042SErik.Nordmark@Sun.COM 
196211042SErik.Nordmark@Sun.COM 	case IPV6_SEC_OPT:
196311042SErik.Nordmark@Sun.COM 		mutex_enter(&connp->conn_lock);
196411042SErik.Nordmark@Sun.COM 		error = ipsec_set_req(cr, connp, (ipsec_req_t *)invalp);
196511042SErik.Nordmark@Sun.COM 		mutex_exit(&connp->conn_lock);
196611042SErik.Nordmark@Sun.COM 		if (error != 0) {
196711042SErik.Nordmark@Sun.COM 			return (error);
196811042SErik.Nordmark@Sun.COM 		}
196911042SErik.Nordmark@Sun.COM 		/* This is an IPsec policy change - redo ip_attr_connect */
197011042SErik.Nordmark@Sun.COM 		coa->coa_changed |= COA_ROUTE_CHANGED;
197111042SErik.Nordmark@Sun.COM 		break;
197211042SErik.Nordmark@Sun.COM 	case IPV6_SRC_PREFERENCES:
197311042SErik.Nordmark@Sun.COM 		/*
197411042SErik.Nordmark@Sun.COM 		 * This socket option only affects connected
197511042SErik.Nordmark@Sun.COM 		 * sockets that haven't already bound to a specific
197611042SErik.Nordmark@Sun.COM 		 * IPv6 address.  In other words, sockets that
197711042SErik.Nordmark@Sun.COM 		 * don't call bind() with an address other than the
197811042SErik.Nordmark@Sun.COM 		 * unspecified address and that call connect().
197911042SErik.Nordmark@Sun.COM 		 * ip_set_destination_v6() passes these preferences
198011042SErik.Nordmark@Sun.COM 		 * to the ipif_select_source_v6() function.
198111042SErik.Nordmark@Sun.COM 		 */
198211042SErik.Nordmark@Sun.COM 		mutex_enter(&connp->conn_lock);
198311042SErik.Nordmark@Sun.COM 		error = ip6_set_src_preferences(ixa, *(uint32_t *)invalp);
198411042SErik.Nordmark@Sun.COM 		mutex_exit(&connp->conn_lock);
198511042SErik.Nordmark@Sun.COM 		if (error != 0) {
198611042SErik.Nordmark@Sun.COM 			return (error);
198711042SErik.Nordmark@Sun.COM 		}
198811042SErik.Nordmark@Sun.COM 		break;
198911042SErik.Nordmark@Sun.COM 	case IPV6_V6ONLY:
199011042SErik.Nordmark@Sun.COM 		mutex_enter(&connp->conn_lock);
199111042SErik.Nordmark@Sun.COM 		connp->conn_ipv6_v6only = onoff;
199211042SErik.Nordmark@Sun.COM 		mutex_exit(&connp->conn_lock);
199311042SErik.Nordmark@Sun.COM 		break;
199411042SErik.Nordmark@Sun.COM 	}
199511042SErik.Nordmark@Sun.COM 	return (0);
199611042SErik.Nordmark@Sun.COM }
199711042SErik.Nordmark@Sun.COM 
199811042SErik.Nordmark@Sun.COM /* Handle IPPROTO_UDP */
199911042SErik.Nordmark@Sun.COM /* ARGSUSED1 */
200011042SErik.Nordmark@Sun.COM static int
200111042SErik.Nordmark@Sun.COM conn_opt_set_udp(conn_opt_arg_t *coa, t_scalar_t name, uint_t inlen,
200211042SErik.Nordmark@Sun.COM     uchar_t *invalp, boolean_t checkonly, cred_t *cr)
200311042SErik.Nordmark@Sun.COM {
200411042SErik.Nordmark@Sun.COM 	conn_t		*connp = coa->coa_connp;
200511042SErik.Nordmark@Sun.COM 	int		*i1 = (int *)invalp;
200611042SErik.Nordmark@Sun.COM 	boolean_t	onoff = (*i1 == 0) ? 0 : 1;
200711042SErik.Nordmark@Sun.COM 	int		error;
200811042SErik.Nordmark@Sun.COM 
200911042SErik.Nordmark@Sun.COM 	switch (name) {
201011042SErik.Nordmark@Sun.COM 	case UDP_ANONPRIVBIND:
201111042SErik.Nordmark@Sun.COM 		if ((error = secpolicy_net_privaddr(cr, 0, IPPROTO_UDP)) != 0) {
201211042SErik.Nordmark@Sun.COM 			return (error);
201311042SErik.Nordmark@Sun.COM 		}
201411042SErik.Nordmark@Sun.COM 		break;
201511042SErik.Nordmark@Sun.COM 	}
201611042SErik.Nordmark@Sun.COM 	if (checkonly)
201711042SErik.Nordmark@Sun.COM 		return (0);
201811042SErik.Nordmark@Sun.COM 
201911042SErik.Nordmark@Sun.COM 	/* Here we set the actual option value */
202011042SErik.Nordmark@Sun.COM 	mutex_enter(&connp->conn_lock);
202111042SErik.Nordmark@Sun.COM 	switch (name) {
202211042SErik.Nordmark@Sun.COM 	case UDP_ANONPRIVBIND:
202311042SErik.Nordmark@Sun.COM 		connp->conn_anon_priv_bind = onoff;
202411042SErik.Nordmark@Sun.COM 		break;
202511042SErik.Nordmark@Sun.COM 	case UDP_EXCLBIND:
202611042SErik.Nordmark@Sun.COM 		connp->conn_exclbind = onoff;
202711042SErik.Nordmark@Sun.COM 		break;
202811042SErik.Nordmark@Sun.COM 	}
202911042SErik.Nordmark@Sun.COM 	mutex_exit(&connp->conn_lock);
203011042SErik.Nordmark@Sun.COM 	return (0);
203111042SErik.Nordmark@Sun.COM }
203211042SErik.Nordmark@Sun.COM 
203311042SErik.Nordmark@Sun.COM /* Handle IPPROTO_TCP */
203411042SErik.Nordmark@Sun.COM /* ARGSUSED1 */
203511042SErik.Nordmark@Sun.COM static int
203611042SErik.Nordmark@Sun.COM conn_opt_set_tcp(conn_opt_arg_t *coa, t_scalar_t name, uint_t inlen,
203711042SErik.Nordmark@Sun.COM     uchar_t *invalp, boolean_t checkonly, cred_t *cr)
203811042SErik.Nordmark@Sun.COM {
203911042SErik.Nordmark@Sun.COM 	conn_t		*connp = coa->coa_connp;
204011042SErik.Nordmark@Sun.COM 	int		*i1 = (int *)invalp;
204111042SErik.Nordmark@Sun.COM 	boolean_t	onoff = (*i1 == 0) ? 0 : 1;
204211042SErik.Nordmark@Sun.COM 	int		error;
204311042SErik.Nordmark@Sun.COM 
204411042SErik.Nordmark@Sun.COM 	switch (name) {
204511042SErik.Nordmark@Sun.COM 	case TCP_ANONPRIVBIND:
204611042SErik.Nordmark@Sun.COM 		if ((error = secpolicy_net_privaddr(cr, 0, IPPROTO_TCP)) != 0) {
204711042SErik.Nordmark@Sun.COM 			return (error);
204811042SErik.Nordmark@Sun.COM 		}
204911042SErik.Nordmark@Sun.COM 		break;
205011042SErik.Nordmark@Sun.COM 	}
205111042SErik.Nordmark@Sun.COM 	if (checkonly)
205211042SErik.Nordmark@Sun.COM 		return (0);
205311042SErik.Nordmark@Sun.COM 
205411042SErik.Nordmark@Sun.COM 	/* Here we set the actual option value */
205511042SErik.Nordmark@Sun.COM 	mutex_enter(&connp->conn_lock);
205611042SErik.Nordmark@Sun.COM 	switch (name) {
205711042SErik.Nordmark@Sun.COM 	case TCP_ANONPRIVBIND:
205811042SErik.Nordmark@Sun.COM 		connp->conn_anon_priv_bind = onoff;
205911042SErik.Nordmark@Sun.COM 		break;
206011042SErik.Nordmark@Sun.COM 	case TCP_EXCLBIND:
206111042SErik.Nordmark@Sun.COM 		connp->conn_exclbind = onoff;
206211042SErik.Nordmark@Sun.COM 		break;
206311042SErik.Nordmark@Sun.COM 	case TCP_RECVDSTADDR:
206411042SErik.Nordmark@Sun.COM 		connp->conn_recv_ancillary.crb_recvdstaddr = onoff;
206511042SErik.Nordmark@Sun.COM 		break;
206611042SErik.Nordmark@Sun.COM 	}
206711042SErik.Nordmark@Sun.COM 	mutex_exit(&connp->conn_lock);
206811042SErik.Nordmark@Sun.COM 	return (0);
206911042SErik.Nordmark@Sun.COM }
207011042SErik.Nordmark@Sun.COM 
207111042SErik.Nordmark@Sun.COM int
207211042SErik.Nordmark@Sun.COM conn_getsockname(conn_t *connp, struct sockaddr *sa, uint_t *salenp)
207311042SErik.Nordmark@Sun.COM {
207411042SErik.Nordmark@Sun.COM 	sin_t		*sin;
207511042SErik.Nordmark@Sun.COM 	sin6_t		*sin6;
207611042SErik.Nordmark@Sun.COM 
207711042SErik.Nordmark@Sun.COM 	if (connp->conn_family == AF_INET) {
207811042SErik.Nordmark@Sun.COM 		if (*salenp < sizeof (sin_t))
207911042SErik.Nordmark@Sun.COM 			return (EINVAL);
208011042SErik.Nordmark@Sun.COM 
208111042SErik.Nordmark@Sun.COM 		*salenp = sizeof (sin_t);
208211042SErik.Nordmark@Sun.COM 		/* Fill zeroes and then initialize non-zero fields */
208311042SErik.Nordmark@Sun.COM 		sin = (sin_t *)sa;
208411042SErik.Nordmark@Sun.COM 		*sin = sin_null;
208511042SErik.Nordmark@Sun.COM 		sin->sin_family = AF_INET;
208611042SErik.Nordmark@Sun.COM 		if (!IN6_IS_ADDR_V4MAPPED_ANY(&connp->conn_saddr_v6) &&
208711042SErik.Nordmark@Sun.COM 		    !IN6_IS_ADDR_UNSPECIFIED(&connp->conn_saddr_v6)) {
208811042SErik.Nordmark@Sun.COM 			sin->sin_addr.s_addr = connp->conn_saddr_v4;
208911042SErik.Nordmark@Sun.COM 		} else {
209011042SErik.Nordmark@Sun.COM 			/*
209111042SErik.Nordmark@Sun.COM 			 * INADDR_ANY
209211042SErik.Nordmark@Sun.COM 			 * conn_saddr is not set, we might be bound to
209311042SErik.Nordmark@Sun.COM 			 * broadcast/multicast. Use conn_bound_addr as
209411042SErik.Nordmark@Sun.COM 			 * local address instead (that could
209511042SErik.Nordmark@Sun.COM 			 * also still be INADDR_ANY)
209611042SErik.Nordmark@Sun.COM 			 */
209711042SErik.Nordmark@Sun.COM 			sin->sin_addr.s_addr = connp->conn_bound_addr_v4;
209811042SErik.Nordmark@Sun.COM 		}
209911042SErik.Nordmark@Sun.COM 		sin->sin_port = connp->conn_lport;
210011042SErik.Nordmark@Sun.COM 	} else {
210111042SErik.Nordmark@Sun.COM 		if (*salenp < sizeof (sin6_t))
210211042SErik.Nordmark@Sun.COM 			return (EINVAL);
210311042SErik.Nordmark@Sun.COM 
210411042SErik.Nordmark@Sun.COM 		*salenp = sizeof (sin6_t);
210511042SErik.Nordmark@Sun.COM 		/* Fill zeroes and then initialize non-zero fields */
210611042SErik.Nordmark@Sun.COM 		sin6 = (sin6_t *)sa;
210711042SErik.Nordmark@Sun.COM 		*sin6 = sin6_null;
210811042SErik.Nordmark@Sun.COM 		sin6->sin6_family = AF_INET6;
210911042SErik.Nordmark@Sun.COM 		if (!IN6_IS_ADDR_UNSPECIFIED(&connp->conn_saddr_v6)) {
211011042SErik.Nordmark@Sun.COM 			sin6->sin6_addr = connp->conn_saddr_v6;
211111042SErik.Nordmark@Sun.COM 		} else {
211211042SErik.Nordmark@Sun.COM 			/*
211311042SErik.Nordmark@Sun.COM 			 * conn_saddr is not set, we might be bound to
211411042SErik.Nordmark@Sun.COM 			 * broadcast/multicast. Use conn_bound_addr as
211511042SErik.Nordmark@Sun.COM 			 * local address instead (which could
211611042SErik.Nordmark@Sun.COM 			 * also still be unspecified)
211711042SErik.Nordmark@Sun.COM 			 */
211811042SErik.Nordmark@Sun.COM 			sin6->sin6_addr = connp->conn_bound_addr_v6;
211911042SErik.Nordmark@Sun.COM 		}
212011042SErik.Nordmark@Sun.COM 		sin6->sin6_port = connp->conn_lport;
212111042SErik.Nordmark@Sun.COM 		if (IN6_IS_ADDR_LINKSCOPE(&sin6->sin6_addr) &&
212211042SErik.Nordmark@Sun.COM 		    (connp->conn_ixa->ixa_flags & IXAF_SCOPEID_SET))
212311042SErik.Nordmark@Sun.COM 			sin6->sin6_scope_id = connp->conn_ixa->ixa_scopeid;
212411042SErik.Nordmark@Sun.COM 	}
212511042SErik.Nordmark@Sun.COM 	return (0);
212611042SErik.Nordmark@Sun.COM }
212711042SErik.Nordmark@Sun.COM 
212811042SErik.Nordmark@Sun.COM int
212911042SErik.Nordmark@Sun.COM conn_getpeername(conn_t *connp, struct sockaddr *sa, uint_t *salenp)
213011042SErik.Nordmark@Sun.COM {
213111042SErik.Nordmark@Sun.COM 	struct sockaddr_in	*sin;
213211042SErik.Nordmark@Sun.COM 	struct sockaddr_in6	*sin6;
213311042SErik.Nordmark@Sun.COM 
213411042SErik.Nordmark@Sun.COM 	if (connp->conn_family == AF_INET) {
213511042SErik.Nordmark@Sun.COM 		if (*salenp < sizeof (sin_t))
213611042SErik.Nordmark@Sun.COM 			return (EINVAL);
213711042SErik.Nordmark@Sun.COM 
213811042SErik.Nordmark@Sun.COM 		*salenp = sizeof (sin_t);
213911042SErik.Nordmark@Sun.COM 		/* initialize */
214011042SErik.Nordmark@Sun.COM 		sin = (sin_t *)sa;
214111042SErik.Nordmark@Sun.COM 		*sin = sin_null;
214211042SErik.Nordmark@Sun.COM 		sin->sin_family = AF_INET;
214311042SErik.Nordmark@Sun.COM 		sin->sin_addr.s_addr = connp->conn_faddr_v4;
214411042SErik.Nordmark@Sun.COM 		sin->sin_port = connp->conn_fport;
214511042SErik.Nordmark@Sun.COM 	} else {
214611042SErik.Nordmark@Sun.COM 		if (*salenp < sizeof (sin6_t))
214711042SErik.Nordmark@Sun.COM 			return (EINVAL);
214811042SErik.Nordmark@Sun.COM 
214911042SErik.Nordmark@Sun.COM 		*salenp = sizeof (sin6_t);
215011042SErik.Nordmark@Sun.COM 		/* initialize */
215111042SErik.Nordmark@Sun.COM 		sin6 = (sin6_t *)sa;
215211042SErik.Nordmark@Sun.COM 		*sin6 = sin6_null;
215311042SErik.Nordmark@Sun.COM 		sin6->sin6_family = AF_INET6;
215411042SErik.Nordmark@Sun.COM 		sin6->sin6_addr = connp->conn_faddr_v6;
215511042SErik.Nordmark@Sun.COM 		sin6->sin6_port =  connp->conn_fport;
215611042SErik.Nordmark@Sun.COM 		sin6->sin6_flowinfo = connp->conn_flowinfo;
215711042SErik.Nordmark@Sun.COM 		if (IN6_IS_ADDR_LINKSCOPE(&sin6->sin6_addr) &&
215811042SErik.Nordmark@Sun.COM 		    (connp->conn_ixa->ixa_flags & IXAF_SCOPEID_SET))
215911042SErik.Nordmark@Sun.COM 			sin6->sin6_scope_id = connp->conn_ixa->ixa_scopeid;
216011042SErik.Nordmark@Sun.COM 	}
216111042SErik.Nordmark@Sun.COM 	return (0);
216211042SErik.Nordmark@Sun.COM }
216311042SErik.Nordmark@Sun.COM 
216411042SErik.Nordmark@Sun.COM static uint32_t	cksum_massage_options_v4(ipha_t *, netstack_t *);
216511042SErik.Nordmark@Sun.COM static uint32_t cksum_massage_options_v6(ip6_t *, uint_t, netstack_t *);
216611042SErik.Nordmark@Sun.COM 
216711042SErik.Nordmark@Sun.COM /*
216811042SErik.Nordmark@Sun.COM  * Allocate and fill in conn_ht_iphc based on the current information
216911042SErik.Nordmark@Sun.COM  * in the conn.
217011042SErik.Nordmark@Sun.COM  * Normally used when we bind() and connect().
217111042SErik.Nordmark@Sun.COM  * Returns failure if can't allocate memory, or if there is a problem
217211042SErik.Nordmark@Sun.COM  * with a routing header/option.
217311042SErik.Nordmark@Sun.COM  *
217411042SErik.Nordmark@Sun.COM  * We allocate space for the transport header (ulp_hdr_len + extra) and
217511042SErik.Nordmark@Sun.COM  * indicate the offset of the ulp header by setting ixa_ip_hdr_length.
217611042SErik.Nordmark@Sun.COM  * The extra is there for transports that want some spare room for future
217711042SErik.Nordmark@Sun.COM  * options. conn_ht_iphc_allocated is what was allocated; conn_ht_iphc_len
217811042SErik.Nordmark@Sun.COM  * excludes the extra part.
217911042SErik.Nordmark@Sun.COM  *
218011042SErik.Nordmark@Sun.COM  * We massage an routing option/header and store the ckecksum difference
218111042SErik.Nordmark@Sun.COM  * in conn_sum.
218211042SErik.Nordmark@Sun.COM  *
218311042SErik.Nordmark@Sun.COM  * Caller needs to update conn_wroff if desired.
218411042SErik.Nordmark@Sun.COM  */
218511042SErik.Nordmark@Sun.COM int
218611042SErik.Nordmark@Sun.COM conn_build_hdr_template(conn_t *connp, uint_t ulp_hdr_length, uint_t extra,
218711042SErik.Nordmark@Sun.COM     const in6_addr_t *v6src, const in6_addr_t *v6dst, uint32_t flowinfo)
218811042SErik.Nordmark@Sun.COM {
218911042SErik.Nordmark@Sun.COM 	ip_xmit_attr_t	*ixa = connp->conn_ixa;
219011042SErik.Nordmark@Sun.COM 	ip_pkt_t	*ipp = &connp->conn_xmit_ipp;
219111042SErik.Nordmark@Sun.COM 	uint_t		ip_hdr_length;
219211042SErik.Nordmark@Sun.COM 	uchar_t		*hdrs;
219311042SErik.Nordmark@Sun.COM 	uint_t		hdrs_len;
219411042SErik.Nordmark@Sun.COM 
219511042SErik.Nordmark@Sun.COM 	ASSERT(MUTEX_HELD(&connp->conn_lock));
219611042SErik.Nordmark@Sun.COM 
219711042SErik.Nordmark@Sun.COM 	if (ixa->ixa_flags & IXAF_IS_IPV4) {
219811042SErik.Nordmark@Sun.COM 		ip_hdr_length = ip_total_hdrs_len_v4(ipp);
219911042SErik.Nordmark@Sun.COM 		/* In case of TX label and IP options it can be too much */
220011042SErik.Nordmark@Sun.COM 		if (ip_hdr_length > IP_MAX_HDR_LENGTH) {
220111042SErik.Nordmark@Sun.COM 			/* Preserves existing TX errno for this */
220211042SErik.Nordmark@Sun.COM 			return (EHOSTUNREACH);
220311042SErik.Nordmark@Sun.COM 		}
220411042SErik.Nordmark@Sun.COM 	} else {
220511042SErik.Nordmark@Sun.COM 		ip_hdr_length = ip_total_hdrs_len_v6(ipp);
220611042SErik.Nordmark@Sun.COM 	}
220711042SErik.Nordmark@Sun.COM 	ixa->ixa_ip_hdr_length = ip_hdr_length;
220811042SErik.Nordmark@Sun.COM 	hdrs_len = ip_hdr_length + ulp_hdr_length + extra;
220911042SErik.Nordmark@Sun.COM 	ASSERT(hdrs_len != 0);
221011042SErik.Nordmark@Sun.COM 
221111042SErik.Nordmark@Sun.COM 	if (hdrs_len != connp->conn_ht_iphc_allocated) {
221211042SErik.Nordmark@Sun.COM 		/* Allocate new before we free any old */
221311042SErik.Nordmark@Sun.COM 		hdrs = kmem_alloc(hdrs_len, KM_NOSLEEP);
221411042SErik.Nordmark@Sun.COM 		if (hdrs == NULL)
221511042SErik.Nordmark@Sun.COM 			return (ENOMEM);
221611042SErik.Nordmark@Sun.COM 
221711042SErik.Nordmark@Sun.COM 		if (connp->conn_ht_iphc != NULL) {
221811042SErik.Nordmark@Sun.COM 			kmem_free(connp->conn_ht_iphc,
221911042SErik.Nordmark@Sun.COM 			    connp->conn_ht_iphc_allocated);
222011042SErik.Nordmark@Sun.COM 		}
222111042SErik.Nordmark@Sun.COM 		connp->conn_ht_iphc = hdrs;
222211042SErik.Nordmark@Sun.COM 		connp->conn_ht_iphc_allocated = hdrs_len;
222311042SErik.Nordmark@Sun.COM 	} else {
222411042SErik.Nordmark@Sun.COM 		hdrs = connp->conn_ht_iphc;
222511042SErik.Nordmark@Sun.COM 	}
222611042SErik.Nordmark@Sun.COM 	hdrs_len -= extra;
222711042SErik.Nordmark@Sun.COM 	connp->conn_ht_iphc_len = hdrs_len;
222811042SErik.Nordmark@Sun.COM 
222911042SErik.Nordmark@Sun.COM 	connp->conn_ht_ulp = hdrs + ip_hdr_length;
223011042SErik.Nordmark@Sun.COM 	connp->conn_ht_ulp_len = ulp_hdr_length;
223111042SErik.Nordmark@Sun.COM 
223211042SErik.Nordmark@Sun.COM 	if (ixa->ixa_flags & IXAF_IS_IPV4) {
223311042SErik.Nordmark@Sun.COM 		ipha_t	*ipha = (ipha_t *)hdrs;
223411042SErik.Nordmark@Sun.COM 
223511042SErik.Nordmark@Sun.COM 		IN6_V4MAPPED_TO_IPADDR(v6src, ipha->ipha_src);
223611042SErik.Nordmark@Sun.COM 		IN6_V4MAPPED_TO_IPADDR(v6dst, ipha->ipha_dst);
223711042SErik.Nordmark@Sun.COM 		ip_build_hdrs_v4(hdrs, ip_hdr_length, ipp, connp->conn_proto);
223811042SErik.Nordmark@Sun.COM 		ipha->ipha_length = htons(hdrs_len);
223911042SErik.Nordmark@Sun.COM 		if (ixa->ixa_flags & IXAF_PMTU_IPV4_DF)
224011042SErik.Nordmark@Sun.COM 			ipha->ipha_fragment_offset_and_flags |= IPH_DF_HTONS;
224111042SErik.Nordmark@Sun.COM 		else
224211042SErik.Nordmark@Sun.COM 			ipha->ipha_fragment_offset_and_flags &= ~IPH_DF_HTONS;
224311042SErik.Nordmark@Sun.COM 
224411042SErik.Nordmark@Sun.COM 		if (ipp->ipp_fields & IPPF_IPV4_OPTIONS) {
224511042SErik.Nordmark@Sun.COM 			connp->conn_sum = cksum_massage_options_v4(ipha,
224611042SErik.Nordmark@Sun.COM 			    connp->conn_netstack);
224711042SErik.Nordmark@Sun.COM 		} else {
224811042SErik.Nordmark@Sun.COM 			connp->conn_sum = 0;
224911042SErik.Nordmark@Sun.COM 		}
225011042SErik.Nordmark@Sun.COM 	} else {
225111042SErik.Nordmark@Sun.COM 		ip6_t	*ip6h = (ip6_t *)hdrs;
225211042SErik.Nordmark@Sun.COM 
225311042SErik.Nordmark@Sun.COM 		ip6h->ip6_src = *v6src;
225411042SErik.Nordmark@Sun.COM 		ip6h->ip6_dst = *v6dst;
225511042SErik.Nordmark@Sun.COM 		ip_build_hdrs_v6(hdrs, ip_hdr_length, ipp, connp->conn_proto,
225611042SErik.Nordmark@Sun.COM 		    flowinfo);
225711042SErik.Nordmark@Sun.COM 		ip6h->ip6_plen = htons(hdrs_len - IPV6_HDR_LEN);
225811042SErik.Nordmark@Sun.COM 
225911042SErik.Nordmark@Sun.COM 		if (ipp->ipp_fields & IPPF_RTHDR) {
226011042SErik.Nordmark@Sun.COM 			connp->conn_sum = cksum_massage_options_v6(ip6h,
226111042SErik.Nordmark@Sun.COM 			    ip_hdr_length, connp->conn_netstack);
226211042SErik.Nordmark@Sun.COM 
226311042SErik.Nordmark@Sun.COM 			/*
226411042SErik.Nordmark@Sun.COM 			 * Verify that the first hop isn't a mapped address.
226511042SErik.Nordmark@Sun.COM 			 * Routers along the path need to do this verification
226611042SErik.Nordmark@Sun.COM 			 * for subsequent hops.
226711042SErik.Nordmark@Sun.COM 			 */
226811042SErik.Nordmark@Sun.COM 			if (IN6_IS_ADDR_V4MAPPED(&ip6h->ip6_dst))
226911042SErik.Nordmark@Sun.COM 				return (EADDRNOTAVAIL);
227011042SErik.Nordmark@Sun.COM 
227111042SErik.Nordmark@Sun.COM 		} else {
227211042SErik.Nordmark@Sun.COM 			connp->conn_sum = 0;
227311042SErik.Nordmark@Sun.COM 		}
227411042SErik.Nordmark@Sun.COM 	}
227511042SErik.Nordmark@Sun.COM 	return (0);
227611042SErik.Nordmark@Sun.COM }
227711042SErik.Nordmark@Sun.COM 
227811042SErik.Nordmark@Sun.COM /*
227911042SErik.Nordmark@Sun.COM  * Prepend a header template to data_mp based on the ip_pkt_t
228011042SErik.Nordmark@Sun.COM  * and the passed in source, destination and protocol.
228111042SErik.Nordmark@Sun.COM  *
228211042SErik.Nordmark@Sun.COM  * Returns failure if can't allocate memory, in which case data_mp is freed.
228311042SErik.Nordmark@Sun.COM  * We allocate space for the transport header (ulp_hdr_len) and
228411042SErik.Nordmark@Sun.COM  * indicate the offset of the ulp header by setting ixa_ip_hdr_length.
228511042SErik.Nordmark@Sun.COM  *
228611042SErik.Nordmark@Sun.COM  * We massage an routing option/header and return the ckecksum difference
228711042SErik.Nordmark@Sun.COM  * in *sump. This is in host byte order.
228811042SErik.Nordmark@Sun.COM  *
228911042SErik.Nordmark@Sun.COM  * Caller needs to update conn_wroff if desired.
229011042SErik.Nordmark@Sun.COM  */
229111042SErik.Nordmark@Sun.COM mblk_t *
229211042SErik.Nordmark@Sun.COM conn_prepend_hdr(ip_xmit_attr_t *ixa, const ip_pkt_t *ipp,
229311042SErik.Nordmark@Sun.COM     const in6_addr_t *v6src, const in6_addr_t *v6dst,
229411042SErik.Nordmark@Sun.COM     uint8_t protocol, uint32_t flowinfo, uint_t ulp_hdr_length, mblk_t *data_mp,
229511042SErik.Nordmark@Sun.COM     uint_t data_length, uint_t wroff_extra, uint32_t *sump, int *errorp)
229611042SErik.Nordmark@Sun.COM {
229711042SErik.Nordmark@Sun.COM 	uint_t		ip_hdr_length;
229811042SErik.Nordmark@Sun.COM 	uchar_t		*hdrs;
229911042SErik.Nordmark@Sun.COM 	uint_t		hdrs_len;
230011042SErik.Nordmark@Sun.COM 	mblk_t		*mp;
230111042SErik.Nordmark@Sun.COM 
230211042SErik.Nordmark@Sun.COM 	if (ixa->ixa_flags & IXAF_IS_IPV4) {
230311042SErik.Nordmark@Sun.COM 		ip_hdr_length = ip_total_hdrs_len_v4(ipp);
230411042SErik.Nordmark@Sun.COM 		ASSERT(ip_hdr_length <= IP_MAX_HDR_LENGTH);
230511042SErik.Nordmark@Sun.COM 	} else {
230611042SErik.Nordmark@Sun.COM 		ip_hdr_length = ip_total_hdrs_len_v6(ipp);
230711042SErik.Nordmark@Sun.COM 	}
230811042SErik.Nordmark@Sun.COM 	hdrs_len = ip_hdr_length + ulp_hdr_length;
230911042SErik.Nordmark@Sun.COM 	ASSERT(hdrs_len != 0);
231011042SErik.Nordmark@Sun.COM 
231111042SErik.Nordmark@Sun.COM 	ixa->ixa_ip_hdr_length = ip_hdr_length;
231211042SErik.Nordmark@Sun.COM 
231311042SErik.Nordmark@Sun.COM 	/* Can we prepend to data_mp? */
231411042SErik.Nordmark@Sun.COM 	if (data_mp != NULL &&
231511042SErik.Nordmark@Sun.COM 	    data_mp->b_rptr - data_mp->b_datap->db_base >= hdrs_len &&
231611042SErik.Nordmark@Sun.COM 	    data_mp->b_datap->db_ref == 1) {
231711042SErik.Nordmark@Sun.COM 		hdrs = data_mp->b_rptr - hdrs_len;
231811042SErik.Nordmark@Sun.COM 		data_mp->b_rptr = hdrs;
231911042SErik.Nordmark@Sun.COM 		mp = data_mp;
232011042SErik.Nordmark@Sun.COM 	} else {
232111042SErik.Nordmark@Sun.COM 		mp = allocb(hdrs_len + wroff_extra, BPRI_MED);
232211042SErik.Nordmark@Sun.COM 		if (mp == NULL) {
232311042SErik.Nordmark@Sun.COM 			freemsg(data_mp);
232411042SErik.Nordmark@Sun.COM 			*errorp = ENOMEM;
232511042SErik.Nordmark@Sun.COM 			return (NULL);
232611042SErik.Nordmark@Sun.COM 		}
232711042SErik.Nordmark@Sun.COM 		mp->b_wptr = mp->b_datap->db_lim;
232811042SErik.Nordmark@Sun.COM 		hdrs = mp->b_rptr = mp->b_wptr - hdrs_len;
232911042SErik.Nordmark@Sun.COM 		mp->b_cont = data_mp;
233011042SErik.Nordmark@Sun.COM 	}
233111042SErik.Nordmark@Sun.COM 
233211042SErik.Nordmark@Sun.COM 	/*
233311042SErik.Nordmark@Sun.COM 	 * Set the source in the header. ip_build_hdrs_v4/v6 will overwrite it
233411042SErik.Nordmark@Sun.COM 	 * if PKTINFO (aka IPPF_ADDR) was set.
233511042SErik.Nordmark@Sun.COM 	 */
233611042SErik.Nordmark@Sun.COM 	if (ixa->ixa_flags & IXAF_IS_IPV4) {
233711042SErik.Nordmark@Sun.COM 		ipha_t *ipha = (ipha_t *)hdrs;
233811042SErik.Nordmark@Sun.COM 
233911042SErik.Nordmark@Sun.COM 		ASSERT(IN6_IS_ADDR_V4MAPPED(v6dst));
234011042SErik.Nordmark@Sun.COM 		IN6_V4MAPPED_TO_IPADDR(v6src, ipha->ipha_src);
234111042SErik.Nordmark@Sun.COM 		IN6_V4MAPPED_TO_IPADDR(v6dst, ipha->ipha_dst);
234211042SErik.Nordmark@Sun.COM 		ip_build_hdrs_v4(hdrs, ip_hdr_length, ipp, protocol);
234311042SErik.Nordmark@Sun.COM 		ipha->ipha_length = htons(hdrs_len + data_length);
234411042SErik.Nordmark@Sun.COM 		if (ixa->ixa_flags & IXAF_PMTU_IPV4_DF)
234511042SErik.Nordmark@Sun.COM 			ipha->ipha_fragment_offset_and_flags |= IPH_DF_HTONS;
234611042SErik.Nordmark@Sun.COM 		else
234711042SErik.Nordmark@Sun.COM 			ipha->ipha_fragment_offset_and_flags &= ~IPH_DF_HTONS;
234811042SErik.Nordmark@Sun.COM 
234911042SErik.Nordmark@Sun.COM 		if (ipp->ipp_fields & IPPF_IPV4_OPTIONS) {
235011042SErik.Nordmark@Sun.COM 			*sump = cksum_massage_options_v4(ipha,
235111042SErik.Nordmark@Sun.COM 			    ixa->ixa_ipst->ips_netstack);
235211042SErik.Nordmark@Sun.COM 		} else {
235311042SErik.Nordmark@Sun.COM 			*sump = 0;
235411042SErik.Nordmark@Sun.COM 		}
235511042SErik.Nordmark@Sun.COM 	} else {
235611042SErik.Nordmark@Sun.COM 		ip6_t *ip6h = (ip6_t *)hdrs;
235711042SErik.Nordmark@Sun.COM 
235811042SErik.Nordmark@Sun.COM 		ip6h->ip6_src = *v6src;
235911042SErik.Nordmark@Sun.COM 		ip6h->ip6_dst = *v6dst;
236011042SErik.Nordmark@Sun.COM 		ip_build_hdrs_v6(hdrs, ip_hdr_length, ipp, protocol, flowinfo);
236111042SErik.Nordmark@Sun.COM 		ip6h->ip6_plen = htons(hdrs_len + data_length - IPV6_HDR_LEN);
236211042SErik.Nordmark@Sun.COM 
236311042SErik.Nordmark@Sun.COM 		if (ipp->ipp_fields & IPPF_RTHDR) {
236411042SErik.Nordmark@Sun.COM 			*sump = cksum_massage_options_v6(ip6h,
236511042SErik.Nordmark@Sun.COM 			    ip_hdr_length, ixa->ixa_ipst->ips_netstack);
236611042SErik.Nordmark@Sun.COM 
236711042SErik.Nordmark@Sun.COM 			/*
236811042SErik.Nordmark@Sun.COM 			 * Verify that the first hop isn't a mapped address.
236911042SErik.Nordmark@Sun.COM 			 * Routers along the path need to do this verification
237011042SErik.Nordmark@Sun.COM 			 * for subsequent hops.
237111042SErik.Nordmark@Sun.COM 			 */
237211042SErik.Nordmark@Sun.COM 			if (IN6_IS_ADDR_V4MAPPED(&ip6h->ip6_dst)) {
237311042SErik.Nordmark@Sun.COM 				*errorp = EADDRNOTAVAIL;
237411042SErik.Nordmark@Sun.COM 				freemsg(mp);
237511042SErik.Nordmark@Sun.COM 				return (NULL);
237611042SErik.Nordmark@Sun.COM 			}
237711042SErik.Nordmark@Sun.COM 		} else {
237811042SErik.Nordmark@Sun.COM 			*sump = 0;
237911042SErik.Nordmark@Sun.COM 		}
238011042SErik.Nordmark@Sun.COM 	}
238111042SErik.Nordmark@Sun.COM 	return (mp);
238211042SErik.Nordmark@Sun.COM }
238311042SErik.Nordmark@Sun.COM 
238411042SErik.Nordmark@Sun.COM /*
238511042SErik.Nordmark@Sun.COM  * Massage a source route if any putting the first hop
238611042SErik.Nordmark@Sun.COM  * in ipha_dst. Compute a starting value for the checksum which
238711042SErik.Nordmark@Sun.COM  * takes into account that the original ipha_dst should be
238811042SErik.Nordmark@Sun.COM  * included in the checksum but that IP will include the
238911042SErik.Nordmark@Sun.COM  * first hop from the source route in the tcp checksum.
239011042SErik.Nordmark@Sun.COM  */
239111042SErik.Nordmark@Sun.COM static uint32_t
239211042SErik.Nordmark@Sun.COM cksum_massage_options_v4(ipha_t *ipha, netstack_t *ns)
239311042SErik.Nordmark@Sun.COM {
239411042SErik.Nordmark@Sun.COM 	in_addr_t	dst;
239511042SErik.Nordmark@Sun.COM 	uint32_t	cksum;
239611042SErik.Nordmark@Sun.COM 
239711042SErik.Nordmark@Sun.COM 	/* Get last hop then diff against first hop */
239811042SErik.Nordmark@Sun.COM 	cksum = ip_massage_options(ipha, ns);
239911042SErik.Nordmark@Sun.COM 	cksum = (cksum & 0xFFFF) + (cksum >> 16);
240011042SErik.Nordmark@Sun.COM 	dst = ipha->ipha_dst;
240111042SErik.Nordmark@Sun.COM 	cksum -= ((dst >> 16) + (dst & 0xffff));
240211042SErik.Nordmark@Sun.COM 	if ((int)cksum < 0)
240311042SErik.Nordmark@Sun.COM 		cksum--;
240411042SErik.Nordmark@Sun.COM 	cksum = (cksum & 0xFFFF) + (cksum >> 16);
240511042SErik.Nordmark@Sun.COM 	cksum = (cksum & 0xFFFF) + (cksum >> 16);
240611042SErik.Nordmark@Sun.COM 	ASSERT(cksum < 0x10000);
240711042SErik.Nordmark@Sun.COM 	return (ntohs(cksum));
240811042SErik.Nordmark@Sun.COM }
240911042SErik.Nordmark@Sun.COM 
241011042SErik.Nordmark@Sun.COM static uint32_t
241111042SErik.Nordmark@Sun.COM cksum_massage_options_v6(ip6_t *ip6h, uint_t ip_hdr_len, netstack_t *ns)
241211042SErik.Nordmark@Sun.COM {
241311042SErik.Nordmark@Sun.COM 	uint8_t		*end;
241411042SErik.Nordmark@Sun.COM 	ip6_rthdr_t	*rth;
241511042SErik.Nordmark@Sun.COM 	uint32_t	cksum;
241611042SErik.Nordmark@Sun.COM 
241711042SErik.Nordmark@Sun.COM 	end = (uint8_t *)ip6h + ip_hdr_len;
241811042SErik.Nordmark@Sun.COM 	rth = ip_find_rthdr_v6(ip6h, end);
241911042SErik.Nordmark@Sun.COM 	if (rth == NULL)
242011042SErik.Nordmark@Sun.COM 		return (0);
242111042SErik.Nordmark@Sun.COM 
242211042SErik.Nordmark@Sun.COM 	cksum = ip_massage_options_v6(ip6h, rth, ns);
242311042SErik.Nordmark@Sun.COM 	cksum = (cksum & 0xFFFF) + (cksum >> 16);
242411042SErik.Nordmark@Sun.COM 	ASSERT(cksum < 0x10000);
242511042SErik.Nordmark@Sun.COM 	return (ntohs(cksum));
242611042SErik.Nordmark@Sun.COM }
242711042SErik.Nordmark@Sun.COM 
242811042SErik.Nordmark@Sun.COM /*
242911042SErik.Nordmark@Sun.COM  * ULPs that change the destination address need to call this for each
243011042SErik.Nordmark@Sun.COM  * change to discard any state about a previous destination that might
243111042SErik.Nordmark@Sun.COM  * have been multicast or multirt.
243211042SErik.Nordmark@Sun.COM  */
243311042SErik.Nordmark@Sun.COM void
243411042SErik.Nordmark@Sun.COM ip_attr_newdst(ip_xmit_attr_t *ixa)
243511042SErik.Nordmark@Sun.COM {
243611042SErik.Nordmark@Sun.COM 	ixa->ixa_flags &= ~(IXAF_LOOPBACK_COPY | IXAF_NO_HW_CKSUM |
243711042SErik.Nordmark@Sun.COM 	    IXAF_NO_TTL_CHANGE | IXAF_IPV6_ADD_FRAGHDR |
243811042SErik.Nordmark@Sun.COM 	    IXAF_NO_LOOP_ZONEID_SET);
243911042SErik.Nordmark@Sun.COM }
244011042SErik.Nordmark@Sun.COM 
244111042SErik.Nordmark@Sun.COM /*
244211042SErik.Nordmark@Sun.COM  * Determine the nexthop which will be used.
244311042SErik.Nordmark@Sun.COM  * Normally this is just the destination, but if a IPv4 source route, or
244411042SErik.Nordmark@Sun.COM  * IPv6 routing header, is in the ip_pkt_t then we extract the nexthop from
244511042SErik.Nordmark@Sun.COM  * there.
244611042SErik.Nordmark@Sun.COM  */
244711042SErik.Nordmark@Sun.COM void
244811042SErik.Nordmark@Sun.COM ip_attr_nexthop(const ip_pkt_t *ipp, const ip_xmit_attr_t *ixa,
244911042SErik.Nordmark@Sun.COM     const in6_addr_t *dst, in6_addr_t *nexthop)
245011042SErik.Nordmark@Sun.COM {
245111131SErik.Nordmark@Sun.COM 	if (!(ipp->ipp_fields & (IPPF_IPV4_OPTIONS|IPPF_RTHDR))) {
245211131SErik.Nordmark@Sun.COM 		*nexthop = *dst;
245311131SErik.Nordmark@Sun.COM 		return;
245411131SErik.Nordmark@Sun.COM 	}
245511042SErik.Nordmark@Sun.COM 	if (ixa->ixa_flags & IXAF_IS_IPV4) {
245611042SErik.Nordmark@Sun.COM 		ipaddr_t v4dst;
245711042SErik.Nordmark@Sun.COM 		ipaddr_t v4nexthop;
245811042SErik.Nordmark@Sun.COM 
245911042SErik.Nordmark@Sun.COM 		IN6_V4MAPPED_TO_IPADDR(dst, v4dst);
246011042SErik.Nordmark@Sun.COM 		v4nexthop = ip_pkt_source_route_v4(ipp);
246111042SErik.Nordmark@Sun.COM 		if (v4nexthop == INADDR_ANY)
246211042SErik.Nordmark@Sun.COM 			v4nexthop = v4dst;
246311042SErik.Nordmark@Sun.COM 
246411042SErik.Nordmark@Sun.COM 		IN6_IPADDR_TO_V4MAPPED(v4nexthop, nexthop);
246511042SErik.Nordmark@Sun.COM 	} else {
246611042SErik.Nordmark@Sun.COM 		const in6_addr_t *v6nexthop;
246711042SErik.Nordmark@Sun.COM 
246811042SErik.Nordmark@Sun.COM 		v6nexthop = ip_pkt_source_route_v6(ipp);
246911042SErik.Nordmark@Sun.COM 		if (v6nexthop == NULL)
247011042SErik.Nordmark@Sun.COM 			v6nexthop = dst;
247111042SErik.Nordmark@Sun.COM 
247211042SErik.Nordmark@Sun.COM 		*nexthop = *v6nexthop;
247311042SErik.Nordmark@Sun.COM 	}
247411042SErik.Nordmark@Sun.COM }
247511042SErik.Nordmark@Sun.COM 
247611042SErik.Nordmark@Sun.COM /*
247711042SErik.Nordmark@Sun.COM  * Update the ip_xmit_attr_t based the addresses, conn_xmit_ipp and conn_ixa.
247811042SErik.Nordmark@Sun.COM  * If IPDF_IPSEC is set we cache the IPsec policy to handle the unconnected
247911042SErik.Nordmark@Sun.COM  * case (connected latching is done in conn_connect).
248011042SErik.Nordmark@Sun.COM  * Note that IPsec policy lookup requires conn_proto and conn_laddr to be
248111042SErik.Nordmark@Sun.COM  * set, but doesn't otherwise use the conn_t.
248211042SErik.Nordmark@Sun.COM  *
248311042SErik.Nordmark@Sun.COM  * Caller must set/clear IXAF_IS_IPV4 as appropriately.
248411042SErik.Nordmark@Sun.COM  * Caller must use ip_attr_nexthop() to determine the nexthop argument.
248511042SErik.Nordmark@Sun.COM  *
248611042SErik.Nordmark@Sun.COM  * The caller must NOT hold conn_lock (to avoid problems with ill_refrele
248711042SErik.Nordmark@Sun.COM  * causing the squeue to run doing ipcl_walk grabbing conn_lock.)
248811042SErik.Nordmark@Sun.COM  *
248911042SErik.Nordmark@Sun.COM  * Updates laddrp and uinfo if they are non-NULL.
249011042SErik.Nordmark@Sun.COM  *
249111042SErik.Nordmark@Sun.COM  * TSOL notes: The callers if ip_attr_connect must check if the destination
249211042SErik.Nordmark@Sun.COM  * is different than before and in that case redo conn_update_label.
249311042SErik.Nordmark@Sun.COM  * The callers of conn_connect do not need that since conn_connect
249411042SErik.Nordmark@Sun.COM  * performs the conn_update_label.
249511042SErik.Nordmark@Sun.COM  */
249611042SErik.Nordmark@Sun.COM int
249711042SErik.Nordmark@Sun.COM ip_attr_connect(const conn_t *connp, ip_xmit_attr_t *ixa,
249811042SErik.Nordmark@Sun.COM     const in6_addr_t *v6src, const in6_addr_t *v6dst,
249911042SErik.Nordmark@Sun.COM     const in6_addr_t *v6nexthop, in_port_t dstport, in6_addr_t *laddrp,
250011042SErik.Nordmark@Sun.COM     iulp_t *uinfo, uint32_t flags)
250111042SErik.Nordmark@Sun.COM {
250211042SErik.Nordmark@Sun.COM 	in6_addr_t		laddr = *v6src;
250311042SErik.Nordmark@Sun.COM 	int			error;
250411042SErik.Nordmark@Sun.COM 
250511042SErik.Nordmark@Sun.COM 	ASSERT(MUTEX_NOT_HELD(&connp->conn_lock));
250611042SErik.Nordmark@Sun.COM 
250711042SErik.Nordmark@Sun.COM 	if (connp->conn_zone_is_global)
250811042SErik.Nordmark@Sun.COM 		flags |= IPDF_ZONE_IS_GLOBAL;
250911042SErik.Nordmark@Sun.COM 	else
251011042SErik.Nordmark@Sun.COM 		flags &= ~IPDF_ZONE_IS_GLOBAL;
251111042SErik.Nordmark@Sun.COM 
251211042SErik.Nordmark@Sun.COM 	/*
251311042SErik.Nordmark@Sun.COM 	 * Lookup the route to determine a source address and the uinfo.
251411042SErik.Nordmark@Sun.COM 	 * If the ULP has a source route option then the caller will
251511042SErik.Nordmark@Sun.COM 	 * have set v6nexthop to be the first hop.
251611042SErik.Nordmark@Sun.COM 	 */
251711042SErik.Nordmark@Sun.COM 	if (ixa->ixa_flags & IXAF_IS_IPV4) {
251811042SErik.Nordmark@Sun.COM 		ipaddr_t v4dst;
251911042SErik.Nordmark@Sun.COM 		ipaddr_t v4src, v4nexthop;
252011042SErik.Nordmark@Sun.COM 
252111042SErik.Nordmark@Sun.COM 		IN6_V4MAPPED_TO_IPADDR(v6dst, v4dst);
252211042SErik.Nordmark@Sun.COM 		IN6_V4MAPPED_TO_IPADDR(v6nexthop, v4nexthop);
252311042SErik.Nordmark@Sun.COM 		IN6_V4MAPPED_TO_IPADDR(v6src, v4src);
252411042SErik.Nordmark@Sun.COM 
252511042SErik.Nordmark@Sun.COM 		if (connp->conn_unspec_src || v4src != INADDR_ANY)
252611042SErik.Nordmark@Sun.COM 			flags &= ~IPDF_SELECT_SRC;
252711042SErik.Nordmark@Sun.COM 		else
252811042SErik.Nordmark@Sun.COM 			flags |= IPDF_SELECT_SRC;
252911042SErik.Nordmark@Sun.COM 
253011042SErik.Nordmark@Sun.COM 		error = ip_set_destination_v4(&v4src, v4dst, v4nexthop, ixa,
253111042SErik.Nordmark@Sun.COM 		    uinfo, flags, connp->conn_mac_mode);
253211042SErik.Nordmark@Sun.COM 		IN6_IPADDR_TO_V4MAPPED(v4src, &laddr);
253311042SErik.Nordmark@Sun.COM 	} else {
253411042SErik.Nordmark@Sun.COM 		if (connp->conn_unspec_src || !IN6_IS_ADDR_UNSPECIFIED(v6src))
253511042SErik.Nordmark@Sun.COM 			flags &= ~IPDF_SELECT_SRC;
253611042SErik.Nordmark@Sun.COM 		else
253711042SErik.Nordmark@Sun.COM 			flags |= IPDF_SELECT_SRC;
253811042SErik.Nordmark@Sun.COM 
253911042SErik.Nordmark@Sun.COM 		error = ip_set_destination_v6(&laddr, v6dst, v6nexthop, ixa,
254011042SErik.Nordmark@Sun.COM 		    uinfo, flags, connp->conn_mac_mode);
254111042SErik.Nordmark@Sun.COM 	}
254211042SErik.Nordmark@Sun.COM 	/* Pass out some address even if we hit a RTF_REJECT etc */
254311042SErik.Nordmark@Sun.COM 	if (laddrp != NULL)
254411042SErik.Nordmark@Sun.COM 		*laddrp = laddr;
254511042SErik.Nordmark@Sun.COM 
254611042SErik.Nordmark@Sun.COM 	if (error != 0)
254711042SErik.Nordmark@Sun.COM 		return (error);
254811042SErik.Nordmark@Sun.COM 
254911042SErik.Nordmark@Sun.COM 	if (flags & IPDF_IPSEC) {
255011042SErik.Nordmark@Sun.COM 		/*
255111042SErik.Nordmark@Sun.COM 		 * Set any IPsec policy in ixa. Routine also looks at ULP
255211042SErik.Nordmark@Sun.COM 		 * ports.
255311042SErik.Nordmark@Sun.COM 		 */
255411042SErik.Nordmark@Sun.COM 		ipsec_cache_outbound_policy(connp, v6src, v6dst, dstport, ixa);
255511042SErik.Nordmark@Sun.COM 	}
255611042SErik.Nordmark@Sun.COM 	return (0);
255711042SErik.Nordmark@Sun.COM }
255811042SErik.Nordmark@Sun.COM 
255911042SErik.Nordmark@Sun.COM /*
256011042SErik.Nordmark@Sun.COM  * Connect the conn based on the addresses, conn_xmit_ipp and conn_ixa.
256111042SErik.Nordmark@Sun.COM  * Assumes that conn_faddr and conn_fport are already set. As such it is not
256211042SErik.Nordmark@Sun.COM  * usable for SCTP, since SCTP has multiple faddrs.
256311042SErik.Nordmark@Sun.COM  *
256411042SErik.Nordmark@Sun.COM  * Caller must hold conn_lock to provide atomic constency between the
256511042SErik.Nordmark@Sun.COM  * conn_t's addresses and the ixa.
256611042SErik.Nordmark@Sun.COM  * NOTE: this function drops and reaquires conn_lock since it can't be
256711042SErik.Nordmark@Sun.COM  * held across ip_attr_connect/ip_set_destination.
256811042SErik.Nordmark@Sun.COM  *
256911042SErik.Nordmark@Sun.COM  * The caller needs to handle inserting in the receive-side fanout when
257011042SErik.Nordmark@Sun.COM  * appropriate after conn_connect returns.
257111042SErik.Nordmark@Sun.COM  */
257211042SErik.Nordmark@Sun.COM int
257311042SErik.Nordmark@Sun.COM conn_connect(conn_t *connp, iulp_t *uinfo, uint32_t flags)
257411042SErik.Nordmark@Sun.COM {
257511042SErik.Nordmark@Sun.COM 	ip_xmit_attr_t	*ixa = connp->conn_ixa;
257611042SErik.Nordmark@Sun.COM 	in6_addr_t	nexthop;
257711042SErik.Nordmark@Sun.COM 	in6_addr_t	saddr, faddr;
257811042SErik.Nordmark@Sun.COM 	in_port_t	fport;
257911042SErik.Nordmark@Sun.COM 	int		error;
258011042SErik.Nordmark@Sun.COM 
258111042SErik.Nordmark@Sun.COM 	ASSERT(MUTEX_HELD(&connp->conn_lock));
258211042SErik.Nordmark@Sun.COM 
258311042SErik.Nordmark@Sun.COM 	if (connp->conn_ipversion == IPV4_VERSION)
258411042SErik.Nordmark@Sun.COM 		ixa->ixa_flags |= IXAF_IS_IPV4;
258511042SErik.Nordmark@Sun.COM 	else
258611042SErik.Nordmark@Sun.COM 		ixa->ixa_flags &= ~IXAF_IS_IPV4;
258711042SErik.Nordmark@Sun.COM 
258811042SErik.Nordmark@Sun.COM 	/* We do IPsec latching below - hence no caching in ip_attr_connect */
258911042SErik.Nordmark@Sun.COM 	flags &= ~IPDF_IPSEC;
259011042SErik.Nordmark@Sun.COM 
259111042SErik.Nordmark@Sun.COM 	/* In case we had previously done an ip_attr_connect */
259211042SErik.Nordmark@Sun.COM 	ip_attr_newdst(ixa);
259311042SErik.Nordmark@Sun.COM 
259411042SErik.Nordmark@Sun.COM 	/*
259511042SErik.Nordmark@Sun.COM 	 * Determine the nexthop and copy the addresses before dropping
259611042SErik.Nordmark@Sun.COM 	 * conn_lock.
259711042SErik.Nordmark@Sun.COM 	 */
259811042SErik.Nordmark@Sun.COM 	ip_attr_nexthop(&connp->conn_xmit_ipp, connp->conn_ixa,
259911042SErik.Nordmark@Sun.COM 	    &connp->conn_faddr_v6, &nexthop);
260011042SErik.Nordmark@Sun.COM 	saddr = connp->conn_saddr_v6;
260111042SErik.Nordmark@Sun.COM 	faddr = connp->conn_faddr_v6;
260211042SErik.Nordmark@Sun.COM 	fport = connp->conn_fport;
260311042SErik.Nordmark@Sun.COM 
260411042SErik.Nordmark@Sun.COM 	mutex_exit(&connp->conn_lock);
260511042SErik.Nordmark@Sun.COM 	error = ip_attr_connect(connp, ixa, &saddr, &faddr, &nexthop, fport,
260611042SErik.Nordmark@Sun.COM 	    &saddr, uinfo, flags | IPDF_VERIFY_DST);
260711042SErik.Nordmark@Sun.COM 	mutex_enter(&connp->conn_lock);
260811042SErik.Nordmark@Sun.COM 
260911042SErik.Nordmark@Sun.COM 	/* Could have changed even if an error */
261011042SErik.Nordmark@Sun.COM 	connp->conn_saddr_v6 = saddr;
261111042SErik.Nordmark@Sun.COM 	if (error != 0)
261211042SErik.Nordmark@Sun.COM 		return (error);
261311042SErik.Nordmark@Sun.COM 
261411042SErik.Nordmark@Sun.COM 	/*
261511042SErik.Nordmark@Sun.COM 	 * Check whether Trusted Solaris policy allows communication with this
261611042SErik.Nordmark@Sun.COM 	 * host, and pretend that the destination is unreachable if not.
261711042SErik.Nordmark@Sun.COM 	 * Compute any needed label and place it in ipp_label_v4/v6.
261811042SErik.Nordmark@Sun.COM 	 *
261911042SErik.Nordmark@Sun.COM 	 * Later conn_build_hdr_template() takes ipp_label_v4/v6 to form
262011042SErik.Nordmark@Sun.COM 	 * the packet.
262111042SErik.Nordmark@Sun.COM 	 *
262211042SErik.Nordmark@Sun.COM 	 * TSOL Note: Any concurrent threads would pick a different ixa
262311042SErik.Nordmark@Sun.COM 	 * (and ipp if they are to change the ipp)  so we
262411042SErik.Nordmark@Sun.COM 	 * don't have to worry about concurrent threads.
262511042SErik.Nordmark@Sun.COM 	 */
262611042SErik.Nordmark@Sun.COM 	if (is_system_labeled()) {
262711042SErik.Nordmark@Sun.COM 		if (connp->conn_mlp_type != mlptSingle)
262811042SErik.Nordmark@Sun.COM 			return (ECONNREFUSED);
262911042SErik.Nordmark@Sun.COM 
263011042SErik.Nordmark@Sun.COM 		/*
263111042SErik.Nordmark@Sun.COM 		 * conn_update_label will set ipp_label* which will later
263211042SErik.Nordmark@Sun.COM 		 * be used by conn_build_hdr_template.
263311042SErik.Nordmark@Sun.COM 		 */
263411042SErik.Nordmark@Sun.COM 		error = conn_update_label(connp, ixa,
263511042SErik.Nordmark@Sun.COM 		    &connp->conn_faddr_v6, &connp->conn_xmit_ipp);
263611042SErik.Nordmark@Sun.COM 		if (error != 0)
263711042SErik.Nordmark@Sun.COM 			return (error);
263811042SErik.Nordmark@Sun.COM 	}
263911042SErik.Nordmark@Sun.COM 
264011042SErik.Nordmark@Sun.COM 	/*
264111042SErik.Nordmark@Sun.COM 	 * Ensure that we match on the selected local address.
264211042SErik.Nordmark@Sun.COM 	 * This overrides conn_laddr in the case we had earlier bound to a
264311042SErik.Nordmark@Sun.COM 	 * multicast or broadcast address.
264411042SErik.Nordmark@Sun.COM 	 */
264511042SErik.Nordmark@Sun.COM 	connp->conn_laddr_v6 = connp->conn_saddr_v6;
264611042SErik.Nordmark@Sun.COM 
264711042SErik.Nordmark@Sun.COM 	/*
264811042SErik.Nordmark@Sun.COM 	 * Allow setting new policies.
264911042SErik.Nordmark@Sun.COM 	 * The addresses/ports are already set, thus the IPsec policy calls
265011042SErik.Nordmark@Sun.COM 	 * can handle their passed-in conn's.
265111042SErik.Nordmark@Sun.COM 	 */
265211042SErik.Nordmark@Sun.COM 	connp->conn_policy_cached = B_FALSE;
265311042SErik.Nordmark@Sun.COM 
265411042SErik.Nordmark@Sun.COM 	/*
265511042SErik.Nordmark@Sun.COM 	 * Cache IPsec policy in this conn.  If we have per-socket policy,
265611042SErik.Nordmark@Sun.COM 	 * we'll cache that.  If we don't, we'll inherit global policy.
265711042SErik.Nordmark@Sun.COM 	 *
265811042SErik.Nordmark@Sun.COM 	 * This is done before the caller inserts in the receive-side fanout.
265911042SErik.Nordmark@Sun.COM 	 * Note that conn_policy_cached is set by ipsec_conn_cache_policy() even
266011042SErik.Nordmark@Sun.COM 	 * for connections where we don't have a policy. This is to prevent
266111042SErik.Nordmark@Sun.COM 	 * global policy lookups in the inbound path.
266211042SErik.Nordmark@Sun.COM 	 *
266311042SErik.Nordmark@Sun.COM 	 * If we insert before we set conn_policy_cached,
266411042SErik.Nordmark@Sun.COM 	 * CONN_INBOUND_POLICY_PRESENT() check can still evaluate true
266511042SErik.Nordmark@Sun.COM 	 * because global policy cound be non-empty. We normally call
266611042SErik.Nordmark@Sun.COM 	 * ipsec_check_policy() for conn_policy_cached connections only if
266711042SErik.Nordmark@Sun.COM 	 * conn_in_enforce_policy is set. But in this case,
266811042SErik.Nordmark@Sun.COM 	 * conn_policy_cached can get set anytime since we made the
266911042SErik.Nordmark@Sun.COM 	 * CONN_INBOUND_POLICY_PRESENT() check and ipsec_check_policy() is
267011042SErik.Nordmark@Sun.COM 	 * called, which will make the above assumption false.  Thus, we
267111042SErik.Nordmark@Sun.COM 	 * need to insert after we set conn_policy_cached.
267211042SErik.Nordmark@Sun.COM 	 */
267311042SErik.Nordmark@Sun.COM 	error = ipsec_conn_cache_policy(connp,
267411042SErik.Nordmark@Sun.COM 	    connp->conn_ipversion == IPV4_VERSION);
267511042SErik.Nordmark@Sun.COM 	if (error != 0)
267611042SErik.Nordmark@Sun.COM 		return (error);
267711042SErik.Nordmark@Sun.COM 
267811042SErik.Nordmark@Sun.COM 	/*
267911042SErik.Nordmark@Sun.COM 	 * We defer to do LSO check until here since now we have better idea
268011042SErik.Nordmark@Sun.COM 	 * whether IPsec is present. If the underlying ill is LSO capable,
268111042SErik.Nordmark@Sun.COM 	 * copy its capability in so the ULP can decide whether to enable LSO
268211042SErik.Nordmark@Sun.COM 	 * on this connection. So far, only TCP/IPv4 is implemented, so won't
268311042SErik.Nordmark@Sun.COM 	 * claim LSO for IPv6.
268411042SErik.Nordmark@Sun.COM 	 *
268511042SErik.Nordmark@Sun.COM 	 * Currently, won't enable LSO for IRE_LOOPBACK or IRE_LOCAL, because
268611042SErik.Nordmark@Sun.COM 	 * the receiver can not handle it. Also not to enable LSO for MULTIRT.
268711042SErik.Nordmark@Sun.COM 	 */
268811042SErik.Nordmark@Sun.COM 	ixa->ixa_flags &= ~IXAF_LSO_CAPAB;
268911042SErik.Nordmark@Sun.COM 
269011042SErik.Nordmark@Sun.COM 	ASSERT(ixa->ixa_ire != NULL);
269111042SErik.Nordmark@Sun.COM 	if (ixa->ixa_ipst->ips_ip_lso_outbound && (flags & IPDF_LSO) &&
269211042SErik.Nordmark@Sun.COM 	    !(ixa->ixa_flags & IXAF_IPSEC_SECURE) &&
269311042SErik.Nordmark@Sun.COM 	    !(ixa->ixa_ire->ire_type & (IRE_LOCAL | IRE_LOOPBACK)) &&
269411042SErik.Nordmark@Sun.COM 	    !(ixa->ixa_ire->ire_flags & RTF_MULTIRT) &&
269511042SErik.Nordmark@Sun.COM 	    (ixa->ixa_nce != NULL) &&
269611042SErik.Nordmark@Sun.COM 	    ((ixa->ixa_flags & IXAF_IS_IPV4) ?
269711042SErik.Nordmark@Sun.COM 	    ILL_LSO_TCP_IPV4_USABLE(ixa->ixa_nce->nce_ill) :
269811042SErik.Nordmark@Sun.COM 	    ILL_LSO_TCP_IPV6_USABLE(ixa->ixa_nce->nce_ill))) {
269911042SErik.Nordmark@Sun.COM 		ixa->ixa_lso_capab = *ixa->ixa_nce->nce_ill->ill_lso_capab;
270011042SErik.Nordmark@Sun.COM 		ixa->ixa_flags |= IXAF_LSO_CAPAB;
270111042SErik.Nordmark@Sun.COM 	}
270211042SErik.Nordmark@Sun.COM 
270311042SErik.Nordmark@Sun.COM 	/* Check whether ZEROCOPY capability is usable for this connection. */
270411042SErik.Nordmark@Sun.COM 	ixa->ixa_flags &= ~IXAF_ZCOPY_CAPAB;
270511042SErik.Nordmark@Sun.COM 
270611042SErik.Nordmark@Sun.COM 	if ((flags & IPDF_ZCOPY) &&
270711042SErik.Nordmark@Sun.COM 	    !(ixa->ixa_flags & IXAF_IPSEC_SECURE) &&
270811042SErik.Nordmark@Sun.COM 	    !(ixa->ixa_ire->ire_type & (IRE_LOCAL | IRE_LOOPBACK)) &&
270911042SErik.Nordmark@Sun.COM 	    !(ixa->ixa_ire->ire_flags & RTF_MULTIRT) &&
271011042SErik.Nordmark@Sun.COM 	    (ixa->ixa_nce != NULL) &&
271111042SErik.Nordmark@Sun.COM 	    ILL_ZCOPY_USABLE(ixa->ixa_nce->nce_ill)) {
271211042SErik.Nordmark@Sun.COM 		ixa->ixa_flags |= IXAF_ZCOPY_CAPAB;
271311042SErik.Nordmark@Sun.COM 	}
271411042SErik.Nordmark@Sun.COM 	return (0);
271511042SErik.Nordmark@Sun.COM }
271611042SErik.Nordmark@Sun.COM 
271711042SErik.Nordmark@Sun.COM /*
271811042SErik.Nordmark@Sun.COM  * Predicates to check if the addresses match conn_last*
271911042SErik.Nordmark@Sun.COM  */
272011042SErik.Nordmark@Sun.COM 
272111042SErik.Nordmark@Sun.COM /*
272211042SErik.Nordmark@Sun.COM  * Compare the conn against an address.
272311042SErik.Nordmark@Sun.COM  * If using mapped addresses on AF_INET6 sockets, use the _v6 function
272411042SErik.Nordmark@Sun.COM  */
272511042SErik.Nordmark@Sun.COM boolean_t
272611042SErik.Nordmark@Sun.COM conn_same_as_last_v4(conn_t *connp, sin_t *sin)
272711042SErik.Nordmark@Sun.COM {
272811042SErik.Nordmark@Sun.COM 	ASSERT(connp->conn_family == AF_INET);
272911042SErik.Nordmark@Sun.COM 	return (sin->sin_addr.s_addr == connp->conn_v4lastdst &&
273011042SErik.Nordmark@Sun.COM 	    sin->sin_port == connp->conn_lastdstport);
273111042SErik.Nordmark@Sun.COM }
273211042SErik.Nordmark@Sun.COM 
273311042SErik.Nordmark@Sun.COM /*
273411042SErik.Nordmark@Sun.COM  * Compare, including for mapped addresses
273511042SErik.Nordmark@Sun.COM  */
273611042SErik.Nordmark@Sun.COM boolean_t
273711042SErik.Nordmark@Sun.COM conn_same_as_last_v6(conn_t *connp, sin6_t *sin6)
273811042SErik.Nordmark@Sun.COM {
273911042SErik.Nordmark@Sun.COM 	return (IN6_ARE_ADDR_EQUAL(&connp->conn_v6lastdst, &sin6->sin6_addr) &&
274011042SErik.Nordmark@Sun.COM 	    sin6->sin6_port == connp->conn_lastdstport &&
274111042SErik.Nordmark@Sun.COM 	    sin6->sin6_flowinfo == connp->conn_lastflowinfo &&
274211042SErik.Nordmark@Sun.COM 	    sin6->sin6_scope_id == connp->conn_lastscopeid);
274311042SErik.Nordmark@Sun.COM }
274411042SErik.Nordmark@Sun.COM 
274511042SErik.Nordmark@Sun.COM /*
274611042SErik.Nordmark@Sun.COM  * Compute a label and place it in the ip_packet_t.
274711042SErik.Nordmark@Sun.COM  * Handles IPv4 and IPv6.
274811042SErik.Nordmark@Sun.COM  * The caller should have a correct ixa_tsl and ixa_zoneid and have
274911042SErik.Nordmark@Sun.COM  * already called conn_connect or ip_attr_connect to ensure that tsol_check_dest
275011042SErik.Nordmark@Sun.COM  * has been called.
275111042SErik.Nordmark@Sun.COM  */
275211042SErik.Nordmark@Sun.COM int
275311042SErik.Nordmark@Sun.COM conn_update_label(const conn_t *connp, const ip_xmit_attr_t *ixa,
275411042SErik.Nordmark@Sun.COM     const in6_addr_t *v6dst, ip_pkt_t *ipp)
275511042SErik.Nordmark@Sun.COM {
275611042SErik.Nordmark@Sun.COM 	int		err;
275711042SErik.Nordmark@Sun.COM 	ipaddr_t	v4dst;
275811042SErik.Nordmark@Sun.COM 
275911042SErik.Nordmark@Sun.COM 	if (IN6_IS_ADDR_V4MAPPED(v6dst)) {
276011042SErik.Nordmark@Sun.COM 		uchar_t		opt_storage[IP_MAX_OPT_LENGTH];
276111042SErik.Nordmark@Sun.COM 
276211042SErik.Nordmark@Sun.COM 		IN6_V4MAPPED_TO_IPADDR(v6dst, v4dst);
276311042SErik.Nordmark@Sun.COM 
276411042SErik.Nordmark@Sun.COM 		err = tsol_compute_label_v4(ixa->ixa_tsl, ixa->ixa_zoneid,
276511042SErik.Nordmark@Sun.COM 		    v4dst, opt_storage, ixa->ixa_ipst);
276611042SErik.Nordmark@Sun.COM 		if (err == 0) {
276711042SErik.Nordmark@Sun.COM 			/* Length contained in opt_storage[IPOPT_OLEN] */
276811042SErik.Nordmark@Sun.COM 			err = optcom_pkt_set(opt_storage,
276911042SErik.Nordmark@Sun.COM 			    opt_storage[IPOPT_OLEN],
277011042SErik.Nordmark@Sun.COM 			    (uchar_t **)&ipp->ipp_label_v4,
277111042SErik.Nordmark@Sun.COM 			    &ipp->ipp_label_len_v4);
277211042SErik.Nordmark@Sun.COM 		}
277311042SErik.Nordmark@Sun.COM 		if (err != 0) {
277411042SErik.Nordmark@Sun.COM 			DTRACE_PROBE4(tx__ip__log__info__updatelabel,
277511042SErik.Nordmark@Sun.COM 			    char *, "conn(1) failed to update options(2) "
277611042SErik.Nordmark@Sun.COM 			    "on ixa(3)",
277711042SErik.Nordmark@Sun.COM 			    conn_t *, connp, char *, opt_storage,
277811042SErik.Nordmark@Sun.COM 			    ip_xmit_attr_t *, ixa);
277911042SErik.Nordmark@Sun.COM 		}
278011042SErik.Nordmark@Sun.COM 		if (ipp->ipp_label_len_v4 != 0)
278111042SErik.Nordmark@Sun.COM 			ipp->ipp_fields |= IPPF_LABEL_V4;
278211042SErik.Nordmark@Sun.COM 		else
278311042SErik.Nordmark@Sun.COM 			ipp->ipp_fields &= ~IPPF_LABEL_V4;
278411042SErik.Nordmark@Sun.COM 	} else {
278511042SErik.Nordmark@Sun.COM 		uchar_t		opt_storage[TSOL_MAX_IPV6_OPTION];
278611042SErik.Nordmark@Sun.COM 		uint_t		optlen;
278711042SErik.Nordmark@Sun.COM 
278811042SErik.Nordmark@Sun.COM 		err = tsol_compute_label_v6(ixa->ixa_tsl, ixa->ixa_zoneid,
278911042SErik.Nordmark@Sun.COM 		    v6dst, opt_storage, ixa->ixa_ipst);
279011042SErik.Nordmark@Sun.COM 		if (err == 0) {
279111042SErik.Nordmark@Sun.COM 			/*
279211042SErik.Nordmark@Sun.COM 			 * Note that ipp_label_v6 is just the option - not
279311042SErik.Nordmark@Sun.COM 			 * the hopopts extension header.
279411042SErik.Nordmark@Sun.COM 			 *
279511042SErik.Nordmark@Sun.COM 			 * Length contained in opt_storage[IPOPT_OLEN], but
279611042SErik.Nordmark@Sun.COM 			 * that doesn't include the two byte options header.
279711042SErik.Nordmark@Sun.COM 			 */
279811042SErik.Nordmark@Sun.COM 			optlen = opt_storage[IPOPT_OLEN];
279911042SErik.Nordmark@Sun.COM 			if (optlen != 0)
280011042SErik.Nordmark@Sun.COM 				optlen += 2;
280111042SErik.Nordmark@Sun.COM 
280211042SErik.Nordmark@Sun.COM 			err = optcom_pkt_set(opt_storage, optlen,
280311042SErik.Nordmark@Sun.COM 			    (uchar_t **)&ipp->ipp_label_v6,
280411042SErik.Nordmark@Sun.COM 			    &ipp->ipp_label_len_v6);
280511042SErik.Nordmark@Sun.COM 		}
280611042SErik.Nordmark@Sun.COM 		if (err != 0) {
280711042SErik.Nordmark@Sun.COM 			DTRACE_PROBE4(tx__ip__log__info__updatelabel,
280811042SErik.Nordmark@Sun.COM 			    char *, "conn(1) failed to update options(2) "
280911042SErik.Nordmark@Sun.COM 			    "on ixa(3)",
281011042SErik.Nordmark@Sun.COM 			    conn_t *, connp, char *, opt_storage,
281111042SErik.Nordmark@Sun.COM 			    ip_xmit_attr_t *, ixa);
281211042SErik.Nordmark@Sun.COM 		}
281311042SErik.Nordmark@Sun.COM 		if (ipp->ipp_label_len_v6 != 0)
281411042SErik.Nordmark@Sun.COM 			ipp->ipp_fields |= IPPF_LABEL_V6;
281511042SErik.Nordmark@Sun.COM 		else
281611042SErik.Nordmark@Sun.COM 			ipp->ipp_fields &= ~IPPF_LABEL_V6;
281711042SErik.Nordmark@Sun.COM 	}
281811042SErik.Nordmark@Sun.COM 	return (err);
281911042SErik.Nordmark@Sun.COM }
282011042SErik.Nordmark@Sun.COM 
282111042SErik.Nordmark@Sun.COM /*
282211042SErik.Nordmark@Sun.COM  * Inherit all options settings from the parent/listener to the eager.
282311042SErik.Nordmark@Sun.COM  * Returns zero on success; ENOMEM if memory allocation failed.
282411042SErik.Nordmark@Sun.COM  *
282511042SErik.Nordmark@Sun.COM  * We assume that the eager has not had any work done i.e., the conn_ixa
282611042SErik.Nordmark@Sun.COM  * and conn_xmit_ipp are all zero.
282711042SErik.Nordmark@Sun.COM  * Furthermore we assume that no other thread can access the eager (because
282811042SErik.Nordmark@Sun.COM  * it isn't inserted in any fanout list).
282911042SErik.Nordmark@Sun.COM  */
283011042SErik.Nordmark@Sun.COM int
283111042SErik.Nordmark@Sun.COM conn_inherit_parent(conn_t *lconnp, conn_t *econnp)
283211042SErik.Nordmark@Sun.COM {
283311042SErik.Nordmark@Sun.COM 	cred_t	*credp;
283411042SErik.Nordmark@Sun.COM 	int	err;
283511042SErik.Nordmark@Sun.COM 	void	*notify_cookie;
283611352SRao.Shoaib@Sun.COM 	uint32_t xmit_hint;
283711042SErik.Nordmark@Sun.COM 
283811042SErik.Nordmark@Sun.COM 	econnp->conn_family = lconnp->conn_family;
283911042SErik.Nordmark@Sun.COM 	econnp->conn_ipv6_v6only = lconnp->conn_ipv6_v6only;
284011042SErik.Nordmark@Sun.COM 	econnp->conn_wq = lconnp->conn_wq;
284111042SErik.Nordmark@Sun.COM 	econnp->conn_rq = lconnp->conn_rq;
284211042SErik.Nordmark@Sun.COM 
284311042SErik.Nordmark@Sun.COM 	/*
284411042SErik.Nordmark@Sun.COM 	 * Make a safe copy of the transmit attributes.
284511042SErik.Nordmark@Sun.COM 	 * conn_connect will later be used by the caller to setup the ire etc.
284611042SErik.Nordmark@Sun.COM 	 */
284711042SErik.Nordmark@Sun.COM 	ASSERT(econnp->conn_ixa->ixa_refcnt == 1);
284811042SErik.Nordmark@Sun.COM 	ASSERT(econnp->conn_ixa->ixa_ire == NULL);
284911042SErik.Nordmark@Sun.COM 	ASSERT(econnp->conn_ixa->ixa_dce == NULL);
285011042SErik.Nordmark@Sun.COM 	ASSERT(econnp->conn_ixa->ixa_nce == NULL);
285111042SErik.Nordmark@Sun.COM 
285211352SRao.Shoaib@Sun.COM 	/* Preserve ixa_notify_cookie and xmit_hint */
285311042SErik.Nordmark@Sun.COM 	notify_cookie = econnp->conn_ixa->ixa_notify_cookie;
285411352SRao.Shoaib@Sun.COM 	xmit_hint = econnp->conn_ixa->ixa_xmit_hint;
285511042SErik.Nordmark@Sun.COM 	ixa_safe_copy(lconnp->conn_ixa, econnp->conn_ixa);
285611042SErik.Nordmark@Sun.COM 	econnp->conn_ixa->ixa_notify_cookie = notify_cookie;
285711352SRao.Shoaib@Sun.COM 	econnp->conn_ixa->ixa_xmit_hint = xmit_hint;
285811042SErik.Nordmark@Sun.COM 
285911042SErik.Nordmark@Sun.COM 	econnp->conn_bound_if = lconnp->conn_bound_if;
286011042SErik.Nordmark@Sun.COM 	econnp->conn_incoming_ifindex = lconnp->conn_incoming_ifindex;
286111042SErik.Nordmark@Sun.COM 
286211042SErik.Nordmark@Sun.COM 	/* Inherit all RECV options */
286311042SErik.Nordmark@Sun.COM 	econnp->conn_recv_ancillary = lconnp->conn_recv_ancillary;
286411042SErik.Nordmark@Sun.COM 
286511042SErik.Nordmark@Sun.COM 	err = ip_pkt_copy(&lconnp->conn_xmit_ipp, &econnp->conn_xmit_ipp,
286611042SErik.Nordmark@Sun.COM 	    KM_NOSLEEP);
286711042SErik.Nordmark@Sun.COM 	if (err != 0)
286811042SErik.Nordmark@Sun.COM 		return (err);
286911042SErik.Nordmark@Sun.COM 
287011042SErik.Nordmark@Sun.COM 	econnp->conn_zoneid = lconnp->conn_zoneid;
287111042SErik.Nordmark@Sun.COM 	econnp->conn_allzones = lconnp->conn_allzones;
287211042SErik.Nordmark@Sun.COM 
287311042SErik.Nordmark@Sun.COM 	/* This is odd. Pick a flowlabel for each connection instead? */
287411042SErik.Nordmark@Sun.COM 	econnp->conn_flowinfo = lconnp->conn_flowinfo;
287511042SErik.Nordmark@Sun.COM 
287611042SErik.Nordmark@Sun.COM 	econnp->conn_default_ttl = lconnp->conn_default_ttl;
287711042SErik.Nordmark@Sun.COM 
287811042SErik.Nordmark@Sun.COM 	/*
287911042SErik.Nordmark@Sun.COM 	 * TSOL: tsol_input_proc() needs the eager's cred before the
288011042SErik.Nordmark@Sun.COM 	 * eager is accepted
288111042SErik.Nordmark@Sun.COM 	 */
288211042SErik.Nordmark@Sun.COM 	ASSERT(lconnp->conn_cred != NULL);
288311042SErik.Nordmark@Sun.COM 	econnp->conn_cred = credp = lconnp->conn_cred;
288411042SErik.Nordmark@Sun.COM 	crhold(credp);
288511042SErik.Nordmark@Sun.COM 	econnp->conn_cpid = lconnp->conn_cpid;
288611066Srafael.vanoni@sun.com 	econnp->conn_open_time = ddi_get_lbolt64();
288711042SErik.Nordmark@Sun.COM 
288811042SErik.Nordmark@Sun.COM 	/*
288911042SErik.Nordmark@Sun.COM 	 * Cache things in the ixa without any refhold.
289011042SErik.Nordmark@Sun.COM 	 * Listener might not have set up ixa_cred
289111042SErik.Nordmark@Sun.COM 	 */
2892*11849SErik.Nordmark@Sun.COM 	ASSERT(!(econnp->conn_ixa->ixa_free_flags & IXA_FREE_CRED));
289311042SErik.Nordmark@Sun.COM 	econnp->conn_ixa->ixa_cred = econnp->conn_cred;
289411042SErik.Nordmark@Sun.COM 	econnp->conn_ixa->ixa_cpid = econnp->conn_cpid;
289511042SErik.Nordmark@Sun.COM 	if (is_system_labeled())
289611042SErik.Nordmark@Sun.COM 		econnp->conn_ixa->ixa_tsl = crgetlabel(econnp->conn_cred);
289711042SErik.Nordmark@Sun.COM 
289811042SErik.Nordmark@Sun.COM 	/*
289911042SErik.Nordmark@Sun.COM 	 * If the caller has the process-wide flag set, then default to MAC
290011042SErik.Nordmark@Sun.COM 	 * exempt mode.  This allows read-down to unlabeled hosts.
290111042SErik.Nordmark@Sun.COM 	 */
290211042SErik.Nordmark@Sun.COM 	if (getpflags(NET_MAC_AWARE, credp) != 0)
290311042SErik.Nordmark@Sun.COM 		econnp->conn_mac_mode = CONN_MAC_AWARE;
290411042SErik.Nordmark@Sun.COM 
290511042SErik.Nordmark@Sun.COM 	econnp->conn_zone_is_global = lconnp->conn_zone_is_global;
290611042SErik.Nordmark@Sun.COM 
290711042SErik.Nordmark@Sun.COM 	/*
290811042SErik.Nordmark@Sun.COM 	 * We eliminate the need for sockfs to send down a T_SVR4_OPTMGMT_REQ
290911042SErik.Nordmark@Sun.COM 	 * via soaccept()->soinheritoptions() which essentially applies
291011042SErik.Nordmark@Sun.COM 	 * all the listener options to the new connection. The options that we
291111042SErik.Nordmark@Sun.COM 	 * need to take care of are:
291211042SErik.Nordmark@Sun.COM 	 * SO_DEBUG, SO_REUSEADDR, SO_KEEPALIVE, SO_DONTROUTE, SO_BROADCAST,
291311042SErik.Nordmark@Sun.COM 	 * SO_USELOOPBACK, SO_OOBINLINE, SO_DGRAM_ERRIND, SO_LINGER,
291411042SErik.Nordmark@Sun.COM 	 * SO_SNDBUF, SO_RCVBUF.
291511042SErik.Nordmark@Sun.COM 	 *
291611042SErik.Nordmark@Sun.COM 	 * SO_RCVBUF:	conn_rcvbuf is set.
291711042SErik.Nordmark@Sun.COM 	 * SO_SNDBUF:	conn_sndbuf is set.
291811042SErik.Nordmark@Sun.COM 	 */
291911042SErik.Nordmark@Sun.COM 
292011131SErik.Nordmark@Sun.COM 	/* Could we define a struct and use a struct copy for this? */
292111042SErik.Nordmark@Sun.COM 	econnp->conn_sndbuf = lconnp->conn_sndbuf;
292211042SErik.Nordmark@Sun.COM 	econnp->conn_rcvbuf = lconnp->conn_rcvbuf;
292311042SErik.Nordmark@Sun.COM 	econnp->conn_sndlowat = lconnp->conn_sndlowat;
292411042SErik.Nordmark@Sun.COM 	econnp->conn_rcvlowat = lconnp->conn_rcvlowat;
292511042SErik.Nordmark@Sun.COM 	econnp->conn_dgram_errind = lconnp->conn_dgram_errind;
292611042SErik.Nordmark@Sun.COM 	econnp->conn_oobinline = lconnp->conn_oobinline;
292711042SErik.Nordmark@Sun.COM 	econnp->conn_debug = lconnp->conn_debug;
292811042SErik.Nordmark@Sun.COM 	econnp->conn_keepalive = lconnp->conn_keepalive;
292911042SErik.Nordmark@Sun.COM 	econnp->conn_linger = lconnp->conn_linger;
293011042SErik.Nordmark@Sun.COM 	econnp->conn_lingertime = lconnp->conn_lingertime;
293111042SErik.Nordmark@Sun.COM 
293211042SErik.Nordmark@Sun.COM 	/* Set the IP options */
293311042SErik.Nordmark@Sun.COM 	econnp->conn_broadcast = lconnp->conn_broadcast;
293411042SErik.Nordmark@Sun.COM 	econnp->conn_useloopback = lconnp->conn_useloopback;
293511042SErik.Nordmark@Sun.COM 	econnp->conn_reuseaddr = lconnp->conn_reuseaddr;
293611042SErik.Nordmark@Sun.COM 	return (0);
293711042SErik.Nordmark@Sun.COM }
2938