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