xref: /illumos-gate/usr/src/uts/common/inet/udp/udp.c (revision da14cebe459d3275048785f25bd869cb09b5307f)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
595579064Sja  * Common Development and Distribution License (the "License").
695579064Sja  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22aa92d85bSgt  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate /* Copyright (c) 1990 Mentat Inc. */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate #include <sys/types.h>
287c478bd9Sstevel@tonic-gate #include <sys/stream.h>
29ff550d0eSmasputra #include <sys/dlpi.h>
30ff550d0eSmasputra #include <sys/pattr.h>
317c478bd9Sstevel@tonic-gate #include <sys/stropts.h>
327c478bd9Sstevel@tonic-gate #include <sys/strlog.h>
337c478bd9Sstevel@tonic-gate #include <sys/strsun.h>
34e4f35dbaSgt #include <sys/time.h>
357c478bd9Sstevel@tonic-gate #define	_SUN_TPI_VERSION 2
367c478bd9Sstevel@tonic-gate #include <sys/tihdr.h>
377c478bd9Sstevel@tonic-gate #include <sys/timod.h>
387c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
397c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
407c478bd9Sstevel@tonic-gate #include <sys/strsubr.h>
417c478bd9Sstevel@tonic-gate #include <sys/suntpi.h>
427c478bd9Sstevel@tonic-gate #include <sys/xti_inet.h>
437c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
447c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
457c478bd9Sstevel@tonic-gate #include <sys/policy.h>
467c478bd9Sstevel@tonic-gate #include <sys/ucred.h>
477c478bd9Sstevel@tonic-gate #include <sys/zone.h>
487c478bd9Sstevel@tonic-gate 
497c478bd9Sstevel@tonic-gate #include <sys/socket.h>
50ff550d0eSmasputra #include <sys/sockio.h>
517c478bd9Sstevel@tonic-gate #include <sys/vtrace.h>
52381a2a9aSdr #include <sys/sdt.h>
537c478bd9Sstevel@tonic-gate #include <sys/debug.h>
547c478bd9Sstevel@tonic-gate #include <sys/isa_defs.h>
557c478bd9Sstevel@tonic-gate #include <sys/random.h>
567c478bd9Sstevel@tonic-gate #include <netinet/in.h>
577c478bd9Sstevel@tonic-gate #include <netinet/ip6.h>
587c478bd9Sstevel@tonic-gate #include <netinet/icmp6.h>
597c478bd9Sstevel@tonic-gate #include <netinet/udp.h>
607c478bd9Sstevel@tonic-gate #include <net/if.h>
61ff550d0eSmasputra #include <net/route.h>
627c478bd9Sstevel@tonic-gate 
637c478bd9Sstevel@tonic-gate #include <inet/common.h>
647c478bd9Sstevel@tonic-gate #include <inet/ip.h>
65ff550d0eSmasputra #include <inet/ip_impl.h>
667c478bd9Sstevel@tonic-gate #include <inet/ip6.h>
677c478bd9Sstevel@tonic-gate #include <inet/ip_ire.h>
68ff550d0eSmasputra #include <inet/ip_if.h>
69ff550d0eSmasputra #include <inet/ip_multi.h>
70c793af95Ssangeeta #include <inet/ip_ndp.h>
717c478bd9Sstevel@tonic-gate #include <inet/mi.h>
727c478bd9Sstevel@tonic-gate #include <inet/mib2.h>
737c478bd9Sstevel@tonic-gate #include <inet/nd.h>
747c478bd9Sstevel@tonic-gate #include <inet/optcom.h>
757c478bd9Sstevel@tonic-gate #include <inet/snmpcom.h>
767c478bd9Sstevel@tonic-gate #include <inet/kstatcom.h>
777c478bd9Sstevel@tonic-gate #include <inet/udp_impl.h>
78ff550d0eSmasputra #include <inet/ipclassifier.h>
79ff550d0eSmasputra #include <inet/ipsec_impl.h>
80ff550d0eSmasputra #include <inet/ipp_common.h>
81*da14cebeSEric Cheng #include <sys/squeue_impl.h>
82b127ac41SPhilip Kirk #include <inet/ipnet.h>
837c478bd9Sstevel@tonic-gate 
847c478bd9Sstevel@tonic-gate /*
85ff550d0eSmasputra  * The ipsec_info.h header file is here since it has the definition for the
867c478bd9Sstevel@tonic-gate  * M_CTL message types used by IP to convey information to the ULP. The
8745916cd2Sjpk  * ipsec_info.h needs the pfkeyv2.h, hence the latter's presence.
887c478bd9Sstevel@tonic-gate  */
897c478bd9Sstevel@tonic-gate #include <net/pfkeyv2.h>
907c478bd9Sstevel@tonic-gate #include <inet/ipsec_info.h>
917c478bd9Sstevel@tonic-gate 
9245916cd2Sjpk #include <sys/tsol/label.h>
9345916cd2Sjpk #include <sys/tsol/tnet.h>
9445916cd2Sjpk #include <rpc/pmap_prot.h>
9545916cd2Sjpk 
967c478bd9Sstevel@tonic-gate /*
977c478bd9Sstevel@tonic-gate  * Synchronization notes:
987c478bd9Sstevel@tonic-gate  *
99fc80c0dfSnordmark  * UDP is MT and uses the usual kernel synchronization primitives. There are 2
100fc80c0dfSnordmark  * locks, the fanout lock (uf_lock) and the udp endpoint lock udp_rwlock.
101fc80c0dfSnordmark  * We also use conn_lock when updating things that affect the IP classifier
102fc80c0dfSnordmark  * lookup.
103fc80c0dfSnordmark  * The lock order is udp_rwlock -> uf_lock and is udp_rwlock -> conn_lock.
1047c478bd9Sstevel@tonic-gate  *
105fc80c0dfSnordmark  * The fanout lock uf_lock:
106ff550d0eSmasputra  * When a UDP endpoint is bound to a local port, it is inserted into
1077c478bd9Sstevel@tonic-gate  * a bind hash list.  The list consists of an array of udp_fanout_t buckets.
1087c478bd9Sstevel@tonic-gate  * The size of the array is controlled by the udp_bind_fanout_size variable.
1097c478bd9Sstevel@tonic-gate  * This variable can be changed in /etc/system if the default value is
110ff550d0eSmasputra  * not large enough.  Each bind hash bucket is protected by a per bucket
111ff550d0eSmasputra  * lock.  It protects the udp_bind_hash and udp_ptpbhn fields in the udp_t
112fc80c0dfSnordmark  * structure and a few other fields in the udp_t. A UDP endpoint is removed
113fc80c0dfSnordmark  * from the bind hash list only when it is being unbound or being closed.
114fc80c0dfSnordmark  * The per bucket lock also protects a UDP endpoint's state changes.
115ff550d0eSmasputra  *
116fc80c0dfSnordmark  * The udp_rwlock:
117fc80c0dfSnordmark  * This protects most of the other fields in the udp_t. The exact list of
118fc80c0dfSnordmark  * fields which are protected by each of the above locks is documented in
119fc80c0dfSnordmark  * the udp_t structure definition.
120ff550d0eSmasputra  *
121fc80c0dfSnordmark  * Plumbing notes:
122fc80c0dfSnordmark  * UDP is always a device driver. For compatibility with mibopen() code
123fc80c0dfSnordmark  * it is possible to I_PUSH "udp", but that results in pushing a passthrough
124fc80c0dfSnordmark  * dummy module.
125ff550d0eSmasputra  *
126fc80c0dfSnordmark  * The above implies that we don't support any intermediate module to
127ff550d0eSmasputra  * reside in between /dev/ip and udp -- in fact, we never supported such
128ff550d0eSmasputra  * scenario in the past as the inter-layer communication semantics have
129fc80c0dfSnordmark  * always been private.
1307c478bd9Sstevel@tonic-gate  */
1317c478bd9Sstevel@tonic-gate 
132f4b3ec61Sdh /* For /etc/system control */
1337c478bd9Sstevel@tonic-gate uint_t udp_bind_fanout_size = UDP_BIND_FANOUT_SIZE;
1347c478bd9Sstevel@tonic-gate 
1357c478bd9Sstevel@tonic-gate #define	NDD_TOO_QUICK_MSG \
13645916cd2Sjpk 	"ndd get info rate too high for non-privileged users, try again " \
1377c478bd9Sstevel@tonic-gate 	"later.\n"
1387c478bd9Sstevel@tonic-gate #define	NDD_OUT_OF_BUF_MSG	"<< Out of buffer >>\n"
1397c478bd9Sstevel@tonic-gate 
14045916cd2Sjpk /* Option processing attrs */
14145916cd2Sjpk typedef struct udpattrs_s {
14219a30e1aSrshoaib 	union {
14319a30e1aSrshoaib 		ip6_pkt_t	*udpattr_ipp6;	/* For V6 */
14419a30e1aSrshoaib 		ip4_pkt_t 	*udpattr_ipp4;	/* For V4 */
14519a30e1aSrshoaib 	} udpattr_ippu;
14619a30e1aSrshoaib #define	udpattr_ipp6 udpattr_ippu.udpattr_ipp6
14719a30e1aSrshoaib #define	udpattr_ipp4 udpattr_ippu.udpattr_ipp4
14845916cd2Sjpk 	mblk_t		*udpattr_mb;
14945916cd2Sjpk 	boolean_t	udpattr_credset;
15045916cd2Sjpk } udpattrs_t;
15145916cd2Sjpk 
1527c478bd9Sstevel@tonic-gate static void	udp_addr_req(queue_t *q, mblk_t *mp);
1537c478bd9Sstevel@tonic-gate static void	udp_bind(queue_t *q, mblk_t *mp);
1547c478bd9Sstevel@tonic-gate static void	udp_bind_hash_insert(udp_fanout_t *uf, udp_t *udp);
1557c478bd9Sstevel@tonic-gate static void	udp_bind_hash_remove(udp_t *udp, boolean_t caller_holds_lock);
156fc80c0dfSnordmark static void	udp_bind_result(conn_t *, mblk_t *);
157fc80c0dfSnordmark static void	udp_bind_ack(conn_t *, mblk_t *mp);
158fc80c0dfSnordmark static void	udp_bind_error(conn_t *, mblk_t *mp);
159fc80c0dfSnordmark static int	udp_build_hdrs(udp_t *udp);
1607c478bd9Sstevel@tonic-gate static void	udp_capability_req(queue_t *q, mblk_t *mp);
1617c478bd9Sstevel@tonic-gate static int	udp_close(queue_t *q);
1627c478bd9Sstevel@tonic-gate static void	udp_connect(queue_t *q, mblk_t *mp);
1637c478bd9Sstevel@tonic-gate static void	udp_disconnect(queue_t *q, mblk_t *mp);
1647c478bd9Sstevel@tonic-gate static void	udp_err_ack(queue_t *q, mblk_t *mp, t_scalar_t t_error,
1657c478bd9Sstevel@tonic-gate 		    int sys_error);
1667c478bd9Sstevel@tonic-gate static void	udp_err_ack_prim(queue_t *q, mblk_t *mp, int primitive,
1677c478bd9Sstevel@tonic-gate 		    t_scalar_t tlierr, int unixerr);
1687c478bd9Sstevel@tonic-gate static int	udp_extra_priv_ports_get(queue_t *q, mblk_t *mp, caddr_t cp,
1697c478bd9Sstevel@tonic-gate 		    cred_t *cr);
1707c478bd9Sstevel@tonic-gate static int	udp_extra_priv_ports_add(queue_t *q, mblk_t *mp,
1717c478bd9Sstevel@tonic-gate 		    char *value, caddr_t cp, cred_t *cr);
1727c478bd9Sstevel@tonic-gate static int	udp_extra_priv_ports_del(queue_t *q, mblk_t *mp,
1737c478bd9Sstevel@tonic-gate 		    char *value, caddr_t cp, cred_t *cr);
1747c478bd9Sstevel@tonic-gate static void	udp_icmp_error(queue_t *q, mblk_t *mp);
1757c478bd9Sstevel@tonic-gate static void	udp_icmp_error_ipv6(queue_t *q, mblk_t *mp);
1767c478bd9Sstevel@tonic-gate static void	udp_info_req(queue_t *q, mblk_t *mp);
177fc80c0dfSnordmark static void	udp_input(void *, mblk_t *, void *);
1787c478bd9Sstevel@tonic-gate static mblk_t	*udp_ip_bind_mp(udp_t *udp, t_scalar_t bind_prim,
1797c478bd9Sstevel@tonic-gate 		    t_scalar_t addr_length);
180fc80c0dfSnordmark static void	udp_lrput(queue_t *, mblk_t *);
181fc80c0dfSnordmark static void	udp_lwput(queue_t *, mblk_t *);
1827c478bd9Sstevel@tonic-gate static int	udp_open(queue_t *q, dev_t *devp, int flag, int sflag,
183fc80c0dfSnordmark 		    cred_t *credp, boolean_t isv6);
184fc80c0dfSnordmark static int	udp_openv4(queue_t *q, dev_t *devp, int flag, int sflag,
185fc80c0dfSnordmark 		    cred_t *credp);
186fc80c0dfSnordmark static int	udp_openv6(queue_t *q, dev_t *devp, int flag, int sflag,
1877c478bd9Sstevel@tonic-gate 		    cred_t *credp);
1887c478bd9Sstevel@tonic-gate static  int	udp_unitdata_opt_process(queue_t *q, mblk_t *mp,
18945916cd2Sjpk 		    int *errorp, udpattrs_t *udpattrs);
1907c478bd9Sstevel@tonic-gate static boolean_t udp_opt_allow_udr_set(t_scalar_t level, t_scalar_t name);
1917c478bd9Sstevel@tonic-gate static int	udp_param_get(queue_t *q, mblk_t *mp, caddr_t cp, cred_t *cr);
192f4b3ec61Sdh static boolean_t udp_param_register(IDP *ndp, udpparam_t *udppa, int cnt);
1937c478bd9Sstevel@tonic-gate static int	udp_param_set(queue_t *q, mblk_t *mp, char *value, caddr_t cp,
1947c478bd9Sstevel@tonic-gate 		    cred_t *cr);
1957c478bd9Sstevel@tonic-gate static void	udp_report_item(mblk_t *mp, udp_t *udp);
196ff550d0eSmasputra static int	udp_rinfop(queue_t *q, infod_t *dp);
197ff550d0eSmasputra static int	udp_rrw(queue_t *q, struiod_t *dp);
1987c478bd9Sstevel@tonic-gate static int	udp_status_report(queue_t *q, mblk_t *mp, caddr_t cp,
1997c478bd9Sstevel@tonic-gate 		    cred_t *cr);
200*da14cebeSEric Cheng static void	udp_send_data(udp_t *udp, queue_t *q, mblk_t *mp,
201*da14cebeSEric Cheng 		    ipha_t *ipha);
202ff550d0eSmasputra static void	udp_ud_err(queue_t *q, mblk_t *mp, uchar_t *destaddr,
203ff550d0eSmasputra 		    t_scalar_t destlen, t_scalar_t err);
2047c478bd9Sstevel@tonic-gate static void	udp_unbind(queue_t *q, mblk_t *mp);
20545916cd2Sjpk static in_port_t udp_update_next_port(udp_t *udp, in_port_t port,
20645916cd2Sjpk     boolean_t random);
207437220cdSdanmcd static mblk_t	*udp_output_v4(conn_t *, mblk_t *, ipaddr_t, uint16_t, uint_t,
208*da14cebeSEric Cheng     int *, boolean_t);
209ff550d0eSmasputra static mblk_t	*udp_output_v6(conn_t *connp, mblk_t *mp, sin6_t *sin6,
21045916cd2Sjpk 		    int *error);
2117c478bd9Sstevel@tonic-gate static void	udp_wput_other(queue_t *q, mblk_t *mp);
2127c478bd9Sstevel@tonic-gate static void	udp_wput_iocdata(queue_t *q, mblk_t *mp);
213ff550d0eSmasputra static size_t	udp_set_rcv_hiwat(udp_t *udp, size_t size);
2147c478bd9Sstevel@tonic-gate 
215f4b3ec61Sdh static void	*udp_stack_init(netstackid_t stackid, netstack_t *ns);
216f4b3ec61Sdh static void	udp_stack_fini(netstackid_t stackid, void *arg);
217f4b3ec61Sdh 
218f4b3ec61Sdh static void	*udp_kstat_init(netstackid_t stackid);
219f4b3ec61Sdh static void	udp_kstat_fini(netstackid_t stackid, kstat_t *ksp);
220f4b3ec61Sdh static void	*udp_kstat2_init(netstackid_t, udp_stat_t *);
221f4b3ec61Sdh static void	udp_kstat2_fini(netstackid_t, kstat_t *);
2227c478bd9Sstevel@tonic-gate static int	udp_kstat_update(kstat_t *kp, int rw);
223ff550d0eSmasputra 
224ff550d0eSmasputra static void	udp_rcv_enqueue(queue_t *q, udp_t *udp, mblk_t *mp,
225ff550d0eSmasputra 		    uint_t pkt_len);
226ff550d0eSmasputra static void	udp_rcv_drain(queue_t *q, udp_t *udp, boolean_t closing);
227fc80c0dfSnordmark static void	udp_xmit(queue_t *, mblk_t *, ire_t *ire, conn_t *, zoneid_t);
228ff550d0eSmasputra 
229ff550d0eSmasputra #define	UDP_RECV_HIWATER	(56 * 1024)
230ff550d0eSmasputra #define	UDP_RECV_LOWATER	128
231ff550d0eSmasputra #define	UDP_XMIT_HIWATER	(56 * 1024)
232ff550d0eSmasputra #define	UDP_XMIT_LOWATER	1024
233ff550d0eSmasputra 
234fc80c0dfSnordmark static struct module_info udp_mod_info =  {
235ff550d0eSmasputra 	UDP_MOD_ID, UDP_MOD_NAME, 1, INFPSZ, UDP_RECV_HIWATER, UDP_RECV_LOWATER
236ff550d0eSmasputra };
2377c478bd9Sstevel@tonic-gate 
238fc80c0dfSnordmark /*
239fc80c0dfSnordmark  * Entry points for UDP as a device.
240fc80c0dfSnordmark  * We have separate open functions for the /dev/udp and /dev/udp6 devices.
241fc80c0dfSnordmark  */
242fc80c0dfSnordmark static struct qinit udp_rinitv4 = {
243fc80c0dfSnordmark 	NULL, NULL, udp_openv4, udp_close, NULL,
244fc80c0dfSnordmark 	&udp_mod_info, NULL, udp_rrw, udp_rinfop, STRUIOT_STANDARD
245fc80c0dfSnordmark };
246fc80c0dfSnordmark 
247fc80c0dfSnordmark static struct qinit udp_rinitv6 = {
248fc80c0dfSnordmark 	NULL, NULL, udp_openv6, udp_close, NULL,
249fc80c0dfSnordmark 	&udp_mod_info, NULL, udp_rrw, udp_rinfop, STRUIOT_STANDARD
250ff550d0eSmasputra };
2517c478bd9Sstevel@tonic-gate 
252ff550d0eSmasputra static struct qinit udp_winit = {
253a9737be2Snordmark 	(pfi_t)udp_wput, (pfi_t)ip_wsrv, NULL, NULL, NULL,
254fc80c0dfSnordmark 	&udp_mod_info, NULL, NULL, NULL, STRUIOT_NONE
255fc80c0dfSnordmark };
256fc80c0dfSnordmark 
257fc80c0dfSnordmark /*
258fc80c0dfSnordmark  * UDP needs to handle I_LINK and I_PLINK since ifconfig
259fc80c0dfSnordmark  * likes to use it as a place to hang the various streams.
260fc80c0dfSnordmark  */
261fc80c0dfSnordmark static struct qinit udp_lrinit = {
262fc80c0dfSnordmark 	(pfi_t)udp_lrput, NULL, udp_openv4, udp_close, NULL,
263fc80c0dfSnordmark 	&udp_mod_info
2647c478bd9Sstevel@tonic-gate };
2657c478bd9Sstevel@tonic-gate 
266fc80c0dfSnordmark static struct qinit udp_lwinit = {
267fc80c0dfSnordmark 	(pfi_t)udp_lwput, NULL, udp_openv4, udp_close, NULL,
268fc80c0dfSnordmark 	&udp_mod_info
2697c478bd9Sstevel@tonic-gate };
2707c478bd9Sstevel@tonic-gate 
271fc80c0dfSnordmark /* For AF_INET aka /dev/udp */
272fc80c0dfSnordmark struct streamtab udpinfov4 = {
273fc80c0dfSnordmark 	&udp_rinitv4, &udp_winit, &udp_lrinit, &udp_lwinit
2747c478bd9Sstevel@tonic-gate };
2757c478bd9Sstevel@tonic-gate 
276fc80c0dfSnordmark /* For AF_INET6 aka /dev/udp6 */
277fc80c0dfSnordmark struct streamtab udpinfov6 = {
278fc80c0dfSnordmark 	&udp_rinitv6, &udp_winit, &udp_lrinit, &udp_lwinit
2797c478bd9Sstevel@tonic-gate };
2807c478bd9Sstevel@tonic-gate 
2817c478bd9Sstevel@tonic-gate static	sin_t	sin_null;	/* Zero address for quick clears */
2827c478bd9Sstevel@tonic-gate static	sin6_t	sin6_null;	/* Zero address for quick clears */
2837c478bd9Sstevel@tonic-gate 
284ff550d0eSmasputra #define	UDP_MAXPACKET_IPV4 (IP_MAXPACKET - UDPH_SIZE - IP_SIMPLE_HDR_LENGTH)
2857c478bd9Sstevel@tonic-gate 
2867c478bd9Sstevel@tonic-gate /* Default structure copied into T_INFO_ACK messages */
2877c478bd9Sstevel@tonic-gate static struct T_info_ack udp_g_t_info_ack_ipv4 = {
2887c478bd9Sstevel@tonic-gate 	T_INFO_ACK,
2897c478bd9Sstevel@tonic-gate 	UDP_MAXPACKET_IPV4,	/* TSDU_size. Excl. headers */
2907c478bd9Sstevel@tonic-gate 	T_INVALID,	/* ETSU_size.  udp does not support expedited data. */
2917c478bd9Sstevel@tonic-gate 	T_INVALID,	/* CDATA_size. udp does not support connect data. */
2927c478bd9Sstevel@tonic-gate 	T_INVALID,	/* DDATA_size. udp does not support disconnect data. */
2937c478bd9Sstevel@tonic-gate 	sizeof (sin_t),	/* ADDR_size. */
2947c478bd9Sstevel@tonic-gate 	0,		/* OPT_size - not initialized here */
2957c478bd9Sstevel@tonic-gate 	UDP_MAXPACKET_IPV4,	/* TIDU_size.  Excl. headers */
2967c478bd9Sstevel@tonic-gate 	T_CLTS,		/* SERV_type.  udp supports connection-less. */
2977c478bd9Sstevel@tonic-gate 	TS_UNBND,	/* CURRENT_state.  This is set from udp_state. */
2987c478bd9Sstevel@tonic-gate 	(XPG4_1|SENDZERO) /* PROVIDER_flag */
2997c478bd9Sstevel@tonic-gate };
3007c478bd9Sstevel@tonic-gate 
301ff550d0eSmasputra #define	UDP_MAXPACKET_IPV6 (IP_MAXPACKET - UDPH_SIZE - IPV6_HDR_LEN)
302ff550d0eSmasputra 
3037c478bd9Sstevel@tonic-gate static	struct T_info_ack udp_g_t_info_ack_ipv6 = {
3047c478bd9Sstevel@tonic-gate 	T_INFO_ACK,
3057c478bd9Sstevel@tonic-gate 	UDP_MAXPACKET_IPV6,	/* TSDU_size.  Excl. headers */
3067c478bd9Sstevel@tonic-gate 	T_INVALID,	/* ETSU_size.  udp does not support expedited data. */
3077c478bd9Sstevel@tonic-gate 	T_INVALID,	/* CDATA_size. udp does not support connect data. */
3087c478bd9Sstevel@tonic-gate 	T_INVALID,	/* DDATA_size. udp does not support disconnect data. */
3097c478bd9Sstevel@tonic-gate 	sizeof (sin6_t), /* ADDR_size. */
3107c478bd9Sstevel@tonic-gate 	0,		/* OPT_size - not initialized here */
3117c478bd9Sstevel@tonic-gate 	UDP_MAXPACKET_IPV6,	/* TIDU_size. Excl. headers */
3127c478bd9Sstevel@tonic-gate 	T_CLTS,		/* SERV_type.  udp supports connection-less. */
3137c478bd9Sstevel@tonic-gate 	TS_UNBND,	/* CURRENT_state.  This is set from udp_state. */
3147c478bd9Sstevel@tonic-gate 	(XPG4_1|SENDZERO) /* PROVIDER_flag */
3157c478bd9Sstevel@tonic-gate };
3167c478bd9Sstevel@tonic-gate 
3177c478bd9Sstevel@tonic-gate /* largest UDP port number */
3187c478bd9Sstevel@tonic-gate #define	UDP_MAX_PORT	65535
3197c478bd9Sstevel@tonic-gate 
3207c478bd9Sstevel@tonic-gate /*
321f4b3ec61Sdh  * Table of ND variables supported by udp.  These are loaded into us_nd
3227c478bd9Sstevel@tonic-gate  * in udp_open.
3237c478bd9Sstevel@tonic-gate  * All of these are alterable, within the min/max values given, at run time.
3247c478bd9Sstevel@tonic-gate  */
325ff550d0eSmasputra /* BEGIN CSTYLED */
326ff550d0eSmasputra udpparam_t udp_param_arr[] = {
327ff550d0eSmasputra  /*min		max		value		name */
328ff550d0eSmasputra  { 0L,		256,		32,		"udp_wroff_extra" },
329ff550d0eSmasputra  { 1L,		255,		255,		"udp_ipv4_ttl" },
330ff550d0eSmasputra  { 0,		IPV6_MAX_HOPS,	IPV6_DEFAULT_HOPS, "udp_ipv6_hoplimit"},
331ff550d0eSmasputra  { 1024,	(32 * 1024),	1024,		"udp_smallest_nonpriv_port" },
332ff550d0eSmasputra  { 0,		1,		1,		"udp_do_checksum" },
333ff550d0eSmasputra  { 1024,	UDP_MAX_PORT,	(32 * 1024),	"udp_smallest_anon_port" },
334ff550d0eSmasputra  { 1024,	UDP_MAX_PORT,	UDP_MAX_PORT,	"udp_largest_anon_port" },
335ff550d0eSmasputra  { UDP_XMIT_LOWATER, (1<<30), UDP_XMIT_HIWATER,	"udp_xmit_hiwat"},
336ff550d0eSmasputra  { 0,		     (1<<30), UDP_XMIT_LOWATER, "udp_xmit_lowat"},
337ff550d0eSmasputra  { UDP_RECV_LOWATER, (1<<30), UDP_RECV_HIWATER,	"udp_recv_hiwat"},
338ff550d0eSmasputra  { 65536,	(1<<30),	2*1024*1024,	"udp_max_buf"},
339ff550d0eSmasputra  { 100,		60000,		1000,		"udp_ndd_get_info_interval"},
3407c478bd9Sstevel@tonic-gate };
341ff550d0eSmasputra /* END CSTYLED */
3427c478bd9Sstevel@tonic-gate 
343f4b3ec61Sdh /* Setable in /etc/system */
3447c478bd9Sstevel@tonic-gate /* If set to 0, pick ephemeral port sequentially; otherwise randomly. */
3457c478bd9Sstevel@tonic-gate uint32_t udp_random_anon_port = 1;
3467c478bd9Sstevel@tonic-gate 
3477c478bd9Sstevel@tonic-gate /*
3487c478bd9Sstevel@tonic-gate  * Hook functions to enable cluster networking.
3497c478bd9Sstevel@tonic-gate  * On non-clustered systems these vectors must always be NULL
3507c478bd9Sstevel@tonic-gate  */
3517c478bd9Sstevel@tonic-gate 
3527c478bd9Sstevel@tonic-gate void (*cl_inet_bind)(uchar_t protocol, sa_family_t addr_family,
353ff550d0eSmasputra     uint8_t *laddrp, in_port_t lport) = NULL;
3547c478bd9Sstevel@tonic-gate void (*cl_inet_unbind)(uint8_t protocol, sa_family_t addr_family,
355ff550d0eSmasputra     uint8_t *laddrp, in_port_t lport) = NULL;
356ff550d0eSmasputra 
357ff550d0eSmasputra typedef union T_primitives *t_primp_t;
358ff550d0eSmasputra 
3597c478bd9Sstevel@tonic-gate /*
36045916cd2Sjpk  * Return the next anonymous port in the privileged port range for
3617c478bd9Sstevel@tonic-gate  * bind checking.
36245916cd2Sjpk  *
36345916cd2Sjpk  * Trusted Extension (TX) notes: TX allows administrator to mark or
36445916cd2Sjpk  * reserve ports as Multilevel ports (MLP). MLP has special function
36545916cd2Sjpk  * on TX systems. Once a port is made MLP, it's not available as
36645916cd2Sjpk  * ordinary port. This creates "holes" in the port name space. It
36745916cd2Sjpk  * may be necessary to skip the "holes" find a suitable anon port.
3687c478bd9Sstevel@tonic-gate  */
3697c478bd9Sstevel@tonic-gate static in_port_t
37045916cd2Sjpk udp_get_next_priv_port(udp_t *udp)
3717c478bd9Sstevel@tonic-gate {
3727c478bd9Sstevel@tonic-gate 	static in_port_t next_priv_port = IPPORT_RESERVED - 1;
37345916cd2Sjpk 	in_port_t nextport;
37445916cd2Sjpk 	boolean_t restart = B_FALSE;
375f4b3ec61Sdh 	udp_stack_t *us = udp->udp_us;
3767c478bd9Sstevel@tonic-gate 
37745916cd2Sjpk retry:
378f4b3ec61Sdh 	if (next_priv_port < us->us_min_anonpriv_port ||
37945916cd2Sjpk 	    next_priv_port >= IPPORT_RESERVED) {
3807c478bd9Sstevel@tonic-gate 		next_priv_port = IPPORT_RESERVED - 1;
38145916cd2Sjpk 		if (restart)
38245916cd2Sjpk 			return (0);
38345916cd2Sjpk 		restart = B_TRUE;
38445916cd2Sjpk 	}
38545916cd2Sjpk 
38645916cd2Sjpk 	if (is_system_labeled() &&
38745916cd2Sjpk 	    (nextport = tsol_next_port(crgetzone(udp->udp_connp->conn_cred),
38845916cd2Sjpk 	    next_priv_port, IPPROTO_UDP, B_FALSE)) != 0) {
38945916cd2Sjpk 		next_priv_port = nextport;
39045916cd2Sjpk 		goto retry;
3917c478bd9Sstevel@tonic-gate 	}
39245916cd2Sjpk 
3937c478bd9Sstevel@tonic-gate 	return (next_priv_port--);
3947c478bd9Sstevel@tonic-gate }
3957c478bd9Sstevel@tonic-gate 
3967c478bd9Sstevel@tonic-gate /* UDP bind hash report triggered via the Named Dispatch mechanism. */
3977c478bd9Sstevel@tonic-gate /* ARGSUSED */
3987c478bd9Sstevel@tonic-gate static int
3997c478bd9Sstevel@tonic-gate udp_bind_hash_report(queue_t *q, mblk_t *mp, caddr_t cp, cred_t *cr)
4007c478bd9Sstevel@tonic-gate {
4017c478bd9Sstevel@tonic-gate 	udp_fanout_t	*udpf;
4027c478bd9Sstevel@tonic-gate 	int		i;
4037c478bd9Sstevel@tonic-gate 	zoneid_t	zoneid;
404ff550d0eSmasputra 	conn_t		*connp;
405ff550d0eSmasputra 	udp_t		*udp;
406f4b3ec61Sdh 	udp_stack_t	*us;
407ff550d0eSmasputra 
408ff550d0eSmasputra 	connp = Q_TO_CONN(q);
409ff550d0eSmasputra 	udp = connp->conn_udp;
410f4b3ec61Sdh 	us = udp->udp_us;
4117c478bd9Sstevel@tonic-gate 
4127c478bd9Sstevel@tonic-gate 	/* Refer to comments in udp_status_report(). */
413f4b3ec61Sdh 	if (cr == NULL || secpolicy_ip_config(cr, B_TRUE) != 0) {
414f4b3ec61Sdh 		if (ddi_get_lbolt() - us->us_last_ndd_get_info_time <
415f4b3ec61Sdh 		    drv_usectohz(us->us_ndd_get_info_interval * 1000)) {
4167c478bd9Sstevel@tonic-gate 			(void) mi_mpprintf(mp, NDD_TOO_QUICK_MSG);
4177c478bd9Sstevel@tonic-gate 			return (0);
4187c478bd9Sstevel@tonic-gate 		}
4197c478bd9Sstevel@tonic-gate 	}
4207c478bd9Sstevel@tonic-gate 	if ((mp->b_cont = allocb(ND_MAX_BUF_LEN, BPRI_HI)) == NULL) {
4217c478bd9Sstevel@tonic-gate 		/* The following may work even if we cannot get a large buf. */
4227c478bd9Sstevel@tonic-gate 		(void) mi_mpprintf(mp, NDD_OUT_OF_BUF_MSG);
4237c478bd9Sstevel@tonic-gate 		return (0);
4247c478bd9Sstevel@tonic-gate 	}
4257c478bd9Sstevel@tonic-gate 
4267c478bd9Sstevel@tonic-gate 	(void) mi_mpprintf(mp,
4277c478bd9Sstevel@tonic-gate 	    "UDP     " MI_COL_HDRPAD_STR
4287c478bd9Sstevel@tonic-gate 	/*   12345678[89ABCDEF] */
4297c478bd9Sstevel@tonic-gate 	    " zone lport src addr        dest addr       port  state");
4307c478bd9Sstevel@tonic-gate 	/*    1234 12345 xxx.xxx.xxx.xxx xxx.xxx.xxx.xxx 12345 UNBOUND */
4317c478bd9Sstevel@tonic-gate 
432ff550d0eSmasputra 	zoneid = connp->conn_zoneid;
4337c478bd9Sstevel@tonic-gate 
434f4b3ec61Sdh 	for (i = 0; i < us->us_bind_fanout_size; i++) {
435f4b3ec61Sdh 		udpf = &us->us_bind_fanout[i];
4367c478bd9Sstevel@tonic-gate 		mutex_enter(&udpf->uf_lock);
4377c478bd9Sstevel@tonic-gate 
4387c478bd9Sstevel@tonic-gate 		/* Print the hash index. */
4397c478bd9Sstevel@tonic-gate 		udp = udpf->uf_udp;
4407c478bd9Sstevel@tonic-gate 		if (zoneid != GLOBAL_ZONEID) {
4417c478bd9Sstevel@tonic-gate 			/* skip to first entry in this zone; might be none */
4427c478bd9Sstevel@tonic-gate 			while (udp != NULL &&
443ff550d0eSmasputra 			    udp->udp_connp->conn_zoneid != zoneid)
4447c478bd9Sstevel@tonic-gate 				udp = udp->udp_bind_hash;
4457c478bd9Sstevel@tonic-gate 		}
4467c478bd9Sstevel@tonic-gate 		if (udp != NULL) {
4477c478bd9Sstevel@tonic-gate 			uint_t print_len, buf_len;
4487c478bd9Sstevel@tonic-gate 
4497c478bd9Sstevel@tonic-gate 			buf_len = mp->b_cont->b_datap->db_lim -
4507c478bd9Sstevel@tonic-gate 			    mp->b_cont->b_wptr;
4517c478bd9Sstevel@tonic-gate 			print_len = snprintf((char *)mp->b_cont->b_wptr,
4527c478bd9Sstevel@tonic-gate 			    buf_len, "%d\n", i);
4537c478bd9Sstevel@tonic-gate 			if (print_len < buf_len) {
4547c478bd9Sstevel@tonic-gate 				mp->b_cont->b_wptr += print_len;
4557c478bd9Sstevel@tonic-gate 			} else {
4567c478bd9Sstevel@tonic-gate 				mp->b_cont->b_wptr += buf_len;
4577c478bd9Sstevel@tonic-gate 			}
4587c478bd9Sstevel@tonic-gate 			for (; udp != NULL; udp = udp->udp_bind_hash) {
4597c478bd9Sstevel@tonic-gate 				if (zoneid == GLOBAL_ZONEID ||
460ff550d0eSmasputra 				    zoneid == udp->udp_connp->conn_zoneid)
4617c478bd9Sstevel@tonic-gate 					udp_report_item(mp->b_cont, udp);
4627c478bd9Sstevel@tonic-gate 			}
4637c478bd9Sstevel@tonic-gate 		}
4647c478bd9Sstevel@tonic-gate 		mutex_exit(&udpf->uf_lock);
4657c478bd9Sstevel@tonic-gate 	}
466f4b3ec61Sdh 	us->us_last_ndd_get_info_time = ddi_get_lbolt();
4677c478bd9Sstevel@tonic-gate 	return (0);
4687c478bd9Sstevel@tonic-gate }
4697c478bd9Sstevel@tonic-gate 
4707c478bd9Sstevel@tonic-gate /*
4717c478bd9Sstevel@tonic-gate  * Hash list removal routine for udp_t structures.
4727c478bd9Sstevel@tonic-gate  */
4737c478bd9Sstevel@tonic-gate static void
4747c478bd9Sstevel@tonic-gate udp_bind_hash_remove(udp_t *udp, boolean_t caller_holds_lock)
4757c478bd9Sstevel@tonic-gate {
4767c478bd9Sstevel@tonic-gate 	udp_t	*udpnext;
4777c478bd9Sstevel@tonic-gate 	kmutex_t *lockp;
478f4b3ec61Sdh 	udp_stack_t *us = udp->udp_us;
4797c478bd9Sstevel@tonic-gate 
4807c478bd9Sstevel@tonic-gate 	if (udp->udp_ptpbhn == NULL)
4817c478bd9Sstevel@tonic-gate 		return;
4827c478bd9Sstevel@tonic-gate 
4837c478bd9Sstevel@tonic-gate 	/*
4847c478bd9Sstevel@tonic-gate 	 * Extract the lock pointer in case there are concurrent
4857c478bd9Sstevel@tonic-gate 	 * hash_remove's for this instance.
4867c478bd9Sstevel@tonic-gate 	 */
4877c478bd9Sstevel@tonic-gate 	ASSERT(udp->udp_port != 0);
4887c478bd9Sstevel@tonic-gate 	if (!caller_holds_lock) {
489f4b3ec61Sdh 		lockp = &us->us_bind_fanout[UDP_BIND_HASH(udp->udp_port,
490437220cdSdanmcd 		    us->us_bind_fanout_size)].uf_lock;
4917c478bd9Sstevel@tonic-gate 		ASSERT(lockp != NULL);
4927c478bd9Sstevel@tonic-gate 		mutex_enter(lockp);
4937c478bd9Sstevel@tonic-gate 	}
4947c478bd9Sstevel@tonic-gate 	if (udp->udp_ptpbhn != NULL) {
4957c478bd9Sstevel@tonic-gate 		udpnext = udp->udp_bind_hash;
4967c478bd9Sstevel@tonic-gate 		if (udpnext != NULL) {
4977c478bd9Sstevel@tonic-gate 			udpnext->udp_ptpbhn = udp->udp_ptpbhn;
4987c478bd9Sstevel@tonic-gate 			udp->udp_bind_hash = NULL;
4997c478bd9Sstevel@tonic-gate 		}
5007c478bd9Sstevel@tonic-gate 		*udp->udp_ptpbhn = udpnext;
5017c478bd9Sstevel@tonic-gate 		udp->udp_ptpbhn = NULL;
5027c478bd9Sstevel@tonic-gate 	}
5037c478bd9Sstevel@tonic-gate 	if (!caller_holds_lock) {
5047c478bd9Sstevel@tonic-gate 		mutex_exit(lockp);
5057c478bd9Sstevel@tonic-gate 	}
5067c478bd9Sstevel@tonic-gate }
5077c478bd9Sstevel@tonic-gate 
5087c478bd9Sstevel@tonic-gate static void
5097c478bd9Sstevel@tonic-gate udp_bind_hash_insert(udp_fanout_t *uf, udp_t *udp)
5107c478bd9Sstevel@tonic-gate {
5117c478bd9Sstevel@tonic-gate 	udp_t	**udpp;
5127c478bd9Sstevel@tonic-gate 	udp_t	*udpnext;
5137c478bd9Sstevel@tonic-gate 
5147c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&uf->uf_lock));
515fc80c0dfSnordmark 	ASSERT(udp->udp_ptpbhn == NULL);
5167c478bd9Sstevel@tonic-gate 	udpp = &uf->uf_udp;
5177c478bd9Sstevel@tonic-gate 	udpnext = udpp[0];
5187c478bd9Sstevel@tonic-gate 	if (udpnext != NULL) {
5197c478bd9Sstevel@tonic-gate 		/*
5207c478bd9Sstevel@tonic-gate 		 * If the new udp bound to the INADDR_ANY address
5217c478bd9Sstevel@tonic-gate 		 * and the first one in the list is not bound to
5227c478bd9Sstevel@tonic-gate 		 * INADDR_ANY we skip all entries until we find the
5237c478bd9Sstevel@tonic-gate 		 * first one bound to INADDR_ANY.
5247c478bd9Sstevel@tonic-gate 		 * This makes sure that applications binding to a
5257c478bd9Sstevel@tonic-gate 		 * specific address get preference over those binding to
5267c478bd9Sstevel@tonic-gate 		 * INADDR_ANY.
5277c478bd9Sstevel@tonic-gate 		 */
5287c478bd9Sstevel@tonic-gate 		if (V6_OR_V4_INADDR_ANY(udp->udp_bound_v6src) &&
5297c478bd9Sstevel@tonic-gate 		    !V6_OR_V4_INADDR_ANY(udpnext->udp_bound_v6src)) {
5307c478bd9Sstevel@tonic-gate 			while ((udpnext = udpp[0]) != NULL &&
5317c478bd9Sstevel@tonic-gate 			    !V6_OR_V4_INADDR_ANY(
5327c478bd9Sstevel@tonic-gate 			    udpnext->udp_bound_v6src)) {
5337c478bd9Sstevel@tonic-gate 				udpp = &(udpnext->udp_bind_hash);
5347c478bd9Sstevel@tonic-gate 			}
5357c478bd9Sstevel@tonic-gate 			if (udpnext != NULL)
5367c478bd9Sstevel@tonic-gate 				udpnext->udp_ptpbhn = &udp->udp_bind_hash;
5377c478bd9Sstevel@tonic-gate 		} else {
5387c478bd9Sstevel@tonic-gate 			udpnext->udp_ptpbhn = &udp->udp_bind_hash;
5397c478bd9Sstevel@tonic-gate 		}
5407c478bd9Sstevel@tonic-gate 	}
5417c478bd9Sstevel@tonic-gate 	udp->udp_bind_hash = udpnext;
5427c478bd9Sstevel@tonic-gate 	udp->udp_ptpbhn = udpp;
5437c478bd9Sstevel@tonic-gate 	udpp[0] = udp;
5447c478bd9Sstevel@tonic-gate }
5457c478bd9Sstevel@tonic-gate 
5467c478bd9Sstevel@tonic-gate /*
5477c478bd9Sstevel@tonic-gate  * This routine is called to handle each O_T_BIND_REQ/T_BIND_REQ message
5487c478bd9Sstevel@tonic-gate  * passed to udp_wput.
5497c478bd9Sstevel@tonic-gate  * It associates a port number and local address with the stream.
5507c478bd9Sstevel@tonic-gate  * The O_T_BIND_REQ/T_BIND_REQ is passed downstream to ip with the UDP
5517c478bd9Sstevel@tonic-gate  * protocol type (IPPROTO_UDP) placed in the message following the address.
5527c478bd9Sstevel@tonic-gate  * A T_BIND_ACK message is passed upstream when ip acknowledges the request.
5537c478bd9Sstevel@tonic-gate  * (Called as writer.)
5547c478bd9Sstevel@tonic-gate  *
5557c478bd9Sstevel@tonic-gate  * Note that UDP over IPv4 and IPv6 sockets can use the same port number
5567c478bd9Sstevel@tonic-gate  * without setting SO_REUSEADDR. This is needed so that they
5577c478bd9Sstevel@tonic-gate  * can be viewed as two independent transport protocols.
5587c478bd9Sstevel@tonic-gate  * However, anonymouns ports are allocated from the same range to avoid
559f4b3ec61Sdh  * duplicating the us->us_next_port_to_try.
5607c478bd9Sstevel@tonic-gate  */
5617c478bd9Sstevel@tonic-gate static void
5627c478bd9Sstevel@tonic-gate udp_bind(queue_t *q, mblk_t *mp)
5637c478bd9Sstevel@tonic-gate {
5647c478bd9Sstevel@tonic-gate 	sin_t		*sin;
5657c478bd9Sstevel@tonic-gate 	sin6_t		*sin6;
5667c478bd9Sstevel@tonic-gate 	mblk_t		*mp1;
5677c478bd9Sstevel@tonic-gate 	in_port_t	port;		/* Host byte order */
5687c478bd9Sstevel@tonic-gate 	in_port_t	requested_port;	/* Host byte order */
5697c478bd9Sstevel@tonic-gate 	struct T_bind_req *tbr;
5707c478bd9Sstevel@tonic-gate 	int		count;
5717c478bd9Sstevel@tonic-gate 	in6_addr_t	v6src;
5727c478bd9Sstevel@tonic-gate 	boolean_t	bind_to_req_port_only;
5737c478bd9Sstevel@tonic-gate 	int		loopmax;
5747c478bd9Sstevel@tonic-gate 	udp_fanout_t	*udpf;
5757c478bd9Sstevel@tonic-gate 	in_port_t	lport;		/* Network byte order */
5767c478bd9Sstevel@tonic-gate 	zoneid_t	zoneid;
577ff550d0eSmasputra 	conn_t		*connp;
578ff550d0eSmasputra 	udp_t		*udp;
57945916cd2Sjpk 	boolean_t	is_inaddr_any;
58045916cd2Sjpk 	mlp_type_t	addrtype, mlptype;
581f4b3ec61Sdh 	udp_stack_t	*us;
5827c478bd9Sstevel@tonic-gate 
583ff550d0eSmasputra 	connp = Q_TO_CONN(q);
584ff550d0eSmasputra 	udp = connp->conn_udp;
585f4b3ec61Sdh 	us = udp->udp_us;
5867c478bd9Sstevel@tonic-gate 	if ((mp->b_wptr - mp->b_rptr) < sizeof (*tbr)) {
5877c478bd9Sstevel@tonic-gate 		(void) mi_strlog(q, 1, SL_ERROR|SL_TRACE,
5887c478bd9Sstevel@tonic-gate 		    "udp_bind: bad req, len %u",
5897c478bd9Sstevel@tonic-gate 		    (uint_t)(mp->b_wptr - mp->b_rptr));
5907c478bd9Sstevel@tonic-gate 		udp_err_ack(q, mp, TPROTO, 0);
5917c478bd9Sstevel@tonic-gate 		return;
5927c478bd9Sstevel@tonic-gate 	}
5937c478bd9Sstevel@tonic-gate 	if (udp->udp_state != TS_UNBND) {
5947c478bd9Sstevel@tonic-gate 		(void) mi_strlog(q, 1, SL_ERROR|SL_TRACE,
5957c478bd9Sstevel@tonic-gate 		    "udp_bind: bad state, %u", udp->udp_state);
5967c478bd9Sstevel@tonic-gate 		udp_err_ack(q, mp, TOUTSTATE, 0);
5977c478bd9Sstevel@tonic-gate 		return;
5987c478bd9Sstevel@tonic-gate 	}
5997c478bd9Sstevel@tonic-gate 	/*
6007c478bd9Sstevel@tonic-gate 	 * Reallocate the message to make sure we have enough room for an
6017c478bd9Sstevel@tonic-gate 	 * address and the protocol type.
6027c478bd9Sstevel@tonic-gate 	 */
6037c478bd9Sstevel@tonic-gate 	mp1 = reallocb(mp, sizeof (struct T_bind_ack) + sizeof (sin6_t) + 1, 1);
6047c478bd9Sstevel@tonic-gate 	if (!mp1) {
6057c478bd9Sstevel@tonic-gate 		udp_err_ack(q, mp, TSYSERR, ENOMEM);
6067c478bd9Sstevel@tonic-gate 		return;
6077c478bd9Sstevel@tonic-gate 	}
6087c478bd9Sstevel@tonic-gate 
6097c478bd9Sstevel@tonic-gate 	mp = mp1;
6107c478bd9Sstevel@tonic-gate 	tbr = (struct T_bind_req *)mp->b_rptr;
6117c478bd9Sstevel@tonic-gate 	switch (tbr->ADDR_length) {
6127c478bd9Sstevel@tonic-gate 	case 0:			/* Request for a generic port */
6137c478bd9Sstevel@tonic-gate 		tbr->ADDR_offset = sizeof (struct T_bind_req);
6147c478bd9Sstevel@tonic-gate 		if (udp->udp_family == AF_INET) {
6157c478bd9Sstevel@tonic-gate 			tbr->ADDR_length = sizeof (sin_t);
6167c478bd9Sstevel@tonic-gate 			sin = (sin_t *)&tbr[1];
6177c478bd9Sstevel@tonic-gate 			*sin = sin_null;
6187c478bd9Sstevel@tonic-gate 			sin->sin_family = AF_INET;
6197c478bd9Sstevel@tonic-gate 			mp->b_wptr = (uchar_t *)&sin[1];
6207c478bd9Sstevel@tonic-gate 		} else {
6217c478bd9Sstevel@tonic-gate 			ASSERT(udp->udp_family == AF_INET6);
6227c478bd9Sstevel@tonic-gate 			tbr->ADDR_length = sizeof (sin6_t);
6237c478bd9Sstevel@tonic-gate 			sin6 = (sin6_t *)&tbr[1];
6247c478bd9Sstevel@tonic-gate 			*sin6 = sin6_null;
6257c478bd9Sstevel@tonic-gate 			sin6->sin6_family = AF_INET6;
6267c478bd9Sstevel@tonic-gate 			mp->b_wptr = (uchar_t *)&sin6[1];
6277c478bd9Sstevel@tonic-gate 		}
6287c478bd9Sstevel@tonic-gate 		port = 0;
6297c478bd9Sstevel@tonic-gate 		break;
6307c478bd9Sstevel@tonic-gate 
6317c478bd9Sstevel@tonic-gate 	case sizeof (sin_t):	/* Complete IPv4 address */
6327c478bd9Sstevel@tonic-gate 		sin = (sin_t *)mi_offset_param(mp, tbr->ADDR_offset,
6337c478bd9Sstevel@tonic-gate 		    sizeof (sin_t));
6347c478bd9Sstevel@tonic-gate 		if (sin == NULL || !OK_32PTR((char *)sin)) {
6357c478bd9Sstevel@tonic-gate 			udp_err_ack(q, mp, TSYSERR, EINVAL);
6367c478bd9Sstevel@tonic-gate 			return;
6377c478bd9Sstevel@tonic-gate 		}
6387c478bd9Sstevel@tonic-gate 		if (udp->udp_family != AF_INET ||
6397c478bd9Sstevel@tonic-gate 		    sin->sin_family != AF_INET) {
6407c478bd9Sstevel@tonic-gate 			udp_err_ack(q, mp, TSYSERR, EAFNOSUPPORT);
6417c478bd9Sstevel@tonic-gate 			return;
6427c478bd9Sstevel@tonic-gate 		}
6437c478bd9Sstevel@tonic-gate 		port = ntohs(sin->sin_port);
6447c478bd9Sstevel@tonic-gate 		break;
6457c478bd9Sstevel@tonic-gate 
6467c478bd9Sstevel@tonic-gate 	case sizeof (sin6_t):	/* complete IPv6 address */
6477c478bd9Sstevel@tonic-gate 		sin6 = (sin6_t *)mi_offset_param(mp, tbr->ADDR_offset,
6487c478bd9Sstevel@tonic-gate 		    sizeof (sin6_t));
6497c478bd9Sstevel@tonic-gate 		if (sin6 == NULL || !OK_32PTR((char *)sin6)) {
6507c478bd9Sstevel@tonic-gate 			udp_err_ack(q, mp, TSYSERR, EINVAL);
6517c478bd9Sstevel@tonic-gate 			return;
6527c478bd9Sstevel@tonic-gate 		}
6537c478bd9Sstevel@tonic-gate 		if (udp->udp_family != AF_INET6 ||
6547c478bd9Sstevel@tonic-gate 		    sin6->sin6_family != AF_INET6) {
6557c478bd9Sstevel@tonic-gate 			udp_err_ack(q, mp, TSYSERR, EAFNOSUPPORT);
6567c478bd9Sstevel@tonic-gate 			return;
6577c478bd9Sstevel@tonic-gate 		}
6587c478bd9Sstevel@tonic-gate 		port = ntohs(sin6->sin6_port);
6597c478bd9Sstevel@tonic-gate 		break;
6607c478bd9Sstevel@tonic-gate 
6617c478bd9Sstevel@tonic-gate 	default:		/* Invalid request */
6627c478bd9Sstevel@tonic-gate 		(void) mi_strlog(q, 1, SL_ERROR|SL_TRACE,
6637c478bd9Sstevel@tonic-gate 		    "udp_bind: bad ADDR_length length %u", tbr->ADDR_length);
6647c478bd9Sstevel@tonic-gate 		udp_err_ack(q, mp, TBADADDR, 0);
6657c478bd9Sstevel@tonic-gate 		return;
6667c478bd9Sstevel@tonic-gate 	}
6677c478bd9Sstevel@tonic-gate 
6687c478bd9Sstevel@tonic-gate 	requested_port = port;
6697c478bd9Sstevel@tonic-gate 
6707c478bd9Sstevel@tonic-gate 	if (requested_port == 0 || tbr->PRIM_type == O_T_BIND_REQ)
6717c478bd9Sstevel@tonic-gate 		bind_to_req_port_only = B_FALSE;
6727c478bd9Sstevel@tonic-gate 	else			/* T_BIND_REQ and requested_port != 0 */
6737c478bd9Sstevel@tonic-gate 		bind_to_req_port_only = B_TRUE;
6747c478bd9Sstevel@tonic-gate 
6757c478bd9Sstevel@tonic-gate 	if (requested_port == 0) {
6767c478bd9Sstevel@tonic-gate 		/*
6777c478bd9Sstevel@tonic-gate 		 * If the application passed in zero for the port number, it
6787c478bd9Sstevel@tonic-gate 		 * doesn't care which port number we bind to. Get one in the
6797c478bd9Sstevel@tonic-gate 		 * valid range.
6807c478bd9Sstevel@tonic-gate 		 */
6817c478bd9Sstevel@tonic-gate 		if (udp->udp_anon_priv_bind) {
68245916cd2Sjpk 			port = udp_get_next_priv_port(udp);
6837c478bd9Sstevel@tonic-gate 		} else {
68445916cd2Sjpk 			port = udp_update_next_port(udp,
685f4b3ec61Sdh 			    us->us_next_port_to_try, B_TRUE);
6867c478bd9Sstevel@tonic-gate 		}
6877c478bd9Sstevel@tonic-gate 	} else {
6887c478bd9Sstevel@tonic-gate 		/*
6897c478bd9Sstevel@tonic-gate 		 * If the port is in the well-known privileged range,
6907c478bd9Sstevel@tonic-gate 		 * make sure the caller was privileged.
6917c478bd9Sstevel@tonic-gate 		 */
6927c478bd9Sstevel@tonic-gate 		int i;
6937c478bd9Sstevel@tonic-gate 		boolean_t priv = B_FALSE;
6947c478bd9Sstevel@tonic-gate 
695f4b3ec61Sdh 		if (port < us->us_smallest_nonpriv_port) {
6967c478bd9Sstevel@tonic-gate 			priv = B_TRUE;
6977c478bd9Sstevel@tonic-gate 		} else {
698f4b3ec61Sdh 			for (i = 0; i < us->us_num_epriv_ports; i++) {
699f4b3ec61Sdh 				if (port == us->us_epriv_ports[i]) {
7007c478bd9Sstevel@tonic-gate 					priv = B_TRUE;
7017c478bd9Sstevel@tonic-gate 					break;
7027c478bd9Sstevel@tonic-gate 				}
7037c478bd9Sstevel@tonic-gate 			}
7047c478bd9Sstevel@tonic-gate 		}
7057c478bd9Sstevel@tonic-gate 
7067c478bd9Sstevel@tonic-gate 		if (priv) {
707ff550d0eSmasputra 			cred_t *cr = DB_CREDDEF(mp, connp->conn_cred);
7087c478bd9Sstevel@tonic-gate 
709ddf7fe95Scasper 			if (secpolicy_net_privaddr(cr, port,
710ddf7fe95Scasper 			    IPPROTO_UDP) != 0) {
7117c478bd9Sstevel@tonic-gate 				udp_err_ack(q, mp, TACCES, 0);
7127c478bd9Sstevel@tonic-gate 				return;
7137c478bd9Sstevel@tonic-gate 			}
7147c478bd9Sstevel@tonic-gate 		}
7157c478bd9Sstevel@tonic-gate 	}
7167c478bd9Sstevel@tonic-gate 
71745916cd2Sjpk 	if (port == 0) {
71845916cd2Sjpk 		udp_err_ack(q, mp, TNOADDR, 0);
71945916cd2Sjpk 		return;
72045916cd2Sjpk 	}
72145916cd2Sjpk 
722fc80c0dfSnordmark 	/*
723fc80c0dfSnordmark 	 * The state must be TS_UNBND. TPI mandates that users must send
724fc80c0dfSnordmark 	 * TPI primitives only 1 at a time and wait for the response before
725fc80c0dfSnordmark 	 * sending the next primitive.
726fc80c0dfSnordmark 	 */
727fc80c0dfSnordmark 	rw_enter(&udp->udp_rwlock, RW_WRITER);
728fc80c0dfSnordmark 	if (udp->udp_state != TS_UNBND || udp->udp_pending_op != -1) {
729fc80c0dfSnordmark 		rw_exit(&udp->udp_rwlock);
730fc80c0dfSnordmark 		(void) mi_strlog(q, 1, SL_ERROR|SL_TRACE,
731fc80c0dfSnordmark 		    "udp_bind: bad state, %u", udp->udp_state);
732fc80c0dfSnordmark 		udp_err_ack(q, mp, TOUTSTATE, 0);
733fc80c0dfSnordmark 		return;
734fc80c0dfSnordmark 	}
735fc80c0dfSnordmark 	udp->udp_pending_op = tbr->PRIM_type;
7367c478bd9Sstevel@tonic-gate 	/*
7377c478bd9Sstevel@tonic-gate 	 * Copy the source address into our udp structure. This address
7387c478bd9Sstevel@tonic-gate 	 * may still be zero; if so, IP will fill in the correct address
739fc80c0dfSnordmark 	 * each time an outbound packet is passed to it. Since the udp is
740fc80c0dfSnordmark 	 * not yet in the bind hash list, we don't grab the uf_lock to
741fc80c0dfSnordmark 	 * change udp_ipversion
7427c478bd9Sstevel@tonic-gate 	 */
7437c478bd9Sstevel@tonic-gate 	if (udp->udp_family == AF_INET) {
7447c478bd9Sstevel@tonic-gate 		ASSERT(sin != NULL);
7457c478bd9Sstevel@tonic-gate 		ASSERT(udp->udp_ipversion == IPV4_VERSION);
7467c478bd9Sstevel@tonic-gate 		udp->udp_max_hdr_len = IP_SIMPLE_HDR_LENGTH + UDPH_SIZE +
7477c478bd9Sstevel@tonic-gate 		    udp->udp_ip_snd_options_len;
7487c478bd9Sstevel@tonic-gate 		IN6_IPADDR_TO_V4MAPPED(sin->sin_addr.s_addr, &v6src);
7497c478bd9Sstevel@tonic-gate 	} else {
7507c478bd9Sstevel@tonic-gate 		ASSERT(sin6 != NULL);
7517c478bd9Sstevel@tonic-gate 		v6src = sin6->sin6_addr;
7527c478bd9Sstevel@tonic-gate 		if (IN6_IS_ADDR_V4MAPPED(&v6src)) {
753fc80c0dfSnordmark 			/*
754fc80c0dfSnordmark 			 * no need to hold the uf_lock to set the udp_ipversion
755fc80c0dfSnordmark 			 * since we are not yet in the fanout list
756fc80c0dfSnordmark 			 */
7577c478bd9Sstevel@tonic-gate 			udp->udp_ipversion = IPV4_VERSION;
7587c478bd9Sstevel@tonic-gate 			udp->udp_max_hdr_len = IP_SIMPLE_HDR_LENGTH +
7597c478bd9Sstevel@tonic-gate 			    UDPH_SIZE + udp->udp_ip_snd_options_len;
7607c478bd9Sstevel@tonic-gate 		} else {
7617c478bd9Sstevel@tonic-gate 			udp->udp_ipversion = IPV6_VERSION;
7627c478bd9Sstevel@tonic-gate 			udp->udp_max_hdr_len = udp->udp_sticky_hdrs_len;
7637c478bd9Sstevel@tonic-gate 		}
7647c478bd9Sstevel@tonic-gate 	}
7657c478bd9Sstevel@tonic-gate 
7667c478bd9Sstevel@tonic-gate 	/*
7677c478bd9Sstevel@tonic-gate 	 * If udp_reuseaddr is not set, then we have to make sure that
7687c478bd9Sstevel@tonic-gate 	 * the IP address and port number the application requested
7697c478bd9Sstevel@tonic-gate 	 * (or we selected for the application) is not being used by
7707c478bd9Sstevel@tonic-gate 	 * another stream.  If another stream is already using the
7717c478bd9Sstevel@tonic-gate 	 * requested IP address and port, the behavior depends on
7727c478bd9Sstevel@tonic-gate 	 * "bind_to_req_port_only". If set the bind fails; otherwise we
7737c478bd9Sstevel@tonic-gate 	 * search for any an unused port to bind to the the stream.
7747c478bd9Sstevel@tonic-gate 	 *
7757c478bd9Sstevel@tonic-gate 	 * As per the BSD semantics, as modified by the Deering multicast
7767c478bd9Sstevel@tonic-gate 	 * changes, if udp_reuseaddr is set, then we allow multiple binds
7777c478bd9Sstevel@tonic-gate 	 * to the same port independent of the local IP address.
7787c478bd9Sstevel@tonic-gate 	 *
7797c478bd9Sstevel@tonic-gate 	 * This is slightly different than in SunOS 4.X which did not
7807c478bd9Sstevel@tonic-gate 	 * support IP multicast. Note that the change implemented by the
7817c478bd9Sstevel@tonic-gate 	 * Deering multicast code effects all binds - not only binding
7827c478bd9Sstevel@tonic-gate 	 * to IP multicast addresses.
7837c478bd9Sstevel@tonic-gate 	 *
7847c478bd9Sstevel@tonic-gate 	 * Note that when binding to port zero we ignore SO_REUSEADDR in
7857c478bd9Sstevel@tonic-gate 	 * order to guarantee a unique port.
7867c478bd9Sstevel@tonic-gate 	 */
7877c478bd9Sstevel@tonic-gate 
7887c478bd9Sstevel@tonic-gate 	count = 0;
7897c478bd9Sstevel@tonic-gate 	if (udp->udp_anon_priv_bind) {
790f4b3ec61Sdh 		/*
791f4b3ec61Sdh 		 * loopmax = (IPPORT_RESERVED-1) -
792f4b3ec61Sdh 		 *    us->us_min_anonpriv_port + 1
793f4b3ec61Sdh 		 */
794f4b3ec61Sdh 		loopmax = IPPORT_RESERVED - us->us_min_anonpriv_port;
7957c478bd9Sstevel@tonic-gate 	} else {
796f4b3ec61Sdh 		loopmax = us->us_largest_anon_port -
797f4b3ec61Sdh 		    us->us_smallest_anon_port + 1;
7987c478bd9Sstevel@tonic-gate 	}
7997c478bd9Sstevel@tonic-gate 
80045916cd2Sjpk 	is_inaddr_any = V6_OR_V4_INADDR_ANY(v6src);
801ff550d0eSmasputra 	zoneid = connp->conn_zoneid;
80245916cd2Sjpk 
8037c478bd9Sstevel@tonic-gate 	for (;;) {
8047c478bd9Sstevel@tonic-gate 		udp_t		*udp1;
8057c478bd9Sstevel@tonic-gate 		boolean_t	found_exclbind = B_FALSE;
8067c478bd9Sstevel@tonic-gate 
8077c478bd9Sstevel@tonic-gate 		/*
8087c478bd9Sstevel@tonic-gate 		 * Walk through the list of udp streams bound to
8097c478bd9Sstevel@tonic-gate 		 * requested port with the same IP address.
8107c478bd9Sstevel@tonic-gate 		 */
8117c478bd9Sstevel@tonic-gate 		lport = htons(port);
812f4b3ec61Sdh 		udpf = &us->us_bind_fanout[UDP_BIND_HASH(lport,
813f4b3ec61Sdh 		    us->us_bind_fanout_size)];
8147c478bd9Sstevel@tonic-gate 		mutex_enter(&udpf->uf_lock);
8157c478bd9Sstevel@tonic-gate 		for (udp1 = udpf->uf_udp; udp1 != NULL;
8167c478bd9Sstevel@tonic-gate 		    udp1 = udp1->udp_bind_hash) {
81745916cd2Sjpk 			if (lport != udp1->udp_port)
81845916cd2Sjpk 				continue;
81945916cd2Sjpk 
82045916cd2Sjpk 			/*
82145916cd2Sjpk 			 * On a labeled system, we must treat bindings to ports
82245916cd2Sjpk 			 * on shared IP addresses by sockets with MAC exemption
82345916cd2Sjpk 			 * privilege as being in all zones, as there's
82445916cd2Sjpk 			 * otherwise no way to identify the right receiver.
82545916cd2Sjpk 			 */
82655e77fafSrk 			if (!(IPCL_ZONE_MATCH(udp1->udp_connp, zoneid) ||
82755e77fafSrk 			    IPCL_ZONE_MATCH(connp,
82855e77fafSrk 			    udp1->udp_connp->conn_zoneid)) &&
829222c5bceSkp 			    !connp->conn_mac_exempt && \
830222c5bceSkp 			    !udp1->udp_connp->conn_mac_exempt)
8317c478bd9Sstevel@tonic-gate 				continue;
8327c478bd9Sstevel@tonic-gate 
8337c478bd9Sstevel@tonic-gate 			/*
8347c478bd9Sstevel@tonic-gate 			 * If UDP_EXCLBIND is set for either the bound or
8357c478bd9Sstevel@tonic-gate 			 * binding endpoint, the semantics of bind
8367c478bd9Sstevel@tonic-gate 			 * is changed according to the following chart.
8377c478bd9Sstevel@tonic-gate 			 *
8387c478bd9Sstevel@tonic-gate 			 * spec = specified address (v4 or v6)
8397c478bd9Sstevel@tonic-gate 			 * unspec = unspecified address (v4 or v6)
8407c478bd9Sstevel@tonic-gate 			 * A = specified addresses are different for endpoints
8417c478bd9Sstevel@tonic-gate 			 *
8427c478bd9Sstevel@tonic-gate 			 * bound	bind to		allowed?
8437c478bd9Sstevel@tonic-gate 			 * -------------------------------------
8447c478bd9Sstevel@tonic-gate 			 * unspec	unspec		no
8457c478bd9Sstevel@tonic-gate 			 * unspec	spec		no
8467c478bd9Sstevel@tonic-gate 			 * spec		unspec		no
8477c478bd9Sstevel@tonic-gate 			 * spec		spec		yes if A
84845916cd2Sjpk 			 *
84945916cd2Sjpk 			 * For labeled systems, SO_MAC_EXEMPT behaves the same
85045916cd2Sjpk 			 * as UDP_EXCLBIND, except that zoneid is ignored.
8517c478bd9Sstevel@tonic-gate 			 */
85245916cd2Sjpk 			if (udp1->udp_exclbind || udp->udp_exclbind ||
853222c5bceSkp 			    udp1->udp_connp->conn_mac_exempt ||
854222c5bceSkp 			    connp->conn_mac_exempt) {
8557c478bd9Sstevel@tonic-gate 				if (V6_OR_V4_INADDR_ANY(
8567c478bd9Sstevel@tonic-gate 				    udp1->udp_bound_v6src) ||
8577c478bd9Sstevel@tonic-gate 				    is_inaddr_any ||
8587c478bd9Sstevel@tonic-gate 				    IN6_ARE_ADDR_EQUAL(&udp1->udp_bound_v6src,
8597c478bd9Sstevel@tonic-gate 				    &v6src)) {
8607c478bd9Sstevel@tonic-gate 					found_exclbind = B_TRUE;
8617c478bd9Sstevel@tonic-gate 					break;
8627c478bd9Sstevel@tonic-gate 				}
8637c478bd9Sstevel@tonic-gate 				continue;
8647c478bd9Sstevel@tonic-gate 			}
8657c478bd9Sstevel@tonic-gate 
8667c478bd9Sstevel@tonic-gate 			/*
8677c478bd9Sstevel@tonic-gate 			 * Check ipversion to allow IPv4 and IPv6 sockets to
868738d543bSdduvall 			 * have disjoint port number spaces.
8697c478bd9Sstevel@tonic-gate 			 */
870f952bcd5Sse 			if (udp->udp_ipversion != udp1->udp_ipversion) {
871f952bcd5Sse 
872f952bcd5Sse 				/*
873f952bcd5Sse 				 * On the first time through the loop, if the
874f952bcd5Sse 				 * the user intentionally specified a
875f952bcd5Sse 				 * particular port number, then ignore any
876f952bcd5Sse 				 * bindings of the other protocol that may
877f952bcd5Sse 				 * conflict. This allows the user to bind IPv6
878f952bcd5Sse 				 * alone and get both v4 and v6, or bind both
879f952bcd5Sse 				 * both and get each seperately. On subsequent
880f952bcd5Sse 				 * times through the loop, we're checking a
881f952bcd5Sse 				 * port that we chose (not the user) and thus
882f952bcd5Sse 				 * we do not allow casual duplicate bindings.
883f952bcd5Sse 				 */
884f952bcd5Sse 				if (count == 0 && requested_port != 0)
885f952bcd5Sse 					continue;
886f952bcd5Sse 			}
8877c478bd9Sstevel@tonic-gate 
8887c478bd9Sstevel@tonic-gate 			/*
8897c478bd9Sstevel@tonic-gate 			 * No difference depending on SO_REUSEADDR.
8907c478bd9Sstevel@tonic-gate 			 *
8917c478bd9Sstevel@tonic-gate 			 * If existing port is bound to a
8927c478bd9Sstevel@tonic-gate 			 * non-wildcard IP address and
8937c478bd9Sstevel@tonic-gate 			 * the requesting stream is bound to
8947c478bd9Sstevel@tonic-gate 			 * a distinct different IP addresses
8957c478bd9Sstevel@tonic-gate 			 * (non-wildcard, also), keep going.
8967c478bd9Sstevel@tonic-gate 			 */
8977c478bd9Sstevel@tonic-gate 			if (!is_inaddr_any &&
8987c478bd9Sstevel@tonic-gate 			    !V6_OR_V4_INADDR_ANY(udp1->udp_bound_v6src) &&
8997c478bd9Sstevel@tonic-gate 			    !IN6_ARE_ADDR_EQUAL(&udp1->udp_bound_v6src,
9007c478bd9Sstevel@tonic-gate 			    &v6src)) {
9017c478bd9Sstevel@tonic-gate 				continue;
9027c478bd9Sstevel@tonic-gate 			}
9037c478bd9Sstevel@tonic-gate 			break;
9047c478bd9Sstevel@tonic-gate 		}
9057c478bd9Sstevel@tonic-gate 
9067c478bd9Sstevel@tonic-gate 		if (!found_exclbind &&
9077c478bd9Sstevel@tonic-gate 		    (udp->udp_reuseaddr && requested_port != 0)) {
9087c478bd9Sstevel@tonic-gate 			break;
9097c478bd9Sstevel@tonic-gate 		}
9107c478bd9Sstevel@tonic-gate 
9117c478bd9Sstevel@tonic-gate 		if (udp1 == NULL) {
9127c478bd9Sstevel@tonic-gate 			/*
9137c478bd9Sstevel@tonic-gate 			 * No other stream has this IP address
9147c478bd9Sstevel@tonic-gate 			 * and port number. We can use it.
9157c478bd9Sstevel@tonic-gate 			 */
9167c478bd9Sstevel@tonic-gate 			break;
9177c478bd9Sstevel@tonic-gate 		}
9187c478bd9Sstevel@tonic-gate 		mutex_exit(&udpf->uf_lock);
9197c478bd9Sstevel@tonic-gate 		if (bind_to_req_port_only) {
9207c478bd9Sstevel@tonic-gate 			/*
9217c478bd9Sstevel@tonic-gate 			 * We get here only when requested port
9227c478bd9Sstevel@tonic-gate 			 * is bound (and only first  of the for()
9237c478bd9Sstevel@tonic-gate 			 * loop iteration).
9247c478bd9Sstevel@tonic-gate 			 *
9257c478bd9Sstevel@tonic-gate 			 * The semantics of this bind request
9267c478bd9Sstevel@tonic-gate 			 * require it to fail so we return from
9277c478bd9Sstevel@tonic-gate 			 * the routine (and exit the loop).
9287c478bd9Sstevel@tonic-gate 			 *
9297c478bd9Sstevel@tonic-gate 			 */
930fc80c0dfSnordmark 			udp->udp_pending_op = -1;
931fc80c0dfSnordmark 			rw_exit(&udp->udp_rwlock);
9327c478bd9Sstevel@tonic-gate 			udp_err_ack(q, mp, TADDRBUSY, 0);
9337c478bd9Sstevel@tonic-gate 			return;
9347c478bd9Sstevel@tonic-gate 		}
9357c478bd9Sstevel@tonic-gate 
9367c478bd9Sstevel@tonic-gate 		if (udp->udp_anon_priv_bind) {
93745916cd2Sjpk 			port = udp_get_next_priv_port(udp);
9387c478bd9Sstevel@tonic-gate 		} else {
9397c478bd9Sstevel@tonic-gate 			if ((count == 0) && (requested_port != 0)) {
9407c478bd9Sstevel@tonic-gate 				/*
9417c478bd9Sstevel@tonic-gate 				 * If the application wants us to find
9427c478bd9Sstevel@tonic-gate 				 * a port, get one to start with. Set
9437c478bd9Sstevel@tonic-gate 				 * requested_port to 0, so that we will
944f4b3ec61Sdh 				 * update us->us_next_port_to_try below.
9457c478bd9Sstevel@tonic-gate 				 */
94645916cd2Sjpk 				port = udp_update_next_port(udp,
947f4b3ec61Sdh 				    us->us_next_port_to_try, B_TRUE);
9487c478bd9Sstevel@tonic-gate 				requested_port = 0;
9497c478bd9Sstevel@tonic-gate 			} else {
95045916cd2Sjpk 				port = udp_update_next_port(udp, port + 1,
95145916cd2Sjpk 				    B_FALSE);
9527c478bd9Sstevel@tonic-gate 			}
9537c478bd9Sstevel@tonic-gate 		}
9547c478bd9Sstevel@tonic-gate 
95545916cd2Sjpk 		if (port == 0 || ++count >= loopmax) {
9567c478bd9Sstevel@tonic-gate 			/*
9577c478bd9Sstevel@tonic-gate 			 * We've tried every possible port number and
9587c478bd9Sstevel@tonic-gate 			 * there are none available, so send an error
9597c478bd9Sstevel@tonic-gate 			 * to the user.
9607c478bd9Sstevel@tonic-gate 			 */
961fc80c0dfSnordmark 			udp->udp_pending_op = -1;
962fc80c0dfSnordmark 			rw_exit(&udp->udp_rwlock);
9637c478bd9Sstevel@tonic-gate 			udp_err_ack(q, mp, TNOADDR, 0);
9647c478bd9Sstevel@tonic-gate 			return;
9657c478bd9Sstevel@tonic-gate 		}
9667c478bd9Sstevel@tonic-gate 	}
9677c478bd9Sstevel@tonic-gate 
9687c478bd9Sstevel@tonic-gate 	/*
9697c478bd9Sstevel@tonic-gate 	 * Copy the source address into our udp structure.  This address
9707c478bd9Sstevel@tonic-gate 	 * may still be zero; if so, ip will fill in the correct address
9717c478bd9Sstevel@tonic-gate 	 * each time an outbound packet is passed to it.
972fc80c0dfSnordmark 	 * If we are binding to a broadcast or multicast address then
973fc80c0dfSnordmark 	 * udp_bind_ack will clear the source address when it receives
974fc80c0dfSnordmark 	 * the T_BIND_ACK.
9757c478bd9Sstevel@tonic-gate 	 */
9767c478bd9Sstevel@tonic-gate 	udp->udp_v6src = udp->udp_bound_v6src = v6src;
9777c478bd9Sstevel@tonic-gate 	udp->udp_port = lport;
9787c478bd9Sstevel@tonic-gate 	/*
9797c478bd9Sstevel@tonic-gate 	 * Now reset the the next anonymous port if the application requested
9807c478bd9Sstevel@tonic-gate 	 * an anonymous port, or we handed out the next anonymous port.
9817c478bd9Sstevel@tonic-gate 	 */
9827c478bd9Sstevel@tonic-gate 	if ((requested_port == 0) && (!udp->udp_anon_priv_bind)) {
983f4b3ec61Sdh 		us->us_next_port_to_try = port + 1;
9847c478bd9Sstevel@tonic-gate 	}
9857c478bd9Sstevel@tonic-gate 
9867c478bd9Sstevel@tonic-gate 	/* Initialize the O_T_BIND_REQ/T_BIND_REQ for ip. */
9877c478bd9Sstevel@tonic-gate 	if (udp->udp_family == AF_INET) {
9887c478bd9Sstevel@tonic-gate 		sin->sin_port = udp->udp_port;
9897c478bd9Sstevel@tonic-gate 	} else {
9907c478bd9Sstevel@tonic-gate 		int error;
9917c478bd9Sstevel@tonic-gate 
9927c478bd9Sstevel@tonic-gate 		sin6->sin6_port = udp->udp_port;
9937c478bd9Sstevel@tonic-gate 		/* Rebuild the header template */
994fc80c0dfSnordmark 		error = udp_build_hdrs(udp);
9957c478bd9Sstevel@tonic-gate 		if (error != 0) {
996fc80c0dfSnordmark 			udp->udp_pending_op = -1;
997fc80c0dfSnordmark 			rw_exit(&udp->udp_rwlock);
9987c478bd9Sstevel@tonic-gate 			mutex_exit(&udpf->uf_lock);
9997c478bd9Sstevel@tonic-gate 			udp_err_ack(q, mp, TSYSERR, error);
10007c478bd9Sstevel@tonic-gate 			return;
10017c478bd9Sstevel@tonic-gate 		}
10027c478bd9Sstevel@tonic-gate 	}
10037c478bd9Sstevel@tonic-gate 	udp->udp_state = TS_IDLE;
10047c478bd9Sstevel@tonic-gate 	udp_bind_hash_insert(udpf, udp);
10057c478bd9Sstevel@tonic-gate 	mutex_exit(&udpf->uf_lock);
1006fc80c0dfSnordmark 	rw_exit(&udp->udp_rwlock);
10077c478bd9Sstevel@tonic-gate 
10087c478bd9Sstevel@tonic-gate 	if (cl_inet_bind) {
10097c478bd9Sstevel@tonic-gate 		/*
10107c478bd9Sstevel@tonic-gate 		 * Running in cluster mode - register bind information
10117c478bd9Sstevel@tonic-gate 		 */
10127c478bd9Sstevel@tonic-gate 		if (udp->udp_ipversion == IPV4_VERSION) {
10137c478bd9Sstevel@tonic-gate 			(*cl_inet_bind)(IPPROTO_UDP, AF_INET,
10147c478bd9Sstevel@tonic-gate 			    (uint8_t *)(&V4_PART_OF_V6(udp->udp_v6src)),
10157c478bd9Sstevel@tonic-gate 			    (in_port_t)udp->udp_port);
10167c478bd9Sstevel@tonic-gate 		} else {
10177c478bd9Sstevel@tonic-gate 			(*cl_inet_bind)(IPPROTO_UDP, AF_INET6,
10187c478bd9Sstevel@tonic-gate 			    (uint8_t *)&(udp->udp_v6src),
10197c478bd9Sstevel@tonic-gate 			    (in_port_t)udp->udp_port);
10207c478bd9Sstevel@tonic-gate 		}
10217c478bd9Sstevel@tonic-gate 
10227c478bd9Sstevel@tonic-gate 	}
10237c478bd9Sstevel@tonic-gate 
102445916cd2Sjpk 	connp->conn_anon_port = (is_system_labeled() && requested_port == 0);
102545916cd2Sjpk 	if (is_system_labeled() && (!connp->conn_anon_port ||
102645916cd2Sjpk 	    connp->conn_anon_mlp)) {
102745916cd2Sjpk 		uint16_t mlpport;
102845916cd2Sjpk 		cred_t *cr = connp->conn_cred;
102945916cd2Sjpk 		zone_t *zone;
103045916cd2Sjpk 
1031f4b3ec61Sdh 		zone = crgetzone(cr);
103245916cd2Sjpk 		connp->conn_mlp_type = udp->udp_recvucred ? mlptBoth :
103345916cd2Sjpk 		    mlptSingle;
1034f4b3ec61Sdh 		addrtype = tsol_mlp_addr_type(zone->zone_id, IPV6_VERSION,
1035fc80c0dfSnordmark 		    &v6src, us->us_netstack->netstack_ip);
103645916cd2Sjpk 		if (addrtype == mlptSingle) {
1037fc80c0dfSnordmark 			rw_enter(&udp->udp_rwlock, RW_WRITER);
1038fc80c0dfSnordmark 			udp->udp_pending_op = -1;
1039fc80c0dfSnordmark 			rw_exit(&udp->udp_rwlock);
104045916cd2Sjpk 			udp_err_ack(q, mp, TNOADDR, 0);
104145916cd2Sjpk 			connp->conn_anon_port = B_FALSE;
104245916cd2Sjpk 			connp->conn_mlp_type = mlptSingle;
104345916cd2Sjpk 			return;
104445916cd2Sjpk 		}
104545916cd2Sjpk 		mlpport = connp->conn_anon_port ? PMAPPORT : port;
104645916cd2Sjpk 		mlptype = tsol_mlp_port_type(zone, IPPROTO_UDP, mlpport,
104745916cd2Sjpk 		    addrtype);
104845916cd2Sjpk 		if (mlptype != mlptSingle &&
104945916cd2Sjpk 		    (connp->conn_mlp_type == mlptSingle ||
105045916cd2Sjpk 		    secpolicy_net_bindmlp(cr) != 0)) {
105145916cd2Sjpk 			if (udp->udp_debug) {
105245916cd2Sjpk 				(void) strlog(UDP_MOD_ID, 0, 1,
105345916cd2Sjpk 				    SL_ERROR|SL_TRACE,
105445916cd2Sjpk 				    "udp_bind: no priv for multilevel port %d",
105545916cd2Sjpk 				    mlpport);
105645916cd2Sjpk 			}
1057fc80c0dfSnordmark 			rw_enter(&udp->udp_rwlock, RW_WRITER);
1058fc80c0dfSnordmark 			udp->udp_pending_op = -1;
1059fc80c0dfSnordmark 			rw_exit(&udp->udp_rwlock);
106045916cd2Sjpk 			udp_err_ack(q, mp, TACCES, 0);
106145916cd2Sjpk 			connp->conn_anon_port = B_FALSE;
106245916cd2Sjpk 			connp->conn_mlp_type = mlptSingle;
106345916cd2Sjpk 			return;
106445916cd2Sjpk 		}
106545916cd2Sjpk 
106645916cd2Sjpk 		/*
106745916cd2Sjpk 		 * If we're specifically binding a shared IP address and the
106845916cd2Sjpk 		 * port is MLP on shared addresses, then check to see if this
106945916cd2Sjpk 		 * zone actually owns the MLP.  Reject if not.
107045916cd2Sjpk 		 */
107145916cd2Sjpk 		if (mlptype == mlptShared && addrtype == mlptShared) {
1072f4b3ec61Sdh 			/*
1073f4b3ec61Sdh 			 * No need to handle exclusive-stack zones since
1074f4b3ec61Sdh 			 * ALL_ZONES only applies to the shared stack.
1075f4b3ec61Sdh 			 */
107645916cd2Sjpk 			zoneid_t mlpzone;
107745916cd2Sjpk 
107845916cd2Sjpk 			mlpzone = tsol_mlp_findzone(IPPROTO_UDP,
107945916cd2Sjpk 			    htons(mlpport));
108045916cd2Sjpk 			if (connp->conn_zoneid != mlpzone) {
108145916cd2Sjpk 				if (udp->udp_debug) {
108245916cd2Sjpk 					(void) strlog(UDP_MOD_ID, 0, 1,
108345916cd2Sjpk 					    SL_ERROR|SL_TRACE,
108445916cd2Sjpk 					    "udp_bind: attempt to bind port "
108545916cd2Sjpk 					    "%d on shared addr in zone %d "
108645916cd2Sjpk 					    "(should be %d)",
108745916cd2Sjpk 					    mlpport, connp->conn_zoneid,
108845916cd2Sjpk 					    mlpzone);
108945916cd2Sjpk 				}
1090fc80c0dfSnordmark 				rw_enter(&udp->udp_rwlock, RW_WRITER);
1091fc80c0dfSnordmark 				udp->udp_pending_op = -1;
1092fc80c0dfSnordmark 				rw_exit(&udp->udp_rwlock);
109345916cd2Sjpk 				udp_err_ack(q, mp, TACCES, 0);
109445916cd2Sjpk 				connp->conn_anon_port = B_FALSE;
109545916cd2Sjpk 				connp->conn_mlp_type = mlptSingle;
109645916cd2Sjpk 				return;
109745916cd2Sjpk 			}
109845916cd2Sjpk 		}
109945916cd2Sjpk 		if (connp->conn_anon_port) {
110045916cd2Sjpk 			int error;
110145916cd2Sjpk 
110245916cd2Sjpk 			error = tsol_mlp_anon(zone, mlptype, connp->conn_ulp,
110345916cd2Sjpk 			    port, B_TRUE);
110445916cd2Sjpk 			if (error != 0) {
110545916cd2Sjpk 				if (udp->udp_debug) {
110645916cd2Sjpk 					(void) strlog(UDP_MOD_ID, 0, 1,
110745916cd2Sjpk 					    SL_ERROR|SL_TRACE,
110845916cd2Sjpk 					    "udp_bind: cannot establish anon "
110945916cd2Sjpk 					    "MLP for port %d", port);
111045916cd2Sjpk 				}
1111fc80c0dfSnordmark 				rw_enter(&udp->udp_rwlock, RW_WRITER);
1112fc80c0dfSnordmark 				udp->udp_pending_op = -1;
1113fc80c0dfSnordmark 				rw_exit(&udp->udp_rwlock);
111445916cd2Sjpk 				udp_err_ack(q, mp, TACCES, 0);
111545916cd2Sjpk 				connp->conn_anon_port = B_FALSE;
111645916cd2Sjpk 				connp->conn_mlp_type = mlptSingle;
111745916cd2Sjpk 				return;
111845916cd2Sjpk 			}
111945916cd2Sjpk 		}
112045916cd2Sjpk 		connp->conn_mlp_type = mlptype;
112145916cd2Sjpk 	}
112245916cd2Sjpk 
11237c478bd9Sstevel@tonic-gate 	/* Pass the protocol number in the message following the address. */
11247c478bd9Sstevel@tonic-gate 	*mp->b_wptr++ = IPPROTO_UDP;
11257c478bd9Sstevel@tonic-gate 	if (!V6_OR_V4_INADDR_ANY(udp->udp_v6src)) {
11267c478bd9Sstevel@tonic-gate 		/*
11277c478bd9Sstevel@tonic-gate 		 * Append a request for an IRE if udp_v6src not
11287c478bd9Sstevel@tonic-gate 		 * zero (IPv4 - INADDR_ANY, or IPv6 - all-zeroes address).
11297c478bd9Sstevel@tonic-gate 		 */
11307c478bd9Sstevel@tonic-gate 		mp->b_cont = allocb(sizeof (ire_t), BPRI_HI);
11317c478bd9Sstevel@tonic-gate 		if (!mp->b_cont) {
1132fc80c0dfSnordmark 			rw_enter(&udp->udp_rwlock, RW_WRITER);
1133fc80c0dfSnordmark 			udp->udp_pending_op = -1;
1134fc80c0dfSnordmark 			rw_exit(&udp->udp_rwlock);
11357c478bd9Sstevel@tonic-gate 			udp_err_ack(q, mp, TSYSERR, ENOMEM);
11367c478bd9Sstevel@tonic-gate 			return;
11377c478bd9Sstevel@tonic-gate 		}
11387c478bd9Sstevel@tonic-gate 		mp->b_cont->b_wptr += sizeof (ire_t);
11397c478bd9Sstevel@tonic-gate 		mp->b_cont->b_datap->db_type = IRE_DB_REQ_TYPE;
11407c478bd9Sstevel@tonic-gate 	}
1141ff550d0eSmasputra 	if (udp->udp_family == AF_INET6)
1142ff550d0eSmasputra 		mp = ip_bind_v6(q, mp, connp, NULL);
1143ff550d0eSmasputra 	else
1144ff550d0eSmasputra 		mp = ip_bind_v4(q, mp, connp);
1145ff550d0eSmasputra 
1146fc80c0dfSnordmark 	/* The above return NULL if the bind needs to be deferred */
1147ff550d0eSmasputra 	if (mp != NULL)
1148fc80c0dfSnordmark 		udp_bind_result(connp, mp);
1149ff550d0eSmasputra 	else
1150ff550d0eSmasputra 		CONN_INC_REF(connp);
1151ff550d0eSmasputra }
1152ff550d0eSmasputra 
1153ff550d0eSmasputra /*
1154fc80c0dfSnordmark  * This is called from ip_wput_nondata to handle the results of a
1155fc80c0dfSnordmark  * deferred UDP bind. It is called once the bind has been completed.
1156ff550d0eSmasputra  */
1157fc80c0dfSnordmark void
1158fc80c0dfSnordmark udp_resume_bind(conn_t *connp, mblk_t *mp)
1159ff550d0eSmasputra {
1160ff550d0eSmasputra 	ASSERT(connp != NULL && IPCL_IS_UDP(connp));
1161ff550d0eSmasputra 
1162fc80c0dfSnordmark 	udp_bind_result(connp, mp);
1163ff550d0eSmasputra 
1164ff550d0eSmasputra 	CONN_OPER_PENDING_DONE(connp);
11657c478bd9Sstevel@tonic-gate }
11667c478bd9Sstevel@tonic-gate 
11677c478bd9Sstevel@tonic-gate /*
11687c478bd9Sstevel@tonic-gate  * This routine handles each T_CONN_REQ message passed to udp.  It
11697c478bd9Sstevel@tonic-gate  * associates a default destination address with the stream.
11707c478bd9Sstevel@tonic-gate  *
11717c478bd9Sstevel@tonic-gate  * This routine sends down a T_BIND_REQ to IP with the following mblks:
11727c478bd9Sstevel@tonic-gate  *	T_BIND_REQ	- specifying local and remote address/port
11737c478bd9Sstevel@tonic-gate  *	IRE_DB_REQ_TYPE	- to get an IRE back containing ire_type and src
11747c478bd9Sstevel@tonic-gate  *	T_OK_ACK	- for the T_CONN_REQ
11757c478bd9Sstevel@tonic-gate  *	T_CONN_CON	- to keep the TPI user happy
11767c478bd9Sstevel@tonic-gate  *
1177fc80c0dfSnordmark  * The connect completes in udp_bind_result.
11787c478bd9Sstevel@tonic-gate  * When a T_BIND_ACK is received information is extracted from the IRE
11797c478bd9Sstevel@tonic-gate  * and the two appended messages are sent to the TPI user.
1180fc80c0dfSnordmark  * Should udp_bind_result receive T_ERROR_ACK for the T_BIND_REQ it will
1181fc80c0dfSnordmark  * convert it to an error ack for the appropriate primitive.
11827c478bd9Sstevel@tonic-gate  */
11837c478bd9Sstevel@tonic-gate static void
11847c478bd9Sstevel@tonic-gate udp_connect(queue_t *q, mblk_t *mp)
11857c478bd9Sstevel@tonic-gate {
11867c478bd9Sstevel@tonic-gate 	sin6_t	*sin6;
11877c478bd9Sstevel@tonic-gate 	sin_t	*sin;
11887c478bd9Sstevel@tonic-gate 	struct T_conn_req	*tcr;
11897c478bd9Sstevel@tonic-gate 	in6_addr_t v6dst;
11907c478bd9Sstevel@tonic-gate 	ipaddr_t v4dst;
11917c478bd9Sstevel@tonic-gate 	uint16_t dstport;
11927c478bd9Sstevel@tonic-gate 	uint32_t flowinfo;
11937c478bd9Sstevel@tonic-gate 	mblk_t	*mp1, *mp2;
11947c478bd9Sstevel@tonic-gate 	udp_fanout_t	*udpf;
1195ff550d0eSmasputra 	udp_t	*udp, *udp1;
1196fc80c0dfSnordmark 	ushort_t	ipversion;
1197f4b3ec61Sdh 	udp_stack_t	*us;
1198fc80c0dfSnordmark 	conn_t		*connp = Q_TO_CONN(q);
1199ff550d0eSmasputra 
1200fc80c0dfSnordmark 	udp = connp->conn_udp;
12017c478bd9Sstevel@tonic-gate 	tcr = (struct T_conn_req *)mp->b_rptr;
1202f4b3ec61Sdh 	us = udp->udp_us;
12037c478bd9Sstevel@tonic-gate 
12047c478bd9Sstevel@tonic-gate 	/* A bit of sanity checking */
12057c478bd9Sstevel@tonic-gate 	if ((mp->b_wptr - mp->b_rptr) < sizeof (struct T_conn_req)) {
12067c478bd9Sstevel@tonic-gate 		udp_err_ack(q, mp, TPROTO, 0);
12077c478bd9Sstevel@tonic-gate 		return;
12087c478bd9Sstevel@tonic-gate 	}
12097c478bd9Sstevel@tonic-gate 
12107c478bd9Sstevel@tonic-gate 	if (tcr->OPT_length != 0) {
12117c478bd9Sstevel@tonic-gate 		udp_err_ack(q, mp, TBADOPT, 0);
12127c478bd9Sstevel@tonic-gate 		return;
12137c478bd9Sstevel@tonic-gate 	}
12147c478bd9Sstevel@tonic-gate 
12157c478bd9Sstevel@tonic-gate 	/*
12167c478bd9Sstevel@tonic-gate 	 * Determine packet type based on type of address passed in
12177c478bd9Sstevel@tonic-gate 	 * the request should contain an IPv4 or IPv6 address.
12187c478bd9Sstevel@tonic-gate 	 * Make sure that address family matches the type of
12197c478bd9Sstevel@tonic-gate 	 * family of the the address passed down
12207c478bd9Sstevel@tonic-gate 	 */
12217c478bd9Sstevel@tonic-gate 	switch (tcr->DEST_length) {
12227c478bd9Sstevel@tonic-gate 	default:
12237c478bd9Sstevel@tonic-gate 		udp_err_ack(q, mp, TBADADDR, 0);
12247c478bd9Sstevel@tonic-gate 		return;
12257c478bd9Sstevel@tonic-gate 
12267c478bd9Sstevel@tonic-gate 	case sizeof (sin_t):
12277c478bd9Sstevel@tonic-gate 		sin = (sin_t *)mi_offset_param(mp, tcr->DEST_offset,
12287c478bd9Sstevel@tonic-gate 		    sizeof (sin_t));
12297c478bd9Sstevel@tonic-gate 		if (sin == NULL || !OK_32PTR((char *)sin)) {
12307c478bd9Sstevel@tonic-gate 			udp_err_ack(q, mp, TSYSERR, EINVAL);
12317c478bd9Sstevel@tonic-gate 			return;
12327c478bd9Sstevel@tonic-gate 		}
12337c478bd9Sstevel@tonic-gate 		if (udp->udp_family != AF_INET ||
12347c478bd9Sstevel@tonic-gate 		    sin->sin_family != AF_INET) {
12357c478bd9Sstevel@tonic-gate 			udp_err_ack(q, mp, TSYSERR, EAFNOSUPPORT);
12367c478bd9Sstevel@tonic-gate 			return;
12377c478bd9Sstevel@tonic-gate 		}
12387c478bd9Sstevel@tonic-gate 		v4dst = sin->sin_addr.s_addr;
12397c478bd9Sstevel@tonic-gate 		dstport = sin->sin_port;
12407c478bd9Sstevel@tonic-gate 		IN6_IPADDR_TO_V4MAPPED(v4dst, &v6dst);
12417c478bd9Sstevel@tonic-gate 		ASSERT(udp->udp_ipversion == IPV4_VERSION);
1242fc80c0dfSnordmark 		ipversion = IPV4_VERSION;
12437c478bd9Sstevel@tonic-gate 		break;
12447c478bd9Sstevel@tonic-gate 
12457c478bd9Sstevel@tonic-gate 	case sizeof (sin6_t):
12467c478bd9Sstevel@tonic-gate 		sin6 = (sin6_t *)mi_offset_param(mp, tcr->DEST_offset,
12477c478bd9Sstevel@tonic-gate 		    sizeof (sin6_t));
12487c478bd9Sstevel@tonic-gate 		if (sin6 == NULL || !OK_32PTR((char *)sin6)) {
12497c478bd9Sstevel@tonic-gate 			udp_err_ack(q, mp, TSYSERR, EINVAL);
12507c478bd9Sstevel@tonic-gate 			return;
12517c478bd9Sstevel@tonic-gate 		}
12527c478bd9Sstevel@tonic-gate 		if (udp->udp_family != AF_INET6 ||
12537c478bd9Sstevel@tonic-gate 		    sin6->sin6_family != AF_INET6) {
12547c478bd9Sstevel@tonic-gate 			udp_err_ack(q, mp, TSYSERR, EAFNOSUPPORT);
12557c478bd9Sstevel@tonic-gate 			return;
12567c478bd9Sstevel@tonic-gate 		}
12577c478bd9Sstevel@tonic-gate 		v6dst = sin6->sin6_addr;
1258fc80c0dfSnordmark 		dstport = sin6->sin6_port;
12597c478bd9Sstevel@tonic-gate 		if (IN6_IS_ADDR_V4MAPPED(&v6dst)) {
12607c478bd9Sstevel@tonic-gate 			IN6_V4MAPPED_TO_IPADDR(&v6dst, v4dst);
1261fc80c0dfSnordmark 			ipversion = IPV4_VERSION;
12627c478bd9Sstevel@tonic-gate 			flowinfo = 0;
12637c478bd9Sstevel@tonic-gate 		} else {
1264fc80c0dfSnordmark 			ipversion = IPV6_VERSION;
12657c478bd9Sstevel@tonic-gate 			flowinfo = sin6->sin6_flowinfo;
12667c478bd9Sstevel@tonic-gate 		}
12677c478bd9Sstevel@tonic-gate 		break;
12687c478bd9Sstevel@tonic-gate 	}
12697c478bd9Sstevel@tonic-gate 	if (dstport == 0) {
12707c478bd9Sstevel@tonic-gate 		udp_err_ack(q, mp, TBADADDR, 0);
12717c478bd9Sstevel@tonic-gate 		return;
12727c478bd9Sstevel@tonic-gate 	}
12737c478bd9Sstevel@tonic-gate 
1274fc80c0dfSnordmark 	rw_enter(&udp->udp_rwlock, RW_WRITER);
1275fc80c0dfSnordmark 
1276fc80c0dfSnordmark 	/*
1277fc80c0dfSnordmark 	 * This UDP must have bound to a port already before doing a connect.
1278fc80c0dfSnordmark 	 * TPI mandates that users must send TPI primitives only 1 at a time
1279fc80c0dfSnordmark 	 * and wait for the response before sending the next primitive.
1280fc80c0dfSnordmark 	 */
1281fc80c0dfSnordmark 	if (udp->udp_state == TS_UNBND || udp->udp_pending_op != -1) {
1282fc80c0dfSnordmark 		rw_exit(&udp->udp_rwlock);
1283fc80c0dfSnordmark 		(void) mi_strlog(q, 1, SL_ERROR|SL_TRACE,
1284fc80c0dfSnordmark 		    "udp_connect: bad state, %u", udp->udp_state);
1285fc80c0dfSnordmark 		udp_err_ack(q, mp, TOUTSTATE, 0);
1286fc80c0dfSnordmark 		return;
1287fc80c0dfSnordmark 	}
1288fc80c0dfSnordmark 	udp->udp_pending_op = T_CONN_REQ;
1289fc80c0dfSnordmark 	ASSERT(udp->udp_port != 0 && udp->udp_ptpbhn != NULL);
1290fc80c0dfSnordmark 
1291fc80c0dfSnordmark 	if (ipversion == IPV4_VERSION) {
1292fc80c0dfSnordmark 		udp->udp_max_hdr_len = IP_SIMPLE_HDR_LENGTH + UDPH_SIZE +
1293fc80c0dfSnordmark 		    udp->udp_ip_snd_options_len;
1294fc80c0dfSnordmark 	} else {
1295fc80c0dfSnordmark 		udp->udp_max_hdr_len = udp->udp_sticky_hdrs_len;
1296fc80c0dfSnordmark 	}
1297fc80c0dfSnordmark 
1298fc80c0dfSnordmark 	udpf = &us->us_bind_fanout[UDP_BIND_HASH(udp->udp_port,
1299fc80c0dfSnordmark 	    us->us_bind_fanout_size)];
1300fc80c0dfSnordmark 
1301fc80c0dfSnordmark 	mutex_enter(&udpf->uf_lock);
1302fc80c0dfSnordmark 	if (udp->udp_state == TS_DATA_XFER) {
1303fc80c0dfSnordmark 		/* Already connected - clear out state */
1304fc80c0dfSnordmark 		udp->udp_v6src = udp->udp_bound_v6src;
1305fc80c0dfSnordmark 		udp->udp_state = TS_IDLE;
1306fc80c0dfSnordmark 	}
1307fc80c0dfSnordmark 
13087c478bd9Sstevel@tonic-gate 	/*
13097c478bd9Sstevel@tonic-gate 	 * Create a default IP header with no IP options.
13107c478bd9Sstevel@tonic-gate 	 */
13117c478bd9Sstevel@tonic-gate 	udp->udp_dstport = dstport;
1312fc80c0dfSnordmark 	udp->udp_ipversion = ipversion;
1313fc80c0dfSnordmark 	if (ipversion == IPV4_VERSION) {
13147c478bd9Sstevel@tonic-gate 		/*
13157c478bd9Sstevel@tonic-gate 		 * Interpret a zero destination to mean loopback.
13167c478bd9Sstevel@tonic-gate 		 * Update the T_CONN_REQ (sin/sin6) since it is used to
13177c478bd9Sstevel@tonic-gate 		 * generate the T_CONN_CON.
13187c478bd9Sstevel@tonic-gate 		 */
13197c478bd9Sstevel@tonic-gate 		if (v4dst == INADDR_ANY) {
13207c478bd9Sstevel@tonic-gate 			v4dst = htonl(INADDR_LOOPBACK);
13217c478bd9Sstevel@tonic-gate 			IN6_IPADDR_TO_V4MAPPED(v4dst, &v6dst);
13227c478bd9Sstevel@tonic-gate 			if (udp->udp_family == AF_INET) {
13237c478bd9Sstevel@tonic-gate 				sin->sin_addr.s_addr = v4dst;
13247c478bd9Sstevel@tonic-gate 			} else {
13257c478bd9Sstevel@tonic-gate 				sin6->sin6_addr = v6dst;
13267c478bd9Sstevel@tonic-gate 			}
13277c478bd9Sstevel@tonic-gate 		}
13287c478bd9Sstevel@tonic-gate 		udp->udp_v6dst = v6dst;
13297c478bd9Sstevel@tonic-gate 		udp->udp_flowinfo = 0;
13307c478bd9Sstevel@tonic-gate 
13317c478bd9Sstevel@tonic-gate 		/*
13327c478bd9Sstevel@tonic-gate 		 * If the destination address is multicast and
13337c478bd9Sstevel@tonic-gate 		 * an outgoing multicast interface has been set,
13347c478bd9Sstevel@tonic-gate 		 * use the address of that interface as our
13357c478bd9Sstevel@tonic-gate 		 * source address if no source address has been set.
13367c478bd9Sstevel@tonic-gate 		 */
13377c478bd9Sstevel@tonic-gate 		if (V4_PART_OF_V6(udp->udp_v6src) == INADDR_ANY &&
13387c478bd9Sstevel@tonic-gate 		    CLASSD(v4dst) &&
13397c478bd9Sstevel@tonic-gate 		    udp->udp_multicast_if_addr != INADDR_ANY) {
13407c478bd9Sstevel@tonic-gate 			IN6_IPADDR_TO_V4MAPPED(udp->udp_multicast_if_addr,
13417c478bd9Sstevel@tonic-gate 			    &udp->udp_v6src);
13427c478bd9Sstevel@tonic-gate 		}
13437c478bd9Sstevel@tonic-gate 	} else {
13447c478bd9Sstevel@tonic-gate 		ASSERT(udp->udp_ipversion == IPV6_VERSION);
13457c478bd9Sstevel@tonic-gate 		/*
13467c478bd9Sstevel@tonic-gate 		 * Interpret a zero destination to mean loopback.
13477c478bd9Sstevel@tonic-gate 		 * Update the T_CONN_REQ (sin/sin6) since it is used to
13487c478bd9Sstevel@tonic-gate 		 * generate the T_CONN_CON.
13497c478bd9Sstevel@tonic-gate 		 */
13507c478bd9Sstevel@tonic-gate 		if (IN6_IS_ADDR_UNSPECIFIED(&v6dst)) {
13517c478bd9Sstevel@tonic-gate 			v6dst = ipv6_loopback;
13527c478bd9Sstevel@tonic-gate 			sin6->sin6_addr = v6dst;
13537c478bd9Sstevel@tonic-gate 		}
13547c478bd9Sstevel@tonic-gate 		udp->udp_v6dst = v6dst;
13557c478bd9Sstevel@tonic-gate 		udp->udp_flowinfo = flowinfo;
13567c478bd9Sstevel@tonic-gate 		/*
13577c478bd9Sstevel@tonic-gate 		 * If the destination address is multicast and
13587c478bd9Sstevel@tonic-gate 		 * an outgoing multicast interface has been set,
13597c478bd9Sstevel@tonic-gate 		 * then the ip bind logic will pick the correct source
13607c478bd9Sstevel@tonic-gate 		 * address (i.e. matching the outgoing multicast interface).
13617c478bd9Sstevel@tonic-gate 		 */
13627c478bd9Sstevel@tonic-gate 	}
13637c478bd9Sstevel@tonic-gate 
13647c478bd9Sstevel@tonic-gate 	/*
1365fc80c0dfSnordmark 	 * Verify that the src/port/dst/port is unique for all
13667c478bd9Sstevel@tonic-gate 	 * connections in TS_DATA_XFER
13677c478bd9Sstevel@tonic-gate 	 */
13687c478bd9Sstevel@tonic-gate 	for (udp1 = udpf->uf_udp; udp1 != NULL; udp1 = udp1->udp_bind_hash) {
13697c478bd9Sstevel@tonic-gate 		if (udp1->udp_state != TS_DATA_XFER)
13707c478bd9Sstevel@tonic-gate 			continue;
13717c478bd9Sstevel@tonic-gate 		if (udp->udp_port != udp1->udp_port ||
13727c478bd9Sstevel@tonic-gate 		    udp->udp_ipversion != udp1->udp_ipversion ||
13737c478bd9Sstevel@tonic-gate 		    dstport != udp1->udp_dstport ||
13747c478bd9Sstevel@tonic-gate 		    !IN6_ARE_ADDR_EQUAL(&udp->udp_v6src, &udp1->udp_v6src) ||
13759b3fddaeSrk 		    !IN6_ARE_ADDR_EQUAL(&v6dst, &udp1->udp_v6dst) ||
137655e77fafSrk 		    !(IPCL_ZONE_MATCH(udp->udp_connp,
137755e77fafSrk 		    udp1->udp_connp->conn_zoneid) ||
137855e77fafSrk 		    IPCL_ZONE_MATCH(udp1->udp_connp,
137955e77fafSrk 		    udp->udp_connp->conn_zoneid)))
13807c478bd9Sstevel@tonic-gate 			continue;
13817c478bd9Sstevel@tonic-gate 		mutex_exit(&udpf->uf_lock);
1382fc80c0dfSnordmark 		udp->udp_pending_op = -1;
1383fc80c0dfSnordmark 		rw_exit(&udp->udp_rwlock);
13847c478bd9Sstevel@tonic-gate 		udp_err_ack(q, mp, TBADADDR, 0);
13857c478bd9Sstevel@tonic-gate 		return;
13867c478bd9Sstevel@tonic-gate 	}
13877c478bd9Sstevel@tonic-gate 	udp->udp_state = TS_DATA_XFER;
13887c478bd9Sstevel@tonic-gate 	mutex_exit(&udpf->uf_lock);
13897c478bd9Sstevel@tonic-gate 
13907c478bd9Sstevel@tonic-gate 	/*
13917c478bd9Sstevel@tonic-gate 	 * Send down bind to IP to verify that there is a route
13927c478bd9Sstevel@tonic-gate 	 * and to determine the source address.
13937c478bd9Sstevel@tonic-gate 	 * This will come back as T_BIND_ACK with an IRE_DB_TYPE in rput.
13947c478bd9Sstevel@tonic-gate 	 */
13957c478bd9Sstevel@tonic-gate 	if (udp->udp_family == AF_INET)
13967c478bd9Sstevel@tonic-gate 		mp1 = udp_ip_bind_mp(udp, O_T_BIND_REQ, sizeof (ipa_conn_t));
13977c478bd9Sstevel@tonic-gate 	else
13987c478bd9Sstevel@tonic-gate 		mp1 = udp_ip_bind_mp(udp, O_T_BIND_REQ, sizeof (ipa6_conn_t));
13997c478bd9Sstevel@tonic-gate 	if (mp1 == NULL) {
14007c478bd9Sstevel@tonic-gate bind_failed:
14017c478bd9Sstevel@tonic-gate 		mutex_enter(&udpf->uf_lock);
14027c478bd9Sstevel@tonic-gate 		udp->udp_state = TS_IDLE;
1403fc80c0dfSnordmark 		udp->udp_pending_op = -1;
14047c478bd9Sstevel@tonic-gate 		mutex_exit(&udpf->uf_lock);
1405fc80c0dfSnordmark 		rw_exit(&udp->udp_rwlock);
1406fc80c0dfSnordmark 		udp_err_ack(q, mp, TSYSERR, ENOMEM);
14077c478bd9Sstevel@tonic-gate 		return;
14087c478bd9Sstevel@tonic-gate 	}
14097c478bd9Sstevel@tonic-gate 
1410fc80c0dfSnordmark 	rw_exit(&udp->udp_rwlock);
14117c478bd9Sstevel@tonic-gate 	/*
14127c478bd9Sstevel@tonic-gate 	 * We also have to send a connection confirmation to
1413fc80c0dfSnordmark 	 * keep TLI happy. Prepare it for udp_bind_result.
14147c478bd9Sstevel@tonic-gate 	 */
14157c478bd9Sstevel@tonic-gate 	if (udp->udp_family == AF_INET)
14167c478bd9Sstevel@tonic-gate 		mp2 = mi_tpi_conn_con(NULL, (char *)sin,
14177c478bd9Sstevel@tonic-gate 		    sizeof (*sin), NULL, 0);
14187c478bd9Sstevel@tonic-gate 	else
14197c478bd9Sstevel@tonic-gate 		mp2 = mi_tpi_conn_con(NULL, (char *)sin6,
14207c478bd9Sstevel@tonic-gate 		    sizeof (*sin6), NULL, 0);
14217c478bd9Sstevel@tonic-gate 	if (mp2 == NULL) {
14227c478bd9Sstevel@tonic-gate 		freemsg(mp1);
1423fc80c0dfSnordmark 		rw_enter(&udp->udp_rwlock, RW_WRITER);
14247c478bd9Sstevel@tonic-gate 		goto bind_failed;
14257c478bd9Sstevel@tonic-gate 	}
14267c478bd9Sstevel@tonic-gate 
14277c478bd9Sstevel@tonic-gate 	mp = mi_tpi_ok_ack_alloc(mp);
14287c478bd9Sstevel@tonic-gate 	if (mp == NULL) {
14297c478bd9Sstevel@tonic-gate 		/* Unable to reuse the T_CONN_REQ for the ack. */
14307c478bd9Sstevel@tonic-gate 		freemsg(mp2);
1431fc80c0dfSnordmark 		rw_enter(&udp->udp_rwlock, RW_WRITER);
1432fc80c0dfSnordmark 		mutex_enter(&udpf->uf_lock);
1433fc80c0dfSnordmark 		udp->udp_state = TS_IDLE;
1434fc80c0dfSnordmark 		udp->udp_pending_op = -1;
1435fc80c0dfSnordmark 		mutex_exit(&udpf->uf_lock);
1436fc80c0dfSnordmark 		rw_exit(&udp->udp_rwlock);
14377c478bd9Sstevel@tonic-gate 		udp_err_ack_prim(q, mp1, T_CONN_REQ, TSYSERR, ENOMEM);
1438fc80c0dfSnordmark 		return;
14397c478bd9Sstevel@tonic-gate 	}
14407c478bd9Sstevel@tonic-gate 
14417c478bd9Sstevel@tonic-gate 	/* Hang onto the T_OK_ACK and T_CONN_CON for later. */
14427c478bd9Sstevel@tonic-gate 	linkb(mp1, mp);
14437c478bd9Sstevel@tonic-gate 	linkb(mp1, mp2);
14447c478bd9Sstevel@tonic-gate 
1445fc80c0dfSnordmark 	mblk_setcred(mp1, connp->conn_cred);
1446ff550d0eSmasputra 	if (udp->udp_family == AF_INET)
1447fc80c0dfSnordmark 		mp1 = ip_bind_v4(q, mp1, connp);
1448ff550d0eSmasputra 	else
1449fc80c0dfSnordmark 		mp1 = ip_bind_v6(q, mp1, connp, NULL);
1450ff550d0eSmasputra 
1451fc80c0dfSnordmark 	/* The above return NULL if the bind needs to be deferred */
1452ff550d0eSmasputra 	if (mp1 != NULL)
1453fc80c0dfSnordmark 		udp_bind_result(connp, mp1);
1454ff550d0eSmasputra 	else
1455fc80c0dfSnordmark 		CONN_INC_REF(connp);
14567c478bd9Sstevel@tonic-gate }
14577c478bd9Sstevel@tonic-gate 
14587c478bd9Sstevel@tonic-gate static int
14597c478bd9Sstevel@tonic-gate udp_close(queue_t *q)
14607c478bd9Sstevel@tonic-gate {
1461fc80c0dfSnordmark 	conn_t	*connp = (conn_t *)q->q_ptr;
1462ff550d0eSmasputra 	udp_t	*udp;
14637c478bd9Sstevel@tonic-gate 
1464ff550d0eSmasputra 	ASSERT(connp != NULL && IPCL_IS_UDP(connp));
1465ff550d0eSmasputra 	udp = connp->conn_udp;
1466ff550d0eSmasputra 
1467fc80c0dfSnordmark 	udp_quiesce_conn(connp);
1468ff550d0eSmasputra 	ip_quiesce_conn(connp);
1469ff550d0eSmasputra 	/*
1470ff550d0eSmasputra 	 * Disable read-side synchronous stream
1471ff550d0eSmasputra 	 * interface and drain any queued data.
1472ff550d0eSmasputra 	 */
1473ff550d0eSmasputra 	udp_rcv_drain(q, udp, B_TRUE);
1474ff550d0eSmasputra 	ASSERT(!udp->udp_direct_sockfs);
14757c478bd9Sstevel@tonic-gate 
14767c478bd9Sstevel@tonic-gate 	qprocsoff(q);
14777c478bd9Sstevel@tonic-gate 
1478ff550d0eSmasputra 	ASSERT(udp->udp_rcv_cnt == 0);
1479ff550d0eSmasputra 	ASSERT(udp->udp_rcv_msgcnt == 0);
1480ff550d0eSmasputra 	ASSERT(udp->udp_rcv_list_head == NULL);
1481ff550d0eSmasputra 	ASSERT(udp->udp_rcv_list_tail == NULL);
1482ff550d0eSmasputra 
1483ff550d0eSmasputra 	udp_close_free(connp);
1484c62a497dSgeorges 
1485ff550d0eSmasputra 	/*
1486fc80c0dfSnordmark 	 * Now we are truly single threaded on this stream, and can
1487fc80c0dfSnordmark 	 * delete the things hanging off the connp, and finally the connp.
1488fc80c0dfSnordmark 	 * We removed this connp from the fanout list, it cannot be
1489fc80c0dfSnordmark 	 * accessed thru the fanouts, and we already waited for the
1490fc80c0dfSnordmark 	 * conn_ref to drop to 0. We are already in close, so
1491fc80c0dfSnordmark 	 * there cannot be any other thread from the top. qprocsoff
1492fc80c0dfSnordmark 	 * has completed, and service has completed or won't run in
1493fc80c0dfSnordmark 	 * future.
1494ff550d0eSmasputra 	 */
1495fc80c0dfSnordmark 	ASSERT(connp->conn_ref == 1);
1496aa92d85bSgt 	inet_minor_free(connp->conn_minor_arena, connp->conn_dev);
1497fc80c0dfSnordmark 	connp->conn_ref--;
1498fc80c0dfSnordmark 	ipcl_conn_destroy(connp);
1499fc80c0dfSnordmark 
1500fc80c0dfSnordmark 	q->q_ptr = WR(q)->q_ptr = NULL;
1501ff550d0eSmasputra 	return (0);
1502ff550d0eSmasputra }
1503ff550d0eSmasputra 
1504ff550d0eSmasputra /*
1505fc80c0dfSnordmark  * Called in the close path to quiesce the conn
1506ff550d0eSmasputra  */
1507ff550d0eSmasputra void
1508ff550d0eSmasputra udp_quiesce_conn(conn_t *connp)
1509ff550d0eSmasputra {
1510ff550d0eSmasputra 	udp_t	*udp = connp->conn_udp;
1511ff550d0eSmasputra 
15127c478bd9Sstevel@tonic-gate 	if (cl_inet_unbind != NULL && udp->udp_state == TS_IDLE) {
15137c478bd9Sstevel@tonic-gate 		/*
15147c478bd9Sstevel@tonic-gate 		 * Running in cluster mode - register unbind information
15157c478bd9Sstevel@tonic-gate 		 */
15167c478bd9Sstevel@tonic-gate 		if (udp->udp_ipversion == IPV4_VERSION) {
15177c478bd9Sstevel@tonic-gate 			(*cl_inet_unbind)(IPPROTO_UDP, AF_INET,
15187c478bd9Sstevel@tonic-gate 			    (uint8_t *)(&(V4_PART_OF_V6(udp->udp_v6src))),
15197c478bd9Sstevel@tonic-gate 			    (in_port_t)udp->udp_port);
15207c478bd9Sstevel@tonic-gate 		} else {
15217c478bd9Sstevel@tonic-gate 			(*cl_inet_unbind)(IPPROTO_UDP, AF_INET6,
15227c478bd9Sstevel@tonic-gate 			    (uint8_t *)(&(udp->udp_v6src)),
15237c478bd9Sstevel@tonic-gate 			    (in_port_t)udp->udp_port);
15247c478bd9Sstevel@tonic-gate 		}
15257c478bd9Sstevel@tonic-gate 	}
15267c478bd9Sstevel@tonic-gate 
15277c478bd9Sstevel@tonic-gate 	udp_bind_hash_remove(udp, B_FALSE);
1528ff550d0eSmasputra 
1529ff550d0eSmasputra }
1530ff550d0eSmasputra 
1531ff550d0eSmasputra void
1532ff550d0eSmasputra udp_close_free(conn_t *connp)
1533ff550d0eSmasputra {
1534ff550d0eSmasputra 	udp_t *udp = connp->conn_udp;
1535ff550d0eSmasputra 
15367c478bd9Sstevel@tonic-gate 	/* If there are any options associated with the stream, free them. */
1537e845e33dSnordmark 	if (udp->udp_ip_snd_options != NULL) {
15387c478bd9Sstevel@tonic-gate 		mi_free((char *)udp->udp_ip_snd_options);
1539ff550d0eSmasputra 		udp->udp_ip_snd_options = NULL;
1540e845e33dSnordmark 		udp->udp_ip_snd_options_len = 0;
1541ff550d0eSmasputra 	}
15427c478bd9Sstevel@tonic-gate 
1543e845e33dSnordmark 	if (udp->udp_ip_rcv_options != NULL) {
15447c478bd9Sstevel@tonic-gate 		mi_free((char *)udp->udp_ip_rcv_options);
1545ff550d0eSmasputra 		udp->udp_ip_rcv_options = NULL;
1546e845e33dSnordmark 		udp->udp_ip_rcv_options_len = 0;
1547ff550d0eSmasputra 	}
15487c478bd9Sstevel@tonic-gate 
15497c478bd9Sstevel@tonic-gate 	/* Free memory associated with sticky options */
15507c478bd9Sstevel@tonic-gate 	if (udp->udp_sticky_hdrs_len != 0) {
15517c478bd9Sstevel@tonic-gate 		kmem_free(udp->udp_sticky_hdrs,
15527c478bd9Sstevel@tonic-gate 		    udp->udp_sticky_hdrs_len);
15537c478bd9Sstevel@tonic-gate 		udp->udp_sticky_hdrs = NULL;
15547c478bd9Sstevel@tonic-gate 		udp->udp_sticky_hdrs_len = 0;
15557c478bd9Sstevel@tonic-gate 	}
1556ff550d0eSmasputra 
155745916cd2Sjpk 	ip6_pkt_free(&udp->udp_sticky_ipp);
1558e845e33dSnordmark 
1559e845e33dSnordmark 	/*
1560e845e33dSnordmark 	 * Clear any fields which the kmem_cache constructor clears.
1561e845e33dSnordmark 	 * Only udp_connp needs to be preserved.
1562e845e33dSnordmark 	 * TBD: We should make this more efficient to avoid clearing
1563e845e33dSnordmark 	 * everything.
1564e845e33dSnordmark 	 */
1565e845e33dSnordmark 	ASSERT(udp->udp_connp == connp);
1566e845e33dSnordmark 	bzero(udp, sizeof (udp_t));
1567e845e33dSnordmark 	udp->udp_connp = connp;
15687c478bd9Sstevel@tonic-gate }
15697c478bd9Sstevel@tonic-gate 
15707c478bd9Sstevel@tonic-gate /*
15717c478bd9Sstevel@tonic-gate  * This routine handles each T_DISCON_REQ message passed to udp
15727c478bd9Sstevel@tonic-gate  * as an indicating that UDP is no longer connected. This results
15737c478bd9Sstevel@tonic-gate  * in sending a T_BIND_REQ to IP to restore the binding to just
15747c478bd9Sstevel@tonic-gate  * the local address/port.
15757c478bd9Sstevel@tonic-gate  *
15767c478bd9Sstevel@tonic-gate  * This routine sends down a T_BIND_REQ to IP with the following mblks:
15777c478bd9Sstevel@tonic-gate  *	T_BIND_REQ	- specifying just the local address/port
15787c478bd9Sstevel@tonic-gate  *	T_OK_ACK	- for the T_DISCON_REQ
15797c478bd9Sstevel@tonic-gate  *
1580fc80c0dfSnordmark  * The disconnect completes in udp_bind_result.
15817c478bd9Sstevel@tonic-gate  * When a T_BIND_ACK is received the appended T_OK_ACK is sent to the TPI user.
1582fc80c0dfSnordmark  * Should udp_bind_result receive T_ERROR_ACK for the T_BIND_REQ it will
1583fc80c0dfSnordmark  * convert it to an error ack for the appropriate primitive.
15847c478bd9Sstevel@tonic-gate  */
15857c478bd9Sstevel@tonic-gate static void
15867c478bd9Sstevel@tonic-gate udp_disconnect(queue_t *q, mblk_t *mp)
15877c478bd9Sstevel@tonic-gate {
1588fc80c0dfSnordmark 	udp_t	*udp;
15897c478bd9Sstevel@tonic-gate 	mblk_t	*mp1;
15907c478bd9Sstevel@tonic-gate 	udp_fanout_t *udpf;
1591f4b3ec61Sdh 	udp_stack_t *us;
1592fc80c0dfSnordmark 	conn_t	*connp = Q_TO_CONN(q);
15937c478bd9Sstevel@tonic-gate 
1594fc80c0dfSnordmark 	udp = connp->conn_udp;
1595f4b3ec61Sdh 	us = udp->udp_us;
1596fc80c0dfSnordmark 	rw_enter(&udp->udp_rwlock, RW_WRITER);
1597fc80c0dfSnordmark 	if (udp->udp_state != TS_DATA_XFER || udp->udp_pending_op != -1) {
1598fc80c0dfSnordmark 		rw_exit(&udp->udp_rwlock);
15997c478bd9Sstevel@tonic-gate 		(void) mi_strlog(q, 1, SL_ERROR|SL_TRACE,
16007c478bd9Sstevel@tonic-gate 		    "udp_disconnect: bad state, %u", udp->udp_state);
16017c478bd9Sstevel@tonic-gate 		udp_err_ack(q, mp, TOUTSTATE, 0);
16027c478bd9Sstevel@tonic-gate 		return;
16037c478bd9Sstevel@tonic-gate 	}
1604fc80c0dfSnordmark 	udp->udp_pending_op = T_DISCON_REQ;
1605f4b3ec61Sdh 	udpf = &us->us_bind_fanout[UDP_BIND_HASH(udp->udp_port,
1606f4b3ec61Sdh 	    us->us_bind_fanout_size)];
16077c478bd9Sstevel@tonic-gate 	mutex_enter(&udpf->uf_lock);
16087c478bd9Sstevel@tonic-gate 	udp->udp_v6src = udp->udp_bound_v6src;
16097c478bd9Sstevel@tonic-gate 	udp->udp_state = TS_IDLE;
16107c478bd9Sstevel@tonic-gate 	mutex_exit(&udpf->uf_lock);
16117c478bd9Sstevel@tonic-gate 
16127c478bd9Sstevel@tonic-gate 	/*
16137c478bd9Sstevel@tonic-gate 	 * Send down bind to IP to remove the full binding and revert
16147c478bd9Sstevel@tonic-gate 	 * to the local address binding.
16157c478bd9Sstevel@tonic-gate 	 */
16167c478bd9Sstevel@tonic-gate 	if (udp->udp_family == AF_INET)
16177c478bd9Sstevel@tonic-gate 		mp1 = udp_ip_bind_mp(udp, O_T_BIND_REQ, sizeof (sin_t));
16187c478bd9Sstevel@tonic-gate 	else
16197c478bd9Sstevel@tonic-gate 		mp1 = udp_ip_bind_mp(udp, O_T_BIND_REQ, sizeof (sin6_t));
16207c478bd9Sstevel@tonic-gate 	if (mp1 == NULL) {
1621fc80c0dfSnordmark 		udp->udp_pending_op = -1;
1622fc80c0dfSnordmark 		rw_exit(&udp->udp_rwlock);
16237c478bd9Sstevel@tonic-gate 		udp_err_ack(q, mp, TSYSERR, ENOMEM);
16247c478bd9Sstevel@tonic-gate 		return;
16257c478bd9Sstevel@tonic-gate 	}
16267c478bd9Sstevel@tonic-gate 	mp = mi_tpi_ok_ack_alloc(mp);
16277c478bd9Sstevel@tonic-gate 	if (mp == NULL) {
16287c478bd9Sstevel@tonic-gate 		/* Unable to reuse the T_DISCON_REQ for the ack. */
1629fc80c0dfSnordmark 		udp->udp_pending_op = -1;
1630fc80c0dfSnordmark 		rw_exit(&udp->udp_rwlock);
16317c478bd9Sstevel@tonic-gate 		udp_err_ack_prim(q, mp1, T_DISCON_REQ, TSYSERR, ENOMEM);
16327c478bd9Sstevel@tonic-gate 		return;
16337c478bd9Sstevel@tonic-gate 	}
16347c478bd9Sstevel@tonic-gate 
16357c478bd9Sstevel@tonic-gate 	if (udp->udp_family == AF_INET6) {
16367c478bd9Sstevel@tonic-gate 		int error;
16377c478bd9Sstevel@tonic-gate 
16387c478bd9Sstevel@tonic-gate 		/* Rebuild the header template */
1639fc80c0dfSnordmark 		error = udp_build_hdrs(udp);
16407c478bd9Sstevel@tonic-gate 		if (error != 0) {
1641fc80c0dfSnordmark 			udp->udp_pending_op = -1;
1642fc80c0dfSnordmark 			rw_exit(&udp->udp_rwlock);
16437c478bd9Sstevel@tonic-gate 			udp_err_ack_prim(q, mp, T_DISCON_REQ, TSYSERR, error);
16447c478bd9Sstevel@tonic-gate 			freemsg(mp1);
16457c478bd9Sstevel@tonic-gate 			return;
16467c478bd9Sstevel@tonic-gate 		}
16477c478bd9Sstevel@tonic-gate 	}
16487c478bd9Sstevel@tonic-gate 
1649fc80c0dfSnordmark 	rw_exit(&udp->udp_rwlock);
1650fc80c0dfSnordmark 	/* Append the T_OK_ACK to the T_BIND_REQ for udp_bind_ack */
16517c478bd9Sstevel@tonic-gate 	linkb(mp1, mp);
1652ff550d0eSmasputra 
1653ff550d0eSmasputra 	if (udp->udp_family == AF_INET6)
1654fc80c0dfSnordmark 		mp1 = ip_bind_v6(q, mp1, connp, NULL);
1655ff550d0eSmasputra 	else
1656fc80c0dfSnordmark 		mp1 = ip_bind_v4(q, mp1, connp);
1657ff550d0eSmasputra 
1658fc80c0dfSnordmark 	/* The above return NULL if the bind needs to be deferred */
1659ff550d0eSmasputra 	if (mp1 != NULL)
1660fc80c0dfSnordmark 		udp_bind_result(connp, mp1);
1661ff550d0eSmasputra 	else
1662fc80c0dfSnordmark 		CONN_INC_REF(connp);
16637c478bd9Sstevel@tonic-gate }
16647c478bd9Sstevel@tonic-gate 
16657c478bd9Sstevel@tonic-gate /* This routine creates a T_ERROR_ACK message and passes it upstream. */
16667c478bd9Sstevel@tonic-gate static void
16677c478bd9Sstevel@tonic-gate udp_err_ack(queue_t *q, mblk_t *mp, t_scalar_t t_error, int sys_error)
16687c478bd9Sstevel@tonic-gate {
16697c478bd9Sstevel@tonic-gate 	if ((mp = mi_tpi_err_ack_alloc(mp, t_error, sys_error)) != NULL)
1670fc80c0dfSnordmark 		qreply(q, mp);
16717c478bd9Sstevel@tonic-gate }
16727c478bd9Sstevel@tonic-gate 
16737c478bd9Sstevel@tonic-gate /* Shorthand to generate and send TPI error acks to our client */
16747c478bd9Sstevel@tonic-gate static void
16757c478bd9Sstevel@tonic-gate udp_err_ack_prim(queue_t *q, mblk_t *mp, int primitive, t_scalar_t t_error,
16767c478bd9Sstevel@tonic-gate     int sys_error)
16777c478bd9Sstevel@tonic-gate {
16787c478bd9Sstevel@tonic-gate 	struct T_error_ack	*teackp;
16797c478bd9Sstevel@tonic-gate 
16807c478bd9Sstevel@tonic-gate 	if ((mp = tpi_ack_alloc(mp, sizeof (struct T_error_ack),
16817c478bd9Sstevel@tonic-gate 	    M_PCPROTO, T_ERROR_ACK)) != NULL) {
16827c478bd9Sstevel@tonic-gate 		teackp = (struct T_error_ack *)mp->b_rptr;
16837c478bd9Sstevel@tonic-gate 		teackp->ERROR_prim = primitive;
16847c478bd9Sstevel@tonic-gate 		teackp->TLI_error = t_error;
16857c478bd9Sstevel@tonic-gate 		teackp->UNIX_error = sys_error;
1686fc80c0dfSnordmark 		qreply(q, mp);
16877c478bd9Sstevel@tonic-gate 	}
16887c478bd9Sstevel@tonic-gate }
16897c478bd9Sstevel@tonic-gate 
16907c478bd9Sstevel@tonic-gate /*ARGSUSED*/
16917c478bd9Sstevel@tonic-gate static int
16927c478bd9Sstevel@tonic-gate udp_extra_priv_ports_get(queue_t *q, mblk_t *mp, caddr_t cp, cred_t *cr)
16937c478bd9Sstevel@tonic-gate {
16947c478bd9Sstevel@tonic-gate 	int i;
1695f4b3ec61Sdh 	udp_t		*udp = Q_TO_UDP(q);
1696f4b3ec61Sdh 	udp_stack_t *us = udp->udp_us;
16977c478bd9Sstevel@tonic-gate 
1698f4b3ec61Sdh 	for (i = 0; i < us->us_num_epriv_ports; i++) {
1699f4b3ec61Sdh 		if (us->us_epriv_ports[i] != 0)
1700f4b3ec61Sdh 			(void) mi_mpprintf(mp, "%d ", us->us_epriv_ports[i]);
17017c478bd9Sstevel@tonic-gate 	}
17027c478bd9Sstevel@tonic-gate 	return (0);
17037c478bd9Sstevel@tonic-gate }
17047c478bd9Sstevel@tonic-gate 
17057c478bd9Sstevel@tonic-gate /* ARGSUSED */
17067c478bd9Sstevel@tonic-gate static int
17077c478bd9Sstevel@tonic-gate udp_extra_priv_ports_add(queue_t *q, mblk_t *mp, char *value, caddr_t cp,
17087c478bd9Sstevel@tonic-gate     cred_t *cr)
17097c478bd9Sstevel@tonic-gate {
17107c478bd9Sstevel@tonic-gate 	long	new_value;
17117c478bd9Sstevel@tonic-gate 	int	i;
1712f4b3ec61Sdh 	udp_t		*udp = Q_TO_UDP(q);
1713f4b3ec61Sdh 	udp_stack_t *us = udp->udp_us;
17147c478bd9Sstevel@tonic-gate 
17157c478bd9Sstevel@tonic-gate 	/*
17167c478bd9Sstevel@tonic-gate 	 * Fail the request if the new value does not lie within the
17177c478bd9Sstevel@tonic-gate 	 * port number limits.
17187c478bd9Sstevel@tonic-gate 	 */
17197c478bd9Sstevel@tonic-gate 	if (ddi_strtol(value, NULL, 10, &new_value) != 0 ||
17207c478bd9Sstevel@tonic-gate 	    new_value <= 0 || new_value >= 65536) {
17217c478bd9Sstevel@tonic-gate 		return (EINVAL);
17227c478bd9Sstevel@tonic-gate 	}
17237c478bd9Sstevel@tonic-gate 
17247c478bd9Sstevel@tonic-gate 	/* Check if the value is already in the list */
1725f4b3ec61Sdh 	for (i = 0; i < us->us_num_epriv_ports; i++) {
1726f4b3ec61Sdh 		if (new_value == us->us_epriv_ports[i]) {
17277c478bd9Sstevel@tonic-gate 			return (EEXIST);
17287c478bd9Sstevel@tonic-gate 		}
17297c478bd9Sstevel@tonic-gate 	}
17307c478bd9Sstevel@tonic-gate 	/* Find an empty slot */
1731f4b3ec61Sdh 	for (i = 0; i < us->us_num_epriv_ports; i++) {
1732f4b3ec61Sdh 		if (us->us_epriv_ports[i] == 0)
17337c478bd9Sstevel@tonic-gate 			break;
17347c478bd9Sstevel@tonic-gate 	}
1735f4b3ec61Sdh 	if (i == us->us_num_epriv_ports) {
17367c478bd9Sstevel@tonic-gate 		return (EOVERFLOW);
17377c478bd9Sstevel@tonic-gate 	}
17387c478bd9Sstevel@tonic-gate 
17397c478bd9Sstevel@tonic-gate 	/* Set the new value */
1740f4b3ec61Sdh 	us->us_epriv_ports[i] = (in_port_t)new_value;
17417c478bd9Sstevel@tonic-gate 	return (0);
17427c478bd9Sstevel@tonic-gate }
17437c478bd9Sstevel@tonic-gate 
17447c478bd9Sstevel@tonic-gate /* ARGSUSED */
17457c478bd9Sstevel@tonic-gate static int
17467c478bd9Sstevel@tonic-gate udp_extra_priv_ports_del(queue_t *q, mblk_t *mp, char *value, caddr_t cp,
17477c478bd9Sstevel@tonic-gate     cred_t *cr)
17487c478bd9Sstevel@tonic-gate {
17497c478bd9Sstevel@tonic-gate 	long	new_value;
17507c478bd9Sstevel@tonic-gate 	int	i;
1751f4b3ec61Sdh 	udp_t		*udp = Q_TO_UDP(q);
1752f4b3ec61Sdh 	udp_stack_t *us = udp->udp_us;
17537c478bd9Sstevel@tonic-gate 
17547c478bd9Sstevel@tonic-gate 	/*
17557c478bd9Sstevel@tonic-gate 	 * Fail the request if the new value does not lie within the
17567c478bd9Sstevel@tonic-gate 	 * port number limits.
17577c478bd9Sstevel@tonic-gate 	 */
17587c478bd9Sstevel@tonic-gate 	if (ddi_strtol(value, NULL, 10, &new_value) != 0 ||
17597c478bd9Sstevel@tonic-gate 	    new_value <= 0 || new_value >= 65536) {
17607c478bd9Sstevel@tonic-gate 		return (EINVAL);
17617c478bd9Sstevel@tonic-gate 	}
17627c478bd9Sstevel@tonic-gate 
17637c478bd9Sstevel@tonic-gate 	/* Check that the value is already in the list */
1764f4b3ec61Sdh 	for (i = 0; i < us->us_num_epriv_ports; i++) {
1765f4b3ec61Sdh 		if (us->us_epriv_ports[i] == new_value)
17667c478bd9Sstevel@tonic-gate 			break;
17677c478bd9Sstevel@tonic-gate 	}
1768f4b3ec61Sdh 	if (i == us->us_num_epriv_ports) {
17697c478bd9Sstevel@tonic-gate 		return (ESRCH);
17707c478bd9Sstevel@tonic-gate 	}
17717c478bd9Sstevel@tonic-gate 
17727c478bd9Sstevel@tonic-gate 	/* Clear the value */
1773f4b3ec61Sdh 	us->us_epriv_ports[i] = 0;
17747c478bd9Sstevel@tonic-gate 	return (0);
17757c478bd9Sstevel@tonic-gate }
17767c478bd9Sstevel@tonic-gate 
17777c478bd9Sstevel@tonic-gate /* At minimum we need 4 bytes of UDP header */
17787c478bd9Sstevel@tonic-gate #define	ICMP_MIN_UDP_HDR	4
17797c478bd9Sstevel@tonic-gate 
17807c478bd9Sstevel@tonic-gate /*
1781fc80c0dfSnordmark  * udp_icmp_error is called by udp_input to process ICMP msgs. passed up by IP.
17827c478bd9Sstevel@tonic-gate  * Generates the appropriate T_UDERROR_IND for permanent (non-transient) errors.
17837c478bd9Sstevel@tonic-gate  * Assumes that IP has pulled up everything up to and including the ICMP header.
17847c478bd9Sstevel@tonic-gate  */
17857c478bd9Sstevel@tonic-gate static void
17867c478bd9Sstevel@tonic-gate udp_icmp_error(queue_t *q, mblk_t *mp)
17877c478bd9Sstevel@tonic-gate {
17887c478bd9Sstevel@tonic-gate 	icmph_t *icmph;
17897c478bd9Sstevel@tonic-gate 	ipha_t	*ipha;
17907c478bd9Sstevel@tonic-gate 	int	iph_hdr_length;
17917c478bd9Sstevel@tonic-gate 	udpha_t	*udpha;
17927c478bd9Sstevel@tonic-gate 	sin_t	sin;
17937c478bd9Sstevel@tonic-gate 	sin6_t	sin6;
17947c478bd9Sstevel@tonic-gate 	mblk_t	*mp1;
17957c478bd9Sstevel@tonic-gate 	int	error = 0;
1796ff550d0eSmasputra 	udp_t	*udp = Q_TO_UDP(q);
17977c478bd9Sstevel@tonic-gate 
1798fc80c0dfSnordmark 	ipha = (ipha_t *)mp->b_rptr;
17997c478bd9Sstevel@tonic-gate 
1800fc80c0dfSnordmark 	ASSERT(OK_32PTR(mp->b_rptr));
18017c478bd9Sstevel@tonic-gate 
1802fc80c0dfSnordmark 	if (IPH_HDR_VERSION(ipha) != IPV4_VERSION) {
1803fc80c0dfSnordmark 		ASSERT(IPH_HDR_VERSION(ipha) == IPV6_VERSION);
18047c478bd9Sstevel@tonic-gate 		udp_icmp_error_ipv6(q, mp);
18057c478bd9Sstevel@tonic-gate 		return;
18067c478bd9Sstevel@tonic-gate 	}
1807fc80c0dfSnordmark 	ASSERT(IPH_HDR_VERSION(ipha) == IPV4_VERSION);
18087c478bd9Sstevel@tonic-gate 
18097c478bd9Sstevel@tonic-gate 	/* Skip past the outer IP and ICMP headers */
18107c478bd9Sstevel@tonic-gate 	iph_hdr_length = IPH_HDR_LENGTH(ipha);
18117c478bd9Sstevel@tonic-gate 	icmph = (icmph_t *)&mp->b_rptr[iph_hdr_length];
18127c478bd9Sstevel@tonic-gate 	ipha = (ipha_t *)&icmph[1];
18137c478bd9Sstevel@tonic-gate 
18147c478bd9Sstevel@tonic-gate 	/* Skip past the inner IP and find the ULP header */
18157c478bd9Sstevel@tonic-gate 	iph_hdr_length = IPH_HDR_LENGTH(ipha);
18167c478bd9Sstevel@tonic-gate 	udpha = (udpha_t *)((char *)ipha + iph_hdr_length);
18177c478bd9Sstevel@tonic-gate 
18187c478bd9Sstevel@tonic-gate 	switch (icmph->icmph_type) {
18197c478bd9Sstevel@tonic-gate 	case ICMP_DEST_UNREACHABLE:
18207c478bd9Sstevel@tonic-gate 		switch (icmph->icmph_code) {
18217c478bd9Sstevel@tonic-gate 		case ICMP_FRAGMENTATION_NEEDED:
18227c478bd9Sstevel@tonic-gate 			/*
18237c478bd9Sstevel@tonic-gate 			 * IP has already adjusted the path MTU.
18247c478bd9Sstevel@tonic-gate 			 */
18257c478bd9Sstevel@tonic-gate 			break;
18267c478bd9Sstevel@tonic-gate 		case ICMP_PORT_UNREACHABLE:
18277c478bd9Sstevel@tonic-gate 		case ICMP_PROTOCOL_UNREACHABLE:
18287c478bd9Sstevel@tonic-gate 			error = ECONNREFUSED;
18297c478bd9Sstevel@tonic-gate 			break;
18307c478bd9Sstevel@tonic-gate 		default:
18317c478bd9Sstevel@tonic-gate 			/* Transient errors */
18327c478bd9Sstevel@tonic-gate 			break;
18337c478bd9Sstevel@tonic-gate 		}
18347c478bd9Sstevel@tonic-gate 		break;
18357c478bd9Sstevel@tonic-gate 	default:
18367c478bd9Sstevel@tonic-gate 		/* Transient errors */
18377c478bd9Sstevel@tonic-gate 		break;
18387c478bd9Sstevel@tonic-gate 	}
18397c478bd9Sstevel@tonic-gate 	if (error == 0) {
18407c478bd9Sstevel@tonic-gate 		freemsg(mp);
18417c478bd9Sstevel@tonic-gate 		return;
18427c478bd9Sstevel@tonic-gate 	}
18437c478bd9Sstevel@tonic-gate 
1844fc80c0dfSnordmark 	/*
1845fc80c0dfSnordmark 	 * Deliver T_UDERROR_IND when the application has asked for it.
1846fc80c0dfSnordmark 	 * The socket layer enables this automatically when connected.
1847fc80c0dfSnordmark 	 */
1848fc80c0dfSnordmark 	if (!udp->udp_dgram_errind) {
1849fc80c0dfSnordmark 		freemsg(mp);
1850fc80c0dfSnordmark 		return;
1851fc80c0dfSnordmark 	}
1852fc80c0dfSnordmark 
18537c478bd9Sstevel@tonic-gate 	switch (udp->udp_family) {
18547c478bd9Sstevel@tonic-gate 	case AF_INET:
18557c478bd9Sstevel@tonic-gate 		sin = sin_null;
18567c478bd9Sstevel@tonic-gate 		sin.sin_family = AF_INET;
18577c478bd9Sstevel@tonic-gate 		sin.sin_addr.s_addr = ipha->ipha_dst;
18587c478bd9Sstevel@tonic-gate 		sin.sin_port = udpha->uha_dst_port;
18597c478bd9Sstevel@tonic-gate 		mp1 = mi_tpi_uderror_ind((char *)&sin, sizeof (sin_t), NULL, 0,
18607c478bd9Sstevel@tonic-gate 		    error);
18617c478bd9Sstevel@tonic-gate 		break;
18627c478bd9Sstevel@tonic-gate 	case AF_INET6:
18637c478bd9Sstevel@tonic-gate 		sin6 = sin6_null;
18647c478bd9Sstevel@tonic-gate 		sin6.sin6_family = AF_INET6;
18657c478bd9Sstevel@tonic-gate 		IN6_IPADDR_TO_V4MAPPED(ipha->ipha_dst, &sin6.sin6_addr);
18667c478bd9Sstevel@tonic-gate 		sin6.sin6_port = udpha->uha_dst_port;
18677c478bd9Sstevel@tonic-gate 
18687c478bd9Sstevel@tonic-gate 		mp1 = mi_tpi_uderror_ind((char *)&sin6, sizeof (sin6_t),
18697c478bd9Sstevel@tonic-gate 		    NULL, 0, error);
18707c478bd9Sstevel@tonic-gate 		break;
18717c478bd9Sstevel@tonic-gate 	}
18727c478bd9Sstevel@tonic-gate 	if (mp1)
1873fc80c0dfSnordmark 		putnext(q, mp1);
18747c478bd9Sstevel@tonic-gate 	freemsg(mp);
18757c478bd9Sstevel@tonic-gate }
18767c478bd9Sstevel@tonic-gate 
18777c478bd9Sstevel@tonic-gate /*
18787c478bd9Sstevel@tonic-gate  * udp_icmp_error_ipv6 is called by udp_icmp_error to process ICMP for IPv6.
18797c478bd9Sstevel@tonic-gate  * Generates the appropriate T_UDERROR_IND for permanent (non-transient) errors.
18807c478bd9Sstevel@tonic-gate  * Assumes that IP has pulled up all the extension headers as well as the
18817c478bd9Sstevel@tonic-gate  * ICMPv6 header.
18827c478bd9Sstevel@tonic-gate  */
18837c478bd9Sstevel@tonic-gate static void
18847c478bd9Sstevel@tonic-gate udp_icmp_error_ipv6(queue_t *q, mblk_t *mp)
18857c478bd9Sstevel@tonic-gate {
18867c478bd9Sstevel@tonic-gate 	icmp6_t		*icmp6;
18877c478bd9Sstevel@tonic-gate 	ip6_t		*ip6h, *outer_ip6h;
1888fc80c0dfSnordmark 	uint16_t	iph_hdr_length;
18897c478bd9Sstevel@tonic-gate 	uint8_t		*nexthdrp;
18907c478bd9Sstevel@tonic-gate 	udpha_t		*udpha;
18917c478bd9Sstevel@tonic-gate 	sin6_t		sin6;
18927c478bd9Sstevel@tonic-gate 	mblk_t		*mp1;
18937c478bd9Sstevel@tonic-gate 	int		error = 0;
1894ff550d0eSmasputra 	udp_t		*udp = Q_TO_UDP(q);
1895fc80c0dfSnordmark 	udp_stack_t	*us = udp->udp_us;
18967c478bd9Sstevel@tonic-gate 
18977c478bd9Sstevel@tonic-gate 	outer_ip6h = (ip6_t *)mp->b_rptr;
1898fc80c0dfSnordmark 	if (outer_ip6h->ip6_nxt != IPPROTO_ICMPV6)
1899fc80c0dfSnordmark 		iph_hdr_length = ip_hdr_length_v6(mp, outer_ip6h);
1900fc80c0dfSnordmark 	else
1901fc80c0dfSnordmark 		iph_hdr_length = IPV6_HDR_LEN;
1902fc80c0dfSnordmark 	icmp6 = (icmp6_t *)&mp->b_rptr[iph_hdr_length];
19037c478bd9Sstevel@tonic-gate 	ip6h = (ip6_t *)&icmp6[1];
1904fc80c0dfSnordmark 	if (!ip_hdr_length_nexthdr_v6(mp, ip6h, &iph_hdr_length, &nexthdrp)) {
1905fc80c0dfSnordmark 		freemsg(mp);
1906fc80c0dfSnordmark 		return;
19077c478bd9Sstevel@tonic-gate 	}
1908fc80c0dfSnordmark 	udpha = (udpha_t *)((char *)ip6h + iph_hdr_length);
19097c478bd9Sstevel@tonic-gate 
19107c478bd9Sstevel@tonic-gate 	switch (icmp6->icmp6_type) {
19117c478bd9Sstevel@tonic-gate 	case ICMP6_DST_UNREACH:
19127c478bd9Sstevel@tonic-gate 		switch (icmp6->icmp6_code) {
19137c478bd9Sstevel@tonic-gate 		case ICMP6_DST_UNREACH_NOPORT:
19147c478bd9Sstevel@tonic-gate 			error = ECONNREFUSED;
19157c478bd9Sstevel@tonic-gate 			break;
19167c478bd9Sstevel@tonic-gate 		case ICMP6_DST_UNREACH_ADMIN:
19177c478bd9Sstevel@tonic-gate 		case ICMP6_DST_UNREACH_NOROUTE:
19187c478bd9Sstevel@tonic-gate 		case ICMP6_DST_UNREACH_BEYONDSCOPE:
19197c478bd9Sstevel@tonic-gate 		case ICMP6_DST_UNREACH_ADDR:
19207c478bd9Sstevel@tonic-gate 			/* Transient errors */
19217c478bd9Sstevel@tonic-gate 			break;
19227c478bd9Sstevel@tonic-gate 		default:
19237c478bd9Sstevel@tonic-gate 			break;
19247c478bd9Sstevel@tonic-gate 		}
19257c478bd9Sstevel@tonic-gate 		break;
19267c478bd9Sstevel@tonic-gate 	case ICMP6_PACKET_TOO_BIG: {
19277c478bd9Sstevel@tonic-gate 		struct T_unitdata_ind	*tudi;
19287c478bd9Sstevel@tonic-gate 		struct T_opthdr		*toh;
19297c478bd9Sstevel@tonic-gate 		size_t			udi_size;
19307c478bd9Sstevel@tonic-gate 		mblk_t			*newmp;
19317c478bd9Sstevel@tonic-gate 		t_scalar_t		opt_length = sizeof (struct T_opthdr) +
19327c478bd9Sstevel@tonic-gate 		    sizeof (struct ip6_mtuinfo);
19337c478bd9Sstevel@tonic-gate 		sin6_t			*sin6;
19347c478bd9Sstevel@tonic-gate 		struct ip6_mtuinfo	*mtuinfo;
19357c478bd9Sstevel@tonic-gate 
19367c478bd9Sstevel@tonic-gate 		/*
19377c478bd9Sstevel@tonic-gate 		 * If the application has requested to receive path mtu
19387c478bd9Sstevel@tonic-gate 		 * information, send up an empty message containing an
19397c478bd9Sstevel@tonic-gate 		 * IPV6_PATHMTU ancillary data item.
19407c478bd9Sstevel@tonic-gate 		 */
19417c478bd9Sstevel@tonic-gate 		if (!udp->udp_ipv6_recvpathmtu)
19427c478bd9Sstevel@tonic-gate 			break;
19437c478bd9Sstevel@tonic-gate 
19447c478bd9Sstevel@tonic-gate 		udi_size = sizeof (struct T_unitdata_ind) + sizeof (sin6_t) +
19457c478bd9Sstevel@tonic-gate 		    opt_length;
19467c478bd9Sstevel@tonic-gate 		if ((newmp = allocb(udi_size, BPRI_MED)) == NULL) {
1947fc80c0dfSnordmark 			BUMP_MIB(&us->us_udp_mib, udpInErrors);
19487c478bd9Sstevel@tonic-gate 			break;
19497c478bd9Sstevel@tonic-gate 		}
19507c478bd9Sstevel@tonic-gate 
19517c478bd9Sstevel@tonic-gate 		/*
19527c478bd9Sstevel@tonic-gate 		 * newmp->b_cont is left to NULL on purpose.  This is an
19537c478bd9Sstevel@tonic-gate 		 * empty message containing only ancillary data.
19547c478bd9Sstevel@tonic-gate 		 */
19557c478bd9Sstevel@tonic-gate 		newmp->b_datap->db_type = M_PROTO;
19567c478bd9Sstevel@tonic-gate 		tudi = (struct T_unitdata_ind *)newmp->b_rptr;
19577c478bd9Sstevel@tonic-gate 		newmp->b_wptr = (uchar_t *)tudi + udi_size;
19587c478bd9Sstevel@tonic-gate 		tudi->PRIM_type = T_UNITDATA_IND;
19597c478bd9Sstevel@tonic-gate 		tudi->SRC_length = sizeof (sin6_t);
19607c478bd9Sstevel@tonic-gate 		tudi->SRC_offset = sizeof (struct T_unitdata_ind);
19617c478bd9Sstevel@tonic-gate 		tudi->OPT_offset = tudi->SRC_offset + sizeof (sin6_t);
19627c478bd9Sstevel@tonic-gate 		tudi->OPT_length = opt_length;
19637c478bd9Sstevel@tonic-gate 
19647c478bd9Sstevel@tonic-gate 		sin6 = (sin6_t *)&tudi[1];
19657c478bd9Sstevel@tonic-gate 		bzero(sin6, sizeof (sin6_t));
19667c478bd9Sstevel@tonic-gate 		sin6->sin6_family = AF_INET6;
19677c478bd9Sstevel@tonic-gate 		sin6->sin6_addr = udp->udp_v6dst;
19687c478bd9Sstevel@tonic-gate 
19697c478bd9Sstevel@tonic-gate 		toh = (struct T_opthdr *)&sin6[1];
19707c478bd9Sstevel@tonic-gate 		toh->level = IPPROTO_IPV6;
19717c478bd9Sstevel@tonic-gate 		toh->name = IPV6_PATHMTU;
19727c478bd9Sstevel@tonic-gate 		toh->len = opt_length;
19737c478bd9Sstevel@tonic-gate 		toh->status = 0;
19747c478bd9Sstevel@tonic-gate 
19757c478bd9Sstevel@tonic-gate 		mtuinfo = (struct ip6_mtuinfo *)&toh[1];
19767c478bd9Sstevel@tonic-gate 		bzero(mtuinfo, sizeof (struct ip6_mtuinfo));
19777c478bd9Sstevel@tonic-gate 		mtuinfo->ip6m_addr.sin6_family = AF_INET6;
19787c478bd9Sstevel@tonic-gate 		mtuinfo->ip6m_addr.sin6_addr = ip6h->ip6_dst;
19797c478bd9Sstevel@tonic-gate 		mtuinfo->ip6m_mtu = icmp6->icmp6_mtu;
19807c478bd9Sstevel@tonic-gate 		/*
19817c478bd9Sstevel@tonic-gate 		 * We've consumed everything we need from the original
19827c478bd9Sstevel@tonic-gate 		 * message.  Free it, then send our empty message.
19837c478bd9Sstevel@tonic-gate 		 */
19847c478bd9Sstevel@tonic-gate 		freemsg(mp);
1985fc80c0dfSnordmark 		putnext(q, newmp);
19867c478bd9Sstevel@tonic-gate 		return;
19877c478bd9Sstevel@tonic-gate 	}
19887c478bd9Sstevel@tonic-gate 	case ICMP6_TIME_EXCEEDED:
19897c478bd9Sstevel@tonic-gate 		/* Transient errors */
19907c478bd9Sstevel@tonic-gate 		break;
19917c478bd9Sstevel@tonic-gate 	case ICMP6_PARAM_PROB:
19927c478bd9Sstevel@tonic-gate 		/* If this corresponds to an ICMP_PROTOCOL_UNREACHABLE */
19937c478bd9Sstevel@tonic-gate 		if (icmp6->icmp6_code == ICMP6_PARAMPROB_NEXTHEADER &&
19947c478bd9Sstevel@tonic-gate 		    (uchar_t *)ip6h + icmp6->icmp6_pptr ==
19957c478bd9Sstevel@tonic-gate 		    (uchar_t *)nexthdrp) {
19967c478bd9Sstevel@tonic-gate 			error = ECONNREFUSED;
19977c478bd9Sstevel@tonic-gate 			break;
19987c478bd9Sstevel@tonic-gate 		}
19997c478bd9Sstevel@tonic-gate 		break;
20007c478bd9Sstevel@tonic-gate 	}
20017c478bd9Sstevel@tonic-gate 	if (error == 0) {
20027c478bd9Sstevel@tonic-gate 		freemsg(mp);
20037c478bd9Sstevel@tonic-gate 		return;
20047c478bd9Sstevel@tonic-gate 	}
20057c478bd9Sstevel@tonic-gate 
2006fc80c0dfSnordmark 	/*
2007fc80c0dfSnordmark 	 * Deliver T_UDERROR_IND when the application has asked for it.
2008fc80c0dfSnordmark 	 * The socket layer enables this automatically when connected.
2009fc80c0dfSnordmark 	 */
2010fc80c0dfSnordmark 	if (!udp->udp_dgram_errind) {
2011fc80c0dfSnordmark 		freemsg(mp);
2012fc80c0dfSnordmark 		return;
2013fc80c0dfSnordmark 	}
2014fc80c0dfSnordmark 
20157c478bd9Sstevel@tonic-gate 	sin6 = sin6_null;
20167c478bd9Sstevel@tonic-gate 	sin6.sin6_family = AF_INET6;
20177c478bd9Sstevel@tonic-gate 	sin6.sin6_addr = ip6h->ip6_dst;
20187c478bd9Sstevel@tonic-gate 	sin6.sin6_port = udpha->uha_dst_port;
20197c478bd9Sstevel@tonic-gate 	sin6.sin6_flowinfo = ip6h->ip6_vcf & ~IPV6_VERS_AND_FLOW_MASK;
20207c478bd9Sstevel@tonic-gate 
20217c478bd9Sstevel@tonic-gate 	mp1 = mi_tpi_uderror_ind((char *)&sin6, sizeof (sin6_t), NULL, 0,
20227c478bd9Sstevel@tonic-gate 	    error);
20237c478bd9Sstevel@tonic-gate 	if (mp1)
2024fc80c0dfSnordmark 		putnext(q, mp1);
20257c478bd9Sstevel@tonic-gate 	freemsg(mp);
20267c478bd9Sstevel@tonic-gate }
20277c478bd9Sstevel@tonic-gate 
20287c478bd9Sstevel@tonic-gate /*
20297c478bd9Sstevel@tonic-gate  * This routine responds to T_ADDR_REQ messages.  It is called by udp_wput.
20307c478bd9Sstevel@tonic-gate  * The local address is filled in if endpoint is bound. The remote address
20317c478bd9Sstevel@tonic-gate  * is filled in if remote address has been precified ("connected endpoint")
20327c478bd9Sstevel@tonic-gate  * (The concept of connected CLTS sockets is alien to published TPI
20337c478bd9Sstevel@tonic-gate  *  but we support it anyway).
20347c478bd9Sstevel@tonic-gate  */
20357c478bd9Sstevel@tonic-gate static void
20367c478bd9Sstevel@tonic-gate udp_addr_req(queue_t *q, mblk_t *mp)
20377c478bd9Sstevel@tonic-gate {
20387c478bd9Sstevel@tonic-gate 	sin_t	*sin;
20397c478bd9Sstevel@tonic-gate 	sin6_t	*sin6;
20407c478bd9Sstevel@tonic-gate 	mblk_t	*ackmp;
20417c478bd9Sstevel@tonic-gate 	struct T_addr_ack *taa;
2042ff550d0eSmasputra 	udp_t	*udp = Q_TO_UDP(q);
20437c478bd9Sstevel@tonic-gate 
20447c478bd9Sstevel@tonic-gate 	/* Make it large enough for worst case */
20457c478bd9Sstevel@tonic-gate 	ackmp = reallocb(mp, sizeof (struct T_addr_ack) +
20467c478bd9Sstevel@tonic-gate 	    2 * sizeof (sin6_t), 1);
20477c478bd9Sstevel@tonic-gate 	if (ackmp == NULL) {
20487c478bd9Sstevel@tonic-gate 		udp_err_ack(q, mp, TSYSERR, ENOMEM);
20497c478bd9Sstevel@tonic-gate 		return;
20507c478bd9Sstevel@tonic-gate 	}
20517c478bd9Sstevel@tonic-gate 	taa = (struct T_addr_ack *)ackmp->b_rptr;
20527c478bd9Sstevel@tonic-gate 
20537c478bd9Sstevel@tonic-gate 	bzero(taa, sizeof (struct T_addr_ack));
20547c478bd9Sstevel@tonic-gate 	ackmp->b_wptr = (uchar_t *)&taa[1];
20557c478bd9Sstevel@tonic-gate 
20567c478bd9Sstevel@tonic-gate 	taa->PRIM_type = T_ADDR_ACK;
20577c478bd9Sstevel@tonic-gate 	ackmp->b_datap->db_type = M_PCPROTO;
2058fc80c0dfSnordmark 	rw_enter(&udp->udp_rwlock, RW_READER);
20597c478bd9Sstevel@tonic-gate 	/*
20607c478bd9Sstevel@tonic-gate 	 * Note: Following code assumes 32 bit alignment of basic
20617c478bd9Sstevel@tonic-gate 	 * data structures like sin_t and struct T_addr_ack.
20627c478bd9Sstevel@tonic-gate 	 */
20637c478bd9Sstevel@tonic-gate 	if (udp->udp_state != TS_UNBND) {
20647c478bd9Sstevel@tonic-gate 		/*
20657c478bd9Sstevel@tonic-gate 		 * Fill in local address first
20667c478bd9Sstevel@tonic-gate 		 */
20677c478bd9Sstevel@tonic-gate 		taa->LOCADDR_offset = sizeof (*taa);
20687c478bd9Sstevel@tonic-gate 		if (udp->udp_family == AF_INET) {
20697c478bd9Sstevel@tonic-gate 			taa->LOCADDR_length = sizeof (sin_t);
20707c478bd9Sstevel@tonic-gate 			sin = (sin_t *)&taa[1];
20717c478bd9Sstevel@tonic-gate 			/* Fill zeroes and then initialize non-zero fields */
20727c478bd9Sstevel@tonic-gate 			*sin = sin_null;
20737c478bd9Sstevel@tonic-gate 			sin->sin_family = AF_INET;
20747c478bd9Sstevel@tonic-gate 			if (!IN6_IS_ADDR_V4MAPPED_ANY(&udp->udp_v6src) &&
20757c478bd9Sstevel@tonic-gate 			    !IN6_IS_ADDR_UNSPECIFIED(&udp->udp_v6src)) {
20767c478bd9Sstevel@tonic-gate 				IN6_V4MAPPED_TO_IPADDR(&udp->udp_v6src,
20777c478bd9Sstevel@tonic-gate 				    sin->sin_addr.s_addr);
20787c478bd9Sstevel@tonic-gate 			} else {
20797c478bd9Sstevel@tonic-gate 				/*
20807c478bd9Sstevel@tonic-gate 				 * INADDR_ANY
20817c478bd9Sstevel@tonic-gate 				 * udp_v6src is not set, we might be bound to
20827c478bd9Sstevel@tonic-gate 				 * broadcast/multicast. Use udp_bound_v6src as
20837c478bd9Sstevel@tonic-gate 				 * local address instead (that could
20847c478bd9Sstevel@tonic-gate 				 * also still be INADDR_ANY)
20857c478bd9Sstevel@tonic-gate 				 */
20867c478bd9Sstevel@tonic-gate 				IN6_V4MAPPED_TO_IPADDR(&udp->udp_bound_v6src,
20877c478bd9Sstevel@tonic-gate 				    sin->sin_addr.s_addr);
20887c478bd9Sstevel@tonic-gate 			}
20897c478bd9Sstevel@tonic-gate 			sin->sin_port = udp->udp_port;
20907c478bd9Sstevel@tonic-gate 			ackmp->b_wptr = (uchar_t *)&sin[1];
20917c478bd9Sstevel@tonic-gate 			if (udp->udp_state == TS_DATA_XFER) {
20927c478bd9Sstevel@tonic-gate 				/*
20937c478bd9Sstevel@tonic-gate 				 * connected, fill remote address too
20947c478bd9Sstevel@tonic-gate 				 */
20957c478bd9Sstevel@tonic-gate 				taa->REMADDR_length = sizeof (sin_t);
20967c478bd9Sstevel@tonic-gate 				/* assumed 32-bit alignment */
20977c478bd9Sstevel@tonic-gate 				taa->REMADDR_offset = taa->LOCADDR_offset +
20987c478bd9Sstevel@tonic-gate 				    taa->LOCADDR_length;
20997c478bd9Sstevel@tonic-gate 
21007c478bd9Sstevel@tonic-gate 				sin = (sin_t *)(ackmp->b_rptr +
21017c478bd9Sstevel@tonic-gate 				    taa->REMADDR_offset);
21027c478bd9Sstevel@tonic-gate 				/* initialize */
21037c478bd9Sstevel@tonic-gate 				*sin = sin_null;
21047c478bd9Sstevel@tonic-gate 				sin->sin_family = AF_INET;
21057c478bd9Sstevel@tonic-gate 				sin->sin_addr.s_addr =
21067c478bd9Sstevel@tonic-gate 				    V4_PART_OF_V6(udp->udp_v6dst);
21077c478bd9Sstevel@tonic-gate 				sin->sin_port = udp->udp_dstport;
21087c478bd9Sstevel@tonic-gate 				ackmp->b_wptr = (uchar_t *)&sin[1];
21097c478bd9Sstevel@tonic-gate 			}
21107c478bd9Sstevel@tonic-gate 		} else {
21117c478bd9Sstevel@tonic-gate 			taa->LOCADDR_length = sizeof (sin6_t);
21127c478bd9Sstevel@tonic-gate 			sin6 = (sin6_t *)&taa[1];
21137c478bd9Sstevel@tonic-gate 			/* Fill zeroes and then initialize non-zero fields */
21147c478bd9Sstevel@tonic-gate 			*sin6 = sin6_null;
21157c478bd9Sstevel@tonic-gate 			sin6->sin6_family = AF_INET6;
21167c478bd9Sstevel@tonic-gate 			if (!IN6_IS_ADDR_UNSPECIFIED(&udp->udp_v6src)) {
21177c478bd9Sstevel@tonic-gate 				sin6->sin6_addr = udp->udp_v6src;
21187c478bd9Sstevel@tonic-gate 			} else {
21197c478bd9Sstevel@tonic-gate 				/*
21207c478bd9Sstevel@tonic-gate 				 * UNSPECIFIED
21217c478bd9Sstevel@tonic-gate 				 * udp_v6src is not set, we might be bound to
21227c478bd9Sstevel@tonic-gate 				 * broadcast/multicast. Use udp_bound_v6src as
21237c478bd9Sstevel@tonic-gate 				 * local address instead (that could
21247c478bd9Sstevel@tonic-gate 				 * also still be UNSPECIFIED)
21257c478bd9Sstevel@tonic-gate 				 */
21267c478bd9Sstevel@tonic-gate 				sin6->sin6_addr =
21277c478bd9Sstevel@tonic-gate 				    udp->udp_bound_v6src;
21287c478bd9Sstevel@tonic-gate 			}
21297c478bd9Sstevel@tonic-gate 			sin6->sin6_port = udp->udp_port;
21307c478bd9Sstevel@tonic-gate 			ackmp->b_wptr = (uchar_t *)&sin6[1];
21317c478bd9Sstevel@tonic-gate 			if (udp->udp_state == TS_DATA_XFER) {
21327c478bd9Sstevel@tonic-gate 				/*
21337c478bd9Sstevel@tonic-gate 				 * connected, fill remote address too
21347c478bd9Sstevel@tonic-gate 				 */
21357c478bd9Sstevel@tonic-gate 				taa->REMADDR_length = sizeof (sin6_t);
21367c478bd9Sstevel@tonic-gate 				/* assumed 32-bit alignment */
21377c478bd9Sstevel@tonic-gate 				taa->REMADDR_offset = taa->LOCADDR_offset +
21387c478bd9Sstevel@tonic-gate 				    taa->LOCADDR_length;
21397c478bd9Sstevel@tonic-gate 
21407c478bd9Sstevel@tonic-gate 				sin6 = (sin6_t *)(ackmp->b_rptr +
21417c478bd9Sstevel@tonic-gate 				    taa->REMADDR_offset);
21427c478bd9Sstevel@tonic-gate 				/* initialize */
21437c478bd9Sstevel@tonic-gate 				*sin6 = sin6_null;
21447c478bd9Sstevel@tonic-gate 				sin6->sin6_family = AF_INET6;
21457c478bd9Sstevel@tonic-gate 				sin6->sin6_addr = udp->udp_v6dst;
21467c478bd9Sstevel@tonic-gate 				sin6->sin6_port =  udp->udp_dstport;
21477c478bd9Sstevel@tonic-gate 				ackmp->b_wptr = (uchar_t *)&sin6[1];
21487c478bd9Sstevel@tonic-gate 			}
21497c478bd9Sstevel@tonic-gate 			ackmp->b_wptr = (uchar_t *)&sin6[1];
21507c478bd9Sstevel@tonic-gate 		}
21517c478bd9Sstevel@tonic-gate 	}
2152fc80c0dfSnordmark 	rw_exit(&udp->udp_rwlock);
21537c478bd9Sstevel@tonic-gate 	ASSERT(ackmp->b_wptr <= ackmp->b_datap->db_lim);
2154fc80c0dfSnordmark 	qreply(q, ackmp);
21557c478bd9Sstevel@tonic-gate }
21567c478bd9Sstevel@tonic-gate 
21577c478bd9Sstevel@tonic-gate static void
21587c478bd9Sstevel@tonic-gate udp_copy_info(struct T_info_ack *tap, udp_t *udp)
21597c478bd9Sstevel@tonic-gate {
21607c478bd9Sstevel@tonic-gate 	if (udp->udp_family == AF_INET) {
21617c478bd9Sstevel@tonic-gate 		*tap = udp_g_t_info_ack_ipv4;
21627c478bd9Sstevel@tonic-gate 	} else {
21637c478bd9Sstevel@tonic-gate 		*tap = udp_g_t_info_ack_ipv6;
21647c478bd9Sstevel@tonic-gate 	}
21657c478bd9Sstevel@tonic-gate 	tap->CURRENT_state = udp->udp_state;
21667c478bd9Sstevel@tonic-gate 	tap->OPT_size = udp_max_optsize;
21677c478bd9Sstevel@tonic-gate }
21687c478bd9Sstevel@tonic-gate 
21697c478bd9Sstevel@tonic-gate /*
21707c478bd9Sstevel@tonic-gate  * This routine responds to T_CAPABILITY_REQ messages.  It is called by
21717c478bd9Sstevel@tonic-gate  * udp_wput.  Much of the T_CAPABILITY_ACK information is copied from
21727c478bd9Sstevel@tonic-gate  * udp_g_t_info_ack.  The current state of the stream is copied from
21737c478bd9Sstevel@tonic-gate  * udp_state.
21747c478bd9Sstevel@tonic-gate  */
21757c478bd9Sstevel@tonic-gate static void
21767c478bd9Sstevel@tonic-gate udp_capability_req(queue_t *q, mblk_t *mp)
21777c478bd9Sstevel@tonic-gate {
21787c478bd9Sstevel@tonic-gate 	t_uscalar_t		cap_bits1;
21797c478bd9Sstevel@tonic-gate 	struct T_capability_ack	*tcap;
2180ff550d0eSmasputra 	udp_t	*udp = Q_TO_UDP(q);
21817c478bd9Sstevel@tonic-gate 
21827c478bd9Sstevel@tonic-gate 	cap_bits1 = ((struct T_capability_req *)mp->b_rptr)->CAP_bits1;
21837c478bd9Sstevel@tonic-gate 
21847c478bd9Sstevel@tonic-gate 	mp = tpi_ack_alloc(mp, sizeof (struct T_capability_ack),
21857c478bd9Sstevel@tonic-gate 	    mp->b_datap->db_type, T_CAPABILITY_ACK);
21867c478bd9Sstevel@tonic-gate 	if (!mp)
21877c478bd9Sstevel@tonic-gate 		return;
21887c478bd9Sstevel@tonic-gate 
21897c478bd9Sstevel@tonic-gate 	tcap = (struct T_capability_ack *)mp->b_rptr;
21907c478bd9Sstevel@tonic-gate 	tcap->CAP_bits1 = 0;
21917c478bd9Sstevel@tonic-gate 
21927c478bd9Sstevel@tonic-gate 	if (cap_bits1 & TC1_INFO) {
21937c478bd9Sstevel@tonic-gate 		udp_copy_info(&tcap->INFO_ack, udp);
21947c478bd9Sstevel@tonic-gate 		tcap->CAP_bits1 |= TC1_INFO;
21957c478bd9Sstevel@tonic-gate 	}
21967c478bd9Sstevel@tonic-gate 
2197fc80c0dfSnordmark 	qreply(q, mp);
21987c478bd9Sstevel@tonic-gate }
21997c478bd9Sstevel@tonic-gate 
22007c478bd9Sstevel@tonic-gate /*
22017c478bd9Sstevel@tonic-gate  * This routine responds to T_INFO_REQ messages.  It is called by udp_wput.
22027c478bd9Sstevel@tonic-gate  * Most of the T_INFO_ACK information is copied from udp_g_t_info_ack.
22037c478bd9Sstevel@tonic-gate  * The current state of the stream is copied from udp_state.
22047c478bd9Sstevel@tonic-gate  */
22057c478bd9Sstevel@tonic-gate static void
22067c478bd9Sstevel@tonic-gate udp_info_req(queue_t *q, mblk_t *mp)
22077c478bd9Sstevel@tonic-gate {
2208ff550d0eSmasputra 	udp_t *udp = Q_TO_UDP(q);
22097c478bd9Sstevel@tonic-gate 
22107c478bd9Sstevel@tonic-gate 	/* Create a T_INFO_ACK message. */
22117c478bd9Sstevel@tonic-gate 	mp = tpi_ack_alloc(mp, sizeof (struct T_info_ack), M_PCPROTO,
22127c478bd9Sstevel@tonic-gate 	    T_INFO_ACK);
22137c478bd9Sstevel@tonic-gate 	if (!mp)
22147c478bd9Sstevel@tonic-gate 		return;
22157c478bd9Sstevel@tonic-gate 	udp_copy_info((struct T_info_ack *)mp->b_rptr, udp);
2216fc80c0dfSnordmark 	qreply(q, mp);
22177c478bd9Sstevel@tonic-gate }
22187c478bd9Sstevel@tonic-gate 
22197c478bd9Sstevel@tonic-gate /*
22207c478bd9Sstevel@tonic-gate  * IP recognizes seven kinds of bind requests:
22217c478bd9Sstevel@tonic-gate  *
22227c478bd9Sstevel@tonic-gate  * - A zero-length address binds only to the protocol number.
22237c478bd9Sstevel@tonic-gate  *
22247c478bd9Sstevel@tonic-gate  * - A 4-byte address is treated as a request to
22257c478bd9Sstevel@tonic-gate  * validate that the address is a valid local IPv4
22267c478bd9Sstevel@tonic-gate  * address, appropriate for an application to bind to.
22277c478bd9Sstevel@tonic-gate  * IP does the verification, but does not make any note
22287c478bd9Sstevel@tonic-gate  * of the address at this time.
22297c478bd9Sstevel@tonic-gate  *
22307c478bd9Sstevel@tonic-gate  * - A 16-byte address contains is treated as a request
22317c478bd9Sstevel@tonic-gate  * to validate a local IPv6 address, as the 4-byte
22327c478bd9Sstevel@tonic-gate  * address case above.
22337c478bd9Sstevel@tonic-gate  *
22347c478bd9Sstevel@tonic-gate  * - A 16-byte sockaddr_in to validate the local IPv4 address and also
22357c478bd9Sstevel@tonic-gate  * use it for the inbound fanout of packets.
22367c478bd9Sstevel@tonic-gate  *
22377c478bd9Sstevel@tonic-gate  * - A 24-byte sockaddr_in6 to validate the local IPv6 address and also
22387c478bd9Sstevel@tonic-gate  * use it for the inbound fanout of packets.
22397c478bd9Sstevel@tonic-gate  *
22407c478bd9Sstevel@tonic-gate  * - A 12-byte address (ipa_conn_t) containing complete IPv4 fanout
22417c478bd9Sstevel@tonic-gate  * information consisting of local and remote addresses
22427c478bd9Sstevel@tonic-gate  * and ports.  In this case, the addresses are both
22437c478bd9Sstevel@tonic-gate  * validated as appropriate for this operation, and, if
22447c478bd9Sstevel@tonic-gate  * so, the information is retained for use in the
22457c478bd9Sstevel@tonic-gate  * inbound fanout.
22467c478bd9Sstevel@tonic-gate  *
22477c478bd9Sstevel@tonic-gate  * - A 36-byte address address (ipa6_conn_t) containing complete IPv6
22487c478bd9Sstevel@tonic-gate  * fanout information, like the 12-byte case above.
22497c478bd9Sstevel@tonic-gate  *
22507c478bd9Sstevel@tonic-gate  * IP will also fill in the IRE request mblk with information
22517c478bd9Sstevel@tonic-gate  * regarding our peer.  In all cases, we notify IP of our protocol
22527c478bd9Sstevel@tonic-gate  * type by appending a single protocol byte to the bind request.
22537c478bd9Sstevel@tonic-gate  */
22547c478bd9Sstevel@tonic-gate static mblk_t *
22557c478bd9Sstevel@tonic-gate udp_ip_bind_mp(udp_t *udp, t_scalar_t bind_prim, t_scalar_t addr_length)
22567c478bd9Sstevel@tonic-gate {
22577c478bd9Sstevel@tonic-gate 	char	*cp;
22587c478bd9Sstevel@tonic-gate 	mblk_t	*mp;
22597c478bd9Sstevel@tonic-gate 	struct T_bind_req *tbr;
22607c478bd9Sstevel@tonic-gate 	ipa_conn_t	*ac;
22617c478bd9Sstevel@tonic-gate 	ipa6_conn_t	*ac6;
22627c478bd9Sstevel@tonic-gate 	sin_t		*sin;
22637c478bd9Sstevel@tonic-gate 	sin6_t		*sin6;
22647c478bd9Sstevel@tonic-gate 
22657c478bd9Sstevel@tonic-gate 	ASSERT(bind_prim == O_T_BIND_REQ || bind_prim == T_BIND_REQ);
2266fc80c0dfSnordmark 	ASSERT(RW_LOCK_HELD(&udp->udp_rwlock));
22677c478bd9Sstevel@tonic-gate 	mp = allocb(sizeof (*tbr) + addr_length + 1, BPRI_HI);
22687c478bd9Sstevel@tonic-gate 	if (!mp)
22697c478bd9Sstevel@tonic-gate 		return (mp);
22707c478bd9Sstevel@tonic-gate 	mp->b_datap->db_type = M_PROTO;
22717c478bd9Sstevel@tonic-gate 	tbr = (struct T_bind_req *)mp->b_rptr;
22727c478bd9Sstevel@tonic-gate 	tbr->PRIM_type = bind_prim;
22737c478bd9Sstevel@tonic-gate 	tbr->ADDR_offset = sizeof (*tbr);
22747c478bd9Sstevel@tonic-gate 	tbr->CONIND_number = 0;
22757c478bd9Sstevel@tonic-gate 	tbr->ADDR_length = addr_length;
22767c478bd9Sstevel@tonic-gate 	cp = (char *)&tbr[1];
22777c478bd9Sstevel@tonic-gate 	switch (addr_length) {
22787c478bd9Sstevel@tonic-gate 	case sizeof (ipa_conn_t):
22797c478bd9Sstevel@tonic-gate 		ASSERT(udp->udp_family == AF_INET);
22807c478bd9Sstevel@tonic-gate 		/* Append a request for an IRE */
22817c478bd9Sstevel@tonic-gate 		mp->b_cont = allocb(sizeof (ire_t), BPRI_HI);
22827c478bd9Sstevel@tonic-gate 		if (!mp->b_cont) {
22837c478bd9Sstevel@tonic-gate 			freemsg(mp);
22847c478bd9Sstevel@tonic-gate 			return (NULL);
22857c478bd9Sstevel@tonic-gate 		}
22867c478bd9Sstevel@tonic-gate 		mp->b_cont->b_wptr += sizeof (ire_t);
22877c478bd9Sstevel@tonic-gate 		mp->b_cont->b_datap->db_type = IRE_DB_REQ_TYPE;
22887c478bd9Sstevel@tonic-gate 
22897c478bd9Sstevel@tonic-gate 		/* cp known to be 32 bit aligned */
22907c478bd9Sstevel@tonic-gate 		ac = (ipa_conn_t *)cp;
22917c478bd9Sstevel@tonic-gate 		ac->ac_laddr = V4_PART_OF_V6(udp->udp_v6src);
22927c478bd9Sstevel@tonic-gate 		ac->ac_faddr = V4_PART_OF_V6(udp->udp_v6dst);
22937c478bd9Sstevel@tonic-gate 		ac->ac_fport = udp->udp_dstport;
22947c478bd9Sstevel@tonic-gate 		ac->ac_lport = udp->udp_port;
22957c478bd9Sstevel@tonic-gate 		break;
22967c478bd9Sstevel@tonic-gate 
22977c478bd9Sstevel@tonic-gate 	case sizeof (ipa6_conn_t):
22987c478bd9Sstevel@tonic-gate 		ASSERT(udp->udp_family == AF_INET6);
22997c478bd9Sstevel@tonic-gate 		/* Append a request for an IRE */
23007c478bd9Sstevel@tonic-gate 		mp->b_cont = allocb(sizeof (ire_t), BPRI_HI);
23017c478bd9Sstevel@tonic-gate 		if (!mp->b_cont) {
23027c478bd9Sstevel@tonic-gate 			freemsg(mp);
23037c478bd9Sstevel@tonic-gate 			return (NULL);
23047c478bd9Sstevel@tonic-gate 		}
23057c478bd9Sstevel@tonic-gate 		mp->b_cont->b_wptr += sizeof (ire_t);
23067c478bd9Sstevel@tonic-gate 		mp->b_cont->b_datap->db_type = IRE_DB_REQ_TYPE;
23077c478bd9Sstevel@tonic-gate 
23087c478bd9Sstevel@tonic-gate 		/* cp known to be 32 bit aligned */
23097c478bd9Sstevel@tonic-gate 		ac6 = (ipa6_conn_t *)cp;
23107c478bd9Sstevel@tonic-gate 		ac6->ac6_laddr = udp->udp_v6src;
23117c478bd9Sstevel@tonic-gate 		ac6->ac6_faddr = udp->udp_v6dst;
23127c478bd9Sstevel@tonic-gate 		ac6->ac6_fport = udp->udp_dstport;
23137c478bd9Sstevel@tonic-gate 		ac6->ac6_lport = udp->udp_port;
23147c478bd9Sstevel@tonic-gate 		break;
23157c478bd9Sstevel@tonic-gate 
23167c478bd9Sstevel@tonic-gate 	case sizeof (sin_t):
23177c478bd9Sstevel@tonic-gate 		ASSERT(udp->udp_family == AF_INET);
23187c478bd9Sstevel@tonic-gate 		/* Append a request for an IRE */
23197c478bd9Sstevel@tonic-gate 		mp->b_cont = allocb(sizeof (ire_t), BPRI_HI);
23207c478bd9Sstevel@tonic-gate 		if (!mp->b_cont) {
23217c478bd9Sstevel@tonic-gate 			freemsg(mp);
23227c478bd9Sstevel@tonic-gate 			return (NULL);
23237c478bd9Sstevel@tonic-gate 		}
23247c478bd9Sstevel@tonic-gate 		mp->b_cont->b_wptr += sizeof (ire_t);
23257c478bd9Sstevel@tonic-gate 		mp->b_cont->b_datap->db_type = IRE_DB_REQ_TYPE;
23267c478bd9Sstevel@tonic-gate 
23277c478bd9Sstevel@tonic-gate 		sin = (sin_t *)cp;
23287c478bd9Sstevel@tonic-gate 		*sin = sin_null;
23297c478bd9Sstevel@tonic-gate 		sin->sin_family = AF_INET;
23307c478bd9Sstevel@tonic-gate 		sin->sin_addr.s_addr = V4_PART_OF_V6(udp->udp_bound_v6src);
23317c478bd9Sstevel@tonic-gate 		sin->sin_port = udp->udp_port;
23327c478bd9Sstevel@tonic-gate 		break;
23337c478bd9Sstevel@tonic-gate 
23347c478bd9Sstevel@tonic-gate 	case sizeof (sin6_t):
23357c478bd9Sstevel@tonic-gate 		ASSERT(udp->udp_family == AF_INET6);
23367c478bd9Sstevel@tonic-gate 		/* Append a request for an IRE */
23377c478bd9Sstevel@tonic-gate 		mp->b_cont = allocb(sizeof (ire_t), BPRI_HI);
23387c478bd9Sstevel@tonic-gate 		if (!mp->b_cont) {
23397c478bd9Sstevel@tonic-gate 			freemsg(mp);
23407c478bd9Sstevel@tonic-gate 			return (NULL);
23417c478bd9Sstevel@tonic-gate 		}
23427c478bd9Sstevel@tonic-gate 		mp->b_cont->b_wptr += sizeof (ire_t);
23437c478bd9Sstevel@tonic-gate 		mp->b_cont->b_datap->db_type = IRE_DB_REQ_TYPE;
23447c478bd9Sstevel@tonic-gate 
23457c478bd9Sstevel@tonic-gate 		sin6 = (sin6_t *)cp;
23467c478bd9Sstevel@tonic-gate 		*sin6 = sin6_null;
23477c478bd9Sstevel@tonic-gate 		sin6->sin6_family = AF_INET6;
23487c478bd9Sstevel@tonic-gate 		sin6->sin6_addr = udp->udp_bound_v6src;
23497c478bd9Sstevel@tonic-gate 		sin6->sin6_port = udp->udp_port;
23507c478bd9Sstevel@tonic-gate 		break;
23517c478bd9Sstevel@tonic-gate 	}
23527c478bd9Sstevel@tonic-gate 	/* Add protocol number to end */
23537c478bd9Sstevel@tonic-gate 	cp[addr_length] = (char)IPPROTO_UDP;
23547c478bd9Sstevel@tonic-gate 	mp->b_wptr = (uchar_t *)&cp[addr_length + 1];
23557c478bd9Sstevel@tonic-gate 	return (mp);
23567c478bd9Sstevel@tonic-gate }
23577c478bd9Sstevel@tonic-gate 
2358fc80c0dfSnordmark /* For /dev/udp aka AF_INET open */
2359fc80c0dfSnordmark static int
2360fc80c0dfSnordmark udp_openv4(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp)
2361fc80c0dfSnordmark {
2362fc80c0dfSnordmark 	return (udp_open(q, devp, flag, sflag, credp, B_FALSE));
2363fc80c0dfSnordmark }
2364fc80c0dfSnordmark 
2365fc80c0dfSnordmark /* For /dev/udp6 aka AF_INET6 open */
2366fc80c0dfSnordmark static int
2367fc80c0dfSnordmark udp_openv6(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp)
2368fc80c0dfSnordmark {
2369fc80c0dfSnordmark 	return (udp_open(q, devp, flag, sflag, credp, B_TRUE));
2370fc80c0dfSnordmark }
2371fc80c0dfSnordmark 
23727c478bd9Sstevel@tonic-gate /*
23737c478bd9Sstevel@tonic-gate  * This is the open routine for udp.  It allocates a udp_t structure for
23747c478bd9Sstevel@tonic-gate  * the stream and, on the first open of the module, creates an ND table.
23757c478bd9Sstevel@tonic-gate  */
2376fc80c0dfSnordmark /*ARGSUSED2*/
23777c478bd9Sstevel@tonic-gate static int
2378fc80c0dfSnordmark udp_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp,
2379fc80c0dfSnordmark     boolean_t isv6)
23807c478bd9Sstevel@tonic-gate {
2381aa92d85bSgt 	int		err;
2382aa92d85bSgt 	udp_t		*udp;
2383aa92d85bSgt 	conn_t		*connp;
2384aa92d85bSgt 	dev_t		conn_dev;
2385aa92d85bSgt 	zoneid_t	zoneid;
2386aa92d85bSgt 	netstack_t	*ns;
2387aa92d85bSgt 	udp_stack_t	*us;
2388aa92d85bSgt 	vmem_t		*minor_arena;
23897c478bd9Sstevel@tonic-gate 
23907c478bd9Sstevel@tonic-gate 	TRACE_1(TR_FAC_UDP, TR_UDP_OPEN, "udp_open: q %p", q);
23917c478bd9Sstevel@tonic-gate 
23927c478bd9Sstevel@tonic-gate 	/* If the stream is already open, return immediately. */
23937c478bd9Sstevel@tonic-gate 	if (q->q_ptr != NULL)
23947c478bd9Sstevel@tonic-gate 		return (0);
23957c478bd9Sstevel@tonic-gate 
2396fc80c0dfSnordmark 	if (sflag == MODOPEN)
23977c478bd9Sstevel@tonic-gate 		return (EINVAL);
23987c478bd9Sstevel@tonic-gate 
2399f4b3ec61Sdh 	ns = netstack_find_by_cred(credp);
2400f4b3ec61Sdh 	ASSERT(ns != NULL);
2401f4b3ec61Sdh 	us = ns->netstack_udp;
2402f4b3ec61Sdh 	ASSERT(us != NULL);
2403f4b3ec61Sdh 
2404f4b3ec61Sdh 	/*
2405f4b3ec61Sdh 	 * For exclusive stacks we set the zoneid to zero
2406f4b3ec61Sdh 	 * to make UDP operate as if in the global zone.
2407f4b3ec61Sdh 	 */
2408fc80c0dfSnordmark 	if (ns->netstack_stackid != GLOBAL_NETSTACKID)
2409f4b3ec61Sdh 		zoneid = GLOBAL_ZONEID;
2410f4b3ec61Sdh 	else
2411f4b3ec61Sdh 		zoneid = crgetzoneid(credp);
2412f4b3ec61Sdh 
2413aa92d85bSgt 	if ((ip_minor_arena_la != NULL) && (flag & SO_SOCKSTR) &&
2414aa92d85bSgt 	    ((conn_dev = inet_minor_alloc(ip_minor_arena_la)) != 0)) {
2415aa92d85bSgt 		minor_arena = ip_minor_arena_la;
2416aa92d85bSgt 	} else {
2417aa92d85bSgt 		/*
2418aa92d85bSgt 		 * Either minor numbers in the large arena were exhausted
2419aa92d85bSgt 		 * or a non socket application is doing the open.
2420aa92d85bSgt 		 * Try to allocate from the small arena.
2421aa92d85bSgt 		 */
2422aa92d85bSgt 		if ((conn_dev = inet_minor_alloc(ip_minor_arena_sa)) == 0) {
2423aa92d85bSgt 			netstack_rele(ns);
2424aa92d85bSgt 			return (EBUSY);
2425aa92d85bSgt 		}
2426aa92d85bSgt 		minor_arena = ip_minor_arena_sa;
2427fc80c0dfSnordmark 	}
2428aa92d85bSgt 
2429fc80c0dfSnordmark 	*devp = makedevice(getemajor(*devp), (minor_t)conn_dev);
2430ff550d0eSmasputra 
2431fc80c0dfSnordmark 	connp = ipcl_conn_create(IPCL_UDPCONN, KM_SLEEP, ns);
2432fc80c0dfSnordmark 	connp->conn_dev = conn_dev;
2433aa92d85bSgt 	connp->conn_minor_arena = minor_arena;
2434fc80c0dfSnordmark 	udp = connp->conn_udp;
2435ff550d0eSmasputra 
2436ff550d0eSmasputra 	/*
2437fc80c0dfSnordmark 	 * ipcl_conn_create did a netstack_hold. Undo the hold that was
2438fc80c0dfSnordmark 	 * done by netstack_find_by_cred()
2439ff550d0eSmasputra 	 */
2440fc80c0dfSnordmark 	netstack_rele(ns);
2441ff550d0eSmasputra 
24427c478bd9Sstevel@tonic-gate 	/*
2443ff550d0eSmasputra 	 * Initialize the udp_t structure for this stream.
24447c478bd9Sstevel@tonic-gate 	 */
2445fc80c0dfSnordmark 	q->q_ptr = connp;
2446fc80c0dfSnordmark 	WR(q)->q_ptr = connp;
2447fc80c0dfSnordmark 	connp->conn_rq = q;
2448fc80c0dfSnordmark 	connp->conn_wq = WR(q);
2449fc80c0dfSnordmark 
2450fc80c0dfSnordmark 	rw_enter(&udp->udp_rwlock, RW_WRITER);
2451fc80c0dfSnordmark 	ASSERT(connp->conn_ulp == IPPROTO_UDP);
2452fc80c0dfSnordmark 	ASSERT(connp->conn_udp == udp);
2453fc80c0dfSnordmark 	ASSERT(udp->udp_connp == connp);
24547c478bd9Sstevel@tonic-gate 
24557c478bd9Sstevel@tonic-gate 	/* Set the initial state of the stream and the privilege status. */
24567c478bd9Sstevel@tonic-gate 	udp->udp_state = TS_UNBND;
2457fc80c0dfSnordmark 	if (isv6) {
24587c478bd9Sstevel@tonic-gate 		udp->udp_family = AF_INET6;
24597c478bd9Sstevel@tonic-gate 		udp->udp_ipversion = IPV6_VERSION;
24607c478bd9Sstevel@tonic-gate 		udp->udp_max_hdr_len = IPV6_HDR_LEN + UDPH_SIZE;
2461f4b3ec61Sdh 		udp->udp_ttl = us->us_ipv6_hoplimit;
2462ff550d0eSmasputra 		connp->conn_af_isv6 = B_TRUE;
2463ff550d0eSmasputra 		connp->conn_flags |= IPCL_ISV6;
24647c478bd9Sstevel@tonic-gate 	} else {
24657c478bd9Sstevel@tonic-gate 		udp->udp_family = AF_INET;
24667c478bd9Sstevel@tonic-gate 		udp->udp_ipversion = IPV4_VERSION;
24677c478bd9Sstevel@tonic-gate 		udp->udp_max_hdr_len = IP_SIMPLE_HDR_LENGTH + UDPH_SIZE;
2468f4b3ec61Sdh 		udp->udp_ttl = us->us_ipv4_ttl;
2469ff550d0eSmasputra 		connp->conn_af_isv6 = B_FALSE;
2470ff550d0eSmasputra 		connp->conn_flags &= ~IPCL_ISV6;
24717c478bd9Sstevel@tonic-gate 	}
24727c478bd9Sstevel@tonic-gate 
24737c478bd9Sstevel@tonic-gate 	udp->udp_multicast_ttl = IP_DEFAULT_MULTICAST_TTL;
2474fc80c0dfSnordmark 	udp->udp_pending_op = -1;
2475ff550d0eSmasputra 	connp->conn_multicast_loop = IP_DEFAULT_MULTICAST_LOOP;
2476ff550d0eSmasputra 	connp->conn_zoneid = zoneid;
24777c478bd9Sstevel@tonic-gate 
24783173664eSapersson 	udp->udp_open_time = lbolt64;
24793173664eSapersson 	udp->udp_open_pid = curproc->p_pid;
24803173664eSapersson 
248145916cd2Sjpk 	/*
248245916cd2Sjpk 	 * If the caller has the process-wide flag set, then default to MAC
248345916cd2Sjpk 	 * exempt mode.  This allows read-down to unlabeled hosts.
248445916cd2Sjpk 	 */
248545916cd2Sjpk 	if (getpflags(NET_MAC_AWARE, credp) != 0)
2486222c5bceSkp 		connp->conn_mac_exempt = B_TRUE;
248745916cd2Sjpk 
2488fc80c0dfSnordmark 	if (flag & SO_SOCKSTR) {
2489fc80c0dfSnordmark 		connp->conn_flags |= IPCL_SOCKET;
2490ff550d0eSmasputra 		udp->udp_issocket = B_TRUE;
2491ff550d0eSmasputra 		udp->udp_direct_sockfs = B_TRUE;
2492ff550d0eSmasputra 	}
249345916cd2Sjpk 
249445916cd2Sjpk 	connp->conn_ulp_labeled = is_system_labeled();
249545916cd2Sjpk 
2496f4b3ec61Sdh 	udp->udp_us = us;
24977c478bd9Sstevel@tonic-gate 
2498f4b3ec61Sdh 	q->q_hiwat = us->us_recv_hiwat;
2499f4b3ec61Sdh 	WR(q)->q_hiwat = us->us_xmit_hiwat;
2500f4b3ec61Sdh 	WR(q)->q_lowat = us->us_xmit_lowat;
25017c478bd9Sstevel@tonic-gate 
2502fc80c0dfSnordmark 	connp->conn_recv = udp_input;
2503fc80c0dfSnordmark 	crhold(credp);
2504fc80c0dfSnordmark 	connp->conn_cred = credp;
2505fc80c0dfSnordmark 
2506fc80c0dfSnordmark 	mutex_enter(&connp->conn_lock);
2507fc80c0dfSnordmark 	connp->conn_state_flags &= ~CONN_INCIPIENT;
2508fc80c0dfSnordmark 	mutex_exit(&connp->conn_lock);
2509fc80c0dfSnordmark 
2510fc80c0dfSnordmark 	qprocson(q);
2511fc80c0dfSnordmark 
25127c478bd9Sstevel@tonic-gate 	if (udp->udp_family == AF_INET6) {
25137c478bd9Sstevel@tonic-gate 		/* Build initial header template for transmit */
2514fc80c0dfSnordmark 		if ((err = udp_build_hdrs(udp)) != 0) {
2515fc80c0dfSnordmark 			rw_exit(&udp->udp_rwlock);
2516fc80c0dfSnordmark 			qprocsoff(q);
2517fc80c0dfSnordmark 			ipcl_conn_destroy(connp);
2518ff550d0eSmasputra 			return (err);
25197c478bd9Sstevel@tonic-gate 		}
25207c478bd9Sstevel@tonic-gate 	}
2521fc80c0dfSnordmark 	rw_exit(&udp->udp_rwlock);
25227c478bd9Sstevel@tonic-gate 
2523ff550d0eSmasputra 	/* Set the Stream head write offset and high watermark. */
2524fc80c0dfSnordmark 	(void) mi_set_sth_wroff(q,
2525f4b3ec61Sdh 	    udp->udp_max_hdr_len + us->us_wroff_extra);
2526fc80c0dfSnordmark 	(void) mi_set_sth_hiwat(q, udp_set_rcv_hiwat(udp, q->q_hiwat));
25277c478bd9Sstevel@tonic-gate 
2528ff550d0eSmasputra 	return (0);
25297c478bd9Sstevel@tonic-gate }
25307c478bd9Sstevel@tonic-gate 
25317c478bd9Sstevel@tonic-gate /*
25327c478bd9Sstevel@tonic-gate  * Which UDP options OK to set through T_UNITDATA_REQ...
25337c478bd9Sstevel@tonic-gate  */
25347c478bd9Sstevel@tonic-gate /* ARGSUSED */
25357c478bd9Sstevel@tonic-gate static boolean_t
25367c478bd9Sstevel@tonic-gate udp_opt_allow_udr_set(t_scalar_t level, t_scalar_t name)
25377c478bd9Sstevel@tonic-gate {
25387c478bd9Sstevel@tonic-gate 	return (B_TRUE);
25397c478bd9Sstevel@tonic-gate }
25407c478bd9Sstevel@tonic-gate 
25417c478bd9Sstevel@tonic-gate /*
25427c478bd9Sstevel@tonic-gate  * This routine gets default values of certain options whose default
25437c478bd9Sstevel@tonic-gate  * values are maintained by protcol specific code
25447c478bd9Sstevel@tonic-gate  */
25457c478bd9Sstevel@tonic-gate /* ARGSUSED */
25467c478bd9Sstevel@tonic-gate int
2547fc80c0dfSnordmark udp_opt_default(queue_t *q, t_scalar_t level, t_scalar_t name, uchar_t *ptr)
25487c478bd9Sstevel@tonic-gate {
2549f4b3ec61Sdh 	udp_t		*udp = Q_TO_UDP(q);
2550f4b3ec61Sdh 	udp_stack_t *us = udp->udp_us;
25517c478bd9Sstevel@tonic-gate 	int *i1 = (int *)ptr;
25527c478bd9Sstevel@tonic-gate 
25537c478bd9Sstevel@tonic-gate 	switch (level) {
25547c478bd9Sstevel@tonic-gate 	case IPPROTO_IP:
25557c478bd9Sstevel@tonic-gate 		switch (name) {
25567c478bd9Sstevel@tonic-gate 		case IP_MULTICAST_TTL:
25577c478bd9Sstevel@tonic-gate 			*ptr = (uchar_t)IP_DEFAULT_MULTICAST_TTL;
25587c478bd9Sstevel@tonic-gate 			return (sizeof (uchar_t));
25597c478bd9Sstevel@tonic-gate 		case IP_MULTICAST_LOOP:
25607c478bd9Sstevel@tonic-gate 			*ptr = (uchar_t)IP_DEFAULT_MULTICAST_LOOP;
25617c478bd9Sstevel@tonic-gate 			return (sizeof (uchar_t));
25627c478bd9Sstevel@tonic-gate 		}
25637c478bd9Sstevel@tonic-gate 		break;
25647c478bd9Sstevel@tonic-gate 	case IPPROTO_IPV6:
25657c478bd9Sstevel@tonic-gate 		switch (name) {
25667c478bd9Sstevel@tonic-gate 		case IPV6_MULTICAST_HOPS:
25677c478bd9Sstevel@tonic-gate 			*i1 = IP_DEFAULT_MULTICAST_TTL;
25687c478bd9Sstevel@tonic-gate 			return (sizeof (int));
25697c478bd9Sstevel@tonic-gate 		case IPV6_MULTICAST_LOOP:
25707c478bd9Sstevel@tonic-gate 			*i1 = IP_DEFAULT_MULTICAST_LOOP;
25717c478bd9Sstevel@tonic-gate 			return (sizeof (int));
25727c478bd9Sstevel@tonic-gate 		case IPV6_UNICAST_HOPS:
2573f4b3ec61Sdh 			*i1 = us->us_ipv6_hoplimit;
25747c478bd9Sstevel@tonic-gate 			return (sizeof (int));
25757c478bd9Sstevel@tonic-gate 		}
25767c478bd9Sstevel@tonic-gate 		break;
25777c478bd9Sstevel@tonic-gate 	}
25787c478bd9Sstevel@tonic-gate 	return (-1);
25797c478bd9Sstevel@tonic-gate }
25807c478bd9Sstevel@tonic-gate 
25817c478bd9Sstevel@tonic-gate /*
2582fc80c0dfSnordmark  * This routine retrieves the current status of socket options.
2583fc80c0dfSnordmark  * It returns the size of the option retrieved.
25847c478bd9Sstevel@tonic-gate  */
25857c478bd9Sstevel@tonic-gate int
2586fc80c0dfSnordmark udp_opt_get_locked(queue_t *q, t_scalar_t level, t_scalar_t name, uchar_t *ptr)
25877c478bd9Sstevel@tonic-gate {
25887c478bd9Sstevel@tonic-gate 	int	*i1 = (int *)ptr;
2589ff550d0eSmasputra 	conn_t	*connp;
2590ff550d0eSmasputra 	udp_t	*udp;
2591ff550d0eSmasputra 	ip6_pkt_t *ipp;
259245916cd2Sjpk 	int	len;
2593f4b3ec61Sdh 	udp_stack_t	*us;
2594ff550d0eSmasputra 
2595ff550d0eSmasputra 	connp = Q_TO_CONN(q);
2596ff550d0eSmasputra 	udp = connp->conn_udp;
2597ff550d0eSmasputra 	ipp = &udp->udp_sticky_ipp;
2598f4b3ec61Sdh 	us = udp->udp_us;
25997c478bd9Sstevel@tonic-gate 
26007c478bd9Sstevel@tonic-gate 	switch (level) {
26017c478bd9Sstevel@tonic-gate 	case SOL_SOCKET:
26027c478bd9Sstevel@tonic-gate 		switch (name) {
26037c478bd9Sstevel@tonic-gate 		case SO_DEBUG:
26047c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_debug;
26057c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
26067c478bd9Sstevel@tonic-gate 		case SO_REUSEADDR:
26077c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_reuseaddr;
26087c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
26097c478bd9Sstevel@tonic-gate 		case SO_TYPE:
26107c478bd9Sstevel@tonic-gate 			*i1 = SOCK_DGRAM;
26117c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
26127c478bd9Sstevel@tonic-gate 
26137c478bd9Sstevel@tonic-gate 		/*
26147c478bd9Sstevel@tonic-gate 		 * The following three items are available here,
26157c478bd9Sstevel@tonic-gate 		 * but are only meaningful to IP.
26167c478bd9Sstevel@tonic-gate 		 */
26177c478bd9Sstevel@tonic-gate 		case SO_DONTROUTE:
26187c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_dontroute;
26197c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
26207c478bd9Sstevel@tonic-gate 		case SO_USELOOPBACK:
26217c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_useloopback;
26227c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
26237c478bd9Sstevel@tonic-gate 		case SO_BROADCAST:
26247c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_broadcast;
26257c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
26267c478bd9Sstevel@tonic-gate 
26277c478bd9Sstevel@tonic-gate 		case SO_SNDBUF:
26287c478bd9Sstevel@tonic-gate 			*i1 = q->q_hiwat;
26297c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
26307c478bd9Sstevel@tonic-gate 		case SO_RCVBUF:
26317c478bd9Sstevel@tonic-gate 			*i1 = RD(q)->q_hiwat;
26327c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
26337c478bd9Sstevel@tonic-gate 		case SO_DGRAM_ERRIND:
26347c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_dgram_errind;
26357c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
26367c478bd9Sstevel@tonic-gate 		case SO_RECVUCRED:
26377c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_recvucred;
26387c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
2639e4f35dbaSgt 		case SO_TIMESTAMP:
2640e4f35dbaSgt 			*i1 = udp->udp_timestamp;
26415d0bc3edSsommerfe 			break;	/* goto sizeof (int) option return */
264245916cd2Sjpk 		case SO_ANON_MLP:
2643222c5bceSkp 			*i1 = connp->conn_anon_mlp;
264445916cd2Sjpk 			break;	/* goto sizeof (int) option return */
264545916cd2Sjpk 		case SO_MAC_EXEMPT:
2646222c5bceSkp 			*i1 = connp->conn_mac_exempt;
264745916cd2Sjpk 			break;	/* goto sizeof (int) option return */
26485d0bc3edSsommerfe 		case SO_ALLZONES:
26495d0bc3edSsommerfe 			*i1 = connp->conn_allzones;
26505d0bc3edSsommerfe 			break;	/* goto sizeof (int) option return */
2651ae347574Skcpoon 		case SO_EXCLBIND:
2652ae347574Skcpoon 			*i1 = udp->udp_exclbind ? SO_EXCLBIND : 0;
2653ae347574Skcpoon 			break;
265488cda078Skcpoon 		case SO_PROTOTYPE:
265588cda078Skcpoon 			*i1 = IPPROTO_UDP;
265688cda078Skcpoon 			break;
265788cda078Skcpoon 		case SO_DOMAIN:
265888cda078Skcpoon 			*i1 = udp->udp_family;
265988cda078Skcpoon 			break;
26607c478bd9Sstevel@tonic-gate 		default:
26617c478bd9Sstevel@tonic-gate 			return (-1);
26627c478bd9Sstevel@tonic-gate 		}
26637c478bd9Sstevel@tonic-gate 		break;
26647c478bd9Sstevel@tonic-gate 	case IPPROTO_IP:
26657c478bd9Sstevel@tonic-gate 		if (udp->udp_family != AF_INET)
26667c478bd9Sstevel@tonic-gate 			return (-1);
26677c478bd9Sstevel@tonic-gate 		switch (name) {
26687c478bd9Sstevel@tonic-gate 		case IP_OPTIONS:
26697c478bd9Sstevel@tonic-gate 		case T_IP_OPTIONS:
267045916cd2Sjpk 			len = udp->udp_ip_rcv_options_len - udp->udp_label_len;
267145916cd2Sjpk 			if (len > 0) {
267245916cd2Sjpk 				bcopy(udp->udp_ip_rcv_options +
267345916cd2Sjpk 				    udp->udp_label_len, ptr, len);
267445916cd2Sjpk 			}
267545916cd2Sjpk 			return (len);
26767c478bd9Sstevel@tonic-gate 		case IP_TOS:
26777c478bd9Sstevel@tonic-gate 		case T_IP_TOS:
26787c478bd9Sstevel@tonic-gate 			*i1 = (int)udp->udp_type_of_service;
26797c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
26807c478bd9Sstevel@tonic-gate 		case IP_TTL:
26817c478bd9Sstevel@tonic-gate 			*i1 = (int)udp->udp_ttl;
26827c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
2683e704a8f2Smeem 		case IP_DHCPINIT_IF:
2684e704a8f2Smeem 			return (-EINVAL);
268543d18f1cSpriyanka 		case IP_NEXTHOP:
268619a30e1aSrshoaib 		case IP_RECVPKTINFO:
268719a30e1aSrshoaib 			/*
268819a30e1aSrshoaib 			 * This also handles IP_PKTINFO.
268919a30e1aSrshoaib 			 * IP_PKTINFO and IP_RECVPKTINFO have the same value.
269019a30e1aSrshoaib 			 * Differentiation is based on the size of the argument
269119a30e1aSrshoaib 			 * passed in.
269219a30e1aSrshoaib 			 * This option is handled in IP which will return an
269319a30e1aSrshoaib 			 * error for IP_PKTINFO as it's not supported as a
269419a30e1aSrshoaib 			 * sticky option.
269519a30e1aSrshoaib 			 */
269643d18f1cSpriyanka 			return (-EINVAL);
26977c478bd9Sstevel@tonic-gate 		case IP_MULTICAST_IF:
26987c478bd9Sstevel@tonic-gate 			/* 0 address if not set */
26997c478bd9Sstevel@tonic-gate 			*(ipaddr_t *)ptr = udp->udp_multicast_if_addr;
27007c478bd9Sstevel@tonic-gate 			return (sizeof (ipaddr_t));
27017c478bd9Sstevel@tonic-gate 		case IP_MULTICAST_TTL:
27027c478bd9Sstevel@tonic-gate 			*(uchar_t *)ptr = udp->udp_multicast_ttl;
27037c478bd9Sstevel@tonic-gate 			return (sizeof (uchar_t));
27047c478bd9Sstevel@tonic-gate 		case IP_MULTICAST_LOOP:
2705ff550d0eSmasputra 			*ptr = connp->conn_multicast_loop;
27067c478bd9Sstevel@tonic-gate 			return (sizeof (uint8_t));
27077c478bd9Sstevel@tonic-gate 		case IP_RECVOPTS:
27087c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_recvopts;
27097c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
27107c478bd9Sstevel@tonic-gate 		case IP_RECVDSTADDR:
27117c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_recvdstaddr;
27127c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
27137c478bd9Sstevel@tonic-gate 		case IP_RECVIF:
27147c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_recvif;
27157c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
27167c478bd9Sstevel@tonic-gate 		case IP_RECVSLLA:
27177c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_recvslla;
27187c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
27197c478bd9Sstevel@tonic-gate 		case IP_RECVTTL:
27207c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_recvttl;
27217c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
27227c478bd9Sstevel@tonic-gate 		case IP_ADD_MEMBERSHIP:
27237c478bd9Sstevel@tonic-gate 		case IP_DROP_MEMBERSHIP:
27247c478bd9Sstevel@tonic-gate 		case IP_BLOCK_SOURCE:
27257c478bd9Sstevel@tonic-gate 		case IP_UNBLOCK_SOURCE:
27267c478bd9Sstevel@tonic-gate 		case IP_ADD_SOURCE_MEMBERSHIP:
27277c478bd9Sstevel@tonic-gate 		case IP_DROP_SOURCE_MEMBERSHIP:
27287c478bd9Sstevel@tonic-gate 		case MCAST_JOIN_GROUP:
27297c478bd9Sstevel@tonic-gate 		case MCAST_LEAVE_GROUP:
27307c478bd9Sstevel@tonic-gate 		case MCAST_BLOCK_SOURCE:
27317c478bd9Sstevel@tonic-gate 		case MCAST_UNBLOCK_SOURCE:
27327c478bd9Sstevel@tonic-gate 		case MCAST_JOIN_SOURCE_GROUP:
27337c478bd9Sstevel@tonic-gate 		case MCAST_LEAVE_SOURCE_GROUP:
27347c478bd9Sstevel@tonic-gate 		case IP_DONTFAILOVER_IF:
27357c478bd9Sstevel@tonic-gate 			/* cannot "get" the value for these */
27367c478bd9Sstevel@tonic-gate 			return (-1);
27377c478bd9Sstevel@tonic-gate 		case IP_BOUND_IF:
27387c478bd9Sstevel@tonic-gate 			/* Zero if not set */
27397c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_bound_if;
27407c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
27417c478bd9Sstevel@tonic-gate 		case IP_UNSPEC_SRC:
27427c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_unspec_source;
27437c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
2744a7107231Smeem 		case IP_BROADCAST_TTL:
2745a7107231Smeem 			*(uchar_t *)ptr = connp->conn_broadcast_ttl;
2746a7107231Smeem 			return (sizeof (uchar_t));
27477c478bd9Sstevel@tonic-gate 		default:
27487c478bd9Sstevel@tonic-gate 			return (-1);
27497c478bd9Sstevel@tonic-gate 		}
27507c478bd9Sstevel@tonic-gate 		break;
27517c478bd9Sstevel@tonic-gate 	case IPPROTO_IPV6:
27527c478bd9Sstevel@tonic-gate 		if (udp->udp_family != AF_INET6)
27537c478bd9Sstevel@tonic-gate 			return (-1);
27547c478bd9Sstevel@tonic-gate 		switch (name) {
27557c478bd9Sstevel@tonic-gate 		case IPV6_UNICAST_HOPS:
27567c478bd9Sstevel@tonic-gate 			*i1 = (unsigned int)udp->udp_ttl;
27577c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
27587c478bd9Sstevel@tonic-gate 		case IPV6_MULTICAST_IF:
27597c478bd9Sstevel@tonic-gate 			/* 0 index if not set */
27607c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_multicast_if_index;
27617c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
27627c478bd9Sstevel@tonic-gate 		case IPV6_MULTICAST_HOPS:
27637c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_multicast_ttl;
27647c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
27657c478bd9Sstevel@tonic-gate 		case IPV6_MULTICAST_LOOP:
2766ff550d0eSmasputra 			*i1 = connp->conn_multicast_loop;
27677c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
27687c478bd9Sstevel@tonic-gate 		case IPV6_JOIN_GROUP:
27697c478bd9Sstevel@tonic-gate 		case IPV6_LEAVE_GROUP:
27707c478bd9Sstevel@tonic-gate 		case MCAST_JOIN_GROUP:
27717c478bd9Sstevel@tonic-gate 		case MCAST_LEAVE_GROUP:
27727c478bd9Sstevel@tonic-gate 		case MCAST_BLOCK_SOURCE:
27737c478bd9Sstevel@tonic-gate 		case MCAST_UNBLOCK_SOURCE:
27747c478bd9Sstevel@tonic-gate 		case MCAST_JOIN_SOURCE_GROUP:
27757c478bd9Sstevel@tonic-gate 		case MCAST_LEAVE_SOURCE_GROUP:
27767c478bd9Sstevel@tonic-gate 			/* cannot "get" the value for these */
27777c478bd9Sstevel@tonic-gate 			return (-1);
27787c478bd9Sstevel@tonic-gate 		case IPV6_BOUND_IF:
27797c478bd9Sstevel@tonic-gate 			/* Zero if not set */
27807c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_bound_if;
27817c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
27827c478bd9Sstevel@tonic-gate 		case IPV6_UNSPEC_SRC:
27837c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_unspec_source;
27847c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
27857c478bd9Sstevel@tonic-gate 		case IPV6_RECVPKTINFO:
278619a30e1aSrshoaib 			*i1 = udp->udp_ip_recvpktinfo;
27877c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
27887c478bd9Sstevel@tonic-gate 		case IPV6_RECVTCLASS:
27897c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_ipv6_recvtclass;
27907c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
27917c478bd9Sstevel@tonic-gate 		case IPV6_RECVPATHMTU:
27927c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_ipv6_recvpathmtu;
27937c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
27947c478bd9Sstevel@tonic-gate 		case IPV6_RECVHOPLIMIT:
27957c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_ipv6_recvhoplimit;
27967c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
27977c478bd9Sstevel@tonic-gate 		case IPV6_RECVHOPOPTS:
27987c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_ipv6_recvhopopts;
27997c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
28007c478bd9Sstevel@tonic-gate 		case IPV6_RECVDSTOPTS:
28017c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_ipv6_recvdstopts;
28027c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
28037c478bd9Sstevel@tonic-gate 		case _OLD_IPV6_RECVDSTOPTS:
28047c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_old_ipv6_recvdstopts;
28057c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
28067c478bd9Sstevel@tonic-gate 		case IPV6_RECVRTHDRDSTOPTS:
28077c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_ipv6_recvrthdrdstopts;
28087c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
28097c478bd9Sstevel@tonic-gate 		case IPV6_RECVRTHDR:
28107c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_ipv6_recvrthdr;
28117c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
28127c478bd9Sstevel@tonic-gate 		case IPV6_PKTINFO: {
28137c478bd9Sstevel@tonic-gate 			/* XXX assumes that caller has room for max size! */
28147c478bd9Sstevel@tonic-gate 			struct in6_pktinfo *pkti;
28157c478bd9Sstevel@tonic-gate 
28167c478bd9Sstevel@tonic-gate 			pkti = (struct in6_pktinfo *)ptr;
28177c478bd9Sstevel@tonic-gate 			if (ipp->ipp_fields & IPPF_IFINDEX)
28187c478bd9Sstevel@tonic-gate 				pkti->ipi6_ifindex = ipp->ipp_ifindex;
28197c478bd9Sstevel@tonic-gate 			else
28207c478bd9Sstevel@tonic-gate 				pkti->ipi6_ifindex = 0;
28217c478bd9Sstevel@tonic-gate 			if (ipp->ipp_fields & IPPF_ADDR)
28227c478bd9Sstevel@tonic-gate 				pkti->ipi6_addr = ipp->ipp_addr;
28237c478bd9Sstevel@tonic-gate 			else
28247c478bd9Sstevel@tonic-gate 				pkti->ipi6_addr = ipv6_all_zeros;
28257c478bd9Sstevel@tonic-gate 			return (sizeof (struct in6_pktinfo));
28267c478bd9Sstevel@tonic-gate 		}
28277c478bd9Sstevel@tonic-gate 		case IPV6_TCLASS:
28287c478bd9Sstevel@tonic-gate 			if (ipp->ipp_fields & IPPF_TCLASS)
28297c478bd9Sstevel@tonic-gate 				*i1 = ipp->ipp_tclass;
28307c478bd9Sstevel@tonic-gate 			else
28317c478bd9Sstevel@tonic-gate 				*i1 = IPV6_FLOW_TCLASS(
28327c478bd9Sstevel@tonic-gate 				    IPV6_DEFAULT_VERS_AND_FLOW);
28337c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
28347c478bd9Sstevel@tonic-gate 		case IPV6_NEXTHOP: {
28357c478bd9Sstevel@tonic-gate 			sin6_t *sin6 = (sin6_t *)ptr;
28367c478bd9Sstevel@tonic-gate 
28377c478bd9Sstevel@tonic-gate 			if (!(ipp->ipp_fields & IPPF_NEXTHOP))
28387c478bd9Sstevel@tonic-gate 				return (0);
28397c478bd9Sstevel@tonic-gate 			*sin6 = sin6_null;
28407c478bd9Sstevel@tonic-gate 			sin6->sin6_family = AF_INET6;
28417c478bd9Sstevel@tonic-gate 			sin6->sin6_addr = ipp->ipp_nexthop;
28427c478bd9Sstevel@tonic-gate 			return (sizeof (sin6_t));
28437c478bd9Sstevel@tonic-gate 		}
28447c478bd9Sstevel@tonic-gate 		case IPV6_HOPOPTS:
28457c478bd9Sstevel@tonic-gate 			if (!(ipp->ipp_fields & IPPF_HOPOPTS))
28467c478bd9Sstevel@tonic-gate 				return (0);
284745916cd2Sjpk 			if (ipp->ipp_hopoptslen <= udp->udp_label_len_v6)
284845916cd2Sjpk 				return (0);
284945916cd2Sjpk 			/*
285045916cd2Sjpk 			 * The cipso/label option is added by kernel.
285145916cd2Sjpk 			 * User is not usually aware of this option.
285245916cd2Sjpk 			 * We copy out the hbh opt after the label option.
285345916cd2Sjpk 			 */
285445916cd2Sjpk 			bcopy((char *)ipp->ipp_hopopts + udp->udp_label_len_v6,
285545916cd2Sjpk 			    ptr, ipp->ipp_hopoptslen - udp->udp_label_len_v6);
285645916cd2Sjpk 			if (udp->udp_label_len_v6 > 0) {
285745916cd2Sjpk 				ptr[0] = ((char *)ipp->ipp_hopopts)[0];
285845916cd2Sjpk 				ptr[1] = (ipp->ipp_hopoptslen -
285945916cd2Sjpk 				    udp->udp_label_len_v6 + 7) / 8 - 1;
286045916cd2Sjpk 			}
286145916cd2Sjpk 			return (ipp->ipp_hopoptslen - udp->udp_label_len_v6);
28627c478bd9Sstevel@tonic-gate 		case IPV6_RTHDRDSTOPTS:
28637c478bd9Sstevel@tonic-gate 			if (!(ipp->ipp_fields & IPPF_RTDSTOPTS))
28647c478bd9Sstevel@tonic-gate 				return (0);
28657c478bd9Sstevel@tonic-gate 			bcopy(ipp->ipp_rtdstopts, ptr, ipp->ipp_rtdstoptslen);
28667c478bd9Sstevel@tonic-gate 			return (ipp->ipp_rtdstoptslen);
28677c478bd9Sstevel@tonic-gate 		case IPV6_RTHDR:
28687c478bd9Sstevel@tonic-gate 			if (!(ipp->ipp_fields & IPPF_RTHDR))
28697c478bd9Sstevel@tonic-gate 				return (0);
28707c478bd9Sstevel@tonic-gate 			bcopy(ipp->ipp_rthdr, ptr, ipp->ipp_rthdrlen);
28717c478bd9Sstevel@tonic-gate 			return (ipp->ipp_rthdrlen);
28727c478bd9Sstevel@tonic-gate 		case IPV6_DSTOPTS:
28737c478bd9Sstevel@tonic-gate 			if (!(ipp->ipp_fields & IPPF_DSTOPTS))
28747c478bd9Sstevel@tonic-gate 				return (0);
28757c478bd9Sstevel@tonic-gate 			bcopy(ipp->ipp_dstopts, ptr, ipp->ipp_dstoptslen);
28767c478bd9Sstevel@tonic-gate 			return (ipp->ipp_dstoptslen);
28777c478bd9Sstevel@tonic-gate 		case IPV6_PATHMTU:
28787c478bd9Sstevel@tonic-gate 			return (ip_fill_mtuinfo(&udp->udp_v6dst,
2879437220cdSdanmcd 			    udp->udp_dstport, (struct ip6_mtuinfo *)ptr,
2880437220cdSdanmcd 			    us->us_netstack));
28817c478bd9Sstevel@tonic-gate 		default:
28827c478bd9Sstevel@tonic-gate 			return (-1);
28837c478bd9Sstevel@tonic-gate 		}
28847c478bd9Sstevel@tonic-gate 		break;
28857c478bd9Sstevel@tonic-gate 	case IPPROTO_UDP:
28867c478bd9Sstevel@tonic-gate 		switch (name) {
28877c478bd9Sstevel@tonic-gate 		case UDP_ANONPRIVBIND:
28887c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_anon_priv_bind;
28897c478bd9Sstevel@tonic-gate 			break;
28907c478bd9Sstevel@tonic-gate 		case UDP_EXCLBIND:
28917c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_exclbind ? UDP_EXCLBIND : 0;
28927c478bd9Sstevel@tonic-gate 			break;
28937c478bd9Sstevel@tonic-gate 		case UDP_RCVHDR:
28947c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_rcvhdr ? 1 : 0;
28957c478bd9Sstevel@tonic-gate 			break;
2896437220cdSdanmcd 		case UDP_NAT_T_ENDPOINT:
2897437220cdSdanmcd 			*i1 = udp->udp_nat_t_endpoint;
2898437220cdSdanmcd 			break;
28997c478bd9Sstevel@tonic-gate 		default:
29007c478bd9Sstevel@tonic-gate 			return (-1);
29017c478bd9Sstevel@tonic-gate 		}
29027c478bd9Sstevel@tonic-gate 		break;
29037c478bd9Sstevel@tonic-gate 	default:
29047c478bd9Sstevel@tonic-gate 		return (-1);
29057c478bd9Sstevel@tonic-gate 	}
29067c478bd9Sstevel@tonic-gate 	return (sizeof (int));
29077c478bd9Sstevel@tonic-gate }
29087c478bd9Sstevel@tonic-gate 
2909fc80c0dfSnordmark int
2910fc80c0dfSnordmark udp_opt_get(queue_t *q, t_scalar_t level, t_scalar_t name, uchar_t *ptr)
2911fc80c0dfSnordmark {
2912fc80c0dfSnordmark 	udp_t	*udp;
2913fc80c0dfSnordmark 	int	err;
2914fc80c0dfSnordmark 
2915fc80c0dfSnordmark 	udp = Q_TO_UDP(q);
2916fc80c0dfSnordmark 
2917fc80c0dfSnordmark 	rw_enter(&udp->udp_rwlock, RW_READER);
2918fc80c0dfSnordmark 	err = udp_opt_get_locked(q, level, name, ptr);
2919fc80c0dfSnordmark 	rw_exit(&udp->udp_rwlock);
2920fc80c0dfSnordmark 	return (err);
2921fc80c0dfSnordmark }
2922fc80c0dfSnordmark 
2923ff550d0eSmasputra /*
2924fc80c0dfSnordmark  * This routine sets socket options.
2925ff550d0eSmasputra  */
29267c478bd9Sstevel@tonic-gate /* ARGSUSED */
29277c478bd9Sstevel@tonic-gate int
2928fc80c0dfSnordmark udp_opt_set_locked(queue_t *q, uint_t optset_context, int level,
29297c478bd9Sstevel@tonic-gate     int name, uint_t inlen, uchar_t *invalp, uint_t *outlenp,
29307c478bd9Sstevel@tonic-gate     uchar_t *outvalp, void *thisdg_attrs, cred_t *cr, mblk_t *mblk)
29317c478bd9Sstevel@tonic-gate {
293245916cd2Sjpk 	udpattrs_t *attrs = thisdg_attrs;
29337c478bd9Sstevel@tonic-gate 	int	*i1 = (int *)invalp;
29347c478bd9Sstevel@tonic-gate 	boolean_t onoff = (*i1 == 0) ? 0 : 1;
29357c478bd9Sstevel@tonic-gate 	boolean_t checkonly;
29367c478bd9Sstevel@tonic-gate 	int	error;
2937ff550d0eSmasputra 	conn_t	*connp;
2938ff550d0eSmasputra 	udp_t	*udp;
293945916cd2Sjpk 	uint_t	newlen;
2940f4b3ec61Sdh 	udp_stack_t *us;
2941fc80c0dfSnordmark 	size_t	sth_wroff;
2942ff550d0eSmasputra 
2943ff550d0eSmasputra 	connp = Q_TO_CONN(q);
2944ff550d0eSmasputra 	udp = connp->conn_udp;
2945f4b3ec61Sdh 	us = udp->udp_us;
29467c478bd9Sstevel@tonic-gate 
29477c478bd9Sstevel@tonic-gate 	switch (optset_context) {
29487c478bd9Sstevel@tonic-gate 	case SETFN_OPTCOM_CHECKONLY:
29497c478bd9Sstevel@tonic-gate 		checkonly = B_TRUE;
29507c478bd9Sstevel@tonic-gate 		/*
29517c478bd9Sstevel@tonic-gate 		 * Note: Implies T_CHECK semantics for T_OPTCOM_REQ
29527c478bd9Sstevel@tonic-gate 		 * inlen != 0 implies value supplied and
29537c478bd9Sstevel@tonic-gate 		 * 	we have to "pretend" to set it.
29547c478bd9Sstevel@tonic-gate 		 * inlen == 0 implies that there is no
29557c478bd9Sstevel@tonic-gate 		 * 	value part in T_CHECK request and just validation
29567c478bd9Sstevel@tonic-gate 		 * done elsewhere should be enough, we just return here.
29577c478bd9Sstevel@tonic-gate 		 */
29587c478bd9Sstevel@tonic-gate 		if (inlen == 0) {
29597c478bd9Sstevel@tonic-gate 			*outlenp = 0;
29607c478bd9Sstevel@tonic-gate 			return (0);
29617c478bd9Sstevel@tonic-gate 		}
29627c478bd9Sstevel@tonic-gate 		break;
29637c478bd9Sstevel@tonic-gate 	case SETFN_OPTCOM_NEGOTIATE:
29647c478bd9Sstevel@tonic-gate 		checkonly = B_FALSE;
29657c478bd9Sstevel@tonic-gate 		break;
29667c478bd9Sstevel@tonic-gate 	case SETFN_UD_NEGOTIATE:
29677c478bd9Sstevel@tonic-gate 	case SETFN_CONN_NEGOTIATE:
29687c478bd9Sstevel@tonic-gate 		checkonly = B_FALSE;
29697c478bd9Sstevel@tonic-gate 		/*
29707c478bd9Sstevel@tonic-gate 		 * Negotiating local and "association-related" options
29717c478bd9Sstevel@tonic-gate 		 * through T_UNITDATA_REQ.
29727c478bd9Sstevel@tonic-gate 		 *
29737c478bd9Sstevel@tonic-gate 		 * Following routine can filter out ones we do not
29747c478bd9Sstevel@tonic-gate 		 * want to be "set" this way.
29757c478bd9Sstevel@tonic-gate 		 */
29767c478bd9Sstevel@tonic-gate 		if (!udp_opt_allow_udr_set(level, name)) {
29777c478bd9Sstevel@tonic-gate 			*outlenp = 0;
29787c478bd9Sstevel@tonic-gate 			return (EINVAL);
29797c478bd9Sstevel@tonic-gate 		}
29807c478bd9Sstevel@tonic-gate 		break;
29817c478bd9Sstevel@tonic-gate 	default:
29827c478bd9Sstevel@tonic-gate 		/*
29837c478bd9Sstevel@tonic-gate 		 * We should never get here
29847c478bd9Sstevel@tonic-gate 		 */
29857c478bd9Sstevel@tonic-gate 		*outlenp = 0;
29867c478bd9Sstevel@tonic-gate 		return (EINVAL);
29877c478bd9Sstevel@tonic-gate 	}
29887c478bd9Sstevel@tonic-gate 
29897c478bd9Sstevel@tonic-gate 	ASSERT((optset_context != SETFN_OPTCOM_CHECKONLY) ||
29907c478bd9Sstevel@tonic-gate 	    (optset_context == SETFN_OPTCOM_CHECKONLY && inlen != 0));
29917c478bd9Sstevel@tonic-gate 
29927c478bd9Sstevel@tonic-gate 	/*
29937c478bd9Sstevel@tonic-gate 	 * For fixed length options, no sanity check
29947c478bd9Sstevel@tonic-gate 	 * of passed in length is done. It is assumed *_optcom_req()
29957c478bd9Sstevel@tonic-gate 	 * routines do the right thing.
29967c478bd9Sstevel@tonic-gate 	 */
29977c478bd9Sstevel@tonic-gate 
29987c478bd9Sstevel@tonic-gate 	switch (level) {
29997c478bd9Sstevel@tonic-gate 	case SOL_SOCKET:
30007c478bd9Sstevel@tonic-gate 		switch (name) {
30017c478bd9Sstevel@tonic-gate 		case SO_REUSEADDR:
30027c478bd9Sstevel@tonic-gate 			if (!checkonly)
30037c478bd9Sstevel@tonic-gate 				udp->udp_reuseaddr = onoff;
30047c478bd9Sstevel@tonic-gate 			break;
30057c478bd9Sstevel@tonic-gate 		case SO_DEBUG:
30067c478bd9Sstevel@tonic-gate 			if (!checkonly)
30077c478bd9Sstevel@tonic-gate 				udp->udp_debug = onoff;
30087c478bd9Sstevel@tonic-gate 			break;
30097c478bd9Sstevel@tonic-gate 		/*
30107c478bd9Sstevel@tonic-gate 		 * The following three items are available here,
30117c478bd9Sstevel@tonic-gate 		 * but are only meaningful to IP.
30127c478bd9Sstevel@tonic-gate 		 */
30137c478bd9Sstevel@tonic-gate 		case SO_DONTROUTE:
30147c478bd9Sstevel@tonic-gate 			if (!checkonly)
30157c478bd9Sstevel@tonic-gate 				udp->udp_dontroute = onoff;
30167c478bd9Sstevel@tonic-gate 			break;
30177c478bd9Sstevel@tonic-gate 		case SO_USELOOPBACK:
30187c478bd9Sstevel@tonic-gate 			if (!checkonly)
30197c478bd9Sstevel@tonic-gate 				udp->udp_useloopback = onoff;
30207c478bd9Sstevel@tonic-gate 			break;
30217c478bd9Sstevel@tonic-gate 		case SO_BROADCAST:
30227c478bd9Sstevel@tonic-gate 			if (!checkonly)
30237c478bd9Sstevel@tonic-gate 				udp->udp_broadcast = onoff;
30247c478bd9Sstevel@tonic-gate 			break;
30257c478bd9Sstevel@tonic-gate 
30267c478bd9Sstevel@tonic-gate 		case SO_SNDBUF:
3027f4b3ec61Sdh 			if (*i1 > us->us_max_buf) {
30287c478bd9Sstevel@tonic-gate 				*outlenp = 0;
30297c478bd9Sstevel@tonic-gate 				return (ENOBUFS);
30307c478bd9Sstevel@tonic-gate 			}
30317c478bd9Sstevel@tonic-gate 			if (!checkonly) {
30327c478bd9Sstevel@tonic-gate 				q->q_hiwat = *i1;
30337c478bd9Sstevel@tonic-gate 			}
30347c478bd9Sstevel@tonic-gate 			break;
30357c478bd9Sstevel@tonic-gate 		case SO_RCVBUF:
3036f4b3ec61Sdh 			if (*i1 > us->us_max_buf) {
30377c478bd9Sstevel@tonic-gate 				*outlenp = 0;
30387c478bd9Sstevel@tonic-gate 				return (ENOBUFS);
30397c478bd9Sstevel@tonic-gate 			}
30407c478bd9Sstevel@tonic-gate 			if (!checkonly) {
30417c478bd9Sstevel@tonic-gate 				RD(q)->q_hiwat = *i1;
3042fc80c0dfSnordmark 				rw_exit(&udp->udp_rwlock);
3043fc80c0dfSnordmark 				(void) mi_set_sth_hiwat(RD(q),
3044ff550d0eSmasputra 				    udp_set_rcv_hiwat(udp, *i1));
3045fc80c0dfSnordmark 				rw_enter(&udp->udp_rwlock, RW_WRITER);
30467c478bd9Sstevel@tonic-gate 			}
30477c478bd9Sstevel@tonic-gate 			break;
30487c478bd9Sstevel@tonic-gate 		case SO_DGRAM_ERRIND:
30497c478bd9Sstevel@tonic-gate 			if (!checkonly)
30507c478bd9Sstevel@tonic-gate 				udp->udp_dgram_errind = onoff;
30517c478bd9Sstevel@tonic-gate 			break;
30527c478bd9Sstevel@tonic-gate 		case SO_RECVUCRED:
30537c478bd9Sstevel@tonic-gate 			if (!checkonly)
30547c478bd9Sstevel@tonic-gate 				udp->udp_recvucred = onoff;
30557c478bd9Sstevel@tonic-gate 			break;
30565d0bc3edSsommerfe 		case SO_ALLZONES:
30575d0bc3edSsommerfe 			/*
30585d0bc3edSsommerfe 			 * "soft" error (negative)
30595d0bc3edSsommerfe 			 * option not handled at this level
30605d0bc3edSsommerfe 			 * Do not modify *outlenp.
30615d0bc3edSsommerfe 			 */
30625d0bc3edSsommerfe 			return (-EINVAL);
3063e4f35dbaSgt 		case SO_TIMESTAMP:
3064e4f35dbaSgt 			if (!checkonly)
3065e4f35dbaSgt 				udp->udp_timestamp = onoff;
3066e4f35dbaSgt 			break;
306745916cd2Sjpk 		case SO_ANON_MLP:
3068222c5bceSkp 			/* Pass option along to IP level for handling */
3069222c5bceSkp 			return (-EINVAL);
307045916cd2Sjpk 		case SO_MAC_EXEMPT:
3071222c5bceSkp 			/* Pass option along to IP level for handling */
3072222c5bceSkp 			return (-EINVAL);
307345916cd2Sjpk 		case SCM_UCRED: {
307445916cd2Sjpk 			struct ucred_s *ucr;
307545916cd2Sjpk 			cred_t *cr, *newcr;
307645916cd2Sjpk 			ts_label_t *tsl;
307745916cd2Sjpk 
307845916cd2Sjpk 			/*
307945916cd2Sjpk 			 * Only sockets that have proper privileges and are
308045916cd2Sjpk 			 * bound to MLPs will have any other value here, so
308145916cd2Sjpk 			 * this implicitly tests for privilege to set label.
308245916cd2Sjpk 			 */
308345916cd2Sjpk 			if (connp->conn_mlp_type == mlptSingle)
308445916cd2Sjpk 				break;
308545916cd2Sjpk 			ucr = (struct ucred_s *)invalp;
308645916cd2Sjpk 			if (inlen != ucredsize ||
308745916cd2Sjpk 			    ucr->uc_labeloff < sizeof (*ucr) ||
308845916cd2Sjpk 			    ucr->uc_labeloff + sizeof (bslabel_t) > inlen)
308945916cd2Sjpk 				return (EINVAL);
309045916cd2Sjpk 			if (!checkonly) {
309145916cd2Sjpk 				mblk_t *mb;
309245916cd2Sjpk 
309345916cd2Sjpk 				if (attrs == NULL ||
309445916cd2Sjpk 				    (mb = attrs->udpattr_mb) == NULL)
309545916cd2Sjpk 					return (EINVAL);
309645916cd2Sjpk 				if ((cr = DB_CRED(mb)) == NULL)
309745916cd2Sjpk 					cr = udp->udp_connp->conn_cred;
309845916cd2Sjpk 				ASSERT(cr != NULL);
309945916cd2Sjpk 				if ((tsl = crgetlabel(cr)) == NULL)
310045916cd2Sjpk 					return (EINVAL);
310145916cd2Sjpk 				newcr = copycred_from_bslabel(cr, UCLABEL(ucr),
310245916cd2Sjpk 				    tsl->tsl_doi, KM_NOSLEEP);
310345916cd2Sjpk 				if (newcr == NULL)
310445916cd2Sjpk 					return (ENOSR);
310545916cd2Sjpk 				mblk_setcred(mb, newcr);
310645916cd2Sjpk 				attrs->udpattr_credset = B_TRUE;
310745916cd2Sjpk 				crfree(newcr);
310845916cd2Sjpk 			}
310945916cd2Sjpk 			break;
311045916cd2Sjpk 		}
3111ae347574Skcpoon 		case SO_EXCLBIND:
3112ae347574Skcpoon 			if (!checkonly)
3113ae347574Skcpoon 				udp->udp_exclbind = onoff;
3114ae347574Skcpoon 			break;
31157c478bd9Sstevel@tonic-gate 		default:
31167c478bd9Sstevel@tonic-gate 			*outlenp = 0;
31177c478bd9Sstevel@tonic-gate 			return (EINVAL);
31187c478bd9Sstevel@tonic-gate 		}
31197c478bd9Sstevel@tonic-gate 		break;
31207c478bd9Sstevel@tonic-gate 	case IPPROTO_IP:
31217c478bd9Sstevel@tonic-gate 		if (udp->udp_family != AF_INET) {
31227c478bd9Sstevel@tonic-gate 			*outlenp = 0;
31237c478bd9Sstevel@tonic-gate 			return (ENOPROTOOPT);
31247c478bd9Sstevel@tonic-gate 		}
31257c478bd9Sstevel@tonic-gate 		switch (name) {
31267c478bd9Sstevel@tonic-gate 		case IP_OPTIONS:
31277c478bd9Sstevel@tonic-gate 		case T_IP_OPTIONS:
31287c478bd9Sstevel@tonic-gate 			/* Save options for use by IP. */
312945916cd2Sjpk 			newlen = inlen + udp->udp_label_len;
313045916cd2Sjpk 			if ((inlen & 0x3) || newlen > IP_MAX_OPT_LENGTH) {
31317c478bd9Sstevel@tonic-gate 				*outlenp = 0;
31327c478bd9Sstevel@tonic-gate 				return (EINVAL);
31337c478bd9Sstevel@tonic-gate 			}
31347c478bd9Sstevel@tonic-gate 			if (checkonly)
31357c478bd9Sstevel@tonic-gate 				break;
31367c478bd9Sstevel@tonic-gate 
3137fc80c0dfSnordmark 			/*
3138fc80c0dfSnordmark 			 * Update the stored options taking into account
3139fc80c0dfSnordmark 			 * any CIPSO option which we should not overwrite.
3140fc80c0dfSnordmark 			 */
314145916cd2Sjpk 			if (!tsol_option_set(&udp->udp_ip_snd_options,
314245916cd2Sjpk 			    &udp->udp_ip_snd_options_len,
314345916cd2Sjpk 			    udp->udp_label_len, invalp, inlen)) {
314445916cd2Sjpk 				*outlenp = 0;
314545916cd2Sjpk 				return (ENOMEM);
31467c478bd9Sstevel@tonic-gate 			}
314745916cd2Sjpk 
31487c478bd9Sstevel@tonic-gate 			udp->udp_max_hdr_len = IP_SIMPLE_HDR_LENGTH +
31497c478bd9Sstevel@tonic-gate 			    UDPH_SIZE + udp->udp_ip_snd_options_len;
3150fc80c0dfSnordmark 			sth_wroff = udp->udp_max_hdr_len + us->us_wroff_extra;
3151fc80c0dfSnordmark 			rw_exit(&udp->udp_rwlock);
3152fc80c0dfSnordmark 			(void) mi_set_sth_wroff(RD(q), sth_wroff);
3153fc80c0dfSnordmark 			rw_enter(&udp->udp_rwlock, RW_WRITER);
31547c478bd9Sstevel@tonic-gate 			break;
315545916cd2Sjpk 
31567c478bd9Sstevel@tonic-gate 		case IP_TTL:
31577c478bd9Sstevel@tonic-gate 			if (!checkonly) {
31587c478bd9Sstevel@tonic-gate 				udp->udp_ttl = (uchar_t)*i1;
31597c478bd9Sstevel@tonic-gate 			}
31607c478bd9Sstevel@tonic-gate 			break;
31617c478bd9Sstevel@tonic-gate 		case IP_TOS:
31627c478bd9Sstevel@tonic-gate 		case T_IP_TOS:
31637c478bd9Sstevel@tonic-gate 			if (!checkonly) {
31647c478bd9Sstevel@tonic-gate 				udp->udp_type_of_service = (uchar_t)*i1;
31657c478bd9Sstevel@tonic-gate 			}
31667c478bd9Sstevel@tonic-gate 			break;
31677c478bd9Sstevel@tonic-gate 		case IP_MULTICAST_IF: {
31687c478bd9Sstevel@tonic-gate 			/*
31697c478bd9Sstevel@tonic-gate 			 * TODO should check OPTMGMT reply and undo this if
31707c478bd9Sstevel@tonic-gate 			 * there is an error.
31717c478bd9Sstevel@tonic-gate 			 */
31727c478bd9Sstevel@tonic-gate 			struct in_addr *inap = (struct in_addr *)invalp;
31737c478bd9Sstevel@tonic-gate 			if (!checkonly) {
31747c478bd9Sstevel@tonic-gate 				udp->udp_multicast_if_addr =
31757c478bd9Sstevel@tonic-gate 				    inap->s_addr;
31767c478bd9Sstevel@tonic-gate 			}
31777c478bd9Sstevel@tonic-gate 			break;
31787c478bd9Sstevel@tonic-gate 		}
31797c478bd9Sstevel@tonic-gate 		case IP_MULTICAST_TTL:
31807c478bd9Sstevel@tonic-gate 			if (!checkonly)
31817c478bd9Sstevel@tonic-gate 				udp->udp_multicast_ttl = *invalp;
31827c478bd9Sstevel@tonic-gate 			break;
31837c478bd9Sstevel@tonic-gate 		case IP_MULTICAST_LOOP:
31847c478bd9Sstevel@tonic-gate 			if (!checkonly)
3185ff550d0eSmasputra 				connp->conn_multicast_loop = *invalp;
31867c478bd9Sstevel@tonic-gate 			break;
31877c478bd9Sstevel@tonic-gate 		case IP_RECVOPTS:
31887c478bd9Sstevel@tonic-gate 			if (!checkonly)
31897c478bd9Sstevel@tonic-gate 				udp->udp_recvopts = onoff;
31907c478bd9Sstevel@tonic-gate 			break;
31917c478bd9Sstevel@tonic-gate 		case IP_RECVDSTADDR:
31927c478bd9Sstevel@tonic-gate 			if (!checkonly)
31937c478bd9Sstevel@tonic-gate 				udp->udp_recvdstaddr = onoff;
31947c478bd9Sstevel@tonic-gate 			break;
31957c478bd9Sstevel@tonic-gate 		case IP_RECVIF:
31967c478bd9Sstevel@tonic-gate 			if (!checkonly)
31977c478bd9Sstevel@tonic-gate 				udp->udp_recvif = onoff;
31987c478bd9Sstevel@tonic-gate 			break;
31997c478bd9Sstevel@tonic-gate 		case IP_RECVSLLA:
32007c478bd9Sstevel@tonic-gate 			if (!checkonly)
32017c478bd9Sstevel@tonic-gate 				udp->udp_recvslla = onoff;
32027c478bd9Sstevel@tonic-gate 			break;
32037c478bd9Sstevel@tonic-gate 		case IP_RECVTTL:
32047c478bd9Sstevel@tonic-gate 			if (!checkonly)
32057c478bd9Sstevel@tonic-gate 				udp->udp_recvttl = onoff;
32067c478bd9Sstevel@tonic-gate 			break;
320719a30e1aSrshoaib 		case IP_PKTINFO: {
320819a30e1aSrshoaib 			/*
320919a30e1aSrshoaib 			 * This also handles IP_RECVPKTINFO.
321019a30e1aSrshoaib 			 * IP_PKTINFO and IP_RECVPKTINFO have same value.
321119a30e1aSrshoaib 			 * Differentiation is based on the size of the
321219a30e1aSrshoaib 			 * argument passed in.
321319a30e1aSrshoaib 			 */
321419a30e1aSrshoaib 			struct in_pktinfo *pktinfop;
321519a30e1aSrshoaib 			ip4_pkt_t *attr_pktinfop;
321619a30e1aSrshoaib 
321719a30e1aSrshoaib 			if (checkonly)
321819a30e1aSrshoaib 				break;
321919a30e1aSrshoaib 
322019a30e1aSrshoaib 			if (inlen == sizeof (int)) {
322119a30e1aSrshoaib 				/*
322219a30e1aSrshoaib 				 * This is IP_RECVPKTINFO option.
322319a30e1aSrshoaib 				 * Keep a local copy of whether this option is
322419a30e1aSrshoaib 				 * set or not and pass it down to IP for
322519a30e1aSrshoaib 				 * processing.
322619a30e1aSrshoaib 				 */
322719a30e1aSrshoaib 
322819a30e1aSrshoaib 				udp->udp_ip_recvpktinfo = onoff;
322919a30e1aSrshoaib 				return (-EINVAL);
323019a30e1aSrshoaib 			}
323119a30e1aSrshoaib 
323219a30e1aSrshoaib 			if (attrs == NULL ||
323319a30e1aSrshoaib 			    (attr_pktinfop = attrs->udpattr_ipp4) == NULL) {
323419a30e1aSrshoaib 				/*
323519a30e1aSrshoaib 				 * sticky option or no buffer to return
323619a30e1aSrshoaib 				 * the results.
323719a30e1aSrshoaib 				 */
323819a30e1aSrshoaib 				return (EINVAL);
323919a30e1aSrshoaib 			}
324019a30e1aSrshoaib 
324119a30e1aSrshoaib 			if (inlen != sizeof (struct in_pktinfo))
324219a30e1aSrshoaib 				return (EINVAL);
324319a30e1aSrshoaib 
324419a30e1aSrshoaib 			pktinfop = (struct in_pktinfo *)invalp;
324519a30e1aSrshoaib 
324619a30e1aSrshoaib 			/*
324719a30e1aSrshoaib 			 * At least one of the values should be specified
324819a30e1aSrshoaib 			 */
324919a30e1aSrshoaib 			if (pktinfop->ipi_ifindex == 0 &&
325019a30e1aSrshoaib 			    pktinfop->ipi_spec_dst.s_addr == INADDR_ANY) {
325119a30e1aSrshoaib 				return (EINVAL);
325219a30e1aSrshoaib 			}
325319a30e1aSrshoaib 
325419a30e1aSrshoaib 			attr_pktinfop->ip4_addr = pktinfop->ipi_spec_dst.s_addr;
325519a30e1aSrshoaib 			attr_pktinfop->ip4_ill_index = pktinfop->ipi_ifindex;
325619a30e1aSrshoaib 
325719a30e1aSrshoaib 			break;
325819a30e1aSrshoaib 		}
32597c478bd9Sstevel@tonic-gate 		case IP_ADD_MEMBERSHIP:
32607c478bd9Sstevel@tonic-gate 		case IP_DROP_MEMBERSHIP:
32617c478bd9Sstevel@tonic-gate 		case IP_BLOCK_SOURCE:
32627c478bd9Sstevel@tonic-gate 		case IP_UNBLOCK_SOURCE:
32637c478bd9Sstevel@tonic-gate 		case IP_ADD_SOURCE_MEMBERSHIP:
32647c478bd9Sstevel@tonic-gate 		case IP_DROP_SOURCE_MEMBERSHIP:
32657c478bd9Sstevel@tonic-gate 		case MCAST_JOIN_GROUP:
32667c478bd9Sstevel@tonic-gate 		case MCAST_LEAVE_GROUP:
32677c478bd9Sstevel@tonic-gate 		case MCAST_BLOCK_SOURCE:
32687c478bd9Sstevel@tonic-gate 		case MCAST_UNBLOCK_SOURCE:
32697c478bd9Sstevel@tonic-gate 		case MCAST_JOIN_SOURCE_GROUP:
32707c478bd9Sstevel@tonic-gate 		case MCAST_LEAVE_SOURCE_GROUP:
32717c478bd9Sstevel@tonic-gate 		case IP_SEC_OPT:
327243d18f1cSpriyanka 		case IP_NEXTHOP:
3273e704a8f2Smeem 		case IP_DHCPINIT_IF:
32747c478bd9Sstevel@tonic-gate 			/*
32757c478bd9Sstevel@tonic-gate 			 * "soft" error (negative)
32767c478bd9Sstevel@tonic-gate 			 * option not handled at this level
32777c478bd9Sstevel@tonic-gate 			 * Do not modify *outlenp.
32787c478bd9Sstevel@tonic-gate 			 */
32797c478bd9Sstevel@tonic-gate 			return (-EINVAL);
32807c478bd9Sstevel@tonic-gate 		case IP_BOUND_IF:
32817c478bd9Sstevel@tonic-gate 			if (!checkonly)
32827c478bd9Sstevel@tonic-gate 				udp->udp_bound_if = *i1;
32837c478bd9Sstevel@tonic-gate 			break;
32847c478bd9Sstevel@tonic-gate 		case IP_UNSPEC_SRC:
32857c478bd9Sstevel@tonic-gate 			if (!checkonly)
32867c478bd9Sstevel@tonic-gate 				udp->udp_unspec_source = onoff;
32877c478bd9Sstevel@tonic-gate 			break;
3288a7107231Smeem 		case IP_BROADCAST_TTL:
3289a7107231Smeem 			if (!checkonly)
3290a7107231Smeem 				connp->conn_broadcast_ttl = *invalp;
3291a7107231Smeem 			break;
32927c478bd9Sstevel@tonic-gate 		default:
32937c478bd9Sstevel@tonic-gate 			*outlenp = 0;
32947c478bd9Sstevel@tonic-gate 			return (EINVAL);
32957c478bd9Sstevel@tonic-gate 		}
32967c478bd9Sstevel@tonic-gate 		break;
32977c478bd9Sstevel@tonic-gate 	case IPPROTO_IPV6: {
32987c478bd9Sstevel@tonic-gate 		ip6_pkt_t		*ipp;
32997c478bd9Sstevel@tonic-gate 		boolean_t		sticky;
33007c478bd9Sstevel@tonic-gate 
33017c478bd9Sstevel@tonic-gate 		if (udp->udp_family != AF_INET6) {
33027c478bd9Sstevel@tonic-gate 			*outlenp = 0;
33037c478bd9Sstevel@tonic-gate 			return (ENOPROTOOPT);
33047c478bd9Sstevel@tonic-gate 		}
33057c478bd9Sstevel@tonic-gate 		/*
33067c478bd9Sstevel@tonic-gate 		 * Deal with both sticky options and ancillary data
33077c478bd9Sstevel@tonic-gate 		 */
330845916cd2Sjpk 		sticky = B_FALSE;
330919a30e1aSrshoaib 		if (attrs == NULL || (ipp = attrs->udpattr_ipp6) ==
331019a30e1aSrshoaib 		    NULL) {
33117c478bd9Sstevel@tonic-gate 			/* sticky options, or none */
33127c478bd9Sstevel@tonic-gate 			ipp = &udp->udp_sticky_ipp;
33137c478bd9Sstevel@tonic-gate 			sticky = B_TRUE;
33147c478bd9Sstevel@tonic-gate 		}
33157c478bd9Sstevel@tonic-gate 
33167c478bd9Sstevel@tonic-gate 		switch (name) {
33177c478bd9Sstevel@tonic-gate 		case IPV6_MULTICAST_IF:
33187c478bd9Sstevel@tonic-gate 			if (!checkonly)
33197c478bd9Sstevel@tonic-gate 				udp->udp_multicast_if_index = *i1;
33207c478bd9Sstevel@tonic-gate 			break;
33217c478bd9Sstevel@tonic-gate 		case IPV6_UNICAST_HOPS:
33227c478bd9Sstevel@tonic-gate 			/* -1 means use default */
33237c478bd9Sstevel@tonic-gate 			if (*i1 < -1 || *i1 > IPV6_MAX_HOPS) {
33247c478bd9Sstevel@tonic-gate 				*outlenp = 0;
33257c478bd9Sstevel@tonic-gate 				return (EINVAL);
33267c478bd9Sstevel@tonic-gate 			}
33277c478bd9Sstevel@tonic-gate 			if (!checkonly) {
33287c478bd9Sstevel@tonic-gate 				if (*i1 == -1) {
3329b3d0fa4fSseb 					udp->udp_ttl = ipp->ipp_unicast_hops =
3330f4b3ec61Sdh 					    us->us_ipv6_hoplimit;
3331b3d0fa4fSseb 					ipp->ipp_fields &= ~IPPF_UNICAST_HOPS;
33327c478bd9Sstevel@tonic-gate 					/* Pass modified value to IP. */
33337c478bd9Sstevel@tonic-gate 					*i1 = udp->udp_ttl;
33347c478bd9Sstevel@tonic-gate 				} else {
3335b3d0fa4fSseb 					udp->udp_ttl = ipp->ipp_unicast_hops =
33367c478bd9Sstevel@tonic-gate 					    (uint8_t)*i1;
3337b3d0fa4fSseb 					ipp->ipp_fields |= IPPF_UNICAST_HOPS;
33387c478bd9Sstevel@tonic-gate 				}
33397c478bd9Sstevel@tonic-gate 				/* Rebuild the header template */
3340fc80c0dfSnordmark 				error = udp_build_hdrs(udp);
33417c478bd9Sstevel@tonic-gate 				if (error != 0) {
33427c478bd9Sstevel@tonic-gate 					*outlenp = 0;
33437c478bd9Sstevel@tonic-gate 					return (error);
33447c478bd9Sstevel@tonic-gate 				}
33457c478bd9Sstevel@tonic-gate 			}
33467c478bd9Sstevel@tonic-gate 			break;
33477c478bd9Sstevel@tonic-gate 		case IPV6_MULTICAST_HOPS:
33487c478bd9Sstevel@tonic-gate 			/* -1 means use default */
33497c478bd9Sstevel@tonic-gate 			if (*i1 < -1 || *i1 > IPV6_MAX_HOPS) {
33507c478bd9Sstevel@tonic-gate 				*outlenp = 0;
33517c478bd9Sstevel@tonic-gate 				return (EINVAL);
33527c478bd9Sstevel@tonic-gate 			}
33537c478bd9Sstevel@tonic-gate 			if (!checkonly) {
33547c478bd9Sstevel@tonic-gate 				if (*i1 == -1) {
33557c478bd9Sstevel@tonic-gate 					udp->udp_multicast_ttl =
3356b3d0fa4fSseb 					    ipp->ipp_multicast_hops =
33577c478bd9Sstevel@tonic-gate 					    IP_DEFAULT_MULTICAST_TTL;
3358b3d0fa4fSseb 					ipp->ipp_fields &= ~IPPF_MULTICAST_HOPS;
33597c478bd9Sstevel@tonic-gate 					/* Pass modified value to IP. */
33607c478bd9Sstevel@tonic-gate 					*i1 = udp->udp_multicast_ttl;
33617c478bd9Sstevel@tonic-gate 				} else {
33627c478bd9Sstevel@tonic-gate 					udp->udp_multicast_ttl =
3363b3d0fa4fSseb 					    ipp->ipp_multicast_hops =
33647c478bd9Sstevel@tonic-gate 					    (uint8_t)*i1;
3365b3d0fa4fSseb 					ipp->ipp_fields |= IPPF_MULTICAST_HOPS;
33667c478bd9Sstevel@tonic-gate 				}
33677c478bd9Sstevel@tonic-gate 			}
33687c478bd9Sstevel@tonic-gate 			break;
33697c478bd9Sstevel@tonic-gate 		case IPV6_MULTICAST_LOOP:
33707c478bd9Sstevel@tonic-gate 			if (*i1 != 0 && *i1 != 1) {
33717c478bd9Sstevel@tonic-gate 				*outlenp = 0;
33727c478bd9Sstevel@tonic-gate 				return (EINVAL);
33737c478bd9Sstevel@tonic-gate 			}
33747c478bd9Sstevel@tonic-gate 			if (!checkonly)
3375ff550d0eSmasputra 				connp->conn_multicast_loop = *i1;
33767c478bd9Sstevel@tonic-gate 			break;
33777c478bd9Sstevel@tonic-gate 		case IPV6_JOIN_GROUP:
33787c478bd9Sstevel@tonic-gate 		case IPV6_LEAVE_GROUP:
33797c478bd9Sstevel@tonic-gate 		case MCAST_JOIN_GROUP:
33807c478bd9Sstevel@tonic-gate 		case MCAST_LEAVE_GROUP:
33817c478bd9Sstevel@tonic-gate 		case MCAST_BLOCK_SOURCE:
33827c478bd9Sstevel@tonic-gate 		case MCAST_UNBLOCK_SOURCE:
33837c478bd9Sstevel@tonic-gate 		case MCAST_JOIN_SOURCE_GROUP:
33847c478bd9Sstevel@tonic-gate 		case MCAST_LEAVE_SOURCE_GROUP:
33857c478bd9Sstevel@tonic-gate 			/*
33867c478bd9Sstevel@tonic-gate 			 * "soft" error (negative)
33877c478bd9Sstevel@tonic-gate 			 * option not handled at this level
33887c478bd9Sstevel@tonic-gate 			 * Note: Do not modify *outlenp
33897c478bd9Sstevel@tonic-gate 			 */
33907c478bd9Sstevel@tonic-gate 			return (-EINVAL);
33917c478bd9Sstevel@tonic-gate 		case IPV6_BOUND_IF:
33927c478bd9Sstevel@tonic-gate 			if (!checkonly)
33937c478bd9Sstevel@tonic-gate 				udp->udp_bound_if = *i1;
33947c478bd9Sstevel@tonic-gate 			break;
33957c478bd9Sstevel@tonic-gate 		case IPV6_UNSPEC_SRC:
33967c478bd9Sstevel@tonic-gate 			if (!checkonly)
33977c478bd9Sstevel@tonic-gate 				udp->udp_unspec_source = onoff;
33987c478bd9Sstevel@tonic-gate 			break;
33997c478bd9Sstevel@tonic-gate 		/*
34007c478bd9Sstevel@tonic-gate 		 * Set boolean switches for ancillary data delivery
34017c478bd9Sstevel@tonic-gate 		 */
34027c478bd9Sstevel@tonic-gate 		case IPV6_RECVPKTINFO:
34037c478bd9Sstevel@tonic-gate 			if (!checkonly)
340419a30e1aSrshoaib 				udp->udp_ip_recvpktinfo = onoff;
34057c478bd9Sstevel@tonic-gate 			break;
34067c478bd9Sstevel@tonic-gate 		case IPV6_RECVTCLASS:
34077c478bd9Sstevel@tonic-gate 			if (!checkonly) {
34087c478bd9Sstevel@tonic-gate 				udp->udp_ipv6_recvtclass = onoff;
34097c478bd9Sstevel@tonic-gate 			}
34107c478bd9Sstevel@tonic-gate 			break;
34117c478bd9Sstevel@tonic-gate 		case IPV6_RECVPATHMTU:
34127c478bd9Sstevel@tonic-gate 			if (!checkonly) {
34137c478bd9Sstevel@tonic-gate 				udp->udp_ipv6_recvpathmtu = onoff;
34147c478bd9Sstevel@tonic-gate 			}
34157c478bd9Sstevel@tonic-gate 			break;
34167c478bd9Sstevel@tonic-gate 		case IPV6_RECVHOPLIMIT:
34177c478bd9Sstevel@tonic-gate 			if (!checkonly)
34187c478bd9Sstevel@tonic-gate 				udp->udp_ipv6_recvhoplimit = onoff;
34197c478bd9Sstevel@tonic-gate 			break;
34207c478bd9Sstevel@tonic-gate 		case IPV6_RECVHOPOPTS:
34217c478bd9Sstevel@tonic-gate 			if (!checkonly)
34227c478bd9Sstevel@tonic-gate 				udp->udp_ipv6_recvhopopts = onoff;
34237c478bd9Sstevel@tonic-gate 			break;
34247c478bd9Sstevel@tonic-gate 		case IPV6_RECVDSTOPTS:
34257c478bd9Sstevel@tonic-gate 			if (!checkonly)
34267c478bd9Sstevel@tonic-gate 				udp->udp_ipv6_recvdstopts = onoff;
34277c478bd9Sstevel@tonic-gate 			break;
34287c478bd9Sstevel@tonic-gate 		case _OLD_IPV6_RECVDSTOPTS:
34297c478bd9Sstevel@tonic-gate 			if (!checkonly)
34307c478bd9Sstevel@tonic-gate 				udp->udp_old_ipv6_recvdstopts = onoff;
34317c478bd9Sstevel@tonic-gate 			break;
34327c478bd9Sstevel@tonic-gate 		case IPV6_RECVRTHDRDSTOPTS:
34337c478bd9Sstevel@tonic-gate 			if (!checkonly)
34347c478bd9Sstevel@tonic-gate 				udp->udp_ipv6_recvrthdrdstopts = onoff;
34357c478bd9Sstevel@tonic-gate 			break;
34367c478bd9Sstevel@tonic-gate 		case IPV6_RECVRTHDR:
34377c478bd9Sstevel@tonic-gate 			if (!checkonly)
34387c478bd9Sstevel@tonic-gate 				udp->udp_ipv6_recvrthdr = onoff;
34397c478bd9Sstevel@tonic-gate 			break;
34407c478bd9Sstevel@tonic-gate 		/*
34417c478bd9Sstevel@tonic-gate 		 * Set sticky options or ancillary data.
34427c478bd9Sstevel@tonic-gate 		 * If sticky options, (re)build any extension headers
34437c478bd9Sstevel@tonic-gate 		 * that might be needed as a result.
34447c478bd9Sstevel@tonic-gate 		 */
34457c478bd9Sstevel@tonic-gate 		case IPV6_PKTINFO:
34467c478bd9Sstevel@tonic-gate 			/*
34477c478bd9Sstevel@tonic-gate 			 * The source address and ifindex are verified
34487c478bd9Sstevel@tonic-gate 			 * in ip_opt_set(). For ancillary data the
34497c478bd9Sstevel@tonic-gate 			 * source address is checked in ip_wput_v6.
34507c478bd9Sstevel@tonic-gate 			 */
34517c478bd9Sstevel@tonic-gate 			if (inlen != 0 && inlen != sizeof (struct in6_pktinfo))
34527c478bd9Sstevel@tonic-gate 				return (EINVAL);
34537c478bd9Sstevel@tonic-gate 			if (checkonly)
34547c478bd9Sstevel@tonic-gate 				break;
34557c478bd9Sstevel@tonic-gate 
34567c478bd9Sstevel@tonic-gate 			if (inlen == 0) {
34577c478bd9Sstevel@tonic-gate 				ipp->ipp_fields &= ~(IPPF_IFINDEX|IPPF_ADDR);
34587c478bd9Sstevel@tonic-gate 				ipp->ipp_sticky_ignored |=
34597c478bd9Sstevel@tonic-gate 				    (IPPF_IFINDEX|IPPF_ADDR);
34607c478bd9Sstevel@tonic-gate 			} else {
34617c478bd9Sstevel@tonic-gate 				struct in6_pktinfo *pkti;
34627c478bd9Sstevel@tonic-gate 
34637c478bd9Sstevel@tonic-gate 				pkti = (struct in6_pktinfo *)invalp;
34647c478bd9Sstevel@tonic-gate 				ipp->ipp_ifindex = pkti->ipi6_ifindex;
34657c478bd9Sstevel@tonic-gate 				ipp->ipp_addr = pkti->ipi6_addr;
34667c478bd9Sstevel@tonic-gate 				if (ipp->ipp_ifindex != 0)
34677c478bd9Sstevel@tonic-gate 					ipp->ipp_fields |= IPPF_IFINDEX;
34687c478bd9Sstevel@tonic-gate 				else
34697c478bd9Sstevel@tonic-gate 					ipp->ipp_fields &= ~IPPF_IFINDEX;
34707c478bd9Sstevel@tonic-gate 				if (!IN6_IS_ADDR_UNSPECIFIED(
34717c478bd9Sstevel@tonic-gate 				    &ipp->ipp_addr))
34727c478bd9Sstevel@tonic-gate 					ipp->ipp_fields |= IPPF_ADDR;
34737c478bd9Sstevel@tonic-gate 				else
34747c478bd9Sstevel@tonic-gate 					ipp->ipp_fields &= ~IPPF_ADDR;
34757c478bd9Sstevel@tonic-gate 			}
34767c478bd9Sstevel@tonic-gate 			if (sticky) {
3477fc80c0dfSnordmark 				error = udp_build_hdrs(udp);
34787c478bd9Sstevel@tonic-gate 				if (error != 0)
34797c478bd9Sstevel@tonic-gate 					return (error);
34807c478bd9Sstevel@tonic-gate 			}
34817c478bd9Sstevel@tonic-gate 			break;
34827c478bd9Sstevel@tonic-gate 		case IPV6_HOPLIMIT:
3483b3d0fa4fSseb 			if (sticky)
3484b3d0fa4fSseb 				return (EINVAL);
34857c478bd9Sstevel@tonic-gate 			if (inlen != 0 && inlen != sizeof (int))
34867c478bd9Sstevel@tonic-gate 				return (EINVAL);
34877c478bd9Sstevel@tonic-gate 			if (checkonly)
34887c478bd9Sstevel@tonic-gate 				break;
34897c478bd9Sstevel@tonic-gate 
34907c478bd9Sstevel@tonic-gate 			if (inlen == 0) {
34917c478bd9Sstevel@tonic-gate 				ipp->ipp_fields &= ~IPPF_HOPLIMIT;
34927c478bd9Sstevel@tonic-gate 				ipp->ipp_sticky_ignored |= IPPF_HOPLIMIT;
34937c478bd9Sstevel@tonic-gate 			} else {
34947c478bd9Sstevel@tonic-gate 				if (*i1 > 255 || *i1 < -1)
34957c478bd9Sstevel@tonic-gate 					return (EINVAL);
34967c478bd9Sstevel@tonic-gate 				if (*i1 == -1)
3497f4b3ec61Sdh 					ipp->ipp_hoplimit =
3498f4b3ec61Sdh 					    us->us_ipv6_hoplimit;
34997c478bd9Sstevel@tonic-gate 				else
35007c478bd9Sstevel@tonic-gate 					ipp->ipp_hoplimit = *i1;
35017c478bd9Sstevel@tonic-gate 				ipp->ipp_fields |= IPPF_HOPLIMIT;
35027c478bd9Sstevel@tonic-gate 			}
35037c478bd9Sstevel@tonic-gate 			break;
35047c478bd9Sstevel@tonic-gate 		case IPV6_TCLASS:
35057c478bd9Sstevel@tonic-gate 			if (inlen != 0 && inlen != sizeof (int))
35067c478bd9Sstevel@tonic-gate 				return (EINVAL);
35077c478bd9Sstevel@tonic-gate 			if (checkonly)
35087c478bd9Sstevel@tonic-gate 				break;
35097c478bd9Sstevel@tonic-gate 
35107c478bd9Sstevel@tonic-gate 			if (inlen == 0) {
35117c478bd9Sstevel@tonic-gate 				ipp->ipp_fields &= ~IPPF_TCLASS;
35127c478bd9Sstevel@tonic-gate 				ipp->ipp_sticky_ignored |= IPPF_TCLASS;
35137c478bd9Sstevel@tonic-gate 			} else {
35147c478bd9Sstevel@tonic-gate 				if (*i1 > 255 || *i1 < -1)
35157c478bd9Sstevel@tonic-gate 					return (EINVAL);
35167c478bd9Sstevel@tonic-gate 				if (*i1 == -1)
35177c478bd9Sstevel@tonic-gate 					ipp->ipp_tclass = 0;
35187c478bd9Sstevel@tonic-gate 				else
35197c478bd9Sstevel@tonic-gate 					ipp->ipp_tclass = *i1;
35207c478bd9Sstevel@tonic-gate 				ipp->ipp_fields |= IPPF_TCLASS;
35217c478bd9Sstevel@tonic-gate 			}
35227c478bd9Sstevel@tonic-gate 			if (sticky) {
3523fc80c0dfSnordmark 				error = udp_build_hdrs(udp);
35247c478bd9Sstevel@tonic-gate 				if (error != 0)
35257c478bd9Sstevel@tonic-gate 					return (error);
35267c478bd9Sstevel@tonic-gate 			}
35277c478bd9Sstevel@tonic-gate 			break;
35287c478bd9Sstevel@tonic-gate 		case IPV6_NEXTHOP:
35297c478bd9Sstevel@tonic-gate 			/*
35307c478bd9Sstevel@tonic-gate 			 * IP will verify that the nexthop is reachable
35317c478bd9Sstevel@tonic-gate 			 * and fail for sticky options.
35327c478bd9Sstevel@tonic-gate 			 */
35337c478bd9Sstevel@tonic-gate 			if (inlen != 0 && inlen != sizeof (sin6_t))
35347c478bd9Sstevel@tonic-gate 				return (EINVAL);
35357c478bd9Sstevel@tonic-gate 			if (checkonly)
35367c478bd9Sstevel@tonic-gate 				break;
35377c478bd9Sstevel@tonic-gate 
35387c478bd9Sstevel@tonic-gate 			if (inlen == 0) {
35397c478bd9Sstevel@tonic-gate 				ipp->ipp_fields &= ~IPPF_NEXTHOP;
35407c478bd9Sstevel@tonic-gate 				ipp->ipp_sticky_ignored |= IPPF_NEXTHOP;
35417c478bd9Sstevel@tonic-gate 			} else {
35427c478bd9Sstevel@tonic-gate 				sin6_t *sin6 = (sin6_t *)invalp;
35437c478bd9Sstevel@tonic-gate 
35447c478bd9Sstevel@tonic-gate 				if (sin6->sin6_family != AF_INET6)
35457c478bd9Sstevel@tonic-gate 					return (EAFNOSUPPORT);
35467c478bd9Sstevel@tonic-gate 				if (IN6_IS_ADDR_V4MAPPED(
35477c478bd9Sstevel@tonic-gate 				    &sin6->sin6_addr))
35487c478bd9Sstevel@tonic-gate 					return (EADDRNOTAVAIL);
35497c478bd9Sstevel@tonic-gate 				ipp->ipp_nexthop = sin6->sin6_addr;
35507c478bd9Sstevel@tonic-gate 				if (!IN6_IS_ADDR_UNSPECIFIED(
35517c478bd9Sstevel@tonic-gate 				    &ipp->ipp_nexthop))
35527c478bd9Sstevel@tonic-gate 					ipp->ipp_fields |= IPPF_NEXTHOP;
35537c478bd9Sstevel@tonic-gate 				else
35547c478bd9Sstevel@tonic-gate 					ipp->ipp_fields &= ~IPPF_NEXTHOP;
35557c478bd9Sstevel@tonic-gate 			}
35567c478bd9Sstevel@tonic-gate 			if (sticky) {
3557fc80c0dfSnordmark 				error = udp_build_hdrs(udp);
35587c478bd9Sstevel@tonic-gate 				if (error != 0)
35597c478bd9Sstevel@tonic-gate 					return (error);
35607c478bd9Sstevel@tonic-gate 			}
35617c478bd9Sstevel@tonic-gate 			break;
35627c478bd9Sstevel@tonic-gate 		case IPV6_HOPOPTS: {
35637c478bd9Sstevel@tonic-gate 			ip6_hbh_t *hopts = (ip6_hbh_t *)invalp;
35647c478bd9Sstevel@tonic-gate 			/*
35657c478bd9Sstevel@tonic-gate 			 * Sanity checks - minimum size, size a multiple of
35667c478bd9Sstevel@tonic-gate 			 * eight bytes, and matching size passed in.
35677c478bd9Sstevel@tonic-gate 			 */
35687c478bd9Sstevel@tonic-gate 			if (inlen != 0 &&
35697c478bd9Sstevel@tonic-gate 			    inlen != (8 * (hopts->ip6h_len + 1)))
35707c478bd9Sstevel@tonic-gate 				return (EINVAL);
35717c478bd9Sstevel@tonic-gate 
35727c478bd9Sstevel@tonic-gate 			if (checkonly)
35737c478bd9Sstevel@tonic-gate 				break;
35747c478bd9Sstevel@tonic-gate 
357545916cd2Sjpk 			error = optcom_pkt_set(invalp, inlen, sticky,
357645916cd2Sjpk 			    (uchar_t **)&ipp->ipp_hopopts,
357745916cd2Sjpk 			    &ipp->ipp_hopoptslen,
357845916cd2Sjpk 			    sticky ? udp->udp_label_len_v6 : 0);
357945916cd2Sjpk 			if (error != 0)
358045916cd2Sjpk 				return (error);
358145916cd2Sjpk 			if (ipp->ipp_hopoptslen == 0) {
35827c478bd9Sstevel@tonic-gate 				ipp->ipp_fields &= ~IPPF_HOPOPTS;
35837c478bd9Sstevel@tonic-gate 				ipp->ipp_sticky_ignored |= IPPF_HOPOPTS;
35847c478bd9Sstevel@tonic-gate 			} else {
35857c478bd9Sstevel@tonic-gate 				ipp->ipp_fields |= IPPF_HOPOPTS;
35867c478bd9Sstevel@tonic-gate 			}
35877c478bd9Sstevel@tonic-gate 			if (sticky) {
3588fc80c0dfSnordmark 				error = udp_build_hdrs(udp);
35897c478bd9Sstevel@tonic-gate 				if (error != 0)
35907c478bd9Sstevel@tonic-gate 					return (error);
35917c478bd9Sstevel@tonic-gate 			}
35927c478bd9Sstevel@tonic-gate 			break;
35937c478bd9Sstevel@tonic-gate 		}
35947c478bd9Sstevel@tonic-gate 		case IPV6_RTHDRDSTOPTS: {
35957c478bd9Sstevel@tonic-gate 			ip6_dest_t *dopts = (ip6_dest_t *)invalp;
35967c478bd9Sstevel@tonic-gate 
35977c478bd9Sstevel@tonic-gate 			/*
35987c478bd9Sstevel@tonic-gate 			 * Sanity checks - minimum size, size a multiple of
35997c478bd9Sstevel@tonic-gate 			 * eight bytes, and matching size passed in.
36007c478bd9Sstevel@tonic-gate 			 */
36017c478bd9Sstevel@tonic-gate 			if (inlen != 0 &&
36027c478bd9Sstevel@tonic-gate 			    inlen != (8 * (dopts->ip6d_len + 1)))
36037c478bd9Sstevel@tonic-gate 				return (EINVAL);
36047c478bd9Sstevel@tonic-gate 
36057c478bd9Sstevel@tonic-gate 			if (checkonly)
36067c478bd9Sstevel@tonic-gate 				break;
36077c478bd9Sstevel@tonic-gate 
36087c478bd9Sstevel@tonic-gate 			if (inlen == 0) {
36097c478bd9Sstevel@tonic-gate 				if (sticky &&
36107c478bd9Sstevel@tonic-gate 				    (ipp->ipp_fields & IPPF_RTDSTOPTS) != 0) {
36117c478bd9Sstevel@tonic-gate 					kmem_free(ipp->ipp_rtdstopts,
36127c478bd9Sstevel@tonic-gate 					    ipp->ipp_rtdstoptslen);
36137c478bd9Sstevel@tonic-gate 					ipp->ipp_rtdstopts = NULL;
36147c478bd9Sstevel@tonic-gate 					ipp->ipp_rtdstoptslen = 0;
36157c478bd9Sstevel@tonic-gate 				}
3616ff550d0eSmasputra 
36177c478bd9Sstevel@tonic-gate 				ipp->ipp_fields &= ~IPPF_RTDSTOPTS;
36187c478bd9Sstevel@tonic-gate 				ipp->ipp_sticky_ignored |= IPPF_RTDSTOPTS;
36197c478bd9Sstevel@tonic-gate 			} else {
362045916cd2Sjpk 				error = optcom_pkt_set(invalp, inlen, sticky,
36217c478bd9Sstevel@tonic-gate 				    (uchar_t **)&ipp->ipp_rtdstopts,
362245916cd2Sjpk 				    &ipp->ipp_rtdstoptslen, 0);
36237c478bd9Sstevel@tonic-gate 				if (error != 0)
36247c478bd9Sstevel@tonic-gate 					return (error);
36257c478bd9Sstevel@tonic-gate 				ipp->ipp_fields |= IPPF_RTDSTOPTS;
36267c478bd9Sstevel@tonic-gate 			}
36277c478bd9Sstevel@tonic-gate 			if (sticky) {
3628fc80c0dfSnordmark 				error = udp_build_hdrs(udp);
36297c478bd9Sstevel@tonic-gate 				if (error != 0)
36307c478bd9Sstevel@tonic-gate 					return (error);
36317c478bd9Sstevel@tonic-gate 			}
36327c478bd9Sstevel@tonic-gate 			break;
36337c478bd9Sstevel@tonic-gate 		}
36347c478bd9Sstevel@tonic-gate 		case IPV6_DSTOPTS: {
36357c478bd9Sstevel@tonic-gate 			ip6_dest_t *dopts = (ip6_dest_t *)invalp;
36367c478bd9Sstevel@tonic-gate 
36377c478bd9Sstevel@tonic-gate 			/*
36387c478bd9Sstevel@tonic-gate 			 * Sanity checks - minimum size, size a multiple of
36397c478bd9Sstevel@tonic-gate 			 * eight bytes, and matching size passed in.
36407c478bd9Sstevel@tonic-gate 			 */
36417c478bd9Sstevel@tonic-gate 			if (inlen != 0 &&
36427c478bd9Sstevel@tonic-gate 			    inlen != (8 * (dopts->ip6d_len + 1)))
36437c478bd9Sstevel@tonic-gate 				return (EINVAL);
36447c478bd9Sstevel@tonic-gate 
36457c478bd9Sstevel@tonic-gate 			if (checkonly)
36467c478bd9Sstevel@tonic-gate 				break;
36477c478bd9Sstevel@tonic-gate 
36487c478bd9Sstevel@tonic-gate 			if (inlen == 0) {
36497c478bd9Sstevel@tonic-gate 				if (sticky &&
36507c478bd9Sstevel@tonic-gate 				    (ipp->ipp_fields & IPPF_DSTOPTS) != 0) {
36517c478bd9Sstevel@tonic-gate 					kmem_free(ipp->ipp_dstopts,
36527c478bd9Sstevel@tonic-gate 					    ipp->ipp_dstoptslen);
36537c478bd9Sstevel@tonic-gate 					ipp->ipp_dstopts = NULL;
36547c478bd9Sstevel@tonic-gate 					ipp->ipp_dstoptslen = 0;
36557c478bd9Sstevel@tonic-gate 				}
36567c478bd9Sstevel@tonic-gate 				ipp->ipp_fields &= ~IPPF_DSTOPTS;
36577c478bd9Sstevel@tonic-gate 				ipp->ipp_sticky_ignored |= IPPF_DSTOPTS;
36587c478bd9Sstevel@tonic-gate 			} else {
365945916cd2Sjpk 				error = optcom_pkt_set(invalp, inlen, sticky,
36607c478bd9Sstevel@tonic-gate 				    (uchar_t **)&ipp->ipp_dstopts,
366145916cd2Sjpk 				    &ipp->ipp_dstoptslen, 0);
36627c478bd9Sstevel@tonic-gate 				if (error != 0)
36637c478bd9Sstevel@tonic-gate 					return (error);
36647c478bd9Sstevel@tonic-gate 				ipp->ipp_fields |= IPPF_DSTOPTS;
36657c478bd9Sstevel@tonic-gate 			}
36667c478bd9Sstevel@tonic-gate 			if (sticky) {
3667fc80c0dfSnordmark 				error = udp_build_hdrs(udp);
36687c478bd9Sstevel@tonic-gate 				if (error != 0)
36697c478bd9Sstevel@tonic-gate 					return (error);
36707c478bd9Sstevel@tonic-gate 			}
36717c478bd9Sstevel@tonic-gate 			break;
36727c478bd9Sstevel@tonic-gate 		}
36737c478bd9Sstevel@tonic-gate 		case IPV6_RTHDR: {
36747c478bd9Sstevel@tonic-gate 			ip6_rthdr_t *rt = (ip6_rthdr_t *)invalp;
36757c478bd9Sstevel@tonic-gate 
36767c478bd9Sstevel@tonic-gate 			/*
36777c478bd9Sstevel@tonic-gate 			 * Sanity checks - minimum size, size a multiple of
36787c478bd9Sstevel@tonic-gate 			 * eight bytes, and matching size passed in.
36797c478bd9Sstevel@tonic-gate 			 */
36807c478bd9Sstevel@tonic-gate 			if (inlen != 0 &&
36817c478bd9Sstevel@tonic-gate 			    inlen != (8 * (rt->ip6r_len + 1)))
36827c478bd9Sstevel@tonic-gate 				return (EINVAL);
36837c478bd9Sstevel@tonic-gate 
36847c478bd9Sstevel@tonic-gate 			if (checkonly)
36857c478bd9Sstevel@tonic-gate 				break;
36867c478bd9Sstevel@tonic-gate 
36877c478bd9Sstevel@tonic-gate 			if (inlen == 0) {
36887c478bd9Sstevel@tonic-gate 				if (sticky &&
36897c478bd9Sstevel@tonic-gate 				    (ipp->ipp_fields & IPPF_RTHDR) != 0) {
36907c478bd9Sstevel@tonic-gate 					kmem_free(ipp->ipp_rthdr,
36917c478bd9Sstevel@tonic-gate 					    ipp->ipp_rthdrlen);
36927c478bd9Sstevel@tonic-gate 					ipp->ipp_rthdr = NULL;
36937c478bd9Sstevel@tonic-gate 					ipp->ipp_rthdrlen = 0;
36947c478bd9Sstevel@tonic-gate 				}
36957c478bd9Sstevel@tonic-gate 				ipp->ipp_fields &= ~IPPF_RTHDR;
36967c478bd9Sstevel@tonic-gate 				ipp->ipp_sticky_ignored |= IPPF_RTHDR;
36977c478bd9Sstevel@tonic-gate 			} else {
369845916cd2Sjpk 				error = optcom_pkt_set(invalp, inlen, sticky,
36997c478bd9Sstevel@tonic-gate 				    (uchar_t **)&ipp->ipp_rthdr,
370045916cd2Sjpk 				    &ipp->ipp_rthdrlen, 0);
37017c478bd9Sstevel@tonic-gate 				if (error != 0)
37027c478bd9Sstevel@tonic-gate 					return (error);
37037c478bd9Sstevel@tonic-gate 				ipp->ipp_fields |= IPPF_RTHDR;
37047c478bd9Sstevel@tonic-gate 			}
37057c478bd9Sstevel@tonic-gate 			if (sticky) {
3706fc80c0dfSnordmark 				error = udp_build_hdrs(udp);
37077c478bd9Sstevel@tonic-gate 				if (error != 0)
37087c478bd9Sstevel@tonic-gate 					return (error);
37097c478bd9Sstevel@tonic-gate 			}
37107c478bd9Sstevel@tonic-gate 			break;
37117c478bd9Sstevel@tonic-gate 		}
37127c478bd9Sstevel@tonic-gate 
37137c478bd9Sstevel@tonic-gate 		case IPV6_DONTFRAG:
37147c478bd9Sstevel@tonic-gate 			if (checkonly)
37157c478bd9Sstevel@tonic-gate 				break;
37167c478bd9Sstevel@tonic-gate 
37177c478bd9Sstevel@tonic-gate 			if (onoff) {
37187c478bd9Sstevel@tonic-gate 				ipp->ipp_fields |= IPPF_DONTFRAG;
37197c478bd9Sstevel@tonic-gate 			} else {
37207c478bd9Sstevel@tonic-gate 				ipp->ipp_fields &= ~IPPF_DONTFRAG;
37217c478bd9Sstevel@tonic-gate 			}
37227c478bd9Sstevel@tonic-gate 			break;
37237c478bd9Sstevel@tonic-gate 
37247c478bd9Sstevel@tonic-gate 		case IPV6_USE_MIN_MTU:
37257c478bd9Sstevel@tonic-gate 			if (inlen != sizeof (int))
37267c478bd9Sstevel@tonic-gate 				return (EINVAL);
37277c478bd9Sstevel@tonic-gate 
37287c478bd9Sstevel@tonic-gate 			if (*i1 < -1 || *i1 > 1)
37297c478bd9Sstevel@tonic-gate 				return (EINVAL);
37307c478bd9Sstevel@tonic-gate 
37317c478bd9Sstevel@tonic-gate 			if (checkonly)
37327c478bd9Sstevel@tonic-gate 				break;
37337c478bd9Sstevel@tonic-gate 
37347c478bd9Sstevel@tonic-gate 			ipp->ipp_fields |= IPPF_USE_MIN_MTU;
37357c478bd9Sstevel@tonic-gate 			ipp->ipp_use_min_mtu = *i1;
37367c478bd9Sstevel@tonic-gate 			break;
37377c478bd9Sstevel@tonic-gate 
37387c478bd9Sstevel@tonic-gate 		case IPV6_BOUND_PIF:
37397c478bd9Sstevel@tonic-gate 		case IPV6_SEC_OPT:
37407c478bd9Sstevel@tonic-gate 		case IPV6_DONTFAILOVER_IF:
37417c478bd9Sstevel@tonic-gate 		case IPV6_SRC_PREFERENCES:
37427c478bd9Sstevel@tonic-gate 		case IPV6_V6ONLY:
37437c478bd9Sstevel@tonic-gate 			/* Handled at the IP level */
37447c478bd9Sstevel@tonic-gate 			return (-EINVAL);
37457c478bd9Sstevel@tonic-gate 		default:
37467c478bd9Sstevel@tonic-gate 			*outlenp = 0;
37477c478bd9Sstevel@tonic-gate 			return (EINVAL);
37487c478bd9Sstevel@tonic-gate 		}
37497c478bd9Sstevel@tonic-gate 		break;
37507c478bd9Sstevel@tonic-gate 		}		/* end IPPROTO_IPV6 */
37517c478bd9Sstevel@tonic-gate 	case IPPROTO_UDP:
37527c478bd9Sstevel@tonic-gate 		switch (name) {
37537c478bd9Sstevel@tonic-gate 		case UDP_ANONPRIVBIND:
3754ddf7fe95Scasper 			if ((error = secpolicy_net_privaddr(cr, 0,
3755ddf7fe95Scasper 			    IPPROTO_UDP)) != 0) {
37567c478bd9Sstevel@tonic-gate 				*outlenp = 0;
37577c478bd9Sstevel@tonic-gate 				return (error);
37587c478bd9Sstevel@tonic-gate 			}
37597c478bd9Sstevel@tonic-gate 			if (!checkonly) {
37607c478bd9Sstevel@tonic-gate 				udp->udp_anon_priv_bind = onoff;
37617c478bd9Sstevel@tonic-gate 			}
37627c478bd9Sstevel@tonic-gate 			break;
37637c478bd9Sstevel@tonic-gate 		case UDP_EXCLBIND:
37647c478bd9Sstevel@tonic-gate 			if (!checkonly)
37657c478bd9Sstevel@tonic-gate 				udp->udp_exclbind = onoff;
37667c478bd9Sstevel@tonic-gate 			break;
37677c478bd9Sstevel@tonic-gate 		case UDP_RCVHDR:
37687c478bd9Sstevel@tonic-gate 			if (!checkonly)
37697c478bd9Sstevel@tonic-gate 				udp->udp_rcvhdr = onoff;
37707c478bd9Sstevel@tonic-gate 			break;
3771437220cdSdanmcd 		case UDP_NAT_T_ENDPOINT:
3772437220cdSdanmcd 			if ((error = secpolicy_ip_config(cr, B_FALSE)) != 0) {
3773437220cdSdanmcd 				*outlenp = 0;
3774437220cdSdanmcd 				return (error);
3775437220cdSdanmcd 			}
3776437220cdSdanmcd 
3777437220cdSdanmcd 			/*
3778437220cdSdanmcd 			 * Use udp_family instead so we can avoid ambiguitites
3779437220cdSdanmcd 			 * with AF_INET6 sockets that may switch from IPv4
3780437220cdSdanmcd 			 * to IPv6.
3781437220cdSdanmcd 			 */
3782437220cdSdanmcd 			if (udp->udp_family != AF_INET) {
3783437220cdSdanmcd 				*outlenp = 0;
3784437220cdSdanmcd 				return (EAFNOSUPPORT);
3785437220cdSdanmcd 			}
3786437220cdSdanmcd 
3787437220cdSdanmcd 			if (!checkonly) {
3788437220cdSdanmcd 				udp->udp_nat_t_endpoint = onoff;
3789437220cdSdanmcd 
3790437220cdSdanmcd 				udp->udp_max_hdr_len = IP_SIMPLE_HDR_LENGTH +
3791437220cdSdanmcd 				    UDPH_SIZE + udp->udp_ip_snd_options_len;
3792437220cdSdanmcd 
3793437220cdSdanmcd 				/* Also, adjust wroff */
3794437220cdSdanmcd 				if (onoff) {
3795437220cdSdanmcd 					udp->udp_max_hdr_len +=
3796437220cdSdanmcd 					    sizeof (uint32_t);
3797437220cdSdanmcd 				}
3798437220cdSdanmcd 				(void) mi_set_sth_wroff(RD(q),
3799437220cdSdanmcd 				    udp->udp_max_hdr_len + us->us_wroff_extra);
3800437220cdSdanmcd 			}
3801437220cdSdanmcd 			break;
38027c478bd9Sstevel@tonic-gate 		default:
38037c478bd9Sstevel@tonic-gate 			*outlenp = 0;
38047c478bd9Sstevel@tonic-gate 			return (EINVAL);
38057c478bd9Sstevel@tonic-gate 		}
38067c478bd9Sstevel@tonic-gate 		break;
38077c478bd9Sstevel@tonic-gate 	default:
38087c478bd9Sstevel@tonic-gate 		*outlenp = 0;
38097c478bd9Sstevel@tonic-gate 		return (EINVAL);
38107c478bd9Sstevel@tonic-gate 	}
38117c478bd9Sstevel@tonic-gate 	/*
38127c478bd9Sstevel@tonic-gate 	 * Common case of OK return with outval same as inval.
38137c478bd9Sstevel@tonic-gate 	 */
38147c478bd9Sstevel@tonic-gate 	if (invalp != outvalp) {
38157c478bd9Sstevel@tonic-gate 		/* don't trust bcopy for identical src/dst */
38167c478bd9Sstevel@tonic-gate 		(void) bcopy(invalp, outvalp, inlen);
38177c478bd9Sstevel@tonic-gate 	}
38187c478bd9Sstevel@tonic-gate 	*outlenp = inlen;
38197c478bd9Sstevel@tonic-gate 	return (0);
38207c478bd9Sstevel@tonic-gate }
38217c478bd9Sstevel@tonic-gate 
3822fc80c0dfSnordmark int
3823fc80c0dfSnordmark udp_opt_set(queue_t *q, uint_t optset_context, int level,
3824fc80c0dfSnordmark     int name, uint_t inlen, uchar_t *invalp, uint_t *outlenp,
3825fc80c0dfSnordmark     uchar_t *outvalp, void *thisdg_attrs, cred_t *cr, mblk_t *mblk)
3826fc80c0dfSnordmark {
3827fc80c0dfSnordmark 	udp_t	*udp;
3828fc80c0dfSnordmark 	int	err;
3829fc80c0dfSnordmark 
3830fc80c0dfSnordmark 	udp = Q_TO_UDP(q);
3831fc80c0dfSnordmark 
3832fc80c0dfSnordmark 	rw_enter(&udp->udp_rwlock, RW_WRITER);
3833fc80c0dfSnordmark 	err = udp_opt_set_locked(q, optset_context, level, name, inlen, invalp,
3834fc80c0dfSnordmark 	    outlenp, outvalp, thisdg_attrs, cr, mblk);
3835fc80c0dfSnordmark 	rw_exit(&udp->udp_rwlock);
3836fc80c0dfSnordmark 	return (err);
3837fc80c0dfSnordmark }
3838fc80c0dfSnordmark 
38397c478bd9Sstevel@tonic-gate /*
38407c478bd9Sstevel@tonic-gate  * Update udp_sticky_hdrs based on udp_sticky_ipp, udp_v6src, and udp_ttl.
38417c478bd9Sstevel@tonic-gate  * The headers include ip6i_t (if needed), ip6_t, any sticky extension
38427c478bd9Sstevel@tonic-gate  * headers, and the udp header.
38437c478bd9Sstevel@tonic-gate  * Returns failure if can't allocate memory.
38447c478bd9Sstevel@tonic-gate  */
38457c478bd9Sstevel@tonic-gate static int
3846fc80c0dfSnordmark udp_build_hdrs(udp_t *udp)
38477c478bd9Sstevel@tonic-gate {
3848f4b3ec61Sdh 	udp_stack_t *us = udp->udp_us;
38497c478bd9Sstevel@tonic-gate 	uchar_t	*hdrs;
38507c478bd9Sstevel@tonic-gate 	uint_t	hdrs_len;
38517c478bd9Sstevel@tonic-gate 	ip6_t	*ip6h;
38527c478bd9Sstevel@tonic-gate 	ip6i_t	*ip6i;
38537c478bd9Sstevel@tonic-gate 	udpha_t	*udpha;
38547c478bd9Sstevel@tonic-gate 	ip6_pkt_t *ipp = &udp->udp_sticky_ipp;
3855fc80c0dfSnordmark 	size_t	sth_wroff;
38567c478bd9Sstevel@tonic-gate 
3857fc80c0dfSnordmark 	ASSERT(RW_WRITE_HELD(&udp->udp_rwlock));
38587c478bd9Sstevel@tonic-gate 	hdrs_len = ip_total_hdrs_len_v6(ipp) + UDPH_SIZE;
38597c478bd9Sstevel@tonic-gate 	ASSERT(hdrs_len != 0);
38607c478bd9Sstevel@tonic-gate 	if (hdrs_len != udp->udp_sticky_hdrs_len) {
38617c478bd9Sstevel@tonic-gate 		/* Need to reallocate */
38627c478bd9Sstevel@tonic-gate 		hdrs = kmem_alloc(hdrs_len, KM_NOSLEEP);
38637c478bd9Sstevel@tonic-gate 		if (hdrs == NULL)
38647c478bd9Sstevel@tonic-gate 			return (ENOMEM);
38657c478bd9Sstevel@tonic-gate 
38667c478bd9Sstevel@tonic-gate 		if (udp->udp_sticky_hdrs_len != 0) {
38677c478bd9Sstevel@tonic-gate 			kmem_free(udp->udp_sticky_hdrs,
38687c478bd9Sstevel@tonic-gate 			    udp->udp_sticky_hdrs_len);
38697c478bd9Sstevel@tonic-gate 		}
38707c478bd9Sstevel@tonic-gate 		udp->udp_sticky_hdrs = hdrs;
38717c478bd9Sstevel@tonic-gate 		udp->udp_sticky_hdrs_len = hdrs_len;
38727c478bd9Sstevel@tonic-gate 	}
38737c478bd9Sstevel@tonic-gate 	ip_build_hdrs_v6(udp->udp_sticky_hdrs,
38747c478bd9Sstevel@tonic-gate 	    udp->udp_sticky_hdrs_len - UDPH_SIZE, ipp, IPPROTO_UDP);
38757c478bd9Sstevel@tonic-gate 
38767c478bd9Sstevel@tonic-gate 	/* Set header fields not in ipp */
38777c478bd9Sstevel@tonic-gate 	if (ipp->ipp_fields & IPPF_HAS_IP6I) {
38787c478bd9Sstevel@tonic-gate 		ip6i = (ip6i_t *)udp->udp_sticky_hdrs;
38797c478bd9Sstevel@tonic-gate 		ip6h = (ip6_t *)&ip6i[1];
38807c478bd9Sstevel@tonic-gate 	} else {
38817c478bd9Sstevel@tonic-gate 		ip6h = (ip6_t *)udp->udp_sticky_hdrs;
38827c478bd9Sstevel@tonic-gate 	}
38837c478bd9Sstevel@tonic-gate 
38847c478bd9Sstevel@tonic-gate 	if (!(ipp->ipp_fields & IPPF_ADDR))
38857c478bd9Sstevel@tonic-gate 		ip6h->ip6_src = udp->udp_v6src;
38867c478bd9Sstevel@tonic-gate 
38877c478bd9Sstevel@tonic-gate 	udpha = (udpha_t *)(udp->udp_sticky_hdrs + hdrs_len - UDPH_SIZE);
38887c478bd9Sstevel@tonic-gate 	udpha->uha_src_port = udp->udp_port;
38897c478bd9Sstevel@tonic-gate 
38907c478bd9Sstevel@tonic-gate 	/* Try to get everything in a single mblk */
38917c478bd9Sstevel@tonic-gate 	if (hdrs_len > udp->udp_max_hdr_len) {
38927c478bd9Sstevel@tonic-gate 		udp->udp_max_hdr_len = hdrs_len;
3893fc80c0dfSnordmark 		sth_wroff = udp->udp_max_hdr_len + us->us_wroff_extra;
3894fc80c0dfSnordmark 		rw_exit(&udp->udp_rwlock);
3895fc80c0dfSnordmark 		(void) mi_set_sth_wroff(udp->udp_connp->conn_rq, sth_wroff);
3896fc80c0dfSnordmark 		rw_enter(&udp->udp_rwlock, RW_WRITER);
38977c478bd9Sstevel@tonic-gate 	}
38987c478bd9Sstevel@tonic-gate 	return (0);
38997c478bd9Sstevel@tonic-gate }
39007c478bd9Sstevel@tonic-gate 
39017c478bd9Sstevel@tonic-gate /*
39027c478bd9Sstevel@tonic-gate  * This routine retrieves the value of an ND variable in a udpparam_t
39037c478bd9Sstevel@tonic-gate  * structure.  It is called through nd_getset when a user reads the
39047c478bd9Sstevel@tonic-gate  * variable.
39057c478bd9Sstevel@tonic-gate  */
39067c478bd9Sstevel@tonic-gate /* ARGSUSED */
39077c478bd9Sstevel@tonic-gate static int
39087c478bd9Sstevel@tonic-gate udp_param_get(queue_t *q, mblk_t *mp, caddr_t cp, cred_t *cr)
39097c478bd9Sstevel@tonic-gate {
39107c478bd9Sstevel@tonic-gate 	udpparam_t *udppa = (udpparam_t *)cp;
39117c478bd9Sstevel@tonic-gate 
39127c478bd9Sstevel@tonic-gate 	(void) mi_mpprintf(mp, "%d", udppa->udp_param_value);
39137c478bd9Sstevel@tonic-gate 	return (0);
39147c478bd9Sstevel@tonic-gate }
39157c478bd9Sstevel@tonic-gate 
39167c478bd9Sstevel@tonic-gate /*
39177c478bd9Sstevel@tonic-gate  * Walk through the param array specified registering each element with the
39187c478bd9Sstevel@tonic-gate  * named dispatch (ND) handler.
39197c478bd9Sstevel@tonic-gate  */
39207c478bd9Sstevel@tonic-gate static boolean_t
3921f4b3ec61Sdh udp_param_register(IDP *ndp, udpparam_t *udppa, int cnt)
39227c478bd9Sstevel@tonic-gate {
39237c478bd9Sstevel@tonic-gate 	for (; cnt-- > 0; udppa++) {
39247c478bd9Sstevel@tonic-gate 		if (udppa->udp_param_name && udppa->udp_param_name[0]) {
3925f4b3ec61Sdh 			if (!nd_load(ndp, udppa->udp_param_name,
39267c478bd9Sstevel@tonic-gate 			    udp_param_get, udp_param_set,
39277c478bd9Sstevel@tonic-gate 			    (caddr_t)udppa)) {
3928f4b3ec61Sdh 				nd_free(ndp);
39297c478bd9Sstevel@tonic-gate 				return (B_FALSE);
39307c478bd9Sstevel@tonic-gate 			}
39317c478bd9Sstevel@tonic-gate 		}
39327c478bd9Sstevel@tonic-gate 	}
3933f4b3ec61Sdh 	if (!nd_load(ndp, "udp_extra_priv_ports",
39347c478bd9Sstevel@tonic-gate 	    udp_extra_priv_ports_get, NULL, NULL)) {
3935f4b3ec61Sdh 		nd_free(ndp);
39367c478bd9Sstevel@tonic-gate 		return (B_FALSE);
39377c478bd9Sstevel@tonic-gate 	}
3938f4b3ec61Sdh 	if (!nd_load(ndp, "udp_extra_priv_ports_add",
39397c478bd9Sstevel@tonic-gate 	    NULL, udp_extra_priv_ports_add, NULL)) {
3940f4b3ec61Sdh 		nd_free(ndp);
39417c478bd9Sstevel@tonic-gate 		return (B_FALSE);
39427c478bd9Sstevel@tonic-gate 	}
3943f4b3ec61Sdh 	if (!nd_load(ndp, "udp_extra_priv_ports_del",
39447c478bd9Sstevel@tonic-gate 	    NULL, udp_extra_priv_ports_del, NULL)) {
3945f4b3ec61Sdh 		nd_free(ndp);
39467c478bd9Sstevel@tonic-gate 		return (B_FALSE);
39477c478bd9Sstevel@tonic-gate 	}
3948f4b3ec61Sdh 	if (!nd_load(ndp, "udp_status", udp_status_report, NULL,
39497c478bd9Sstevel@tonic-gate 	    NULL)) {
3950f4b3ec61Sdh 		nd_free(ndp);
39517c478bd9Sstevel@tonic-gate 		return (B_FALSE);
39527c478bd9Sstevel@tonic-gate 	}
3953f4b3ec61Sdh 	if (!nd_load(ndp, "udp_bind_hash", udp_bind_hash_report, NULL,
39547c478bd9Sstevel@tonic-gate 	    NULL)) {
3955f4b3ec61Sdh 		nd_free(ndp);
39567c478bd9Sstevel@tonic-gate 		return (B_FALSE);
39577c478bd9Sstevel@tonic-gate 	}
39587c478bd9Sstevel@tonic-gate 	return (B_TRUE);
39597c478bd9Sstevel@tonic-gate }
39607c478bd9Sstevel@tonic-gate 
39617c478bd9Sstevel@tonic-gate /* This routine sets an ND variable in a udpparam_t structure. */
39627c478bd9Sstevel@tonic-gate /* ARGSUSED */
39637c478bd9Sstevel@tonic-gate static int
39647c478bd9Sstevel@tonic-gate udp_param_set(queue_t *q, mblk_t *mp, char *value, caddr_t cp, cred_t *cr)
39657c478bd9Sstevel@tonic-gate {
39667c478bd9Sstevel@tonic-gate 	long		new_value;
39677c478bd9Sstevel@tonic-gate 	udpparam_t	*udppa = (udpparam_t *)cp;
39687c478bd9Sstevel@tonic-gate 
39697c478bd9Sstevel@tonic-gate 	/*
39707c478bd9Sstevel@tonic-gate 	 * Fail the request if the new value does not lie within the
39717c478bd9Sstevel@tonic-gate 	 * required bounds.
39727c478bd9Sstevel@tonic-gate 	 */
39737c478bd9Sstevel@tonic-gate 	if (ddi_strtol(value, NULL, 10, &new_value) != 0 ||
39747c478bd9Sstevel@tonic-gate 	    new_value < udppa->udp_param_min ||
39757c478bd9Sstevel@tonic-gate 	    new_value > udppa->udp_param_max) {
39767c478bd9Sstevel@tonic-gate 		return (EINVAL);
39777c478bd9Sstevel@tonic-gate 	}
39787c478bd9Sstevel@tonic-gate 
39797c478bd9Sstevel@tonic-gate 	/* Set the new value */
39807c478bd9Sstevel@tonic-gate 	udppa->udp_param_value = new_value;
39817c478bd9Sstevel@tonic-gate 	return (0);
39827c478bd9Sstevel@tonic-gate }
39837c478bd9Sstevel@tonic-gate 
398445916cd2Sjpk /*
398545916cd2Sjpk  * Copy hop-by-hop option from ipp->ipp_hopopts to the buffer provided (with
398645916cd2Sjpk  * T_opthdr) and return the number of bytes copied.  'dbuf' may be NULL to
398745916cd2Sjpk  * just count the length needed for allocation.  If 'dbuf' is non-NULL,
398845916cd2Sjpk  * then it's assumed to be allocated to be large enough.
398945916cd2Sjpk  *
399045916cd2Sjpk  * Returns zero if trimming of the security option causes all options to go
399145916cd2Sjpk  * away.
399245916cd2Sjpk  */
399345916cd2Sjpk static size_t
399445916cd2Sjpk copy_hop_opts(const ip6_pkt_t *ipp, uchar_t *dbuf)
399545916cd2Sjpk {
399645916cd2Sjpk 	struct T_opthdr *toh;
399745916cd2Sjpk 	size_t hol = ipp->ipp_hopoptslen;
399845916cd2Sjpk 	ip6_hbh_t *dstopt = NULL;
399945916cd2Sjpk 	const ip6_hbh_t *srcopt = ipp->ipp_hopopts;
400045916cd2Sjpk 	size_t tlen, olen, plen;
400145916cd2Sjpk 	boolean_t deleting;
400245916cd2Sjpk 	const struct ip6_opt *sopt, *lastpad;
400345916cd2Sjpk 	struct ip6_opt *dopt;
400445916cd2Sjpk 
400545916cd2Sjpk 	if ((toh = (struct T_opthdr *)dbuf) != NULL) {
400645916cd2Sjpk 		toh->level = IPPROTO_IPV6;
400745916cd2Sjpk 		toh->name = IPV6_HOPOPTS;
400845916cd2Sjpk 		toh->status = 0;
400945916cd2Sjpk 		dstopt = (ip6_hbh_t *)(toh + 1);
401045916cd2Sjpk 	}
401145916cd2Sjpk 
401245916cd2Sjpk 	/*
401345916cd2Sjpk 	 * If labeling is enabled, then skip the label option
401445916cd2Sjpk 	 * but get other options if there are any.
401545916cd2Sjpk 	 */
401645916cd2Sjpk 	if (is_system_labeled()) {
401745916cd2Sjpk 		dopt = NULL;
401845916cd2Sjpk 		if (dstopt != NULL) {
401945916cd2Sjpk 			/* will fill in ip6h_len later */
402045916cd2Sjpk 			dstopt->ip6h_nxt = srcopt->ip6h_nxt;
402145916cd2Sjpk 			dopt = (struct ip6_opt *)(dstopt + 1);
402245916cd2Sjpk 		}
402345916cd2Sjpk 		sopt = (const struct ip6_opt *)(srcopt + 1);
402445916cd2Sjpk 		hol -= sizeof (*srcopt);
402545916cd2Sjpk 		tlen = sizeof (*dstopt);
402645916cd2Sjpk 		lastpad = NULL;
402745916cd2Sjpk 		deleting = B_FALSE;
402845916cd2Sjpk 		/*
402945916cd2Sjpk 		 * This loop finds the first (lastpad pointer) of any number of
403045916cd2Sjpk 		 * pads that preceeds the security option, then treats the
403145916cd2Sjpk 		 * security option as though it were a pad, and then finds the
403245916cd2Sjpk 		 * next non-pad option (or end of list).
403345916cd2Sjpk 		 *
403445916cd2Sjpk 		 * It then treats the entire block as one big pad.  To preserve
403545916cd2Sjpk 		 * alignment of any options that follow, or just the end of the
403645916cd2Sjpk 		 * list, it computes a minimal new padding size that keeps the
403745916cd2Sjpk 		 * same alignment for the next option.
403845916cd2Sjpk 		 *
403945916cd2Sjpk 		 * If it encounters just a sequence of pads with no security
404045916cd2Sjpk 		 * option, those are copied as-is rather than collapsed.
404145916cd2Sjpk 		 *
404245916cd2Sjpk 		 * Note that to handle the end of list case, the code makes one
404345916cd2Sjpk 		 * loop with 'hol' set to zero.
404445916cd2Sjpk 		 */
404545916cd2Sjpk 		for (;;) {
404645916cd2Sjpk 			if (hol > 0) {
404745916cd2Sjpk 				if (sopt->ip6o_type == IP6OPT_PAD1) {
404845916cd2Sjpk 					if (lastpad == NULL)
404945916cd2Sjpk 						lastpad = sopt;
405045916cd2Sjpk 					sopt = (const struct ip6_opt *)
405145916cd2Sjpk 					    &sopt->ip6o_len;
405245916cd2Sjpk 					hol--;
405345916cd2Sjpk 					continue;
405445916cd2Sjpk 				}
405545916cd2Sjpk 				olen = sopt->ip6o_len + sizeof (*sopt);
405645916cd2Sjpk 				if (olen > hol)
405745916cd2Sjpk 					olen = hol;
405845916cd2Sjpk 				if (sopt->ip6o_type == IP6OPT_PADN ||
405945916cd2Sjpk 				    sopt->ip6o_type == ip6opt_ls) {
406045916cd2Sjpk 					if (sopt->ip6o_type == ip6opt_ls)
406145916cd2Sjpk 						deleting = B_TRUE;
406245916cd2Sjpk 					if (lastpad == NULL)
406345916cd2Sjpk 						lastpad = sopt;
406445916cd2Sjpk 					sopt = (const struct ip6_opt *)
406545916cd2Sjpk 					    ((const char *)sopt + olen);
406645916cd2Sjpk 					hol -= olen;
406745916cd2Sjpk 					continue;
406845916cd2Sjpk 				}
406945916cd2Sjpk 			} else {
407045916cd2Sjpk 				/* if nothing was copied at all, then delete */
407145916cd2Sjpk 				if (tlen == sizeof (*dstopt))
407245916cd2Sjpk 					return (0);
407345916cd2Sjpk 				/* last pass; pick up any trailing padding */
407445916cd2Sjpk 				olen = 0;
407545916cd2Sjpk 			}
407645916cd2Sjpk 			if (deleting) {
407745916cd2Sjpk 				/*
407845916cd2Sjpk 				 * compute aligning effect of deleted material
407945916cd2Sjpk 				 * to reproduce with pad.
408045916cd2Sjpk 				 */
408145916cd2Sjpk 				plen = ((const char *)sopt -
408245916cd2Sjpk 				    (const char *)lastpad) & 7;
408345916cd2Sjpk 				tlen += plen;
408445916cd2Sjpk 				if (dopt != NULL) {
408545916cd2Sjpk 					if (plen == 1) {
408645916cd2Sjpk 						dopt->ip6o_type = IP6OPT_PAD1;
408745916cd2Sjpk 					} else if (plen > 1) {
408845916cd2Sjpk 						plen -= sizeof (*dopt);
408945916cd2Sjpk 						dopt->ip6o_type = IP6OPT_PADN;
409045916cd2Sjpk 						dopt->ip6o_len = plen;
409145916cd2Sjpk 						if (plen > 0)
409245916cd2Sjpk 							bzero(dopt + 1, plen);
409345916cd2Sjpk 					}
409445916cd2Sjpk 					dopt = (struct ip6_opt *)
409545916cd2Sjpk 					    ((char *)dopt + plen);
409645916cd2Sjpk 				}
409745916cd2Sjpk 				deleting = B_FALSE;
409845916cd2Sjpk 				lastpad = NULL;
409945916cd2Sjpk 			}
410045916cd2Sjpk 			/* if there's uncopied padding, then copy that now */
410145916cd2Sjpk 			if (lastpad != NULL) {
410245916cd2Sjpk 				olen += (const char *)sopt -
410345916cd2Sjpk 				    (const char *)lastpad;
410445916cd2Sjpk 				sopt = lastpad;
410545916cd2Sjpk 				lastpad = NULL;
410645916cd2Sjpk 			}
410745916cd2Sjpk 			if (dopt != NULL && olen > 0) {
410845916cd2Sjpk 				bcopy(sopt, dopt, olen);
410945916cd2Sjpk 				dopt = (struct ip6_opt *)((char *)dopt + olen);
411045916cd2Sjpk 			}
411145916cd2Sjpk 			if (hol == 0)
411245916cd2Sjpk 				break;
411345916cd2Sjpk 			tlen += olen;
411445916cd2Sjpk 			sopt = (const struct ip6_opt *)
411545916cd2Sjpk 			    ((const char *)sopt + olen);
411645916cd2Sjpk 			hol -= olen;
411745916cd2Sjpk 		}
411845916cd2Sjpk 		/* go back and patch up the length value, rounded upward */
411945916cd2Sjpk 		if (dstopt != NULL)
412045916cd2Sjpk 			dstopt->ip6h_len = (tlen - 1) >> 3;
412145916cd2Sjpk 	} else {
412245916cd2Sjpk 		tlen = hol;
412345916cd2Sjpk 		if (dstopt != NULL)
412445916cd2Sjpk 			bcopy(srcopt, dstopt, hol);
412545916cd2Sjpk 	}
412645916cd2Sjpk 
412745916cd2Sjpk 	tlen += sizeof (*toh);
412845916cd2Sjpk 	if (toh != NULL)
412945916cd2Sjpk 		toh->len = tlen;
413045916cd2Sjpk 
413145916cd2Sjpk 	return (tlen);
413245916cd2Sjpk }
413345916cd2Sjpk 
4134fc80c0dfSnordmark /*
4135fc80c0dfSnordmark  * Update udp_rcv_opt_len from the packet.
4136fc80c0dfSnordmark  * Called when options received, and when no options received but
4137fc80c0dfSnordmark  * udp_ip_recv_opt_len has previously recorded options.
4138fc80c0dfSnordmark  */
41397c478bd9Sstevel@tonic-gate static void
4140fc80c0dfSnordmark udp_save_ip_rcv_opt(udp_t *udp, void *opt, int opt_len)
41417c478bd9Sstevel@tonic-gate {
4142fc80c0dfSnordmark 	/* Save the options if any */
4143fc80c0dfSnordmark 	if (opt_len > 0) {
4144fc80c0dfSnordmark 		if (opt_len > udp->udp_ip_rcv_options_len) {
4145fc80c0dfSnordmark 			/* Need to allocate larger buffer */
4146fc80c0dfSnordmark 			if (udp->udp_ip_rcv_options_len != 0)
4147fc80c0dfSnordmark 				mi_free((char *)udp->udp_ip_rcv_options);
4148fc80c0dfSnordmark 			udp->udp_ip_rcv_options_len = 0;
4149fc80c0dfSnordmark 			udp->udp_ip_rcv_options =
4150fc80c0dfSnordmark 			    (uchar_t *)mi_alloc(opt_len, BPRI_HI);
4151fc80c0dfSnordmark 			if (udp->udp_ip_rcv_options != NULL)
4152fc80c0dfSnordmark 				udp->udp_ip_rcv_options_len = opt_len;
4153fc80c0dfSnordmark 		}
4154fc80c0dfSnordmark 		if (udp->udp_ip_rcv_options_len != 0) {
4155fc80c0dfSnordmark 			bcopy(opt, udp->udp_ip_rcv_options, opt_len);
4156fc80c0dfSnordmark 			/* Adjust length if we are resusing the space */
4157fc80c0dfSnordmark 			udp->udp_ip_rcv_options_len = opt_len;
4158fc80c0dfSnordmark 		}
4159fc80c0dfSnordmark 	} else if (udp->udp_ip_rcv_options_len != 0) {
4160fc80c0dfSnordmark 		/* Clear out previously recorded options */
4161fc80c0dfSnordmark 		mi_free((char *)udp->udp_ip_rcv_options);
4162fc80c0dfSnordmark 		udp->udp_ip_rcv_options = NULL;
4163fc80c0dfSnordmark 		udp->udp_ip_rcv_options_len = 0;
4164fc80c0dfSnordmark 	}
4165fc80c0dfSnordmark }
4166fc80c0dfSnordmark 
4167fc80c0dfSnordmark /* ARGSUSED2 */
4168fc80c0dfSnordmark static void
4169fc80c0dfSnordmark udp_input(void *arg1, mblk_t *mp, void *arg2)
4170fc80c0dfSnordmark {
4171fc80c0dfSnordmark 	conn_t *connp = (conn_t *)arg1;
41727c478bd9Sstevel@tonic-gate 	struct T_unitdata_ind	*tudi;
4173ff550d0eSmasputra 	uchar_t			*rptr;		/* Pointer to IP header */
4174ff550d0eSmasputra 	int			hdr_length;	/* Length of IP+UDP headers */
4175fc80c0dfSnordmark 	int			opt_len;
41767c478bd9Sstevel@tonic-gate 	int			udi_size;	/* Size of T_unitdata_ind */
4177ff550d0eSmasputra 	int			mp_len;
41787c478bd9Sstevel@tonic-gate 	udp_t			*udp;
41797c478bd9Sstevel@tonic-gate 	udpha_t			*udpha;
41807c478bd9Sstevel@tonic-gate 	int			ipversion;
41817c478bd9Sstevel@tonic-gate 	ip6_pkt_t		ipp;
41827c478bd9Sstevel@tonic-gate 	ip6_t			*ip6h;
41837c478bd9Sstevel@tonic-gate 	ip6i_t			*ip6i;
41847c478bd9Sstevel@tonic-gate 	mblk_t			*mp1;
41857c478bd9Sstevel@tonic-gate 	mblk_t			*options_mp = NULL;
418619a30e1aSrshoaib 	ip_pktinfo_t		*pinfo = NULL;
41877c478bd9Sstevel@tonic-gate 	cred_t			*cr = NULL;
41887c478bd9Sstevel@tonic-gate 	pid_t			cpid;
4189fc80c0dfSnordmark 	uint32_t		udp_ip_rcv_options_len;
4190fc80c0dfSnordmark 	udp_bits_t		udp_bits;
419145916cd2Sjpk 	cred_t			*rcr = connp->conn_cred;
4192f4b3ec61Sdh 	udp_stack_t *us;
41937c478bd9Sstevel@tonic-gate 
4194fc80c0dfSnordmark 	ASSERT(connp->conn_flags & IPCL_UDPCONN);
41957c478bd9Sstevel@tonic-gate 
4196ff550d0eSmasputra 	udp = connp->conn_udp;
4197f4b3ec61Sdh 	us = udp->udp_us;
41987c478bd9Sstevel@tonic-gate 	rptr = mp->b_rptr;
4199ff550d0eSmasputra 	ASSERT(DB_TYPE(mp) == M_DATA || DB_TYPE(mp) == M_CTL);
4200ff550d0eSmasputra 	ASSERT(OK_32PTR(rptr));
42017c478bd9Sstevel@tonic-gate 
4202ff550d0eSmasputra 	/*
4203ff550d0eSmasputra 	 * IP should have prepended the options data in an M_CTL
4204ff550d0eSmasputra 	 * Check M_CTL "type" to make sure are not here bcos of
4205ff550d0eSmasputra 	 * a valid ICMP message
4206ff550d0eSmasputra 	 */
4207ff550d0eSmasputra 	if (DB_TYPE(mp) == M_CTL) {
420819a30e1aSrshoaib 		if (MBLKL(mp) == sizeof (ip_pktinfo_t) &&
420919a30e1aSrshoaib 		    ((ip_pktinfo_t *)mp->b_rptr)->ip_pkt_ulp_type ==
4210ff550d0eSmasputra 		    IN_PKTINFO) {
42117c478bd9Sstevel@tonic-gate 			/*
421219a30e1aSrshoaib 			 * IP_RECVIF or IP_RECVSLLA or IPF_RECVADDR information
4213fc80c0dfSnordmark 			 * has been prepended to the packet by IP. We need to
4214ff550d0eSmasputra 			 * extract the mblk and adjust the rptr
42157c478bd9Sstevel@tonic-gate 			 */
421619a30e1aSrshoaib 			pinfo = (ip_pktinfo_t *)mp->b_rptr;
4217ff550d0eSmasputra 			options_mp = mp;
4218ff550d0eSmasputra 			mp = mp->b_cont;
4219ff550d0eSmasputra 			rptr = mp->b_rptr;
4220f4b3ec61Sdh 			UDP_STAT(us, udp_in_pktinfo);
4221ff550d0eSmasputra 		} else {
4222ff550d0eSmasputra 			/*
4223ff550d0eSmasputra 			 * ICMP messages.
4224ff550d0eSmasputra 			 */
4225fc80c0dfSnordmark 			udp_icmp_error(connp->conn_rq, mp);
4226ff550d0eSmasputra 			return;
42277c478bd9Sstevel@tonic-gate 		}
42287c478bd9Sstevel@tonic-gate 	}
42297c478bd9Sstevel@tonic-gate 
4230ff550d0eSmasputra 	mp_len = msgdsize(mp);
42317c478bd9Sstevel@tonic-gate 	/*
42327c478bd9Sstevel@tonic-gate 	 * This is the inbound data path.
42337c478bd9Sstevel@tonic-gate 	 * First, we check to make sure the IP version number is correct,
42347c478bd9Sstevel@tonic-gate 	 * and then pull the IP and UDP headers into the first mblk.
42357c478bd9Sstevel@tonic-gate 	 */
42367c478bd9Sstevel@tonic-gate 
42377c478bd9Sstevel@tonic-gate 	/* Initialize regardless if ipversion is IPv4 or IPv6 */
42387c478bd9Sstevel@tonic-gate 	ipp.ipp_fields = 0;
42397c478bd9Sstevel@tonic-gate 
42407c478bd9Sstevel@tonic-gate 	ipversion = IPH_HDR_VERSION(rptr);
4241fc80c0dfSnordmark 
4242fc80c0dfSnordmark 	rw_enter(&udp->udp_rwlock, RW_READER);
4243fc80c0dfSnordmark 	udp_ip_rcv_options_len = udp->udp_ip_rcv_options_len;
4244fc80c0dfSnordmark 	udp_bits = udp->udp_bits;
4245fc80c0dfSnordmark 	rw_exit(&udp->udp_rwlock);
4246fc80c0dfSnordmark 
42477c478bd9Sstevel@tonic-gate 	switch (ipversion) {
42487c478bd9Sstevel@tonic-gate 	case IPV4_VERSION:
4249ff550d0eSmasputra 		ASSERT(MBLKL(mp) >= sizeof (ipha_t));
4250ff550d0eSmasputra 		ASSERT(((ipha_t *)rptr)->ipha_protocol == IPPROTO_UDP);
42517c478bd9Sstevel@tonic-gate 		hdr_length = IPH_HDR_LENGTH(rptr) + UDPH_SIZE;
4252fc80c0dfSnordmark 		opt_len = hdr_length - (IP_SIMPLE_HDR_LENGTH + UDPH_SIZE);
4253fc80c0dfSnordmark 		if ((opt_len > 0 || udp_ip_rcv_options_len > 0) &&
4254fc80c0dfSnordmark 		    udp->udp_family == AF_INET) {
42557c478bd9Sstevel@tonic-gate 			/*
4256fc80c0dfSnordmark 			 * Record/update udp_ip_rcv_options with the lock
4257fc80c0dfSnordmark 			 * held. Not needed for AF_INET6 sockets
42587c478bd9Sstevel@tonic-gate 			 * since they don't support a getsockopt of IP_OPTIONS.
42597c478bd9Sstevel@tonic-gate 			 */
4260fc80c0dfSnordmark 			rw_enter(&udp->udp_rwlock, RW_WRITER);
4261fc80c0dfSnordmark 			udp_save_ip_rcv_opt(udp, rptr + IP_SIMPLE_HDR_LENGTH,
4262fc80c0dfSnordmark 			    opt_len);
4263fc80c0dfSnordmark 			rw_exit(&udp->udp_rwlock);
42647c478bd9Sstevel@tonic-gate 		}
4265fc80c0dfSnordmark 		/* Handle IPV6_RECVPKTINFO even for IPv4 packet. */
4266ff550d0eSmasputra 		if ((udp->udp_family == AF_INET6) && (pinfo != NULL) &&
426719a30e1aSrshoaib 		    udp->udp_ip_recvpktinfo) {
426819a30e1aSrshoaib 			if (pinfo->ip_pkt_flags & IPF_RECVIF) {
42697c478bd9Sstevel@tonic-gate 				ipp.ipp_fields |= IPPF_IFINDEX;
427019a30e1aSrshoaib 				ipp.ipp_ifindex = pinfo->ip_pkt_ifindex;
42717c478bd9Sstevel@tonic-gate 			}
42727c478bd9Sstevel@tonic-gate 		}
42737c478bd9Sstevel@tonic-gate 		break;
42747c478bd9Sstevel@tonic-gate 	case IPV6_VERSION:
42757c478bd9Sstevel@tonic-gate 		/*
42767c478bd9Sstevel@tonic-gate 		 * IPv6 packets can only be received by applications
42777c478bd9Sstevel@tonic-gate 		 * that are prepared to receive IPv6 addresses.
42787c478bd9Sstevel@tonic-gate 		 * The IP fanout must ensure this.
42797c478bd9Sstevel@tonic-gate 		 */
42807c478bd9Sstevel@tonic-gate 		ASSERT(udp->udp_family == AF_INET6);
42817c478bd9Sstevel@tonic-gate 
42827c478bd9Sstevel@tonic-gate 		ip6h = (ip6_t *)rptr;
4283ff550d0eSmasputra 		ASSERT((uchar_t *)&ip6h[1] <= mp->b_wptr);
42847c478bd9Sstevel@tonic-gate 
42857c478bd9Sstevel@tonic-gate 		if (ip6h->ip6_nxt != IPPROTO_UDP) {
42867c478bd9Sstevel@tonic-gate 			uint8_t nexthdrp;
42877c478bd9Sstevel@tonic-gate 			/* Look for ifindex information */
42887c478bd9Sstevel@tonic-gate 			if (ip6h->ip6_nxt == IPPROTO_RAW) {
42897c478bd9Sstevel@tonic-gate 				ip6i = (ip6i_t *)ip6h;
42907c478bd9Sstevel@tonic-gate 				if ((uchar_t *)&ip6i[1] > mp->b_wptr)
42917c478bd9Sstevel@tonic-gate 					goto tossit;
42927c478bd9Sstevel@tonic-gate 
42937c478bd9Sstevel@tonic-gate 				if (ip6i->ip6i_flags & IP6I_IFINDEX) {
42947c478bd9Sstevel@tonic-gate 					ASSERT(ip6i->ip6i_ifindex != 0);
42957c478bd9Sstevel@tonic-gate 					ipp.ipp_fields |= IPPF_IFINDEX;
42967c478bd9Sstevel@tonic-gate 					ipp.ipp_ifindex = ip6i->ip6i_ifindex;
42977c478bd9Sstevel@tonic-gate 				}
42987c478bd9Sstevel@tonic-gate 				rptr = (uchar_t *)&ip6i[1];
42997c478bd9Sstevel@tonic-gate 				mp->b_rptr = rptr;
43007c478bd9Sstevel@tonic-gate 				if (rptr == mp->b_wptr) {
43017c478bd9Sstevel@tonic-gate 					mp1 = mp->b_cont;
43027c478bd9Sstevel@tonic-gate 					freeb(mp);
43037c478bd9Sstevel@tonic-gate 					mp = mp1;
43047c478bd9Sstevel@tonic-gate 					rptr = mp->b_rptr;
43057c478bd9Sstevel@tonic-gate 				}
43067c478bd9Sstevel@tonic-gate 				if (MBLKL(mp) < (IPV6_HDR_LEN + UDPH_SIZE))
43077c478bd9Sstevel@tonic-gate 					goto tossit;
43087c478bd9Sstevel@tonic-gate 				ip6h = (ip6_t *)rptr;
4309ff550d0eSmasputra 				mp_len = msgdsize(mp);
43107c478bd9Sstevel@tonic-gate 			}
43117c478bd9Sstevel@tonic-gate 			/*
43127c478bd9Sstevel@tonic-gate 			 * Find any potentially interesting extension headers
43137c478bd9Sstevel@tonic-gate 			 * as well as the length of the IPv6 + extension
43147c478bd9Sstevel@tonic-gate 			 * headers.
43157c478bd9Sstevel@tonic-gate 			 */
43167c478bd9Sstevel@tonic-gate 			hdr_length = ip_find_hdr_v6(mp, ip6h, &ipp, &nexthdrp) +
43177c478bd9Sstevel@tonic-gate 			    UDPH_SIZE;
4318ff550d0eSmasputra 			ASSERT(nexthdrp == IPPROTO_UDP);
43197c478bd9Sstevel@tonic-gate 		} else {
43207c478bd9Sstevel@tonic-gate 			hdr_length = IPV6_HDR_LEN + UDPH_SIZE;
43217c478bd9Sstevel@tonic-gate 			ip6i = NULL;
43227c478bd9Sstevel@tonic-gate 		}
43237c478bd9Sstevel@tonic-gate 		break;
43247c478bd9Sstevel@tonic-gate 	default:
4325ff550d0eSmasputra 		ASSERT(0);
43267c478bd9Sstevel@tonic-gate 	}
43277c478bd9Sstevel@tonic-gate 
43287c478bd9Sstevel@tonic-gate 	/*
43297c478bd9Sstevel@tonic-gate 	 * IP inspected the UDP header thus all of it must be in the mblk.
43307c478bd9Sstevel@tonic-gate 	 * UDP length check is performed for IPv6 packets and IPv4 packets
4331fc80c0dfSnordmark 	 * to check if the size of the packet as specified
43327c478bd9Sstevel@tonic-gate 	 * by the header is the same as the physical size of the packet.
4333fc80c0dfSnordmark 	 * FIXME? Didn't IP already check this?
43347c478bd9Sstevel@tonic-gate 	 */
43357c478bd9Sstevel@tonic-gate 	udpha = (udpha_t *)(rptr + (hdr_length - UDPH_SIZE));
43367c478bd9Sstevel@tonic-gate 	if ((MBLKL(mp) < hdr_length) ||
4337ff550d0eSmasputra 	    (mp_len != (ntohs(udpha->uha_length) + hdr_length - UDPH_SIZE))) {
43387c478bd9Sstevel@tonic-gate 		goto tossit;
43397c478bd9Sstevel@tonic-gate 	}
43407c478bd9Sstevel@tonic-gate 
4341fc80c0dfSnordmark 
4342fc80c0dfSnordmark 	/* Walk past the headers unless IP_RECVHDR was set. */
4343fc80c0dfSnordmark 	if (!udp_bits.udpb_rcvhdr) {
43447c478bd9Sstevel@tonic-gate 		mp->b_rptr = rptr + hdr_length;
4345ff550d0eSmasputra 		mp_len -= hdr_length;
4346ff550d0eSmasputra 	}
43477c478bd9Sstevel@tonic-gate 
43487c478bd9Sstevel@tonic-gate 	/*
43497c478bd9Sstevel@tonic-gate 	 * This is the inbound data path.  Packets are passed upstream as
43507c478bd9Sstevel@tonic-gate 	 * T_UNITDATA_IND messages with full IP headers still attached.
43517c478bd9Sstevel@tonic-gate 	 */
43527c478bd9Sstevel@tonic-gate 	if (udp->udp_family == AF_INET) {
43537c478bd9Sstevel@tonic-gate 		sin_t *sin;
43547c478bd9Sstevel@tonic-gate 
43557c478bd9Sstevel@tonic-gate 		ASSERT(IPH_HDR_VERSION((ipha_t *)rptr) == IPV4_VERSION);
43567c478bd9Sstevel@tonic-gate 
43577c478bd9Sstevel@tonic-gate 		/*
4358fc80c0dfSnordmark 		 * Normally only send up the source address.
43597c478bd9Sstevel@tonic-gate 		 * If IP_RECVDSTADDR is set we include the destination IP
43607c478bd9Sstevel@tonic-gate 		 * address as an option. With IP_RECVOPTS we include all
4361fc80c0dfSnordmark 		 * the IP options.
43627c478bd9Sstevel@tonic-gate 		 */
43637c478bd9Sstevel@tonic-gate 		udi_size = sizeof (struct T_unitdata_ind) + sizeof (sin_t);
4364fc80c0dfSnordmark 		if (udp_bits.udpb_recvdstaddr) {
43657c478bd9Sstevel@tonic-gate 			udi_size += sizeof (struct T_opthdr) +
43667c478bd9Sstevel@tonic-gate 			    sizeof (struct in_addr);
4367f4b3ec61Sdh 			UDP_STAT(us, udp_in_recvdstaddr);
43687c478bd9Sstevel@tonic-gate 		}
43697c478bd9Sstevel@tonic-gate 
4370fc80c0dfSnordmark 		if (udp_bits.udpb_ip_recvpktinfo && (pinfo != NULL) &&
437119a30e1aSrshoaib 		    (pinfo->ip_pkt_flags & IPF_RECVADDR)) {
437219a30e1aSrshoaib 			udi_size += sizeof (struct T_opthdr) +
437319a30e1aSrshoaib 			    sizeof (struct in_pktinfo);
4374fc80c0dfSnordmark 			UDP_STAT(us, udp_ip_rcvpktinfo);
4375fc80c0dfSnordmark 		}
4376fc80c0dfSnordmark 
4377fc80c0dfSnordmark 		if ((udp_bits.udpb_recvopts) && opt_len > 0) {
4378fc80c0dfSnordmark 			udi_size += sizeof (struct T_opthdr) + opt_len;
4379fc80c0dfSnordmark 			UDP_STAT(us, udp_in_recvopts);
438019a30e1aSrshoaib 		}
438119a30e1aSrshoaib 
43827c478bd9Sstevel@tonic-gate 		/*
43837c478bd9Sstevel@tonic-gate 		 * If the IP_RECVSLLA or the IP_RECVIF is set then allocate
43847c478bd9Sstevel@tonic-gate 		 * space accordingly
43857c478bd9Sstevel@tonic-gate 		 */
4386fc80c0dfSnordmark 		if ((udp_bits.udpb_recvif) && (pinfo != NULL) &&
438719a30e1aSrshoaib 		    (pinfo->ip_pkt_flags & IPF_RECVIF)) {
4388ff550d0eSmasputra 			udi_size += sizeof (struct T_opthdr) + sizeof (uint_t);
4389f4b3ec61Sdh 			UDP_STAT(us, udp_in_recvif);
43907c478bd9Sstevel@tonic-gate 		}
43917c478bd9Sstevel@tonic-gate 
4392fc80c0dfSnordmark 		if ((udp_bits.udpb_recvslla) && (pinfo != NULL) &&
439319a30e1aSrshoaib 		    (pinfo->ip_pkt_flags & IPF_RECVSLLA)) {
43947c478bd9Sstevel@tonic-gate 			udi_size += sizeof (struct T_opthdr) +
4395ff550d0eSmasputra 			    sizeof (struct sockaddr_dl);
4396f4b3ec61Sdh 			UDP_STAT(us, udp_in_recvslla);
43977c478bd9Sstevel@tonic-gate 		}
43987c478bd9Sstevel@tonic-gate 
4399fc80c0dfSnordmark 		if ((udp_bits.udpb_recvucred) &&
4400fc80c0dfSnordmark 		    (cr = DB_CRED(mp)) != NULL) {
44017c478bd9Sstevel@tonic-gate 			udi_size += sizeof (struct T_opthdr) + ucredsize;
44027c478bd9Sstevel@tonic-gate 			cpid = DB_CPID(mp);
4403f4b3ec61Sdh 			UDP_STAT(us, udp_in_recvucred);
44047c478bd9Sstevel@tonic-gate 		}
4405d6287788Srshoaib 
4406*da14cebeSEric Cheng 		/* XXX FIXME: apply to AF_INET6 as well */
4407e4f35dbaSgt 		/*
4408e4f35dbaSgt 		 * If SO_TIMESTAMP is set allocate the appropriate sized
4409e4f35dbaSgt 		 * buffer. Since gethrestime() expects a pointer aligned
4410e4f35dbaSgt 		 * argument, we allocate space necessary for extra
4411e4f35dbaSgt 		 * alignment (even though it might not be used).
4412e4f35dbaSgt 		 */
4413fc80c0dfSnordmark 		if (udp_bits.udpb_timestamp) {
4414e4f35dbaSgt 			udi_size += sizeof (struct T_opthdr) +
4415e4f35dbaSgt 			    sizeof (timestruc_t) + _POINTER_ALIGNMENT;
4416f4b3ec61Sdh 			UDP_STAT(us, udp_in_timestamp);
4417e4f35dbaSgt 		}
4418d6287788Srshoaib 
4419d6287788Srshoaib 		/*
4420d6287788Srshoaib 		 * If IP_RECVTTL is set allocate the appropriate sized buffer
4421d6287788Srshoaib 		 */
4422fc80c0dfSnordmark 		if (udp_bits.udpb_recvttl) {
4423d6287788Srshoaib 			udi_size += sizeof (struct T_opthdr) + sizeof (uint8_t);
4424f4b3ec61Sdh 			UDP_STAT(us, udp_in_recvttl);
4425d6287788Srshoaib 		}
44267c478bd9Sstevel@tonic-gate 
44277c478bd9Sstevel@tonic-gate 		/* Allocate a message block for the T_UNITDATA_IND structure. */
44287c478bd9Sstevel@tonic-gate 		mp1 = allocb(udi_size, BPRI_MED);
44297c478bd9Sstevel@tonic-gate 		if (mp1 == NULL) {
44307c478bd9Sstevel@tonic-gate 			freemsg(mp);
44317c478bd9Sstevel@tonic-gate 			if (options_mp != NULL)
44327c478bd9Sstevel@tonic-gate 				freeb(options_mp);
4433fc80c0dfSnordmark 			BUMP_MIB(&us->us_udp_mib, udpInErrors);
44347c478bd9Sstevel@tonic-gate 			return;
44357c478bd9Sstevel@tonic-gate 		}
44367c478bd9Sstevel@tonic-gate 		mp1->b_cont = mp;
44377c478bd9Sstevel@tonic-gate 		mp = mp1;
44387c478bd9Sstevel@tonic-gate 		mp->b_datap->db_type = M_PROTO;
44397c478bd9Sstevel@tonic-gate 		tudi = (struct T_unitdata_ind *)mp->b_rptr;
44407c478bd9Sstevel@tonic-gate 		mp->b_wptr = (uchar_t *)tudi + udi_size;
44417c478bd9Sstevel@tonic-gate 		tudi->PRIM_type = T_UNITDATA_IND;
44427c478bd9Sstevel@tonic-gate 		tudi->SRC_length = sizeof (sin_t);
44437c478bd9Sstevel@tonic-gate 		tudi->SRC_offset = sizeof (struct T_unitdata_ind);
44447c478bd9Sstevel@tonic-gate 		tudi->OPT_offset = sizeof (struct T_unitdata_ind) +
44457c478bd9Sstevel@tonic-gate 		    sizeof (sin_t);
44467c478bd9Sstevel@tonic-gate 		udi_size -= (sizeof (struct T_unitdata_ind) + sizeof (sin_t));
44477c478bd9Sstevel@tonic-gate 		tudi->OPT_length = udi_size;
44487c478bd9Sstevel@tonic-gate 		sin = (sin_t *)&tudi[1];
44497c478bd9Sstevel@tonic-gate 		sin->sin_addr.s_addr = ((ipha_t *)rptr)->ipha_src;
44507c478bd9Sstevel@tonic-gate 		sin->sin_port =	udpha->uha_src_port;
44517c478bd9Sstevel@tonic-gate 		sin->sin_family = udp->udp_family;
44527c478bd9Sstevel@tonic-gate 		*(uint32_t *)&sin->sin_zero[0] = 0;
44537c478bd9Sstevel@tonic-gate 		*(uint32_t *)&sin->sin_zero[4] = 0;
44547c478bd9Sstevel@tonic-gate 
44557c478bd9Sstevel@tonic-gate 		/*
44567c478bd9Sstevel@tonic-gate 		 * Add options if IP_RECVDSTADDR, IP_RECVIF, IP_RECVSLLA or
44577c478bd9Sstevel@tonic-gate 		 * IP_RECVTTL has been set.
44587c478bd9Sstevel@tonic-gate 		 */
44597c478bd9Sstevel@tonic-gate 		if (udi_size != 0) {
44607c478bd9Sstevel@tonic-gate 			/*
44617c478bd9Sstevel@tonic-gate 			 * Copy in destination address before options to avoid
44627c478bd9Sstevel@tonic-gate 			 * any padding issues.
44637c478bd9Sstevel@tonic-gate 			 */
44647c478bd9Sstevel@tonic-gate 			char *dstopt;
44657c478bd9Sstevel@tonic-gate 
44667c478bd9Sstevel@tonic-gate 			dstopt = (char *)&sin[1];
4467fc80c0dfSnordmark 			if (udp_bits.udpb_recvdstaddr) {
44687c478bd9Sstevel@tonic-gate 				struct T_opthdr *toh;
44697c478bd9Sstevel@tonic-gate 				ipaddr_t *dstptr;
44707c478bd9Sstevel@tonic-gate 
44717c478bd9Sstevel@tonic-gate 				toh = (struct T_opthdr *)dstopt;
44727c478bd9Sstevel@tonic-gate 				toh->level = IPPROTO_IP;
44737c478bd9Sstevel@tonic-gate 				toh->name = IP_RECVDSTADDR;
44747c478bd9Sstevel@tonic-gate 				toh->len = sizeof (struct T_opthdr) +
44757c478bd9Sstevel@tonic-gate 				    sizeof (ipaddr_t);
44767c478bd9Sstevel@tonic-gate 				toh->status = 0;
44777c478bd9Sstevel@tonic-gate 				dstopt += sizeof (struct T_opthdr);
44787c478bd9Sstevel@tonic-gate 				dstptr = (ipaddr_t *)dstopt;
44797c478bd9Sstevel@tonic-gate 				*dstptr = ((ipha_t *)rptr)->ipha_dst;
4480fc80c0dfSnordmark 				dstopt += sizeof (ipaddr_t);
4481fc80c0dfSnordmark 				udi_size -= toh->len;
4482fc80c0dfSnordmark 			}
4483fc80c0dfSnordmark 
4484fc80c0dfSnordmark 			if (udp_bits.udpb_recvopts && opt_len > 0) {
4485fc80c0dfSnordmark 				struct T_opthdr *toh;
4486fc80c0dfSnordmark 
4487fc80c0dfSnordmark 				toh = (struct T_opthdr *)dstopt;
4488fc80c0dfSnordmark 				toh->level = IPPROTO_IP;
4489fc80c0dfSnordmark 				toh->name = IP_RECVOPTS;
4490fc80c0dfSnordmark 				toh->len = sizeof (struct T_opthdr) + opt_len;
4491fc80c0dfSnordmark 				toh->status = 0;
4492fc80c0dfSnordmark 				dstopt += sizeof (struct T_opthdr);
4493fc80c0dfSnordmark 				bcopy(rptr + IP_SIMPLE_HDR_LENGTH, dstopt,
4494fc80c0dfSnordmark 				    opt_len);
4495fc80c0dfSnordmark 				dstopt += opt_len;
44967c478bd9Sstevel@tonic-gate 				udi_size -= toh->len;
44977c478bd9Sstevel@tonic-gate 			}
44987c478bd9Sstevel@tonic-gate 
4499fc80c0dfSnordmark 			if ((udp_bits.udpb_ip_recvpktinfo) && (pinfo != NULL) &&
450019a30e1aSrshoaib 			    (pinfo->ip_pkt_flags & IPF_RECVADDR)) {
450119a30e1aSrshoaib 				struct T_opthdr *toh;
450219a30e1aSrshoaib 				struct in_pktinfo *pktinfop;
450319a30e1aSrshoaib 
450419a30e1aSrshoaib 				toh = (struct T_opthdr *)dstopt;
450519a30e1aSrshoaib 				toh->level = IPPROTO_IP;
450619a30e1aSrshoaib 				toh->name = IP_PKTINFO;
450719a30e1aSrshoaib 				toh->len = sizeof (struct T_opthdr) +
450819a30e1aSrshoaib 				    sizeof (*pktinfop);
450919a30e1aSrshoaib 				toh->status = 0;
451019a30e1aSrshoaib 				dstopt += sizeof (struct T_opthdr);
451119a30e1aSrshoaib 				pktinfop = (struct in_pktinfo *)dstopt;
451219a30e1aSrshoaib 				pktinfop->ipi_ifindex = pinfo->ip_pkt_ifindex;
451319a30e1aSrshoaib 				pktinfop->ipi_spec_dst =
451419a30e1aSrshoaib 				    pinfo->ip_pkt_match_addr;
451519a30e1aSrshoaib 				pktinfop->ipi_addr.s_addr =
451619a30e1aSrshoaib 				    ((ipha_t *)rptr)->ipha_dst;
451719a30e1aSrshoaib 
451819a30e1aSrshoaib 				dstopt += sizeof (struct in_pktinfo);
451919a30e1aSrshoaib 				udi_size -= toh->len;
452019a30e1aSrshoaib 			}
452119a30e1aSrshoaib 
4522fc80c0dfSnordmark 			if ((udp_bits.udpb_recvslla) && (pinfo != NULL) &&
452319a30e1aSrshoaib 			    (pinfo->ip_pkt_flags & IPF_RECVSLLA)) {
45247c478bd9Sstevel@tonic-gate 
45257c478bd9Sstevel@tonic-gate 				struct T_opthdr *toh;
45267c478bd9Sstevel@tonic-gate 				struct sockaddr_dl	*dstptr;
45277c478bd9Sstevel@tonic-gate 
45287c478bd9Sstevel@tonic-gate 				toh = (struct T_opthdr *)dstopt;
45297c478bd9Sstevel@tonic-gate 				toh->level = IPPROTO_IP;
45307c478bd9Sstevel@tonic-gate 				toh->name = IP_RECVSLLA;
45317c478bd9Sstevel@tonic-gate 				toh->len = sizeof (struct T_opthdr) +
4532437220cdSdanmcd 				    sizeof (struct sockaddr_dl);
45337c478bd9Sstevel@tonic-gate 				toh->status = 0;
45347c478bd9Sstevel@tonic-gate 				dstopt += sizeof (struct T_opthdr);
45357c478bd9Sstevel@tonic-gate 				dstptr = (struct sockaddr_dl *)dstopt;
453619a30e1aSrshoaib 				bcopy(&pinfo->ip_pkt_slla, dstptr,
45377c478bd9Sstevel@tonic-gate 				    sizeof (struct sockaddr_dl));
4538fc80c0dfSnordmark 				dstopt += sizeof (struct sockaddr_dl);
45397c478bd9Sstevel@tonic-gate 				udi_size -= toh->len;
45407c478bd9Sstevel@tonic-gate 			}
45417c478bd9Sstevel@tonic-gate 
4542fc80c0dfSnordmark 			if ((udp_bits.udpb_recvif) && (pinfo != NULL) &&
454319a30e1aSrshoaib 			    (pinfo->ip_pkt_flags & IPF_RECVIF)) {
45447c478bd9Sstevel@tonic-gate 
45457c478bd9Sstevel@tonic-gate 				struct T_opthdr *toh;
45467c478bd9Sstevel@tonic-gate 				uint_t		*dstptr;
45477c478bd9Sstevel@tonic-gate 
45487c478bd9Sstevel@tonic-gate 				toh = (struct T_opthdr *)dstopt;
45497c478bd9Sstevel@tonic-gate 				toh->level = IPPROTO_IP;
45507c478bd9Sstevel@tonic-gate 				toh->name = IP_RECVIF;
45517c478bd9Sstevel@tonic-gate 				toh->len = sizeof (struct T_opthdr) +
4552437220cdSdanmcd 				    sizeof (uint_t);
45537c478bd9Sstevel@tonic-gate 				toh->status = 0;
45547c478bd9Sstevel@tonic-gate 				dstopt += sizeof (struct T_opthdr);
45557c478bd9Sstevel@tonic-gate 				dstptr = (uint_t *)dstopt;
455619a30e1aSrshoaib 				*dstptr = pinfo->ip_pkt_ifindex;
4557fc80c0dfSnordmark 				dstopt += sizeof (uint_t);
45587c478bd9Sstevel@tonic-gate 				udi_size -= toh->len;
45597c478bd9Sstevel@tonic-gate 			}
45607c478bd9Sstevel@tonic-gate 
45617c478bd9Sstevel@tonic-gate 			if (cr != NULL) {
45627c478bd9Sstevel@tonic-gate 				struct T_opthdr *toh;
45637c478bd9Sstevel@tonic-gate 
45647c478bd9Sstevel@tonic-gate 				toh = (struct T_opthdr *)dstopt;
45657c478bd9Sstevel@tonic-gate 				toh->level = SOL_SOCKET;
45667c478bd9Sstevel@tonic-gate 				toh->name = SCM_UCRED;
45677c478bd9Sstevel@tonic-gate 				toh->len = sizeof (struct T_opthdr) + ucredsize;
45687c478bd9Sstevel@tonic-gate 				toh->status = 0;
4569fc80c0dfSnordmark 				dstopt += sizeof (struct T_opthdr);
4570fc80c0dfSnordmark 				(void) cred2ucred(cr, cpid, dstopt, rcr);
4571fc80c0dfSnordmark 				dstopt += ucredsize;
45727c478bd9Sstevel@tonic-gate 				udi_size -= toh->len;
45737c478bd9Sstevel@tonic-gate 			}
45747c478bd9Sstevel@tonic-gate 
4575fc80c0dfSnordmark 			if (udp_bits.udpb_timestamp) {
4576e4f35dbaSgt 				struct	T_opthdr *toh;
4577e4f35dbaSgt 
4578e4f35dbaSgt 				toh = (struct T_opthdr *)dstopt;
4579e4f35dbaSgt 				toh->level = SOL_SOCKET;
4580e4f35dbaSgt 				toh->name = SCM_TIMESTAMP;
4581e4f35dbaSgt 				toh->len = sizeof (struct T_opthdr) +
4582e4f35dbaSgt 				    sizeof (timestruc_t) + _POINTER_ALIGNMENT;
4583e4f35dbaSgt 				toh->status = 0;
4584e4f35dbaSgt 				dstopt += sizeof (struct T_opthdr);
4585e4f35dbaSgt 				/* Align for gethrestime() */
4586e4f35dbaSgt 				dstopt = (char *)P2ROUNDUP((intptr_t)dstopt,
4587e4f35dbaSgt 				    sizeof (intptr_t));
4588e4f35dbaSgt 				gethrestime((timestruc_t *)dstopt);
4589d6287788Srshoaib 				dstopt = (char *)toh + toh->len;
4590d6287788Srshoaib 				udi_size -= toh->len;
4591d6287788Srshoaib 			}
4592d6287788Srshoaib 
4593d6287788Srshoaib 			/*
4594d6287788Srshoaib 			 * CAUTION:
4595d6287788Srshoaib 			 * Due to aligment issues
4596d6287788Srshoaib 			 * Processing of IP_RECVTTL option
4597d6287788Srshoaib 			 * should always be the last. Adding
4598d6287788Srshoaib 			 * any option processing after this will
4599d6287788Srshoaib 			 * cause alignment panic.
4600d6287788Srshoaib 			 */
4601fc80c0dfSnordmark 			if (udp_bits.udpb_recvttl) {
4602d6287788Srshoaib 				struct	T_opthdr *toh;
4603d6287788Srshoaib 				uint8_t	*dstptr;
4604d6287788Srshoaib 
4605d6287788Srshoaib 				toh = (struct T_opthdr *)dstopt;
4606d6287788Srshoaib 				toh->level = IPPROTO_IP;
4607d6287788Srshoaib 				toh->name = IP_RECVTTL;
4608d6287788Srshoaib 				toh->len = sizeof (struct T_opthdr) +
4609d6287788Srshoaib 				    sizeof (uint8_t);
4610d6287788Srshoaib 				toh->status = 0;
4611d6287788Srshoaib 				dstopt += sizeof (struct T_opthdr);
4612d6287788Srshoaib 				dstptr = (uint8_t *)dstopt;
4613d6287788Srshoaib 				*dstptr = ((ipha_t *)rptr)->ipha_ttl;
4614fc80c0dfSnordmark 				dstopt += sizeof (uint8_t);
4615e4f35dbaSgt 				udi_size -= toh->len;
4616e4f35dbaSgt 			}
46177c478bd9Sstevel@tonic-gate 
46187c478bd9Sstevel@tonic-gate 			/* Consumed all of allocated space */
46197c478bd9Sstevel@tonic-gate 			ASSERT(udi_size == 0);
46207c478bd9Sstevel@tonic-gate 		}
46217c478bd9Sstevel@tonic-gate 	} else {
46227c478bd9Sstevel@tonic-gate 		sin6_t *sin6;
46237c478bd9Sstevel@tonic-gate 
46247c478bd9Sstevel@tonic-gate 		/*
46257c478bd9Sstevel@tonic-gate 		 * Handle both IPv4 and IPv6 packets for IPv6 sockets.
46267c478bd9Sstevel@tonic-gate 		 *
46277c478bd9Sstevel@tonic-gate 		 * Normally we only send up the address. If receiving of any
46287c478bd9Sstevel@tonic-gate 		 * optional receive side information is enabled, we also send
46297c478bd9Sstevel@tonic-gate 		 * that up as options.
46307c478bd9Sstevel@tonic-gate 		 */
46317c478bd9Sstevel@tonic-gate 		udi_size = sizeof (struct T_unitdata_ind) + sizeof (sin6_t);
46327c478bd9Sstevel@tonic-gate 
46337c478bd9Sstevel@tonic-gate 		if (ipp.ipp_fields & (IPPF_HOPOPTS|IPPF_DSTOPTS|IPPF_RTDSTOPTS|
46347c478bd9Sstevel@tonic-gate 		    IPPF_RTHDR|IPPF_IFINDEX)) {
4635fc80c0dfSnordmark 			if ((udp_bits.udpb_ipv6_recvhopopts) &&
46367c478bd9Sstevel@tonic-gate 			    (ipp.ipp_fields & IPPF_HOPOPTS)) {
463745916cd2Sjpk 				size_t hlen;
463845916cd2Sjpk 
4639f4b3ec61Sdh 				UDP_STAT(us, udp_in_recvhopopts);
464045916cd2Sjpk 				hlen = copy_hop_opts(&ipp, NULL);
464145916cd2Sjpk 				if (hlen == 0)
464245916cd2Sjpk 					ipp.ipp_fields &= ~IPPF_HOPOPTS;
464345916cd2Sjpk 				udi_size += hlen;
46447c478bd9Sstevel@tonic-gate 			}
4645fc80c0dfSnordmark 			if (((udp_bits.udpb_ipv6_recvdstopts) ||
4646fc80c0dfSnordmark 			    udp_bits.udpb_old_ipv6_recvdstopts) &&
46477c478bd9Sstevel@tonic-gate 			    (ipp.ipp_fields & IPPF_DSTOPTS)) {
46487c478bd9Sstevel@tonic-gate 				udi_size += sizeof (struct T_opthdr) +
46497c478bd9Sstevel@tonic-gate 				    ipp.ipp_dstoptslen;
4650f4b3ec61Sdh 				UDP_STAT(us, udp_in_recvdstopts);
46517c478bd9Sstevel@tonic-gate 			}
4652fc80c0dfSnordmark 			if ((((udp_bits.udpb_ipv6_recvdstopts) &&
4653fc80c0dfSnordmark 			    udp_bits.udpb_ipv6_recvrthdr &&
46547c478bd9Sstevel@tonic-gate 			    (ipp.ipp_fields & IPPF_RTHDR)) ||
4655fc80c0dfSnordmark 			    (udp_bits.udpb_ipv6_recvrthdrdstopts)) &&
46567c478bd9Sstevel@tonic-gate 			    (ipp.ipp_fields & IPPF_RTDSTOPTS)) {
46577c478bd9Sstevel@tonic-gate 				udi_size += sizeof (struct T_opthdr) +
46587c478bd9Sstevel@tonic-gate 				    ipp.ipp_rtdstoptslen;
4659f4b3ec61Sdh 				UDP_STAT(us, udp_in_recvrtdstopts);
46607c478bd9Sstevel@tonic-gate 			}
4661fc80c0dfSnordmark 			if ((udp_bits.udpb_ipv6_recvrthdr) &&
46627c478bd9Sstevel@tonic-gate 			    (ipp.ipp_fields & IPPF_RTHDR)) {
46637c478bd9Sstevel@tonic-gate 				udi_size += sizeof (struct T_opthdr) +
46647c478bd9Sstevel@tonic-gate 				    ipp.ipp_rthdrlen;
4665f4b3ec61Sdh 				UDP_STAT(us, udp_in_recvrthdr);
46667c478bd9Sstevel@tonic-gate 			}
4667fc80c0dfSnordmark 			if ((udp_bits.udpb_ip_recvpktinfo) &&
46687c478bd9Sstevel@tonic-gate 			    (ipp.ipp_fields & IPPF_IFINDEX)) {
46697c478bd9Sstevel@tonic-gate 				udi_size += sizeof (struct T_opthdr) +
46707c478bd9Sstevel@tonic-gate 				    sizeof (struct in6_pktinfo);
4671f4b3ec61Sdh 				UDP_STAT(us, udp_in_recvpktinfo);
46727c478bd9Sstevel@tonic-gate 			}
46737c478bd9Sstevel@tonic-gate 
46747c478bd9Sstevel@tonic-gate 		}
4675fc80c0dfSnordmark 		if ((udp_bits.udpb_recvucred) &&
4676fc80c0dfSnordmark 		    (cr = DB_CRED(mp)) != NULL) {
46777c478bd9Sstevel@tonic-gate 			udi_size += sizeof (struct T_opthdr) + ucredsize;
46787c478bd9Sstevel@tonic-gate 			cpid = DB_CPID(mp);
4679f4b3ec61Sdh 			UDP_STAT(us, udp_in_recvucred);
46807c478bd9Sstevel@tonic-gate 		}
46817c478bd9Sstevel@tonic-gate 
4682ae43f94fSnordmark 		/*
4683ae43f94fSnordmark 		 * If SO_TIMESTAMP is set allocate the appropriate sized
4684ae43f94fSnordmark 		 * buffer. Since gethrestime() expects a pointer aligned
4685ae43f94fSnordmark 		 * argument, we allocate space necessary for extra
4686ae43f94fSnordmark 		 * alignment (even though it might not be used).
4687ae43f94fSnordmark 		 */
4688ae43f94fSnordmark 		if (udp_bits.udpb_timestamp) {
4689ae43f94fSnordmark 			udi_size += sizeof (struct T_opthdr) +
4690ae43f94fSnordmark 			    sizeof (timestruc_t) + _POINTER_ALIGNMENT;
4691ae43f94fSnordmark 			UDP_STAT(us, udp_in_timestamp);
4692ae43f94fSnordmark 		}
4693ae43f94fSnordmark 
4694fc80c0dfSnordmark 		if (udp_bits.udpb_ipv6_recvhoplimit) {
46957c478bd9Sstevel@tonic-gate 			udi_size += sizeof (struct T_opthdr) + sizeof (int);
4696f4b3ec61Sdh 			UDP_STAT(us, udp_in_recvhoplimit);
4697ff550d0eSmasputra 		}
46987c478bd9Sstevel@tonic-gate 
4699fc80c0dfSnordmark 		if (udp_bits.udpb_ipv6_recvtclass) {
47007c478bd9Sstevel@tonic-gate 			udi_size += sizeof (struct T_opthdr) + sizeof (int);
4701f4b3ec61Sdh 			UDP_STAT(us, udp_in_recvtclass);
4702ff550d0eSmasputra 		}
47037c478bd9Sstevel@tonic-gate 
47047c478bd9Sstevel@tonic-gate 		mp1 = allocb(udi_size, BPRI_MED);
47057c478bd9Sstevel@tonic-gate 		if (mp1 == NULL) {
47067c478bd9Sstevel@tonic-gate 			freemsg(mp);
47077c478bd9Sstevel@tonic-gate 			if (options_mp != NULL)
47087c478bd9Sstevel@tonic-gate 				freeb(options_mp);
4709fc80c0dfSnordmark 			BUMP_MIB(&us->us_udp_mib, udpInErrors);
47107c478bd9Sstevel@tonic-gate 			return;
47117c478bd9Sstevel@tonic-gate 		}
47127c478bd9Sstevel@tonic-gate 		mp1->b_cont = mp;
47137c478bd9Sstevel@tonic-gate 		mp = mp1;
47147c478bd9Sstevel@tonic-gate 		mp->b_datap->db_type = M_PROTO;
47157c478bd9Sstevel@tonic-gate 		tudi = (struct T_unitdata_ind *)mp->b_rptr;
47167c478bd9Sstevel@tonic-gate 		mp->b_wptr = (uchar_t *)tudi + udi_size;
47177c478bd9Sstevel@tonic-gate 		tudi->PRIM_type = T_UNITDATA_IND;
47187c478bd9Sstevel@tonic-gate 		tudi->SRC_length = sizeof (sin6_t);
47197c478bd9Sstevel@tonic-gate 		tudi->SRC_offset = sizeof (struct T_unitdata_ind);
47207c478bd9Sstevel@tonic-gate 		tudi->OPT_offset = sizeof (struct T_unitdata_ind) +
47217c478bd9Sstevel@tonic-gate 		    sizeof (sin6_t);
47227c478bd9Sstevel@tonic-gate 		udi_size -= (sizeof (struct T_unitdata_ind) + sizeof (sin6_t));
47237c478bd9Sstevel@tonic-gate 		tudi->OPT_length = udi_size;
47247c478bd9Sstevel@tonic-gate 		sin6 = (sin6_t *)&tudi[1];
47257c478bd9Sstevel@tonic-gate 		if (ipversion == IPV4_VERSION) {
47267c478bd9Sstevel@tonic-gate 			in6_addr_t v6dst;
47277c478bd9Sstevel@tonic-gate 
47287c478bd9Sstevel@tonic-gate 			IN6_IPADDR_TO_V4MAPPED(((ipha_t *)rptr)->ipha_src,
47297c478bd9Sstevel@tonic-gate 			    &sin6->sin6_addr);
47307c478bd9Sstevel@tonic-gate 			IN6_IPADDR_TO_V4MAPPED(((ipha_t *)rptr)->ipha_dst,
47317c478bd9Sstevel@tonic-gate 			    &v6dst);
47327c478bd9Sstevel@tonic-gate 			sin6->sin6_flowinfo = 0;
47337c478bd9Sstevel@tonic-gate 			sin6->sin6_scope_id = 0;
47347c478bd9Sstevel@tonic-gate 			sin6->__sin6_src_id = ip_srcid_find_addr(&v6dst,
4735f4b3ec61Sdh 			    connp->conn_zoneid, us->us_netstack);
47367c478bd9Sstevel@tonic-gate 		} else {
47377c478bd9Sstevel@tonic-gate 			sin6->sin6_addr = ip6h->ip6_src;
47387c478bd9Sstevel@tonic-gate 			/* No sin6_flowinfo per API */
47397c478bd9Sstevel@tonic-gate 			sin6->sin6_flowinfo = 0;
47407c478bd9Sstevel@tonic-gate 			/* For link-scope source pass up scope id */
47417c478bd9Sstevel@tonic-gate 			if ((ipp.ipp_fields & IPPF_IFINDEX) &&
47427c478bd9Sstevel@tonic-gate 			    IN6_IS_ADDR_LINKSCOPE(&ip6h->ip6_src))
47437c478bd9Sstevel@tonic-gate 				sin6->sin6_scope_id = ipp.ipp_ifindex;
47447c478bd9Sstevel@tonic-gate 			else
47457c478bd9Sstevel@tonic-gate 				sin6->sin6_scope_id = 0;
4746ff550d0eSmasputra 			sin6->__sin6_src_id = ip_srcid_find_addr(
4747f4b3ec61Sdh 			    &ip6h->ip6_dst, connp->conn_zoneid,
4748f4b3ec61Sdh 			    us->us_netstack);
47497c478bd9Sstevel@tonic-gate 		}
47507c478bd9Sstevel@tonic-gate 		sin6->sin6_port = udpha->uha_src_port;
47517c478bd9Sstevel@tonic-gate 		sin6->sin6_family = udp->udp_family;
47527c478bd9Sstevel@tonic-gate 
47537c478bd9Sstevel@tonic-gate 		if (udi_size != 0) {
47547c478bd9Sstevel@tonic-gate 			uchar_t *dstopt;
47557c478bd9Sstevel@tonic-gate 
47567c478bd9Sstevel@tonic-gate 			dstopt = (uchar_t *)&sin6[1];
4757fc80c0dfSnordmark 			if ((udp_bits.udpb_ip_recvpktinfo) &&
47587c478bd9Sstevel@tonic-gate 			    (ipp.ipp_fields & IPPF_IFINDEX)) {
47597c478bd9Sstevel@tonic-gate 				struct T_opthdr *toh;
47607c478bd9Sstevel@tonic-gate 				struct in6_pktinfo *pkti;
47617c478bd9Sstevel@tonic-gate 
47627c478bd9Sstevel@tonic-gate 				toh = (struct T_opthdr *)dstopt;
47637c478bd9Sstevel@tonic-gate 				toh->level = IPPROTO_IPV6;
47647c478bd9Sstevel@tonic-gate 				toh->name = IPV6_PKTINFO;
47657c478bd9Sstevel@tonic-gate 				toh->len = sizeof (struct T_opthdr) +
47667c478bd9Sstevel@tonic-gate 				    sizeof (*pkti);
47677c478bd9Sstevel@tonic-gate 				toh->status = 0;
47687c478bd9Sstevel@tonic-gate 				dstopt += sizeof (struct T_opthdr);
47697c478bd9Sstevel@tonic-gate 				pkti = (struct in6_pktinfo *)dstopt;
47707c478bd9Sstevel@tonic-gate 				if (ipversion == IPV6_VERSION)
47717c478bd9Sstevel@tonic-gate 					pkti->ipi6_addr = ip6h->ip6_dst;
47727c478bd9Sstevel@tonic-gate 				else
47737c478bd9Sstevel@tonic-gate 					IN6_IPADDR_TO_V4MAPPED(
4774437220cdSdanmcd 					    ((ipha_t *)rptr)->ipha_dst,
4775437220cdSdanmcd 					    &pkti->ipi6_addr);
47767c478bd9Sstevel@tonic-gate 				pkti->ipi6_ifindex = ipp.ipp_ifindex;
47777c478bd9Sstevel@tonic-gate 				dstopt += sizeof (*pkti);
47787c478bd9Sstevel@tonic-gate 				udi_size -= toh->len;
47797c478bd9Sstevel@tonic-gate 			}
4780fc80c0dfSnordmark 			if (udp_bits.udpb_ipv6_recvhoplimit) {
47817c478bd9Sstevel@tonic-gate 				struct T_opthdr *toh;
47827c478bd9Sstevel@tonic-gate 
47837c478bd9Sstevel@tonic-gate 				toh = (struct T_opthdr *)dstopt;
47847c478bd9Sstevel@tonic-gate 				toh->level = IPPROTO_IPV6;
47857c478bd9Sstevel@tonic-gate 				toh->name = IPV6_HOPLIMIT;
47867c478bd9Sstevel@tonic-gate 				toh->len = sizeof (struct T_opthdr) +
47877c478bd9Sstevel@tonic-gate 				    sizeof (uint_t);
47887c478bd9Sstevel@tonic-gate 				toh->status = 0;
47897c478bd9Sstevel@tonic-gate 				dstopt += sizeof (struct T_opthdr);
47907c478bd9Sstevel@tonic-gate 				if (ipversion == IPV6_VERSION)
47917c478bd9Sstevel@tonic-gate 					*(uint_t *)dstopt = ip6h->ip6_hops;
47927c478bd9Sstevel@tonic-gate 				else
47937c478bd9Sstevel@tonic-gate 					*(uint_t *)dstopt =
47947c478bd9Sstevel@tonic-gate 					    ((ipha_t *)rptr)->ipha_ttl;
47957c478bd9Sstevel@tonic-gate 				dstopt += sizeof (uint_t);
47967c478bd9Sstevel@tonic-gate 				udi_size -= toh->len;
47977c478bd9Sstevel@tonic-gate 			}
4798fc80c0dfSnordmark 			if (udp_bits.udpb_ipv6_recvtclass) {
47997c478bd9Sstevel@tonic-gate 				struct T_opthdr *toh;
48007c478bd9Sstevel@tonic-gate 
48017c478bd9Sstevel@tonic-gate 				toh = (struct T_opthdr *)dstopt;
48027c478bd9Sstevel@tonic-gate 				toh->level = IPPROTO_IPV6;
48037c478bd9Sstevel@tonic-gate 				toh->name = IPV6_TCLASS;
48047c478bd9Sstevel@tonic-gate 				toh->len = sizeof (struct T_opthdr) +
48057c478bd9Sstevel@tonic-gate 				    sizeof (uint_t);
48067c478bd9Sstevel@tonic-gate 				toh->status = 0;
48077c478bd9Sstevel@tonic-gate 				dstopt += sizeof (struct T_opthdr);
48087c478bd9Sstevel@tonic-gate 				if (ipversion == IPV6_VERSION) {
48097c478bd9Sstevel@tonic-gate 					*(uint_t *)dstopt =
4810437220cdSdanmcd 					    IPV6_FLOW_TCLASS(ip6h->ip6_flow);
48117c478bd9Sstevel@tonic-gate 				} else {
48127c478bd9Sstevel@tonic-gate 					ipha_t *ipha = (ipha_t *)rptr;
48137c478bd9Sstevel@tonic-gate 					*(uint_t *)dstopt =
48147c478bd9Sstevel@tonic-gate 					    ipha->ipha_type_of_service;
48157c478bd9Sstevel@tonic-gate 				}
48167c478bd9Sstevel@tonic-gate 				dstopt += sizeof (uint_t);
48177c478bd9Sstevel@tonic-gate 				udi_size -= toh->len;
48187c478bd9Sstevel@tonic-gate 			}
4819fc80c0dfSnordmark 			if ((udp_bits.udpb_ipv6_recvhopopts) &&
48207c478bd9Sstevel@tonic-gate 			    (ipp.ipp_fields & IPPF_HOPOPTS)) {
482145916cd2Sjpk 				size_t hlen;
48227c478bd9Sstevel@tonic-gate 
482345916cd2Sjpk 				hlen = copy_hop_opts(&ipp, dstopt);
482445916cd2Sjpk 				dstopt += hlen;
482545916cd2Sjpk 				udi_size -= hlen;
48267c478bd9Sstevel@tonic-gate 			}
4827fc80c0dfSnordmark 			if ((udp_bits.udpb_ipv6_recvdstopts) &&
4828fc80c0dfSnordmark 			    (udp_bits.udpb_ipv6_recvrthdr) &&
48297c478bd9Sstevel@tonic-gate 			    (ipp.ipp_fields & IPPF_RTHDR) &&
48307c478bd9Sstevel@tonic-gate 			    (ipp.ipp_fields & IPPF_RTDSTOPTS)) {
48317c478bd9Sstevel@tonic-gate 				struct T_opthdr *toh;
48327c478bd9Sstevel@tonic-gate 
48337c478bd9Sstevel@tonic-gate 				toh = (struct T_opthdr *)dstopt;
48347c478bd9Sstevel@tonic-gate 				toh->level = IPPROTO_IPV6;
48357c478bd9Sstevel@tonic-gate 				toh->name = IPV6_DSTOPTS;
48367c478bd9Sstevel@tonic-gate 				toh->len = sizeof (struct T_opthdr) +
48377c478bd9Sstevel@tonic-gate 				    ipp.ipp_rtdstoptslen;
48387c478bd9Sstevel@tonic-gate 				toh->status = 0;
48397c478bd9Sstevel@tonic-gate 				dstopt += sizeof (struct T_opthdr);
48407c478bd9Sstevel@tonic-gate 				bcopy(ipp.ipp_rtdstopts, dstopt,
48417c478bd9Sstevel@tonic-gate 				    ipp.ipp_rtdstoptslen);
48427c478bd9Sstevel@tonic-gate 				dstopt += ipp.ipp_rtdstoptslen;
48437c478bd9Sstevel@tonic-gate 				udi_size -= toh->len;
48447c478bd9Sstevel@tonic-gate 			}
4845fc80c0dfSnordmark 			if ((udp_bits.udpb_ipv6_recvrthdr) &&
48467c478bd9Sstevel@tonic-gate 			    (ipp.ipp_fields & IPPF_RTHDR)) {
48477c478bd9Sstevel@tonic-gate 				struct T_opthdr *toh;
48487c478bd9Sstevel@tonic-gate 
48497c478bd9Sstevel@tonic-gate 				toh = (struct T_opthdr *)dstopt;
48507c478bd9Sstevel@tonic-gate 				toh->level = IPPROTO_IPV6;
48517c478bd9Sstevel@tonic-gate 				toh->name = IPV6_RTHDR;
48527c478bd9Sstevel@tonic-gate 				toh->len = sizeof (struct T_opthdr) +
48537c478bd9Sstevel@tonic-gate 				    ipp.ipp_rthdrlen;
48547c478bd9Sstevel@tonic-gate 				toh->status = 0;
48557c478bd9Sstevel@tonic-gate 				dstopt += sizeof (struct T_opthdr);
48567c478bd9Sstevel@tonic-gate 				bcopy(ipp.ipp_rthdr, dstopt, ipp.ipp_rthdrlen);
48577c478bd9Sstevel@tonic-gate 				dstopt += ipp.ipp_rthdrlen;
48587c478bd9Sstevel@tonic-gate 				udi_size -= toh->len;
48597c478bd9Sstevel@tonic-gate 			}
4860fc80c0dfSnordmark 			if ((udp_bits.udpb_ipv6_recvdstopts) &&
48617c478bd9Sstevel@tonic-gate 			    (ipp.ipp_fields & IPPF_DSTOPTS)) {
48627c478bd9Sstevel@tonic-gate 				struct T_opthdr *toh;
48637c478bd9Sstevel@tonic-gate 
48647c478bd9Sstevel@tonic-gate 				toh = (struct T_opthdr *)dstopt;
48657c478bd9Sstevel@tonic-gate 				toh->level = IPPROTO_IPV6;
48667c478bd9Sstevel@tonic-gate 				toh->name = IPV6_DSTOPTS;
48677c478bd9Sstevel@tonic-gate 				toh->len = sizeof (struct T_opthdr) +
48687c478bd9Sstevel@tonic-gate 				    ipp.ipp_dstoptslen;
48697c478bd9Sstevel@tonic-gate 				toh->status = 0;
48707c478bd9Sstevel@tonic-gate 				dstopt += sizeof (struct T_opthdr);
48717c478bd9Sstevel@tonic-gate 				bcopy(ipp.ipp_dstopts, dstopt,
48727c478bd9Sstevel@tonic-gate 				    ipp.ipp_dstoptslen);
48737c478bd9Sstevel@tonic-gate 				dstopt += ipp.ipp_dstoptslen;
48747c478bd9Sstevel@tonic-gate 				udi_size -= toh->len;
48757c478bd9Sstevel@tonic-gate 			}
48767c478bd9Sstevel@tonic-gate 
48777c478bd9Sstevel@tonic-gate 			if (cr != NULL) {
48787c478bd9Sstevel@tonic-gate 				struct T_opthdr *toh;
48797c478bd9Sstevel@tonic-gate 
48807c478bd9Sstevel@tonic-gate 				toh = (struct T_opthdr *)dstopt;
48817c478bd9Sstevel@tonic-gate 				toh->level = SOL_SOCKET;
48827c478bd9Sstevel@tonic-gate 				toh->name = SCM_UCRED;
48837c478bd9Sstevel@tonic-gate 				toh->len = sizeof (struct T_opthdr) + ucredsize;
48847c478bd9Sstevel@tonic-gate 				toh->status = 0;
488545916cd2Sjpk 				(void) cred2ucred(cr, cpid, &toh[1], rcr);
48867c478bd9Sstevel@tonic-gate 				dstopt += toh->len;
48877c478bd9Sstevel@tonic-gate 				udi_size -= toh->len;
48887c478bd9Sstevel@tonic-gate 			}
4889ae43f94fSnordmark 			if (udp_bits.udpb_timestamp) {
4890ae43f94fSnordmark 				struct	T_opthdr *toh;
4891ae43f94fSnordmark 
4892ae43f94fSnordmark 				toh = (struct T_opthdr *)dstopt;
4893ae43f94fSnordmark 				toh->level = SOL_SOCKET;
4894ae43f94fSnordmark 				toh->name = SCM_TIMESTAMP;
4895ae43f94fSnordmark 				toh->len = sizeof (struct T_opthdr) +
4896ae43f94fSnordmark 				    sizeof (timestruc_t) + _POINTER_ALIGNMENT;
4897ae43f94fSnordmark 				toh->status = 0;
4898ae43f94fSnordmark 				dstopt += sizeof (struct T_opthdr);
4899ae43f94fSnordmark 				/* Align for gethrestime() */
4900ae43f94fSnordmark 				dstopt = (uchar_t *)P2ROUNDUP((intptr_t)dstopt,
4901ae43f94fSnordmark 				    sizeof (intptr_t));
4902ae43f94fSnordmark 				gethrestime((timestruc_t *)dstopt);
4903ae43f94fSnordmark 				dstopt = (uchar_t *)toh + toh->len;
4904ae43f94fSnordmark 				udi_size -= toh->len;
4905ae43f94fSnordmark 			}
4906ae43f94fSnordmark 
49077c478bd9Sstevel@tonic-gate 			/* Consumed all of allocated space */
49087c478bd9Sstevel@tonic-gate 			ASSERT(udi_size == 0);
49097c478bd9Sstevel@tonic-gate 		}
49107c478bd9Sstevel@tonic-gate #undef	sin6
49117c478bd9Sstevel@tonic-gate 		/* No IP_RECVDSTADDR for IPv6. */
49127c478bd9Sstevel@tonic-gate 	}
49137c478bd9Sstevel@tonic-gate 
4914fc80c0dfSnordmark 	BUMP_MIB(&us->us_udp_mib, udpHCInDatagrams);
49157c478bd9Sstevel@tonic-gate 	if (options_mp != NULL)
49167c478bd9Sstevel@tonic-gate 		freeb(options_mp);
4917ff550d0eSmasputra 
4918fc80c0dfSnordmark 	if (udp_bits.udpb_direct_sockfs) {
4919ff550d0eSmasputra 		/*
4920ff550d0eSmasputra 		 * There is nothing above us except for the stream head;
4921ff550d0eSmasputra 		 * use the read-side synchronous stream interface in
4922ff550d0eSmasputra 		 * order to reduce the time spent in interrupt thread.
4923ff550d0eSmasputra 		 */
4924ff550d0eSmasputra 		ASSERT(udp->udp_issocket);
4925fc80c0dfSnordmark 		udp_rcv_enqueue(connp->conn_rq, udp, mp, mp_len);
4926ff550d0eSmasputra 	} else {
4927ff550d0eSmasputra 		/*
4928ff550d0eSmasputra 		 * Use regular STREAMS interface to pass data upstream
4929ff550d0eSmasputra 		 * if this is not a socket endpoint, or if we have
4930ff550d0eSmasputra 		 * switched over to the slow mode due to sockmod being
4931ff550d0eSmasputra 		 * popped or a module being pushed on top of us.
4932ff550d0eSmasputra 		 */
4933fc80c0dfSnordmark 		putnext(connp->conn_rq, mp);
4934ff550d0eSmasputra 	}
4935ff550d0eSmasputra 	return;
4936ff550d0eSmasputra 
4937ff550d0eSmasputra tossit:
4938ff550d0eSmasputra 	freemsg(mp);
4939ff550d0eSmasputra 	if (options_mp != NULL)
4940ff550d0eSmasputra 		freeb(options_mp);
4941fc80c0dfSnordmark 	BUMP_MIB(&us->us_udp_mib, udpInErrors);
49427c478bd9Sstevel@tonic-gate }
49437c478bd9Sstevel@tonic-gate 
49447c478bd9Sstevel@tonic-gate /*
4945fc80c0dfSnordmark  * Handle the results of a T_BIND_REQ whether deferred by IP or handled
4946fc80c0dfSnordmark  * immediately.
49477c478bd9Sstevel@tonic-gate  */
49487c478bd9Sstevel@tonic-gate static void
4949fc80c0dfSnordmark udp_bind_result(conn_t *connp, mblk_t *mp)
49507c478bd9Sstevel@tonic-gate {
49517c478bd9Sstevel@tonic-gate 	struct T_error_ack	*tea;
49527c478bd9Sstevel@tonic-gate 
49537c478bd9Sstevel@tonic-gate 	switch (mp->b_datap->db_type) {
49547c478bd9Sstevel@tonic-gate 	case M_PROTO:
49557c478bd9Sstevel@tonic-gate 	case M_PCPROTO:
49567c478bd9Sstevel@tonic-gate 		/* M_PROTO messages contain some type of TPI message. */
4957fc80c0dfSnordmark 		ASSERT((uintptr_t)(mp->b_wptr - mp->b_rptr) <=
4958fc80c0dfSnordmark 		    (uintptr_t)INT_MAX);
4959fc80c0dfSnordmark 		if (mp->b_wptr - mp->b_rptr < sizeof (t_scalar_t)) {
49607c478bd9Sstevel@tonic-gate 			freemsg(mp);
49617c478bd9Sstevel@tonic-gate 			return;
49627c478bd9Sstevel@tonic-gate 		}
4963fc80c0dfSnordmark 		tea = (struct T_error_ack *)mp->b_rptr;
49647c478bd9Sstevel@tonic-gate 
49657c478bd9Sstevel@tonic-gate 		switch (tea->PRIM_type) {
49667c478bd9Sstevel@tonic-gate 		case T_ERROR_ACK:
49677c478bd9Sstevel@tonic-gate 			switch (tea->ERROR_prim) {
49687c478bd9Sstevel@tonic-gate 			case O_T_BIND_REQ:
4969fc80c0dfSnordmark 			case T_BIND_REQ:
4970fc80c0dfSnordmark 				udp_bind_error(connp, mp);
4971fc80c0dfSnordmark 				return;
49727c478bd9Sstevel@tonic-gate 			default:
49737c478bd9Sstevel@tonic-gate 				break;
49747c478bd9Sstevel@tonic-gate 			}
4975fc80c0dfSnordmark 			ASSERT(0);
49767c478bd9Sstevel@tonic-gate 			freemsg(mp);
49777c478bd9Sstevel@tonic-gate 			return;
49787c478bd9Sstevel@tonic-gate 
4979fc80c0dfSnordmark 		case T_BIND_ACK:
4980fc80c0dfSnordmark 			udp_bind_ack(connp, mp);
49817c478bd9Sstevel@tonic-gate 			return;
49827c478bd9Sstevel@tonic-gate 
4983fc80c0dfSnordmark 		default:
4984fc80c0dfSnordmark 			break;
49857c478bd9Sstevel@tonic-gate 		}
49867c478bd9Sstevel@tonic-gate 		freemsg(mp);
4987fc80c0dfSnordmark 		return;
4988fc80c0dfSnordmark 	default:
4989fc80c0dfSnordmark 		/* FIXME: other cases? */
4990fc80c0dfSnordmark 		ASSERT(0);
4991fc80c0dfSnordmark 		freemsg(mp);
49927c478bd9Sstevel@tonic-gate 		return;
49937c478bd9Sstevel@tonic-gate 	}
49947c478bd9Sstevel@tonic-gate }
49957c478bd9Sstevel@tonic-gate 
49967c478bd9Sstevel@tonic-gate /*
49977c478bd9Sstevel@tonic-gate  * Process a T_BIND_ACK
49987c478bd9Sstevel@tonic-gate  */
49997c478bd9Sstevel@tonic-gate static void
5000fc80c0dfSnordmark udp_bind_ack(conn_t *connp, mblk_t *mp)
50017c478bd9Sstevel@tonic-gate {
5002fc80c0dfSnordmark 	udp_t	*udp = connp->conn_udp;
50037c478bd9Sstevel@tonic-gate 	mblk_t	*mp1;
50047c478bd9Sstevel@tonic-gate 	ire_t	*ire;
50057c478bd9Sstevel@tonic-gate 	struct T_bind_ack *tba;
50067c478bd9Sstevel@tonic-gate 	uchar_t *addrp;
50077c478bd9Sstevel@tonic-gate 	ipa_conn_t	*ac;
50087c478bd9Sstevel@tonic-gate 	ipa6_conn_t	*ac6;
5009fc80c0dfSnordmark 	udp_fanout_t	*udpf;
5010fc80c0dfSnordmark 	udp_stack_t	*us = udp->udp_us;
50117c478bd9Sstevel@tonic-gate 
5012fc80c0dfSnordmark 	ASSERT(udp->udp_pending_op != -1);
5013fc80c0dfSnordmark 	rw_enter(&udp->udp_rwlock, RW_WRITER);
50147c478bd9Sstevel@tonic-gate 	/*
50157c478bd9Sstevel@tonic-gate 	 * If a broadcast/multicast address was bound set
50167c478bd9Sstevel@tonic-gate 	 * the source address to 0.
50177c478bd9Sstevel@tonic-gate 	 * This ensures no datagrams with broadcast address
50187c478bd9Sstevel@tonic-gate 	 * as source address are emitted (which would violate
50197c478bd9Sstevel@tonic-gate 	 * RFC1122 - Hosts requirements)
50207c478bd9Sstevel@tonic-gate 	 *
50217c478bd9Sstevel@tonic-gate 	 * Note that when connecting the returned IRE is
50227c478bd9Sstevel@tonic-gate 	 * for the destination address and we only perform
50237c478bd9Sstevel@tonic-gate 	 * the broadcast check for the source address (it
50247c478bd9Sstevel@tonic-gate 	 * is OK to connect to a broadcast/multicast address.)
50257c478bd9Sstevel@tonic-gate 	 */
50267c478bd9Sstevel@tonic-gate 	mp1 = mp->b_cont;
50277c478bd9Sstevel@tonic-gate 	if (mp1 != NULL && mp1->b_datap->db_type == IRE_DB_TYPE) {
50287c478bd9Sstevel@tonic-gate 		ire = (ire_t *)mp1->b_rptr;
50297c478bd9Sstevel@tonic-gate 
50307c478bd9Sstevel@tonic-gate 		/*
50317c478bd9Sstevel@tonic-gate 		 * Note: we get IRE_BROADCAST for IPv6 to "mark" a multicast
50327c478bd9Sstevel@tonic-gate 		 * local address.
50337c478bd9Sstevel@tonic-gate 		 */
5034fc80c0dfSnordmark 		udpf = &us->us_bind_fanout[UDP_BIND_HASH(udp->udp_port,
5035fc80c0dfSnordmark 		    us->us_bind_fanout_size)];
50367c478bd9Sstevel@tonic-gate 		if (ire->ire_type == IRE_BROADCAST &&
50377c478bd9Sstevel@tonic-gate 		    udp->udp_state != TS_DATA_XFER) {
5038fc80c0dfSnordmark 			ASSERT(udp->udp_pending_op == T_BIND_REQ ||
5039fc80c0dfSnordmark 			    udp->udp_pending_op == O_T_BIND_REQ);
50407c478bd9Sstevel@tonic-gate 			/* This was just a local bind to a broadcast addr */
5041fc80c0dfSnordmark 			mutex_enter(&udpf->uf_lock);
50427c478bd9Sstevel@tonic-gate 			V6_SET_ZERO(udp->udp_v6src);
5043fc80c0dfSnordmark 			mutex_exit(&udpf->uf_lock);
50447c478bd9Sstevel@tonic-gate 			if (udp->udp_family == AF_INET6)
5045fc80c0dfSnordmark 				(void) udp_build_hdrs(udp);
50467c478bd9Sstevel@tonic-gate 		} else if (V6_OR_V4_INADDR_ANY(udp->udp_v6src)) {
50477c478bd9Sstevel@tonic-gate 			/*
50487c478bd9Sstevel@tonic-gate 			 * Local address not yet set - pick it from the
50497c478bd9Sstevel@tonic-gate 			 * T_bind_ack
50507c478bd9Sstevel@tonic-gate 			 */
50517c478bd9Sstevel@tonic-gate 			tba = (struct T_bind_ack *)mp->b_rptr;
50527c478bd9Sstevel@tonic-gate 			addrp = &mp->b_rptr[tba->ADDR_offset];
50537c478bd9Sstevel@tonic-gate 			switch (udp->udp_family) {
50547c478bd9Sstevel@tonic-gate 			case AF_INET:
50557c478bd9Sstevel@tonic-gate 				if (tba->ADDR_length == sizeof (ipa_conn_t)) {
50567c478bd9Sstevel@tonic-gate 					ac = (ipa_conn_t *)addrp;
50577c478bd9Sstevel@tonic-gate 				} else {
50587c478bd9Sstevel@tonic-gate 					ASSERT(tba->ADDR_length ==
50597c478bd9Sstevel@tonic-gate 					    sizeof (ipa_conn_x_t));
50607c478bd9Sstevel@tonic-gate 					ac = &((ipa_conn_x_t *)addrp)->acx_conn;
50617c478bd9Sstevel@tonic-gate 				}
5062fc80c0dfSnordmark 				mutex_enter(&udpf->uf_lock);
50637c478bd9Sstevel@tonic-gate 				IN6_IPADDR_TO_V4MAPPED(ac->ac_laddr,
50647c478bd9Sstevel@tonic-gate 				    &udp->udp_v6src);
5065fc80c0dfSnordmark 				mutex_exit(&udpf->uf_lock);
50667c478bd9Sstevel@tonic-gate 				break;
50677c478bd9Sstevel@tonic-gate 			case AF_INET6:
50687c478bd9Sstevel@tonic-gate 				if (tba->ADDR_length == sizeof (ipa6_conn_t)) {
50697c478bd9Sstevel@tonic-gate 					ac6 = (ipa6_conn_t *)addrp;
50707c478bd9Sstevel@tonic-gate 				} else {
50717c478bd9Sstevel@tonic-gate 					ASSERT(tba->ADDR_length ==
50727c478bd9Sstevel@tonic-gate 					    sizeof (ipa6_conn_x_t));
50737c478bd9Sstevel@tonic-gate 					ac6 = &((ipa6_conn_x_t *)
50747c478bd9Sstevel@tonic-gate 					    addrp)->ac6x_conn;
50757c478bd9Sstevel@tonic-gate 				}
5076fc80c0dfSnordmark 				mutex_enter(&udpf->uf_lock);
50777c478bd9Sstevel@tonic-gate 				udp->udp_v6src = ac6->ac6_laddr;
5078fc80c0dfSnordmark 				mutex_exit(&udpf->uf_lock);
5079fc80c0dfSnordmark 				(void) udp_build_hdrs(udp);
50807c478bd9Sstevel@tonic-gate 				break;
50817c478bd9Sstevel@tonic-gate 			}
50827c478bd9Sstevel@tonic-gate 		}
50837c478bd9Sstevel@tonic-gate 		mp1 = mp1->b_cont;
50847c478bd9Sstevel@tonic-gate 	}
5085fc80c0dfSnordmark 	udp->udp_pending_op = -1;
5086fc80c0dfSnordmark 	rw_exit(&udp->udp_rwlock);
50877c478bd9Sstevel@tonic-gate 	/*
50887c478bd9Sstevel@tonic-gate 	 * Look for one or more appended ACK message added by
50897c478bd9Sstevel@tonic-gate 	 * udp_connect or udp_disconnect.
50907c478bd9Sstevel@tonic-gate 	 * If none found just send up the T_BIND_ACK.
50917c478bd9Sstevel@tonic-gate 	 * udp_connect has appended a T_OK_ACK and a T_CONN_CON.
50927c478bd9Sstevel@tonic-gate 	 * udp_disconnect has appended a T_OK_ACK.
50937c478bd9Sstevel@tonic-gate 	 */
50947c478bd9Sstevel@tonic-gate 	if (mp1 != NULL) {
50957c478bd9Sstevel@tonic-gate 		if (mp->b_cont == mp1)
50967c478bd9Sstevel@tonic-gate 			mp->b_cont = NULL;
50977c478bd9Sstevel@tonic-gate 		else {
50987c478bd9Sstevel@tonic-gate 			ASSERT(mp->b_cont->b_cont == mp1);
50997c478bd9Sstevel@tonic-gate 			mp->b_cont->b_cont = NULL;
51007c478bd9Sstevel@tonic-gate 		}
51017c478bd9Sstevel@tonic-gate 		freemsg(mp);
51027c478bd9Sstevel@tonic-gate 		mp = mp1;
51037c478bd9Sstevel@tonic-gate 		while (mp != NULL) {
51047c478bd9Sstevel@tonic-gate 			mp1 = mp->b_cont;
51057c478bd9Sstevel@tonic-gate 			mp->b_cont = NULL;
5106fc80c0dfSnordmark 			putnext(connp->conn_rq, mp);
51077c478bd9Sstevel@tonic-gate 			mp = mp1;
51087c478bd9Sstevel@tonic-gate 		}
51097c478bd9Sstevel@tonic-gate 		return;
51107c478bd9Sstevel@tonic-gate 	}
51117c478bd9Sstevel@tonic-gate 	freemsg(mp->b_cont);
51127c478bd9Sstevel@tonic-gate 	mp->b_cont = NULL;
5113fc80c0dfSnordmark 	putnext(connp->conn_rq, mp);
5114fc80c0dfSnordmark }
5115fc80c0dfSnordmark 
5116fc80c0dfSnordmark static void
5117fc80c0dfSnordmark udp_bind_error(conn_t *connp, mblk_t *mp)
5118fc80c0dfSnordmark {
5119fc80c0dfSnordmark 	udp_t	*udp = connp->conn_udp;
5120fc80c0dfSnordmark 	struct T_error_ack *tea;
5121fc80c0dfSnordmark 	udp_fanout_t	*udpf;
5122fc80c0dfSnordmark 	udp_stack_t	*us = udp->udp_us;
5123fc80c0dfSnordmark 
5124fc80c0dfSnordmark 	tea = (struct T_error_ack *)mp->b_rptr;
5125fc80c0dfSnordmark 
5126fc80c0dfSnordmark 	/*
5127fc80c0dfSnordmark 	 * If our O_T_BIND_REQ/T_BIND_REQ fails,
5128fc80c0dfSnordmark 	 * clear out the associated port and source
5129fc80c0dfSnordmark 	 * address before passing the message
5130fc80c0dfSnordmark 	 * upstream. If this was caused by a T_CONN_REQ
5131fc80c0dfSnordmark 	 * revert back to bound state.
5132fc80c0dfSnordmark 	 */
5133fc80c0dfSnordmark 
5134fc80c0dfSnordmark 	rw_enter(&udp->udp_rwlock, RW_WRITER);
5135fc80c0dfSnordmark 	ASSERT(udp->udp_pending_op != -1);
5136fc80c0dfSnordmark 	tea->ERROR_prim = udp->udp_pending_op;
5137fc80c0dfSnordmark 	udp->udp_pending_op = -1;
5138fc80c0dfSnordmark 	udpf = &us->us_bind_fanout[
5139fc80c0dfSnordmark 	    UDP_BIND_HASH(udp->udp_port,
5140fc80c0dfSnordmark 	    us->us_bind_fanout_size)];
5141fc80c0dfSnordmark 	mutex_enter(&udpf->uf_lock);
5142fc80c0dfSnordmark 
5143fc80c0dfSnordmark 	switch (tea->ERROR_prim) {
5144fc80c0dfSnordmark 	case T_CONN_REQ:
5145fc80c0dfSnordmark 		ASSERT(udp->udp_state == TS_DATA_XFER);
5146fc80c0dfSnordmark 		/* Connect failed */
5147fc80c0dfSnordmark 		/* Revert back to the bound source */
5148fc80c0dfSnordmark 		udp->udp_v6src = udp->udp_bound_v6src;
5149fc80c0dfSnordmark 		udp->udp_state = TS_IDLE;
5150fc80c0dfSnordmark 		mutex_exit(&udpf->uf_lock);
5151fc80c0dfSnordmark 		if (udp->udp_family == AF_INET6)
5152fc80c0dfSnordmark 			(void) udp_build_hdrs(udp);
5153fc80c0dfSnordmark 		rw_exit(&udp->udp_rwlock);
5154fc80c0dfSnordmark 		break;
5155fc80c0dfSnordmark 
5156fc80c0dfSnordmark 	case T_DISCON_REQ:
5157fc80c0dfSnordmark 	case T_BIND_REQ:
5158fc80c0dfSnordmark 	case O_T_BIND_REQ:
5159fc80c0dfSnordmark 		V6_SET_ZERO(udp->udp_v6src);
5160fc80c0dfSnordmark 		V6_SET_ZERO(udp->udp_bound_v6src);
5161fc80c0dfSnordmark 		udp->udp_state = TS_UNBND;
5162fc80c0dfSnordmark 		udp_bind_hash_remove(udp, B_TRUE);
5163fc80c0dfSnordmark 		udp->udp_port = 0;
5164fc80c0dfSnordmark 		mutex_exit(&udpf->uf_lock);
5165fc80c0dfSnordmark 		if (udp->udp_family == AF_INET6)
5166fc80c0dfSnordmark 			(void) udp_build_hdrs(udp);
5167fc80c0dfSnordmark 		rw_exit(&udp->udp_rwlock);
5168fc80c0dfSnordmark 		break;
5169fc80c0dfSnordmark 
5170fc80c0dfSnordmark 	default:
5171fc80c0dfSnordmark 		mutex_exit(&udpf->uf_lock);
5172fc80c0dfSnordmark 		rw_exit(&udp->udp_rwlock);
5173fc80c0dfSnordmark 		(void) mi_strlog(connp->conn_rq, 1,
5174fc80c0dfSnordmark 		    SL_ERROR|SL_TRACE,
5175fc80c0dfSnordmark 		    "udp_input_other: bad ERROR_prim, "
5176fc80c0dfSnordmark 		    "len %d", tea->ERROR_prim);
5177fc80c0dfSnordmark 	}
5178fc80c0dfSnordmark 	putnext(connp->conn_rq, mp);
51797c478bd9Sstevel@tonic-gate }
51807c478bd9Sstevel@tonic-gate 
51817c478bd9Sstevel@tonic-gate /*
5182fc80c0dfSnordmark  * return SNMP stuff in buffer in mpdata. We don't hold any lock and report
5183fc80c0dfSnordmark  * information that can be changing beneath us.
51847c478bd9Sstevel@tonic-gate  */
5185fc80c0dfSnordmark mblk_t *
51867c478bd9Sstevel@tonic-gate udp_snmp_get(queue_t *q, mblk_t *mpctl)
51877c478bd9Sstevel@tonic-gate {
51887c478bd9Sstevel@tonic-gate 	mblk_t			*mpdata;
51897c478bd9Sstevel@tonic-gate 	mblk_t			*mp_conn_ctl;
519045916cd2Sjpk 	mblk_t			*mp_attr_ctl;
51917c478bd9Sstevel@tonic-gate 	mblk_t			*mp6_conn_ctl;
519245916cd2Sjpk 	mblk_t			*mp6_attr_ctl;
519345916cd2Sjpk 	mblk_t			*mp_conn_tail;
519445916cd2Sjpk 	mblk_t			*mp_attr_tail;
519545916cd2Sjpk 	mblk_t			*mp6_conn_tail;
519645916cd2Sjpk 	mblk_t			*mp6_attr_tail;
51977c478bd9Sstevel@tonic-gate 	struct opthdr		*optp;
51987c478bd9Sstevel@tonic-gate 	mib2_udpEntry_t		ude;
51997c478bd9Sstevel@tonic-gate 	mib2_udp6Entry_t	ude6;
520045916cd2Sjpk 	mib2_transportMLPEntry_t mlp;
52017c478bd9Sstevel@tonic-gate 	int			state;
52027c478bd9Sstevel@tonic-gate 	zoneid_t		zoneid;
5203ff550d0eSmasputra 	int			i;
5204ff550d0eSmasputra 	connf_t			*connfp;
5205ff550d0eSmasputra 	conn_t			*connp = Q_TO_CONN(q);
520645916cd2Sjpk 	int			v4_conn_idx;
520745916cd2Sjpk 	int			v6_conn_idx;
520845916cd2Sjpk 	boolean_t		needattr;
5209fc80c0dfSnordmark 	udp_t			*udp;
5210f4b3ec61Sdh 	ip_stack_t		*ipst = connp->conn_netstack->netstack_ip;
5211fc80c0dfSnordmark 	udp_stack_t		*us = connp->conn_netstack->netstack_udp;
5212fc80c0dfSnordmark 	mblk_t			*mp2ctl;
5213fc80c0dfSnordmark 
5214fc80c0dfSnordmark 	/*
5215fc80c0dfSnordmark 	 * make a copy of the original message
5216fc80c0dfSnordmark 	 */
5217fc80c0dfSnordmark 	mp2ctl = copymsg(mpctl);
52187c478bd9Sstevel@tonic-gate 
521945916cd2Sjpk 	mp_conn_ctl = mp_attr_ctl = mp6_conn_ctl = NULL;
52207c478bd9Sstevel@tonic-gate 	if (mpctl == NULL ||
52217c478bd9Sstevel@tonic-gate 	    (mpdata = mpctl->b_cont) == NULL ||
52227c478bd9Sstevel@tonic-gate 	    (mp_conn_ctl = copymsg(mpctl)) == NULL ||
522345916cd2Sjpk 	    (mp_attr_ctl = copymsg(mpctl)) == NULL ||
522445916cd2Sjpk 	    (mp6_conn_ctl = copymsg(mpctl)) == NULL ||
522545916cd2Sjpk 	    (mp6_attr_ctl = copymsg(mpctl)) == NULL) {
52267c478bd9Sstevel@tonic-gate 		freemsg(mp_conn_ctl);
522745916cd2Sjpk 		freemsg(mp_attr_ctl);
522845916cd2Sjpk 		freemsg(mp6_conn_ctl);
5229fc80c0dfSnordmark 		freemsg(mpctl);
5230fc80c0dfSnordmark 		freemsg(mp2ctl);
52317c478bd9Sstevel@tonic-gate 		return (0);
52327c478bd9Sstevel@tonic-gate 	}
52337c478bd9Sstevel@tonic-gate 
5234ff550d0eSmasputra 	zoneid = connp->conn_zoneid;
52357c478bd9Sstevel@tonic-gate 
52367c478bd9Sstevel@tonic-gate 	/* fixed length structure for IPv4 and IPv6 counters */
5237fc80c0dfSnordmark 	SET_MIB(us->us_udp_mib.udpEntrySize, sizeof (mib2_udpEntry_t));
5238fc80c0dfSnordmark 	SET_MIB(us->us_udp_mib.udp6EntrySize, sizeof (mib2_udp6Entry_t));
52393173664eSapersson 	/* synchronize 64- and 32-bit counters */
5240fc80c0dfSnordmark 	SYNC32_MIB(&us->us_udp_mib, udpInDatagrams, udpHCInDatagrams);
5241fc80c0dfSnordmark 	SYNC32_MIB(&us->us_udp_mib, udpOutDatagrams, udpHCOutDatagrams);
52423173664eSapersson 
52437c478bd9Sstevel@tonic-gate 	optp = (struct opthdr *)&mpctl->b_rptr[sizeof (struct T_optmgmt_ack)];
52447c478bd9Sstevel@tonic-gate 	optp->level = MIB2_UDP;
52457c478bd9Sstevel@tonic-gate 	optp->name = 0;
5246fc80c0dfSnordmark 	(void) snmp_append_data(mpdata, (char *)&us->us_udp_mib,
5247fc80c0dfSnordmark 	    sizeof (us->us_udp_mib));
52487c478bd9Sstevel@tonic-gate 	optp->len = msgdsize(mpdata);
52497c478bd9Sstevel@tonic-gate 	qreply(q, mpctl);
52507c478bd9Sstevel@tonic-gate 
525145916cd2Sjpk 	mp_conn_tail = mp_attr_tail = mp6_conn_tail = mp6_attr_tail = NULL;
525245916cd2Sjpk 	v4_conn_idx = v6_conn_idx = 0;
525345916cd2Sjpk 
5254ff550d0eSmasputra 	for (i = 0; i < CONN_G_HASH_SIZE; i++) {
5255f4b3ec61Sdh 		connfp = &ipst->ips_ipcl_globalhash_fanout[i];
5256ff550d0eSmasputra 		connp = NULL;
52577c478bd9Sstevel@tonic-gate 
5258ff550d0eSmasputra 		while ((connp = ipcl_get_next_conn(connfp, connp,
5259fc80c0dfSnordmark 		    IPCL_UDPCONN))) {
5260ff550d0eSmasputra 			udp = connp->conn_udp;
5261ff550d0eSmasputra 			if (zoneid != connp->conn_zoneid)
5262ff550d0eSmasputra 				continue;
52637c478bd9Sstevel@tonic-gate 
5264ff550d0eSmasputra 			/*
5265ff550d0eSmasputra 			 * Note that the port numbers are sent in
5266ff550d0eSmasputra 			 * host byte order
5267ff550d0eSmasputra 			 */
52687c478bd9Sstevel@tonic-gate 
5269ff550d0eSmasputra 			if (udp->udp_state == TS_UNBND)
5270ff550d0eSmasputra 				state = MIB2_UDP_unbound;
5271ff550d0eSmasputra 			else if (udp->udp_state == TS_IDLE)
5272ff550d0eSmasputra 				state = MIB2_UDP_idle;
5273ff550d0eSmasputra 			else if (udp->udp_state == TS_DATA_XFER)
5274ff550d0eSmasputra 				state = MIB2_UDP_connected;
5275ff550d0eSmasputra 			else
5276ff550d0eSmasputra 				state = MIB2_UDP_unknown;
52777c478bd9Sstevel@tonic-gate 
527845916cd2Sjpk 			needattr = B_FALSE;
527945916cd2Sjpk 			bzero(&mlp, sizeof (mlp));
528045916cd2Sjpk 			if (connp->conn_mlp_type != mlptSingle) {
528145916cd2Sjpk 				if (connp->conn_mlp_type == mlptShared ||
528245916cd2Sjpk 				    connp->conn_mlp_type == mlptBoth)
528345916cd2Sjpk 					mlp.tme_flags |= MIB2_TMEF_SHARED;
528445916cd2Sjpk 				if (connp->conn_mlp_type == mlptPrivate ||
528545916cd2Sjpk 				    connp->conn_mlp_type == mlptBoth)
528645916cd2Sjpk 					mlp.tme_flags |= MIB2_TMEF_PRIVATE;
528745916cd2Sjpk 				needattr = B_TRUE;
528845916cd2Sjpk 			}
528945916cd2Sjpk 
5290ff550d0eSmasputra 			/*
5291ff550d0eSmasputra 			 * Create an IPv4 table entry for IPv4 entries and also
5292ff550d0eSmasputra 			 * any IPv6 entries which are bound to in6addr_any
5293ff550d0eSmasputra 			 * (i.e. anything a IPv4 peer could connect/send to).
5294ff550d0eSmasputra 			 */
5295ff550d0eSmasputra 			if (udp->udp_ipversion == IPV4_VERSION ||
5296ff550d0eSmasputra 			    (udp->udp_state <= TS_IDLE &&
5297ff550d0eSmasputra 			    IN6_IS_ADDR_UNSPECIFIED(&udp->udp_v6src))) {
5298ff550d0eSmasputra 				ude.udpEntryInfo.ue_state = state;
52997c478bd9Sstevel@tonic-gate 				/*
5300ff550d0eSmasputra 				 * If in6addr_any this will set it to
5301ff550d0eSmasputra 				 * INADDR_ANY
53027c478bd9Sstevel@tonic-gate 				 */
5303ff550d0eSmasputra 				ude.udpLocalAddress =
5304ff550d0eSmasputra 				    V4_PART_OF_V6(udp->udp_v6src);
5305ff550d0eSmasputra 				ude.udpLocalPort = ntohs(udp->udp_port);
5306ff550d0eSmasputra 				if (udp->udp_state == TS_DATA_XFER) {
5307ff550d0eSmasputra 					/*
5308ff550d0eSmasputra 					 * Can potentially get here for
5309ff550d0eSmasputra 					 * v6 socket if another process
5310ff550d0eSmasputra 					 * (say, ping) has just done a
5311ff550d0eSmasputra 					 * sendto(), changing the state
5312ff550d0eSmasputra 					 * from the TS_IDLE above to
5313ff550d0eSmasputra 					 * TS_DATA_XFER by the time we hit
5314ff550d0eSmasputra 					 * this part of the code.
5315ff550d0eSmasputra 					 */
5316ff550d0eSmasputra 					ude.udpEntryInfo.ue_RemoteAddress =
5317ff550d0eSmasputra 					    V4_PART_OF_V6(udp->udp_v6dst);
5318ff550d0eSmasputra 					ude.udpEntryInfo.ue_RemotePort =
5319ff550d0eSmasputra 					    ntohs(udp->udp_dstport);
5320ff550d0eSmasputra 				} else {
5321ff550d0eSmasputra 					ude.udpEntryInfo.ue_RemoteAddress = 0;
5322ff550d0eSmasputra 					ude.udpEntryInfo.ue_RemotePort = 0;
5323ff550d0eSmasputra 				}
53243173664eSapersson 
53253173664eSapersson 				/*
53263173664eSapersson 				 * We make the assumption that all udp_t
53273173664eSapersson 				 * structs will be created within an address
53283173664eSapersson 				 * region no larger than 32-bits.
53293173664eSapersson 				 */
53303173664eSapersson 				ude.udpInstance = (uint32_t)(uintptr_t)udp;
53313173664eSapersson 				ude.udpCreationProcess =
53323173664eSapersson 				    (udp->udp_open_pid < 0) ?
53333173664eSapersson 				    MIB2_UNKNOWN_PROCESS :
53343173664eSapersson 				    udp->udp_open_pid;
53353173664eSapersson 				ude.udpCreationTime = udp->udp_open_time;
53363173664eSapersson 
533745916cd2Sjpk 				(void) snmp_append_data2(mp_conn_ctl->b_cont,
5338ff550d0eSmasputra 				    &mp_conn_tail, (char *)&ude, sizeof (ude));
533945916cd2Sjpk 				mlp.tme_connidx = v4_conn_idx++;
534045916cd2Sjpk 				if (needattr)
534145916cd2Sjpk 					(void) snmp_append_data2(
534245916cd2Sjpk 					    mp_attr_ctl->b_cont, &mp_attr_tail,
534345916cd2Sjpk 					    (char *)&mlp, sizeof (mlp));
53447c478bd9Sstevel@tonic-gate 			}
5345ff550d0eSmasputra 			if (udp->udp_ipversion == IPV6_VERSION) {
5346ff550d0eSmasputra 				ude6.udp6EntryInfo.ue_state  = state;
5347ff550d0eSmasputra 				ude6.udp6LocalAddress = udp->udp_v6src;
5348ff550d0eSmasputra 				ude6.udp6LocalPort = ntohs(udp->udp_port);
5349ff550d0eSmasputra 				ude6.udp6IfIndex = udp->udp_bound_if;
5350ff550d0eSmasputra 				if (udp->udp_state == TS_DATA_XFER) {
5351ff550d0eSmasputra 					ude6.udp6EntryInfo.ue_RemoteAddress =
5352ff550d0eSmasputra 					    udp->udp_v6dst;
5353ff550d0eSmasputra 					ude6.udp6EntryInfo.ue_RemotePort =
5354ff550d0eSmasputra 					    ntohs(udp->udp_dstport);
5355ff550d0eSmasputra 				} else {
5356ff550d0eSmasputra 					ude6.udp6EntryInfo.ue_RemoteAddress =
5357ff550d0eSmasputra 					    sin6_null.sin6_addr;
5358ff550d0eSmasputra 					ude6.udp6EntryInfo.ue_RemotePort = 0;
5359ff550d0eSmasputra 				}
53603173664eSapersson 				/*
53613173664eSapersson 				 * We make the assumption that all udp_t
53623173664eSapersson 				 * structs will be created within an address
53633173664eSapersson 				 * region no larger than 32-bits.
53643173664eSapersson 				 */
53653173664eSapersson 				ude6.udp6Instance = (uint32_t)(uintptr_t)udp;
53663173664eSapersson 				ude6.udp6CreationProcess =
53673173664eSapersson 				    (udp->udp_open_pid < 0) ?
53683173664eSapersson 				    MIB2_UNKNOWN_PROCESS :
53693173664eSapersson 				    udp->udp_open_pid;
53703173664eSapersson 				ude6.udp6CreationTime = udp->udp_open_time;
53713173664eSapersson 
537245916cd2Sjpk 				(void) snmp_append_data2(mp6_conn_ctl->b_cont,
5373ff550d0eSmasputra 				    &mp6_conn_tail, (char *)&ude6,
5374ff550d0eSmasputra 				    sizeof (ude6));
537545916cd2Sjpk 				mlp.tme_connidx = v6_conn_idx++;
537645916cd2Sjpk 				if (needattr)
537745916cd2Sjpk 					(void) snmp_append_data2(
537845916cd2Sjpk 					    mp6_attr_ctl->b_cont,
537945916cd2Sjpk 					    &mp6_attr_tail, (char *)&mlp,
538045916cd2Sjpk 					    sizeof (mlp));
53817c478bd9Sstevel@tonic-gate 			}
53827c478bd9Sstevel@tonic-gate 		}
53837c478bd9Sstevel@tonic-gate 	}
53847c478bd9Sstevel@tonic-gate 
53857c478bd9Sstevel@tonic-gate 	/* IPv4 UDP endpoints */
53867c478bd9Sstevel@tonic-gate 	optp = (struct opthdr *)&mp_conn_ctl->b_rptr[
53877c478bd9Sstevel@tonic-gate 	    sizeof (struct T_optmgmt_ack)];
53887c478bd9Sstevel@tonic-gate 	optp->level = MIB2_UDP;
53897c478bd9Sstevel@tonic-gate 	optp->name = MIB2_UDP_ENTRY;
539045916cd2Sjpk 	optp->len = msgdsize(mp_conn_ctl->b_cont);
53917c478bd9Sstevel@tonic-gate 	qreply(q, mp_conn_ctl);
53927c478bd9Sstevel@tonic-gate 
539345916cd2Sjpk 	/* table of MLP attributes... */
539445916cd2Sjpk 	optp = (struct opthdr *)&mp_attr_ctl->b_rptr[
539545916cd2Sjpk 	    sizeof (struct T_optmgmt_ack)];
539645916cd2Sjpk 	optp->level = MIB2_UDP;
539745916cd2Sjpk 	optp->name = EXPER_XPORT_MLP;
539845916cd2Sjpk 	optp->len = msgdsize(mp_attr_ctl->b_cont);
539945916cd2Sjpk 	if (optp->len == 0)
540045916cd2Sjpk 		freemsg(mp_attr_ctl);
540145916cd2Sjpk 	else
540245916cd2Sjpk 		qreply(q, mp_attr_ctl);
540345916cd2Sjpk 
54047c478bd9Sstevel@tonic-gate 	/* IPv6 UDP endpoints */
54057c478bd9Sstevel@tonic-gate 	optp = (struct opthdr *)&mp6_conn_ctl->b_rptr[
54067c478bd9Sstevel@tonic-gate 	    sizeof (struct T_optmgmt_ack)];
54077c478bd9Sstevel@tonic-gate 	optp->level = MIB2_UDP6;
54087c478bd9Sstevel@tonic-gate 	optp->name = MIB2_UDP6_ENTRY;
540945916cd2Sjpk 	optp->len = msgdsize(mp6_conn_ctl->b_cont);
54107c478bd9Sstevel@tonic-gate 	qreply(q, mp6_conn_ctl);
54117c478bd9Sstevel@tonic-gate 
541245916cd2Sjpk 	/* table of MLP attributes... */
541345916cd2Sjpk 	optp = (struct opthdr *)&mp6_attr_ctl->b_rptr[
541445916cd2Sjpk 	    sizeof (struct T_optmgmt_ack)];
541545916cd2Sjpk 	optp->level = MIB2_UDP6;
541645916cd2Sjpk 	optp->name = EXPER_XPORT_MLP;
541745916cd2Sjpk 	optp->len = msgdsize(mp6_attr_ctl->b_cont);
541845916cd2Sjpk 	if (optp->len == 0)
541945916cd2Sjpk 		freemsg(mp6_attr_ctl);
542045916cd2Sjpk 	else
542145916cd2Sjpk 		qreply(q, mp6_attr_ctl);
542245916cd2Sjpk 
5423fc80c0dfSnordmark 	return (mp2ctl);
54247c478bd9Sstevel@tonic-gate }
54257c478bd9Sstevel@tonic-gate 
54267c478bd9Sstevel@tonic-gate /*
54277c478bd9Sstevel@tonic-gate  * Return 0 if invalid set request, 1 otherwise, including non-udp requests.
54287c478bd9Sstevel@tonic-gate  * NOTE: Per MIB-II, UDP has no writable data.
54297c478bd9Sstevel@tonic-gate  * TODO:  If this ever actually tries to set anything, it needs to be
54307c478bd9Sstevel@tonic-gate  * to do the appropriate locking.
54317c478bd9Sstevel@tonic-gate  */
54327c478bd9Sstevel@tonic-gate /* ARGSUSED */
5433ff550d0eSmasputra int
54347c478bd9Sstevel@tonic-gate udp_snmp_set(queue_t *q, t_scalar_t level, t_scalar_t name,
54357c478bd9Sstevel@tonic-gate     uchar_t *ptr, int len)
54367c478bd9Sstevel@tonic-gate {
54377c478bd9Sstevel@tonic-gate 	switch (level) {
54387c478bd9Sstevel@tonic-gate 	case MIB2_UDP:
54397c478bd9Sstevel@tonic-gate 		return (0);
54407c478bd9Sstevel@tonic-gate 	default:
54417c478bd9Sstevel@tonic-gate 		return (1);
54427c478bd9Sstevel@tonic-gate 	}
54437c478bd9Sstevel@tonic-gate }
54447c478bd9Sstevel@tonic-gate 
54457c478bd9Sstevel@tonic-gate static void
54467c478bd9Sstevel@tonic-gate udp_report_item(mblk_t *mp, udp_t *udp)
54477c478bd9Sstevel@tonic-gate {
54487c478bd9Sstevel@tonic-gate 	char *state;
54497c478bd9Sstevel@tonic-gate 	char addrbuf1[INET6_ADDRSTRLEN];
54507c478bd9Sstevel@tonic-gate 	char addrbuf2[INET6_ADDRSTRLEN];
54517c478bd9Sstevel@tonic-gate 	uint_t print_len, buf_len;
54527c478bd9Sstevel@tonic-gate 
54537c478bd9Sstevel@tonic-gate 	buf_len = mp->b_datap->db_lim - mp->b_wptr;
54547c478bd9Sstevel@tonic-gate 	ASSERT(buf_len >= 0);
54557c478bd9Sstevel@tonic-gate 	if (buf_len == 0)
54567c478bd9Sstevel@tonic-gate 		return;
54577c478bd9Sstevel@tonic-gate 
54587c478bd9Sstevel@tonic-gate 	if (udp->udp_state == TS_UNBND)
54597c478bd9Sstevel@tonic-gate 		state = "UNBOUND";
54607c478bd9Sstevel@tonic-gate 	else if (udp->udp_state == TS_IDLE)
54617c478bd9Sstevel@tonic-gate 		state = "IDLE";
54627c478bd9Sstevel@tonic-gate 	else if (udp->udp_state == TS_DATA_XFER)
54637c478bd9Sstevel@tonic-gate 		state = "CONNECTED";
54647c478bd9Sstevel@tonic-gate 	else
54657c478bd9Sstevel@tonic-gate 		state = "UnkState";
54667c478bd9Sstevel@tonic-gate 	print_len = snprintf((char *)mp->b_wptr, buf_len,
54677c478bd9Sstevel@tonic-gate 	    MI_COL_PTRFMT_STR "%4d %5u %s %s %5u %s\n",
5468ff550d0eSmasputra 	    (void *)udp, udp->udp_connp->conn_zoneid, ntohs(udp->udp_port),
5469437220cdSdanmcd 	    inet_ntop(AF_INET6, &udp->udp_v6src, addrbuf1, sizeof (addrbuf1)),
5470437220cdSdanmcd 	    inet_ntop(AF_INET6, &udp->udp_v6dst, addrbuf2, sizeof (addrbuf2)),
54717c478bd9Sstevel@tonic-gate 	    ntohs(udp->udp_dstport), state);
54727c478bd9Sstevel@tonic-gate 	if (print_len < buf_len) {
54737c478bd9Sstevel@tonic-gate 		mp->b_wptr += print_len;
54747c478bd9Sstevel@tonic-gate 	} else {
54757c478bd9Sstevel@tonic-gate 		mp->b_wptr += buf_len;
54767c478bd9Sstevel@tonic-gate 	}
54777c478bd9Sstevel@tonic-gate }
54787c478bd9Sstevel@tonic-gate 
54797c478bd9Sstevel@tonic-gate /* Report for ndd "udp_status" */
54807c478bd9Sstevel@tonic-gate /* ARGSUSED */
54817c478bd9Sstevel@tonic-gate static int
54827c478bd9Sstevel@tonic-gate udp_status_report(queue_t *q, mblk_t *mp, caddr_t cp, cred_t *cr)
54837c478bd9Sstevel@tonic-gate {
54847c478bd9Sstevel@tonic-gate 	zoneid_t zoneid;
5485ff550d0eSmasputra 	connf_t	*connfp;
5486ff550d0eSmasputra 	conn_t	*connp = Q_TO_CONN(q);
5487ff550d0eSmasputra 	udp_t	*udp = connp->conn_udp;
5488ff550d0eSmasputra 	int	i;
5489f4b3ec61Sdh 	udp_stack_t *us = udp->udp_us;
5490f4b3ec61Sdh 	ip_stack_t *ipst = connp->conn_netstack->netstack_ip;
54917c478bd9Sstevel@tonic-gate 
54927c478bd9Sstevel@tonic-gate 	/*
54937c478bd9Sstevel@tonic-gate 	 * Because of the ndd constraint, at most we can have 64K buffer
54947c478bd9Sstevel@tonic-gate 	 * to put in all UDP info.  So to be more efficient, just
54957c478bd9Sstevel@tonic-gate 	 * allocate a 64K buffer here, assuming we need that large buffer.
54967c478bd9Sstevel@tonic-gate 	 * This may be a problem as any user can read udp_status.  Therefore
5497f4b3ec61Sdh 	 * we limit the rate of doing this using us_ndd_get_info_interval.
54987c478bd9Sstevel@tonic-gate 	 * This should be OK as normal users should not do this too often.
54997c478bd9Sstevel@tonic-gate 	 */
5500f4b3ec61Sdh 	if (cr == NULL || secpolicy_ip_config(cr, B_TRUE) != 0) {
5501f4b3ec61Sdh 		if (ddi_get_lbolt() - us->us_last_ndd_get_info_time <
5502f4b3ec61Sdh 		    drv_usectohz(us->us_ndd_get_info_interval * 1000)) {
55037c478bd9Sstevel@tonic-gate 			(void) mi_mpprintf(mp, NDD_TOO_QUICK_MSG);
55047c478bd9Sstevel@tonic-gate 			return (0);
55057c478bd9Sstevel@tonic-gate 		}
55067c478bd9Sstevel@tonic-gate 	}
55077c478bd9Sstevel@tonic-gate 	if ((mp->b_cont = allocb(ND_MAX_BUF_LEN, BPRI_HI)) == NULL) {
55087c478bd9Sstevel@tonic-gate 		/* The following may work even if we cannot get a large buf. */
55097c478bd9Sstevel@tonic-gate 		(void) mi_mpprintf(mp, NDD_OUT_OF_BUF_MSG);
55107c478bd9Sstevel@tonic-gate 		return (0);
55117c478bd9Sstevel@tonic-gate 	}
55127c478bd9Sstevel@tonic-gate 	(void) mi_mpprintf(mp,
55137c478bd9Sstevel@tonic-gate 	    "UDP     " MI_COL_HDRPAD_STR
55147c478bd9Sstevel@tonic-gate 	/*   12345678[89ABCDEF] */
55157c478bd9Sstevel@tonic-gate 	    " zone lport src addr        dest addr       port  state");
55167c478bd9Sstevel@tonic-gate 	/*    1234 12345 xxx.xxx.xxx.xxx xxx.xxx.xxx.xxx 12345 UNBOUND */
55177c478bd9Sstevel@tonic-gate 
5518ff550d0eSmasputra 	zoneid = connp->conn_zoneid;
55197c478bd9Sstevel@tonic-gate 
5520ff550d0eSmasputra 	for (i = 0; i < CONN_G_HASH_SIZE; i++) {
5521f4b3ec61Sdh 		connfp = &ipst->ips_ipcl_globalhash_fanout[i];
5522ff550d0eSmasputra 		connp = NULL;
55237c478bd9Sstevel@tonic-gate 
5524ff550d0eSmasputra 		while ((connp = ipcl_get_next_conn(connfp, connp,
5525fc80c0dfSnordmark 		    IPCL_UDPCONN))) {
5526ff550d0eSmasputra 			udp = connp->conn_udp;
5527ff550d0eSmasputra 			if (zoneid != GLOBAL_ZONEID &&
5528ff550d0eSmasputra 			    zoneid != connp->conn_zoneid)
5529ff550d0eSmasputra 				continue;
55307c478bd9Sstevel@tonic-gate 
5531ff550d0eSmasputra 			udp_report_item(mp->b_cont, udp);
5532ff550d0eSmasputra 		}
55337c478bd9Sstevel@tonic-gate 	}
5534f4b3ec61Sdh 	us->us_last_ndd_get_info_time = ddi_get_lbolt();
55357c478bd9Sstevel@tonic-gate 	return (0);
55367c478bd9Sstevel@tonic-gate }
55377c478bd9Sstevel@tonic-gate 
55387c478bd9Sstevel@tonic-gate /*
55397c478bd9Sstevel@tonic-gate  * This routine creates a T_UDERROR_IND message and passes it upstream.
55407c478bd9Sstevel@tonic-gate  * The address and options are copied from the T_UNITDATA_REQ message
55417c478bd9Sstevel@tonic-gate  * passed in mp.  This message is freed.
55427c478bd9Sstevel@tonic-gate  */
55437c478bd9Sstevel@tonic-gate static void
5544ff550d0eSmasputra udp_ud_err(queue_t *q, mblk_t *mp, uchar_t *destaddr, t_scalar_t destlen,
5545ff550d0eSmasputra     t_scalar_t err)
55467c478bd9Sstevel@tonic-gate {
5547ff550d0eSmasputra 	struct T_unitdata_req *tudr;
55487c478bd9Sstevel@tonic-gate 	mblk_t	*mp1;
5549ff550d0eSmasputra 	uchar_t	*optaddr;
5550ff550d0eSmasputra 	t_scalar_t optlen;
55517c478bd9Sstevel@tonic-gate 
5552ff550d0eSmasputra 	if (DB_TYPE(mp) == M_DATA) {
5553ff550d0eSmasputra 		ASSERT(destaddr != NULL && destlen != 0);
5554ff550d0eSmasputra 		optaddr = NULL;
5555ff550d0eSmasputra 		optlen = 0;
5556ff550d0eSmasputra 	} else {
5557ff550d0eSmasputra 		if ((mp->b_wptr < mp->b_rptr) ||
5558ff550d0eSmasputra 		    (MBLKL(mp)) < sizeof (struct T_unitdata_req)) {
5559ff550d0eSmasputra 			goto done;
5560ff550d0eSmasputra 		}
5561ff550d0eSmasputra 		tudr = (struct T_unitdata_req *)mp->b_rptr;
5562ff550d0eSmasputra 		destaddr = mp->b_rptr + tudr->DEST_offset;
5563ff550d0eSmasputra 		if (destaddr < mp->b_rptr || destaddr >= mp->b_wptr ||
5564ff550d0eSmasputra 		    destaddr + tudr->DEST_length < mp->b_rptr ||
5565ff550d0eSmasputra 		    destaddr + tudr->DEST_length > mp->b_wptr) {
5566ff550d0eSmasputra 			goto done;
5567ff550d0eSmasputra 		}
5568ff550d0eSmasputra 		optaddr = mp->b_rptr + tudr->OPT_offset;
5569ff550d0eSmasputra 		if (optaddr < mp->b_rptr || optaddr >= mp->b_wptr ||
5570ff550d0eSmasputra 		    optaddr + tudr->OPT_length < mp->b_rptr ||
5571ff550d0eSmasputra 		    optaddr + tudr->OPT_length > mp->b_wptr) {
5572ff550d0eSmasputra 			goto done;
5573ff550d0eSmasputra 		}
5574ff550d0eSmasputra 		destlen = tudr->DEST_length;
5575ff550d0eSmasputra 		optlen = tudr->OPT_length;
55767c478bd9Sstevel@tonic-gate 	}
5577ff550d0eSmasputra 
5578ff550d0eSmasputra 	mp1 = mi_tpi_uderror_ind((char *)destaddr, destlen,
5579ff550d0eSmasputra 	    (char *)optaddr, optlen, err);
5580ff550d0eSmasputra 	if (mp1 != NULL)
5581fc80c0dfSnordmark 		qreply(q, mp1);
55827c478bd9Sstevel@tonic-gate 
55837c478bd9Sstevel@tonic-gate done:
55847c478bd9Sstevel@tonic-gate 	freemsg(mp);
55857c478bd9Sstevel@tonic-gate }
55867c478bd9Sstevel@tonic-gate 
55877c478bd9Sstevel@tonic-gate /*
55887c478bd9Sstevel@tonic-gate  * This routine removes a port number association from a stream.  It
55897c478bd9Sstevel@tonic-gate  * is called by udp_wput to handle T_UNBIND_REQ messages.
55907c478bd9Sstevel@tonic-gate  */
55917c478bd9Sstevel@tonic-gate static void
55927c478bd9Sstevel@tonic-gate udp_unbind(queue_t *q, mblk_t *mp)
55937c478bd9Sstevel@tonic-gate {
5594ff550d0eSmasputra 	udp_t *udp = Q_TO_UDP(q);
5595fc80c0dfSnordmark 	udp_fanout_t	*udpf;
5596fc80c0dfSnordmark 	udp_stack_t	*us = udp->udp_us;
55977c478bd9Sstevel@tonic-gate 
55987c478bd9Sstevel@tonic-gate 	if (cl_inet_unbind != NULL) {
55997c478bd9Sstevel@tonic-gate 		/*
56007c478bd9Sstevel@tonic-gate 		 * Running in cluster mode - register unbind information
56017c478bd9Sstevel@tonic-gate 		 */
56027c478bd9Sstevel@tonic-gate 		if (udp->udp_ipversion == IPV4_VERSION) {
56037c478bd9Sstevel@tonic-gate 			(*cl_inet_unbind)(IPPROTO_UDP, AF_INET,
56047c478bd9Sstevel@tonic-gate 			    (uint8_t *)(&V4_PART_OF_V6(udp->udp_v6src)),
56057c478bd9Sstevel@tonic-gate 			    (in_port_t)udp->udp_port);
56067c478bd9Sstevel@tonic-gate 		} else {
56077c478bd9Sstevel@tonic-gate 			(*cl_inet_unbind)(IPPROTO_UDP, AF_INET6,
56087c478bd9Sstevel@tonic-gate 			    (uint8_t *)&(udp->udp_v6src),
56097c478bd9Sstevel@tonic-gate 			    (in_port_t)udp->udp_port);
56107c478bd9Sstevel@tonic-gate 		}
56117c478bd9Sstevel@tonic-gate 	}
56127c478bd9Sstevel@tonic-gate 
5613fc80c0dfSnordmark 	rw_enter(&udp->udp_rwlock, RW_WRITER);
5614fc80c0dfSnordmark 	if (udp->udp_state == TS_UNBND || udp->udp_pending_op != -1) {
5615fc80c0dfSnordmark 		rw_exit(&udp->udp_rwlock);
5616fc80c0dfSnordmark 		udp_err_ack(q, mp, TOUTSTATE, 0);
5617fc80c0dfSnordmark 		return;
56187c478bd9Sstevel@tonic-gate 	}
5619fc80c0dfSnordmark 	udp->udp_pending_op = T_UNBIND_REQ;
5620fc80c0dfSnordmark 	rw_exit(&udp->udp_rwlock);
5621fc80c0dfSnordmark 
5622ff550d0eSmasputra 	/*
5623ff550d0eSmasputra 	 * Pass the unbind to IP; T_UNBIND_REQ is larger than T_OK_ACK
5624ff550d0eSmasputra 	 * and therefore ip_unbind must never return NULL.
5625ff550d0eSmasputra 	 */
5626ff550d0eSmasputra 	mp = ip_unbind(q, mp);
5627ff550d0eSmasputra 	ASSERT(mp != NULL);
5628fc80c0dfSnordmark 	ASSERT(((struct T_ok_ack *)mp->b_rptr)->PRIM_type == T_OK_ACK);
5629fc80c0dfSnordmark 
5630fc80c0dfSnordmark 	/*
5631fc80c0dfSnordmark 	 * Once we're unbound from IP, the pending operation may be cleared
5632fc80c0dfSnordmark 	 * here.
5633fc80c0dfSnordmark 	 */
5634fc80c0dfSnordmark 	rw_enter(&udp->udp_rwlock, RW_WRITER);
5635fc80c0dfSnordmark 	udpf = &us->us_bind_fanout[UDP_BIND_HASH(udp->udp_port,
5636fc80c0dfSnordmark 	    us->us_bind_fanout_size)];
5637fc80c0dfSnordmark 	mutex_enter(&udpf->uf_lock);
5638fc80c0dfSnordmark 	udp_bind_hash_remove(udp, B_TRUE);
5639fc80c0dfSnordmark 	V6_SET_ZERO(udp->udp_v6src);
5640fc80c0dfSnordmark 	V6_SET_ZERO(udp->udp_bound_v6src);
5641fc80c0dfSnordmark 	udp->udp_port = 0;
5642fc80c0dfSnordmark 	mutex_exit(&udpf->uf_lock);
5643fc80c0dfSnordmark 
5644fc80c0dfSnordmark 	udp->udp_pending_op = -1;
5645fc80c0dfSnordmark 	udp->udp_state = TS_UNBND;
5646fc80c0dfSnordmark 	if (udp->udp_family == AF_INET6)
5647fc80c0dfSnordmark 		(void) udp_build_hdrs(udp);
5648fc80c0dfSnordmark 	rw_exit(&udp->udp_rwlock);
5649fc80c0dfSnordmark 
5650fc80c0dfSnordmark 	qreply(q, mp);
56517c478bd9Sstevel@tonic-gate }
56527c478bd9Sstevel@tonic-gate 
56537c478bd9Sstevel@tonic-gate /*
56547c478bd9Sstevel@tonic-gate  * Don't let port fall into the privileged range.
565545916cd2Sjpk  * Since the extra privileged ports can be arbitrary we also
56567c478bd9Sstevel@tonic-gate  * ensure that we exclude those from consideration.
5657f4b3ec61Sdh  * us->us_epriv_ports is not sorted thus we loop over it until
56587c478bd9Sstevel@tonic-gate  * there are no changes.
56597c478bd9Sstevel@tonic-gate  */
56607c478bd9Sstevel@tonic-gate static in_port_t
566145916cd2Sjpk udp_update_next_port(udp_t *udp, in_port_t port, boolean_t random)
56627c478bd9Sstevel@tonic-gate {
56637c478bd9Sstevel@tonic-gate 	int i;
566445916cd2Sjpk 	in_port_t nextport;
566545916cd2Sjpk 	boolean_t restart = B_FALSE;
5666f4b3ec61Sdh 	udp_stack_t *us = udp->udp_us;
56677c478bd9Sstevel@tonic-gate 
56687c478bd9Sstevel@tonic-gate 	if (random && udp_random_anon_port != 0) {
56697c478bd9Sstevel@tonic-gate 		(void) random_get_pseudo_bytes((uint8_t *)&port,
56707c478bd9Sstevel@tonic-gate 		    sizeof (in_port_t));
56717c478bd9Sstevel@tonic-gate 		/*
56727c478bd9Sstevel@tonic-gate 		 * Unless changed by a sys admin, the smallest anon port
56737c478bd9Sstevel@tonic-gate 		 * is 32768 and the largest anon port is 65535.  It is
56747c478bd9Sstevel@tonic-gate 		 * very likely (50%) for the random port to be smaller
56757c478bd9Sstevel@tonic-gate 		 * than the smallest anon port.  When that happens,
56767c478bd9Sstevel@tonic-gate 		 * add port % (anon port range) to the smallest anon
56777c478bd9Sstevel@tonic-gate 		 * port to get the random port.  It should fall into the
56787c478bd9Sstevel@tonic-gate 		 * valid anon port range.
56797c478bd9Sstevel@tonic-gate 		 */
5680f4b3ec61Sdh 		if (port < us->us_smallest_anon_port) {
5681f4b3ec61Sdh 			port = us->us_smallest_anon_port +
5682f4b3ec61Sdh 			    port % (us->us_largest_anon_port -
5683f4b3ec61Sdh 			    us->us_smallest_anon_port);
56847c478bd9Sstevel@tonic-gate 		}
56857c478bd9Sstevel@tonic-gate 	}
56867c478bd9Sstevel@tonic-gate 
56877c478bd9Sstevel@tonic-gate retry:
5688f4b3ec61Sdh 	if (port < us->us_smallest_anon_port)
5689f4b3ec61Sdh 		port = us->us_smallest_anon_port;
56907c478bd9Sstevel@tonic-gate 
5691f4b3ec61Sdh 	if (port > us->us_largest_anon_port) {
5692f4b3ec61Sdh 		port = us->us_smallest_anon_port;
569345916cd2Sjpk 		if (restart)
569445916cd2Sjpk 			return (0);
569545916cd2Sjpk 		restart = B_TRUE;
569645916cd2Sjpk 	}
569745916cd2Sjpk 
5698f4b3ec61Sdh 	if (port < us->us_smallest_nonpriv_port)
5699f4b3ec61Sdh 		port = us->us_smallest_nonpriv_port;
57007c478bd9Sstevel@tonic-gate 
5701f4b3ec61Sdh 	for (i = 0; i < us->us_num_epriv_ports; i++) {
5702f4b3ec61Sdh 		if (port == us->us_epriv_ports[i]) {
57037c478bd9Sstevel@tonic-gate 			port++;
57047c478bd9Sstevel@tonic-gate 			/*
57057c478bd9Sstevel@tonic-gate 			 * Make sure that the port is in the
57067c478bd9Sstevel@tonic-gate 			 * valid range.
57077c478bd9Sstevel@tonic-gate 			 */
57087c478bd9Sstevel@tonic-gate 			goto retry;
57097c478bd9Sstevel@tonic-gate 		}
57107c478bd9Sstevel@tonic-gate 	}
571145916cd2Sjpk 
571245916cd2Sjpk 	if (is_system_labeled() &&
571345916cd2Sjpk 	    (nextport = tsol_next_port(crgetzone(udp->udp_connp->conn_cred),
571445916cd2Sjpk 	    port, IPPROTO_UDP, B_TRUE)) != 0) {
571545916cd2Sjpk 		port = nextport;
571645916cd2Sjpk 		goto retry;
571745916cd2Sjpk 	}
571845916cd2Sjpk 
57197c478bd9Sstevel@tonic-gate 	return (port);
57207c478bd9Sstevel@tonic-gate }
57217c478bd9Sstevel@tonic-gate 
572245916cd2Sjpk static int
572345916cd2Sjpk udp_update_label(queue_t *wq, mblk_t *mp, ipaddr_t dst)
572445916cd2Sjpk {
572545916cd2Sjpk 	int err;
572645916cd2Sjpk 	uchar_t opt_storage[IP_MAX_OPT_LENGTH];
572745916cd2Sjpk 	udp_t *udp = Q_TO_UDP(wq);
5728fc80c0dfSnordmark 	udp_stack_t	*us = udp->udp_us;
572945916cd2Sjpk 
573045916cd2Sjpk 	err = tsol_compute_label(DB_CREDDEF(mp, udp->udp_connp->conn_cred), dst,
5731222c5bceSkp 	    opt_storage, udp->udp_connp->conn_mac_exempt,
5732fc80c0dfSnordmark 	    us->us_netstack->netstack_ip);
573345916cd2Sjpk 	if (err == 0) {
573445916cd2Sjpk 		err = tsol_update_options(&udp->udp_ip_snd_options,
573545916cd2Sjpk 		    &udp->udp_ip_snd_options_len, &udp->udp_label_len,
573645916cd2Sjpk 		    opt_storage);
573745916cd2Sjpk 	}
573845916cd2Sjpk 	if (err != 0) {
573945916cd2Sjpk 		DTRACE_PROBE4(
574045916cd2Sjpk 		    tx__ip__log__info__updatelabel__udp,
574145916cd2Sjpk 		    char *, "queue(1) failed to update options(2) on mp(3)",
574245916cd2Sjpk 		    queue_t *, wq, char *, opt_storage, mblk_t *, mp);
574345916cd2Sjpk 	} else {
574445916cd2Sjpk 		IN6_IPADDR_TO_V4MAPPED(dst, &udp->udp_v6lastdst);
574545916cd2Sjpk 	}
574645916cd2Sjpk 	return (err);
574745916cd2Sjpk }
574845916cd2Sjpk 
5749ff550d0eSmasputra static mblk_t *
5750ff550d0eSmasputra udp_output_v4(conn_t *connp, mblk_t *mp, ipaddr_t v4dst, uint16_t port,
5751437220cdSdanmcd     uint_t srcid, int *error, boolean_t insert_spi)
57527c478bd9Sstevel@tonic-gate {
5753ff550d0eSmasputra 	udp_t	*udp = connp->conn_udp;
5754ff550d0eSmasputra 	queue_t	*q = connp->conn_wq;
575545916cd2Sjpk 	mblk_t	*mp1 = mp;
5756ff550d0eSmasputra 	mblk_t	*mp2;
5757ff550d0eSmasputra 	ipha_t	*ipha;
5758ff550d0eSmasputra 	int	ip_hdr_length;
5759ff550d0eSmasputra 	uint32_t ip_len;
5760ff550d0eSmasputra 	udpha_t	*udpha;
5761fc80c0dfSnordmark 	boolean_t lock_held = B_FALSE;
5762fc80c0dfSnordmark 	in_port_t	uha_src_port;
576345916cd2Sjpk 	udpattrs_t	attrs;
5764dc3879f9Sjarrett 	uchar_t	ip_snd_opt[IP_MAX_OPT_LENGTH];
5765dc3879f9Sjarrett 	uint32_t	ip_snd_opt_len = 0;
576619a30e1aSrshoaib 	ip4_pkt_t  pktinfo;
576719a30e1aSrshoaib 	ip4_pkt_t  *pktinfop = &pktinfo;
576819a30e1aSrshoaib 	ip_opt_info_t optinfo;
5769f4b3ec61Sdh 	ip_stack_t	*ipst = connp->conn_netstack->netstack_ip;
5770f4b3ec61Sdh 	udp_stack_t	*us = udp->udp_us;
5771f4b3ec61Sdh 	ipsec_stack_t	*ipss = ipst->ips_netstack->netstack_ipsec;
577219a30e1aSrshoaib 
57737c478bd9Sstevel@tonic-gate 
5774ff550d0eSmasputra 	*error = 0;
577519a30e1aSrshoaib 	pktinfop->ip4_ill_index = 0;
577619a30e1aSrshoaib 	pktinfop->ip4_addr = INADDR_ANY;
577719a30e1aSrshoaib 	optinfo.ip_opt_flags = 0;
577819a30e1aSrshoaib 	optinfo.ip_opt_ill_index = 0;
57797c478bd9Sstevel@tonic-gate 
578045916cd2Sjpk 	if (v4dst == INADDR_ANY)
578145916cd2Sjpk 		v4dst = htonl(INADDR_LOOPBACK);
578245916cd2Sjpk 
578345916cd2Sjpk 	/*
578445916cd2Sjpk 	 * If options passed in, feed it for verification and handling
578545916cd2Sjpk 	 */
578645916cd2Sjpk 	attrs.udpattr_credset = B_FALSE;
578745916cd2Sjpk 	if (DB_TYPE(mp) != M_DATA) {
578845916cd2Sjpk 		mp1 = mp->b_cont;
578945916cd2Sjpk 		if (((struct T_unitdata_req *)mp->b_rptr)->OPT_length != 0) {
579019a30e1aSrshoaib 			attrs.udpattr_ipp4 = pktinfop;
579145916cd2Sjpk 			attrs.udpattr_mb = mp;
579245916cd2Sjpk 			if (udp_unitdata_opt_process(q, mp, error, &attrs) < 0)
579345916cd2Sjpk 				goto done;
579445916cd2Sjpk 			/*
579545916cd2Sjpk 			 * Note: success in processing options.
579645916cd2Sjpk 			 * mp option buffer represented by
579745916cd2Sjpk 			 * OPT_length/offset now potentially modified
579845916cd2Sjpk 			 * and contain option setting results
579945916cd2Sjpk 			 */
580045916cd2Sjpk 			ASSERT(*error == 0);
580145916cd2Sjpk 		}
580245916cd2Sjpk 	}
580345916cd2Sjpk 
5804ff550d0eSmasputra 	/* mp1 points to the M_DATA mblk carrying the packet */
5805ff550d0eSmasputra 	ASSERT(mp1 != NULL && DB_TYPE(mp1) == M_DATA);
58067c478bd9Sstevel@tonic-gate 
5807fc80c0dfSnordmark 	rw_enter(&udp->udp_rwlock, RW_READER);
5808fc80c0dfSnordmark 	lock_held = B_TRUE;
5809dc3879f9Sjarrett 	/*
581003986916Sjarrett 	 * Check if our saved options are valid; update if not.
5811dc3879f9Sjarrett 	 * TSOL Note: Since we are not in WRITER mode, UDP packets
581203986916Sjarrett 	 * to different destination may require different labels,
581303986916Sjarrett 	 * or worse, UDP packets to same IP address may require
581403986916Sjarrett 	 * different labels due to use of shared all-zones address.
5815dc3879f9Sjarrett 	 * We use conn_lock to ensure that lastdst, ip_snd_options,
5816dc3879f9Sjarrett 	 * and ip_snd_options_len are consistent for the current
5817dc3879f9Sjarrett 	 * destination and are updated atomically.
5818dc3879f9Sjarrett 	 */
5819dc3879f9Sjarrett 	mutex_enter(&connp->conn_lock);
582045916cd2Sjpk 	if (is_system_labeled()) {
582145916cd2Sjpk 		/* Using UDP MLP requires SCM_UCRED from user */
582245916cd2Sjpk 		if (connp->conn_mlp_type != mlptSingle &&
582345916cd2Sjpk 		    !attrs.udpattr_credset) {
5824dc3879f9Sjarrett 			mutex_exit(&connp->conn_lock);
582545916cd2Sjpk 			DTRACE_PROBE4(
582645916cd2Sjpk 			    tx__ip__log__info__output__udp,
582745916cd2Sjpk 			    char *, "MLP mp(1) lacks SCM_UCRED attr(2) on q(3)",
582845916cd2Sjpk 			    mblk_t *, mp1, udpattrs_t *, &attrs, queue_t *, q);
582945916cd2Sjpk 			*error = ECONNREFUSED;
583045916cd2Sjpk 			goto done;
583145916cd2Sjpk 		}
583203986916Sjarrett 		/*
583303986916Sjarrett 		 * update label option for this UDP socket if
583403986916Sjarrett 		 * - the destination has changed, or
583503986916Sjarrett 		 * - the UDP socket is MLP
583603986916Sjarrett 		 */
583745916cd2Sjpk 		if ((!IN6_IS_ADDR_V4MAPPED(&udp->udp_v6lastdst) ||
583803986916Sjarrett 		    V4_PART_OF_V6(udp->udp_v6lastdst) != v4dst ||
583903986916Sjarrett 		    connp->conn_mlp_type != mlptSingle) &&
5840dc3879f9Sjarrett 		    (*error = udp_update_label(q, mp, v4dst)) != 0) {
5841dc3879f9Sjarrett 			mutex_exit(&connp->conn_lock);
584245916cd2Sjpk 			goto done;
5843dc3879f9Sjarrett 		}
584445916cd2Sjpk 	}
5845dc3879f9Sjarrett 	if (udp->udp_ip_snd_options_len > 0) {
5846dc3879f9Sjarrett 		ip_snd_opt_len = udp->udp_ip_snd_options_len;
5847dc3879f9Sjarrett 		bcopy(udp->udp_ip_snd_options, ip_snd_opt, ip_snd_opt_len);
5848dc3879f9Sjarrett 	}
5849dc3879f9Sjarrett 	mutex_exit(&connp->conn_lock);
585045916cd2Sjpk 
5851ff550d0eSmasputra 	/* Add an IP header */
5852437220cdSdanmcd 	ip_hdr_length = IP_SIMPLE_HDR_LENGTH + UDPH_SIZE + ip_snd_opt_len +
5853437220cdSdanmcd 	    (insert_spi ? sizeof (uint32_t) : 0);
5854ff550d0eSmasputra 	ipha = (ipha_t *)&mp1->b_rptr[-ip_hdr_length];
5855ff550d0eSmasputra 	if (DB_REF(mp1) != 1 || (uchar_t *)ipha < DB_BASE(mp1) ||
5856ff550d0eSmasputra 	    !OK_32PTR(ipha)) {
5857f4b3ec61Sdh 		mp2 = allocb(ip_hdr_length + us->us_wroff_extra, BPRI_LO);
5858ff550d0eSmasputra 		if (mp2 == NULL) {
5859ff550d0eSmasputra 			TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_END,
5860ff550d0eSmasputra 			    "udp_wput_end: q %p (%S)", q, "allocbfail2");
5861ff550d0eSmasputra 			*error = ENOMEM;
5862ff550d0eSmasputra 			goto done;
5863ff550d0eSmasputra 		}
5864ff550d0eSmasputra 		mp2->b_wptr = DB_LIM(mp2);
5865ff550d0eSmasputra 		mp2->b_cont = mp1;
5866ff550d0eSmasputra 		mp1 = mp2;
5867ff550d0eSmasputra 		if (DB_TYPE(mp) != M_DATA)
5868ff550d0eSmasputra 			mp->b_cont = mp1;
5869ff550d0eSmasputra 		else
5870ff550d0eSmasputra 			mp = mp1;
58717c478bd9Sstevel@tonic-gate 
5872ff550d0eSmasputra 		ipha = (ipha_t *)(mp1->b_wptr - ip_hdr_length);
58737c478bd9Sstevel@tonic-gate 	}
5874437220cdSdanmcd 	ip_hdr_length -= (UDPH_SIZE + (insert_spi ? sizeof (uint32_t) : 0));
5875ff550d0eSmasputra #ifdef	_BIG_ENDIAN
5876ff550d0eSmasputra 	/* Set version, header length, and tos */
5877ff550d0eSmasputra 	*(uint16_t *)&ipha->ipha_version_and_hdr_length =
5878ff550d0eSmasputra 	    ((((IP_VERSION << 4) | (ip_hdr_length>>2)) << 8) |
5879437220cdSdanmcd 	    udp->udp_type_of_service);
5880ff550d0eSmasputra 	/* Set ttl and protocol */
5881ff550d0eSmasputra 	*(uint16_t *)&ipha->ipha_ttl = (udp->udp_ttl << 8) | IPPROTO_UDP;
5882ff550d0eSmasputra #else
5883ff550d0eSmasputra 	/* Set version, header length, and tos */
5884ff550d0eSmasputra 	*(uint16_t *)&ipha->ipha_version_and_hdr_length =
5885437220cdSdanmcd 	    ((udp->udp_type_of_service << 8) |
5886437220cdSdanmcd 	    ((IP_VERSION << 4) | (ip_hdr_length>>2)));
5887ff550d0eSmasputra 	/* Set ttl and protocol */
5888ff550d0eSmasputra 	*(uint16_t *)&ipha->ipha_ttl = (IPPROTO_UDP << 8) | udp->udp_ttl;
5889ff550d0eSmasputra #endif
589019a30e1aSrshoaib 	if (pktinfop->ip4_addr != INADDR_ANY) {
589119a30e1aSrshoaib 		ipha->ipha_src = pktinfop->ip4_addr;
589219a30e1aSrshoaib 		optinfo.ip_opt_flags = IP_VERIFY_SRC;
589319a30e1aSrshoaib 	} else {
589419a30e1aSrshoaib 		/*
589519a30e1aSrshoaib 		 * Copy our address into the packet.  If this is zero,
589619a30e1aSrshoaib 		 * first look at __sin6_src_id for a hint. If we leave the
589719a30e1aSrshoaib 		 * source as INADDR_ANY then ip will fill in the real source
589819a30e1aSrshoaib 		 * address.
589919a30e1aSrshoaib 		 */
590019a30e1aSrshoaib 		IN6_V4MAPPED_TO_IPADDR(&udp->udp_v6src, ipha->ipha_src);
590119a30e1aSrshoaib 		if (srcid != 0 && ipha->ipha_src == INADDR_ANY) {
590219a30e1aSrshoaib 			in6_addr_t v6src;
59037c478bd9Sstevel@tonic-gate 
5904f4b3ec61Sdh 			ip_srcid_find_id(srcid, &v6src, connp->conn_zoneid,
5905f4b3ec61Sdh 			    us->us_netstack);
590619a30e1aSrshoaib 			IN6_V4MAPPED_TO_IPADDR(&v6src, ipha->ipha_src);
590719a30e1aSrshoaib 		}
590819a30e1aSrshoaib 	}
5909fc80c0dfSnordmark 	uha_src_port = udp->udp_port;
5910fc80c0dfSnordmark 	if (ip_hdr_length == IP_SIMPLE_HDR_LENGTH) {
5911fc80c0dfSnordmark 		rw_exit(&udp->udp_rwlock);
5912fc80c0dfSnordmark 		lock_held = B_FALSE;
5913fc80c0dfSnordmark 	}
591419a30e1aSrshoaib 
591519a30e1aSrshoaib 	if (pktinfop->ip4_ill_index != 0) {
591619a30e1aSrshoaib 		optinfo.ip_opt_ill_index = pktinfop->ip4_ill_index;
59177c478bd9Sstevel@tonic-gate 	}
59187c478bd9Sstevel@tonic-gate 
5919ff550d0eSmasputra 	ipha->ipha_fragment_offset_and_flags = 0;
5920ff550d0eSmasputra 	ipha->ipha_ident = 0;
59217c478bd9Sstevel@tonic-gate 
5922ff550d0eSmasputra 	mp1->b_rptr = (uchar_t *)ipha;
59237c478bd9Sstevel@tonic-gate 
59247c478bd9Sstevel@tonic-gate 	ASSERT((uintptr_t)(mp1->b_wptr - (uchar_t *)ipha) <=
59257c478bd9Sstevel@tonic-gate 	    (uintptr_t)UINT_MAX);
59267c478bd9Sstevel@tonic-gate 
59277c478bd9Sstevel@tonic-gate 	/* Determine length of packet */
59287c478bd9Sstevel@tonic-gate 	ip_len = (uint32_t)(mp1->b_wptr - (uchar_t *)ipha);
5929ff550d0eSmasputra 	if ((mp2 = mp1->b_cont) != NULL) {
5930ff550d0eSmasputra 		do {
5931ff550d0eSmasputra 			ASSERT((uintptr_t)MBLKL(mp2) <= (uintptr_t)UINT_MAX);
5932ff550d0eSmasputra 			ip_len += (uint32_t)MBLKL(mp2);
5933ff550d0eSmasputra 		} while ((mp2 = mp2->b_cont) != NULL);
59347c478bd9Sstevel@tonic-gate 	}
59357c478bd9Sstevel@tonic-gate 	/*
59367c478bd9Sstevel@tonic-gate 	 * If the size of the packet is greater than the maximum allowed by
59377c478bd9Sstevel@tonic-gate 	 * ip, return an error. Passing this down could cause panics because
59387c478bd9Sstevel@tonic-gate 	 * the size will have wrapped and be inconsistent with the msg size.
59397c478bd9Sstevel@tonic-gate 	 */
59407c478bd9Sstevel@tonic-gate 	if (ip_len > IP_MAXPACKET) {
59417c478bd9Sstevel@tonic-gate 		TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_END,
59427c478bd9Sstevel@tonic-gate 		    "udp_wput_end: q %p (%S)", q, "IP length exceeded");
5943ff550d0eSmasputra 		*error = EMSGSIZE;
5944ff550d0eSmasputra 		goto done;
59457c478bd9Sstevel@tonic-gate 	}
59467c478bd9Sstevel@tonic-gate 	ipha->ipha_length = htons((uint16_t)ip_len);
59477c478bd9Sstevel@tonic-gate 	ip_len -= ip_hdr_length;
59487c478bd9Sstevel@tonic-gate 	ip_len = htons((uint16_t)ip_len);
59497c478bd9Sstevel@tonic-gate 	udpha = (udpha_t *)(((uchar_t *)ipha) + ip_hdr_length);
5950ff550d0eSmasputra 
5951437220cdSdanmcd 	/* Insert all-0s SPI now. */
5952437220cdSdanmcd 	if (insert_spi)
5953437220cdSdanmcd 		*((uint32_t *)(udpha + 1)) = 0;
5954437220cdSdanmcd 
59557c478bd9Sstevel@tonic-gate 	/*
5956ff550d0eSmasputra 	 * Copy in the destination address
59577c478bd9Sstevel@tonic-gate 	 */
595845916cd2Sjpk 	ipha->ipha_dst = v4dst;
59597c478bd9Sstevel@tonic-gate 
59607c478bd9Sstevel@tonic-gate 	/*
59617c478bd9Sstevel@tonic-gate 	 * Set ttl based on IP_MULTICAST_TTL to match IPv6 logic.
59627c478bd9Sstevel@tonic-gate 	 */
59637c478bd9Sstevel@tonic-gate 	if (CLASSD(v4dst))
59647c478bd9Sstevel@tonic-gate 		ipha->ipha_ttl = udp->udp_multicast_ttl;
59657c478bd9Sstevel@tonic-gate 
59667c478bd9Sstevel@tonic-gate 	udpha->uha_dst_port = port;
5967fc80c0dfSnordmark 	udpha->uha_src_port = uha_src_port;
59687c478bd9Sstevel@tonic-gate 
5969437220cdSdanmcd 	if (ip_snd_opt_len > 0) {
59707c478bd9Sstevel@tonic-gate 		uint32_t	cksum;
59717c478bd9Sstevel@tonic-gate 
5972dc3879f9Sjarrett 		bcopy(ip_snd_opt, &ipha[1], ip_snd_opt_len);
5973fc80c0dfSnordmark 		lock_held = B_FALSE;
5974fc80c0dfSnordmark 		rw_exit(&udp->udp_rwlock);
59757c478bd9Sstevel@tonic-gate 		/*
59767c478bd9Sstevel@tonic-gate 		 * Massage source route putting first source route in ipha_dst.
59777c478bd9Sstevel@tonic-gate 		 * Ignore the destination in T_unitdata_req.
59787c478bd9Sstevel@tonic-gate 		 * Create a checksum adjustment for a source route, if any.
59797c478bd9Sstevel@tonic-gate 		 */
5980f4b3ec61Sdh 		cksum = ip_massage_options(ipha, us->us_netstack);
59817c478bd9Sstevel@tonic-gate 		cksum = (cksum & 0xFFFF) + (cksum >> 16);
59827c478bd9Sstevel@tonic-gate 		cksum -= ((ipha->ipha_dst >> 16) & 0xFFFF) +
59837c478bd9Sstevel@tonic-gate 		    (ipha->ipha_dst & 0xFFFF);
59847c478bd9Sstevel@tonic-gate 		if ((int)cksum < 0)
59857c478bd9Sstevel@tonic-gate 			cksum--;
59867c478bd9Sstevel@tonic-gate 		cksum = (cksum & 0xFFFF) + (cksum >> 16);
59877c478bd9Sstevel@tonic-gate 		/*
59887c478bd9Sstevel@tonic-gate 		 * IP does the checksum if uha_checksum is non-zero,
59897c478bd9Sstevel@tonic-gate 		 * We make it easy for IP to include our pseudo header
59907c478bd9Sstevel@tonic-gate 		 * by putting our length in uha_checksum.
59917c478bd9Sstevel@tonic-gate 		 */
59927c478bd9Sstevel@tonic-gate 		cksum += ip_len;
59937c478bd9Sstevel@tonic-gate 		cksum = (cksum & 0xFFFF) + (cksum >> 16);
59947c478bd9Sstevel@tonic-gate 		/* There might be a carry. */
59957c478bd9Sstevel@tonic-gate 		cksum = (cksum & 0xFFFF) + (cksum >> 16);
59967c478bd9Sstevel@tonic-gate #ifdef _LITTLE_ENDIAN
5997f4b3ec61Sdh 		if (us->us_do_checksum)
59987c478bd9Sstevel@tonic-gate 			ip_len = (cksum << 16) | ip_len;
59997c478bd9Sstevel@tonic-gate #else
6000f4b3ec61Sdh 		if (us->us_do_checksum)
60017c478bd9Sstevel@tonic-gate 			ip_len = (ip_len << 16) | cksum;
60027c478bd9Sstevel@tonic-gate 		else
60037c478bd9Sstevel@tonic-gate 			ip_len <<= 16;
60047c478bd9Sstevel@tonic-gate #endif
60057c478bd9Sstevel@tonic-gate 	} else {
60067c478bd9Sstevel@tonic-gate 		/*
60077c478bd9Sstevel@tonic-gate 		 * IP does the checksum if uha_checksum is non-zero,
60087c478bd9Sstevel@tonic-gate 		 * We make it easy for IP to include our pseudo header
60097c478bd9Sstevel@tonic-gate 		 * by putting our length in uha_checksum.
60107c478bd9Sstevel@tonic-gate 		 */
6011f4b3ec61Sdh 		if (us->us_do_checksum)
60127c478bd9Sstevel@tonic-gate 			ip_len |= (ip_len << 16);
60137c478bd9Sstevel@tonic-gate #ifndef _LITTLE_ENDIAN
60147c478bd9Sstevel@tonic-gate 		else
60157c478bd9Sstevel@tonic-gate 			ip_len <<= 16;
60167c478bd9Sstevel@tonic-gate #endif
60177c478bd9Sstevel@tonic-gate 	}
6018fc80c0dfSnordmark 	ASSERT(!lock_held);
60197c478bd9Sstevel@tonic-gate 	/* Set UDP length and checksum */
60207c478bd9Sstevel@tonic-gate 	*((uint32_t *)&udpha->uha_length) = ip_len;
602145916cd2Sjpk 	if (DB_CRED(mp) != NULL)
602245916cd2Sjpk 		mblk_setcred(mp1, DB_CRED(mp));
60237c478bd9Sstevel@tonic-gate 
6024ff550d0eSmasputra 	if (DB_TYPE(mp) != M_DATA) {
6025ff550d0eSmasputra 		ASSERT(mp != mp1);
6026ff550d0eSmasputra 		freeb(mp);
6027ff550d0eSmasputra 	}
6028ff550d0eSmasputra 
6029ff550d0eSmasputra 	/* mp has been consumed and we'll return success */
6030ff550d0eSmasputra 	ASSERT(*error == 0);
6031ff550d0eSmasputra 	mp = NULL;
60327c478bd9Sstevel@tonic-gate 
60337c478bd9Sstevel@tonic-gate 	/* We're done.  Pass the packet to ip. */
6034fc80c0dfSnordmark 	BUMP_MIB(&us->us_udp_mib, udpHCOutDatagrams);
60357c478bd9Sstevel@tonic-gate 	TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_END,
6036437220cdSdanmcd 	    "udp_wput_end: q %p (%S)", q, "end");
6037ff550d0eSmasputra 
6038ff550d0eSmasputra 	if ((connp->conn_flags & IPCL_CHECK_POLICY) != 0 ||
6039f4b3ec61Sdh 	    CONN_OUTBOUND_POLICY_PRESENT(connp, ipss) ||
6040e704a8f2Smeem 	    connp->conn_dontroute ||
6041ff550d0eSmasputra 	    connp->conn_nofailover_ill != NULL ||
604219a30e1aSrshoaib 	    connp->conn_outgoing_ill != NULL || optinfo.ip_opt_flags != 0 ||
604319a30e1aSrshoaib 	    optinfo.ip_opt_ill_index != 0 ||
6044ff550d0eSmasputra 	    ipha->ipha_version_and_hdr_length != IP_SIMPLE_HDR_VERSION ||
6045f4b3ec61Sdh 	    IPP_ENABLED(IPP_LOCAL_OUT, ipst) ||
6046f4b3ec61Sdh 	    ipst->ips_ip_g_mrouter != NULL) {
6047f4b3ec61Sdh 		UDP_STAT(us, udp_ip_send);
604819a30e1aSrshoaib 		ip_output_options(connp, mp1, connp->conn_wq, IP_WPUT,
604919a30e1aSrshoaib 		    &optinfo);
6050ff550d0eSmasputra 	} else {
6051ff550d0eSmasputra 		udp_send_data(udp, connp->conn_wq, mp1, ipha);
6052ff550d0eSmasputra 	}
6053ff550d0eSmasputra 
6054ff550d0eSmasputra done:
6055fc80c0dfSnordmark 	if (lock_held)
6056fc80c0dfSnordmark 		rw_exit(&udp->udp_rwlock);
6057ff550d0eSmasputra 	if (*error != 0) {
6058ff550d0eSmasputra 		ASSERT(mp != NULL);
6059fc80c0dfSnordmark 		BUMP_MIB(&us->us_udp_mib, udpOutErrors);
6060ff550d0eSmasputra 	}
6061ff550d0eSmasputra 	return (mp);
6062ff550d0eSmasputra }
6063ff550d0eSmasputra 
6064ff550d0eSmasputra static void
6065ff550d0eSmasputra udp_send_data(udp_t *udp, queue_t *q, mblk_t *mp, ipha_t *ipha)
6066ff550d0eSmasputra {
6067ff550d0eSmasputra 	conn_t	*connp = udp->udp_connp;
6068ff550d0eSmasputra 	ipaddr_t src, dst;
6069ff550d0eSmasputra 	ire_t	*ire;
6070ff550d0eSmasputra 	ipif_t	*ipif = NULL;
6071ff550d0eSmasputra 	mblk_t	*ire_fp_mp;
6072ff550d0eSmasputra 	boolean_t retry_caching;
6073f4b3ec61Sdh 	udp_stack_t *us = udp->udp_us;
6074f4b3ec61Sdh 	ip_stack_t	*ipst = connp->conn_netstack->netstack_ip;
6075ff550d0eSmasputra 
6076ff550d0eSmasputra 	dst = ipha->ipha_dst;
6077ff550d0eSmasputra 	src = ipha->ipha_src;
6078ff550d0eSmasputra 	ASSERT(ipha->ipha_ident == 0);
6079ff550d0eSmasputra 
6080ff550d0eSmasputra 	if (CLASSD(dst)) {
6081ff550d0eSmasputra 		int err;
6082ff550d0eSmasputra 
6083ff550d0eSmasputra 		ipif = conn_get_held_ipif(connp,
6084ff550d0eSmasputra 		    &connp->conn_multicast_ipif, &err);
6085ff550d0eSmasputra 
6086ff550d0eSmasputra 		if (ipif == NULL || ipif->ipif_isv6 ||
6087ff550d0eSmasputra 		    (ipif->ipif_ill->ill_phyint->phyint_flags &
6088ff550d0eSmasputra 		    PHYI_LOOPBACK)) {
6089ff550d0eSmasputra 			if (ipif != NULL)
6090ff550d0eSmasputra 				ipif_refrele(ipif);
6091f4b3ec61Sdh 			UDP_STAT(us, udp_ip_send);
6092ff550d0eSmasputra 			ip_output(connp, mp, q, IP_WPUT);
6093ff550d0eSmasputra 			return;
6094ff550d0eSmasputra 		}
6095ff550d0eSmasputra 	}
6096ff550d0eSmasputra 
6097ff550d0eSmasputra 	retry_caching = B_FALSE;
6098ff550d0eSmasputra 	mutex_enter(&connp->conn_lock);
6099ff550d0eSmasputra 	ire = connp->conn_ire_cache;
6100ff550d0eSmasputra 	ASSERT(!(connp->conn_state_flags & CONN_INCIPIENT));
6101ff550d0eSmasputra 
6102ff550d0eSmasputra 	if (ire == NULL || ire->ire_addr != dst ||
6103ff550d0eSmasputra 	    (ire->ire_marks & IRE_MARK_CONDEMNED)) {
6104ff550d0eSmasputra 		retry_caching = B_TRUE;
6105ff550d0eSmasputra 	} else if (CLASSD(dst) && (ire->ire_type & IRE_CACHE)) {
6106ff550d0eSmasputra 		ill_t *stq_ill = (ill_t *)ire->ire_stq->q_ptr;
6107ff550d0eSmasputra 
6108ff550d0eSmasputra 		ASSERT(ipif != NULL);
6109ff550d0eSmasputra 		if (stq_ill != ipif->ipif_ill && (stq_ill->ill_group == NULL ||
6110ff550d0eSmasputra 		    stq_ill->ill_group != ipif->ipif_ill->ill_group))
6111ff550d0eSmasputra 			retry_caching = B_TRUE;
6112ff550d0eSmasputra 	}
6113ff550d0eSmasputra 
6114ff550d0eSmasputra 	if (!retry_caching) {
6115ff550d0eSmasputra 		ASSERT(ire != NULL);
6116ff550d0eSmasputra 		IRE_REFHOLD(ire);
6117ff550d0eSmasputra 		mutex_exit(&connp->conn_lock);
6118ff550d0eSmasputra 	} else {
6119ff550d0eSmasputra 		boolean_t cached = B_FALSE;
6120ff550d0eSmasputra 
6121ff550d0eSmasputra 		connp->conn_ire_cache = NULL;
6122ff550d0eSmasputra 		mutex_exit(&connp->conn_lock);
6123ff550d0eSmasputra 
6124ff550d0eSmasputra 		/* Release the old ire */
6125ff550d0eSmasputra 		if (ire != NULL) {
6126ff550d0eSmasputra 			IRE_REFRELE_NOTR(ire);
6127ff550d0eSmasputra 			ire = NULL;
6128ff550d0eSmasputra 		}
6129ff550d0eSmasputra 
6130ff550d0eSmasputra 		if (CLASSD(dst)) {
6131ff550d0eSmasputra 			ASSERT(ipif != NULL);
6132ff550d0eSmasputra 			ire = ire_ctable_lookup(dst, 0, 0, ipif,
613345916cd2Sjpk 			    connp->conn_zoneid, MBLK_GETLABEL(mp),
6134f4b3ec61Sdh 			    MATCH_IRE_ILL_GROUP, ipst);
6135ff550d0eSmasputra 		} else {
6136ff550d0eSmasputra 			ASSERT(ipif == NULL);
613745916cd2Sjpk 			ire = ire_cache_lookup(dst, connp->conn_zoneid,
6138f4b3ec61Sdh 			    MBLK_GETLABEL(mp), ipst);
6139ff550d0eSmasputra 		}
6140ff550d0eSmasputra 
6141ff550d0eSmasputra 		if (ire == NULL) {
6142ff550d0eSmasputra 			if (ipif != NULL)
6143ff550d0eSmasputra 				ipif_refrele(ipif);
6144f4b3ec61Sdh 			UDP_STAT(us, udp_ire_null);
6145ff550d0eSmasputra 			ip_output(connp, mp, q, IP_WPUT);
6146ff550d0eSmasputra 			return;
6147ff550d0eSmasputra 		}
6148ff550d0eSmasputra 		IRE_REFHOLD_NOTR(ire);
6149ff550d0eSmasputra 
6150ff550d0eSmasputra 		mutex_enter(&connp->conn_lock);
61519a09d68dSja 		if (CONN_CACHE_IRE(connp) && connp->conn_ire_cache == NULL &&
61529a09d68dSja 		    !(ire->ire_marks & IRE_MARK_CONDEMNED)) {
61539a09d68dSja 			irb_t		*irb = ire->ire_bucket;
61549a09d68dSja 
61559a09d68dSja 			/*
61569a09d68dSja 			 * IRE's created for non-connection oriented transports
61579a09d68dSja 			 * are normally initialized with IRE_MARK_TEMPORARY set
61589a09d68dSja 			 * in the ire_marks. These IRE's are preferentially
61599a09d68dSja 			 * reaped when the hash chain length in the cache
61609a09d68dSja 			 * bucket exceeds the maximum value specified in
61619a09d68dSja 			 * ip[6]_ire_max_bucket_cnt. This can severely affect
61629a09d68dSja 			 * UDP performance if IRE cache entries that we need
61639a09d68dSja 			 * to reuse are continually removed. To remedy this,
61649a09d68dSja 			 * when we cache the IRE in the conn_t, we remove the
61659a09d68dSja 			 * IRE_MARK_TEMPORARY bit from the ire_marks if it was
61669a09d68dSja 			 * set.
61679a09d68dSja 			 */
61689a09d68dSja 			if (ire->ire_marks & IRE_MARK_TEMPORARY) {
61699a09d68dSja 				rw_enter(&irb->irb_lock, RW_WRITER);
61709a09d68dSja 				if (ire->ire_marks & IRE_MARK_TEMPORARY) {
61719a09d68dSja 					ire->ire_marks &= ~IRE_MARK_TEMPORARY;
61729a09d68dSja 					irb->irb_tmp_ire_cnt--;
61739a09d68dSja 				}
61749a09d68dSja 				rw_exit(&irb->irb_lock);
6175ff550d0eSmasputra 			}
61769a09d68dSja 			connp->conn_ire_cache = ire;
61779a09d68dSja 			cached = B_TRUE;
6178ff550d0eSmasputra 		}
6179ff550d0eSmasputra 		mutex_exit(&connp->conn_lock);
6180ff550d0eSmasputra 
6181ff550d0eSmasputra 		/*
6182ff550d0eSmasputra 		 * We can continue to use the ire but since it was not
6183ff550d0eSmasputra 		 * cached, we should drop the extra reference.
6184ff550d0eSmasputra 		 */
6185ff550d0eSmasputra 		if (!cached)
6186ff550d0eSmasputra 			IRE_REFRELE_NOTR(ire);
6187ff550d0eSmasputra 	}
6188ff550d0eSmasputra 	ASSERT(ire != NULL && ire->ire_ipversion == IPV4_VERSION);
6189ff550d0eSmasputra 	ASSERT(!CLASSD(dst) || ipif != NULL);
6190ff550d0eSmasputra 
6191c793af95Ssangeeta 	/*
6192c793af95Ssangeeta 	 * Check if we can take the fast-path.
6193c793af95Ssangeeta 	 * Note that "incomplete" ire's (where the link-layer for next hop
6194c793af95Ssangeeta 	 * is not resolved, or where the fast-path header in nce_fp_mp is not
6195c793af95Ssangeeta 	 * available yet) are sent down the legacy (slow) path
6196c793af95Ssangeeta 	 */
6197ff550d0eSmasputra 	if ((ire->ire_type & (IRE_BROADCAST|IRE_LOCAL|IRE_LOOPBACK)) ||
61988347601bSyl 	    (ire->ire_flags & RTF_MULTIRT) || (ire->ire_stq == NULL) ||
61998347601bSyl 	    (ire->ire_max_frag < ntohs(ipha->ipha_length)) ||
6200fc80c0dfSnordmark 	    ((ire->ire_nce == NULL) ||
6201fc80c0dfSnordmark 	    ((ire_fp_mp = ire->ire_nce->nce_fp_mp) == NULL)) ||
6202fc80c0dfSnordmark 	    connp->conn_nexthop_set || (MBLKL(ire_fp_mp) > MBLKHEAD(mp))) {
6203ff550d0eSmasputra 		if (ipif != NULL)
6204ff550d0eSmasputra 			ipif_refrele(ipif);
6205f4b3ec61Sdh 		UDP_STAT(us, udp_ip_ire_send);
6206ff550d0eSmasputra 		IRE_REFRELE(ire);
6207ff550d0eSmasputra 		ip_output(connp, mp, q, IP_WPUT);
6208ff550d0eSmasputra 		return;
6209ff550d0eSmasputra 	}
6210ff550d0eSmasputra 
6211fc80c0dfSnordmark 	if (src == INADDR_ANY && !connp->conn_unspec_src) {
6212fc80c0dfSnordmark 		if (CLASSD(dst) && !(ire->ire_flags & RTF_SETSRC))
6213fc80c0dfSnordmark 			ipha->ipha_src = ipif->ipif_src_addr;
6214fc80c0dfSnordmark 		else
6215fc80c0dfSnordmark 			ipha->ipha_src = ire->ire_src_addr;
6216fc80c0dfSnordmark 	}
6217ff550d0eSmasputra 
6218fc80c0dfSnordmark 	if (ipif != NULL)
6219fc80c0dfSnordmark 		ipif_refrele(ipif);
6220fc80c0dfSnordmark 
6221fc80c0dfSnordmark 	udp_xmit(connp->conn_wq, mp, ire, connp, connp->conn_zoneid);
6222fc80c0dfSnordmark }
6223fc80c0dfSnordmark 
6224fc80c0dfSnordmark static void
6225fc80c0dfSnordmark udp_xmit(queue_t *q, mblk_t *mp, ire_t *ire, conn_t *connp, zoneid_t zoneid)
6226fc80c0dfSnordmark {
6227fc80c0dfSnordmark 	ipaddr_t src, dst;
6228fc80c0dfSnordmark 	ill_t	*ill;
6229fc80c0dfSnordmark 	mblk_t	*ire_fp_mp;
6230fc80c0dfSnordmark 	uint_t	ire_fp_mp_len;
6231fc80c0dfSnordmark 	uint16_t *up;
6232fc80c0dfSnordmark 	uint32_t cksum, hcksum_txflags;
6233fc80c0dfSnordmark 	queue_t	*dev_q;
6234fc80c0dfSnordmark 	udp_t	*udp = connp->conn_udp;
6235fc80c0dfSnordmark 	ipha_t	*ipha = (ipha_t *)mp->b_rptr;
6236fc80c0dfSnordmark 	udp_stack_t	*us = udp->udp_us;
6237fc80c0dfSnordmark 	ip_stack_t	*ipst = connp->conn_netstack->netstack_ip;
62381b47e080Sdr 	boolean_t ll_multicast = B_FALSE;
62393173664eSapersson 
6240ff550d0eSmasputra 	dev_q = ire->ire_stq->q_next;
6241ff550d0eSmasputra 	ASSERT(dev_q != NULL);
6242fc80c0dfSnordmark 
6243*da14cebeSEric Cheng 	ill = ire_to_ill(ire);
6244*da14cebeSEric Cheng 	ASSERT(ill != NULL);
6245fc80c0dfSnordmark 
6246*da14cebeSEric Cheng 	/* is queue flow controlled? */
6247*da14cebeSEric Cheng 	if (q->q_first != NULL || connp->conn_draining ||
6248*da14cebeSEric Cheng 	    DEV_Q_FLOW_BLOCKED(dev_q)) {
6249fc80c0dfSnordmark 		BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsHCOutRequests);
6250fc80c0dfSnordmark 		BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsOutDiscards);
62515c54b31dSnordmark 		if (ipst->ips_ip_output_queue)
62525c54b31dSnordmark 			(void) putq(connp->conn_wq, mp);
62535c54b31dSnordmark 		else
62545c54b31dSnordmark 			freemsg(mp);
6255fc80c0dfSnordmark 		ire_refrele(ire);
6256ff550d0eSmasputra 		return;
6257ff550d0eSmasputra 	}
6258ff550d0eSmasputra 
6259fc80c0dfSnordmark 	ire_fp_mp = ire->ire_nce->nce_fp_mp;
6260fc80c0dfSnordmark 	ire_fp_mp_len = MBLKL(ire_fp_mp);
6261fc80c0dfSnordmark 	ASSERT(MBLKHEAD(mp) >= ire_fp_mp_len);
6262fc80c0dfSnordmark 
6263fc80c0dfSnordmark 	dst = ipha->ipha_dst;
6264fc80c0dfSnordmark 	src = ipha->ipha_src;
6265fc80c0dfSnordmark 
6266fc80c0dfSnordmark 
6267fc80c0dfSnordmark 	BUMP_MIB(ill->ill_ip_mib, ipIfStatsHCOutRequests);
6268fc80c0dfSnordmark 
6269ff550d0eSmasputra 	ipha->ipha_ident = (uint16_t)atomic_add_32_nv(&ire->ire_ident, 1);
6270ff550d0eSmasputra #ifndef _BIG_ENDIAN
6271ff550d0eSmasputra 	ipha->ipha_ident = (ipha->ipha_ident << 8) | (ipha->ipha_ident >> 8);
6272ff550d0eSmasputra #endif
6273ff550d0eSmasputra 
6274ff550d0eSmasputra 	if (ILL_HCKSUM_CAPABLE(ill) && dohwcksum) {
6275ff550d0eSmasputra 		ASSERT(ill->ill_hcksum_capab != NULL);
6276ff550d0eSmasputra 		hcksum_txflags = ill->ill_hcksum_capab->ill_hcksum_txflags;
6277ff550d0eSmasputra 	} else {
6278ff550d0eSmasputra 		hcksum_txflags = 0;
6279ff550d0eSmasputra 	}
6280ff550d0eSmasputra 
6281ff550d0eSmasputra 	/* pseudo-header checksum (do it in parts for IP header checksum) */
6282ff550d0eSmasputra 	cksum = (dst >> 16) + (dst & 0xFFFF) + (src >> 16) + (src & 0xFFFF);
6283ff550d0eSmasputra 
6284ff550d0eSmasputra 	ASSERT(ipha->ipha_version_and_hdr_length == IP_SIMPLE_HDR_VERSION);
6285ff550d0eSmasputra 	up = IPH_UDPH_CHECKSUMP(ipha, IP_SIMPLE_HDR_LENGTH);
6286ff550d0eSmasputra 	if (*up != 0) {
6287ff550d0eSmasputra 		IP_CKSUM_XMIT_FAST(ire->ire_ipversion, hcksum_txflags,
6288ff550d0eSmasputra 		    mp, ipha, up, IPPROTO_UDP, IP_SIMPLE_HDR_LENGTH,
6289ff550d0eSmasputra 		    ntohs(ipha->ipha_length), cksum);
6290ff550d0eSmasputra 
6291ff550d0eSmasputra 		/* Software checksum? */
6292ff550d0eSmasputra 		if (DB_CKSUMFLAGS(mp) == 0) {
6293f4b3ec61Sdh 			UDP_STAT(us, udp_out_sw_cksum);
6294f4b3ec61Sdh 			UDP_STAT_UPDATE(us, udp_out_sw_cksum_bytes,
6295ff550d0eSmasputra 			    ntohs(ipha->ipha_length) - IP_SIMPLE_HDR_LENGTH);
6296ff550d0eSmasputra 		}
6297ff550d0eSmasputra 	}
6298ff550d0eSmasputra 
6299a1165259Sblu 	if (!CLASSD(dst)) {
6300a1165259Sblu 		ipha->ipha_fragment_offset_and_flags |=
6301a1165259Sblu 		    (uint32_t)htons(ire->ire_frag_flag);
6302a1165259Sblu 	}
6303ff550d0eSmasputra 
6304ff550d0eSmasputra 	/* Calculate IP header checksum if hardware isn't capable */
6305ff550d0eSmasputra 	if (!(DB_CKSUMFLAGS(mp) & HCK_IPV4_HDRCKSUM)) {
6306ff550d0eSmasputra 		IP_HDR_CKSUM(ipha, cksum, ((uint32_t *)ipha)[0],
6307ff550d0eSmasputra 		    ((uint16_t *)ipha)[4]);
6308ff550d0eSmasputra 	}
6309ff550d0eSmasputra 
6310ff550d0eSmasputra 	if (CLASSD(dst)) {
6311968d2fd1Ssowmini 		boolean_t ilm_exists;
6312ff550d0eSmasputra 
6313ff550d0eSmasputra 		ILM_WALKER_HOLD(ill);
6314968d2fd1Ssowmini 		ilm_exists = (ilm_lookup_ill(ill, dst, ALL_ZONES) != NULL);
6315ff550d0eSmasputra 		ILM_WALKER_RELE(ill);
6316968d2fd1Ssowmini 		if (ilm_exists) {
6317ff550d0eSmasputra 			ip_multicast_loopback(q, ill, mp,
6318ff550d0eSmasputra 			    connp->conn_multicast_loop ? 0 :
6319fc80c0dfSnordmark 			    IP_FF_NO_MCAST_LOOP, zoneid);
6320ff550d0eSmasputra 		}
6321ff550d0eSmasputra 
6322ff550d0eSmasputra 		/* If multicast TTL is 0 then we are done */
6323ff550d0eSmasputra 		if (ipha->ipha_ttl == 0) {
6324ff550d0eSmasputra 			freemsg(mp);
6325fc80c0dfSnordmark 			ire_refrele(ire);
6326ff550d0eSmasputra 			return;
6327ff550d0eSmasputra 		}
63281b47e080Sdr 		ll_multicast = B_TRUE;
6329ff550d0eSmasputra 	}
6330ff550d0eSmasputra 
6331ff550d0eSmasputra 	ASSERT(DB_TYPE(ire_fp_mp) == M_DATA);
6332ff550d0eSmasputra 	mp->b_rptr = (uchar_t *)ipha - ire_fp_mp_len;
6333ff550d0eSmasputra 	bcopy(ire_fp_mp->b_rptr, mp->b_rptr, ire_fp_mp_len);
6334ff550d0eSmasputra 
6335ff550d0eSmasputra 	UPDATE_OB_PKT_COUNT(ire);
6336ff550d0eSmasputra 	ire->ire_last_used_time = lbolt;
6337ff550d0eSmasputra 
63383173664eSapersson 	BUMP_MIB(ill->ill_ip_mib, ipIfStatsHCOutTransmits);
63393173664eSapersson 	UPDATE_MIB(ill->ill_ip_mib, ipIfStatsHCOutOctets,
63403173664eSapersson 	    ntohs(ipha->ipha_length));
63413173664eSapersson 
6342*da14cebeSEric Cheng 	DTRACE_PROBE4(ip4__physical__out__start,
6343*da14cebeSEric Cheng 	    ill_t *, NULL, ill_t *, ill, ipha_t *, ipha, mblk_t *, mp);
6344*da14cebeSEric Cheng 	FW_HOOKS(ipst->ips_ip4_physical_out_event,
6345*da14cebeSEric Cheng 	    ipst->ips_ipv4firewall_physical_out, NULL, ill, ipha, mp, mp,
6346*da14cebeSEric Cheng 	    ll_multicast, ipst);
6347*da14cebeSEric Cheng 	DTRACE_PROBE1(ip4__physical__out__end, mblk_t *, mp);
6348*da14cebeSEric Cheng 	if (ipst->ips_ipobs_enabled && mp != NULL) {
6349*da14cebeSEric Cheng 		zoneid_t szone;
6350*da14cebeSEric Cheng 
6351*da14cebeSEric Cheng 		szone = ip_get_zoneid_v4(ipha->ipha_src, mp,
6352*da14cebeSEric Cheng 		    ipst, ALL_ZONES);
6353*da14cebeSEric Cheng 		ipobs_hook(mp, IPOBS_HOOK_OUTBOUND, szone,
6354*da14cebeSEric Cheng 		    ALL_ZONES, ill, IPV4_VERSION, ire_fp_mp_len, ipst);
6355*da14cebeSEric Cheng 	}
6356*da14cebeSEric Cheng 
6357*da14cebeSEric Cheng 	if (mp != NULL) {
6358*da14cebeSEric Cheng 		DTRACE_IP7(send, mblk_t *, mp, conn_t *, NULL,
6359*da14cebeSEric Cheng 		    void_ip_t *, ipha, __dtrace_ipsr_ill_t *, ill,
6360*da14cebeSEric Cheng 		    ipha_t *, ipha, ip6_t *, NULL, int, 0);
6361*da14cebeSEric Cheng 
6362*da14cebeSEric Cheng 		if (ILL_DIRECT_CAPABLE(ill)) {
6363*da14cebeSEric Cheng 			ill_dld_direct_t *idd = &ill->ill_dld_capab->idc_direct;
6364*da14cebeSEric Cheng 
6365*da14cebeSEric Cheng 			(void) idd->idd_tx_df(idd->idd_tx_dh, mp,
6366*da14cebeSEric Cheng 			    (uintptr_t)connp, 0);
6367*da14cebeSEric Cheng 		} else {
6368381a2a9aSdr 			putnext(ire->ire_stq, mp);
636910e6dadfSbrendan 		}
6370ff550d0eSmasputra 	}
6371ff550d0eSmasputra 	IRE_REFRELE(ire);
63727c478bd9Sstevel@tonic-gate }
63737c478bd9Sstevel@tonic-gate 
637445916cd2Sjpk static boolean_t
637545916cd2Sjpk udp_update_label_v6(queue_t *wq, mblk_t *mp, in6_addr_t *dst)
637645916cd2Sjpk {
637745916cd2Sjpk 	udp_t *udp = Q_TO_UDP(wq);
637845916cd2Sjpk 	int err;
637945916cd2Sjpk 	uchar_t opt_storage[TSOL_MAX_IPV6_OPTION];
6380fc80c0dfSnordmark 	udp_stack_t		*us = udp->udp_us;
638145916cd2Sjpk 
638245916cd2Sjpk 	err = tsol_compute_label_v6(DB_CREDDEF(mp, udp->udp_connp->conn_cred),
6383222c5bceSkp 	    dst, opt_storage, udp->udp_connp->conn_mac_exempt,
6384fc80c0dfSnordmark 	    us->us_netstack->netstack_ip);
638545916cd2Sjpk 	if (err == 0) {
638645916cd2Sjpk 		err = tsol_update_sticky(&udp->udp_sticky_ipp,
638745916cd2Sjpk 		    &udp->udp_label_len_v6, opt_storage);
638845916cd2Sjpk 	}
638945916cd2Sjpk 	if (err != 0) {
639045916cd2Sjpk 		DTRACE_PROBE4(
639145916cd2Sjpk 		    tx__ip__log__drop__updatelabel__udp6,
639245916cd2Sjpk 		    char *, "queue(1) failed to update options(2) on mp(3)",
639345916cd2Sjpk 		    queue_t *, wq, char *, opt_storage, mblk_t *, mp);
639445916cd2Sjpk 	} else {
639545916cd2Sjpk 		udp->udp_v6lastdst = *dst;
639645916cd2Sjpk 	}
639745916cd2Sjpk 	return (err);
639845916cd2Sjpk }
639945916cd2Sjpk 
6400fc80c0dfSnordmark void
6401fc80c0dfSnordmark udp_output_connected(void *arg, mblk_t *mp)
6402fc80c0dfSnordmark {
6403fc80c0dfSnordmark 	conn_t	*connp = (conn_t *)arg;
6404fc80c0dfSnordmark 	udp_t	*udp = connp->conn_udp;
6405fc80c0dfSnordmark 	udp_stack_t	*us = udp->udp_us;
6406fc80c0dfSnordmark 	ipaddr_t	v4dst;
6407fc80c0dfSnordmark 	in_port_t	dstport;
6408fc80c0dfSnordmark 	boolean_t	mapped_addr;
6409fc80c0dfSnordmark 	struct sockaddr_storage ss;
6410fc80c0dfSnordmark 	sin_t		*sin;
6411fc80c0dfSnordmark 	sin6_t		*sin6;
6412fc80c0dfSnordmark 	struct sockaddr	*addr;
6413fc80c0dfSnordmark 	socklen_t	addrlen;
6414fc80c0dfSnordmark 	int		error;
6415fc80c0dfSnordmark 	boolean_t	insert_spi = udp->udp_nat_t_endpoint;
6416fc80c0dfSnordmark 
6417fc80c0dfSnordmark 	/* M_DATA for connected socket */
6418fc80c0dfSnordmark 
6419fc80c0dfSnordmark 	ASSERT(udp->udp_issocket);
6420fc80c0dfSnordmark 	UDP_DBGSTAT(us, udp_data_conn);
6421fc80c0dfSnordmark 
6422fc80c0dfSnordmark 	mutex_enter(&connp->conn_lock);
6423fc80c0dfSnordmark 	if (udp->udp_state != TS_DATA_XFER) {
6424fc80c0dfSnordmark 		mutex_exit(&connp->conn_lock);
6425fc80c0dfSnordmark 		BUMP_MIB(&us->us_udp_mib, udpOutErrors);
6426fc80c0dfSnordmark 		UDP_STAT(us, udp_out_err_notconn);
6427fc80c0dfSnordmark 		freemsg(mp);
6428fc80c0dfSnordmark 		TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_END,
6429fc80c0dfSnordmark 		    "udp_wput_end: connp %p (%S)", connp,
6430fc80c0dfSnordmark 		    "not-connected; address required");
6431fc80c0dfSnordmark 		return;
6432fc80c0dfSnordmark 	}
6433fc80c0dfSnordmark 
6434fc80c0dfSnordmark 	mapped_addr = IN6_IS_ADDR_V4MAPPED(&udp->udp_v6dst);
6435fc80c0dfSnordmark 	if (mapped_addr)
6436fc80c0dfSnordmark 		IN6_V4MAPPED_TO_IPADDR(&udp->udp_v6dst, v4dst);
6437fc80c0dfSnordmark 
6438fc80c0dfSnordmark 	/* Initialize addr and addrlen as if they're passed in */
6439fc80c0dfSnordmark 	if (udp->udp_family == AF_INET) {
6440fc80c0dfSnordmark 		sin = (sin_t *)&ss;
6441fc80c0dfSnordmark 		sin->sin_family = AF_INET;
6442fc80c0dfSnordmark 		dstport = sin->sin_port = udp->udp_dstport;
6443fc80c0dfSnordmark 		ASSERT(mapped_addr);
6444fc80c0dfSnordmark 		sin->sin_addr.s_addr = v4dst;
6445fc80c0dfSnordmark 		addr = (struct sockaddr *)sin;
6446fc80c0dfSnordmark 		addrlen = sizeof (*sin);
6447fc80c0dfSnordmark 	} else {
6448fc80c0dfSnordmark 		sin6 = (sin6_t *)&ss;
6449fc80c0dfSnordmark 		sin6->sin6_family = AF_INET6;
6450fc80c0dfSnordmark 		dstport = sin6->sin6_port = udp->udp_dstport;
6451fc80c0dfSnordmark 		sin6->sin6_flowinfo = udp->udp_flowinfo;
6452fc80c0dfSnordmark 		sin6->sin6_addr = udp->udp_v6dst;
6453fc80c0dfSnordmark 		sin6->sin6_scope_id = 0;
6454fc80c0dfSnordmark 		sin6->__sin6_src_id = 0;
6455fc80c0dfSnordmark 		addr = (struct sockaddr *)sin6;
6456fc80c0dfSnordmark 		addrlen = sizeof (*sin6);
6457fc80c0dfSnordmark 	}
6458fc80c0dfSnordmark 	mutex_exit(&connp->conn_lock);
6459fc80c0dfSnordmark 
6460fc80c0dfSnordmark 	if (mapped_addr) {
6461fc80c0dfSnordmark 		/*
6462fc80c0dfSnordmark 		 * Handle both AF_INET and AF_INET6; the latter
6463fc80c0dfSnordmark 		 * for IPV4 mapped destination addresses.  Note
6464fc80c0dfSnordmark 		 * here that both addr and addrlen point to the
6465fc80c0dfSnordmark 		 * corresponding struct depending on the address
6466fc80c0dfSnordmark 		 * family of the socket.
6467fc80c0dfSnordmark 		 */
6468fc80c0dfSnordmark 		mp = udp_output_v4(connp, mp, v4dst, dstport, 0, &error,
6469fc80c0dfSnordmark 		    insert_spi);
6470fc80c0dfSnordmark 	} else {
6471fc80c0dfSnordmark 		mp = udp_output_v6(connp, mp, sin6, &error);
6472fc80c0dfSnordmark 	}
6473fc80c0dfSnordmark 	if (error == 0) {
6474fc80c0dfSnordmark 		ASSERT(mp == NULL);
6475fc80c0dfSnordmark 		return;
6476fc80c0dfSnordmark 	}
6477fc80c0dfSnordmark 
6478fc80c0dfSnordmark 	UDP_STAT(us, udp_out_err_output);
6479fc80c0dfSnordmark 	ASSERT(mp != NULL);
6480fc80c0dfSnordmark 	/* mp is freed by the following routine */
6481fc80c0dfSnordmark 	udp_ud_err(connp->conn_wq, mp, (uchar_t *)addr, (t_scalar_t)addrlen,
6482fc80c0dfSnordmark 	    (t_scalar_t)error);
6483fc80c0dfSnordmark }
6484fc80c0dfSnordmark 
64857c478bd9Sstevel@tonic-gate /*
6486ff550d0eSmasputra  * This routine handles all messages passed downstream.  It either
6487ff550d0eSmasputra  * consumes the message or passes it downstream; it never queues a
6488ff550d0eSmasputra  * a message.
6489fc80c0dfSnordmark  *
6490fc80c0dfSnordmark  * Also entry point for sockfs when udp is in "direct sockfs" mode.  This mode
6491fc80c0dfSnordmark  * is valid when we are directly beneath the stream head, and thus sockfs
6492fc80c0dfSnordmark  * is able to bypass STREAMS and directly call us, passing along the sockaddr
6493fc80c0dfSnordmark  * structure without the cumbersome T_UNITDATA_REQ interface for the case of
6494fc80c0dfSnordmark  * connected endpoints.
64957c478bd9Sstevel@tonic-gate  */
6496fc80c0dfSnordmark void
6497fc80c0dfSnordmark udp_wput(queue_t *q, mblk_t *mp)
64987c478bd9Sstevel@tonic-gate {
6499ff550d0eSmasputra 	sin6_t		*sin6;
6500ff550d0eSmasputra 	sin_t		*sin;
6501ff550d0eSmasputra 	ipaddr_t	v4dst;
6502ff550d0eSmasputra 	uint16_t	port;
6503ff550d0eSmasputra 	uint_t		srcid;
6504fc80c0dfSnordmark 	conn_t		*connp = Q_TO_CONN(q);
6505ff550d0eSmasputra 	udp_t		*udp = connp->conn_udp;
6506ff550d0eSmasputra 	int		error = 0;
6507fc80c0dfSnordmark 	struct sockaddr	*addr;
6508fc80c0dfSnordmark 	socklen_t	addrlen;
6509f4b3ec61Sdh 	udp_stack_t *us = udp->udp_us;
6510437220cdSdanmcd 	boolean_t	insert_spi = udp->udp_nat_t_endpoint;
65117c478bd9Sstevel@tonic-gate 
6512ff550d0eSmasputra 	TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_START,
6513fc80c0dfSnordmark 	    "udp_wput_start: queue %p mp %p", q, mp);
6514ff550d0eSmasputra 
6515ff550d0eSmasputra 	/*
6516ff550d0eSmasputra 	 * We directly handle several cases here: T_UNITDATA_REQ message
6517fc80c0dfSnordmark 	 * coming down as M_PROTO/M_PCPROTO and M_DATA messages for connected
6518fc80c0dfSnordmark 	 * socket.
6519ff550d0eSmasputra 	 */
6520ff550d0eSmasputra 	switch (DB_TYPE(mp)) {
6521ff550d0eSmasputra 	case M_DATA:
6522fc80c0dfSnordmark 		/*
6523fc80c0dfSnordmark 		 * Quick check for error cases. Checks will be done again
6524fc80c0dfSnordmark 		 * under the lock later on
6525fc80c0dfSnordmark 		 */
6526ff550d0eSmasputra 		if (!udp->udp_direct_sockfs || udp->udp_state != TS_DATA_XFER) {
6527fc80c0dfSnordmark 			/* Not connected; address is required */
6528fc80c0dfSnordmark 			BUMP_MIB(&us->us_udp_mib, udpOutErrors);
6529fc80c0dfSnordmark 			UDP_STAT(us, udp_out_err_notconn);
6530fc80c0dfSnordmark 			freemsg(mp);
6531fc80c0dfSnordmark 			TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_END,
6532fc80c0dfSnordmark 			    "udp_wput_end: connp %p (%S)", connp,
6533fc80c0dfSnordmark 			    "not-connected; address required");
6534fc80c0dfSnordmark 			return;
6535ff550d0eSmasputra 		}
6536fc80c0dfSnordmark 		udp_output_connected(connp, mp);
6537ff550d0eSmasputra 		return;
6538fc80c0dfSnordmark 
6539ff550d0eSmasputra 	case M_PROTO:
6540ff550d0eSmasputra 	case M_PCPROTO: {
6541ff550d0eSmasputra 		struct T_unitdata_req *tudr;
6542ff550d0eSmasputra 
6543ff550d0eSmasputra 		ASSERT((uintptr_t)MBLKL(mp) <= (uintptr_t)INT_MAX);
6544ff550d0eSmasputra 		tudr = (struct T_unitdata_req *)mp->b_rptr;
6545ff550d0eSmasputra 
6546ff550d0eSmasputra 		/* Handle valid T_UNITDATA_REQ here */
6547ff550d0eSmasputra 		if (MBLKL(mp) >= sizeof (*tudr) &&
6548ff550d0eSmasputra 		    ((t_primp_t)mp->b_rptr)->type == T_UNITDATA_REQ) {
6549ff550d0eSmasputra 			if (mp->b_cont == NULL) {
6550ff550d0eSmasputra 				TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_END,
6551ff550d0eSmasputra 				    "udp_wput_end: q %p (%S)", q, "badaddr");
6552ff550d0eSmasputra 				error = EPROTO;
6553ff550d0eSmasputra 				goto ud_error;
6554ff550d0eSmasputra 			}
6555ff550d0eSmasputra 
6556ff550d0eSmasputra 			if (!MBLKIN(mp, 0, tudr->DEST_offset +
6557ff550d0eSmasputra 			    tudr->DEST_length)) {
6558ff550d0eSmasputra 				TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_END,
6559ff550d0eSmasputra 				    "udp_wput_end: q %p (%S)", q, "badaddr");
6560ff550d0eSmasputra 				error = EADDRNOTAVAIL;
6561ff550d0eSmasputra 				goto ud_error;
6562ff550d0eSmasputra 			}
6563ff550d0eSmasputra 			/*
6564ff550d0eSmasputra 			 * If a port has not been bound to the stream, fail.
6565ff550d0eSmasputra 			 * This is not a problem when sockfs is directly
6566ff550d0eSmasputra 			 * above us, because it will ensure that the socket
6567ff550d0eSmasputra 			 * is first bound before allowing data to be sent.
6568ff550d0eSmasputra 			 */
6569ff550d0eSmasputra 			if (udp->udp_state == TS_UNBND) {
6570ff550d0eSmasputra 				TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_END,
6571ff550d0eSmasputra 				    "udp_wput_end: q %p (%S)", q, "outstate");
6572ff550d0eSmasputra 				error = EPROTO;
6573ff550d0eSmasputra 				goto ud_error;
6574ff550d0eSmasputra 			}
6575ff550d0eSmasputra 			addr = (struct sockaddr *)
6576ff550d0eSmasputra 			    &mp->b_rptr[tudr->DEST_offset];
6577ff550d0eSmasputra 			addrlen = tudr->DEST_length;
657845916cd2Sjpk 			if (tudr->OPT_length != 0)
6579f4b3ec61Sdh 				UDP_STAT(us, udp_out_opt);
6580ff550d0eSmasputra 			break;
6581ff550d0eSmasputra 		}
6582ff550d0eSmasputra 		/* FALLTHRU */
6583ff550d0eSmasputra 	}
6584ff550d0eSmasputra 	default:
6585fc80c0dfSnordmark 		udp_wput_other(q, mp);
6586ff550d0eSmasputra 		return;
6587ff550d0eSmasputra 	}
6588ff550d0eSmasputra 	ASSERT(addr != NULL);
6589ff550d0eSmasputra 
6590ff550d0eSmasputra 	switch (udp->udp_family) {
6591ff550d0eSmasputra 	case AF_INET6:
6592ff550d0eSmasputra 		sin6 = (sin6_t *)addr;
6593fc80c0dfSnordmark 		if (!OK_32PTR((char *)sin6) || (addrlen != sizeof (sin6_t)) ||
6594fc80c0dfSnordmark 		    (sin6->sin6_family != AF_INET6)) {
6595ff550d0eSmasputra 			TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_END,
6596ff550d0eSmasputra 			    "udp_wput_end: q %p (%S)", q, "badaddr");
6597ff550d0eSmasputra 			error = EADDRNOTAVAIL;
6598ff550d0eSmasputra 			goto ud_error;
6599ff550d0eSmasputra 		}
6600ff550d0eSmasputra 
6601ff550d0eSmasputra 		if (!IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
6602ff550d0eSmasputra 			/*
6603ff550d0eSmasputra 			 * Destination is a non-IPv4-compatible IPv6 address.
6604ff550d0eSmasputra 			 * Send out an IPv6 format packet.
6605ff550d0eSmasputra 			 */
660645916cd2Sjpk 			mp = udp_output_v6(connp, mp, sin6, &error);
6607ff550d0eSmasputra 			if (error != 0)
6608ff550d0eSmasputra 				goto ud_error;
6609ff550d0eSmasputra 
6610ff550d0eSmasputra 			TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_END,
6611ff550d0eSmasputra 			    "udp_wput_end: q %p (%S)", q, "udp_output_v6");
6612ff550d0eSmasputra 			return;
6613ff550d0eSmasputra 		}
6614ff550d0eSmasputra 		/*
6615ff550d0eSmasputra 		 * If the local address is not zero or a mapped address
6616ff550d0eSmasputra 		 * return an error.  It would be possible to send an IPv4
6617ff550d0eSmasputra 		 * packet but the response would never make it back to the
6618ff550d0eSmasputra 		 * application since it is bound to a non-mapped address.
6619ff550d0eSmasputra 		 */
6620ff550d0eSmasputra 		if (!IN6_IS_ADDR_V4MAPPED(&udp->udp_v6src) &&
6621ff550d0eSmasputra 		    !IN6_IS_ADDR_UNSPECIFIED(&udp->udp_v6src)) {
6622ff550d0eSmasputra 			TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_END,
6623ff550d0eSmasputra 			    "udp_wput_end: q %p (%S)", q, "badaddr");
6624ff550d0eSmasputra 			error = EADDRNOTAVAIL;
6625ff550d0eSmasputra 			goto ud_error;
6626ff550d0eSmasputra 		}
6627ff550d0eSmasputra 		/* Send IPv4 packet without modifying udp_ipversion */
6628ff550d0eSmasputra 		/* Extract port and ipaddr */
6629ff550d0eSmasputra 		port = sin6->sin6_port;
6630ff550d0eSmasputra 		IN6_V4MAPPED_TO_IPADDR(&sin6->sin6_addr, v4dst);
6631ff550d0eSmasputra 		srcid = sin6->__sin6_src_id;
6632ff550d0eSmasputra 		break;
6633ff550d0eSmasputra 
6634ff550d0eSmasputra 	case AF_INET:
6635ff550d0eSmasputra 		sin = (sin_t *)addr;
6636fc80c0dfSnordmark 		if ((!OK_32PTR((char *)sin) || addrlen != sizeof (sin_t)) ||
6637fc80c0dfSnordmark 		    (sin->sin_family != AF_INET)) {
6638ff550d0eSmasputra 			TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_END,
6639ff550d0eSmasputra 			    "udp_wput_end: q %p (%S)", q, "badaddr");
6640ff550d0eSmasputra 			error = EADDRNOTAVAIL;
6641ff550d0eSmasputra 			goto ud_error;
6642ff550d0eSmasputra 		}
6643ff550d0eSmasputra 		/* Extract port and ipaddr */
6644ff550d0eSmasputra 		port = sin->sin_port;
6645ff550d0eSmasputra 		v4dst = sin->sin_addr.s_addr;
6646ff550d0eSmasputra 		srcid = 0;
6647ff550d0eSmasputra 		break;
6648ff550d0eSmasputra 	}
6649ff550d0eSmasputra 
6650437220cdSdanmcd 	mp = udp_output_v4(connp, mp, v4dst, port, srcid, &error, insert_spi);
6651ff550d0eSmasputra 	if (error != 0) {
6652ff550d0eSmasputra ud_error:
6653f4b3ec61Sdh 		UDP_STAT(us, udp_out_err_output);
6654ff550d0eSmasputra 		ASSERT(mp != NULL);
6655ff550d0eSmasputra 		/* mp is freed by the following routine */
6656ff550d0eSmasputra 		udp_ud_err(q, mp, (uchar_t *)addr, (t_scalar_t)addrlen,
6657ff550d0eSmasputra 		    (t_scalar_t)error);
6658ff550d0eSmasputra 	}
6659ff550d0eSmasputra }
6660ff550d0eSmasputra 
6661ff550d0eSmasputra /*
6662ff550d0eSmasputra  * udp_output_v6():
6663ff550d0eSmasputra  * Assumes that udp_wput did some sanity checking on the destination
6664ff550d0eSmasputra  * address.
6665ff550d0eSmasputra  */
6666ff550d0eSmasputra static mblk_t *
666745916cd2Sjpk udp_output_v6(conn_t *connp, mblk_t *mp, sin6_t *sin6, int *error)
6668ff550d0eSmasputra {
6669ff550d0eSmasputra 	ip6_t		*ip6h;
6670ff550d0eSmasputra 	ip6i_t		*ip6i;	/* mp1->b_rptr even if no ip6i_t */
667145916cd2Sjpk 	mblk_t		*mp1 = mp;
6672ff550d0eSmasputra 	mblk_t		*mp2;
6673ff550d0eSmasputra 	int		udp_ip_hdr_len = IPV6_HDR_LEN + UDPH_SIZE;
6674ff550d0eSmasputra 	size_t		ip_len;
6675ff550d0eSmasputra 	udpha_t		*udph;
6676ff550d0eSmasputra 	udp_t		*udp = connp->conn_udp;
6677ff550d0eSmasputra 	queue_t		*q = connp->conn_wq;
6678ff550d0eSmasputra 	ip6_pkt_t	ipp_s;	/* For ancillary data options */
6679ff550d0eSmasputra 	ip6_pkt_t	*ipp = &ipp_s;
6680ff550d0eSmasputra 	ip6_pkt_t	*tipp;	/* temporary ipp */
6681ff550d0eSmasputra 	uint32_t	csum = 0;
6682ff550d0eSmasputra 	uint_t		ignore = 0;
6683ff550d0eSmasputra 	uint_t		option_exists = 0, is_sticky = 0;
6684ff550d0eSmasputra 	uint8_t		*cp;
6685ff550d0eSmasputra 	uint8_t		*nxthdr_ptr;
668645916cd2Sjpk 	in6_addr_t	ip6_dst;
668745916cd2Sjpk 	udpattrs_t	attrs;
668845916cd2Sjpk 	boolean_t	opt_present;
6689dc3879f9Sjarrett 	ip6_hbh_t	*hopoptsptr = NULL;
6690dc3879f9Sjarrett 	uint_t		hopoptslen = 0;
6691dc3879f9Sjarrett 	boolean_t	is_ancillary = B_FALSE;
6692f4b3ec61Sdh 	udp_stack_t	*us = udp->udp_us;
6693fc80c0dfSnordmark 	size_t		sth_wroff = 0;
6694ff550d0eSmasputra 
6695ff550d0eSmasputra 	*error = 0;
6696ff550d0eSmasputra 
66977c478bd9Sstevel@tonic-gate 	/*
66987c478bd9Sstevel@tonic-gate 	 * If the local address is a mapped address return
66997c478bd9Sstevel@tonic-gate 	 * an error.
67007c478bd9Sstevel@tonic-gate 	 * It would be possible to send an IPv6 packet but the
67017c478bd9Sstevel@tonic-gate 	 * response would never make it back to the application
67027c478bd9Sstevel@tonic-gate 	 * since it is bound to a mapped address.
67037c478bd9Sstevel@tonic-gate 	 */
67047c478bd9Sstevel@tonic-gate 	if (IN6_IS_ADDR_V4MAPPED(&udp->udp_v6src)) {
6705ff550d0eSmasputra 		*error = EADDRNOTAVAIL;
6706ff550d0eSmasputra 		goto done;
67077c478bd9Sstevel@tonic-gate 	}
67087c478bd9Sstevel@tonic-gate 
67097c478bd9Sstevel@tonic-gate 	ipp->ipp_fields = 0;
67107c478bd9Sstevel@tonic-gate 	ipp->ipp_sticky_ignored = 0;
67117c478bd9Sstevel@tonic-gate 
67127c478bd9Sstevel@tonic-gate 	/*
67137c478bd9Sstevel@tonic-gate 	 * If TPI options passed in, feed it for verification and handling
67147c478bd9Sstevel@tonic-gate 	 */
671545916cd2Sjpk 	attrs.udpattr_credset = B_FALSE;
671645916cd2Sjpk 	opt_present = B_FALSE;
671745916cd2Sjpk 	if (DB_TYPE(mp) != M_DATA) {
671845916cd2Sjpk 		mp1 = mp->b_cont;
671945916cd2Sjpk 		if (((struct T_unitdata_req *)mp->b_rptr)->OPT_length != 0) {
672019a30e1aSrshoaib 			attrs.udpattr_ipp6 = ipp;
672145916cd2Sjpk 			attrs.udpattr_mb = mp;
6722fc80c0dfSnordmark 			if (udp_unitdata_opt_process(q, mp, error,
6723fc80c0dfSnordmark 			    &attrs) < 0) {
672445916cd2Sjpk 				goto done;
6725fc80c0dfSnordmark 			}
672645916cd2Sjpk 			ASSERT(*error == 0);
672745916cd2Sjpk 			opt_present = B_TRUE;
67287c478bd9Sstevel@tonic-gate 		}
67297c478bd9Sstevel@tonic-gate 	}
6730fc80c0dfSnordmark 	rw_enter(&udp->udp_rwlock, RW_READER);
673145916cd2Sjpk 	ignore = ipp->ipp_sticky_ignored;
673245916cd2Sjpk 
673345916cd2Sjpk 	/* mp1 points to the M_DATA mblk carrying the packet */
673445916cd2Sjpk 	ASSERT(mp1 != NULL && DB_TYPE(mp1) == M_DATA);
67357c478bd9Sstevel@tonic-gate 
67367c478bd9Sstevel@tonic-gate 	if (sin6->sin6_scope_id != 0 &&
67377c478bd9Sstevel@tonic-gate 	    IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
67387c478bd9Sstevel@tonic-gate 		/*
67397c478bd9Sstevel@tonic-gate 		 * IPPF_SCOPE_ID is special.  It's neither a sticky
67407c478bd9Sstevel@tonic-gate 		 * option nor ancillary data.  It needs to be
67417c478bd9Sstevel@tonic-gate 		 * explicitly set in options_exists.
67427c478bd9Sstevel@tonic-gate 		 */
67437c478bd9Sstevel@tonic-gate 		option_exists |= IPPF_SCOPE_ID;
67447c478bd9Sstevel@tonic-gate 	}
67457c478bd9Sstevel@tonic-gate 
674645916cd2Sjpk 	/*
674745916cd2Sjpk 	 * Compute the destination address
674845916cd2Sjpk 	 */
674945916cd2Sjpk 	ip6_dst = sin6->sin6_addr;
675045916cd2Sjpk 	if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr))
675145916cd2Sjpk 		ip6_dst = ipv6_loopback;
675245916cd2Sjpk 
675345916cd2Sjpk 	/*
675445916cd2Sjpk 	 * If we're not going to the same destination as last time, then
675545916cd2Sjpk 	 * recompute the label required.  This is done in a separate routine to
675645916cd2Sjpk 	 * avoid blowing up our stack here.
6757dc3879f9Sjarrett 	 *
6758dc3879f9Sjarrett 	 * TSOL Note: Since we are not in WRITER mode, UDP packets
675903986916Sjarrett 	 * to different destination may require different labels,
676003986916Sjarrett 	 * or worse, UDP packets to same IP address may require
676103986916Sjarrett 	 * different labels due to use of shared all-zones address.
6762dc3879f9Sjarrett 	 * We use conn_lock to ensure that lastdst, sticky ipp_hopopts,
6763dc3879f9Sjarrett 	 * and sticky ipp_hopoptslen are consistent for the current
6764dc3879f9Sjarrett 	 * destination and are updated atomically.
676545916cd2Sjpk 	 */
6766dc3879f9Sjarrett 	mutex_enter(&connp->conn_lock);
676745916cd2Sjpk 	if (is_system_labeled()) {
676845916cd2Sjpk 		/* Using UDP MLP requires SCM_UCRED from user */
676945916cd2Sjpk 		if (connp->conn_mlp_type != mlptSingle &&
677045916cd2Sjpk 		    !attrs.udpattr_credset) {
677145916cd2Sjpk 			DTRACE_PROBE4(
677245916cd2Sjpk 			    tx__ip__log__info__output__udp6,
677345916cd2Sjpk 			    char *, "MLP mp(1) lacks SCM_UCRED attr(2) on q(3)",
677445916cd2Sjpk 			    mblk_t *, mp1, udpattrs_t *, &attrs, queue_t *, q);
677545916cd2Sjpk 			*error = ECONNREFUSED;
6776fc80c0dfSnordmark 			rw_exit(&udp->udp_rwlock);
6777dc3879f9Sjarrett 			mutex_exit(&connp->conn_lock);
677845916cd2Sjpk 			goto done;
677945916cd2Sjpk 		}
678003986916Sjarrett 		/*
678103986916Sjarrett 		 * update label option for this UDP socket if
678203986916Sjarrett 		 * - the destination has changed, or
678303986916Sjarrett 		 * - the UDP socket is MLP
678403986916Sjarrett 		 */
678545916cd2Sjpk 		if ((opt_present ||
678603986916Sjarrett 		    !IN6_ARE_ADDR_EQUAL(&udp->udp_v6lastdst, &ip6_dst) ||
678703986916Sjarrett 		    connp->conn_mlp_type != mlptSingle) &&
6788dc3879f9Sjarrett 		    (*error = udp_update_label_v6(q, mp, &ip6_dst)) != 0) {
6789fc80c0dfSnordmark 			rw_exit(&udp->udp_rwlock);
6790dc3879f9Sjarrett 			mutex_exit(&connp->conn_lock);
679145916cd2Sjpk 			goto done;
6792dc3879f9Sjarrett 		}
679345916cd2Sjpk 	}
679445916cd2Sjpk 
679545916cd2Sjpk 	/*
679645916cd2Sjpk 	 * If there's a security label here, then we ignore any options the
679745916cd2Sjpk 	 * user may try to set.  We keep the peer's label as a hidden sticky
6798dc3879f9Sjarrett 	 * option. We make a private copy of this label before releasing the
6799dc3879f9Sjarrett 	 * lock so that label is kept consistent with the destination addr.
680045916cd2Sjpk 	 */
680145916cd2Sjpk 	if (udp->udp_label_len_v6 > 0) {
680245916cd2Sjpk 		ignore &= ~IPPF_HOPOPTS;
680345916cd2Sjpk 		ipp->ipp_fields &= ~IPPF_HOPOPTS;
680445916cd2Sjpk 	}
680545916cd2Sjpk 
6806ff550d0eSmasputra 	if ((udp->udp_sticky_ipp.ipp_fields == 0) && (ipp->ipp_fields == 0)) {
68077c478bd9Sstevel@tonic-gate 		/* No sticky options nor ancillary data. */
6808dc3879f9Sjarrett 		mutex_exit(&connp->conn_lock);
68097c478bd9Sstevel@tonic-gate 		goto no_options;
68107c478bd9Sstevel@tonic-gate 	}
68117c478bd9Sstevel@tonic-gate 
68127c478bd9Sstevel@tonic-gate 	/*
68137c478bd9Sstevel@tonic-gate 	 * Go through the options figuring out where each is going to
68147c478bd9Sstevel@tonic-gate 	 * come from and build two masks.  The first mask indicates if
68157c478bd9Sstevel@tonic-gate 	 * the option exists at all.  The second mask indicates if the
68167c478bd9Sstevel@tonic-gate 	 * option is sticky or ancillary.
68177c478bd9Sstevel@tonic-gate 	 */
68187c478bd9Sstevel@tonic-gate 	if (!(ignore & IPPF_HOPOPTS)) {
68197c478bd9Sstevel@tonic-gate 		if (ipp->ipp_fields & IPPF_HOPOPTS) {
68207c478bd9Sstevel@tonic-gate 			option_exists |= IPPF_HOPOPTS;
68217c478bd9Sstevel@tonic-gate 			udp_ip_hdr_len += ipp->ipp_hopoptslen;
68227c478bd9Sstevel@tonic-gate 		} else if (udp->udp_sticky_ipp.ipp_fields & IPPF_HOPOPTS) {
68237c478bd9Sstevel@tonic-gate 			option_exists |= IPPF_HOPOPTS;
68247c478bd9Sstevel@tonic-gate 			is_sticky |= IPPF_HOPOPTS;
6825dc3879f9Sjarrett 			ASSERT(udp->udp_sticky_ipp.ipp_hopoptslen != 0);
6826dc3879f9Sjarrett 			hopoptsptr = kmem_alloc(
6827dc3879f9Sjarrett 			    udp->udp_sticky_ipp.ipp_hopoptslen, KM_NOSLEEP);
6828dc3879f9Sjarrett 			if (hopoptsptr == NULL) {
6829dc3879f9Sjarrett 				*error = ENOMEM;
6830dc3879f9Sjarrett 				mutex_exit(&connp->conn_lock);
6831dc3879f9Sjarrett 				goto done;
6832dc3879f9Sjarrett 			}
6833dc3879f9Sjarrett 			hopoptslen = udp->udp_sticky_ipp.ipp_hopoptslen;
6834dc3879f9Sjarrett 			bcopy(udp->udp_sticky_ipp.ipp_hopopts, hopoptsptr,
6835dc3879f9Sjarrett 			    hopoptslen);
6836dc3879f9Sjarrett 			udp_ip_hdr_len += hopoptslen;
68377c478bd9Sstevel@tonic-gate 		}
68387c478bd9Sstevel@tonic-gate 	}
6839dc3879f9Sjarrett 	mutex_exit(&connp->conn_lock);
68407c478bd9Sstevel@tonic-gate 
68417c478bd9Sstevel@tonic-gate 	if (!(ignore & IPPF_RTHDR)) {
68427c478bd9Sstevel@tonic-gate 		if (ipp->ipp_fields & IPPF_RTHDR) {
68437c478bd9Sstevel@tonic-gate 			option_exists |= IPPF_RTHDR;
68447c478bd9Sstevel@tonic-gate 			udp_ip_hdr_len += ipp->ipp_rthdrlen;
68457c478bd9Sstevel@tonic-gate 		} else if (udp->udp_sticky_ipp.ipp_fields & IPPF_RTHDR) {
68467c478bd9Sstevel@tonic-gate 			option_exists |= IPPF_RTHDR;
68477c478bd9Sstevel@tonic-gate 			is_sticky |= IPPF_RTHDR;
68487c478bd9Sstevel@tonic-gate 			udp_ip_hdr_len += udp->udp_sticky_ipp.ipp_rthdrlen;
68497c478bd9Sstevel@tonic-gate 		}
68507c478bd9Sstevel@tonic-gate 	}
68517c478bd9Sstevel@tonic-gate 
68527c478bd9Sstevel@tonic-gate 	if (!(ignore & IPPF_RTDSTOPTS) && (option_exists & IPPF_RTHDR)) {
68537c478bd9Sstevel@tonic-gate 		if (ipp->ipp_fields & IPPF_RTDSTOPTS) {
68547c478bd9Sstevel@tonic-gate 			option_exists |= IPPF_RTDSTOPTS;
68557c478bd9Sstevel@tonic-gate 			udp_ip_hdr_len += ipp->ipp_rtdstoptslen;
68567c478bd9Sstevel@tonic-gate 		} else if (udp->udp_sticky_ipp.ipp_fields & IPPF_RTDSTOPTS) {
68577c478bd9Sstevel@tonic-gate 			option_exists |= IPPF_RTDSTOPTS;
68587c478bd9Sstevel@tonic-gate 			is_sticky |= IPPF_RTDSTOPTS;
68597c478bd9Sstevel@tonic-gate 			udp_ip_hdr_len += udp->udp_sticky_ipp.ipp_rtdstoptslen;
68607c478bd9Sstevel@tonic-gate 		}
68617c478bd9Sstevel@tonic-gate 	}
68627c478bd9Sstevel@tonic-gate 
68637c478bd9Sstevel@tonic-gate 	if (!(ignore & IPPF_DSTOPTS)) {
68647c478bd9Sstevel@tonic-gate 		if (ipp->ipp_fields & IPPF_DSTOPTS) {
68657c478bd9Sstevel@tonic-gate 			option_exists |= IPPF_DSTOPTS;
68667c478bd9Sstevel@tonic-gate 			udp_ip_hdr_len += ipp->ipp_dstoptslen;
68677c478bd9Sstevel@tonic-gate 		} else if (udp->udp_sticky_ipp.ipp_fields & IPPF_DSTOPTS) {
68687c478bd9Sstevel@tonic-gate 			option_exists |= IPPF_DSTOPTS;
68697c478bd9Sstevel@tonic-gate 			is_sticky |= IPPF_DSTOPTS;
68707c478bd9Sstevel@tonic-gate 			udp_ip_hdr_len += udp->udp_sticky_ipp.ipp_dstoptslen;
68717c478bd9Sstevel@tonic-gate 		}
68727c478bd9Sstevel@tonic-gate 	}
68737c478bd9Sstevel@tonic-gate 
68747c478bd9Sstevel@tonic-gate 	if (!(ignore & IPPF_IFINDEX)) {
68757c478bd9Sstevel@tonic-gate 		if (ipp->ipp_fields & IPPF_IFINDEX) {
68767c478bd9Sstevel@tonic-gate 			option_exists |= IPPF_IFINDEX;
68777c478bd9Sstevel@tonic-gate 		} else if (udp->udp_sticky_ipp.ipp_fields & IPPF_IFINDEX) {
68787c478bd9Sstevel@tonic-gate 			option_exists |= IPPF_IFINDEX;
68797c478bd9Sstevel@tonic-gate 			is_sticky |= IPPF_IFINDEX;
68807c478bd9Sstevel@tonic-gate 		}
68817c478bd9Sstevel@tonic-gate 	}
68827c478bd9Sstevel@tonic-gate 
68837c478bd9Sstevel@tonic-gate 	if (!(ignore & IPPF_ADDR)) {
68847c478bd9Sstevel@tonic-gate 		if (ipp->ipp_fields & IPPF_ADDR) {
68857c478bd9Sstevel@tonic-gate 			option_exists |= IPPF_ADDR;
68867c478bd9Sstevel@tonic-gate 		} else if (udp->udp_sticky_ipp.ipp_fields & IPPF_ADDR) {
68877c478bd9Sstevel@tonic-gate 			option_exists |= IPPF_ADDR;
68887c478bd9Sstevel@tonic-gate 			is_sticky |= IPPF_ADDR;
68897c478bd9Sstevel@tonic-gate 		}
68907c478bd9Sstevel@tonic-gate 	}
68917c478bd9Sstevel@tonic-gate 
68927c478bd9Sstevel@tonic-gate 	if (!(ignore & IPPF_DONTFRAG)) {
68937c478bd9Sstevel@tonic-gate 		if (ipp->ipp_fields & IPPF_DONTFRAG) {
68947c478bd9Sstevel@tonic-gate 			option_exists |= IPPF_DONTFRAG;
68957c478bd9Sstevel@tonic-gate 		} else if (udp->udp_sticky_ipp.ipp_fields & IPPF_DONTFRAG) {
68967c478bd9Sstevel@tonic-gate 			option_exists |= IPPF_DONTFRAG;
68977c478bd9Sstevel@tonic-gate 			is_sticky |= IPPF_DONTFRAG;
68987c478bd9Sstevel@tonic-gate 		}
68997c478bd9Sstevel@tonic-gate 	}
69007c478bd9Sstevel@tonic-gate 
69017c478bd9Sstevel@tonic-gate 	if (!(ignore & IPPF_USE_MIN_MTU)) {
69027c478bd9Sstevel@tonic-gate 		if (ipp->ipp_fields & IPPF_USE_MIN_MTU) {
69037c478bd9Sstevel@tonic-gate 			option_exists |= IPPF_USE_MIN_MTU;
6904ff550d0eSmasputra 		} else if (udp->udp_sticky_ipp.ipp_fields &
6905ff550d0eSmasputra 		    IPPF_USE_MIN_MTU) {
69067c478bd9Sstevel@tonic-gate 			option_exists |= IPPF_USE_MIN_MTU;
69077c478bd9Sstevel@tonic-gate 			is_sticky |= IPPF_USE_MIN_MTU;
69087c478bd9Sstevel@tonic-gate 		}
69097c478bd9Sstevel@tonic-gate 	}
69107c478bd9Sstevel@tonic-gate 
6911b3d0fa4fSseb 	if (!(ignore & IPPF_HOPLIMIT) && (ipp->ipp_fields & IPPF_HOPLIMIT))
6912b3d0fa4fSseb 		option_exists |= IPPF_HOPLIMIT;
6913b3d0fa4fSseb 	/* IPV6_HOPLIMIT can never be sticky */
6914b3d0fa4fSseb 	ASSERT(!(udp->udp_sticky_ipp.ipp_fields & IPPF_HOPLIMIT));
6915b3d0fa4fSseb 
6916b3d0fa4fSseb 	if (!(ignore & IPPF_UNICAST_HOPS) &&
6917b3d0fa4fSseb 	    (udp->udp_sticky_ipp.ipp_fields & IPPF_UNICAST_HOPS)) {
6918b3d0fa4fSseb 		option_exists |= IPPF_UNICAST_HOPS;
6919b3d0fa4fSseb 		is_sticky |= IPPF_UNICAST_HOPS;
6920b3d0fa4fSseb 	}
6921b3d0fa4fSseb 
6922b3d0fa4fSseb 	if (!(ignore & IPPF_MULTICAST_HOPS) &&
6923b3d0fa4fSseb 	    (udp->udp_sticky_ipp.ipp_fields & IPPF_MULTICAST_HOPS)) {
6924b3d0fa4fSseb 		option_exists |= IPPF_MULTICAST_HOPS;
6925b3d0fa4fSseb 		is_sticky |= IPPF_MULTICAST_HOPS;
69267c478bd9Sstevel@tonic-gate 	}
69277c478bd9Sstevel@tonic-gate 
69287c478bd9Sstevel@tonic-gate 	if (!(ignore & IPPF_TCLASS)) {
69297c478bd9Sstevel@tonic-gate 		if (ipp->ipp_fields & IPPF_TCLASS) {
69307c478bd9Sstevel@tonic-gate 			option_exists |= IPPF_TCLASS;
69317c478bd9Sstevel@tonic-gate 		} else if (udp->udp_sticky_ipp.ipp_fields & IPPF_TCLASS) {
69327c478bd9Sstevel@tonic-gate 			option_exists |= IPPF_TCLASS;
69337c478bd9Sstevel@tonic-gate 			is_sticky |= IPPF_TCLASS;
69347c478bd9Sstevel@tonic-gate 		}
69357c478bd9Sstevel@tonic-gate 	}
69367c478bd9Sstevel@tonic-gate 
69374c10bc16Spwernau 	if (!(ignore & IPPF_NEXTHOP) &&
69384c10bc16Spwernau 	    (udp->udp_sticky_ipp.ipp_fields & IPPF_NEXTHOP)) {
69394c10bc16Spwernau 		option_exists |= IPPF_NEXTHOP;
69404c10bc16Spwernau 		is_sticky |= IPPF_NEXTHOP;
69414c10bc16Spwernau 	}
69424c10bc16Spwernau 
69437c478bd9Sstevel@tonic-gate no_options:
69447c478bd9Sstevel@tonic-gate 
69457c478bd9Sstevel@tonic-gate 	/*
69467c478bd9Sstevel@tonic-gate 	 * If any options carried in the ip6i_t were specified, we
69477c478bd9Sstevel@tonic-gate 	 * need to account for the ip6i_t in the data we'll be sending
69487c478bd9Sstevel@tonic-gate 	 * down.
69497c478bd9Sstevel@tonic-gate 	 */
69507c478bd9Sstevel@tonic-gate 	if (option_exists & IPPF_HAS_IP6I)
69517c478bd9Sstevel@tonic-gate 		udp_ip_hdr_len += sizeof (ip6i_t);
69527c478bd9Sstevel@tonic-gate 
69537c478bd9Sstevel@tonic-gate 	/* check/fix buffer config, setup pointers into it */
69547c478bd9Sstevel@tonic-gate 	ip6h = (ip6_t *)&mp1->b_rptr[-udp_ip_hdr_len];
6955ff550d0eSmasputra 	if (DB_REF(mp1) != 1 || ((unsigned char *)ip6h < DB_BASE(mp1)) ||
69567c478bd9Sstevel@tonic-gate 	    !OK_32PTR(ip6h)) {
6957fc80c0dfSnordmark 
69587c478bd9Sstevel@tonic-gate 		/* Try to get everything in a single mblk next time */
69597c478bd9Sstevel@tonic-gate 		if (udp_ip_hdr_len > udp->udp_max_hdr_len) {
69607c478bd9Sstevel@tonic-gate 			udp->udp_max_hdr_len = udp_ip_hdr_len;
6961fc80c0dfSnordmark 			sth_wroff = udp->udp_max_hdr_len + us->us_wroff_extra;
69627c478bd9Sstevel@tonic-gate 		}
6963fc80c0dfSnordmark 
6964f4b3ec61Sdh 		mp2 = allocb(udp_ip_hdr_len + us->us_wroff_extra, BPRI_LO);
6965ff550d0eSmasputra 		if (mp2 == NULL) {
6966ff550d0eSmasputra 			*error = ENOMEM;
6967fc80c0dfSnordmark 			rw_exit(&udp->udp_rwlock);
6968ff550d0eSmasputra 			goto done;
69697c478bd9Sstevel@tonic-gate 		}
6970ff550d0eSmasputra 		mp2->b_wptr = DB_LIM(mp2);
6971ff550d0eSmasputra 		mp2->b_cont = mp1;
6972ff550d0eSmasputra 		mp1 = mp2;
6973ff550d0eSmasputra 		if (DB_TYPE(mp) != M_DATA)
6974ff550d0eSmasputra 			mp->b_cont = mp1;
6975ff550d0eSmasputra 		else
6976ff550d0eSmasputra 			mp = mp1;
6977ff550d0eSmasputra 
69787c478bd9Sstevel@tonic-gate 		ip6h = (ip6_t *)(mp1->b_wptr - udp_ip_hdr_len);
69797c478bd9Sstevel@tonic-gate 	}
69807c478bd9Sstevel@tonic-gate 	mp1->b_rptr = (unsigned char *)ip6h;
69817c478bd9Sstevel@tonic-gate 	ip6i = (ip6i_t *)ip6h;
69827c478bd9Sstevel@tonic-gate 
69837c478bd9Sstevel@tonic-gate #define	ANCIL_OR_STICKY_PTR(f) ((is_sticky & f) ? &udp->udp_sticky_ipp : ipp)
69847c478bd9Sstevel@tonic-gate 	if (option_exists & IPPF_HAS_IP6I) {
69857c478bd9Sstevel@tonic-gate 		ip6h = (ip6_t *)&ip6i[1];
69867c478bd9Sstevel@tonic-gate 		ip6i->ip6i_flags = 0;
69877c478bd9Sstevel@tonic-gate 		ip6i->ip6i_vcf = IPV6_DEFAULT_VERS_AND_FLOW;
69887c478bd9Sstevel@tonic-gate 
69897c478bd9Sstevel@tonic-gate 		/* sin6_scope_id takes precendence over IPPF_IFINDEX */
69907c478bd9Sstevel@tonic-gate 		if (option_exists & IPPF_SCOPE_ID) {
69917c478bd9Sstevel@tonic-gate 			ip6i->ip6i_flags |= IP6I_IFINDEX;
69927c478bd9Sstevel@tonic-gate 			ip6i->ip6i_ifindex = sin6->sin6_scope_id;
69937c478bd9Sstevel@tonic-gate 		} else if (option_exists & IPPF_IFINDEX) {
69947c478bd9Sstevel@tonic-gate 			tipp = ANCIL_OR_STICKY_PTR(IPPF_IFINDEX);
69957c478bd9Sstevel@tonic-gate 			ASSERT(tipp->ipp_ifindex != 0);
69967c478bd9Sstevel@tonic-gate 			ip6i->ip6i_flags |= IP6I_IFINDEX;
69977c478bd9Sstevel@tonic-gate 			ip6i->ip6i_ifindex = tipp->ipp_ifindex;
69987c478bd9Sstevel@tonic-gate 		}
69997c478bd9Sstevel@tonic-gate 
70007c478bd9Sstevel@tonic-gate 		if (option_exists & IPPF_ADDR) {
70017c478bd9Sstevel@tonic-gate 			/*
70027c478bd9Sstevel@tonic-gate 			 * Enable per-packet source address verification if
70037c478bd9Sstevel@tonic-gate 			 * IPV6_PKTINFO specified the source address.
70047c478bd9Sstevel@tonic-gate 			 * ip6_src is set in the transport's _wput function.
70057c478bd9Sstevel@tonic-gate 			 */
70067c478bd9Sstevel@tonic-gate 			ip6i->ip6i_flags |= IP6I_VERIFY_SRC;
70077c478bd9Sstevel@tonic-gate 		}
70087c478bd9Sstevel@tonic-gate 
70097c478bd9Sstevel@tonic-gate 		if (option_exists & IPPF_DONTFRAG) {
70107c478bd9Sstevel@tonic-gate 			ip6i->ip6i_flags |= IP6I_DONTFRAG;
70117c478bd9Sstevel@tonic-gate 		}
70127c478bd9Sstevel@tonic-gate 
70137c478bd9Sstevel@tonic-gate 		if (option_exists & IPPF_USE_MIN_MTU) {
70147c478bd9Sstevel@tonic-gate 			ip6i->ip6i_flags = IP6I_API_USE_MIN_MTU(
70157c478bd9Sstevel@tonic-gate 			    ip6i->ip6i_flags, ipp->ipp_use_min_mtu);
70167c478bd9Sstevel@tonic-gate 		}
70177c478bd9Sstevel@tonic-gate 
70187c478bd9Sstevel@tonic-gate 		if (option_exists & IPPF_NEXTHOP) {
70197c478bd9Sstevel@tonic-gate 			tipp = ANCIL_OR_STICKY_PTR(IPPF_NEXTHOP);
70207c478bd9Sstevel@tonic-gate 			ASSERT(!IN6_IS_ADDR_UNSPECIFIED(&tipp->ipp_nexthop));
70217c478bd9Sstevel@tonic-gate 			ip6i->ip6i_flags |= IP6I_NEXTHOP;
70227c478bd9Sstevel@tonic-gate 			ip6i->ip6i_nexthop = tipp->ipp_nexthop;
70237c478bd9Sstevel@tonic-gate 		}
70247c478bd9Sstevel@tonic-gate 
70257c478bd9Sstevel@tonic-gate 		/*
70267c478bd9Sstevel@tonic-gate 		 * tell IP this is an ip6i_t private header
70277c478bd9Sstevel@tonic-gate 		 */
70287c478bd9Sstevel@tonic-gate 		ip6i->ip6i_nxt = IPPROTO_RAW;
70297c478bd9Sstevel@tonic-gate 	}
70307c478bd9Sstevel@tonic-gate 
70317c478bd9Sstevel@tonic-gate 	/* Initialize IPv6 header */
70327c478bd9Sstevel@tonic-gate 	ip6h->ip6_vcf = IPV6_DEFAULT_VERS_AND_FLOW;
70337c478bd9Sstevel@tonic-gate 	bzero(&ip6h->ip6_src, sizeof (ip6h->ip6_src));
70347c478bd9Sstevel@tonic-gate 
7035b3d0fa4fSseb 	/* Set the hoplimit of the outgoing packet. */
70367c478bd9Sstevel@tonic-gate 	if (option_exists & IPPF_HOPLIMIT) {
7037b3d0fa4fSseb 		/* IPV6_HOPLIMIT ancillary data overrides all other settings. */
7038b3d0fa4fSseb 		ip6h->ip6_hops = ipp->ipp_hoplimit;
70397c478bd9Sstevel@tonic-gate 		ip6i->ip6i_flags |= IP6I_HOPLIMIT;
7040b3d0fa4fSseb 	} else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) {
7041b3d0fa4fSseb 		ip6h->ip6_hops = udp->udp_multicast_ttl;
7042b3d0fa4fSseb 		if (option_exists & IPPF_MULTICAST_HOPS)
7043b3d0fa4fSseb 			ip6i->ip6i_flags |= IP6I_HOPLIMIT;
70447c478bd9Sstevel@tonic-gate 	} else {
70457c478bd9Sstevel@tonic-gate 		ip6h->ip6_hops = udp->udp_ttl;
7046b3d0fa4fSseb 		if (option_exists & IPPF_UNICAST_HOPS)
7047b3d0fa4fSseb 			ip6i->ip6i_flags |= IP6I_HOPLIMIT;
70487c478bd9Sstevel@tonic-gate 	}
70497c478bd9Sstevel@tonic-gate 
70507c478bd9Sstevel@tonic-gate 	if (option_exists & IPPF_ADDR) {
70517c478bd9Sstevel@tonic-gate 		tipp = ANCIL_OR_STICKY_PTR(IPPF_ADDR);
70527c478bd9Sstevel@tonic-gate 		ASSERT(!IN6_IS_ADDR_UNSPECIFIED(&tipp->ipp_addr));
70537c478bd9Sstevel@tonic-gate 		ip6h->ip6_src = tipp->ipp_addr;
70547c478bd9Sstevel@tonic-gate 	} else {
70557c478bd9Sstevel@tonic-gate 		/*
70567c478bd9Sstevel@tonic-gate 		 * The source address was not set using IPV6_PKTINFO.
70577c478bd9Sstevel@tonic-gate 		 * First look at the bound source.
70587c478bd9Sstevel@tonic-gate 		 * If unspecified fallback to __sin6_src_id.
70597c478bd9Sstevel@tonic-gate 		 */
70607c478bd9Sstevel@tonic-gate 		ip6h->ip6_src = udp->udp_v6src;
70617c478bd9Sstevel@tonic-gate 		if (sin6->__sin6_src_id != 0 &&
70627c478bd9Sstevel@tonic-gate 		    IN6_IS_ADDR_UNSPECIFIED(&ip6h->ip6_src)) {
70637c478bd9Sstevel@tonic-gate 			ip_srcid_find_id(sin6->__sin6_src_id,
7064f4b3ec61Sdh 			    &ip6h->ip6_src, connp->conn_zoneid,
7065f4b3ec61Sdh 			    us->us_netstack);
70667c478bd9Sstevel@tonic-gate 		}
70677c478bd9Sstevel@tonic-gate 	}
70687c478bd9Sstevel@tonic-gate 
70697c478bd9Sstevel@tonic-gate 	nxthdr_ptr = (uint8_t *)&ip6h->ip6_nxt;
70707c478bd9Sstevel@tonic-gate 	cp = (uint8_t *)&ip6h[1];
70717c478bd9Sstevel@tonic-gate 
70727c478bd9Sstevel@tonic-gate 	/*
70737c478bd9Sstevel@tonic-gate 	 * Here's where we have to start stringing together
70747c478bd9Sstevel@tonic-gate 	 * any extension headers in the right order:
70757c478bd9Sstevel@tonic-gate 	 * Hop-by-hop, destination, routing, and final destination opts.
70767c478bd9Sstevel@tonic-gate 	 */
70777c478bd9Sstevel@tonic-gate 	if (option_exists & IPPF_HOPOPTS) {
70787c478bd9Sstevel@tonic-gate 		/* Hop-by-hop options */
70797c478bd9Sstevel@tonic-gate 		ip6_hbh_t *hbh = (ip6_hbh_t *)cp;
70807c478bd9Sstevel@tonic-gate 		tipp = ANCIL_OR_STICKY_PTR(IPPF_HOPOPTS);
7081dc3879f9Sjarrett 		if (hopoptslen == 0) {
7082dc3879f9Sjarrett 			hopoptsptr = tipp->ipp_hopopts;
7083dc3879f9Sjarrett 			hopoptslen = tipp->ipp_hopoptslen;
7084dc3879f9Sjarrett 			is_ancillary = B_TRUE;
7085dc3879f9Sjarrett 		}
70867c478bd9Sstevel@tonic-gate 
70877c478bd9Sstevel@tonic-gate 		*nxthdr_ptr = IPPROTO_HOPOPTS;
70887c478bd9Sstevel@tonic-gate 		nxthdr_ptr = &hbh->ip6h_nxt;
70897c478bd9Sstevel@tonic-gate 
7090dc3879f9Sjarrett 		bcopy(hopoptsptr, cp, hopoptslen);
7091dc3879f9Sjarrett 		cp += hopoptslen;
7092dc3879f9Sjarrett 
7093dc3879f9Sjarrett 		if (hopoptsptr != NULL && !is_ancillary) {
7094dc3879f9Sjarrett 			kmem_free(hopoptsptr, hopoptslen);
7095dc3879f9Sjarrett 			hopoptsptr = NULL;
7096dc3879f9Sjarrett 			hopoptslen = 0;
7097dc3879f9Sjarrett 		}
70987c478bd9Sstevel@tonic-gate 	}
70997c478bd9Sstevel@tonic-gate 	/*
71007c478bd9Sstevel@tonic-gate 	 * En-route destination options
71017c478bd9Sstevel@tonic-gate 	 * Only do them if there's a routing header as well
71027c478bd9Sstevel@tonic-gate 	 */
71037c478bd9Sstevel@tonic-gate 	if (option_exists & IPPF_RTDSTOPTS) {
71047c478bd9Sstevel@tonic-gate 		ip6_dest_t *dst = (ip6_dest_t *)cp;
71057c478bd9Sstevel@tonic-gate 		tipp = ANCIL_OR_STICKY_PTR(IPPF_RTDSTOPTS);
71067c478bd9Sstevel@tonic-gate 
71077c478bd9Sstevel@tonic-gate 		*nxthdr_ptr = IPPROTO_DSTOPTS;
71087c478bd9Sstevel@tonic-gate 		nxthdr_ptr = &dst->ip6d_nxt;
71097c478bd9Sstevel@tonic-gate 
71107c478bd9Sstevel@tonic-gate 		bcopy(tipp->ipp_rtdstopts, cp, tipp->ipp_rtdstoptslen);
71117c478bd9Sstevel@tonic-gate 		cp += tipp->ipp_rtdstoptslen;
71127c478bd9Sstevel@tonic-gate 	}
71137c478bd9Sstevel@tonic-gate 	/*
71147c478bd9Sstevel@tonic-gate 	 * Routing header next
71157c478bd9Sstevel@tonic-gate 	 */
71167c478bd9Sstevel@tonic-gate 	if (option_exists & IPPF_RTHDR) {
71177c478bd9Sstevel@tonic-gate 		ip6_rthdr_t *rt = (ip6_rthdr_t *)cp;
71187c478bd9Sstevel@tonic-gate 		tipp = ANCIL_OR_STICKY_PTR(IPPF_RTHDR);
71197c478bd9Sstevel@tonic-gate 
71207c478bd9Sstevel@tonic-gate 		*nxthdr_ptr = IPPROTO_ROUTING;
71217c478bd9Sstevel@tonic-gate 		nxthdr_ptr = &rt->ip6r_nxt;
71227c478bd9Sstevel@tonic-gate 
71237c478bd9Sstevel@tonic-gate 		bcopy(tipp->ipp_rthdr, cp, tipp->ipp_rthdrlen);
71247c478bd9Sstevel@tonic-gate 		cp += tipp->ipp_rthdrlen;
71257c478bd9Sstevel@tonic-gate 	}
71267c478bd9Sstevel@tonic-gate 	/*
71277c478bd9Sstevel@tonic-gate 	 * Do ultimate destination options
71287c478bd9Sstevel@tonic-gate 	 */
71297c478bd9Sstevel@tonic-gate 	if (option_exists & IPPF_DSTOPTS) {
71307c478bd9Sstevel@tonic-gate 		ip6_dest_t *dest = (ip6_dest_t *)cp;
71317c478bd9Sstevel@tonic-gate 		tipp = ANCIL_OR_STICKY_PTR(IPPF_DSTOPTS);
71327c478bd9Sstevel@tonic-gate 
71337c478bd9Sstevel@tonic-gate 		*nxthdr_ptr = IPPROTO_DSTOPTS;
71347c478bd9Sstevel@tonic-gate 		nxthdr_ptr = &dest->ip6d_nxt;
71357c478bd9Sstevel@tonic-gate 
71367c478bd9Sstevel@tonic-gate 		bcopy(tipp->ipp_dstopts, cp, tipp->ipp_dstoptslen);
71377c478bd9Sstevel@tonic-gate 		cp += tipp->ipp_dstoptslen;
71387c478bd9Sstevel@tonic-gate 	}
71397c478bd9Sstevel@tonic-gate 	/*
71407c478bd9Sstevel@tonic-gate 	 * Now set the last header pointer to the proto passed in
71417c478bd9Sstevel@tonic-gate 	 */
71427c478bd9Sstevel@tonic-gate 	ASSERT((int)(cp - (uint8_t *)ip6i) == (udp_ip_hdr_len - UDPH_SIZE));
71437c478bd9Sstevel@tonic-gate 	*nxthdr_ptr = IPPROTO_UDP;
71447c478bd9Sstevel@tonic-gate 
71457c478bd9Sstevel@tonic-gate 	/* Update UDP header */
71467c478bd9Sstevel@tonic-gate 	udph = (udpha_t *)((uchar_t *)ip6i + udp_ip_hdr_len - UDPH_SIZE);
71477c478bd9Sstevel@tonic-gate 	udph->uha_dst_port = sin6->sin6_port;
71487c478bd9Sstevel@tonic-gate 	udph->uha_src_port = udp->udp_port;
71497c478bd9Sstevel@tonic-gate 
71507c478bd9Sstevel@tonic-gate 	/*
71517c478bd9Sstevel@tonic-gate 	 * Copy in the destination address
71527c478bd9Sstevel@tonic-gate 	 */
715345916cd2Sjpk 	ip6h->ip6_dst = ip6_dst;
71547c478bd9Sstevel@tonic-gate 
71557c478bd9Sstevel@tonic-gate 	ip6h->ip6_vcf =
71567c478bd9Sstevel@tonic-gate 	    (IPV6_DEFAULT_VERS_AND_FLOW & IPV6_VERS_AND_FLOW_MASK) |
71577c478bd9Sstevel@tonic-gate 	    (sin6->sin6_flowinfo & ~IPV6_VERS_AND_FLOW_MASK);
71587c478bd9Sstevel@tonic-gate 
71597c478bd9Sstevel@tonic-gate 	if (option_exists & IPPF_TCLASS) {
71607c478bd9Sstevel@tonic-gate 		tipp = ANCIL_OR_STICKY_PTR(IPPF_TCLASS);
71617c478bd9Sstevel@tonic-gate 		ip6h->ip6_vcf = IPV6_TCLASS_FLOW(ip6h->ip6_vcf,
71627c478bd9Sstevel@tonic-gate 		    tipp->ipp_tclass);
71637c478bd9Sstevel@tonic-gate 	}
7164fc80c0dfSnordmark 	rw_exit(&udp->udp_rwlock);
71657c478bd9Sstevel@tonic-gate 
71667c478bd9Sstevel@tonic-gate 	if (option_exists & IPPF_RTHDR) {
71677c478bd9Sstevel@tonic-gate 		ip6_rthdr_t	*rth;
71687c478bd9Sstevel@tonic-gate 
71697c478bd9Sstevel@tonic-gate 		/*
71707c478bd9Sstevel@tonic-gate 		 * Perform any processing needed for source routing.
71717c478bd9Sstevel@tonic-gate 		 * We know that all extension headers will be in the same mblk
71727c478bd9Sstevel@tonic-gate 		 * as the IPv6 header.
71737c478bd9Sstevel@tonic-gate 		 */
71747c478bd9Sstevel@tonic-gate 		rth = ip_find_rthdr_v6(ip6h, mp1->b_wptr);
71757c478bd9Sstevel@tonic-gate 		if (rth != NULL && rth->ip6r_segleft != 0) {
71767c478bd9Sstevel@tonic-gate 			if (rth->ip6r_type != IPV6_RTHDR_TYPE_0) {
71777c478bd9Sstevel@tonic-gate 				/*
71787c478bd9Sstevel@tonic-gate 				 * Drop packet - only support Type 0 routing.
71797c478bd9Sstevel@tonic-gate 				 * Notify the application as well.
71807c478bd9Sstevel@tonic-gate 				 */
7181ff550d0eSmasputra 				*error = EPROTO;
7182ff550d0eSmasputra 				goto done;
71837c478bd9Sstevel@tonic-gate 			}
71847c478bd9Sstevel@tonic-gate 
71857c478bd9Sstevel@tonic-gate 			/*
71867c478bd9Sstevel@tonic-gate 			 * rth->ip6r_len is twice the number of
71877c478bd9Sstevel@tonic-gate 			 * addresses in the header. Thus it must be even.
71887c478bd9Sstevel@tonic-gate 			 */
71897c478bd9Sstevel@tonic-gate 			if (rth->ip6r_len & 0x1) {
7190ff550d0eSmasputra 				*error = EPROTO;
7191ff550d0eSmasputra 				goto done;
71927c478bd9Sstevel@tonic-gate 			}
71937c478bd9Sstevel@tonic-gate 			/*
71947c478bd9Sstevel@tonic-gate 			 * Shuffle the routing header and ip6_dst
71957c478bd9Sstevel@tonic-gate 			 * addresses, and get the checksum difference
71967c478bd9Sstevel@tonic-gate 			 * between the first hop (in ip6_dst) and
71977c478bd9Sstevel@tonic-gate 			 * the destination (in the last routing hdr entry).
71987c478bd9Sstevel@tonic-gate 			 */
7199f4b3ec61Sdh 			csum = ip_massage_options_v6(ip6h, rth,
7200f4b3ec61Sdh 			    us->us_netstack);
72017c478bd9Sstevel@tonic-gate 			/*
72027c478bd9Sstevel@tonic-gate 			 * Verify that the first hop isn't a mapped address.
72037c478bd9Sstevel@tonic-gate 			 * Routers along the path need to do this verification
72047c478bd9Sstevel@tonic-gate 			 * for subsequent hops.
72057c478bd9Sstevel@tonic-gate 			 */
72067c478bd9Sstevel@tonic-gate 			if (IN6_IS_ADDR_V4MAPPED(&ip6h->ip6_dst)) {
7207ff550d0eSmasputra 				*error = EADDRNOTAVAIL;
7208ff550d0eSmasputra 				goto done;
72097c478bd9Sstevel@tonic-gate 			}
72107c478bd9Sstevel@tonic-gate 
72117c478bd9Sstevel@tonic-gate 			cp += (rth->ip6r_len + 1)*8;
72127c478bd9Sstevel@tonic-gate 		}
72137c478bd9Sstevel@tonic-gate 	}
72147c478bd9Sstevel@tonic-gate 
72157c478bd9Sstevel@tonic-gate 	/* count up length of UDP packet */
72167c478bd9Sstevel@tonic-gate 	ip_len = (mp1->b_wptr - (unsigned char *)ip6h) - IPV6_HDR_LEN;
7217ff550d0eSmasputra 	if ((mp2 = mp1->b_cont) != NULL) {
7218ff550d0eSmasputra 		do {
7219ff550d0eSmasputra 			ASSERT((uintptr_t)MBLKL(mp2) <= (uintptr_t)UINT_MAX);
7220ff550d0eSmasputra 			ip_len += (uint32_t)MBLKL(mp2);
7221ff550d0eSmasputra 		} while ((mp2 = mp2->b_cont) != NULL);
72227c478bd9Sstevel@tonic-gate 	}
72237c478bd9Sstevel@tonic-gate 
72247c478bd9Sstevel@tonic-gate 	/*
72257c478bd9Sstevel@tonic-gate 	 * If the size of the packet is greater than the maximum allowed by
72267c478bd9Sstevel@tonic-gate 	 * ip, return an error. Passing this down could cause panics because
72277c478bd9Sstevel@tonic-gate 	 * the size will have wrapped and be inconsistent with the msg size.
72287c478bd9Sstevel@tonic-gate 	 */
72297c478bd9Sstevel@tonic-gate 	if (ip_len > IP_MAXPACKET) {
7230ff550d0eSmasputra 		*error = EMSGSIZE;
7231ff550d0eSmasputra 		goto done;
72327c478bd9Sstevel@tonic-gate 	}
72337c478bd9Sstevel@tonic-gate 
72347c478bd9Sstevel@tonic-gate 	/* Store the UDP length. Subtract length of extension hdrs */
72357c478bd9Sstevel@tonic-gate 	udph->uha_length = htons(ip_len + IPV6_HDR_LEN -
72367c478bd9Sstevel@tonic-gate 	    (int)((uchar_t *)udph - (uchar_t *)ip6h));
72377c478bd9Sstevel@tonic-gate 
72387c478bd9Sstevel@tonic-gate 	/*
72397c478bd9Sstevel@tonic-gate 	 * We make it easy for IP to include our pseudo header
72407c478bd9Sstevel@tonic-gate 	 * by putting our length in uh_checksum, modified (if
72417c478bd9Sstevel@tonic-gate 	 * we have a routing header) by the checksum difference
72427c478bd9Sstevel@tonic-gate 	 * between the ultimate destination and first hop addresses.
72437c478bd9Sstevel@tonic-gate 	 * Note: UDP over IPv6 must always checksum the packet.
72447c478bd9Sstevel@tonic-gate 	 */
72457c478bd9Sstevel@tonic-gate 	csum += udph->uha_length;
72467c478bd9Sstevel@tonic-gate 	csum = (csum & 0xFFFF) + (csum >> 16);
72477c478bd9Sstevel@tonic-gate 	udph->uha_checksum = (uint16_t)csum;
72487c478bd9Sstevel@tonic-gate 
72497c478bd9Sstevel@tonic-gate #ifdef _LITTLE_ENDIAN
72507c478bd9Sstevel@tonic-gate 	ip_len = htons(ip_len);
72517c478bd9Sstevel@tonic-gate #endif
72527c478bd9Sstevel@tonic-gate 	ip6h->ip6_plen = ip_len;
725345916cd2Sjpk 	if (DB_CRED(mp) != NULL)
725445916cd2Sjpk 		mblk_setcred(mp1, DB_CRED(mp));
72557c478bd9Sstevel@tonic-gate 
7256ff550d0eSmasputra 	if (DB_TYPE(mp) != M_DATA) {
7257ff550d0eSmasputra 		ASSERT(mp != mp1);
7258ff550d0eSmasputra 		freeb(mp);
7259ff550d0eSmasputra 	}
7260ff550d0eSmasputra 
7261ff550d0eSmasputra 	/* mp has been consumed and we'll return success */
7262ff550d0eSmasputra 	ASSERT(*error == 0);
7263ff550d0eSmasputra 	mp = NULL;
72647c478bd9Sstevel@tonic-gate 
72657c478bd9Sstevel@tonic-gate 	/* We're done. Pass the packet to IP */
7266fc80c0dfSnordmark 	BUMP_MIB(&us->us_udp_mib, udpHCOutDatagrams);
7267ff550d0eSmasputra 	ip_output_v6(connp, mp1, q, IP_WPUT);
7268ff550d0eSmasputra 
7269ff550d0eSmasputra done:
7270fc80c0dfSnordmark 	if (sth_wroff != 0) {
7271fc80c0dfSnordmark 		(void) mi_set_sth_wroff(RD(q),
7272fc80c0dfSnordmark 		    udp->udp_max_hdr_len + us->us_wroff_extra);
7273fc80c0dfSnordmark 	}
7274dc3879f9Sjarrett 	if (hopoptsptr != NULL && !is_ancillary) {
7275dc3879f9Sjarrett 		kmem_free(hopoptsptr, hopoptslen);
7276dc3879f9Sjarrett 		hopoptsptr = NULL;
7277dc3879f9Sjarrett 	}
7278ff550d0eSmasputra 	if (*error != 0) {
7279ff550d0eSmasputra 		ASSERT(mp != NULL);
7280fc80c0dfSnordmark 		BUMP_MIB(&us->us_udp_mib, udpOutErrors);
7281ff550d0eSmasputra 	}
7282ff550d0eSmasputra 	return (mp);
72837c478bd9Sstevel@tonic-gate }
72847c478bd9Sstevel@tonic-gate 
7285ca9327a6Smeem 
7286ca9327a6Smeem static int
7287ca9327a6Smeem udp_getpeername(udp_t *udp, struct sockaddr *sa, uint_t *salenp)
7288ca9327a6Smeem {
7289ca9327a6Smeem 	sin_t *sin = (sin_t *)sa;
7290ca9327a6Smeem 	sin6_t *sin6 = (sin6_t *)sa;
7291ca9327a6Smeem 
7292ca9327a6Smeem 	ASSERT(RW_LOCK_HELD(&udp->udp_rwlock));
7293ca9327a6Smeem 
7294ca9327a6Smeem 	if (udp->udp_state != TS_DATA_XFER)
7295ca9327a6Smeem 		return (ENOTCONN);
7296ca9327a6Smeem 
7297ca9327a6Smeem 	switch (udp->udp_family) {
7298ca9327a6Smeem 	case AF_INET:
7299ca9327a6Smeem 		ASSERT(udp->udp_ipversion == IPV4_VERSION);
7300ca9327a6Smeem 
7301ca9327a6Smeem 		if (*salenp < sizeof (sin_t))
7302ca9327a6Smeem 			return (EINVAL);
7303ca9327a6Smeem 
7304ca9327a6Smeem 		*salenp = sizeof (sin_t);
7305ca9327a6Smeem 		*sin = sin_null;
7306ca9327a6Smeem 		sin->sin_family = AF_INET;
7307ca9327a6Smeem 		sin->sin_port = udp->udp_dstport;
7308ca9327a6Smeem 		sin->sin_addr.s_addr = V4_PART_OF_V6(udp->udp_v6dst);
7309ca9327a6Smeem 		break;
7310ca9327a6Smeem 
7311ca9327a6Smeem 	case AF_INET6:
7312ca9327a6Smeem 		if (*salenp < sizeof (sin6_t))
7313ca9327a6Smeem 			return (EINVAL);
7314ca9327a6Smeem 
7315ca9327a6Smeem 		*salenp = sizeof (sin6_t);
7316ca9327a6Smeem 		*sin6 = sin6_null;
7317ca9327a6Smeem 		sin6->sin6_family = AF_INET6;
7318ca9327a6Smeem 		sin6->sin6_port = udp->udp_dstport;
7319ca9327a6Smeem 		sin6->sin6_addr = udp->udp_v6dst;
7320ca9327a6Smeem 		sin6->sin6_flowinfo = udp->udp_flowinfo;
7321ca9327a6Smeem 		break;
7322ca9327a6Smeem 	}
7323ca9327a6Smeem 
7324ca9327a6Smeem 	return (0);
7325ca9327a6Smeem }
7326ca9327a6Smeem 
7327ca9327a6Smeem static int
7328ca9327a6Smeem udp_getmyname(udp_t *udp, struct sockaddr *sa, uint_t *salenp)
7329ca9327a6Smeem {
7330ca9327a6Smeem 	sin_t *sin = (sin_t *)sa;
7331ca9327a6Smeem 	sin6_t *sin6 = (sin6_t *)sa;
7332ca9327a6Smeem 
7333ca9327a6Smeem 	ASSERT(RW_LOCK_HELD(&udp->udp_rwlock));
7334ca9327a6Smeem 
7335ca9327a6Smeem 	switch (udp->udp_family) {
7336ca9327a6Smeem 	case AF_INET:
7337ca9327a6Smeem 		ASSERT(udp->udp_ipversion == IPV4_VERSION);
7338ca9327a6Smeem 
7339ca9327a6Smeem 		if (*salenp < sizeof (sin_t))
7340ca9327a6Smeem 			return (EINVAL);
7341ca9327a6Smeem 
7342ca9327a6Smeem 		*salenp = sizeof (sin_t);
7343ca9327a6Smeem 		*sin = sin_null;
7344ca9327a6Smeem 		sin->sin_family = AF_INET;
7345ca9327a6Smeem 		sin->sin_port = udp->udp_port;
7346ca9327a6Smeem 
7347ca9327a6Smeem 		/*
7348ca9327a6Smeem 		 * If udp_v6src is unspecified, we might be bound to broadcast
7349ca9327a6Smeem 		 * / multicast.  Use udp_bound_v6src as local address instead
7350ca9327a6Smeem 		 * (that could also still be unspecified).
7351ca9327a6Smeem 		 */
7352ca9327a6Smeem 		if (!IN6_IS_ADDR_V4MAPPED_ANY(&udp->udp_v6src) &&
7353ca9327a6Smeem 		    !IN6_IS_ADDR_UNSPECIFIED(&udp->udp_v6src)) {
7354ca9327a6Smeem 			sin->sin_addr.s_addr = V4_PART_OF_V6(udp->udp_v6src);
7355ca9327a6Smeem 		} else {
7356ca9327a6Smeem 			sin->sin_addr.s_addr =
7357ca9327a6Smeem 			    V4_PART_OF_V6(udp->udp_bound_v6src);
7358ca9327a6Smeem 		}
7359ca9327a6Smeem 		break;
7360ca9327a6Smeem 
7361ca9327a6Smeem 	case AF_INET6:
7362ca9327a6Smeem 		if (*salenp < sizeof (sin6_t))
7363ca9327a6Smeem 			return (EINVAL);
7364ca9327a6Smeem 
7365ca9327a6Smeem 		*salenp = sizeof (sin6_t);
7366ca9327a6Smeem 		*sin6 = sin6_null;
7367ca9327a6Smeem 		sin6->sin6_family = AF_INET6;
7368ca9327a6Smeem 		sin6->sin6_port = udp->udp_port;
7369ca9327a6Smeem 		sin6->sin6_flowinfo = udp->udp_flowinfo;
7370ca9327a6Smeem 
7371ca9327a6Smeem 		/*
7372ca9327a6Smeem 		 * If udp_v6src is unspecified, we might be bound to broadcast
7373ca9327a6Smeem 		 * / multicast.  Use udp_bound_v6src as local address instead
7374ca9327a6Smeem 		 * (that could also still be unspecified).
7375ca9327a6Smeem 		 */
7376ca9327a6Smeem 		if (!IN6_IS_ADDR_UNSPECIFIED(&udp->udp_v6src))
7377ca9327a6Smeem 			sin6->sin6_addr = udp->udp_v6src;
7378ca9327a6Smeem 		else
7379ca9327a6Smeem 			sin6->sin6_addr = udp->udp_bound_v6src;
7380ca9327a6Smeem 		break;
7381ca9327a6Smeem 	}
7382ca9327a6Smeem 
7383ca9327a6Smeem 	return (0);
7384ca9327a6Smeem }
7385ca9327a6Smeem 
7386ca9327a6Smeem /*
7387ca9327a6Smeem  * Handle special out-of-band ioctl requests (see PSARC/2008/265).
7388ca9327a6Smeem  */
7389ca9327a6Smeem static void
7390ca9327a6Smeem udp_wput_cmdblk(queue_t *q, mblk_t *mp)
7391ca9327a6Smeem {
7392ca9327a6Smeem 	void	*data;
7393ca9327a6Smeem 	mblk_t	*datamp = mp->b_cont;
7394ca9327a6Smeem 	udp_t	*udp = Q_TO_UDP(q);
7395ca9327a6Smeem 	cmdblk_t *cmdp = (cmdblk_t *)mp->b_rptr;
7396ca9327a6Smeem 
7397ca9327a6Smeem 	if (datamp == NULL || MBLKL(datamp) < cmdp->cb_len) {
7398ca9327a6Smeem 		cmdp->cb_error = EPROTO;
7399ca9327a6Smeem 		qreply(q, mp);
7400ca9327a6Smeem 		return;
7401ca9327a6Smeem 	}
7402ca9327a6Smeem 	data = datamp->b_rptr;
7403ca9327a6Smeem 
7404ca9327a6Smeem 	rw_enter(&udp->udp_rwlock, RW_READER);
7405ca9327a6Smeem 	switch (cmdp->cb_cmd) {
7406ca9327a6Smeem 	case TI_GETPEERNAME:
7407ca9327a6Smeem 		cmdp->cb_error = udp_getpeername(udp, data, &cmdp->cb_len);
7408ca9327a6Smeem 		break;
7409ca9327a6Smeem 	case TI_GETMYNAME:
7410ca9327a6Smeem 		cmdp->cb_error = udp_getmyname(udp, data, &cmdp->cb_len);
7411ca9327a6Smeem 		break;
7412ca9327a6Smeem 	default:
7413ca9327a6Smeem 		cmdp->cb_error = EINVAL;
7414ca9327a6Smeem 		break;
7415ca9327a6Smeem 	}
7416ca9327a6Smeem 	rw_exit(&udp->udp_rwlock);
7417ca9327a6Smeem 
7418ca9327a6Smeem 	qreply(q, mp);
7419ca9327a6Smeem }
7420ca9327a6Smeem 
74217c478bd9Sstevel@tonic-gate static void
74227c478bd9Sstevel@tonic-gate udp_wput_other(queue_t *q, mblk_t *mp)
74237c478bd9Sstevel@tonic-gate {
74247c478bd9Sstevel@tonic-gate 	uchar_t	*rptr = mp->b_rptr;
74257c478bd9Sstevel@tonic-gate 	struct datab *db;
74267c478bd9Sstevel@tonic-gate 	struct iocblk *iocp;
74277c478bd9Sstevel@tonic-gate 	cred_t	*cr;
7428ff550d0eSmasputra 	conn_t	*connp = Q_TO_CONN(q);
7429ff550d0eSmasputra 	udp_t	*udp = connp->conn_udp;
7430f4b3ec61Sdh 	udp_stack_t *us;
74317c478bd9Sstevel@tonic-gate 
74327c478bd9Sstevel@tonic-gate 	TRACE_1(TR_FAC_UDP, TR_UDP_WPUT_OTHER_START,
7433437220cdSdanmcd 	    "udp_wput_other_start: q %p", q);
74347c478bd9Sstevel@tonic-gate 
7435f4b3ec61Sdh 	us = udp->udp_us;
74367c478bd9Sstevel@tonic-gate 	db = mp->b_datap;
74377c478bd9Sstevel@tonic-gate 
7438ff550d0eSmasputra 	cr = DB_CREDDEF(mp, connp->conn_cred);
74397c478bd9Sstevel@tonic-gate 
74407c478bd9Sstevel@tonic-gate 	switch (db->db_type) {
7441ca9327a6Smeem 	case M_CMD:
7442ca9327a6Smeem 		udp_wput_cmdblk(q, mp);
7443ca9327a6Smeem 		return;
7444ca9327a6Smeem 
74457c478bd9Sstevel@tonic-gate 	case M_PROTO:
74467c478bd9Sstevel@tonic-gate 	case M_PCPROTO:
74477c478bd9Sstevel@tonic-gate 		if (mp->b_wptr - rptr < sizeof (t_scalar_t)) {
74487c478bd9Sstevel@tonic-gate 			freemsg(mp);
74497c478bd9Sstevel@tonic-gate 			TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END,
7450437220cdSdanmcd 			    "udp_wput_other_end: q %p (%S)", q, "protoshort");
74517c478bd9Sstevel@tonic-gate 			return;
74527c478bd9Sstevel@tonic-gate 		}
7453ff550d0eSmasputra 		switch (((t_primp_t)rptr)->type) {
74547c478bd9Sstevel@tonic-gate 		case T_ADDR_REQ:
74557c478bd9Sstevel@tonic-gate 			udp_addr_req(q, mp);
74567c478bd9Sstevel@tonic-gate 			TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END,
7457437220cdSdanmcd 			    "udp_wput_other_end: q %p (%S)", q, "addrreq");
74587c478bd9Sstevel@tonic-gate 			return;
74597c478bd9Sstevel@tonic-gate 		case O_T_BIND_REQ:
74607c478bd9Sstevel@tonic-gate 		case T_BIND_REQ:
74617c478bd9Sstevel@tonic-gate 			udp_bind(q, mp);
74627c478bd9Sstevel@tonic-gate 			TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END,
7463437220cdSdanmcd 			    "udp_wput_other_end: q %p (%S)", q, "bindreq");
74647c478bd9Sstevel@tonic-gate 			return;
74657c478bd9Sstevel@tonic-gate 		case T_CONN_REQ:
74667c478bd9Sstevel@tonic-gate 			udp_connect(q, mp);
74677c478bd9Sstevel@tonic-gate 			TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END,
7468437220cdSdanmcd 			    "udp_wput_other_end: q %p (%S)", q, "connreq");
74697c478bd9Sstevel@tonic-gate 			return;
74707c478bd9Sstevel@tonic-gate 		case T_CAPABILITY_REQ:
74717c478bd9Sstevel@tonic-gate 			udp_capability_req(q, mp);
74727c478bd9Sstevel@tonic-gate 			TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END,
7473437220cdSdanmcd 			    "udp_wput_other_end: q %p (%S)", q, "capabreq");
74747c478bd9Sstevel@tonic-gate 			return;
74757c478bd9Sstevel@tonic-gate 		case T_INFO_REQ:
74767c478bd9Sstevel@tonic-gate 			udp_info_req(q, mp);
74777c478bd9Sstevel@tonic-gate 			TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END,
7478437220cdSdanmcd 			    "udp_wput_other_end: q %p (%S)", q, "inforeq");
74797c478bd9Sstevel@tonic-gate 			return;
74807c478bd9Sstevel@tonic-gate 		case T_UNITDATA_REQ:
74817c478bd9Sstevel@tonic-gate 			/*
74827c478bd9Sstevel@tonic-gate 			 * If a T_UNITDATA_REQ gets here, the address must
74837c478bd9Sstevel@tonic-gate 			 * be bad.  Valid T_UNITDATA_REQs are handled
74847c478bd9Sstevel@tonic-gate 			 * in udp_wput.
74857c478bd9Sstevel@tonic-gate 			 */
7486ff550d0eSmasputra 			udp_ud_err(q, mp, NULL, 0, EADDRNOTAVAIL);
74877c478bd9Sstevel@tonic-gate 			TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END,
7488437220cdSdanmcd 			    "udp_wput_other_end: q %p (%S)", q, "unitdatareq");
74897c478bd9Sstevel@tonic-gate 			return;
74907c478bd9Sstevel@tonic-gate 		case T_UNBIND_REQ:
74917c478bd9Sstevel@tonic-gate 			udp_unbind(q, mp);
74927c478bd9Sstevel@tonic-gate 			TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END,
74937c478bd9Sstevel@tonic-gate 			    "udp_wput_other_end: q %p (%S)", q, "unbindreq");
74947c478bd9Sstevel@tonic-gate 			return;
74957c478bd9Sstevel@tonic-gate 		case T_SVR4_OPTMGMT_REQ:
7496fc80c0dfSnordmark 			if (!snmpcom_req(q, mp, udp_snmp_set, ip_snmp_get,
7497fc80c0dfSnordmark 			    cr)) {
7498fc80c0dfSnordmark 				(void) svr4_optcom_req(q,
7499fc80c0dfSnordmark 				    mp, cr, &udp_opt_obj, B_TRUE);
7500fc80c0dfSnordmark 			}
75017c478bd9Sstevel@tonic-gate 			TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END,
7502437220cdSdanmcd 			    "udp_wput_other_end: q %p (%S)", q, "optmgmtreq");
75037c478bd9Sstevel@tonic-gate 			return;
75047c478bd9Sstevel@tonic-gate 
75057c478bd9Sstevel@tonic-gate 		case T_OPTMGMT_REQ:
7506fc80c0dfSnordmark 			(void) tpi_optcom_req(q, mp, cr, &udp_opt_obj, B_TRUE);
75077c478bd9Sstevel@tonic-gate 			TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END,
7508437220cdSdanmcd 			    "udp_wput_other_end: q %p (%S)", q, "optmgmtreq");
75097c478bd9Sstevel@tonic-gate 			return;
75107c478bd9Sstevel@tonic-gate 
75117c478bd9Sstevel@tonic-gate 		case T_DISCON_REQ:
75127c478bd9Sstevel@tonic-gate 			udp_disconnect(q, mp);
75137c478bd9Sstevel@tonic-gate 			TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END,
7514437220cdSdanmcd 			    "udp_wput_other_end: q %p (%S)", q, "disconreq");
75157c478bd9Sstevel@tonic-gate 			return;
75167c478bd9Sstevel@tonic-gate 
75177c478bd9Sstevel@tonic-gate 		/* The following TPI message is not supported by udp. */
75187c478bd9Sstevel@tonic-gate 		case O_T_CONN_RES:
75197c478bd9Sstevel@tonic-gate 		case T_CONN_RES:
75207c478bd9Sstevel@tonic-gate 			udp_err_ack(q, mp, TNOTSUPPORT, 0);
75217c478bd9Sstevel@tonic-gate 			TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END,
7522437220cdSdanmcd 			    "udp_wput_other_end: q %p (%S)", q,
7523437220cdSdanmcd 			    "connres/disconreq");
75247c478bd9Sstevel@tonic-gate 			return;
75257c478bd9Sstevel@tonic-gate 
75267c478bd9Sstevel@tonic-gate 		/* The following 3 TPI messages are illegal for udp. */
75277c478bd9Sstevel@tonic-gate 		case T_DATA_REQ:
75287c478bd9Sstevel@tonic-gate 		case T_EXDATA_REQ:
75297c478bd9Sstevel@tonic-gate 		case T_ORDREL_REQ:
75307c478bd9Sstevel@tonic-gate 			udp_err_ack(q, mp, TNOTSUPPORT, 0);
75317c478bd9Sstevel@tonic-gate 			TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END,
7532437220cdSdanmcd 			    "udp_wput_other_end: q %p (%S)", q,
7533437220cdSdanmcd 			    "data/exdata/ordrel");
75347c478bd9Sstevel@tonic-gate 			return;
75357c478bd9Sstevel@tonic-gate 		default:
75367c478bd9Sstevel@tonic-gate 			break;
75377c478bd9Sstevel@tonic-gate 		}
75387c478bd9Sstevel@tonic-gate 		break;
75397c478bd9Sstevel@tonic-gate 	case M_FLUSH:
75407c478bd9Sstevel@tonic-gate 		if (*rptr & FLUSHW)
75417c478bd9Sstevel@tonic-gate 			flushq(q, FLUSHDATA);
75427c478bd9Sstevel@tonic-gate 		break;
75437c478bd9Sstevel@tonic-gate 	case M_IOCTL:
75447c478bd9Sstevel@tonic-gate 		iocp = (struct iocblk *)mp->b_rptr;
75457c478bd9Sstevel@tonic-gate 		switch (iocp->ioc_cmd) {
75467c478bd9Sstevel@tonic-gate 		case TI_GETPEERNAME:
75477c478bd9Sstevel@tonic-gate 			if (udp->udp_state != TS_DATA_XFER) {
75487c478bd9Sstevel@tonic-gate 				/*
75497c478bd9Sstevel@tonic-gate 				 * If a default destination address has not
75507c478bd9Sstevel@tonic-gate 				 * been associated with the stream, then we
75517c478bd9Sstevel@tonic-gate 				 * don't know the peer's name.
75527c478bd9Sstevel@tonic-gate 				 */
75537c478bd9Sstevel@tonic-gate 				iocp->ioc_error = ENOTCONN;
75547c478bd9Sstevel@tonic-gate 				iocp->ioc_count = 0;
75557c478bd9Sstevel@tonic-gate 				mp->b_datap->db_type = M_IOCACK;
7556fc80c0dfSnordmark 				qreply(q, mp);
75577c478bd9Sstevel@tonic-gate 				TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END,
7558437220cdSdanmcd 				    "udp_wput_other_end: q %p (%S)", q,
7559437220cdSdanmcd 				    "getpeername");
75607c478bd9Sstevel@tonic-gate 				return;
75617c478bd9Sstevel@tonic-gate 			}
75627c478bd9Sstevel@tonic-gate 			/* FALLTHRU */
75637c478bd9Sstevel@tonic-gate 		case TI_GETMYNAME: {
75647c478bd9Sstevel@tonic-gate 			/*
75657c478bd9Sstevel@tonic-gate 			 * For TI_GETPEERNAME and TI_GETMYNAME, we first
75667c478bd9Sstevel@tonic-gate 			 * need to copyin the user's strbuf structure.
75677c478bd9Sstevel@tonic-gate 			 * Processing will continue in the M_IOCDATA case
75687c478bd9Sstevel@tonic-gate 			 * below.
75697c478bd9Sstevel@tonic-gate 			 */
75707c478bd9Sstevel@tonic-gate 			mi_copyin(q, mp, NULL,
75717c478bd9Sstevel@tonic-gate 			    SIZEOF_STRUCT(strbuf, iocp->ioc_flag));
75727c478bd9Sstevel@tonic-gate 			TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END,
7573437220cdSdanmcd 			    "udp_wput_other_end: q %p (%S)", q, "getmyname");
75747c478bd9Sstevel@tonic-gate 			return;
75757c478bd9Sstevel@tonic-gate 			}
75767c478bd9Sstevel@tonic-gate 		case ND_SET:
75777c478bd9Sstevel@tonic-gate 			/* nd_getset performs the necessary checking */
75787c478bd9Sstevel@tonic-gate 		case ND_GET:
7579f4b3ec61Sdh 			if (nd_getset(q, us->us_nd, mp)) {
7580fc80c0dfSnordmark 				qreply(q, mp);
75817c478bd9Sstevel@tonic-gate 				TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END,
7582437220cdSdanmcd 				    "udp_wput_other_end: q %p (%S)", q, "get");
75837c478bd9Sstevel@tonic-gate 				return;
75847c478bd9Sstevel@tonic-gate 			}
75857c478bd9Sstevel@tonic-gate 			break;
7586ff550d0eSmasputra 		case _SIOCSOCKFALLBACK:
7587ff550d0eSmasputra 			/*
7588ff550d0eSmasputra 			 * Either sockmod is about to be popped and the
7589ff550d0eSmasputra 			 * socket would now be treated as a plain stream,
7590ff550d0eSmasputra 			 * or a module is about to be pushed so we could
7591ff550d0eSmasputra 			 * no longer use read-side synchronous stream.
7592ff550d0eSmasputra 			 * Drain any queued data and disable direct sockfs
7593ff550d0eSmasputra 			 * interface from now on.
7594ff550d0eSmasputra 			 */
7595ff550d0eSmasputra 			if (!udp->udp_issocket) {
7596ff550d0eSmasputra 				DB_TYPE(mp) = M_IOCNAK;
7597ff550d0eSmasputra 				iocp->ioc_error = EINVAL;
7598ff550d0eSmasputra 			} else {
7599ff550d0eSmasputra 				udp->udp_issocket = B_FALSE;
7600ff550d0eSmasputra 				if (udp->udp_direct_sockfs) {
7601ff550d0eSmasputra 					/*
7602ff550d0eSmasputra 					 * Disable read-side synchronous
7603ff550d0eSmasputra 					 * stream interface and drain any
7604ff550d0eSmasputra 					 * queued data.
7605ff550d0eSmasputra 					 */
7606fc80c0dfSnordmark 					udp_rcv_drain(RD(q), udp,
7607ff550d0eSmasputra 					    B_FALSE);
7608ff550d0eSmasputra 					ASSERT(!udp->udp_direct_sockfs);
7609f4b3ec61Sdh 					UDP_STAT(us, udp_sock_fallback);
7610ff550d0eSmasputra 				}
7611ff550d0eSmasputra 				DB_TYPE(mp) = M_IOCACK;
7612ff550d0eSmasputra 				iocp->ioc_error = 0;
7613ff550d0eSmasputra 			}
7614ff550d0eSmasputra 			iocp->ioc_count = 0;
7615ff550d0eSmasputra 			iocp->ioc_rval = 0;
7616fc80c0dfSnordmark 			qreply(q, mp);
7617ff550d0eSmasputra 			return;
76187c478bd9Sstevel@tonic-gate 		default:
76197c478bd9Sstevel@tonic-gate 			break;
76207c478bd9Sstevel@tonic-gate 		}
76217c478bd9Sstevel@tonic-gate 		break;
76227c478bd9Sstevel@tonic-gate 	case M_IOCDATA:
76237c478bd9Sstevel@tonic-gate 		udp_wput_iocdata(q, mp);
76247c478bd9Sstevel@tonic-gate 		TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END,
7625437220cdSdanmcd 		    "udp_wput_other_end: q %p (%S)", q, "iocdata");
76267c478bd9Sstevel@tonic-gate 		return;
76277c478bd9Sstevel@tonic-gate 	default:
76287c478bd9Sstevel@tonic-gate 		/* Unrecognized messages are passed through without change. */
76297c478bd9Sstevel@tonic-gate 		break;
76307c478bd9Sstevel@tonic-gate 	}
76317c478bd9Sstevel@tonic-gate 	TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END,
7632437220cdSdanmcd 	    "udp_wput_other_end: q %p (%S)", q, "end");
7633ff550d0eSmasputra 	ip_output(connp, mp, q, IP_WPUT);
7634ff550d0eSmasputra }
7635ff550d0eSmasputra 
76367c478bd9Sstevel@tonic-gate /*
76377c478bd9Sstevel@tonic-gate  * udp_wput_iocdata is called by udp_wput_other to handle all M_IOCDATA
76387c478bd9Sstevel@tonic-gate  * messages.
76397c478bd9Sstevel@tonic-gate  */
76407c478bd9Sstevel@tonic-gate static void
76417c478bd9Sstevel@tonic-gate udp_wput_iocdata(queue_t *q, mblk_t *mp)
76427c478bd9Sstevel@tonic-gate {
76437c478bd9Sstevel@tonic-gate 	mblk_t	*mp1;
7644ca9327a6Smeem 	struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
76457c478bd9Sstevel@tonic-gate 	STRUCT_HANDLE(strbuf, sb);
7646ca9327a6Smeem 	udp_t	*udp = Q_TO_UDP(q);
7647ca9327a6Smeem 	int	error;
7648ca9327a6Smeem 	uint_t	addrlen;
76497c478bd9Sstevel@tonic-gate 
76507c478bd9Sstevel@tonic-gate 	/* Make sure it is one of ours. */
7651ca9327a6Smeem 	switch (iocp->ioc_cmd) {
76527c478bd9Sstevel@tonic-gate 	case TI_GETMYNAME:
76537c478bd9Sstevel@tonic-gate 	case TI_GETPEERNAME:
76547c478bd9Sstevel@tonic-gate 		break;
76557c478bd9Sstevel@tonic-gate 	default:
76565597b60aSnordmark 		ip_output(udp->udp_connp, mp, q, IP_WPUT);
76577c478bd9Sstevel@tonic-gate 		return;
76587c478bd9Sstevel@tonic-gate 	}
7659ff550d0eSmasputra 
76607c478bd9Sstevel@tonic-gate 	switch (mi_copy_state(q, mp, &mp1)) {
76617c478bd9Sstevel@tonic-gate 	case -1:
76627c478bd9Sstevel@tonic-gate 		return;
76637c478bd9Sstevel@tonic-gate 	case MI_COPY_CASE(MI_COPY_IN, 1):
76647c478bd9Sstevel@tonic-gate 		break;
76657c478bd9Sstevel@tonic-gate 	case MI_COPY_CASE(MI_COPY_OUT, 1):
76667c478bd9Sstevel@tonic-gate 		/*
76677c478bd9Sstevel@tonic-gate 		 * The address has been copied out, so now
76687c478bd9Sstevel@tonic-gate 		 * copyout the strbuf.
76697c478bd9Sstevel@tonic-gate 		 */
76707c478bd9Sstevel@tonic-gate 		mi_copyout(q, mp);
76717c478bd9Sstevel@tonic-gate 		return;
76727c478bd9Sstevel@tonic-gate 	case MI_COPY_CASE(MI_COPY_OUT, 2):
76737c478bd9Sstevel@tonic-gate 		/*
76747c478bd9Sstevel@tonic-gate 		 * The address and strbuf have been copied out.
76757c478bd9Sstevel@tonic-gate 		 * We're done, so just acknowledge the original
76767c478bd9Sstevel@tonic-gate 		 * M_IOCTL.
76777c478bd9Sstevel@tonic-gate 		 */
76787c478bd9Sstevel@tonic-gate 		mi_copy_done(q, mp, 0);
76797c478bd9Sstevel@tonic-gate 		return;
76807c478bd9Sstevel@tonic-gate 	default:
76817c478bd9Sstevel@tonic-gate 		/*
76827c478bd9Sstevel@tonic-gate 		 * Something strange has happened, so acknowledge
76837c478bd9Sstevel@tonic-gate 		 * the original M_IOCTL with an EPROTO error.
76847c478bd9Sstevel@tonic-gate 		 */
76857c478bd9Sstevel@tonic-gate 		mi_copy_done(q, mp, EPROTO);
76867c478bd9Sstevel@tonic-gate 		return;
76877c478bd9Sstevel@tonic-gate 	}
76887c478bd9Sstevel@tonic-gate 
76897c478bd9Sstevel@tonic-gate 	/*
76907c478bd9Sstevel@tonic-gate 	 * Now we have the strbuf structure for TI_GETMYNAME
76917c478bd9Sstevel@tonic-gate 	 * and TI_GETPEERNAME.  Next we copyout the requested
76927c478bd9Sstevel@tonic-gate 	 * address and then we'll copyout the strbuf.
76937c478bd9Sstevel@tonic-gate 	 */
7694ca9327a6Smeem 	STRUCT_SET_HANDLE(sb, iocp->ioc_flag, (void *)mp1->b_rptr);
7695ca9327a6Smeem 	addrlen = udp->udp_family == AF_INET ? sizeof (sin_t) : sizeof (sin6_t);
76967c478bd9Sstevel@tonic-gate 	if (STRUCT_FGET(sb, maxlen) < addrlen) {
76977c478bd9Sstevel@tonic-gate 		mi_copy_done(q, mp, EINVAL);
76987c478bd9Sstevel@tonic-gate 		return;
76997c478bd9Sstevel@tonic-gate 	}
7700ca9327a6Smeem 
7701ca9327a6Smeem 	mp1 = mi_copyout_alloc(q, mp, STRUCT_FGETP(sb, buf), addrlen, B_TRUE);
7702ca9327a6Smeem 	if (mp1 == NULL)
7703ca9327a6Smeem 		return;
7704ca9327a6Smeem 
7705ca9327a6Smeem 	rw_enter(&udp->udp_rwlock, RW_READER);
7706ca9327a6Smeem 	switch (iocp->ioc_cmd) {
77077c478bd9Sstevel@tonic-gate 	case TI_GETMYNAME:
7708ca9327a6Smeem 		error = udp_getmyname(udp, (void *)mp1->b_rptr, &addrlen);
77097c478bd9Sstevel@tonic-gate 		break;
77107c478bd9Sstevel@tonic-gate 	case TI_GETPEERNAME:
7711ca9327a6Smeem 		error = udp_getpeername(udp, (void *)mp1->b_rptr, &addrlen);
77127c478bd9Sstevel@tonic-gate 		break;
77137c478bd9Sstevel@tonic-gate 	}
7714ca9327a6Smeem 	rw_exit(&udp->udp_rwlock);
77157c478bd9Sstevel@tonic-gate 
7716ca9327a6Smeem 	if (error != 0) {
7717ca9327a6Smeem 		mi_copy_done(q, mp, error);
77187c478bd9Sstevel@tonic-gate 	} else {
7719ca9327a6Smeem 		mp1->b_wptr += addrlen;
7720ca9327a6Smeem 		STRUCT_FSET(sb, len, addrlen);
77217c478bd9Sstevel@tonic-gate 
7722ca9327a6Smeem 		/* Copy out the address */
7723ca9327a6Smeem 		mi_copyout(q, mp);
77247c478bd9Sstevel@tonic-gate 	}
77257c478bd9Sstevel@tonic-gate }
77267c478bd9Sstevel@tonic-gate 
77277c478bd9Sstevel@tonic-gate static int
77287c478bd9Sstevel@tonic-gate udp_unitdata_opt_process(queue_t *q, mblk_t *mp, int *errorp,
772945916cd2Sjpk     udpattrs_t *udpattrs)
77307c478bd9Sstevel@tonic-gate {
77317c478bd9Sstevel@tonic-gate 	struct T_unitdata_req *udreqp;
77327c478bd9Sstevel@tonic-gate 	int is_absreq_failure;
77337c478bd9Sstevel@tonic-gate 	cred_t *cr;
7734ff550d0eSmasputra 	conn_t	*connp = Q_TO_CONN(q);
77357c478bd9Sstevel@tonic-gate 
7736ff550d0eSmasputra 	ASSERT(((t_primp_t)mp->b_rptr)->type);
77377c478bd9Sstevel@tonic-gate 
7738ff550d0eSmasputra 	cr = DB_CREDDEF(mp, connp->conn_cred);
77397c478bd9Sstevel@tonic-gate 
77407c478bd9Sstevel@tonic-gate 	udreqp = (struct T_unitdata_req *)mp->b_rptr;
77417c478bd9Sstevel@tonic-gate 
7742fc80c0dfSnordmark 	*errorp = tpi_optcom_buf(q, mp, &udreqp->OPT_length,
77437c478bd9Sstevel@tonic-gate 	    udreqp->OPT_offset, cr, &udp_opt_obj,
774445916cd2Sjpk 	    udpattrs, &is_absreq_failure);
77457c478bd9Sstevel@tonic-gate 
77467c478bd9Sstevel@tonic-gate 	if (*errorp != 0) {
77477c478bd9Sstevel@tonic-gate 		/*
77487c478bd9Sstevel@tonic-gate 		 * Note: No special action needed in this
77497c478bd9Sstevel@tonic-gate 		 * module for "is_absreq_failure"
77507c478bd9Sstevel@tonic-gate 		 */
77517c478bd9Sstevel@tonic-gate 		return (-1);		/* failure */
77527c478bd9Sstevel@tonic-gate 	}
77537c478bd9Sstevel@tonic-gate 	ASSERT(is_absreq_failure == 0);
77547c478bd9Sstevel@tonic-gate 	return (0);	/* success */
77557c478bd9Sstevel@tonic-gate }
77567c478bd9Sstevel@tonic-gate 
77577c478bd9Sstevel@tonic-gate void
77587c478bd9Sstevel@tonic-gate udp_ddi_init(void)
77597c478bd9Sstevel@tonic-gate {
77607c478bd9Sstevel@tonic-gate 	udp_max_optsize = optcom_max_optsize(udp_opt_obj.odb_opt_des_arr,
77617c478bd9Sstevel@tonic-gate 	    udp_opt_obj.odb_opt_arr_cnt);
77627c478bd9Sstevel@tonic-gate 
7763f4b3ec61Sdh 	/*
7764f4b3ec61Sdh 	 * We want to be informed each time a stack is created or
7765f4b3ec61Sdh 	 * destroyed in the kernel, so we can maintain the
7766f4b3ec61Sdh 	 * set of udp_stack_t's.
7767f4b3ec61Sdh 	 */
7768f4b3ec61Sdh 	netstack_register(NS_UDP, udp_stack_init, NULL, udp_stack_fini);
7769f4b3ec61Sdh }
7770f4b3ec61Sdh 
7771f4b3ec61Sdh void
7772f4b3ec61Sdh udp_ddi_destroy(void)
7773f4b3ec61Sdh {
7774f4b3ec61Sdh 	netstack_unregister(NS_UDP);
7775f4b3ec61Sdh }
7776f4b3ec61Sdh 
7777f4b3ec61Sdh /*
7778f4b3ec61Sdh  * Initialize the UDP stack instance.
7779f4b3ec61Sdh  */
7780f4b3ec61Sdh static void *
7781f4b3ec61Sdh udp_stack_init(netstackid_t stackid, netstack_t *ns)
7782f4b3ec61Sdh {
7783f4b3ec61Sdh 	udp_stack_t	*us;
7784f4b3ec61Sdh 	udpparam_t	*pa;
7785f4b3ec61Sdh 	int		i;
7786f4b3ec61Sdh 
7787f4b3ec61Sdh 	us = (udp_stack_t *)kmem_zalloc(sizeof (*us), KM_SLEEP);
7788f4b3ec61Sdh 	us->us_netstack = ns;
7789f4b3ec61Sdh 
7790f4b3ec61Sdh 	us->us_num_epriv_ports = UDP_NUM_EPRIV_PORTS;
7791f4b3ec61Sdh 	us->us_epriv_ports[0] = 2049;
7792f4b3ec61Sdh 	us->us_epriv_ports[1] = 4045;
7793f4b3ec61Sdh 
7794f4b3ec61Sdh 	/*
7795f4b3ec61Sdh 	 * The smallest anonymous port in the priviledged port range which UDP
7796f4b3ec61Sdh 	 * looks for free port.  Use in the option UDP_ANONPRIVBIND.
7797f4b3ec61Sdh 	 */
7798f4b3ec61Sdh 	us->us_min_anonpriv_port = 512;
7799f4b3ec61Sdh 
7800f4b3ec61Sdh 	us->us_bind_fanout_size = udp_bind_fanout_size;
7801f4b3ec61Sdh 
7802f4b3ec61Sdh 	/* Roundup variable that might have been modified in /etc/system */
7803f4b3ec61Sdh 	if (us->us_bind_fanout_size & (us->us_bind_fanout_size - 1)) {
78047c478bd9Sstevel@tonic-gate 		/* Not a power of two. Round up to nearest power of two */
78057c478bd9Sstevel@tonic-gate 		for (i = 0; i < 31; i++) {
7806f4b3ec61Sdh 			if (us->us_bind_fanout_size < (1 << i))
78077c478bd9Sstevel@tonic-gate 				break;
78087c478bd9Sstevel@tonic-gate 		}
7809f4b3ec61Sdh 		us->us_bind_fanout_size = 1 << i;
78107c478bd9Sstevel@tonic-gate 	}
7811f4b3ec61Sdh 	us->us_bind_fanout = kmem_zalloc(us->us_bind_fanout_size *
78127c478bd9Sstevel@tonic-gate 	    sizeof (udp_fanout_t), KM_SLEEP);
7813f4b3ec61Sdh 	for (i = 0; i < us->us_bind_fanout_size; i++) {
7814f4b3ec61Sdh 		mutex_init(&us->us_bind_fanout[i].uf_lock, NULL, MUTEX_DEFAULT,
78157c478bd9Sstevel@tonic-gate 		    NULL);
78167c478bd9Sstevel@tonic-gate 	}
7817ff550d0eSmasputra 
7818f4b3ec61Sdh 	pa = (udpparam_t *)kmem_alloc(sizeof (udp_param_arr), KM_SLEEP);
7819ff550d0eSmasputra 
7820f4b3ec61Sdh 	us->us_param_arr = pa;
7821f4b3ec61Sdh 	bcopy(udp_param_arr, us->us_param_arr, sizeof (udp_param_arr));
7822f4b3ec61Sdh 
7823f4b3ec61Sdh 	(void) udp_param_register(&us->us_nd,
7824f4b3ec61Sdh 	    us->us_param_arr, A_CNT(udp_param_arr));
7825f4b3ec61Sdh 
7826f4b3ec61Sdh 	us->us_kstat = udp_kstat2_init(stackid, &us->us_statistics);
7827f4b3ec61Sdh 	us->us_mibkp = udp_kstat_init(stackid);
7828f4b3ec61Sdh 	return (us);
78297c478bd9Sstevel@tonic-gate }
78307c478bd9Sstevel@tonic-gate 
7831f4b3ec61Sdh /*
7832f4b3ec61Sdh  * Free the UDP stack instance.
7833f4b3ec61Sdh  */
7834f4b3ec61Sdh static void
7835f4b3ec61Sdh udp_stack_fini(netstackid_t stackid, void *arg)
78367c478bd9Sstevel@tonic-gate {
7837f4b3ec61Sdh 	udp_stack_t *us = (udp_stack_t *)arg;
78387c478bd9Sstevel@tonic-gate 	int i;
78397c478bd9Sstevel@tonic-gate 
7840f4b3ec61Sdh 	for (i = 0; i < us->us_bind_fanout_size; i++) {
7841f4b3ec61Sdh 		mutex_destroy(&us->us_bind_fanout[i].uf_lock);
78427c478bd9Sstevel@tonic-gate 	}
7843ff550d0eSmasputra 
7844f4b3ec61Sdh 	kmem_free(us->us_bind_fanout, us->us_bind_fanout_size *
78457c478bd9Sstevel@tonic-gate 	    sizeof (udp_fanout_t));
7846ff550d0eSmasputra 
7847f4b3ec61Sdh 	us->us_bind_fanout = NULL;
78487c478bd9Sstevel@tonic-gate 
7849f4b3ec61Sdh 	nd_free(&us->us_nd);
7850f4b3ec61Sdh 	kmem_free(us->us_param_arr, sizeof (udp_param_arr));
7851f4b3ec61Sdh 	us->us_param_arr = NULL;
7852f4b3ec61Sdh 
7853f4b3ec61Sdh 	udp_kstat_fini(stackid, us->us_mibkp);
7854f4b3ec61Sdh 	us->us_mibkp = NULL;
7855f4b3ec61Sdh 
7856f4b3ec61Sdh 	udp_kstat2_fini(stackid, us->us_kstat);
7857f4b3ec61Sdh 	us->us_kstat = NULL;
7858f4b3ec61Sdh 	bzero(&us->us_statistics, sizeof (us->us_statistics));
7859f4b3ec61Sdh 	kmem_free(us, sizeof (*us));
7860f4b3ec61Sdh }
7861f4b3ec61Sdh 
7862f4b3ec61Sdh static void *
7863f4b3ec61Sdh udp_kstat2_init(netstackid_t stackid, udp_stat_t *us_statisticsp)
7864f4b3ec61Sdh {
7865f4b3ec61Sdh 	kstat_t *ksp;
7866f4b3ec61Sdh 
7867f4b3ec61Sdh 	udp_stat_t template = {
7868f4b3ec61Sdh 		{ "udp_ip_send",		KSTAT_DATA_UINT64 },
7869f4b3ec61Sdh 		{ "udp_ip_ire_send",		KSTAT_DATA_UINT64 },
7870f4b3ec61Sdh 		{ "udp_ire_null",		KSTAT_DATA_UINT64 },
7871f4b3ec61Sdh 		{ "udp_drain",			KSTAT_DATA_UINT64 },
7872f4b3ec61Sdh 		{ "udp_sock_fallback",		KSTAT_DATA_UINT64 },
7873f4b3ec61Sdh 		{ "udp_rrw_busy",		KSTAT_DATA_UINT64 },
7874f4b3ec61Sdh 		{ "udp_rrw_msgcnt",		KSTAT_DATA_UINT64 },
7875f4b3ec61Sdh 		{ "udp_out_sw_cksum",		KSTAT_DATA_UINT64 },
7876f4b3ec61Sdh 		{ "udp_out_sw_cksum_bytes",	KSTAT_DATA_UINT64 },
7877f4b3ec61Sdh 		{ "udp_out_opt",		KSTAT_DATA_UINT64 },
7878f4b3ec61Sdh 		{ "udp_out_err_notconn",	KSTAT_DATA_UINT64 },
7879f4b3ec61Sdh 		{ "udp_out_err_output",		KSTAT_DATA_UINT64 },
7880f4b3ec61Sdh 		{ "udp_out_err_tudr",		KSTAT_DATA_UINT64 },
7881f4b3ec61Sdh 		{ "udp_in_pktinfo",		KSTAT_DATA_UINT64 },
7882f4b3ec61Sdh 		{ "udp_in_recvdstaddr",		KSTAT_DATA_UINT64 },
7883f4b3ec61Sdh 		{ "udp_in_recvopts",		KSTAT_DATA_UINT64 },
7884f4b3ec61Sdh 		{ "udp_in_recvif",		KSTAT_DATA_UINT64 },
7885f4b3ec61Sdh 		{ "udp_in_recvslla",		KSTAT_DATA_UINT64 },
7886f4b3ec61Sdh 		{ "udp_in_recvucred",		KSTAT_DATA_UINT64 },
7887f4b3ec61Sdh 		{ "udp_in_recvttl",		KSTAT_DATA_UINT64 },
7888f4b3ec61Sdh 		{ "udp_in_recvhopopts",		KSTAT_DATA_UINT64 },
7889f4b3ec61Sdh 		{ "udp_in_recvhoplimit",	KSTAT_DATA_UINT64 },
7890f4b3ec61Sdh 		{ "udp_in_recvdstopts",		KSTAT_DATA_UINT64 },
7891f4b3ec61Sdh 		{ "udp_in_recvrtdstopts",	KSTAT_DATA_UINT64 },
7892f4b3ec61Sdh 		{ "udp_in_recvrthdr",		KSTAT_DATA_UINT64 },
7893f4b3ec61Sdh 		{ "udp_in_recvpktinfo",		KSTAT_DATA_UINT64 },
7894f4b3ec61Sdh 		{ "udp_in_recvtclass",		KSTAT_DATA_UINT64 },
7895f4b3ec61Sdh 		{ "udp_in_timestamp",		KSTAT_DATA_UINT64 },
7896f4b3ec61Sdh #ifdef DEBUG
7897f4b3ec61Sdh 		{ "udp_data_conn",		KSTAT_DATA_UINT64 },
7898f4b3ec61Sdh 		{ "udp_data_notconn",		KSTAT_DATA_UINT64 },
7899f4b3ec61Sdh #endif
7900f4b3ec61Sdh 	};
7901f4b3ec61Sdh 
7902f4b3ec61Sdh 	ksp = kstat_create_netstack(UDP_MOD_NAME, 0, "udpstat", "net",
7903f4b3ec61Sdh 	    KSTAT_TYPE_NAMED, sizeof (template) / sizeof (kstat_named_t),
7904f4b3ec61Sdh 	    KSTAT_FLAG_VIRTUAL, stackid);
7905f4b3ec61Sdh 
7906f4b3ec61Sdh 	if (ksp == NULL)
7907f4b3ec61Sdh 		return (NULL);
7908f4b3ec61Sdh 
7909f4b3ec61Sdh 	bcopy(&template, us_statisticsp, sizeof (template));
7910f4b3ec61Sdh 	ksp->ks_data = (void *)us_statisticsp;
7911f4b3ec61Sdh 	ksp->ks_private = (void *)(uintptr_t)stackid;
7912f4b3ec61Sdh 
7913f4b3ec61Sdh 	kstat_install(ksp);
7914f4b3ec61Sdh 	return (ksp);
79157c478bd9Sstevel@tonic-gate }
79167c478bd9Sstevel@tonic-gate 
79177c478bd9Sstevel@tonic-gate static void
7918f4b3ec61Sdh udp_kstat2_fini(netstackid_t stackid, kstat_t *ksp)
79197c478bd9Sstevel@tonic-gate {
7920f4b3ec61Sdh 	if (ksp != NULL) {
7921f4b3ec61Sdh 		ASSERT(stackid == (netstackid_t)(uintptr_t)ksp->ks_private);
7922f4b3ec61Sdh 		kstat_delete_netstack(ksp, stackid);
7923f4b3ec61Sdh 	}
7924f4b3ec61Sdh }
7925f4b3ec61Sdh 
7926f4b3ec61Sdh static void *
7927f4b3ec61Sdh udp_kstat_init(netstackid_t stackid)
7928f4b3ec61Sdh {
7929f4b3ec61Sdh 	kstat_t	*ksp;
7930f4b3ec61Sdh 
79317c478bd9Sstevel@tonic-gate 	udp_named_kstat_t template = {
79323173664eSapersson 		{ "inDatagrams",	KSTAT_DATA_UINT64, 0 },
79337c478bd9Sstevel@tonic-gate 		{ "inErrors",		KSTAT_DATA_UINT32, 0 },
79343173664eSapersson 		{ "outDatagrams",	KSTAT_DATA_UINT64, 0 },
79357c478bd9Sstevel@tonic-gate 		{ "entrySize",		KSTAT_DATA_INT32, 0 },
79367c478bd9Sstevel@tonic-gate 		{ "entry6Size",		KSTAT_DATA_INT32, 0 },
79377c478bd9Sstevel@tonic-gate 		{ "outErrors",		KSTAT_DATA_UINT32, 0 },
79387c478bd9Sstevel@tonic-gate 	};
79397c478bd9Sstevel@tonic-gate 
7940f4b3ec61Sdh 	ksp = kstat_create_netstack(UDP_MOD_NAME, 0, UDP_MOD_NAME, "mib2",
7941f4b3ec61Sdh 	    KSTAT_TYPE_NAMED,
7942f4b3ec61Sdh 	    NUM_OF_FIELDS(udp_named_kstat_t), 0, stackid);
7943ff550d0eSmasputra 
7944f4b3ec61Sdh 	if (ksp == NULL || ksp->ks_data == NULL)
7945f4b3ec61Sdh 		return (NULL);
79467c478bd9Sstevel@tonic-gate 
79477c478bd9Sstevel@tonic-gate 	template.entrySize.value.ui32 = sizeof (mib2_udpEntry_t);
79487c478bd9Sstevel@tonic-gate 	template.entry6Size.value.ui32 = sizeof (mib2_udp6Entry_t);
79497c478bd9Sstevel@tonic-gate 
7950f4b3ec61Sdh 	bcopy(&template, ksp->ks_data, sizeof (template));
7951f4b3ec61Sdh 	ksp->ks_update = udp_kstat_update;
7952f4b3ec61Sdh 	ksp->ks_private = (void *)(uintptr_t)stackid;
79537c478bd9Sstevel@tonic-gate 
7954f4b3ec61Sdh 	kstat_install(ksp);
7955f4b3ec61Sdh 	return (ksp);
79567c478bd9Sstevel@tonic-gate }
79577c478bd9Sstevel@tonic-gate 
79587c478bd9Sstevel@tonic-gate static void
7959f4b3ec61Sdh udp_kstat_fini(netstackid_t stackid, kstat_t *ksp)
79607c478bd9Sstevel@tonic-gate {
7961f4b3ec61Sdh 	if (ksp != NULL) {
7962f4b3ec61Sdh 		ASSERT(stackid == (netstackid_t)(uintptr_t)ksp->ks_private);
7963f4b3ec61Sdh 		kstat_delete_netstack(ksp, stackid);
79647c478bd9Sstevel@tonic-gate 	}
79657c478bd9Sstevel@tonic-gate }
79667c478bd9Sstevel@tonic-gate 
79677c478bd9Sstevel@tonic-gate static int
79687c478bd9Sstevel@tonic-gate udp_kstat_update(kstat_t *kp, int rw)
79697c478bd9Sstevel@tonic-gate {
79707c478bd9Sstevel@tonic-gate 	udp_named_kstat_t *udpkp;
7971f4b3ec61Sdh 	netstackid_t	stackid = (netstackid_t)(uintptr_t)kp->ks_private;
7972f4b3ec61Sdh 	netstack_t	*ns;
7973f4b3ec61Sdh 	udp_stack_t	*us;
79747c478bd9Sstevel@tonic-gate 
79757c478bd9Sstevel@tonic-gate 	if ((kp == NULL) || (kp->ks_data == NULL))
79767c478bd9Sstevel@tonic-gate 		return (EIO);
79777c478bd9Sstevel@tonic-gate 
79787c478bd9Sstevel@tonic-gate 	if (rw == KSTAT_WRITE)
79797c478bd9Sstevel@tonic-gate 		return (EACCES);
79807c478bd9Sstevel@tonic-gate 
7981f4b3ec61Sdh 	ns = netstack_find_by_stackid(stackid);
7982f4b3ec61Sdh 	if (ns == NULL)
7983f4b3ec61Sdh 		return (-1);
7984f4b3ec61Sdh 	us = ns->netstack_udp;
7985f4b3ec61Sdh 	if (us == NULL) {
7986f4b3ec61Sdh 		netstack_rele(ns);
7987f4b3ec61Sdh 		return (-1);
7988f4b3ec61Sdh 	}
79897c478bd9Sstevel@tonic-gate 	udpkp = (udp_named_kstat_t *)kp->ks_data;
79907c478bd9Sstevel@tonic-gate 
799127c48ed9Snordmark 	udpkp->inDatagrams.value.ui64 =	us->us_udp_mib.udpHCInDatagrams;
7992f4b3ec61Sdh 	udpkp->inErrors.value.ui32 =	us->us_udp_mib.udpInErrors;
799327c48ed9Snordmark 	udpkp->outDatagrams.value.ui64 = us->us_udp_mib.udpHCOutDatagrams;
7994f4b3ec61Sdh 	udpkp->outErrors.value.ui32 =	us->us_udp_mib.udpOutErrors;
7995f4b3ec61Sdh 	netstack_rele(ns);
79967c478bd9Sstevel@tonic-gate 	return (0);
79977c478bd9Sstevel@tonic-gate }
79987c478bd9Sstevel@tonic-gate 
7999ff550d0eSmasputra /*
8000ff550d0eSmasputra  * Read-side synchronous stream info entry point, called as a
8001ff550d0eSmasputra  * result of handling certain STREAMS ioctl operations.
8002ff550d0eSmasputra  */
8003ff550d0eSmasputra static int
8004ff550d0eSmasputra udp_rinfop(queue_t *q, infod_t *dp)
8005ff550d0eSmasputra {
8006ff550d0eSmasputra 	mblk_t	*mp;
8007ff550d0eSmasputra 	uint_t	cmd = dp->d_cmd;
8008ff550d0eSmasputra 	int	res = 0;
8009ff550d0eSmasputra 	int	error = 0;
8010fc80c0dfSnordmark 	udp_t	*udp = Q_TO_UDP(q);
8011ff550d0eSmasputra 	struct stdata *stp = STREAM(q);
8012ff550d0eSmasputra 
8013ff550d0eSmasputra 	mutex_enter(&udp->udp_drain_lock);
8014ff550d0eSmasputra 	/* If shutdown on read has happened, return nothing */
8015ff550d0eSmasputra 	mutex_enter(&stp->sd_lock);
8016ff550d0eSmasputra 	if (stp->sd_flag & STREOF) {
8017ff550d0eSmasputra 		mutex_exit(&stp->sd_lock);
8018ff550d0eSmasputra 		goto done;
8019ff550d0eSmasputra 	}
8020ff550d0eSmasputra 	mutex_exit(&stp->sd_lock);
8021ff550d0eSmasputra 
8022ff550d0eSmasputra 	if ((mp = udp->udp_rcv_list_head) == NULL)
8023ff550d0eSmasputra 		goto done;
8024ff550d0eSmasputra 
8025ff550d0eSmasputra 	ASSERT(DB_TYPE(mp) != M_DATA && mp->b_cont != NULL);
8026ff550d0eSmasputra 
8027ff550d0eSmasputra 	if (cmd & INFOD_COUNT) {
8028ff550d0eSmasputra 		/*
8029ff550d0eSmasputra 		 * Return the number of messages.
8030ff550d0eSmasputra 		 */
8031ff550d0eSmasputra 		dp->d_count += udp->udp_rcv_msgcnt;
8032ff550d0eSmasputra 		res |= INFOD_COUNT;
8033ff550d0eSmasputra 	}
8034ff550d0eSmasputra 	if (cmd & INFOD_BYTES) {
8035ff550d0eSmasputra 		/*
8036ff550d0eSmasputra 		 * Return size of all data messages.
8037ff550d0eSmasputra 		 */
8038ff550d0eSmasputra 		dp->d_bytes += udp->udp_rcv_cnt;
8039ff550d0eSmasputra 		res |= INFOD_BYTES;
8040ff550d0eSmasputra 	}
8041ff550d0eSmasputra 	if (cmd & INFOD_FIRSTBYTES) {
8042ff550d0eSmasputra 		/*
8043ff550d0eSmasputra 		 * Return size of first data message.
8044ff550d0eSmasputra 		 */
8045ff550d0eSmasputra 		dp->d_bytes = msgdsize(mp);
8046ff550d0eSmasputra 		res |= INFOD_FIRSTBYTES;
8047ff550d0eSmasputra 		dp->d_cmd &= ~INFOD_FIRSTBYTES;
8048ff550d0eSmasputra 	}
8049ff550d0eSmasputra 	if (cmd & INFOD_COPYOUT) {
8050ff550d0eSmasputra 		mblk_t *mp1 = mp->b_cont;
8051ff550d0eSmasputra 		int n;
8052ff550d0eSmasputra 		/*
8053ff550d0eSmasputra 		 * Return data contents of first message.
8054ff550d0eSmasputra 		 */
8055ff550d0eSmasputra 		ASSERT(DB_TYPE(mp1) == M_DATA);
8056ff550d0eSmasputra 		while (mp1 != NULL && dp->d_uiop->uio_resid > 0) {
8057ff550d0eSmasputra 			n = MIN(dp->d_uiop->uio_resid, MBLKL(mp1));
8058ff550d0eSmasputra 			if (n != 0 && (error = uiomove((char *)mp1->b_rptr, n,
8059ff550d0eSmasputra 			    UIO_READ, dp->d_uiop)) != 0) {
8060ff550d0eSmasputra 				goto done;
8061ff550d0eSmasputra 			}
8062ff550d0eSmasputra 			mp1 = mp1->b_cont;
8063ff550d0eSmasputra 		}
8064ff550d0eSmasputra 		res |= INFOD_COPYOUT;
8065ff550d0eSmasputra 		dp->d_cmd &= ~INFOD_COPYOUT;
8066ff550d0eSmasputra 	}
8067ff550d0eSmasputra done:
8068ff550d0eSmasputra 	mutex_exit(&udp->udp_drain_lock);
8069ff550d0eSmasputra 
8070ff550d0eSmasputra 	dp->d_res |= res;
8071ff550d0eSmasputra 
8072ff550d0eSmasputra 	return (error);
8073ff550d0eSmasputra }
8074ff550d0eSmasputra 
8075ff550d0eSmasputra /*
8076ff550d0eSmasputra  * Read-side synchronous stream entry point.  This is called as a result
8077ff550d0eSmasputra  * of recv/read operation done at sockfs, and is guaranteed to execute
8078ff550d0eSmasputra  * outside of the interrupt thread context.  It returns a single datagram
8079ff550d0eSmasputra  * (b_cont chain of T_UNITDATA_IND plus data) to the upper layer.
8080ff550d0eSmasputra  */
8081ff550d0eSmasputra static int
8082ff550d0eSmasputra udp_rrw(queue_t *q, struiod_t *dp)
8083ff550d0eSmasputra {
8084ff550d0eSmasputra 	mblk_t	*mp;
8085fc80c0dfSnordmark 	udp_t	*udp = Q_TO_UDP(q);
8086f4b3ec61Sdh 	udp_stack_t *us = udp->udp_us;
8087ff550d0eSmasputra 
8088ff550d0eSmasputra 	/*
8089ff550d0eSmasputra 	 * Dequeue datagram from the head of the list and return
8090ff550d0eSmasputra 	 * it to caller; also ensure that RSLEEP sd_wakeq flag is
8091ff550d0eSmasputra 	 * set/cleared depending on whether or not there's data
8092ff550d0eSmasputra 	 * remaining in the list.
8093ff550d0eSmasputra 	 */
8094ff550d0eSmasputra 	mutex_enter(&udp->udp_drain_lock);
8095ff550d0eSmasputra 	if (!udp->udp_direct_sockfs) {
8096ff550d0eSmasputra 		mutex_exit(&udp->udp_drain_lock);
8097f4b3ec61Sdh 		UDP_STAT(us, udp_rrw_busy);
8098ff550d0eSmasputra 		return (EBUSY);
8099ff550d0eSmasputra 	}
8100ff550d0eSmasputra 	if ((mp = udp->udp_rcv_list_head) != NULL) {
8101ff550d0eSmasputra 		uint_t size = msgdsize(mp);
8102ff550d0eSmasputra 
8103ff550d0eSmasputra 		/* Last datagram in the list? */
8104ff550d0eSmasputra 		if ((udp->udp_rcv_list_head = mp->b_next) == NULL)
8105ff550d0eSmasputra 			udp->udp_rcv_list_tail = NULL;
8106ff550d0eSmasputra 		mp->b_next = NULL;
8107ff550d0eSmasputra 
8108ff550d0eSmasputra 		udp->udp_rcv_cnt -= size;
8109ff550d0eSmasputra 		udp->udp_rcv_msgcnt--;
8110f4b3ec61Sdh 		UDP_STAT(us, udp_rrw_msgcnt);
8111ff550d0eSmasputra 
8112ff550d0eSmasputra 		/* No longer flow-controlling? */
8113ff550d0eSmasputra 		if (udp->udp_rcv_cnt < udp->udp_rcv_hiwat &&
8114ff550d0eSmasputra 		    udp->udp_rcv_msgcnt < udp->udp_rcv_hiwat)
8115ff550d0eSmasputra 			udp->udp_drain_qfull = B_FALSE;
8116ff550d0eSmasputra 	}
8117ff550d0eSmasputra 	if (udp->udp_rcv_list_head == NULL) {
8118ff550d0eSmasputra 		/*
8119ff550d0eSmasputra 		 * Either we just dequeued the last datagram or
8120ff550d0eSmasputra 		 * we get here from sockfs and have nothing to
8121ff550d0eSmasputra 		 * return; in this case clear RSLEEP.
8122ff550d0eSmasputra 		 */
8123ff550d0eSmasputra 		ASSERT(udp->udp_rcv_cnt == 0);
8124ff550d0eSmasputra 		ASSERT(udp->udp_rcv_msgcnt == 0);
8125ff550d0eSmasputra 		ASSERT(udp->udp_rcv_list_tail == NULL);
8126ff550d0eSmasputra 		STR_WAKEUP_CLEAR(STREAM(q));
8127ff550d0eSmasputra 	} else {
8128ff550d0eSmasputra 		/*
8129ff550d0eSmasputra 		 * More data follows; we need udp_rrw() to be
8130ff550d0eSmasputra 		 * called in future to pick up the rest.
8131ff550d0eSmasputra 		 */
8132ff550d0eSmasputra 		STR_WAKEUP_SET(STREAM(q));
8133ff550d0eSmasputra 	}
8134ff550d0eSmasputra 	mutex_exit(&udp->udp_drain_lock);
8135ff550d0eSmasputra 	dp->d_mp = mp;
8136ff550d0eSmasputra 	return (0);
8137ff550d0eSmasputra }
8138ff550d0eSmasputra 
8139ff550d0eSmasputra /*
8140ff550d0eSmasputra  * Enqueue a completely-built T_UNITDATA_IND message into the receive
8141ff550d0eSmasputra  * list; this is typically executed within the interrupt thread context
8142ff550d0eSmasputra  * and so we do things as quickly as possible.
8143ff550d0eSmasputra  */
8144ff550d0eSmasputra static void
8145ff550d0eSmasputra udp_rcv_enqueue(queue_t *q, udp_t *udp, mblk_t *mp, uint_t pkt_len)
8146ff550d0eSmasputra {
8147ff550d0eSmasputra 	ASSERT(q == RD(q));
8148ff550d0eSmasputra 	ASSERT(pkt_len == msgdsize(mp));
8149ff550d0eSmasputra 	ASSERT(mp->b_next == NULL && mp->b_cont != NULL);
8150ff550d0eSmasputra 	ASSERT(DB_TYPE(mp) == M_PROTO && DB_TYPE(mp->b_cont) == M_DATA);
8151ff550d0eSmasputra 	ASSERT(MBLKL(mp) >= sizeof (struct T_unitdata_ind));
8152ff550d0eSmasputra 
8153ff550d0eSmasputra 	mutex_enter(&udp->udp_drain_lock);
8154ff550d0eSmasputra 	/*
8155ff550d0eSmasputra 	 * Wake up and signal the receiving app; it is okay to do this
8156ff550d0eSmasputra 	 * before enqueueing the mp because we are holding the drain lock.
8157ff550d0eSmasputra 	 * One of the advantages of synchronous stream is the ability for
8158ff550d0eSmasputra 	 * us to find out when the application performs a read on the
8159ff550d0eSmasputra 	 * socket by way of udp_rrw() entry point being called.  We need
8160ff550d0eSmasputra 	 * to generate SIGPOLL/SIGIO for each received data in the case
8161ff550d0eSmasputra 	 * of asynchronous socket just as in the strrput() case.  However,
8162ff550d0eSmasputra 	 * we only wake the application up when necessary, i.e. during the
8163ff550d0eSmasputra 	 * first enqueue.  When udp_rrw() is called, we send up a single
8164ff550d0eSmasputra 	 * datagram upstream and call STR_WAKEUP_SET() again when there
8165ff550d0eSmasputra 	 * are still data remaining in our receive queue.
8166ff550d0eSmasputra 	 */
81678c0bf406Sja 	STR_WAKEUP_SENDSIG(STREAM(q), udp->udp_rcv_list_head);
81688c0bf406Sja 	if (udp->udp_rcv_list_head == NULL)
8169ff550d0eSmasputra 		udp->udp_rcv_list_head = mp;
81708c0bf406Sja 	else
8171ff550d0eSmasputra 		udp->udp_rcv_list_tail->b_next = mp;
8172ff550d0eSmasputra 	udp->udp_rcv_list_tail = mp;
8173ff550d0eSmasputra 	udp->udp_rcv_cnt += pkt_len;
8174ff550d0eSmasputra 	udp->udp_rcv_msgcnt++;
8175ff550d0eSmasputra 
8176ff550d0eSmasputra 	/* Need to flow-control? */
8177ff550d0eSmasputra 	if (udp->udp_rcv_cnt >= udp->udp_rcv_hiwat ||
8178ff550d0eSmasputra 	    udp->udp_rcv_msgcnt >= udp->udp_rcv_hiwat)
8179ff550d0eSmasputra 		udp->udp_drain_qfull = B_TRUE;
8180ff550d0eSmasputra 
8181ff550d0eSmasputra 	mutex_exit(&udp->udp_drain_lock);
8182ff550d0eSmasputra }
8183ff550d0eSmasputra 
8184ff550d0eSmasputra /*
8185ff550d0eSmasputra  * Drain the contents of receive list to the module upstream; we do
8186ff550d0eSmasputra  * this during close or when we fallback to the slow mode due to
8187ff550d0eSmasputra  * sockmod being popped or a module being pushed on top of us.
8188ff550d0eSmasputra  */
8189ff550d0eSmasputra static void
8190ff550d0eSmasputra udp_rcv_drain(queue_t *q, udp_t *udp, boolean_t closing)
8191ff550d0eSmasputra {
8192ff550d0eSmasputra 	mblk_t *mp;
8193f4b3ec61Sdh 	udp_stack_t *us = udp->udp_us;
8194ff550d0eSmasputra 
8195ff550d0eSmasputra 	ASSERT(q == RD(q));
8196ff550d0eSmasputra 
8197ff550d0eSmasputra 	mutex_enter(&udp->udp_drain_lock);
8198ff550d0eSmasputra 	/*
8199ff550d0eSmasputra 	 * There is no race with a concurrent udp_input() sending
8200ff550d0eSmasputra 	 * up packets using putnext() after we have cleared the
8201ff550d0eSmasputra 	 * udp_direct_sockfs flag but before we have completed
8202ff550d0eSmasputra 	 * sending up the packets in udp_rcv_list, since we are
8203ff550d0eSmasputra 	 * either a writer or we have quiesced the conn.
8204ff550d0eSmasputra 	 */
8205ff550d0eSmasputra 	udp->udp_direct_sockfs = B_FALSE;
8206ff550d0eSmasputra 	mutex_exit(&udp->udp_drain_lock);
8207ff550d0eSmasputra 
8208ff550d0eSmasputra 	if (udp->udp_rcv_list_head != NULL)
8209f4b3ec61Sdh 		UDP_STAT(us, udp_drain);
8210ff550d0eSmasputra 
8211ff550d0eSmasputra 	/*
8212ff550d0eSmasputra 	 * Send up everything via putnext(); note here that we
8213ff550d0eSmasputra 	 * don't need the udp_drain_lock to protect us since
8214ff550d0eSmasputra 	 * nothing can enter udp_rrw() and that we currently
8215ff550d0eSmasputra 	 * have exclusive access to this udp.
8216ff550d0eSmasputra 	 */
8217ff550d0eSmasputra 	while ((mp = udp->udp_rcv_list_head) != NULL) {
8218ff550d0eSmasputra 		udp->udp_rcv_list_head = mp->b_next;
8219ff550d0eSmasputra 		mp->b_next = NULL;
8220ff550d0eSmasputra 		udp->udp_rcv_cnt -= msgdsize(mp);
8221ff550d0eSmasputra 		udp->udp_rcv_msgcnt--;
8222ff550d0eSmasputra 		if (closing) {
8223ff550d0eSmasputra 			freemsg(mp);
8224ff550d0eSmasputra 		} else {
8225ff550d0eSmasputra 			putnext(q, mp);
8226ff550d0eSmasputra 		}
8227ff550d0eSmasputra 	}
8228ff550d0eSmasputra 	ASSERT(udp->udp_rcv_cnt == 0);
8229ff550d0eSmasputra 	ASSERT(udp->udp_rcv_msgcnt == 0);
8230ff550d0eSmasputra 	ASSERT(udp->udp_rcv_list_head == NULL);
8231ff550d0eSmasputra 	udp->udp_rcv_list_tail = NULL;
8232ff550d0eSmasputra 	udp->udp_drain_qfull = B_FALSE;
8233ff550d0eSmasputra }
8234ff550d0eSmasputra 
8235ff550d0eSmasputra static size_t
8236ff550d0eSmasputra udp_set_rcv_hiwat(udp_t *udp, size_t size)
8237ff550d0eSmasputra {
8238f4b3ec61Sdh 	udp_stack_t *us = udp->udp_us;
8239f4b3ec61Sdh 
8240ff550d0eSmasputra 	/* We add a bit of extra buffering */
8241ff550d0eSmasputra 	size += size >> 1;
8242f4b3ec61Sdh 	if (size > us->us_max_buf)
8243f4b3ec61Sdh 		size = us->us_max_buf;
8244ff550d0eSmasputra 
8245ff550d0eSmasputra 	udp->udp_rcv_hiwat = size;
8246ff550d0eSmasputra 	return (size);
8247ff550d0eSmasputra }
8248fc80c0dfSnordmark 
8249fc80c0dfSnordmark /*
8250fc80c0dfSnordmark  * For the lower queue so that UDP can be a dummy mux.
8251fc80c0dfSnordmark  * Nobody should be sending
8252fc80c0dfSnordmark  * packets up this stream
8253fc80c0dfSnordmark  */
8254fc80c0dfSnordmark static void
8255fc80c0dfSnordmark udp_lrput(queue_t *q, mblk_t *mp)
8256fc80c0dfSnordmark {
8257fc80c0dfSnordmark 	mblk_t *mp1;
8258fc80c0dfSnordmark 
8259fc80c0dfSnordmark 	switch (mp->b_datap->db_type) {
8260fc80c0dfSnordmark 	case M_FLUSH:
8261fc80c0dfSnordmark 		/* Turn around */
8262fc80c0dfSnordmark 		if (*mp->b_rptr & FLUSHW) {
8263fc80c0dfSnordmark 			*mp->b_rptr &= ~FLUSHR;
8264fc80c0dfSnordmark 			qreply(q, mp);
8265fc80c0dfSnordmark 			return;
8266fc80c0dfSnordmark 		}
8267fc80c0dfSnordmark 		break;
8268fc80c0dfSnordmark 	}
8269fc80c0dfSnordmark 	/* Could receive messages that passed through ar_rput */
8270fc80c0dfSnordmark 	for (mp1 = mp; mp1; mp1 = mp1->b_cont)
8271fc80c0dfSnordmark 		mp1->b_prev = mp1->b_next = NULL;
8272fc80c0dfSnordmark 	freemsg(mp);
8273fc80c0dfSnordmark }
8274fc80c0dfSnordmark 
8275fc80c0dfSnordmark /*
8276fc80c0dfSnordmark  * For the lower queue so that UDP can be a dummy mux.
8277fc80c0dfSnordmark  * Nobody should be sending packets down this stream.
8278fc80c0dfSnordmark  */
8279fc80c0dfSnordmark /* ARGSUSED */
8280fc80c0dfSnordmark void
8281fc80c0dfSnordmark udp_lwput(queue_t *q, mblk_t *mp)
8282fc80c0dfSnordmark {
8283fc80c0dfSnordmark 	freemsg(mp);
8284fc80c0dfSnordmark }
8285