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