xref: /illumos-gate/usr/src/uts/common/inet/udp/udp.c (revision 738d543b3dfacfef0bde58a2262ae46181599e63)
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
57c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
67c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
77c478bd9Sstevel@tonic-gate  * with the License.
87c478bd9Sstevel@tonic-gate  *
97c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
107c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
117c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
127c478bd9Sstevel@tonic-gate  * and limitations under the License.
137c478bd9Sstevel@tonic-gate  *
147c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
157c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
167c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
177c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
187c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
197c478bd9Sstevel@tonic-gate  *
207c478bd9Sstevel@tonic-gate  * CDDL HEADER END
217c478bd9Sstevel@tonic-gate  */
227c478bd9Sstevel@tonic-gate /*
23d045b987Smasputra  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate /* Copyright (c) 1990 Mentat Inc. */
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
297c478bd9Sstevel@tonic-gate 
307c478bd9Sstevel@tonic-gate const char udp_version[] = "%Z%%M%	%I%	%E% SMI";
317c478bd9Sstevel@tonic-gate 
327c478bd9Sstevel@tonic-gate #include <sys/types.h>
337c478bd9Sstevel@tonic-gate #include <sys/stream.h>
34ff550d0eSmasputra #include <sys/dlpi.h>
35ff550d0eSmasputra #include <sys/pattr.h>
367c478bd9Sstevel@tonic-gate #include <sys/stropts.h>
377c478bd9Sstevel@tonic-gate #include <sys/strlog.h>
387c478bd9Sstevel@tonic-gate #include <sys/strsun.h>
397c478bd9Sstevel@tonic-gate #define	_SUN_TPI_VERSION 2
407c478bd9Sstevel@tonic-gate #include <sys/tihdr.h>
417c478bd9Sstevel@tonic-gate #include <sys/timod.h>
427c478bd9Sstevel@tonic-gate #include <sys/tiuser.h>
437c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
447c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
457c478bd9Sstevel@tonic-gate #include <sys/strsubr.h>
467c478bd9Sstevel@tonic-gate #include <sys/suntpi.h>
477c478bd9Sstevel@tonic-gate #include <sys/xti_inet.h>
487c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
497c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
507c478bd9Sstevel@tonic-gate #include <sys/policy.h>
517c478bd9Sstevel@tonic-gate #include <sys/ucred.h>
527c478bd9Sstevel@tonic-gate #include <sys/zone.h>
537c478bd9Sstevel@tonic-gate 
547c478bd9Sstevel@tonic-gate #include <sys/socket.h>
55ff550d0eSmasputra #include <sys/sockio.h>
567c478bd9Sstevel@tonic-gate #include <sys/vtrace.h>
577c478bd9Sstevel@tonic-gate #include <sys/debug.h>
587c478bd9Sstevel@tonic-gate #include <sys/isa_defs.h>
597c478bd9Sstevel@tonic-gate #include <sys/random.h>
607c478bd9Sstevel@tonic-gate #include <netinet/in.h>
617c478bd9Sstevel@tonic-gate #include <netinet/ip6.h>
627c478bd9Sstevel@tonic-gate #include <netinet/icmp6.h>
637c478bd9Sstevel@tonic-gate #include <netinet/udp.h>
647c478bd9Sstevel@tonic-gate #include <net/if.h>
65ff550d0eSmasputra #include <net/route.h>
667c478bd9Sstevel@tonic-gate 
677c478bd9Sstevel@tonic-gate #include <inet/common.h>
687c478bd9Sstevel@tonic-gate #include <inet/ip.h>
69ff550d0eSmasputra #include <inet/ip_impl.h>
707c478bd9Sstevel@tonic-gate #include <inet/ip6.h>
717c478bd9Sstevel@tonic-gate #include <inet/ip_ire.h>
72ff550d0eSmasputra #include <inet/ip_if.h>
73ff550d0eSmasputra #include <inet/ip_multi.h>
747c478bd9Sstevel@tonic-gate #include <inet/mi.h>
757c478bd9Sstevel@tonic-gate #include <inet/mib2.h>
767c478bd9Sstevel@tonic-gate #include <inet/nd.h>
777c478bd9Sstevel@tonic-gate #include <inet/optcom.h>
787c478bd9Sstevel@tonic-gate #include <inet/snmpcom.h>
797c478bd9Sstevel@tonic-gate #include <inet/kstatcom.h>
807c478bd9Sstevel@tonic-gate #include <inet/udp_impl.h>
81ff550d0eSmasputra #include <inet/ipclassifier.h>
82ff550d0eSmasputra #include <inet/ipsec_impl.h>
83ff550d0eSmasputra #include <inet/ipp_common.h>
847c478bd9Sstevel@tonic-gate 
857c478bd9Sstevel@tonic-gate /*
86ff550d0eSmasputra  * The ipsec_info.h header file is here since it has the definition for the
877c478bd9Sstevel@tonic-gate  * M_CTL message types used by IP to convey information to the ULP. The
887c478bd9Sstevel@tonic-gate  * ipsec_info.h needs the pfkeyv2.h, hence the latters presence.
897c478bd9Sstevel@tonic-gate  */
907c478bd9Sstevel@tonic-gate #include <net/pfkeyv2.h>
917c478bd9Sstevel@tonic-gate #include <inet/ipsec_info.h>
927c478bd9Sstevel@tonic-gate 
937c478bd9Sstevel@tonic-gate /*
947c478bd9Sstevel@tonic-gate  * Synchronization notes:
957c478bd9Sstevel@tonic-gate  *
96ff550d0eSmasputra  * UDP uses a combination of its internal perimeter, a global lock and
97ff550d0eSmasputra  * a set of bind hash locks to protect its data structures.  Please see
98ff550d0eSmasputra  * the note above udp_mode_assertions for details about the internal
99ff550d0eSmasputra  * perimeter.
1007c478bd9Sstevel@tonic-gate  *
101ff550d0eSmasputra  * When a UDP endpoint is bound to a local port, it is inserted into
1027c478bd9Sstevel@tonic-gate  * a bind hash list.  The list consists of an array of udp_fanout_t buckets.
1037c478bd9Sstevel@tonic-gate  * The size of the array is controlled by the udp_bind_fanout_size variable.
1047c478bd9Sstevel@tonic-gate  * This variable can be changed in /etc/system if the default value is
105ff550d0eSmasputra  * not large enough.  Each bind hash bucket is protected by a per bucket
106ff550d0eSmasputra  * lock.  It protects the udp_bind_hash and udp_ptpbhn fields in the udp_t
1077c478bd9Sstevel@tonic-gate  * structure.  An UDP endpoint is removed from the bind hash list only
1087c478bd9Sstevel@tonic-gate  * when it is being unbound or being closed.  The per bucket lock also
109ff550d0eSmasputra  * protects a UDP endpoint's state changes.
110ff550d0eSmasputra  *
111ff550d0eSmasputra  * Plumbing notes:
112ff550d0eSmasputra  *
113ff550d0eSmasputra  * Both udp and ip are merged, but the streams plumbing is kept unchanged
114ff550d0eSmasputra  * in that udp is always pushed atop /dev/ip.  This is done to preserve
115ff550d0eSmasputra  * backwards compatibility for certain applications which rely on such
116ff550d0eSmasputra  * plumbing geometry to do things such as issuing I_POP on the stream
117ff550d0eSmasputra  * in order to obtain direct access to /dev/ip, etc.
118ff550d0eSmasputra  *
119ff550d0eSmasputra  * All UDP processings happen in the /dev/ip instance; the udp module
120ff550d0eSmasputra  * instance does not possess any state about the endpoint, and merely
121ff550d0eSmasputra  * acts as a dummy module whose presence is to keep the streams plumbing
122ff550d0eSmasputra  * appearance unchanged.  At open time /dev/ip allocates a conn_t that
123ff550d0eSmasputra  * happens to embed a udp_t.  This stays dormant until the time udp is
124ff550d0eSmasputra  * pushed, which indicates to /dev/ip that it must convert itself from
125ff550d0eSmasputra  * an IP to a UDP endpoint.
126ff550d0eSmasputra  *
127ff550d0eSmasputra  * We only allow for the following plumbing cases:
128ff550d0eSmasputra  *
129ff550d0eSmasputra  * Normal:
130ff550d0eSmasputra  *	/dev/ip is first opened and later udp is pushed directly on top.
131ff550d0eSmasputra  *	This is the default action that happens when a udp socket or
132ff550d0eSmasputra  *	/dev/udp is opened.  The conn_t created by /dev/ip instance is
133ff550d0eSmasputra  *	now shared and is marked with IPCL_UDP.
134ff550d0eSmasputra  *
135ff550d0eSmasputra  * SNMP-only:
136ff550d0eSmasputra  *	udp is pushed on top of a module other than /dev/ip.  When this
137ff550d0eSmasputra  *	happens it will support only SNMP semantics.  A new conn_t is
138ff550d0eSmasputra  *	allocated and marked with IPCL_UDPMOD.
139ff550d0eSmasputra  *
140ff550d0eSmasputra  * The above cases imply that we don't support any intermediate module to
141ff550d0eSmasputra  * reside in between /dev/ip and udp -- in fact, we never supported such
142ff550d0eSmasputra  * scenario in the past as the inter-layer communication semantics have
143ff550d0eSmasputra  * always been private.  Also note that the normal case allows for SNMP
144ff550d0eSmasputra  * requests to be processed in addition to the rest of UDP operations.
145ff550d0eSmasputra  *
146ff550d0eSmasputra  * The normal case plumbing is depicted by the following diagram:
147ff550d0eSmasputra  *
148ff550d0eSmasputra  *	+---------------+---------------+
149ff550d0eSmasputra  *	|		|		| udp
150ff550d0eSmasputra  *	|     udp_wq	|    udp_rq	|
151ff550d0eSmasputra  *	|		|    UDP_RD	|
152ff550d0eSmasputra  *	|		|		|
153ff550d0eSmasputra  *	+---------------+---------------+
154ff550d0eSmasputra  *		|		^
155ff550d0eSmasputra  *		v		|
156ff550d0eSmasputra  *	+---------------+---------------+
157ff550d0eSmasputra  *	|		|		| /dev/ip
158ff550d0eSmasputra  *	|     ip_wq	|     ip_rq	| conn_t
159ff550d0eSmasputra  *	|     UDP_WR	|		|
160ff550d0eSmasputra  *	|		|		|
161ff550d0eSmasputra  *	+---------------+---------------+
162ff550d0eSmasputra  *
163ff550d0eSmasputra  * Messages arriving at udp_wq from above will end up in ip_wq before
164ff550d0eSmasputra  * it gets processed, i.e. udp write entry points will advance udp_wq
165ff550d0eSmasputra  * and use its q_next value as ip_wq in order to use the conn_t that
166ff550d0eSmasputra  * is stored in its q_ptr.  Likewise, messages generated by ip to the
167ff550d0eSmasputra  * module above udp will appear as if they are originated from udp_rq,
168ff550d0eSmasputra  * i.e. putnext() calls to the module above udp is done using the
169ff550d0eSmasputra  * udp_rq instead of ip_rq in order to avoid udp_rput() which does
170ff550d0eSmasputra  * nothing more than calling putnext().
171ff550d0eSmasputra  *
172ff550d0eSmasputra  * The above implies the following rule of thumb:
173ff550d0eSmasputra  *
174ff550d0eSmasputra  *   1. udp_t is obtained from conn_t, which is created by the /dev/ip
175ff550d0eSmasputra  *	instance and is stored in q_ptr of both ip_wq and ip_rq.  There
176ff550d0eSmasputra  *	is no direct reference to conn_t from either udp_wq or udp_rq.
177ff550d0eSmasputra  *
178ff550d0eSmasputra  *   2. Write-side entry points of udp can obtain the conn_t via the
179ff550d0eSmasputra  *	Q_TO_CONN() macro, using the queue value obtain from UDP_WR().
180ff550d0eSmasputra  *
181ff550d0eSmasputra  *   3. While in /dev/ip context, putnext() to the module above udp can
182ff550d0eSmasputra  *	be done by supplying the queue value obtained from UDP_RD().
183ff550d0eSmasputra  *
1847c478bd9Sstevel@tonic-gate  */
1857c478bd9Sstevel@tonic-gate 
186ff550d0eSmasputra static queue_t *UDP_WR(queue_t *);
187ff550d0eSmasputra static queue_t *UDP_RD(queue_t *);
188ff550d0eSmasputra 
189ff550d0eSmasputra udp_stat_t udp_statistics = {
190ff550d0eSmasputra 	{ "udp_ip_send",		KSTAT_DATA_UINT64 },
191ff550d0eSmasputra 	{ "udp_ip_ire_send",		KSTAT_DATA_UINT64 },
192ff550d0eSmasputra 	{ "udp_ire_null",		KSTAT_DATA_UINT64 },
193ff550d0eSmasputra 	{ "udp_drain",			KSTAT_DATA_UINT64 },
194ff550d0eSmasputra 	{ "udp_sock_fallback",		KSTAT_DATA_UINT64 },
195ff550d0eSmasputra 	{ "udp_rrw_busy",		KSTAT_DATA_UINT64 },
196ff550d0eSmasputra 	{ "udp_rrw_msgcnt",		KSTAT_DATA_UINT64 },
197ff550d0eSmasputra 	{ "udp_out_sw_cksum",		KSTAT_DATA_UINT64 },
198ff550d0eSmasputra 	{ "udp_out_sw_cksum_bytes",	KSTAT_DATA_UINT64 },
199ff550d0eSmasputra 	{ "udp_out_opt",		KSTAT_DATA_UINT64 },
200ff550d0eSmasputra 	{ "udp_out_err_notconn",	KSTAT_DATA_UINT64 },
201ff550d0eSmasputra 	{ "udp_out_err_output",		KSTAT_DATA_UINT64 },
202ff550d0eSmasputra 	{ "udp_out_err_tudr",		KSTAT_DATA_UINT64 },
203ff550d0eSmasputra 	{ "udp_in_pktinfo",		KSTAT_DATA_UINT64 },
204ff550d0eSmasputra 	{ "udp_in_recvdstaddr",		KSTAT_DATA_UINT64 },
205ff550d0eSmasputra 	{ "udp_in_recvopts",		KSTAT_DATA_UINT64 },
206ff550d0eSmasputra 	{ "udp_in_recvif",		KSTAT_DATA_UINT64 },
207ff550d0eSmasputra 	{ "udp_in_recvslla",		KSTAT_DATA_UINT64 },
208ff550d0eSmasputra 	{ "udp_in_recvucred",		KSTAT_DATA_UINT64 },
209ff550d0eSmasputra 	{ "udp_in_recvttl",		KSTAT_DATA_UINT64 },
210ff550d0eSmasputra 	{ "udp_in_recvhopopts",		KSTAT_DATA_UINT64 },
211ff550d0eSmasputra 	{ "udp_in_recvhoplimit",	KSTAT_DATA_UINT64 },
212ff550d0eSmasputra 	{ "udp_in_recvdstopts",		KSTAT_DATA_UINT64 },
213ff550d0eSmasputra 	{ "udp_in_recvrtdstopts",	KSTAT_DATA_UINT64 },
214ff550d0eSmasputra 	{ "udp_in_recvrthdr",		KSTAT_DATA_UINT64 },
215ff550d0eSmasputra 	{ "udp_in_recvpktinfo",		KSTAT_DATA_UINT64 },
216ff550d0eSmasputra 	{ "udp_in_recvtclass",		KSTAT_DATA_UINT64 },
217ff550d0eSmasputra #ifdef DEBUG
218ff550d0eSmasputra 	{ "udp_data_conn",		KSTAT_DATA_UINT64 },
219ff550d0eSmasputra 	{ "udp_data_notconn",		KSTAT_DATA_UINT64 },
220ff550d0eSmasputra #endif
221ff550d0eSmasputra };
222ff550d0eSmasputra 
223ff550d0eSmasputra static kstat_t *udp_ksp;
224ff550d0eSmasputra struct kmem_cache *udp_cache;
225ff550d0eSmasputra 
2267c478bd9Sstevel@tonic-gate /*
2277c478bd9Sstevel@tonic-gate  * Bind hash list size and hash function.  It has to be a power of 2 for
2287c478bd9Sstevel@tonic-gate  * hashing.
2297c478bd9Sstevel@tonic-gate  */
2307c478bd9Sstevel@tonic-gate #define	UDP_BIND_FANOUT_SIZE	512
2317c478bd9Sstevel@tonic-gate #define	UDP_BIND_HASH(lport) \
2327c478bd9Sstevel@tonic-gate 	((ntohs((uint16_t)lport)) & (udp_bind_fanout_size - 1))
2337c478bd9Sstevel@tonic-gate 
2347c478bd9Sstevel@tonic-gate /* UDP bind fanout hash structure. */
2357c478bd9Sstevel@tonic-gate typedef struct udp_fanout_s {
2367c478bd9Sstevel@tonic-gate 	udp_t *uf_udp;
2377c478bd9Sstevel@tonic-gate 	kmutex_t uf_lock;
2387c478bd9Sstevel@tonic-gate #if defined(_LP64) || defined(_I32LPx)
2397c478bd9Sstevel@tonic-gate 	char	uf_pad[48];
2407c478bd9Sstevel@tonic-gate #else
2417c478bd9Sstevel@tonic-gate 	char	uf_pad[56];
2427c478bd9Sstevel@tonic-gate #endif
2437c478bd9Sstevel@tonic-gate } udp_fanout_t;
2447c478bd9Sstevel@tonic-gate 
2457c478bd9Sstevel@tonic-gate uint_t udp_bind_fanout_size = UDP_BIND_FANOUT_SIZE;
2467c478bd9Sstevel@tonic-gate /* udp_fanout_t *udp_bind_fanout. */
2477c478bd9Sstevel@tonic-gate static udp_fanout_t *udp_bind_fanout;
2487c478bd9Sstevel@tonic-gate 
2497c478bd9Sstevel@tonic-gate /*
2507c478bd9Sstevel@tonic-gate  * This controls the rate some ndd info report functions can be used
2517c478bd9Sstevel@tonic-gate  * by non-priviledged users.  It stores the last time such info is
2527c478bd9Sstevel@tonic-gate  * requested.  When those report functions are called again, this
2537c478bd9Sstevel@tonic-gate  * is checked with the current time and compare with the ndd param
2547c478bd9Sstevel@tonic-gate  * udp_ndd_get_info_interval.
2557c478bd9Sstevel@tonic-gate  */
2567c478bd9Sstevel@tonic-gate static clock_t udp_last_ndd_get_info_time;
2577c478bd9Sstevel@tonic-gate #define	NDD_TOO_QUICK_MSG \
2587c478bd9Sstevel@tonic-gate 	"ndd get info rate too high for non-priviledged users, try again " \
2597c478bd9Sstevel@tonic-gate 	"later.\n"
2607c478bd9Sstevel@tonic-gate #define	NDD_OUT_OF_BUF_MSG	"<< Out of buffer >>\n"
2617c478bd9Sstevel@tonic-gate 
2627c478bd9Sstevel@tonic-gate static void	udp_addr_req(queue_t *q, mblk_t *mp);
2637c478bd9Sstevel@tonic-gate static void	udp_bind(queue_t *q, mblk_t *mp);
2647c478bd9Sstevel@tonic-gate static void	udp_bind_hash_insert(udp_fanout_t *uf, udp_t *udp);
2657c478bd9Sstevel@tonic-gate static void	udp_bind_hash_remove(udp_t *udp, boolean_t caller_holds_lock);
2667c478bd9Sstevel@tonic-gate static int	udp_build_hdrs(queue_t *q, udp_t *udp);
2677c478bd9Sstevel@tonic-gate static void	udp_capability_req(queue_t *q, mblk_t *mp);
2687c478bd9Sstevel@tonic-gate static int	udp_close(queue_t *q);
2697c478bd9Sstevel@tonic-gate static void	udp_connect(queue_t *q, mblk_t *mp);
2707c478bd9Sstevel@tonic-gate static void	udp_disconnect(queue_t *q, mblk_t *mp);
2717c478bd9Sstevel@tonic-gate static void	udp_err_ack(queue_t *q, mblk_t *mp, t_scalar_t t_error,
2727c478bd9Sstevel@tonic-gate 		    int sys_error);
2737c478bd9Sstevel@tonic-gate static void	udp_err_ack_prim(queue_t *q, mblk_t *mp, int primitive,
2747c478bd9Sstevel@tonic-gate 		    t_scalar_t tlierr, int unixerr);
2757c478bd9Sstevel@tonic-gate static int	udp_extra_priv_ports_get(queue_t *q, mblk_t *mp, caddr_t cp,
2767c478bd9Sstevel@tonic-gate 		    cred_t *cr);
2777c478bd9Sstevel@tonic-gate static int	udp_extra_priv_ports_add(queue_t *q, mblk_t *mp,
2787c478bd9Sstevel@tonic-gate 		    char *value, caddr_t cp, cred_t *cr);
2797c478bd9Sstevel@tonic-gate static int	udp_extra_priv_ports_del(queue_t *q, mblk_t *mp,
2807c478bd9Sstevel@tonic-gate 		    char *value, caddr_t cp, cred_t *cr);
2817c478bd9Sstevel@tonic-gate static void	udp_icmp_error(queue_t *q, mblk_t *mp);
2827c478bd9Sstevel@tonic-gate static void	udp_icmp_error_ipv6(queue_t *q, mblk_t *mp);
2837c478bd9Sstevel@tonic-gate static void	udp_info_req(queue_t *q, mblk_t *mp);
2847c478bd9Sstevel@tonic-gate static mblk_t	*udp_ip_bind_mp(udp_t *udp, t_scalar_t bind_prim,
2857c478bd9Sstevel@tonic-gate 		    t_scalar_t addr_length);
2867c478bd9Sstevel@tonic-gate static int	udp_open(queue_t *q, dev_t *devp, int flag, int sflag,
2877c478bd9Sstevel@tonic-gate 		    cred_t *credp);
2887c478bd9Sstevel@tonic-gate static  int	udp_unitdata_opt_process(queue_t *q, mblk_t *mp,
2897c478bd9Sstevel@tonic-gate 		    int *errorp, void *thisdg_attrs);
2907c478bd9Sstevel@tonic-gate static boolean_t udp_opt_allow_udr_set(t_scalar_t level, t_scalar_t name);
2917c478bd9Sstevel@tonic-gate static int	udp_param_get(queue_t *q, mblk_t *mp, caddr_t cp, cred_t *cr);
2927c478bd9Sstevel@tonic-gate static boolean_t udp_param_register(udpparam_t *udppa, int cnt);
2937c478bd9Sstevel@tonic-gate static int	udp_param_set(queue_t *q, mblk_t *mp, char *value, caddr_t cp,
2947c478bd9Sstevel@tonic-gate 		    cred_t *cr);
2957c478bd9Sstevel@tonic-gate static int	udp_pkt_set(uchar_t *invalp, uint_t inlen, boolean_t sticky,
2967c478bd9Sstevel@tonic-gate 		    uchar_t **optbufp, uint_t *optlenp);
2977c478bd9Sstevel@tonic-gate static void	udp_report_item(mblk_t *mp, udp_t *udp);
2987c478bd9Sstevel@tonic-gate static void	udp_rput(queue_t *q, mblk_t *mp);
299ff550d0eSmasputra static void	udp_rput_other(queue_t *, mblk_t *);
300ff550d0eSmasputra static int	udp_rinfop(queue_t *q, infod_t *dp);
301ff550d0eSmasputra static int	udp_rrw(queue_t *q, struiod_t *dp);
3027c478bd9Sstevel@tonic-gate static	void	udp_rput_bind_ack(queue_t *q, mblk_t *mp);
3037c478bd9Sstevel@tonic-gate static int	udp_status_report(queue_t *q, mblk_t *mp, caddr_t cp,
3047c478bd9Sstevel@tonic-gate 		    cred_t *cr);
305ff550d0eSmasputra static void	udp_send_data(udp_t *udp, queue_t *q, mblk_t *mp, ipha_t *ipha);
306ff550d0eSmasputra static void	udp_ud_err(queue_t *q, mblk_t *mp, uchar_t *destaddr,
307ff550d0eSmasputra 		    t_scalar_t destlen, t_scalar_t err);
3087c478bd9Sstevel@tonic-gate static void	udp_unbind(queue_t *q, mblk_t *mp);
3097c478bd9Sstevel@tonic-gate static in_port_t udp_update_next_port(in_port_t port, boolean_t random);
3107c478bd9Sstevel@tonic-gate static void	udp_wput(queue_t *q, mblk_t *mp);
311ff550d0eSmasputra static mblk_t	*udp_output_v4(conn_t *, mblk_t *mp, ipaddr_t v4dst,
312ff550d0eSmasputra 		    uint16_t port, uint_t srcid, int *error);
313ff550d0eSmasputra static mblk_t	*udp_output_v6(conn_t *connp, mblk_t *mp, sin6_t *sin6,
314ff550d0eSmasputra 		    t_scalar_t tudr_optlen, int *error);
3157c478bd9Sstevel@tonic-gate static void	udp_wput_other(queue_t *q, mblk_t *mp);
3167c478bd9Sstevel@tonic-gate static void	udp_wput_iocdata(queue_t *q, mblk_t *mp);
317ff550d0eSmasputra static void	udp_output(conn_t *connp, mblk_t *mp, struct sockaddr *addr,
318ff550d0eSmasputra 		    socklen_t addrlen);
319ff550d0eSmasputra static size_t	udp_set_rcv_hiwat(udp_t *udp, size_t size);
3207c478bd9Sstevel@tonic-gate 
3217c478bd9Sstevel@tonic-gate static void	udp_kstat_init(void);
3227c478bd9Sstevel@tonic-gate static void	udp_kstat_fini(void);
3237c478bd9Sstevel@tonic-gate static int	udp_kstat_update(kstat_t *kp, int rw);
324ff550d0eSmasputra static void	udp_input_wrapper(void *arg, mblk_t *mp, void *arg2);
325ff550d0eSmasputra static void	udp_rput_other_wrapper(void *arg, mblk_t *mp, void *arg2);
326ff550d0eSmasputra static void	udp_wput_other_wrapper(void *arg, mblk_t *mp, void *arg2);
327ff550d0eSmasputra static void	udp_resume_bind_cb(void *arg, mblk_t *mp, void *arg2);
328ff550d0eSmasputra 
329ff550d0eSmasputra static void	udp_rcv_enqueue(queue_t *q, udp_t *udp, mblk_t *mp,
330ff550d0eSmasputra 		    uint_t pkt_len);
331ff550d0eSmasputra static void	udp_rcv_drain(queue_t *q, udp_t *udp, boolean_t closing);
332ff550d0eSmasputra static void	udp_enter(conn_t *, mblk_t *, sqproc_t, uint8_t);
333ff550d0eSmasputra static void	udp_exit(conn_t *);
334ff550d0eSmasputra static void	udp_become_writer(conn_t *, mblk_t *, sqproc_t, uint8_t);
335ff550d0eSmasputra #ifdef DEBUG
336ff550d0eSmasputra static void	udp_mode_assertions(udp_t *, int);
337ff550d0eSmasputra #endif /* DEBUG */
3387c478bd9Sstevel@tonic-gate 
3397c478bd9Sstevel@tonic-gate major_t UDP6_MAJ;
340ff550d0eSmasputra #define	UDP6 "udp6"
341ff550d0eSmasputra 
342ff550d0eSmasputra #define	UDP_RECV_HIWATER	(56 * 1024)
343ff550d0eSmasputra #define	UDP_RECV_LOWATER	128
344ff550d0eSmasputra #define	UDP_XMIT_HIWATER	(56 * 1024)
345ff550d0eSmasputra #define	UDP_XMIT_LOWATER	1024
346ff550d0eSmasputra 
347ff550d0eSmasputra static struct module_info udp_info =  {
348ff550d0eSmasputra 	UDP_MOD_ID, UDP_MOD_NAME, 1, INFPSZ, UDP_RECV_HIWATER, UDP_RECV_LOWATER
349ff550d0eSmasputra };
3507c478bd9Sstevel@tonic-gate 
351ff550d0eSmasputra static struct qinit udp_rinit = {
352ff550d0eSmasputra 	(pfi_t)udp_rput, NULL, udp_open, udp_close, NULL,
353ff550d0eSmasputra 	&udp_info, NULL, udp_rrw, udp_rinfop, STRUIOT_STANDARD
354ff550d0eSmasputra };
3557c478bd9Sstevel@tonic-gate 
356ff550d0eSmasputra static struct qinit udp_winit = {
357ff550d0eSmasputra 	(pfi_t)udp_wput, NULL, NULL, NULL, NULL,
358ff550d0eSmasputra 	&udp_info, NULL, NULL, NULL, STRUIOT_NONE
3597c478bd9Sstevel@tonic-gate };
3607c478bd9Sstevel@tonic-gate 
361d045b987Smasputra static struct qinit winit = {
362d045b987Smasputra 	(pfi_t)putnext, NULL, NULL, NULL, NULL,
363d045b987Smasputra 	&udp_info, NULL, NULL, NULL, STRUIOT_NONE
364d045b987Smasputra };
365d045b987Smasputra 
366ff550d0eSmasputra /* Support for just SNMP if UDP is not pushed directly over device IP */
367ff550d0eSmasputra struct qinit udp_snmp_rinit = {
368ff550d0eSmasputra 	(pfi_t)putnext, NULL, udp_open, ip_snmpmod_close, NULL,
369ff550d0eSmasputra 	&udp_info, NULL, NULL, NULL, STRUIOT_NONE
3707c478bd9Sstevel@tonic-gate };
3717c478bd9Sstevel@tonic-gate 
372ff550d0eSmasputra struct qinit udp_snmp_winit = {
373ff550d0eSmasputra 	(pfi_t)ip_snmpmod_wput, NULL, udp_open, ip_snmpmod_close, NULL,
374ff550d0eSmasputra 	&udp_info, NULL, NULL, NULL, STRUIOT_NONE
3757c478bd9Sstevel@tonic-gate };
3767c478bd9Sstevel@tonic-gate 
3777c478bd9Sstevel@tonic-gate struct streamtab udpinfo = {
378d045b987Smasputra 	&udp_rinit, &winit
3797c478bd9Sstevel@tonic-gate };
3807c478bd9Sstevel@tonic-gate 
3817c478bd9Sstevel@tonic-gate static	sin_t	sin_null;	/* Zero address for quick clears */
3827c478bd9Sstevel@tonic-gate static	sin6_t	sin6_null;	/* Zero address for quick clears */
3837c478bd9Sstevel@tonic-gate 
3847c478bd9Sstevel@tonic-gate /* Hint not protected by any lock */
3857c478bd9Sstevel@tonic-gate static in_port_t	udp_g_next_port_to_try;
3867c478bd9Sstevel@tonic-gate 
3877c478bd9Sstevel@tonic-gate /*
388ff550d0eSmasputra  * Extra privileged ports. In host byte order.
3897c478bd9Sstevel@tonic-gate  */
3907c478bd9Sstevel@tonic-gate #define	UDP_NUM_EPRIV_PORTS	64
3917c478bd9Sstevel@tonic-gate static int	udp_g_num_epriv_ports = UDP_NUM_EPRIV_PORTS;
3927c478bd9Sstevel@tonic-gate static in_port_t udp_g_epriv_ports[UDP_NUM_EPRIV_PORTS] = { 2049, 4045 };
3937c478bd9Sstevel@tonic-gate 
3947c478bd9Sstevel@tonic-gate /* Only modified during _init and _fini thus no locking is needed. */
3957c478bd9Sstevel@tonic-gate static IDP	udp_g_nd;	/* Points to table of UDP ND variables. */
3967c478bd9Sstevel@tonic-gate 
3977c478bd9Sstevel@tonic-gate /* MIB-2 stuff for SNMP */
3987c478bd9Sstevel@tonic-gate static mib2_udp_t	udp_mib;	/* SNMP fixed size info */
3997c478bd9Sstevel@tonic-gate static kstat_t		*udp_mibkp;	/* kstat exporting udp_mib data */
4007c478bd9Sstevel@tonic-gate 
401ff550d0eSmasputra #define	UDP_MAXPACKET_IPV4 (IP_MAXPACKET - UDPH_SIZE - IP_SIMPLE_HDR_LENGTH)
4027c478bd9Sstevel@tonic-gate 
4037c478bd9Sstevel@tonic-gate /* Default structure copied into T_INFO_ACK messages */
4047c478bd9Sstevel@tonic-gate static struct T_info_ack udp_g_t_info_ack_ipv4 = {
4057c478bd9Sstevel@tonic-gate 	T_INFO_ACK,
4067c478bd9Sstevel@tonic-gate 	UDP_MAXPACKET_IPV4,	/* TSDU_size. Excl. headers */
4077c478bd9Sstevel@tonic-gate 	T_INVALID,	/* ETSU_size.  udp does not support expedited data. */
4087c478bd9Sstevel@tonic-gate 	T_INVALID,	/* CDATA_size. udp does not support connect data. */
4097c478bd9Sstevel@tonic-gate 	T_INVALID,	/* DDATA_size. udp does not support disconnect data. */
4107c478bd9Sstevel@tonic-gate 	sizeof (sin_t),	/* ADDR_size. */
4117c478bd9Sstevel@tonic-gate 	0,		/* OPT_size - not initialized here */
4127c478bd9Sstevel@tonic-gate 	UDP_MAXPACKET_IPV4,	/* TIDU_size.  Excl. headers */
4137c478bd9Sstevel@tonic-gate 	T_CLTS,		/* SERV_type.  udp supports connection-less. */
4147c478bd9Sstevel@tonic-gate 	TS_UNBND,	/* CURRENT_state.  This is set from udp_state. */
4157c478bd9Sstevel@tonic-gate 	(XPG4_1|SENDZERO) /* PROVIDER_flag */
4167c478bd9Sstevel@tonic-gate };
4177c478bd9Sstevel@tonic-gate 
418ff550d0eSmasputra #define	UDP_MAXPACKET_IPV6 (IP_MAXPACKET - UDPH_SIZE - IPV6_HDR_LEN)
419ff550d0eSmasputra 
4207c478bd9Sstevel@tonic-gate static	struct T_info_ack udp_g_t_info_ack_ipv6 = {
4217c478bd9Sstevel@tonic-gate 	T_INFO_ACK,
4227c478bd9Sstevel@tonic-gate 	UDP_MAXPACKET_IPV6,	/* TSDU_size.  Excl. headers */
4237c478bd9Sstevel@tonic-gate 	T_INVALID,	/* ETSU_size.  udp does not support expedited data. */
4247c478bd9Sstevel@tonic-gate 	T_INVALID,	/* CDATA_size. udp does not support connect data. */
4257c478bd9Sstevel@tonic-gate 	T_INVALID,	/* DDATA_size. udp does not support disconnect data. */
4267c478bd9Sstevel@tonic-gate 	sizeof (sin6_t), /* ADDR_size. */
4277c478bd9Sstevel@tonic-gate 	0,		/* OPT_size - not initialized here */
4287c478bd9Sstevel@tonic-gate 	UDP_MAXPACKET_IPV6,	/* TIDU_size. Excl. headers */
4297c478bd9Sstevel@tonic-gate 	T_CLTS,		/* SERV_type.  udp supports connection-less. */
4307c478bd9Sstevel@tonic-gate 	TS_UNBND,	/* CURRENT_state.  This is set from udp_state. */
4317c478bd9Sstevel@tonic-gate 	(XPG4_1|SENDZERO) /* PROVIDER_flag */
4327c478bd9Sstevel@tonic-gate };
4337c478bd9Sstevel@tonic-gate 
4347c478bd9Sstevel@tonic-gate /* largest UDP port number */
4357c478bd9Sstevel@tonic-gate #define	UDP_MAX_PORT	65535
4367c478bd9Sstevel@tonic-gate 
4377c478bd9Sstevel@tonic-gate /*
4387c478bd9Sstevel@tonic-gate  * Table of ND variables supported by udp.  These are loaded into udp_g_nd
4397c478bd9Sstevel@tonic-gate  * in udp_open.
4407c478bd9Sstevel@tonic-gate  * All of these are alterable, within the min/max values given, at run time.
4417c478bd9Sstevel@tonic-gate  */
442ff550d0eSmasputra /* BEGIN CSTYLED */
443ff550d0eSmasputra udpparam_t udp_param_arr[] = {
444ff550d0eSmasputra  /*min		max		value		name */
445ff550d0eSmasputra  { 0L,		256,		32,		"udp_wroff_extra" },
446ff550d0eSmasputra  { 1L,		255,		255,		"udp_ipv4_ttl" },
447ff550d0eSmasputra  { 0,		IPV6_MAX_HOPS,	IPV6_DEFAULT_HOPS, "udp_ipv6_hoplimit"},
448ff550d0eSmasputra  { 1024,	(32 * 1024),	1024,		"udp_smallest_nonpriv_port" },
449ff550d0eSmasputra  { 0,		1,		1,		"udp_do_checksum" },
450ff550d0eSmasputra  { 1024,	UDP_MAX_PORT,	(32 * 1024),	"udp_smallest_anon_port" },
451ff550d0eSmasputra  { 1024,	UDP_MAX_PORT,	UDP_MAX_PORT,	"udp_largest_anon_port" },
452ff550d0eSmasputra  { UDP_XMIT_LOWATER, (1<<30), UDP_XMIT_HIWATER,	"udp_xmit_hiwat"},
453ff550d0eSmasputra  { 0,		     (1<<30), UDP_XMIT_LOWATER, "udp_xmit_lowat"},
454ff550d0eSmasputra  { UDP_RECV_LOWATER, (1<<30), UDP_RECV_HIWATER,	"udp_recv_hiwat"},
455ff550d0eSmasputra  { 65536,	(1<<30),	2*1024*1024,	"udp_max_buf"},
456ff550d0eSmasputra  { 100,		60000,		1000,		"udp_ndd_get_info_interval"},
4577c478bd9Sstevel@tonic-gate };
458ff550d0eSmasputra /* END CSTYLED */
4597c478bd9Sstevel@tonic-gate 
4607c478bd9Sstevel@tonic-gate /*
4617c478bd9Sstevel@tonic-gate  * The smallest anonymous port in the priviledged port range which UDP
4627c478bd9Sstevel@tonic-gate  * looks for free port.  Use in the option UDP_ANONPRIVBIND.
4637c478bd9Sstevel@tonic-gate  */
4647c478bd9Sstevel@tonic-gate static in_port_t udp_min_anonpriv_port = 512;
4657c478bd9Sstevel@tonic-gate 
4667c478bd9Sstevel@tonic-gate /* If set to 0, pick ephemeral port sequentially; otherwise randomly. */
4677c478bd9Sstevel@tonic-gate uint32_t udp_random_anon_port = 1;
4687c478bd9Sstevel@tonic-gate 
4697c478bd9Sstevel@tonic-gate /*
4707c478bd9Sstevel@tonic-gate  * Hook functions to enable cluster networking.
4717c478bd9Sstevel@tonic-gate  * On non-clustered systems these vectors must always be NULL
4727c478bd9Sstevel@tonic-gate  */
4737c478bd9Sstevel@tonic-gate 
4747c478bd9Sstevel@tonic-gate void (*cl_inet_bind)(uchar_t protocol, sa_family_t addr_family,
475ff550d0eSmasputra     uint8_t *laddrp, in_port_t lport) = NULL;
4767c478bd9Sstevel@tonic-gate void (*cl_inet_unbind)(uint8_t protocol, sa_family_t addr_family,
477ff550d0eSmasputra     uint8_t *laddrp, in_port_t lport) = NULL;
478ff550d0eSmasputra 
479ff550d0eSmasputra typedef union T_primitives *t_primp_t;
480ff550d0eSmasputra 
481ff550d0eSmasputra #define	UDP_ENQUEUE_MP(udp, mp, proc, tag) {			\
482ff550d0eSmasputra 	ASSERT((mp)->b_prev == NULL && (mp)->b_queue == NULL);	\
483ff550d0eSmasputra 	ASSERT(MUTEX_HELD(&(udp)->udp_connp->conn_lock));	\
484ff550d0eSmasputra 	(mp)->b_queue = (queue_t *)((uintptr_t)tag);		\
485ff550d0eSmasputra 	(mp)->b_prev = (mblk_t *)proc;				\
486ff550d0eSmasputra 	if ((udp)->udp_mphead == NULL)				\
487ff550d0eSmasputra 		(udp)->udp_mphead = (mp);			\
488ff550d0eSmasputra 	else							\
489ff550d0eSmasputra 		(udp)->udp_mptail->b_next = (mp);		\
490ff550d0eSmasputra 	(udp)->udp_mptail = (mp);				\
491ff550d0eSmasputra 	(udp)->udp_mpcount++;					\
492ff550d0eSmasputra }
493ff550d0eSmasputra 
494ff550d0eSmasputra #define	UDP_READERS_INCREF(udp) {				\
495ff550d0eSmasputra 	ASSERT(MUTEX_HELD(&(udp)->udp_connp->conn_lock));	\
496ff550d0eSmasputra 	(udp)->udp_reader_count++;				\
497ff550d0eSmasputra }
498ff550d0eSmasputra 
499ff550d0eSmasputra #define	UDP_READERS_DECREF(udp) {				\
500ff550d0eSmasputra 	ASSERT(MUTEX_HELD(&(udp)->udp_connp->conn_lock));	\
501ff550d0eSmasputra 	(udp)->udp_reader_count--;				\
502ff550d0eSmasputra 	if ((udp)->udp_reader_count == 0)			\
503ff550d0eSmasputra 		cv_broadcast(&(udp)->udp_connp->conn_cv);	\
504ff550d0eSmasputra }
505ff550d0eSmasputra 
506ff550d0eSmasputra #define	UDP_SQUEUE_DECREF(udp) {				\
507ff550d0eSmasputra 	ASSERT(MUTEX_HELD(&(udp)->udp_connp->conn_lock));	\
508ff550d0eSmasputra 	(udp)->udp_squeue_count--;				\
509ff550d0eSmasputra 	if ((udp)->udp_squeue_count == 0)			\
510ff550d0eSmasputra 		cv_broadcast(&(udp)->udp_connp->conn_cv);	\
511ff550d0eSmasputra }
512ff550d0eSmasputra 
513ff550d0eSmasputra /*
514ff550d0eSmasputra  * Notes on UDP endpoint synchronization:
515ff550d0eSmasputra  *
516ff550d0eSmasputra  * UDP needs exclusive operation on a per endpoint basis, when executing
517ff550d0eSmasputra  * functions that modify the endpoint state.  udp_rput_other() deals with
518ff550d0eSmasputra  * packets with IP options, and processing these packets end up having
519ff550d0eSmasputra  * to update the endpoint's option related state.  udp_wput_other() deals
520ff550d0eSmasputra  * with control operations from the top, e.g. connect() that needs to
521ff550d0eSmasputra  * update the endpoint state.  These could be synchronized using locks,
522ff550d0eSmasputra  * but the current version uses squeues for this purpose.  squeues may
523ff550d0eSmasputra  * give performance improvement for certain cases such as connected UDP
524ff550d0eSmasputra  * sockets; thus the framework allows for using squeues.
525ff550d0eSmasputra  *
526ff550d0eSmasputra  * The perimeter routines are described as follows:
527ff550d0eSmasputra  *
528ff550d0eSmasputra  * udp_enter():
529ff550d0eSmasputra  *	Enter the UDP endpoint perimeter.
530ff550d0eSmasputra  *
531ff550d0eSmasputra  * udp_become_writer():
532ff550d0eSmasputra  *	Become exclusive on the UDP endpoint.  Specifies a function
533ff550d0eSmasputra  *	that will be called exclusively either immediately or later
534ff550d0eSmasputra  *	when the perimeter is available exclusively.
535ff550d0eSmasputra  *
536ff550d0eSmasputra  * udp_exit():
537ff550d0eSmasputra  *	Exit the UDP perimeter.
538ff550d0eSmasputra  *
539ff550d0eSmasputra  * Entering UDP from the top or from the bottom must be done using
540ff550d0eSmasputra  * udp_enter().  No lock must be held while attempting to enter the UDP
541ff550d0eSmasputra  * perimeter.  When finished, udp_exit() must be called to get out of
542ff550d0eSmasputra  * the perimeter.
543ff550d0eSmasputra  *
544ff550d0eSmasputra  * UDP operates in either MT_HOT mode or in SQUEUE mode.  In MT_HOT mode,
545ff550d0eSmasputra  * multiple threads may enter a UDP endpoint concurrently.  This is used
546ff550d0eSmasputra  * for sending and/or receiving normal data.  Control operations and other
547ff550d0eSmasputra  * special cases call udp_become_writer() to become exclusive on a per
548ff550d0eSmasputra  * endpoint basis and this results in transitioning to SQUEUE mode.  squeue
549ff550d0eSmasputra  * by definition serializes access to the conn_t.  When there are no more
550ff550d0eSmasputra  * pending messages on the squeue for the UDP connection, the endpoint
551ff550d0eSmasputra  * reverts to MT_HOT mode.  During the interregnum when not all MT threads
552ff550d0eSmasputra  * of an endpoint have finished, messages are queued in the UDP endpoint
553ff550d0eSmasputra  * and the UDP is in UDP_MT_QUEUED mode or UDP_QUEUED_SQUEUE mode.
554ff550d0eSmasputra  *
555ff550d0eSmasputra  * These modes have the following analogs:
556ff550d0eSmasputra  *
557ff550d0eSmasputra  *	UDP_MT_HOT/udp_reader_count==0		none
558ff550d0eSmasputra  *	UDP_MT_HOT/udp_reader_count>0		RW_READ_LOCK
559ff550d0eSmasputra  *	UDP_MT_QUEUED				RW_WRITE_WANTED
560ff550d0eSmasputra  *	UDP_SQUEUE or UDP_QUEUED_SQUEUE		RW_WRITE_LOCKED
561ff550d0eSmasputra  *
562ff550d0eSmasputra  * Stable modes:	UDP_MT_HOT, UDP_SQUEUE
563ff550d0eSmasputra  * Transient modes:	UDP_MT_QUEUED, UDP_QUEUED_SQUEUE
564ff550d0eSmasputra  *
565ff550d0eSmasputra  * While in stable modes, UDP keeps track of the number of threads
566ff550d0eSmasputra  * operating on the endpoint.  The udp_reader_count variable represents
567ff550d0eSmasputra  * the number of threads entering the endpoint as readers while it is
568ff550d0eSmasputra  * in UDP_MT_HOT mode.  Transitioning to UDP_SQUEUE happens when there
569ff550d0eSmasputra  * is only a single reader, i.e. when this counter drops to 1.  Likewise,
570ff550d0eSmasputra  * udp_squeue_count represents the number of threads operating on the
571ff550d0eSmasputra  * endpoint's squeue while it is in UDP_SQUEUE mode.  The mode transition
572ff550d0eSmasputra  * to UDP_MT_HOT happens after the last thread exits the endpoint, i.e.
573ff550d0eSmasputra  * when this counter drops to 0.
574ff550d0eSmasputra  *
575ff550d0eSmasputra  * The default mode is set to UDP_MT_HOT and UDP alternates between
576ff550d0eSmasputra  * UDP_MT_HOT and UDP_SQUEUE as shown in the state transition below.
577ff550d0eSmasputra  *
578ff550d0eSmasputra  * Mode transition:
579ff550d0eSmasputra  * ----------------------------------------------------------------
580ff550d0eSmasputra  * old mode		Event				New mode
581ff550d0eSmasputra  * ----------------------------------------------------------------
582ff550d0eSmasputra  * UDP_MT_HOT		Call to udp_become_writer()	UDP_SQUEUE
583ff550d0eSmasputra  *			and udp_reader_count == 1
584ff550d0eSmasputra  *
585ff550d0eSmasputra  * UDP_MT_HOT		Call to udp_become_writer()	UDP_MT_QUEUED
586ff550d0eSmasputra  *			and udp_reader_count > 1
587ff550d0eSmasputra  *
588ff550d0eSmasputra  * UDP_MT_QUEUED	udp_reader_count drops to zero	UDP_QUEUED_SQUEUE
589ff550d0eSmasputra  *
590ff550d0eSmasputra  * UDP_QUEUED_SQUEUE	All messages enqueued on the	UDP_SQUEUE
591ff550d0eSmasputra  *			internal UDP queue successfully
592ff550d0eSmasputra  *			moved to squeue AND udp_squeue_count != 0
593ff550d0eSmasputra  *
594ff550d0eSmasputra  * UDP_QUEUED_SQUEUE	All messages enqueued on the	UDP_MT_HOT
595ff550d0eSmasputra  *			internal UDP queue successfully
596ff550d0eSmasputra  *			moved to squeue AND udp_squeue_count
597ff550d0eSmasputra  *			drops to zero
598ff550d0eSmasputra  *
599ff550d0eSmasputra  * UDP_SQUEUE		udp_squeue_count drops to zero	UDP_MT_HOT
600ff550d0eSmasputra  * ----------------------------------------------------------------
601ff550d0eSmasputra  */
602ff550d0eSmasputra 
603ff550d0eSmasputra static queue_t *
604ff550d0eSmasputra UDP_WR(queue_t *q)
605ff550d0eSmasputra {
606ff550d0eSmasputra 	ASSERT(q->q_ptr == NULL && _OTHERQ(q)->q_ptr == NULL);
607ff550d0eSmasputra 	ASSERT(WR(q)->q_next != NULL && WR(q)->q_next->q_ptr != NULL);
608ff550d0eSmasputra 	ASSERT(IPCL_IS_UDP(Q_TO_CONN(WR(q)->q_next)));
609ff550d0eSmasputra 
610ff550d0eSmasputra 	return (_WR(q)->q_next);
611ff550d0eSmasputra }
612ff550d0eSmasputra 
613ff550d0eSmasputra static queue_t *
614ff550d0eSmasputra UDP_RD(queue_t *q)
615ff550d0eSmasputra {
616ff550d0eSmasputra 	ASSERT(q->q_ptr != NULL && _OTHERQ(q)->q_ptr != NULL);
617ff550d0eSmasputra 	ASSERT(IPCL_IS_UDP(Q_TO_CONN(q)));
618ff550d0eSmasputra 	ASSERT(RD(q)->q_next != NULL && RD(q)->q_next->q_ptr == NULL);
619ff550d0eSmasputra 
620ff550d0eSmasputra 	return (_RD(q)->q_next);
621ff550d0eSmasputra }
622ff550d0eSmasputra 
623ff550d0eSmasputra #ifdef DEBUG
624ff550d0eSmasputra #define	UDP_MODE_ASSERTIONS(udp, caller) udp_mode_assertions(udp, caller)
625ff550d0eSmasputra #else
626ff550d0eSmasputra #define	UDP_MODE_ASSERTIONS(udp, caller)
627ff550d0eSmasputra #endif
628ff550d0eSmasputra 
629ff550d0eSmasputra /* Invariants */
630ff550d0eSmasputra #ifdef DEBUG
631ff550d0eSmasputra 
632ff550d0eSmasputra uint32_t udp_count[4];
633ff550d0eSmasputra 
634ff550d0eSmasputra /* Context of udp_mode_assertions */
635ff550d0eSmasputra #define	UDP_ENTER		1
636ff550d0eSmasputra #define	UDP_BECOME_WRITER	2
637ff550d0eSmasputra #define	UDP_EXIT		3
638ff550d0eSmasputra 
639ff550d0eSmasputra static void
640ff550d0eSmasputra udp_mode_assertions(udp_t *udp, int caller)
641ff550d0eSmasputra {
642ff550d0eSmasputra 	ASSERT(MUTEX_HELD(&udp->udp_connp->conn_lock));
643ff550d0eSmasputra 
644ff550d0eSmasputra 	switch (udp->udp_mode) {
645ff550d0eSmasputra 	case UDP_MT_HOT:
646ff550d0eSmasputra 		/*
647ff550d0eSmasputra 		 * Messages have not yet been enqueued on the internal queue,
648ff550d0eSmasputra 		 * otherwise we would have switched to UDP_MT_QUEUED. Likewise
649ff550d0eSmasputra 		 * by definition, there can't be any messages enqueued on the
650ff550d0eSmasputra 		 * squeue. The UDP could be quiescent, so udp_reader_count
651ff550d0eSmasputra 		 * could be zero at entry.
652ff550d0eSmasputra 		 */
653ff550d0eSmasputra 		ASSERT(udp->udp_mphead == NULL && udp->udp_mpcount == 0 &&
654ff550d0eSmasputra 		    udp->udp_squeue_count == 0);
655ff550d0eSmasputra 		ASSERT(caller == UDP_ENTER || udp->udp_reader_count != 0);
656ff550d0eSmasputra 		udp_count[0]++;
657ff550d0eSmasputra 		break;
658ff550d0eSmasputra 
659ff550d0eSmasputra 	case UDP_MT_QUEUED:
660ff550d0eSmasputra 		/*
661ff550d0eSmasputra 		 * The last MT thread to exit the udp perimeter empties the
662ff550d0eSmasputra 		 * internal queue and then switches the UDP to
663ff550d0eSmasputra 		 * UDP_QUEUED_SQUEUE mode. Since we are still in UDP_MT_QUEUED
664ff550d0eSmasputra 		 * mode, it means there must be at least 1 MT thread still in
665ff550d0eSmasputra 		 * the perimeter and at least 1 message on the internal queue.
666ff550d0eSmasputra 		 */
667ff550d0eSmasputra 		ASSERT(udp->udp_reader_count >= 1 && udp->udp_mphead != NULL &&
668ff550d0eSmasputra 		    udp->udp_mpcount != 0 && udp->udp_squeue_count == 0);
669ff550d0eSmasputra 		udp_count[1]++;
670ff550d0eSmasputra 		break;
671ff550d0eSmasputra 
672ff550d0eSmasputra 	case UDP_QUEUED_SQUEUE:
673ff550d0eSmasputra 		/*
674ff550d0eSmasputra 		 * The switch has happened from MT to SQUEUE. So there can't
675ff550d0eSmasputra 		 * any MT threads. Messages could still pile up on the internal
676ff550d0eSmasputra 		 * queue until the transition is complete and we move to
677ff550d0eSmasputra 		 * UDP_SQUEUE mode. We can't assert on nonzero udp_squeue_count
678ff550d0eSmasputra 		 * since the squeue could drain any time.
679ff550d0eSmasputra 		 */
680ff550d0eSmasputra 		ASSERT(udp->udp_reader_count == 0);
681ff550d0eSmasputra 		udp_count[2]++;
682ff550d0eSmasputra 		break;
683ff550d0eSmasputra 
684ff550d0eSmasputra 	case UDP_SQUEUE:
685ff550d0eSmasputra 		/*
686ff550d0eSmasputra 		 * The transition is complete. Thre can't be any messages on
687ff550d0eSmasputra 		 * the internal queue. The udp could be quiescent or the squeue
688ff550d0eSmasputra 		 * could drain any time, so we can't assert on nonzero
689ff550d0eSmasputra 		 * udp_squeue_count during entry. Nor can we assert that
690ff550d0eSmasputra 		 * udp_reader_count is zero, since, a reader thread could have
691ff550d0eSmasputra 		 * directly become writer in line by calling udp_become_writer
692ff550d0eSmasputra 		 * without going through the queued states.
693ff550d0eSmasputra 		 */
694ff550d0eSmasputra 		ASSERT(udp->udp_mphead == NULL && udp->udp_mpcount == 0);
695ff550d0eSmasputra 		ASSERT(caller == UDP_ENTER || udp->udp_squeue_count != 0);
696ff550d0eSmasputra 		udp_count[3]++;
697ff550d0eSmasputra 		break;
698ff550d0eSmasputra 	}
699ff550d0eSmasputra }
700ff550d0eSmasputra #endif
701ff550d0eSmasputra 
702ff550d0eSmasputra #define	_UDP_ENTER(connp, mp, proc, tag) {				\
703ff550d0eSmasputra 	udp_t *_udp = (connp)->conn_udp;				\
704ff550d0eSmasputra 									\
705ff550d0eSmasputra 	mutex_enter(&(connp)->conn_lock);				\
706ff550d0eSmasputra 	if ((connp)->conn_state_flags & CONN_CLOSING) {			\
707ff550d0eSmasputra 		mutex_exit(&(connp)->conn_lock);			\
708ff550d0eSmasputra 		freemsg(mp);						\
709ff550d0eSmasputra 	} else {							\
710ff550d0eSmasputra 		UDP_MODE_ASSERTIONS(_udp, UDP_ENTER);			\
711ff550d0eSmasputra 									\
712ff550d0eSmasputra 		switch (_udp->udp_mode) {				\
713ff550d0eSmasputra 		case UDP_MT_HOT:					\
714ff550d0eSmasputra 			/* We can execute as reader right away. */	\
715ff550d0eSmasputra 			UDP_READERS_INCREF(_udp);			\
716ff550d0eSmasputra 			mutex_exit(&(connp)->conn_lock);		\
717ff550d0eSmasputra 			(*(proc))(connp, mp, (connp)->conn_sqp);	\
718ff550d0eSmasputra 			break;						\
719ff550d0eSmasputra 									\
720ff550d0eSmasputra 		case UDP_SQUEUE:					\
721ff550d0eSmasputra 			/*						\
722ff550d0eSmasputra 			 * We are in squeue mode, send the		\
723ff550d0eSmasputra 			 * packet to the squeue				\
724ff550d0eSmasputra 			 */						\
725ff550d0eSmasputra 			_udp->udp_squeue_count++;			\
726ff550d0eSmasputra 			CONN_INC_REF_LOCKED(connp);			\
727ff550d0eSmasputra 			mutex_exit(&(connp)->conn_lock);		\
728ff550d0eSmasputra 			squeue_enter((connp)->conn_sqp, mp, proc,	\
729ff550d0eSmasputra 			    connp, tag);				\
730ff550d0eSmasputra 			break;						\
731ff550d0eSmasputra 									\
732ff550d0eSmasputra 		case UDP_MT_QUEUED:					\
733ff550d0eSmasputra 		case UDP_QUEUED_SQUEUE:					\
734ff550d0eSmasputra 			/*						\
735ff550d0eSmasputra 			 * Some messages may have been enqueued		\
736ff550d0eSmasputra 			 * ahead of us.  Enqueue the new message	\
737ff550d0eSmasputra 			 * at the tail of the internal queue to		\
738ff550d0eSmasputra 			 * preserve message ordering.			\
739ff550d0eSmasputra 			 */						\
740ff550d0eSmasputra 			UDP_ENQUEUE_MP(_udp, mp, proc, tag);		\
741ff550d0eSmasputra 			mutex_exit(&(connp)->conn_lock);		\
742ff550d0eSmasputra 			break;						\
743ff550d0eSmasputra 		}							\
744ff550d0eSmasputra 	}								\
745ff550d0eSmasputra }
746ff550d0eSmasputra 
747ff550d0eSmasputra static void
748ff550d0eSmasputra udp_enter(conn_t *connp, mblk_t *mp, sqproc_t proc, uint8_t tag)
749ff550d0eSmasputra {
750ff550d0eSmasputra 	_UDP_ENTER(connp, mp, proc, tag);
751ff550d0eSmasputra }
752ff550d0eSmasputra 
753ff550d0eSmasputra static void
754ff550d0eSmasputra udp_become_writer(conn_t *connp, mblk_t *mp, sqproc_t proc, uint8_t tag)
755ff550d0eSmasputra {
756ff550d0eSmasputra 	udp_t	*udp;
757ff550d0eSmasputra 
758ff550d0eSmasputra 	udp = connp->conn_udp;
759ff550d0eSmasputra 
760ff550d0eSmasputra 	mutex_enter(&connp->conn_lock);
761ff550d0eSmasputra 
762ff550d0eSmasputra 	UDP_MODE_ASSERTIONS(udp, UDP_BECOME_WRITER);
763ff550d0eSmasputra 
764ff550d0eSmasputra 	switch (udp->udp_mode) {
765ff550d0eSmasputra 	case UDP_MT_HOT:
766ff550d0eSmasputra 		if (udp->udp_reader_count == 1) {
767ff550d0eSmasputra 			/*
768ff550d0eSmasputra 			 * We are the only MT thread. Switch to squeue mode
769ff550d0eSmasputra 			 * immediately.
770ff550d0eSmasputra 			 */
771ff550d0eSmasputra 			udp->udp_mode = UDP_SQUEUE;
772ff550d0eSmasputra 			udp->udp_squeue_count = 1;
773ff550d0eSmasputra 			CONN_INC_REF_LOCKED(connp);
774ff550d0eSmasputra 			mutex_exit(&connp->conn_lock);
775ff550d0eSmasputra 			squeue_enter(connp->conn_sqp, mp, proc, connp, tag);
776ff550d0eSmasputra 			return;
777ff550d0eSmasputra 		}
778ff550d0eSmasputra 		/* FALLTHRU */
779ff550d0eSmasputra 
780ff550d0eSmasputra 	case UDP_MT_QUEUED:
781ff550d0eSmasputra 		/* Enqueue the packet internally in UDP */
782ff550d0eSmasputra 		udp->udp_mode = UDP_MT_QUEUED;
783ff550d0eSmasputra 		UDP_ENQUEUE_MP(udp, mp, proc, tag);
784ff550d0eSmasputra 		mutex_exit(&connp->conn_lock);
785ff550d0eSmasputra 		return;
786ff550d0eSmasputra 
787ff550d0eSmasputra 	case UDP_SQUEUE:
788ff550d0eSmasputra 	case UDP_QUEUED_SQUEUE:
789ff550d0eSmasputra 		/*
790ff550d0eSmasputra 		 * We are already exclusive. i.e. we are already
791ff550d0eSmasputra 		 * writer. Simply call the desired function.
792ff550d0eSmasputra 		 */
793ff550d0eSmasputra 		udp->udp_squeue_count++;
794ff550d0eSmasputra 		mutex_exit(&connp->conn_lock);
795ff550d0eSmasputra 		(*proc)(connp, mp, connp->conn_sqp);
796ff550d0eSmasputra 		return;
797ff550d0eSmasputra 	}
798ff550d0eSmasputra }
799ff550d0eSmasputra 
800ff550d0eSmasputra /*
801ff550d0eSmasputra  * Transition from MT mode to SQUEUE mode, when the last MT thread
802ff550d0eSmasputra  * is exiting the UDP perimeter. Move all messages from the internal
803ff550d0eSmasputra  * udp queue to the squeue. A better way would be to move all the
804ff550d0eSmasputra  * messages in one shot, this needs more support from the squeue framework
805ff550d0eSmasputra  */
806ff550d0eSmasputra static void
807ff550d0eSmasputra udp_switch_to_squeue(udp_t *udp)
808ff550d0eSmasputra {
809ff550d0eSmasputra 	mblk_t *mp;
810ff550d0eSmasputra 	mblk_t	*mp_next;
811ff550d0eSmasputra 	sqproc_t proc;
812ff550d0eSmasputra 	uint8_t	tag;
813ff550d0eSmasputra 	conn_t	*connp = udp->udp_connp;
814ff550d0eSmasputra 
815ff550d0eSmasputra 	ASSERT(MUTEX_HELD(&connp->conn_lock));
816ff550d0eSmasputra 	ASSERT(udp->udp_mode == UDP_MT_QUEUED);
817ff550d0eSmasputra 	while (udp->udp_mphead != NULL) {
818ff550d0eSmasputra 		mp = udp->udp_mphead;
819ff550d0eSmasputra 		udp->udp_mphead = NULL;
820ff550d0eSmasputra 		udp->udp_mptail = NULL;
821ff550d0eSmasputra 		udp->udp_mpcount = 0;
822ff550d0eSmasputra 		udp->udp_mode = UDP_QUEUED_SQUEUE;
823ff550d0eSmasputra 		mutex_exit(&connp->conn_lock);
824ff550d0eSmasputra 		/*
825ff550d0eSmasputra 		 * It is best not to hold any locks across the calls
826ff550d0eSmasputra 		 * to squeue functions. Since we drop the lock we
827ff550d0eSmasputra 		 * need to go back and check the udp_mphead once again
828ff550d0eSmasputra 		 * after the squeue_fill and hence the while loop at
829ff550d0eSmasputra 		 * the top of this function
830ff550d0eSmasputra 		 */
831ff550d0eSmasputra 		for (; mp != NULL; mp = mp_next) {
832ff550d0eSmasputra 			mp_next = mp->b_next;
833ff550d0eSmasputra 			proc = (sqproc_t)mp->b_prev;
834ff550d0eSmasputra 			tag = (uint8_t)((uintptr_t)mp->b_queue);
835ff550d0eSmasputra 			mp->b_next = NULL;
836ff550d0eSmasputra 			mp->b_prev = NULL;
837ff550d0eSmasputra 			mp->b_queue = NULL;
838ff550d0eSmasputra 			CONN_INC_REF(connp);
839ff550d0eSmasputra 			udp->udp_squeue_count++;
840ff550d0eSmasputra 			squeue_fill(connp->conn_sqp, mp, proc, connp,
841ff550d0eSmasputra 			    tag);
842ff550d0eSmasputra 		}
843ff550d0eSmasputra 		mutex_enter(&connp->conn_lock);
844ff550d0eSmasputra 	}
845ff550d0eSmasputra 	/*
846ff550d0eSmasputra 	 * udp_squeue_count of zero implies that the squeue has drained
847ff550d0eSmasputra 	 * even before we arrived here (i.e. after the squeue_fill above)
848ff550d0eSmasputra 	 */
849ff550d0eSmasputra 	udp->udp_mode = (udp->udp_squeue_count != 0) ?
850ff550d0eSmasputra 	    UDP_SQUEUE : UDP_MT_HOT;
851ff550d0eSmasputra }
852ff550d0eSmasputra 
853ff550d0eSmasputra #define	_UDP_EXIT(connp) {						\
854ff550d0eSmasputra 	udp_t *_udp = (connp)->conn_udp;				\
855ff550d0eSmasputra 									\
856ff550d0eSmasputra 	mutex_enter(&(connp)->conn_lock);				\
857ff550d0eSmasputra 	UDP_MODE_ASSERTIONS(_udp, UDP_EXIT);				\
858ff550d0eSmasputra 									\
859ff550d0eSmasputra 	switch (_udp->udp_mode) {					\
860ff550d0eSmasputra 	case UDP_MT_HOT:						\
861ff550d0eSmasputra 		UDP_READERS_DECREF(_udp);				\
862ff550d0eSmasputra 		mutex_exit(&(connp)->conn_lock);			\
863ff550d0eSmasputra 		break;							\
864ff550d0eSmasputra 									\
865ff550d0eSmasputra 	case UDP_SQUEUE:						\
866ff550d0eSmasputra 		UDP_SQUEUE_DECREF(_udp);				\
867ff550d0eSmasputra 		if (_udp->udp_squeue_count == 0)			\
868ff550d0eSmasputra 		    _udp->udp_mode = UDP_MT_HOT;			\
869ff550d0eSmasputra 		mutex_exit(&(connp)->conn_lock);			\
870ff550d0eSmasputra 		break;							\
871ff550d0eSmasputra 									\
872ff550d0eSmasputra 	case UDP_MT_QUEUED:						\
873ff550d0eSmasputra 		/*							\
874ff550d0eSmasputra 		 * If this is the last MT thread, we need to		\
875ff550d0eSmasputra 		 * switch to squeue mode				\
876ff550d0eSmasputra 		 */							\
877ff550d0eSmasputra 		UDP_READERS_DECREF(_udp);				\
878ff550d0eSmasputra 		if (_udp->udp_reader_count == 0)			\
879ff550d0eSmasputra 			udp_switch_to_squeue(_udp);			\
880ff550d0eSmasputra 		mutex_exit(&(connp)->conn_lock);			\
881ff550d0eSmasputra 		break;							\
882ff550d0eSmasputra 									\
883ff550d0eSmasputra 	case UDP_QUEUED_SQUEUE:						\
884ff550d0eSmasputra 		UDP_SQUEUE_DECREF(_udp);				\
885ff550d0eSmasputra 		/*							\
886ff550d0eSmasputra 		 * Even if the udp_squeue_count drops to zero, we	\
887ff550d0eSmasputra 		 * don't want to change udp_mode to UDP_MT_HOT here.	\
888ff550d0eSmasputra 		 * The thread in udp_switch_to_squeue will take care	\
889ff550d0eSmasputra 		 * of the transition to UDP_MT_HOT, after emptying	\
890ff550d0eSmasputra 		 * any more new messages that have been enqueued in	\
891ff550d0eSmasputra 		 * udp_mphead.						\
892ff550d0eSmasputra 		 */							\
893ff550d0eSmasputra 		mutex_exit(&(connp)->conn_lock);			\
894ff550d0eSmasputra 		break;							\
895ff550d0eSmasputra 	}								\
896ff550d0eSmasputra }
897ff550d0eSmasputra 
898ff550d0eSmasputra static void
899ff550d0eSmasputra udp_exit(conn_t *connp)
900ff550d0eSmasputra {
901ff550d0eSmasputra 	_UDP_EXIT(connp);
902ff550d0eSmasputra }
9037c478bd9Sstevel@tonic-gate 
9047c478bd9Sstevel@tonic-gate /*
9057c478bd9Sstevel@tonic-gate  * Return the next anonymous port in the priviledged port range for
9067c478bd9Sstevel@tonic-gate  * bind checking.
9077c478bd9Sstevel@tonic-gate  */
9087c478bd9Sstevel@tonic-gate static in_port_t
9097c478bd9Sstevel@tonic-gate udp_get_next_priv_port(void)
9107c478bd9Sstevel@tonic-gate {
9117c478bd9Sstevel@tonic-gate 	static in_port_t next_priv_port = IPPORT_RESERVED - 1;
9127c478bd9Sstevel@tonic-gate 
9137c478bd9Sstevel@tonic-gate 	if (next_priv_port < udp_min_anonpriv_port) {
9147c478bd9Sstevel@tonic-gate 		next_priv_port = IPPORT_RESERVED - 1;
9157c478bd9Sstevel@tonic-gate 	}
9167c478bd9Sstevel@tonic-gate 	return (next_priv_port--);
9177c478bd9Sstevel@tonic-gate }
9187c478bd9Sstevel@tonic-gate 
9197c478bd9Sstevel@tonic-gate /* UDP bind hash report triggered via the Named Dispatch mechanism. */
9207c478bd9Sstevel@tonic-gate /* ARGSUSED */
9217c478bd9Sstevel@tonic-gate static int
9227c478bd9Sstevel@tonic-gate udp_bind_hash_report(queue_t *q, mblk_t *mp, caddr_t cp, cred_t *cr)
9237c478bd9Sstevel@tonic-gate {
9247c478bd9Sstevel@tonic-gate 	udp_fanout_t	*udpf;
9257c478bd9Sstevel@tonic-gate 	int		i;
9267c478bd9Sstevel@tonic-gate 	zoneid_t	zoneid;
927ff550d0eSmasputra 	conn_t		*connp;
928ff550d0eSmasputra 	udp_t		*udp;
929ff550d0eSmasputra 
930ff550d0eSmasputra 	connp = Q_TO_CONN(q);
931ff550d0eSmasputra 	udp = connp->conn_udp;
9327c478bd9Sstevel@tonic-gate 
9337c478bd9Sstevel@tonic-gate 	/* Refer to comments in udp_status_report(). */
9347c478bd9Sstevel@tonic-gate 	if (cr == NULL || secpolicy_net_config(cr, B_TRUE) != 0) {
9357c478bd9Sstevel@tonic-gate 		if (ddi_get_lbolt() - udp_last_ndd_get_info_time <
9367c478bd9Sstevel@tonic-gate 		    drv_usectohz(udp_ndd_get_info_interval * 1000)) {
9377c478bd9Sstevel@tonic-gate 			(void) mi_mpprintf(mp, NDD_TOO_QUICK_MSG);
9387c478bd9Sstevel@tonic-gate 			return (0);
9397c478bd9Sstevel@tonic-gate 		}
9407c478bd9Sstevel@tonic-gate 	}
9417c478bd9Sstevel@tonic-gate 	if ((mp->b_cont = allocb(ND_MAX_BUF_LEN, BPRI_HI)) == NULL) {
9427c478bd9Sstevel@tonic-gate 		/* The following may work even if we cannot get a large buf. */
9437c478bd9Sstevel@tonic-gate 		(void) mi_mpprintf(mp, NDD_OUT_OF_BUF_MSG);
9447c478bd9Sstevel@tonic-gate 		return (0);
9457c478bd9Sstevel@tonic-gate 	}
9467c478bd9Sstevel@tonic-gate 
9477c478bd9Sstevel@tonic-gate 	(void) mi_mpprintf(mp,
9487c478bd9Sstevel@tonic-gate 	    "UDP     " MI_COL_HDRPAD_STR
9497c478bd9Sstevel@tonic-gate 	/*   12345678[89ABCDEF] */
9507c478bd9Sstevel@tonic-gate 	    " zone lport src addr        dest addr       port  state");
9517c478bd9Sstevel@tonic-gate 	/*    1234 12345 xxx.xxx.xxx.xxx xxx.xxx.xxx.xxx 12345 UNBOUND */
9527c478bd9Sstevel@tonic-gate 
953ff550d0eSmasputra 	zoneid = connp->conn_zoneid;
9547c478bd9Sstevel@tonic-gate 
9557c478bd9Sstevel@tonic-gate 	for (i = 0; i < udp_bind_fanout_size; i++) {
9567c478bd9Sstevel@tonic-gate 		udpf = &udp_bind_fanout[i];
9577c478bd9Sstevel@tonic-gate 		mutex_enter(&udpf->uf_lock);
9587c478bd9Sstevel@tonic-gate 
9597c478bd9Sstevel@tonic-gate 		/* Print the hash index. */
9607c478bd9Sstevel@tonic-gate 		udp = udpf->uf_udp;
9617c478bd9Sstevel@tonic-gate 		if (zoneid != GLOBAL_ZONEID) {
9627c478bd9Sstevel@tonic-gate 			/* skip to first entry in this zone; might be none */
9637c478bd9Sstevel@tonic-gate 			while (udp != NULL &&
964ff550d0eSmasputra 			    udp->udp_connp->conn_zoneid != zoneid)
9657c478bd9Sstevel@tonic-gate 				udp = udp->udp_bind_hash;
9667c478bd9Sstevel@tonic-gate 		}
9677c478bd9Sstevel@tonic-gate 		if (udp != NULL) {
9687c478bd9Sstevel@tonic-gate 			uint_t print_len, buf_len;
9697c478bd9Sstevel@tonic-gate 
9707c478bd9Sstevel@tonic-gate 			buf_len = mp->b_cont->b_datap->db_lim -
9717c478bd9Sstevel@tonic-gate 			    mp->b_cont->b_wptr;
9727c478bd9Sstevel@tonic-gate 			print_len = snprintf((char *)mp->b_cont->b_wptr,
9737c478bd9Sstevel@tonic-gate 			    buf_len, "%d\n", i);
9747c478bd9Sstevel@tonic-gate 			if (print_len < buf_len) {
9757c478bd9Sstevel@tonic-gate 				mp->b_cont->b_wptr += print_len;
9767c478bd9Sstevel@tonic-gate 			} else {
9777c478bd9Sstevel@tonic-gate 				mp->b_cont->b_wptr += buf_len;
9787c478bd9Sstevel@tonic-gate 			}
9797c478bd9Sstevel@tonic-gate 			for (; udp != NULL; udp = udp->udp_bind_hash) {
9807c478bd9Sstevel@tonic-gate 				if (zoneid == GLOBAL_ZONEID ||
981ff550d0eSmasputra 				    zoneid == udp->udp_connp->conn_zoneid)
9827c478bd9Sstevel@tonic-gate 					udp_report_item(mp->b_cont, udp);
9837c478bd9Sstevel@tonic-gate 			}
9847c478bd9Sstevel@tonic-gate 		}
9857c478bd9Sstevel@tonic-gate 		mutex_exit(&udpf->uf_lock);
9867c478bd9Sstevel@tonic-gate 	}
9877c478bd9Sstevel@tonic-gate 	udp_last_ndd_get_info_time = ddi_get_lbolt();
9887c478bd9Sstevel@tonic-gate 	return (0);
9897c478bd9Sstevel@tonic-gate }
9907c478bd9Sstevel@tonic-gate 
9917c478bd9Sstevel@tonic-gate /*
9927c478bd9Sstevel@tonic-gate  * Hash list removal routine for udp_t structures.
9937c478bd9Sstevel@tonic-gate  */
9947c478bd9Sstevel@tonic-gate static void
9957c478bd9Sstevel@tonic-gate udp_bind_hash_remove(udp_t *udp, boolean_t caller_holds_lock)
9967c478bd9Sstevel@tonic-gate {
9977c478bd9Sstevel@tonic-gate 	udp_t	*udpnext;
9987c478bd9Sstevel@tonic-gate 	kmutex_t *lockp;
9997c478bd9Sstevel@tonic-gate 
10007c478bd9Sstevel@tonic-gate 	if (udp->udp_ptpbhn == NULL)
10017c478bd9Sstevel@tonic-gate 		return;
10027c478bd9Sstevel@tonic-gate 
10037c478bd9Sstevel@tonic-gate 	/*
10047c478bd9Sstevel@tonic-gate 	 * Extract the lock pointer in case there are concurrent
10057c478bd9Sstevel@tonic-gate 	 * hash_remove's for this instance.
10067c478bd9Sstevel@tonic-gate 	 */
10077c478bd9Sstevel@tonic-gate 	ASSERT(udp->udp_port != 0);
10087c478bd9Sstevel@tonic-gate 	if (!caller_holds_lock) {
10097c478bd9Sstevel@tonic-gate 		lockp = &udp_bind_fanout[UDP_BIND_HASH(udp->udp_port)].uf_lock;
10107c478bd9Sstevel@tonic-gate 		ASSERT(lockp != NULL);
10117c478bd9Sstevel@tonic-gate 		mutex_enter(lockp);
10127c478bd9Sstevel@tonic-gate 	}
10137c478bd9Sstevel@tonic-gate 	if (udp->udp_ptpbhn != NULL) {
10147c478bd9Sstevel@tonic-gate 		udpnext = udp->udp_bind_hash;
10157c478bd9Sstevel@tonic-gate 		if (udpnext != NULL) {
10167c478bd9Sstevel@tonic-gate 			udpnext->udp_ptpbhn = udp->udp_ptpbhn;
10177c478bd9Sstevel@tonic-gate 			udp->udp_bind_hash = NULL;
10187c478bd9Sstevel@tonic-gate 		}
10197c478bd9Sstevel@tonic-gate 		*udp->udp_ptpbhn = udpnext;
10207c478bd9Sstevel@tonic-gate 		udp->udp_ptpbhn = NULL;
10217c478bd9Sstevel@tonic-gate 	}
10227c478bd9Sstevel@tonic-gate 	if (!caller_holds_lock) {
10237c478bd9Sstevel@tonic-gate 		mutex_exit(lockp);
10247c478bd9Sstevel@tonic-gate 	}
10257c478bd9Sstevel@tonic-gate }
10267c478bd9Sstevel@tonic-gate 
10277c478bd9Sstevel@tonic-gate static void
10287c478bd9Sstevel@tonic-gate udp_bind_hash_insert(udp_fanout_t *uf, udp_t *udp)
10297c478bd9Sstevel@tonic-gate {
10307c478bd9Sstevel@tonic-gate 	udp_t	**udpp;
10317c478bd9Sstevel@tonic-gate 	udp_t	*udpnext;
10327c478bd9Sstevel@tonic-gate 
10337c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&uf->uf_lock));
10347c478bd9Sstevel@tonic-gate 	if (udp->udp_ptpbhn != NULL) {
10357c478bd9Sstevel@tonic-gate 		udp_bind_hash_remove(udp, B_TRUE);
10367c478bd9Sstevel@tonic-gate 	}
10377c478bd9Sstevel@tonic-gate 	udpp = &uf->uf_udp;
10387c478bd9Sstevel@tonic-gate 	udpnext = udpp[0];
10397c478bd9Sstevel@tonic-gate 	if (udpnext != NULL) {
10407c478bd9Sstevel@tonic-gate 		/*
10417c478bd9Sstevel@tonic-gate 		 * If the new udp bound to the INADDR_ANY address
10427c478bd9Sstevel@tonic-gate 		 * and the first one in the list is not bound to
10437c478bd9Sstevel@tonic-gate 		 * INADDR_ANY we skip all entries until we find the
10447c478bd9Sstevel@tonic-gate 		 * first one bound to INADDR_ANY.
10457c478bd9Sstevel@tonic-gate 		 * This makes sure that applications binding to a
10467c478bd9Sstevel@tonic-gate 		 * specific address get preference over those binding to
10477c478bd9Sstevel@tonic-gate 		 * INADDR_ANY.
10487c478bd9Sstevel@tonic-gate 		 */
10497c478bd9Sstevel@tonic-gate 		if (V6_OR_V4_INADDR_ANY(udp->udp_bound_v6src) &&
10507c478bd9Sstevel@tonic-gate 		    !V6_OR_V4_INADDR_ANY(udpnext->udp_bound_v6src)) {
10517c478bd9Sstevel@tonic-gate 			while ((udpnext = udpp[0]) != NULL &&
10527c478bd9Sstevel@tonic-gate 			    !V6_OR_V4_INADDR_ANY(
10537c478bd9Sstevel@tonic-gate 			    udpnext->udp_bound_v6src)) {
10547c478bd9Sstevel@tonic-gate 				udpp = &(udpnext->udp_bind_hash);
10557c478bd9Sstevel@tonic-gate 			}
10567c478bd9Sstevel@tonic-gate 			if (udpnext != NULL)
10577c478bd9Sstevel@tonic-gate 				udpnext->udp_ptpbhn = &udp->udp_bind_hash;
10587c478bd9Sstevel@tonic-gate 		} else {
10597c478bd9Sstevel@tonic-gate 			udpnext->udp_ptpbhn = &udp->udp_bind_hash;
10607c478bd9Sstevel@tonic-gate 		}
10617c478bd9Sstevel@tonic-gate 	}
10627c478bd9Sstevel@tonic-gate 	udp->udp_bind_hash = udpnext;
10637c478bd9Sstevel@tonic-gate 	udp->udp_ptpbhn = udpp;
10647c478bd9Sstevel@tonic-gate 	udpp[0] = udp;
10657c478bd9Sstevel@tonic-gate }
10667c478bd9Sstevel@tonic-gate 
10677c478bd9Sstevel@tonic-gate /*
10687c478bd9Sstevel@tonic-gate  * This routine is called to handle each O_T_BIND_REQ/T_BIND_REQ message
10697c478bd9Sstevel@tonic-gate  * passed to udp_wput.
10707c478bd9Sstevel@tonic-gate  * It associates a port number and local address with the stream.
10717c478bd9Sstevel@tonic-gate  * The O_T_BIND_REQ/T_BIND_REQ is passed downstream to ip with the UDP
10727c478bd9Sstevel@tonic-gate  * protocol type (IPPROTO_UDP) placed in the message following the address.
10737c478bd9Sstevel@tonic-gate  * A T_BIND_ACK message is passed upstream when ip acknowledges the request.
10747c478bd9Sstevel@tonic-gate  * (Called as writer.)
10757c478bd9Sstevel@tonic-gate  *
10767c478bd9Sstevel@tonic-gate  * Note that UDP over IPv4 and IPv6 sockets can use the same port number
10777c478bd9Sstevel@tonic-gate  * without setting SO_REUSEADDR. This is needed so that they
10787c478bd9Sstevel@tonic-gate  * can be viewed as two independent transport protocols.
10797c478bd9Sstevel@tonic-gate  * However, anonymouns ports are allocated from the same range to avoid
10807c478bd9Sstevel@tonic-gate  * duplicating the udp_g_next_port_to_try.
10817c478bd9Sstevel@tonic-gate  */
10827c478bd9Sstevel@tonic-gate static void
10837c478bd9Sstevel@tonic-gate udp_bind(queue_t *q, mblk_t *mp)
10847c478bd9Sstevel@tonic-gate {
10857c478bd9Sstevel@tonic-gate 	sin_t		*sin;
10867c478bd9Sstevel@tonic-gate 	sin6_t		*sin6;
10877c478bd9Sstevel@tonic-gate 	mblk_t		*mp1;
10887c478bd9Sstevel@tonic-gate 	in_port_t	port;		/* Host byte order */
10897c478bd9Sstevel@tonic-gate 	in_port_t	requested_port;	/* Host byte order */
10907c478bd9Sstevel@tonic-gate 	struct T_bind_req *tbr;
10917c478bd9Sstevel@tonic-gate 	int		count;
10927c478bd9Sstevel@tonic-gate 	in6_addr_t	v6src;
10937c478bd9Sstevel@tonic-gate 	boolean_t	bind_to_req_port_only;
10947c478bd9Sstevel@tonic-gate 	int		loopmax;
10957c478bd9Sstevel@tonic-gate 	udp_fanout_t	*udpf;
10967c478bd9Sstevel@tonic-gate 	in_port_t	lport;		/* Network byte order */
10977c478bd9Sstevel@tonic-gate 	zoneid_t	zoneid;
1098ff550d0eSmasputra 	conn_t		*connp;
1099ff550d0eSmasputra 	udp_t		*udp;
11007c478bd9Sstevel@tonic-gate 
1101ff550d0eSmasputra 	connp = Q_TO_CONN(q);
1102ff550d0eSmasputra 	udp = connp->conn_udp;
11037c478bd9Sstevel@tonic-gate 	if ((mp->b_wptr - mp->b_rptr) < sizeof (*tbr)) {
11047c478bd9Sstevel@tonic-gate 		(void) mi_strlog(q, 1, SL_ERROR|SL_TRACE,
11057c478bd9Sstevel@tonic-gate 		    "udp_bind: bad req, len %u",
11067c478bd9Sstevel@tonic-gate 		    (uint_t)(mp->b_wptr - mp->b_rptr));
11077c478bd9Sstevel@tonic-gate 		udp_err_ack(q, mp, TPROTO, 0);
11087c478bd9Sstevel@tonic-gate 		return;
11097c478bd9Sstevel@tonic-gate 	}
1110ff550d0eSmasputra 
11117c478bd9Sstevel@tonic-gate 	if (udp->udp_state != TS_UNBND) {
11127c478bd9Sstevel@tonic-gate 		(void) mi_strlog(q, 1, SL_ERROR|SL_TRACE,
11137c478bd9Sstevel@tonic-gate 		    "udp_bind: bad state, %u", udp->udp_state);
11147c478bd9Sstevel@tonic-gate 		udp_err_ack(q, mp, TOUTSTATE, 0);
11157c478bd9Sstevel@tonic-gate 		return;
11167c478bd9Sstevel@tonic-gate 	}
11177c478bd9Sstevel@tonic-gate 	/*
11187c478bd9Sstevel@tonic-gate 	 * Reallocate the message to make sure we have enough room for an
11197c478bd9Sstevel@tonic-gate 	 * address and the protocol type.
11207c478bd9Sstevel@tonic-gate 	 */
11217c478bd9Sstevel@tonic-gate 	mp1 = reallocb(mp, sizeof (struct T_bind_ack) + sizeof (sin6_t) + 1, 1);
11227c478bd9Sstevel@tonic-gate 	if (!mp1) {
11237c478bd9Sstevel@tonic-gate 		udp_err_ack(q, mp, TSYSERR, ENOMEM);
11247c478bd9Sstevel@tonic-gate 		return;
11257c478bd9Sstevel@tonic-gate 	}
11267c478bd9Sstevel@tonic-gate 
11277c478bd9Sstevel@tonic-gate 	mp = mp1;
11287c478bd9Sstevel@tonic-gate 	tbr = (struct T_bind_req *)mp->b_rptr;
11297c478bd9Sstevel@tonic-gate 	switch (tbr->ADDR_length) {
11307c478bd9Sstevel@tonic-gate 	case 0:			/* Request for a generic port */
11317c478bd9Sstevel@tonic-gate 		tbr->ADDR_offset = sizeof (struct T_bind_req);
11327c478bd9Sstevel@tonic-gate 		if (udp->udp_family == AF_INET) {
11337c478bd9Sstevel@tonic-gate 			tbr->ADDR_length = sizeof (sin_t);
11347c478bd9Sstevel@tonic-gate 			sin = (sin_t *)&tbr[1];
11357c478bd9Sstevel@tonic-gate 			*sin = sin_null;
11367c478bd9Sstevel@tonic-gate 			sin->sin_family = AF_INET;
11377c478bd9Sstevel@tonic-gate 			mp->b_wptr = (uchar_t *)&sin[1];
11387c478bd9Sstevel@tonic-gate 		} else {
11397c478bd9Sstevel@tonic-gate 			ASSERT(udp->udp_family == AF_INET6);
11407c478bd9Sstevel@tonic-gate 			tbr->ADDR_length = sizeof (sin6_t);
11417c478bd9Sstevel@tonic-gate 			sin6 = (sin6_t *)&tbr[1];
11427c478bd9Sstevel@tonic-gate 			*sin6 = sin6_null;
11437c478bd9Sstevel@tonic-gate 			sin6->sin6_family = AF_INET6;
11447c478bd9Sstevel@tonic-gate 			mp->b_wptr = (uchar_t *)&sin6[1];
11457c478bd9Sstevel@tonic-gate 		}
11467c478bd9Sstevel@tonic-gate 		port = 0;
11477c478bd9Sstevel@tonic-gate 		break;
11487c478bd9Sstevel@tonic-gate 
11497c478bd9Sstevel@tonic-gate 	case sizeof (sin_t):	/* Complete IPv4 address */
11507c478bd9Sstevel@tonic-gate 		sin = (sin_t *)mi_offset_param(mp, tbr->ADDR_offset,
11517c478bd9Sstevel@tonic-gate 		    sizeof (sin_t));
11527c478bd9Sstevel@tonic-gate 		if (sin == NULL || !OK_32PTR((char *)sin)) {
11537c478bd9Sstevel@tonic-gate 			udp_err_ack(q, mp, TSYSERR, EINVAL);
11547c478bd9Sstevel@tonic-gate 			return;
11557c478bd9Sstevel@tonic-gate 		}
11567c478bd9Sstevel@tonic-gate 		if (udp->udp_family != AF_INET ||
11577c478bd9Sstevel@tonic-gate 		    sin->sin_family != AF_INET) {
11587c478bd9Sstevel@tonic-gate 			udp_err_ack(q, mp, TSYSERR, EAFNOSUPPORT);
11597c478bd9Sstevel@tonic-gate 			return;
11607c478bd9Sstevel@tonic-gate 		}
11617c478bd9Sstevel@tonic-gate 		port = ntohs(sin->sin_port);
11627c478bd9Sstevel@tonic-gate 		break;
11637c478bd9Sstevel@tonic-gate 
11647c478bd9Sstevel@tonic-gate 	case sizeof (sin6_t):	/* complete IPv6 address */
11657c478bd9Sstevel@tonic-gate 		sin6 = (sin6_t *)mi_offset_param(mp, tbr->ADDR_offset,
11667c478bd9Sstevel@tonic-gate 		    sizeof (sin6_t));
11677c478bd9Sstevel@tonic-gate 		if (sin6 == NULL || !OK_32PTR((char *)sin6)) {
11687c478bd9Sstevel@tonic-gate 			udp_err_ack(q, mp, TSYSERR, EINVAL);
11697c478bd9Sstevel@tonic-gate 			return;
11707c478bd9Sstevel@tonic-gate 		}
11717c478bd9Sstevel@tonic-gate 		if (udp->udp_family != AF_INET6 ||
11727c478bd9Sstevel@tonic-gate 		    sin6->sin6_family != AF_INET6) {
11737c478bd9Sstevel@tonic-gate 			udp_err_ack(q, mp, TSYSERR, EAFNOSUPPORT);
11747c478bd9Sstevel@tonic-gate 			return;
11757c478bd9Sstevel@tonic-gate 		}
11767c478bd9Sstevel@tonic-gate 		port = ntohs(sin6->sin6_port);
11777c478bd9Sstevel@tonic-gate 		break;
11787c478bd9Sstevel@tonic-gate 
11797c478bd9Sstevel@tonic-gate 	default:		/* Invalid request */
11807c478bd9Sstevel@tonic-gate 		(void) mi_strlog(q, 1, SL_ERROR|SL_TRACE,
11817c478bd9Sstevel@tonic-gate 		    "udp_bind: bad ADDR_length length %u", tbr->ADDR_length);
11827c478bd9Sstevel@tonic-gate 		udp_err_ack(q, mp, TBADADDR, 0);
11837c478bd9Sstevel@tonic-gate 		return;
11847c478bd9Sstevel@tonic-gate 	}
11857c478bd9Sstevel@tonic-gate 
11867c478bd9Sstevel@tonic-gate 	requested_port = port;
11877c478bd9Sstevel@tonic-gate 
11887c478bd9Sstevel@tonic-gate 	if (requested_port == 0 || tbr->PRIM_type == O_T_BIND_REQ)
11897c478bd9Sstevel@tonic-gate 		bind_to_req_port_only = B_FALSE;
11907c478bd9Sstevel@tonic-gate 	else			/* T_BIND_REQ and requested_port != 0 */
11917c478bd9Sstevel@tonic-gate 		bind_to_req_port_only = B_TRUE;
11927c478bd9Sstevel@tonic-gate 
11937c478bd9Sstevel@tonic-gate 	if (requested_port == 0) {
11947c478bd9Sstevel@tonic-gate 		/*
11957c478bd9Sstevel@tonic-gate 		 * If the application passed in zero for the port number, it
11967c478bd9Sstevel@tonic-gate 		 * doesn't care which port number we bind to. Get one in the
11977c478bd9Sstevel@tonic-gate 		 * valid range.
11987c478bd9Sstevel@tonic-gate 		 */
11997c478bd9Sstevel@tonic-gate 		if (udp->udp_anon_priv_bind) {
12007c478bd9Sstevel@tonic-gate 			port = udp_get_next_priv_port();
12017c478bd9Sstevel@tonic-gate 		} else {
12027c478bd9Sstevel@tonic-gate 			port = udp_update_next_port(udp_g_next_port_to_try,
12037c478bd9Sstevel@tonic-gate 			    B_TRUE);
12047c478bd9Sstevel@tonic-gate 		}
12057c478bd9Sstevel@tonic-gate 	} else {
12067c478bd9Sstevel@tonic-gate 		/*
12077c478bd9Sstevel@tonic-gate 		 * If the port is in the well-known privileged range,
12087c478bd9Sstevel@tonic-gate 		 * make sure the caller was privileged.
12097c478bd9Sstevel@tonic-gate 		 */
12107c478bd9Sstevel@tonic-gate 		int i;
12117c478bd9Sstevel@tonic-gate 		boolean_t priv = B_FALSE;
12127c478bd9Sstevel@tonic-gate 
12137c478bd9Sstevel@tonic-gate 		if (port < udp_smallest_nonpriv_port) {
12147c478bd9Sstevel@tonic-gate 			priv = B_TRUE;
12157c478bd9Sstevel@tonic-gate 		} else {
12167c478bd9Sstevel@tonic-gate 			for (i = 0; i < udp_g_num_epriv_ports; i++) {
12177c478bd9Sstevel@tonic-gate 				if (port == udp_g_epriv_ports[i]) {
12187c478bd9Sstevel@tonic-gate 					priv = B_TRUE;
12197c478bd9Sstevel@tonic-gate 					break;
12207c478bd9Sstevel@tonic-gate 				}
12217c478bd9Sstevel@tonic-gate 			}
12227c478bd9Sstevel@tonic-gate 		}
12237c478bd9Sstevel@tonic-gate 
12247c478bd9Sstevel@tonic-gate 		if (priv) {
1225ff550d0eSmasputra 			cred_t *cr = DB_CREDDEF(mp, connp->conn_cred);
12267c478bd9Sstevel@tonic-gate 
12277c478bd9Sstevel@tonic-gate 			if (secpolicy_net_privaddr(cr, port) != 0) {
12287c478bd9Sstevel@tonic-gate 				udp_err_ack(q, mp, TACCES, 0);
12297c478bd9Sstevel@tonic-gate 				return;
12307c478bd9Sstevel@tonic-gate 			}
12317c478bd9Sstevel@tonic-gate 		}
12327c478bd9Sstevel@tonic-gate 	}
12337c478bd9Sstevel@tonic-gate 
12347c478bd9Sstevel@tonic-gate 	/*
12357c478bd9Sstevel@tonic-gate 	 * Copy the source address into our udp structure. This address
12367c478bd9Sstevel@tonic-gate 	 * may still be zero; if so, IP will fill in the correct address
12377c478bd9Sstevel@tonic-gate 	 * each time an outbound packet is passed to it.
12387c478bd9Sstevel@tonic-gate 	 */
12397c478bd9Sstevel@tonic-gate 	if (udp->udp_family == AF_INET) {
12407c478bd9Sstevel@tonic-gate 		ASSERT(sin != NULL);
12417c478bd9Sstevel@tonic-gate 		ASSERT(udp->udp_ipversion == IPV4_VERSION);
12427c478bd9Sstevel@tonic-gate 		udp->udp_max_hdr_len = IP_SIMPLE_HDR_LENGTH + UDPH_SIZE +
12437c478bd9Sstevel@tonic-gate 		    udp->udp_ip_snd_options_len;
12447c478bd9Sstevel@tonic-gate 		IN6_IPADDR_TO_V4MAPPED(sin->sin_addr.s_addr, &v6src);
12457c478bd9Sstevel@tonic-gate 	} else {
12467c478bd9Sstevel@tonic-gate 		ASSERT(sin6 != NULL);
12477c478bd9Sstevel@tonic-gate 		v6src = sin6->sin6_addr;
12487c478bd9Sstevel@tonic-gate 		if (IN6_IS_ADDR_V4MAPPED(&v6src)) {
12497c478bd9Sstevel@tonic-gate 			udp->udp_ipversion = IPV4_VERSION;
12507c478bd9Sstevel@tonic-gate 			udp->udp_max_hdr_len = IP_SIMPLE_HDR_LENGTH +
12517c478bd9Sstevel@tonic-gate 			    UDPH_SIZE + udp->udp_ip_snd_options_len;
12527c478bd9Sstevel@tonic-gate 		} else {
12537c478bd9Sstevel@tonic-gate 			udp->udp_ipversion = IPV6_VERSION;
12547c478bd9Sstevel@tonic-gate 			udp->udp_max_hdr_len = udp->udp_sticky_hdrs_len;
12557c478bd9Sstevel@tonic-gate 		}
12567c478bd9Sstevel@tonic-gate 	}
12577c478bd9Sstevel@tonic-gate 
12587c478bd9Sstevel@tonic-gate 	/*
12597c478bd9Sstevel@tonic-gate 	 * If udp_reuseaddr is not set, then we have to make sure that
12607c478bd9Sstevel@tonic-gate 	 * the IP address and port number the application requested
12617c478bd9Sstevel@tonic-gate 	 * (or we selected for the application) is not being used by
12627c478bd9Sstevel@tonic-gate 	 * another stream.  If another stream is already using the
12637c478bd9Sstevel@tonic-gate 	 * requested IP address and port, the behavior depends on
12647c478bd9Sstevel@tonic-gate 	 * "bind_to_req_port_only". If set the bind fails; otherwise we
12657c478bd9Sstevel@tonic-gate 	 * search for any an unused port to bind to the the stream.
12667c478bd9Sstevel@tonic-gate 	 *
12677c478bd9Sstevel@tonic-gate 	 * As per the BSD semantics, as modified by the Deering multicast
12687c478bd9Sstevel@tonic-gate 	 * changes, if udp_reuseaddr is set, then we allow multiple binds
12697c478bd9Sstevel@tonic-gate 	 * to the same port independent of the local IP address.
12707c478bd9Sstevel@tonic-gate 	 *
12717c478bd9Sstevel@tonic-gate 	 * This is slightly different than in SunOS 4.X which did not
12727c478bd9Sstevel@tonic-gate 	 * support IP multicast. Note that the change implemented by the
12737c478bd9Sstevel@tonic-gate 	 * Deering multicast code effects all binds - not only binding
12747c478bd9Sstevel@tonic-gate 	 * to IP multicast addresses.
12757c478bd9Sstevel@tonic-gate 	 *
12767c478bd9Sstevel@tonic-gate 	 * Note that when binding to port zero we ignore SO_REUSEADDR in
12777c478bd9Sstevel@tonic-gate 	 * order to guarantee a unique port.
12787c478bd9Sstevel@tonic-gate 	 */
12797c478bd9Sstevel@tonic-gate 
12807c478bd9Sstevel@tonic-gate 	count = 0;
12817c478bd9Sstevel@tonic-gate 	if (udp->udp_anon_priv_bind) {
12827c478bd9Sstevel@tonic-gate 		/* loopmax = (IPPORT_RESERVED-1) - udp_min_anonpriv_port + 1 */
12837c478bd9Sstevel@tonic-gate 		loopmax = IPPORT_RESERVED - udp_min_anonpriv_port;
12847c478bd9Sstevel@tonic-gate 	} else {
12857c478bd9Sstevel@tonic-gate 		loopmax = udp_largest_anon_port - udp_smallest_anon_port + 1;
12867c478bd9Sstevel@tonic-gate 	}
12877c478bd9Sstevel@tonic-gate 
1288ff550d0eSmasputra 	zoneid = connp->conn_zoneid;
12897c478bd9Sstevel@tonic-gate 	for (;;) {
12907c478bd9Sstevel@tonic-gate 		udp_t		*udp1;
12917c478bd9Sstevel@tonic-gate 		boolean_t	is_inaddr_any;
12927c478bd9Sstevel@tonic-gate 		boolean_t	found_exclbind = B_FALSE;
12937c478bd9Sstevel@tonic-gate 
12947c478bd9Sstevel@tonic-gate 		is_inaddr_any = V6_OR_V4_INADDR_ANY(v6src);
12957c478bd9Sstevel@tonic-gate 		/*
12967c478bd9Sstevel@tonic-gate 		 * Walk through the list of udp streams bound to
12977c478bd9Sstevel@tonic-gate 		 * requested port with the same IP address.
12987c478bd9Sstevel@tonic-gate 		 */
12997c478bd9Sstevel@tonic-gate 		lport = htons(port);
13007c478bd9Sstevel@tonic-gate 		udpf = &udp_bind_fanout[UDP_BIND_HASH(lport)];
13017c478bd9Sstevel@tonic-gate 		mutex_enter(&udpf->uf_lock);
13027c478bd9Sstevel@tonic-gate 		for (udp1 = udpf->uf_udp; udp1 != NULL;
13037c478bd9Sstevel@tonic-gate 		    udp1 = udp1->udp_bind_hash) {
13047c478bd9Sstevel@tonic-gate 			if (lport != udp1->udp_port ||
1305ff550d0eSmasputra 			    zoneid != udp1->udp_connp->conn_zoneid)
13067c478bd9Sstevel@tonic-gate 				continue;
13077c478bd9Sstevel@tonic-gate 
13087c478bd9Sstevel@tonic-gate 			/*
13097c478bd9Sstevel@tonic-gate 			 * If UDP_EXCLBIND is set for either the bound or
13107c478bd9Sstevel@tonic-gate 			 * binding endpoint, the semantics of bind
13117c478bd9Sstevel@tonic-gate 			 * is changed according to the following chart.
13127c478bd9Sstevel@tonic-gate 			 *
13137c478bd9Sstevel@tonic-gate 			 * spec = specified address (v4 or v6)
13147c478bd9Sstevel@tonic-gate 			 * unspec = unspecified address (v4 or v6)
13157c478bd9Sstevel@tonic-gate 			 * A = specified addresses are different for endpoints
13167c478bd9Sstevel@tonic-gate 			 *
13177c478bd9Sstevel@tonic-gate 			 * bound	bind to		allowed?
13187c478bd9Sstevel@tonic-gate 			 * -------------------------------------
13197c478bd9Sstevel@tonic-gate 			 * unspec	unspec		no
13207c478bd9Sstevel@tonic-gate 			 * unspec	spec		no
13217c478bd9Sstevel@tonic-gate 			 * spec		unspec		no
13227c478bd9Sstevel@tonic-gate 			 * spec		spec		yes if A
13237c478bd9Sstevel@tonic-gate 			 */
13247c478bd9Sstevel@tonic-gate 			if (udp1->udp_exclbind || udp->udp_exclbind) {
13257c478bd9Sstevel@tonic-gate 				if (V6_OR_V4_INADDR_ANY(
13267c478bd9Sstevel@tonic-gate 				    udp1->udp_bound_v6src) ||
13277c478bd9Sstevel@tonic-gate 				    is_inaddr_any ||
13287c478bd9Sstevel@tonic-gate 				    IN6_ARE_ADDR_EQUAL(&udp1->udp_bound_v6src,
13297c478bd9Sstevel@tonic-gate 				    &v6src)) {
13307c478bd9Sstevel@tonic-gate 					found_exclbind = B_TRUE;
13317c478bd9Sstevel@tonic-gate 					break;
13327c478bd9Sstevel@tonic-gate 				}
13337c478bd9Sstevel@tonic-gate 				continue;
13347c478bd9Sstevel@tonic-gate 			}
13357c478bd9Sstevel@tonic-gate 
13367c478bd9Sstevel@tonic-gate 			/*
13377c478bd9Sstevel@tonic-gate 			 * Check ipversion to allow IPv4 and IPv6 sockets to
1338*738d543bSdduvall 			 * have disjoint port number spaces.
13397c478bd9Sstevel@tonic-gate 			 */
1340*738d543bSdduvall 			if (udp->udp_ipversion != udp1->udp_ipversion)
13417c478bd9Sstevel@tonic-gate 				continue;
13427c478bd9Sstevel@tonic-gate 
13437c478bd9Sstevel@tonic-gate 			/*
13447c478bd9Sstevel@tonic-gate 			 * No difference depending on SO_REUSEADDR.
13457c478bd9Sstevel@tonic-gate 			 *
13467c478bd9Sstevel@tonic-gate 			 * If existing port is bound to a
13477c478bd9Sstevel@tonic-gate 			 * non-wildcard IP address and
13487c478bd9Sstevel@tonic-gate 			 * the requesting stream is bound to
13497c478bd9Sstevel@tonic-gate 			 * a distinct different IP addresses
13507c478bd9Sstevel@tonic-gate 			 * (non-wildcard, also), keep going.
13517c478bd9Sstevel@tonic-gate 			 */
13527c478bd9Sstevel@tonic-gate 			if (!is_inaddr_any &&
13537c478bd9Sstevel@tonic-gate 			    !V6_OR_V4_INADDR_ANY(udp1->udp_bound_v6src) &&
13547c478bd9Sstevel@tonic-gate 			    !IN6_ARE_ADDR_EQUAL(&udp1->udp_bound_v6src,
13557c478bd9Sstevel@tonic-gate 			    &v6src)) {
13567c478bd9Sstevel@tonic-gate 				continue;
13577c478bd9Sstevel@tonic-gate 			}
13587c478bd9Sstevel@tonic-gate 			break;
13597c478bd9Sstevel@tonic-gate 		}
13607c478bd9Sstevel@tonic-gate 
13617c478bd9Sstevel@tonic-gate 		if (!found_exclbind &&
13627c478bd9Sstevel@tonic-gate 		    (udp->udp_reuseaddr && requested_port != 0)) {
13637c478bd9Sstevel@tonic-gate 			break;
13647c478bd9Sstevel@tonic-gate 		}
13657c478bd9Sstevel@tonic-gate 
13667c478bd9Sstevel@tonic-gate 		if (udp1 == NULL) {
13677c478bd9Sstevel@tonic-gate 			/*
13687c478bd9Sstevel@tonic-gate 			 * No other stream has this IP address
13697c478bd9Sstevel@tonic-gate 			 * and port number. We can use it.
13707c478bd9Sstevel@tonic-gate 			 */
13717c478bd9Sstevel@tonic-gate 			break;
13727c478bd9Sstevel@tonic-gate 		}
13737c478bd9Sstevel@tonic-gate 		mutex_exit(&udpf->uf_lock);
13747c478bd9Sstevel@tonic-gate 		if (bind_to_req_port_only) {
13757c478bd9Sstevel@tonic-gate 			/*
13767c478bd9Sstevel@tonic-gate 			 * We get here only when requested port
13777c478bd9Sstevel@tonic-gate 			 * is bound (and only first  of the for()
13787c478bd9Sstevel@tonic-gate 			 * loop iteration).
13797c478bd9Sstevel@tonic-gate 			 *
13807c478bd9Sstevel@tonic-gate 			 * The semantics of this bind request
13817c478bd9Sstevel@tonic-gate 			 * require it to fail so we return from
13827c478bd9Sstevel@tonic-gate 			 * the routine (and exit the loop).
13837c478bd9Sstevel@tonic-gate 			 *
13847c478bd9Sstevel@tonic-gate 			 */
13857c478bd9Sstevel@tonic-gate 			udp_err_ack(q, mp, TADDRBUSY, 0);
13867c478bd9Sstevel@tonic-gate 			return;
13877c478bd9Sstevel@tonic-gate 		}
13887c478bd9Sstevel@tonic-gate 
13897c478bd9Sstevel@tonic-gate 		if (udp->udp_anon_priv_bind) {
13907c478bd9Sstevel@tonic-gate 			port = udp_get_next_priv_port();
13917c478bd9Sstevel@tonic-gate 		} else {
13927c478bd9Sstevel@tonic-gate 			if ((count == 0) && (requested_port != 0)) {
13937c478bd9Sstevel@tonic-gate 				/*
13947c478bd9Sstevel@tonic-gate 				 * If the application wants us to find
13957c478bd9Sstevel@tonic-gate 				 * a port, get one to start with. Set
13967c478bd9Sstevel@tonic-gate 				 * requested_port to 0, so that we will
13977c478bd9Sstevel@tonic-gate 				 * update udp_g_next_port_to_try below.
13987c478bd9Sstevel@tonic-gate 				 */
13997c478bd9Sstevel@tonic-gate 				port = udp_update_next_port(
14007c478bd9Sstevel@tonic-gate 				    udp_g_next_port_to_try, B_TRUE);
14017c478bd9Sstevel@tonic-gate 				requested_port = 0;
14027c478bd9Sstevel@tonic-gate 			} else {
14037c478bd9Sstevel@tonic-gate 				port = udp_update_next_port(port + 1, B_FALSE);
14047c478bd9Sstevel@tonic-gate 			}
14057c478bd9Sstevel@tonic-gate 		}
14067c478bd9Sstevel@tonic-gate 
14077c478bd9Sstevel@tonic-gate 		if (++count >= loopmax) {
14087c478bd9Sstevel@tonic-gate 			/*
14097c478bd9Sstevel@tonic-gate 			 * We've tried every possible port number and
14107c478bd9Sstevel@tonic-gate 			 * there are none available, so send an error
14117c478bd9Sstevel@tonic-gate 			 * to the user.
14127c478bd9Sstevel@tonic-gate 			 */
14137c478bd9Sstevel@tonic-gate 			udp_err_ack(q, mp, TNOADDR, 0);
14147c478bd9Sstevel@tonic-gate 			return;
14157c478bd9Sstevel@tonic-gate 		}
14167c478bd9Sstevel@tonic-gate 	}
14177c478bd9Sstevel@tonic-gate 
14187c478bd9Sstevel@tonic-gate 	/*
14197c478bd9Sstevel@tonic-gate 	 * Copy the source address into our udp structure.  This address
14207c478bd9Sstevel@tonic-gate 	 * may still be zero; if so, ip will fill in the correct address
14217c478bd9Sstevel@tonic-gate 	 * each time an outbound packet is passed to it.
14227c478bd9Sstevel@tonic-gate 	 * If we are binding to a broadcast or multicast address udp_rput
14237c478bd9Sstevel@tonic-gate 	 * will clear the source address when it receives the T_BIND_ACK.
14247c478bd9Sstevel@tonic-gate 	 */
14257c478bd9Sstevel@tonic-gate 	udp->udp_v6src = udp->udp_bound_v6src = v6src;
14267c478bd9Sstevel@tonic-gate 	udp->udp_port = lport;
14277c478bd9Sstevel@tonic-gate 	/*
14287c478bd9Sstevel@tonic-gate 	 * Now reset the the next anonymous port if the application requested
14297c478bd9Sstevel@tonic-gate 	 * an anonymous port, or we handed out the next anonymous port.
14307c478bd9Sstevel@tonic-gate 	 */
14317c478bd9Sstevel@tonic-gate 	if ((requested_port == 0) && (!udp->udp_anon_priv_bind)) {
14327c478bd9Sstevel@tonic-gate 		udp_g_next_port_to_try = port + 1;
14337c478bd9Sstevel@tonic-gate 	}
14347c478bd9Sstevel@tonic-gate 
14357c478bd9Sstevel@tonic-gate 	/* Initialize the O_T_BIND_REQ/T_BIND_REQ for ip. */
14367c478bd9Sstevel@tonic-gate 	if (udp->udp_family == AF_INET) {
14377c478bd9Sstevel@tonic-gate 		sin->sin_port = udp->udp_port;
14387c478bd9Sstevel@tonic-gate 	} else {
14397c478bd9Sstevel@tonic-gate 		int error;
14407c478bd9Sstevel@tonic-gate 
14417c478bd9Sstevel@tonic-gate 		sin6->sin6_port = udp->udp_port;
14427c478bd9Sstevel@tonic-gate 		/* Rebuild the header template */
14437c478bd9Sstevel@tonic-gate 		error = udp_build_hdrs(q, udp);
14447c478bd9Sstevel@tonic-gate 		if (error != 0) {
14457c478bd9Sstevel@tonic-gate 			mutex_exit(&udpf->uf_lock);
14467c478bd9Sstevel@tonic-gate 			udp_err_ack(q, mp, TSYSERR, error);
14477c478bd9Sstevel@tonic-gate 			return;
14487c478bd9Sstevel@tonic-gate 		}
14497c478bd9Sstevel@tonic-gate 	}
14507c478bd9Sstevel@tonic-gate 	udp->udp_state = TS_IDLE;
14517c478bd9Sstevel@tonic-gate 	udp_bind_hash_insert(udpf, udp);
14527c478bd9Sstevel@tonic-gate 	mutex_exit(&udpf->uf_lock);
14537c478bd9Sstevel@tonic-gate 
14547c478bd9Sstevel@tonic-gate 	if (cl_inet_bind) {
14557c478bd9Sstevel@tonic-gate 		/*
14567c478bd9Sstevel@tonic-gate 		 * Running in cluster mode - register bind information
14577c478bd9Sstevel@tonic-gate 		 */
14587c478bd9Sstevel@tonic-gate 		if (udp->udp_ipversion == IPV4_VERSION) {
14597c478bd9Sstevel@tonic-gate 			(*cl_inet_bind)(IPPROTO_UDP, AF_INET,
14607c478bd9Sstevel@tonic-gate 			    (uint8_t *)(&V4_PART_OF_V6(udp->udp_v6src)),
14617c478bd9Sstevel@tonic-gate 			    (in_port_t)udp->udp_port);
14627c478bd9Sstevel@tonic-gate 		} else {
14637c478bd9Sstevel@tonic-gate 			(*cl_inet_bind)(IPPROTO_UDP, AF_INET6,
14647c478bd9Sstevel@tonic-gate 			    (uint8_t *)&(udp->udp_v6src),
14657c478bd9Sstevel@tonic-gate 			    (in_port_t)udp->udp_port);
14667c478bd9Sstevel@tonic-gate 		}
14677c478bd9Sstevel@tonic-gate 
14687c478bd9Sstevel@tonic-gate 	}
14697c478bd9Sstevel@tonic-gate 
14707c478bd9Sstevel@tonic-gate 	/* Pass the protocol number in the message following the address. */
14717c478bd9Sstevel@tonic-gate 	*mp->b_wptr++ = IPPROTO_UDP;
14727c478bd9Sstevel@tonic-gate 	if (!V6_OR_V4_INADDR_ANY(udp->udp_v6src)) {
14737c478bd9Sstevel@tonic-gate 		/*
14747c478bd9Sstevel@tonic-gate 		 * Append a request for an IRE if udp_v6src not
14757c478bd9Sstevel@tonic-gate 		 * zero (IPv4 - INADDR_ANY, or IPv6 - all-zeroes address).
14767c478bd9Sstevel@tonic-gate 		 */
14777c478bd9Sstevel@tonic-gate 		mp->b_cont = allocb(sizeof (ire_t), BPRI_HI);
14787c478bd9Sstevel@tonic-gate 		if (!mp->b_cont) {
14797c478bd9Sstevel@tonic-gate 			udp_err_ack(q, mp, TSYSERR, ENOMEM);
14807c478bd9Sstevel@tonic-gate 			return;
14817c478bd9Sstevel@tonic-gate 		}
14827c478bd9Sstevel@tonic-gate 		mp->b_cont->b_wptr += sizeof (ire_t);
14837c478bd9Sstevel@tonic-gate 		mp->b_cont->b_datap->db_type = IRE_DB_REQ_TYPE;
14847c478bd9Sstevel@tonic-gate 	}
1485ff550d0eSmasputra 	if (udp->udp_family == AF_INET6)
1486ff550d0eSmasputra 		mp = ip_bind_v6(q, mp, connp, NULL);
1487ff550d0eSmasputra 	else
1488ff550d0eSmasputra 		mp = ip_bind_v4(q, mp, connp);
1489ff550d0eSmasputra 
1490ff550d0eSmasputra 	if (mp != NULL)
1491ff550d0eSmasputra 		udp_rput_other(_RD(q), mp);
1492ff550d0eSmasputra 	else
1493ff550d0eSmasputra 		CONN_INC_REF(connp);
1494ff550d0eSmasputra }
1495ff550d0eSmasputra 
1496ff550d0eSmasputra 
1497ff550d0eSmasputra void
1498ff550d0eSmasputra udp_resume_bind(conn_t *connp, mblk_t *mp)
1499ff550d0eSmasputra {
1500ff550d0eSmasputra 	udp_enter(connp, mp, udp_resume_bind_cb, SQTAG_BIND_RETRY);
1501ff550d0eSmasputra }
1502ff550d0eSmasputra 
1503ff550d0eSmasputra /*
1504ff550d0eSmasputra  * This is called from ip_wput_nondata to resume a deferred UDP bind.
1505ff550d0eSmasputra  */
1506ff550d0eSmasputra /* ARGSUSED */
1507ff550d0eSmasputra static void
1508ff550d0eSmasputra udp_resume_bind_cb(void *arg, mblk_t *mp, void *arg2)
1509ff550d0eSmasputra {
1510ff550d0eSmasputra 	conn_t *connp = arg;
1511ff550d0eSmasputra 
1512ff550d0eSmasputra 	ASSERT(connp != NULL && IPCL_IS_UDP(connp));
1513ff550d0eSmasputra 
1514ff550d0eSmasputra 	udp_rput_other(connp->conn_rq, mp);
1515ff550d0eSmasputra 
1516ff550d0eSmasputra 	CONN_OPER_PENDING_DONE(connp);
1517ff550d0eSmasputra 	udp_exit(connp);
15187c478bd9Sstevel@tonic-gate }
15197c478bd9Sstevel@tonic-gate 
15207c478bd9Sstevel@tonic-gate /*
15217c478bd9Sstevel@tonic-gate  * This routine handles each T_CONN_REQ message passed to udp.  It
15227c478bd9Sstevel@tonic-gate  * associates a default destination address with the stream.
15237c478bd9Sstevel@tonic-gate  *
15247c478bd9Sstevel@tonic-gate  * This routine sends down a T_BIND_REQ to IP with the following mblks:
15257c478bd9Sstevel@tonic-gate  *	T_BIND_REQ	- specifying local and remote address/port
15267c478bd9Sstevel@tonic-gate  *	IRE_DB_REQ_TYPE	- to get an IRE back containing ire_type and src
15277c478bd9Sstevel@tonic-gate  *	T_OK_ACK	- for the T_CONN_REQ
15287c478bd9Sstevel@tonic-gate  *	T_CONN_CON	- to keep the TPI user happy
15297c478bd9Sstevel@tonic-gate  *
15307c478bd9Sstevel@tonic-gate  * The connect completes in udp_rput.
15317c478bd9Sstevel@tonic-gate  * When a T_BIND_ACK is received information is extracted from the IRE
15327c478bd9Sstevel@tonic-gate  * and the two appended messages are sent to the TPI user.
15337c478bd9Sstevel@tonic-gate  * Should udp_rput receive T_ERROR_ACK for the T_BIND_REQ it will convert
15347c478bd9Sstevel@tonic-gate  * it to an error ack for the appropriate primitive.
15357c478bd9Sstevel@tonic-gate  */
15367c478bd9Sstevel@tonic-gate static void
15377c478bd9Sstevel@tonic-gate udp_connect(queue_t *q, mblk_t *mp)
15387c478bd9Sstevel@tonic-gate {
15397c478bd9Sstevel@tonic-gate 	sin6_t	*sin6;
15407c478bd9Sstevel@tonic-gate 	sin_t	*sin;
15417c478bd9Sstevel@tonic-gate 	struct T_conn_req	*tcr;
15427c478bd9Sstevel@tonic-gate 	in6_addr_t v6dst;
15437c478bd9Sstevel@tonic-gate 	ipaddr_t v4dst;
15447c478bd9Sstevel@tonic-gate 	uint16_t dstport;
15457c478bd9Sstevel@tonic-gate 	uint32_t flowinfo;
15467c478bd9Sstevel@tonic-gate 	mblk_t	*mp1, *mp2;
15477c478bd9Sstevel@tonic-gate 	udp_fanout_t	*udpf;
1548ff550d0eSmasputra 	udp_t	*udp, *udp1;
1549ff550d0eSmasputra 
1550ff550d0eSmasputra 	udp = Q_TO_UDP(q);
15517c478bd9Sstevel@tonic-gate 
15527c478bd9Sstevel@tonic-gate 	tcr = (struct T_conn_req *)mp->b_rptr;
15537c478bd9Sstevel@tonic-gate 
15547c478bd9Sstevel@tonic-gate 	/* A bit of sanity checking */
15557c478bd9Sstevel@tonic-gate 	if ((mp->b_wptr - mp->b_rptr) < sizeof (struct T_conn_req)) {
15567c478bd9Sstevel@tonic-gate 		udp_err_ack(q, mp, TPROTO, 0);
15577c478bd9Sstevel@tonic-gate 		return;
15587c478bd9Sstevel@tonic-gate 	}
15597c478bd9Sstevel@tonic-gate 	/*
15607c478bd9Sstevel@tonic-gate 	 * This UDP must have bound to a port already before doing
15617c478bd9Sstevel@tonic-gate 	 * a connect.
15627c478bd9Sstevel@tonic-gate 	 */
15637c478bd9Sstevel@tonic-gate 	if (udp->udp_state == TS_UNBND) {
15647c478bd9Sstevel@tonic-gate 		(void) mi_strlog(q, 1, SL_ERROR|SL_TRACE,
15657c478bd9Sstevel@tonic-gate 		    "udp_connect: bad state, %u", udp->udp_state);
15667c478bd9Sstevel@tonic-gate 		udp_err_ack(q, mp, TOUTSTATE, 0);
15677c478bd9Sstevel@tonic-gate 		return;
15687c478bd9Sstevel@tonic-gate 	}
15697c478bd9Sstevel@tonic-gate 	ASSERT(udp->udp_port != 0 && udp->udp_ptpbhn != NULL);
15707c478bd9Sstevel@tonic-gate 
15717c478bd9Sstevel@tonic-gate 	udpf = &udp_bind_fanout[UDP_BIND_HASH(udp->udp_port)];
1572ff550d0eSmasputra 
15737c478bd9Sstevel@tonic-gate 	if (udp->udp_state == TS_DATA_XFER) {
15747c478bd9Sstevel@tonic-gate 		/* Already connected - clear out state */
15757c478bd9Sstevel@tonic-gate 		mutex_enter(&udpf->uf_lock);
15767c478bd9Sstevel@tonic-gate 		udp->udp_v6src = udp->udp_bound_v6src;
15777c478bd9Sstevel@tonic-gate 		udp->udp_state = TS_IDLE;
15787c478bd9Sstevel@tonic-gate 		mutex_exit(&udpf->uf_lock);
15797c478bd9Sstevel@tonic-gate 	}
15807c478bd9Sstevel@tonic-gate 
15817c478bd9Sstevel@tonic-gate 	if (tcr->OPT_length != 0) {
15827c478bd9Sstevel@tonic-gate 		udp_err_ack(q, mp, TBADOPT, 0);
15837c478bd9Sstevel@tonic-gate 		return;
15847c478bd9Sstevel@tonic-gate 	}
15857c478bd9Sstevel@tonic-gate 
15867c478bd9Sstevel@tonic-gate 	/*
15877c478bd9Sstevel@tonic-gate 	 * Determine packet type based on type of address passed in
15887c478bd9Sstevel@tonic-gate 	 * the request should contain an IPv4 or IPv6 address.
15897c478bd9Sstevel@tonic-gate 	 * Make sure that address family matches the type of
15907c478bd9Sstevel@tonic-gate 	 * family of the the address passed down
15917c478bd9Sstevel@tonic-gate 	 */
15927c478bd9Sstevel@tonic-gate 	switch (tcr->DEST_length) {
15937c478bd9Sstevel@tonic-gate 	default:
15947c478bd9Sstevel@tonic-gate 		udp_err_ack(q, mp, TBADADDR, 0);
15957c478bd9Sstevel@tonic-gate 		return;
15967c478bd9Sstevel@tonic-gate 
15977c478bd9Sstevel@tonic-gate 	case sizeof (sin_t):
15987c478bd9Sstevel@tonic-gate 		sin = (sin_t *)mi_offset_param(mp, tcr->DEST_offset,
15997c478bd9Sstevel@tonic-gate 		    sizeof (sin_t));
16007c478bd9Sstevel@tonic-gate 		if (sin == NULL || !OK_32PTR((char *)sin)) {
16017c478bd9Sstevel@tonic-gate 			udp_err_ack(q, mp, TSYSERR, EINVAL);
16027c478bd9Sstevel@tonic-gate 			return;
16037c478bd9Sstevel@tonic-gate 		}
16047c478bd9Sstevel@tonic-gate 		if (udp->udp_family != AF_INET ||
16057c478bd9Sstevel@tonic-gate 		    sin->sin_family != AF_INET) {
16067c478bd9Sstevel@tonic-gate 			udp_err_ack(q, mp, TSYSERR, EAFNOSUPPORT);
16077c478bd9Sstevel@tonic-gate 			return;
16087c478bd9Sstevel@tonic-gate 		}
16097c478bd9Sstevel@tonic-gate 		v4dst = sin->sin_addr.s_addr;
16107c478bd9Sstevel@tonic-gate 		dstport = sin->sin_port;
16117c478bd9Sstevel@tonic-gate 		IN6_IPADDR_TO_V4MAPPED(v4dst, &v6dst);
16127c478bd9Sstevel@tonic-gate 		ASSERT(udp->udp_ipversion == IPV4_VERSION);
16137c478bd9Sstevel@tonic-gate 		udp->udp_max_hdr_len = IP_SIMPLE_HDR_LENGTH + UDPH_SIZE +
16147c478bd9Sstevel@tonic-gate 		    udp->udp_ip_snd_options_len;
16157c478bd9Sstevel@tonic-gate 		break;
16167c478bd9Sstevel@tonic-gate 
16177c478bd9Sstevel@tonic-gate 	case sizeof (sin6_t):
16187c478bd9Sstevel@tonic-gate 		sin6 = (sin6_t *)mi_offset_param(mp, tcr->DEST_offset,
16197c478bd9Sstevel@tonic-gate 		    sizeof (sin6_t));
16207c478bd9Sstevel@tonic-gate 		if (sin6 == NULL || !OK_32PTR((char *)sin6)) {
16217c478bd9Sstevel@tonic-gate 			udp_err_ack(q, mp, TSYSERR, EINVAL);
16227c478bd9Sstevel@tonic-gate 			return;
16237c478bd9Sstevel@tonic-gate 		}
16247c478bd9Sstevel@tonic-gate 		if (udp->udp_family != AF_INET6 ||
16257c478bd9Sstevel@tonic-gate 		    sin6->sin6_family != AF_INET6) {
16267c478bd9Sstevel@tonic-gate 			udp_err_ack(q, mp, TSYSERR, EAFNOSUPPORT);
16277c478bd9Sstevel@tonic-gate 			return;
16287c478bd9Sstevel@tonic-gate 		}
16297c478bd9Sstevel@tonic-gate 		v6dst = sin6->sin6_addr;
16307c478bd9Sstevel@tonic-gate 		if (IN6_IS_ADDR_V4MAPPED(&v6dst)) {
16317c478bd9Sstevel@tonic-gate 			IN6_V4MAPPED_TO_IPADDR(&v6dst, v4dst);
16327c478bd9Sstevel@tonic-gate 			udp->udp_ipversion = IPV4_VERSION;
16337c478bd9Sstevel@tonic-gate 			udp->udp_max_hdr_len = IP_SIMPLE_HDR_LENGTH +
16347c478bd9Sstevel@tonic-gate 			    UDPH_SIZE + udp->udp_ip_snd_options_len;
16357c478bd9Sstevel@tonic-gate 			flowinfo = 0;
16367c478bd9Sstevel@tonic-gate 		} else {
16377c478bd9Sstevel@tonic-gate 			udp->udp_ipversion = IPV6_VERSION;
16387c478bd9Sstevel@tonic-gate 			udp->udp_max_hdr_len = udp->udp_sticky_hdrs_len;
16397c478bd9Sstevel@tonic-gate 			flowinfo = sin6->sin6_flowinfo;
16407c478bd9Sstevel@tonic-gate 		}
16417c478bd9Sstevel@tonic-gate 		dstport = sin6->sin6_port;
16427c478bd9Sstevel@tonic-gate 		break;
16437c478bd9Sstevel@tonic-gate 	}
16447c478bd9Sstevel@tonic-gate 	if (dstport == 0) {
16457c478bd9Sstevel@tonic-gate 		udp_err_ack(q, mp, TBADADDR, 0);
16467c478bd9Sstevel@tonic-gate 		return;
16477c478bd9Sstevel@tonic-gate 	}
16487c478bd9Sstevel@tonic-gate 
16497c478bd9Sstevel@tonic-gate 	/*
16507c478bd9Sstevel@tonic-gate 	 * Create a default IP header with no IP options.
16517c478bd9Sstevel@tonic-gate 	 */
16527c478bd9Sstevel@tonic-gate 	udp->udp_dstport = dstport;
16537c478bd9Sstevel@tonic-gate 	if (udp->udp_ipversion == IPV4_VERSION) {
16547c478bd9Sstevel@tonic-gate 		/*
16557c478bd9Sstevel@tonic-gate 		 * Interpret a zero destination to mean loopback.
16567c478bd9Sstevel@tonic-gate 		 * Update the T_CONN_REQ (sin/sin6) since it is used to
16577c478bd9Sstevel@tonic-gate 		 * generate the T_CONN_CON.
16587c478bd9Sstevel@tonic-gate 		 */
16597c478bd9Sstevel@tonic-gate 		if (v4dst == INADDR_ANY) {
16607c478bd9Sstevel@tonic-gate 			v4dst = htonl(INADDR_LOOPBACK);
16617c478bd9Sstevel@tonic-gate 			IN6_IPADDR_TO_V4MAPPED(v4dst, &v6dst);
16627c478bd9Sstevel@tonic-gate 			if (udp->udp_family == AF_INET) {
16637c478bd9Sstevel@tonic-gate 				sin->sin_addr.s_addr = v4dst;
16647c478bd9Sstevel@tonic-gate 			} else {
16657c478bd9Sstevel@tonic-gate 				sin6->sin6_addr = v6dst;
16667c478bd9Sstevel@tonic-gate 			}
16677c478bd9Sstevel@tonic-gate 		}
16687c478bd9Sstevel@tonic-gate 		udp->udp_v6dst = v6dst;
16697c478bd9Sstevel@tonic-gate 		udp->udp_flowinfo = 0;
16707c478bd9Sstevel@tonic-gate 
16717c478bd9Sstevel@tonic-gate 		/*
16727c478bd9Sstevel@tonic-gate 		 * If the destination address is multicast and
16737c478bd9Sstevel@tonic-gate 		 * an outgoing multicast interface has been set,
16747c478bd9Sstevel@tonic-gate 		 * use the address of that interface as our
16757c478bd9Sstevel@tonic-gate 		 * source address if no source address has been set.
16767c478bd9Sstevel@tonic-gate 		 */
16777c478bd9Sstevel@tonic-gate 		if (V4_PART_OF_V6(udp->udp_v6src) == INADDR_ANY &&
16787c478bd9Sstevel@tonic-gate 		    CLASSD(v4dst) &&
16797c478bd9Sstevel@tonic-gate 		    udp->udp_multicast_if_addr != INADDR_ANY) {
16807c478bd9Sstevel@tonic-gate 			IN6_IPADDR_TO_V4MAPPED(udp->udp_multicast_if_addr,
16817c478bd9Sstevel@tonic-gate 			    &udp->udp_v6src);
16827c478bd9Sstevel@tonic-gate 		}
16837c478bd9Sstevel@tonic-gate 	} else {
16847c478bd9Sstevel@tonic-gate 		ASSERT(udp->udp_ipversion == IPV6_VERSION);
16857c478bd9Sstevel@tonic-gate 		/*
16867c478bd9Sstevel@tonic-gate 		 * Interpret a zero destination to mean loopback.
16877c478bd9Sstevel@tonic-gate 		 * Update the T_CONN_REQ (sin/sin6) since it is used to
16887c478bd9Sstevel@tonic-gate 		 * generate the T_CONN_CON.
16897c478bd9Sstevel@tonic-gate 		 */
16907c478bd9Sstevel@tonic-gate 		if (IN6_IS_ADDR_UNSPECIFIED(&v6dst)) {
16917c478bd9Sstevel@tonic-gate 			v6dst = ipv6_loopback;
16927c478bd9Sstevel@tonic-gate 			sin6->sin6_addr = v6dst;
16937c478bd9Sstevel@tonic-gate 		}
16947c478bd9Sstevel@tonic-gate 		udp->udp_v6dst = v6dst;
16957c478bd9Sstevel@tonic-gate 		udp->udp_flowinfo = flowinfo;
16967c478bd9Sstevel@tonic-gate 		/*
16977c478bd9Sstevel@tonic-gate 		 * If the destination address is multicast and
16987c478bd9Sstevel@tonic-gate 		 * an outgoing multicast interface has been set,
16997c478bd9Sstevel@tonic-gate 		 * then the ip bind logic will pick the correct source
17007c478bd9Sstevel@tonic-gate 		 * address (i.e. matching the outgoing multicast interface).
17017c478bd9Sstevel@tonic-gate 		 */
17027c478bd9Sstevel@tonic-gate 	}
17037c478bd9Sstevel@tonic-gate 
17047c478bd9Sstevel@tonic-gate 	/*
17057c478bd9Sstevel@tonic-gate 	 * Verify that the src/port/dst/port is unique for all
17067c478bd9Sstevel@tonic-gate 	 * connections in TS_DATA_XFER
17077c478bd9Sstevel@tonic-gate 	 */
17087c478bd9Sstevel@tonic-gate 	mutex_enter(&udpf->uf_lock);
17097c478bd9Sstevel@tonic-gate 	for (udp1 = udpf->uf_udp; udp1 != NULL; udp1 = udp1->udp_bind_hash) {
17107c478bd9Sstevel@tonic-gate 		if (udp1->udp_state != TS_DATA_XFER)
17117c478bd9Sstevel@tonic-gate 			continue;
17127c478bd9Sstevel@tonic-gate 		if (udp->udp_port != udp1->udp_port ||
17137c478bd9Sstevel@tonic-gate 		    udp->udp_ipversion != udp1->udp_ipversion ||
17147c478bd9Sstevel@tonic-gate 		    dstport != udp1->udp_dstport ||
17157c478bd9Sstevel@tonic-gate 		    !IN6_ARE_ADDR_EQUAL(&udp->udp_v6src, &udp1->udp_v6src) ||
17167c478bd9Sstevel@tonic-gate 		    !IN6_ARE_ADDR_EQUAL(&v6dst, &udp1->udp_v6dst))
17177c478bd9Sstevel@tonic-gate 			continue;
17187c478bd9Sstevel@tonic-gate 		mutex_exit(&udpf->uf_lock);
17197c478bd9Sstevel@tonic-gate 		udp_err_ack(q, mp, TBADADDR, 0);
17207c478bd9Sstevel@tonic-gate 		return;
17217c478bd9Sstevel@tonic-gate 	}
17227c478bd9Sstevel@tonic-gate 	udp->udp_state = TS_DATA_XFER;
17237c478bd9Sstevel@tonic-gate 	mutex_exit(&udpf->uf_lock);
17247c478bd9Sstevel@tonic-gate 
17257c478bd9Sstevel@tonic-gate 	/*
17267c478bd9Sstevel@tonic-gate 	 * Send down bind to IP to verify that there is a route
17277c478bd9Sstevel@tonic-gate 	 * and to determine the source address.
17287c478bd9Sstevel@tonic-gate 	 * This will come back as T_BIND_ACK with an IRE_DB_TYPE in rput.
17297c478bd9Sstevel@tonic-gate 	 */
17307c478bd9Sstevel@tonic-gate 	if (udp->udp_family == AF_INET)
17317c478bd9Sstevel@tonic-gate 		mp1 = udp_ip_bind_mp(udp, O_T_BIND_REQ, sizeof (ipa_conn_t));
17327c478bd9Sstevel@tonic-gate 	else
17337c478bd9Sstevel@tonic-gate 		mp1 = udp_ip_bind_mp(udp, O_T_BIND_REQ, sizeof (ipa6_conn_t));
17347c478bd9Sstevel@tonic-gate 	if (mp1 == NULL) {
17357c478bd9Sstevel@tonic-gate 		udp_err_ack(q, mp, TSYSERR, ENOMEM);
17367c478bd9Sstevel@tonic-gate bind_failed:
17377c478bd9Sstevel@tonic-gate 		mutex_enter(&udpf->uf_lock);
17387c478bd9Sstevel@tonic-gate 		udp->udp_state = TS_IDLE;
17397c478bd9Sstevel@tonic-gate 		mutex_exit(&udpf->uf_lock);
17407c478bd9Sstevel@tonic-gate 		return;
17417c478bd9Sstevel@tonic-gate 	}
17427c478bd9Sstevel@tonic-gate 
17437c478bd9Sstevel@tonic-gate 	/*
17447c478bd9Sstevel@tonic-gate 	 * We also have to send a connection confirmation to
17457c478bd9Sstevel@tonic-gate 	 * keep TLI happy. Prepare it for udp_rput.
17467c478bd9Sstevel@tonic-gate 	 */
17477c478bd9Sstevel@tonic-gate 	if (udp->udp_family == AF_INET)
17487c478bd9Sstevel@tonic-gate 		mp2 = mi_tpi_conn_con(NULL, (char *)sin,
17497c478bd9Sstevel@tonic-gate 		    sizeof (*sin), NULL, 0);
17507c478bd9Sstevel@tonic-gate 	else
17517c478bd9Sstevel@tonic-gate 		mp2 = mi_tpi_conn_con(NULL, (char *)sin6,
17527c478bd9Sstevel@tonic-gate 		    sizeof (*sin6), NULL, 0);
17537c478bd9Sstevel@tonic-gate 	if (mp2 == NULL) {
17547c478bd9Sstevel@tonic-gate 		freemsg(mp1);
17557c478bd9Sstevel@tonic-gate 		udp_err_ack(q, mp, TSYSERR, ENOMEM);
17567c478bd9Sstevel@tonic-gate 		goto bind_failed;
17577c478bd9Sstevel@tonic-gate 	}
17587c478bd9Sstevel@tonic-gate 
17597c478bd9Sstevel@tonic-gate 	mp = mi_tpi_ok_ack_alloc(mp);
17607c478bd9Sstevel@tonic-gate 	if (mp == NULL) {
17617c478bd9Sstevel@tonic-gate 		/* Unable to reuse the T_CONN_REQ for the ack. */
17627c478bd9Sstevel@tonic-gate 		freemsg(mp2);
17637c478bd9Sstevel@tonic-gate 		udp_err_ack_prim(q, mp1, T_CONN_REQ, TSYSERR, ENOMEM);
17647c478bd9Sstevel@tonic-gate 		goto bind_failed;
17657c478bd9Sstevel@tonic-gate 	}
17667c478bd9Sstevel@tonic-gate 
17677c478bd9Sstevel@tonic-gate 	/* Hang onto the T_OK_ACK and T_CONN_CON for later. */
17687c478bd9Sstevel@tonic-gate 	linkb(mp1, mp);
17697c478bd9Sstevel@tonic-gate 	linkb(mp1, mp2);
17707c478bd9Sstevel@tonic-gate 
1771ff550d0eSmasputra 	if (udp->udp_family == AF_INET)
1772ff550d0eSmasputra 		mp1 = ip_bind_v4(q, mp1, udp->udp_connp);
1773ff550d0eSmasputra 	else
1774ff550d0eSmasputra 		mp1 = ip_bind_v6(q, mp1, udp->udp_connp, NULL);
1775ff550d0eSmasputra 
1776ff550d0eSmasputra 	if (mp1 != NULL)
1777ff550d0eSmasputra 		udp_rput_other(_RD(q), mp1);
1778ff550d0eSmasputra 	else
1779ff550d0eSmasputra 		CONN_INC_REF(udp->udp_connp);
17807c478bd9Sstevel@tonic-gate }
17817c478bd9Sstevel@tonic-gate 
17827c478bd9Sstevel@tonic-gate static int
17837c478bd9Sstevel@tonic-gate udp_close(queue_t *q)
17847c478bd9Sstevel@tonic-gate {
1785ff550d0eSmasputra 	conn_t	*connp = Q_TO_CONN(UDP_WR(q));
1786ff550d0eSmasputra 	udp_t	*udp;
1787ff550d0eSmasputra 	queue_t	*ip_rq = RD(UDP_WR(q));
17887c478bd9Sstevel@tonic-gate 
1789ff550d0eSmasputra 	ASSERT(connp != NULL && IPCL_IS_UDP(connp));
1790ff550d0eSmasputra 	udp = connp->conn_udp;
1791ff550d0eSmasputra 
1792ff550d0eSmasputra 	ip_quiesce_conn(connp);
1793ff550d0eSmasputra 	/*
1794ff550d0eSmasputra 	 * Disable read-side synchronous stream
1795ff550d0eSmasputra 	 * interface and drain any queued data.
1796ff550d0eSmasputra 	 */
1797ff550d0eSmasputra 	udp_rcv_drain(q, udp, B_TRUE);
1798ff550d0eSmasputra 	ASSERT(!udp->udp_direct_sockfs);
17997c478bd9Sstevel@tonic-gate 
18007c478bd9Sstevel@tonic-gate 	qprocsoff(q);
18017c478bd9Sstevel@tonic-gate 
1802ff550d0eSmasputra 	/* restore IP module's high and low water marks to default values */
1803ff550d0eSmasputra 	ip_rq->q_hiwat = ip_rq->q_qinfo->qi_minfo->mi_hiwat;
1804ff550d0eSmasputra 	WR(ip_rq)->q_hiwat = WR(ip_rq)->q_qinfo->qi_minfo->mi_hiwat;
1805ff550d0eSmasputra 	WR(ip_rq)->q_lowat = WR(ip_rq)->q_qinfo->qi_minfo->mi_lowat;
1806ff550d0eSmasputra 
1807ff550d0eSmasputra 	ASSERT(udp->udp_rcv_cnt == 0);
1808ff550d0eSmasputra 	ASSERT(udp->udp_rcv_msgcnt == 0);
1809ff550d0eSmasputra 	ASSERT(udp->udp_rcv_list_head == NULL);
1810ff550d0eSmasputra 	ASSERT(udp->udp_rcv_list_tail == NULL);
1811ff550d0eSmasputra 
1812ff550d0eSmasputra 	/* connp is now single threaded. */
1813ff550d0eSmasputra 	udp_close_free(connp);
1814ff550d0eSmasputra 	/*
1815ff550d0eSmasputra 	 * Restore connp as an IP endpoint.  We don't need
1816ff550d0eSmasputra 	 * any locks since we are now single threaded
1817ff550d0eSmasputra 	 */
1818ff550d0eSmasputra 	connp->conn_flags &= ~IPCL_UDP;
1819ff550d0eSmasputra 	connp->conn_state_flags &=
1820ff550d0eSmasputra 	    ~(CONN_CLOSING | CONN_CONDEMNED | CONN_QUIESCED);
1821ff550d0eSmasputra 	return (0);
1822ff550d0eSmasputra }
1823ff550d0eSmasputra 
1824ff550d0eSmasputra /*
1825ff550d0eSmasputra  * Called in the close path from IP (ip_quiesce_conn) to quiesce the conn
1826ff550d0eSmasputra  */
1827ff550d0eSmasputra void
1828ff550d0eSmasputra udp_quiesce_conn(conn_t *connp)
1829ff550d0eSmasputra {
1830ff550d0eSmasputra 	udp_t	*udp = connp->conn_udp;
1831ff550d0eSmasputra 
18327c478bd9Sstevel@tonic-gate 	if (cl_inet_unbind != NULL && udp->udp_state == TS_IDLE) {
18337c478bd9Sstevel@tonic-gate 		/*
18347c478bd9Sstevel@tonic-gate 		 * Running in cluster mode - register unbind information
18357c478bd9Sstevel@tonic-gate 		 */
18367c478bd9Sstevel@tonic-gate 		if (udp->udp_ipversion == IPV4_VERSION) {
18377c478bd9Sstevel@tonic-gate 			(*cl_inet_unbind)(IPPROTO_UDP, AF_INET,
18387c478bd9Sstevel@tonic-gate 			    (uint8_t *)(&(V4_PART_OF_V6(udp->udp_v6src))),
18397c478bd9Sstevel@tonic-gate 			    (in_port_t)udp->udp_port);
18407c478bd9Sstevel@tonic-gate 		} else {
18417c478bd9Sstevel@tonic-gate 			(*cl_inet_unbind)(IPPROTO_UDP, AF_INET6,
18427c478bd9Sstevel@tonic-gate 			    (uint8_t *)(&(udp->udp_v6src)),
18437c478bd9Sstevel@tonic-gate 			    (in_port_t)udp->udp_port);
18447c478bd9Sstevel@tonic-gate 		}
18457c478bd9Sstevel@tonic-gate 	}
18467c478bd9Sstevel@tonic-gate 
18477c478bd9Sstevel@tonic-gate 	udp_bind_hash_remove(udp, B_FALSE);
1848ff550d0eSmasputra 
1849ff550d0eSmasputra 	mutex_enter(&connp->conn_lock);
1850ff550d0eSmasputra 	while (udp->udp_reader_count != 0 || udp->udp_squeue_count != 0 ||
1851ff550d0eSmasputra 	    udp->udp_mode != UDP_MT_HOT) {
1852ff550d0eSmasputra 		cv_wait(&connp->conn_cv, &connp->conn_lock);
1853ff550d0eSmasputra 	}
1854ff550d0eSmasputra 	mutex_exit(&connp->conn_lock);
1855ff550d0eSmasputra }
1856ff550d0eSmasputra 
1857ff550d0eSmasputra void
1858ff550d0eSmasputra udp_close_free(conn_t *connp)
1859ff550d0eSmasputra {
1860ff550d0eSmasputra 	udp_t *udp = connp->conn_udp;
1861ff550d0eSmasputra 
18627c478bd9Sstevel@tonic-gate 	/* If there are any options associated with the stream, free them. */
1863ff550d0eSmasputra 	if (udp->udp_ip_snd_options) {
18647c478bd9Sstevel@tonic-gate 		mi_free((char *)udp->udp_ip_snd_options);
1865ff550d0eSmasputra 		udp->udp_ip_snd_options = NULL;
1866ff550d0eSmasputra 	}
18677c478bd9Sstevel@tonic-gate 
1868ff550d0eSmasputra 	if (udp->udp_ip_rcv_options) {
18697c478bd9Sstevel@tonic-gate 		mi_free((char *)udp->udp_ip_rcv_options);
1870ff550d0eSmasputra 		udp->udp_ip_rcv_options = NULL;
1871ff550d0eSmasputra 	}
18727c478bd9Sstevel@tonic-gate 
18737c478bd9Sstevel@tonic-gate 	/* Free memory associated with sticky options */
18747c478bd9Sstevel@tonic-gate 	if (udp->udp_sticky_hdrs_len != 0) {
18757c478bd9Sstevel@tonic-gate 		kmem_free(udp->udp_sticky_hdrs,
18767c478bd9Sstevel@tonic-gate 		    udp->udp_sticky_hdrs_len);
18777c478bd9Sstevel@tonic-gate 		udp->udp_sticky_hdrs = NULL;
18787c478bd9Sstevel@tonic-gate 		udp->udp_sticky_hdrs_len = 0;
18797c478bd9Sstevel@tonic-gate 	}
1880ff550d0eSmasputra 
18817c478bd9Sstevel@tonic-gate 	if (udp->udp_sticky_ipp.ipp_fields & IPPF_HOPOPTS) {
18827c478bd9Sstevel@tonic-gate 		kmem_free(udp->udp_sticky_ipp.ipp_hopopts,
18837c478bd9Sstevel@tonic-gate 		    udp->udp_sticky_ipp.ipp_hopoptslen);
1884ff550d0eSmasputra 		udp->udp_sticky_ipp.ipp_hopopts = NULL;
18857c478bd9Sstevel@tonic-gate 	}
18867c478bd9Sstevel@tonic-gate 	if (udp->udp_sticky_ipp.ipp_fields & IPPF_RTDSTOPTS) {
18877c478bd9Sstevel@tonic-gate 		kmem_free(udp->udp_sticky_ipp.ipp_rtdstopts,
18887c478bd9Sstevel@tonic-gate 		    udp->udp_sticky_ipp.ipp_rtdstoptslen);
1889ff550d0eSmasputra 		udp->udp_sticky_ipp.ipp_rtdstopts = NULL;
18907c478bd9Sstevel@tonic-gate 	}
18917c478bd9Sstevel@tonic-gate 	if (udp->udp_sticky_ipp.ipp_fields & IPPF_RTHDR) {
18927c478bd9Sstevel@tonic-gate 		kmem_free(udp->udp_sticky_ipp.ipp_rthdr,
18937c478bd9Sstevel@tonic-gate 		    udp->udp_sticky_ipp.ipp_rthdrlen);
1894ff550d0eSmasputra 		udp->udp_sticky_ipp.ipp_rthdr = NULL;
18957c478bd9Sstevel@tonic-gate 	}
18967c478bd9Sstevel@tonic-gate 	if (udp->udp_sticky_ipp.ipp_fields & IPPF_DSTOPTS) {
18977c478bd9Sstevel@tonic-gate 		kmem_free(udp->udp_sticky_ipp.ipp_dstopts,
18987c478bd9Sstevel@tonic-gate 		    udp->udp_sticky_ipp.ipp_dstoptslen);
1899ff550d0eSmasputra 		udp->udp_sticky_ipp.ipp_dstopts = NULL;
19007c478bd9Sstevel@tonic-gate 	}
19017c478bd9Sstevel@tonic-gate 	udp->udp_sticky_ipp.ipp_fields &=
19027c478bd9Sstevel@tonic-gate 	    ~(IPPF_HOPOPTS|IPPF_RTDSTOPTS|IPPF_RTHDR|IPPF_DSTOPTS);
19037c478bd9Sstevel@tonic-gate 
1904ff550d0eSmasputra 	udp->udp_connp = NULL;
1905ff550d0eSmasputra 	connp->conn_udp = NULL;
1906ff550d0eSmasputra 	kmem_cache_free(udp_cache, udp);
19077c478bd9Sstevel@tonic-gate }
19087c478bd9Sstevel@tonic-gate 
19097c478bd9Sstevel@tonic-gate /*
19107c478bd9Sstevel@tonic-gate  * This routine handles each T_DISCON_REQ message passed to udp
19117c478bd9Sstevel@tonic-gate  * as an indicating that UDP is no longer connected. This results
19127c478bd9Sstevel@tonic-gate  * in sending a T_BIND_REQ to IP to restore the binding to just
19137c478bd9Sstevel@tonic-gate  * the local address/port.
19147c478bd9Sstevel@tonic-gate  *
19157c478bd9Sstevel@tonic-gate  * This routine sends down a T_BIND_REQ to IP with the following mblks:
19167c478bd9Sstevel@tonic-gate  *	T_BIND_REQ	- specifying just the local address/port
19177c478bd9Sstevel@tonic-gate  *	T_OK_ACK	- for the T_DISCON_REQ
19187c478bd9Sstevel@tonic-gate  *
19197c478bd9Sstevel@tonic-gate  * The disconnect completes in udp_rput.
19207c478bd9Sstevel@tonic-gate  * When a T_BIND_ACK is received the appended T_OK_ACK is sent to the TPI user.
19217c478bd9Sstevel@tonic-gate  * Should udp_rput receive T_ERROR_ACK for the T_BIND_REQ it will convert
19227c478bd9Sstevel@tonic-gate  * it to an error ack for the appropriate primitive.
19237c478bd9Sstevel@tonic-gate  */
19247c478bd9Sstevel@tonic-gate static void
19257c478bd9Sstevel@tonic-gate udp_disconnect(queue_t *q, mblk_t *mp)
19267c478bd9Sstevel@tonic-gate {
1927ff550d0eSmasputra 	udp_t	*udp = Q_TO_UDP(q);
19287c478bd9Sstevel@tonic-gate 	mblk_t	*mp1;
19297c478bd9Sstevel@tonic-gate 	udp_fanout_t *udpf;
19307c478bd9Sstevel@tonic-gate 
19317c478bd9Sstevel@tonic-gate 	if (udp->udp_state != TS_DATA_XFER) {
19327c478bd9Sstevel@tonic-gate 		(void) mi_strlog(q, 1, SL_ERROR|SL_TRACE,
19337c478bd9Sstevel@tonic-gate 		    "udp_disconnect: bad state, %u", udp->udp_state);
19347c478bd9Sstevel@tonic-gate 		udp_err_ack(q, mp, TOUTSTATE, 0);
19357c478bd9Sstevel@tonic-gate 		return;
19367c478bd9Sstevel@tonic-gate 	}
19377c478bd9Sstevel@tonic-gate 	udpf = &udp_bind_fanout[UDP_BIND_HASH(udp->udp_port)];
19387c478bd9Sstevel@tonic-gate 	mutex_enter(&udpf->uf_lock);
19397c478bd9Sstevel@tonic-gate 	udp->udp_v6src = udp->udp_bound_v6src;
19407c478bd9Sstevel@tonic-gate 	udp->udp_state = TS_IDLE;
19417c478bd9Sstevel@tonic-gate 	mutex_exit(&udpf->uf_lock);
19427c478bd9Sstevel@tonic-gate 
19437c478bd9Sstevel@tonic-gate 	/*
19447c478bd9Sstevel@tonic-gate 	 * Send down bind to IP to remove the full binding and revert
19457c478bd9Sstevel@tonic-gate 	 * to the local address binding.
19467c478bd9Sstevel@tonic-gate 	 */
19477c478bd9Sstevel@tonic-gate 	if (udp->udp_family == AF_INET)
19487c478bd9Sstevel@tonic-gate 		mp1 = udp_ip_bind_mp(udp, O_T_BIND_REQ, sizeof (sin_t));
19497c478bd9Sstevel@tonic-gate 	else
19507c478bd9Sstevel@tonic-gate 		mp1 = udp_ip_bind_mp(udp, O_T_BIND_REQ, sizeof (sin6_t));
19517c478bd9Sstevel@tonic-gate 	if (mp1 == NULL) {
19527c478bd9Sstevel@tonic-gate 		udp_err_ack(q, mp, TSYSERR, ENOMEM);
19537c478bd9Sstevel@tonic-gate 		return;
19547c478bd9Sstevel@tonic-gate 	}
19557c478bd9Sstevel@tonic-gate 	mp = mi_tpi_ok_ack_alloc(mp);
19567c478bd9Sstevel@tonic-gate 	if (mp == NULL) {
19577c478bd9Sstevel@tonic-gate 		/* Unable to reuse the T_DISCON_REQ for the ack. */
19587c478bd9Sstevel@tonic-gate 		udp_err_ack_prim(q, mp1, T_DISCON_REQ, TSYSERR, ENOMEM);
19597c478bd9Sstevel@tonic-gate 		return;
19607c478bd9Sstevel@tonic-gate 	}
19617c478bd9Sstevel@tonic-gate 
19627c478bd9Sstevel@tonic-gate 	if (udp->udp_family == AF_INET6) {
19637c478bd9Sstevel@tonic-gate 		int error;
19647c478bd9Sstevel@tonic-gate 
19657c478bd9Sstevel@tonic-gate 		/* Rebuild the header template */
19667c478bd9Sstevel@tonic-gate 		error = udp_build_hdrs(q, udp);
19677c478bd9Sstevel@tonic-gate 		if (error != 0) {
19687c478bd9Sstevel@tonic-gate 			udp_err_ack_prim(q, mp, T_DISCON_REQ, TSYSERR, error);
19697c478bd9Sstevel@tonic-gate 			freemsg(mp1);
19707c478bd9Sstevel@tonic-gate 			return;
19717c478bd9Sstevel@tonic-gate 		}
19727c478bd9Sstevel@tonic-gate 	}
19737c478bd9Sstevel@tonic-gate 	mutex_enter(&udpf->uf_lock);
19747c478bd9Sstevel@tonic-gate 	udp->udp_discon_pending = 1;
19757c478bd9Sstevel@tonic-gate 	mutex_exit(&udpf->uf_lock);
19767c478bd9Sstevel@tonic-gate 
19777c478bd9Sstevel@tonic-gate 	/* Append the T_OK_ACK to the T_BIND_REQ for udp_rput */
19787c478bd9Sstevel@tonic-gate 	linkb(mp1, mp);
1979ff550d0eSmasputra 
1980ff550d0eSmasputra 	if (udp->udp_family == AF_INET6)
1981ff550d0eSmasputra 		mp1 = ip_bind_v6(q, mp1, udp->udp_connp, NULL);
1982ff550d0eSmasputra 	else
1983ff550d0eSmasputra 		mp1 = ip_bind_v4(q, mp1, udp->udp_connp);
1984ff550d0eSmasputra 
1985ff550d0eSmasputra 	if (mp1 != NULL)
1986ff550d0eSmasputra 		udp_rput_other(_RD(q), mp1);
1987ff550d0eSmasputra 	else
1988ff550d0eSmasputra 		CONN_INC_REF(udp->udp_connp);
19897c478bd9Sstevel@tonic-gate }
19907c478bd9Sstevel@tonic-gate 
19917c478bd9Sstevel@tonic-gate /* This routine creates a T_ERROR_ACK message and passes it upstream. */
19927c478bd9Sstevel@tonic-gate static void
19937c478bd9Sstevel@tonic-gate udp_err_ack(queue_t *q, mblk_t *mp, t_scalar_t t_error, int sys_error)
19947c478bd9Sstevel@tonic-gate {
19957c478bd9Sstevel@tonic-gate 	if ((mp = mi_tpi_err_ack_alloc(mp, t_error, sys_error)) != NULL)
1996ff550d0eSmasputra 		putnext(UDP_RD(q), mp);
19977c478bd9Sstevel@tonic-gate }
19987c478bd9Sstevel@tonic-gate 
19997c478bd9Sstevel@tonic-gate /* Shorthand to generate and send TPI error acks to our client */
20007c478bd9Sstevel@tonic-gate static void
20017c478bd9Sstevel@tonic-gate udp_err_ack_prim(queue_t *q, mblk_t *mp, int primitive, t_scalar_t t_error,
20027c478bd9Sstevel@tonic-gate     int sys_error)
20037c478bd9Sstevel@tonic-gate {
20047c478bd9Sstevel@tonic-gate 	struct T_error_ack	*teackp;
20057c478bd9Sstevel@tonic-gate 
20067c478bd9Sstevel@tonic-gate 	if ((mp = tpi_ack_alloc(mp, sizeof (struct T_error_ack),
20077c478bd9Sstevel@tonic-gate 	    M_PCPROTO, T_ERROR_ACK)) != NULL) {
20087c478bd9Sstevel@tonic-gate 		teackp = (struct T_error_ack *)mp->b_rptr;
20097c478bd9Sstevel@tonic-gate 		teackp->ERROR_prim = primitive;
20107c478bd9Sstevel@tonic-gate 		teackp->TLI_error = t_error;
20117c478bd9Sstevel@tonic-gate 		teackp->UNIX_error = sys_error;
2012ff550d0eSmasputra 		putnext(UDP_RD(q), mp);
20137c478bd9Sstevel@tonic-gate 	}
20147c478bd9Sstevel@tonic-gate }
20157c478bd9Sstevel@tonic-gate 
20167c478bd9Sstevel@tonic-gate /*ARGSUSED*/
20177c478bd9Sstevel@tonic-gate static int
20187c478bd9Sstevel@tonic-gate udp_extra_priv_ports_get(queue_t *q, mblk_t *mp, caddr_t cp, cred_t *cr)
20197c478bd9Sstevel@tonic-gate {
20207c478bd9Sstevel@tonic-gate 	int i;
20217c478bd9Sstevel@tonic-gate 
20227c478bd9Sstevel@tonic-gate 	for (i = 0; i < udp_g_num_epriv_ports; i++) {
20237c478bd9Sstevel@tonic-gate 		if (udp_g_epriv_ports[i] != 0)
20247c478bd9Sstevel@tonic-gate 			(void) mi_mpprintf(mp, "%d ", udp_g_epriv_ports[i]);
20257c478bd9Sstevel@tonic-gate 	}
20267c478bd9Sstevel@tonic-gate 	return (0);
20277c478bd9Sstevel@tonic-gate }
20287c478bd9Sstevel@tonic-gate 
20297c478bd9Sstevel@tonic-gate /* ARGSUSED */
20307c478bd9Sstevel@tonic-gate static int
20317c478bd9Sstevel@tonic-gate udp_extra_priv_ports_add(queue_t *q, mblk_t *mp, char *value, caddr_t cp,
20327c478bd9Sstevel@tonic-gate     cred_t *cr)
20337c478bd9Sstevel@tonic-gate {
20347c478bd9Sstevel@tonic-gate 	long	new_value;
20357c478bd9Sstevel@tonic-gate 	int	i;
20367c478bd9Sstevel@tonic-gate 
20377c478bd9Sstevel@tonic-gate 	/*
20387c478bd9Sstevel@tonic-gate 	 * Fail the request if the new value does not lie within the
20397c478bd9Sstevel@tonic-gate 	 * port number limits.
20407c478bd9Sstevel@tonic-gate 	 */
20417c478bd9Sstevel@tonic-gate 	if (ddi_strtol(value, NULL, 10, &new_value) != 0 ||
20427c478bd9Sstevel@tonic-gate 	    new_value <= 0 || new_value >= 65536) {
20437c478bd9Sstevel@tonic-gate 		return (EINVAL);
20447c478bd9Sstevel@tonic-gate 	}
20457c478bd9Sstevel@tonic-gate 
20467c478bd9Sstevel@tonic-gate 	/* Check if the value is already in the list */
20477c478bd9Sstevel@tonic-gate 	for (i = 0; i < udp_g_num_epriv_ports; i++) {
20487c478bd9Sstevel@tonic-gate 		if (new_value == udp_g_epriv_ports[i]) {
20497c478bd9Sstevel@tonic-gate 			return (EEXIST);
20507c478bd9Sstevel@tonic-gate 		}
20517c478bd9Sstevel@tonic-gate 	}
20527c478bd9Sstevel@tonic-gate 	/* Find an empty slot */
20537c478bd9Sstevel@tonic-gate 	for (i = 0; i < udp_g_num_epriv_ports; i++) {
20547c478bd9Sstevel@tonic-gate 		if (udp_g_epriv_ports[i] == 0)
20557c478bd9Sstevel@tonic-gate 			break;
20567c478bd9Sstevel@tonic-gate 	}
20577c478bd9Sstevel@tonic-gate 	if (i == udp_g_num_epriv_ports) {
20587c478bd9Sstevel@tonic-gate 		return (EOVERFLOW);
20597c478bd9Sstevel@tonic-gate 	}
20607c478bd9Sstevel@tonic-gate 
20617c478bd9Sstevel@tonic-gate 	/* Set the new value */
20627c478bd9Sstevel@tonic-gate 	udp_g_epriv_ports[i] = (in_port_t)new_value;
20637c478bd9Sstevel@tonic-gate 	return (0);
20647c478bd9Sstevel@tonic-gate }
20657c478bd9Sstevel@tonic-gate 
20667c478bd9Sstevel@tonic-gate /* ARGSUSED */
20677c478bd9Sstevel@tonic-gate static int
20687c478bd9Sstevel@tonic-gate udp_extra_priv_ports_del(queue_t *q, mblk_t *mp, char *value, caddr_t cp,
20697c478bd9Sstevel@tonic-gate     cred_t *cr)
20707c478bd9Sstevel@tonic-gate {
20717c478bd9Sstevel@tonic-gate 	long	new_value;
20727c478bd9Sstevel@tonic-gate 	int	i;
20737c478bd9Sstevel@tonic-gate 
20747c478bd9Sstevel@tonic-gate 	/*
20757c478bd9Sstevel@tonic-gate 	 * Fail the request if the new value does not lie within the
20767c478bd9Sstevel@tonic-gate 	 * port number limits.
20777c478bd9Sstevel@tonic-gate 	 */
20787c478bd9Sstevel@tonic-gate 	if (ddi_strtol(value, NULL, 10, &new_value) != 0 ||
20797c478bd9Sstevel@tonic-gate 	    new_value <= 0 || new_value >= 65536) {
20807c478bd9Sstevel@tonic-gate 		return (EINVAL);
20817c478bd9Sstevel@tonic-gate 	}
20827c478bd9Sstevel@tonic-gate 
20837c478bd9Sstevel@tonic-gate 	/* Check that the value is already in the list */
20847c478bd9Sstevel@tonic-gate 	for (i = 0; i < udp_g_num_epriv_ports; i++) {
20857c478bd9Sstevel@tonic-gate 		if (udp_g_epriv_ports[i] == new_value)
20867c478bd9Sstevel@tonic-gate 			break;
20877c478bd9Sstevel@tonic-gate 	}
20887c478bd9Sstevel@tonic-gate 	if (i == udp_g_num_epriv_ports) {
20897c478bd9Sstevel@tonic-gate 		return (ESRCH);
20907c478bd9Sstevel@tonic-gate 	}
20917c478bd9Sstevel@tonic-gate 
20927c478bd9Sstevel@tonic-gate 	/* Clear the value */
20937c478bd9Sstevel@tonic-gate 	udp_g_epriv_ports[i] = 0;
20947c478bd9Sstevel@tonic-gate 	return (0);
20957c478bd9Sstevel@tonic-gate }
20967c478bd9Sstevel@tonic-gate 
20977c478bd9Sstevel@tonic-gate /* At minimum we need 4 bytes of UDP header */
20987c478bd9Sstevel@tonic-gate #define	ICMP_MIN_UDP_HDR	4
20997c478bd9Sstevel@tonic-gate 
21007c478bd9Sstevel@tonic-gate /*
21017c478bd9Sstevel@tonic-gate  * udp_icmp_error is called by udp_rput to process ICMP msgs. passed up by IP.
21027c478bd9Sstevel@tonic-gate  * Generates the appropriate T_UDERROR_IND for permanent (non-transient) errors.
21037c478bd9Sstevel@tonic-gate  * Assumes that IP has pulled up everything up to and including the ICMP header.
21047c478bd9Sstevel@tonic-gate  * An M_CTL could potentially come here from some other module (i.e. if UDP
21057c478bd9Sstevel@tonic-gate  * is pushed on some module other than IP). Thus, if we find that the M_CTL
21067c478bd9Sstevel@tonic-gate  * does not have enough ICMP information , following STREAMS conventions,
21077c478bd9Sstevel@tonic-gate  * we send it upstream assuming it is an M_CTL we don't understand.
21087c478bd9Sstevel@tonic-gate  */
21097c478bd9Sstevel@tonic-gate static void
21107c478bd9Sstevel@tonic-gate udp_icmp_error(queue_t *q, mblk_t *mp)
21117c478bd9Sstevel@tonic-gate {
21127c478bd9Sstevel@tonic-gate 	icmph_t *icmph;
21137c478bd9Sstevel@tonic-gate 	ipha_t	*ipha;
21147c478bd9Sstevel@tonic-gate 	int	iph_hdr_length;
21157c478bd9Sstevel@tonic-gate 	udpha_t	*udpha;
21167c478bd9Sstevel@tonic-gate 	sin_t	sin;
21177c478bd9Sstevel@tonic-gate 	sin6_t	sin6;
21187c478bd9Sstevel@tonic-gate 	mblk_t	*mp1;
21197c478bd9Sstevel@tonic-gate 	int	error = 0;
21207c478bd9Sstevel@tonic-gate 	size_t	mp_size = MBLKL(mp);
2121ff550d0eSmasputra 	udp_t	*udp = Q_TO_UDP(q);
21227c478bd9Sstevel@tonic-gate 
21237c478bd9Sstevel@tonic-gate 	/*
21247c478bd9Sstevel@tonic-gate 	 * Assume IP provides aligned packets - otherwise toss
21257c478bd9Sstevel@tonic-gate 	 */
21267c478bd9Sstevel@tonic-gate 	if (!OK_32PTR(mp->b_rptr)) {
21277c478bd9Sstevel@tonic-gate 		freemsg(mp);
21287c478bd9Sstevel@tonic-gate 		return;
21297c478bd9Sstevel@tonic-gate 	}
21307c478bd9Sstevel@tonic-gate 
21317c478bd9Sstevel@tonic-gate 	/*
21327c478bd9Sstevel@tonic-gate 	 * Verify that we have a complete IP header and the application has
21337c478bd9Sstevel@tonic-gate 	 * asked for errors. If not, send it upstream.
21347c478bd9Sstevel@tonic-gate 	 */
21357c478bd9Sstevel@tonic-gate 	if (!udp->udp_dgram_errind || mp_size < sizeof (ipha_t)) {
21367c478bd9Sstevel@tonic-gate noticmpv4:
2137ff550d0eSmasputra 		putnext(UDP_RD(q), mp);
21387c478bd9Sstevel@tonic-gate 		return;
21397c478bd9Sstevel@tonic-gate 	}
21407c478bd9Sstevel@tonic-gate 
21417c478bd9Sstevel@tonic-gate 	ipha = (ipha_t *)mp->b_rptr;
21427c478bd9Sstevel@tonic-gate 	/*
21437c478bd9Sstevel@tonic-gate 	 * Verify IP version. Anything other than IPv4 or IPv6 packet is sent
21447c478bd9Sstevel@tonic-gate 	 * upstream. ICMPv6  is handled in udp_icmp_error_ipv6.
21457c478bd9Sstevel@tonic-gate 	 */
21467c478bd9Sstevel@tonic-gate 	switch (IPH_HDR_VERSION(ipha)) {
21477c478bd9Sstevel@tonic-gate 	case IPV6_VERSION:
21487c478bd9Sstevel@tonic-gate 		udp_icmp_error_ipv6(q, mp);
21497c478bd9Sstevel@tonic-gate 		return;
21507c478bd9Sstevel@tonic-gate 	case IPV4_VERSION:
21517c478bd9Sstevel@tonic-gate 		break;
21527c478bd9Sstevel@tonic-gate 	default:
21537c478bd9Sstevel@tonic-gate 		goto noticmpv4;
21547c478bd9Sstevel@tonic-gate 	}
21557c478bd9Sstevel@tonic-gate 
21567c478bd9Sstevel@tonic-gate 	/* Skip past the outer IP and ICMP headers */
21577c478bd9Sstevel@tonic-gate 	iph_hdr_length = IPH_HDR_LENGTH(ipha);
21587c478bd9Sstevel@tonic-gate 	icmph = (icmph_t *)&mp->b_rptr[iph_hdr_length];
21597c478bd9Sstevel@tonic-gate 	/*
21607c478bd9Sstevel@tonic-gate 	 * If we don't have the correct outer IP header length or if the ULP
21617c478bd9Sstevel@tonic-gate 	 * is not IPPROTO_ICMP or if we don't have a complete inner IP header
21627c478bd9Sstevel@tonic-gate 	 * send the packet upstream.
21637c478bd9Sstevel@tonic-gate 	 */
21647c478bd9Sstevel@tonic-gate 	if (iph_hdr_length < sizeof (ipha_t) ||
21657c478bd9Sstevel@tonic-gate 	    ipha->ipha_protocol != IPPROTO_ICMP ||
21667c478bd9Sstevel@tonic-gate 	    (ipha_t *)&icmph[1] + 1 > (ipha_t *)mp->b_wptr) {
21677c478bd9Sstevel@tonic-gate 		goto noticmpv4;
21687c478bd9Sstevel@tonic-gate 	}
21697c478bd9Sstevel@tonic-gate 	ipha = (ipha_t *)&icmph[1];
21707c478bd9Sstevel@tonic-gate 
21717c478bd9Sstevel@tonic-gate 	/* Skip past the inner IP and find the ULP header */
21727c478bd9Sstevel@tonic-gate 	iph_hdr_length = IPH_HDR_LENGTH(ipha);
21737c478bd9Sstevel@tonic-gate 	udpha = (udpha_t *)((char *)ipha + iph_hdr_length);
21747c478bd9Sstevel@tonic-gate 	/*
21757c478bd9Sstevel@tonic-gate 	 * If we don't have the correct inner IP header length or if the ULP
21767c478bd9Sstevel@tonic-gate 	 * is not IPPROTO_UDP or if we don't have at least ICMP_MIN_UDP_HDR
21777c478bd9Sstevel@tonic-gate 	 * bytes of UDP header, send it upstream.
21787c478bd9Sstevel@tonic-gate 	 */
21797c478bd9Sstevel@tonic-gate 	if (iph_hdr_length < sizeof (ipha_t) ||
21807c478bd9Sstevel@tonic-gate 	    ipha->ipha_protocol != IPPROTO_UDP ||
21817c478bd9Sstevel@tonic-gate 	    (uchar_t *)udpha + ICMP_MIN_UDP_HDR > mp->b_wptr) {
21827c478bd9Sstevel@tonic-gate 		goto noticmpv4;
21837c478bd9Sstevel@tonic-gate 	}
21847c478bd9Sstevel@tonic-gate 
21857c478bd9Sstevel@tonic-gate 	switch (icmph->icmph_type) {
21867c478bd9Sstevel@tonic-gate 	case ICMP_DEST_UNREACHABLE:
21877c478bd9Sstevel@tonic-gate 		switch (icmph->icmph_code) {
21887c478bd9Sstevel@tonic-gate 		case ICMP_FRAGMENTATION_NEEDED:
21897c478bd9Sstevel@tonic-gate 			/*
21907c478bd9Sstevel@tonic-gate 			 * IP has already adjusted the path MTU.
21917c478bd9Sstevel@tonic-gate 			 * XXX Somehow pass MTU indication to application?
21927c478bd9Sstevel@tonic-gate 			 */
21937c478bd9Sstevel@tonic-gate 			break;
21947c478bd9Sstevel@tonic-gate 		case ICMP_PORT_UNREACHABLE:
21957c478bd9Sstevel@tonic-gate 		case ICMP_PROTOCOL_UNREACHABLE:
21967c478bd9Sstevel@tonic-gate 			error = ECONNREFUSED;
21977c478bd9Sstevel@tonic-gate 			break;
21987c478bd9Sstevel@tonic-gate 		default:
21997c478bd9Sstevel@tonic-gate 			/* Transient errors */
22007c478bd9Sstevel@tonic-gate 			break;
22017c478bd9Sstevel@tonic-gate 		}
22027c478bd9Sstevel@tonic-gate 		break;
22037c478bd9Sstevel@tonic-gate 	default:
22047c478bd9Sstevel@tonic-gate 		/* Transient errors */
22057c478bd9Sstevel@tonic-gate 		break;
22067c478bd9Sstevel@tonic-gate 	}
22077c478bd9Sstevel@tonic-gate 	if (error == 0) {
22087c478bd9Sstevel@tonic-gate 		freemsg(mp);
22097c478bd9Sstevel@tonic-gate 		return;
22107c478bd9Sstevel@tonic-gate 	}
22117c478bd9Sstevel@tonic-gate 
22127c478bd9Sstevel@tonic-gate 	switch (udp->udp_family) {
22137c478bd9Sstevel@tonic-gate 	case AF_INET:
22147c478bd9Sstevel@tonic-gate 		sin = sin_null;
22157c478bd9Sstevel@tonic-gate 		sin.sin_family = AF_INET;
22167c478bd9Sstevel@tonic-gate 		sin.sin_addr.s_addr = ipha->ipha_dst;
22177c478bd9Sstevel@tonic-gate 		sin.sin_port = udpha->uha_dst_port;
22187c478bd9Sstevel@tonic-gate 		mp1 = mi_tpi_uderror_ind((char *)&sin, sizeof (sin_t), NULL, 0,
22197c478bd9Sstevel@tonic-gate 		    error);
22207c478bd9Sstevel@tonic-gate 		break;
22217c478bd9Sstevel@tonic-gate 	case AF_INET6:
22227c478bd9Sstevel@tonic-gate 		sin6 = sin6_null;
22237c478bd9Sstevel@tonic-gate 		sin6.sin6_family = AF_INET6;
22247c478bd9Sstevel@tonic-gate 		IN6_IPADDR_TO_V4MAPPED(ipha->ipha_dst, &sin6.sin6_addr);
22257c478bd9Sstevel@tonic-gate 		sin6.sin6_port = udpha->uha_dst_port;
22267c478bd9Sstevel@tonic-gate 
22277c478bd9Sstevel@tonic-gate 		mp1 = mi_tpi_uderror_ind((char *)&sin6, sizeof (sin6_t),
22287c478bd9Sstevel@tonic-gate 		    NULL, 0, error);
22297c478bd9Sstevel@tonic-gate 		break;
22307c478bd9Sstevel@tonic-gate 	}
22317c478bd9Sstevel@tonic-gate 	if (mp1)
2232ff550d0eSmasputra 		putnext(UDP_RD(q), mp1);
22337c478bd9Sstevel@tonic-gate 	freemsg(mp);
22347c478bd9Sstevel@tonic-gate }
22357c478bd9Sstevel@tonic-gate 
22367c478bd9Sstevel@tonic-gate /*
22377c478bd9Sstevel@tonic-gate  * udp_icmp_error_ipv6 is called by udp_icmp_error to process ICMP for IPv6.
22387c478bd9Sstevel@tonic-gate  * Generates the appropriate T_UDERROR_IND for permanent (non-transient) errors.
22397c478bd9Sstevel@tonic-gate  * Assumes that IP has pulled up all the extension headers as well as the
22407c478bd9Sstevel@tonic-gate  * ICMPv6 header.
22417c478bd9Sstevel@tonic-gate  * An M_CTL could potentially come here from some other module (i.e. if UDP
22427c478bd9Sstevel@tonic-gate  * is pushed on some module other than IP). Thus, if we find that the M_CTL
22437c478bd9Sstevel@tonic-gate  * does not have enough ICMP information , following STREAMS conventions,
22447c478bd9Sstevel@tonic-gate  * we send it upstream assuming it is an M_CTL we don't understand. The reason
22457c478bd9Sstevel@tonic-gate  * it might get here is if the non-ICMP M_CTL accidently has 6 in the version
22467c478bd9Sstevel@tonic-gate  * field (when cast to ipha_t in udp_icmp_error).
22477c478bd9Sstevel@tonic-gate  */
22487c478bd9Sstevel@tonic-gate static void
22497c478bd9Sstevel@tonic-gate udp_icmp_error_ipv6(queue_t *q, mblk_t *mp)
22507c478bd9Sstevel@tonic-gate {
22517c478bd9Sstevel@tonic-gate 	icmp6_t		*icmp6;
22527c478bd9Sstevel@tonic-gate 	ip6_t		*ip6h, *outer_ip6h;
22537c478bd9Sstevel@tonic-gate 	uint16_t	hdr_length;
22547c478bd9Sstevel@tonic-gate 	uint8_t		*nexthdrp;
22557c478bd9Sstevel@tonic-gate 	udpha_t		*udpha;
22567c478bd9Sstevel@tonic-gate 	sin6_t		sin6;
22577c478bd9Sstevel@tonic-gate 	mblk_t		*mp1;
22587c478bd9Sstevel@tonic-gate 	int		error = 0;
22597c478bd9Sstevel@tonic-gate 	size_t		mp_size = MBLKL(mp);
2260ff550d0eSmasputra 	udp_t		*udp = Q_TO_UDP(q);
22617c478bd9Sstevel@tonic-gate 
22627c478bd9Sstevel@tonic-gate 	/*
22637c478bd9Sstevel@tonic-gate 	 * Verify that we have a complete IP header. If not, send it upstream.
22647c478bd9Sstevel@tonic-gate 	 */
22657c478bd9Sstevel@tonic-gate 	if (mp_size < sizeof (ip6_t)) {
22667c478bd9Sstevel@tonic-gate noticmpv6:
2267ff550d0eSmasputra 		putnext(UDP_RD(q), mp);
22687c478bd9Sstevel@tonic-gate 		return;
22697c478bd9Sstevel@tonic-gate 	}
22707c478bd9Sstevel@tonic-gate 
22717c478bd9Sstevel@tonic-gate 	outer_ip6h = (ip6_t *)mp->b_rptr;
22727c478bd9Sstevel@tonic-gate 	/*
22737c478bd9Sstevel@tonic-gate 	 * Verify this is an ICMPV6 packet, else send it upstream
22747c478bd9Sstevel@tonic-gate 	 */
22757c478bd9Sstevel@tonic-gate 	if (outer_ip6h->ip6_nxt == IPPROTO_ICMPV6) {
22767c478bd9Sstevel@tonic-gate 		hdr_length = IPV6_HDR_LEN;
22777c478bd9Sstevel@tonic-gate 	} else if (!ip_hdr_length_nexthdr_v6(mp, outer_ip6h, &hdr_length,
22787c478bd9Sstevel@tonic-gate 	    &nexthdrp) ||
22797c478bd9Sstevel@tonic-gate 	    *nexthdrp != IPPROTO_ICMPV6) {
22807c478bd9Sstevel@tonic-gate 		goto noticmpv6;
22817c478bd9Sstevel@tonic-gate 	}
22827c478bd9Sstevel@tonic-gate 	icmp6 = (icmp6_t *)&mp->b_rptr[hdr_length];
22837c478bd9Sstevel@tonic-gate 	ip6h = (ip6_t *)&icmp6[1];
22847c478bd9Sstevel@tonic-gate 	/*
22857c478bd9Sstevel@tonic-gate 	 * Verify we have a complete ICMP and inner IP header.
22867c478bd9Sstevel@tonic-gate 	 */
22877c478bd9Sstevel@tonic-gate 	if ((uchar_t *)&ip6h[1] > mp->b_wptr)
22887c478bd9Sstevel@tonic-gate 		goto noticmpv6;
22897c478bd9Sstevel@tonic-gate 
22907c478bd9Sstevel@tonic-gate 	if (!ip_hdr_length_nexthdr_v6(mp, ip6h, &hdr_length, &nexthdrp))
22917c478bd9Sstevel@tonic-gate 		goto noticmpv6;
22927c478bd9Sstevel@tonic-gate 	udpha = (udpha_t *)((char *)ip6h + hdr_length);
22937c478bd9Sstevel@tonic-gate 	/*
22947c478bd9Sstevel@tonic-gate 	 * Validate inner header. If the ULP is not IPPROTO_UDP or if we don't
22957c478bd9Sstevel@tonic-gate 	 * have at least ICMP_MIN_UDP_HDR bytes of  UDP header send the
22967c478bd9Sstevel@tonic-gate 	 * packet upstream.
22977c478bd9Sstevel@tonic-gate 	 */
22987c478bd9Sstevel@tonic-gate 	if ((*nexthdrp != IPPROTO_UDP) ||
22997c478bd9Sstevel@tonic-gate 	    ((uchar_t *)udpha + ICMP_MIN_UDP_HDR) > mp->b_wptr) {
23007c478bd9Sstevel@tonic-gate 		goto noticmpv6;
23017c478bd9Sstevel@tonic-gate 	}
23027c478bd9Sstevel@tonic-gate 
23037c478bd9Sstevel@tonic-gate 	switch (icmp6->icmp6_type) {
23047c478bd9Sstevel@tonic-gate 	case ICMP6_DST_UNREACH:
23057c478bd9Sstevel@tonic-gate 		switch (icmp6->icmp6_code) {
23067c478bd9Sstevel@tonic-gate 		case ICMP6_DST_UNREACH_NOPORT:
23077c478bd9Sstevel@tonic-gate 			error = ECONNREFUSED;
23087c478bd9Sstevel@tonic-gate 			break;
23097c478bd9Sstevel@tonic-gate 		case ICMP6_DST_UNREACH_ADMIN:
23107c478bd9Sstevel@tonic-gate 		case ICMP6_DST_UNREACH_NOROUTE:
23117c478bd9Sstevel@tonic-gate 		case ICMP6_DST_UNREACH_BEYONDSCOPE:
23127c478bd9Sstevel@tonic-gate 		case ICMP6_DST_UNREACH_ADDR:
23137c478bd9Sstevel@tonic-gate 			/* Transient errors */
23147c478bd9Sstevel@tonic-gate 			break;
23157c478bd9Sstevel@tonic-gate 		default:
23167c478bd9Sstevel@tonic-gate 			break;
23177c478bd9Sstevel@tonic-gate 		}
23187c478bd9Sstevel@tonic-gate 		break;
23197c478bd9Sstevel@tonic-gate 	case ICMP6_PACKET_TOO_BIG: {
23207c478bd9Sstevel@tonic-gate 		struct T_unitdata_ind	*tudi;
23217c478bd9Sstevel@tonic-gate 		struct T_opthdr		*toh;
23227c478bd9Sstevel@tonic-gate 		size_t			udi_size;
23237c478bd9Sstevel@tonic-gate 		mblk_t			*newmp;
23247c478bd9Sstevel@tonic-gate 		t_scalar_t		opt_length = sizeof (struct T_opthdr) +
23257c478bd9Sstevel@tonic-gate 		    sizeof (struct ip6_mtuinfo);
23267c478bd9Sstevel@tonic-gate 		sin6_t			*sin6;
23277c478bd9Sstevel@tonic-gate 		struct ip6_mtuinfo	*mtuinfo;
23287c478bd9Sstevel@tonic-gate 
23297c478bd9Sstevel@tonic-gate 		/*
23307c478bd9Sstevel@tonic-gate 		 * If the application has requested to receive path mtu
23317c478bd9Sstevel@tonic-gate 		 * information, send up an empty message containing an
23327c478bd9Sstevel@tonic-gate 		 * IPV6_PATHMTU ancillary data item.
23337c478bd9Sstevel@tonic-gate 		 */
23347c478bd9Sstevel@tonic-gate 		if (!udp->udp_ipv6_recvpathmtu)
23357c478bd9Sstevel@tonic-gate 			break;
23367c478bd9Sstevel@tonic-gate 
23377c478bd9Sstevel@tonic-gate 		udi_size = sizeof (struct T_unitdata_ind) + sizeof (sin6_t) +
23387c478bd9Sstevel@tonic-gate 		    opt_length;
23397c478bd9Sstevel@tonic-gate 		if ((newmp = allocb(udi_size, BPRI_MED)) == NULL) {
23407c478bd9Sstevel@tonic-gate 			BUMP_MIB(&udp_mib, udpInErrors);
23417c478bd9Sstevel@tonic-gate 			break;
23427c478bd9Sstevel@tonic-gate 		}
23437c478bd9Sstevel@tonic-gate 
23447c478bd9Sstevel@tonic-gate 		/*
23457c478bd9Sstevel@tonic-gate 		 * newmp->b_cont is left to NULL on purpose.  This is an
23467c478bd9Sstevel@tonic-gate 		 * empty message containing only ancillary data.
23477c478bd9Sstevel@tonic-gate 		 */
23487c478bd9Sstevel@tonic-gate 		newmp->b_datap->db_type = M_PROTO;
23497c478bd9Sstevel@tonic-gate 		tudi = (struct T_unitdata_ind *)newmp->b_rptr;
23507c478bd9Sstevel@tonic-gate 		newmp->b_wptr = (uchar_t *)tudi + udi_size;
23517c478bd9Sstevel@tonic-gate 		tudi->PRIM_type = T_UNITDATA_IND;
23527c478bd9Sstevel@tonic-gate 		tudi->SRC_length = sizeof (sin6_t);
23537c478bd9Sstevel@tonic-gate 		tudi->SRC_offset = sizeof (struct T_unitdata_ind);
23547c478bd9Sstevel@tonic-gate 		tudi->OPT_offset = tudi->SRC_offset + sizeof (sin6_t);
23557c478bd9Sstevel@tonic-gate 		tudi->OPT_length = opt_length;
23567c478bd9Sstevel@tonic-gate 
23577c478bd9Sstevel@tonic-gate 		sin6 = (sin6_t *)&tudi[1];
23587c478bd9Sstevel@tonic-gate 		bzero(sin6, sizeof (sin6_t));
23597c478bd9Sstevel@tonic-gate 		sin6->sin6_family = AF_INET6;
23607c478bd9Sstevel@tonic-gate 		sin6->sin6_addr = udp->udp_v6dst;
23617c478bd9Sstevel@tonic-gate 
23627c478bd9Sstevel@tonic-gate 		toh = (struct T_opthdr *)&sin6[1];
23637c478bd9Sstevel@tonic-gate 		toh->level = IPPROTO_IPV6;
23647c478bd9Sstevel@tonic-gate 		toh->name = IPV6_PATHMTU;
23657c478bd9Sstevel@tonic-gate 		toh->len = opt_length;
23667c478bd9Sstevel@tonic-gate 		toh->status = 0;
23677c478bd9Sstevel@tonic-gate 
23687c478bd9Sstevel@tonic-gate 		mtuinfo = (struct ip6_mtuinfo *)&toh[1];
23697c478bd9Sstevel@tonic-gate 		bzero(mtuinfo, sizeof (struct ip6_mtuinfo));
23707c478bd9Sstevel@tonic-gate 		mtuinfo->ip6m_addr.sin6_family = AF_INET6;
23717c478bd9Sstevel@tonic-gate 		mtuinfo->ip6m_addr.sin6_addr = ip6h->ip6_dst;
23727c478bd9Sstevel@tonic-gate 		mtuinfo->ip6m_mtu = icmp6->icmp6_mtu;
23737c478bd9Sstevel@tonic-gate 		/*
23747c478bd9Sstevel@tonic-gate 		 * We've consumed everything we need from the original
23757c478bd9Sstevel@tonic-gate 		 * message.  Free it, then send our empty message.
23767c478bd9Sstevel@tonic-gate 		 */
23777c478bd9Sstevel@tonic-gate 		freemsg(mp);
2378ff550d0eSmasputra 		putnext(UDP_RD(q), newmp);
23797c478bd9Sstevel@tonic-gate 		return;
23807c478bd9Sstevel@tonic-gate 	}
23817c478bd9Sstevel@tonic-gate 	case ICMP6_TIME_EXCEEDED:
23827c478bd9Sstevel@tonic-gate 		/* Transient errors */
23837c478bd9Sstevel@tonic-gate 		break;
23847c478bd9Sstevel@tonic-gate 	case ICMP6_PARAM_PROB:
23857c478bd9Sstevel@tonic-gate 		/* If this corresponds to an ICMP_PROTOCOL_UNREACHABLE */
23867c478bd9Sstevel@tonic-gate 		if (icmp6->icmp6_code == ICMP6_PARAMPROB_NEXTHEADER &&
23877c478bd9Sstevel@tonic-gate 		    (uchar_t *)ip6h + icmp6->icmp6_pptr ==
23887c478bd9Sstevel@tonic-gate 		    (uchar_t *)nexthdrp) {
23897c478bd9Sstevel@tonic-gate 			error = ECONNREFUSED;
23907c478bd9Sstevel@tonic-gate 			break;
23917c478bd9Sstevel@tonic-gate 		}
23927c478bd9Sstevel@tonic-gate 		break;
23937c478bd9Sstevel@tonic-gate 	}
23947c478bd9Sstevel@tonic-gate 	if (error == 0) {
23957c478bd9Sstevel@tonic-gate 		freemsg(mp);
23967c478bd9Sstevel@tonic-gate 		return;
23977c478bd9Sstevel@tonic-gate 	}
23987c478bd9Sstevel@tonic-gate 
23997c478bd9Sstevel@tonic-gate 	sin6 = sin6_null;
24007c478bd9Sstevel@tonic-gate 	sin6.sin6_family = AF_INET6;
24017c478bd9Sstevel@tonic-gate 	sin6.sin6_addr = ip6h->ip6_dst;
24027c478bd9Sstevel@tonic-gate 	sin6.sin6_port = udpha->uha_dst_port;
24037c478bd9Sstevel@tonic-gate 	sin6.sin6_flowinfo = ip6h->ip6_vcf & ~IPV6_VERS_AND_FLOW_MASK;
24047c478bd9Sstevel@tonic-gate 
24057c478bd9Sstevel@tonic-gate 	mp1 = mi_tpi_uderror_ind((char *)&sin6, sizeof (sin6_t), NULL, 0,
24067c478bd9Sstevel@tonic-gate 	    error);
24077c478bd9Sstevel@tonic-gate 	if (mp1)
2408ff550d0eSmasputra 		putnext(UDP_RD(q), mp1);
24097c478bd9Sstevel@tonic-gate 	freemsg(mp);
24107c478bd9Sstevel@tonic-gate }
24117c478bd9Sstevel@tonic-gate 
24127c478bd9Sstevel@tonic-gate /*
24137c478bd9Sstevel@tonic-gate  * This routine responds to T_ADDR_REQ messages.  It is called by udp_wput.
24147c478bd9Sstevel@tonic-gate  * The local address is filled in if endpoint is bound. The remote address
24157c478bd9Sstevel@tonic-gate  * is filled in if remote address has been precified ("connected endpoint")
24167c478bd9Sstevel@tonic-gate  * (The concept of connected CLTS sockets is alien to published TPI
24177c478bd9Sstevel@tonic-gate  *  but we support it anyway).
24187c478bd9Sstevel@tonic-gate  */
24197c478bd9Sstevel@tonic-gate static void
24207c478bd9Sstevel@tonic-gate udp_addr_req(queue_t *q, mblk_t *mp)
24217c478bd9Sstevel@tonic-gate {
24227c478bd9Sstevel@tonic-gate 	sin_t	*sin;
24237c478bd9Sstevel@tonic-gate 	sin6_t	*sin6;
24247c478bd9Sstevel@tonic-gate 	mblk_t	*ackmp;
24257c478bd9Sstevel@tonic-gate 	struct T_addr_ack *taa;
2426ff550d0eSmasputra 	udp_t	*udp = Q_TO_UDP(q);
24277c478bd9Sstevel@tonic-gate 
24287c478bd9Sstevel@tonic-gate 	/* Make it large enough for worst case */
24297c478bd9Sstevel@tonic-gate 	ackmp = reallocb(mp, sizeof (struct T_addr_ack) +
24307c478bd9Sstevel@tonic-gate 	    2 * sizeof (sin6_t), 1);
24317c478bd9Sstevel@tonic-gate 	if (ackmp == NULL) {
24327c478bd9Sstevel@tonic-gate 		udp_err_ack(q, mp, TSYSERR, ENOMEM);
24337c478bd9Sstevel@tonic-gate 		return;
24347c478bd9Sstevel@tonic-gate 	}
24357c478bd9Sstevel@tonic-gate 	taa = (struct T_addr_ack *)ackmp->b_rptr;
24367c478bd9Sstevel@tonic-gate 
24377c478bd9Sstevel@tonic-gate 	bzero(taa, sizeof (struct T_addr_ack));
24387c478bd9Sstevel@tonic-gate 	ackmp->b_wptr = (uchar_t *)&taa[1];
24397c478bd9Sstevel@tonic-gate 
24407c478bd9Sstevel@tonic-gate 	taa->PRIM_type = T_ADDR_ACK;
24417c478bd9Sstevel@tonic-gate 	ackmp->b_datap->db_type = M_PCPROTO;
24427c478bd9Sstevel@tonic-gate 	/*
24437c478bd9Sstevel@tonic-gate 	 * Note: Following code assumes 32 bit alignment of basic
24447c478bd9Sstevel@tonic-gate 	 * data structures like sin_t and struct T_addr_ack.
24457c478bd9Sstevel@tonic-gate 	 */
24467c478bd9Sstevel@tonic-gate 	if (udp->udp_state != TS_UNBND) {
24477c478bd9Sstevel@tonic-gate 		/*
24487c478bd9Sstevel@tonic-gate 		 * Fill in local address first
24497c478bd9Sstevel@tonic-gate 		 */
24507c478bd9Sstevel@tonic-gate 		taa->LOCADDR_offset = sizeof (*taa);
24517c478bd9Sstevel@tonic-gate 		if (udp->udp_family == AF_INET) {
24527c478bd9Sstevel@tonic-gate 			taa->LOCADDR_length = sizeof (sin_t);
24537c478bd9Sstevel@tonic-gate 			sin = (sin_t *)&taa[1];
24547c478bd9Sstevel@tonic-gate 			/* Fill zeroes and then initialize non-zero fields */
24557c478bd9Sstevel@tonic-gate 			*sin = sin_null;
24567c478bd9Sstevel@tonic-gate 			sin->sin_family = AF_INET;
24577c478bd9Sstevel@tonic-gate 			if (!IN6_IS_ADDR_V4MAPPED_ANY(&udp->udp_v6src) &&
24587c478bd9Sstevel@tonic-gate 			    !IN6_IS_ADDR_UNSPECIFIED(&udp->udp_v6src)) {
24597c478bd9Sstevel@tonic-gate 				IN6_V4MAPPED_TO_IPADDR(&udp->udp_v6src,
24607c478bd9Sstevel@tonic-gate 				    sin->sin_addr.s_addr);
24617c478bd9Sstevel@tonic-gate 			} else {
24627c478bd9Sstevel@tonic-gate 				/*
24637c478bd9Sstevel@tonic-gate 				 * INADDR_ANY
24647c478bd9Sstevel@tonic-gate 				 * udp_v6src is not set, we might be bound to
24657c478bd9Sstevel@tonic-gate 				 * broadcast/multicast. Use udp_bound_v6src as
24667c478bd9Sstevel@tonic-gate 				 * local address instead (that could
24677c478bd9Sstevel@tonic-gate 				 * also still be INADDR_ANY)
24687c478bd9Sstevel@tonic-gate 				 */
24697c478bd9Sstevel@tonic-gate 				IN6_V4MAPPED_TO_IPADDR(&udp->udp_bound_v6src,
24707c478bd9Sstevel@tonic-gate 				    sin->sin_addr.s_addr);
24717c478bd9Sstevel@tonic-gate 			}
24727c478bd9Sstevel@tonic-gate 			sin->sin_port = udp->udp_port;
24737c478bd9Sstevel@tonic-gate 			ackmp->b_wptr = (uchar_t *)&sin[1];
24747c478bd9Sstevel@tonic-gate 			if (udp->udp_state == TS_DATA_XFER) {
24757c478bd9Sstevel@tonic-gate 				/*
24767c478bd9Sstevel@tonic-gate 				 * connected, fill remote address too
24777c478bd9Sstevel@tonic-gate 				 */
24787c478bd9Sstevel@tonic-gate 				taa->REMADDR_length = sizeof (sin_t);
24797c478bd9Sstevel@tonic-gate 				/* assumed 32-bit alignment */
24807c478bd9Sstevel@tonic-gate 				taa->REMADDR_offset = taa->LOCADDR_offset +
24817c478bd9Sstevel@tonic-gate 				    taa->LOCADDR_length;
24827c478bd9Sstevel@tonic-gate 
24837c478bd9Sstevel@tonic-gate 				sin = (sin_t *)(ackmp->b_rptr +
24847c478bd9Sstevel@tonic-gate 				    taa->REMADDR_offset);
24857c478bd9Sstevel@tonic-gate 				/* initialize */
24867c478bd9Sstevel@tonic-gate 				*sin = sin_null;
24877c478bd9Sstevel@tonic-gate 				sin->sin_family = AF_INET;
24887c478bd9Sstevel@tonic-gate 				sin->sin_addr.s_addr =
24897c478bd9Sstevel@tonic-gate 				    V4_PART_OF_V6(udp->udp_v6dst);
24907c478bd9Sstevel@tonic-gate 				sin->sin_port = udp->udp_dstport;
24917c478bd9Sstevel@tonic-gate 				ackmp->b_wptr = (uchar_t *)&sin[1];
24927c478bd9Sstevel@tonic-gate 			}
24937c478bd9Sstevel@tonic-gate 		} else {
24947c478bd9Sstevel@tonic-gate 			taa->LOCADDR_length = sizeof (sin6_t);
24957c478bd9Sstevel@tonic-gate 			sin6 = (sin6_t *)&taa[1];
24967c478bd9Sstevel@tonic-gate 			/* Fill zeroes and then initialize non-zero fields */
24977c478bd9Sstevel@tonic-gate 			*sin6 = sin6_null;
24987c478bd9Sstevel@tonic-gate 			sin6->sin6_family = AF_INET6;
24997c478bd9Sstevel@tonic-gate 			if (!IN6_IS_ADDR_UNSPECIFIED(&udp->udp_v6src)) {
25007c478bd9Sstevel@tonic-gate 				sin6->sin6_addr = udp->udp_v6src;
25017c478bd9Sstevel@tonic-gate 			} else {
25027c478bd9Sstevel@tonic-gate 				/*
25037c478bd9Sstevel@tonic-gate 				 * UNSPECIFIED
25047c478bd9Sstevel@tonic-gate 				 * udp_v6src is not set, we might be bound to
25057c478bd9Sstevel@tonic-gate 				 * broadcast/multicast. Use udp_bound_v6src as
25067c478bd9Sstevel@tonic-gate 				 * local address instead (that could
25077c478bd9Sstevel@tonic-gate 				 * also still be UNSPECIFIED)
25087c478bd9Sstevel@tonic-gate 				 */
25097c478bd9Sstevel@tonic-gate 				sin6->sin6_addr =
25107c478bd9Sstevel@tonic-gate 				    udp->udp_bound_v6src;
25117c478bd9Sstevel@tonic-gate 			}
25127c478bd9Sstevel@tonic-gate 			sin6->sin6_port = udp->udp_port;
25137c478bd9Sstevel@tonic-gate 			ackmp->b_wptr = (uchar_t *)&sin6[1];
25147c478bd9Sstevel@tonic-gate 			if (udp->udp_state == TS_DATA_XFER) {
25157c478bd9Sstevel@tonic-gate 				/*
25167c478bd9Sstevel@tonic-gate 				 * connected, fill remote address too
25177c478bd9Sstevel@tonic-gate 				 */
25187c478bd9Sstevel@tonic-gate 				taa->REMADDR_length = sizeof (sin6_t);
25197c478bd9Sstevel@tonic-gate 				/* assumed 32-bit alignment */
25207c478bd9Sstevel@tonic-gate 				taa->REMADDR_offset = taa->LOCADDR_offset +
25217c478bd9Sstevel@tonic-gate 				    taa->LOCADDR_length;
25227c478bd9Sstevel@tonic-gate 
25237c478bd9Sstevel@tonic-gate 				sin6 = (sin6_t *)(ackmp->b_rptr +
25247c478bd9Sstevel@tonic-gate 				    taa->REMADDR_offset);
25257c478bd9Sstevel@tonic-gate 				/* initialize */
25267c478bd9Sstevel@tonic-gate 				*sin6 = sin6_null;
25277c478bd9Sstevel@tonic-gate 				sin6->sin6_family = AF_INET6;
25287c478bd9Sstevel@tonic-gate 				sin6->sin6_addr = udp->udp_v6dst;
25297c478bd9Sstevel@tonic-gate 				sin6->sin6_port =  udp->udp_dstport;
25307c478bd9Sstevel@tonic-gate 				ackmp->b_wptr = (uchar_t *)&sin6[1];
25317c478bd9Sstevel@tonic-gate 			}
25327c478bd9Sstevel@tonic-gate 			ackmp->b_wptr = (uchar_t *)&sin6[1];
25337c478bd9Sstevel@tonic-gate 		}
25347c478bd9Sstevel@tonic-gate 	}
25357c478bd9Sstevel@tonic-gate 	ASSERT(ackmp->b_wptr <= ackmp->b_datap->db_lim);
2536ff550d0eSmasputra 	putnext(UDP_RD(q), ackmp);
25377c478bd9Sstevel@tonic-gate }
25387c478bd9Sstevel@tonic-gate 
25397c478bd9Sstevel@tonic-gate static void
25407c478bd9Sstevel@tonic-gate udp_copy_info(struct T_info_ack *tap, udp_t *udp)
25417c478bd9Sstevel@tonic-gate {
25427c478bd9Sstevel@tonic-gate 	if (udp->udp_family == AF_INET) {
25437c478bd9Sstevel@tonic-gate 		*tap = udp_g_t_info_ack_ipv4;
25447c478bd9Sstevel@tonic-gate 	} else {
25457c478bd9Sstevel@tonic-gate 		*tap = udp_g_t_info_ack_ipv6;
25467c478bd9Sstevel@tonic-gate 	}
25477c478bd9Sstevel@tonic-gate 	tap->CURRENT_state = udp->udp_state;
25487c478bd9Sstevel@tonic-gate 	tap->OPT_size = udp_max_optsize;
25497c478bd9Sstevel@tonic-gate }
25507c478bd9Sstevel@tonic-gate 
25517c478bd9Sstevel@tonic-gate /*
25527c478bd9Sstevel@tonic-gate  * This routine responds to T_CAPABILITY_REQ messages.  It is called by
25537c478bd9Sstevel@tonic-gate  * udp_wput.  Much of the T_CAPABILITY_ACK information is copied from
25547c478bd9Sstevel@tonic-gate  * udp_g_t_info_ack.  The current state of the stream is copied from
25557c478bd9Sstevel@tonic-gate  * udp_state.
25567c478bd9Sstevel@tonic-gate  */
25577c478bd9Sstevel@tonic-gate static void
25587c478bd9Sstevel@tonic-gate udp_capability_req(queue_t *q, mblk_t *mp)
25597c478bd9Sstevel@tonic-gate {
25607c478bd9Sstevel@tonic-gate 	t_uscalar_t		cap_bits1;
25617c478bd9Sstevel@tonic-gate 	struct T_capability_ack	*tcap;
2562ff550d0eSmasputra 	udp_t	*udp = Q_TO_UDP(q);
25637c478bd9Sstevel@tonic-gate 
25647c478bd9Sstevel@tonic-gate 	cap_bits1 = ((struct T_capability_req *)mp->b_rptr)->CAP_bits1;
25657c478bd9Sstevel@tonic-gate 
25667c478bd9Sstevel@tonic-gate 	mp = tpi_ack_alloc(mp, sizeof (struct T_capability_ack),
25677c478bd9Sstevel@tonic-gate 	    mp->b_datap->db_type, T_CAPABILITY_ACK);
25687c478bd9Sstevel@tonic-gate 	if (!mp)
25697c478bd9Sstevel@tonic-gate 		return;
25707c478bd9Sstevel@tonic-gate 
25717c478bd9Sstevel@tonic-gate 	tcap = (struct T_capability_ack *)mp->b_rptr;
25727c478bd9Sstevel@tonic-gate 	tcap->CAP_bits1 = 0;
25737c478bd9Sstevel@tonic-gate 
25747c478bd9Sstevel@tonic-gate 	if (cap_bits1 & TC1_INFO) {
25757c478bd9Sstevel@tonic-gate 		udp_copy_info(&tcap->INFO_ack, udp);
25767c478bd9Sstevel@tonic-gate 		tcap->CAP_bits1 |= TC1_INFO;
25777c478bd9Sstevel@tonic-gate 	}
25787c478bd9Sstevel@tonic-gate 
2579ff550d0eSmasputra 	putnext(UDP_RD(q), mp);
25807c478bd9Sstevel@tonic-gate }
25817c478bd9Sstevel@tonic-gate 
25827c478bd9Sstevel@tonic-gate /*
25837c478bd9Sstevel@tonic-gate  * This routine responds to T_INFO_REQ messages.  It is called by udp_wput.
25847c478bd9Sstevel@tonic-gate  * Most of the T_INFO_ACK information is copied from udp_g_t_info_ack.
25857c478bd9Sstevel@tonic-gate  * The current state of the stream is copied from udp_state.
25867c478bd9Sstevel@tonic-gate  */
25877c478bd9Sstevel@tonic-gate static void
25887c478bd9Sstevel@tonic-gate udp_info_req(queue_t *q, mblk_t *mp)
25897c478bd9Sstevel@tonic-gate {
2590ff550d0eSmasputra 	udp_t *udp = Q_TO_UDP(q);
25917c478bd9Sstevel@tonic-gate 
25927c478bd9Sstevel@tonic-gate 	/* Create a T_INFO_ACK message. */
25937c478bd9Sstevel@tonic-gate 	mp = tpi_ack_alloc(mp, sizeof (struct T_info_ack), M_PCPROTO,
25947c478bd9Sstevel@tonic-gate 	    T_INFO_ACK);
25957c478bd9Sstevel@tonic-gate 	if (!mp)
25967c478bd9Sstevel@tonic-gate 		return;
25977c478bd9Sstevel@tonic-gate 	udp_copy_info((struct T_info_ack *)mp->b_rptr, udp);
2598ff550d0eSmasputra 	putnext(UDP_RD(q), mp);
25997c478bd9Sstevel@tonic-gate }
26007c478bd9Sstevel@tonic-gate 
26017c478bd9Sstevel@tonic-gate /*
26027c478bd9Sstevel@tonic-gate  * IP recognizes seven kinds of bind requests:
26037c478bd9Sstevel@tonic-gate  *
26047c478bd9Sstevel@tonic-gate  * - A zero-length address binds only to the protocol number.
26057c478bd9Sstevel@tonic-gate  *
26067c478bd9Sstevel@tonic-gate  * - A 4-byte address is treated as a request to
26077c478bd9Sstevel@tonic-gate  * validate that the address is a valid local IPv4
26087c478bd9Sstevel@tonic-gate  * address, appropriate for an application to bind to.
26097c478bd9Sstevel@tonic-gate  * IP does the verification, but does not make any note
26107c478bd9Sstevel@tonic-gate  * of the address at this time.
26117c478bd9Sstevel@tonic-gate  *
26127c478bd9Sstevel@tonic-gate  * - A 16-byte address contains is treated as a request
26137c478bd9Sstevel@tonic-gate  * to validate a local IPv6 address, as the 4-byte
26147c478bd9Sstevel@tonic-gate  * address case above.
26157c478bd9Sstevel@tonic-gate  *
26167c478bd9Sstevel@tonic-gate  * - A 16-byte sockaddr_in to validate the local IPv4 address and also
26177c478bd9Sstevel@tonic-gate  * use it for the inbound fanout of packets.
26187c478bd9Sstevel@tonic-gate  *
26197c478bd9Sstevel@tonic-gate  * - A 24-byte sockaddr_in6 to validate the local IPv6 address and also
26207c478bd9Sstevel@tonic-gate  * use it for the inbound fanout of packets.
26217c478bd9Sstevel@tonic-gate  *
26227c478bd9Sstevel@tonic-gate  * - A 12-byte address (ipa_conn_t) containing complete IPv4 fanout
26237c478bd9Sstevel@tonic-gate  * information consisting of local and remote addresses
26247c478bd9Sstevel@tonic-gate  * and ports.  In this case, the addresses are both
26257c478bd9Sstevel@tonic-gate  * validated as appropriate for this operation, and, if
26267c478bd9Sstevel@tonic-gate  * so, the information is retained for use in the
26277c478bd9Sstevel@tonic-gate  * inbound fanout.
26287c478bd9Sstevel@tonic-gate  *
26297c478bd9Sstevel@tonic-gate  * - A 36-byte address address (ipa6_conn_t) containing complete IPv6
26307c478bd9Sstevel@tonic-gate  * fanout information, like the 12-byte case above.
26317c478bd9Sstevel@tonic-gate  *
26327c478bd9Sstevel@tonic-gate  * IP will also fill in the IRE request mblk with information
26337c478bd9Sstevel@tonic-gate  * regarding our peer.  In all cases, we notify IP of our protocol
26347c478bd9Sstevel@tonic-gate  * type by appending a single protocol byte to the bind request.
26357c478bd9Sstevel@tonic-gate  */
26367c478bd9Sstevel@tonic-gate static mblk_t *
26377c478bd9Sstevel@tonic-gate udp_ip_bind_mp(udp_t *udp, t_scalar_t bind_prim, t_scalar_t addr_length)
26387c478bd9Sstevel@tonic-gate {
26397c478bd9Sstevel@tonic-gate 	char	*cp;
26407c478bd9Sstevel@tonic-gate 	mblk_t	*mp;
26417c478bd9Sstevel@tonic-gate 	struct T_bind_req *tbr;
26427c478bd9Sstevel@tonic-gate 	ipa_conn_t	*ac;
26437c478bd9Sstevel@tonic-gate 	ipa6_conn_t	*ac6;
26447c478bd9Sstevel@tonic-gate 	sin_t		*sin;
26457c478bd9Sstevel@tonic-gate 	sin6_t		*sin6;
26467c478bd9Sstevel@tonic-gate 
26477c478bd9Sstevel@tonic-gate 	ASSERT(bind_prim == O_T_BIND_REQ || bind_prim == T_BIND_REQ);
26487c478bd9Sstevel@tonic-gate 
26497c478bd9Sstevel@tonic-gate 	mp = allocb(sizeof (*tbr) + addr_length + 1, BPRI_HI);
26507c478bd9Sstevel@tonic-gate 	if (!mp)
26517c478bd9Sstevel@tonic-gate 		return (mp);
26527c478bd9Sstevel@tonic-gate 	mp->b_datap->db_type = M_PROTO;
26537c478bd9Sstevel@tonic-gate 	tbr = (struct T_bind_req *)mp->b_rptr;
26547c478bd9Sstevel@tonic-gate 	tbr->PRIM_type = bind_prim;
26557c478bd9Sstevel@tonic-gate 	tbr->ADDR_offset = sizeof (*tbr);
26567c478bd9Sstevel@tonic-gate 	tbr->CONIND_number = 0;
26577c478bd9Sstevel@tonic-gate 	tbr->ADDR_length = addr_length;
26587c478bd9Sstevel@tonic-gate 	cp = (char *)&tbr[1];
26597c478bd9Sstevel@tonic-gate 	switch (addr_length) {
26607c478bd9Sstevel@tonic-gate 	case sizeof (ipa_conn_t):
26617c478bd9Sstevel@tonic-gate 		ASSERT(udp->udp_family == AF_INET);
26627c478bd9Sstevel@tonic-gate 		/* Append a request for an IRE */
26637c478bd9Sstevel@tonic-gate 		mp->b_cont = allocb(sizeof (ire_t), BPRI_HI);
26647c478bd9Sstevel@tonic-gate 		if (!mp->b_cont) {
26657c478bd9Sstevel@tonic-gate 			freemsg(mp);
26667c478bd9Sstevel@tonic-gate 			return (NULL);
26677c478bd9Sstevel@tonic-gate 		}
26687c478bd9Sstevel@tonic-gate 		mp->b_cont->b_wptr += sizeof (ire_t);
26697c478bd9Sstevel@tonic-gate 		mp->b_cont->b_datap->db_type = IRE_DB_REQ_TYPE;
26707c478bd9Sstevel@tonic-gate 
26717c478bd9Sstevel@tonic-gate 		/* cp known to be 32 bit aligned */
26727c478bd9Sstevel@tonic-gate 		ac = (ipa_conn_t *)cp;
26737c478bd9Sstevel@tonic-gate 		ac->ac_laddr = V4_PART_OF_V6(udp->udp_v6src);
26747c478bd9Sstevel@tonic-gate 		ac->ac_faddr = V4_PART_OF_V6(udp->udp_v6dst);
26757c478bd9Sstevel@tonic-gate 		ac->ac_fport = udp->udp_dstport;
26767c478bd9Sstevel@tonic-gate 		ac->ac_lport = udp->udp_port;
26777c478bd9Sstevel@tonic-gate 		break;
26787c478bd9Sstevel@tonic-gate 
26797c478bd9Sstevel@tonic-gate 	case sizeof (ipa6_conn_t):
26807c478bd9Sstevel@tonic-gate 		ASSERT(udp->udp_family == AF_INET6);
26817c478bd9Sstevel@tonic-gate 		/* Append a request for an IRE */
26827c478bd9Sstevel@tonic-gate 		mp->b_cont = allocb(sizeof (ire_t), BPRI_HI);
26837c478bd9Sstevel@tonic-gate 		if (!mp->b_cont) {
26847c478bd9Sstevel@tonic-gate 			freemsg(mp);
26857c478bd9Sstevel@tonic-gate 			return (NULL);
26867c478bd9Sstevel@tonic-gate 		}
26877c478bd9Sstevel@tonic-gate 		mp->b_cont->b_wptr += sizeof (ire_t);
26887c478bd9Sstevel@tonic-gate 		mp->b_cont->b_datap->db_type = IRE_DB_REQ_TYPE;
26897c478bd9Sstevel@tonic-gate 
26907c478bd9Sstevel@tonic-gate 		/* cp known to be 32 bit aligned */
26917c478bd9Sstevel@tonic-gate 		ac6 = (ipa6_conn_t *)cp;
26927c478bd9Sstevel@tonic-gate 		ac6->ac6_laddr = udp->udp_v6src;
26937c478bd9Sstevel@tonic-gate 		ac6->ac6_faddr = udp->udp_v6dst;
26947c478bd9Sstevel@tonic-gate 		ac6->ac6_fport = udp->udp_dstport;
26957c478bd9Sstevel@tonic-gate 		ac6->ac6_lport = udp->udp_port;
26967c478bd9Sstevel@tonic-gate 		break;
26977c478bd9Sstevel@tonic-gate 
26987c478bd9Sstevel@tonic-gate 	case sizeof (sin_t):
26997c478bd9Sstevel@tonic-gate 		ASSERT(udp->udp_family == AF_INET);
27007c478bd9Sstevel@tonic-gate 		/* Append a request for an IRE */
27017c478bd9Sstevel@tonic-gate 		mp->b_cont = allocb(sizeof (ire_t), BPRI_HI);
27027c478bd9Sstevel@tonic-gate 		if (!mp->b_cont) {
27037c478bd9Sstevel@tonic-gate 			freemsg(mp);
27047c478bd9Sstevel@tonic-gate 			return (NULL);
27057c478bd9Sstevel@tonic-gate 		}
27067c478bd9Sstevel@tonic-gate 		mp->b_cont->b_wptr += sizeof (ire_t);
27077c478bd9Sstevel@tonic-gate 		mp->b_cont->b_datap->db_type = IRE_DB_REQ_TYPE;
27087c478bd9Sstevel@tonic-gate 
27097c478bd9Sstevel@tonic-gate 		sin = (sin_t *)cp;
27107c478bd9Sstevel@tonic-gate 		*sin = sin_null;
27117c478bd9Sstevel@tonic-gate 		sin->sin_family = AF_INET;
27127c478bd9Sstevel@tonic-gate 		sin->sin_addr.s_addr = V4_PART_OF_V6(udp->udp_bound_v6src);
27137c478bd9Sstevel@tonic-gate 		sin->sin_port = udp->udp_port;
27147c478bd9Sstevel@tonic-gate 		break;
27157c478bd9Sstevel@tonic-gate 
27167c478bd9Sstevel@tonic-gate 	case sizeof (sin6_t):
27177c478bd9Sstevel@tonic-gate 		ASSERT(udp->udp_family == AF_INET6);
27187c478bd9Sstevel@tonic-gate 		/* Append a request for an IRE */
27197c478bd9Sstevel@tonic-gate 		mp->b_cont = allocb(sizeof (ire_t), BPRI_HI);
27207c478bd9Sstevel@tonic-gate 		if (!mp->b_cont) {
27217c478bd9Sstevel@tonic-gate 			freemsg(mp);
27227c478bd9Sstevel@tonic-gate 			return (NULL);
27237c478bd9Sstevel@tonic-gate 		}
27247c478bd9Sstevel@tonic-gate 		mp->b_cont->b_wptr += sizeof (ire_t);
27257c478bd9Sstevel@tonic-gate 		mp->b_cont->b_datap->db_type = IRE_DB_REQ_TYPE;
27267c478bd9Sstevel@tonic-gate 
27277c478bd9Sstevel@tonic-gate 		sin6 = (sin6_t *)cp;
27287c478bd9Sstevel@tonic-gate 		*sin6 = sin6_null;
27297c478bd9Sstevel@tonic-gate 		sin6->sin6_family = AF_INET6;
27307c478bd9Sstevel@tonic-gate 		sin6->sin6_addr = udp->udp_bound_v6src;
27317c478bd9Sstevel@tonic-gate 		sin6->sin6_port = udp->udp_port;
27327c478bd9Sstevel@tonic-gate 		break;
27337c478bd9Sstevel@tonic-gate 	}
27347c478bd9Sstevel@tonic-gate 	/* Add protocol number to end */
27357c478bd9Sstevel@tonic-gate 	cp[addr_length] = (char)IPPROTO_UDP;
27367c478bd9Sstevel@tonic-gate 	mp->b_wptr = (uchar_t *)&cp[addr_length + 1];
27377c478bd9Sstevel@tonic-gate 	return (mp);
27387c478bd9Sstevel@tonic-gate }
27397c478bd9Sstevel@tonic-gate 
27407c478bd9Sstevel@tonic-gate /*
27417c478bd9Sstevel@tonic-gate  * This is the open routine for udp.  It allocates a udp_t structure for
27427c478bd9Sstevel@tonic-gate  * the stream and, on the first open of the module, creates an ND table.
27437c478bd9Sstevel@tonic-gate  */
2744ff550d0eSmasputra /* ARGSUSED */
27457c478bd9Sstevel@tonic-gate static int
27467c478bd9Sstevel@tonic-gate udp_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp)
27477c478bd9Sstevel@tonic-gate {
27487c478bd9Sstevel@tonic-gate 	int	err;
27497c478bd9Sstevel@tonic-gate 	udp_t	*udp;
2750ff550d0eSmasputra 	conn_t *connp;
2751ff550d0eSmasputra 	zoneid_t zoneid = getzoneid();
2752ff550d0eSmasputra 	queue_t	*ip_wq;
2753ff550d0eSmasputra 	char	*name;
27547c478bd9Sstevel@tonic-gate 
27557c478bd9Sstevel@tonic-gate 	TRACE_1(TR_FAC_UDP, TR_UDP_OPEN, "udp_open: q %p", q);
27567c478bd9Sstevel@tonic-gate 
27577c478bd9Sstevel@tonic-gate 	/* If the stream is already open, return immediately. */
27587c478bd9Sstevel@tonic-gate 	if (q->q_ptr != NULL)
27597c478bd9Sstevel@tonic-gate 		return (0);
27607c478bd9Sstevel@tonic-gate 
27617c478bd9Sstevel@tonic-gate 	/* If this is not a push of udp as a module, fail. */
27627c478bd9Sstevel@tonic-gate 	if (sflag != MODOPEN)
27637c478bd9Sstevel@tonic-gate 		return (EINVAL);
27647c478bd9Sstevel@tonic-gate 
2765ff550d0eSmasputra 	q->q_hiwat = udp_recv_hiwat;
2766ff550d0eSmasputra 	WR(q)->q_hiwat = udp_xmit_hiwat;
2767ff550d0eSmasputra 	WR(q)->q_lowat = udp_xmit_lowat;
2768ff550d0eSmasputra 
2769ff550d0eSmasputra 	/* Insert ourselves in the stream since we're about to walk q_next */
2770ff550d0eSmasputra 	qprocson(q);
2771ff550d0eSmasputra 
2772ff550d0eSmasputra 	udp = kmem_cache_alloc(udp_cache, KM_SLEEP);
2773ff550d0eSmasputra 	bzero(udp, sizeof (*udp));
2774ff550d0eSmasputra 
2775ff550d0eSmasputra 	/*
2776ff550d0eSmasputra 	 * UDP is supported only as a module and it has to be pushed directly
2777ff550d0eSmasputra 	 * above the device instance of IP. If UDP is pushed anywhere else
2778ff550d0eSmasputra 	 * on a stream, it will support just T_SVR4_OPTMGMT_REQ for the
2779ff550d0eSmasputra 	 * sake of MIB browsers and fail everything else.
2780ff550d0eSmasputra 	 */
2781ff550d0eSmasputra 	ip_wq = WR(q)->q_next;
2782ff550d0eSmasputra 	if (ip_wq->q_next != NULL ||
2783ff550d0eSmasputra 	    (name = ip_wq->q_qinfo->qi_minfo->mi_idname) == NULL ||
2784ff550d0eSmasputra 	    strcmp(name, IP_MOD_NAME) != 0 ||
2785ff550d0eSmasputra 	    ip_wq->q_qinfo->qi_minfo->mi_idnum != IP_MOD_ID) {
2786ff550d0eSmasputra 		/* Support just SNMP for MIB browsers */
2787ff550d0eSmasputra 		connp = ipcl_conn_create(IPCL_IPCCONN, KM_SLEEP);
2788ff550d0eSmasputra 		connp->conn_rq = q;
2789ff550d0eSmasputra 		connp->conn_wq = WR(q);
2790ff550d0eSmasputra 		connp->conn_flags |= IPCL_UDPMOD;
2791ff550d0eSmasputra 		connp->conn_cred = credp;
2792ff550d0eSmasputra 		connp->conn_zoneid = zoneid;
2793ff550d0eSmasputra 		connp->conn_udp = udp;
2794ff550d0eSmasputra 		udp->udp_connp = connp;
2795ff550d0eSmasputra 		q->q_ptr = WR(q)->q_ptr = connp;
2796ff550d0eSmasputra 		crhold(credp);
2797ff550d0eSmasputra 		q->q_qinfo = &udp_snmp_rinit;
2798ff550d0eSmasputra 		WR(q)->q_qinfo = &udp_snmp_winit;
2799ff550d0eSmasputra 		return (0);
2800ff550d0eSmasputra 	}
2801ff550d0eSmasputra 
28027c478bd9Sstevel@tonic-gate 	/*
2803ff550d0eSmasputra 	 * Initialize the udp_t structure for this stream.
28047c478bd9Sstevel@tonic-gate 	 */
2805ff550d0eSmasputra 	q = RD(ip_wq);
2806ff550d0eSmasputra 	connp = Q_TO_CONN(q);
2807ff550d0eSmasputra 	mutex_enter(&connp->conn_lock);
2808ff550d0eSmasputra 	connp->conn_proto = IPPROTO_UDP;
2809ff550d0eSmasputra 	connp->conn_flags |= IPCL_UDP;
2810ff550d0eSmasputra 	connp->conn_sqp = IP_SQUEUE_GET(lbolt);
2811ff550d0eSmasputra 	connp->conn_udp = udp;
28127c478bd9Sstevel@tonic-gate 
28137c478bd9Sstevel@tonic-gate 	/* Set the initial state of the stream and the privilege status. */
2814ff550d0eSmasputra 	udp->udp_connp = connp;
28157c478bd9Sstevel@tonic-gate 	udp->udp_state = TS_UNBND;
2816ff550d0eSmasputra 	udp->udp_mode = UDP_MT_HOT;
28177c478bd9Sstevel@tonic-gate 	if (getmajor(*devp) == (major_t)UDP6_MAJ) {
28187c478bd9Sstevel@tonic-gate 		udp->udp_family = AF_INET6;
28197c478bd9Sstevel@tonic-gate 		udp->udp_ipversion = IPV6_VERSION;
28207c478bd9Sstevel@tonic-gate 		udp->udp_max_hdr_len = IPV6_HDR_LEN + UDPH_SIZE;
28217c478bd9Sstevel@tonic-gate 		udp->udp_ttl = udp_ipv6_hoplimit;
2822ff550d0eSmasputra 		connp->conn_af_isv6 = B_TRUE;
2823ff550d0eSmasputra 		connp->conn_flags |= IPCL_ISV6;
28247c478bd9Sstevel@tonic-gate 	} else {
28257c478bd9Sstevel@tonic-gate 		udp->udp_family = AF_INET;
28267c478bd9Sstevel@tonic-gate 		udp->udp_ipversion = IPV4_VERSION;
28277c478bd9Sstevel@tonic-gate 		udp->udp_max_hdr_len = IP_SIMPLE_HDR_LENGTH + UDPH_SIZE;
28287c478bd9Sstevel@tonic-gate 		udp->udp_ttl = udp_ipv4_ttl;
2829ff550d0eSmasputra 		connp->conn_af_isv6 = B_FALSE;
2830ff550d0eSmasputra 		connp->conn_flags &= ~IPCL_ISV6;
28317c478bd9Sstevel@tonic-gate 	}
28327c478bd9Sstevel@tonic-gate 
28337c478bd9Sstevel@tonic-gate 	udp->udp_multicast_ttl = IP_DEFAULT_MULTICAST_TTL;
2834ff550d0eSmasputra 	connp->conn_multicast_loop = IP_DEFAULT_MULTICAST_LOOP;
2835ff550d0eSmasputra 	connp->conn_zoneid = zoneid;
28367c478bd9Sstevel@tonic-gate 
2837ff550d0eSmasputra 	if (connp->conn_flags & IPCL_SOCKET) {
2838ff550d0eSmasputra 		udp->udp_issocket = B_TRUE;
2839ff550d0eSmasputra 		udp->udp_direct_sockfs = B_TRUE;
2840ff550d0eSmasputra 	}
2841ff550d0eSmasputra 	mutex_exit(&connp->conn_lock);
28427c478bd9Sstevel@tonic-gate 
28437c478bd9Sstevel@tonic-gate 	/*
28447c478bd9Sstevel@tonic-gate 	 * The transmit hiwat/lowat is only looked at on IP's queue.
2845ff550d0eSmasputra 	 * Store in q_hiwat in order to return on SO_SNDBUF/SO_RCVBUF
28467c478bd9Sstevel@tonic-gate 	 * getsockopts.
28477c478bd9Sstevel@tonic-gate 	 */
2848ff550d0eSmasputra 	q->q_hiwat = udp_recv_hiwat;
28497c478bd9Sstevel@tonic-gate 	WR(q)->q_hiwat = udp_xmit_hiwat;
28507c478bd9Sstevel@tonic-gate 	WR(q)->q_lowat = udp_xmit_lowat;
28517c478bd9Sstevel@tonic-gate 
28527c478bd9Sstevel@tonic-gate 	if (udp->udp_family == AF_INET6) {
28537c478bd9Sstevel@tonic-gate 		/* Build initial header template for transmit */
28547c478bd9Sstevel@tonic-gate 		if ((err = udp_build_hdrs(q, udp)) != 0) {
2855ff550d0eSmasputra 			qprocsoff(UDP_RD(q));
2856ff550d0eSmasputra 			udp->udp_connp = NULL;
2857ff550d0eSmasputra 			connp->conn_udp = NULL;
2858ff550d0eSmasputra 			kmem_cache_free(udp_cache, udp);
2859ff550d0eSmasputra 			return (err);
28607c478bd9Sstevel@tonic-gate 		}
28617c478bd9Sstevel@tonic-gate 	}
28627c478bd9Sstevel@tonic-gate 
2863ff550d0eSmasputra 	/* Set the Stream head write offset and high watermark. */
2864ff550d0eSmasputra 	(void) mi_set_sth_wroff(UDP_RD(q),
2865ff550d0eSmasputra 	    udp->udp_max_hdr_len + udp_wroff_extra);
2866ff550d0eSmasputra 	(void) mi_set_sth_hiwat(UDP_RD(q), udp_set_rcv_hiwat(udp, q->q_hiwat));
28677c478bd9Sstevel@tonic-gate 
2868d045b987Smasputra 	WR(UDP_RD(q))->q_qinfo = &udp_winit;
2869d045b987Smasputra 
2870ff550d0eSmasputra 	return (0);
28717c478bd9Sstevel@tonic-gate }
28727c478bd9Sstevel@tonic-gate 
28737c478bd9Sstevel@tonic-gate /*
28747c478bd9Sstevel@tonic-gate  * Which UDP options OK to set through T_UNITDATA_REQ...
28757c478bd9Sstevel@tonic-gate  */
28767c478bd9Sstevel@tonic-gate /* ARGSUSED */
28777c478bd9Sstevel@tonic-gate static boolean_t
28787c478bd9Sstevel@tonic-gate udp_opt_allow_udr_set(t_scalar_t level, t_scalar_t name)
28797c478bd9Sstevel@tonic-gate {
28807c478bd9Sstevel@tonic-gate 	return (B_TRUE);
28817c478bd9Sstevel@tonic-gate }
28827c478bd9Sstevel@tonic-gate 
28837c478bd9Sstevel@tonic-gate /*
28847c478bd9Sstevel@tonic-gate  * This routine gets default values of certain options whose default
28857c478bd9Sstevel@tonic-gate  * values are maintained by protcol specific code
28867c478bd9Sstevel@tonic-gate  */
28877c478bd9Sstevel@tonic-gate /* ARGSUSED */
28887c478bd9Sstevel@tonic-gate int
28897c478bd9Sstevel@tonic-gate udp_opt_default(queue_t	*q, t_scalar_t level, t_scalar_t name, uchar_t *ptr)
28907c478bd9Sstevel@tonic-gate {
28917c478bd9Sstevel@tonic-gate 	int *i1 = (int *)ptr;
28927c478bd9Sstevel@tonic-gate 
28937c478bd9Sstevel@tonic-gate 	switch (level) {
28947c478bd9Sstevel@tonic-gate 	case IPPROTO_IP:
28957c478bd9Sstevel@tonic-gate 		switch (name) {
28967c478bd9Sstevel@tonic-gate 		case IP_MULTICAST_TTL:
28977c478bd9Sstevel@tonic-gate 			*ptr = (uchar_t)IP_DEFAULT_MULTICAST_TTL;
28987c478bd9Sstevel@tonic-gate 			return (sizeof (uchar_t));
28997c478bd9Sstevel@tonic-gate 		case IP_MULTICAST_LOOP:
29007c478bd9Sstevel@tonic-gate 			*ptr = (uchar_t)IP_DEFAULT_MULTICAST_LOOP;
29017c478bd9Sstevel@tonic-gate 			return (sizeof (uchar_t));
29027c478bd9Sstevel@tonic-gate 		}
29037c478bd9Sstevel@tonic-gate 		break;
29047c478bd9Sstevel@tonic-gate 	case IPPROTO_IPV6:
29057c478bd9Sstevel@tonic-gate 		switch (name) {
29067c478bd9Sstevel@tonic-gate 		case IPV6_MULTICAST_HOPS:
29077c478bd9Sstevel@tonic-gate 			*i1 = IP_DEFAULT_MULTICAST_TTL;
29087c478bd9Sstevel@tonic-gate 			return (sizeof (int));
29097c478bd9Sstevel@tonic-gate 		case IPV6_MULTICAST_LOOP:
29107c478bd9Sstevel@tonic-gate 			*i1 = IP_DEFAULT_MULTICAST_LOOP;
29117c478bd9Sstevel@tonic-gate 			return (sizeof (int));
29127c478bd9Sstevel@tonic-gate 		case IPV6_UNICAST_HOPS:
29137c478bd9Sstevel@tonic-gate 			*i1 = udp_ipv6_hoplimit;
29147c478bd9Sstevel@tonic-gate 			return (sizeof (int));
29157c478bd9Sstevel@tonic-gate 		}
29167c478bd9Sstevel@tonic-gate 		break;
29177c478bd9Sstevel@tonic-gate 	}
29187c478bd9Sstevel@tonic-gate 	return (-1);
29197c478bd9Sstevel@tonic-gate }
29207c478bd9Sstevel@tonic-gate 
29217c478bd9Sstevel@tonic-gate /*
2922ff550d0eSmasputra  * This routine retrieves the current status of socket options
2923ff550d0eSmasputra  * and expects the caller to pass in the queue pointer of the
2924ff550d0eSmasputra  * upper instance.  It returns the size of the option retrieved.
29257c478bd9Sstevel@tonic-gate  */
29267c478bd9Sstevel@tonic-gate int
29277c478bd9Sstevel@tonic-gate udp_opt_get(queue_t *q, t_scalar_t level, t_scalar_t name, uchar_t *ptr)
29287c478bd9Sstevel@tonic-gate {
29297c478bd9Sstevel@tonic-gate 	int	*i1 = (int *)ptr;
2930ff550d0eSmasputra 	conn_t	*connp;
2931ff550d0eSmasputra 	udp_t	*udp;
2932ff550d0eSmasputra 	ip6_pkt_t *ipp;
2933ff550d0eSmasputra 
2934ff550d0eSmasputra 	q = UDP_WR(q);
2935ff550d0eSmasputra 	connp = Q_TO_CONN(q);
2936ff550d0eSmasputra 	udp = connp->conn_udp;
2937ff550d0eSmasputra 	ipp = &udp->udp_sticky_ipp;
29387c478bd9Sstevel@tonic-gate 
29397c478bd9Sstevel@tonic-gate 	switch (level) {
29407c478bd9Sstevel@tonic-gate 	case SOL_SOCKET:
29417c478bd9Sstevel@tonic-gate 		switch (name) {
29427c478bd9Sstevel@tonic-gate 		case SO_DEBUG:
29437c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_debug;
29447c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
29457c478bd9Sstevel@tonic-gate 		case SO_REUSEADDR:
29467c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_reuseaddr;
29477c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
29487c478bd9Sstevel@tonic-gate 		case SO_TYPE:
29497c478bd9Sstevel@tonic-gate 			*i1 = SOCK_DGRAM;
29507c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
29517c478bd9Sstevel@tonic-gate 
29527c478bd9Sstevel@tonic-gate 		/*
29537c478bd9Sstevel@tonic-gate 		 * The following three items are available here,
29547c478bd9Sstevel@tonic-gate 		 * but are only meaningful to IP.
29557c478bd9Sstevel@tonic-gate 		 */
29567c478bd9Sstevel@tonic-gate 		case SO_DONTROUTE:
29577c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_dontroute;
29587c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
29597c478bd9Sstevel@tonic-gate 		case SO_USELOOPBACK:
29607c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_useloopback;
29617c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
29627c478bd9Sstevel@tonic-gate 		case SO_BROADCAST:
29637c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_broadcast;
29647c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
29657c478bd9Sstevel@tonic-gate 
29667c478bd9Sstevel@tonic-gate 		case SO_SNDBUF:
29677c478bd9Sstevel@tonic-gate 			*i1 = q->q_hiwat;
29687c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
29697c478bd9Sstevel@tonic-gate 		case SO_RCVBUF:
29707c478bd9Sstevel@tonic-gate 			*i1 = RD(q)->q_hiwat;
29717c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
29727c478bd9Sstevel@tonic-gate 		case SO_DGRAM_ERRIND:
29737c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_dgram_errind;
29747c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
29757c478bd9Sstevel@tonic-gate 		case SO_RECVUCRED:
29767c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_recvucred;
29777c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
29787c478bd9Sstevel@tonic-gate 		default:
29797c478bd9Sstevel@tonic-gate 			return (-1);
29807c478bd9Sstevel@tonic-gate 		}
29817c478bd9Sstevel@tonic-gate 		break;
29827c478bd9Sstevel@tonic-gate 	case IPPROTO_IP:
29837c478bd9Sstevel@tonic-gate 		if (udp->udp_family != AF_INET)
29847c478bd9Sstevel@tonic-gate 			return (-1);
29857c478bd9Sstevel@tonic-gate 		switch (name) {
29867c478bd9Sstevel@tonic-gate 		case IP_OPTIONS:
29877c478bd9Sstevel@tonic-gate 		case T_IP_OPTIONS:
29887c478bd9Sstevel@tonic-gate 			if (udp->udp_ip_rcv_options_len)
29897c478bd9Sstevel@tonic-gate 				bcopy(udp->udp_ip_rcv_options, ptr,
29907c478bd9Sstevel@tonic-gate 				    udp->udp_ip_rcv_options_len);
29917c478bd9Sstevel@tonic-gate 			return (udp->udp_ip_rcv_options_len);
29927c478bd9Sstevel@tonic-gate 		case IP_TOS:
29937c478bd9Sstevel@tonic-gate 		case T_IP_TOS:
29947c478bd9Sstevel@tonic-gate 			*i1 = (int)udp->udp_type_of_service;
29957c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
29967c478bd9Sstevel@tonic-gate 		case IP_TTL:
29977c478bd9Sstevel@tonic-gate 			*i1 = (int)udp->udp_ttl;
29987c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
299943d18f1cSpriyanka 		case IP_NEXTHOP:
300043d18f1cSpriyanka 			/* Handled at IP level */
300143d18f1cSpriyanka 			return (-EINVAL);
30027c478bd9Sstevel@tonic-gate 		case IP_MULTICAST_IF:
30037c478bd9Sstevel@tonic-gate 			/* 0 address if not set */
30047c478bd9Sstevel@tonic-gate 			*(ipaddr_t *)ptr = udp->udp_multicast_if_addr;
30057c478bd9Sstevel@tonic-gate 			return (sizeof (ipaddr_t));
30067c478bd9Sstevel@tonic-gate 		case IP_MULTICAST_TTL:
30077c478bd9Sstevel@tonic-gate 			*(uchar_t *)ptr = udp->udp_multicast_ttl;
30087c478bd9Sstevel@tonic-gate 			return (sizeof (uchar_t));
30097c478bd9Sstevel@tonic-gate 		case IP_MULTICAST_LOOP:
3010ff550d0eSmasputra 			*ptr = connp->conn_multicast_loop;
30117c478bd9Sstevel@tonic-gate 			return (sizeof (uint8_t));
30127c478bd9Sstevel@tonic-gate 		case IP_RECVOPTS:
30137c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_recvopts;
30147c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
30157c478bd9Sstevel@tonic-gate 		case IP_RECVDSTADDR:
30167c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_recvdstaddr;
30177c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
30187c478bd9Sstevel@tonic-gate 		case IP_RECVIF:
30197c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_recvif;
30207c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
30217c478bd9Sstevel@tonic-gate 		case IP_RECVSLLA:
30227c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_recvslla;
30237c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
30247c478bd9Sstevel@tonic-gate 		case IP_RECVTTL:
30257c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_recvttl;
30267c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
30277c478bd9Sstevel@tonic-gate 		case IP_ADD_MEMBERSHIP:
30287c478bd9Sstevel@tonic-gate 		case IP_DROP_MEMBERSHIP:
30297c478bd9Sstevel@tonic-gate 		case IP_BLOCK_SOURCE:
30307c478bd9Sstevel@tonic-gate 		case IP_UNBLOCK_SOURCE:
30317c478bd9Sstevel@tonic-gate 		case IP_ADD_SOURCE_MEMBERSHIP:
30327c478bd9Sstevel@tonic-gate 		case IP_DROP_SOURCE_MEMBERSHIP:
30337c478bd9Sstevel@tonic-gate 		case MCAST_JOIN_GROUP:
30347c478bd9Sstevel@tonic-gate 		case MCAST_LEAVE_GROUP:
30357c478bd9Sstevel@tonic-gate 		case MCAST_BLOCK_SOURCE:
30367c478bd9Sstevel@tonic-gate 		case MCAST_UNBLOCK_SOURCE:
30377c478bd9Sstevel@tonic-gate 		case MCAST_JOIN_SOURCE_GROUP:
30387c478bd9Sstevel@tonic-gate 		case MCAST_LEAVE_SOURCE_GROUP:
30397c478bd9Sstevel@tonic-gate 		case IP_DONTFAILOVER_IF:
30407c478bd9Sstevel@tonic-gate 			/* cannot "get" the value for these */
30417c478bd9Sstevel@tonic-gate 			return (-1);
30427c478bd9Sstevel@tonic-gate 		case IP_BOUND_IF:
30437c478bd9Sstevel@tonic-gate 			/* Zero if not set */
30447c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_bound_if;
30457c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
30467c478bd9Sstevel@tonic-gate 		case IP_UNSPEC_SRC:
30477c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_unspec_source;
30487c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
30497c478bd9Sstevel@tonic-gate 		case IP_XMIT_IF:
30507c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_xmit_if;
30517c478bd9Sstevel@tonic-gate 			break; /* goto sizeof (int) option return */
30527c478bd9Sstevel@tonic-gate 		default:
30537c478bd9Sstevel@tonic-gate 			return (-1);
30547c478bd9Sstevel@tonic-gate 		}
30557c478bd9Sstevel@tonic-gate 		break;
30567c478bd9Sstevel@tonic-gate 	case IPPROTO_IPV6:
30577c478bd9Sstevel@tonic-gate 		if (udp->udp_family != AF_INET6)
30587c478bd9Sstevel@tonic-gate 			return (-1);
30597c478bd9Sstevel@tonic-gate 		switch (name) {
30607c478bd9Sstevel@tonic-gate 		case IPV6_UNICAST_HOPS:
30617c478bd9Sstevel@tonic-gate 			*i1 = (unsigned int)udp->udp_ttl;
30627c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
30637c478bd9Sstevel@tonic-gate 		case IPV6_MULTICAST_IF:
30647c478bd9Sstevel@tonic-gate 			/* 0 index if not set */
30657c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_multicast_if_index;
30667c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
30677c478bd9Sstevel@tonic-gate 		case IPV6_MULTICAST_HOPS:
30687c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_multicast_ttl;
30697c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
30707c478bd9Sstevel@tonic-gate 		case IPV6_MULTICAST_LOOP:
3071ff550d0eSmasputra 			*i1 = connp->conn_multicast_loop;
30727c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
30737c478bd9Sstevel@tonic-gate 		case IPV6_JOIN_GROUP:
30747c478bd9Sstevel@tonic-gate 		case IPV6_LEAVE_GROUP:
30757c478bd9Sstevel@tonic-gate 		case MCAST_JOIN_GROUP:
30767c478bd9Sstevel@tonic-gate 		case MCAST_LEAVE_GROUP:
30777c478bd9Sstevel@tonic-gate 		case MCAST_BLOCK_SOURCE:
30787c478bd9Sstevel@tonic-gate 		case MCAST_UNBLOCK_SOURCE:
30797c478bd9Sstevel@tonic-gate 		case MCAST_JOIN_SOURCE_GROUP:
30807c478bd9Sstevel@tonic-gate 		case MCAST_LEAVE_SOURCE_GROUP:
30817c478bd9Sstevel@tonic-gate 			/* cannot "get" the value for these */
30827c478bd9Sstevel@tonic-gate 			return (-1);
30837c478bd9Sstevel@tonic-gate 		case IPV6_BOUND_IF:
30847c478bd9Sstevel@tonic-gate 			/* Zero if not set */
30857c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_bound_if;
30867c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
30877c478bd9Sstevel@tonic-gate 		case IPV6_UNSPEC_SRC:
30887c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_unspec_source;
30897c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
30907c478bd9Sstevel@tonic-gate 		case IPV6_RECVPKTINFO:
30917c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_ipv6_recvpktinfo;
30927c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
30937c478bd9Sstevel@tonic-gate 		case IPV6_RECVTCLASS:
30947c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_ipv6_recvtclass;
30957c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
30967c478bd9Sstevel@tonic-gate 		case IPV6_RECVPATHMTU:
30977c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_ipv6_recvpathmtu;
30987c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
30997c478bd9Sstevel@tonic-gate 		case IPV6_RECVHOPLIMIT:
31007c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_ipv6_recvhoplimit;
31017c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
31027c478bd9Sstevel@tonic-gate 		case IPV6_RECVHOPOPTS:
31037c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_ipv6_recvhopopts;
31047c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
31057c478bd9Sstevel@tonic-gate 		case IPV6_RECVDSTOPTS:
31067c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_ipv6_recvdstopts;
31077c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
31087c478bd9Sstevel@tonic-gate 		case _OLD_IPV6_RECVDSTOPTS:
31097c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_old_ipv6_recvdstopts;
31107c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
31117c478bd9Sstevel@tonic-gate 		case IPV6_RECVRTHDRDSTOPTS:
31127c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_ipv6_recvrthdrdstopts;
31137c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
31147c478bd9Sstevel@tonic-gate 		case IPV6_RECVRTHDR:
31157c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_ipv6_recvrthdr;
31167c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
31177c478bd9Sstevel@tonic-gate 		case IPV6_PKTINFO: {
31187c478bd9Sstevel@tonic-gate 			/* XXX assumes that caller has room for max size! */
31197c478bd9Sstevel@tonic-gate 			struct in6_pktinfo *pkti;
31207c478bd9Sstevel@tonic-gate 
31217c478bd9Sstevel@tonic-gate 			pkti = (struct in6_pktinfo *)ptr;
31227c478bd9Sstevel@tonic-gate 			if (ipp->ipp_fields & IPPF_IFINDEX)
31237c478bd9Sstevel@tonic-gate 				pkti->ipi6_ifindex = ipp->ipp_ifindex;
31247c478bd9Sstevel@tonic-gate 			else
31257c478bd9Sstevel@tonic-gate 				pkti->ipi6_ifindex = 0;
31267c478bd9Sstevel@tonic-gate 			if (ipp->ipp_fields & IPPF_ADDR)
31277c478bd9Sstevel@tonic-gate 				pkti->ipi6_addr = ipp->ipp_addr;
31287c478bd9Sstevel@tonic-gate 			else
31297c478bd9Sstevel@tonic-gate 				pkti->ipi6_addr = ipv6_all_zeros;
31307c478bd9Sstevel@tonic-gate 			return (sizeof (struct in6_pktinfo));
31317c478bd9Sstevel@tonic-gate 		}
31327c478bd9Sstevel@tonic-gate 		case IPV6_TCLASS:
31337c478bd9Sstevel@tonic-gate 			if (ipp->ipp_fields & IPPF_TCLASS)
31347c478bd9Sstevel@tonic-gate 				*i1 = ipp->ipp_tclass;
31357c478bd9Sstevel@tonic-gate 			else
31367c478bd9Sstevel@tonic-gate 				*i1 = IPV6_FLOW_TCLASS(
31377c478bd9Sstevel@tonic-gate 				    IPV6_DEFAULT_VERS_AND_FLOW);
31387c478bd9Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
31397c478bd9Sstevel@tonic-gate 		case IPV6_NEXTHOP: {
31407c478bd9Sstevel@tonic-gate 			sin6_t *sin6 = (sin6_t *)ptr;
31417c478bd9Sstevel@tonic-gate 
31427c478bd9Sstevel@tonic-gate 			if (!(ipp->ipp_fields & IPPF_NEXTHOP))
31437c478bd9Sstevel@tonic-gate 				return (0);
31447c478bd9Sstevel@tonic-gate 			*sin6 = sin6_null;
31457c478bd9Sstevel@tonic-gate 			sin6->sin6_family = AF_INET6;
31467c478bd9Sstevel@tonic-gate 			sin6->sin6_addr = ipp->ipp_nexthop;
31477c478bd9Sstevel@tonic-gate 			return (sizeof (sin6_t));
31487c478bd9Sstevel@tonic-gate 		}
31497c478bd9Sstevel@tonic-gate 		case IPV6_HOPOPTS:
31507c478bd9Sstevel@tonic-gate 			if (!(ipp->ipp_fields & IPPF_HOPOPTS))
31517c478bd9Sstevel@tonic-gate 				return (0);
31527c478bd9Sstevel@tonic-gate 			bcopy(ipp->ipp_hopopts, ptr, ipp->ipp_hopoptslen);
31537c478bd9Sstevel@tonic-gate 			return (ipp->ipp_hopoptslen);
31547c478bd9Sstevel@tonic-gate 		case IPV6_RTHDRDSTOPTS:
31557c478bd9Sstevel@tonic-gate 			if (!(ipp->ipp_fields & IPPF_RTDSTOPTS))
31567c478bd9Sstevel@tonic-gate 				return (0);
31577c478bd9Sstevel@tonic-gate 			bcopy(ipp->ipp_rtdstopts, ptr, ipp->ipp_rtdstoptslen);
31587c478bd9Sstevel@tonic-gate 			return (ipp->ipp_rtdstoptslen);
31597c478bd9Sstevel@tonic-gate 		case IPV6_RTHDR:
31607c478bd9Sstevel@tonic-gate 			if (!(ipp->ipp_fields & IPPF_RTHDR))
31617c478bd9Sstevel@tonic-gate 				return (0);
31627c478bd9Sstevel@tonic-gate 			bcopy(ipp->ipp_rthdr, ptr, ipp->ipp_rthdrlen);
31637c478bd9Sstevel@tonic-gate 			return (ipp->ipp_rthdrlen);
31647c478bd9Sstevel@tonic-gate 		case IPV6_DSTOPTS:
31657c478bd9Sstevel@tonic-gate 			if (!(ipp->ipp_fields & IPPF_DSTOPTS))
31667c478bd9Sstevel@tonic-gate 				return (0);
31677c478bd9Sstevel@tonic-gate 			bcopy(ipp->ipp_dstopts, ptr, ipp->ipp_dstoptslen);
31687c478bd9Sstevel@tonic-gate 			return (ipp->ipp_dstoptslen);
31697c478bd9Sstevel@tonic-gate 		case IPV6_PATHMTU:
31707c478bd9Sstevel@tonic-gate 			return (ip_fill_mtuinfo(&udp->udp_v6dst,
31717c478bd9Sstevel@tonic-gate 				udp->udp_dstport, (struct ip6_mtuinfo *)ptr));
31727c478bd9Sstevel@tonic-gate 		default:
31737c478bd9Sstevel@tonic-gate 			return (-1);
31747c478bd9Sstevel@tonic-gate 		}
31757c478bd9Sstevel@tonic-gate 		break;
31767c478bd9Sstevel@tonic-gate 	case IPPROTO_UDP:
31777c478bd9Sstevel@tonic-gate 		switch (name) {
31787c478bd9Sstevel@tonic-gate 		case UDP_ANONPRIVBIND:
31797c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_anon_priv_bind;
31807c478bd9Sstevel@tonic-gate 			break;
31817c478bd9Sstevel@tonic-gate 		case UDP_EXCLBIND:
31827c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_exclbind ? UDP_EXCLBIND : 0;
31837c478bd9Sstevel@tonic-gate 			break;
31847c478bd9Sstevel@tonic-gate 		case UDP_RCVHDR:
31857c478bd9Sstevel@tonic-gate 			*i1 = udp->udp_rcvhdr ? 1 : 0;
31867c478bd9Sstevel@tonic-gate 			break;
31877c478bd9Sstevel@tonic-gate 		default:
31887c478bd9Sstevel@tonic-gate 			return (-1);
31897c478bd9Sstevel@tonic-gate 		}
31907c478bd9Sstevel@tonic-gate 		break;
31917c478bd9Sstevel@tonic-gate 	default:
31927c478bd9Sstevel@tonic-gate 		return (-1);
31937c478bd9Sstevel@tonic-gate 	}
31947c478bd9Sstevel@tonic-gate 	return (sizeof (int));
31957c478bd9Sstevel@tonic-gate }
31967c478bd9Sstevel@tonic-gate 
3197ff550d0eSmasputra /*
3198ff550d0eSmasputra  * This routine sets socket options; it expects the caller
3199ff550d0eSmasputra  * to pass in the queue pointer of the upper instance.
3200ff550d0eSmasputra  */
32017c478bd9Sstevel@tonic-gate /* ARGSUSED */
32027c478bd9Sstevel@tonic-gate int
32037c478bd9Sstevel@tonic-gate udp_opt_set(queue_t *q, uint_t optset_context, int level,
32047c478bd9Sstevel@tonic-gate     int name, uint_t inlen, uchar_t *invalp, uint_t *outlenp,
32057c478bd9Sstevel@tonic-gate     uchar_t *outvalp, void *thisdg_attrs, cred_t *cr, mblk_t *mblk)
32067c478bd9Sstevel@tonic-gate {
32077c478bd9Sstevel@tonic-gate 	int	*i1 = (int *)invalp;
32087c478bd9Sstevel@tonic-gate 	boolean_t onoff = (*i1 == 0) ? 0 : 1;
32097c478bd9Sstevel@tonic-gate 	boolean_t checkonly;
32107c478bd9Sstevel@tonic-gate 	int	error;
3211ff550d0eSmasputra 	conn_t	*connp;
3212ff550d0eSmasputra 	udp_t	*udp;
3213ff550d0eSmasputra 
3214ff550d0eSmasputra 	q = UDP_WR(q);
3215ff550d0eSmasputra 	connp = Q_TO_CONN(q);
3216ff550d0eSmasputra 	udp = connp->conn_udp;
32177c478bd9Sstevel@tonic-gate 
32187c478bd9Sstevel@tonic-gate 	switch (optset_context) {
32197c478bd9Sstevel@tonic-gate 	case SETFN_OPTCOM_CHECKONLY:
32207c478bd9Sstevel@tonic-gate 		checkonly = B_TRUE;
32217c478bd9Sstevel@tonic-gate 		/*
32227c478bd9Sstevel@tonic-gate 		 * Note: Implies T_CHECK semantics for T_OPTCOM_REQ
32237c478bd9Sstevel@tonic-gate 		 * inlen != 0 implies value supplied and
32247c478bd9Sstevel@tonic-gate 		 * 	we have to "pretend" to set it.
32257c478bd9Sstevel@tonic-gate 		 * inlen == 0 implies that there is no
32267c478bd9Sstevel@tonic-gate 		 * 	value part in T_CHECK request and just validation
32277c478bd9Sstevel@tonic-gate 		 * done elsewhere should be enough, we just return here.
32287c478bd9Sstevel@tonic-gate 		 */
32297c478bd9Sstevel@tonic-gate 		if (inlen == 0) {
32307c478bd9Sstevel@tonic-gate 			*outlenp = 0;
32317c478bd9Sstevel@tonic-gate 			return (0);
32327c478bd9Sstevel@tonic-gate 		}
32337c478bd9Sstevel@tonic-gate 		break;
32347c478bd9Sstevel@tonic-gate 	case SETFN_OPTCOM_NEGOTIATE:
32357c478bd9Sstevel@tonic-gate 		checkonly = B_FALSE;
32367c478bd9Sstevel@tonic-gate 		break;
32377c478bd9Sstevel@tonic-gate 	case SETFN_UD_NEGOTIATE:
32387c478bd9Sstevel@tonic-gate 	case SETFN_CONN_NEGOTIATE:
32397c478bd9Sstevel@tonic-gate 		checkonly = B_FALSE;
32407c478bd9Sstevel@tonic-gate 		/*
32417c478bd9Sstevel@tonic-gate 		 * Negotiating local and "association-related" options
32427c478bd9Sstevel@tonic-gate 		 * through T_UNITDATA_REQ.
32437c478bd9Sstevel@tonic-gate 		 *
32447c478bd9Sstevel@tonic-gate 		 * Following routine can filter out ones we do not
32457c478bd9Sstevel@tonic-gate 		 * want to be "set" this way.
32467c478bd9Sstevel@tonic-gate 		 */
32477c478bd9Sstevel@tonic-gate 		if (!udp_opt_allow_udr_set(level, name)) {
32487c478bd9Sstevel@tonic-gate 			*outlenp = 0;
32497c478bd9Sstevel@tonic-gate 			return (EINVAL);
32507c478bd9Sstevel@tonic-gate 		}
32517c478bd9Sstevel@tonic-gate 		break;
32527c478bd9Sstevel@tonic-gate 	default:
32537c478bd9Sstevel@tonic-gate 		/*
32547c478bd9Sstevel@tonic-gate 		 * We should never get here
32557c478bd9Sstevel@tonic-gate 		 */
32567c478bd9Sstevel@tonic-gate 		*outlenp = 0;
32577c478bd9Sstevel@tonic-gate 		return (EINVAL);
32587c478bd9Sstevel@tonic-gate 	}
32597c478bd9Sstevel@tonic-gate 
32607c478bd9Sstevel@tonic-gate 	ASSERT((optset_context != SETFN_OPTCOM_CHECKONLY) ||
32617c478bd9Sstevel@tonic-gate 	    (optset_context == SETFN_OPTCOM_CHECKONLY && inlen != 0));
32627c478bd9Sstevel@tonic-gate 
32637c478bd9Sstevel@tonic-gate 	/*
32647c478bd9Sstevel@tonic-gate 	 * For fixed length options, no sanity check
32657c478bd9Sstevel@tonic-gate 	 * of passed in length is done. It is assumed *_optcom_req()
32667c478bd9Sstevel@tonic-gate 	 * routines do the right thing.
32677c478bd9Sstevel@tonic-gate 	 */
32687c478bd9Sstevel@tonic-gate 
32697c478bd9Sstevel@tonic-gate 	switch (level) {
32707c478bd9Sstevel@tonic-gate 	case SOL_SOCKET:
32717c478bd9Sstevel@tonic-gate 		switch (name) {
32727c478bd9Sstevel@tonic-gate 		case SO_REUSEADDR:
32737c478bd9Sstevel@tonic-gate 			if (!checkonly)
32747c478bd9Sstevel@tonic-gate 				udp->udp_reuseaddr = onoff;
32757c478bd9Sstevel@tonic-gate 			break;
32767c478bd9Sstevel@tonic-gate 		case SO_DEBUG:
32777c478bd9Sstevel@tonic-gate 			if (!checkonly)
32787c478bd9Sstevel@tonic-gate 				udp->udp_debug = onoff;
32797c478bd9Sstevel@tonic-gate 			break;
32807c478bd9Sstevel@tonic-gate 		/*
32817c478bd9Sstevel@tonic-gate 		 * The following three items are available here,
32827c478bd9Sstevel@tonic-gate 		 * but are only meaningful to IP.
32837c478bd9Sstevel@tonic-gate 		 */
32847c478bd9Sstevel@tonic-gate 		case SO_DONTROUTE:
32857c478bd9Sstevel@tonic-gate 			if (!checkonly)
32867c478bd9Sstevel@tonic-gate 				udp->udp_dontroute = onoff;
32877c478bd9Sstevel@tonic-gate 			break;
32887c478bd9Sstevel@tonic-gate 		case SO_USELOOPBACK:
32897c478bd9Sstevel@tonic-gate 			if (!checkonly)
32907c478bd9Sstevel@tonic-gate 				udp->udp_useloopback = onoff;
32917c478bd9Sstevel@tonic-gate 			break;
32927c478bd9Sstevel@tonic-gate 		case SO_BROADCAST:
32937c478bd9Sstevel@tonic-gate 			if (!checkonly)
32947c478bd9Sstevel@tonic-gate 				udp->udp_broadcast = onoff;
32957c478bd9Sstevel@tonic-gate 			break;
32967c478bd9Sstevel@tonic-gate 
32977c478bd9Sstevel@tonic-gate 		case SO_SNDBUF:
32987c478bd9Sstevel@tonic-gate 			if (*i1 > udp_max_buf) {
32997c478bd9Sstevel@tonic-gate 				*outlenp = 0;
33007c478bd9Sstevel@tonic-gate 				return (ENOBUFS);
33017c478bd9Sstevel@tonic-gate 			}
33027c478bd9Sstevel@tonic-gate 			if (!checkonly) {
33037c478bd9Sstevel@tonic-gate 				q->q_hiwat = *i1;
3304ff550d0eSmasputra 				WR(UDP_RD(q))->q_hiwat = *i1;
33057c478bd9Sstevel@tonic-gate 			}
33067c478bd9Sstevel@tonic-gate 			break;
33077c478bd9Sstevel@tonic-gate 		case SO_RCVBUF:
33087c478bd9Sstevel@tonic-gate 			if (*i1 > udp_max_buf) {
33097c478bd9Sstevel@tonic-gate 				*outlenp = 0;
33107c478bd9Sstevel@tonic-gate 				return (ENOBUFS);
33117c478bd9Sstevel@tonic-gate 			}
33127c478bd9Sstevel@tonic-gate 			if (!checkonly) {
33137c478bd9Sstevel@tonic-gate 				RD(q)->q_hiwat = *i1;
3314ff550d0eSmasputra 				UDP_RD(q)->q_hiwat = *i1;
3315ff550d0eSmasputra 				(void) mi_set_sth_hiwat(UDP_RD(q),
3316ff550d0eSmasputra 				    udp_set_rcv_hiwat(udp, *i1));
33177c478bd9Sstevel@tonic-gate 			}
33187c478bd9Sstevel@tonic-gate 			break;
33197c478bd9Sstevel@tonic-gate 		case SO_DGRAM_ERRIND:
33207c478bd9Sstevel@tonic-gate 			if (!checkonly)
33217c478bd9Sstevel@tonic-gate 				udp->udp_dgram_errind = onoff;
33227c478bd9Sstevel@tonic-gate 			break;
33237c478bd9Sstevel@tonic-gate 		case SO_RECVUCRED:
33247c478bd9Sstevel@tonic-gate 			if (!checkonly)
33257c478bd9Sstevel@tonic-gate 				udp->udp_recvucred = onoff;
33267c478bd9Sstevel@tonic-gate 			break;
33277c478bd9Sstevel@tonic-gate 		default:
33287c478bd9Sstevel@tonic-gate 			*outlenp = 0;
33297c478bd9Sstevel@tonic-gate 			return (EINVAL);
33307c478bd9Sstevel@tonic-gate 		}
33317c478bd9Sstevel@tonic-gate 		break;
33327c478bd9Sstevel@tonic-gate 	case IPPROTO_IP:
33337c478bd9Sstevel@tonic-gate 		if (udp->udp_family != AF_INET) {
33347c478bd9Sstevel@tonic-gate 			*outlenp = 0;
33357c478bd9Sstevel@tonic-gate 			return (ENOPROTOOPT);
33367c478bd9Sstevel@tonic-gate 		}
33377c478bd9Sstevel@tonic-gate 		switch (name) {
33387c478bd9Sstevel@tonic-gate 		case IP_OPTIONS:
33397c478bd9Sstevel@tonic-gate 		case T_IP_OPTIONS:
33407c478bd9Sstevel@tonic-gate 			/* Save options for use by IP. */
33417c478bd9Sstevel@tonic-gate 			if (inlen & 0x3) {
33427c478bd9Sstevel@tonic-gate 				*outlenp = 0;
33437c478bd9Sstevel@tonic-gate 				return (EINVAL);
33447c478bd9Sstevel@tonic-gate 			}
33457c478bd9Sstevel@tonic-gate 			if (checkonly)
33467c478bd9Sstevel@tonic-gate 				break;
33477c478bd9Sstevel@tonic-gate 
33487c478bd9Sstevel@tonic-gate 			if (udp->udp_ip_snd_options) {
33497c478bd9Sstevel@tonic-gate 				mi_free((char *)udp->udp_ip_snd_options);
33507c478bd9Sstevel@tonic-gate 				udp->udp_ip_snd_options_len = 0;
33517c478bd9Sstevel@tonic-gate 				udp->udp_ip_snd_options = NULL;
33527c478bd9Sstevel@tonic-gate 			}
33537c478bd9Sstevel@tonic-gate 			if (inlen) {
33547c478bd9Sstevel@tonic-gate 				udp->udp_ip_snd_options =
33557c478bd9Sstevel@tonic-gate 					(uchar_t *)mi_alloc(inlen, BPRI_HI);
33567c478bd9Sstevel@tonic-gate 				if (udp->udp_ip_snd_options) {
33577c478bd9Sstevel@tonic-gate 					bcopy(invalp, udp->udp_ip_snd_options,
33587c478bd9Sstevel@tonic-gate 					    inlen);
33597c478bd9Sstevel@tonic-gate 					udp->udp_ip_snd_options_len = inlen;
33607c478bd9Sstevel@tonic-gate 				}
33617c478bd9Sstevel@tonic-gate 			}
33627c478bd9Sstevel@tonic-gate 			udp->udp_max_hdr_len = IP_SIMPLE_HDR_LENGTH +
33637c478bd9Sstevel@tonic-gate 			    UDPH_SIZE + udp->udp_ip_snd_options_len;
33647c478bd9Sstevel@tonic-gate 			(void) mi_set_sth_wroff(RD(q), udp->udp_max_hdr_len +
33657c478bd9Sstevel@tonic-gate 			    udp_wroff_extra);
33667c478bd9Sstevel@tonic-gate 			break;
33677c478bd9Sstevel@tonic-gate 		case IP_TTL:
33687c478bd9Sstevel@tonic-gate 			if (!checkonly) {
33697c478bd9Sstevel@tonic-gate 				udp->udp_ttl = (uchar_t)*i1;
33707c478bd9Sstevel@tonic-gate 			}
33717c478bd9Sstevel@tonic-gate 			break;
33727c478bd9Sstevel@tonic-gate 		case IP_TOS:
33737c478bd9Sstevel@tonic-gate 		case T_IP_TOS:
33747c478bd9Sstevel@tonic-gate 			if (!checkonly) {
33757c478bd9Sstevel@tonic-gate 				udp->udp_type_of_service = (uchar_t)*i1;
33767c478bd9Sstevel@tonic-gate 			}
33777c478bd9Sstevel@tonic-gate 			break;
33787c478bd9Sstevel@tonic-gate 		case IP_MULTICAST_IF: {
33797c478bd9Sstevel@tonic-gate 			/*
33807c478bd9Sstevel@tonic-gate 			 * TODO should check OPTMGMT reply and undo this if
33817c478bd9Sstevel@tonic-gate 			 * there is an error.
33827c478bd9Sstevel@tonic-gate 			 */
33837c478bd9Sstevel@tonic-gate 			struct in_addr *inap = (struct in_addr *)invalp;
33847c478bd9Sstevel@tonic-gate 			if (!checkonly) {
33857c478bd9Sstevel@tonic-gate 				udp->udp_multicast_if_addr =
33867c478bd9Sstevel@tonic-gate 				    inap->s_addr;
33877c478bd9Sstevel@tonic-gate 			}
33887c478bd9Sstevel@tonic-gate 			break;
33897c478bd9Sstevel@tonic-gate 		}
33907c478bd9Sstevel@tonic-gate 		case IP_MULTICAST_TTL:
33917c478bd9Sstevel@tonic-gate 			if (!checkonly)
33927c478bd9Sstevel@tonic-gate 				udp->udp_multicast_ttl = *invalp;
33937c478bd9Sstevel@tonic-gate 			break;
33947c478bd9Sstevel@tonic-gate 		case IP_MULTICAST_LOOP:
33957c478bd9Sstevel@tonic-gate 			if (!checkonly)
3396ff550d0eSmasputra 				connp->conn_multicast_loop = *invalp;
33977c478bd9Sstevel@tonic-gate 			break;
33987c478bd9Sstevel@tonic-gate 		case IP_RECVOPTS:
33997c478bd9Sstevel@tonic-gate 			if (!checkonly)
34007c478bd9Sstevel@tonic-gate 				udp->udp_recvopts = onoff;
34017c478bd9Sstevel@tonic-gate 			break;
34027c478bd9Sstevel@tonic-gate 		case IP_RECVDSTADDR:
34037c478bd9Sstevel@tonic-gate 			if (!checkonly)
34047c478bd9Sstevel@tonic-gate 				udp->udp_recvdstaddr = onoff;
34057c478bd9Sstevel@tonic-gate 			break;
34067c478bd9Sstevel@tonic-gate 		case IP_RECVIF:
34077c478bd9Sstevel@tonic-gate 			if (!checkonly)
34087c478bd9Sstevel@tonic-gate 				udp->udp_recvif = onoff;
34097c478bd9Sstevel@tonic-gate 			break;
34107c478bd9Sstevel@tonic-gate 		case IP_RECVSLLA:
34117c478bd9Sstevel@tonic-gate 			if (!checkonly)
34127c478bd9Sstevel@tonic-gate 				udp->udp_recvslla = onoff;
34137c478bd9Sstevel@tonic-gate 			break;
34147c478bd9Sstevel@tonic-gate 		case IP_RECVTTL:
34157c478bd9Sstevel@tonic-gate 			if (!checkonly)
34167c478bd9Sstevel@tonic-gate 				udp->udp_recvttl = onoff;
34177c478bd9Sstevel@tonic-gate 			break;
34187c478bd9Sstevel@tonic-gate 		case IP_ADD_MEMBERSHIP:
34197c478bd9Sstevel@tonic-gate 		case IP_DROP_MEMBERSHIP:
34207c478bd9Sstevel@tonic-gate 		case IP_BLOCK_SOURCE:
34217c478bd9Sstevel@tonic-gate 		case IP_UNBLOCK_SOURCE:
34227c478bd9Sstevel@tonic-gate 		case IP_ADD_SOURCE_MEMBERSHIP:
34237c478bd9Sstevel@tonic-gate 		case IP_DROP_SOURCE_MEMBERSHIP:
34247c478bd9Sstevel@tonic-gate 		case MCAST_JOIN_GROUP:
34257c478bd9Sstevel@tonic-gate 		case MCAST_LEAVE_GROUP:
34267c478bd9Sstevel@tonic-gate 		case MCAST_BLOCK_SOURCE:
34277c478bd9Sstevel@tonic-gate 		case MCAST_UNBLOCK_SOURCE:
34287c478bd9Sstevel@tonic-gate 		case MCAST_JOIN_SOURCE_GROUP:
34297c478bd9Sstevel@tonic-gate 		case MCAST_LEAVE_SOURCE_GROUP:
34307c478bd9Sstevel@tonic-gate 		case IP_SEC_OPT:
343143d18f1cSpriyanka 		case IP_NEXTHOP:
34327c478bd9Sstevel@tonic-gate 			/*
34337c478bd9Sstevel@tonic-gate 			 * "soft" error (negative)
34347c478bd9Sstevel@tonic-gate 			 * option not handled at this level
34357c478bd9Sstevel@tonic-gate 			 * Do not modify *outlenp.
34367c478bd9Sstevel@tonic-gate 			 */
34377c478bd9Sstevel@tonic-gate 			return (-EINVAL);
34387c478bd9Sstevel@tonic-gate 		case IP_BOUND_IF:
34397c478bd9Sstevel@tonic-gate 			if (!checkonly)
34407c478bd9Sstevel@tonic-gate 				udp->udp_bound_if = *i1;
34417c478bd9Sstevel@tonic-gate 			break;
34427c478bd9Sstevel@tonic-gate 		case IP_UNSPEC_SRC:
34437c478bd9Sstevel@tonic-gate 			if (!checkonly)
34447c478bd9Sstevel@tonic-gate 				udp->udp_unspec_source = onoff;
34457c478bd9Sstevel@tonic-gate 			break;
34467c478bd9Sstevel@tonic-gate 		case IP_XMIT_IF:
34477c478bd9Sstevel@tonic-gate 			if (!checkonly)
34487c478bd9Sstevel@tonic-gate 				udp->udp_xmit_if = *i1;
34497c478bd9Sstevel@tonic-gate 			break;
34507c478bd9Sstevel@tonic-gate 		default:
34517c478bd9Sstevel@tonic-gate 			*outlenp = 0;
34527c478bd9Sstevel@tonic-gate 			return (EINVAL);
34537c478bd9Sstevel@tonic-gate 		}
34547c478bd9Sstevel@tonic-gate 		break;
34557c478bd9Sstevel@tonic-gate 	case IPPROTO_IPV6: {
34567c478bd9Sstevel@tonic-gate 		ip6_pkt_t		*ipp;
34577c478bd9Sstevel@tonic-gate 		boolean_t		sticky;
34587c478bd9Sstevel@tonic-gate 
34597c478bd9Sstevel@tonic-gate 		if (udp->udp_family != AF_INET6) {
34607c478bd9Sstevel@tonic-gate 			*outlenp = 0;
34617c478bd9Sstevel@tonic-gate 			return (ENOPROTOOPT);
34627c478bd9Sstevel@tonic-gate 		}
34637c478bd9Sstevel@tonic-gate 		/*
34647c478bd9Sstevel@tonic-gate 		 * Deal with both sticky options and ancillary data
34657c478bd9Sstevel@tonic-gate 		 */
34667c478bd9Sstevel@tonic-gate 		if (thisdg_attrs == NULL) {
34677c478bd9Sstevel@tonic-gate 			/* sticky options, or none */
34687c478bd9Sstevel@tonic-gate 			ipp = &udp->udp_sticky_ipp;
34697c478bd9Sstevel@tonic-gate 			sticky = B_TRUE;
34707c478bd9Sstevel@tonic-gate 		} else {
34717c478bd9Sstevel@tonic-gate 			/* ancillary data */
34727c478bd9Sstevel@tonic-gate 			ipp = (ip6_pkt_t *)thisdg_attrs;
34737c478bd9Sstevel@tonic-gate 			sticky = B_FALSE;
34747c478bd9Sstevel@tonic-gate 		}
34757c478bd9Sstevel@tonic-gate 
34767c478bd9Sstevel@tonic-gate 		switch (name) {
34777c478bd9Sstevel@tonic-gate 		case IPV6_MULTICAST_IF:
34787c478bd9Sstevel@tonic-gate 			if (!checkonly)
34797c478bd9Sstevel@tonic-gate 				udp->udp_multicast_if_index = *i1;
34807c478bd9Sstevel@tonic-gate 			break;
34817c478bd9Sstevel@tonic-gate 		case IPV6_UNICAST_HOPS:
34827c478bd9Sstevel@tonic-gate 			/* -1 means use default */
34837c478bd9Sstevel@tonic-gate 			if (*i1 < -1 || *i1 > IPV6_MAX_HOPS) {
34847c478bd9Sstevel@tonic-gate 				*outlenp = 0;
34857c478bd9Sstevel@tonic-gate 				return (EINVAL);
34867c478bd9Sstevel@tonic-gate 			}
34877c478bd9Sstevel@tonic-gate 			if (!checkonly) {
34887c478bd9Sstevel@tonic-gate 				if (*i1 == -1) {
3489b3d0fa4fSseb 					udp->udp_ttl = ipp->ipp_unicast_hops =
34907c478bd9Sstevel@tonic-gate 					    udp_ipv6_hoplimit;
3491b3d0fa4fSseb 					ipp->ipp_fields &= ~IPPF_UNICAST_HOPS;
34927c478bd9Sstevel@tonic-gate 					/* Pass modified value to IP. */
34937c478bd9Sstevel@tonic-gate 					*i1 = udp->udp_ttl;
34947c478bd9Sstevel@tonic-gate 				} else {
3495b3d0fa4fSseb 					udp->udp_ttl = ipp->ipp_unicast_hops =
34967c478bd9Sstevel@tonic-gate 					    (uint8_t)*i1;
3497b3d0fa4fSseb 					ipp->ipp_fields |= IPPF_UNICAST_HOPS;
34987c478bd9Sstevel@tonic-gate 				}
34997c478bd9Sstevel@tonic-gate 				/* Rebuild the header template */
35007c478bd9Sstevel@tonic-gate 				error = udp_build_hdrs(q, udp);
35017c478bd9Sstevel@tonic-gate 				if (error != 0) {
35027c478bd9Sstevel@tonic-gate 					*outlenp = 0;
35037c478bd9Sstevel@tonic-gate 					return (error);
35047c478bd9Sstevel@tonic-gate 				}
35057c478bd9Sstevel@tonic-gate 			}
35067c478bd9Sstevel@tonic-gate 			break;
35077c478bd9Sstevel@tonic-gate 		case IPV6_MULTICAST_HOPS:
35087c478bd9Sstevel@tonic-gate 			/* -1 means use default */
35097c478bd9Sstevel@tonic-gate 			if (*i1 < -1 || *i1 > IPV6_MAX_HOPS) {
35107c478bd9Sstevel@tonic-gate 				*outlenp = 0;
35117c478bd9Sstevel@tonic-gate 				return (EINVAL);
35127c478bd9Sstevel@tonic-gate 			}
35137c478bd9Sstevel@tonic-gate 			if (!checkonly) {
35147c478bd9Sstevel@tonic-gate 				if (*i1 == -1) {
35157c478bd9Sstevel@tonic-gate 					udp->udp_multicast_ttl =
3516b3d0fa4fSseb 					    ipp->ipp_multicast_hops =
35177c478bd9Sstevel@tonic-gate 					    IP_DEFAULT_MULTICAST_TTL;
3518b3d0fa4fSseb 					ipp->ipp_fields &= ~IPPF_MULTICAST_HOPS;
35197c478bd9Sstevel@tonic-gate 					/* Pass modified value to IP. */
35207c478bd9Sstevel@tonic-gate 					*i1 = udp->udp_multicast_ttl;
35217c478bd9Sstevel@tonic-gate 				} else {
35227c478bd9Sstevel@tonic-gate 					udp->udp_multicast_ttl =
3523b3d0fa4fSseb 					    ipp->ipp_multicast_hops =
35247c478bd9Sstevel@tonic-gate 					    (uint8_t)*i1;
3525b3d0fa4fSseb 					ipp->ipp_fields |= IPPF_MULTICAST_HOPS;
35267c478bd9Sstevel@tonic-gate 				}
35277c478bd9Sstevel@tonic-gate 			}
35287c478bd9Sstevel@tonic-gate 			break;
35297c478bd9Sstevel@tonic-gate 		case IPV6_MULTICAST_LOOP:
35307c478bd9Sstevel@tonic-gate 			if (*i1 != 0 && *i1 != 1) {
35317c478bd9Sstevel@tonic-gate 				*outlenp = 0;
35327c478bd9Sstevel@tonic-gate 				return (EINVAL);
35337c478bd9Sstevel@tonic-gate 			}
35347c478bd9Sstevel@tonic-gate 			if (!checkonly)
3535ff550d0eSmasputra 				connp->conn_multicast_loop = *i1;
35367c478bd9Sstevel@tonic-gate 			break;
35377c478bd9Sstevel@tonic-gate 		case IPV6_JOIN_GROUP:
35387c478bd9Sstevel@tonic-gate 		case IPV6_LEAVE_GROUP:
35397c478bd9Sstevel@tonic-gate 		case MCAST_JOIN_GROUP:
35407c478bd9Sstevel@tonic-gate 		case MCAST_LEAVE_GROUP:
35417c478bd9Sstevel@tonic-gate 		case MCAST_BLOCK_SOURCE:
35427c478bd9Sstevel@tonic-gate 		case MCAST_UNBLOCK_SOURCE:
35437c478bd9Sstevel@tonic-gate 		case MCAST_JOIN_SOURCE_GROUP:
35447c478bd9Sstevel@tonic-gate 		case MCAST_LEAVE_SOURCE_GROUP:
35457c478bd9Sstevel@tonic-gate 			/*
35467c478bd9Sstevel@tonic-gate 			 * "soft" error (negative)
35477c478bd9Sstevel@tonic-gate 			 * option not handled at this level
35487c478bd9Sstevel@tonic-gate 			 * Note: Do not modify *outlenp
35497c478bd9Sstevel@tonic-gate 			 */
35507c478bd9Sstevel@tonic-gate 			return (-EINVAL);
35517c478bd9Sstevel@tonic-gate 		case IPV6_BOUND_IF:
35527c478bd9Sstevel@tonic-gate 			if (!checkonly)
35537c478bd9Sstevel@tonic-gate 				udp->udp_bound_if = *i1;
35547c478bd9Sstevel@tonic-gate 			break;
35557c478bd9Sstevel@tonic-gate 		case IPV6_UNSPEC_SRC:
35567c478bd9Sstevel@tonic-gate 			if (!checkonly)
35577c478bd9Sstevel@tonic-gate 				udp->udp_unspec_source = onoff;
35587c478bd9Sstevel@tonic-gate 			break;
35597c478bd9Sstevel@tonic-gate 		/*
35607c478bd9Sstevel@tonic-gate 		 * Set boolean switches for ancillary data delivery
35617c478bd9Sstevel@tonic-gate 		 */
35627c478bd9Sstevel@tonic-gate 		case IPV6_RECVPKTINFO:
35637c478bd9Sstevel@tonic-gate 			if (!checkonly)
35647c478bd9Sstevel@tonic-gate 				udp->udp_ipv6_recvpktinfo = onoff;
35657c478bd9Sstevel@tonic-gate 			break;
35667c478bd9Sstevel@tonic-gate 		case IPV6_RECVTCLASS:
35677c478bd9Sstevel@tonic-gate 			if (!checkonly) {
35687c478bd9Sstevel@tonic-gate 				udp->udp_ipv6_recvtclass = onoff;
35697c478bd9Sstevel@tonic-gate 			}
35707c478bd9Sstevel@tonic-gate 			break;
35717c478bd9Sstevel@tonic-gate 		case IPV6_RECVPATHMTU:
35727c478bd9Sstevel@tonic-gate 			if (!checkonly) {
35737c478bd9Sstevel@tonic-gate 				udp->udp_ipv6_recvpathmtu = onoff;
35747c478bd9Sstevel@tonic-gate 			}
35757c478bd9Sstevel@tonic-gate 			break;
35767c478bd9Sstevel@tonic-gate 		case IPV6_RECVHOPLIMIT:
35777c478bd9Sstevel@tonic-gate 			if (!checkonly)
35787c478bd9Sstevel@tonic-gate 				udp->udp_ipv6_recvhoplimit = onoff;
35797c478bd9Sstevel@tonic-gate 			break;
35807c478bd9Sstevel@tonic-gate 		case IPV6_RECVHOPOPTS:
35817c478bd9Sstevel@tonic-gate 			if (!checkonly)
35827c478bd9Sstevel@tonic-gate 				udp->udp_ipv6_recvhopopts = onoff;
35837c478bd9Sstevel@tonic-gate 			break;
35847c478bd9Sstevel@tonic-gate 		case IPV6_RECVDSTOPTS:
35857c478bd9Sstevel@tonic-gate 			if (!checkonly)
35867c478bd9Sstevel@tonic-gate 				udp->udp_ipv6_recvdstopts = onoff;
35877c478bd9Sstevel@tonic-gate 			break;
35887c478bd9Sstevel@tonic-gate 		case _OLD_IPV6_RECVDSTOPTS:
35897c478bd9Sstevel@tonic-gate 			if (!checkonly)
35907c478bd9Sstevel@tonic-gate 				udp->udp_old_ipv6_recvdstopts = onoff;
35917c478bd9Sstevel@tonic-gate 			break;
35927c478bd9Sstevel@tonic-gate 		case IPV6_RECVRTHDRDSTOPTS:
35937c478bd9Sstevel@tonic-gate 			if (!checkonly)
35947c478bd9Sstevel@tonic-gate 				udp->udp_ipv6_recvrthdrdstopts = onoff;
35957c478bd9Sstevel@tonic-gate 			break;
35967c478bd9Sstevel@tonic-gate 		case IPV6_RECVRTHDR:
35977c478bd9Sstevel@tonic-gate 			if (!checkonly)
35987c478bd9Sstevel@tonic-gate 				udp->udp_ipv6_recvrthdr = onoff;
35997c478bd9Sstevel@tonic-gate 			break;
36007c478bd9Sstevel@tonic-gate 		/*
36017c478bd9Sstevel@tonic-gate 		 * Set sticky options or ancillary data.
36027c478bd9Sstevel@tonic-gate 		 * If sticky options, (re)build any extension headers
36037c478bd9Sstevel@tonic-gate 		 * that might be needed as a result.
36047c478bd9Sstevel@tonic-gate 		 */
36057c478bd9Sstevel@tonic-gate 		case IPV6_PKTINFO:
36067c478bd9Sstevel@tonic-gate 			/*
36077c478bd9Sstevel@tonic-gate 			 * The source address and ifindex are verified
36087c478bd9Sstevel@tonic-gate 			 * in ip_opt_set(). For ancillary data the
36097c478bd9Sstevel@tonic-gate 			 * source address is checked in ip_wput_v6.
36107c478bd9Sstevel@tonic-gate 			 */
36117c478bd9Sstevel@tonic-gate 			if (inlen != 0 && inlen != sizeof (struct in6_pktinfo))
36127c478bd9Sstevel@tonic-gate 				return (EINVAL);
36137c478bd9Sstevel@tonic-gate 			if (checkonly)
36147c478bd9Sstevel@tonic-gate 				break;
36157c478bd9Sstevel@tonic-gate 
36167c478bd9Sstevel@tonic-gate 			if (inlen == 0) {
36177c478bd9Sstevel@tonic-gate 				ipp->ipp_fields &= ~(IPPF_IFINDEX|IPPF_ADDR);
36187c478bd9Sstevel@tonic-gate 				ipp->ipp_sticky_ignored |=
36197c478bd9Sstevel@tonic-gate 				    (IPPF_IFINDEX|IPPF_ADDR);
36207c478bd9Sstevel@tonic-gate 			} else {
36217c478bd9Sstevel@tonic-gate 				struct in6_pktinfo *pkti;
36227c478bd9Sstevel@tonic-gate 
36237c478bd9Sstevel@tonic-gate 				pkti = (struct in6_pktinfo *)invalp;
36247c478bd9Sstevel@tonic-gate 				ipp->ipp_ifindex = pkti->ipi6_ifindex;
36257c478bd9Sstevel@tonic-gate 				ipp->ipp_addr = pkti->ipi6_addr;
36267c478bd9Sstevel@tonic-gate 				if (ipp->ipp_ifindex != 0)
36277c478bd9Sstevel@tonic-gate 					ipp->ipp_fields |= IPPF_IFINDEX;
36287c478bd9Sstevel@tonic-gate 				else
36297c478bd9Sstevel@tonic-gate 					ipp->ipp_fields &= ~IPPF_IFINDEX;
36307c478bd9Sstevel@tonic-gate 				if (!IN6_IS_ADDR_UNSPECIFIED(
36317c478bd9Sstevel@tonic-gate 				    &ipp->ipp_addr))
36327c478bd9Sstevel@tonic-gate 					ipp->ipp_fields |= IPPF_ADDR;
36337c478bd9Sstevel@tonic-gate 				else
36347c478bd9Sstevel@tonic-gate 					ipp->ipp_fields &= ~IPPF_ADDR;
36357c478bd9Sstevel@tonic-gate 			}
36367c478bd9Sstevel@tonic-gate 			if (sticky) {
36377c478bd9Sstevel@tonic-gate 				error = udp_build_hdrs(q, udp);
36387c478bd9Sstevel@tonic-gate 				if (error != 0)
36397c478bd9Sstevel@tonic-gate 					return (error);
36407c478bd9Sstevel@tonic-gate 			}
36417c478bd9Sstevel@tonic-gate 			break;
36427c478bd9Sstevel@tonic-gate 		case IPV6_HOPLIMIT:
3643b3d0fa4fSseb 			if (sticky)
3644b3d0fa4fSseb 				return (EINVAL);
36457c478bd9Sstevel@tonic-gate 			if (inlen != 0 && inlen != sizeof (int))
36467c478bd9Sstevel@tonic-gate 				return (EINVAL);
36477c478bd9Sstevel@tonic-gate 			if (checkonly)
36487c478bd9Sstevel@tonic-gate 				break;
36497c478bd9Sstevel@tonic-gate 
36507c478bd9Sstevel@tonic-gate 			if (inlen == 0) {
36517c478bd9Sstevel@tonic-gate 				ipp->ipp_fields &= ~IPPF_HOPLIMIT;
36527c478bd9Sstevel@tonic-gate 				ipp->ipp_sticky_ignored |= IPPF_HOPLIMIT;
36537c478bd9Sstevel@tonic-gate 			} else {
36547c478bd9Sstevel@tonic-gate 				if (*i1 > 255 || *i1 < -1)
36557c478bd9Sstevel@tonic-gate 					return (EINVAL);
36567c478bd9Sstevel@tonic-gate 				if (*i1 == -1)
36577c478bd9Sstevel@tonic-gate 					ipp->ipp_hoplimit = udp_ipv6_hoplimit;
36587c478bd9Sstevel@tonic-gate 				else
36597c478bd9Sstevel@tonic-gate 					ipp->ipp_hoplimit = *i1;
36607c478bd9Sstevel@tonic-gate 				ipp->ipp_fields |= IPPF_HOPLIMIT;
36617c478bd9Sstevel@tonic-gate 			}
36627c478bd9Sstevel@tonic-gate 			break;
36637c478bd9Sstevel@tonic-gate 		case IPV6_TCLASS:
36647c478bd9Sstevel@tonic-gate 			if (inlen != 0 && inlen != sizeof (int))
36657c478bd9Sstevel@tonic-gate 				return (EINVAL);
36667c478bd9Sstevel@tonic-gate 			if (checkonly)
36677c478bd9Sstevel@tonic-gate 				break;
36687c478bd9Sstevel@tonic-gate 
36697c478bd9Sstevel@tonic-gate 			if (inlen == 0) {
36707c478bd9Sstevel@tonic-gate 				ipp->ipp_fields &= ~IPPF_TCLASS;
36717c478bd9Sstevel@tonic-gate 				ipp->ipp_sticky_ignored |= IPPF_TCLASS;
36727c478bd9Sstevel@tonic-gate 			} else {
36737c478bd9Sstevel@tonic-gate 				if (*i1 > 255 || *i1 < -1)
36747c478bd9Sstevel@tonic-gate 					return (EINVAL);
36757c478bd9Sstevel@tonic-gate 				if (*i1 == -1)
36767c478bd9Sstevel@tonic-gate 					ipp->ipp_tclass = 0;
36777c478bd9Sstevel@tonic-gate 				else
36787c478bd9Sstevel@tonic-gate 					ipp->ipp_tclass = *i1;
36797c478bd9Sstevel@tonic-gate 				ipp->ipp_fields |= IPPF_TCLASS;
36807c478bd9Sstevel@tonic-gate 			}
36817c478bd9Sstevel@tonic-gate 			if (sticky) {
36827c478bd9Sstevel@tonic-gate 				error = udp_build_hdrs(q, udp);
36837c478bd9Sstevel@tonic-gate 				if (error != 0)
36847c478bd9Sstevel@tonic-gate 					return (error);
36857c478bd9Sstevel@tonic-gate 			}
36867c478bd9Sstevel@tonic-gate 			break;
36877c478bd9Sstevel@tonic-gate 		case IPV6_NEXTHOP:
36887c478bd9Sstevel@tonic-gate 			/*
36897c478bd9Sstevel@tonic-gate 			 * IP will verify that the nexthop is reachable
36907c478bd9Sstevel@tonic-gate 			 * and fail for sticky options.
36917c478bd9Sstevel@tonic-gate 			 */
36927c478bd9Sstevel@tonic-gate 			if (inlen != 0 && inlen != sizeof (sin6_t))
36937c478bd9Sstevel@tonic-gate 				return (EINVAL);
36947c478bd9Sstevel@tonic-gate 			if (checkonly)
36957c478bd9Sstevel@tonic-gate 				break;
36967c478bd9Sstevel@tonic-gate 
36977c478bd9Sstevel@tonic-gate 			if (inlen == 0) {
36987c478bd9Sstevel@tonic-gate 				ipp->ipp_fields &= ~IPPF_NEXTHOP;
36997c478bd9Sstevel@tonic-gate 				ipp->ipp_sticky_ignored |= IPPF_NEXTHOP;
37007c478bd9Sstevel@tonic-gate 			} else {
37017c478bd9Sstevel@tonic-gate 				sin6_t *sin6 = (sin6_t *)invalp;
37027c478bd9Sstevel@tonic-gate 
37037c478bd9Sstevel@tonic-gate 				if (sin6->sin6_family != AF_INET6)
37047c478bd9Sstevel@tonic-gate 					return (EAFNOSUPPORT);
37057c478bd9Sstevel@tonic-gate 				if (IN6_IS_ADDR_V4MAPPED(
37067c478bd9Sstevel@tonic-gate 				    &sin6->sin6_addr))
37077c478bd9Sstevel@tonic-gate 					return (EADDRNOTAVAIL);
37087c478bd9Sstevel@tonic-gate 				ipp->ipp_nexthop = sin6->sin6_addr;
37097c478bd9Sstevel@tonic-gate 				if (!IN6_IS_ADDR_UNSPECIFIED(
37107c478bd9Sstevel@tonic-gate 				    &ipp->ipp_nexthop))
37117c478bd9Sstevel@tonic-gate 					ipp->ipp_fields |= IPPF_NEXTHOP;
37127c478bd9Sstevel@tonic-gate 				else
37137c478bd9Sstevel@tonic-gate 					ipp->ipp_fields &= ~IPPF_NEXTHOP;
37147c478bd9Sstevel@tonic-gate 			}
37157c478bd9Sstevel@tonic-gate 			if (sticky) {
37167c478bd9Sstevel@tonic-gate 				error = udp_build_hdrs(q, udp);
37177c478bd9Sstevel@tonic-gate 				if (error != 0)
37187c478bd9Sstevel@tonic-gate 					return (error);
37197c478bd9Sstevel@tonic-gate 			}
37207c478bd9Sstevel@tonic-gate 			break;
37217c478bd9Sstevel@tonic-gate 		case IPV6_HOPOPTS: {
37227c478bd9Sstevel@tonic-gate 			ip6_hbh_t *hopts = (ip6_hbh_t *)invalp;
37237c478bd9Sstevel@tonic-gate 			/*
37247c478bd9Sstevel@tonic-gate 			 * Sanity checks - minimum size, size a multiple of
37257c478bd9Sstevel@tonic-gate 			 * eight bytes, and matching size passed in.
37267c478bd9Sstevel@tonic-gate 			 */
37277c478bd9Sstevel@tonic-gate 			if (inlen != 0 &&
37287c478bd9Sstevel@tonic-gate 			    inlen != (8 * (hopts->ip6h_len + 1)))
37297c478bd9Sstevel@tonic-gate 				return (EINVAL);
37307c478bd9Sstevel@tonic-gate 
37317c478bd9Sstevel@tonic-gate 			if (checkonly)
37327c478bd9Sstevel@tonic-gate 				break;
37337c478bd9Sstevel@tonic-gate 
37347c478bd9Sstevel@tonic-gate 			if (inlen == 0) {
37357c478bd9Sstevel@tonic-gate 				if (sticky &&
37367c478bd9Sstevel@tonic-gate 				    (ipp->ipp_fields & IPPF_HOPOPTS) != 0) {
37377c478bd9Sstevel@tonic-gate 					kmem_free(ipp->ipp_hopopts,
37387c478bd9Sstevel@tonic-gate 					    ipp->ipp_hopoptslen);
37397c478bd9Sstevel@tonic-gate 					ipp->ipp_hopopts = NULL;
37407c478bd9Sstevel@tonic-gate 					ipp->ipp_hopoptslen = 0;
37417c478bd9Sstevel@tonic-gate 				}
37427c478bd9Sstevel@tonic-gate 				ipp->ipp_fields &= ~IPPF_HOPOPTS;
37437c478bd9Sstevel@tonic-gate 				ipp->ipp_sticky_ignored |= IPPF_HOPOPTS;
37447c478bd9Sstevel@tonic-gate 			} else {
37457c478bd9Sstevel@tonic-gate 				error = udp_pkt_set(invalp, inlen, sticky,
37467c478bd9Sstevel@tonic-gate 				    (uchar_t **)&ipp->ipp_hopopts,
37477c478bd9Sstevel@tonic-gate 				    &ipp->ipp_hopoptslen);
37487c478bd9Sstevel@tonic-gate 				if (error != 0)
37497c478bd9Sstevel@tonic-gate 					return (error);
37507c478bd9Sstevel@tonic-gate 				ipp->ipp_fields |= IPPF_HOPOPTS;
37517c478bd9Sstevel@tonic-gate 			}
37527c478bd9Sstevel@tonic-gate 			if (sticky) {
37537c478bd9Sstevel@tonic-gate 				error = udp_build_hdrs(q, udp);
37547c478bd9Sstevel@tonic-gate 				if (error != 0)
37557c478bd9Sstevel@tonic-gate 					return (error);
37567c478bd9Sstevel@tonic-gate 			}
37577c478bd9Sstevel@tonic-gate 			break;
37587c478bd9Sstevel@tonic-gate 		}
37597c478bd9Sstevel@tonic-gate 		case IPV6_RTHDRDSTOPTS: {
37607c478bd9Sstevel@tonic-gate 			ip6_dest_t *dopts = (ip6_dest_t *)invalp;
37617c478bd9Sstevel@tonic-gate 
37627c478bd9Sstevel@tonic-gate 			/*
37637c478bd9Sstevel@tonic-gate 			 * Sanity checks - minimum size, size a multiple of
37647c478bd9Sstevel@tonic-gate 			 * eight bytes, and matching size passed in.
37657c478bd9Sstevel@tonic-gate 			 */
37667c478bd9Sstevel@tonic-gate 			if (inlen != 0 &&
37677c478bd9Sstevel@tonic-gate 			    inlen != (8 * (dopts->ip6d_len + 1)))
37687c478bd9Sstevel@tonic-gate 				return (EINVAL);
37697c478bd9Sstevel@tonic-gate 
37707c478bd9Sstevel@tonic-gate 			if (checkonly)
37717c478bd9Sstevel@tonic-gate 				break;
37727c478bd9Sstevel@tonic-gate 
37737c478bd9Sstevel@tonic-gate 			if (inlen == 0) {
37747c478bd9Sstevel@tonic-gate 				if (sticky &&
37757c478bd9Sstevel@tonic-gate 				    (ipp->ipp_fields & IPPF_RTDSTOPTS) != 0) {
37767c478bd9Sstevel@tonic-gate 					kmem_free(ipp->ipp_rtdstopts,
37777c478bd9Sstevel@tonic-gate 					    ipp->ipp_rtdstoptslen);
37787c478bd9Sstevel@tonic-gate 					ipp->ipp_rtdstopts = NULL;
37797c478bd9Sstevel@tonic-gate 					ipp->ipp_rtdstoptslen = 0;
37807c478bd9Sstevel@tonic-gate 				}
3781ff550d0eSmasputra 
37827c478bd9Sstevel@tonic-gate 				ipp->ipp_fields &= ~IPPF_RTDSTOPTS;
37837c478bd9Sstevel@tonic-gate 				ipp->ipp_sticky_ignored |= IPPF_RTDSTOPTS;
37847c478bd9Sstevel@tonic-gate 			} else {
37857c478bd9Sstevel@tonic-gate 				error = udp_pkt_set(invalp, inlen, sticky,
37867c478bd9Sstevel@tonic-gate 				    (uchar_t **)&ipp->ipp_rtdstopts,
37877c478bd9Sstevel@tonic-gate 				    &ipp->ipp_rtdstoptslen);
37887c478bd9Sstevel@tonic-gate 				if (error != 0)
37897c478bd9Sstevel@tonic-gate 					return (error);
37907c478bd9Sstevel@tonic-gate 				ipp->ipp_fields |= IPPF_RTDSTOPTS;
37917c478bd9Sstevel@tonic-gate 			}
37927c478bd9Sstevel@tonic-gate 			if (sticky) {
37937c478bd9Sstevel@tonic-gate 				error = udp_build_hdrs(q, udp);
37947c478bd9Sstevel@tonic-gate 				if (error != 0)
37957c478bd9Sstevel@tonic-gate 					return (error);
37967c478bd9Sstevel@tonic-gate 			}
37977c478bd9Sstevel@tonic-gate 			break;
37987c478bd9Sstevel@tonic-gate 		}
37997c478bd9Sstevel@tonic-gate 		case IPV6_DSTOPTS: {
38007c478bd9Sstevel@tonic-gate 			ip6_dest_t *dopts = (ip6_dest_t *)invalp;
38017c478bd9Sstevel@tonic-gate 
38027c478bd9Sstevel@tonic-gate 			/*
38037c478bd9Sstevel@tonic-gate 			 * Sanity checks - minimum size, size a multiple of
38047c478bd9Sstevel@tonic-gate 			 * eight bytes, and matching size passed in.
38057c478bd9Sstevel@tonic-gate 			 */
38067c478bd9Sstevel@tonic-gate 			if (inlen != 0 &&
38077c478bd9Sstevel@tonic-gate 			    inlen != (8 * (dopts->ip6d_len + 1)))
38087c478bd9Sstevel@tonic-gate 				return (EINVAL);
38097c478bd9Sstevel@tonic-gate 
38107c478bd9Sstevel@tonic-gate 			if (checkonly)
38117c478bd9Sstevel@tonic-gate 				break;
38127c478bd9Sstevel@tonic-gate 
38137c478bd9Sstevel@tonic-gate 			if (inlen == 0) {
38147c478bd9Sstevel@tonic-gate 				if (sticky &&
38157c478bd9Sstevel@tonic-gate 				    (ipp->ipp_fields & IPPF_DSTOPTS) != 0) {
38167c478bd9Sstevel@tonic-gate 					kmem_free(ipp->ipp_dstopts,
38177c478bd9Sstevel@tonic-gate 					    ipp->ipp_dstoptslen);
38187c478bd9Sstevel@tonic-gate 					ipp->ipp_dstopts = NULL;
38197c478bd9Sstevel@tonic-gate 					ipp->ipp_dstoptslen = 0;
38207c478bd9Sstevel@tonic-gate 				}
38217c478bd9Sstevel@tonic-gate 				ipp->ipp_fields &= ~IPPF_DSTOPTS;
38227c478bd9Sstevel@tonic-gate 				ipp->ipp_sticky_ignored |= IPPF_DSTOPTS;
38237c478bd9Sstevel@tonic-gate 			} else {
38247c478bd9Sstevel@tonic-gate 				error = udp_pkt_set(invalp, inlen, sticky,
38257c478bd9Sstevel@tonic-gate 				    (uchar_t **)&ipp->ipp_dstopts,
38267c478bd9Sstevel@tonic-gate 				    &ipp->ipp_dstoptslen);
38277c478bd9Sstevel@tonic-gate 				if (error != 0)
38287c478bd9Sstevel@tonic-gate 					return (error);
38297c478bd9Sstevel@tonic-gate 				ipp->ipp_fields |= IPPF_DSTOPTS;
38307c478bd9Sstevel@tonic-gate 			}
38317c478bd9Sstevel@tonic-gate 			if (sticky) {
38327c478bd9Sstevel@tonic-gate 				error = udp_build_hdrs(q, udp);
38337c478bd9Sstevel@tonic-gate 				if (error != 0)
38347c478bd9Sstevel@tonic-gate 					return (error);
38357c478bd9Sstevel@tonic-gate 			}
38367c478bd9Sstevel@tonic-gate 			break;
38377c478bd9Sstevel@tonic-gate 		}
38387c478bd9Sstevel@tonic-gate 		case IPV6_RTHDR: {
38397c478bd9Sstevel@tonic-gate 			ip6_rthdr_t *rt = (ip6_rthdr_t *)invalp;
38407c478bd9Sstevel@tonic-gate 
38417c478bd9Sstevel@tonic-gate 			/*
38427c478bd9Sstevel@tonic-gate 			 * Sanity checks - minimum size, size a multiple of
38437c478bd9Sstevel@tonic-gate 			 * eight bytes, and matching size passed in.
38447c478bd9Sstevel@tonic-gate 			 */
38457c478bd9Sstevel@tonic-gate 			if (inlen != 0 &&
38467c478bd9Sstevel@tonic-gate 			    inlen != (8 * (rt->ip6r_len + 1)))
38477c478bd9Sstevel@tonic-gate 				return (EINVAL);
38487c478bd9Sstevel@tonic-gate 
38497c478bd9Sstevel@tonic-gate 			if (checkonly)
38507c478bd9Sstevel@tonic-gate 				break;
38517c478bd9Sstevel@tonic-gate 
38527c478bd9Sstevel@tonic-gate 			if (inlen == 0) {
38537c478bd9Sstevel@tonic-gate 				if (sticky &&
38547c478bd9Sstevel@tonic-gate 				    (ipp->ipp_fields & IPPF_RTHDR) != 0) {
38557c478bd9Sstevel@tonic-gate 					kmem_free(ipp->ipp_rthdr,
38567c478bd9Sstevel@tonic-gate 					    ipp->ipp_rthdrlen);
38577c478bd9Sstevel@tonic-gate 					ipp->ipp_rthdr = NULL;
38587c478bd9Sstevel@tonic-gate 					ipp->ipp_rthdrlen = 0;
38597c478bd9Sstevel@tonic-gate 				}
38607c478bd9Sstevel@tonic-gate 				ipp->ipp_fields &= ~IPPF_RTHDR;
38617c478bd9Sstevel@tonic-gate 				ipp->ipp_sticky_ignored |= IPPF_RTHDR;
38627c478bd9Sstevel@tonic-gate 			} else {
38637c478bd9Sstevel@tonic-gate 				error = udp_pkt_set(invalp, inlen, sticky,
38647c478bd9Sstevel@tonic-gate 				    (uchar_t **)&ipp->ipp_rthdr,
38657c478bd9Sstevel@tonic-gate 				    &ipp->ipp_rthdrlen);
38667c478bd9Sstevel@tonic-gate 				if (error != 0)
38677c478bd9Sstevel@tonic-gate 					return (error);
38687c478bd9Sstevel@tonic-gate 				ipp->ipp_fields |= IPPF_RTHDR;
38697c478bd9Sstevel@tonic-gate 			}
38707c478bd9Sstevel@tonic-gate 			if (sticky) {
38717c478bd9Sstevel@tonic-gate 				error = udp_build_hdrs(q, udp);
38727c478bd9Sstevel@tonic-gate 				if (error != 0)
38737c478bd9Sstevel@tonic-gate 					return (error);
38747c478bd9Sstevel@tonic-gate 			}
38757c478bd9Sstevel@tonic-gate 			break;
38767c478bd9Sstevel@tonic-gate 		}
38777c478bd9Sstevel@tonic-gate 
38787c478bd9Sstevel@tonic-gate 		case IPV6_DONTFRAG:
38797c478bd9Sstevel@tonic-gate 			if (checkonly)
38807c478bd9Sstevel@tonic-gate 				break;
38817c478bd9Sstevel@tonic-gate 
38827c478bd9Sstevel@tonic-gate 			if (onoff) {
38837c478bd9Sstevel@tonic-gate 				ipp->ipp_fields |= IPPF_DONTFRAG;
38847c478bd9Sstevel@tonic-gate 			} else {
38857c478bd9Sstevel@tonic-gate 				ipp->ipp_fields &= ~IPPF_DONTFRAG;
38867c478bd9Sstevel@tonic-gate 			}
38877c478bd9Sstevel@tonic-gate 			break;
38887c478bd9Sstevel@tonic-gate 
38897c478bd9Sstevel@tonic-gate 		case IPV6_USE_MIN_MTU:
38907c478bd9Sstevel@tonic-gate 			if (inlen != sizeof (int))
38917c478bd9Sstevel@tonic-gate 				return (EINVAL);
38927c478bd9Sstevel@tonic-gate 
38937c478bd9Sstevel@tonic-gate 			if (*i1 < -1 || *i1 > 1)
38947c478bd9Sstevel@tonic-gate 				return (EINVAL);
38957c478bd9Sstevel@tonic-gate 
38967c478bd9Sstevel@tonic-gate 			if (checkonly)
38977c478bd9Sstevel@tonic-gate 				break;
38987c478bd9Sstevel@tonic-gate 
38997c478bd9Sstevel@tonic-gate 			ipp->ipp_fields |= IPPF_USE_MIN_MTU;
39007c478bd9Sstevel@tonic-gate 			ipp->ipp_use_min_mtu = *i1;
39017c478bd9Sstevel@tonic-gate 			break;
39027c478bd9Sstevel@tonic-gate 
39037c478bd9Sstevel@tonic-gate 		case IPV6_BOUND_PIF:
39047c478bd9Sstevel@tonic-gate 		case IPV6_SEC_OPT:
39057c478bd9Sstevel@tonic-gate 		case IPV6_DONTFAILOVER_IF:
39067c478bd9Sstevel@tonic-gate 		case IPV6_SRC_PREFERENCES:
39077c478bd9Sstevel@tonic-gate 		case IPV6_V6ONLY:
39087c478bd9Sstevel@tonic-gate 			/* Handled at the IP level */
39097c478bd9Sstevel@tonic-gate 			return (-EINVAL);
39107c478bd9Sstevel@tonic-gate 		default:
39117c478bd9Sstevel@tonic-gate 			*outlenp = 0;
39127c478bd9Sstevel@tonic-gate 			return (EINVAL);
39137c478bd9Sstevel@tonic-gate 		}
39147c478bd9Sstevel@tonic-gate 		break;
39157c478bd9Sstevel@tonic-gate 		}		/* end IPPROTO_IPV6 */
39167c478bd9Sstevel@tonic-gate 	case IPPROTO_UDP:
39177c478bd9Sstevel@tonic-gate 		switch (name) {
39187c478bd9Sstevel@tonic-gate 		case UDP_ANONPRIVBIND:
39197c478bd9Sstevel@tonic-gate 			if ((error = secpolicy_net_privaddr(cr, 0)) != 0) {
39207c478bd9Sstevel@tonic-gate 				*outlenp = 0;
39217c478bd9Sstevel@tonic-gate 				return (error);
39227c478bd9Sstevel@tonic-gate 			}
39237c478bd9Sstevel@tonic-gate 			if (!checkonly) {
39247c478bd9Sstevel@tonic-gate 				udp->udp_anon_priv_bind = onoff;
39257c478bd9Sstevel@tonic-gate 			}
39267c478bd9Sstevel@tonic-gate 			break;
39277c478bd9Sstevel@tonic-gate 		case UDP_EXCLBIND:
39287c478bd9Sstevel@tonic-gate 			if (!checkonly)
39297c478bd9Sstevel@tonic-gate 				udp->udp_exclbind = onoff;
39307c478bd9Sstevel@tonic-gate 			break;
39317c478bd9Sstevel@tonic-gate 		case UDP_RCVHDR:
39327c478bd9Sstevel@tonic-gate 			if (!checkonly)
39337c478bd9Sstevel@tonic-gate 				udp->udp_rcvhdr = onoff;
39347c478bd9Sstevel@tonic-gate 			break;
39357c478bd9Sstevel@tonic-gate 		default:
39367c478bd9Sstevel@tonic-gate 			*outlenp = 0;
39377c478bd9Sstevel@tonic-gate 			return (EINVAL);
39387c478bd9Sstevel@tonic-gate 		}
39397c478bd9Sstevel@tonic-gate 		break;
39407c478bd9Sstevel@tonic-gate 	default:
39417c478bd9Sstevel@tonic-gate 		*outlenp = 0;
39427c478bd9Sstevel@tonic-gate 		return (EINVAL);
39437c478bd9Sstevel@tonic-gate 	}
39447c478bd9Sstevel@tonic-gate 	/*
39457c478bd9Sstevel@tonic-gate 	 * Common case of OK return with outval same as inval.
39467c478bd9Sstevel@tonic-gate 	 */
39477c478bd9Sstevel@tonic-gate 	if (invalp != outvalp) {
39487c478bd9Sstevel@tonic-gate 		/* don't trust bcopy for identical src/dst */
39497c478bd9Sstevel@tonic-gate 		(void) bcopy(invalp, outvalp, inlen);
39507c478bd9Sstevel@tonic-gate 	}
39517c478bd9Sstevel@tonic-gate 	*outlenp = inlen;
39527c478bd9Sstevel@tonic-gate 	return (0);
39537c478bd9Sstevel@tonic-gate }
39547c478bd9Sstevel@tonic-gate 
39557c478bd9Sstevel@tonic-gate /*
39567c478bd9Sstevel@tonic-gate  * Update udp_sticky_hdrs based on udp_sticky_ipp, udp_v6src, and udp_ttl.
39577c478bd9Sstevel@tonic-gate  * The headers include ip6i_t (if needed), ip6_t, any sticky extension
39587c478bd9Sstevel@tonic-gate  * headers, and the udp header.
39597c478bd9Sstevel@tonic-gate  * Returns failure if can't allocate memory.
39607c478bd9Sstevel@tonic-gate  */
39617c478bd9Sstevel@tonic-gate static int
39627c478bd9Sstevel@tonic-gate udp_build_hdrs(queue_t *q, udp_t *udp)
39637c478bd9Sstevel@tonic-gate {
39647c478bd9Sstevel@tonic-gate 	uchar_t	*hdrs;
39657c478bd9Sstevel@tonic-gate 	uint_t	hdrs_len;
39667c478bd9Sstevel@tonic-gate 	ip6_t	*ip6h;
39677c478bd9Sstevel@tonic-gate 	ip6i_t	*ip6i;
39687c478bd9Sstevel@tonic-gate 	udpha_t	*udpha;
39697c478bd9Sstevel@tonic-gate 	ip6_pkt_t *ipp = &udp->udp_sticky_ipp;
39707c478bd9Sstevel@tonic-gate 
39717c478bd9Sstevel@tonic-gate 	hdrs_len = ip_total_hdrs_len_v6(ipp) + UDPH_SIZE;
39727c478bd9Sstevel@tonic-gate 	ASSERT(hdrs_len != 0);
39737c478bd9Sstevel@tonic-gate 	if (hdrs_len != udp->udp_sticky_hdrs_len) {
39747c478bd9Sstevel@tonic-gate 		/* Need to reallocate */
39757c478bd9Sstevel@tonic-gate 		hdrs = kmem_alloc(hdrs_len, KM_NOSLEEP);
39767c478bd9Sstevel@tonic-gate 		if (hdrs == NULL)
39777c478bd9Sstevel@tonic-gate 			return (ENOMEM);
39787c478bd9Sstevel@tonic-gate 
39797c478bd9Sstevel@tonic-gate 		if (udp->udp_sticky_hdrs_len != 0) {
39807c478bd9Sstevel@tonic-gate 			kmem_free(udp->udp_sticky_hdrs,
39817c478bd9Sstevel@tonic-gate 			    udp->udp_sticky_hdrs_len);
39827c478bd9Sstevel@tonic-gate 		}
39837c478bd9Sstevel@tonic-gate 		udp->udp_sticky_hdrs = hdrs;
39847c478bd9Sstevel@tonic-gate 		udp->udp_sticky_hdrs_len = hdrs_len;
39857c478bd9Sstevel@tonic-gate 	}
39867c478bd9Sstevel@tonic-gate 	ip_build_hdrs_v6(udp->udp_sticky_hdrs,
39877c478bd9Sstevel@tonic-gate 	    udp->udp_sticky_hdrs_len - UDPH_SIZE, ipp, IPPROTO_UDP);
39887c478bd9Sstevel@tonic-gate 
39897c478bd9Sstevel@tonic-gate 	/* Set header fields not in ipp */
39907c478bd9Sstevel@tonic-gate 	if (ipp->ipp_fields & IPPF_HAS_IP6I) {
39917c478bd9Sstevel@tonic-gate 		ip6i = (ip6i_t *)udp->udp_sticky_hdrs;
39927c478bd9Sstevel@tonic-gate 		ip6h = (ip6_t *)&ip6i[1];
39937c478bd9Sstevel@tonic-gate 	} else {
39947c478bd9Sstevel@tonic-gate 		ip6h = (ip6_t *)udp->udp_sticky_hdrs;
39957c478bd9Sstevel@tonic-gate 	}
39967c478bd9Sstevel@tonic-gate 
39977c478bd9Sstevel@tonic-gate 	if (!(ipp->ipp_fields & IPPF_ADDR))
39987c478bd9Sstevel@tonic-gate 		ip6h->ip6_src = udp->udp_v6src;
39997c478bd9Sstevel@tonic-gate 
40007c478bd9Sstevel@tonic-gate 	udpha = (udpha_t *)(udp->udp_sticky_hdrs + hdrs_len - UDPH_SIZE);
40017c478bd9Sstevel@tonic-gate 	udpha->uha_src_port = udp->udp_port;
40027c478bd9Sstevel@tonic-gate 
40037c478bd9Sstevel@tonic-gate 	/* Try to get everything in a single mblk */
40047c478bd9Sstevel@tonic-gate 	if (hdrs_len > udp->udp_max_hdr_len) {
40057c478bd9Sstevel@tonic-gate 		udp->udp_max_hdr_len = hdrs_len;
40067c478bd9Sstevel@tonic-gate 		(void) mi_set_sth_wroff(RD(q), udp->udp_max_hdr_len +
40077c478bd9Sstevel@tonic-gate 		    udp_wroff_extra);
40087c478bd9Sstevel@tonic-gate 	}
40097c478bd9Sstevel@tonic-gate 	return (0);
40107c478bd9Sstevel@tonic-gate }
40117c478bd9Sstevel@tonic-gate 
40127c478bd9Sstevel@tonic-gate /*
40137c478bd9Sstevel@tonic-gate  * Set optbuf and optlen for the option.
40147c478bd9Sstevel@tonic-gate  * If sticky is set allocate memory (if not already present).
40157c478bd9Sstevel@tonic-gate  * Otherwise just point optbuf and optlen at invalp and inlen.
40167c478bd9Sstevel@tonic-gate  * Returns failure if memory can not be allocated.
40177c478bd9Sstevel@tonic-gate  */
40187c478bd9Sstevel@tonic-gate static int
40197c478bd9Sstevel@tonic-gate udp_pkt_set(uchar_t *invalp, uint_t inlen, boolean_t sticky,
40207c478bd9Sstevel@tonic-gate     uchar_t **optbufp, uint_t *optlenp)
40217c478bd9Sstevel@tonic-gate {
40227c478bd9Sstevel@tonic-gate 	uchar_t *optbuf;
40237c478bd9Sstevel@tonic-gate 
40247c478bd9Sstevel@tonic-gate 	if (!sticky) {
40257c478bd9Sstevel@tonic-gate 		*optbufp = invalp;
40267c478bd9Sstevel@tonic-gate 		*optlenp = inlen;
40277c478bd9Sstevel@tonic-gate 		return (0);
40287c478bd9Sstevel@tonic-gate 	}
40297c478bd9Sstevel@tonic-gate 	if (inlen == *optlenp) {
40307c478bd9Sstevel@tonic-gate 		/* Unchanged length - no need to realocate */
40317c478bd9Sstevel@tonic-gate 		bcopy(invalp, *optbufp, inlen);
40327c478bd9Sstevel@tonic-gate 		return (0);
40337c478bd9Sstevel@tonic-gate 	}
40347c478bd9Sstevel@tonic-gate 	if (inlen != 0) {
40357c478bd9Sstevel@tonic-gate 		/* Allocate new buffer before free */
40367c478bd9Sstevel@tonic-gate 		optbuf = kmem_alloc(inlen, KM_NOSLEEP);
40377c478bd9Sstevel@tonic-gate 		if (optbuf == NULL)
40387c478bd9Sstevel@tonic-gate 			return (ENOMEM);
40397c478bd9Sstevel@tonic-gate 	} else {
40407c478bd9Sstevel@tonic-gate 		optbuf = NULL;
40417c478bd9Sstevel@tonic-gate 	}
40427c478bd9Sstevel@tonic-gate 	/* Free old buffer */
40437c478bd9Sstevel@tonic-gate 	if (*optlenp != 0)
40447c478bd9Sstevel@tonic-gate 		kmem_free(*optbufp, *optlenp);
40457c478bd9Sstevel@tonic-gate 
40467c478bd9Sstevel@tonic-gate 	bcopy(invalp, optbuf, inlen);
40477c478bd9Sstevel@tonic-gate 	*optbufp = optbuf;
40487c478bd9Sstevel@tonic-gate 	*optlenp = inlen;
40497c478bd9Sstevel@tonic-gate 	return (0);
40507c478bd9Sstevel@tonic-gate }
40517c478bd9Sstevel@tonic-gate 
40527c478bd9Sstevel@tonic-gate /*
40537c478bd9Sstevel@tonic-gate  * This routine retrieves the value of an ND variable in a udpparam_t
40547c478bd9Sstevel@tonic-gate  * structure.  It is called through nd_getset when a user reads the
40557c478bd9Sstevel@tonic-gate  * variable.
40567c478bd9Sstevel@tonic-gate  */
40577c478bd9Sstevel@tonic-gate /* ARGSUSED */
40587c478bd9Sstevel@tonic-gate static int
40597c478bd9Sstevel@tonic-gate udp_param_get(queue_t *q, mblk_t *mp, caddr_t cp, cred_t *cr)
40607c478bd9Sstevel@tonic-gate {
40617c478bd9Sstevel@tonic-gate 	udpparam_t *udppa = (udpparam_t *)cp;
40627c478bd9Sstevel@tonic-gate 
40637c478bd9Sstevel@tonic-gate 	(void) mi_mpprintf(mp, "%d", udppa->udp_param_value);
40647c478bd9Sstevel@tonic-gate 	return (0);
40657c478bd9Sstevel@tonic-gate }
40667c478bd9Sstevel@tonic-gate 
40677c478bd9Sstevel@tonic-gate /*
40687c478bd9Sstevel@tonic-gate  * Walk through the param array specified registering each element with the
40697c478bd9Sstevel@tonic-gate  * named dispatch (ND) handler.
40707c478bd9Sstevel@tonic-gate  */
40717c478bd9Sstevel@tonic-gate static boolean_t
40727c478bd9Sstevel@tonic-gate udp_param_register(udpparam_t *udppa, int cnt)
40737c478bd9Sstevel@tonic-gate {
40747c478bd9Sstevel@tonic-gate 	for (; cnt-- > 0; udppa++) {
40757c478bd9Sstevel@tonic-gate 		if (udppa->udp_param_name && udppa->udp_param_name[0]) {
40767c478bd9Sstevel@tonic-gate 			if (!nd_load(&udp_g_nd, udppa->udp_param_name,
40777c478bd9Sstevel@tonic-gate 			    udp_param_get, udp_param_set,
40787c478bd9Sstevel@tonic-gate 			    (caddr_t)udppa)) {
40797c478bd9Sstevel@tonic-gate 				nd_free(&udp_g_nd);
40807c478bd9Sstevel@tonic-gate 				return (B_FALSE);
40817c478bd9Sstevel@tonic-gate 			}
40827c478bd9Sstevel@tonic-gate 		}
40837c478bd9Sstevel@tonic-gate 	}
40847c478bd9Sstevel@tonic-gate 	if (!nd_load(&udp_g_nd, "udp_extra_priv_ports",
40857c478bd9Sstevel@tonic-gate 	    udp_extra_priv_ports_get, NULL, NULL)) {
40867c478bd9Sstevel@tonic-gate 		nd_free(&udp_g_nd);
40877c478bd9Sstevel@tonic-gate 		return (B_FALSE);
40887c478bd9Sstevel@tonic-gate 	}
40897c478bd9Sstevel@tonic-gate 	if (!nd_load(&udp_g_nd, "udp_extra_priv_ports_add",
40907c478bd9Sstevel@tonic-gate 	    NULL, udp_extra_priv_ports_add, NULL)) {
40917c478bd9Sstevel@tonic-gate 		nd_free(&udp_g_nd);
40927c478bd9Sstevel@tonic-gate 		return (B_FALSE);
40937c478bd9Sstevel@tonic-gate 	}
40947c478bd9Sstevel@tonic-gate 	if (!nd_load(&udp_g_nd, "udp_extra_priv_ports_del",
40957c478bd9Sstevel@tonic-gate 	    NULL, udp_extra_priv_ports_del, NULL)) {
40967c478bd9Sstevel@tonic-gate 		nd_free(&udp_g_nd);
40977c478bd9Sstevel@tonic-gate 		return (B_FALSE);
40987c478bd9Sstevel@tonic-gate 	}
40997c478bd9Sstevel@tonic-gate 	if (!nd_load(&udp_g_nd, "udp_status", udp_status_report, NULL,
41007c478bd9Sstevel@tonic-gate 	    NULL)) {
41017c478bd9Sstevel@tonic-gate 		nd_free(&udp_g_nd);
41027c478bd9Sstevel@tonic-gate 		return (B_FALSE);
41037c478bd9Sstevel@tonic-gate 	}
41047c478bd9Sstevel@tonic-gate 	if (!nd_load(&udp_g_nd, "udp_bind_hash", udp_bind_hash_report, NULL,
41057c478bd9Sstevel@tonic-gate 	    NULL)) {
41067c478bd9Sstevel@tonic-gate 		nd_free(&udp_g_nd);
41077c478bd9Sstevel@tonic-gate 		return (B_FALSE);
41087c478bd9Sstevel@tonic-gate 	}
41097c478bd9Sstevel@tonic-gate 	return (B_TRUE);
41107c478bd9Sstevel@tonic-gate }
41117c478bd9Sstevel@tonic-gate 
41127c478bd9Sstevel@tonic-gate /* This routine sets an ND variable in a udpparam_t structure. */
41137c478bd9Sstevel@tonic-gate /* ARGSUSED */
41147c478bd9Sstevel@tonic-gate static int
41157c478bd9Sstevel@tonic-gate udp_param_set(queue_t *q, mblk_t *mp, char *value, caddr_t cp, cred_t *cr)
41167c478bd9Sstevel@tonic-gate {
41177c478bd9Sstevel@tonic-gate 	long		new_value;
41187c478bd9Sstevel@tonic-gate 	udpparam_t	*udppa = (udpparam_t *)cp;
41197c478bd9Sstevel@tonic-gate 
41207c478bd9Sstevel@tonic-gate 	/*
41217c478bd9Sstevel@tonic-gate 	 * Fail the request if the new value does not lie within the
41227c478bd9Sstevel@tonic-gate 	 * required bounds.
41237c478bd9Sstevel@tonic-gate 	 */
41247c478bd9Sstevel@tonic-gate 	if (ddi_strtol(value, NULL, 10, &new_value) != 0 ||
41257c478bd9Sstevel@tonic-gate 	    new_value < udppa->udp_param_min ||
41267c478bd9Sstevel@tonic-gate 	    new_value > udppa->udp_param_max) {
41277c478bd9Sstevel@tonic-gate 		return (EINVAL);
41287c478bd9Sstevel@tonic-gate 	}
41297c478bd9Sstevel@tonic-gate 
41307c478bd9Sstevel@tonic-gate 	/* Set the new value */
41317c478bd9Sstevel@tonic-gate 	udppa->udp_param_value = new_value;
41327c478bd9Sstevel@tonic-gate 	return (0);
41337c478bd9Sstevel@tonic-gate }
41347c478bd9Sstevel@tonic-gate 
41357c478bd9Sstevel@tonic-gate static void
4136ff550d0eSmasputra udp_input(conn_t *connp, mblk_t *mp)
41377c478bd9Sstevel@tonic-gate {
41387c478bd9Sstevel@tonic-gate 	struct T_unitdata_ind	*tudi;
4139ff550d0eSmasputra 	uchar_t			*rptr;		/* Pointer to IP header */
4140ff550d0eSmasputra 	int			hdr_length;	/* Length of IP+UDP headers */
41417c478bd9Sstevel@tonic-gate 	int			udi_size;	/* Size of T_unitdata_ind */
4142ff550d0eSmasputra 	int			mp_len;
41437c478bd9Sstevel@tonic-gate 	udp_t			*udp;
41447c478bd9Sstevel@tonic-gate 	udpha_t			*udpha;
41457c478bd9Sstevel@tonic-gate 	int			ipversion;
41467c478bd9Sstevel@tonic-gate 	ip6_pkt_t		ipp;
41477c478bd9Sstevel@tonic-gate 	ip6_t			*ip6h;
41487c478bd9Sstevel@tonic-gate 	ip6i_t			*ip6i;
41497c478bd9Sstevel@tonic-gate 	mblk_t			*mp1;
41507c478bd9Sstevel@tonic-gate 	mblk_t			*options_mp = NULL;
41517c478bd9Sstevel@tonic-gate 	in_pktinfo_t		*pinfo = NULL;
41527c478bd9Sstevel@tonic-gate 	cred_t			*cr = NULL;
4153ff550d0eSmasputra 	queue_t			*q = connp->conn_rq;
41547c478bd9Sstevel@tonic-gate 	pid_t			cpid;
41557c478bd9Sstevel@tonic-gate 
41567c478bd9Sstevel@tonic-gate 	TRACE_2(TR_FAC_UDP, TR_UDP_RPUT_START,
41577c478bd9Sstevel@tonic-gate 	    "udp_rput_start: q %p mp %p", q, mp);
41587c478bd9Sstevel@tonic-gate 
4159ff550d0eSmasputra 	udp = connp->conn_udp;
41607c478bd9Sstevel@tonic-gate 	rptr = mp->b_rptr;
4161ff550d0eSmasputra 	ASSERT(DB_TYPE(mp) == M_DATA || DB_TYPE(mp) == M_CTL);
4162ff550d0eSmasputra 	ASSERT(OK_32PTR(rptr));
41637c478bd9Sstevel@tonic-gate 
4164ff550d0eSmasputra 	/*
4165ff550d0eSmasputra 	 * IP should have prepended the options data in an M_CTL
4166ff550d0eSmasputra 	 * Check M_CTL "type" to make sure are not here bcos of
4167ff550d0eSmasputra 	 * a valid ICMP message
4168ff550d0eSmasputra 	 */
4169ff550d0eSmasputra 	if (DB_TYPE(mp) == M_CTL) {
4170ff550d0eSmasputra 		if (MBLKL(mp) == sizeof (in_pktinfo_t) &&
4171ff550d0eSmasputra 		    ((in_pktinfo_t *)mp->b_rptr)->in_pkt_ulp_type ==
4172ff550d0eSmasputra 		    IN_PKTINFO) {
41737c478bd9Sstevel@tonic-gate 			/*
4174ff550d0eSmasputra 			 * IP_RECVIF or IP_RECVSLLA information has been
4175ff550d0eSmasputra 			 * appended to the packet by IP. We need to
4176ff550d0eSmasputra 			 * extract the mblk and adjust the rptr
41777c478bd9Sstevel@tonic-gate 			 */
4178ff550d0eSmasputra 			pinfo = (in_pktinfo_t *)mp->b_rptr;
4179ff550d0eSmasputra 			options_mp = mp;
4180ff550d0eSmasputra 			mp = mp->b_cont;
4181ff550d0eSmasputra 			rptr = mp->b_rptr;
4182ff550d0eSmasputra 			UDP_STAT(udp_in_pktinfo);
4183ff550d0eSmasputra 		} else {
4184ff550d0eSmasputra 			/*
4185ff550d0eSmasputra 			 * ICMP messages.
4186ff550d0eSmasputra 			 */
4187ff550d0eSmasputra 			udp_icmp_error(q, mp);
4188ff550d0eSmasputra 			TRACE_2(TR_FAC_UDP, TR_UDP_RPUT_END,
4189ff550d0eSmasputra 				"udp_rput_end: q %p (%S)", q, "m_ctl");
4190ff550d0eSmasputra 			return;
41917c478bd9Sstevel@tonic-gate 		}
41927c478bd9Sstevel@tonic-gate 	}
41937c478bd9Sstevel@tonic-gate 
4194ff550d0eSmasputra 	mp_len = msgdsize(mp);
41957c478bd9Sstevel@tonic-gate 	/*
41967c478bd9Sstevel@tonic-gate 	 * This is the inbound data path.
41977c478bd9Sstevel@tonic-gate 	 * First, we check to make sure the IP version number is correct,
41987c478bd9Sstevel@tonic-gate 	 * and then pull the IP and UDP headers into the first mblk.
41997c478bd9Sstevel@tonic-gate 	 * Assume IP provides aligned packets - otherwise toss.
42007c478bd9Sstevel@tonic-gate 	 * Also, check if we have a complete IP header.
42017c478bd9Sstevel@tonic-gate 	 */
42027c478bd9Sstevel@tonic-gate 
42037c478bd9Sstevel@tonic-gate 	/* Initialize regardless if ipversion is IPv4 or IPv6 */
42047c478bd9Sstevel@tonic-gate 	ipp.ipp_fields = 0;
42057c478bd9Sstevel@tonic-gate 
42067c478bd9Sstevel@tonic-gate 	ipversion = IPH_HDR_VERSION(rptr);
42077c478bd9Sstevel@tonic-gate 	switch (ipversion) {
42087c478bd9Sstevel@tonic-gate 	case IPV4_VERSION:
4209ff550d0eSmasputra 		ASSERT(MBLKL(mp) >= sizeof (ipha_t));
4210ff550d0eSmasputra 		ASSERT(((ipha_t *)rptr)->ipha_protocol == IPPROTO_UDP);
42117c478bd9Sstevel@tonic-gate 		hdr_length = IPH_HDR_LENGTH(rptr) + UDPH_SIZE;
42127c478bd9Sstevel@tonic-gate 		if ((hdr_length > IP_SIMPLE_HDR_LENGTH + UDPH_SIZE) ||
42137c478bd9Sstevel@tonic-gate 		    (udp->udp_ip_rcv_options_len)) {
42147c478bd9Sstevel@tonic-gate 			/*
42157c478bd9Sstevel@tonic-gate 			 * Handle IPv4 packets with options outside of the
42167c478bd9Sstevel@tonic-gate 			 * main data path. Not needed for AF_INET6 sockets
42177c478bd9Sstevel@tonic-gate 			 * since they don't support a getsockopt of IP_OPTIONS.
42187c478bd9Sstevel@tonic-gate 			 */
42197c478bd9Sstevel@tonic-gate 			if (udp->udp_family == AF_INET6)
42207c478bd9Sstevel@tonic-gate 				break;
42217c478bd9Sstevel@tonic-gate 			/*
42227c478bd9Sstevel@tonic-gate 			 * UDP length check performed for IPv4 packets with
42237c478bd9Sstevel@tonic-gate 			 * options to check whether UDP length specified in
42247c478bd9Sstevel@tonic-gate 			 * the header is the same as the physical length of
42257c478bd9Sstevel@tonic-gate 			 * the packet.
42267c478bd9Sstevel@tonic-gate 			 */
42277c478bd9Sstevel@tonic-gate 			udpha = (udpha_t *)(rptr + (hdr_length - UDPH_SIZE));
4228ff550d0eSmasputra 			if (mp_len != (ntohs(udpha->uha_length) +
42297c478bd9Sstevel@tonic-gate 			    hdr_length - UDPH_SIZE)) {
42307c478bd9Sstevel@tonic-gate 				goto tossit;
42317c478bd9Sstevel@tonic-gate 			}
42327c478bd9Sstevel@tonic-gate 			/*
42337c478bd9Sstevel@tonic-gate 			 * Handle the case where the packet has IP options
42347c478bd9Sstevel@tonic-gate 			 * and the IP_RECVSLLA & IP_RECVIF are set
42357c478bd9Sstevel@tonic-gate 			 */
42367c478bd9Sstevel@tonic-gate 			if (pinfo != NULL)
42377c478bd9Sstevel@tonic-gate 				mp = options_mp;
4238ff550d0eSmasputra 			udp_become_writer(connp, mp, udp_rput_other_wrapper,
4239ff550d0eSmasputra 			    SQTAG_UDP_INPUT);
42407c478bd9Sstevel@tonic-gate 			TRACE_2(TR_FAC_UDP, TR_UDP_RPUT_END,
42417c478bd9Sstevel@tonic-gate 				"udp_rput_end: q %p (%S)", q, "end");
42427c478bd9Sstevel@tonic-gate 			return;
42437c478bd9Sstevel@tonic-gate 		}
42447c478bd9Sstevel@tonic-gate 
42457c478bd9Sstevel@tonic-gate 		/* Handle IPV6_RECVHOPLIMIT. */
4246ff550d0eSmasputra 		if ((udp->udp_family == AF_INET6) && (pinfo != NULL) &&
4247ff550d0eSmasputra 		    udp->udp_ipv6_recvpktinfo) {
42487c478bd9Sstevel@tonic-gate 			if (pinfo->in_pkt_flags & IPF_RECVIF) {
42497c478bd9Sstevel@tonic-gate 				ipp.ipp_fields |= IPPF_IFINDEX;
42507c478bd9Sstevel@tonic-gate 				ipp.ipp_ifindex = pinfo->in_pkt_ifindex;
42517c478bd9Sstevel@tonic-gate 			}
42527c478bd9Sstevel@tonic-gate 		}
42537c478bd9Sstevel@tonic-gate 		break;
42547c478bd9Sstevel@tonic-gate 	case IPV6_VERSION:
42557c478bd9Sstevel@tonic-gate 		/*
42567c478bd9Sstevel@tonic-gate 		 * IPv6 packets can only be received by applications
42577c478bd9Sstevel@tonic-gate 		 * that are prepared to receive IPv6 addresses.
42587c478bd9Sstevel@tonic-gate 		 * The IP fanout must ensure this.
42597c478bd9Sstevel@tonic-gate 		 */
42607c478bd9Sstevel@tonic-gate 		ASSERT(udp->udp_family == AF_INET6);
42617c478bd9Sstevel@tonic-gate 
42627c478bd9Sstevel@tonic-gate 		ip6h = (ip6_t *)rptr;
4263ff550d0eSmasputra 		ASSERT((uchar_t *)&ip6h[1] <= mp->b_wptr);
42647c478bd9Sstevel@tonic-gate 
42657c478bd9Sstevel@tonic-gate 		if (ip6h->ip6_nxt != IPPROTO_UDP) {
42667c478bd9Sstevel@tonic-gate 			uint8_t nexthdrp;
42677c478bd9Sstevel@tonic-gate 			/* Look for ifindex information */
42687c478bd9Sstevel@tonic-gate 			if (ip6h->ip6_nxt == IPPROTO_RAW) {
42697c478bd9Sstevel@tonic-gate 				ip6i = (ip6i_t *)ip6h;
42707c478bd9Sstevel@tonic-gate 				if ((uchar_t *)&ip6i[1] > mp->b_wptr)
42717c478bd9Sstevel@tonic-gate 					goto tossit;
42727c478bd9Sstevel@tonic-gate 
42737c478bd9Sstevel@tonic-gate 				if (ip6i->ip6i_flags & IP6I_IFINDEX) {
42747c478bd9Sstevel@tonic-gate 					ASSERT(ip6i->ip6i_ifindex != 0);
42757c478bd9Sstevel@tonic-gate 					ipp.ipp_fields |= IPPF_IFINDEX;
42767c478bd9Sstevel@tonic-gate 					ipp.ipp_ifindex = ip6i->ip6i_ifindex;
42777c478bd9Sstevel@tonic-gate 				}
42787c478bd9Sstevel@tonic-gate 				rptr = (uchar_t *)&ip6i[1];
42797c478bd9Sstevel@tonic-gate 				mp->b_rptr = rptr;
42807c478bd9Sstevel@tonic-gate 				if (rptr == mp->b_wptr) {
42817c478bd9Sstevel@tonic-gate 					mp1 = mp->b_cont;
42827c478bd9Sstevel@tonic-gate 					freeb(mp);
42837c478bd9Sstevel@tonic-gate 					mp = mp1;
42847c478bd9Sstevel@tonic-gate 					rptr = mp->b_rptr;
42857c478bd9Sstevel@tonic-gate 				}
42867c478bd9Sstevel@tonic-gate 				if (MBLKL(mp) < (IPV6_HDR_LEN + UDPH_SIZE))
42877c478bd9Sstevel@tonic-gate 					goto tossit;
42887c478bd9Sstevel@tonic-gate 				ip6h = (ip6_t *)rptr;
4289ff550d0eSmasputra 				mp_len = msgdsize(mp);
42907c478bd9Sstevel@tonic-gate 			}
42917c478bd9Sstevel@tonic-gate 			/*
42927c478bd9Sstevel@tonic-gate 			 * Find any potentially interesting extension headers
42937c478bd9Sstevel@tonic-gate 			 * as well as the length of the IPv6 + extension
42947c478bd9Sstevel@tonic-gate 			 * headers.
42957c478bd9Sstevel@tonic-gate 			 */
42967c478bd9Sstevel@tonic-gate 			hdr_length = ip_find_hdr_v6(mp, ip6h, &ipp, &nexthdrp) +
42977c478bd9Sstevel@tonic-gate 			    UDPH_SIZE;
4298ff550d0eSmasputra 			ASSERT(nexthdrp == IPPROTO_UDP);
42997c478bd9Sstevel@tonic-gate 		} else {
43007c478bd9Sstevel@tonic-gate 			hdr_length = IPV6_HDR_LEN + UDPH_SIZE;
43017c478bd9Sstevel@tonic-gate 			ip6i = NULL;
43027c478bd9Sstevel@tonic-gate 		}
43037c478bd9Sstevel@tonic-gate 		break;
43047c478bd9Sstevel@tonic-gate 	default:
4305ff550d0eSmasputra 		ASSERT(0);
43067c478bd9Sstevel@tonic-gate 	}
43077c478bd9Sstevel@tonic-gate 
43087c478bd9Sstevel@tonic-gate 	/*
43097c478bd9Sstevel@tonic-gate 	 * IP inspected the UDP header thus all of it must be in the mblk.
43107c478bd9Sstevel@tonic-gate 	 * UDP length check is performed for IPv6 packets and IPv4 packets
43117c478bd9Sstevel@tonic-gate 	 * without options to check if the size of the packet as specified
43127c478bd9Sstevel@tonic-gate 	 * by the header is the same as the physical size of the packet.
43137c478bd9Sstevel@tonic-gate 	 */
43147c478bd9Sstevel@tonic-gate 	udpha = (udpha_t *)(rptr + (hdr_length - UDPH_SIZE));
43157c478bd9Sstevel@tonic-gate 	if ((MBLKL(mp) < hdr_length) ||
4316ff550d0eSmasputra 	    (mp_len != (ntohs(udpha->uha_length) + hdr_length - UDPH_SIZE))) {
43177c478bd9Sstevel@tonic-gate 		goto tossit;
43187c478bd9Sstevel@tonic-gate 	}
43197c478bd9Sstevel@tonic-gate 
43207c478bd9Sstevel@tonic-gate 	/* Walk past the headers. */
4321ff550d0eSmasputra 	if (!udp->udp_rcvhdr) {
43227c478bd9Sstevel@tonic-gate 		mp->b_rptr = rptr + hdr_length;
4323ff550d0eSmasputra 		mp_len -= hdr_length;
4324ff550d0eSmasputra 	}
43257c478bd9Sstevel@tonic-gate 
43267c478bd9Sstevel@tonic-gate 	/*
43277c478bd9Sstevel@tonic-gate 	 * This is the inbound data path.  Packets are passed upstream as
43287c478bd9Sstevel@tonic-gate 	 * T_UNITDATA_IND messages with full IP headers still attached.
43297c478bd9Sstevel@tonic-gate 	 */
43307c478bd9Sstevel@tonic-gate 	if (udp->udp_family == AF_INET) {
43317c478bd9Sstevel@tonic-gate 		sin_t *sin;
43327c478bd9Sstevel@tonic-gate 
43337c478bd9Sstevel@tonic-gate 		ASSERT(IPH_HDR_VERSION((ipha_t *)rptr) == IPV4_VERSION);
43347c478bd9Sstevel@tonic-gate 
43357c478bd9Sstevel@tonic-gate 		/*
43367c478bd9Sstevel@tonic-gate 		 * Normally only send up the address.
43377c478bd9Sstevel@tonic-gate 		 * If IP_RECVDSTADDR is set we include the destination IP
43387c478bd9Sstevel@tonic-gate 		 * address as an option. With IP_RECVOPTS we include all
43397c478bd9Sstevel@tonic-gate 		 * the IP options. Only ip_rput_other() handles packets
43407c478bd9Sstevel@tonic-gate 		 * that contain IP options.
43417c478bd9Sstevel@tonic-gate 		 */
43427c478bd9Sstevel@tonic-gate 		udi_size = sizeof (struct T_unitdata_ind) + sizeof (sin_t);
43437c478bd9Sstevel@tonic-gate 		if (udp->udp_recvdstaddr) {
43447c478bd9Sstevel@tonic-gate 			udi_size += sizeof (struct T_opthdr) +
43457c478bd9Sstevel@tonic-gate 			    sizeof (struct in_addr);
4346ff550d0eSmasputra 			UDP_STAT(udp_in_recvdstaddr);
43477c478bd9Sstevel@tonic-gate 		}
43487c478bd9Sstevel@tonic-gate 
43497c478bd9Sstevel@tonic-gate 		/*
43507c478bd9Sstevel@tonic-gate 		 * If the IP_RECVSLLA or the IP_RECVIF is set then allocate
43517c478bd9Sstevel@tonic-gate 		 * space accordingly
43527c478bd9Sstevel@tonic-gate 		 */
43537c478bd9Sstevel@tonic-gate 		if (udp->udp_recvif && (pinfo != NULL) &&
43547c478bd9Sstevel@tonic-gate 		    (pinfo->in_pkt_flags & IPF_RECVIF)) {
4355ff550d0eSmasputra 			udi_size += sizeof (struct T_opthdr) + sizeof (uint_t);
4356ff550d0eSmasputra 			UDP_STAT(udp_in_recvif);
43577c478bd9Sstevel@tonic-gate 		}
43587c478bd9Sstevel@tonic-gate 
43597c478bd9Sstevel@tonic-gate 		if (udp->udp_recvslla && (pinfo != NULL) &&
43607c478bd9Sstevel@tonic-gate 		    (pinfo->in_pkt_flags & IPF_RECVSLLA)) {
43617c478bd9Sstevel@tonic-gate 			udi_size += sizeof (struct T_opthdr) +
4362ff550d0eSmasputra 			    sizeof (struct sockaddr_dl);
4363ff550d0eSmasputra 			UDP_STAT(udp_in_recvslla);
43647c478bd9Sstevel@tonic-gate 		}
43657c478bd9Sstevel@tonic-gate 
43667c478bd9Sstevel@tonic-gate 		if (udp->udp_recvucred && (cr = DB_CRED(mp)) != NULL) {
43677c478bd9Sstevel@tonic-gate 			udi_size += sizeof (struct T_opthdr) + ucredsize;
43687c478bd9Sstevel@tonic-gate 			cpid = DB_CPID(mp);
4369ff550d0eSmasputra 			UDP_STAT(udp_in_recvucred);
43707c478bd9Sstevel@tonic-gate 		}
43717c478bd9Sstevel@tonic-gate 		/*
43727c478bd9Sstevel@tonic-gate 		 * If IP_RECVTTL is set allocate the appropriate sized buffer
43737c478bd9Sstevel@tonic-gate 		 */
43747c478bd9Sstevel@tonic-gate 		if (udp->udp_recvttl) {
43757c478bd9Sstevel@tonic-gate 			udi_size += sizeof (struct T_opthdr) + sizeof (uint8_t);
4376ff550d0eSmasputra 			UDP_STAT(udp_in_recvttl);
43777c478bd9Sstevel@tonic-gate 		}
43787c478bd9Sstevel@tonic-gate 
43797c478bd9Sstevel@tonic-gate 		ASSERT(IPH_HDR_LENGTH((ipha_t *)rptr) == IP_SIMPLE_HDR_LENGTH);
43807c478bd9Sstevel@tonic-gate 
43817c478bd9Sstevel@tonic-gate 		/* Allocate a message block for the T_UNITDATA_IND structure. */
43827c478bd9Sstevel@tonic-gate 		mp1 = allocb(udi_size, BPRI_MED);
43837c478bd9Sstevel@tonic-gate 		if (mp1 == NULL) {
43847c478bd9Sstevel@tonic-gate 			freemsg(mp);
43857c478bd9Sstevel@tonic-gate 			if (options_mp != NULL)
43867c478bd9Sstevel@tonic-gate 				freeb(options_mp);
43877c478bd9Sstevel@tonic-gate 			TRACE_2(TR_FAC_UDP, TR_UDP_RPUT_END,
43887c478bd9Sstevel@tonic-gate 				"udp_rput_end: q %p (%S)", q, "allocbfail");
43897c478bd9Sstevel@tonic-gate 			BUMP_MIB(&udp_mib, udpInErrors);
43907c478bd9Sstevel@tonic-gate 			return;
43917c478bd9Sstevel@tonic-gate 		}
43927c478bd9Sstevel@tonic-gate 		mp1->b_cont = mp;
43937c478bd9Sstevel@tonic-gate 		mp = mp1;
43947c478bd9Sstevel@tonic-gate 		mp->b_datap->db_type = M_PROTO;
43957c478bd9Sstevel@tonic-gate 		tudi = (struct T_unitdata_ind *)mp->b_rptr;
43967c478bd9Sstevel@tonic-gate 		mp->b_wptr = (uchar_t *)tudi + udi_size;
43977c478bd9Sstevel@tonic-gate 		tudi->PRIM_type = T_UNITDATA_IND;
43987c478bd9Sstevel@tonic-gate 		tudi->SRC_length = sizeof (sin_t);
43997c478bd9Sstevel@tonic-gate 		tudi->SRC_offset = sizeof (struct T_unitdata_ind);
44007c478bd9Sstevel@tonic-gate 		tudi->OPT_offset = sizeof (struct T_unitdata_ind) +
44017c478bd9Sstevel@tonic-gate 		    sizeof (sin_t);
44027c478bd9Sstevel@tonic-gate 		udi_size -= (sizeof (struct T_unitdata_ind) + sizeof (sin_t));
44037c478bd9Sstevel@tonic-gate 		tudi->OPT_length = udi_size;
44047c478bd9Sstevel@tonic-gate 		sin = (sin_t *)&tudi[1];
44057c478bd9Sstevel@tonic-gate 		sin->sin_addr.s_addr = ((ipha_t *)rptr)->ipha_src;
44067c478bd9Sstevel@tonic-gate 		sin->sin_port =	udpha->uha_src_port;
44077c478bd9Sstevel@tonic-gate 		sin->sin_family = udp->udp_family;
44087c478bd9Sstevel@tonic-gate 		*(uint32_t *)&sin->sin_zero[0] = 0;
44097c478bd9Sstevel@tonic-gate 		*(uint32_t *)&sin->sin_zero[4] = 0;
44107c478bd9Sstevel@tonic-gate 
44117c478bd9Sstevel@tonic-gate 		/*
44127c478bd9Sstevel@tonic-gate 		 * Add options if IP_RECVDSTADDR, IP_RECVIF, IP_RECVSLLA or
44137c478bd9Sstevel@tonic-gate 		 * IP_RECVTTL has been set.
44147c478bd9Sstevel@tonic-gate 		 */
44157c478bd9Sstevel@tonic-gate 		if (udi_size != 0) {
44167c478bd9Sstevel@tonic-gate 			/*
44177c478bd9Sstevel@tonic-gate 			 * Copy in destination address before options to avoid
44187c478bd9Sstevel@tonic-gate 			 * any padding issues.
44197c478bd9Sstevel@tonic-gate 			 */
44207c478bd9Sstevel@tonic-gate 			char *dstopt;
44217c478bd9Sstevel@tonic-gate 
44227c478bd9Sstevel@tonic-gate 			dstopt = (char *)&sin[1];
44237c478bd9Sstevel@tonic-gate 			if (udp->udp_recvdstaddr) {
44247c478bd9Sstevel@tonic-gate 				struct T_opthdr *toh;
44257c478bd9Sstevel@tonic-gate 				ipaddr_t *dstptr;
44267c478bd9Sstevel@tonic-gate 
44277c478bd9Sstevel@tonic-gate 				toh = (struct T_opthdr *)dstopt;
44287c478bd9Sstevel@tonic-gate 				toh->level = IPPROTO_IP;
44297c478bd9Sstevel@tonic-gate 				toh->name = IP_RECVDSTADDR;
44307c478bd9Sstevel@tonic-gate 				toh->len = sizeof (struct T_opthdr) +
44317c478bd9Sstevel@tonic-gate 				    sizeof (ipaddr_t);
44327c478bd9Sstevel@tonic-gate 				toh->status = 0;
44337c478bd9Sstevel@tonic-gate 				dstopt += sizeof (struct T_opthdr);
44347c478bd9Sstevel@tonic-gate 				dstptr = (ipaddr_t *)dstopt;
44357c478bd9Sstevel@tonic-gate 				*dstptr = ((ipha_t *)rptr)->ipha_dst;
44367c478bd9Sstevel@tonic-gate 				dstopt += sizeof (ipaddr_t);
44377c478bd9Sstevel@tonic-gate 				udi_size -= toh->len;
44387c478bd9Sstevel@tonic-gate 			}
44397c478bd9Sstevel@tonic-gate 
44407c478bd9Sstevel@tonic-gate 			if (udp->udp_recvslla && (pinfo != NULL) &&
44417c478bd9Sstevel@tonic-gate 			    (pinfo->in_pkt_flags & IPF_RECVSLLA)) {
44427c478bd9Sstevel@tonic-gate 
44437c478bd9Sstevel@tonic-gate 				struct T_opthdr *toh;
44447c478bd9Sstevel@tonic-gate 				struct sockaddr_dl	*dstptr;
44457c478bd9Sstevel@tonic-gate 
44467c478bd9Sstevel@tonic-gate 				toh = (struct T_opthdr *)dstopt;
44477c478bd9Sstevel@tonic-gate 				toh->level = IPPROTO_IP;
44487c478bd9Sstevel@tonic-gate 				toh->name = IP_RECVSLLA;
44497c478bd9Sstevel@tonic-gate 				toh->len = sizeof (struct T_opthdr) +
44507c478bd9Sstevel@tonic-gate 					sizeof (struct sockaddr_dl);
44517c478bd9Sstevel@tonic-gate 				toh->status = 0;
44527c478bd9Sstevel@tonic-gate 				dstopt += sizeof (struct T_opthdr);
44537c478bd9Sstevel@tonic-gate 				dstptr = (struct sockaddr_dl *)dstopt;
44547c478bd9Sstevel@tonic-gate 				bcopy(&pinfo->in_pkt_slla, dstptr,
44557c478bd9Sstevel@tonic-gate 				    sizeof (struct sockaddr_dl));
44567c478bd9Sstevel@tonic-gate 				dstopt += sizeof (struct sockaddr_dl);
44577c478bd9Sstevel@tonic-gate 				udi_size -= toh->len;
44587c478bd9Sstevel@tonic-gate 			}
44597c478bd9Sstevel@tonic-gate 
44607c478bd9Sstevel@tonic-gate 			if (udp->udp_recvif && (pinfo != NULL) &&
44617c478bd9Sstevel@tonic-gate 			    (pinfo->in_pkt_flags & IPF_RECVIF)) {
44627c478bd9Sstevel@tonic-gate 
44637c478bd9Sstevel@tonic-gate 				struct T_opthdr *toh;
44647c478bd9Sstevel@tonic-gate 				uint_t		*dstptr;
44657c478bd9Sstevel@tonic-gate 
44667c478bd9Sstevel@tonic-gate 				toh = (struct T_opthdr *)dstopt;
44677c478bd9Sstevel@tonic-gate 				toh->level = IPPROTO_IP;
44687c478bd9Sstevel@tonic-gate 				toh->name = IP_RECVIF;
44697c478bd9Sstevel@tonic-gate 				toh->len = sizeof (struct T_opthdr) +
44707c478bd9Sstevel@tonic-gate 					sizeof (uint_t);
44717c478bd9Sstevel@tonic-gate 				toh->status = 0;
44727c478bd9Sstevel@tonic-gate 				dstopt += sizeof (struct T_opthdr);
44737c478bd9Sstevel@tonic-gate 				dstptr = (uint_t *)dstopt;
44747c478bd9Sstevel@tonic-gate 				*dstptr = pinfo->in_pkt_ifindex;
44757c478bd9Sstevel@tonic-gate 				dstopt += sizeof (uint_t);
44767c478bd9Sstevel@tonic-gate 				udi_size -= toh->len;
44777c478bd9Sstevel@tonic-gate 			}
44787c478bd9Sstevel@tonic-gate 
44797c478bd9Sstevel@tonic-gate 			if (cr != NULL) {
44807c478bd9Sstevel@tonic-gate 				struct T_opthdr *toh;
44817c478bd9Sstevel@tonic-gate 
44827c478bd9Sstevel@tonic-gate 				toh = (struct T_opthdr *)dstopt;
44837c478bd9Sstevel@tonic-gate 				toh->level = SOL_SOCKET;
44847c478bd9Sstevel@tonic-gate 				toh->name = SCM_UCRED;
44857c478bd9Sstevel@tonic-gate 				toh->len = sizeof (struct T_opthdr) + ucredsize;
44867c478bd9Sstevel@tonic-gate 				toh->status = 0;
44877c478bd9Sstevel@tonic-gate 				(void) cred2ucred(cr, cpid, &toh[1]);
44887c478bd9Sstevel@tonic-gate 				dstopt += toh->len;
44897c478bd9Sstevel@tonic-gate 				udi_size -= toh->len;
44907c478bd9Sstevel@tonic-gate 			}
44917c478bd9Sstevel@tonic-gate 
44927c478bd9Sstevel@tonic-gate 			if (udp->udp_recvttl) {
44937c478bd9Sstevel@tonic-gate 				struct	T_opthdr *toh;
44947c478bd9Sstevel@tonic-gate 				uint8_t	*dstptr;
44957c478bd9Sstevel@tonic-gate 
44967c478bd9Sstevel@tonic-gate 				toh = (struct T_opthdr *)dstopt;
44977c478bd9Sstevel@tonic-gate 				toh->level = IPPROTO_IP;
44987c478bd9Sstevel@tonic-gate 				toh->name = IP_RECVTTL;
44997c478bd9Sstevel@tonic-gate 				toh->len = sizeof (struct T_opthdr) +
45007c478bd9Sstevel@tonic-gate 				    sizeof (uint8_t);
45017c478bd9Sstevel@tonic-gate 				toh->status = 0;
45027c478bd9Sstevel@tonic-gate 				dstopt += sizeof (struct T_opthdr);
45037c478bd9Sstevel@tonic-gate 				dstptr = (uint8_t *)dstopt;
45047c478bd9Sstevel@tonic-gate 				*dstptr = ((ipha_t *)rptr)->ipha_ttl;
45057c478bd9Sstevel@tonic-gate 				dstopt += sizeof (uint8_t);
45067c478bd9Sstevel@tonic-gate 				udi_size -= toh->len;
45077c478bd9Sstevel@tonic-gate 			}
45087c478bd9Sstevel@tonic-gate 
45097c478bd9Sstevel@tonic-gate 			/* Consumed all of allocated space */
45107c478bd9Sstevel@tonic-gate 			ASSERT(udi_size == 0);
45117c478bd9Sstevel@tonic-gate 		}
45127c478bd9Sstevel@tonic-gate 	} else {
45137c478bd9Sstevel@tonic-gate 		sin6_t *sin6;
45147c478bd9Sstevel@tonic-gate 
45157c478bd9Sstevel@tonic-gate 		/*
45167c478bd9Sstevel@tonic-gate 		 * Handle both IPv4 and IPv6 packets for IPv6 sockets.
45177c478bd9Sstevel@tonic-gate 		 *
45187c478bd9Sstevel@tonic-gate 		 * Normally we only send up the address. If receiving of any
45197c478bd9Sstevel@tonic-gate 		 * optional receive side information is enabled, we also send
45207c478bd9Sstevel@tonic-gate 		 * that up as options.
45217c478bd9Sstevel@tonic-gate 		 * [ Only udp_rput_other() handles packets that contain IP
45227c478bd9Sstevel@tonic-gate 		 * options so code to account for does not appear immediately
45237c478bd9Sstevel@tonic-gate 		 * below but elsewhere ]
45247c478bd9Sstevel@tonic-gate 		 */
45257c478bd9Sstevel@tonic-gate 		udi_size = sizeof (struct T_unitdata_ind) + sizeof (sin6_t);
45267c478bd9Sstevel@tonic-gate 
45277c478bd9Sstevel@tonic-gate 		if (ipp.ipp_fields & (IPPF_HOPOPTS|IPPF_DSTOPTS|IPPF_RTDSTOPTS|
45287c478bd9Sstevel@tonic-gate 		    IPPF_RTHDR|IPPF_IFINDEX)) {
45297c478bd9Sstevel@tonic-gate 			if (udp->udp_ipv6_recvhopopts &&
45307c478bd9Sstevel@tonic-gate 			    (ipp.ipp_fields & IPPF_HOPOPTS)) {
45317c478bd9Sstevel@tonic-gate 				udi_size += sizeof (struct T_opthdr) +
45327c478bd9Sstevel@tonic-gate 				    ipp.ipp_hopoptslen;
4533ff550d0eSmasputra 				UDP_STAT(udp_in_recvhopopts);
45347c478bd9Sstevel@tonic-gate 			}
45357c478bd9Sstevel@tonic-gate 			if ((udp->udp_ipv6_recvdstopts ||
45367c478bd9Sstevel@tonic-gate 				udp->udp_old_ipv6_recvdstopts) &&
45377c478bd9Sstevel@tonic-gate 			    (ipp.ipp_fields & IPPF_DSTOPTS)) {
45387c478bd9Sstevel@tonic-gate 				udi_size += sizeof (struct T_opthdr) +
45397c478bd9Sstevel@tonic-gate 				    ipp.ipp_dstoptslen;
4540ff550d0eSmasputra 				UDP_STAT(udp_in_recvdstopts);
45417c478bd9Sstevel@tonic-gate 			}
45427c478bd9Sstevel@tonic-gate 			if (((udp->udp_ipv6_recvdstopts &&
45437c478bd9Sstevel@tonic-gate 			    udp->udp_ipv6_recvrthdr &&
45447c478bd9Sstevel@tonic-gate 			    (ipp.ipp_fields & IPPF_RTHDR)) ||
45457c478bd9Sstevel@tonic-gate 			    udp->udp_ipv6_recvrthdrdstopts) &&
45467c478bd9Sstevel@tonic-gate 			    (ipp.ipp_fields & IPPF_RTDSTOPTS)) {
45477c478bd9Sstevel@tonic-gate 				udi_size += sizeof (struct T_opthdr) +
45487c478bd9Sstevel@tonic-gate 				    ipp.ipp_rtdstoptslen;
4549ff550d0eSmasputra 				UDP_STAT(udp_in_recvrtdstopts);
45507c478bd9Sstevel@tonic-gate 			}
45517c478bd9Sstevel@tonic-gate 			if (udp->udp_ipv6_recvrthdr &&
45527c478bd9Sstevel@tonic-gate 			    (ipp.ipp_fields & IPPF_RTHDR)) {
45537c478bd9Sstevel@tonic-gate 				udi_size += sizeof (struct T_opthdr) +
45547c478bd9Sstevel@tonic-gate 				    ipp.ipp_rthdrlen;
4555ff550d0eSmasputra 				UDP_STAT(udp_in_recvrthdr);
45567c478bd9Sstevel@tonic-gate 			}
45577c478bd9Sstevel@tonic-gate 			if (udp->udp_ipv6_recvpktinfo &&
45587c478bd9Sstevel@tonic-gate 			    (ipp.ipp_fields & IPPF_IFINDEX)) {
45597c478bd9Sstevel@tonic-gate 				udi_size += sizeof (struct T_opthdr) +
45607c478bd9Sstevel@tonic-gate 				    sizeof (struct in6_pktinfo);
4561ff550d0eSmasputra 				UDP_STAT(udp_in_recvpktinfo);
45627c478bd9Sstevel@tonic-gate 			}
45637c478bd9Sstevel@tonic-gate 
45647c478bd9Sstevel@tonic-gate 		}
45657c478bd9Sstevel@tonic-gate 		if (udp->udp_recvucred && (cr = DB_CRED(mp)) != NULL) {
45667c478bd9Sstevel@tonic-gate 			udi_size += sizeof (struct T_opthdr) + ucredsize;
45677c478bd9Sstevel@tonic-gate 			cpid = DB_CPID(mp);
4568ff550d0eSmasputra 			UDP_STAT(udp_in_recvucred);
45697c478bd9Sstevel@tonic-gate 		}
45707c478bd9Sstevel@tonic-gate 
4571ff550d0eSmasputra 		if (udp->udp_ipv6_recvhoplimit) {
45727c478bd9Sstevel@tonic-gate 			udi_size += sizeof (struct T_opthdr) + sizeof (int);
4573ff550d0eSmasputra 			UDP_STAT(udp_in_recvhoplimit);
4574ff550d0eSmasputra 		}
45757c478bd9Sstevel@tonic-gate 
4576ff550d0eSmasputra 		if (udp->udp_ipv6_recvtclass) {
45777c478bd9Sstevel@tonic-gate 			udi_size += sizeof (struct T_opthdr) + sizeof (int);
4578ff550d0eSmasputra 			UDP_STAT(udp_in_recvtclass);
4579ff550d0eSmasputra 		}
45807c478bd9Sstevel@tonic-gate 
45817c478bd9Sstevel@tonic-gate 		mp1 = allocb(udi_size, BPRI_MED);
45827c478bd9Sstevel@tonic-gate 		if (mp1 == NULL) {
45837c478bd9Sstevel@tonic-gate 			freemsg(mp);
45847c478bd9Sstevel@tonic-gate 			if (options_mp != NULL)
45857c478bd9Sstevel@tonic-gate 				freeb(options_mp);
45867c478bd9Sstevel@tonic-gate 			TRACE_2(TR_FAC_UDP, TR_UDP_RPUT_END,
45877c478bd9Sstevel@tonic-gate 				"udp_rput_end: q %p (%S)", q, "allocbfail");
45887c478bd9Sstevel@tonic-gate 			BUMP_MIB(&udp_mib, udpInErrors);
45897c478bd9Sstevel@tonic-gate 			return;
45907c478bd9Sstevel@tonic-gate 		}
45917c478bd9Sstevel@tonic-gate 		mp1->b_cont = mp;
45927c478bd9Sstevel@tonic-gate 		mp = mp1;
45937c478bd9Sstevel@tonic-gate 		mp->b_datap->db_type = M_PROTO;
45947c478bd9Sstevel@tonic-gate 		tudi = (struct T_unitdata_ind *)mp->b_rptr;
45957c478bd9Sstevel@tonic-gate 		mp->b_wptr = (uchar_t *)tudi + udi_size;
45967c478bd9Sstevel@tonic-gate 		tudi->PRIM_type = T_UNITDATA_IND;
45977c478bd9Sstevel@tonic-gate 		tudi->SRC_length = sizeof (sin6_t);
45987c478bd9Sstevel@tonic-gate 		tudi->SRC_offset = sizeof (struct T_unitdata_ind);
45997c478bd9Sstevel@tonic-gate 		tudi->OPT_offset = sizeof (struct T_unitdata_ind) +
46007c478bd9Sstevel@tonic-gate 		    sizeof (sin6_t);
46017c478bd9Sstevel@tonic-gate 		udi_size -= (sizeof (struct T_unitdata_ind) + sizeof (sin6_t));
46027c478bd9Sstevel@tonic-gate 		tudi->OPT_length = udi_size;
46037c478bd9Sstevel@tonic-gate 		sin6 = (sin6_t *)&tudi[1];
46047c478bd9Sstevel@tonic-gate 		if (ipversion == IPV4_VERSION) {
46057c478bd9Sstevel@tonic-gate 			in6_addr_t v6dst;
46067c478bd9Sstevel@tonic-gate 
46077c478bd9Sstevel@tonic-gate 			IN6_IPADDR_TO_V4MAPPED(((ipha_t *)rptr)->ipha_src,
46087c478bd9Sstevel@tonic-gate 			    &sin6->sin6_addr);
46097c478bd9Sstevel@tonic-gate 			IN6_IPADDR_TO_V4MAPPED(((ipha_t *)rptr)->ipha_dst,
46107c478bd9Sstevel@tonic-gate 			    &v6dst);
46117c478bd9Sstevel@tonic-gate 			sin6->sin6_flowinfo = 0;
46127c478bd9Sstevel@tonic-gate 			sin6->sin6_scope_id = 0;
46137c478bd9Sstevel@tonic-gate 			sin6->__sin6_src_id = ip_srcid_find_addr(&v6dst,
4614ff550d0eSmasputra 			    connp->conn_zoneid);
46157c478bd9Sstevel@tonic-gate 		} else {
46167c478bd9Sstevel@tonic-gate 			sin6->sin6_addr = ip6h->ip6_src;
46177c478bd9Sstevel@tonic-gate 			/* No sin6_flowinfo per API */
46187c478bd9Sstevel@tonic-gate 			sin6->sin6_flowinfo = 0;
46197c478bd9Sstevel@tonic-gate 			/* For link-scope source pass up scope id */
46207c478bd9Sstevel@tonic-gate 			if ((ipp.ipp_fields & IPPF_IFINDEX) &&
46217c478bd9Sstevel@tonic-gate 			    IN6_IS_ADDR_LINKSCOPE(&ip6h->ip6_src))
46227c478bd9Sstevel@tonic-gate 				sin6->sin6_scope_id = ipp.ipp_ifindex;
46237c478bd9Sstevel@tonic-gate 			else
46247c478bd9Sstevel@tonic-gate 				sin6->sin6_scope_id = 0;
4625ff550d0eSmasputra 			sin6->__sin6_src_id = ip_srcid_find_addr(
4626ff550d0eSmasputra 			    &ip6h->ip6_dst, connp->conn_zoneid);
46277c478bd9Sstevel@tonic-gate 		}
46287c478bd9Sstevel@tonic-gate 		sin6->sin6_port = udpha->uha_src_port;
46297c478bd9Sstevel@tonic-gate 		sin6->sin6_family = udp->udp_family;
46307c478bd9Sstevel@tonic-gate 
46317c478bd9Sstevel@tonic-gate 		if (udi_size != 0) {
46327c478bd9Sstevel@tonic-gate 			uchar_t *dstopt;
46337c478bd9Sstevel@tonic-gate 
46347c478bd9Sstevel@tonic-gate 			dstopt = (uchar_t *)&sin6[1];
46357c478bd9Sstevel@tonic-gate 			if (udp->udp_ipv6_recvpktinfo &&
46367c478bd9Sstevel@tonic-gate 			    (ipp.ipp_fields & IPPF_IFINDEX)) {
46377c478bd9Sstevel@tonic-gate 				struct T_opthdr *toh;
46387c478bd9Sstevel@tonic-gate 				struct in6_pktinfo *pkti;
46397c478bd9Sstevel@tonic-gate 
46407c478bd9Sstevel@tonic-gate 				toh = (struct T_opthdr *)dstopt;
46417c478bd9Sstevel@tonic-gate 				toh->level = IPPROTO_IPV6;
46427c478bd9Sstevel@tonic-gate 				toh->name = IPV6_PKTINFO;
46437c478bd9Sstevel@tonic-gate 				toh->len = sizeof (struct T_opthdr) +
46447c478bd9Sstevel@tonic-gate 				    sizeof (*pkti);
46457c478bd9Sstevel@tonic-gate 				toh->status = 0;
46467c478bd9Sstevel@tonic-gate 				dstopt += sizeof (struct T_opthdr);
46477c478bd9Sstevel@tonic-gate 				pkti = (struct in6_pktinfo *)dstopt;
46487c478bd9Sstevel@tonic-gate 				if (ipversion == IPV6_VERSION)
46497c478bd9Sstevel@tonic-gate 					pkti->ipi6_addr = ip6h->ip6_dst;
46507c478bd9Sstevel@tonic-gate 				else
46517c478bd9Sstevel@tonic-gate 					IN6_IPADDR_TO_V4MAPPED(
46527c478bd9Sstevel@tonic-gate 						((ipha_t *)rptr)->ipha_dst,
46537c478bd9Sstevel@tonic-gate 						    &pkti->ipi6_addr);
46547c478bd9Sstevel@tonic-gate 				pkti->ipi6_ifindex = ipp.ipp_ifindex;
46557c478bd9Sstevel@tonic-gate 				dstopt += sizeof (*pkti);
46567c478bd9Sstevel@tonic-gate 				udi_size -= toh->len;
46577c478bd9Sstevel@tonic-gate 			}
46587c478bd9Sstevel@tonic-gate 			if (udp->udp_ipv6_recvhoplimit) {
46597c478bd9Sstevel@tonic-gate 				struct T_opthdr *toh;
46607c478bd9Sstevel@tonic-gate 
46617c478bd9Sstevel@tonic-gate 				toh = (struct T_opthdr *)dstopt;
46627c478bd9Sstevel@tonic-gate 				toh->level = IPPROTO_IPV6;
46637c478bd9Sstevel@tonic-gate 				toh->name = IPV6_HOPLIMIT;
46647c478bd9Sstevel@tonic-gate 				toh->len = sizeof (struct T_opthdr) +
46657c478bd9Sstevel@tonic-gate 				    sizeof (uint_t);
46667c478bd9Sstevel@tonic-gate 				toh->status = 0;
46677c478bd9Sstevel@tonic-gate 				dstopt += sizeof (struct T_opthdr);
46687c478bd9Sstevel@tonic-gate 				if (ipversion == IPV6_VERSION)
46697c478bd9Sstevel@tonic-gate 					*(uint_t *)dstopt = ip6h->ip6_hops;
46707c478bd9Sstevel@tonic-gate 				else
46717c478bd9Sstevel@tonic-gate 					*(uint_t *)dstopt =
46727c478bd9Sstevel@tonic-gate 					    ((ipha_t *)rptr)->ipha_ttl;
46737c478bd9Sstevel@tonic-gate 				dstopt += sizeof (uint_t);
46747c478bd9Sstevel@tonic-gate 				udi_size -= toh->len;
46757c478bd9Sstevel@tonic-gate 			}
46767c478bd9Sstevel@tonic-gate 			if (udp->udp_ipv6_recvtclass) {
46777c478bd9Sstevel@tonic-gate 				struct T_opthdr *toh;
46787c478bd9Sstevel@tonic-gate 
46797c478bd9Sstevel@tonic-gate 				toh = (struct T_opthdr *)dstopt;
46807c478bd9Sstevel@tonic-gate 				toh->level = IPPROTO_IPV6;
46817c478bd9Sstevel@tonic-gate 				toh->name = IPV6_TCLASS;
46827c478bd9Sstevel@tonic-gate 				toh->len = sizeof (struct T_opthdr) +
46837c478bd9Sstevel@tonic-gate 				    sizeof (uint_t);
46847c478bd9Sstevel@tonic-gate 				toh->status = 0;
46857c478bd9Sstevel@tonic-gate 				dstopt += sizeof (struct T_opthdr);
46867c478bd9Sstevel@tonic-gate 				if (ipversion == IPV6_VERSION) {
46877c478bd9Sstevel@tonic-gate 					*(uint_t *)dstopt =
46887c478bd9Sstevel@tonic-gate 					IPV6_FLOW_TCLASS(ip6h->ip6_flow);
46897c478bd9Sstevel@tonic-gate 				} else {
46907c478bd9Sstevel@tonic-gate 					ipha_t *ipha = (ipha_t *)rptr;
46917c478bd9Sstevel@tonic-gate 					*(uint_t *)dstopt =
46927c478bd9Sstevel@tonic-gate 					    ipha->ipha_type_of_service;
46937c478bd9Sstevel@tonic-gate 				}
46947c478bd9Sstevel@tonic-gate 				dstopt += sizeof (uint_t);
46957c478bd9Sstevel@tonic-gate 				udi_size -= toh->len;
46967c478bd9Sstevel@tonic-gate 			}
46977c478bd9Sstevel@tonic-gate 			if (udp->udp_ipv6_recvhopopts &&
46987c478bd9Sstevel@tonic-gate 			    (ipp.ipp_fields & IPPF_HOPOPTS)) {
46997c478bd9Sstevel@tonic-gate 				struct T_opthdr *toh;
47007c478bd9Sstevel@tonic-gate 
47017c478bd9Sstevel@tonic-gate 				toh = (struct T_opthdr *)dstopt;
47027c478bd9Sstevel@tonic-gate 				toh->level = IPPROTO_IPV6;
47037c478bd9Sstevel@tonic-gate 				toh->name = IPV6_HOPOPTS;
47047c478bd9Sstevel@tonic-gate 				toh->len = sizeof (struct T_opthdr) +
47057c478bd9Sstevel@tonic-gate 				    ipp.ipp_hopoptslen;
47067c478bd9Sstevel@tonic-gate 				toh->status = 0;
47077c478bd9Sstevel@tonic-gate 				dstopt += sizeof (struct T_opthdr);
47087c478bd9Sstevel@tonic-gate 				bcopy(ipp.ipp_hopopts, dstopt,
47097c478bd9Sstevel@tonic-gate 				    ipp.ipp_hopoptslen);
47107c478bd9Sstevel@tonic-gate 				dstopt += ipp.ipp_hopoptslen;
47117c478bd9Sstevel@tonic-gate 				udi_size -= toh->len;
47127c478bd9Sstevel@tonic-gate 			}
47137c478bd9Sstevel@tonic-gate 			if (udp->udp_ipv6_recvdstopts &&
47147c478bd9Sstevel@tonic-gate 			    udp->udp_ipv6_recvrthdr &&
47157c478bd9Sstevel@tonic-gate 			    (ipp.ipp_fields & IPPF_RTHDR) &&
47167c478bd9Sstevel@tonic-gate 			    (ipp.ipp_fields & IPPF_RTDSTOPTS)) {
47177c478bd9Sstevel@tonic-gate 				struct T_opthdr *toh;
47187c478bd9Sstevel@tonic-gate 
47197c478bd9Sstevel@tonic-gate 				toh = (struct T_opthdr *)dstopt;
47207c478bd9Sstevel@tonic-gate 				toh->level = IPPROTO_IPV6;
47217c478bd9Sstevel@tonic-gate 				toh->name = IPV6_DSTOPTS;
47227c478bd9Sstevel@tonic-gate 				toh->len = sizeof (struct T_opthdr) +
47237c478bd9Sstevel@tonic-gate 				    ipp.ipp_rtdstoptslen;
47247c478bd9Sstevel@tonic-gate 				toh->status = 0;
47257c478bd9Sstevel@tonic-gate 				dstopt += sizeof (struct T_opthdr);
47267c478bd9Sstevel@tonic-gate 				bcopy(ipp.ipp_rtdstopts, dstopt,
47277c478bd9Sstevel@tonic-gate 				    ipp.ipp_rtdstoptslen);
47287c478bd9Sstevel@tonic-gate 				dstopt += ipp.ipp_rtdstoptslen;
47297c478bd9Sstevel@tonic-gate 				udi_size -= toh->len;
47307c478bd9Sstevel@tonic-gate 			}
47317c478bd9Sstevel@tonic-gate 			if (udp->udp_ipv6_recvrthdr &&
47327c478bd9Sstevel@tonic-gate 			    (ipp.ipp_fields & IPPF_RTHDR)) {
47337c478bd9Sstevel@tonic-gate 				struct T_opthdr *toh;
47347c478bd9Sstevel@tonic-gate 
47357c478bd9Sstevel@tonic-gate 				toh = (struct T_opthdr *)dstopt;
47367c478bd9Sstevel@tonic-gate 				toh->level = IPPROTO_IPV6;
47377c478bd9Sstevel@tonic-gate 				toh->name = IPV6_RTHDR;
47387c478bd9Sstevel@tonic-gate 				toh->len = sizeof (struct T_opthdr) +
47397c478bd9Sstevel@tonic-gate 				    ipp.ipp_rthdrlen;
47407c478bd9Sstevel@tonic-gate 				toh->status = 0;
47417c478bd9Sstevel@tonic-gate 				dstopt += sizeof (struct T_opthdr);
47427c478bd9Sstevel@tonic-gate 				bcopy(ipp.ipp_rthdr, dstopt, ipp.ipp_rthdrlen);
47437c478bd9Sstevel@tonic-gate 				dstopt += ipp.ipp_rthdrlen;
47447c478bd9Sstevel@tonic-gate 				udi_size -= toh->len;
47457c478bd9Sstevel@tonic-gate 			}
47467c478bd9Sstevel@tonic-gate 			if (udp->udp_ipv6_recvdstopts &&
47477c478bd9Sstevel@tonic-gate 			    (ipp.ipp_fields & IPPF_DSTOPTS)) {
47487c478bd9Sstevel@tonic-gate 				struct T_opthdr *toh;
47497c478bd9Sstevel@tonic-gate 
47507c478bd9Sstevel@tonic-gate 				toh = (struct T_opthdr *)dstopt;
47517c478bd9Sstevel@tonic-gate 				toh->level = IPPROTO_IPV6;
47527c478bd9Sstevel@tonic-gate 				toh->name = IPV6_DSTOPTS;
47537c478bd9Sstevel@tonic-gate 				toh->len = sizeof (struct T_opthdr) +
47547c478bd9Sstevel@tonic-gate 				    ipp.ipp_dstoptslen;
47557c478bd9Sstevel@tonic-gate 				toh->status = 0;
47567c478bd9Sstevel@tonic-gate 				dstopt += sizeof (struct T_opthdr);
47577c478bd9Sstevel@tonic-gate 				bcopy(ipp.ipp_dstopts, dstopt,
47587c478bd9Sstevel@tonic-gate 				    ipp.ipp_dstoptslen);
47597c478bd9Sstevel@tonic-gate 				dstopt += ipp.ipp_dstoptslen;
47607c478bd9Sstevel@tonic-gate 				udi_size -= toh->len;
47617c478bd9Sstevel@tonic-gate 			}
47627c478bd9Sstevel@tonic-gate 
47637c478bd9Sstevel@tonic-gate 			if (cr != NULL) {
47647c478bd9Sstevel@tonic-gate 				struct T_opthdr *toh;
47657c478bd9Sstevel@tonic-gate 
47667c478bd9Sstevel@tonic-gate 				toh = (struct T_opthdr *)dstopt;
47677c478bd9Sstevel@tonic-gate 				toh->level = SOL_SOCKET;
47687c478bd9Sstevel@tonic-gate 				toh->name = SCM_UCRED;
47697c478bd9Sstevel@tonic-gate 				toh->len = sizeof (struct T_opthdr) + ucredsize;
47707c478bd9Sstevel@tonic-gate 				toh->status = 0;
47717c478bd9Sstevel@tonic-gate 				(void) cred2ucred(cr, cpid, &toh[1]);
47727c478bd9Sstevel@tonic-gate 				dstopt += toh->len;
47737c478bd9Sstevel@tonic-gate 				udi_size -= toh->len;
47747c478bd9Sstevel@tonic-gate 			}
47757c478bd9Sstevel@tonic-gate 			/* Consumed all of allocated space */
47767c478bd9Sstevel@tonic-gate 			ASSERT(udi_size == 0);
47777c478bd9Sstevel@tonic-gate 		}
47787c478bd9Sstevel@tonic-gate #undef	sin6
47797c478bd9Sstevel@tonic-gate 		/* No IP_RECVDSTADDR for IPv6. */
47807c478bd9Sstevel@tonic-gate 	}
47817c478bd9Sstevel@tonic-gate 
47827c478bd9Sstevel@tonic-gate 	BUMP_MIB(&udp_mib, udpInDatagrams);
47837c478bd9Sstevel@tonic-gate 	TRACE_2(TR_FAC_UDP, TR_UDP_RPUT_END,
47847c478bd9Sstevel@tonic-gate 		"udp_rput_end: q %p (%S)", q, "end");
47857c478bd9Sstevel@tonic-gate 	if (options_mp != NULL)
47867c478bd9Sstevel@tonic-gate 		freeb(options_mp);
4787ff550d0eSmasputra 
4788ff550d0eSmasputra 	if (udp->udp_direct_sockfs) {
4789ff550d0eSmasputra 		/*
4790ff550d0eSmasputra 		 * There is nothing above us except for the stream head;
4791ff550d0eSmasputra 		 * use the read-side synchronous stream interface in
4792ff550d0eSmasputra 		 * order to reduce the time spent in interrupt thread.
4793ff550d0eSmasputra 		 */
4794ff550d0eSmasputra 		ASSERT(udp->udp_issocket);
4795ff550d0eSmasputra 		udp_rcv_enqueue(UDP_RD(q), udp, mp, mp_len);
4796ff550d0eSmasputra 	} else {
4797ff550d0eSmasputra 		/*
4798ff550d0eSmasputra 		 * Use regular STREAMS interface to pass data upstream
4799ff550d0eSmasputra 		 * if this is not a socket endpoint, or if we have
4800ff550d0eSmasputra 		 * switched over to the slow mode due to sockmod being
4801ff550d0eSmasputra 		 * popped or a module being pushed on top of us.
4802ff550d0eSmasputra 		 */
4803ff550d0eSmasputra 		putnext(UDP_RD(q), mp);
4804ff550d0eSmasputra 	}
4805ff550d0eSmasputra 	return;
4806ff550d0eSmasputra 
4807ff550d0eSmasputra tossit:
4808ff550d0eSmasputra 	freemsg(mp);
4809ff550d0eSmasputra 	if (options_mp != NULL)
4810ff550d0eSmasputra 		freeb(options_mp);
4811ff550d0eSmasputra 	BUMP_MIB(&udp_mib, udpInErrors);
4812ff550d0eSmasputra }
4813ff550d0eSmasputra 
4814ff550d0eSmasputra void
4815ff550d0eSmasputra udp_conn_recv(conn_t *connp, mblk_t *mp)
4816ff550d0eSmasputra {
4817ff550d0eSmasputra 	_UDP_ENTER(connp, mp, udp_input_wrapper, SQTAG_UDP_FANOUT);
4818ff550d0eSmasputra }
4819ff550d0eSmasputra 
4820ff550d0eSmasputra /* ARGSUSED */
4821ff550d0eSmasputra static void
4822ff550d0eSmasputra udp_input_wrapper(void *arg, mblk_t *mp, void *arg2)
4823ff550d0eSmasputra {
4824ff550d0eSmasputra 	udp_input((conn_t *)arg, mp);
4825ff550d0eSmasputra 	_UDP_EXIT((conn_t *)arg);
48267c478bd9Sstevel@tonic-gate }
48277c478bd9Sstevel@tonic-gate 
48287c478bd9Sstevel@tonic-gate /*
48297c478bd9Sstevel@tonic-gate  * Process non-M_DATA messages as well as M_DATA messages that requires
48307c478bd9Sstevel@tonic-gate  * modifications to udp_ip_rcv_options i.e. IPv4 packets with IP options.
48317c478bd9Sstevel@tonic-gate  */
48327c478bd9Sstevel@tonic-gate static void
48337c478bd9Sstevel@tonic-gate udp_rput_other(queue_t *q, mblk_t *mp)
48347c478bd9Sstevel@tonic-gate {
48357c478bd9Sstevel@tonic-gate 	struct T_unitdata_ind	*tudi;
48367c478bd9Sstevel@tonic-gate 	mblk_t			*mp1;
48377c478bd9Sstevel@tonic-gate 	uchar_t			*rptr;
48387c478bd9Sstevel@tonic-gate 	uchar_t			*new_rptr;
48397c478bd9Sstevel@tonic-gate 	int			hdr_length;
48407c478bd9Sstevel@tonic-gate 	int			udi_size;	/* Size of T_unitdata_ind */
48417c478bd9Sstevel@tonic-gate 	int			opt_len;	/* Length of IP options */
48427c478bd9Sstevel@tonic-gate 	sin_t			*sin;
48437c478bd9Sstevel@tonic-gate 	struct T_error_ack	*tea;
48447c478bd9Sstevel@tonic-gate 	mblk_t			*options_mp = NULL;
48457c478bd9Sstevel@tonic-gate 	in_pktinfo_t		*pinfo;
48467c478bd9Sstevel@tonic-gate 	boolean_t		recv_on = B_FALSE;
48477c478bd9Sstevel@tonic-gate 	cred_t			*cr = NULL;
4848ff550d0eSmasputra 	udp_t			*udp = Q_TO_UDP(q);
48497c478bd9Sstevel@tonic-gate 	pid_t			cpid;
48507c478bd9Sstevel@tonic-gate 
48517c478bd9Sstevel@tonic-gate 	TRACE_2(TR_FAC_UDP, TR_UDP_RPUT_START,
48527c478bd9Sstevel@tonic-gate 	    "udp_rput_other: q %p mp %p", q, mp);
48537c478bd9Sstevel@tonic-gate 
48547c478bd9Sstevel@tonic-gate 	ASSERT(OK_32PTR(mp->b_rptr));
48557c478bd9Sstevel@tonic-gate 	rptr = mp->b_rptr;
48567c478bd9Sstevel@tonic-gate 
48577c478bd9Sstevel@tonic-gate 	switch (mp->b_datap->db_type) {
48587c478bd9Sstevel@tonic-gate 	case M_CTL:
48597c478bd9Sstevel@tonic-gate 		/*
48607c478bd9Sstevel@tonic-gate 		 * We are here only if IP_RECVSLLA and/or IP_RECVIF are set
48617c478bd9Sstevel@tonic-gate 		 */
48627c478bd9Sstevel@tonic-gate 		recv_on = B_TRUE;
48637c478bd9Sstevel@tonic-gate 		options_mp = mp;
48647c478bd9Sstevel@tonic-gate 		pinfo = (in_pktinfo_t *)options_mp->b_rptr;
48657c478bd9Sstevel@tonic-gate 
48667c478bd9Sstevel@tonic-gate 		/*
48677c478bd9Sstevel@tonic-gate 		 * The actual data is in mp->b_cont
48687c478bd9Sstevel@tonic-gate 		 */
48697c478bd9Sstevel@tonic-gate 		mp = mp->b_cont;
48707c478bd9Sstevel@tonic-gate 		ASSERT(OK_32PTR(mp->b_rptr));
48717c478bd9Sstevel@tonic-gate 		rptr = mp->b_rptr;
48727c478bd9Sstevel@tonic-gate 		break;
48737c478bd9Sstevel@tonic-gate 	case M_DATA:
48747c478bd9Sstevel@tonic-gate 		/*
48757c478bd9Sstevel@tonic-gate 		 * M_DATA messages contain IPv4 datagrams.  They are handled
48767c478bd9Sstevel@tonic-gate 		 * after this switch.
48777c478bd9Sstevel@tonic-gate 		 */
48787c478bd9Sstevel@tonic-gate 		break;
48797c478bd9Sstevel@tonic-gate 	case M_PROTO:
48807c478bd9Sstevel@tonic-gate 	case M_PCPROTO:
48817c478bd9Sstevel@tonic-gate 		/* M_PROTO messages contain some type of TPI message. */
48827c478bd9Sstevel@tonic-gate 		ASSERT((uintptr_t)(mp->b_wptr - rptr) <= (uintptr_t)INT_MAX);
48837c478bd9Sstevel@tonic-gate 		if (mp->b_wptr - rptr < sizeof (t_scalar_t)) {
48847c478bd9Sstevel@tonic-gate 			freemsg(mp);
48857c478bd9Sstevel@tonic-gate 			TRACE_2(TR_FAC_UDP, TR_UDP_RPUT_END,
48867c478bd9Sstevel@tonic-gate 			    "udp_rput_other_end: q %p (%S)", q, "protoshort");
48877c478bd9Sstevel@tonic-gate 			return;
48887c478bd9Sstevel@tonic-gate 		}
48897c478bd9Sstevel@tonic-gate 		tea = (struct T_error_ack *)rptr;
48907c478bd9Sstevel@tonic-gate 
48917c478bd9Sstevel@tonic-gate 		switch (tea->PRIM_type) {
48927c478bd9Sstevel@tonic-gate 		case T_ERROR_ACK:
48937c478bd9Sstevel@tonic-gate 			switch (tea->ERROR_prim) {
48947c478bd9Sstevel@tonic-gate 			case O_T_BIND_REQ:
48957c478bd9Sstevel@tonic-gate 			case T_BIND_REQ: {
48967c478bd9Sstevel@tonic-gate 				/*
48977c478bd9Sstevel@tonic-gate 				 * If our O_T_BIND_REQ/T_BIND_REQ fails,
48987c478bd9Sstevel@tonic-gate 				 * clear out the associated port and source
48997c478bd9Sstevel@tonic-gate 				 * address before passing the message
49007c478bd9Sstevel@tonic-gate 				 * upstream. If this was caused by a T_CONN_REQ
49017c478bd9Sstevel@tonic-gate 				 * revert back to bound state.
49027c478bd9Sstevel@tonic-gate 				 */
49037c478bd9Sstevel@tonic-gate 				udp_fanout_t	*udpf;
49047c478bd9Sstevel@tonic-gate 
49057c478bd9Sstevel@tonic-gate 				udpf = &udp_bind_fanout[
49067c478bd9Sstevel@tonic-gate 				    UDP_BIND_HASH(udp->udp_port)];
49077c478bd9Sstevel@tonic-gate 				mutex_enter(&udpf->uf_lock);
49087c478bd9Sstevel@tonic-gate 				if (udp->udp_state == TS_DATA_XFER) {
49097c478bd9Sstevel@tonic-gate 					/* Connect failed */
49107c478bd9Sstevel@tonic-gate 					tea->ERROR_prim = T_CONN_REQ;
49117c478bd9Sstevel@tonic-gate 					/* Revert back to the bound source */
49127c478bd9Sstevel@tonic-gate 					udp->udp_v6src = udp->udp_bound_v6src;
49137c478bd9Sstevel@tonic-gate 					udp->udp_state = TS_IDLE;
49147c478bd9Sstevel@tonic-gate 					mutex_exit(&udpf->uf_lock);
49157c478bd9Sstevel@tonic-gate 					if (udp->udp_family == AF_INET6)
49167c478bd9Sstevel@tonic-gate 						(void) udp_build_hdrs(q, udp);
49177c478bd9Sstevel@tonic-gate 					break;
49187c478bd9Sstevel@tonic-gate 				}
49197c478bd9Sstevel@tonic-gate 
49207c478bd9Sstevel@tonic-gate 				if (udp->udp_discon_pending) {
49217c478bd9Sstevel@tonic-gate 					tea->ERROR_prim = T_DISCON_REQ;
49227c478bd9Sstevel@tonic-gate 					udp->udp_discon_pending = 0;
49237c478bd9Sstevel@tonic-gate 				}
49247c478bd9Sstevel@tonic-gate 				V6_SET_ZERO(udp->udp_v6src);
49257c478bd9Sstevel@tonic-gate 				V6_SET_ZERO(udp->udp_bound_v6src);
49267c478bd9Sstevel@tonic-gate 				udp->udp_state = TS_UNBND;
49277c478bd9Sstevel@tonic-gate 				udp_bind_hash_remove(udp, B_TRUE);
49287c478bd9Sstevel@tonic-gate 				udp->udp_port = 0;
49297c478bd9Sstevel@tonic-gate 				mutex_exit(&udpf->uf_lock);
49307c478bd9Sstevel@tonic-gate 				if (udp->udp_family == AF_INET6)
49317c478bd9Sstevel@tonic-gate 					(void) udp_build_hdrs(q, udp);
49327c478bd9Sstevel@tonic-gate 				break;
49337c478bd9Sstevel@tonic-gate 			}
49347c478bd9Sstevel@tonic-gate 			default:
49357c478bd9Sstevel@tonic-gate 				break;
49367c478bd9Sstevel@tonic-gate 			}
49377c478bd9Sstevel@tonic-gate 			break;
49387c478bd9Sstevel@tonic-gate 		case T_BIND_ACK:
49397c478bd9Sstevel@tonic-gate 			udp_rput_bind_ack(q, mp);
49407c478bd9Sstevel@tonic-gate 			return;
49417c478bd9Sstevel@tonic-gate 
49427c478bd9Sstevel@tonic-gate 		case T_OPTMGMT_ACK:
49437c478bd9Sstevel@tonic-gate 		case T_OK_ACK:
49447c478bd9Sstevel@tonic-gate 			break;
49457c478bd9Sstevel@tonic-gate 		default:
49467c478bd9Sstevel@tonic-gate 			freemsg(mp);
49477c478bd9Sstevel@tonic-gate 			return;
49487c478bd9Sstevel@tonic-gate 		}
4949ff550d0eSmasputra 		putnext(UDP_RD(q), mp);
49507c478bd9Sstevel@tonic-gate 		return;
49517c478bd9Sstevel@tonic-gate 	}
49527c478bd9Sstevel@tonic-gate 
49537c478bd9Sstevel@tonic-gate 	/*
49547c478bd9Sstevel@tonic-gate 	 * This is the inbound data path.
49557c478bd9Sstevel@tonic-gate 	 * First, we make sure the data contains both IP and UDP headers.
49567c478bd9Sstevel@tonic-gate 	 *
49577c478bd9Sstevel@tonic-gate 	 * This handle IPv4 packets for only AF_INET sockets.
49587c478bd9Sstevel@tonic-gate 	 * AF_INET6 sockets can never access udp_ip_rcv_options thus there
49597c478bd9Sstevel@tonic-gate 	 * is no need saving the options.
49607c478bd9Sstevel@tonic-gate 	 */
49617c478bd9Sstevel@tonic-gate 	ASSERT(IPH_HDR_VERSION((ipha_t *)rptr) == IPV4_VERSION);
49627c478bd9Sstevel@tonic-gate 	hdr_length = IPH_HDR_LENGTH(rptr) + UDPH_SIZE;
49637c478bd9Sstevel@tonic-gate 	if (mp->b_wptr - rptr < hdr_length) {
49647c478bd9Sstevel@tonic-gate 		if (!pullupmsg(mp, hdr_length)) {
49657c478bd9Sstevel@tonic-gate 			freemsg(mp);
49667c478bd9Sstevel@tonic-gate 			if (options_mp != NULL)
49677c478bd9Sstevel@tonic-gate 				freeb(options_mp);
49687c478bd9Sstevel@tonic-gate 			BUMP_MIB(&udp_mib, udpInErrors);
49697c478bd9Sstevel@tonic-gate 			TRACE_2(TR_FAC_UDP, TR_UDP_RPUT_END,
49707c478bd9Sstevel@tonic-gate 			    "udp_rput_other_end: q %p (%S)", q, "hdrshort");
49717c478bd9Sstevel@tonic-gate 			BUMP_MIB(&udp_mib, udpInErrors);
49727c478bd9Sstevel@tonic-gate 			return;
49737c478bd9Sstevel@tonic-gate 		}
49747c478bd9Sstevel@tonic-gate 		rptr = mp->b_rptr;
49757c478bd9Sstevel@tonic-gate 	}
49767c478bd9Sstevel@tonic-gate 	/* Walk past the headers. */
49777c478bd9Sstevel@tonic-gate 	new_rptr = rptr + hdr_length;
49787c478bd9Sstevel@tonic-gate 	if (!udp->udp_rcvhdr)
49797c478bd9Sstevel@tonic-gate 		mp->b_rptr = new_rptr;
49807c478bd9Sstevel@tonic-gate 
49817c478bd9Sstevel@tonic-gate 	/* Save the options if any */
49827c478bd9Sstevel@tonic-gate 	opt_len = hdr_length - (IP_SIMPLE_HDR_LENGTH + UDPH_SIZE);
49837c478bd9Sstevel@tonic-gate 	if (opt_len > 0) {
49847c478bd9Sstevel@tonic-gate 		if (opt_len > udp->udp_ip_rcv_options_len) {
49857c478bd9Sstevel@tonic-gate 			if (udp->udp_ip_rcv_options_len)
49867c478bd9Sstevel@tonic-gate 				mi_free((char *)udp->udp_ip_rcv_options);
49877c478bd9Sstevel@tonic-gate 			udp->udp_ip_rcv_options_len = 0;
49887c478bd9Sstevel@tonic-gate 			udp->udp_ip_rcv_options =
49897c478bd9Sstevel@tonic-gate 			    (uchar_t *)mi_alloc(opt_len, BPRI_HI);
49907c478bd9Sstevel@tonic-gate 			if (udp->udp_ip_rcv_options)
49917c478bd9Sstevel@tonic-gate 				udp->udp_ip_rcv_options_len = opt_len;
49927c478bd9Sstevel@tonic-gate 		}
49937c478bd9Sstevel@tonic-gate 		if (udp->udp_ip_rcv_options_len) {
49947c478bd9Sstevel@tonic-gate 			bcopy(rptr + IP_SIMPLE_HDR_LENGTH,
49957c478bd9Sstevel@tonic-gate 			    udp->udp_ip_rcv_options, opt_len);
49967c478bd9Sstevel@tonic-gate 			/* Adjust length if we are resusing the space */
49977c478bd9Sstevel@tonic-gate 			udp->udp_ip_rcv_options_len = opt_len;
49987c478bd9Sstevel@tonic-gate 		}
49997c478bd9Sstevel@tonic-gate 	} else if (udp->udp_ip_rcv_options_len) {
50007c478bd9Sstevel@tonic-gate 		mi_free((char *)udp->udp_ip_rcv_options);
50017c478bd9Sstevel@tonic-gate 		udp->udp_ip_rcv_options = NULL;
50027c478bd9Sstevel@tonic-gate 		udp->udp_ip_rcv_options_len = 0;
50037c478bd9Sstevel@tonic-gate 	}
50047c478bd9Sstevel@tonic-gate 
50057c478bd9Sstevel@tonic-gate 	/*
50067c478bd9Sstevel@tonic-gate 	 * Normally only send up the address.
50077c478bd9Sstevel@tonic-gate 	 * If IP_RECVDSTADDR is set we include the destination IP
50087c478bd9Sstevel@tonic-gate 	 * address as an option. With IP_RECVOPTS we include all
50097c478bd9Sstevel@tonic-gate 	 * the IP options.
50107c478bd9Sstevel@tonic-gate 	 */
50117c478bd9Sstevel@tonic-gate 	udi_size = sizeof (struct T_unitdata_ind) + sizeof (sin_t);
50127c478bd9Sstevel@tonic-gate 	if (udp->udp_recvdstaddr) {
50137c478bd9Sstevel@tonic-gate 		udi_size += sizeof (struct T_opthdr) + sizeof (struct in_addr);
5014ff550d0eSmasputra 		UDP_STAT(udp_in_recvdstaddr);
50157c478bd9Sstevel@tonic-gate 	}
5016ff550d0eSmasputra 	if (udp->udp_recvopts && opt_len > 0) {
50177c478bd9Sstevel@tonic-gate 		udi_size += sizeof (struct T_opthdr) + opt_len;
5018ff550d0eSmasputra 		UDP_STAT(udp_in_recvopts);
5019ff550d0eSmasputra 	}
50207c478bd9Sstevel@tonic-gate 
50217c478bd9Sstevel@tonic-gate 	/*
50227c478bd9Sstevel@tonic-gate 	 * If the IP_RECVSLLA or the IP_RECVIF is set then allocate
50237c478bd9Sstevel@tonic-gate 	 * space accordingly
50247c478bd9Sstevel@tonic-gate 	 */
50257c478bd9Sstevel@tonic-gate 	if (udp->udp_recvif && recv_on &&
50267c478bd9Sstevel@tonic-gate 	    (pinfo->in_pkt_flags & IPF_RECVIF)) {
5027ff550d0eSmasputra 		udi_size += sizeof (struct T_opthdr) + sizeof (uint_t);
5028ff550d0eSmasputra 		UDP_STAT(udp_in_recvif);
50297c478bd9Sstevel@tonic-gate 	}
50307c478bd9Sstevel@tonic-gate 
50317c478bd9Sstevel@tonic-gate 	if (udp->udp_recvslla && recv_on &&
50327c478bd9Sstevel@tonic-gate 	    (pinfo->in_pkt_flags & IPF_RECVSLLA)) {
50337c478bd9Sstevel@tonic-gate 		udi_size += sizeof (struct T_opthdr) +
50347c478bd9Sstevel@tonic-gate 		    sizeof (struct sockaddr_dl);
5035ff550d0eSmasputra 		UDP_STAT(udp_in_recvslla);
50367c478bd9Sstevel@tonic-gate 	}
50377c478bd9Sstevel@tonic-gate 
50387c478bd9Sstevel@tonic-gate 	if (udp->udp_recvucred && (cr = DB_CRED(mp)) != NULL) {
50397c478bd9Sstevel@tonic-gate 		udi_size += sizeof (struct T_opthdr) + ucredsize;
50407c478bd9Sstevel@tonic-gate 		cpid = DB_CPID(mp);
5041ff550d0eSmasputra 		UDP_STAT(udp_in_recvucred);
50427c478bd9Sstevel@tonic-gate 	}
50437c478bd9Sstevel@tonic-gate 	/*
50447c478bd9Sstevel@tonic-gate 	 * If IP_RECVTTL is set allocate the appropriate sized buffer
50457c478bd9Sstevel@tonic-gate 	 */
50467c478bd9Sstevel@tonic-gate 	if (udp->udp_recvttl) {
50477c478bd9Sstevel@tonic-gate 		udi_size += sizeof (struct T_opthdr) + sizeof (uint8_t);
5048ff550d0eSmasputra 		UDP_STAT(udp_in_recvttl);
50497c478bd9Sstevel@tonic-gate 	}
50507c478bd9Sstevel@tonic-gate 
50517c478bd9Sstevel@tonic-gate 	/* Allocate a message block for the T_UNITDATA_IND structure. */
50527c478bd9Sstevel@tonic-gate 	mp1 = allocb(udi_size, BPRI_MED);
50537c478bd9Sstevel@tonic-gate 	if (mp1 == NULL) {
50547c478bd9Sstevel@tonic-gate 		freemsg(mp);
50557c478bd9Sstevel@tonic-gate 		if (options_mp != NULL)
50567c478bd9Sstevel@tonic-gate 			freeb(options_mp);
50577c478bd9Sstevel@tonic-gate 		TRACE_2(TR_FAC_UDP, TR_UDP_RPUT_END,
50587c478bd9Sstevel@tonic-gate 			"udp_rput_other_end: q %p (%S)", q, "allocbfail");
50597c478bd9Sstevel@tonic-gate 		BUMP_MIB(&udp_mib, udpInErrors);
50607c478bd9Sstevel@tonic-gate 		return;
50617c478bd9Sstevel@tonic-gate 	}
50627c478bd9Sstevel@tonic-gate 	mp1->b_cont = mp;
50637c478bd9Sstevel@tonic-gate 	mp = mp1;
50647c478bd9Sstevel@tonic-gate 	mp->b_datap->db_type = M_PROTO;
50657c478bd9Sstevel@tonic-gate 	tudi = (struct T_unitdata_ind *)mp->b_rptr;
50667c478bd9Sstevel@tonic-gate 	mp->b_wptr = (uchar_t *)tudi + udi_size;
50677c478bd9Sstevel@tonic-gate 	tudi->PRIM_type = T_UNITDATA_IND;
50687c478bd9Sstevel@tonic-gate 	tudi->SRC_length = sizeof (sin_t);
50697c478bd9Sstevel@tonic-gate 	tudi->SRC_offset = sizeof (struct T_unitdata_ind);
50707c478bd9Sstevel@tonic-gate 	tudi->OPT_offset = sizeof (struct T_unitdata_ind) + sizeof (sin_t);
50717c478bd9Sstevel@tonic-gate 	udi_size -= (sizeof (struct T_unitdata_ind) + sizeof (sin_t));
50727c478bd9Sstevel@tonic-gate 	tudi->OPT_length = udi_size;
50737c478bd9Sstevel@tonic-gate 
50747c478bd9Sstevel@tonic-gate 	sin = (sin_t *)&tudi[1];
50757c478bd9Sstevel@tonic-gate 	sin->sin_addr.s_addr = ((ipha_t *)rptr)->ipha_src;
50767c478bd9Sstevel@tonic-gate 	sin->sin_port =	((in_port_t *)
50777c478bd9Sstevel@tonic-gate 	    new_rptr)[-(UDPH_SIZE/sizeof (in_port_t))];
50787c478bd9Sstevel@tonic-gate 	sin->sin_family = AF_INET;
50797c478bd9Sstevel@tonic-gate 	*(uint32_t *)&sin->sin_zero[0] = 0;
50807c478bd9Sstevel@tonic-gate 	*(uint32_t *)&sin->sin_zero[4] = 0;
50817c478bd9Sstevel@tonic-gate 
50827c478bd9Sstevel@tonic-gate 	/*
50837c478bd9Sstevel@tonic-gate 	 * Add options if IP_RECVDSTADDR, IP_RECVIF, IP_RECVSLLA or
50847c478bd9Sstevel@tonic-gate 	 * IP_RECVTTL has been set.
50857c478bd9Sstevel@tonic-gate 	 */
50867c478bd9Sstevel@tonic-gate 	if (udi_size != 0) {
50877c478bd9Sstevel@tonic-gate 		/*
50887c478bd9Sstevel@tonic-gate 		 * Copy in destination address before options to avoid any
50897c478bd9Sstevel@tonic-gate 		 * padding issues.
50907c478bd9Sstevel@tonic-gate 		 */
50917c478bd9Sstevel@tonic-gate 		char *dstopt;
50927c478bd9Sstevel@tonic-gate 
50937c478bd9Sstevel@tonic-gate 		dstopt = (char *)&sin[1];
50947c478bd9Sstevel@tonic-gate 		if (udp->udp_recvdstaddr) {
50957c478bd9Sstevel@tonic-gate 			struct T_opthdr *toh;
50967c478bd9Sstevel@tonic-gate 			ipaddr_t *dstptr;
50977c478bd9Sstevel@tonic-gate 
50987c478bd9Sstevel@tonic-gate 			toh = (struct T_opthdr *)dstopt;
50997c478bd9Sstevel@tonic-gate 			toh->level = IPPROTO_IP;
51007c478bd9Sstevel@tonic-gate 			toh->name = IP_RECVDSTADDR;
51017c478bd9Sstevel@tonic-gate 			toh->len = sizeof (struct T_opthdr) + sizeof (ipaddr_t);
51027c478bd9Sstevel@tonic-gate 			toh->status = 0;
51037c478bd9Sstevel@tonic-gate 			dstopt += sizeof (struct T_opthdr);
51047c478bd9Sstevel@tonic-gate 			dstptr = (ipaddr_t *)dstopt;
51057c478bd9Sstevel@tonic-gate 			*dstptr = (((ipaddr_t *)rptr)[4]);
51067c478bd9Sstevel@tonic-gate 			dstopt += sizeof (ipaddr_t);
51077c478bd9Sstevel@tonic-gate 			udi_size -= toh->len;
51087c478bd9Sstevel@tonic-gate 		}
51097c478bd9Sstevel@tonic-gate 		if (udp->udp_recvopts && udi_size != 0) {
51107c478bd9Sstevel@tonic-gate 			struct T_opthdr *toh;
51117c478bd9Sstevel@tonic-gate 
51127c478bd9Sstevel@tonic-gate 			toh = (struct T_opthdr *)dstopt;
51137c478bd9Sstevel@tonic-gate 			toh->level = IPPROTO_IP;
51147c478bd9Sstevel@tonic-gate 			toh->name = IP_RECVOPTS;
51157c478bd9Sstevel@tonic-gate 			toh->len = sizeof (struct T_opthdr) + opt_len;
51167c478bd9Sstevel@tonic-gate 			toh->status = 0;
51177c478bd9Sstevel@tonic-gate 			dstopt += sizeof (struct T_opthdr);
51187c478bd9Sstevel@tonic-gate 			bcopy(rptr + IP_SIMPLE_HDR_LENGTH, dstopt, opt_len);
51197c478bd9Sstevel@tonic-gate 			dstopt += opt_len;
51207c478bd9Sstevel@tonic-gate 			udi_size -= toh->len;
51217c478bd9Sstevel@tonic-gate 		}
51227c478bd9Sstevel@tonic-gate 
51237c478bd9Sstevel@tonic-gate 		if (udp->udp_recvslla && recv_on &&
51247c478bd9Sstevel@tonic-gate 		    (pinfo->in_pkt_flags & IPF_RECVSLLA)) {
51257c478bd9Sstevel@tonic-gate 
51267c478bd9Sstevel@tonic-gate 			struct T_opthdr *toh;
51277c478bd9Sstevel@tonic-gate 			struct sockaddr_dl	*dstptr;
51287c478bd9Sstevel@tonic-gate 
51297c478bd9Sstevel@tonic-gate 			toh = (struct T_opthdr *)dstopt;
51307c478bd9Sstevel@tonic-gate 			toh->level = IPPROTO_IP;
51317c478bd9Sstevel@tonic-gate 			toh->name = IP_RECVSLLA;
51327c478bd9Sstevel@tonic-gate 			toh->len = sizeof (struct T_opthdr) +
51337c478bd9Sstevel@tonic-gate 			    sizeof (struct sockaddr_dl);
51347c478bd9Sstevel@tonic-gate 			toh->status = 0;
51357c478bd9Sstevel@tonic-gate 			dstopt += sizeof (struct T_opthdr);
51367c478bd9Sstevel@tonic-gate 			dstptr = (struct sockaddr_dl *)dstopt;
51377c478bd9Sstevel@tonic-gate 			bcopy(&pinfo->in_pkt_slla, dstptr,
51387c478bd9Sstevel@tonic-gate 			    sizeof (struct sockaddr_dl));
51397c478bd9Sstevel@tonic-gate 			dstopt += sizeof (struct sockaddr_dl);
51407c478bd9Sstevel@tonic-gate 			udi_size -= toh->len;
51417c478bd9Sstevel@tonic-gate 		}
51427c478bd9Sstevel@tonic-gate 
51437c478bd9Sstevel@tonic-gate 		if (udp->udp_recvif && recv_on &&
51447c478bd9Sstevel@tonic-gate 		    (pinfo->in_pkt_flags & IPF_RECVIF)) {
51457c478bd9Sstevel@tonic-gate 
51467c478bd9Sstevel@tonic-gate 			struct T_opthdr *toh;
51477c478bd9Sstevel@tonic-gate 			uint_t		*dstptr;
51487c478bd9Sstevel@tonic-gate 
51497c478bd9Sstevel@tonic-gate 			toh = (struct T_opthdr *)dstopt;
51507c478bd9Sstevel@tonic-gate 			toh->level = IPPROTO_IP;
51517c478bd9Sstevel@tonic-gate 			toh->name = IP_RECVIF;
51527c478bd9Sstevel@tonic-gate 			toh->len = sizeof (struct T_opthdr) +
51537c478bd9Sstevel@tonic-gate 			    sizeof (uint_t);
51547c478bd9Sstevel@tonic-gate 			toh->status = 0;
51557c478bd9Sstevel@tonic-gate 			dstopt += sizeof (struct T_opthdr);
51567c478bd9Sstevel@tonic-gate 			dstptr = (uint_t *)dstopt;
51577c478bd9Sstevel@tonic-gate 			*dstptr = pinfo->in_pkt_ifindex;
51587c478bd9Sstevel@tonic-gate 			dstopt += sizeof (uint_t);
51597c478bd9Sstevel@tonic-gate 			udi_size -= toh->len;
51607c478bd9Sstevel@tonic-gate 		}
51617c478bd9Sstevel@tonic-gate 
51627c478bd9Sstevel@tonic-gate 		if (cr != NULL) {
51637c478bd9Sstevel@tonic-gate 			struct T_opthdr *toh;
51647c478bd9Sstevel@tonic-gate 
51657c478bd9Sstevel@tonic-gate 			toh = (struct T_opthdr *)dstopt;
51667c478bd9Sstevel@tonic-gate 			toh->level = SOL_SOCKET;
51677c478bd9Sstevel@tonic-gate 			toh->name = SCM_UCRED;
51687c478bd9Sstevel@tonic-gate 			toh->len = sizeof (struct T_opthdr) + ucredsize;
51697c478bd9Sstevel@tonic-gate 			toh->status = 0;
51707c478bd9Sstevel@tonic-gate 			(void) cred2ucred(cr, cpid, &toh[1]);
51717c478bd9Sstevel@tonic-gate 			dstopt += toh->len;
51727c478bd9Sstevel@tonic-gate 			udi_size -= toh->len;
51737c478bd9Sstevel@tonic-gate 		}
51747c478bd9Sstevel@tonic-gate 
51757c478bd9Sstevel@tonic-gate 		if (udp->udp_recvttl) {
51767c478bd9Sstevel@tonic-gate 			struct	T_opthdr *toh;
51777c478bd9Sstevel@tonic-gate 			uint8_t	*dstptr;
51787c478bd9Sstevel@tonic-gate 
51797c478bd9Sstevel@tonic-gate 			toh = (struct T_opthdr *)dstopt;
51807c478bd9Sstevel@tonic-gate 			toh->level = IPPROTO_IP;
51817c478bd9Sstevel@tonic-gate 			toh->name = IP_RECVTTL;
51827c478bd9Sstevel@tonic-gate 			toh->len = sizeof (struct T_opthdr) +
51837c478bd9Sstevel@tonic-gate 			    sizeof (uint8_t);
51847c478bd9Sstevel@tonic-gate 			toh->status = 0;
51857c478bd9Sstevel@tonic-gate 			dstopt += sizeof (struct T_opthdr);
51867c478bd9Sstevel@tonic-gate 			dstptr = (uint8_t *)dstopt;
51877c478bd9Sstevel@tonic-gate 			*dstptr = ((ipha_t *)rptr)->ipha_ttl;
51887c478bd9Sstevel@tonic-gate 			dstopt += sizeof (uint8_t);
51897c478bd9Sstevel@tonic-gate 			udi_size -= toh->len;
51907c478bd9Sstevel@tonic-gate 		}
51917c478bd9Sstevel@tonic-gate 
51927c478bd9Sstevel@tonic-gate 		ASSERT(udi_size == 0);	/* "Consumed" all of allocated space */
51937c478bd9Sstevel@tonic-gate 	}
51947c478bd9Sstevel@tonic-gate 	BUMP_MIB(&udp_mib, udpInDatagrams);
51957c478bd9Sstevel@tonic-gate 	TRACE_2(TR_FAC_UDP, TR_UDP_RPUT_END,
51967c478bd9Sstevel@tonic-gate 	    "udp_rput_other_end: q %p (%S)", q, "end");
51977c478bd9Sstevel@tonic-gate 	if (options_mp != NULL)
51987c478bd9Sstevel@tonic-gate 		freeb(options_mp);
5199ff550d0eSmasputra 
5200ff550d0eSmasputra 	if (udp->udp_direct_sockfs) {
5201ff550d0eSmasputra 		/*
5202ff550d0eSmasputra 		 * There is nothing above us except for the stream head;
5203ff550d0eSmasputra 		 * use the read-side synchronous stream interface in
5204ff550d0eSmasputra 		 * order to reduce the time spent in interrupt thread.
5205ff550d0eSmasputra 		 */
5206ff550d0eSmasputra 		ASSERT(udp->udp_issocket);
5207ff550d0eSmasputra 		udp_rcv_enqueue(UDP_RD(q), udp, mp, msgdsize(mp));
5208ff550d0eSmasputra 	} else {
5209ff550d0eSmasputra 		/*
5210ff550d0eSmasputra 		 * Use regular STREAMS interface to pass data upstream
5211ff550d0eSmasputra 		 * if this is not a socket endpoint, or if we have
5212ff550d0eSmasputra 		 * switched over to the slow mode due to sockmod being
5213ff550d0eSmasputra 		 * popped or a module being pushed on top of us.
5214ff550d0eSmasputra 		 */
5215ff550d0eSmasputra 		putnext(UDP_RD(q), mp);
5216ff550d0eSmasputra 	}
5217ff550d0eSmasputra }
5218ff550d0eSmasputra 
5219ff550d0eSmasputra /* ARGSUSED */
5220ff550d0eSmasputra static void
5221ff550d0eSmasputra udp_rput_other_wrapper(void *arg, mblk_t *mp, void *arg2)
5222ff550d0eSmasputra {
5223ff550d0eSmasputra 	conn_t *connp = arg;
5224ff550d0eSmasputra 
5225ff550d0eSmasputra 	udp_rput_other(connp->conn_rq, mp);
5226ff550d0eSmasputra 	udp_exit(connp);
52277c478bd9Sstevel@tonic-gate }
52287c478bd9Sstevel@tonic-gate 
52297c478bd9Sstevel@tonic-gate /*
52307c478bd9Sstevel@tonic-gate  * Process a T_BIND_ACK
52317c478bd9Sstevel@tonic-gate  */
52327c478bd9Sstevel@tonic-gate static void
52337c478bd9Sstevel@tonic-gate udp_rput_bind_ack(queue_t *q, mblk_t *mp)
52347c478bd9Sstevel@tonic-gate {
5235ff550d0eSmasputra 	udp_t	*udp = Q_TO_UDP(q);
52367c478bd9Sstevel@tonic-gate 	mblk_t	*mp1;
52377c478bd9Sstevel@tonic-gate 	ire_t	*ire;
52387c478bd9Sstevel@tonic-gate 	struct T_bind_ack *tba;
52397c478bd9Sstevel@tonic-gate 	uchar_t *addrp;
52407c478bd9Sstevel@tonic-gate 	ipa_conn_t	*ac;
52417c478bd9Sstevel@tonic-gate 	ipa6_conn_t	*ac6;
52427c478bd9Sstevel@tonic-gate 
52437c478bd9Sstevel@tonic-gate 	if (udp->udp_discon_pending)
52447c478bd9Sstevel@tonic-gate 		udp->udp_discon_pending = 0;
52457c478bd9Sstevel@tonic-gate 
52467c478bd9Sstevel@tonic-gate 	/*
52477c478bd9Sstevel@tonic-gate 	 * If a broadcast/multicast address was bound set
52487c478bd9Sstevel@tonic-gate 	 * the source address to 0.
52497c478bd9Sstevel@tonic-gate 	 * This ensures no datagrams with broadcast address
52507c478bd9Sstevel@tonic-gate 	 * as source address are emitted (which would violate
52517c478bd9Sstevel@tonic-gate 	 * RFC1122 - Hosts requirements)
52527c478bd9Sstevel@tonic-gate 	 *
52537c478bd9Sstevel@tonic-gate 	 * Note that when connecting the returned IRE is
52547c478bd9Sstevel@tonic-gate 	 * for the destination address and we only perform
52557c478bd9Sstevel@tonic-gate 	 * the broadcast check for the source address (it
52567c478bd9Sstevel@tonic-gate 	 * is OK to connect to a broadcast/multicast address.)
52577c478bd9Sstevel@tonic-gate 	 */
52587c478bd9Sstevel@tonic-gate 	mp1 = mp->b_cont;
52597c478bd9Sstevel@tonic-gate 	if (mp1 != NULL && mp1->b_datap->db_type == IRE_DB_TYPE) {
52607c478bd9Sstevel@tonic-gate 		ire = (ire_t *)mp1->b_rptr;
52617c478bd9Sstevel@tonic-gate 
52627c478bd9Sstevel@tonic-gate 		/*
52637c478bd9Sstevel@tonic-gate 		 * Note: we get IRE_BROADCAST for IPv6 to "mark" a multicast
52647c478bd9Sstevel@tonic-gate 		 * local address.
52657c478bd9Sstevel@tonic-gate 		 */
52667c478bd9Sstevel@tonic-gate 		if (ire->ire_type == IRE_BROADCAST &&
52677c478bd9Sstevel@tonic-gate 		    udp->udp_state != TS_DATA_XFER) {
52687c478bd9Sstevel@tonic-gate 			/* This was just a local bind to a broadcast addr */
52697c478bd9Sstevel@tonic-gate 			V6_SET_ZERO(udp->udp_v6src);
52707c478bd9Sstevel@tonic-gate 			if (udp->udp_family == AF_INET6)
52717c478bd9Sstevel@tonic-gate 				(void) udp_build_hdrs(q, udp);
52727c478bd9Sstevel@tonic-gate 		} else if (V6_OR_V4_INADDR_ANY(udp->udp_v6src)) {
52737c478bd9Sstevel@tonic-gate 			/*
52747c478bd9Sstevel@tonic-gate 			 * Local address not yet set - pick it from the
52757c478bd9Sstevel@tonic-gate 			 * T_bind_ack
52767c478bd9Sstevel@tonic-gate 			 */
52777c478bd9Sstevel@tonic-gate 			tba = (struct T_bind_ack *)mp->b_rptr;
52787c478bd9Sstevel@tonic-gate 			addrp = &mp->b_rptr[tba->ADDR_offset];
52797c478bd9Sstevel@tonic-gate 			switch (udp->udp_family) {
52807c478bd9Sstevel@tonic-gate 			case AF_INET:
52817c478bd9Sstevel@tonic-gate 				if (tba->ADDR_length == sizeof (ipa_conn_t)) {
52827c478bd9Sstevel@tonic-gate 					ac = (ipa_conn_t *)addrp;
52837c478bd9Sstevel@tonic-gate 				} else {
52847c478bd9Sstevel@tonic-gate 					ASSERT(tba->ADDR_length ==
52857c478bd9Sstevel@tonic-gate 					    sizeof (ipa_conn_x_t));
52867c478bd9Sstevel@tonic-gate 					ac = &((ipa_conn_x_t *)addrp)->acx_conn;
52877c478bd9Sstevel@tonic-gate 				}
52887c478bd9Sstevel@tonic-gate 				IN6_IPADDR_TO_V4MAPPED(ac->ac_laddr,
52897c478bd9Sstevel@tonic-gate 				    &udp->udp_v6src);
52907c478bd9Sstevel@tonic-gate 				break;
52917c478bd9Sstevel@tonic-gate 			case AF_INET6:
52927c478bd9Sstevel@tonic-gate 				if (tba->ADDR_length == sizeof (ipa6_conn_t)) {
52937c478bd9Sstevel@tonic-gate 					ac6 = (ipa6_conn_t *)addrp;
52947c478bd9Sstevel@tonic-gate 				} else {
52957c478bd9Sstevel@tonic-gate 					ASSERT(tba->ADDR_length ==
52967c478bd9Sstevel@tonic-gate 					    sizeof (ipa6_conn_x_t));
52977c478bd9Sstevel@tonic-gate 					ac6 = &((ipa6_conn_x_t *)
52987c478bd9Sstevel@tonic-gate 					    addrp)->ac6x_conn;
52997c478bd9Sstevel@tonic-gate 				}
53007c478bd9Sstevel@tonic-gate 				udp->udp_v6src = ac6->ac6_laddr;
53017c478bd9Sstevel@tonic-gate 				(void) udp_build_hdrs(q, udp);
53027c478bd9Sstevel@tonic-gate 				break;
53037c478bd9Sstevel@tonic-gate 			}
53047c478bd9Sstevel@tonic-gate 		}
53057c478bd9Sstevel@tonic-gate 		mp1 = mp1->b_cont;
53067c478bd9Sstevel@tonic-gate 	}
53077c478bd9Sstevel@tonic-gate 	/*
53087c478bd9Sstevel@tonic-gate 	 * Look for one or more appended ACK message added by
53097c478bd9Sstevel@tonic-gate 	 * udp_connect or udp_disconnect.
53107c478bd9Sstevel@tonic-gate 	 * If none found just send up the T_BIND_ACK.
53117c478bd9Sstevel@tonic-gate 	 * udp_connect has appended a T_OK_ACK and a T_CONN_CON.
53127c478bd9Sstevel@tonic-gate 	 * udp_disconnect has appended a T_OK_ACK.
53137c478bd9Sstevel@tonic-gate 	 */
53147c478bd9Sstevel@tonic-gate 	if (mp1 != NULL) {
53157c478bd9Sstevel@tonic-gate 		if (mp->b_cont == mp1)
53167c478bd9Sstevel@tonic-gate 			mp->b_cont = NULL;
53177c478bd9Sstevel@tonic-gate 		else {
53187c478bd9Sstevel@tonic-gate 			ASSERT(mp->b_cont->b_cont == mp1);
53197c478bd9Sstevel@tonic-gate 			mp->b_cont->b_cont = NULL;
53207c478bd9Sstevel@tonic-gate 		}
53217c478bd9Sstevel@tonic-gate 		freemsg(mp);
53227c478bd9Sstevel@tonic-gate 		mp = mp1;
53237c478bd9Sstevel@tonic-gate 		while (mp != NULL) {
53247c478bd9Sstevel@tonic-gate 			mp1 = mp->b_cont;
53257c478bd9Sstevel@tonic-gate 			mp->b_cont = NULL;
5326ff550d0eSmasputra 			putnext(UDP_RD(q), mp);
53277c478bd9Sstevel@tonic-gate 			mp = mp1;
53287c478bd9Sstevel@tonic-gate 		}
53297c478bd9Sstevel@tonic-gate 		return;
53307c478bd9Sstevel@tonic-gate 	}
53317c478bd9Sstevel@tonic-gate 	freemsg(mp->b_cont);
53327c478bd9Sstevel@tonic-gate 	mp->b_cont = NULL;
5333ff550d0eSmasputra 	putnext(UDP_RD(q), mp);
53347c478bd9Sstevel@tonic-gate }
53357c478bd9Sstevel@tonic-gate 
53367c478bd9Sstevel@tonic-gate /*
53377c478bd9Sstevel@tonic-gate  * return SNMP stuff in buffer in mpdata
53387c478bd9Sstevel@tonic-gate  */
5339ff550d0eSmasputra int
53407c478bd9Sstevel@tonic-gate udp_snmp_get(queue_t *q, mblk_t *mpctl)
53417c478bd9Sstevel@tonic-gate {
53427c478bd9Sstevel@tonic-gate 	mblk_t			*mpdata;
53437c478bd9Sstevel@tonic-gate 	mblk_t			*mp_conn_ctl;
53447c478bd9Sstevel@tonic-gate 	mblk_t			*mp6_conn_ctl;
53457c478bd9Sstevel@tonic-gate 	mblk_t			*mp_conn_data;
53467c478bd9Sstevel@tonic-gate 	mblk_t			*mp6_conn_data;
53477c478bd9Sstevel@tonic-gate 	mblk_t			*mp_conn_tail = NULL;
53487c478bd9Sstevel@tonic-gate 	mblk_t			*mp6_conn_tail = NULL;
53497c478bd9Sstevel@tonic-gate 	struct opthdr		*optp;
53507c478bd9Sstevel@tonic-gate 	mib2_udpEntry_t		ude;
53517c478bd9Sstevel@tonic-gate 	mib2_udp6Entry_t	ude6;
53527c478bd9Sstevel@tonic-gate 	int			state;
53537c478bd9Sstevel@tonic-gate 	zoneid_t		zoneid;
5354ff550d0eSmasputra 	int			i;
5355ff550d0eSmasputra 	connf_t			*connfp;
5356ff550d0eSmasputra 	conn_t			*connp = Q_TO_CONN(q);
5357ff550d0eSmasputra 	udp_t			*udp = connp->conn_udp;
53587c478bd9Sstevel@tonic-gate 
53597c478bd9Sstevel@tonic-gate 	if (mpctl == NULL ||
53607c478bd9Sstevel@tonic-gate 	    (mpdata = mpctl->b_cont) == NULL ||
53617c478bd9Sstevel@tonic-gate 	    (mp_conn_ctl = copymsg(mpctl)) == NULL ||
53627c478bd9Sstevel@tonic-gate 	    (mp6_conn_ctl = copymsg(mpctl)) == NULL) {
53637c478bd9Sstevel@tonic-gate 		freemsg(mp_conn_ctl);
53647c478bd9Sstevel@tonic-gate 		return (0);
53657c478bd9Sstevel@tonic-gate 	}
53667c478bd9Sstevel@tonic-gate 
53677c478bd9Sstevel@tonic-gate 	mp_conn_data = mp_conn_ctl->b_cont;
53687c478bd9Sstevel@tonic-gate 	mp6_conn_data = mp6_conn_ctl->b_cont;
53697c478bd9Sstevel@tonic-gate 
5370ff550d0eSmasputra 	zoneid = connp->conn_zoneid;
53717c478bd9Sstevel@tonic-gate 
53727c478bd9Sstevel@tonic-gate 	/* fixed length structure for IPv4 and IPv6 counters */
53737c478bd9Sstevel@tonic-gate 	SET_MIB(udp_mib.udpEntrySize, sizeof (mib2_udpEntry_t));
53747c478bd9Sstevel@tonic-gate 	SET_MIB(udp_mib.udp6EntrySize, sizeof (mib2_udp6Entry_t));
53757c478bd9Sstevel@tonic-gate 	optp = (struct opthdr *)&mpctl->b_rptr[sizeof (struct T_optmgmt_ack)];
53767c478bd9Sstevel@tonic-gate 	optp->level = MIB2_UDP;
53777c478bd9Sstevel@tonic-gate 	optp->name = 0;
53787c478bd9Sstevel@tonic-gate 	(void) snmp_append_data(mpdata, (char *)&udp_mib, sizeof (udp_mib));
53797c478bd9Sstevel@tonic-gate 	optp->len = msgdsize(mpdata);
53807c478bd9Sstevel@tonic-gate 	qreply(q, mpctl);
53817c478bd9Sstevel@tonic-gate 
5382ff550d0eSmasputra 	for (i = 0; i < CONN_G_HASH_SIZE; i++) {
5383ff550d0eSmasputra 		connfp = &ipcl_globalhash_fanout[i];
5384ff550d0eSmasputra 		connp = NULL;
53857c478bd9Sstevel@tonic-gate 
5386ff550d0eSmasputra 		while ((connp = ipcl_get_next_conn(connfp, connp,
5387ff550d0eSmasputra 		    IPCL_UDP))) {
5388ff550d0eSmasputra 			udp = connp->conn_udp;
5389ff550d0eSmasputra 			if (zoneid != connp->conn_zoneid)
5390ff550d0eSmasputra 				continue;
53917c478bd9Sstevel@tonic-gate 
5392ff550d0eSmasputra 			/*
5393ff550d0eSmasputra 			 * Note that the port numbers are sent in
5394ff550d0eSmasputra 			 * host byte order
5395ff550d0eSmasputra 			 */
53967c478bd9Sstevel@tonic-gate 
5397ff550d0eSmasputra 			if (udp->udp_state == TS_UNBND)
5398ff550d0eSmasputra 				state = MIB2_UDP_unbound;
5399ff550d0eSmasputra 			else if (udp->udp_state == TS_IDLE)
5400ff550d0eSmasputra 				state = MIB2_UDP_idle;
5401ff550d0eSmasputra 			else if (udp->udp_state == TS_DATA_XFER)
5402ff550d0eSmasputra 				state = MIB2_UDP_connected;
5403ff550d0eSmasputra 			else
5404ff550d0eSmasputra 				state = MIB2_UDP_unknown;
54057c478bd9Sstevel@tonic-gate 
5406ff550d0eSmasputra 			/*
5407ff550d0eSmasputra 			 * Create an IPv4 table entry for IPv4 entries and also
5408ff550d0eSmasputra 			 * any IPv6 entries which are bound to in6addr_any
5409ff550d0eSmasputra 			 * (i.e. anything a IPv4 peer could connect/send to).
5410ff550d0eSmasputra 			 */
5411ff550d0eSmasputra 			if (udp->udp_ipversion == IPV4_VERSION ||
5412ff550d0eSmasputra 			    (udp->udp_state <= TS_IDLE &&
5413ff550d0eSmasputra 			    IN6_IS_ADDR_UNSPECIFIED(&udp->udp_v6src))) {
5414ff550d0eSmasputra 				ude.udpEntryInfo.ue_state = state;
54157c478bd9Sstevel@tonic-gate 				/*
5416ff550d0eSmasputra 				 * If in6addr_any this will set it to
5417ff550d0eSmasputra 				 * INADDR_ANY
54187c478bd9Sstevel@tonic-gate 				 */
5419ff550d0eSmasputra 				ude.udpLocalAddress =
5420ff550d0eSmasputra 				    V4_PART_OF_V6(udp->udp_v6src);
5421ff550d0eSmasputra 				ude.udpLocalPort = ntohs(udp->udp_port);
5422ff550d0eSmasputra 				if (udp->udp_state == TS_DATA_XFER) {
5423ff550d0eSmasputra 					/*
5424ff550d0eSmasputra 					 * Can potentially get here for
5425ff550d0eSmasputra 					 * v6 socket if another process
5426ff550d0eSmasputra 					 * (say, ping) has just done a
5427ff550d0eSmasputra 					 * sendto(), changing the state
5428ff550d0eSmasputra 					 * from the TS_IDLE above to
5429ff550d0eSmasputra 					 * TS_DATA_XFER by the time we hit
5430ff550d0eSmasputra 					 * this part of the code.
5431ff550d0eSmasputra 					 */
5432ff550d0eSmasputra 					ude.udpEntryInfo.ue_RemoteAddress =
5433ff550d0eSmasputra 					    V4_PART_OF_V6(udp->udp_v6dst);
5434ff550d0eSmasputra 					ude.udpEntryInfo.ue_RemotePort =
5435ff550d0eSmasputra 					    ntohs(udp->udp_dstport);
5436ff550d0eSmasputra 				} else {
5437ff550d0eSmasputra 					ude.udpEntryInfo.ue_RemoteAddress = 0;
5438ff550d0eSmasputra 					ude.udpEntryInfo.ue_RemotePort = 0;
5439ff550d0eSmasputra 				}
5440ff550d0eSmasputra 				(void) snmp_append_data2(mp_conn_data,
5441ff550d0eSmasputra 				    &mp_conn_tail, (char *)&ude, sizeof (ude));
54427c478bd9Sstevel@tonic-gate 			}
5443ff550d0eSmasputra 			if (udp->udp_ipversion == IPV6_VERSION) {
5444ff550d0eSmasputra 				ude6.udp6EntryInfo.ue_state  = state;
5445ff550d0eSmasputra 				ude6.udp6LocalAddress = udp->udp_v6src;
5446ff550d0eSmasputra 				ude6.udp6LocalPort = ntohs(udp->udp_port);
5447ff550d0eSmasputra 				ude6.udp6IfIndex = udp->udp_bound_if;
5448ff550d0eSmasputra 				if (udp->udp_state == TS_DATA_XFER) {
5449ff550d0eSmasputra 					ude6.udp6EntryInfo.ue_RemoteAddress =
5450ff550d0eSmasputra 					    udp->udp_v6dst;
5451ff550d0eSmasputra 					ude6.udp6EntryInfo.ue_RemotePort =
5452ff550d0eSmasputra 					    ntohs(udp->udp_dstport);
5453ff550d0eSmasputra 				} else {
5454ff550d0eSmasputra 					ude6.udp6EntryInfo.ue_RemoteAddress =
5455ff550d0eSmasputra 					    sin6_null.sin6_addr;
5456ff550d0eSmasputra 					ude6.udp6EntryInfo.ue_RemotePort = 0;
5457ff550d0eSmasputra 				}
5458ff550d0eSmasputra 				(void) snmp_append_data2(mp6_conn_data,
5459ff550d0eSmasputra 				    &mp6_conn_tail, (char *)&ude6,
5460ff550d0eSmasputra 				    sizeof (ude6));
54617c478bd9Sstevel@tonic-gate 			}
54627c478bd9Sstevel@tonic-gate 		}
54637c478bd9Sstevel@tonic-gate 	}
54647c478bd9Sstevel@tonic-gate 
54657c478bd9Sstevel@tonic-gate 	/* IPv4 UDP endpoints */
54667c478bd9Sstevel@tonic-gate 	optp = (struct opthdr *)&mp_conn_ctl->b_rptr[
54677c478bd9Sstevel@tonic-gate 	    sizeof (struct T_optmgmt_ack)];
54687c478bd9Sstevel@tonic-gate 	optp->level = MIB2_UDP;
54697c478bd9Sstevel@tonic-gate 	optp->name = MIB2_UDP_ENTRY;
54707c478bd9Sstevel@tonic-gate 	optp->len = msgdsize(mp_conn_data);
54717c478bd9Sstevel@tonic-gate 	qreply(q, mp_conn_ctl);
54727c478bd9Sstevel@tonic-gate 
54737c478bd9Sstevel@tonic-gate 	/* IPv6 UDP endpoints */
54747c478bd9Sstevel@tonic-gate 	optp = (struct opthdr *)&mp6_conn_ctl->b_rptr[
54757c478bd9Sstevel@tonic-gate 	    sizeof (struct T_optmgmt_ack)];
54767c478bd9Sstevel@tonic-gate 	optp->level = MIB2_UDP6;
54777c478bd9Sstevel@tonic-gate 	optp->name = MIB2_UDP6_ENTRY;
54787c478bd9Sstevel@tonic-gate 	optp->len = msgdsize(mp6_conn_data);
54797c478bd9Sstevel@tonic-gate 	qreply(q, mp6_conn_ctl);
54807c478bd9Sstevel@tonic-gate 
54817c478bd9Sstevel@tonic-gate 	return (1);
54827c478bd9Sstevel@tonic-gate }
54837c478bd9Sstevel@tonic-gate 
54847c478bd9Sstevel@tonic-gate /*
54857c478bd9Sstevel@tonic-gate  * Return 0 if invalid set request, 1 otherwise, including non-udp requests.
54867c478bd9Sstevel@tonic-gate  * NOTE: Per MIB-II, UDP has no writable data.
54877c478bd9Sstevel@tonic-gate  * TODO:  If this ever actually tries to set anything, it needs to be
54887c478bd9Sstevel@tonic-gate  * to do the appropriate locking.
54897c478bd9Sstevel@tonic-gate  */
54907c478bd9Sstevel@tonic-gate /* ARGSUSED */
5491ff550d0eSmasputra int
54927c478bd9Sstevel@tonic-gate udp_snmp_set(queue_t *q, t_scalar_t level, t_scalar_t name,
54937c478bd9Sstevel@tonic-gate     uchar_t *ptr, int len)
54947c478bd9Sstevel@tonic-gate {
54957c478bd9Sstevel@tonic-gate 	switch (level) {
54967c478bd9Sstevel@tonic-gate 	case MIB2_UDP:
54977c478bd9Sstevel@tonic-gate 		return (0);
54987c478bd9Sstevel@tonic-gate 	default:
54997c478bd9Sstevel@tonic-gate 		return (1);
55007c478bd9Sstevel@tonic-gate 	}
55017c478bd9Sstevel@tonic-gate }
55027c478bd9Sstevel@tonic-gate 
55037c478bd9Sstevel@tonic-gate static void
55047c478bd9Sstevel@tonic-gate udp_report_item(mblk_t *mp, udp_t *udp)
55057c478bd9Sstevel@tonic-gate {
55067c478bd9Sstevel@tonic-gate 	char *state;
55077c478bd9Sstevel@tonic-gate 	char addrbuf1[INET6_ADDRSTRLEN];
55087c478bd9Sstevel@tonic-gate 	char addrbuf2[INET6_ADDRSTRLEN];
55097c478bd9Sstevel@tonic-gate 	uint_t print_len, buf_len;
55107c478bd9Sstevel@tonic-gate 
55117c478bd9Sstevel@tonic-gate 	buf_len = mp->b_datap->db_lim - mp->b_wptr;
55127c478bd9Sstevel@tonic-gate 	ASSERT(buf_len >= 0);
55137c478bd9Sstevel@tonic-gate 	if (buf_len == 0)
55147c478bd9Sstevel@tonic-gate 		return;
55157c478bd9Sstevel@tonic-gate 
55167c478bd9Sstevel@tonic-gate 	if (udp->udp_state == TS_UNBND)
55177c478bd9Sstevel@tonic-gate 		state = "UNBOUND";
55187c478bd9Sstevel@tonic-gate 	else if (udp->udp_state == TS_IDLE)
55197c478bd9Sstevel@tonic-gate 		state = "IDLE";
55207c478bd9Sstevel@tonic-gate 	else if (udp->udp_state == TS_DATA_XFER)
55217c478bd9Sstevel@tonic-gate 		state = "CONNECTED";
55227c478bd9Sstevel@tonic-gate 	else
55237c478bd9Sstevel@tonic-gate 		state = "UnkState";
55247c478bd9Sstevel@tonic-gate 	print_len = snprintf((char *)mp->b_wptr, buf_len,
55257c478bd9Sstevel@tonic-gate 	    MI_COL_PTRFMT_STR "%4d %5u %s %s %5u %s\n",
5526ff550d0eSmasputra 	    (void *)udp, udp->udp_connp->conn_zoneid, ntohs(udp->udp_port),
55277c478bd9Sstevel@tonic-gate 	    inet_ntop(AF_INET6, &udp->udp_v6src,
55287c478bd9Sstevel@tonic-gate 		addrbuf1, sizeof (addrbuf1)),
55297c478bd9Sstevel@tonic-gate 	    inet_ntop(AF_INET6, &udp->udp_v6dst,
55307c478bd9Sstevel@tonic-gate 		addrbuf2, sizeof (addrbuf2)),
55317c478bd9Sstevel@tonic-gate 	    ntohs(udp->udp_dstport), state);
55327c478bd9Sstevel@tonic-gate 	if (print_len < buf_len) {
55337c478bd9Sstevel@tonic-gate 		mp->b_wptr += print_len;
55347c478bd9Sstevel@tonic-gate 	} else {
55357c478bd9Sstevel@tonic-gate 		mp->b_wptr += buf_len;
55367c478bd9Sstevel@tonic-gate 	}
55377c478bd9Sstevel@tonic-gate }
55387c478bd9Sstevel@tonic-gate 
55397c478bd9Sstevel@tonic-gate /* Report for ndd "udp_status" */
55407c478bd9Sstevel@tonic-gate /* ARGSUSED */
55417c478bd9Sstevel@tonic-gate static int
55427c478bd9Sstevel@tonic-gate udp_status_report(queue_t *q, mblk_t *mp, caddr_t cp, cred_t *cr)
55437c478bd9Sstevel@tonic-gate {
55447c478bd9Sstevel@tonic-gate 	zoneid_t zoneid;
5545ff550d0eSmasputra 	connf_t	*connfp;
5546ff550d0eSmasputra 	conn_t	*connp = Q_TO_CONN(q);
5547ff550d0eSmasputra 	udp_t	*udp = connp->conn_udp;
5548ff550d0eSmasputra 	int	i;
55497c478bd9Sstevel@tonic-gate 
55507c478bd9Sstevel@tonic-gate 	/*
55517c478bd9Sstevel@tonic-gate 	 * Because of the ndd constraint, at most we can have 64K buffer
55527c478bd9Sstevel@tonic-gate 	 * to put in all UDP info.  So to be more efficient, just
55537c478bd9Sstevel@tonic-gate 	 * allocate a 64K buffer here, assuming we need that large buffer.
55547c478bd9Sstevel@tonic-gate 	 * This may be a problem as any user can read udp_status.  Therefore
55557c478bd9Sstevel@tonic-gate 	 * we limit the rate of doing this using udp_ndd_get_info_interval.
55567c478bd9Sstevel@tonic-gate 	 * This should be OK as normal users should not do this too often.
55577c478bd9Sstevel@tonic-gate 	 */
55587c478bd9Sstevel@tonic-gate 	if (cr == NULL || secpolicy_net_config(cr, B_TRUE) != 0) {
55597c478bd9Sstevel@tonic-gate 		if (ddi_get_lbolt() - udp_last_ndd_get_info_time <
55607c478bd9Sstevel@tonic-gate 		    drv_usectohz(udp_ndd_get_info_interval * 1000)) {
55617c478bd9Sstevel@tonic-gate 			(void) mi_mpprintf(mp, NDD_TOO_QUICK_MSG);
55627c478bd9Sstevel@tonic-gate 			return (0);
55637c478bd9Sstevel@tonic-gate 		}
55647c478bd9Sstevel@tonic-gate 	}
55657c478bd9Sstevel@tonic-gate 	if ((mp->b_cont = allocb(ND_MAX_BUF_LEN, BPRI_HI)) == NULL) {
55667c478bd9Sstevel@tonic-gate 		/* The following may work even if we cannot get a large buf. */
55677c478bd9Sstevel@tonic-gate 		(void) mi_mpprintf(mp, NDD_OUT_OF_BUF_MSG);
55687c478bd9Sstevel@tonic-gate 		return (0);
55697c478bd9Sstevel@tonic-gate 	}
55707c478bd9Sstevel@tonic-gate 	(void) mi_mpprintf(mp,
55717c478bd9Sstevel@tonic-gate 	    "UDP     " MI_COL_HDRPAD_STR
55727c478bd9Sstevel@tonic-gate 	/*   12345678[89ABCDEF] */
55737c478bd9Sstevel@tonic-gate 	    " zone lport src addr        dest addr       port  state");
55747c478bd9Sstevel@tonic-gate 	/*    1234 12345 xxx.xxx.xxx.xxx xxx.xxx.xxx.xxx 12345 UNBOUND */
55757c478bd9Sstevel@tonic-gate 
5576ff550d0eSmasputra 	zoneid = connp->conn_zoneid;
55777c478bd9Sstevel@tonic-gate 
5578ff550d0eSmasputra 	for (i = 0; i < CONN_G_HASH_SIZE; i++) {
5579ff550d0eSmasputra 		connfp = &ipcl_globalhash_fanout[i];
5580ff550d0eSmasputra 		connp = NULL;
55817c478bd9Sstevel@tonic-gate 
5582ff550d0eSmasputra 		while ((connp = ipcl_get_next_conn(connfp, connp,
5583ff550d0eSmasputra 		    IPCL_UDP))) {
5584ff550d0eSmasputra 			udp = connp->conn_udp;
5585ff550d0eSmasputra 			if (zoneid != GLOBAL_ZONEID &&
5586ff550d0eSmasputra 			    zoneid != connp->conn_zoneid)
5587ff550d0eSmasputra 				continue;
55887c478bd9Sstevel@tonic-gate 
5589ff550d0eSmasputra 			udp_report_item(mp->b_cont, udp);
5590ff550d0eSmasputra 		}
55917c478bd9Sstevel@tonic-gate 	}
55927c478bd9Sstevel@tonic-gate 	udp_last_ndd_get_info_time = ddi_get_lbolt();
55937c478bd9Sstevel@tonic-gate 	return (0);
55947c478bd9Sstevel@tonic-gate }
55957c478bd9Sstevel@tonic-gate 
55967c478bd9Sstevel@tonic-gate /*
55977c478bd9Sstevel@tonic-gate  * This routine creates a T_UDERROR_IND message and passes it upstream.
55987c478bd9Sstevel@tonic-gate  * The address and options are copied from the T_UNITDATA_REQ message
55997c478bd9Sstevel@tonic-gate  * passed in mp.  This message is freed.
56007c478bd9Sstevel@tonic-gate  */
56017c478bd9Sstevel@tonic-gate static void
5602ff550d0eSmasputra udp_ud_err(queue_t *q, mblk_t *mp, uchar_t *destaddr, t_scalar_t destlen,
5603ff550d0eSmasputra     t_scalar_t err)
56047c478bd9Sstevel@tonic-gate {
5605ff550d0eSmasputra 	struct T_unitdata_req *tudr;
56067c478bd9Sstevel@tonic-gate 	mblk_t	*mp1;
5607ff550d0eSmasputra 	uchar_t	*optaddr;
5608ff550d0eSmasputra 	t_scalar_t optlen;
56097c478bd9Sstevel@tonic-gate 
5610ff550d0eSmasputra 	if (DB_TYPE(mp) == M_DATA) {
5611ff550d0eSmasputra 		ASSERT(destaddr != NULL && destlen != 0);
5612ff550d0eSmasputra 		optaddr = NULL;
5613ff550d0eSmasputra 		optlen = 0;
5614ff550d0eSmasputra 	} else {
5615ff550d0eSmasputra 		if ((mp->b_wptr < mp->b_rptr) ||
5616ff550d0eSmasputra 		    (MBLKL(mp)) < sizeof (struct T_unitdata_req)) {
5617ff550d0eSmasputra 			goto done;
5618ff550d0eSmasputra 		}
5619ff550d0eSmasputra 		tudr = (struct T_unitdata_req *)mp->b_rptr;
5620ff550d0eSmasputra 		destaddr = mp->b_rptr + tudr->DEST_offset;
5621ff550d0eSmasputra 		if (destaddr < mp->b_rptr || destaddr >= mp->b_wptr ||
5622ff550d0eSmasputra 		    destaddr + tudr->DEST_length < mp->b_rptr ||
5623ff550d0eSmasputra 		    destaddr + tudr->DEST_length > mp->b_wptr) {
5624ff550d0eSmasputra 			goto done;
5625ff550d0eSmasputra 		}
5626ff550d0eSmasputra 		optaddr = mp->b_rptr + tudr->OPT_offset;
5627ff550d0eSmasputra 		if (optaddr < mp->b_rptr || optaddr >= mp->b_wptr ||
5628ff550d0eSmasputra 		    optaddr + tudr->OPT_length < mp->b_rptr ||
5629ff550d0eSmasputra 		    optaddr + tudr->OPT_length > mp->b_wptr) {
5630ff550d0eSmasputra 			goto done;
5631ff550d0eSmasputra 		}
5632ff550d0eSmasputra 		destlen = tudr->DEST_length;
5633ff550d0eSmasputra 		optlen = tudr->OPT_length;
56347c478bd9Sstevel@tonic-gate 	}
5635ff550d0eSmasputra 
5636ff550d0eSmasputra 	mp1 = mi_tpi_uderror_ind((char *)destaddr, destlen,
5637ff550d0eSmasputra 	    (char *)optaddr, optlen, err);
5638ff550d0eSmasputra 	if (mp1 != NULL)
5639ff550d0eSmasputra 		putnext(UDP_RD(q), mp1);
56407c478bd9Sstevel@tonic-gate 
56417c478bd9Sstevel@tonic-gate done:
56427c478bd9Sstevel@tonic-gate 	freemsg(mp);
56437c478bd9Sstevel@tonic-gate }
56447c478bd9Sstevel@tonic-gate 
56457c478bd9Sstevel@tonic-gate /*
56467c478bd9Sstevel@tonic-gate  * This routine removes a port number association from a stream.  It
56477c478bd9Sstevel@tonic-gate  * is called by udp_wput to handle T_UNBIND_REQ messages.
56487c478bd9Sstevel@tonic-gate  */
56497c478bd9Sstevel@tonic-gate static void
56507c478bd9Sstevel@tonic-gate udp_unbind(queue_t *q, mblk_t *mp)
56517c478bd9Sstevel@tonic-gate {
5652ff550d0eSmasputra 	udp_t *udp = Q_TO_UDP(q);
56537c478bd9Sstevel@tonic-gate 
56547c478bd9Sstevel@tonic-gate 	/* If a bind has not been done, we can't unbind. */
56557c478bd9Sstevel@tonic-gate 	if (udp->udp_state == TS_UNBND) {
56567c478bd9Sstevel@tonic-gate 		udp_err_ack(q, mp, TOUTSTATE, 0);
56577c478bd9Sstevel@tonic-gate 		return;
56587c478bd9Sstevel@tonic-gate 	}
56597c478bd9Sstevel@tonic-gate 	if (cl_inet_unbind != NULL) {
56607c478bd9Sstevel@tonic-gate 		/*
56617c478bd9Sstevel@tonic-gate 		 * Running in cluster mode - register unbind information
56627c478bd9Sstevel@tonic-gate 		 */
56637c478bd9Sstevel@tonic-gate 		if (udp->udp_ipversion == IPV4_VERSION) {
56647c478bd9Sstevel@tonic-gate 			(*cl_inet_unbind)(IPPROTO_UDP, AF_INET,
56657c478bd9Sstevel@tonic-gate 			    (uint8_t *)(&V4_PART_OF_V6(udp->udp_v6src)),
56667c478bd9Sstevel@tonic-gate 			    (in_port_t)udp->udp_port);
56677c478bd9Sstevel@tonic-gate 		} else {
56687c478bd9Sstevel@tonic-gate 			(*cl_inet_unbind)(IPPROTO_UDP, AF_INET6,
56697c478bd9Sstevel@tonic-gate 			    (uint8_t *)&(udp->udp_v6src),
56707c478bd9Sstevel@tonic-gate 			    (in_port_t)udp->udp_port);
56717c478bd9Sstevel@tonic-gate 		}
56727c478bd9Sstevel@tonic-gate 	}
56737c478bd9Sstevel@tonic-gate 
56747c478bd9Sstevel@tonic-gate 	udp_bind_hash_remove(udp, B_FALSE);
56757c478bd9Sstevel@tonic-gate 	V6_SET_ZERO(udp->udp_v6src);
56767c478bd9Sstevel@tonic-gate 	V6_SET_ZERO(udp->udp_bound_v6src);
56777c478bd9Sstevel@tonic-gate 	udp->udp_port = 0;
56787c478bd9Sstevel@tonic-gate 	udp->udp_state = TS_UNBND;
56797c478bd9Sstevel@tonic-gate 
56807c478bd9Sstevel@tonic-gate 	if (udp->udp_family == AF_INET6) {
56817c478bd9Sstevel@tonic-gate 		int error;
56827c478bd9Sstevel@tonic-gate 
56837c478bd9Sstevel@tonic-gate 		/* Rebuild the header template */
56847c478bd9Sstevel@tonic-gate 		error = udp_build_hdrs(q, udp);
56857c478bd9Sstevel@tonic-gate 		if (error != 0) {
56867c478bd9Sstevel@tonic-gate 			udp_err_ack(q, mp, TSYSERR, error);
56877c478bd9Sstevel@tonic-gate 			return;
56887c478bd9Sstevel@tonic-gate 		}
56897c478bd9Sstevel@tonic-gate 	}
5690ff550d0eSmasputra 	/*
5691ff550d0eSmasputra 	 * Pass the unbind to IP; T_UNBIND_REQ is larger than T_OK_ACK
5692ff550d0eSmasputra 	 * and therefore ip_unbind must never return NULL.
5693ff550d0eSmasputra 	 */
5694ff550d0eSmasputra 	mp = ip_unbind(q, mp);
5695ff550d0eSmasputra 	ASSERT(mp != NULL);
5696ff550d0eSmasputra 	putnext(UDP_RD(q), mp);
56977c478bd9Sstevel@tonic-gate }
56987c478bd9Sstevel@tonic-gate 
56997c478bd9Sstevel@tonic-gate /*
57007c478bd9Sstevel@tonic-gate  * Don't let port fall into the privileged range.
57017c478bd9Sstevel@tonic-gate  * Since the extra priviledged ports can be arbitrary we also
57027c478bd9Sstevel@tonic-gate  * ensure that we exclude those from consideration.
57037c478bd9Sstevel@tonic-gate  * udp_g_epriv_ports is not sorted thus we loop over it until
57047c478bd9Sstevel@tonic-gate  * there are no changes.
57057c478bd9Sstevel@tonic-gate  */
57067c478bd9Sstevel@tonic-gate static in_port_t
57077c478bd9Sstevel@tonic-gate udp_update_next_port(in_port_t port, boolean_t random)
57087c478bd9Sstevel@tonic-gate {
57097c478bd9Sstevel@tonic-gate 	int i;
57107c478bd9Sstevel@tonic-gate 
57117c478bd9Sstevel@tonic-gate 	if (random && udp_random_anon_port != 0) {
57127c478bd9Sstevel@tonic-gate 		(void) random_get_pseudo_bytes((uint8_t *)&port,
57137c478bd9Sstevel@tonic-gate 		    sizeof (in_port_t));
57147c478bd9Sstevel@tonic-gate 		/*
57157c478bd9Sstevel@tonic-gate 		 * Unless changed by a sys admin, the smallest anon port
57167c478bd9Sstevel@tonic-gate 		 * is 32768 and the largest anon port is 65535.  It is
57177c478bd9Sstevel@tonic-gate 		 * very likely (50%) for the random port to be smaller
57187c478bd9Sstevel@tonic-gate 		 * than the smallest anon port.  When that happens,
57197c478bd9Sstevel@tonic-gate 		 * add port % (anon port range) to the smallest anon
57207c478bd9Sstevel@tonic-gate 		 * port to get the random port.  It should fall into the
57217c478bd9Sstevel@tonic-gate 		 * valid anon port range.
57227c478bd9Sstevel@tonic-gate 		 */
57237c478bd9Sstevel@tonic-gate 		if (port < udp_smallest_anon_port) {
57247c478bd9Sstevel@tonic-gate 			port = udp_smallest_anon_port +
57257c478bd9Sstevel@tonic-gate 			    port % (udp_largest_anon_port -
57267c478bd9Sstevel@tonic-gate 			    udp_smallest_anon_port);
57277c478bd9Sstevel@tonic-gate 		}
57287c478bd9Sstevel@tonic-gate 	}
57297c478bd9Sstevel@tonic-gate 
57307c478bd9Sstevel@tonic-gate retry:
57317c478bd9Sstevel@tonic-gate 	if (port < udp_smallest_anon_port || port > udp_largest_anon_port)
57327c478bd9Sstevel@tonic-gate 		port = udp_smallest_anon_port;
57337c478bd9Sstevel@tonic-gate 
57347c478bd9Sstevel@tonic-gate 	if (port < udp_smallest_nonpriv_port)
57357c478bd9Sstevel@tonic-gate 		port = udp_smallest_nonpriv_port;
57367c478bd9Sstevel@tonic-gate 
57377c478bd9Sstevel@tonic-gate 	for (i = 0; i < udp_g_num_epriv_ports; i++) {
57387c478bd9Sstevel@tonic-gate 		if (port == udp_g_epriv_ports[i]) {
57397c478bd9Sstevel@tonic-gate 			port++;
57407c478bd9Sstevel@tonic-gate 			/*
57417c478bd9Sstevel@tonic-gate 			 * Make sure that the port is in the
57427c478bd9Sstevel@tonic-gate 			 * valid range.
57437c478bd9Sstevel@tonic-gate 			 */
57447c478bd9Sstevel@tonic-gate 			goto retry;
57457c478bd9Sstevel@tonic-gate 		}
57467c478bd9Sstevel@tonic-gate 	}
57477c478bd9Sstevel@tonic-gate 	return (port);
57487c478bd9Sstevel@tonic-gate }
57497c478bd9Sstevel@tonic-gate 
5750ff550d0eSmasputra static mblk_t *
5751ff550d0eSmasputra udp_output_v4(conn_t *connp, mblk_t *mp, ipaddr_t v4dst, uint16_t port,
5752ff550d0eSmasputra     uint_t srcid, int *error)
57537c478bd9Sstevel@tonic-gate {
5754ff550d0eSmasputra 	udp_t	*udp = connp->conn_udp;
5755ff550d0eSmasputra 	queue_t	*q = connp->conn_wq;
5756ff550d0eSmasputra 	mblk_t	*mp1 = (DB_TYPE(mp) == M_DATA ? mp : mp->b_cont);
5757ff550d0eSmasputra 	mblk_t	*mp2;
5758ff550d0eSmasputra 	ipha_t	*ipha;
5759ff550d0eSmasputra 	int	ip_hdr_length;
5760ff550d0eSmasputra 	uint32_t ip_len;
5761ff550d0eSmasputra 	udpha_t	*udpha;
57627c478bd9Sstevel@tonic-gate 
5763ff550d0eSmasputra 	*error = 0;
57647c478bd9Sstevel@tonic-gate 
5765ff550d0eSmasputra 	/* mp1 points to the M_DATA mblk carrying the packet */
5766ff550d0eSmasputra 	ASSERT(mp1 != NULL && DB_TYPE(mp1) == M_DATA);
57677c478bd9Sstevel@tonic-gate 
5768ff550d0eSmasputra 	/* Add an IP header */
5769ff550d0eSmasputra 	ip_hdr_length = IP_SIMPLE_HDR_LENGTH + UDPH_SIZE +
5770ff550d0eSmasputra 	    udp->udp_ip_snd_options_len;
5771ff550d0eSmasputra 	ipha = (ipha_t *)&mp1->b_rptr[-ip_hdr_length];
5772ff550d0eSmasputra 	if (DB_REF(mp1) != 1 || (uchar_t *)ipha < DB_BASE(mp1) ||
5773ff550d0eSmasputra 	    !OK_32PTR(ipha)) {
5774ff550d0eSmasputra 		mp2 = allocb(ip_hdr_length + udp_wroff_extra, BPRI_LO);
5775ff550d0eSmasputra 		if (mp2 == NULL) {
5776ff550d0eSmasputra 			TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_END,
5777ff550d0eSmasputra 			    "udp_wput_end: q %p (%S)", q, "allocbfail2");
5778ff550d0eSmasputra 			*error = ENOMEM;
5779ff550d0eSmasputra 			goto done;
5780ff550d0eSmasputra 		}
5781ff550d0eSmasputra 		mp2->b_wptr = DB_LIM(mp2);
5782ff550d0eSmasputra 		mp2->b_cont = mp1;
5783ff550d0eSmasputra 		mp1 = mp2;
5784ff550d0eSmasputra 		if (DB_TYPE(mp) != M_DATA)
5785ff550d0eSmasputra 			mp->b_cont = mp1;
5786ff550d0eSmasputra 		else
5787ff550d0eSmasputra 			mp = mp1;
57887c478bd9Sstevel@tonic-gate 
5789ff550d0eSmasputra 		ipha = (ipha_t *)(mp1->b_wptr - ip_hdr_length);
57907c478bd9Sstevel@tonic-gate 	}
5791ff550d0eSmasputra 	ip_hdr_length -= UDPH_SIZE;
5792ff550d0eSmasputra #ifdef	_BIG_ENDIAN
5793ff550d0eSmasputra 	/* Set version, header length, and tos */
5794ff550d0eSmasputra 	*(uint16_t *)&ipha->ipha_version_and_hdr_length =
5795ff550d0eSmasputra 	    ((((IP_VERSION << 4) | (ip_hdr_length>>2)) << 8) |
5796ff550d0eSmasputra 		udp->udp_type_of_service);
5797ff550d0eSmasputra 	/* Set ttl and protocol */
5798ff550d0eSmasputra 	*(uint16_t *)&ipha->ipha_ttl = (udp->udp_ttl << 8) | IPPROTO_UDP;
5799ff550d0eSmasputra #else
5800ff550d0eSmasputra 	/* Set version, header length, and tos */
5801ff550d0eSmasputra 	*(uint16_t *)&ipha->ipha_version_and_hdr_length =
5802ff550d0eSmasputra 		((udp->udp_type_of_service << 8) |
5803ff550d0eSmasputra 		    ((IP_VERSION << 4) | (ip_hdr_length>>2)));
5804ff550d0eSmasputra 	/* Set ttl and protocol */
5805ff550d0eSmasputra 	*(uint16_t *)&ipha->ipha_ttl = (IPPROTO_UDP << 8) | udp->udp_ttl;
5806ff550d0eSmasputra #endif
5807ff550d0eSmasputra 	/*
5808ff550d0eSmasputra 	 * Copy our address into the packet.  If this is zero,
5809ff550d0eSmasputra 	 * first look at __sin6_src_id for a hint. If we leave the source
5810ff550d0eSmasputra 	 * as INADDR_ANY then ip will fill in the real source address.
5811ff550d0eSmasputra 	 */
5812ff550d0eSmasputra 	IN6_V4MAPPED_TO_IPADDR(&udp->udp_v6src, ipha->ipha_src);
5813ff550d0eSmasputra 	if (srcid != 0 && ipha->ipha_src == INADDR_ANY) {
5814ff550d0eSmasputra 		in6_addr_t v6src;
58157c478bd9Sstevel@tonic-gate 
5816ff550d0eSmasputra 		ip_srcid_find_id(srcid, &v6src, connp->conn_zoneid);
5817ff550d0eSmasputra 		IN6_V4MAPPED_TO_IPADDR(&v6src, ipha->ipha_src);
58187c478bd9Sstevel@tonic-gate 	}
58197c478bd9Sstevel@tonic-gate 
5820ff550d0eSmasputra 	ipha->ipha_fragment_offset_and_flags = 0;
5821ff550d0eSmasputra 	ipha->ipha_ident = 0;
58227c478bd9Sstevel@tonic-gate 
5823ff550d0eSmasputra 	mp1->b_rptr = (uchar_t *)ipha;
58247c478bd9Sstevel@tonic-gate 
58257c478bd9Sstevel@tonic-gate 	ASSERT((uintptr_t)(mp1->b_wptr - (uchar_t *)ipha) <=
58267c478bd9Sstevel@tonic-gate 	    (uintptr_t)UINT_MAX);
58277c478bd9Sstevel@tonic-gate 
58287c478bd9Sstevel@tonic-gate 	/* Determine length of packet */
58297c478bd9Sstevel@tonic-gate 	ip_len = (uint32_t)(mp1->b_wptr - (uchar_t *)ipha);
5830ff550d0eSmasputra 	if ((mp2 = mp1->b_cont) != NULL) {
5831ff550d0eSmasputra 		do {
5832ff550d0eSmasputra 			ASSERT((uintptr_t)MBLKL(mp2) <= (uintptr_t)UINT_MAX);
5833ff550d0eSmasputra 			ip_len += (uint32_t)MBLKL(mp2);
5834ff550d0eSmasputra 		} while ((mp2 = mp2->b_cont) != NULL);
58357c478bd9Sstevel@tonic-gate 	}
58367c478bd9Sstevel@tonic-gate 	/*
58377c478bd9Sstevel@tonic-gate 	 * If the size of the packet is greater than the maximum allowed by
58387c478bd9Sstevel@tonic-gate 	 * ip, return an error. Passing this down could cause panics because
58397c478bd9Sstevel@tonic-gate 	 * the size will have wrapped and be inconsistent with the msg size.
58407c478bd9Sstevel@tonic-gate 	 */
58417c478bd9Sstevel@tonic-gate 	if (ip_len > IP_MAXPACKET) {
58427c478bd9Sstevel@tonic-gate 		TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_END,
58437c478bd9Sstevel@tonic-gate 		    "udp_wput_end: q %p (%S)", q, "IP length exceeded");
5844ff550d0eSmasputra 		*error = EMSGSIZE;
5845ff550d0eSmasputra 		goto done;
58467c478bd9Sstevel@tonic-gate 	}
58477c478bd9Sstevel@tonic-gate 	ipha->ipha_length = htons((uint16_t)ip_len);
58487c478bd9Sstevel@tonic-gate 	ip_len -= ip_hdr_length;
58497c478bd9Sstevel@tonic-gate 	ip_len = htons((uint16_t)ip_len);
58507c478bd9Sstevel@tonic-gate 	udpha = (udpha_t *)(((uchar_t *)ipha) + ip_hdr_length);
5851ff550d0eSmasputra 
58527c478bd9Sstevel@tonic-gate 	/*
5853ff550d0eSmasputra 	 * Copy in the destination address
58547c478bd9Sstevel@tonic-gate 	 */
58557c478bd9Sstevel@tonic-gate 	if (v4dst == INADDR_ANY)
58567c478bd9Sstevel@tonic-gate 		ipha->ipha_dst = htonl(INADDR_LOOPBACK);
58577c478bd9Sstevel@tonic-gate 	else
58587c478bd9Sstevel@tonic-gate 		ipha->ipha_dst = v4dst;
58597c478bd9Sstevel@tonic-gate 
58607c478bd9Sstevel@tonic-gate 	/*
58617c478bd9Sstevel@tonic-gate 	 * Set ttl based on IP_MULTICAST_TTL to match IPv6 logic.
58627c478bd9Sstevel@tonic-gate 	 */
58637c478bd9Sstevel@tonic-gate 	if (CLASSD(v4dst))
58647c478bd9Sstevel@tonic-gate 		ipha->ipha_ttl = udp->udp_multicast_ttl;
58657c478bd9Sstevel@tonic-gate 
58667c478bd9Sstevel@tonic-gate 	udpha->uha_dst_port = port;
58677c478bd9Sstevel@tonic-gate 	udpha->uha_src_port = udp->udp_port;
58687c478bd9Sstevel@tonic-gate 
58697c478bd9Sstevel@tonic-gate 	if (ip_hdr_length > IP_SIMPLE_HDR_LENGTH) {
58707c478bd9Sstevel@tonic-gate 		uint32_t	cksum;
58717c478bd9Sstevel@tonic-gate 
58727c478bd9Sstevel@tonic-gate 		bcopy(udp->udp_ip_snd_options, &ipha[1],
58737c478bd9Sstevel@tonic-gate 		    udp->udp_ip_snd_options_len);
58747c478bd9Sstevel@tonic-gate 		/*
58757c478bd9Sstevel@tonic-gate 		 * Massage source route putting first source route in ipha_dst.
58767c478bd9Sstevel@tonic-gate 		 * Ignore the destination in T_unitdata_req.
58777c478bd9Sstevel@tonic-gate 		 * Create a checksum adjustment for a source route, if any.
58787c478bd9Sstevel@tonic-gate 		 */
58797c478bd9Sstevel@tonic-gate 		cksum = ip_massage_options(ipha);
58807c478bd9Sstevel@tonic-gate 		cksum = (cksum & 0xFFFF) + (cksum >> 16);
58817c478bd9Sstevel@tonic-gate 		cksum -= ((ipha->ipha_dst >> 16) & 0xFFFF) +
58827c478bd9Sstevel@tonic-gate 		    (ipha->ipha_dst & 0xFFFF);
58837c478bd9Sstevel@tonic-gate 		if ((int)cksum < 0)
58847c478bd9Sstevel@tonic-gate 			cksum--;
58857c478bd9Sstevel@tonic-gate 		cksum = (cksum & 0xFFFF) + (cksum >> 16);
58867c478bd9Sstevel@tonic-gate 		/*
58877c478bd9Sstevel@tonic-gate 		 * IP does the checksum if uha_checksum is non-zero,
58887c478bd9Sstevel@tonic-gate 		 * We make it easy for IP to include our pseudo header
58897c478bd9Sstevel@tonic-gate 		 * by putting our length in uha_checksum.
58907c478bd9Sstevel@tonic-gate 		 */
58917c478bd9Sstevel@tonic-gate 		cksum += ip_len;
58927c478bd9Sstevel@tonic-gate 		cksum = (cksum & 0xFFFF) + (cksum >> 16);
58937c478bd9Sstevel@tonic-gate 		/* There might be a carry. */
58947c478bd9Sstevel@tonic-gate 		cksum = (cksum & 0xFFFF) + (cksum >> 16);
58957c478bd9Sstevel@tonic-gate #ifdef _LITTLE_ENDIAN
58967c478bd9Sstevel@tonic-gate 		if (udp_do_checksum)
58977c478bd9Sstevel@tonic-gate 			ip_len = (cksum << 16) | ip_len;
58987c478bd9Sstevel@tonic-gate #else
58997c478bd9Sstevel@tonic-gate 		if (udp_do_checksum)
59007c478bd9Sstevel@tonic-gate 			ip_len = (ip_len << 16) | cksum;
59017c478bd9Sstevel@tonic-gate 		else
59027c478bd9Sstevel@tonic-gate 			ip_len <<= 16;
59037c478bd9Sstevel@tonic-gate #endif
59047c478bd9Sstevel@tonic-gate 	} else {
59057c478bd9Sstevel@tonic-gate 		/*
59067c478bd9Sstevel@tonic-gate 		 * IP does the checksum if uha_checksum is non-zero,
59077c478bd9Sstevel@tonic-gate 		 * We make it easy for IP to include our pseudo header
59087c478bd9Sstevel@tonic-gate 		 * by putting our length in uha_checksum.
59097c478bd9Sstevel@tonic-gate 		 */
59107c478bd9Sstevel@tonic-gate 		if (udp_do_checksum)
59117c478bd9Sstevel@tonic-gate 			ip_len |= (ip_len << 16);
59127c478bd9Sstevel@tonic-gate #ifndef _LITTLE_ENDIAN
59137c478bd9Sstevel@tonic-gate 		else
59147c478bd9Sstevel@tonic-gate 			ip_len <<= 16;
59157c478bd9Sstevel@tonic-gate #endif
59167c478bd9Sstevel@tonic-gate 	}
59177c478bd9Sstevel@tonic-gate 	/* Set UDP length and checksum */
59187c478bd9Sstevel@tonic-gate 	*((uint32_t *)&udpha->uha_length) = ip_len;
59197c478bd9Sstevel@tonic-gate 
5920ff550d0eSmasputra 	if (DB_TYPE(mp) != M_DATA) {
5921ff550d0eSmasputra 		ASSERT(mp != mp1);
5922ff550d0eSmasputra 		freeb(mp);
5923ff550d0eSmasputra 	}
5924ff550d0eSmasputra 
5925ff550d0eSmasputra 	/* mp has been consumed and we'll return success */
5926ff550d0eSmasputra 	ASSERT(*error == 0);
5927ff550d0eSmasputra 	mp = NULL;
59287c478bd9Sstevel@tonic-gate 
59297c478bd9Sstevel@tonic-gate 	/* We're done.  Pass the packet to ip. */
59307c478bd9Sstevel@tonic-gate 	BUMP_MIB(&udp_mib, udpOutDatagrams);
59317c478bd9Sstevel@tonic-gate 	TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_END,
59327c478bd9Sstevel@tonic-gate 		"udp_wput_end: q %p (%S)", q, "end");
5933ff550d0eSmasputra 
5934ff550d0eSmasputra 	if ((connp->conn_flags & IPCL_CHECK_POLICY) != 0 ||
5935ff550d0eSmasputra 	    CONN_OUTBOUND_POLICY_PRESENT(connp) ||
5936ff550d0eSmasputra 	    connp->conn_dontroute || connp->conn_xmit_if_ill != NULL ||
5937ff550d0eSmasputra 	    connp->conn_nofailover_ill != NULL ||
5938ff550d0eSmasputra 	    connp->conn_outgoing_ill != NULL ||
5939ff550d0eSmasputra 	    ipha->ipha_version_and_hdr_length != IP_SIMPLE_HDR_VERSION ||
5940ff550d0eSmasputra 	    IPP_ENABLED(IPP_LOCAL_OUT) || ip_g_mrouter != NULL) {
5941ff550d0eSmasputra 		UDP_STAT(udp_ip_send);
5942ff550d0eSmasputra 		ip_output(connp, mp1, connp->conn_wq, IP_WPUT);
5943ff550d0eSmasputra 	} else {
5944ff550d0eSmasputra 		udp_send_data(udp, connp->conn_wq, mp1, ipha);
5945ff550d0eSmasputra 	}
5946ff550d0eSmasputra 
5947ff550d0eSmasputra done:
5948ff550d0eSmasputra 	if (*error != 0) {
5949ff550d0eSmasputra 		ASSERT(mp != NULL);
5950ff550d0eSmasputra 		BUMP_MIB(&udp_mib, udpOutErrors);
5951ff550d0eSmasputra 	}
5952ff550d0eSmasputra 	return (mp);
5953ff550d0eSmasputra }
5954ff550d0eSmasputra 
5955ff550d0eSmasputra static void
5956ff550d0eSmasputra udp_send_data(udp_t *udp, queue_t *q, mblk_t *mp, ipha_t *ipha)
5957ff550d0eSmasputra {
5958ff550d0eSmasputra 	conn_t	*connp = udp->udp_connp;
5959ff550d0eSmasputra 	ipaddr_t src, dst;
5960ff550d0eSmasputra 	ill_t	*ill;
5961ff550d0eSmasputra 	ire_t	*ire;
5962ff550d0eSmasputra 	ipif_t	*ipif = NULL;
5963ff550d0eSmasputra 	mblk_t	*ire_fp_mp;
5964ff550d0eSmasputra 	uint_t	ire_fp_mp_len;
5965ff550d0eSmasputra 	uint16_t *up;
5966ff550d0eSmasputra 	uint32_t cksum, hcksum_txflags;
5967ff550d0eSmasputra 	queue_t	*dev_q;
5968ff550d0eSmasputra 	boolean_t retry_caching;
5969ff550d0eSmasputra 
5970ff550d0eSmasputra 	dst = ipha->ipha_dst;
5971ff550d0eSmasputra 	src = ipha->ipha_src;
5972ff550d0eSmasputra 	ASSERT(ipha->ipha_ident == 0);
5973ff550d0eSmasputra 
5974ff550d0eSmasputra 	if (CLASSD(dst)) {
5975ff550d0eSmasputra 		int err;
5976ff550d0eSmasputra 
5977ff550d0eSmasputra 		ipif = conn_get_held_ipif(connp,
5978ff550d0eSmasputra 		    &connp->conn_multicast_ipif, &err);
5979ff550d0eSmasputra 
5980ff550d0eSmasputra 		if (ipif == NULL || ipif->ipif_isv6 ||
5981ff550d0eSmasputra 		    (ipif->ipif_ill->ill_phyint->phyint_flags &
5982ff550d0eSmasputra 		    PHYI_LOOPBACK)) {
5983ff550d0eSmasputra 			if (ipif != NULL)
5984ff550d0eSmasputra 				ipif_refrele(ipif);
5985ff550d0eSmasputra 			UDP_STAT(udp_ip_send);
5986ff550d0eSmasputra 			ip_output(connp, mp, q, IP_WPUT);
5987ff550d0eSmasputra 			return;
5988ff550d0eSmasputra 		}
5989ff550d0eSmasputra 	}
5990ff550d0eSmasputra 
5991ff550d0eSmasputra 	retry_caching = B_FALSE;
5992ff550d0eSmasputra 	mutex_enter(&connp->conn_lock);
5993ff550d0eSmasputra 	ire = connp->conn_ire_cache;
5994ff550d0eSmasputra 	ASSERT(!(connp->conn_state_flags & CONN_INCIPIENT));
5995ff550d0eSmasputra 
5996ff550d0eSmasputra 	if (ire == NULL || ire->ire_addr != dst ||
5997ff550d0eSmasputra 	    (ire->ire_marks & IRE_MARK_CONDEMNED)) {
5998ff550d0eSmasputra 		retry_caching = B_TRUE;
5999ff550d0eSmasputra 	} else if (CLASSD(dst) && (ire->ire_type & IRE_CACHE)) {
6000ff550d0eSmasputra 		ill_t *stq_ill = (ill_t *)ire->ire_stq->q_ptr;
6001ff550d0eSmasputra 
6002ff550d0eSmasputra 		ASSERT(ipif != NULL);
6003ff550d0eSmasputra 		if (stq_ill != ipif->ipif_ill && (stq_ill->ill_group == NULL ||
6004ff550d0eSmasputra 		    stq_ill->ill_group != ipif->ipif_ill->ill_group))
6005ff550d0eSmasputra 			retry_caching = B_TRUE;
6006ff550d0eSmasputra 	}
6007ff550d0eSmasputra 
6008ff550d0eSmasputra 	if (!retry_caching) {
6009ff550d0eSmasputra 		ASSERT(ire != NULL);
6010ff550d0eSmasputra 		IRE_REFHOLD(ire);
6011ff550d0eSmasputra 		mutex_exit(&connp->conn_lock);
6012ff550d0eSmasputra 	} else {
6013ff550d0eSmasputra 		boolean_t cached = B_FALSE;
6014ff550d0eSmasputra 
6015ff550d0eSmasputra 		connp->conn_ire_cache = NULL;
6016ff550d0eSmasputra 		mutex_exit(&connp->conn_lock);
6017ff550d0eSmasputra 
6018ff550d0eSmasputra 		/* Release the old ire */
6019ff550d0eSmasputra 		if (ire != NULL) {
6020ff550d0eSmasputra 			IRE_REFRELE_NOTR(ire);
6021ff550d0eSmasputra 			ire = NULL;
6022ff550d0eSmasputra 		}
6023ff550d0eSmasputra 
6024ff550d0eSmasputra 		if (CLASSD(dst)) {
6025ff550d0eSmasputra 			ASSERT(ipif != NULL);
6026ff550d0eSmasputra 			ire = ire_ctable_lookup(dst, 0, 0, ipif,
6027ff550d0eSmasputra 			    connp->conn_zoneid, MATCH_IRE_ILL_GROUP);
6028ff550d0eSmasputra 		} else {
6029ff550d0eSmasputra 			ASSERT(ipif == NULL);
6030ff550d0eSmasputra 			ire = ire_cache_lookup(dst, connp->conn_zoneid);
6031ff550d0eSmasputra 		}
6032ff550d0eSmasputra 
6033ff550d0eSmasputra 		if (ire == NULL) {
6034ff550d0eSmasputra 			if (ipif != NULL)
6035ff550d0eSmasputra 				ipif_refrele(ipif);
6036ff550d0eSmasputra 			UDP_STAT(udp_ire_null);
6037ff550d0eSmasputra 			ip_output(connp, mp, q, IP_WPUT);
6038ff550d0eSmasputra 			return;
6039ff550d0eSmasputra 		}
6040ff550d0eSmasputra 		IRE_REFHOLD_NOTR(ire);
6041ff550d0eSmasputra 
6042ff550d0eSmasputra 		mutex_enter(&connp->conn_lock);
6043ff550d0eSmasputra 		if (!(connp->conn_state_flags & CONN_CLOSING) &&
6044ff550d0eSmasputra 		    connp->conn_ire_cache == NULL) {
6045ff550d0eSmasputra 			rw_enter(&ire->ire_bucket->irb_lock, RW_READER);
6046ff550d0eSmasputra 			if (!(ire->ire_marks & IRE_MARK_CONDEMNED)) {
6047ff550d0eSmasputra 				connp->conn_ire_cache = ire;
6048ff550d0eSmasputra 				cached = B_TRUE;
6049ff550d0eSmasputra 			}
6050ff550d0eSmasputra 			rw_exit(&ire->ire_bucket->irb_lock);
6051ff550d0eSmasputra 		}
6052ff550d0eSmasputra 		mutex_exit(&connp->conn_lock);
6053ff550d0eSmasputra 
6054ff550d0eSmasputra 		/*
6055ff550d0eSmasputra 		 * We can continue to use the ire but since it was not
6056ff550d0eSmasputra 		 * cached, we should drop the extra reference.
6057ff550d0eSmasputra 		 */
6058ff550d0eSmasputra 		if (!cached)
6059ff550d0eSmasputra 			IRE_REFRELE_NOTR(ire);
6060ff550d0eSmasputra 	}
6061ff550d0eSmasputra 	ASSERT(ire != NULL && ire->ire_ipversion == IPV4_VERSION);
6062ff550d0eSmasputra 	ASSERT(!CLASSD(dst) || ipif != NULL);
6063ff550d0eSmasputra 
6064ff550d0eSmasputra 	if ((ire->ire_type & (IRE_BROADCAST|IRE_LOCAL|IRE_LOOPBACK)) ||
6065ff550d0eSmasputra 	    (ire->ire_flags & RTF_MULTIRT) || ire->ire_stq == NULL ||
6066ff550d0eSmasputra 	    ire->ire_max_frag < ntohs(ipha->ipha_length) ||
6067ff550d0eSmasputra 	    (ire_fp_mp = ire->ire_fp_mp) == NULL ||
606843d18f1cSpriyanka 	    (connp->conn_nexthop_set) ||
6069ff550d0eSmasputra 	    (ire_fp_mp_len = MBLKL(ire_fp_mp)) > MBLKHEAD(mp)) {
6070ff550d0eSmasputra 		if (ipif != NULL)
6071ff550d0eSmasputra 			ipif_refrele(ipif);
6072ff550d0eSmasputra 		UDP_STAT(udp_ip_ire_send);
6073ff550d0eSmasputra 		IRE_REFRELE(ire);
6074ff550d0eSmasputra 		ip_output(connp, mp, q, IP_WPUT);
6075ff550d0eSmasputra 		return;
6076ff550d0eSmasputra 	}
6077ff550d0eSmasputra 
6078ff550d0eSmasputra 	BUMP_MIB(&ip_mib, ipOutRequests);
6079ff550d0eSmasputra 
6080ff550d0eSmasputra 	ill = ire_to_ill(ire);
6081ff550d0eSmasputra 	ASSERT(ill != NULL);
6082ff550d0eSmasputra 
6083ff550d0eSmasputra 	dev_q = ire->ire_stq->q_next;
6084ff550d0eSmasputra 	ASSERT(dev_q != NULL);
6085ff550d0eSmasputra 	/*
6086ff550d0eSmasputra 	 * If the service thread is already running, or if the driver
6087ff550d0eSmasputra 	 * queue is currently flow-controlled, queue this packet.
6088ff550d0eSmasputra 	 */
6089ff550d0eSmasputra 	if ((q->q_first != NULL || connp->conn_draining) ||
6090ff550d0eSmasputra 	    ((dev_q->q_next || dev_q->q_first) && !canput(dev_q))) {
6091ff550d0eSmasputra 		if (ip_output_queue) {
6092ff550d0eSmasputra 			(void) putq(q, mp);
6093ff550d0eSmasputra 		} else {
6094ff550d0eSmasputra 			BUMP_MIB(&ip_mib, ipOutDiscards);
6095ff550d0eSmasputra 			freemsg(mp);
6096ff550d0eSmasputra 		}
6097ff550d0eSmasputra 		if (ipif != NULL)
6098ff550d0eSmasputra 			ipif_refrele(ipif);
6099ff550d0eSmasputra 		IRE_REFRELE(ire);
6100ff550d0eSmasputra 		return;
6101ff550d0eSmasputra 	}
6102ff550d0eSmasputra 
6103ff550d0eSmasputra 	ipha->ipha_ident = (uint16_t)atomic_add_32_nv(&ire->ire_ident, 1);
6104ff550d0eSmasputra #ifndef _BIG_ENDIAN
6105ff550d0eSmasputra 	ipha->ipha_ident = (ipha->ipha_ident << 8) | (ipha->ipha_ident >> 8);
6106ff550d0eSmasputra #endif
6107ff550d0eSmasputra 
6108ff550d0eSmasputra 	if (src == INADDR_ANY && !connp->conn_unspec_src) {
6109ff550d0eSmasputra 		if (CLASSD(dst) && !(ire->ire_flags & RTF_SETSRC))
6110ff550d0eSmasputra 			src = ipha->ipha_src = ipif->ipif_src_addr;
6111ff550d0eSmasputra 		else
6112ff550d0eSmasputra 			src = ipha->ipha_src = ire->ire_src_addr;
6113ff550d0eSmasputra 	}
6114ff550d0eSmasputra 
6115ff550d0eSmasputra 	if (ILL_HCKSUM_CAPABLE(ill) && dohwcksum) {
6116ff550d0eSmasputra 		ASSERT(ill->ill_hcksum_capab != NULL);
6117ff550d0eSmasputra 		hcksum_txflags = ill->ill_hcksum_capab->ill_hcksum_txflags;
6118ff550d0eSmasputra 	} else {
6119ff550d0eSmasputra 		hcksum_txflags = 0;
6120ff550d0eSmasputra 	}
6121ff550d0eSmasputra 
6122ff550d0eSmasputra 	/* pseudo-header checksum (do it in parts for IP header checksum) */
6123ff550d0eSmasputra 	cksum = (dst >> 16) + (dst & 0xFFFF) + (src >> 16) + (src & 0xFFFF);
6124ff550d0eSmasputra 
6125ff550d0eSmasputra 	ASSERT(ipha->ipha_version_and_hdr_length == IP_SIMPLE_HDR_VERSION);
6126ff550d0eSmasputra 	up = IPH_UDPH_CHECKSUMP(ipha, IP_SIMPLE_HDR_LENGTH);
6127ff550d0eSmasputra 	if (*up != 0) {
6128ff550d0eSmasputra 		IP_CKSUM_XMIT_FAST(ire->ire_ipversion, hcksum_txflags,
6129ff550d0eSmasputra 		    mp, ipha, up, IPPROTO_UDP, IP_SIMPLE_HDR_LENGTH,
6130ff550d0eSmasputra 		    ntohs(ipha->ipha_length), cksum);
6131ff550d0eSmasputra 
6132ff550d0eSmasputra 		/* Software checksum? */
6133ff550d0eSmasputra 		if (DB_CKSUMFLAGS(mp) == 0) {
6134ff550d0eSmasputra 			UDP_STAT(udp_out_sw_cksum);
6135ff550d0eSmasputra 			UDP_STAT_UPDATE(udp_out_sw_cksum_bytes,
6136ff550d0eSmasputra 			    ntohs(ipha->ipha_length) - IP_SIMPLE_HDR_LENGTH);
6137ff550d0eSmasputra 		}
6138ff550d0eSmasputra 	}
6139ff550d0eSmasputra 
6140ff550d0eSmasputra 	ipha->ipha_fragment_offset_and_flags |=
6141ff550d0eSmasputra 	    (uint32_t)htons(ire->ire_frag_flag);
6142ff550d0eSmasputra 
6143ff550d0eSmasputra 	/* Calculate IP header checksum if hardware isn't capable */
6144ff550d0eSmasputra 	if (!(DB_CKSUMFLAGS(mp) & HCK_IPV4_HDRCKSUM)) {
6145ff550d0eSmasputra 		IP_HDR_CKSUM(ipha, cksum, ((uint32_t *)ipha)[0],
6146ff550d0eSmasputra 		    ((uint16_t *)ipha)[4]);
6147ff550d0eSmasputra 	}
6148ff550d0eSmasputra 
6149ff550d0eSmasputra 	if (CLASSD(dst)) {
6150ff550d0eSmasputra 		ilm_t *ilm;
6151ff550d0eSmasputra 
6152ff550d0eSmasputra 		ILM_WALKER_HOLD(ill);
6153ff550d0eSmasputra 		ilm = ilm_lookup_ill(ill, dst, ALL_ZONES);
6154ff550d0eSmasputra 		ILM_WALKER_RELE(ill);
6155ff550d0eSmasputra 		if (ilm != NULL) {
6156ff550d0eSmasputra 			ip_multicast_loopback(q, ill, mp,
6157ff550d0eSmasputra 			    connp->conn_multicast_loop ? 0 :
6158ff550d0eSmasputra 			    IP_FF_NO_MCAST_LOOP, connp->conn_zoneid);
6159ff550d0eSmasputra 		}
6160ff550d0eSmasputra 
6161ff550d0eSmasputra 		/* If multicast TTL is 0 then we are done */
6162ff550d0eSmasputra 		if (ipha->ipha_ttl == 0) {
6163ff550d0eSmasputra 			if (ipif != NULL)
6164ff550d0eSmasputra 				ipif_refrele(ipif);
6165ff550d0eSmasputra 			freemsg(mp);
6166ff550d0eSmasputra 			IRE_REFRELE(ire);
6167ff550d0eSmasputra 			return;
6168ff550d0eSmasputra 		}
6169ff550d0eSmasputra 	}
6170ff550d0eSmasputra 
6171ff550d0eSmasputra 	ASSERT(DB_TYPE(ire_fp_mp) == M_DATA);
6172ff550d0eSmasputra 	mp->b_rptr = (uchar_t *)ipha - ire_fp_mp_len;
6173ff550d0eSmasputra 	bcopy(ire_fp_mp->b_rptr, mp->b_rptr, ire_fp_mp_len);
6174ff550d0eSmasputra 
6175ff550d0eSmasputra 	UPDATE_OB_PKT_COUNT(ire);
6176ff550d0eSmasputra 	ire->ire_last_used_time = lbolt;
6177ff550d0eSmasputra 
61784b46d1efSkrgopi 	if (ILL_DLS_CAPABLE(ill)) {
6179ff550d0eSmasputra 		/*
6180ff550d0eSmasputra 		 * Send the packet directly to DLD, where it may be queued
6181ff550d0eSmasputra 		 * depending on the availability of transmit resources at
6182ff550d0eSmasputra 		 * the media layer.
6183ff550d0eSmasputra 		 */
61844b46d1efSkrgopi 		IP_DLS_ILL_TX(ill, mp);
6185ff550d0eSmasputra 	} else {
6186ff550d0eSmasputra 		putnext(ire->ire_stq, mp);
6187ff550d0eSmasputra 	}
6188ff550d0eSmasputra 
6189ff550d0eSmasputra 	if (ipif != NULL)
6190ff550d0eSmasputra 		ipif_refrele(ipif);
6191ff550d0eSmasputra 	IRE_REFRELE(ire);
61927c478bd9Sstevel@tonic-gate }
61937c478bd9Sstevel@tonic-gate 
61947c478bd9Sstevel@tonic-gate /*
6195ff550d0eSmasputra  * This routine handles all messages passed downstream.  It either
6196ff550d0eSmasputra  * consumes the message or passes it downstream; it never queues a
6197ff550d0eSmasputra  * a message.
61987c478bd9Sstevel@tonic-gate  */
61997c478bd9Sstevel@tonic-gate static void
6200ff550d0eSmasputra udp_output(conn_t *connp, mblk_t *mp, struct sockaddr *addr, socklen_t addrlen)
62017c478bd9Sstevel@tonic-gate {
6202ff550d0eSmasputra 	sin6_t		*sin6;
6203ff550d0eSmasputra 	sin_t		*sin;
6204ff550d0eSmasputra 	ipaddr_t	v4dst;
6205ff550d0eSmasputra 	uint16_t	port;
6206ff550d0eSmasputra 	uint_t		srcid;
6207ff550d0eSmasputra 	queue_t		*q = connp->conn_wq;
6208ff550d0eSmasputra 	udp_t		*udp = connp->conn_udp;
6209ff550d0eSmasputra 	t_scalar_t	optlen;
6210ff550d0eSmasputra 	int		error = 0;
6211ff550d0eSmasputra 	struct sockaddr_storage ss;
62127c478bd9Sstevel@tonic-gate 
6213ff550d0eSmasputra 	TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_START,
6214ff550d0eSmasputra 	    "udp_wput_start: connp %p mp %p", connp, mp);
6215ff550d0eSmasputra 
6216ff550d0eSmasputra 	/*
6217ff550d0eSmasputra 	 * We directly handle several cases here: T_UNITDATA_REQ message
6218ff550d0eSmasputra 	 * coming down as M_PROTO/M_PCPROTO and M_DATA messages for both
6219ff550d0eSmasputra 	 * connected and non-connected socket.  The latter carries the
6220ff550d0eSmasputra 	 * address structure along when this routine gets called.
6221ff550d0eSmasputra 	 */
6222ff550d0eSmasputra 	switch (DB_TYPE(mp)) {
6223ff550d0eSmasputra 	case M_DATA:
6224ff550d0eSmasputra 		if (!udp->udp_direct_sockfs || udp->udp_state != TS_DATA_XFER) {
6225ff550d0eSmasputra 			if (!udp->udp_direct_sockfs ||
6226ff550d0eSmasputra 			    addr == NULL || addrlen == 0) {
6227ff550d0eSmasputra 				/* Not connected; address is required */
6228ff550d0eSmasputra 				BUMP_MIB(&udp_mib, udpOutErrors);
6229ff550d0eSmasputra 				UDP_STAT(udp_out_err_notconn);
6230ff550d0eSmasputra 				freemsg(mp);
6231ff550d0eSmasputra 				TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_END,
6232ff550d0eSmasputra 				    "udp_wput_end: connp %p (%S)", connp,
6233ff550d0eSmasputra 				    "not-connected; address required");
6234ff550d0eSmasputra 				return;
6235ff550d0eSmasputra 			}
6236ff550d0eSmasputra 			ASSERT(udp->udp_issocket);
6237ff550d0eSmasputra 			UDP_DBGSTAT(udp_data_notconn);
6238ff550d0eSmasputra 			/* Not connected; do some more checks below */
6239ff550d0eSmasputra 			optlen = 0;
6240ff550d0eSmasputra 			break;
6241ff550d0eSmasputra 		}
6242ff550d0eSmasputra 		/* M_DATA for connected socket */
6243ff550d0eSmasputra 		UDP_DBGSTAT(udp_data_conn);
6244ff550d0eSmasputra 		IN6_V4MAPPED_TO_IPADDR(&udp->udp_v6dst, v4dst);
6245ff550d0eSmasputra 
6246ff550d0eSmasputra 		/* Initialize addr and addrlen as if they're passed in */
6247ff550d0eSmasputra 		if (udp->udp_family == AF_INET) {
6248ff550d0eSmasputra 			sin = (sin_t *)&ss;
6249ff550d0eSmasputra 			sin->sin_family = AF_INET;
6250ff550d0eSmasputra 			sin->sin_port = udp->udp_dstport;
6251ff550d0eSmasputra 			sin->sin_addr.s_addr = v4dst;
6252ff550d0eSmasputra 			addr = (struct sockaddr *)sin;
6253ff550d0eSmasputra 			addrlen = sizeof (*sin);
6254ff550d0eSmasputra 		} else {
6255ff550d0eSmasputra 			sin6 = (sin6_t *)&ss;
6256ff550d0eSmasputra 			sin6->sin6_family = AF_INET6;
6257ff550d0eSmasputra 			sin6->sin6_port = udp->udp_dstport;
6258ff550d0eSmasputra 			sin6->sin6_flowinfo = udp->udp_flowinfo;
6259ff550d0eSmasputra 			sin6->sin6_addr = udp->udp_v6dst;
6260ff550d0eSmasputra 			sin6->sin6_scope_id = 0;
6261ff550d0eSmasputra 			sin6->__sin6_src_id = 0;
6262ff550d0eSmasputra 			addr = (struct sockaddr *)sin6;
6263ff550d0eSmasputra 			addrlen = sizeof (*sin6);
6264ff550d0eSmasputra 		}
6265ff550d0eSmasputra 
6266ff550d0eSmasputra 		if (udp->udp_family == AF_INET ||
6267ff550d0eSmasputra 		    IN6_IS_ADDR_V4MAPPED(&udp->udp_v6dst)) {
6268ff550d0eSmasputra 			/*
6269ff550d0eSmasputra 			 * Handle both AF_INET and AF_INET6; the latter
6270ff550d0eSmasputra 			 * for IPV4 mapped destination addresses.  Note
6271ff550d0eSmasputra 			 * here that both addr and addrlen point to the
6272ff550d0eSmasputra 			 * corresponding struct depending on the address
6273ff550d0eSmasputra 			 * family of the socket.
6274ff550d0eSmasputra 			 */
6275ff550d0eSmasputra 			mp = udp_output_v4(connp, mp, v4dst,
6276ff550d0eSmasputra 			    udp->udp_dstport, 0, &error);
6277ff550d0eSmasputra 		} else {
6278ff550d0eSmasputra 			mp = udp_output_v6(connp, mp, sin6, 0, &error);
6279ff550d0eSmasputra 		}
6280ff550d0eSmasputra 		if (error != 0) {
6281ff550d0eSmasputra 			ASSERT(addr != NULL && addrlen != 0);
6282ff550d0eSmasputra 			goto ud_error;
6283ff550d0eSmasputra 		}
6284ff550d0eSmasputra 		return;
6285ff550d0eSmasputra 	case M_PROTO:
6286ff550d0eSmasputra 	case M_PCPROTO: {
6287ff550d0eSmasputra 		struct T_unitdata_req *tudr;
6288ff550d0eSmasputra 
6289ff550d0eSmasputra 		ASSERT((uintptr_t)MBLKL(mp) <= (uintptr_t)INT_MAX);
6290ff550d0eSmasputra 		tudr = (struct T_unitdata_req *)mp->b_rptr;
6291ff550d0eSmasputra 
6292ff550d0eSmasputra 		/* Handle valid T_UNITDATA_REQ here */
6293ff550d0eSmasputra 		if (MBLKL(mp) >= sizeof (*tudr) &&
6294ff550d0eSmasputra 		    ((t_primp_t)mp->b_rptr)->type == T_UNITDATA_REQ) {
6295ff550d0eSmasputra 			if (mp->b_cont == NULL) {
6296ff550d0eSmasputra 				TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_END,
6297ff550d0eSmasputra 				    "udp_wput_end: q %p (%S)", q, "badaddr");
6298ff550d0eSmasputra 				error = EPROTO;
6299ff550d0eSmasputra 				goto ud_error;
6300ff550d0eSmasputra 			}
6301ff550d0eSmasputra 
6302ff550d0eSmasputra 			if (!MBLKIN(mp, 0, tudr->DEST_offset +
6303ff550d0eSmasputra 			    tudr->DEST_length)) {
6304ff550d0eSmasputra 				TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_END,
6305ff550d0eSmasputra 				    "udp_wput_end: q %p (%S)", q, "badaddr");
6306ff550d0eSmasputra 				error = EADDRNOTAVAIL;
6307ff550d0eSmasputra 				goto ud_error;
6308ff550d0eSmasputra 			}
6309ff550d0eSmasputra 			/*
6310ff550d0eSmasputra 			 * If a port has not been bound to the stream, fail.
6311ff550d0eSmasputra 			 * This is not a problem when sockfs is directly
6312ff550d0eSmasputra 			 * above us, because it will ensure that the socket
6313ff550d0eSmasputra 			 * is first bound before allowing data to be sent.
6314ff550d0eSmasputra 			 */
6315ff550d0eSmasputra 			if (udp->udp_state == TS_UNBND) {
6316ff550d0eSmasputra 				TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_END,
6317ff550d0eSmasputra 				    "udp_wput_end: q %p (%S)", q, "outstate");
6318ff550d0eSmasputra 				error = EPROTO;
6319ff550d0eSmasputra 				goto ud_error;
6320ff550d0eSmasputra 			}
6321ff550d0eSmasputra 			addr = (struct sockaddr *)
6322ff550d0eSmasputra 			    &mp->b_rptr[tudr->DEST_offset];
6323ff550d0eSmasputra 			addrlen = tudr->DEST_length;
6324ff550d0eSmasputra 			optlen = tudr->OPT_length;
6325ff550d0eSmasputra 			if (optlen != 0)
6326ff550d0eSmasputra 				UDP_STAT(udp_out_opt);
6327ff550d0eSmasputra 			break;
6328ff550d0eSmasputra 		}
6329ff550d0eSmasputra 		/* FALLTHRU */
6330ff550d0eSmasputra 	}
6331ff550d0eSmasputra 	default:
6332ff550d0eSmasputra 		udp_become_writer(connp, mp, udp_wput_other_wrapper,
6333ff550d0eSmasputra 		    SQTAG_UDP_OUTPUT);
6334ff550d0eSmasputra 		return;
6335ff550d0eSmasputra 	}
6336ff550d0eSmasputra 	ASSERT(addr != NULL);
6337ff550d0eSmasputra 
6338ff550d0eSmasputra 	switch (udp->udp_family) {
6339ff550d0eSmasputra 	case AF_INET6:
6340ff550d0eSmasputra 		sin6 = (sin6_t *)addr;
6341ff550d0eSmasputra 		if (!OK_32PTR((char *)sin6) || addrlen != sizeof (sin6_t) ||
6342ff550d0eSmasputra 		    sin6->sin6_family != AF_INET6) {
6343ff550d0eSmasputra 			TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_END,
6344ff550d0eSmasputra 			    "udp_wput_end: q %p (%S)", q, "badaddr");
6345ff550d0eSmasputra 			error = EADDRNOTAVAIL;
6346ff550d0eSmasputra 			goto ud_error;
6347ff550d0eSmasputra 		}
6348ff550d0eSmasputra 
6349ff550d0eSmasputra 		if (!IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
6350ff550d0eSmasputra 			/*
6351ff550d0eSmasputra 			 * Destination is a non-IPv4-compatible IPv6 address.
6352ff550d0eSmasputra 			 * Send out an IPv6 format packet.
6353ff550d0eSmasputra 			 */
6354ff550d0eSmasputra 			mp = udp_output_v6(connp, mp, sin6, optlen, &error);
6355ff550d0eSmasputra 			if (error != 0)
6356ff550d0eSmasputra 				goto ud_error;
6357ff550d0eSmasputra 
6358ff550d0eSmasputra 			TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_END,
6359ff550d0eSmasputra 			    "udp_wput_end: q %p (%S)", q, "udp_output_v6");
6360ff550d0eSmasputra 			return;
6361ff550d0eSmasputra 		}
6362ff550d0eSmasputra 		/*
6363ff550d0eSmasputra 		 * If the local address is not zero or a mapped address
6364ff550d0eSmasputra 		 * return an error.  It would be possible to send an IPv4
6365ff550d0eSmasputra 		 * packet but the response would never make it back to the
6366ff550d0eSmasputra 		 * application since it is bound to a non-mapped address.
6367ff550d0eSmasputra 		 */
6368ff550d0eSmasputra 		if (!IN6_IS_ADDR_V4MAPPED(&udp->udp_v6src) &&
6369ff550d0eSmasputra 		    !IN6_IS_ADDR_UNSPECIFIED(&udp->udp_v6src)) {
6370ff550d0eSmasputra 			TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_END,
6371ff550d0eSmasputra 			    "udp_wput_end: q %p (%S)", q, "badaddr");
6372ff550d0eSmasputra 			error = EADDRNOTAVAIL;
6373ff550d0eSmasputra 			goto ud_error;
6374ff550d0eSmasputra 		}
6375ff550d0eSmasputra 		/* Send IPv4 packet without modifying udp_ipversion */
6376ff550d0eSmasputra 		/* Extract port and ipaddr */
6377ff550d0eSmasputra 		port = sin6->sin6_port;
6378ff550d0eSmasputra 		IN6_V4MAPPED_TO_IPADDR(&sin6->sin6_addr, v4dst);
6379ff550d0eSmasputra 		srcid = sin6->__sin6_src_id;
6380ff550d0eSmasputra 		break;
6381ff550d0eSmasputra 
6382ff550d0eSmasputra 	case AF_INET:
6383ff550d0eSmasputra 		sin = (sin_t *)addr;
6384ff550d0eSmasputra 		if (!OK_32PTR((char *)sin) || addrlen != sizeof (sin_t) ||
6385ff550d0eSmasputra 		    sin->sin_family != AF_INET) {
6386ff550d0eSmasputra 			TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_END,
6387ff550d0eSmasputra 			    "udp_wput_end: q %p (%S)", q, "badaddr");
6388ff550d0eSmasputra 			error = EADDRNOTAVAIL;
6389ff550d0eSmasputra 			goto ud_error;
6390ff550d0eSmasputra 		}
6391ff550d0eSmasputra 		/* Extract port and ipaddr */
6392ff550d0eSmasputra 		port = sin->sin_port;
6393ff550d0eSmasputra 		v4dst = sin->sin_addr.s_addr;
6394ff550d0eSmasputra 		srcid = 0;
6395ff550d0eSmasputra 		break;
6396ff550d0eSmasputra 	}
6397ff550d0eSmasputra 
6398ff550d0eSmasputra 	/*
6399ff550d0eSmasputra 	 * If options passed in, feed it for verification and handling
6400ff550d0eSmasputra 	 */
6401ff550d0eSmasputra 	if (optlen != 0) {
6402ff550d0eSmasputra 		ASSERT(DB_TYPE(mp) != M_DATA);
6403ff550d0eSmasputra 		if (udp_unitdata_opt_process(q, mp, &error, NULL) < 0) {
6404ff550d0eSmasputra 			/* failure */
6405ff550d0eSmasputra 			TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_END,
6406ff550d0eSmasputra 			    "udp_wput_end: q %p (%S)", q,
6407ff550d0eSmasputra 			    "udp_unitdata_opt_process");
6408ff550d0eSmasputra 			goto ud_error;
6409ff550d0eSmasputra 		}
6410ff550d0eSmasputra 		/*
6411ff550d0eSmasputra 		 * Note: success in processing options.
6412ff550d0eSmasputra 		 * mp option buffer represented by
6413ff550d0eSmasputra 		 * OPT_length/offset now potentially modified
6414ff550d0eSmasputra 		 * and contain option setting results
6415ff550d0eSmasputra 		 */
6416ff550d0eSmasputra 	}
6417ff550d0eSmasputra 	ASSERT(error == 0);
6418ff550d0eSmasputra 	mp = udp_output_v4(connp, mp, v4dst, port, srcid, &error);
6419ff550d0eSmasputra 	if (error != 0) {
6420ff550d0eSmasputra ud_error:
6421ff550d0eSmasputra 		UDP_STAT(udp_out_err_output);
6422ff550d0eSmasputra 		ASSERT(mp != NULL);
6423ff550d0eSmasputra 		/* mp is freed by the following routine */
6424ff550d0eSmasputra 		udp_ud_err(q, mp, (uchar_t *)addr, (t_scalar_t)addrlen,
6425ff550d0eSmasputra 		    (t_scalar_t)error);
6426ff550d0eSmasputra 	}
6427ff550d0eSmasputra }
6428ff550d0eSmasputra 
6429ff550d0eSmasputra /* ARGSUSED */
6430ff550d0eSmasputra static void
6431ff550d0eSmasputra udp_output_wrapper(void *arg, mblk_t *mp, void *arg2)
6432ff550d0eSmasputra {
6433ff550d0eSmasputra 	udp_output((conn_t *)arg, mp, NULL, 0);
6434ff550d0eSmasputra 	_UDP_EXIT((conn_t *)arg);
6435ff550d0eSmasputra }
6436ff550d0eSmasputra 
6437ff550d0eSmasputra static void
6438ff550d0eSmasputra udp_wput(queue_t *q, mblk_t *mp)
6439ff550d0eSmasputra {
6440ff550d0eSmasputra 	_UDP_ENTER(Q_TO_CONN(UDP_WR(q)), mp, udp_output_wrapper,
6441ff550d0eSmasputra 	    SQTAG_UDP_WPUT);
6442ff550d0eSmasputra }
6443ff550d0eSmasputra 
6444ff550d0eSmasputra /*
6445ff550d0eSmasputra  * Allocate and prepare a T_UNITDATA_REQ message.
6446ff550d0eSmasputra  */
6447ff550d0eSmasputra static mblk_t *
6448ff550d0eSmasputra udp_tudr_alloc(struct sockaddr *addr, socklen_t addrlen)
6449ff550d0eSmasputra {
6450ff550d0eSmasputra 	struct T_unitdata_req *tudr;
6451ff550d0eSmasputra 	mblk_t *mp;
6452ff550d0eSmasputra 
6453ff550d0eSmasputra 	mp = allocb(sizeof (*tudr) + addrlen, BPRI_MED);
6454ff550d0eSmasputra 	if (mp != NULL) {
6455ff550d0eSmasputra 		mp->b_wptr += sizeof (*tudr) + addrlen;
6456ff550d0eSmasputra 		DB_TYPE(mp) = M_PROTO;
6457ff550d0eSmasputra 
6458ff550d0eSmasputra 		tudr = (struct T_unitdata_req *)mp->b_rptr;
6459ff550d0eSmasputra 		tudr->PRIM_type = T_UNITDATA_REQ;
6460ff550d0eSmasputra 		tudr->DEST_length = addrlen;
6461ff550d0eSmasputra 		tudr->DEST_offset = (t_scalar_t)sizeof (*tudr);
6462ff550d0eSmasputra 		tudr->OPT_length = 0;
6463ff550d0eSmasputra 		tudr->OPT_offset = 0;
6464ff550d0eSmasputra 		bcopy(addr, tudr+1, addrlen);
6465ff550d0eSmasputra 	}
6466ff550d0eSmasputra 	return (mp);
6467ff550d0eSmasputra }
6468ff550d0eSmasputra 
6469ff550d0eSmasputra /*
6470ff550d0eSmasputra  * Entry point for sockfs when udp is in "direct sockfs" mode.  This mode
6471ff550d0eSmasputra  * is valid when we are directly beneath the stream head, and thus sockfs
6472ff550d0eSmasputra  * is able to bypass STREAMS and directly call us, passing along the sockaddr
6473ff550d0eSmasputra  * structure without the cumbersome T_UNITDATA_REQ interface.  Note that
6474ff550d0eSmasputra  * this is done for both connected and non-connected endpoint.
6475ff550d0eSmasputra  */
6476ff550d0eSmasputra void
6477ff550d0eSmasputra udp_wput_data(queue_t *q, mblk_t *mp, struct sockaddr *addr, socklen_t addrlen)
6478ff550d0eSmasputra {
6479ff550d0eSmasputra 	conn_t	*connp;
6480ff550d0eSmasputra 	udp_t	*udp;
6481ff550d0eSmasputra 
6482ff550d0eSmasputra 	q = UDP_WR(q);
6483ff550d0eSmasputra 	connp = Q_TO_CONN(q);
6484ff550d0eSmasputra 	udp = connp->conn_udp;
6485ff550d0eSmasputra 
6486ff550d0eSmasputra 	/* udpsockfs should only send down M_DATA for this entry point */
6487ff550d0eSmasputra 	ASSERT(DB_TYPE(mp) == M_DATA);
6488ff550d0eSmasputra 
6489ff550d0eSmasputra 	mutex_enter(&connp->conn_lock);
6490ff550d0eSmasputra 	UDP_MODE_ASSERTIONS(udp, UDP_ENTER);
6491ff550d0eSmasputra 
6492ff550d0eSmasputra 	if (udp->udp_mode != UDP_MT_HOT) {
6493ff550d0eSmasputra 		/*
6494ff550d0eSmasputra 		 * We can't enter this conn right away because another
6495ff550d0eSmasputra 		 * thread is currently executing as writer; therefore we
6496ff550d0eSmasputra 		 * need to deposit the message into the squeue to be
6497ff550d0eSmasputra 		 * drained later.  If a socket address is present, we
6498ff550d0eSmasputra 		 * need to create a T_UNITDATA_REQ message as placeholder.
6499ff550d0eSmasputra 		 */
6500ff550d0eSmasputra 		if (addr != NULL && addrlen != 0) {
6501ff550d0eSmasputra 			mblk_t *tudr_mp = udp_tudr_alloc(addr, addrlen);
6502ff550d0eSmasputra 
6503ff550d0eSmasputra 			if (tudr_mp == NULL) {
6504ff550d0eSmasputra 				mutex_exit(&connp->conn_lock);
6505ff550d0eSmasputra 				BUMP_MIB(&udp_mib, udpOutErrors);
6506ff550d0eSmasputra 				UDP_STAT(udp_out_err_tudr);
6507ff550d0eSmasputra 				freemsg(mp);
6508ff550d0eSmasputra 				return;
6509ff550d0eSmasputra 			}
6510ff550d0eSmasputra 			/* Tag the packet with T_UNITDATA_REQ */
6511ff550d0eSmasputra 			tudr_mp->b_cont = mp;
6512ff550d0eSmasputra 			mp = tudr_mp;
6513ff550d0eSmasputra 		}
6514ff550d0eSmasputra 		mutex_exit(&connp->conn_lock);
6515ff550d0eSmasputra 		udp_enter(connp, mp, udp_output_wrapper, SQTAG_UDP_WPUT);
6516ff550d0eSmasputra 		return;
6517ff550d0eSmasputra 	}
6518ff550d0eSmasputra 
6519ff550d0eSmasputra 	/* We can execute as reader right away. */
6520ff550d0eSmasputra 	UDP_READERS_INCREF(udp);
6521ff550d0eSmasputra 	mutex_exit(&connp->conn_lock);
6522ff550d0eSmasputra 
6523ff550d0eSmasputra 	udp_output(connp, mp, addr, addrlen);
6524ff550d0eSmasputra 
6525ff550d0eSmasputra 	mutex_enter(&connp->conn_lock);
6526ff550d0eSmasputra 	UDP_MODE_ASSERTIONS(udp, UDP_EXIT);
6527ff550d0eSmasputra 	UDP_READERS_DECREF(udp);
6528ff550d0eSmasputra 	mutex_exit(&connp->conn_lock);
6529ff550d0eSmasputra }
6530ff550d0eSmasputra 
6531ff550d0eSmasputra /*
6532ff550d0eSmasputra  * udp_output_v6():
6533ff550d0eSmasputra  * Assumes that udp_wput did some sanity checking on the destination
6534ff550d0eSmasputra  * address.
6535ff550d0eSmasputra  */
6536ff550d0eSmasputra static mblk_t *
6537ff550d0eSmasputra udp_output_v6(conn_t *connp, mblk_t *mp, sin6_t *sin6, t_scalar_t tudr_optlen,
6538ff550d0eSmasputra     int *error)
6539ff550d0eSmasputra {
6540ff550d0eSmasputra 	ip6_t		*ip6h;
6541ff550d0eSmasputra 	ip6i_t		*ip6i;	/* mp1->b_rptr even if no ip6i_t */
6542ff550d0eSmasputra 	mblk_t		*mp1 = (DB_TYPE(mp) == M_DATA ? mp : mp->b_cont);
6543ff550d0eSmasputra 	mblk_t		*mp2;
6544ff550d0eSmasputra 	int		udp_ip_hdr_len = IPV6_HDR_LEN + UDPH_SIZE;
6545ff550d0eSmasputra 	size_t		ip_len;
6546ff550d0eSmasputra 	udpha_t		*udph;
6547ff550d0eSmasputra 	udp_t		*udp = connp->conn_udp;
6548ff550d0eSmasputra 	queue_t		*q = connp->conn_wq;
6549ff550d0eSmasputra 	ip6_pkt_t	ipp_s;	/* For ancillary data options */
6550ff550d0eSmasputra 	ip6_pkt_t	*ipp = &ipp_s;
6551ff550d0eSmasputra 	ip6_pkt_t	*tipp;	/* temporary ipp */
6552ff550d0eSmasputra 	uint32_t	csum = 0;
6553ff550d0eSmasputra 	uint_t		ignore = 0;
6554ff550d0eSmasputra 	uint_t		option_exists = 0, is_sticky = 0;
6555ff550d0eSmasputra 	uint8_t		*cp;
6556ff550d0eSmasputra 	uint8_t		*nxthdr_ptr;
6557ff550d0eSmasputra 
6558ff550d0eSmasputra 	*error = 0;
6559ff550d0eSmasputra 
6560ff550d0eSmasputra 	/* mp1 points to the M_DATA mblk carrying the packet */
6561ff550d0eSmasputra 	ASSERT(mp1 != NULL && DB_TYPE(mp1) == M_DATA);
6562ff550d0eSmasputra 	ASSERT(tudr_optlen == 0 || DB_TYPE(mp) != M_DATA);
65637c478bd9Sstevel@tonic-gate 
65647c478bd9Sstevel@tonic-gate 	/*
65657c478bd9Sstevel@tonic-gate 	 * If the local address is a mapped address return
65667c478bd9Sstevel@tonic-gate 	 * an error.
65677c478bd9Sstevel@tonic-gate 	 * It would be possible to send an IPv6 packet but the
65687c478bd9Sstevel@tonic-gate 	 * response would never make it back to the application
65697c478bd9Sstevel@tonic-gate 	 * since it is bound to a mapped address.
65707c478bd9Sstevel@tonic-gate 	 */
65717c478bd9Sstevel@tonic-gate 	if (IN6_IS_ADDR_V4MAPPED(&udp->udp_v6src)) {
6572ff550d0eSmasputra 		*error = EADDRNOTAVAIL;
6573ff550d0eSmasputra 		goto done;
65747c478bd9Sstevel@tonic-gate 	}
65757c478bd9Sstevel@tonic-gate 
65767c478bd9Sstevel@tonic-gate 	ipp->ipp_fields = 0;
65777c478bd9Sstevel@tonic-gate 	ipp->ipp_sticky_ignored = 0;
65787c478bd9Sstevel@tonic-gate 
65797c478bd9Sstevel@tonic-gate 	/*
65807c478bd9Sstevel@tonic-gate 	 * If TPI options passed in, feed it for verification and handling
65817c478bd9Sstevel@tonic-gate 	 */
65827c478bd9Sstevel@tonic-gate 	if (tudr_optlen != 0) {
6583ff550d0eSmasputra 		if (udp_unitdata_opt_process(q, mp, error, (void *)ipp) < 0) {
65847c478bd9Sstevel@tonic-gate 			/* failure */
6585ff550d0eSmasputra 			goto done;
65867c478bd9Sstevel@tonic-gate 		}
65877c478bd9Sstevel@tonic-gate 		ignore = ipp->ipp_sticky_ignored;
6588ff550d0eSmasputra 		ASSERT(*error == 0);
65897c478bd9Sstevel@tonic-gate 	}
65907c478bd9Sstevel@tonic-gate 
65917c478bd9Sstevel@tonic-gate 	if (sin6->sin6_scope_id != 0 &&
65927c478bd9Sstevel@tonic-gate 	    IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
65937c478bd9Sstevel@tonic-gate 		/*
65947c478bd9Sstevel@tonic-gate 		 * IPPF_SCOPE_ID is special.  It's neither a sticky
65957c478bd9Sstevel@tonic-gate 		 * option nor ancillary data.  It needs to be
65967c478bd9Sstevel@tonic-gate 		 * explicitly set in options_exists.
65977c478bd9Sstevel@tonic-gate 		 */
65987c478bd9Sstevel@tonic-gate 		option_exists |= IPPF_SCOPE_ID;
65997c478bd9Sstevel@tonic-gate 	}
66007c478bd9Sstevel@tonic-gate 
6601ff550d0eSmasputra 	if ((udp->udp_sticky_ipp.ipp_fields == 0) && (ipp->ipp_fields == 0)) {
66027c478bd9Sstevel@tonic-gate 		/* No sticky options nor ancillary data. */
66037c478bd9Sstevel@tonic-gate 		goto no_options;
66047c478bd9Sstevel@tonic-gate 	}
66057c478bd9Sstevel@tonic-gate 
66067c478bd9Sstevel@tonic-gate 	/*
66077c478bd9Sstevel@tonic-gate 	 * Go through the options figuring out where each is going to
66087c478bd9Sstevel@tonic-gate 	 * come from and build two masks.  The first mask indicates if
66097c478bd9Sstevel@tonic-gate 	 * the option exists at all.  The second mask indicates if the
66107c478bd9Sstevel@tonic-gate 	 * option is sticky or ancillary.
66117c478bd9Sstevel@tonic-gate 	 */
66127c478bd9Sstevel@tonic-gate 	if (!(ignore & IPPF_HOPOPTS)) {
66137c478bd9Sstevel@tonic-gate 		if (ipp->ipp_fields & IPPF_HOPOPTS) {
66147c478bd9Sstevel@tonic-gate 			option_exists |= IPPF_HOPOPTS;
66157c478bd9Sstevel@tonic-gate 			udp_ip_hdr_len += ipp->ipp_hopoptslen;
66167c478bd9Sstevel@tonic-gate 		} else if (udp->udp_sticky_ipp.ipp_fields & IPPF_HOPOPTS) {
66177c478bd9Sstevel@tonic-gate 			option_exists |= IPPF_HOPOPTS;
66187c478bd9Sstevel@tonic-gate 			is_sticky |= IPPF_HOPOPTS;
66197c478bd9Sstevel@tonic-gate 			udp_ip_hdr_len += udp->udp_sticky_ipp.ipp_hopoptslen;
66207c478bd9Sstevel@tonic-gate 		}
66217c478bd9Sstevel@tonic-gate 	}
66227c478bd9Sstevel@tonic-gate 
66237c478bd9Sstevel@tonic-gate 	if (!(ignore & IPPF_RTHDR)) {
66247c478bd9Sstevel@tonic-gate 		if (ipp->ipp_fields & IPPF_RTHDR) {
66257c478bd9Sstevel@tonic-gate 			option_exists |= IPPF_RTHDR;
66267c478bd9Sstevel@tonic-gate 			udp_ip_hdr_len += ipp->ipp_rthdrlen;
66277c478bd9Sstevel@tonic-gate 		} else if (udp->udp_sticky_ipp.ipp_fields & IPPF_RTHDR) {
66287c478bd9Sstevel@tonic-gate 			option_exists |= IPPF_RTHDR;
66297c478bd9Sstevel@tonic-gate 			is_sticky |= IPPF_RTHDR;
66307c478bd9Sstevel@tonic-gate 			udp_ip_hdr_len += udp->udp_sticky_ipp.ipp_rthdrlen;
66317c478bd9Sstevel@tonic-gate 		}
66327c478bd9Sstevel@tonic-gate 	}
66337c478bd9Sstevel@tonic-gate 
66347c478bd9Sstevel@tonic-gate 	if (!(ignore & IPPF_RTDSTOPTS) && (option_exists & IPPF_RTHDR)) {
66357c478bd9Sstevel@tonic-gate 		if (ipp->ipp_fields & IPPF_RTDSTOPTS) {
66367c478bd9Sstevel@tonic-gate 			option_exists |= IPPF_RTDSTOPTS;
66377c478bd9Sstevel@tonic-gate 			udp_ip_hdr_len += ipp->ipp_rtdstoptslen;
66387c478bd9Sstevel@tonic-gate 		} else if (udp->udp_sticky_ipp.ipp_fields & IPPF_RTDSTOPTS) {
66397c478bd9Sstevel@tonic-gate 			option_exists |= IPPF_RTDSTOPTS;
66407c478bd9Sstevel@tonic-gate 			is_sticky |= IPPF_RTDSTOPTS;
66417c478bd9Sstevel@tonic-gate 			udp_ip_hdr_len += udp->udp_sticky_ipp.ipp_rtdstoptslen;
66427c478bd9Sstevel@tonic-gate 		}
66437c478bd9Sstevel@tonic-gate 	}
66447c478bd9Sstevel@tonic-gate 
66457c478bd9Sstevel@tonic-gate 	if (!(ignore & IPPF_DSTOPTS)) {
66467c478bd9Sstevel@tonic-gate 		if (ipp->ipp_fields & IPPF_DSTOPTS) {
66477c478bd9Sstevel@tonic-gate 			option_exists |= IPPF_DSTOPTS;
66487c478bd9Sstevel@tonic-gate 			udp_ip_hdr_len += ipp->ipp_dstoptslen;
66497c478bd9Sstevel@tonic-gate 		} else if (udp->udp_sticky_ipp.ipp_fields & IPPF_DSTOPTS) {
66507c478bd9Sstevel@tonic-gate 			option_exists |= IPPF_DSTOPTS;
66517c478bd9Sstevel@tonic-gate 			is_sticky |= IPPF_DSTOPTS;
66527c478bd9Sstevel@tonic-gate 			udp_ip_hdr_len += udp->udp_sticky_ipp.ipp_dstoptslen;
66537c478bd9Sstevel@tonic-gate 		}
66547c478bd9Sstevel@tonic-gate 	}
66557c478bd9Sstevel@tonic-gate 
66567c478bd9Sstevel@tonic-gate 	if (!(ignore & IPPF_IFINDEX)) {
66577c478bd9Sstevel@tonic-gate 		if (ipp->ipp_fields & IPPF_IFINDEX) {
66587c478bd9Sstevel@tonic-gate 			option_exists |= IPPF_IFINDEX;
66597c478bd9Sstevel@tonic-gate 		} else if (udp->udp_sticky_ipp.ipp_fields & IPPF_IFINDEX) {
66607c478bd9Sstevel@tonic-gate 			option_exists |= IPPF_IFINDEX;
66617c478bd9Sstevel@tonic-gate 			is_sticky |= IPPF_IFINDEX;
66627c478bd9Sstevel@tonic-gate 		}
66637c478bd9Sstevel@tonic-gate 	}
66647c478bd9Sstevel@tonic-gate 
66657c478bd9Sstevel@tonic-gate 	if (!(ignore & IPPF_ADDR)) {
66667c478bd9Sstevel@tonic-gate 		if (ipp->ipp_fields & IPPF_ADDR) {
66677c478bd9Sstevel@tonic-gate 			option_exists |= IPPF_ADDR;
66687c478bd9Sstevel@tonic-gate 		} else if (udp->udp_sticky_ipp.ipp_fields & IPPF_ADDR) {
66697c478bd9Sstevel@tonic-gate 			option_exists |= IPPF_ADDR;
66707c478bd9Sstevel@tonic-gate 			is_sticky |= IPPF_ADDR;
66717c478bd9Sstevel@tonic-gate 		}
66727c478bd9Sstevel@tonic-gate 	}
66737c478bd9Sstevel@tonic-gate 
66747c478bd9Sstevel@tonic-gate 	if (!(ignore & IPPF_DONTFRAG)) {
66757c478bd9Sstevel@tonic-gate 		if (ipp->ipp_fields & IPPF_DONTFRAG) {
66767c478bd9Sstevel@tonic-gate 			option_exists |= IPPF_DONTFRAG;
66777c478bd9Sstevel@tonic-gate 		} else if (udp->udp_sticky_ipp.ipp_fields & IPPF_DONTFRAG) {
66787c478bd9Sstevel@tonic-gate 			option_exists |= IPPF_DONTFRAG;
66797c478bd9Sstevel@tonic-gate 			is_sticky |= IPPF_DONTFRAG;
66807c478bd9Sstevel@tonic-gate 		}
66817c478bd9Sstevel@tonic-gate 	}
66827c478bd9Sstevel@tonic-gate 
66837c478bd9Sstevel@tonic-gate 	if (!(ignore & IPPF_USE_MIN_MTU)) {
66847c478bd9Sstevel@tonic-gate 		if (ipp->ipp_fields & IPPF_USE_MIN_MTU) {
66857c478bd9Sstevel@tonic-gate 			option_exists |= IPPF_USE_MIN_MTU;
6686ff550d0eSmasputra 		} else if (udp->udp_sticky_ipp.ipp_fields &
6687ff550d0eSmasputra 		    IPPF_USE_MIN_MTU) {
66887c478bd9Sstevel@tonic-gate 			option_exists |= IPPF_USE_MIN_MTU;
66897c478bd9Sstevel@tonic-gate 			is_sticky |= IPPF_USE_MIN_MTU;
66907c478bd9Sstevel@tonic-gate 		}
66917c478bd9Sstevel@tonic-gate 	}
66927c478bd9Sstevel@tonic-gate 
6693b3d0fa4fSseb 	if (!(ignore & IPPF_HOPLIMIT) && (ipp->ipp_fields & IPPF_HOPLIMIT))
6694b3d0fa4fSseb 		option_exists |= IPPF_HOPLIMIT;
6695b3d0fa4fSseb 	/* IPV6_HOPLIMIT can never be sticky */
6696b3d0fa4fSseb 	ASSERT(!(udp->udp_sticky_ipp.ipp_fields & IPPF_HOPLIMIT));
6697b3d0fa4fSseb 
6698b3d0fa4fSseb 	if (!(ignore & IPPF_UNICAST_HOPS) &&
6699b3d0fa4fSseb 	    (udp->udp_sticky_ipp.ipp_fields & IPPF_UNICAST_HOPS)) {
6700b3d0fa4fSseb 		option_exists |= IPPF_UNICAST_HOPS;
6701b3d0fa4fSseb 		is_sticky |= IPPF_UNICAST_HOPS;
6702b3d0fa4fSseb 	}
6703b3d0fa4fSseb 
6704b3d0fa4fSseb 	if (!(ignore & IPPF_MULTICAST_HOPS) &&
6705b3d0fa4fSseb 	    (udp->udp_sticky_ipp.ipp_fields & IPPF_MULTICAST_HOPS)) {
6706b3d0fa4fSseb 		option_exists |= IPPF_MULTICAST_HOPS;
6707b3d0fa4fSseb 		is_sticky |= IPPF_MULTICAST_HOPS;
67087c478bd9Sstevel@tonic-gate 	}
67097c478bd9Sstevel@tonic-gate 
67107c478bd9Sstevel@tonic-gate 	if (!(ignore & IPPF_TCLASS)) {
67117c478bd9Sstevel@tonic-gate 		if (ipp->ipp_fields & IPPF_TCLASS) {
67127c478bd9Sstevel@tonic-gate 			option_exists |= IPPF_TCLASS;
67137c478bd9Sstevel@tonic-gate 		} else if (udp->udp_sticky_ipp.ipp_fields & IPPF_TCLASS) {
67147c478bd9Sstevel@tonic-gate 			option_exists |= IPPF_TCLASS;
67157c478bd9Sstevel@tonic-gate 			is_sticky |= IPPF_TCLASS;
67167c478bd9Sstevel@tonic-gate 		}
67177c478bd9Sstevel@tonic-gate 	}
67187c478bd9Sstevel@tonic-gate 
67197c478bd9Sstevel@tonic-gate no_options:
67207c478bd9Sstevel@tonic-gate 
67217c478bd9Sstevel@tonic-gate 	/*
67227c478bd9Sstevel@tonic-gate 	 * If any options carried in the ip6i_t were specified, we
67237c478bd9Sstevel@tonic-gate 	 * need to account for the ip6i_t in the data we'll be sending
67247c478bd9Sstevel@tonic-gate 	 * down.
67257c478bd9Sstevel@tonic-gate 	 */
67267c478bd9Sstevel@tonic-gate 	if (option_exists & IPPF_HAS_IP6I)
67277c478bd9Sstevel@tonic-gate 		udp_ip_hdr_len += sizeof (ip6i_t);
67287c478bd9Sstevel@tonic-gate 
67297c478bd9Sstevel@tonic-gate 	/* check/fix buffer config, setup pointers into it */
67307c478bd9Sstevel@tonic-gate 	ip6h = (ip6_t *)&mp1->b_rptr[-udp_ip_hdr_len];
6731ff550d0eSmasputra 	if (DB_REF(mp1) != 1 || ((unsigned char *)ip6h < DB_BASE(mp1)) ||
67327c478bd9Sstevel@tonic-gate 	    !OK_32PTR(ip6h)) {
67337c478bd9Sstevel@tonic-gate 		/* Try to get everything in a single mblk next time */
67347c478bd9Sstevel@tonic-gate 		if (udp_ip_hdr_len > udp->udp_max_hdr_len) {
67357c478bd9Sstevel@tonic-gate 			udp->udp_max_hdr_len = udp_ip_hdr_len;
6736ff550d0eSmasputra 			(void) mi_set_sth_wroff(UDP_RD(q),
67377c478bd9Sstevel@tonic-gate 			    udp->udp_max_hdr_len + udp_wroff_extra);
67387c478bd9Sstevel@tonic-gate 		}
6739ff550d0eSmasputra 		mp2 = allocb(udp_ip_hdr_len + udp_wroff_extra, BPRI_LO);
6740ff550d0eSmasputra 		if (mp2 == NULL) {
6741ff550d0eSmasputra 			*error = ENOMEM;
6742ff550d0eSmasputra 			goto done;
67437c478bd9Sstevel@tonic-gate 		}
6744ff550d0eSmasputra 		mp2->b_wptr = DB_LIM(mp2);
6745ff550d0eSmasputra 		mp2->b_cont = mp1;
6746ff550d0eSmasputra 		mp1 = mp2;
6747ff550d0eSmasputra 		if (DB_TYPE(mp) != M_DATA)
6748ff550d0eSmasputra 			mp->b_cont = mp1;
6749ff550d0eSmasputra 		else
6750ff550d0eSmasputra 			mp = mp1;
6751ff550d0eSmasputra 
67527c478bd9Sstevel@tonic-gate 		ip6h = (ip6_t *)(mp1->b_wptr - udp_ip_hdr_len);
67537c478bd9Sstevel@tonic-gate 	}
67547c478bd9Sstevel@tonic-gate 	mp1->b_rptr = (unsigned char *)ip6h;
67557c478bd9Sstevel@tonic-gate 	ip6i = (ip6i_t *)ip6h;
67567c478bd9Sstevel@tonic-gate 
67577c478bd9Sstevel@tonic-gate #define	ANCIL_OR_STICKY_PTR(f) ((is_sticky & f) ? &udp->udp_sticky_ipp : ipp)
67587c478bd9Sstevel@tonic-gate 	if (option_exists & IPPF_HAS_IP6I) {
67597c478bd9Sstevel@tonic-gate 		ip6h = (ip6_t *)&ip6i[1];
67607c478bd9Sstevel@tonic-gate 		ip6i->ip6i_flags = 0;
67617c478bd9Sstevel@tonic-gate 		ip6i->ip6i_vcf = IPV6_DEFAULT_VERS_AND_FLOW;
67627c478bd9Sstevel@tonic-gate 
67637c478bd9Sstevel@tonic-gate 		/* sin6_scope_id takes precendence over IPPF_IFINDEX */
67647c478bd9Sstevel@tonic-gate 		if (option_exists & IPPF_SCOPE_ID) {
67657c478bd9Sstevel@tonic-gate 			ip6i->ip6i_flags |= IP6I_IFINDEX;
67667c478bd9Sstevel@tonic-gate 			ip6i->ip6i_ifindex = sin6->sin6_scope_id;
67677c478bd9Sstevel@tonic-gate 		} else if (option_exists & IPPF_IFINDEX) {
67687c478bd9Sstevel@tonic-gate 			tipp = ANCIL_OR_STICKY_PTR(IPPF_IFINDEX);
67697c478bd9Sstevel@tonic-gate 			ASSERT(tipp->ipp_ifindex != 0);
67707c478bd9Sstevel@tonic-gate 			ip6i->ip6i_flags |= IP6I_IFINDEX;
67717c478bd9Sstevel@tonic-gate 			ip6i->ip6i_ifindex = tipp->ipp_ifindex;
67727c478bd9Sstevel@tonic-gate 		}
67737c478bd9Sstevel@tonic-gate 
67747c478bd9Sstevel@tonic-gate 		if (option_exists & IPPF_ADDR) {
67757c478bd9Sstevel@tonic-gate 			/*
67767c478bd9Sstevel@tonic-gate 			 * Enable per-packet source address verification if
67777c478bd9Sstevel@tonic-gate 			 * IPV6_PKTINFO specified the source address.
67787c478bd9Sstevel@tonic-gate 			 * ip6_src is set in the transport's _wput function.
67797c478bd9Sstevel@tonic-gate 			 */
67807c478bd9Sstevel@tonic-gate 			ip6i->ip6i_flags |= IP6I_VERIFY_SRC;
67817c478bd9Sstevel@tonic-gate 		}
67827c478bd9Sstevel@tonic-gate 
67837c478bd9Sstevel@tonic-gate 		if (option_exists & IPPF_DONTFRAG) {
67847c478bd9Sstevel@tonic-gate 			ip6i->ip6i_flags |= IP6I_DONTFRAG;
67857c478bd9Sstevel@tonic-gate 		}
67867c478bd9Sstevel@tonic-gate 
67877c478bd9Sstevel@tonic-gate 		if (option_exists & IPPF_USE_MIN_MTU) {
67887c478bd9Sstevel@tonic-gate 			ip6i->ip6i_flags = IP6I_API_USE_MIN_MTU(
67897c478bd9Sstevel@tonic-gate 			    ip6i->ip6i_flags, ipp->ipp_use_min_mtu);
67907c478bd9Sstevel@tonic-gate 		}
67917c478bd9Sstevel@tonic-gate 
67927c478bd9Sstevel@tonic-gate 		if (option_exists & IPPF_NEXTHOP) {
67937c478bd9Sstevel@tonic-gate 			tipp = ANCIL_OR_STICKY_PTR(IPPF_NEXTHOP);
67947c478bd9Sstevel@tonic-gate 			ASSERT(!IN6_IS_ADDR_UNSPECIFIED(&tipp->ipp_nexthop));
67957c478bd9Sstevel@tonic-gate 			ip6i->ip6i_flags |= IP6I_NEXTHOP;
67967c478bd9Sstevel@tonic-gate 			ip6i->ip6i_nexthop = tipp->ipp_nexthop;
67977c478bd9Sstevel@tonic-gate 		}
67987c478bd9Sstevel@tonic-gate 
67997c478bd9Sstevel@tonic-gate 		/*
68007c478bd9Sstevel@tonic-gate 		 * tell IP this is an ip6i_t private header
68017c478bd9Sstevel@tonic-gate 		 */
68027c478bd9Sstevel@tonic-gate 		ip6i->ip6i_nxt = IPPROTO_RAW;
68037c478bd9Sstevel@tonic-gate 	}
68047c478bd9Sstevel@tonic-gate 
68057c478bd9Sstevel@tonic-gate 	/* Initialize IPv6 header */
68067c478bd9Sstevel@tonic-gate 	ip6h->ip6_vcf = IPV6_DEFAULT_VERS_AND_FLOW;
68077c478bd9Sstevel@tonic-gate 	bzero(&ip6h->ip6_src, sizeof (ip6h->ip6_src));
68087c478bd9Sstevel@tonic-gate 
6809b3d0fa4fSseb 	/* Set the hoplimit of the outgoing packet. */
68107c478bd9Sstevel@tonic-gate 	if (option_exists & IPPF_HOPLIMIT) {
6811b3d0fa4fSseb 		/* IPV6_HOPLIMIT ancillary data overrides all other settings. */
6812b3d0fa4fSseb 		ip6h->ip6_hops = ipp->ipp_hoplimit;
68137c478bd9Sstevel@tonic-gate 		ip6i->ip6i_flags |= IP6I_HOPLIMIT;
6814b3d0fa4fSseb 	} else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) {
6815b3d0fa4fSseb 		ip6h->ip6_hops = udp->udp_multicast_ttl;
6816b3d0fa4fSseb 		if (option_exists & IPPF_MULTICAST_HOPS)
6817b3d0fa4fSseb 			ip6i->ip6i_flags |= IP6I_HOPLIMIT;
68187c478bd9Sstevel@tonic-gate 	} else {
68197c478bd9Sstevel@tonic-gate 		ip6h->ip6_hops = udp->udp_ttl;
6820b3d0fa4fSseb 		if (option_exists & IPPF_UNICAST_HOPS)
6821b3d0fa4fSseb 			ip6i->ip6i_flags |= IP6I_HOPLIMIT;
68227c478bd9Sstevel@tonic-gate 	}
68237c478bd9Sstevel@tonic-gate 
68247c478bd9Sstevel@tonic-gate 	if (option_exists & IPPF_ADDR) {
68257c478bd9Sstevel@tonic-gate 		tipp = ANCIL_OR_STICKY_PTR(IPPF_ADDR);
68267c478bd9Sstevel@tonic-gate 		ASSERT(!IN6_IS_ADDR_UNSPECIFIED(&tipp->ipp_addr));
68277c478bd9Sstevel@tonic-gate 		ip6h->ip6_src = tipp->ipp_addr;
68287c478bd9Sstevel@tonic-gate 	} else {
68297c478bd9Sstevel@tonic-gate 		/*
68307c478bd9Sstevel@tonic-gate 		 * The source address was not set using IPV6_PKTINFO.
68317c478bd9Sstevel@tonic-gate 		 * First look at the bound source.
68327c478bd9Sstevel@tonic-gate 		 * If unspecified fallback to __sin6_src_id.
68337c478bd9Sstevel@tonic-gate 		 */
68347c478bd9Sstevel@tonic-gate 		ip6h->ip6_src = udp->udp_v6src;
68357c478bd9Sstevel@tonic-gate 		if (sin6->__sin6_src_id != 0 &&
68367c478bd9Sstevel@tonic-gate 		    IN6_IS_ADDR_UNSPECIFIED(&ip6h->ip6_src)) {
68377c478bd9Sstevel@tonic-gate 			ip_srcid_find_id(sin6->__sin6_src_id,
6838ff550d0eSmasputra 			    &ip6h->ip6_src, connp->conn_zoneid);
68397c478bd9Sstevel@tonic-gate 		}
68407c478bd9Sstevel@tonic-gate 	}
68417c478bd9Sstevel@tonic-gate 
68427c478bd9Sstevel@tonic-gate 	nxthdr_ptr = (uint8_t *)&ip6h->ip6_nxt;
68437c478bd9Sstevel@tonic-gate 	cp = (uint8_t *)&ip6h[1];
68447c478bd9Sstevel@tonic-gate 
68457c478bd9Sstevel@tonic-gate 	/*
68467c478bd9Sstevel@tonic-gate 	 * Here's where we have to start stringing together
68477c478bd9Sstevel@tonic-gate 	 * any extension headers in the right order:
68487c478bd9Sstevel@tonic-gate 	 * Hop-by-hop, destination, routing, and final destination opts.
68497c478bd9Sstevel@tonic-gate 	 */
68507c478bd9Sstevel@tonic-gate 	if (option_exists & IPPF_HOPOPTS) {
68517c478bd9Sstevel@tonic-gate 		/* Hop-by-hop options */
68527c478bd9Sstevel@tonic-gate 		ip6_hbh_t *hbh = (ip6_hbh_t *)cp;
68537c478bd9Sstevel@tonic-gate 		tipp = ANCIL_OR_STICKY_PTR(IPPF_HOPOPTS);
68547c478bd9Sstevel@tonic-gate 
68557c478bd9Sstevel@tonic-gate 		*nxthdr_ptr = IPPROTO_HOPOPTS;
68567c478bd9Sstevel@tonic-gate 		nxthdr_ptr = &hbh->ip6h_nxt;
68577c478bd9Sstevel@tonic-gate 
68587c478bd9Sstevel@tonic-gate 		bcopy(tipp->ipp_hopopts, cp, tipp->ipp_hopoptslen);
68597c478bd9Sstevel@tonic-gate 		cp += tipp->ipp_hopoptslen;
68607c478bd9Sstevel@tonic-gate 	}
68617c478bd9Sstevel@tonic-gate 	/*
68627c478bd9Sstevel@tonic-gate 	 * En-route destination options
68637c478bd9Sstevel@tonic-gate 	 * Only do them if there's a routing header as well
68647c478bd9Sstevel@tonic-gate 	 */
68657c478bd9Sstevel@tonic-gate 	if (option_exists & IPPF_RTDSTOPTS) {
68667c478bd9Sstevel@tonic-gate 		ip6_dest_t *dst = (ip6_dest_t *)cp;
68677c478bd9Sstevel@tonic-gate 		tipp = ANCIL_OR_STICKY_PTR(IPPF_RTDSTOPTS);
68687c478bd9Sstevel@tonic-gate 
68697c478bd9Sstevel@tonic-gate 		*nxthdr_ptr = IPPROTO_DSTOPTS;
68707c478bd9Sstevel@tonic-gate 		nxthdr_ptr = &dst->ip6d_nxt;
68717c478bd9Sstevel@tonic-gate 
68727c478bd9Sstevel@tonic-gate 		bcopy(tipp->ipp_rtdstopts, cp, tipp->ipp_rtdstoptslen);
68737c478bd9Sstevel@tonic-gate 		cp += tipp->ipp_rtdstoptslen;
68747c478bd9Sstevel@tonic-gate 	}
68757c478bd9Sstevel@tonic-gate 	/*
68767c478bd9Sstevel@tonic-gate 	 * Routing header next
68777c478bd9Sstevel@tonic-gate 	 */
68787c478bd9Sstevel@tonic-gate 	if (option_exists & IPPF_RTHDR) {
68797c478bd9Sstevel@tonic-gate 		ip6_rthdr_t *rt = (ip6_rthdr_t *)cp;
68807c478bd9Sstevel@tonic-gate 		tipp = ANCIL_OR_STICKY_PTR(IPPF_RTHDR);
68817c478bd9Sstevel@tonic-gate 
68827c478bd9Sstevel@tonic-gate 		*nxthdr_ptr = IPPROTO_ROUTING;
68837c478bd9Sstevel@tonic-gate 		nxthdr_ptr = &rt->ip6r_nxt;
68847c478bd9Sstevel@tonic-gate 
68857c478bd9Sstevel@tonic-gate 		bcopy(tipp->ipp_rthdr, cp, tipp->ipp_rthdrlen);
68867c478bd9Sstevel@tonic-gate 		cp += tipp->ipp_rthdrlen;
68877c478bd9Sstevel@tonic-gate 	}
68887c478bd9Sstevel@tonic-gate 	/*
68897c478bd9Sstevel@tonic-gate 	 * Do ultimate destination options
68907c478bd9Sstevel@tonic-gate 	 */
68917c478bd9Sstevel@tonic-gate 	if (option_exists & IPPF_DSTOPTS) {
68927c478bd9Sstevel@tonic-gate 		ip6_dest_t *dest = (ip6_dest_t *)cp;
68937c478bd9Sstevel@tonic-gate 		tipp = ANCIL_OR_STICKY_PTR(IPPF_DSTOPTS);
68947c478bd9Sstevel@tonic-gate 
68957c478bd9Sstevel@tonic-gate 		*nxthdr_ptr = IPPROTO_DSTOPTS;
68967c478bd9Sstevel@tonic-gate 		nxthdr_ptr = &dest->ip6d_nxt;
68977c478bd9Sstevel@tonic-gate 
68987c478bd9Sstevel@tonic-gate 		bcopy(tipp->ipp_dstopts, cp, tipp->ipp_dstoptslen);
68997c478bd9Sstevel@tonic-gate 		cp += tipp->ipp_dstoptslen;
69007c478bd9Sstevel@tonic-gate 	}
69017c478bd9Sstevel@tonic-gate 	/*
69027c478bd9Sstevel@tonic-gate 	 * Now set the last header pointer to the proto passed in
69037c478bd9Sstevel@tonic-gate 	 */
69047c478bd9Sstevel@tonic-gate 	ASSERT((int)(cp - (uint8_t *)ip6i) == (udp_ip_hdr_len - UDPH_SIZE));
69057c478bd9Sstevel@tonic-gate 	*nxthdr_ptr = IPPROTO_UDP;
69067c478bd9Sstevel@tonic-gate 
69077c478bd9Sstevel@tonic-gate 	/* Update UDP header */
69087c478bd9Sstevel@tonic-gate 	udph = (udpha_t *)((uchar_t *)ip6i + udp_ip_hdr_len - UDPH_SIZE);
69097c478bd9Sstevel@tonic-gate 	udph->uha_dst_port = sin6->sin6_port;
69107c478bd9Sstevel@tonic-gate 	udph->uha_src_port = udp->udp_port;
69117c478bd9Sstevel@tonic-gate 
69127c478bd9Sstevel@tonic-gate 	/*
69137c478bd9Sstevel@tonic-gate 	 * Copy in the destination address
69147c478bd9Sstevel@tonic-gate 	 */
69157c478bd9Sstevel@tonic-gate 	if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr))
69167c478bd9Sstevel@tonic-gate 		ip6h->ip6_dst = ipv6_loopback;
69177c478bd9Sstevel@tonic-gate 	else
69187c478bd9Sstevel@tonic-gate 		ip6h->ip6_dst = sin6->sin6_addr;
69197c478bd9Sstevel@tonic-gate 
69207c478bd9Sstevel@tonic-gate 	ip6h->ip6_vcf =
69217c478bd9Sstevel@tonic-gate 	    (IPV6_DEFAULT_VERS_AND_FLOW & IPV6_VERS_AND_FLOW_MASK) |
69227c478bd9Sstevel@tonic-gate 	    (sin6->sin6_flowinfo & ~IPV6_VERS_AND_FLOW_MASK);
69237c478bd9Sstevel@tonic-gate 
69247c478bd9Sstevel@tonic-gate 	if (option_exists & IPPF_TCLASS) {
69257c478bd9Sstevel@tonic-gate 		tipp = ANCIL_OR_STICKY_PTR(IPPF_TCLASS);
69267c478bd9Sstevel@tonic-gate 		ip6h->ip6_vcf = IPV6_TCLASS_FLOW(ip6h->ip6_vcf,
69277c478bd9Sstevel@tonic-gate 		    tipp->ipp_tclass);
69287c478bd9Sstevel@tonic-gate 	}
69297c478bd9Sstevel@tonic-gate 
69307c478bd9Sstevel@tonic-gate 	if (option_exists & IPPF_RTHDR) {
69317c478bd9Sstevel@tonic-gate 		ip6_rthdr_t	*rth;
69327c478bd9Sstevel@tonic-gate 
69337c478bd9Sstevel@tonic-gate 		/*
69347c478bd9Sstevel@tonic-gate 		 * Perform any processing needed for source routing.
69357c478bd9Sstevel@tonic-gate 		 * We know that all extension headers will be in the same mblk
69367c478bd9Sstevel@tonic-gate 		 * as the IPv6 header.
69377c478bd9Sstevel@tonic-gate 		 */
69387c478bd9Sstevel@tonic-gate 		rth = ip_find_rthdr_v6(ip6h, mp1->b_wptr);
69397c478bd9Sstevel@tonic-gate 		if (rth != NULL && rth->ip6r_segleft != 0) {
69407c478bd9Sstevel@tonic-gate 			if (rth->ip6r_type != IPV6_RTHDR_TYPE_0) {
69417c478bd9Sstevel@tonic-gate 				/*
69427c478bd9Sstevel@tonic-gate 				 * Drop packet - only support Type 0 routing.
69437c478bd9Sstevel@tonic-gate 				 * Notify the application as well.
69447c478bd9Sstevel@tonic-gate 				 */
6945ff550d0eSmasputra 				*error = EPROTO;
6946ff550d0eSmasputra 				goto done;
69477c478bd9Sstevel@tonic-gate 			}
69487c478bd9Sstevel@tonic-gate 
69497c478bd9Sstevel@tonic-gate 			/*
69507c478bd9Sstevel@tonic-gate 			 * rth->ip6r_len is twice the number of
69517c478bd9Sstevel@tonic-gate 			 * addresses in the header. Thus it must be even.
69527c478bd9Sstevel@tonic-gate 			 */
69537c478bd9Sstevel@tonic-gate 			if (rth->ip6r_len & 0x1) {
6954ff550d0eSmasputra 				*error = EPROTO;
6955ff550d0eSmasputra 				goto done;
69567c478bd9Sstevel@tonic-gate 			}
69577c478bd9Sstevel@tonic-gate 			/*
69587c478bd9Sstevel@tonic-gate 			 * Shuffle the routing header and ip6_dst
69597c478bd9Sstevel@tonic-gate 			 * addresses, and get the checksum difference
69607c478bd9Sstevel@tonic-gate 			 * between the first hop (in ip6_dst) and
69617c478bd9Sstevel@tonic-gate 			 * the destination (in the last routing hdr entry).
69627c478bd9Sstevel@tonic-gate 			 */
69637c478bd9Sstevel@tonic-gate 			csum = ip_massage_options_v6(ip6h, rth);
69647c478bd9Sstevel@tonic-gate 			/*
69657c478bd9Sstevel@tonic-gate 			 * Verify that the first hop isn't a mapped address.
69667c478bd9Sstevel@tonic-gate 			 * Routers along the path need to do this verification
69677c478bd9Sstevel@tonic-gate 			 * for subsequent hops.
69687c478bd9Sstevel@tonic-gate 			 */
69697c478bd9Sstevel@tonic-gate 			if (IN6_IS_ADDR_V4MAPPED(&ip6h->ip6_dst)) {
6970ff550d0eSmasputra 				*error = EADDRNOTAVAIL;
6971ff550d0eSmasputra 				goto done;
69727c478bd9Sstevel@tonic-gate 			}
69737c478bd9Sstevel@tonic-gate 
69747c478bd9Sstevel@tonic-gate 			cp += (rth->ip6r_len + 1)*8;
69757c478bd9Sstevel@tonic-gate 		}
69767c478bd9Sstevel@tonic-gate 	}
69777c478bd9Sstevel@tonic-gate 
69787c478bd9Sstevel@tonic-gate 	/* count up length of UDP packet */
69797c478bd9Sstevel@tonic-gate 	ip_len = (mp1->b_wptr - (unsigned char *)ip6h) - IPV6_HDR_LEN;
6980ff550d0eSmasputra 	if ((mp2 = mp1->b_cont) != NULL) {
6981ff550d0eSmasputra 		do {
6982ff550d0eSmasputra 			ASSERT((uintptr_t)MBLKL(mp2) <= (uintptr_t)UINT_MAX);
6983ff550d0eSmasputra 			ip_len += (uint32_t)MBLKL(mp2);
6984ff550d0eSmasputra 		} while ((mp2 = mp2->b_cont) != NULL);
69857c478bd9Sstevel@tonic-gate 	}
69867c478bd9Sstevel@tonic-gate 
69877c478bd9Sstevel@tonic-gate 	/*
69887c478bd9Sstevel@tonic-gate 	 * If the size of the packet is greater than the maximum allowed by
69897c478bd9Sstevel@tonic-gate 	 * ip, return an error. Passing this down could cause panics because
69907c478bd9Sstevel@tonic-gate 	 * the size will have wrapped and be inconsistent with the msg size.
69917c478bd9Sstevel@tonic-gate 	 */
69927c478bd9Sstevel@tonic-gate 	if (ip_len > IP_MAXPACKET) {
6993ff550d0eSmasputra 		*error = EMSGSIZE;
6994ff550d0eSmasputra 		goto done;
69957c478bd9Sstevel@tonic-gate 	}
69967c478bd9Sstevel@tonic-gate 
69977c478bd9Sstevel@tonic-gate 	/* Store the UDP length. Subtract length of extension hdrs */
69987c478bd9Sstevel@tonic-gate 	udph->uha_length = htons(ip_len + IPV6_HDR_LEN -
69997c478bd9Sstevel@tonic-gate 	    (int)((uchar_t *)udph - (uchar_t *)ip6h));
70007c478bd9Sstevel@tonic-gate 
70017c478bd9Sstevel@tonic-gate 	/*
70027c478bd9Sstevel@tonic-gate 	 * We make it easy for IP to include our pseudo header
70037c478bd9Sstevel@tonic-gate 	 * by putting our length in uh_checksum, modified (if
70047c478bd9Sstevel@tonic-gate 	 * we have a routing header) by the checksum difference
70057c478bd9Sstevel@tonic-gate 	 * between the ultimate destination and first hop addresses.
70067c478bd9Sstevel@tonic-gate 	 * Note: UDP over IPv6 must always checksum the packet.
70077c478bd9Sstevel@tonic-gate 	 */
70087c478bd9Sstevel@tonic-gate 	csum += udph->uha_length;
70097c478bd9Sstevel@tonic-gate 	csum = (csum & 0xFFFF) + (csum >> 16);
70107c478bd9Sstevel@tonic-gate 	udph->uha_checksum = (uint16_t)csum;
70117c478bd9Sstevel@tonic-gate 
70127c478bd9Sstevel@tonic-gate #ifdef _LITTLE_ENDIAN
70137c478bd9Sstevel@tonic-gate 	ip_len = htons(ip_len);
70147c478bd9Sstevel@tonic-gate #endif
70157c478bd9Sstevel@tonic-gate 	ip6h->ip6_plen = ip_len;
70167c478bd9Sstevel@tonic-gate 
7017ff550d0eSmasputra 	if (DB_TYPE(mp) != M_DATA) {
7018ff550d0eSmasputra 		ASSERT(mp != mp1);
7019ff550d0eSmasputra 		freeb(mp);
7020ff550d0eSmasputra 	}
7021ff550d0eSmasputra 
7022ff550d0eSmasputra 	/* mp has been consumed and we'll return success */
7023ff550d0eSmasputra 	ASSERT(*error == 0);
7024ff550d0eSmasputra 	mp = NULL;
70257c478bd9Sstevel@tonic-gate 
70267c478bd9Sstevel@tonic-gate 	/* We're done. Pass the packet to IP */
70277c478bd9Sstevel@tonic-gate 	BUMP_MIB(&udp_mib, udpOutDatagrams);
7028ff550d0eSmasputra 	ip_output_v6(connp, mp1, q, IP_WPUT);
7029ff550d0eSmasputra 
7030ff550d0eSmasputra done:
7031ff550d0eSmasputra 	if (*error != 0) {
7032ff550d0eSmasputra 		ASSERT(mp != NULL);
7033ff550d0eSmasputra 		BUMP_MIB(&udp_mib, udpOutErrors);
7034ff550d0eSmasputra 	}
7035ff550d0eSmasputra 	return (mp);
70367c478bd9Sstevel@tonic-gate }
70377c478bd9Sstevel@tonic-gate 
70387c478bd9Sstevel@tonic-gate static void
70397c478bd9Sstevel@tonic-gate udp_wput_other(queue_t *q, mblk_t *mp)
70407c478bd9Sstevel@tonic-gate {
70417c478bd9Sstevel@tonic-gate 	uchar_t	*rptr = mp->b_rptr;
70427c478bd9Sstevel@tonic-gate 	struct datab *db;
70437c478bd9Sstevel@tonic-gate 	struct iocblk *iocp;
70447c478bd9Sstevel@tonic-gate 	cred_t	*cr;
7045ff550d0eSmasputra 	conn_t	*connp = Q_TO_CONN(q);
7046ff550d0eSmasputra 	udp_t	*udp = connp->conn_udp;
70477c478bd9Sstevel@tonic-gate 
70487c478bd9Sstevel@tonic-gate 	TRACE_1(TR_FAC_UDP, TR_UDP_WPUT_OTHER_START,
70497c478bd9Sstevel@tonic-gate 		"udp_wput_other_start: q %p", q);
70507c478bd9Sstevel@tonic-gate 
70517c478bd9Sstevel@tonic-gate 	db = mp->b_datap;
70527c478bd9Sstevel@tonic-gate 
7053ff550d0eSmasputra 	cr = DB_CREDDEF(mp, connp->conn_cred);
70547c478bd9Sstevel@tonic-gate 
70557c478bd9Sstevel@tonic-gate 	switch (db->db_type) {
70567c478bd9Sstevel@tonic-gate 	case M_PROTO:
70577c478bd9Sstevel@tonic-gate 	case M_PCPROTO:
70587c478bd9Sstevel@tonic-gate 		if (mp->b_wptr - rptr < sizeof (t_scalar_t)) {
70597c478bd9Sstevel@tonic-gate 			freemsg(mp);
70607c478bd9Sstevel@tonic-gate 			TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END,
70617c478bd9Sstevel@tonic-gate 				"udp_wput_other_end: q %p (%S)",
70627c478bd9Sstevel@tonic-gate 				q, "protoshort");
70637c478bd9Sstevel@tonic-gate 			return;
70647c478bd9Sstevel@tonic-gate 		}
7065ff550d0eSmasputra 		switch (((t_primp_t)rptr)->type) {
70667c478bd9Sstevel@tonic-gate 		case T_ADDR_REQ:
70677c478bd9Sstevel@tonic-gate 			udp_addr_req(q, mp);
70687c478bd9Sstevel@tonic-gate 			TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END,
70697c478bd9Sstevel@tonic-gate 				"udp_wput_other_end: q %p (%S)", q, "addrreq");
70707c478bd9Sstevel@tonic-gate 			return;
70717c478bd9Sstevel@tonic-gate 		case O_T_BIND_REQ:
70727c478bd9Sstevel@tonic-gate 		case T_BIND_REQ:
70737c478bd9Sstevel@tonic-gate 			udp_bind(q, mp);
70747c478bd9Sstevel@tonic-gate 			TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END,
70757c478bd9Sstevel@tonic-gate 				"udp_wput_other_end: q %p (%S)", q, "bindreq");
70767c478bd9Sstevel@tonic-gate 			return;
70777c478bd9Sstevel@tonic-gate 		case T_CONN_REQ:
70787c478bd9Sstevel@tonic-gate 			udp_connect(q, mp);
70797c478bd9Sstevel@tonic-gate 			TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END,
70807c478bd9Sstevel@tonic-gate 				"udp_wput_other_end: q %p (%S)", q, "connreq");
70817c478bd9Sstevel@tonic-gate 			return;
70827c478bd9Sstevel@tonic-gate 		case T_CAPABILITY_REQ:
70837c478bd9Sstevel@tonic-gate 			udp_capability_req(q, mp);
70847c478bd9Sstevel@tonic-gate 			TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END,
70857c478bd9Sstevel@tonic-gate 				"udp_wput_other_end: q %p (%S)", q, "capabreq");
70867c478bd9Sstevel@tonic-gate 			return;
70877c478bd9Sstevel@tonic-gate 		case T_INFO_REQ:
70887c478bd9Sstevel@tonic-gate 			udp_info_req(q, mp);
70897c478bd9Sstevel@tonic-gate 			TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END,
70907c478bd9Sstevel@tonic-gate 				"udp_wput_other_end: q %p (%S)", q, "inforeq");
70917c478bd9Sstevel@tonic-gate 			return;
70927c478bd9Sstevel@tonic-gate 		case T_UNITDATA_REQ:
70937c478bd9Sstevel@tonic-gate 			/*
70947c478bd9Sstevel@tonic-gate 			 * If a T_UNITDATA_REQ gets here, the address must
70957c478bd9Sstevel@tonic-gate 			 * be bad.  Valid T_UNITDATA_REQs are handled
70967c478bd9Sstevel@tonic-gate 			 * in udp_wput.
70977c478bd9Sstevel@tonic-gate 			 */
7098ff550d0eSmasputra 			udp_ud_err(q, mp, NULL, 0, EADDRNOTAVAIL);
70997c478bd9Sstevel@tonic-gate 			TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END,
71007c478bd9Sstevel@tonic-gate 				"udp_wput_other_end: q %p (%S)",
71017c478bd9Sstevel@tonic-gate 				q, "unitdatareq");
71027c478bd9Sstevel@tonic-gate 			return;
71037c478bd9Sstevel@tonic-gate 		case T_UNBIND_REQ:
71047c478bd9Sstevel@tonic-gate 			udp_unbind(q, mp);
71057c478bd9Sstevel@tonic-gate 			TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END,
71067c478bd9Sstevel@tonic-gate 			    "udp_wput_other_end: q %p (%S)", q, "unbindreq");
71077c478bd9Sstevel@tonic-gate 			return;
71087c478bd9Sstevel@tonic-gate 		case T_SVR4_OPTMGMT_REQ:
71097c478bd9Sstevel@tonic-gate 			if (!snmpcom_req(q, mp, udp_snmp_set, udp_snmp_get, cr))
7110ff550d0eSmasputra 				/*
7111ff550d0eSmasputra 				 * Use upper queue for option processing in
7112ff550d0eSmasputra 				 * case the request is not handled at this
7113ff550d0eSmasputra 				 * level and needs to be passed down to IP.
7114ff550d0eSmasputra 				 */
7115ff550d0eSmasputra 				(void) svr4_optcom_req(_WR(UDP_RD(q)),
7116ff550d0eSmasputra 				    mp, cr, &udp_opt_obj);
71177c478bd9Sstevel@tonic-gate 			TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END,
71187c478bd9Sstevel@tonic-gate 			    "udp_wput_other_end: q %p (%S)",
71197c478bd9Sstevel@tonic-gate 			    q, "optmgmtreq");
71207c478bd9Sstevel@tonic-gate 			return;
71217c478bd9Sstevel@tonic-gate 
71227c478bd9Sstevel@tonic-gate 		case T_OPTMGMT_REQ:
7123ff550d0eSmasputra 			/*
7124ff550d0eSmasputra 			 * Use upper queue for option processing in
7125ff550d0eSmasputra 			 * case the request is not handled at this
7126ff550d0eSmasputra 			 * level and needs to be passed down to IP.
7127ff550d0eSmasputra 			 */
7128ff550d0eSmasputra 			(void) tpi_optcom_req(_WR(UDP_RD(q)),
7129ff550d0eSmasputra 			    mp, cr, &udp_opt_obj);
71307c478bd9Sstevel@tonic-gate 			TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END,
71317c478bd9Sstevel@tonic-gate 				"udp_wput_other_end: q %p (%S)",
71327c478bd9Sstevel@tonic-gate 				q, "optmgmtreq");
71337c478bd9Sstevel@tonic-gate 			return;
71347c478bd9Sstevel@tonic-gate 
71357c478bd9Sstevel@tonic-gate 		case T_DISCON_REQ:
71367c478bd9Sstevel@tonic-gate 			udp_disconnect(q, mp);
71377c478bd9Sstevel@tonic-gate 			TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END,
71387c478bd9Sstevel@tonic-gate 				"udp_wput_other_end: q %p (%S)",
71397c478bd9Sstevel@tonic-gate 				q, "disconreq");
71407c478bd9Sstevel@tonic-gate 			return;
71417c478bd9Sstevel@tonic-gate 
71427c478bd9Sstevel@tonic-gate 		/* The following TPI message is not supported by udp. */
71437c478bd9Sstevel@tonic-gate 		case O_T_CONN_RES:
71447c478bd9Sstevel@tonic-gate 		case T_CONN_RES:
71457c478bd9Sstevel@tonic-gate 			udp_err_ack(q, mp, TNOTSUPPORT, 0);
71467c478bd9Sstevel@tonic-gate 			TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END,
71477c478bd9Sstevel@tonic-gate 				"udp_wput_other_end: q %p (%S)",
71487c478bd9Sstevel@tonic-gate 				q, "connres/disconreq");
71497c478bd9Sstevel@tonic-gate 			return;
71507c478bd9Sstevel@tonic-gate 
71517c478bd9Sstevel@tonic-gate 		/* The following 3 TPI messages are illegal for udp. */
71527c478bd9Sstevel@tonic-gate 		case T_DATA_REQ:
71537c478bd9Sstevel@tonic-gate 		case T_EXDATA_REQ:
71547c478bd9Sstevel@tonic-gate 		case T_ORDREL_REQ:
71557c478bd9Sstevel@tonic-gate 			udp_err_ack(q, mp, TNOTSUPPORT, 0);
71567c478bd9Sstevel@tonic-gate 			TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END,
71577c478bd9Sstevel@tonic-gate 				"udp_wput_other_end: q %p (%S)",
71587c478bd9Sstevel@tonic-gate 				q, "data/exdata/ordrel");
71597c478bd9Sstevel@tonic-gate 			return;
71607c478bd9Sstevel@tonic-gate 		default:
71617c478bd9Sstevel@tonic-gate 			break;
71627c478bd9Sstevel@tonic-gate 		}
71637c478bd9Sstevel@tonic-gate 		break;
71647c478bd9Sstevel@tonic-gate 	case M_FLUSH:
71657c478bd9Sstevel@tonic-gate 		if (*rptr & FLUSHW)
71667c478bd9Sstevel@tonic-gate 			flushq(q, FLUSHDATA);
71677c478bd9Sstevel@tonic-gate 		break;
71687c478bd9Sstevel@tonic-gate 	case M_IOCTL:
71697c478bd9Sstevel@tonic-gate 		iocp = (struct iocblk *)mp->b_rptr;
71707c478bd9Sstevel@tonic-gate 		switch (iocp->ioc_cmd) {
71717c478bd9Sstevel@tonic-gate 		case TI_GETPEERNAME:
71727c478bd9Sstevel@tonic-gate 			if (udp->udp_state != TS_DATA_XFER) {
71737c478bd9Sstevel@tonic-gate 				/*
71747c478bd9Sstevel@tonic-gate 				 * If a default destination address has not
71757c478bd9Sstevel@tonic-gate 				 * been associated with the stream, then we
71767c478bd9Sstevel@tonic-gate 				 * don't know the peer's name.
71777c478bd9Sstevel@tonic-gate 				 */
71787c478bd9Sstevel@tonic-gate 				iocp->ioc_error = ENOTCONN;
71797c478bd9Sstevel@tonic-gate 				iocp->ioc_count = 0;
71807c478bd9Sstevel@tonic-gate 				mp->b_datap->db_type = M_IOCACK;
7181ff550d0eSmasputra 				putnext(UDP_RD(q), mp);
71827c478bd9Sstevel@tonic-gate 				TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END,
71837c478bd9Sstevel@tonic-gate 					"udp_wput_other_end: q %p (%S)",
71847c478bd9Sstevel@tonic-gate 					q, "getpeername");
71857c478bd9Sstevel@tonic-gate 				return;
71867c478bd9Sstevel@tonic-gate 			}
71877c478bd9Sstevel@tonic-gate 			/* FALLTHRU */
71887c478bd9Sstevel@tonic-gate 		case TI_GETMYNAME: {
71897c478bd9Sstevel@tonic-gate 			/*
71907c478bd9Sstevel@tonic-gate 			 * For TI_GETPEERNAME and TI_GETMYNAME, we first
71917c478bd9Sstevel@tonic-gate 			 * need to copyin the user's strbuf structure.
71927c478bd9Sstevel@tonic-gate 			 * Processing will continue in the M_IOCDATA case
71937c478bd9Sstevel@tonic-gate 			 * below.
71947c478bd9Sstevel@tonic-gate 			 */
71957c478bd9Sstevel@tonic-gate 			mi_copyin(q, mp, NULL,
71967c478bd9Sstevel@tonic-gate 			    SIZEOF_STRUCT(strbuf, iocp->ioc_flag));
71977c478bd9Sstevel@tonic-gate 			TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END,
71987c478bd9Sstevel@tonic-gate 				"udp_wput_other_end: q %p (%S)",
71997c478bd9Sstevel@tonic-gate 				q, "getmyname");
72007c478bd9Sstevel@tonic-gate 			return;
72017c478bd9Sstevel@tonic-gate 			}
72027c478bd9Sstevel@tonic-gate 		case ND_SET:
72037c478bd9Sstevel@tonic-gate 			/* nd_getset performs the necessary checking */
72047c478bd9Sstevel@tonic-gate 		case ND_GET:
72057c478bd9Sstevel@tonic-gate 			if (nd_getset(q, udp_g_nd, mp)) {
7206ff550d0eSmasputra 				putnext(UDP_RD(q), mp);
72077c478bd9Sstevel@tonic-gate 				TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END,
72087c478bd9Sstevel@tonic-gate 					"udp_wput_other_end: q %p (%S)",
72097c478bd9Sstevel@tonic-gate 					q, "get");
72107c478bd9Sstevel@tonic-gate 				return;
72117c478bd9Sstevel@tonic-gate 			}
72127c478bd9Sstevel@tonic-gate 			break;
7213ff550d0eSmasputra 		case _SIOCSOCKFALLBACK:
7214ff550d0eSmasputra 			/*
7215ff550d0eSmasputra 			 * Either sockmod is about to be popped and the
7216ff550d0eSmasputra 			 * socket would now be treated as a plain stream,
7217ff550d0eSmasputra 			 * or a module is about to be pushed so we could
7218ff550d0eSmasputra 			 * no longer use read-side synchronous stream.
7219ff550d0eSmasputra 			 * Drain any queued data and disable direct sockfs
7220ff550d0eSmasputra 			 * interface from now on.
7221ff550d0eSmasputra 			 */
7222ff550d0eSmasputra 			if (!udp->udp_issocket) {
7223ff550d0eSmasputra 				DB_TYPE(mp) = M_IOCNAK;
7224ff550d0eSmasputra 				iocp->ioc_error = EINVAL;
7225ff550d0eSmasputra 			} else {
7226ff550d0eSmasputra 				udp->udp_issocket = B_FALSE;
7227ff550d0eSmasputra 				if (udp->udp_direct_sockfs) {
7228ff550d0eSmasputra 					/*
7229ff550d0eSmasputra 					 * Disable read-side synchronous
7230ff550d0eSmasputra 					 * stream interface and drain any
7231ff550d0eSmasputra 					 * queued data.
7232ff550d0eSmasputra 					 */
7233ff550d0eSmasputra 					udp_rcv_drain(UDP_RD(q), udp,
7234ff550d0eSmasputra 					    B_FALSE);
7235ff550d0eSmasputra 					ASSERT(!udp->udp_direct_sockfs);
7236ff550d0eSmasputra 					UDP_STAT(udp_sock_fallback);
7237ff550d0eSmasputra 				}
7238ff550d0eSmasputra 				DB_TYPE(mp) = M_IOCACK;
7239ff550d0eSmasputra 				iocp->ioc_error = 0;
7240ff550d0eSmasputra 			}
7241ff550d0eSmasputra 			iocp->ioc_count = 0;
7242ff550d0eSmasputra 			iocp->ioc_rval = 0;
7243ff550d0eSmasputra 			putnext(UDP_RD(q), mp);
7244ff550d0eSmasputra 			return;
72457c478bd9Sstevel@tonic-gate 		default:
72467c478bd9Sstevel@tonic-gate 			break;
72477c478bd9Sstevel@tonic-gate 		}
72487c478bd9Sstevel@tonic-gate 		break;
72497c478bd9Sstevel@tonic-gate 	case M_IOCDATA:
72507c478bd9Sstevel@tonic-gate 		udp_wput_iocdata(q, mp);
72517c478bd9Sstevel@tonic-gate 		TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END,
72527c478bd9Sstevel@tonic-gate 			"udp_wput_other_end: q %p (%S)", q, "iocdata");
72537c478bd9Sstevel@tonic-gate 		return;
72547c478bd9Sstevel@tonic-gate 	default:
72557c478bd9Sstevel@tonic-gate 		/* Unrecognized messages are passed through without change. */
72567c478bd9Sstevel@tonic-gate 		break;
72577c478bd9Sstevel@tonic-gate 	}
72587c478bd9Sstevel@tonic-gate 	TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END,
72597c478bd9Sstevel@tonic-gate 		"udp_wput_other_end: q %p (%S)", q, "end");
7260ff550d0eSmasputra 	ip_output(connp, mp, q, IP_WPUT);
7261ff550d0eSmasputra }
7262ff550d0eSmasputra 
7263ff550d0eSmasputra /* ARGSUSED */
7264ff550d0eSmasputra static void
7265ff550d0eSmasputra udp_wput_other_wrapper(void *arg, mblk_t *mp, void *arg2)
7266ff550d0eSmasputra {
7267ff550d0eSmasputra 	udp_wput_other(((conn_t *)arg)->conn_wq, mp);
7268ff550d0eSmasputra 	udp_exit((conn_t *)arg);
72697c478bd9Sstevel@tonic-gate }
72707c478bd9Sstevel@tonic-gate 
72717c478bd9Sstevel@tonic-gate /*
72727c478bd9Sstevel@tonic-gate  * udp_wput_iocdata is called by udp_wput_other to handle all M_IOCDATA
72737c478bd9Sstevel@tonic-gate  * messages.
72747c478bd9Sstevel@tonic-gate  */
72757c478bd9Sstevel@tonic-gate static void
72767c478bd9Sstevel@tonic-gate udp_wput_iocdata(queue_t *q, mblk_t *mp)
72777c478bd9Sstevel@tonic-gate {
72787c478bd9Sstevel@tonic-gate 	mblk_t	*mp1;
72797c478bd9Sstevel@tonic-gate 	STRUCT_HANDLE(strbuf, sb);
72807c478bd9Sstevel@tonic-gate 	uint16_t port;
72817c478bd9Sstevel@tonic-gate 	in6_addr_t	v6addr;
72827c478bd9Sstevel@tonic-gate 	ipaddr_t	v4addr;
72837c478bd9Sstevel@tonic-gate 	uint32_t	flowinfo = 0;
72847c478bd9Sstevel@tonic-gate 	int		addrlen;
7285ff550d0eSmasputra 	udp_t		*udp = Q_TO_UDP(q);
72867c478bd9Sstevel@tonic-gate 
72877c478bd9Sstevel@tonic-gate 	/* Make sure it is one of ours. */
72887c478bd9Sstevel@tonic-gate 	switch (((struct iocblk *)mp->b_rptr)->ioc_cmd) {
72897c478bd9Sstevel@tonic-gate 	case TI_GETMYNAME:
72907c478bd9Sstevel@tonic-gate 	case TI_GETPEERNAME:
72917c478bd9Sstevel@tonic-gate 		break;
72927c478bd9Sstevel@tonic-gate 	default:
7293ff550d0eSmasputra 		ip_output(Q_TO_CONN(q), mp, q, IP_WPUT);
72947c478bd9Sstevel@tonic-gate 		return;
72957c478bd9Sstevel@tonic-gate 	}
7296ff550d0eSmasputra 
7297ff550d0eSmasputra 	q = WR(UDP_RD(q));
72987c478bd9Sstevel@tonic-gate 	switch (mi_copy_state(q, mp, &mp1)) {
72997c478bd9Sstevel@tonic-gate 	case -1:
73007c478bd9Sstevel@tonic-gate 		return;
73017c478bd9Sstevel@tonic-gate 	case MI_COPY_CASE(MI_COPY_IN, 1):
73027c478bd9Sstevel@tonic-gate 		break;
73037c478bd9Sstevel@tonic-gate 	case MI_COPY_CASE(MI_COPY_OUT, 1):
73047c478bd9Sstevel@tonic-gate 		/*
73057c478bd9Sstevel@tonic-gate 		 * The address has been copied out, so now
73067c478bd9Sstevel@tonic-gate 		 * copyout the strbuf.
73077c478bd9Sstevel@tonic-gate 		 */
73087c478bd9Sstevel@tonic-gate 		mi_copyout(q, mp);
73097c478bd9Sstevel@tonic-gate 		return;
73107c478bd9Sstevel@tonic-gate 	case MI_COPY_CASE(MI_COPY_OUT, 2):
73117c478bd9Sstevel@tonic-gate 		/*
73127c478bd9Sstevel@tonic-gate 		 * The address and strbuf have been copied out.
73137c478bd9Sstevel@tonic-gate 		 * We're done, so just acknowledge the original
73147c478bd9Sstevel@tonic-gate 		 * M_IOCTL.
73157c478bd9Sstevel@tonic-gate 		 */
73167c478bd9Sstevel@tonic-gate 		mi_copy_done(q, mp, 0);
73177c478bd9Sstevel@tonic-gate 		return;
73187c478bd9Sstevel@tonic-gate 	default:
73197c478bd9Sstevel@tonic-gate 		/*
73207c478bd9Sstevel@tonic-gate 		 * Something strange has happened, so acknowledge
73217c478bd9Sstevel@tonic-gate 		 * the original M_IOCTL with an EPROTO error.
73227c478bd9Sstevel@tonic-gate 		 */
73237c478bd9Sstevel@tonic-gate 		mi_copy_done(q, mp, EPROTO);
73247c478bd9Sstevel@tonic-gate 		return;
73257c478bd9Sstevel@tonic-gate 	}
73267c478bd9Sstevel@tonic-gate 
73277c478bd9Sstevel@tonic-gate 	/*
73287c478bd9Sstevel@tonic-gate 	 * Now we have the strbuf structure for TI_GETMYNAME
73297c478bd9Sstevel@tonic-gate 	 * and TI_GETPEERNAME.  Next we copyout the requested
73307c478bd9Sstevel@tonic-gate 	 * address and then we'll copyout the strbuf.
73317c478bd9Sstevel@tonic-gate 	 */
73327c478bd9Sstevel@tonic-gate 	STRUCT_SET_HANDLE(sb, ((struct iocblk *)mp->b_rptr)->ioc_flag,
73337c478bd9Sstevel@tonic-gate 	    (void *)mp1->b_rptr);
73347c478bd9Sstevel@tonic-gate 	if (udp->udp_family == AF_INET)
73357c478bd9Sstevel@tonic-gate 		addrlen = sizeof (sin_t);
73367c478bd9Sstevel@tonic-gate 	else
73377c478bd9Sstevel@tonic-gate 		addrlen = sizeof (sin6_t);
73387c478bd9Sstevel@tonic-gate 
73397c478bd9Sstevel@tonic-gate 	if (STRUCT_FGET(sb, maxlen) < addrlen) {
73407c478bd9Sstevel@tonic-gate 		mi_copy_done(q, mp, EINVAL);
73417c478bd9Sstevel@tonic-gate 		return;
73427c478bd9Sstevel@tonic-gate 	}
73437c478bd9Sstevel@tonic-gate 	switch (((struct iocblk *)mp->b_rptr)->ioc_cmd) {
73447c478bd9Sstevel@tonic-gate 	case TI_GETMYNAME:
73457c478bd9Sstevel@tonic-gate 		if (udp->udp_family == AF_INET) {
73467c478bd9Sstevel@tonic-gate 			ASSERT(udp->udp_ipversion == IPV4_VERSION);
73477c478bd9Sstevel@tonic-gate 			if (!IN6_IS_ADDR_V4MAPPED_ANY(&udp->udp_v6src) &&
73487c478bd9Sstevel@tonic-gate 			    !IN6_IS_ADDR_UNSPECIFIED(&udp->udp_v6src)) {
73497c478bd9Sstevel@tonic-gate 				v4addr = V4_PART_OF_V6(udp->udp_v6src);
73507c478bd9Sstevel@tonic-gate 			} else {
73517c478bd9Sstevel@tonic-gate 				/*
73527c478bd9Sstevel@tonic-gate 				 * INADDR_ANY
73537c478bd9Sstevel@tonic-gate 				 * udp_v6src is not set, we might be bound to
73547c478bd9Sstevel@tonic-gate 				 * broadcast/multicast. Use udp_bound_v6src as
73557c478bd9Sstevel@tonic-gate 				 * local address instead (that could
73567c478bd9Sstevel@tonic-gate 				 * also still be INADDR_ANY)
73577c478bd9Sstevel@tonic-gate 				 */
73587c478bd9Sstevel@tonic-gate 				v4addr = V4_PART_OF_V6(udp->udp_bound_v6src);
73597c478bd9Sstevel@tonic-gate 			}
73607c478bd9Sstevel@tonic-gate 		} else {
73617c478bd9Sstevel@tonic-gate 			/* udp->udp_family == AF_INET6 */
73627c478bd9Sstevel@tonic-gate 			if (!IN6_IS_ADDR_UNSPECIFIED(&udp->udp_v6src)) {
73637c478bd9Sstevel@tonic-gate 				v6addr = udp->udp_v6src;
73647c478bd9Sstevel@tonic-gate 			} else {
73657c478bd9Sstevel@tonic-gate 				/*
73667c478bd9Sstevel@tonic-gate 				 * UNSPECIFIED
73677c478bd9Sstevel@tonic-gate 				 * udp_v6src is not set, we might be bound to
73687c478bd9Sstevel@tonic-gate 				 * broadcast/multicast. Use udp_bound_v6src as
73697c478bd9Sstevel@tonic-gate 				 * local address instead (that could
73707c478bd9Sstevel@tonic-gate 				 * also still be UNSPECIFIED)
73717c478bd9Sstevel@tonic-gate 				 */
73727c478bd9Sstevel@tonic-gate 				v6addr = udp->udp_bound_v6src;
73737c478bd9Sstevel@tonic-gate 			}
73747c478bd9Sstevel@tonic-gate 		}
73757c478bd9Sstevel@tonic-gate 		port = udp->udp_port;
73767c478bd9Sstevel@tonic-gate 		break;
73777c478bd9Sstevel@tonic-gate 	case TI_GETPEERNAME:
7378ff550d0eSmasputra 		if (udp->udp_state != TS_DATA_XFER) {
7379ff550d0eSmasputra 			mi_copy_done(q, mp, ENOTCONN);
7380ff550d0eSmasputra 			return;
7381ff550d0eSmasputra 		}
73827c478bd9Sstevel@tonic-gate 		if (udp->udp_family == AF_INET) {
73837c478bd9Sstevel@tonic-gate 			ASSERT(udp->udp_ipversion == IPV4_VERSION);
73847c478bd9Sstevel@tonic-gate 			v4addr = V4_PART_OF_V6(udp->udp_v6dst);
73857c478bd9Sstevel@tonic-gate 		} else {
73867c478bd9Sstevel@tonic-gate 			/* udp->udp_family == AF_INET6) */
73877c478bd9Sstevel@tonic-gate 			v6addr = udp->udp_v6dst;
73887c478bd9Sstevel@tonic-gate 			flowinfo = udp->udp_flowinfo;
73897c478bd9Sstevel@tonic-gate 		}
73907c478bd9Sstevel@tonic-gate 		port = udp->udp_dstport;
73917c478bd9Sstevel@tonic-gate 		break;
73927c478bd9Sstevel@tonic-gate 	default:
73937c478bd9Sstevel@tonic-gate 		mi_copy_done(q, mp, EPROTO);
73947c478bd9Sstevel@tonic-gate 		return;
73957c478bd9Sstevel@tonic-gate 	}
73967c478bd9Sstevel@tonic-gate 	mp1 = mi_copyout_alloc(q, mp, STRUCT_FGETP(sb, buf), addrlen, B_TRUE);
73977c478bd9Sstevel@tonic-gate 	if (!mp1)
73987c478bd9Sstevel@tonic-gate 		return;
73997c478bd9Sstevel@tonic-gate 
74007c478bd9Sstevel@tonic-gate 	if (udp->udp_family == AF_INET) {
74017c478bd9Sstevel@tonic-gate 		sin_t *sin;
74027c478bd9Sstevel@tonic-gate 
74037c478bd9Sstevel@tonic-gate 		STRUCT_FSET(sb, len, (int)sizeof (sin_t));
74047c478bd9Sstevel@tonic-gate 		sin = (sin_t *)mp1->b_rptr;
74057c478bd9Sstevel@tonic-gate 		mp1->b_wptr = (uchar_t *)&sin[1];
74067c478bd9Sstevel@tonic-gate 		*sin = sin_null;
74077c478bd9Sstevel@tonic-gate 		sin->sin_family = AF_INET;
74087c478bd9Sstevel@tonic-gate 		sin->sin_addr.s_addr = v4addr;
74097c478bd9Sstevel@tonic-gate 		sin->sin_port = port;
74107c478bd9Sstevel@tonic-gate 	} else {
74117c478bd9Sstevel@tonic-gate 		/* udp->udp_family == AF_INET6 */
74127c478bd9Sstevel@tonic-gate 		sin6_t *sin6;
74137c478bd9Sstevel@tonic-gate 
74147c478bd9Sstevel@tonic-gate 		STRUCT_FSET(sb, len, (int)sizeof (sin6_t));
74157c478bd9Sstevel@tonic-gate 		sin6 = (sin6_t *)mp1->b_rptr;
74167c478bd9Sstevel@tonic-gate 		mp1->b_wptr = (uchar_t *)&sin6[1];
74177c478bd9Sstevel@tonic-gate 		*sin6 = sin6_null;
74187c478bd9Sstevel@tonic-gate 		sin6->sin6_family = AF_INET6;
74197c478bd9Sstevel@tonic-gate 		sin6->sin6_flowinfo = flowinfo;
74207c478bd9Sstevel@tonic-gate 		sin6->sin6_addr = v6addr;
74217c478bd9Sstevel@tonic-gate 		sin6->sin6_port = port;
74227c478bd9Sstevel@tonic-gate 	}
74237c478bd9Sstevel@tonic-gate 	/* Copy out the address */
74247c478bd9Sstevel@tonic-gate 	mi_copyout(q, mp);
74257c478bd9Sstevel@tonic-gate }
74267c478bd9Sstevel@tonic-gate 
74277c478bd9Sstevel@tonic-gate 
74287c478bd9Sstevel@tonic-gate static int
74297c478bd9Sstevel@tonic-gate udp_unitdata_opt_process(queue_t *q, mblk_t *mp, int *errorp,
74307c478bd9Sstevel@tonic-gate     void *thisdg_attrs)
74317c478bd9Sstevel@tonic-gate {
74327c478bd9Sstevel@tonic-gate 	struct T_unitdata_req *udreqp;
74337c478bd9Sstevel@tonic-gate 	int is_absreq_failure;
74347c478bd9Sstevel@tonic-gate 	cred_t *cr;
7435ff550d0eSmasputra 	conn_t	*connp = Q_TO_CONN(q);
74367c478bd9Sstevel@tonic-gate 
7437ff550d0eSmasputra 	ASSERT(((t_primp_t)mp->b_rptr)->type);
74387c478bd9Sstevel@tonic-gate 
7439ff550d0eSmasputra 	cr = DB_CREDDEF(mp, connp->conn_cred);
74407c478bd9Sstevel@tonic-gate 
74417c478bd9Sstevel@tonic-gate 	udreqp = (struct T_unitdata_req *)mp->b_rptr;
74427c478bd9Sstevel@tonic-gate 	*errorp = 0;
74437c478bd9Sstevel@tonic-gate 
7444ff550d0eSmasputra 	/*
7445ff550d0eSmasputra 	 * Use upper queue for option processing since the callback
7446ff550d0eSmasputra 	 * routines expect to be called in UDP instance instead of IP.
7447ff550d0eSmasputra 	 */
7448ff550d0eSmasputra 	*errorp = tpi_optcom_buf(_WR(UDP_RD(q)), mp, &udreqp->OPT_length,
74497c478bd9Sstevel@tonic-gate 	    udreqp->OPT_offset, cr, &udp_opt_obj,
74507c478bd9Sstevel@tonic-gate 	    thisdg_attrs, &is_absreq_failure);
74517c478bd9Sstevel@tonic-gate 
74527c478bd9Sstevel@tonic-gate 	if (*errorp != 0) {
74537c478bd9Sstevel@tonic-gate 		/*
74547c478bd9Sstevel@tonic-gate 		 * Note: No special action needed in this
74557c478bd9Sstevel@tonic-gate 		 * module for "is_absreq_failure"
74567c478bd9Sstevel@tonic-gate 		 */
74577c478bd9Sstevel@tonic-gate 		return (-1);		/* failure */
74587c478bd9Sstevel@tonic-gate 	}
74597c478bd9Sstevel@tonic-gate 	ASSERT(is_absreq_failure == 0);
74607c478bd9Sstevel@tonic-gate 	return (0);	/* success */
74617c478bd9Sstevel@tonic-gate }
74627c478bd9Sstevel@tonic-gate 
74637c478bd9Sstevel@tonic-gate void
74647c478bd9Sstevel@tonic-gate udp_ddi_init(void)
74657c478bd9Sstevel@tonic-gate {
74667c478bd9Sstevel@tonic-gate 	int i;
74677c478bd9Sstevel@tonic-gate 
74687c478bd9Sstevel@tonic-gate 	UDP6_MAJ = ddi_name_to_major(UDP6);
74697c478bd9Sstevel@tonic-gate 
74707c478bd9Sstevel@tonic-gate 	udp_max_optsize = optcom_max_optsize(udp_opt_obj.odb_opt_des_arr,
74717c478bd9Sstevel@tonic-gate 	    udp_opt_obj.odb_opt_arr_cnt);
74727c478bd9Sstevel@tonic-gate 
74737c478bd9Sstevel@tonic-gate 	if (udp_bind_fanout_size & (udp_bind_fanout_size - 1)) {
74747c478bd9Sstevel@tonic-gate 		/* Not a power of two. Round up to nearest power of two */
74757c478bd9Sstevel@tonic-gate 		for (i = 0; i < 31; i++) {
74767c478bd9Sstevel@tonic-gate 			if (udp_bind_fanout_size < (1 << i))
74777c478bd9Sstevel@tonic-gate 				break;
74787c478bd9Sstevel@tonic-gate 		}
74797c478bd9Sstevel@tonic-gate 		udp_bind_fanout_size = 1 << i;
74807c478bd9Sstevel@tonic-gate 	}
74817c478bd9Sstevel@tonic-gate 	udp_bind_fanout = kmem_zalloc(udp_bind_fanout_size *
74827c478bd9Sstevel@tonic-gate 	    sizeof (udp_fanout_t), KM_SLEEP);
74837c478bd9Sstevel@tonic-gate 	for (i = 0; i < udp_bind_fanout_size; i++) {
74847c478bd9Sstevel@tonic-gate 		mutex_init(&udp_bind_fanout[i].uf_lock, NULL, MUTEX_DEFAULT,
74857c478bd9Sstevel@tonic-gate 		    NULL);
74867c478bd9Sstevel@tonic-gate 	}
74877c478bd9Sstevel@tonic-gate 	(void) udp_param_register(udp_param_arr, A_CNT(udp_param_arr));
7488ff550d0eSmasputra 
74897c478bd9Sstevel@tonic-gate 	udp_kstat_init();
7490ff550d0eSmasputra 
7491ff550d0eSmasputra 	udp_cache = kmem_cache_create("udp_cache", sizeof (udp_t),
7492ff550d0eSmasputra 	    CACHE_ALIGN_SIZE, NULL, NULL, NULL, NULL, NULL, 0);
74937c478bd9Sstevel@tonic-gate }
74947c478bd9Sstevel@tonic-gate 
74957c478bd9Sstevel@tonic-gate void
74967c478bd9Sstevel@tonic-gate udp_ddi_destroy(void)
74977c478bd9Sstevel@tonic-gate {
74987c478bd9Sstevel@tonic-gate 	int i;
74997c478bd9Sstevel@tonic-gate 
75007c478bd9Sstevel@tonic-gate 	nd_free(&udp_g_nd);
75017c478bd9Sstevel@tonic-gate 
75027c478bd9Sstevel@tonic-gate 	for (i = 0; i < udp_bind_fanout_size; i++) {
75037c478bd9Sstevel@tonic-gate 		mutex_destroy(&udp_bind_fanout[i].uf_lock);
75047c478bd9Sstevel@tonic-gate 	}
7505ff550d0eSmasputra 
75067c478bd9Sstevel@tonic-gate 	kmem_free(udp_bind_fanout, udp_bind_fanout_size *
75077c478bd9Sstevel@tonic-gate 	    sizeof (udp_fanout_t));
7508ff550d0eSmasputra 
75097c478bd9Sstevel@tonic-gate 	udp_kstat_fini();
75107c478bd9Sstevel@tonic-gate 
7511ff550d0eSmasputra 	kmem_cache_destroy(udp_cache);
75127c478bd9Sstevel@tonic-gate }
75137c478bd9Sstevel@tonic-gate 
75147c478bd9Sstevel@tonic-gate static void
75157c478bd9Sstevel@tonic-gate udp_kstat_init(void)
75167c478bd9Sstevel@tonic-gate {
75177c478bd9Sstevel@tonic-gate 	udp_named_kstat_t template = {
75187c478bd9Sstevel@tonic-gate 		{ "inDatagrams",	KSTAT_DATA_UINT32, 0 },
75197c478bd9Sstevel@tonic-gate 		{ "inErrors",		KSTAT_DATA_UINT32, 0 },
75207c478bd9Sstevel@tonic-gate 		{ "outDatagrams",	KSTAT_DATA_UINT32, 0 },
75217c478bd9Sstevel@tonic-gate 		{ "entrySize",		KSTAT_DATA_INT32, 0 },
75227c478bd9Sstevel@tonic-gate 		{ "entry6Size",		KSTAT_DATA_INT32, 0 },
75237c478bd9Sstevel@tonic-gate 		{ "outErrors",		KSTAT_DATA_UINT32, 0 },
75247c478bd9Sstevel@tonic-gate 	};
75257c478bd9Sstevel@tonic-gate 
7526ff550d0eSmasputra 	udp_mibkp = kstat_create(UDP_MOD_NAME, 0, UDP_MOD_NAME,
7527ff550d0eSmasputra 	    "mib2", KSTAT_TYPE_NAMED, NUM_OF_FIELDS(udp_named_kstat_t), 0);
7528ff550d0eSmasputra 
75297c478bd9Sstevel@tonic-gate 	if (udp_mibkp == NULL)
75307c478bd9Sstevel@tonic-gate 		return;
75317c478bd9Sstevel@tonic-gate 
75327c478bd9Sstevel@tonic-gate 	template.entrySize.value.ui32 = sizeof (mib2_udpEntry_t);
75337c478bd9Sstevel@tonic-gate 	template.entry6Size.value.ui32 = sizeof (mib2_udp6Entry_t);
75347c478bd9Sstevel@tonic-gate 
75357c478bd9Sstevel@tonic-gate 	bcopy(&template, udp_mibkp->ks_data, sizeof (template));
75367c478bd9Sstevel@tonic-gate 
75377c478bd9Sstevel@tonic-gate 	udp_mibkp->ks_update = udp_kstat_update;
75387c478bd9Sstevel@tonic-gate 
75397c478bd9Sstevel@tonic-gate 	kstat_install(udp_mibkp);
7540ff550d0eSmasputra 
7541ff550d0eSmasputra 	if ((udp_ksp = kstat_create(UDP_MOD_NAME, 0, "udpstat",
7542ff550d0eSmasputra 	    "net", KSTAT_TYPE_NAMED,
7543ff550d0eSmasputra 	    sizeof (udp_statistics) / sizeof (kstat_named_t),
7544ff550d0eSmasputra 	    KSTAT_FLAG_VIRTUAL)) != NULL) {
7545ff550d0eSmasputra 		udp_ksp->ks_data = &udp_statistics;
7546ff550d0eSmasputra 		kstat_install(udp_ksp);
7547ff550d0eSmasputra 	}
75487c478bd9Sstevel@tonic-gate }
75497c478bd9Sstevel@tonic-gate 
75507c478bd9Sstevel@tonic-gate static void
75517c478bd9Sstevel@tonic-gate udp_kstat_fini(void)
75527c478bd9Sstevel@tonic-gate {
7553ff550d0eSmasputra 	if (udp_ksp != NULL) {
7554ff550d0eSmasputra 		kstat_delete(udp_ksp);
7555ff550d0eSmasputra 		udp_ksp = NULL;
7556ff550d0eSmasputra 	}
7557ff550d0eSmasputra 	if (udp_mibkp != NULL) {
75587c478bd9Sstevel@tonic-gate 		kstat_delete(udp_mibkp);
75597c478bd9Sstevel@tonic-gate 		udp_mibkp = NULL;
75607c478bd9Sstevel@tonic-gate 	}
75617c478bd9Sstevel@tonic-gate }
75627c478bd9Sstevel@tonic-gate 
75637c478bd9Sstevel@tonic-gate static int
75647c478bd9Sstevel@tonic-gate udp_kstat_update(kstat_t *kp, int rw)
75657c478bd9Sstevel@tonic-gate {
75667c478bd9Sstevel@tonic-gate 	udp_named_kstat_t *udpkp;
75677c478bd9Sstevel@tonic-gate 
75687c478bd9Sstevel@tonic-gate 	if ((kp == NULL) || (kp->ks_data == NULL))
75697c478bd9Sstevel@tonic-gate 		return (EIO);
75707c478bd9Sstevel@tonic-gate 
75717c478bd9Sstevel@tonic-gate 	if (rw == KSTAT_WRITE)
75727c478bd9Sstevel@tonic-gate 		return (EACCES);
75737c478bd9Sstevel@tonic-gate 
75747c478bd9Sstevel@tonic-gate 	udpkp = (udp_named_kstat_t *)kp->ks_data;
75757c478bd9Sstevel@tonic-gate 
75767c478bd9Sstevel@tonic-gate 	udpkp->inDatagrams.value.ui32 =	udp_mib.udpInDatagrams;
75777c478bd9Sstevel@tonic-gate 	udpkp->inErrors.value.ui32 =	udp_mib.udpInErrors;
75787c478bd9Sstevel@tonic-gate 	udpkp->outDatagrams.value.ui32 = udp_mib.udpOutDatagrams;
75797c478bd9Sstevel@tonic-gate 	udpkp->outErrors.value.ui32 =	udp_mib.udpOutErrors;
75807c478bd9Sstevel@tonic-gate 
75817c478bd9Sstevel@tonic-gate 	return (0);
75827c478bd9Sstevel@tonic-gate }
75837c478bd9Sstevel@tonic-gate 
7584ff550d0eSmasputra /* ARGSUSED */
7585ff550d0eSmasputra static void
7586ff550d0eSmasputra udp_rput(queue_t *q, mblk_t *mp)
7587ff550d0eSmasputra {
7588ff550d0eSmasputra 	/*
7589ff550d0eSmasputra 	 * We get here whenever we do qreply() from IP,
7590ff550d0eSmasputra 	 * i.e as part of handlings ioctls, etc.
7591ff550d0eSmasputra 	 */
7592ff550d0eSmasputra 	putnext(q, mp);
7593ff550d0eSmasputra }
7594ff550d0eSmasputra 
7595ff550d0eSmasputra /*
7596ff550d0eSmasputra  * Read-side synchronous stream info entry point, called as a
7597ff550d0eSmasputra  * result of handling certain STREAMS ioctl operations.
7598ff550d0eSmasputra  */
7599ff550d0eSmasputra static int
7600ff550d0eSmasputra udp_rinfop(queue_t *q, infod_t *dp)
7601ff550d0eSmasputra {
7602ff550d0eSmasputra 	mblk_t	*mp;
7603ff550d0eSmasputra 	uint_t	cmd = dp->d_cmd;
7604ff550d0eSmasputra 	int	res = 0;
7605ff550d0eSmasputra 	int	error = 0;
7606ff550d0eSmasputra 	udp_t	*udp = Q_TO_UDP(RD(UDP_WR(q)));
7607ff550d0eSmasputra 	struct stdata *stp = STREAM(q);
7608ff550d0eSmasputra 
7609ff550d0eSmasputra 	mutex_enter(&udp->udp_drain_lock);
7610ff550d0eSmasputra 	/* If shutdown on read has happened, return nothing */
7611ff550d0eSmasputra 	mutex_enter(&stp->sd_lock);
7612ff550d0eSmasputra 	if (stp->sd_flag & STREOF) {
7613ff550d0eSmasputra 		mutex_exit(&stp->sd_lock);
7614ff550d0eSmasputra 		goto done;
7615ff550d0eSmasputra 	}
7616ff550d0eSmasputra 	mutex_exit(&stp->sd_lock);
7617ff550d0eSmasputra 
7618ff550d0eSmasputra 	if ((mp = udp->udp_rcv_list_head) == NULL)
7619ff550d0eSmasputra 		goto done;
7620ff550d0eSmasputra 
7621ff550d0eSmasputra 	ASSERT(DB_TYPE(mp) != M_DATA && mp->b_cont != NULL);
7622ff550d0eSmasputra 
7623ff550d0eSmasputra 	if (cmd & INFOD_COUNT) {
7624ff550d0eSmasputra 		/*
7625ff550d0eSmasputra 		 * Return the number of messages.
7626ff550d0eSmasputra 		 */
7627ff550d0eSmasputra 		dp->d_count += udp->udp_rcv_msgcnt;
7628ff550d0eSmasputra 		res |= INFOD_COUNT;
7629ff550d0eSmasputra 	}
7630ff550d0eSmasputra 	if (cmd & INFOD_BYTES) {
7631ff550d0eSmasputra 		/*
7632ff550d0eSmasputra 		 * Return size of all data messages.
7633ff550d0eSmasputra 		 */
7634ff550d0eSmasputra 		dp->d_bytes += udp->udp_rcv_cnt;
7635ff550d0eSmasputra 		res |= INFOD_BYTES;
7636ff550d0eSmasputra 	}
7637ff550d0eSmasputra 	if (cmd & INFOD_FIRSTBYTES) {
7638ff550d0eSmasputra 		/*
7639ff550d0eSmasputra 		 * Return size of first data message.
7640ff550d0eSmasputra 		 */
7641ff550d0eSmasputra 		dp->d_bytes = msgdsize(mp);
7642ff550d0eSmasputra 		res |= INFOD_FIRSTBYTES;
7643ff550d0eSmasputra 		dp->d_cmd &= ~INFOD_FIRSTBYTES;
7644ff550d0eSmasputra 	}
7645ff550d0eSmasputra 	if (cmd & INFOD_COPYOUT) {
7646ff550d0eSmasputra 		mblk_t *mp1 = mp->b_cont;
7647ff550d0eSmasputra 		int n;
7648ff550d0eSmasputra 		/*
7649ff550d0eSmasputra 		 * Return data contents of first message.
7650ff550d0eSmasputra 		 */
7651ff550d0eSmasputra 		ASSERT(DB_TYPE(mp1) == M_DATA);
7652ff550d0eSmasputra 		while (mp1 != NULL && dp->d_uiop->uio_resid > 0) {
7653ff550d0eSmasputra 			n = MIN(dp->d_uiop->uio_resid, MBLKL(mp1));
7654ff550d0eSmasputra 			if (n != 0 && (error = uiomove((char *)mp1->b_rptr, n,
7655ff550d0eSmasputra 			    UIO_READ, dp->d_uiop)) != 0) {
7656ff550d0eSmasputra 				goto done;
7657ff550d0eSmasputra 			}
7658ff550d0eSmasputra 			mp1 = mp1->b_cont;
7659ff550d0eSmasputra 		}
7660ff550d0eSmasputra 		res |= INFOD_COPYOUT;
7661ff550d0eSmasputra 		dp->d_cmd &= ~INFOD_COPYOUT;
7662ff550d0eSmasputra 	}
7663ff550d0eSmasputra done:
7664ff550d0eSmasputra 	mutex_exit(&udp->udp_drain_lock);
7665ff550d0eSmasputra 
7666ff550d0eSmasputra 	dp->d_res |= res;
7667ff550d0eSmasputra 
7668ff550d0eSmasputra 	return (error);
7669ff550d0eSmasputra }
7670ff550d0eSmasputra 
7671ff550d0eSmasputra /*
7672ff550d0eSmasputra  * Read-side synchronous stream entry point.  This is called as a result
7673ff550d0eSmasputra  * of recv/read operation done at sockfs, and is guaranteed to execute
7674ff550d0eSmasputra  * outside of the interrupt thread context.  It returns a single datagram
7675ff550d0eSmasputra  * (b_cont chain of T_UNITDATA_IND plus data) to the upper layer.
7676ff550d0eSmasputra  */
7677ff550d0eSmasputra static int
7678ff550d0eSmasputra udp_rrw(queue_t *q, struiod_t *dp)
7679ff550d0eSmasputra {
7680ff550d0eSmasputra 	mblk_t	*mp;
7681ff550d0eSmasputra 	udp_t	*udp = Q_TO_UDP(_RD(UDP_WR(q)));
7682ff550d0eSmasputra 
7683ff550d0eSmasputra 	/* We should never get here when we're in SNMP mode */
7684ff550d0eSmasputra 	ASSERT(!(udp->udp_connp->conn_flags & IPCL_UDPMOD));
7685ff550d0eSmasputra 
7686ff550d0eSmasputra 	/*
7687ff550d0eSmasputra 	 * Dequeue datagram from the head of the list and return
7688ff550d0eSmasputra 	 * it to caller; also ensure that RSLEEP sd_wakeq flag is
7689ff550d0eSmasputra 	 * set/cleared depending on whether or not there's data
7690ff550d0eSmasputra 	 * remaining in the list.
7691ff550d0eSmasputra 	 */
7692ff550d0eSmasputra 	mutex_enter(&udp->udp_drain_lock);
7693ff550d0eSmasputra 	if (!udp->udp_direct_sockfs) {
7694ff550d0eSmasputra 		mutex_exit(&udp->udp_drain_lock);
7695ff550d0eSmasputra 		UDP_STAT(udp_rrw_busy);
7696ff550d0eSmasputra 		return (EBUSY);
7697ff550d0eSmasputra 	}
7698ff550d0eSmasputra 	if ((mp = udp->udp_rcv_list_head) != NULL) {
7699ff550d0eSmasputra 		uint_t size = msgdsize(mp);
7700ff550d0eSmasputra 
7701ff550d0eSmasputra 		/* Last datagram in the list? */
7702ff550d0eSmasputra 		if ((udp->udp_rcv_list_head = mp->b_next) == NULL)
7703ff550d0eSmasputra 			udp->udp_rcv_list_tail = NULL;
7704ff550d0eSmasputra 		mp->b_next = NULL;
7705ff550d0eSmasputra 
7706ff550d0eSmasputra 		udp->udp_rcv_cnt -= size;
7707ff550d0eSmasputra 		udp->udp_rcv_msgcnt--;
7708ff550d0eSmasputra 		UDP_STAT(udp_rrw_msgcnt);
7709ff550d0eSmasputra 
7710ff550d0eSmasputra 		/* No longer flow-controlling? */
7711ff550d0eSmasputra 		if (udp->udp_rcv_cnt < udp->udp_rcv_hiwat &&
7712ff550d0eSmasputra 		    udp->udp_rcv_msgcnt < udp->udp_rcv_hiwat)
7713ff550d0eSmasputra 			udp->udp_drain_qfull = B_FALSE;
7714ff550d0eSmasputra 	}
7715ff550d0eSmasputra 	if (udp->udp_rcv_list_head == NULL) {
7716ff550d0eSmasputra 		/*
7717ff550d0eSmasputra 		 * Either we just dequeued the last datagram or
7718ff550d0eSmasputra 		 * we get here from sockfs and have nothing to
7719ff550d0eSmasputra 		 * return; in this case clear RSLEEP.
7720ff550d0eSmasputra 		 */
7721ff550d0eSmasputra 		ASSERT(udp->udp_rcv_cnt == 0);
7722ff550d0eSmasputra 		ASSERT(udp->udp_rcv_msgcnt == 0);
7723ff550d0eSmasputra 		ASSERT(udp->udp_rcv_list_tail == NULL);
7724ff550d0eSmasputra 		STR_WAKEUP_CLEAR(STREAM(q));
7725ff550d0eSmasputra 	} else {
7726ff550d0eSmasputra 		/*
7727ff550d0eSmasputra 		 * More data follows; we need udp_rrw() to be
7728ff550d0eSmasputra 		 * called in future to pick up the rest.
7729ff550d0eSmasputra 		 */
7730ff550d0eSmasputra 		STR_WAKEUP_SET(STREAM(q));
7731ff550d0eSmasputra 	}
7732ff550d0eSmasputra 	mutex_exit(&udp->udp_drain_lock);
7733ff550d0eSmasputra 	dp->d_mp = mp;
7734ff550d0eSmasputra 	return (0);
7735ff550d0eSmasputra }
7736ff550d0eSmasputra 
7737ff550d0eSmasputra /*
7738ff550d0eSmasputra  * Enqueue a completely-built T_UNITDATA_IND message into the receive
7739ff550d0eSmasputra  * list; this is typically executed within the interrupt thread context
7740ff550d0eSmasputra  * and so we do things as quickly as possible.
7741ff550d0eSmasputra  */
7742ff550d0eSmasputra static void
7743ff550d0eSmasputra udp_rcv_enqueue(queue_t *q, udp_t *udp, mblk_t *mp, uint_t pkt_len)
7744ff550d0eSmasputra {
7745ff550d0eSmasputra 	ASSERT(q == RD(q));
7746ff550d0eSmasputra 	ASSERT(pkt_len == msgdsize(mp));
7747ff550d0eSmasputra 	ASSERT(mp->b_next == NULL && mp->b_cont != NULL);
7748ff550d0eSmasputra 	ASSERT(DB_TYPE(mp) == M_PROTO && DB_TYPE(mp->b_cont) == M_DATA);
7749ff550d0eSmasputra 	ASSERT(MBLKL(mp) >= sizeof (struct T_unitdata_ind));
7750ff550d0eSmasputra 
7751ff550d0eSmasputra 	mutex_enter(&udp->udp_drain_lock);
7752ff550d0eSmasputra 	/*
7753ff550d0eSmasputra 	 * Wake up and signal the receiving app; it is okay to do this
7754ff550d0eSmasputra 	 * before enqueueing the mp because we are holding the drain lock.
7755ff550d0eSmasputra 	 * One of the advantages of synchronous stream is the ability for
7756ff550d0eSmasputra 	 * us to find out when the application performs a read on the
7757ff550d0eSmasputra 	 * socket by way of udp_rrw() entry point being called.  We need
7758ff550d0eSmasputra 	 * to generate SIGPOLL/SIGIO for each received data in the case
7759ff550d0eSmasputra 	 * of asynchronous socket just as in the strrput() case.  However,
7760ff550d0eSmasputra 	 * we only wake the application up when necessary, i.e. during the
7761ff550d0eSmasputra 	 * first enqueue.  When udp_rrw() is called, we send up a single
7762ff550d0eSmasputra 	 * datagram upstream and call STR_WAKEUP_SET() again when there
7763ff550d0eSmasputra 	 * are still data remaining in our receive queue.
7764ff550d0eSmasputra 	 */
7765ff550d0eSmasputra 	if (udp->udp_rcv_list_head == NULL) {
7766ff550d0eSmasputra 		STR_WAKEUP_SET(STREAM(q));
7767ff550d0eSmasputra 		udp->udp_rcv_list_head = mp;
7768ff550d0eSmasputra 	} else {
7769ff550d0eSmasputra 		udp->udp_rcv_list_tail->b_next = mp;
7770ff550d0eSmasputra 	}
7771ff550d0eSmasputra 	udp->udp_rcv_list_tail = mp;
7772ff550d0eSmasputra 	udp->udp_rcv_cnt += pkt_len;
7773ff550d0eSmasputra 	udp->udp_rcv_msgcnt++;
7774ff550d0eSmasputra 
7775ff550d0eSmasputra 	/* Need to flow-control? */
7776ff550d0eSmasputra 	if (udp->udp_rcv_cnt >= udp->udp_rcv_hiwat ||
7777ff550d0eSmasputra 	    udp->udp_rcv_msgcnt >= udp->udp_rcv_hiwat)
7778ff550d0eSmasputra 		udp->udp_drain_qfull = B_TRUE;
7779ff550d0eSmasputra 
7780ff550d0eSmasputra 	/* Update poll events and send SIGPOLL/SIGIO if necessary */
7781ff550d0eSmasputra 	STR_SENDSIG(STREAM(q));
7782ff550d0eSmasputra 	mutex_exit(&udp->udp_drain_lock);
7783ff550d0eSmasputra }
7784ff550d0eSmasputra 
7785ff550d0eSmasputra /*
7786ff550d0eSmasputra  * Drain the contents of receive list to the module upstream; we do
7787ff550d0eSmasputra  * this during close or when we fallback to the slow mode due to
7788ff550d0eSmasputra  * sockmod being popped or a module being pushed on top of us.
7789ff550d0eSmasputra  */
7790ff550d0eSmasputra static void
7791ff550d0eSmasputra udp_rcv_drain(queue_t *q, udp_t *udp, boolean_t closing)
7792ff550d0eSmasputra {
7793ff550d0eSmasputra 	mblk_t *mp;
7794ff550d0eSmasputra 
7795ff550d0eSmasputra 	ASSERT(q == RD(q));
7796ff550d0eSmasputra 
7797ff550d0eSmasputra 	mutex_enter(&udp->udp_drain_lock);
7798ff550d0eSmasputra 	/*
7799ff550d0eSmasputra 	 * There is no race with a concurrent udp_input() sending
7800ff550d0eSmasputra 	 * up packets using putnext() after we have cleared the
7801ff550d0eSmasputra 	 * udp_direct_sockfs flag but before we have completed
7802ff550d0eSmasputra 	 * sending up the packets in udp_rcv_list, since we are
7803ff550d0eSmasputra 	 * either a writer or we have quiesced the conn.
7804ff550d0eSmasputra 	 */
7805ff550d0eSmasputra 	udp->udp_direct_sockfs = B_FALSE;
7806ff550d0eSmasputra 	mutex_exit(&udp->udp_drain_lock);
7807ff550d0eSmasputra 
7808ff550d0eSmasputra 	if (udp->udp_rcv_list_head != NULL)
7809ff550d0eSmasputra 		UDP_STAT(udp_drain);
7810ff550d0eSmasputra 
7811ff550d0eSmasputra 	/*
7812ff550d0eSmasputra 	 * Send up everything via putnext(); note here that we
7813ff550d0eSmasputra 	 * don't need the udp_drain_lock to protect us since
7814ff550d0eSmasputra 	 * nothing can enter udp_rrw() and that we currently
7815ff550d0eSmasputra 	 * have exclusive access to this udp.
7816ff550d0eSmasputra 	 */
7817ff550d0eSmasputra 	while ((mp = udp->udp_rcv_list_head) != NULL) {
7818ff550d0eSmasputra 		udp->udp_rcv_list_head = mp->b_next;
7819ff550d0eSmasputra 		mp->b_next = NULL;
7820ff550d0eSmasputra 		udp->udp_rcv_cnt -= msgdsize(mp);
7821ff550d0eSmasputra 		udp->udp_rcv_msgcnt--;
7822ff550d0eSmasputra 		if (closing) {
7823ff550d0eSmasputra 			freemsg(mp);
7824ff550d0eSmasputra 		} else {
7825ff550d0eSmasputra 			putnext(q, mp);
7826ff550d0eSmasputra 		}
7827ff550d0eSmasputra 	}
7828ff550d0eSmasputra 	ASSERT(udp->udp_rcv_cnt == 0);
7829ff550d0eSmasputra 	ASSERT(udp->udp_rcv_msgcnt == 0);
7830ff550d0eSmasputra 	ASSERT(udp->udp_rcv_list_head == NULL);
7831ff550d0eSmasputra 	udp->udp_rcv_list_tail = NULL;
7832ff550d0eSmasputra 	udp->udp_drain_qfull = B_FALSE;
7833ff550d0eSmasputra }
7834ff550d0eSmasputra 
7835ff550d0eSmasputra static size_t
7836ff550d0eSmasputra udp_set_rcv_hiwat(udp_t *udp, size_t size)
7837ff550d0eSmasputra {
7838ff550d0eSmasputra 	/* We add a bit of extra buffering */
7839ff550d0eSmasputra 	size += size >> 1;
7840ff550d0eSmasputra 	if (size > udp_max_buf)
7841ff550d0eSmasputra 		size = udp_max_buf;
7842ff550d0eSmasputra 
7843ff550d0eSmasputra 	udp->udp_rcv_hiwat = size;
7844ff550d0eSmasputra 	return (size);
7845ff550d0eSmasputra }
7846ff550d0eSmasputra 
78477c478bd9Sstevel@tonic-gate /*
78487c478bd9Sstevel@tonic-gate  * Little helper for IPsec's NAT-T processing.
78497c478bd9Sstevel@tonic-gate  */
78507c478bd9Sstevel@tonic-gate boolean_t
78517c478bd9Sstevel@tonic-gate udp_compute_checksum(void)
78527c478bd9Sstevel@tonic-gate {
78537c478bd9Sstevel@tonic-gate 	return (udp_do_checksum);
78547c478bd9Sstevel@tonic-gate }
7855