xref: /onnv-gate/usr/src/uts/common/inet/ip/icmp.c (revision 8477:eb9540d8095f)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
51289Sja97890  * Common Development and Distribution License (the "License").
61289Sja97890  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*8477SRao.Shoaib@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate /* Copyright (c) 1990 Mentat Inc. */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate #include <sys/types.h>
280Sstevel@tonic-gate #include <sys/stream.h>
290Sstevel@tonic-gate #include <sys/stropts.h>
300Sstevel@tonic-gate #include <sys/strlog.h>
310Sstevel@tonic-gate #include <sys/strsun.h>
320Sstevel@tonic-gate #define	_SUN_TPI_VERSION 2
330Sstevel@tonic-gate #include <sys/tihdr.h>
340Sstevel@tonic-gate #include <sys/timod.h>
350Sstevel@tonic-gate #include <sys/ddi.h>
360Sstevel@tonic-gate #include <sys/sunddi.h>
371676Sjpk #include <sys/strsubr.h>
380Sstevel@tonic-gate #include <sys/cmn_err.h>
390Sstevel@tonic-gate #include <sys/debug.h>
400Sstevel@tonic-gate #include <sys/kmem.h>
410Sstevel@tonic-gate #include <sys/policy.h>
421676Sjpk #include <sys/priv.h>
430Sstevel@tonic-gate #include <sys/zone.h>
441673Sgt145670 #include <sys/time.h>
450Sstevel@tonic-gate 
468348SEric.Yu@Sun.COM #include <sys/sockio.h>
470Sstevel@tonic-gate #include <sys/socket.h>
488348SEric.Yu@Sun.COM #include <sys/socketvar.h>
490Sstevel@tonic-gate #include <sys/isa_defs.h>
500Sstevel@tonic-gate #include <sys/suntpi.h>
510Sstevel@tonic-gate #include <sys/xti_inet.h>
523448Sdh155122 #include <sys/netstack.h>
530Sstevel@tonic-gate 
540Sstevel@tonic-gate #include <net/route.h>
550Sstevel@tonic-gate #include <net/if.h>
560Sstevel@tonic-gate 
570Sstevel@tonic-gate #include <netinet/in.h>
580Sstevel@tonic-gate #include <netinet/ip6.h>
590Sstevel@tonic-gate #include <netinet/icmp6.h>
600Sstevel@tonic-gate #include <inet/common.h>
610Sstevel@tonic-gate #include <inet/ip.h>
620Sstevel@tonic-gate #include <inet/ip6.h>
638348SEric.Yu@Sun.COM #include <inet/proto_set.h>
640Sstevel@tonic-gate #include <inet/nd.h>
650Sstevel@tonic-gate #include <inet/optcom.h>
660Sstevel@tonic-gate #include <inet/snmpcom.h>
670Sstevel@tonic-gate #include <inet/kstatcom.h>
680Sstevel@tonic-gate #include <inet/rawip_impl.h>
690Sstevel@tonic-gate 
700Sstevel@tonic-gate #include <netinet/ip_mroute.h>
710Sstevel@tonic-gate #include <inet/tcp.h>
720Sstevel@tonic-gate #include <net/pfkeyv2.h>
730Sstevel@tonic-gate #include <inet/ipsec_info.h>
740Sstevel@tonic-gate #include <inet/ipclassifier.h>
750Sstevel@tonic-gate 
761676Sjpk #include <sys/tsol/label.h>
771676Sjpk #include <sys/tsol/tnet.h>
781676Sjpk 
793318Srshoaib #include <inet/ip_ire.h>
803318Srshoaib #include <inet/ip_if.h>
813318Srshoaib 
823318Srshoaib #include <inet/ip_impl.h>
838348SEric.Yu@Sun.COM #include <sys/disp.h>
843318Srshoaib 
850Sstevel@tonic-gate /*
860Sstevel@tonic-gate  * Synchronization notes:
870Sstevel@tonic-gate  *
885240Snordmark  * RAWIP is MT and uses the usual kernel synchronization primitives. There is
895240Snordmark  * locks, which is icmp_rwlock. We also use conn_lock when updating things
905240Snordmark  * which affect the IP classifier lookup.
915240Snordmark  * The lock order is icmp_rwlock -> conn_lock.
925240Snordmark  *
935240Snordmark  * The icmp_rwlock:
945240Snordmark  * This protects most of the other fields in the icmp_t. The exact list of
955240Snordmark  * fields which are protected by each of the above locks is documented in
965240Snordmark  * the icmp_t structure definition.
975240Snordmark  *
985240Snordmark  * Plumbing notes:
995240Snordmark  * ICMP is always a device driver. For compatibility with mibopen() code
1005240Snordmark  * it is possible to I_PUSH "icmp", but that results in pushing a passthrough
1015240Snordmark  * dummy module.
1020Sstevel@tonic-gate  */
1030Sstevel@tonic-gate 
1040Sstevel@tonic-gate static void	icmp_addr_req(queue_t *q, mblk_t *mp);
1058348SEric.Yu@Sun.COM static void	icmp_tpi_bind(queue_t *q, mblk_t *mp);
1068348SEric.Yu@Sun.COM static int	icmp_bind_proto(conn_t *connp);
1075240Snordmark static int	icmp_build_hdrs(icmp_t *icmp);
1080Sstevel@tonic-gate static void	icmp_capability_req(queue_t *q, mblk_t *mp);
1098348SEric.Yu@Sun.COM static int	icmp_close(queue_t *q, int flags);
1108348SEric.Yu@Sun.COM static void	icmp_tpi_connect(queue_t *q, mblk_t *mp);
1118348SEric.Yu@Sun.COM static void	icmp_tpi_disconnect(queue_t *q, mblk_t *mp);
1120Sstevel@tonic-gate static void	icmp_err_ack(queue_t *q, mblk_t *mp, t_scalar_t t_error,
1130Sstevel@tonic-gate 		    int sys_error);
1140Sstevel@tonic-gate static void	icmp_err_ack_prim(queue_t *q, mblk_t *mp, t_scalar_t primitive,
1150Sstevel@tonic-gate 		    t_scalar_t t_error, int sys_error);
1168348SEric.Yu@Sun.COM static void	icmp_icmp_error(conn_t *connp, mblk_t *mp);
1178348SEric.Yu@Sun.COM static void	icmp_icmp_error_ipv6(conn_t *connp, mblk_t *mp);
1180Sstevel@tonic-gate static void	icmp_info_req(queue_t *q, mblk_t *mp);
1195240Snordmark static void	icmp_input(void *, mblk_t *, void *);
1208348SEric.Yu@Sun.COM static conn_t 	*icmp_open(int family, cred_t *credp, int *err, int flags);
1215240Snordmark static int	icmp_openv4(queue_t *q, dev_t *devp, int flag, int sflag,
1225240Snordmark 		    cred_t *credp);
1235240Snordmark static int	icmp_openv6(queue_t *q, dev_t *devp, int flag, int sflag,
1245240Snordmark 		    cred_t *credp);
1250Sstevel@tonic-gate static int	icmp_unitdata_opt_process(queue_t *q, mblk_t *mp,
1260Sstevel@tonic-gate 		    int *errorp, void *thisdg_attrs);
1270Sstevel@tonic-gate static boolean_t icmp_opt_allow_udr_set(t_scalar_t level, t_scalar_t name);
1288348SEric.Yu@Sun.COM int		icmp_opt_set(conn_t *connp, uint_t optset_context,
1290Sstevel@tonic-gate 		    int level, int name, uint_t inlen,
1300Sstevel@tonic-gate 		    uchar_t *invalp, uint_t *outlenp, uchar_t *outvalp,
1318348SEric.Yu@Sun.COM 		    void *thisdg_attrs, cred_t *cr);
1328348SEric.Yu@Sun.COM int		icmp_opt_get(conn_t *connp, int level, int name,
1330Sstevel@tonic-gate 		    uchar_t *ptr);
1340Sstevel@tonic-gate static int	icmp_param_get(queue_t *q, mblk_t *mp, caddr_t cp, cred_t *cr);
1353448Sdh155122 static boolean_t icmp_param_register(IDP *ndp, icmpparam_t *icmppa, int cnt);
1360Sstevel@tonic-gate static int	icmp_param_set(queue_t *q, mblk_t *mp, char *value,
1370Sstevel@tonic-gate 		    caddr_t cp, cred_t *cr);
1380Sstevel@tonic-gate static int	icmp_snmp_set(queue_t *q, t_scalar_t level, t_scalar_t name,
1390Sstevel@tonic-gate 		    uchar_t *ptr, int len);
1400Sstevel@tonic-gate static int	icmp_status_report(queue_t *q, mblk_t *mp, caddr_t cp,
1410Sstevel@tonic-gate 		    cred_t *cr);
1420Sstevel@tonic-gate static void	icmp_ud_err(queue_t *q, mblk_t *mp, t_scalar_t err);
1438348SEric.Yu@Sun.COM static void	icmp_tpi_unbind(queue_t *q, mblk_t *mp);
1440Sstevel@tonic-gate static void	icmp_wput(queue_t *q, mblk_t *mp);
1458348SEric.Yu@Sun.COM static void	icmp_wput_fallback(queue_t *q, mblk_t *mp);
1468348SEric.Yu@Sun.COM static int	raw_ip_send_data_v6(queue_t *q, conn_t *connp, mblk_t *mp,
1478348SEric.Yu@Sun.COM 		    sin6_t *sin6, ip6_pkt_t *ipp);
1488348SEric.Yu@Sun.COM static int	raw_ip_send_data_v4(queue_t *q, conn_t *connp, mblk_t *mp,
1498348SEric.Yu@Sun.COM 		    ipaddr_t v4dst, ip4_pkt_t *pktinfop);
1500Sstevel@tonic-gate static void	icmp_wput_other(queue_t *q, mblk_t *mp);
1510Sstevel@tonic-gate static void	icmp_wput_iocdata(queue_t *q, mblk_t *mp);
1520Sstevel@tonic-gate static void	icmp_wput_restricted(queue_t *q, mblk_t *mp);
1530Sstevel@tonic-gate 
1543448Sdh155122 static void	*rawip_stack_init(netstackid_t stackid, netstack_t *ns);
1553448Sdh155122 static void	rawip_stack_fini(netstackid_t stackid, void *arg);
1563448Sdh155122 
1573448Sdh155122 static void	*rawip_kstat_init(netstackid_t stackid);
1583448Sdh155122 static void	rawip_kstat_fini(netstackid_t stackid, kstat_t *ksp);
1590Sstevel@tonic-gate static int	rawip_kstat_update(kstat_t *kp, int rw);
1608348SEric.Yu@Sun.COM static void	rawip_stack_shutdown(netstackid_t stackid, void *arg);
1618348SEric.Yu@Sun.COM static int	rawip_do_getsockname(icmp_t *icmp, struct sockaddr *sa,
1628348SEric.Yu@Sun.COM 		    uint_t *salenp);
1638348SEric.Yu@Sun.COM static int	rawip_do_getpeername(icmp_t *icmp, struct sockaddr *sa,
1648348SEric.Yu@Sun.COM 		    uint_t *salenp);
1658348SEric.Yu@Sun.COM 
1668348SEric.Yu@Sun.COM int		rawip_getsockname(sock_lower_handle_t, struct sockaddr *,
1678348SEric.Yu@Sun.COM 		    socklen_t *, cred_t *);
1688348SEric.Yu@Sun.COM int		rawip_getpeername(sock_lower_handle_t, struct sockaddr *,
1698348SEric.Yu@Sun.COM 		    socklen_t *, cred_t *);
1700Sstevel@tonic-gate 
1715240Snordmark static struct module_info icmp_mod_info =  {
1720Sstevel@tonic-gate 	5707, "icmp", 1, INFPSZ, 512, 128
1730Sstevel@tonic-gate };
1740Sstevel@tonic-gate 
1755240Snordmark /*
1765240Snordmark  * Entry points for ICMP as a device.
1775240Snordmark  * We have separate open functions for the /dev/icmp and /dev/icmp6 devices.
1785240Snordmark  */
1795240Snordmark static struct qinit icmprinitv4 = {
1805240Snordmark 	NULL, NULL, icmp_openv4, icmp_close, NULL, &icmp_mod_info
1815240Snordmark };
1825240Snordmark 
1835240Snordmark static struct qinit icmprinitv6 = {
1845240Snordmark 	NULL, NULL, icmp_openv6, icmp_close, NULL, &icmp_mod_info
1850Sstevel@tonic-gate };
1860Sstevel@tonic-gate 
1875240Snordmark static struct qinit icmpwinit = {
1888348SEric.Yu@Sun.COM 	(pfi_t)icmp_wput, NULL, NULL, NULL, NULL, &icmp_mod_info
1898348SEric.Yu@Sun.COM };
1908348SEric.Yu@Sun.COM 
1918348SEric.Yu@Sun.COM /* ICMP entry point during fallback */
1928348SEric.Yu@Sun.COM static struct qinit icmp_fallback_sock_winit = {
1938348SEric.Yu@Sun.COM 	(pfi_t)icmp_wput_fallback, NULL, NULL, NULL, NULL, &icmp_mod_info
1940Sstevel@tonic-gate };
1950Sstevel@tonic-gate 
1965240Snordmark /* For AF_INET aka /dev/icmp */
1975240Snordmark struct streamtab icmpinfov4 = {
1985240Snordmark 	&icmprinitv4, &icmpwinit
1995240Snordmark };
2005240Snordmark 
2015240Snordmark /* For AF_INET6 aka /dev/icmp6 */
2025240Snordmark struct streamtab icmpinfov6 = {
2035240Snordmark 	&icmprinitv6, &icmpwinit
2040Sstevel@tonic-gate };
2050Sstevel@tonic-gate 
2060Sstevel@tonic-gate static sin_t	sin_null;	/* Zero address for quick clears */
2070Sstevel@tonic-gate static sin6_t	sin6_null;	/* Zero address for quick clears */
2080Sstevel@tonic-gate 
2090Sstevel@tonic-gate /* Default structure copied into T_INFO_ACK messages */
2100Sstevel@tonic-gate static struct T_info_ack icmp_g_t_info_ack = {
2110Sstevel@tonic-gate 	T_INFO_ACK,
2120Sstevel@tonic-gate 	IP_MAXPACKET,	 /* TSDU_size.  icmp allows maximum size messages. */
2130Sstevel@tonic-gate 	T_INVALID,	/* ETSDU_size.  icmp does not support expedited data. */
2140Sstevel@tonic-gate 	T_INVALID,	/* CDATA_size. icmp does not support connect data. */
2150Sstevel@tonic-gate 	T_INVALID,	/* DDATA_size. icmp does not support disconnect data. */
2160Sstevel@tonic-gate 	0,		/* ADDR_size - filled in later. */
2170Sstevel@tonic-gate 	0,		/* OPT_size - not initialized here */
2180Sstevel@tonic-gate 	IP_MAXPACKET,	/* TIDU_size.  icmp allows maximum size messages. */
2190Sstevel@tonic-gate 	T_CLTS,		/* SERV_type.  icmp supports connection-less. */
2200Sstevel@tonic-gate 	TS_UNBND,	/* CURRENT_state.  This is set from icmp_state. */
2210Sstevel@tonic-gate 	(XPG4_1|SENDZERO) /* PROVIDER_flag */
2220Sstevel@tonic-gate };
2230Sstevel@tonic-gate 
2240Sstevel@tonic-gate /*
2253448Sdh155122  * Table of ND variables supported by icmp.  These are loaded into is_nd
2263448Sdh155122  * when the stack instance is created.
2270Sstevel@tonic-gate  * All of these are alterable, within the min/max values given, at run time.
2280Sstevel@tonic-gate  */
2290Sstevel@tonic-gate static icmpparam_t	icmp_param_arr[] = {
2300Sstevel@tonic-gate 	/* min	max	value	name */
2310Sstevel@tonic-gate 	{ 0,	128,	32,	"icmp_wroff_extra" },
2320Sstevel@tonic-gate 	{ 1,	255,	255,	"icmp_ipv4_ttl" },
2330Sstevel@tonic-gate 	{ 0, IPV6_MAX_HOPS, IPV6_DEFAULT_HOPS,	"icmp_ipv6_hoplimit"},
2340Sstevel@tonic-gate 	{ 0,	1,	1,	"icmp_bsd_compat" },
2350Sstevel@tonic-gate 	{ 4096,	65536,	8192,	"icmp_xmit_hiwat"},
2360Sstevel@tonic-gate 	{ 0,	65536,	1024,	"icmp_xmit_lowat"},
2370Sstevel@tonic-gate 	{ 4096,	65536,	8192,	"icmp_recv_hiwat"},
2380Sstevel@tonic-gate 	{ 65536, 1024*1024*1024, 256*1024,	"icmp_max_buf"},
2390Sstevel@tonic-gate };
2403448Sdh155122 #define	is_wroff_extra			is_param_arr[0].icmp_param_value
2413448Sdh155122 #define	is_ipv4_ttl			is_param_arr[1].icmp_param_value
2423448Sdh155122 #define	is_ipv6_hoplimit		is_param_arr[2].icmp_param_value
2433448Sdh155122 #define	is_bsd_compat			is_param_arr[3].icmp_param_value
2443448Sdh155122 #define	is_xmit_hiwat			is_param_arr[4].icmp_param_value
2453448Sdh155122 #define	is_xmit_lowat			is_param_arr[5].icmp_param_value
2463448Sdh155122 #define	is_recv_hiwat			is_param_arr[6].icmp_param_value
2473448Sdh155122 #define	is_max_buf			is_param_arr[7].icmp_param_value
2480Sstevel@tonic-gate 
2498348SEric.Yu@Sun.COM static int rawip_do_bind(conn_t *connp, struct sockaddr *sa, socklen_t len);
2508348SEric.Yu@Sun.COM static int rawip_do_connect(conn_t *connp, const struct sockaddr *sa,
2518348SEric.Yu@Sun.COM     socklen_t len);
2528348SEric.Yu@Sun.COM static void rawip_post_ip_bind_connect(icmp_t *icmp, mblk_t *ire_mp, int error);
2538348SEric.Yu@Sun.COM 
2540Sstevel@tonic-gate /*
2550Sstevel@tonic-gate  * This routine is called to handle each O_T_BIND_REQ/T_BIND_REQ message
2560Sstevel@tonic-gate  * passed to icmp_wput.
2570Sstevel@tonic-gate  * The O_T_BIND_REQ/T_BIND_REQ is passed downstream to ip with the ICMP
2580Sstevel@tonic-gate  * protocol type placed in the message following the address. A T_BIND_ACK
2595240Snordmark  * message is returned by ip_bind_v4/v6.
2600Sstevel@tonic-gate  */
2610Sstevel@tonic-gate static void
2628348SEric.Yu@Sun.COM icmp_tpi_bind(queue_t *q, mblk_t *mp)
2630Sstevel@tonic-gate {
2648348SEric.Yu@Sun.COM 	int	error;
2658348SEric.Yu@Sun.COM 	struct sockaddr *sa;
2668348SEric.Yu@Sun.COM 	struct T_bind_req *tbr;
2678348SEric.Yu@Sun.COM 	socklen_t	len;
2680Sstevel@tonic-gate 	sin_t	*sin;
2690Sstevel@tonic-gate 	sin6_t	*sin6;
2708348SEric.Yu@Sun.COM 	icmp_t		*icmp;
2715240Snordmark 	conn_t	*connp = Q_TO_CONN(q);
2728348SEric.Yu@Sun.COM 	mblk_t *mp1;
2735240Snordmark 
2745240Snordmark 	icmp = connp->conn_icmp;
2750Sstevel@tonic-gate 	if ((mp->b_wptr - mp->b_rptr) < sizeof (*tbr)) {
2760Sstevel@tonic-gate 		(void) mi_strlog(q, 1, SL_ERROR|SL_TRACE,
2770Sstevel@tonic-gate 		    "icmp_bind: bad req, len %u",
2780Sstevel@tonic-gate 		    (uint_t)(mp->b_wptr - mp->b_rptr));
2790Sstevel@tonic-gate 		icmp_err_ack(q, mp, TPROTO, 0);
2800Sstevel@tonic-gate 		return;
2810Sstevel@tonic-gate 	}
2828348SEric.Yu@Sun.COM 
2830Sstevel@tonic-gate 	if (icmp->icmp_state != TS_UNBND) {
2840Sstevel@tonic-gate 		(void) mi_strlog(q, 1, SL_ERROR|SL_TRACE,
2850Sstevel@tonic-gate 		    "icmp_bind: bad state, %d", icmp->icmp_state);
2860Sstevel@tonic-gate 		icmp_err_ack(q, mp, TOUTSTATE, 0);
2870Sstevel@tonic-gate 		return;
2880Sstevel@tonic-gate 	}
2898348SEric.Yu@Sun.COM 
2900Sstevel@tonic-gate 	/*
2910Sstevel@tonic-gate 	 * Reallocate the message to make sure we have enough room for an
2920Sstevel@tonic-gate 	 * address and the protocol type.
2930Sstevel@tonic-gate 	 */
2940Sstevel@tonic-gate 	mp1 = reallocb(mp, sizeof (struct T_bind_ack) + sizeof (sin6_t) + 1, 1);
2950Sstevel@tonic-gate 	if (!mp1) {
2960Sstevel@tonic-gate 		icmp_err_ack(q, mp, TSYSERR, ENOMEM);
2970Sstevel@tonic-gate 		return;
2980Sstevel@tonic-gate 	}
2990Sstevel@tonic-gate 	mp = mp1;
3008348SEric.Yu@Sun.COM 
3018348SEric.Yu@Sun.COM 	/* Reset the message type in preparation for shipping it back. */
3028348SEric.Yu@Sun.COM 	DB_TYPE(mp) = M_PCPROTO;
3030Sstevel@tonic-gate 	tbr = (struct T_bind_req *)mp->b_rptr;
3048348SEric.Yu@Sun.COM 	len = tbr->ADDR_length;
3058348SEric.Yu@Sun.COM 	switch (len) {
3068348SEric.Yu@Sun.COM 	case 0:	/* request for a generic port */
3070Sstevel@tonic-gate 		tbr->ADDR_offset = sizeof (struct T_bind_req);
3080Sstevel@tonic-gate 		if (icmp->icmp_family == AF_INET) {
3090Sstevel@tonic-gate 			tbr->ADDR_length = sizeof (sin_t);
3100Sstevel@tonic-gate 			sin = (sin_t *)&tbr[1];
3110Sstevel@tonic-gate 			*sin = sin_null;
3120Sstevel@tonic-gate 			sin->sin_family = AF_INET;
3130Sstevel@tonic-gate 			mp->b_wptr = (uchar_t *)&sin[1];
3148348SEric.Yu@Sun.COM 			sa = (struct sockaddr *)sin;
3158348SEric.Yu@Sun.COM 			len = sizeof (sin_t);
3160Sstevel@tonic-gate 		} else {
3170Sstevel@tonic-gate 			ASSERT(icmp->icmp_family == AF_INET6);
3180Sstevel@tonic-gate 			tbr->ADDR_length = sizeof (sin6_t);
3190Sstevel@tonic-gate 			sin6 = (sin6_t *)&tbr[1];
3200Sstevel@tonic-gate 			*sin6 = sin6_null;
3210Sstevel@tonic-gate 			sin6->sin6_family = AF_INET6;
3220Sstevel@tonic-gate 			mp->b_wptr = (uchar_t *)&sin6[1];
3238348SEric.Yu@Sun.COM 			sa = (struct sockaddr *)sin6;
3248348SEric.Yu@Sun.COM 			len = sizeof (sin6_t);
3250Sstevel@tonic-gate 		}
3260Sstevel@tonic-gate 		break;
3278348SEric.Yu@Sun.COM 
3288348SEric.Yu@Sun.COM 	case sizeof (sin_t):	/* Complete IPv4 address */
3298348SEric.Yu@Sun.COM 		sa = (struct sockaddr *)mi_offset_param(mp, tbr->ADDR_offset,
3308348SEric.Yu@Sun.COM 		    sizeof (sin_t));
3310Sstevel@tonic-gate 		break;
3328348SEric.Yu@Sun.COM 
3338348SEric.Yu@Sun.COM 	case sizeof (sin6_t):	/* Complete IPv6 address */
3348348SEric.Yu@Sun.COM 		sa = (struct sockaddr *)mi_offset_param(mp,
3358348SEric.Yu@Sun.COM 		    tbr->ADDR_offset, sizeof (sin6_t));
3368348SEric.Yu@Sun.COM 		break;
3378348SEric.Yu@Sun.COM 
3380Sstevel@tonic-gate 	default:
3390Sstevel@tonic-gate 		(void) mi_strlog(q, 1, SL_ERROR|SL_TRACE,
3400Sstevel@tonic-gate 		    "icmp_bind: bad ADDR_length %d", tbr->ADDR_length);
3410Sstevel@tonic-gate 		icmp_err_ack(q, mp, TBADADDR, 0);
3420Sstevel@tonic-gate 		return;
3430Sstevel@tonic-gate 	}
3445240Snordmark 
3458348SEric.Yu@Sun.COM 	error = rawip_do_bind(connp, sa, len);
3468348SEric.Yu@Sun.COM done:
3478348SEric.Yu@Sun.COM 	ASSERT(mp->b_cont == NULL);
3488348SEric.Yu@Sun.COM 	if (error != 0) {
3498348SEric.Yu@Sun.COM 		if (error > 0) {
3508348SEric.Yu@Sun.COM 			icmp_err_ack(q, mp, TSYSERR, error);
3518348SEric.Yu@Sun.COM 		} else {
3528348SEric.Yu@Sun.COM 			icmp_err_ack(q, mp, -error, 0);
3538348SEric.Yu@Sun.COM 		}
3548348SEric.Yu@Sun.COM 	} else {
3558348SEric.Yu@Sun.COM 		tbr->PRIM_type = T_BIND_ACK;
3568348SEric.Yu@Sun.COM 		qreply(q, mp);
3578348SEric.Yu@Sun.COM 	}
3588348SEric.Yu@Sun.COM }
3598348SEric.Yu@Sun.COM 
3608348SEric.Yu@Sun.COM static int
3618348SEric.Yu@Sun.COM rawip_do_bind(conn_t *connp, struct sockaddr *sa, socklen_t len)
3628348SEric.Yu@Sun.COM {
3638348SEric.Yu@Sun.COM 	sin_t		*sin;
3648348SEric.Yu@Sun.COM 	sin6_t		*sin6;
3658348SEric.Yu@Sun.COM 	icmp_t		*icmp;
3668348SEric.Yu@Sun.COM 	int		error = 0;
3678348SEric.Yu@Sun.COM 	mblk_t		*ire_mp;
3688348SEric.Yu@Sun.COM 
3698348SEric.Yu@Sun.COM 
3708348SEric.Yu@Sun.COM 	icmp = connp->conn_icmp;
3718348SEric.Yu@Sun.COM 
3728348SEric.Yu@Sun.COM 	if (sa == NULL || !OK_32PTR((char *)sa)) {
3738348SEric.Yu@Sun.COM 		return (EINVAL);
3748348SEric.Yu@Sun.COM 	}
3758348SEric.Yu@Sun.COM 
3765240Snordmark 	/*
3775240Snordmark 	 * The state must be TS_UNBND. TPI mandates that users must send
3785240Snordmark 	 * TPI primitives only 1 at a time and wait for the response before
3795240Snordmark 	 * sending the next primitive.
3805240Snordmark 	 */
3815240Snordmark 	rw_enter(&icmp->icmp_rwlock, RW_WRITER);
3825240Snordmark 	if (icmp->icmp_state != TS_UNBND || icmp->icmp_pending_op != -1) {
3838348SEric.Yu@Sun.COM 		error = -TOUTSTATE;
3848348SEric.Yu@Sun.COM 		goto done;
3858348SEric.Yu@Sun.COM 	}
3868348SEric.Yu@Sun.COM 
3878348SEric.Yu@Sun.COM 	ASSERT(len != 0);
3888348SEric.Yu@Sun.COM 	switch (len) {
3898348SEric.Yu@Sun.COM 	case sizeof (sin_t):    /* Complete IPv4 address */
3908348SEric.Yu@Sun.COM 		sin = (sin_t *)sa;
3918348SEric.Yu@Sun.COM 		if (sin->sin_family != AF_INET ||
3928348SEric.Yu@Sun.COM 		    icmp->icmp_family != AF_INET) {
3938348SEric.Yu@Sun.COM 			/* TSYSERR, EAFNOSUPPORT */
3948348SEric.Yu@Sun.COM 			error = EAFNOSUPPORT;
3958348SEric.Yu@Sun.COM 			goto done;
3968348SEric.Yu@Sun.COM 		}
3978348SEric.Yu@Sun.COM 		break;
3988348SEric.Yu@Sun.COM 	case sizeof (sin6_t): /* Complete IPv6 address */
3998348SEric.Yu@Sun.COM 		sin6 = (sin6_t *)sa;
4008348SEric.Yu@Sun.COM 		if (sin6->sin6_family != AF_INET6 ||
4018348SEric.Yu@Sun.COM 		    icmp->icmp_family != AF_INET6) {
4028348SEric.Yu@Sun.COM 			/* TSYSERR, EAFNOSUPPORT */
4038348SEric.Yu@Sun.COM 			error = EAFNOSUPPORT;
4048348SEric.Yu@Sun.COM 			goto done;
4058348SEric.Yu@Sun.COM 		}
4068348SEric.Yu@Sun.COM 		/* No support for mapped addresses on raw sockets */
4078348SEric.Yu@Sun.COM 		if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
4088348SEric.Yu@Sun.COM 			/* TSYSERR, EADDRNOTAVAIL */
4098348SEric.Yu@Sun.COM 			error = EADDRNOTAVAIL;
4108348SEric.Yu@Sun.COM 			goto done;
4118348SEric.Yu@Sun.COM 		}
4128348SEric.Yu@Sun.COM 		break;
4138348SEric.Yu@Sun.COM 
4148348SEric.Yu@Sun.COM 	default:
4158348SEric.Yu@Sun.COM 		/* TBADADDR */
4168348SEric.Yu@Sun.COM 		error = EADDRNOTAVAIL;
4178348SEric.Yu@Sun.COM 		goto done;
4188348SEric.Yu@Sun.COM 	}
4198348SEric.Yu@Sun.COM 
4208348SEric.Yu@Sun.COM 	icmp->icmp_pending_op = T_BIND_REQ;
4218348SEric.Yu@Sun.COM 	icmp->icmp_state = TS_IDLE;
4225240Snordmark 
4230Sstevel@tonic-gate 	/*
4240Sstevel@tonic-gate 	 * Copy the source address into our icmp structure.  This address
4250Sstevel@tonic-gate 	 * may still be zero; if so, ip will fill in the correct address
4260Sstevel@tonic-gate 	 * each time an outbound packet is passed to it.
4275240Snordmark 	 * If we are binding to a broadcast or multicast address then
4288348SEric.Yu@Sun.COM 	 * rawip_post_ip_bind_connect will clear the source address.
4290Sstevel@tonic-gate 	 */
4300Sstevel@tonic-gate 
4310Sstevel@tonic-gate 	if (icmp->icmp_family == AF_INET) {
4320Sstevel@tonic-gate 		ASSERT(sin != NULL);
4330Sstevel@tonic-gate 		ASSERT(icmp->icmp_ipversion == IPV4_VERSION);
4340Sstevel@tonic-gate 		IN6_IPADDR_TO_V4MAPPED(sin->sin_addr.s_addr,
4350Sstevel@tonic-gate 		    &icmp->icmp_v6src);
4360Sstevel@tonic-gate 		icmp->icmp_max_hdr_len = IP_SIMPLE_HDR_LENGTH +
4370Sstevel@tonic-gate 		    icmp->icmp_ip_snd_options_len;
4380Sstevel@tonic-gate 		icmp->icmp_bound_v6src = icmp->icmp_v6src;
4390Sstevel@tonic-gate 	} else {
4400Sstevel@tonic-gate 		int error;
4410Sstevel@tonic-gate 
4420Sstevel@tonic-gate 		ASSERT(sin6 != NULL);
4430Sstevel@tonic-gate 		ASSERT(icmp->icmp_ipversion == IPV6_VERSION);
4440Sstevel@tonic-gate 		icmp->icmp_v6src = sin6->sin6_addr;
4450Sstevel@tonic-gate 		icmp->icmp_max_hdr_len = icmp->icmp_sticky_hdrs_len;
4460Sstevel@tonic-gate 		icmp->icmp_bound_v6src = icmp->icmp_v6src;
4470Sstevel@tonic-gate 
4480Sstevel@tonic-gate 		/* Rebuild the header template */
4495240Snordmark 		error = icmp_build_hdrs(icmp);
4500Sstevel@tonic-gate 		if (error != 0) {
4515240Snordmark 			icmp->icmp_pending_op = -1;
4528348SEric.Yu@Sun.COM 			/*
4538348SEric.Yu@Sun.COM 			 * TSYSERR
4548348SEric.Yu@Sun.COM 			 */
4558348SEric.Yu@Sun.COM 			goto done;
4560Sstevel@tonic-gate 		}
4570Sstevel@tonic-gate 	}
4588348SEric.Yu@Sun.COM 
4598348SEric.Yu@Sun.COM 	ire_mp = NULL;
4600Sstevel@tonic-gate 	if (!(V6_OR_V4_INADDR_ANY(icmp->icmp_v6src))) {
4610Sstevel@tonic-gate 		/*
4628348SEric.Yu@Sun.COM 		 * request an IRE if src not 0 (INADDR_ANY)
4630Sstevel@tonic-gate 		 */
4648348SEric.Yu@Sun.COM 		ire_mp = allocb(sizeof (ire_t), BPRI_HI);
4658348SEric.Yu@Sun.COM 		if (ire_mp == NULL) {
4665240Snordmark 			icmp->icmp_pending_op = -1;
4678348SEric.Yu@Sun.COM 			error = ENOMEM;
4688348SEric.Yu@Sun.COM 			goto done;
4690Sstevel@tonic-gate 		}
4708348SEric.Yu@Sun.COM 		DB_TYPE(ire_mp) = IRE_DB_REQ_TYPE;
4718348SEric.Yu@Sun.COM 	}
4728348SEric.Yu@Sun.COM done:
4738348SEric.Yu@Sun.COM 	rw_exit(&icmp->icmp_rwlock);
4748348SEric.Yu@Sun.COM 	if (error != 0)
4758348SEric.Yu@Sun.COM 		return (error);
4768348SEric.Yu@Sun.COM 
4778348SEric.Yu@Sun.COM 	if (icmp->icmp_family == AF_INET6) {
4788348SEric.Yu@Sun.COM 		error = ip_proto_bind_laddr_v6(connp, &ire_mp, icmp->icmp_proto,
4798348SEric.Yu@Sun.COM 		    &sin6->sin6_addr, sin6->sin6_port, B_TRUE);
4808348SEric.Yu@Sun.COM 	} else {
4818348SEric.Yu@Sun.COM 		error = ip_proto_bind_laddr_v4(connp, &ire_mp, icmp->icmp_proto,
4828348SEric.Yu@Sun.COM 		    sin->sin_addr.s_addr, sin->sin_port, B_TRUE);
4838348SEric.Yu@Sun.COM 	}
4848348SEric.Yu@Sun.COM 	rawip_post_ip_bind_connect(icmp, ire_mp, error);
4858348SEric.Yu@Sun.COM 	return (error);
4868348SEric.Yu@Sun.COM }
4878348SEric.Yu@Sun.COM 
4888348SEric.Yu@Sun.COM static void
4898348SEric.Yu@Sun.COM rawip_post_ip_bind_connect(icmp_t *icmp, mblk_t *ire_mp, int error)
4908348SEric.Yu@Sun.COM {
4918348SEric.Yu@Sun.COM 	rw_enter(&icmp->icmp_rwlock, RW_WRITER);
4928348SEric.Yu@Sun.COM 	if (icmp->icmp_state == TS_UNBND) {
4938348SEric.Yu@Sun.COM 		/*
4948348SEric.Yu@Sun.COM 		 * not yet bound - bind sent by icmp_bind_proto.
4958348SEric.Yu@Sun.COM 		 */
4968348SEric.Yu@Sun.COM 		rw_exit(&icmp->icmp_rwlock);
4978348SEric.Yu@Sun.COM 		return;
4988348SEric.Yu@Sun.COM 	}
4998348SEric.Yu@Sun.COM 	ASSERT(icmp->icmp_pending_op != -1);
5008348SEric.Yu@Sun.COM 	icmp->icmp_pending_op = -1;
5018348SEric.Yu@Sun.COM 
5028348SEric.Yu@Sun.COM 	if (error != 0) {
5038348SEric.Yu@Sun.COM 		if (icmp->icmp_state == TS_DATA_XFER) {
5048348SEric.Yu@Sun.COM 			/* Connect failed */
5058348SEric.Yu@Sun.COM 			/* Revert back to the bound source */
5068348SEric.Yu@Sun.COM 			icmp->icmp_v6src = icmp->icmp_bound_v6src;
5078348SEric.Yu@Sun.COM 			icmp->icmp_state = TS_IDLE;
5088348SEric.Yu@Sun.COM 			if (icmp->icmp_family == AF_INET6)
5098348SEric.Yu@Sun.COM 				(void) icmp_build_hdrs(icmp);
5108348SEric.Yu@Sun.COM 		} else {
5118348SEric.Yu@Sun.COM 			V6_SET_ZERO(icmp->icmp_v6src);
5128348SEric.Yu@Sun.COM 			V6_SET_ZERO(icmp->icmp_bound_v6src);
5138348SEric.Yu@Sun.COM 			icmp->icmp_state = TS_UNBND;
5148348SEric.Yu@Sun.COM 			if (icmp->icmp_family == AF_INET6)
5158348SEric.Yu@Sun.COM 				(void) icmp_build_hdrs(icmp);
5168348SEric.Yu@Sun.COM 		}
5178348SEric.Yu@Sun.COM 	} else {
5188348SEric.Yu@Sun.COM 		if (ire_mp != NULL && ire_mp->b_datap->db_type == IRE_DB_TYPE) {
5198348SEric.Yu@Sun.COM 			ire_t *ire;
5208348SEric.Yu@Sun.COM 
5218348SEric.Yu@Sun.COM 			ire = (ire_t *)ire_mp->b_rptr;
5228348SEric.Yu@Sun.COM 			/*
5238348SEric.Yu@Sun.COM 			 * If a broadcast/multicast address was bound set
5248348SEric.Yu@Sun.COM 			 * the source address to 0.
5258348SEric.Yu@Sun.COM 			 * This ensures no datagrams with broadcast address
5268348SEric.Yu@Sun.COM 			 * as source address are emitted (which would violate
5278348SEric.Yu@Sun.COM 			 * RFC1122 - Hosts requirements)
5288348SEric.Yu@Sun.COM 			 * Note: we get IRE_BROADCAST for IPv6
5298348SEric.Yu@Sun.COM 			 * to "mark" a multicast local address.
5308348SEric.Yu@Sun.COM 			 */
5318348SEric.Yu@Sun.COM 
5328348SEric.Yu@Sun.COM 
5338348SEric.Yu@Sun.COM 			if (ire->ire_type == IRE_BROADCAST &&
5348348SEric.Yu@Sun.COM 			    icmp->icmp_state != TS_DATA_XFER) {
5358348SEric.Yu@Sun.COM 				/*
5368348SEric.Yu@Sun.COM 				 * This was just a local bind to a
5378348SEric.Yu@Sun.COM 				 * MC/broadcast addr
5388348SEric.Yu@Sun.COM 				 */
5398348SEric.Yu@Sun.COM 				V6_SET_ZERO(icmp->icmp_v6src);
5408348SEric.Yu@Sun.COM 				if (icmp->icmp_family == AF_INET6)
5418348SEric.Yu@Sun.COM 					(void) icmp_build_hdrs(icmp);
5428348SEric.Yu@Sun.COM 			}
5438348SEric.Yu@Sun.COM 		}
5448348SEric.Yu@Sun.COM 
5450Sstevel@tonic-gate 	}
5465240Snordmark 	rw_exit(&icmp->icmp_rwlock);
5478348SEric.Yu@Sun.COM 	if (ire_mp != NULL)
5488348SEric.Yu@Sun.COM 		freeb(ire_mp);
5490Sstevel@tonic-gate }
5500Sstevel@tonic-gate 
5510Sstevel@tonic-gate /*
5520Sstevel@tonic-gate  * Send message to IP to just bind to the protocol.
5530Sstevel@tonic-gate  */
5548348SEric.Yu@Sun.COM static int
5558348SEric.Yu@Sun.COM icmp_bind_proto(conn_t *connp)
5560Sstevel@tonic-gate {
5570Sstevel@tonic-gate 	icmp_t	*icmp;
5588348SEric.Yu@Sun.COM 	int	error;
5595240Snordmark 
5605240Snordmark 	icmp = connp->conn_icmp;
5615240Snordmark 
5625240Snordmark 	if (icmp->icmp_family == AF_INET6)
5638348SEric.Yu@Sun.COM 		error = ip_proto_bind_laddr_v6(connp, NULL, icmp->icmp_proto,
5648348SEric.Yu@Sun.COM 		    &sin6_null.sin6_addr, 0, B_TRUE);
5655240Snordmark 	else
5668348SEric.Yu@Sun.COM 		error = ip_proto_bind_laddr_v4(connp, NULL, icmp->icmp_proto,
5678348SEric.Yu@Sun.COM 		    sin_null.sin_addr.s_addr, 0, B_TRUE);
5688348SEric.Yu@Sun.COM 
5698348SEric.Yu@Sun.COM 	rawip_post_ip_bind_connect(icmp, NULL, error);
5708348SEric.Yu@Sun.COM 	return (error);
5710Sstevel@tonic-gate }
5720Sstevel@tonic-gate 
5730Sstevel@tonic-gate static void
5748348SEric.Yu@Sun.COM icmp_tpi_connect(queue_t *q, mblk_t *mp)
5750Sstevel@tonic-gate {
5768348SEric.Yu@Sun.COM 	conn_t	*connp = Q_TO_CONN(q);
5770Sstevel@tonic-gate 	struct T_conn_req	*tcr;
5780Sstevel@tonic-gate 	icmp_t	*icmp;
5798348SEric.Yu@Sun.COM 	struct sockaddr *sa;
5808348SEric.Yu@Sun.COM 	socklen_t len;
5818348SEric.Yu@Sun.COM 	int error;
5825240Snordmark 
5835240Snordmark 	icmp = connp->conn_icmp;
5840Sstevel@tonic-gate 	tcr = (struct T_conn_req *)mp->b_rptr;
5850Sstevel@tonic-gate 	/* Sanity checks */
5865240Snordmark 	if ((mp->b_wptr - mp->b_rptr) < sizeof (struct T_conn_req)) {
5870Sstevel@tonic-gate 		icmp_err_ack(q, mp, TPROTO, 0);
5880Sstevel@tonic-gate 		return;
5890Sstevel@tonic-gate 	}
5900Sstevel@tonic-gate 
5910Sstevel@tonic-gate 	if (tcr->OPT_length != 0) {
5920Sstevel@tonic-gate 		icmp_err_ack(q, mp, TBADOPT, 0);
5930Sstevel@tonic-gate 		return;
5940Sstevel@tonic-gate 	}
5955240Snordmark 
5968348SEric.Yu@Sun.COM 	len = tcr->DEST_length;
5978348SEric.Yu@Sun.COM 
5988348SEric.Yu@Sun.COM 	switch (len) {
5990Sstevel@tonic-gate 	default:
6000Sstevel@tonic-gate 		icmp_err_ack(q, mp, TBADADDR, 0);
6010Sstevel@tonic-gate 		return;
6020Sstevel@tonic-gate 	case sizeof (sin_t):
6038348SEric.Yu@Sun.COM 		sa = (struct sockaddr *)mi_offset_param(mp, tcr->DEST_offset,
6040Sstevel@tonic-gate 		    sizeof (sin_t));
6058348SEric.Yu@Sun.COM 		break;
6068348SEric.Yu@Sun.COM 	case sizeof (sin6_t):
6078348SEric.Yu@Sun.COM 		sa = (struct sockaddr *)mi_offset_param(mp,
6088348SEric.Yu@Sun.COM 		    tcr->DEST_offset, sizeof (sin6_t));
6098348SEric.Yu@Sun.COM 		break;
6108348SEric.Yu@Sun.COM 	}
6118348SEric.Yu@Sun.COM 
6128348SEric.Yu@Sun.COM 	error = proto_verify_ip_addr(icmp->icmp_family, sa, len);
6138348SEric.Yu@Sun.COM 	if (error != 0) {
6148348SEric.Yu@Sun.COM 		icmp_err_ack(q, mp, TSYSERR, error);
6158348SEric.Yu@Sun.COM 		return;
6168348SEric.Yu@Sun.COM 	}
6178348SEric.Yu@Sun.COM 
6188348SEric.Yu@Sun.COM 	error = rawip_do_connect(connp, sa, len);
6198348SEric.Yu@Sun.COM 	if (error != 0) {
6208348SEric.Yu@Sun.COM 		if (error < 0) {
6218348SEric.Yu@Sun.COM 			icmp_err_ack(q, mp, -error, 0);
6228348SEric.Yu@Sun.COM 		} else {
6238348SEric.Yu@Sun.COM 			icmp_err_ack(q, mp, 0, error);
6240Sstevel@tonic-gate 		}
6258348SEric.Yu@Sun.COM 	} else {
6268348SEric.Yu@Sun.COM 		mblk_t *mp1;
6278348SEric.Yu@Sun.COM 
6288348SEric.Yu@Sun.COM 		/*
6298348SEric.Yu@Sun.COM 		 * We have to send a connection confirmation to
6308348SEric.Yu@Sun.COM 		 * keep TLI happy.
6318348SEric.Yu@Sun.COM 		 */
6328348SEric.Yu@Sun.COM 		if (icmp->icmp_family == AF_INET) {
6338348SEric.Yu@Sun.COM 			mp1 = mi_tpi_conn_con(NULL, (char *)sa,
6348348SEric.Yu@Sun.COM 			    sizeof (sin_t), NULL, 0);
6358348SEric.Yu@Sun.COM 		} else {
6368348SEric.Yu@Sun.COM 			ASSERT(icmp->icmp_family == AF_INET6);
6378348SEric.Yu@Sun.COM 			mp1 = mi_tpi_conn_con(NULL, (char *)sa,
6388348SEric.Yu@Sun.COM 			    sizeof (sin6_t), NULL, 0);
6398348SEric.Yu@Sun.COM 		}
6408348SEric.Yu@Sun.COM 		if (mp1 == NULL) {
6418348SEric.Yu@Sun.COM 			rw_exit(&icmp->icmp_rwlock);
6428348SEric.Yu@Sun.COM 			icmp_err_ack(q, mp, TSYSERR, ENOMEM);
6430Sstevel@tonic-gate 			return;
6440Sstevel@tonic-gate 		}
6458348SEric.Yu@Sun.COM 
6468348SEric.Yu@Sun.COM 		/*
6478348SEric.Yu@Sun.COM 		 * Send ok_ack for T_CONN_REQ
6488348SEric.Yu@Sun.COM 		 */
6498348SEric.Yu@Sun.COM 		mp = mi_tpi_ok_ack_alloc(mp);
6508348SEric.Yu@Sun.COM 		if (mp == NULL) {
6518348SEric.Yu@Sun.COM 			/* Unable to reuse the T_CONN_REQ for the ack. */
6528348SEric.Yu@Sun.COM 			freemsg(mp1);
6538348SEric.Yu@Sun.COM 			icmp_err_ack_prim(q, mp1, T_CONN_REQ, TSYSERR, ENOMEM);
6540Sstevel@tonic-gate 			return;
6550Sstevel@tonic-gate 		}
6568348SEric.Yu@Sun.COM 		putnext(connp->conn_rq, mp);
6578348SEric.Yu@Sun.COM 		putnext(connp->conn_rq, mp1);
6588348SEric.Yu@Sun.COM 	}
6598348SEric.Yu@Sun.COM }
6608348SEric.Yu@Sun.COM 
6618348SEric.Yu@Sun.COM static int
6628348SEric.Yu@Sun.COM rawip_do_connect(conn_t *connp, const struct sockaddr *sa, socklen_t len)
6638348SEric.Yu@Sun.COM {
6648348SEric.Yu@Sun.COM 	icmp_t	*icmp;
6658348SEric.Yu@Sun.COM 	sin_t	*sin;
6668348SEric.Yu@Sun.COM 	sin6_t	*sin6;
6678348SEric.Yu@Sun.COM 	mblk_t  *ire_mp;
6688348SEric.Yu@Sun.COM 	int	error;
6698348SEric.Yu@Sun.COM 	ipaddr_t	v4dst;
6708348SEric.Yu@Sun.COM 	in6_addr_t	v6dst;
6718348SEric.Yu@Sun.COM 
6728348SEric.Yu@Sun.COM 	icmp = connp->conn_icmp;
6738348SEric.Yu@Sun.COM 
6748348SEric.Yu@Sun.COM 	if (sa == NULL || !OK_32PTR((char *)sa)) {
6758348SEric.Yu@Sun.COM 		return (EINVAL);
6768348SEric.Yu@Sun.COM 	}
6778348SEric.Yu@Sun.COM 
6788348SEric.Yu@Sun.COM 	ire_mp = allocb(sizeof (ire_t), BPRI_HI);
6798348SEric.Yu@Sun.COM 	if (ire_mp == NULL)
6808348SEric.Yu@Sun.COM 		return (ENOMEM);
6818348SEric.Yu@Sun.COM 	DB_TYPE(ire_mp) = IRE_DB_REQ_TYPE;
6828348SEric.Yu@Sun.COM 
6838348SEric.Yu@Sun.COM 
6848348SEric.Yu@Sun.COM 	ASSERT(sa != NULL && len != 0);
6858348SEric.Yu@Sun.COM 
6868348SEric.Yu@Sun.COM 	rw_enter(&icmp->icmp_rwlock, RW_WRITER);
6878348SEric.Yu@Sun.COM 	if (icmp->icmp_state == TS_UNBND || icmp->icmp_pending_op != -1) {
6888348SEric.Yu@Sun.COM 		rw_exit(&icmp->icmp_rwlock);
6898348SEric.Yu@Sun.COM 		freeb(ire_mp);
6908348SEric.Yu@Sun.COM 		return (-TOUTSTATE);
6918348SEric.Yu@Sun.COM 	}
6928348SEric.Yu@Sun.COM 
6938348SEric.Yu@Sun.COM 	switch (len) {
6948348SEric.Yu@Sun.COM 	case sizeof (sin_t):
6958348SEric.Yu@Sun.COM 		sin = (sin_t *)sa;
6968348SEric.Yu@Sun.COM 
6978348SEric.Yu@Sun.COM 		ASSERT(icmp->icmp_family == AF_INET);
6988348SEric.Yu@Sun.COM 		ASSERT(icmp->icmp_ipversion == IPV4_VERSION);
6998348SEric.Yu@Sun.COM 
7008348SEric.Yu@Sun.COM 		v4dst = sin->sin_addr.s_addr;
7010Sstevel@tonic-gate 		/*
7020Sstevel@tonic-gate 		 * Interpret a zero destination to mean loopback.
7030Sstevel@tonic-gate 		 * Update the T_CONN_REQ (sin/sin6) since it is used to
7040Sstevel@tonic-gate 		 * generate the T_CONN_CON.
7050Sstevel@tonic-gate 		 */
7060Sstevel@tonic-gate 		if (v4dst == INADDR_ANY) {
7070Sstevel@tonic-gate 			v4dst = htonl(INADDR_LOOPBACK);
7080Sstevel@tonic-gate 		}
7098348SEric.Yu@Sun.COM 
7108348SEric.Yu@Sun.COM 		IN6_IPADDR_TO_V4MAPPED(v4dst, &v6dst);
7118348SEric.Yu@Sun.COM 		ASSERT(icmp->icmp_ipversion == IPV4_VERSION);
7128348SEric.Yu@Sun.COM 		icmp->icmp_max_hdr_len = IP_SIMPLE_HDR_LENGTH +
7138348SEric.Yu@Sun.COM 		    icmp->icmp_ip_snd_options_len;
7148348SEric.Yu@Sun.COM 		icmp->icmp_v6dst.sin6_addr = v6dst;
7158348SEric.Yu@Sun.COM 		icmp->icmp_v6dst.sin6_family = AF_INET6;
7168348SEric.Yu@Sun.COM 		icmp->icmp_v6dst.sin6_flowinfo = 0;
7178348SEric.Yu@Sun.COM 		icmp->icmp_v6dst.sin6_port = 0;
7180Sstevel@tonic-gate 
7190Sstevel@tonic-gate 		/*
7200Sstevel@tonic-gate 		 * If the destination address is multicast and
7210Sstevel@tonic-gate 		 * an outgoing multicast interface has been set,
7220Sstevel@tonic-gate 		 * use the address of that interface as our
7230Sstevel@tonic-gate 		 * source address if no source address has been set.
7240Sstevel@tonic-gate 		 */
7250Sstevel@tonic-gate 		if (V4_PART_OF_V6(icmp->icmp_v6src) == INADDR_ANY &&
7260Sstevel@tonic-gate 		    CLASSD(v4dst) &&
7270Sstevel@tonic-gate 		    icmp->icmp_multicast_if_addr != INADDR_ANY) {
7280Sstevel@tonic-gate 			IN6_IPADDR_TO_V4MAPPED(icmp->icmp_multicast_if_addr,
7290Sstevel@tonic-gate 			    &icmp->icmp_v6src);
7300Sstevel@tonic-gate 		}
7318348SEric.Yu@Sun.COM 		break;
7328348SEric.Yu@Sun.COM 	case sizeof (sin6_t):
7338348SEric.Yu@Sun.COM 		sin6 = (sin6_t *)sa;
7348348SEric.Yu@Sun.COM 
7358348SEric.Yu@Sun.COM 		/* No support for mapped addresses on raw sockets */
7368348SEric.Yu@Sun.COM 		if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
7378348SEric.Yu@Sun.COM 			rw_exit(&icmp->icmp_rwlock);
7388348SEric.Yu@Sun.COM 			freeb(ire_mp);
7398348SEric.Yu@Sun.COM 			return (EADDRNOTAVAIL);
7408348SEric.Yu@Sun.COM 		}
7418348SEric.Yu@Sun.COM 
7420Sstevel@tonic-gate 		ASSERT(icmp->icmp_ipversion == IPV6_VERSION);
7438348SEric.Yu@Sun.COM 		ASSERT(icmp->icmp_family == AF_INET6);
7448348SEric.Yu@Sun.COM 
7458348SEric.Yu@Sun.COM 		icmp->icmp_max_hdr_len = icmp->icmp_sticky_hdrs_len;
7468348SEric.Yu@Sun.COM 
7478348SEric.Yu@Sun.COM 		icmp->icmp_v6dst = *sin6;
7488348SEric.Yu@Sun.COM 		icmp->icmp_v6dst.sin6_port = 0;
7498348SEric.Yu@Sun.COM 
7500Sstevel@tonic-gate 		/*
7510Sstevel@tonic-gate 		 * Interpret a zero destination to mean loopback.
7520Sstevel@tonic-gate 		 * Update the T_CONN_REQ (sin/sin6) since it is used to
7530Sstevel@tonic-gate 		 * generate the T_CONN_CON.
7540Sstevel@tonic-gate 		 */
7558348SEric.Yu@Sun.COM 		if (IN6_IS_ADDR_UNSPECIFIED(&icmp->icmp_v6dst.sin6_addr)) {
7568348SEric.Yu@Sun.COM 			icmp->icmp_v6dst.sin6_addr = ipv6_loopback;
7570Sstevel@tonic-gate 		}
7580Sstevel@tonic-gate 		/*
7590Sstevel@tonic-gate 		 * If the destination address is multicast and
7600Sstevel@tonic-gate 		 * an outgoing multicast interface has been set,
7610Sstevel@tonic-gate 		 * then the ip bind logic will pick the correct source
7620Sstevel@tonic-gate 		 * address (i.e. matching the outgoing multicast interface).
7630Sstevel@tonic-gate 		 */
7648348SEric.Yu@Sun.COM 		break;
7658348SEric.Yu@Sun.COM 	}
7668348SEric.Yu@Sun.COM 
7675240Snordmark 	icmp->icmp_pending_op = T_CONN_REQ;
7685240Snordmark 
7695240Snordmark 	if (icmp->icmp_state == TS_DATA_XFER) {
7705240Snordmark 		/* Already connected - clear out state */
7715240Snordmark 		icmp->icmp_v6src = icmp->icmp_bound_v6src;
7725240Snordmark 		icmp->icmp_state = TS_IDLE;
7735240Snordmark 	}
7745240Snordmark 
7750Sstevel@tonic-gate 	icmp->icmp_state = TS_DATA_XFER;
7765240Snordmark 	rw_exit(&icmp->icmp_rwlock);
7770Sstevel@tonic-gate 
7788348SEric.Yu@Sun.COM 	if (icmp->icmp_family == AF_INET6) {
7798348SEric.Yu@Sun.COM 		error = ip_proto_bind_connected_v6(connp, &ire_mp,
7808348SEric.Yu@Sun.COM 		    icmp->icmp_proto, &icmp->icmp_v6src, 0,
7818348SEric.Yu@Sun.COM 		    &icmp->icmp_v6dst.sin6_addr,
7828348SEric.Yu@Sun.COM 		    NULL, sin6->sin6_port, B_TRUE, B_TRUE);
7838348SEric.Yu@Sun.COM 	} else {
7848348SEric.Yu@Sun.COM 		error = ip_proto_bind_connected_v4(connp, &ire_mp,
7858348SEric.Yu@Sun.COM 		    icmp->icmp_proto, &V4_PART_OF_V6(icmp->icmp_v6src), 0,
7868348SEric.Yu@Sun.COM 		    V4_PART_OF_V6(icmp->icmp_v6dst.sin6_addr), sin->sin_port,
7878348SEric.Yu@Sun.COM 		    B_TRUE, B_TRUE);
7888348SEric.Yu@Sun.COM 	}
7898348SEric.Yu@Sun.COM 	rawip_post_ip_bind_connect(icmp, ire_mp, error);
7908348SEric.Yu@Sun.COM 	return (error);
7910Sstevel@tonic-gate }
7920Sstevel@tonic-gate 
7935240Snordmark static void
7945240Snordmark icmp_close_free(conn_t *connp)
7950Sstevel@tonic-gate {
7965240Snordmark 	icmp_t *icmp = connp->conn_icmp;
7970Sstevel@tonic-gate 
7980Sstevel@tonic-gate 	/* If there are any options associated with the stream, free them. */
7995315Snordmark 	if (icmp->icmp_ip_snd_options != NULL) {
8000Sstevel@tonic-gate 		mi_free((char *)icmp->icmp_ip_snd_options);
8015315Snordmark 		icmp->icmp_ip_snd_options = NULL;
8025330Snordmark 		icmp->icmp_ip_snd_options_len = 0;
8035315Snordmark 	}
8045315Snordmark 
8055315Snordmark 	if (icmp->icmp_filter != NULL) {
8060Sstevel@tonic-gate 		kmem_free(icmp->icmp_filter, sizeof (icmp6_filter_t));
8075315Snordmark 		icmp->icmp_filter = NULL;
8085315Snordmark 	}
8098348SEric.Yu@Sun.COM 
8100Sstevel@tonic-gate 	/* Free memory associated with sticky options */
8110Sstevel@tonic-gate 	if (icmp->icmp_sticky_hdrs_len != 0) {
8120Sstevel@tonic-gate 		kmem_free(icmp->icmp_sticky_hdrs,
8130Sstevel@tonic-gate 		    icmp->icmp_sticky_hdrs_len);
8140Sstevel@tonic-gate 		icmp->icmp_sticky_hdrs = NULL;
8150Sstevel@tonic-gate 		icmp->icmp_sticky_hdrs_len = 0;
8160Sstevel@tonic-gate 	}
8171676Sjpk 	ip6_pkt_free(&icmp->icmp_sticky_ipp);
8185330Snordmark 
8195330Snordmark 	/*
8205330Snordmark 	 * Clear any fields which the kmem_cache constructor clears.
8215330Snordmark 	 * Only icmp_connp needs to be preserved.
8225330Snordmark 	 * TBD: We should make this more efficient to avoid clearing
8235330Snordmark 	 * everything.
8245330Snordmark 	 */
8255330Snordmark 	ASSERT(icmp->icmp_connp == connp);
8265330Snordmark 	bzero(icmp, sizeof (icmp_t));
8275330Snordmark 	icmp->icmp_connp = connp;
8285240Snordmark }
8295240Snordmark 
8305240Snordmark static int
8318348SEric.Yu@Sun.COM rawip_do_close(conn_t *connp)
8325240Snordmark {
8335240Snordmark 	ASSERT(connp != NULL && IPCL_IS_RAWIP(connp));
8345240Snordmark 
8355240Snordmark 	ip_quiesce_conn(connp);
8365240Snordmark 
8378348SEric.Yu@Sun.COM 	if (!IPCL_IS_NONSTR(connp)) {
8388348SEric.Yu@Sun.COM 		qprocsoff(connp->conn_rq);
8398348SEric.Yu@Sun.COM 	}
8408348SEric.Yu@Sun.COM 
8418348SEric.Yu@Sun.COM 	ASSERT(connp->conn_icmp->icmp_fallback_queue_head == NULL &&
8428348SEric.Yu@Sun.COM 	    connp->conn_icmp->icmp_fallback_queue_tail == NULL);
8435240Snordmark 	icmp_close_free(connp);
8445240Snordmark 
8455240Snordmark 	/*
8465240Snordmark 	 * Now we are truly single threaded on this stream, and can
8475240Snordmark 	 * delete the things hanging off the connp, and finally the connp.
8485240Snordmark 	 * We removed this connp from the fanout list, it cannot be
8495240Snordmark 	 * accessed thru the fanouts, and we already waited for the
8505240Snordmark 	 * conn_ref to drop to 0. We are already in close, so
8515240Snordmark 	 * there cannot be any other thread from the top. qprocsoff
8525240Snordmark 	 * has completed, and service has completed or won't run in
8535240Snordmark 	 * future.
8545240Snordmark 	 */
8555240Snordmark 	ASSERT(connp->conn_ref == 1);
8565240Snordmark 
8578348SEric.Yu@Sun.COM 	if (!IPCL_IS_NONSTR(connp)) {
8588348SEric.Yu@Sun.COM 		inet_minor_free(connp->conn_minor_arena, connp->conn_dev);
8598348SEric.Yu@Sun.COM 	} else {
860*8477SRao.Shoaib@Sun.COM 		ip_free_helper_stream(connp);
8618348SEric.Yu@Sun.COM 	}
8625240Snordmark 
8635240Snordmark 	connp->conn_ref--;
8645240Snordmark 	ipcl_conn_destroy(connp);
8655240Snordmark 
8668348SEric.Yu@Sun.COM 	return (0);
8678348SEric.Yu@Sun.COM }
8688348SEric.Yu@Sun.COM 
8698348SEric.Yu@Sun.COM static int
8708348SEric.Yu@Sun.COM icmp_close(queue_t *q, int flags)
8718348SEric.Yu@Sun.COM {
8728348SEric.Yu@Sun.COM 	conn_t  *connp;
8738348SEric.Yu@Sun.COM 
8748348SEric.Yu@Sun.COM 	if (flags & SO_FALLBACK) {
8758348SEric.Yu@Sun.COM 		/*
8768348SEric.Yu@Sun.COM 		 * stream is being closed while in fallback
8778348SEric.Yu@Sun.COM 		 * simply free the resources that were allocated
8788348SEric.Yu@Sun.COM 		 */
8798348SEric.Yu@Sun.COM 		inet_minor_free(WR(q)->q_ptr, (dev_t)(RD(q)->q_ptr));
8808348SEric.Yu@Sun.COM 		qprocsoff(q);
8818348SEric.Yu@Sun.COM 		goto done;
8828348SEric.Yu@Sun.COM 	}
8838348SEric.Yu@Sun.COM 
8848348SEric.Yu@Sun.COM 	connp = Q_TO_CONN(q);
8858348SEric.Yu@Sun.COM 	(void) rawip_do_close(connp);
8868348SEric.Yu@Sun.COM done:
8875240Snordmark 	q->q_ptr = WR(q)->q_ptr = NULL;
8885240Snordmark 	return (0);
8890Sstevel@tonic-gate }
8900Sstevel@tonic-gate 
8910Sstevel@tonic-gate /*
8920Sstevel@tonic-gate  * This routine handles each T_DISCON_REQ message passed to icmp
8930Sstevel@tonic-gate  * as an indicating that ICMP is no longer connected. This results
8940Sstevel@tonic-gate  * in sending a T_BIND_REQ to IP to restore the binding to just
8950Sstevel@tonic-gate  * the local address.
8960Sstevel@tonic-gate  *
8978348SEric.Yu@Sun.COM  * The disconnect completes in rawip_post_ip_bind_connect.
8980Sstevel@tonic-gate  */
8998348SEric.Yu@Sun.COM static int
9008348SEric.Yu@Sun.COM icmp_do_disconnect(conn_t *connp)
9010Sstevel@tonic-gate {
9020Sstevel@tonic-gate 	icmp_t	*icmp;
9038348SEric.Yu@Sun.COM 	mblk_t	*ire_mp;
9048348SEric.Yu@Sun.COM 	int error;
9055240Snordmark 
9065240Snordmark 	icmp = connp->conn_icmp;
9075240Snordmark 	rw_enter(&icmp->icmp_rwlock, RW_WRITER);
9085240Snordmark 	if (icmp->icmp_state != TS_DATA_XFER || icmp->icmp_pending_op != -1) {
9095240Snordmark 		rw_exit(&icmp->icmp_rwlock);
9108348SEric.Yu@Sun.COM 		return (-TOUTSTATE);
9110Sstevel@tonic-gate 	}
9125240Snordmark 	icmp->icmp_pending_op = T_DISCON_REQ;
9130Sstevel@tonic-gate 	icmp->icmp_v6src = icmp->icmp_bound_v6src;
9140Sstevel@tonic-gate 	icmp->icmp_state = TS_IDLE;
9150Sstevel@tonic-gate 
9160Sstevel@tonic-gate 
9170Sstevel@tonic-gate 	if (icmp->icmp_family == AF_INET6) {
9180Sstevel@tonic-gate 		/* Rebuild the header template */
9195240Snordmark 		error = icmp_build_hdrs(icmp);
9200Sstevel@tonic-gate 		if (error != 0) {
9215240Snordmark 			icmp->icmp_pending_op = -1;
9225240Snordmark 			rw_exit(&icmp->icmp_rwlock);
9238348SEric.Yu@Sun.COM 			return (error);
9240Sstevel@tonic-gate 		}
9250Sstevel@tonic-gate 	}
9265240Snordmark 
9275240Snordmark 	rw_exit(&icmp->icmp_rwlock);
9288348SEric.Yu@Sun.COM 	ire_mp = allocb(sizeof (ire_t), BPRI_HI);
9298348SEric.Yu@Sun.COM 	if (ire_mp == NULL) {
9308348SEric.Yu@Sun.COM 		return (ENOMEM);
9318348SEric.Yu@Sun.COM 	}
9328348SEric.Yu@Sun.COM 
9338348SEric.Yu@Sun.COM 	if (icmp->icmp_family == AF_INET6) {
9348348SEric.Yu@Sun.COM 		error = ip_proto_bind_laddr_v6(connp, &ire_mp, icmp->icmp_proto,
9358348SEric.Yu@Sun.COM 		    &icmp->icmp_bound_v6src, 0, B_TRUE);
9368348SEric.Yu@Sun.COM 	} else {
9378348SEric.Yu@Sun.COM 
9388348SEric.Yu@Sun.COM 		error = ip_proto_bind_laddr_v4(connp, &ire_mp, icmp->icmp_proto,
9398348SEric.Yu@Sun.COM 		    V4_PART_OF_V6(icmp->icmp_bound_v6src), 0, B_TRUE);
9408348SEric.Yu@Sun.COM 	}
9418348SEric.Yu@Sun.COM 
9428348SEric.Yu@Sun.COM 	rawip_post_ip_bind_connect(icmp, ire_mp, error);
9438348SEric.Yu@Sun.COM 
9448348SEric.Yu@Sun.COM 	return (error);
9458348SEric.Yu@Sun.COM }
9468348SEric.Yu@Sun.COM 
9478348SEric.Yu@Sun.COM static void
9488348SEric.Yu@Sun.COM icmp_tpi_disconnect(queue_t *q, mblk_t *mp)
9498348SEric.Yu@Sun.COM {
9508348SEric.Yu@Sun.COM 	conn_t	*connp = Q_TO_CONN(q);
9518348SEric.Yu@Sun.COM 	int	error;
9528348SEric.Yu@Sun.COM 
9538348SEric.Yu@Sun.COM 	/*
9548348SEric.Yu@Sun.COM 	 * Allocate the largest primitive we need to send back
9558348SEric.Yu@Sun.COM 	 * T_error_ack is > than T_ok_ack
9568348SEric.Yu@Sun.COM 	 */
9578348SEric.Yu@Sun.COM 	mp = reallocb(mp, sizeof (struct T_error_ack), 1);
9588348SEric.Yu@Sun.COM 	if (mp == NULL) {
9598348SEric.Yu@Sun.COM 		/* Unable to reuse the T_DISCON_REQ for the ack. */
9608348SEric.Yu@Sun.COM 		icmp_err_ack_prim(q, mp, T_DISCON_REQ, TSYSERR, ENOMEM);
9618348SEric.Yu@Sun.COM 		return;
9628348SEric.Yu@Sun.COM 	}
9638348SEric.Yu@Sun.COM 
9648348SEric.Yu@Sun.COM 	error = icmp_do_disconnect(connp);
9658348SEric.Yu@Sun.COM 
9668348SEric.Yu@Sun.COM 	if (error != 0) {
9678348SEric.Yu@Sun.COM 		if (error > 0) {
9688348SEric.Yu@Sun.COM 			icmp_err_ack(q, mp, 0, error);
9698348SEric.Yu@Sun.COM 		} else {
9708348SEric.Yu@Sun.COM 			icmp_err_ack(q, mp, -error, 0);
9718348SEric.Yu@Sun.COM 		}
9728348SEric.Yu@Sun.COM 	} else {
9738348SEric.Yu@Sun.COM 		mp = mi_tpi_ok_ack_alloc(mp);
9748348SEric.Yu@Sun.COM 		ASSERT(mp != NULL);
9758348SEric.Yu@Sun.COM 		qreply(q, mp);
9768348SEric.Yu@Sun.COM 	}
9778348SEric.Yu@Sun.COM 
9788348SEric.Yu@Sun.COM }
9798348SEric.Yu@Sun.COM 
9808348SEric.Yu@Sun.COM static int
9818348SEric.Yu@Sun.COM icmp_disconnect(conn_t *connp)
9828348SEric.Yu@Sun.COM {
9838348SEric.Yu@Sun.COM 	int	error;
9848348SEric.Yu@Sun.COM 	icmp_t	*icmp = connp->conn_icmp;
9858348SEric.Yu@Sun.COM 
9868348SEric.Yu@Sun.COM 	icmp->icmp_dgram_errind = B_FALSE;
9878348SEric.Yu@Sun.COM 
9888348SEric.Yu@Sun.COM 	error = icmp_do_disconnect(connp);
9898348SEric.Yu@Sun.COM 
9908348SEric.Yu@Sun.COM 	if (error < 0)
9918348SEric.Yu@Sun.COM 		error = proto_tlitosyserr(-error);
9928348SEric.Yu@Sun.COM 	return (error);
9930Sstevel@tonic-gate }
9940Sstevel@tonic-gate 
9950Sstevel@tonic-gate /* This routine creates a T_ERROR_ACK message and passes it upstream. */
9960Sstevel@tonic-gate static void
9970Sstevel@tonic-gate icmp_err_ack(queue_t *q, mblk_t *mp, t_scalar_t t_error, int sys_error)
9980Sstevel@tonic-gate {
9990Sstevel@tonic-gate 	if ((mp = mi_tpi_err_ack_alloc(mp, t_error, sys_error)) != NULL)
10000Sstevel@tonic-gate 		qreply(q, mp);
10010Sstevel@tonic-gate }
10020Sstevel@tonic-gate 
10030Sstevel@tonic-gate /* Shorthand to generate and send TPI error acks to our client */
10040Sstevel@tonic-gate static void
10050Sstevel@tonic-gate icmp_err_ack_prim(queue_t *q, mblk_t *mp, t_scalar_t primitive,
10060Sstevel@tonic-gate     t_scalar_t t_error, int sys_error)
10070Sstevel@tonic-gate {
10080Sstevel@tonic-gate 	struct T_error_ack	*teackp;
10090Sstevel@tonic-gate 
10100Sstevel@tonic-gate 	if ((mp = tpi_ack_alloc(mp, sizeof (struct T_error_ack),
10110Sstevel@tonic-gate 	    M_PCPROTO, T_ERROR_ACK)) != NULL) {
10120Sstevel@tonic-gate 		teackp = (struct T_error_ack *)mp->b_rptr;
10130Sstevel@tonic-gate 		teackp->ERROR_prim = primitive;
10140Sstevel@tonic-gate 		teackp->TLI_error = t_error;
10150Sstevel@tonic-gate 		teackp->UNIX_error = sys_error;
10160Sstevel@tonic-gate 		qreply(q, mp);
10170Sstevel@tonic-gate 	}
10180Sstevel@tonic-gate }
10190Sstevel@tonic-gate 
10200Sstevel@tonic-gate /*
10215240Snordmark  * icmp_icmp_error is called by icmp_input to process ICMP
10220Sstevel@tonic-gate  * messages passed up by IP.
10238348SEric.Yu@Sun.COM  * Generates the appropriate permanent (non-transient) errors.
10240Sstevel@tonic-gate  * Assumes that IP has pulled up everything up to and including
10250Sstevel@tonic-gate  * the ICMP header.
10260Sstevel@tonic-gate  */
10270Sstevel@tonic-gate static void
10288348SEric.Yu@Sun.COM icmp_icmp_error(conn_t *connp, mblk_t *mp)
10290Sstevel@tonic-gate {
10300Sstevel@tonic-gate 	icmph_t *icmph;
10310Sstevel@tonic-gate 	ipha_t	*ipha;
10320Sstevel@tonic-gate 	int	iph_hdr_length;
10330Sstevel@tonic-gate 	sin_t	sin;
10340Sstevel@tonic-gate 	mblk_t	*mp1;
10350Sstevel@tonic-gate 	int	error = 0;
10368348SEric.Yu@Sun.COM 	icmp_t	*icmp = connp->conn_icmp;
10370Sstevel@tonic-gate 
10380Sstevel@tonic-gate 	ipha = (ipha_t *)mp->b_rptr;
10390Sstevel@tonic-gate 
10405240Snordmark 	ASSERT(OK_32PTR(mp->b_rptr));
10415240Snordmark 
10420Sstevel@tonic-gate 	if (IPH_HDR_VERSION(ipha) != IPV4_VERSION) {
10430Sstevel@tonic-gate 		ASSERT(IPH_HDR_VERSION(ipha) == IPV6_VERSION);
10448348SEric.Yu@Sun.COM 		icmp_icmp_error_ipv6(connp, mp);
10450Sstevel@tonic-gate 		return;
10460Sstevel@tonic-gate 	}
10478348SEric.Yu@Sun.COM 
10488348SEric.Yu@Sun.COM 	/*
10498348SEric.Yu@Sun.COM 	 * icmp does not support v4 mapped addresses
10508348SEric.Yu@Sun.COM 	 * so we can never be here for a V6 socket
10518348SEric.Yu@Sun.COM 	 * i.e. icmp_family == AF_INET6
10528348SEric.Yu@Sun.COM 	 */
10538348SEric.Yu@Sun.COM 	ASSERT((IPH_HDR_VERSION(ipha) == IPV4_VERSION) &&
10548348SEric.Yu@Sun.COM 	    (icmp->icmp_family == AF_INET));
10558348SEric.Yu@Sun.COM 
10568348SEric.Yu@Sun.COM 	ASSERT(icmp->icmp_family == AF_INET);
10570Sstevel@tonic-gate 
10585240Snordmark 	/* Skip past the outer IP and ICMP headers */
10590Sstevel@tonic-gate 	iph_hdr_length = IPH_HDR_LENGTH(ipha);
10600Sstevel@tonic-gate 	icmph = (icmph_t *)(&mp->b_rptr[iph_hdr_length]);
10610Sstevel@tonic-gate 	ipha = (ipha_t *)&icmph[1];
10620Sstevel@tonic-gate 	iph_hdr_length = IPH_HDR_LENGTH(ipha);
10630Sstevel@tonic-gate 
10640Sstevel@tonic-gate 	switch (icmph->icmph_type) {
10650Sstevel@tonic-gate 	case ICMP_DEST_UNREACHABLE:
10660Sstevel@tonic-gate 		switch (icmph->icmph_code) {
10670Sstevel@tonic-gate 		case ICMP_FRAGMENTATION_NEEDED:
10680Sstevel@tonic-gate 			/*
10690Sstevel@tonic-gate 			 * IP has already adjusted the path MTU.
10700Sstevel@tonic-gate 			 */
10710Sstevel@tonic-gate 			break;
10720Sstevel@tonic-gate 		case ICMP_PORT_UNREACHABLE:
10730Sstevel@tonic-gate 		case ICMP_PROTOCOL_UNREACHABLE:
10740Sstevel@tonic-gate 			error = ECONNREFUSED;
10750Sstevel@tonic-gate 			break;
10760Sstevel@tonic-gate 		default:
10770Sstevel@tonic-gate 			/* Transient errors */
10780Sstevel@tonic-gate 			break;
10790Sstevel@tonic-gate 		}
10800Sstevel@tonic-gate 		break;
10810Sstevel@tonic-gate 	default:
10820Sstevel@tonic-gate 		/* Transient errors */
10830Sstevel@tonic-gate 		break;
10840Sstevel@tonic-gate 	}
10850Sstevel@tonic-gate 	if (error == 0) {
10860Sstevel@tonic-gate 		freemsg(mp);
10870Sstevel@tonic-gate 		return;
10880Sstevel@tonic-gate 	}
10890Sstevel@tonic-gate 
10905240Snordmark 	/*
10915240Snordmark 	 * Deliver T_UDERROR_IND when the application has asked for it.
10925240Snordmark 	 * The socket layer enables this automatically when connected.
10935240Snordmark 	 */
10945240Snordmark 	if (!icmp->icmp_dgram_errind) {
10955240Snordmark 		freemsg(mp);
10965240Snordmark 		return;
10975240Snordmark 	}
10985240Snordmark 
10998348SEric.Yu@Sun.COM 	sin = sin_null;
11008348SEric.Yu@Sun.COM 	sin.sin_family = AF_INET;
11018348SEric.Yu@Sun.COM 	sin.sin_addr.s_addr = ipha->ipha_dst;
11028348SEric.Yu@Sun.COM 	if (IPCL_IS_NONSTR(connp)) {
11038348SEric.Yu@Sun.COM 		rw_enter(&icmp->icmp_rwlock, RW_WRITER);
11048348SEric.Yu@Sun.COM 		if (icmp->icmp_state == TS_DATA_XFER) {
11058348SEric.Yu@Sun.COM 			if (sin.sin_addr.s_addr ==
11068348SEric.Yu@Sun.COM 			    V4_PART_OF_V6(icmp->icmp_v6dst.sin6_addr)) {
11078348SEric.Yu@Sun.COM 				rw_exit(&icmp->icmp_rwlock);
11088348SEric.Yu@Sun.COM 				(*connp->conn_upcalls->su_set_error)
11098348SEric.Yu@Sun.COM 				    (connp->conn_upper_handle, error);
11108348SEric.Yu@Sun.COM 				goto done;
11118348SEric.Yu@Sun.COM 			}
11128348SEric.Yu@Sun.COM 		} else {
11138348SEric.Yu@Sun.COM 			icmp->icmp_delayed_error = error;
11148348SEric.Yu@Sun.COM 			*((sin_t *)&icmp->icmp_delayed_addr) = sin;
11158348SEric.Yu@Sun.COM 		}
11168348SEric.Yu@Sun.COM 		rw_exit(&icmp->icmp_rwlock);
11178348SEric.Yu@Sun.COM 	} else {
11188348SEric.Yu@Sun.COM 
11198348SEric.Yu@Sun.COM 		mp1 = mi_tpi_uderror_ind((char *)&sin, sizeof (sin_t), NULL,
11208348SEric.Yu@Sun.COM 		    0, error);
11218348SEric.Yu@Sun.COM 		if (mp1 != NULL)
11228348SEric.Yu@Sun.COM 			putnext(connp->conn_rq, mp1);
11238348SEric.Yu@Sun.COM 	}
11248348SEric.Yu@Sun.COM done:
11250Sstevel@tonic-gate 	freemsg(mp);
11260Sstevel@tonic-gate }
11270Sstevel@tonic-gate 
11280Sstevel@tonic-gate /*
11290Sstevel@tonic-gate  * icmp_icmp_error_ipv6 is called by icmp_icmp_error to process ICMPv6
11300Sstevel@tonic-gate  * for IPv6 packets.
11310Sstevel@tonic-gate  * Send permanent (non-transient) errors upstream.
11320Sstevel@tonic-gate  * Assumes that IP has pulled up all the extension headers as well
11330Sstevel@tonic-gate  * as the ICMPv6 header.
11340Sstevel@tonic-gate  */
11350Sstevel@tonic-gate static void
11368348SEric.Yu@Sun.COM icmp_icmp_error_ipv6(conn_t *connp, mblk_t *mp)
11370Sstevel@tonic-gate {
11380Sstevel@tonic-gate 	icmp6_t		*icmp6;
11390Sstevel@tonic-gate 	ip6_t		*ip6h, *outer_ip6h;
11400Sstevel@tonic-gate 	uint16_t	iph_hdr_length;
11410Sstevel@tonic-gate 	uint8_t		*nexthdrp;
11420Sstevel@tonic-gate 	sin6_t		sin6;
11430Sstevel@tonic-gate 	mblk_t		*mp1;
11440Sstevel@tonic-gate 	int		error = 0;
11458348SEric.Yu@Sun.COM 	icmp_t		*icmp = connp->conn_icmp;
11460Sstevel@tonic-gate 
11470Sstevel@tonic-gate 	outer_ip6h = (ip6_t *)mp->b_rptr;
11480Sstevel@tonic-gate 	if (outer_ip6h->ip6_nxt != IPPROTO_ICMPV6)
11490Sstevel@tonic-gate 		iph_hdr_length = ip_hdr_length_v6(mp, outer_ip6h);
11500Sstevel@tonic-gate 	else
11510Sstevel@tonic-gate 		iph_hdr_length = IPV6_HDR_LEN;
11520Sstevel@tonic-gate 
11530Sstevel@tonic-gate 	icmp6 = (icmp6_t *)&mp->b_rptr[iph_hdr_length];
11540Sstevel@tonic-gate 	ip6h = (ip6_t *)&icmp6[1];
11550Sstevel@tonic-gate 	if (!ip_hdr_length_nexthdr_v6(mp, ip6h, &iph_hdr_length, &nexthdrp)) {
11560Sstevel@tonic-gate 		freemsg(mp);
11570Sstevel@tonic-gate 		return;
11580Sstevel@tonic-gate 	}
11595240Snordmark 
11600Sstevel@tonic-gate 	switch (icmp6->icmp6_type) {
11610Sstevel@tonic-gate 	case ICMP6_DST_UNREACH:
11620Sstevel@tonic-gate 		switch (icmp6->icmp6_code) {
11630Sstevel@tonic-gate 		case ICMP6_DST_UNREACH_NOPORT:
11640Sstevel@tonic-gate 			error = ECONNREFUSED;
11650Sstevel@tonic-gate 			break;
11660Sstevel@tonic-gate 		case ICMP6_DST_UNREACH_ADMIN:
11670Sstevel@tonic-gate 		case ICMP6_DST_UNREACH_NOROUTE:
11680Sstevel@tonic-gate 		case ICMP6_DST_UNREACH_BEYONDSCOPE:
11690Sstevel@tonic-gate 		case ICMP6_DST_UNREACH_ADDR:
11700Sstevel@tonic-gate 			/* Transient errors */
11710Sstevel@tonic-gate 			break;
11720Sstevel@tonic-gate 		default:
11730Sstevel@tonic-gate 			break;
11740Sstevel@tonic-gate 		}
11750Sstevel@tonic-gate 		break;
11760Sstevel@tonic-gate 	case ICMP6_PACKET_TOO_BIG: {
11770Sstevel@tonic-gate 		struct T_unitdata_ind	*tudi;
11780Sstevel@tonic-gate 		struct T_opthdr		*toh;
11790Sstevel@tonic-gate 		size_t			udi_size;
11800Sstevel@tonic-gate 		mblk_t			*newmp;
11810Sstevel@tonic-gate 		t_scalar_t		opt_length = sizeof (struct T_opthdr) +
11820Sstevel@tonic-gate 		    sizeof (struct ip6_mtuinfo);
11830Sstevel@tonic-gate 		sin6_t			*sin6;
11840Sstevel@tonic-gate 		struct ip6_mtuinfo	*mtuinfo;
11850Sstevel@tonic-gate 
11860Sstevel@tonic-gate 		/*
11870Sstevel@tonic-gate 		 * If the application has requested to receive path mtu
11880Sstevel@tonic-gate 		 * information, send up an empty message containing an
11890Sstevel@tonic-gate 		 * IPV6_PATHMTU ancillary data item.
11900Sstevel@tonic-gate 		 */
11910Sstevel@tonic-gate 		if (!icmp->icmp_ipv6_recvpathmtu)
11920Sstevel@tonic-gate 			break;
11930Sstevel@tonic-gate 
11940Sstevel@tonic-gate 		udi_size = sizeof (struct T_unitdata_ind) + sizeof (sin6_t) +
11950Sstevel@tonic-gate 		    opt_length;
11960Sstevel@tonic-gate 		if ((newmp = allocb(udi_size, BPRI_MED)) == NULL) {
11975240Snordmark 			BUMP_MIB(&icmp->icmp_is->is_rawip_mib, rawipInErrors);
11980Sstevel@tonic-gate 			break;
11990Sstevel@tonic-gate 		}
12000Sstevel@tonic-gate 
12010Sstevel@tonic-gate 		/*
12020Sstevel@tonic-gate 		 * newmp->b_cont is left to NULL on purpose.  This is an
12030Sstevel@tonic-gate 		 * empty message containing only ancillary data.
12040Sstevel@tonic-gate 		 */
12050Sstevel@tonic-gate 		newmp->b_datap->db_type = M_PROTO;
12060Sstevel@tonic-gate 		tudi = (struct T_unitdata_ind *)newmp->b_rptr;
12070Sstevel@tonic-gate 		newmp->b_wptr = (uchar_t *)tudi + udi_size;
12080Sstevel@tonic-gate 		tudi->PRIM_type = T_UNITDATA_IND;
12090Sstevel@tonic-gate 		tudi->SRC_length = sizeof (sin6_t);
12100Sstevel@tonic-gate 		tudi->SRC_offset = sizeof (struct T_unitdata_ind);
12110Sstevel@tonic-gate 		tudi->OPT_offset = tudi->SRC_offset + sizeof (sin6_t);
12120Sstevel@tonic-gate 		tudi->OPT_length = opt_length;
12130Sstevel@tonic-gate 
12140Sstevel@tonic-gate 		sin6 = (sin6_t *)&tudi[1];
12150Sstevel@tonic-gate 		bzero(sin6, sizeof (sin6_t));
12160Sstevel@tonic-gate 		sin6->sin6_family = AF_INET6;
12178348SEric.Yu@Sun.COM 		sin6->sin6_addr = icmp->icmp_v6dst.sin6_addr;
12180Sstevel@tonic-gate 
12190Sstevel@tonic-gate 		toh = (struct T_opthdr *)&sin6[1];
12200Sstevel@tonic-gate 		toh->level = IPPROTO_IPV6;
12210Sstevel@tonic-gate 		toh->name = IPV6_PATHMTU;
12220Sstevel@tonic-gate 		toh->len = opt_length;
12230Sstevel@tonic-gate 		toh->status = 0;
12240Sstevel@tonic-gate 
12250Sstevel@tonic-gate 		mtuinfo = (struct ip6_mtuinfo *)&toh[1];
12260Sstevel@tonic-gate 		bzero(mtuinfo, sizeof (struct ip6_mtuinfo));
12270Sstevel@tonic-gate 		mtuinfo->ip6m_addr.sin6_family = AF_INET6;
12280Sstevel@tonic-gate 		mtuinfo->ip6m_addr.sin6_addr = ip6h->ip6_dst;
12290Sstevel@tonic-gate 		mtuinfo->ip6m_mtu = icmp6->icmp6_mtu;
12300Sstevel@tonic-gate 		/*
12310Sstevel@tonic-gate 		 * We've consumed everything we need from the original
12320Sstevel@tonic-gate 		 * message.  Free it, then send our empty message.
12330Sstevel@tonic-gate 		 */
12340Sstevel@tonic-gate 		freemsg(mp);
12358348SEric.Yu@Sun.COM 		if (!IPCL_IS_NONSTR(connp)) {
12368348SEric.Yu@Sun.COM 			putnext(connp->conn_rq, newmp);
12378348SEric.Yu@Sun.COM 		} else {
12388348SEric.Yu@Sun.COM 			(*connp->conn_upcalls->su_recv)
12398348SEric.Yu@Sun.COM 			    (connp->conn_upper_handle, newmp, 0, 0, &error,
12408348SEric.Yu@Sun.COM 			    NULL);
12418348SEric.Yu@Sun.COM 			ASSERT(error == 0);
12428348SEric.Yu@Sun.COM 		}
12430Sstevel@tonic-gate 		return;
12440Sstevel@tonic-gate 	}
12450Sstevel@tonic-gate 	case ICMP6_TIME_EXCEEDED:
12460Sstevel@tonic-gate 		/* Transient errors */
12470Sstevel@tonic-gate 		break;
12480Sstevel@tonic-gate 	case ICMP6_PARAM_PROB:
12490Sstevel@tonic-gate 		/* If this corresponds to an ICMP_PROTOCOL_UNREACHABLE */
12500Sstevel@tonic-gate 		if (icmp6->icmp6_code == ICMP6_PARAMPROB_NEXTHEADER &&
12510Sstevel@tonic-gate 		    (uchar_t *)ip6h + icmp6->icmp6_pptr ==
12520Sstevel@tonic-gate 		    (uchar_t *)nexthdrp) {
12530Sstevel@tonic-gate 			error = ECONNREFUSED;
12540Sstevel@tonic-gate 			break;
12550Sstevel@tonic-gate 		}
12560Sstevel@tonic-gate 		break;
12570Sstevel@tonic-gate 	}
12580Sstevel@tonic-gate 	if (error == 0) {
12590Sstevel@tonic-gate 		freemsg(mp);
12600Sstevel@tonic-gate 		return;
12610Sstevel@tonic-gate 	}
12620Sstevel@tonic-gate 
12635240Snordmark 	/*
12645240Snordmark 	 * Deliver T_UDERROR_IND when the application has asked for it.
12655240Snordmark 	 * The socket layer enables this automatically when connected.
12665240Snordmark 	 */
12675240Snordmark 	if (!icmp->icmp_dgram_errind) {
12685240Snordmark 		freemsg(mp);
12695240Snordmark 		return;
12705240Snordmark 	}
12715240Snordmark 
12720Sstevel@tonic-gate 	sin6 = sin6_null;
12730Sstevel@tonic-gate 	sin6.sin6_family = AF_INET6;
12740Sstevel@tonic-gate 	sin6.sin6_addr = ip6h->ip6_dst;
12750Sstevel@tonic-gate 	sin6.sin6_flowinfo = ip6h->ip6_vcf & ~IPV6_VERS_AND_FLOW_MASK;
12760Sstevel@tonic-gate 
12778348SEric.Yu@Sun.COM 	if (IPCL_IS_NONSTR(connp)) {
12788348SEric.Yu@Sun.COM 		rw_enter(&icmp->icmp_rwlock, RW_WRITER);
12798348SEric.Yu@Sun.COM 		if (icmp->icmp_state == TS_DATA_XFER) {
12808348SEric.Yu@Sun.COM 			if (IN6_ARE_ADDR_EQUAL(&sin6.sin6_addr,
12818348SEric.Yu@Sun.COM 			    &icmp->icmp_v6dst.sin6_addr)) {
12828348SEric.Yu@Sun.COM 				rw_exit(&icmp->icmp_rwlock);
12838348SEric.Yu@Sun.COM 				(*connp->conn_upcalls->su_set_error)
12848348SEric.Yu@Sun.COM 				    (connp->conn_upper_handle, error);
12858348SEric.Yu@Sun.COM 				goto done;
12868348SEric.Yu@Sun.COM 			}
12878348SEric.Yu@Sun.COM 		} else {
12888348SEric.Yu@Sun.COM 			icmp->icmp_delayed_error = error;
12898348SEric.Yu@Sun.COM 			*((sin6_t *)&icmp->icmp_delayed_addr) = sin6;
12908348SEric.Yu@Sun.COM 		}
12918348SEric.Yu@Sun.COM 		rw_exit(&icmp->icmp_rwlock);
12928348SEric.Yu@Sun.COM 	} else {
12938348SEric.Yu@Sun.COM 
12948348SEric.Yu@Sun.COM 		mp1 = mi_tpi_uderror_ind((char *)&sin6, sizeof (sin6_t),
12958348SEric.Yu@Sun.COM 		    NULL, 0, error);
12968348SEric.Yu@Sun.COM 		if (mp1 != NULL)
12978348SEric.Yu@Sun.COM 			putnext(connp->conn_rq, mp1);
12988348SEric.Yu@Sun.COM 	}
12998348SEric.Yu@Sun.COM done:
13000Sstevel@tonic-gate 	freemsg(mp);
13010Sstevel@tonic-gate }
13020Sstevel@tonic-gate 
13030Sstevel@tonic-gate /*
13040Sstevel@tonic-gate  * This routine responds to T_ADDR_REQ messages.  It is called by icmp_wput.
13050Sstevel@tonic-gate  * The local address is filled in if endpoint is bound. The remote address
13060Sstevel@tonic-gate  * is filled in if remote address has been precified ("connected endpoint")
13070Sstevel@tonic-gate  * (The concept of connected CLTS sockets is alien to published TPI
13080Sstevel@tonic-gate  *  but we support it anyway).
13090Sstevel@tonic-gate  */
13100Sstevel@tonic-gate static void
13110Sstevel@tonic-gate icmp_addr_req(queue_t *q, mblk_t *mp)
13120Sstevel@tonic-gate {
13135240Snordmark 	icmp_t	*icmp = Q_TO_ICMP(q);
13140Sstevel@tonic-gate 	mblk_t	*ackmp;
13150Sstevel@tonic-gate 	struct T_addr_ack *taa;
13160Sstevel@tonic-gate 
13170Sstevel@tonic-gate 	/* Make it large enough for worst case */
13180Sstevel@tonic-gate 	ackmp = reallocb(mp, sizeof (struct T_addr_ack) +
13190Sstevel@tonic-gate 	    2 * sizeof (sin6_t), 1);
13200Sstevel@tonic-gate 	if (ackmp == NULL) {
13210Sstevel@tonic-gate 		icmp_err_ack(q, mp, TSYSERR, ENOMEM);
13220Sstevel@tonic-gate 		return;
13230Sstevel@tonic-gate 	}
13240Sstevel@tonic-gate 	taa = (struct T_addr_ack *)ackmp->b_rptr;
13250Sstevel@tonic-gate 
13260Sstevel@tonic-gate 	bzero(taa, sizeof (struct T_addr_ack));
13270Sstevel@tonic-gate 	ackmp->b_wptr = (uchar_t *)&taa[1];
13280Sstevel@tonic-gate 
13290Sstevel@tonic-gate 	taa->PRIM_type = T_ADDR_ACK;
13300Sstevel@tonic-gate 	ackmp->b_datap->db_type = M_PCPROTO;
13315240Snordmark 	rw_enter(&icmp->icmp_rwlock, RW_READER);
13320Sstevel@tonic-gate 	/*
13330Sstevel@tonic-gate 	 * Note: Following code assumes 32 bit alignment of basic
13340Sstevel@tonic-gate 	 * data structures like sin_t and struct T_addr_ack.
13350Sstevel@tonic-gate 	 */
13360Sstevel@tonic-gate 	if (icmp->icmp_state != TS_UNBND) {
13370Sstevel@tonic-gate 		/*
13380Sstevel@tonic-gate 		 * Fill in local address
13390Sstevel@tonic-gate 		 */
13400Sstevel@tonic-gate 		taa->LOCADDR_offset = sizeof (*taa);
13410Sstevel@tonic-gate 		if (icmp->icmp_family == AF_INET) {
13420Sstevel@tonic-gate 			sin_t	*sin;
13430Sstevel@tonic-gate 
13440Sstevel@tonic-gate 			taa->LOCADDR_length = sizeof (sin_t);
13450Sstevel@tonic-gate 			sin = (sin_t *)&taa[1];
13460Sstevel@tonic-gate 			/* Fill zeroes and then intialize non-zero fields */
13470Sstevel@tonic-gate 			*sin = sin_null;
13480Sstevel@tonic-gate 			sin->sin_family = AF_INET;
13490Sstevel@tonic-gate 			if (!IN6_IS_ADDR_V4MAPPED_ANY(&icmp->icmp_v6src) &&
13500Sstevel@tonic-gate 			    !IN6_IS_ADDR_UNSPECIFIED(&icmp->icmp_v6src)) {
13510Sstevel@tonic-gate 				IN6_V4MAPPED_TO_IPADDR(&icmp->icmp_v6src,
13520Sstevel@tonic-gate 				    sin->sin_addr.s_addr);
13530Sstevel@tonic-gate 			} else {
13540Sstevel@tonic-gate 				/*
13550Sstevel@tonic-gate 				 * INADDR_ANY
13560Sstevel@tonic-gate 				 * icmp_v6src is not set, we might be bound to
13570Sstevel@tonic-gate 				 * broadcast/multicast. Use icmp_bound_v6src as
13580Sstevel@tonic-gate 				 * local address instead (that could
13590Sstevel@tonic-gate 				 * also still be INADDR_ANY)
13600Sstevel@tonic-gate 				 */
13610Sstevel@tonic-gate 				IN6_V4MAPPED_TO_IPADDR(&icmp->icmp_bound_v6src,
13620Sstevel@tonic-gate 				    sin->sin_addr.s_addr);
13630Sstevel@tonic-gate 			}
13640Sstevel@tonic-gate 			ackmp->b_wptr = (uchar_t *)&sin[1];
13650Sstevel@tonic-gate 		} else {
13660Sstevel@tonic-gate 			sin6_t	*sin6;
13670Sstevel@tonic-gate 
13680Sstevel@tonic-gate 			ASSERT(icmp->icmp_family == AF_INET6);
13690Sstevel@tonic-gate 			taa->LOCADDR_length = sizeof (sin6_t);
13700Sstevel@tonic-gate 			sin6 = (sin6_t *)&taa[1];
13710Sstevel@tonic-gate 			/* Fill zeroes and then intialize non-zero fields */
13720Sstevel@tonic-gate 			*sin6 = sin6_null;
13730Sstevel@tonic-gate 			sin6->sin6_family = AF_INET6;
13740Sstevel@tonic-gate 			if (!IN6_IS_ADDR_UNSPECIFIED(&icmp->icmp_v6src)) {
13750Sstevel@tonic-gate 				sin6->sin6_addr = icmp->icmp_v6src;
13760Sstevel@tonic-gate 			} else {
13770Sstevel@tonic-gate 				/*
13780Sstevel@tonic-gate 				 * UNSPECIFIED
13790Sstevel@tonic-gate 				 * icmp_v6src is not set, we might be bound to
13800Sstevel@tonic-gate 				 * broadcast/multicast. Use icmp_bound_v6src as
13810Sstevel@tonic-gate 				 * local address instead (that could
13820Sstevel@tonic-gate 				 * also still be UNSPECIFIED)
13830Sstevel@tonic-gate 				 */
13840Sstevel@tonic-gate 				sin6->sin6_addr = icmp->icmp_bound_v6src;
13850Sstevel@tonic-gate 			}
13860Sstevel@tonic-gate 			ackmp->b_wptr = (uchar_t *)&sin6[1];
13870Sstevel@tonic-gate 		}
13880Sstevel@tonic-gate 	}
13895240Snordmark 	rw_exit(&icmp->icmp_rwlock);
13900Sstevel@tonic-gate 	ASSERT(ackmp->b_wptr <= ackmp->b_datap->db_lim);
13910Sstevel@tonic-gate 	qreply(q, ackmp);
13920Sstevel@tonic-gate }
13930Sstevel@tonic-gate 
13940Sstevel@tonic-gate static void
13950Sstevel@tonic-gate icmp_copy_info(struct T_info_ack *tap, icmp_t *icmp)
13960Sstevel@tonic-gate {
13970Sstevel@tonic-gate 	*tap = icmp_g_t_info_ack;
13980Sstevel@tonic-gate 
13990Sstevel@tonic-gate 	if (icmp->icmp_family == AF_INET6)
14000Sstevel@tonic-gate 		tap->ADDR_size = sizeof (sin6_t);
14010Sstevel@tonic-gate 	else
14020Sstevel@tonic-gate 		tap->ADDR_size = sizeof (sin_t);
14030Sstevel@tonic-gate 	tap->CURRENT_state = icmp->icmp_state;
14040Sstevel@tonic-gate 	tap->OPT_size = icmp_max_optsize;
14050Sstevel@tonic-gate }
14060Sstevel@tonic-gate 
14078348SEric.Yu@Sun.COM static void
14088348SEric.Yu@Sun.COM icmp_do_capability_ack(icmp_t *icmp, struct T_capability_ack *tcap,
14098348SEric.Yu@Sun.COM     t_uscalar_t cap_bits1)
14108348SEric.Yu@Sun.COM {
14118348SEric.Yu@Sun.COM 	tcap->CAP_bits1 = 0;
14128348SEric.Yu@Sun.COM 
14138348SEric.Yu@Sun.COM 	if (cap_bits1 & TC1_INFO) {
14148348SEric.Yu@Sun.COM 		icmp_copy_info(&tcap->INFO_ack, icmp);
14158348SEric.Yu@Sun.COM 		tcap->CAP_bits1 |= TC1_INFO;
14168348SEric.Yu@Sun.COM 	}
14178348SEric.Yu@Sun.COM }
14188348SEric.Yu@Sun.COM 
14190Sstevel@tonic-gate /*
14200Sstevel@tonic-gate  * This routine responds to T_CAPABILITY_REQ messages.  It is called by
14210Sstevel@tonic-gate  * icmp_wput.  Much of the T_CAPABILITY_ACK information is copied from
14220Sstevel@tonic-gate  * icmp_g_t_info_ack.  The current state of the stream is copied from
14230Sstevel@tonic-gate  * icmp_state.
14240Sstevel@tonic-gate  */
14250Sstevel@tonic-gate static void
14260Sstevel@tonic-gate icmp_capability_req(queue_t *q, mblk_t *mp)
14270Sstevel@tonic-gate {
14285240Snordmark 	icmp_t			*icmp = Q_TO_ICMP(q);
14290Sstevel@tonic-gate 	t_uscalar_t		cap_bits1;
14300Sstevel@tonic-gate 	struct T_capability_ack	*tcap;
14310Sstevel@tonic-gate 
14320Sstevel@tonic-gate 	cap_bits1 = ((struct T_capability_req *)mp->b_rptr)->CAP_bits1;
14330Sstevel@tonic-gate 
14340Sstevel@tonic-gate 	mp = tpi_ack_alloc(mp, sizeof (struct T_capability_ack),
14355240Snordmark 	    mp->b_datap->db_type, T_CAPABILITY_ACK);
14360Sstevel@tonic-gate 	if (!mp)
14370Sstevel@tonic-gate 		return;
14380Sstevel@tonic-gate 
14390Sstevel@tonic-gate 	tcap = (struct T_capability_ack *)mp->b_rptr;
14408348SEric.Yu@Sun.COM 
14418348SEric.Yu@Sun.COM 	icmp_do_capability_ack(icmp, tcap, cap_bits1);
14420Sstevel@tonic-gate 
14430Sstevel@tonic-gate 	qreply(q, mp);
14440Sstevel@tonic-gate }
14450Sstevel@tonic-gate 
14460Sstevel@tonic-gate /*
14470Sstevel@tonic-gate  * This routine responds to T_INFO_REQ messages.  It is called by icmp_wput.
14480Sstevel@tonic-gate  * Most of the T_INFO_ACK information is copied from icmp_g_t_info_ack.
14490Sstevel@tonic-gate  * The current state of the stream is copied from icmp_state.
14500Sstevel@tonic-gate  */
14510Sstevel@tonic-gate static void
14520Sstevel@tonic-gate icmp_info_req(queue_t *q, mblk_t *mp)
14530Sstevel@tonic-gate {
14545240Snordmark 	icmp_t	*icmp = Q_TO_ICMP(q);
14550Sstevel@tonic-gate 
14560Sstevel@tonic-gate 	mp = tpi_ack_alloc(mp, sizeof (struct T_info_ack), M_PCPROTO,
14570Sstevel@tonic-gate 	    T_INFO_ACK);
14580Sstevel@tonic-gate 	if (!mp)
14590Sstevel@tonic-gate 		return;
14600Sstevel@tonic-gate 	icmp_copy_info((struct T_info_ack *)mp->b_rptr, icmp);
14610Sstevel@tonic-gate 	qreply(q, mp);
14620Sstevel@tonic-gate }
14630Sstevel@tonic-gate 
14645240Snordmark /* For /dev/icmp aka AF_INET open */
14655240Snordmark static int
14668348SEric.Yu@Sun.COM icmp_tpi_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp,
14678348SEric.Yu@Sun.COM     int family)
14685240Snordmark {
14695240Snordmark 	conn_t *connp;
14705240Snordmark 	dev_t	conn_dev;
14713448Sdh155122 	icmp_stack_t *is;
14728348SEric.Yu@Sun.COM 	int	error;
14738348SEric.Yu@Sun.COM 
14748348SEric.Yu@Sun.COM 	conn_dev = NULL;
14750Sstevel@tonic-gate 
14760Sstevel@tonic-gate 	/* If the stream is already open, return immediately. */
14770Sstevel@tonic-gate 	if (q->q_ptr != NULL)
14780Sstevel@tonic-gate 		return (0);
14790Sstevel@tonic-gate 
14805240Snordmark 	if (sflag == MODOPEN)
14810Sstevel@tonic-gate 		return (EINVAL);
14820Sstevel@tonic-gate 
14838348SEric.Yu@Sun.COM 	/*
14848348SEric.Yu@Sun.COM 	 * Since ICMP is not used so heavily, allocating from the small
14858348SEric.Yu@Sun.COM 	 * arena should be sufficient.
14868348SEric.Yu@Sun.COM 	 */
14878348SEric.Yu@Sun.COM 	if ((conn_dev = inet_minor_alloc(ip_minor_arena_sa)) == 0) {
14888348SEric.Yu@Sun.COM 		return (EBUSY);
14898348SEric.Yu@Sun.COM 	}
14908348SEric.Yu@Sun.COM 
14918348SEric.Yu@Sun.COM 	if (flag & SO_FALLBACK) {
14928348SEric.Yu@Sun.COM 		/*
14938348SEric.Yu@Sun.COM 		 * Non streams socket needs a stream to fallback to
14948348SEric.Yu@Sun.COM 		 */
14958348SEric.Yu@Sun.COM 		RD(q)->q_ptr = (void *)conn_dev;
14968348SEric.Yu@Sun.COM 		WR(q)->q_qinfo = &icmp_fallback_sock_winit;
14978348SEric.Yu@Sun.COM 		WR(q)->q_ptr = (void *)ip_minor_arena_sa;
14988348SEric.Yu@Sun.COM 		qprocson(q);
14998348SEric.Yu@Sun.COM 		return (0);
15008348SEric.Yu@Sun.COM 	}
15018348SEric.Yu@Sun.COM 
15028348SEric.Yu@Sun.COM 	connp = icmp_open(family, credp, &error, KM_SLEEP);
15038348SEric.Yu@Sun.COM 	if (connp == NULL) {
15048348SEric.Yu@Sun.COM 		ASSERT(error != NULL);
15058348SEric.Yu@Sun.COM 		inet_minor_free(ip_minor_arena_sa, connp->conn_dev);
15068348SEric.Yu@Sun.COM 		return (error);
15078348SEric.Yu@Sun.COM 	}
15088348SEric.Yu@Sun.COM 
15098348SEric.Yu@Sun.COM 	*devp = makedevice(getemajor(*devp), (minor_t)conn_dev);
15108348SEric.Yu@Sun.COM 	connp->conn_dev = conn_dev;
15118348SEric.Yu@Sun.COM 	connp->conn_minor_arena = ip_minor_arena_sa;
15128348SEric.Yu@Sun.COM 
15138348SEric.Yu@Sun.COM 	is = connp->conn_icmp->icmp_is;
15148348SEric.Yu@Sun.COM 
15158348SEric.Yu@Sun.COM 	/*
15168348SEric.Yu@Sun.COM 	 * Initialize the icmp_t structure for this stream.
15178348SEric.Yu@Sun.COM 	 */
15188348SEric.Yu@Sun.COM 	q->q_ptr = connp;
15198348SEric.Yu@Sun.COM 	WR(q)->q_ptr = connp;
15208348SEric.Yu@Sun.COM 	connp->conn_rq = q;
15218348SEric.Yu@Sun.COM 	connp->conn_wq = WR(q);
15228348SEric.Yu@Sun.COM 
15238348SEric.Yu@Sun.COM 	if (connp->conn_icmp->icmp_family == AF_INET6) {
15248348SEric.Yu@Sun.COM 		/* Build initial header template for transmit */
15258348SEric.Yu@Sun.COM 		rw_enter(&connp->conn_icmp->icmp_rwlock, RW_WRITER);
15268348SEric.Yu@Sun.COM 		if ((error = icmp_build_hdrs(connp->conn_icmp)) != 0) {
15278348SEric.Yu@Sun.COM 			rw_exit(&connp->conn_icmp->icmp_rwlock);
15288348SEric.Yu@Sun.COM 			inet_minor_free(ip_minor_arena_sa, connp->conn_dev);
15298348SEric.Yu@Sun.COM 			ipcl_conn_destroy(connp);
15308348SEric.Yu@Sun.COM 			return (error);
15318348SEric.Yu@Sun.COM 		}
15328348SEric.Yu@Sun.COM 		rw_exit(&connp->conn_icmp->icmp_rwlock);
15338348SEric.Yu@Sun.COM 	}
15348348SEric.Yu@Sun.COM 
15358348SEric.Yu@Sun.COM 
15368348SEric.Yu@Sun.COM 	q->q_hiwat = is->is_recv_hiwat;
15378348SEric.Yu@Sun.COM 	WR(q)->q_hiwat = is->is_xmit_hiwat;
15388348SEric.Yu@Sun.COM 	WR(q)->q_lowat = is->is_xmit_lowat;
15398348SEric.Yu@Sun.COM 
15408348SEric.Yu@Sun.COM 	qprocson(q);
15418348SEric.Yu@Sun.COM 
15428348SEric.Yu@Sun.COM 	/* Set the Stream head write offset. */
15438348SEric.Yu@Sun.COM 	(void) proto_set_tx_wroff(q, connp,
15448348SEric.Yu@Sun.COM 	    connp->conn_icmp->icmp_max_hdr_len + is->is_wroff_extra);
15458348SEric.Yu@Sun.COM 	(void) proto_set_rx_hiwat(connp->conn_rq, connp, q->q_hiwat);
15468348SEric.Yu@Sun.COM 
15478348SEric.Yu@Sun.COM 	mutex_enter(&connp->conn_lock);
15488348SEric.Yu@Sun.COM 	connp->conn_state_flags &= ~CONN_INCIPIENT;
15498348SEric.Yu@Sun.COM 	mutex_exit(&connp->conn_lock);
15508348SEric.Yu@Sun.COM 
15518348SEric.Yu@Sun.COM 	return (0);
15528348SEric.Yu@Sun.COM }
15538348SEric.Yu@Sun.COM 
15548348SEric.Yu@Sun.COM /* For /dev/icmp4 aka AF_INET open */
15558348SEric.Yu@Sun.COM static int
15568348SEric.Yu@Sun.COM icmp_openv4(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp)
15578348SEric.Yu@Sun.COM {
15588348SEric.Yu@Sun.COM 	return (icmp_tpi_open(q, devp, flag, sflag, credp, AF_INET));
15598348SEric.Yu@Sun.COM }
15608348SEric.Yu@Sun.COM 
15618348SEric.Yu@Sun.COM /* For /dev/icmp6 aka AF_INET6 open */
15628348SEric.Yu@Sun.COM static int
15638348SEric.Yu@Sun.COM icmp_openv6(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp)
15648348SEric.Yu@Sun.COM {
15658348SEric.Yu@Sun.COM 	return (icmp_tpi_open(q, devp, flag, sflag, credp, AF_INET6));
15668348SEric.Yu@Sun.COM }
15678348SEric.Yu@Sun.COM 
15688348SEric.Yu@Sun.COM /*
15698348SEric.Yu@Sun.COM  * This is the open routine for icmp.  It allocates a icmp_t structure for
15708348SEric.Yu@Sun.COM  * the stream and, on the first open of the module, creates an ND table.
15718348SEric.Yu@Sun.COM  */
15728348SEric.Yu@Sun.COM /* ARGSUSED */
15738348SEric.Yu@Sun.COM static conn_t *
15748348SEric.Yu@Sun.COM icmp_open(int family, cred_t *credp, int *err, int flags)
15758348SEric.Yu@Sun.COM {
15768348SEric.Yu@Sun.COM 	icmp_t	*icmp;
15778348SEric.Yu@Sun.COM 	conn_t *connp;
15788348SEric.Yu@Sun.COM 	zoneid_t zoneid;
15798348SEric.Yu@Sun.COM 	netstack_t *ns;
15808348SEric.Yu@Sun.COM 	icmp_stack_t *is;
15818348SEric.Yu@Sun.COM 	boolean_t isv6 = B_FALSE;
15828348SEric.Yu@Sun.COM 
15838348SEric.Yu@Sun.COM 	*err = secpolicy_net_icmpaccess(credp);
15848348SEric.Yu@Sun.COM 	if (*err != 0)
15858348SEric.Yu@Sun.COM 		return (NULL);
15868348SEric.Yu@Sun.COM 
15878348SEric.Yu@Sun.COM 	if (family == AF_INET6)
15888348SEric.Yu@Sun.COM 		isv6 = B_TRUE;
15893448Sdh155122 	ns = netstack_find_by_cred(credp);
15903448Sdh155122 	ASSERT(ns != NULL);
15913448Sdh155122 	is = ns->netstack_icmp;
15923448Sdh155122 	ASSERT(is != NULL);
15933448Sdh155122 
15943448Sdh155122 	/*
15953448Sdh155122 	 * For exclusive stacks we set the zoneid to zero
15963448Sdh155122 	 * to make ICMP operate as if in the global zone.
15973448Sdh155122 	 */
15985240Snordmark 	if (ns->netstack_stackid != GLOBAL_NETSTACKID)
15993448Sdh155122 		zoneid = GLOBAL_ZONEID;
16003448Sdh155122 	else
16013448Sdh155122 		zoneid = crgetzoneid(credp);
16023448Sdh155122 
16038348SEric.Yu@Sun.COM 	ASSERT(flags == KM_SLEEP || flags == KM_NOSLEEP);
16048348SEric.Yu@Sun.COM 
16058348SEric.Yu@Sun.COM 	connp = ipcl_conn_create(IPCL_RAWIPCONN, flags, ns);
16065240Snordmark 	icmp = connp->conn_icmp;
16078348SEric.Yu@Sun.COM 	icmp->icmp_v6dst = sin6_null;
16080Sstevel@tonic-gate 
16090Sstevel@tonic-gate 	/*
16105240Snordmark 	 * ipcl_conn_create did a netstack_hold. Undo the hold that was
16115240Snordmark 	 * done by netstack_find_by_cred()
16125240Snordmark 	 */
16135240Snordmark 	netstack_rele(ns);
16145240Snordmark 
16155240Snordmark 	rw_enter(&icmp->icmp_rwlock, RW_WRITER);
16165240Snordmark 	ASSERT(connp->conn_ulp == IPPROTO_ICMP);
16175240Snordmark 	ASSERT(connp->conn_icmp == icmp);
16185240Snordmark 	ASSERT(icmp->icmp_connp == connp);
16190Sstevel@tonic-gate 
16200Sstevel@tonic-gate 	/* Set the initial state of the stream and the privilege status. */
16210Sstevel@tonic-gate 	icmp->icmp_state = TS_UNBND;
16225240Snordmark 	if (isv6) {
16230Sstevel@tonic-gate 		icmp->icmp_ipversion = IPV6_VERSION;
16240Sstevel@tonic-gate 		icmp->icmp_family = AF_INET6;
16255240Snordmark 		connp->conn_ulp = IPPROTO_ICMPV6;
16260Sstevel@tonic-gate 		/* May be changed by a SO_PROTOTYPE socket option. */
16270Sstevel@tonic-gate 		icmp->icmp_proto = IPPROTO_ICMPV6;
16280Sstevel@tonic-gate 		icmp->icmp_checksum_off = 2;	/* Offset for icmp6_cksum */
16290Sstevel@tonic-gate 		icmp->icmp_max_hdr_len = IPV6_HDR_LEN;
16303448Sdh155122 		icmp->icmp_ttl = (uint8_t)is->is_ipv6_hoplimit;
16315240Snordmark 		connp->conn_af_isv6 = B_TRUE;
16325240Snordmark 		connp->conn_flags |= IPCL_ISV6;
16330Sstevel@tonic-gate 	} else {
16340Sstevel@tonic-gate 		icmp->icmp_ipversion = IPV4_VERSION;
16350Sstevel@tonic-gate 		icmp->icmp_family = AF_INET;
16360Sstevel@tonic-gate 		/* May be changed by a SO_PROTOTYPE socket option. */
16370Sstevel@tonic-gate 		icmp->icmp_proto = IPPROTO_ICMP;
16380Sstevel@tonic-gate 		icmp->icmp_max_hdr_len = IP_SIMPLE_HDR_LENGTH;
16393448Sdh155122 		icmp->icmp_ttl = (uint8_t)is->is_ipv4_ttl;
16405240Snordmark 		connp->conn_af_isv6 = B_FALSE;
16415240Snordmark 		connp->conn_flags &= ~IPCL_ISV6;
16420Sstevel@tonic-gate 	}
16435240Snordmark 	icmp->icmp_multicast_ttl = IP_DEFAULT_MULTICAST_TTL;
16445240Snordmark 	icmp->icmp_pending_op = -1;
16455240Snordmark 	connp->conn_multicast_loop = IP_DEFAULT_MULTICAST_LOOP;
16465240Snordmark 	connp->conn_zoneid = zoneid;
16470Sstevel@tonic-gate 
16480Sstevel@tonic-gate 	/*
16495240Snordmark 	 * If the caller has the process-wide flag set, then default to MAC
16505240Snordmark 	 * exempt mode.  This allows read-down to unlabeled hosts.
16510Sstevel@tonic-gate 	 */
16525240Snordmark 	if (getpflags(NET_MAC_AWARE, credp) != 0)
16536596Skp158701 		connp->conn_mac_exempt = B_TRUE;
16545240Snordmark 
16555240Snordmark 	connp->conn_ulp_labeled = is_system_labeled();
16565240Snordmark 
16575240Snordmark 	icmp->icmp_is = is;
16585240Snordmark 
16595240Snordmark 	connp->conn_recv = icmp_input;
16605240Snordmark 	crhold(credp);
16615240Snordmark 	connp->conn_cred = credp;
16625240Snordmark 
16635240Snordmark 	rw_exit(&icmp->icmp_rwlock);
16645240Snordmark 
16658348SEric.Yu@Sun.COM 	connp->conn_flow_cntrld = B_FALSE;
16668348SEric.Yu@Sun.COM 	return (connp);
16670Sstevel@tonic-gate }
16680Sstevel@tonic-gate 
16690Sstevel@tonic-gate /*
16700Sstevel@tonic-gate  * Which ICMP options OK to set through T_UNITDATA_REQ...
16710Sstevel@tonic-gate  */
16720Sstevel@tonic-gate /* ARGSUSED */
16730Sstevel@tonic-gate static boolean_t
16740Sstevel@tonic-gate icmp_opt_allow_udr_set(t_scalar_t level, t_scalar_t name)
16750Sstevel@tonic-gate {
16760Sstevel@tonic-gate 	return (B_TRUE);
16770Sstevel@tonic-gate }
16780Sstevel@tonic-gate 
16790Sstevel@tonic-gate /*
16800Sstevel@tonic-gate  * This routine gets default values of certain options whose default
16810Sstevel@tonic-gate  * values are maintained by protcol specific code
16820Sstevel@tonic-gate  */
16830Sstevel@tonic-gate /* ARGSUSED */
16840Sstevel@tonic-gate int
16850Sstevel@tonic-gate icmp_opt_default(queue_t *q, int level, int name, uchar_t *ptr)
16860Sstevel@tonic-gate {
16875240Snordmark 	icmp_t *icmp = Q_TO_ICMP(q);
16883448Sdh155122 	icmp_stack_t *is = icmp->icmp_is;
16890Sstevel@tonic-gate 	int *i1 = (int *)ptr;
16900Sstevel@tonic-gate 
16910Sstevel@tonic-gate 	switch (level) {
16920Sstevel@tonic-gate 	case IPPROTO_IP:
16930Sstevel@tonic-gate 		switch (name) {
16940Sstevel@tonic-gate 		case IP_MULTICAST_TTL:
16950Sstevel@tonic-gate 			*ptr = (uchar_t)IP_DEFAULT_MULTICAST_TTL;
16960Sstevel@tonic-gate 			return (sizeof (uchar_t));
16970Sstevel@tonic-gate 		case IP_MULTICAST_LOOP:
16980Sstevel@tonic-gate 			*ptr = (uchar_t)IP_DEFAULT_MULTICAST_LOOP;
16990Sstevel@tonic-gate 			return (sizeof (uchar_t));
17000Sstevel@tonic-gate 		}
17010Sstevel@tonic-gate 		break;
17020Sstevel@tonic-gate 	case IPPROTO_IPV6:
17030Sstevel@tonic-gate 		switch (name) {
17040Sstevel@tonic-gate 		case IPV6_MULTICAST_HOPS:
17050Sstevel@tonic-gate 			*i1 = IP_DEFAULT_MULTICAST_TTL;
17060Sstevel@tonic-gate 			return (sizeof (int));
17070Sstevel@tonic-gate 		case IPV6_MULTICAST_LOOP:
17080Sstevel@tonic-gate 			*i1 = IP_DEFAULT_MULTICAST_LOOP;
17090Sstevel@tonic-gate 			return (sizeof (int));
17100Sstevel@tonic-gate 		case IPV6_UNICAST_HOPS:
17113448Sdh155122 			*i1 = is->is_ipv6_hoplimit;
17120Sstevel@tonic-gate 			return (sizeof (int));
17130Sstevel@tonic-gate 		}
17140Sstevel@tonic-gate 		break;
17150Sstevel@tonic-gate 	case IPPROTO_ICMPV6:
17160Sstevel@tonic-gate 		switch (name) {
17170Sstevel@tonic-gate 		case ICMP6_FILTER:
17180Sstevel@tonic-gate 			/* Make it look like "pass all" */
17190Sstevel@tonic-gate 			ICMP6_FILTER_SETPASSALL((icmp6_filter_t *)ptr);
17200Sstevel@tonic-gate 			return (sizeof (icmp6_filter_t));
17210Sstevel@tonic-gate 		}
17220Sstevel@tonic-gate 		break;
17230Sstevel@tonic-gate 	}
17240Sstevel@tonic-gate 	return (-1);
17250Sstevel@tonic-gate }
17260Sstevel@tonic-gate 
17270Sstevel@tonic-gate /*
17280Sstevel@tonic-gate  * This routine retrieves the current status of socket options.
17290Sstevel@tonic-gate  * It returns the size of the option retrieved.
17300Sstevel@tonic-gate  */
17310Sstevel@tonic-gate int
17328348SEric.Yu@Sun.COM icmp_opt_get(conn_t *connp, int level, int name, uchar_t *ptr)
17330Sstevel@tonic-gate {
17348348SEric.Yu@Sun.COM 	icmp_t		*icmp = connp->conn_icmp;
17358348SEric.Yu@Sun.COM 	icmp_stack_t	*is = icmp->icmp_is;
17368348SEric.Yu@Sun.COM 	int		*i1 = (int *)ptr;
17370Sstevel@tonic-gate 	ip6_pkt_t	*ipp = &icmp->icmp_sticky_ipp;
17388348SEric.Yu@Sun.COM 	int		ret = 0;
17398348SEric.Yu@Sun.COM 
17408348SEric.Yu@Sun.COM 	ASSERT(RW_READ_HELD(&icmp->icmp_rwlock));
17410Sstevel@tonic-gate 	switch (level) {
17420Sstevel@tonic-gate 	case SOL_SOCKET:
17430Sstevel@tonic-gate 		switch (name) {
17440Sstevel@tonic-gate 		case SO_DEBUG:
17450Sstevel@tonic-gate 			*i1 = icmp->icmp_debug;
17460Sstevel@tonic-gate 			break;
17470Sstevel@tonic-gate 		case SO_TYPE:
17480Sstevel@tonic-gate 			*i1 = SOCK_RAW;
17490Sstevel@tonic-gate 			break;
17500Sstevel@tonic-gate 		case SO_PROTOTYPE:
17510Sstevel@tonic-gate 			*i1 = icmp->icmp_proto;
17520Sstevel@tonic-gate 			break;
17530Sstevel@tonic-gate 		case SO_REUSEADDR:
17540Sstevel@tonic-gate 			*i1 = icmp->icmp_reuseaddr;
17550Sstevel@tonic-gate 			break;
17560Sstevel@tonic-gate 
17570Sstevel@tonic-gate 		/*
17580Sstevel@tonic-gate 		 * The following three items are available here,
17590Sstevel@tonic-gate 		 * but are only meaningful to IP.
17600Sstevel@tonic-gate 		 */
17610Sstevel@tonic-gate 		case SO_DONTROUTE:
17620Sstevel@tonic-gate 			*i1 = icmp->icmp_dontroute;
17630Sstevel@tonic-gate 			break;
17640Sstevel@tonic-gate 		case SO_USELOOPBACK:
17650Sstevel@tonic-gate 			*i1 = icmp->icmp_useloopback;
17660Sstevel@tonic-gate 			break;
17670Sstevel@tonic-gate 		case SO_BROADCAST:
17680Sstevel@tonic-gate 			*i1 = icmp->icmp_broadcast;
17690Sstevel@tonic-gate 			break;
17700Sstevel@tonic-gate 
17710Sstevel@tonic-gate 		case SO_SNDBUF:
17728348SEric.Yu@Sun.COM 			ASSERT(icmp->icmp_xmit_hiwat <= INT_MAX);
17738348SEric.Yu@Sun.COM 			*i1 = icmp->icmp_xmit_hiwat;
17740Sstevel@tonic-gate 			break;
17750Sstevel@tonic-gate 		case SO_RCVBUF:
17768348SEric.Yu@Sun.COM 			ASSERT(icmp->icmp_recv_hiwat <= INT_MAX);
17778348SEric.Yu@Sun.COM 			*i1 = icmp->icmp_recv_hiwat;
17780Sstevel@tonic-gate 			break;
17790Sstevel@tonic-gate 		case SO_DGRAM_ERRIND:
17800Sstevel@tonic-gate 			*i1 = icmp->icmp_dgram_errind;
17810Sstevel@tonic-gate 			break;
17821673Sgt145670 		case SO_TIMESTAMP:
17831673Sgt145670 			*i1 = icmp->icmp_timestamp;
17841673Sgt145670 			break;
17851676Sjpk 		case SO_MAC_EXEMPT:
17866596Skp158701 			*i1 = connp->conn_mac_exempt;
17871676Sjpk 			break;
17883388Skcpoon 		case SO_DOMAIN:
17893388Skcpoon 			*i1 = icmp->icmp_family;
17903388Skcpoon 			break;
17913388Skcpoon 
17920Sstevel@tonic-gate 		/*
17932263Ssommerfe 		 * Following four not meaningful for icmp
17940Sstevel@tonic-gate 		 * Action is same as "default" to which we fallthrough
17950Sstevel@tonic-gate 		 * so we keep them in comments.
17960Sstevel@tonic-gate 		 * case SO_LINGER:
17970Sstevel@tonic-gate 		 * case SO_KEEPALIVE:
17980Sstevel@tonic-gate 		 * case SO_OOBINLINE:
17992263Ssommerfe 		 * case SO_ALLZONES:
18000Sstevel@tonic-gate 		 */
18010Sstevel@tonic-gate 		default:
18028348SEric.Yu@Sun.COM 			ret = -1;
18038348SEric.Yu@Sun.COM 			goto done;
18040Sstevel@tonic-gate 		}
18050Sstevel@tonic-gate 		break;
18060Sstevel@tonic-gate 	case IPPROTO_IP:
18070Sstevel@tonic-gate 		/*
18080Sstevel@tonic-gate 		 * Only allow IPv4 option processing on IPv4 sockets.
18090Sstevel@tonic-gate 		 */
18108348SEric.Yu@Sun.COM 		if (icmp->icmp_family != AF_INET) {
18118348SEric.Yu@Sun.COM 			ret = -1;
18128348SEric.Yu@Sun.COM 			goto done;
18138348SEric.Yu@Sun.COM 		}
18140Sstevel@tonic-gate 
18150Sstevel@tonic-gate 		switch (name) {
18160Sstevel@tonic-gate 		case IP_OPTIONS:
18170Sstevel@tonic-gate 		case T_IP_OPTIONS:
18180Sstevel@tonic-gate 			/* Options are passed up with each packet */
18198348SEric.Yu@Sun.COM 			ret = 0;
18208348SEric.Yu@Sun.COM 			goto done;
18210Sstevel@tonic-gate 		case IP_HDRINCL:
18220Sstevel@tonic-gate 			*i1 = (int)icmp->icmp_hdrincl;
18230Sstevel@tonic-gate 			break;
18240Sstevel@tonic-gate 		case IP_TOS:
18250Sstevel@tonic-gate 		case T_IP_TOS:
18260Sstevel@tonic-gate 			*i1 = (int)icmp->icmp_type_of_service;
18270Sstevel@tonic-gate 			break;
18280Sstevel@tonic-gate 		case IP_TTL:
18290Sstevel@tonic-gate 			*i1 = (int)icmp->icmp_ttl;
18300Sstevel@tonic-gate 			break;
18310Sstevel@tonic-gate 		case IP_MULTICAST_IF:
18320Sstevel@tonic-gate 			/* 0 address if not set */
18330Sstevel@tonic-gate 			*(ipaddr_t *)ptr = icmp->icmp_multicast_if_addr;
18348348SEric.Yu@Sun.COM 			ret = sizeof (ipaddr_t);
18358348SEric.Yu@Sun.COM 			goto done;
18360Sstevel@tonic-gate 		case IP_MULTICAST_TTL:
18370Sstevel@tonic-gate 			*(uchar_t *)ptr = icmp->icmp_multicast_ttl;
18388348SEric.Yu@Sun.COM 			ret = sizeof (uchar_t);
18398348SEric.Yu@Sun.COM 			goto done;
18400Sstevel@tonic-gate 		case IP_MULTICAST_LOOP:
18415240Snordmark 			*ptr = connp->conn_multicast_loop;
18428348SEric.Yu@Sun.COM 			ret = sizeof (uint8_t);
18438348SEric.Yu@Sun.COM 			goto done;
18440Sstevel@tonic-gate 		case IP_BOUND_IF:
18450Sstevel@tonic-gate 			/* Zero if not set */
18460Sstevel@tonic-gate 			*i1 = icmp->icmp_bound_if;
18470Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
18480Sstevel@tonic-gate 		case IP_UNSPEC_SRC:
18490Sstevel@tonic-gate 			*ptr = icmp->icmp_unspec_source;
18500Sstevel@tonic-gate 			break;	/* goto sizeof (int) option return */
18518348SEric.Yu@Sun.COM 		case IP_RECVIF:
18528348SEric.Yu@Sun.COM 			*ptr = icmp->icmp_recvif;
18538348SEric.Yu@Sun.COM 			break;	/* goto sizeof (int) option return */
18545455Smeem 		case IP_BROADCAST_TTL:
18555455Smeem 			*(uchar_t *)ptr = connp->conn_broadcast_ttl;
18565455Smeem 			return (sizeof (uchar_t));
18573318Srshoaib 		case IP_RECVPKTINFO:
18583318Srshoaib 			/*
18593318Srshoaib 			 * This also handles IP_PKTINFO.
18603318Srshoaib 			 * IP_PKTINFO and IP_RECVPKTINFO have the same value.
18613318Srshoaib 			 * Differentiation is based on the size of the argument
18623318Srshoaib 			 * passed in.
18633318Srshoaib 			 * This option is handled in IP which will return an
18643318Srshoaib 			 * error for IP_PKTINFO as it's not supported as a
18653318Srshoaib 			 * sticky option.
18663318Srshoaib 			 */
18678348SEric.Yu@Sun.COM 			ret = -EINVAL;
18688348SEric.Yu@Sun.COM 			goto done;
18690Sstevel@tonic-gate 		/*
18700Sstevel@tonic-gate 		 * Cannot "get" the value of following options
18710Sstevel@tonic-gate 		 * at this level. Action is same as "default" to
18720Sstevel@tonic-gate 		 * which we fallthrough so we keep them in comments.
18730Sstevel@tonic-gate 		 *
18740Sstevel@tonic-gate 		 * case IP_ADD_MEMBERSHIP:
18750Sstevel@tonic-gate 		 * case IP_DROP_MEMBERSHIP:
18760Sstevel@tonic-gate 		 * case IP_BLOCK_SOURCE:
18770Sstevel@tonic-gate 		 * case IP_UNBLOCK_SOURCE:
18780Sstevel@tonic-gate 		 * case IP_ADD_SOURCE_MEMBERSHIP:
18790Sstevel@tonic-gate 		 * case IP_DROP_SOURCE_MEMBERSHIP:
18800Sstevel@tonic-gate 		 * case MCAST_JOIN_GROUP:
18810Sstevel@tonic-gate 		 * case MCAST_LEAVE_GROUP:
18820Sstevel@tonic-gate 		 * case MCAST_BLOCK_SOURCE:
18830Sstevel@tonic-gate 		 * case MCAST_UNBLOCK_SOURCE:
18840Sstevel@tonic-gate 		 * case MCAST_JOIN_SOURCE_GROUP:
18850Sstevel@tonic-gate 		 * case MCAST_LEAVE_SOURCE_GROUP:
18860Sstevel@tonic-gate 		 * case MRT_INIT:
18870Sstevel@tonic-gate 		 * case MRT_DONE:
18880Sstevel@tonic-gate 		 * case MRT_ADD_VIF:
18890Sstevel@tonic-gate 		 * case MRT_DEL_VIF:
18900Sstevel@tonic-gate 		 * case MRT_ADD_MFC:
18910Sstevel@tonic-gate 		 * case MRT_DEL_MFC:
18920Sstevel@tonic-gate 		 * case MRT_VERSION:
18930Sstevel@tonic-gate 		 * case MRT_ASSERT:
18940Sstevel@tonic-gate 		 * case IP_SEC_OPT:
18950Sstevel@tonic-gate 		 * case IP_DONTFAILOVER_IF:
18961663Spriyanka 		 * case IP_NEXTHOP:
18970Sstevel@tonic-gate 		 */
18980Sstevel@tonic-gate 		default:
18998348SEric.Yu@Sun.COM 			ret = -1;
19008348SEric.Yu@Sun.COM 			goto done;
19010Sstevel@tonic-gate 		}
19020Sstevel@tonic-gate 		break;
19030Sstevel@tonic-gate 	case IPPROTO_IPV6:
19040Sstevel@tonic-gate 		/*
19050Sstevel@tonic-gate 		 * Only allow IPv6 option processing on native IPv6 sockets.
19060Sstevel@tonic-gate 		 */
19078348SEric.Yu@Sun.COM 		if (icmp->icmp_family != AF_INET6) {
19088348SEric.Yu@Sun.COM 			ret = -1;
19098348SEric.Yu@Sun.COM 			goto done;
19108348SEric.Yu@Sun.COM 		}
19110Sstevel@tonic-gate 		switch (name) {
19120Sstevel@tonic-gate 		case IPV6_UNICAST_HOPS:
19130Sstevel@tonic-gate 			*i1 = (unsigned int)icmp->icmp_ttl;
19140Sstevel@tonic-gate 			break;
19150Sstevel@tonic-gate 		case IPV6_MULTICAST_IF:
19160Sstevel@tonic-gate 			/* 0 index if not set */
19170Sstevel@tonic-gate 			*i1 = icmp->icmp_multicast_if_index;
19180Sstevel@tonic-gate 			break;
19190Sstevel@tonic-gate 		case IPV6_MULTICAST_HOPS:
19200Sstevel@tonic-gate 			*i1 = icmp->icmp_multicast_ttl;
19210Sstevel@tonic-gate 			break;
19220Sstevel@tonic-gate 		case IPV6_MULTICAST_LOOP:
19235240Snordmark 			*i1 = connp->conn_multicast_loop;
19240Sstevel@tonic-gate 			break;
19250Sstevel@tonic-gate 		case IPV6_BOUND_IF:
19260Sstevel@tonic-gate 			/* Zero if not set */
19270Sstevel@tonic-gate 			*i1 = icmp->icmp_bound_if;
19280Sstevel@tonic-gate 			break;
19290Sstevel@tonic-gate 		case IPV6_UNSPEC_SRC:
19300Sstevel@tonic-gate 			*i1 = icmp->icmp_unspec_source;
19310Sstevel@tonic-gate 			break;
19320Sstevel@tonic-gate 		case IPV6_CHECKSUM:
19330Sstevel@tonic-gate 			/*
19340Sstevel@tonic-gate 			 * Return offset or -1 if no checksum offset.
19350Sstevel@tonic-gate 			 * Does not apply to IPPROTO_ICMPV6
19360Sstevel@tonic-gate 			 */
19378348SEric.Yu@Sun.COM 			if (icmp->icmp_proto == IPPROTO_ICMPV6) {
19388348SEric.Yu@Sun.COM 				ret = -1;
19398348SEric.Yu@Sun.COM 				goto done;
19408348SEric.Yu@Sun.COM 			}
19410Sstevel@tonic-gate 
19420Sstevel@tonic-gate 			if (icmp->icmp_raw_checksum) {
19430Sstevel@tonic-gate 				*i1 = icmp->icmp_checksum_off;
19440Sstevel@tonic-gate 			} else {
19450Sstevel@tonic-gate 				*i1 = -1;
19460Sstevel@tonic-gate 			}
19470Sstevel@tonic-gate 			break;
19480Sstevel@tonic-gate 		case IPV6_JOIN_GROUP:
19490Sstevel@tonic-gate 		case IPV6_LEAVE_GROUP:
19500Sstevel@tonic-gate 		case MCAST_JOIN_GROUP:
19510Sstevel@tonic-gate 		case MCAST_LEAVE_GROUP:
19520Sstevel@tonic-gate 		case MCAST_BLOCK_SOURCE:
19530Sstevel@tonic-gate 		case MCAST_UNBLOCK_SOURCE:
19540Sstevel@tonic-gate 		case MCAST_JOIN_SOURCE_GROUP:
19550Sstevel@tonic-gate 		case MCAST_LEAVE_SOURCE_GROUP:
19560Sstevel@tonic-gate 			/* cannot "get" the value for these */
19578348SEric.Yu@Sun.COM 			ret = -1;
19588348SEric.Yu@Sun.COM 			goto done;
19590Sstevel@tonic-gate 		case IPV6_RECVPKTINFO:
19603318Srshoaib 			*i1 = icmp->icmp_ip_recvpktinfo;
19610Sstevel@tonic-gate 			break;
19620Sstevel@tonic-gate 		case IPV6_RECVTCLASS:
19630Sstevel@tonic-gate 			*i1 = icmp->icmp_ipv6_recvtclass;
19640Sstevel@tonic-gate 			break;
19650Sstevel@tonic-gate 		case IPV6_RECVPATHMTU:
19660Sstevel@tonic-gate 			*i1 = icmp->icmp_ipv6_recvpathmtu;
19670Sstevel@tonic-gate 			break;
19680Sstevel@tonic-gate 		case IPV6_V6ONLY:
19690Sstevel@tonic-gate 			*i1 = 1;
19700Sstevel@tonic-gate 			break;
19710Sstevel@tonic-gate 		case IPV6_RECVHOPLIMIT:
19720Sstevel@tonic-gate 			*i1 = icmp->icmp_ipv6_recvhoplimit;
19730Sstevel@tonic-gate 			break;
19740Sstevel@tonic-gate 		case IPV6_RECVHOPOPTS:
19750Sstevel@tonic-gate 			*i1 = icmp->icmp_ipv6_recvhopopts;
19760Sstevel@tonic-gate 			break;
19770Sstevel@tonic-gate 		case IPV6_RECVDSTOPTS:
19780Sstevel@tonic-gate 			*i1 = icmp->icmp_ipv6_recvdstopts;
19790Sstevel@tonic-gate 			break;
19800Sstevel@tonic-gate 		case _OLD_IPV6_RECVDSTOPTS:
19810Sstevel@tonic-gate 			*i1 = icmp->icmp_old_ipv6_recvdstopts;
19820Sstevel@tonic-gate 			break;
19830Sstevel@tonic-gate 		case IPV6_RECVRTHDRDSTOPTS:
19840Sstevel@tonic-gate 			*i1 = icmp->icmp_ipv6_recvrtdstopts;
19850Sstevel@tonic-gate 			break;
19860Sstevel@tonic-gate 		case IPV6_RECVRTHDR:
19870Sstevel@tonic-gate 			*i1 = icmp->icmp_ipv6_recvrthdr;
19880Sstevel@tonic-gate 			break;
19890Sstevel@tonic-gate 		case IPV6_PKTINFO: {
19900Sstevel@tonic-gate 			/* XXX assumes that caller has room for max size! */
19910Sstevel@tonic-gate 			struct in6_pktinfo *pkti;
19920Sstevel@tonic-gate 
19930Sstevel@tonic-gate 			pkti = (struct in6_pktinfo *)ptr;
19940Sstevel@tonic-gate 			if (ipp->ipp_fields & IPPF_IFINDEX)
19950Sstevel@tonic-gate 				pkti->ipi6_ifindex = ipp->ipp_ifindex;
19960Sstevel@tonic-gate 			else
19970Sstevel@tonic-gate 				pkti->ipi6_ifindex = 0;
19980Sstevel@tonic-gate 			if (ipp->ipp_fields & IPPF_ADDR)
19990Sstevel@tonic-gate 				pkti->ipi6_addr = ipp->ipp_addr;
20000Sstevel@tonic-gate 			else
20010Sstevel@tonic-gate 				pkti->ipi6_addr = ipv6_all_zeros;
20028348SEric.Yu@Sun.COM 			ret = sizeof (struct in6_pktinfo);
20038348SEric.Yu@Sun.COM 			goto done;
20040Sstevel@tonic-gate 		}
20050Sstevel@tonic-gate 		case IPV6_NEXTHOP: {
20060Sstevel@tonic-gate 			sin6_t *sin6 = (sin6_t *)ptr;
20070Sstevel@tonic-gate 
20080Sstevel@tonic-gate 			if (!(ipp->ipp_fields & IPPF_NEXTHOP))
20090Sstevel@tonic-gate 				return (0);
20100Sstevel@tonic-gate 			*sin6 = sin6_null;
20110Sstevel@tonic-gate 			sin6->sin6_family = AF_INET6;
20120Sstevel@tonic-gate 			sin6->sin6_addr = ipp->ipp_nexthop;
20138348SEric.Yu@Sun.COM 			ret = (sizeof (sin6_t));
20148348SEric.Yu@Sun.COM 			goto done;
20150Sstevel@tonic-gate 		}
20160Sstevel@tonic-gate 		case IPV6_HOPOPTS:
20170Sstevel@tonic-gate 			if (!(ipp->ipp_fields & IPPF_HOPOPTS))
20180Sstevel@tonic-gate 				return (0);
20191676Sjpk 			if (ipp->ipp_hopoptslen <= icmp->icmp_label_len_v6)
20201676Sjpk 				return (0);
20211676Sjpk 			bcopy((char *)ipp->ipp_hopopts +
20221676Sjpk 			    icmp->icmp_label_len_v6, ptr,
20231676Sjpk 			    ipp->ipp_hopoptslen - icmp->icmp_label_len_v6);
20241676Sjpk 			if (icmp->icmp_label_len_v6 > 0) {
20251676Sjpk 				ptr[0] = ((char *)ipp->ipp_hopopts)[0];
20261676Sjpk 				ptr[1] = (ipp->ipp_hopoptslen -
20271676Sjpk 				    icmp->icmp_label_len_v6 + 7) / 8 - 1;
20281676Sjpk 			}
20298348SEric.Yu@Sun.COM 			ret = (ipp->ipp_hopoptslen - icmp->icmp_label_len_v6);
20308348SEric.Yu@Sun.COM 			goto done;
20310Sstevel@tonic-gate 		case IPV6_RTHDRDSTOPTS:
20320Sstevel@tonic-gate 			if (!(ipp->ipp_fields & IPPF_RTDSTOPTS))
20330Sstevel@tonic-gate 				return (0);
20340Sstevel@tonic-gate 			bcopy(ipp->ipp_rtdstopts, ptr, ipp->ipp_rtdstoptslen);
20358348SEric.Yu@Sun.COM 			ret = ipp->ipp_rtdstoptslen;
20368348SEric.Yu@Sun.COM 			goto done;
20370Sstevel@tonic-gate 		case IPV6_RTHDR:
20380Sstevel@tonic-gate 			if (!(ipp->ipp_fields & IPPF_RTHDR))
20390Sstevel@tonic-gate 				return (0);
20400Sstevel@tonic-gate 			bcopy(ipp->ipp_rthdr, ptr, ipp->ipp_rthdrlen);
20418348SEric.Yu@Sun.COM 			ret = ipp->ipp_rthdrlen;
20428348SEric.Yu@Sun.COM 			goto done;
20430Sstevel@tonic-gate 		case IPV6_DSTOPTS:
20448348SEric.Yu@Sun.COM 			if (!(ipp->ipp_fields & IPPF_DSTOPTS)) {
20458348SEric.Yu@Sun.COM 				ret = 0;
20468348SEric.Yu@Sun.COM 				goto done;
20478348SEric.Yu@Sun.COM 			}
20480Sstevel@tonic-gate 			bcopy(ipp->ipp_dstopts, ptr, ipp->ipp_dstoptslen);
20498348SEric.Yu@Sun.COM 			ret = ipp->ipp_dstoptslen;
20508348SEric.Yu@Sun.COM 			goto done;
20510Sstevel@tonic-gate 		case IPV6_PATHMTU:
20528348SEric.Yu@Sun.COM 			if (!(ipp->ipp_fields & IPPF_PATHMTU)) {
20538348SEric.Yu@Sun.COM 				ret = 0;
20548348SEric.Yu@Sun.COM 			} else {
20558348SEric.Yu@Sun.COM 				ret = ip_fill_mtuinfo(
20568348SEric.Yu@Sun.COM 				    &icmp->icmp_v6dst.sin6_addr, 0,
20578348SEric.Yu@Sun.COM 				    (struct ip6_mtuinfo *)ptr,
20588348SEric.Yu@Sun.COM 				    is->is_netstack);
20598348SEric.Yu@Sun.COM 			}
20608348SEric.Yu@Sun.COM 			goto done;
20610Sstevel@tonic-gate 		case IPV6_TCLASS:
20620Sstevel@tonic-gate 			if (ipp->ipp_fields & IPPF_TCLASS)
20630Sstevel@tonic-gate 				*i1 = ipp->ipp_tclass;
20640Sstevel@tonic-gate 			else
20650Sstevel@tonic-gate 				*i1 = IPV6_FLOW_TCLASS(
20660Sstevel@tonic-gate 				    IPV6_DEFAULT_VERS_AND_FLOW);
20670Sstevel@tonic-gate 			break;
20680Sstevel@tonic-gate 		default:
20698348SEric.Yu@Sun.COM 			ret = -1;
20708348SEric.Yu@Sun.COM 			goto done;
20710Sstevel@tonic-gate 		}
20720Sstevel@tonic-gate 		break;
20730Sstevel@tonic-gate 	case IPPROTO_ICMPV6:
20740Sstevel@tonic-gate 		/*
20750Sstevel@tonic-gate 		 * Only allow IPv6 option processing on native IPv6 sockets.
20760Sstevel@tonic-gate 		 */
20778348SEric.Yu@Sun.COM 		if (icmp->icmp_family != AF_INET6) {
20788348SEric.Yu@Sun.COM 			ret = -1;
20798348SEric.Yu@Sun.COM 		}
20808348SEric.Yu@Sun.COM 
20818348SEric.Yu@Sun.COM 		if (icmp->icmp_proto != IPPROTO_ICMPV6) {
20828348SEric.Yu@Sun.COM 			ret = -1;
20838348SEric.Yu@Sun.COM 		}
20840Sstevel@tonic-gate 
20850Sstevel@tonic-gate 		switch (name) {
20860Sstevel@tonic-gate 		case ICMP6_FILTER:
20870Sstevel@tonic-gate 			if (icmp->icmp_filter == NULL) {
20880Sstevel@tonic-gate 				/* Make it look like "pass all" */
20890Sstevel@tonic-gate 				ICMP6_FILTER_SETPASSALL((icmp6_filter_t *)ptr);
20900Sstevel@tonic-gate 			} else {
20910Sstevel@tonic-gate 				(void) bcopy(icmp->icmp_filter, ptr,
20920Sstevel@tonic-gate 				    sizeof (icmp6_filter_t));
20930Sstevel@tonic-gate 			}
20948348SEric.Yu@Sun.COM 			ret = sizeof (icmp6_filter_t);
20958348SEric.Yu@Sun.COM 			goto done;
20960Sstevel@tonic-gate 		default:
20978348SEric.Yu@Sun.COM 			ret = -1;
20988348SEric.Yu@Sun.COM 			goto done;
20990Sstevel@tonic-gate 		}
21000Sstevel@tonic-gate 	default:
21018348SEric.Yu@Sun.COM 		ret = -1;
21028348SEric.Yu@Sun.COM 		goto done;
21038348SEric.Yu@Sun.COM 	}
21048348SEric.Yu@Sun.COM 	ret = sizeof (int);
21058348SEric.Yu@Sun.COM done:
21068348SEric.Yu@Sun.COM 	return (ret);
21070Sstevel@tonic-gate }
21080Sstevel@tonic-gate 
21095240Snordmark /*
21105240Snordmark  * This routine retrieves the current status of socket options.
21115240Snordmark  * It returns the size of the option retrieved.
21125240Snordmark  */
21135240Snordmark int
21148348SEric.Yu@Sun.COM icmp_tpi_opt_get(queue_t *q, int level, int name, uchar_t *ptr)
21155240Snordmark {
21168348SEric.Yu@Sun.COM 	conn_t  *connp = Q_TO_CONN(q);
21178348SEric.Yu@Sun.COM 	icmp_t	*icmp = connp->conn_icmp;
21185240Snordmark 	int 	err;
21195240Snordmark 
21205240Snordmark 	rw_enter(&icmp->icmp_rwlock, RW_READER);
21218348SEric.Yu@Sun.COM 	err = icmp_opt_get(connp, level, name, ptr);
21225240Snordmark 	rw_exit(&icmp->icmp_rwlock);
21235240Snordmark 	return (err);
21245240Snordmark }
21255240Snordmark 
21260Sstevel@tonic-gate int
21278348SEric.Yu@Sun.COM icmp_do_opt_set(conn_t *connp, int level, int name, uint_t inlen,
21288348SEric.Yu@Sun.COM     uchar_t *invalp, uint_t *outlenp, uchar_t *outvalp, cred_t *cr,
21298348SEric.Yu@Sun.COM     void *thisdg_attrs, boolean_t checkonly)
21300Sstevel@tonic-gate {
21318348SEric.Yu@Sun.COM 
21320Sstevel@tonic-gate 	int	*i1 = (int *)invalp;
21330Sstevel@tonic-gate 	boolean_t onoff = (*i1 == 0) ? 0 : 1;
21348348SEric.Yu@Sun.COM 	icmp_t *icmp = connp->conn_icmp;
21358348SEric.Yu@Sun.COM 	icmp_stack_t *is = icmp->icmp_is;
21360Sstevel@tonic-gate 	int	error;
21370Sstevel@tonic-gate 
21388348SEric.Yu@Sun.COM 	ASSERT(RW_WRITE_HELD(&icmp->icmp_rwlock));
21390Sstevel@tonic-gate 	/*
21400Sstevel@tonic-gate 	 * For fixed length options, no sanity check
21410Sstevel@tonic-gate 	 * of passed in length is done. It is assumed *_optcom_req()
21420Sstevel@tonic-gate 	 * routines do the right thing.
21430Sstevel@tonic-gate 	 */
21440Sstevel@tonic-gate 	switch (level) {
21450Sstevel@tonic-gate 	case SOL_SOCKET:
21460Sstevel@tonic-gate 		switch (name) {
21470Sstevel@tonic-gate 		case SO_DEBUG:
21480Sstevel@tonic-gate 			if (!checkonly)
21490Sstevel@tonic-gate 				icmp->icmp_debug = onoff;
21500Sstevel@tonic-gate 			break;
21510Sstevel@tonic-gate 		case SO_PROTOTYPE:
21520Sstevel@tonic-gate 			if ((*i1 & 0xFF) != IPPROTO_ICMP &&
21530Sstevel@tonic-gate 			    (*i1 & 0xFF) != IPPROTO_ICMPV6 &&
21540Sstevel@tonic-gate 			    secpolicy_net_rawaccess(cr) != 0) {
21550Sstevel@tonic-gate 				*outlenp = 0;
21560Sstevel@tonic-gate 				return (EACCES);
21570Sstevel@tonic-gate 			}
21580Sstevel@tonic-gate 			/* Can't use IPPROTO_RAW with IPv6 */
21590Sstevel@tonic-gate 			if ((*i1 & 0xFF) == IPPROTO_RAW &&
21600Sstevel@tonic-gate 			    icmp->icmp_family == AF_INET6) {
21610Sstevel@tonic-gate 				*outlenp = 0;
21620Sstevel@tonic-gate 				return (EPROTONOSUPPORT);
21630Sstevel@tonic-gate 			}
21640Sstevel@tonic-gate 			if (checkonly) {
21650Sstevel@tonic-gate 				/* T_CHECK case */
21660Sstevel@tonic-gate 				*(int *)outvalp = (*i1 & 0xFF);
21670Sstevel@tonic-gate 				break;
21680Sstevel@tonic-gate 			}
21690Sstevel@tonic-gate 			icmp->icmp_proto = *i1 & 0xFF;
21700Sstevel@tonic-gate 			if ((icmp->icmp_proto == IPPROTO_RAW ||
21710Sstevel@tonic-gate 			    icmp->icmp_proto == IPPROTO_IGMP) &&
21720Sstevel@tonic-gate 			    icmp->icmp_family == AF_INET)
21730Sstevel@tonic-gate 				icmp->icmp_hdrincl = 1;
21740Sstevel@tonic-gate 			else
21750Sstevel@tonic-gate 				icmp->icmp_hdrincl = 0;
21760Sstevel@tonic-gate 
21770Sstevel@tonic-gate 			if (icmp->icmp_family == AF_INET6 &&
21780Sstevel@tonic-gate 			    icmp->icmp_proto == IPPROTO_ICMPV6) {
21790Sstevel@tonic-gate 				/* Set offset for icmp6_cksum */
21800Sstevel@tonic-gate 				icmp->icmp_raw_checksum = 0;
21810Sstevel@tonic-gate 				icmp->icmp_checksum_off = 2;
21820Sstevel@tonic-gate 			}
21830Sstevel@tonic-gate 			if (icmp->icmp_proto == IPPROTO_UDP ||
21840Sstevel@tonic-gate 			    icmp->icmp_proto == IPPROTO_TCP ||
21850Sstevel@tonic-gate 			    icmp->icmp_proto == IPPROTO_SCTP) {
21860Sstevel@tonic-gate 				icmp->icmp_no_tp_cksum = 1;
21870Sstevel@tonic-gate 				icmp->icmp_sticky_ipp.ipp_fields |=
21880Sstevel@tonic-gate 				    IPPF_NO_CKSUM;
21890Sstevel@tonic-gate 			} else {
21900Sstevel@tonic-gate 				icmp->icmp_no_tp_cksum = 0;
21910Sstevel@tonic-gate 				icmp->icmp_sticky_ipp.ipp_fields &=
21920Sstevel@tonic-gate 				    ~IPPF_NO_CKSUM;
21930Sstevel@tonic-gate 			}
21940Sstevel@tonic-gate 
21950Sstevel@tonic-gate 			if (icmp->icmp_filter != NULL &&
21960Sstevel@tonic-gate 			    icmp->icmp_proto != IPPROTO_ICMPV6) {
21970Sstevel@tonic-gate 				kmem_free(icmp->icmp_filter,
21980Sstevel@tonic-gate 				    sizeof (icmp6_filter_t));
21990Sstevel@tonic-gate 				icmp->icmp_filter = NULL;
22000Sstevel@tonic-gate 			}
22010Sstevel@tonic-gate 
22020Sstevel@tonic-gate 			/* Rebuild the header template */
22035240Snordmark 			error = icmp_build_hdrs(icmp);
22040Sstevel@tonic-gate 			if (error != 0) {
22050Sstevel@tonic-gate 				*outlenp = 0;
22060Sstevel@tonic-gate 				return (error);
22070Sstevel@tonic-gate 			}
22080Sstevel@tonic-gate 
2209409Skcpoon 			/*
2210409Skcpoon 			 * For SCTP, we don't use icmp_bind_proto() for
2211409Skcpoon 			 * raw socket binding.  Note that we do not need
2212409Skcpoon 			 * to set *outlenp.
22135240Snordmark 			 * FIXME: how does SCTP work?
2214409Skcpoon 			 */
2215409Skcpoon 			if (icmp->icmp_proto == IPPROTO_SCTP)
2216409Skcpoon 				return (0);
2217409Skcpoon 
22180Sstevel@tonic-gate 			*outlenp = sizeof (int);
22190Sstevel@tonic-gate 			*(int *)outvalp = *i1 & 0xFF;
22205240Snordmark 
22215240Snordmark 			/* Drop lock across the bind operation */
22225240Snordmark 			rw_exit(&icmp->icmp_rwlock);
22238348SEric.Yu@Sun.COM 			(void) icmp_bind_proto(connp);
22245240Snordmark 			rw_enter(&icmp->icmp_rwlock, RW_WRITER);
22250Sstevel@tonic-gate 			return (0);
22260Sstevel@tonic-gate 		case SO_REUSEADDR:
22278348SEric.Yu@Sun.COM 			if (!checkonly) {
22280Sstevel@tonic-gate 				icmp->icmp_reuseaddr = onoff;
22298348SEric.Yu@Sun.COM 				PASS_OPT_TO_IP(connp);
22308348SEric.Yu@Sun.COM 			}
22310Sstevel@tonic-gate 			break;
22320Sstevel@tonic-gate 
22330Sstevel@tonic-gate 		/*
22340Sstevel@tonic-gate 		 * The following three items are available here,
22350Sstevel@tonic-gate 		 * but are only meaningful to IP.
22360Sstevel@tonic-gate 		 */
22370Sstevel@tonic-gate 		case SO_DONTROUTE:
22388348SEric.Yu@Sun.COM 			if (!checkonly) {
22390Sstevel@tonic-gate 				icmp->icmp_dontroute = onoff;
22408348SEric.Yu@Sun.COM 				PASS_OPT_TO_IP(connp);
22418348SEric.Yu@Sun.COM 			}
22420Sstevel@tonic-gate 			break;
22430Sstevel@tonic-gate 		case SO_USELOOPBACK:
22448348SEric.Yu@Sun.COM 			if (!checkonly) {
22450Sstevel@tonic-gate 				icmp->icmp_useloopback = onoff;
22468348SEric.Yu@Sun.COM 				PASS_OPT_TO_IP(connp);
22478348SEric.Yu@Sun.COM 			}
22480Sstevel@tonic-gate 			break;
22490Sstevel@tonic-gate 		case SO_BROADCAST:
22508348SEric.Yu@Sun.COM 			if (!checkonly) {
22510Sstevel@tonic-gate 				icmp->icmp_broadcast = onoff;
22528348SEric.Yu@Sun.COM 				PASS_OPT_TO_IP(connp);
22538348SEric.Yu@Sun.COM 			}
22540Sstevel@tonic-gate 			break;
22550Sstevel@tonic-gate 
22560Sstevel@tonic-gate 		case SO_SNDBUF:
22573448Sdh155122 			if (*i1 > is->is_max_buf) {
22580Sstevel@tonic-gate 				*outlenp = 0;
22590Sstevel@tonic-gate 				return (ENOBUFS);
22600Sstevel@tonic-gate 			}
22610Sstevel@tonic-gate 			if (!checkonly) {
22628348SEric.Yu@Sun.COM 				if (!IPCL_IS_NONSTR(connp)) {
22638348SEric.Yu@Sun.COM 					connp->conn_wq->q_hiwat = *i1;
22648348SEric.Yu@Sun.COM 				}
22658348SEric.Yu@Sun.COM 				icmp->icmp_xmit_hiwat = *i1;
22660Sstevel@tonic-gate 			}
22670Sstevel@tonic-gate 			break;
22680Sstevel@tonic-gate 		case SO_RCVBUF:
22693448Sdh155122 			if (*i1 > is->is_max_buf) {
22700Sstevel@tonic-gate 				*outlenp = 0;
22710Sstevel@tonic-gate 				return (ENOBUFS);
22720Sstevel@tonic-gate 			}
22730Sstevel@tonic-gate 			if (!checkonly) {
22748348SEric.Yu@Sun.COM 				icmp->icmp_recv_hiwat = *i1;
22755240Snordmark 				rw_exit(&icmp->icmp_rwlock);
22768348SEric.Yu@Sun.COM 				(void) proto_set_rx_hiwat(connp->conn_rq, connp,
22778348SEric.Yu@Sun.COM 				    *i1);
22785240Snordmark 				rw_enter(&icmp->icmp_rwlock, RW_WRITER);
22790Sstevel@tonic-gate 			}
22800Sstevel@tonic-gate 			break;
22810Sstevel@tonic-gate 		case SO_DGRAM_ERRIND:
22820Sstevel@tonic-gate 			if (!checkonly)
22830Sstevel@tonic-gate 				icmp->icmp_dgram_errind = onoff;
22840Sstevel@tonic-gate 			break;
22852263Ssommerfe 		case SO_ALLZONES:
22862263Ssommerfe 			/*
22872263Ssommerfe 			 * "soft" error (negative)
22882263Ssommerfe 			 * option not handled at this level
22892263Ssommerfe 			 * Note: Do not modify *outlenp
22902263Ssommerfe 			 */
22912263Ssommerfe 			return (-EINVAL);
22921673Sgt145670 		case SO_TIMESTAMP:
22931673Sgt145670 			if (!checkonly) {
22941673Sgt145670 				icmp->icmp_timestamp = onoff;
22951673Sgt145670 			}
22961673Sgt145670 			break;
22971676Sjpk 		case SO_MAC_EXEMPT:
22986596Skp158701 			/*
22996596Skp158701 			 * "soft" error (negative)
23006596Skp158701 			 * option not handled at this level
23016596Skp158701 			 * Note: Do not modify *outlenp
23026596Skp158701 			 */
23036596Skp158701 			return (-EINVAL);
23040Sstevel@tonic-gate 		/*
23050Sstevel@tonic-gate 		 * Following three not meaningful for icmp
23060Sstevel@tonic-gate 		 * Action is same as "default" so we keep them
23070Sstevel@tonic-gate 		 * in comments.
23080Sstevel@tonic-gate 		 * case SO_LINGER:
23090Sstevel@tonic-gate 		 * case SO_KEEPALIVE:
23100Sstevel@tonic-gate 		 * case SO_OOBINLINE:
23110Sstevel@tonic-gate 		 */
23120Sstevel@tonic-gate 		default:
23130Sstevel@tonic-gate 			*outlenp = 0;
23140Sstevel@tonic-gate 			return (EINVAL);
23150Sstevel@tonic-gate 		}
23160Sstevel@tonic-gate 		break;
23170Sstevel@tonic-gate 	case IPPROTO_IP:
23180Sstevel@tonic-gate 		/*
23190Sstevel@tonic-gate 		 * Only allow IPv4 option processing on IPv4 sockets.
23200Sstevel@tonic-gate 		 */
23210Sstevel@tonic-gate 		if (icmp->icmp_family != AF_INET) {
23220Sstevel@tonic-gate 			*outlenp = 0;
23230Sstevel@tonic-gate 			return (ENOPROTOOPT);
23240Sstevel@tonic-gate 		}
23250Sstevel@tonic-gate 		switch (name) {
23260Sstevel@tonic-gate 		case IP_OPTIONS:
23270Sstevel@tonic-gate 		case T_IP_OPTIONS:
23280Sstevel@tonic-gate 			/* Save options for use by IP. */
23291676Sjpk 			if ((inlen & 0x3) ||
23301676Sjpk 			    inlen + icmp->icmp_label_len > IP_MAX_OPT_LENGTH) {
23310Sstevel@tonic-gate 				*outlenp = 0;
23320Sstevel@tonic-gate 				return (EINVAL);
23330Sstevel@tonic-gate 			}
23340Sstevel@tonic-gate 			if (checkonly)
23350Sstevel@tonic-gate 				break;
23360Sstevel@tonic-gate 
23371676Sjpk 			if (!tsol_option_set(&icmp->icmp_ip_snd_options,
23381676Sjpk 			    &icmp->icmp_ip_snd_options_len,
23391676Sjpk 			    icmp->icmp_label_len, invalp, inlen)) {
23401676Sjpk 				*outlenp = 0;
23411676Sjpk 				return (ENOMEM);
23420Sstevel@tonic-gate 			}
23431676Sjpk 
23440Sstevel@tonic-gate 			icmp->icmp_max_hdr_len = IP_SIMPLE_HDR_LENGTH +
23450Sstevel@tonic-gate 			    icmp->icmp_ip_snd_options_len;
23465240Snordmark 			rw_exit(&icmp->icmp_rwlock);
23478348SEric.Yu@Sun.COM 			(void) proto_set_tx_wroff(connp->conn_rq == NULL ? NULL:
23488348SEric.Yu@Sun.COM 			    RD(connp->conn_rq), connp,
23498348SEric.Yu@Sun.COM 			    icmp->icmp_max_hdr_len + is->is_wroff_extra);
23505240Snordmark 			rw_enter(&icmp->icmp_rwlock, RW_WRITER);
23510Sstevel@tonic-gate 			break;
23520Sstevel@tonic-gate 		case IP_HDRINCL:
23530Sstevel@tonic-gate 			if (!checkonly)
23540Sstevel@tonic-gate 				icmp->icmp_hdrincl = onoff;
23550Sstevel@tonic-gate 			break;
23560Sstevel@tonic-gate 		case IP_TOS:
23570Sstevel@tonic-gate 		case T_IP_TOS:
23580Sstevel@tonic-gate 			if (!checkonly) {
23590Sstevel@tonic-gate 				icmp->icmp_type_of_service = (uint8_t)*i1;
23600Sstevel@tonic-gate 			}
23610Sstevel@tonic-gate 			break;
23620Sstevel@tonic-gate 		case IP_TTL:
23630Sstevel@tonic-gate 			if (!checkonly) {
23640Sstevel@tonic-gate 				icmp->icmp_ttl = (uint8_t)*i1;
23650Sstevel@tonic-gate 			}
23660Sstevel@tonic-gate 			break;
23670Sstevel@tonic-gate 		case IP_MULTICAST_IF:
23680Sstevel@tonic-gate 			/*
23690Sstevel@tonic-gate 			 * TODO should check OPTMGMT reply and undo this if
23700Sstevel@tonic-gate 			 * there is an error.
23710Sstevel@tonic-gate 			 */
23728348SEric.Yu@Sun.COM 			if (!checkonly) {
23730Sstevel@tonic-gate 				icmp->icmp_multicast_if_addr = *i1;
23748348SEric.Yu@Sun.COM 				PASS_OPT_TO_IP(connp);
23758348SEric.Yu@Sun.COM 			}
23760Sstevel@tonic-gate 			break;
23770Sstevel@tonic-gate 		case IP_MULTICAST_TTL:
23780Sstevel@tonic-gate 			if (!checkonly)
23790Sstevel@tonic-gate 				icmp->icmp_multicast_ttl = *invalp;
23800Sstevel@tonic-gate 			break;
23810Sstevel@tonic-gate 		case IP_MULTICAST_LOOP:
23820Sstevel@tonic-gate 			if (!checkonly) {
23835240Snordmark 				connp->conn_multicast_loop =
23840Sstevel@tonic-gate 				    (*invalp == 0) ? 0 : 1;
23858348SEric.Yu@Sun.COM 				PASS_OPT_TO_IP(connp);
23860Sstevel@tonic-gate 			}
23870Sstevel@tonic-gate 			break;
23880Sstevel@tonic-gate 		case IP_BOUND_IF:
23898348SEric.Yu@Sun.COM 			if (!checkonly) {
23900Sstevel@tonic-gate 				icmp->icmp_bound_if = *i1;
23918348SEric.Yu@Sun.COM 				PASS_OPT_TO_IP(connp);
23928348SEric.Yu@Sun.COM 			}
23930Sstevel@tonic-gate 			break;
23940Sstevel@tonic-gate 		case IP_UNSPEC_SRC:
23958348SEric.Yu@Sun.COM 			if (!checkonly) {
23960Sstevel@tonic-gate 				icmp->icmp_unspec_source = onoff;
23978348SEric.Yu@Sun.COM 				PASS_OPT_TO_IP(connp);
23988348SEric.Yu@Sun.COM 			}
23990Sstevel@tonic-gate 			break;
24005455Smeem 		case IP_BROADCAST_TTL:
24015455Smeem 			if (!checkonly)
24025455Smeem 				connp->conn_broadcast_ttl = *invalp;
24035455Smeem 			break;
24040Sstevel@tonic-gate 		case IP_RECVIF:
24058348SEric.Yu@Sun.COM 			if (!checkonly) {
24060Sstevel@tonic-gate 				icmp->icmp_recvif = onoff;
24078348SEric.Yu@Sun.COM 			}
24085240Snordmark 			/*
24095240Snordmark 			 * pass to ip
24105240Snordmark 			 */
24115240Snordmark 			return (-EINVAL);
24123318Srshoaib 		case IP_PKTINFO: {
24133318Srshoaib 			/*
24143318Srshoaib 			 * This also handles IP_RECVPKTINFO.
24153318Srshoaib 			 * IP_PKTINFO and IP_RECVPKTINFO have the same value.
24163318Srshoaib 			 * Differentiation is based on the size of the argument
24173318Srshoaib 			 * passed in.
24183318Srshoaib 			 */
24193318Srshoaib 			struct in_pktinfo *pktinfop;
24203318Srshoaib 			ip4_pkt_t *attr_pktinfop;
24213318Srshoaib 
24223318Srshoaib 			if (checkonly)
24233318Srshoaib 				break;
24243318Srshoaib 
24253318Srshoaib 			if (inlen == sizeof (int)) {
24263318Srshoaib 				/*
24273318Srshoaib 				 * This is IP_RECVPKTINFO option.
24283318Srshoaib 				 * Keep a local copy of wether this option is
24293318Srshoaib 				 * set or not and pass it down to IP for
24303318Srshoaib 				 * processing.
24313318Srshoaib 				 */
24323318Srshoaib 				icmp->icmp_ip_recvpktinfo = onoff;
24333318Srshoaib 				return (-EINVAL);
24343318Srshoaib 			}
24353318Srshoaib 
24363318Srshoaib 
24378348SEric.Yu@Sun.COM 			if (inlen != sizeof (struct in_pktinfo)) {
24383318Srshoaib 				return (EINVAL);
24398348SEric.Yu@Sun.COM 			}
24403318Srshoaib 
24413318Srshoaib 			if ((attr_pktinfop = (ip4_pkt_t *)thisdg_attrs)
24423318Srshoaib 			    == NULL) {
24433318Srshoaib 				/*
24443318Srshoaib 				 * sticky option is not supported
24453318Srshoaib 				 */
24463318Srshoaib 				return (EINVAL);
24473318Srshoaib 			}
24483318Srshoaib 
24493318Srshoaib 			pktinfop = (struct in_pktinfo *)invalp;
24503318Srshoaib 
24513318Srshoaib 			/*
24523318Srshoaib 			 * Atleast one of the values should be specified
24533318Srshoaib 			 */
24543318Srshoaib 			if (pktinfop->ipi_ifindex == 0 &&
24553318Srshoaib 			    pktinfop->ipi_spec_dst.s_addr == INADDR_ANY) {
24563318Srshoaib 				return (EINVAL);
24573318Srshoaib 			}
24583318Srshoaib 
24593318Srshoaib 			attr_pktinfop->ip4_addr = pktinfop->ipi_spec_dst.s_addr;
24603318Srshoaib 			attr_pktinfop->ip4_ill_index = pktinfop->ipi_ifindex;
24613318Srshoaib 		}
24623318Srshoaib 			break;
24630Sstevel@tonic-gate 		case IP_ADD_MEMBERSHIP:
24640Sstevel@tonic-gate 		case IP_DROP_MEMBERSHIP:
24650Sstevel@tonic-gate 		case IP_BLOCK_SOURCE:
24660Sstevel@tonic-gate 		case IP_UNBLOCK_SOURCE:
24670Sstevel@tonic-gate 		case IP_ADD_SOURCE_MEMBERSHIP:
24680Sstevel@tonic-gate 		case IP_DROP_SOURCE_MEMBERSHIP:
24690Sstevel@tonic-gate 		case MCAST_JOIN_GROUP:
24700Sstevel@tonic-gate 		case MCAST_LEAVE_GROUP:
24710Sstevel@tonic-gate 		case MCAST_BLOCK_SOURCE:
24720Sstevel@tonic-gate 		case MCAST_UNBLOCK_SOURCE:
24730Sstevel@tonic-gate 		case MCAST_JOIN_SOURCE_GROUP:
24740Sstevel@tonic-gate 		case MCAST_LEAVE_SOURCE_GROUP:
24750Sstevel@tonic-gate 		case MRT_INIT:
24760Sstevel@tonic-gate 		case MRT_DONE:
24770Sstevel@tonic-gate 		case MRT_ADD_VIF:
24780Sstevel@tonic-gate 		case MRT_DEL_VIF:
24790Sstevel@tonic-gate 		case MRT_ADD_MFC:
24800Sstevel@tonic-gate 		case MRT_DEL_MFC:
24810Sstevel@tonic-gate 		case MRT_VERSION:
24820Sstevel@tonic-gate 		case MRT_ASSERT:
24830Sstevel@tonic-gate 		case IP_SEC_OPT:
24840Sstevel@tonic-gate 		case IP_DONTFAILOVER_IF:
24851663Spriyanka 		case IP_NEXTHOP:
24860Sstevel@tonic-gate 			/*
24870Sstevel@tonic-gate 			 * "soft" error (negative)
24880Sstevel@tonic-gate 			 * option not handled at this level
24890Sstevel@tonic-gate 			 * Note: Do not modify *outlenp
24900Sstevel@tonic-gate 			 */
24910Sstevel@tonic-gate 			return (-EINVAL);
24920Sstevel@tonic-gate 		default:
24930Sstevel@tonic-gate 			*outlenp = 0;
24940Sstevel@tonic-gate 			return (EINVAL);
24950Sstevel@tonic-gate 		}
24960Sstevel@tonic-gate 		break;
24970Sstevel@tonic-gate 	case IPPROTO_IPV6: {
24980Sstevel@tonic-gate 		ip6_pkt_t		*ipp;
24990Sstevel@tonic-gate 		boolean_t		sticky;
25000Sstevel@tonic-gate 
25010Sstevel@tonic-gate 		if (icmp->icmp_family != AF_INET6) {
25020Sstevel@tonic-gate 			*outlenp = 0;
25030Sstevel@tonic-gate 			return (ENOPROTOOPT);
25040Sstevel@tonic-gate 		}
25050Sstevel@tonic-gate 		/*
25060Sstevel@tonic-gate 		 * Deal with both sticky options and ancillary data
25070Sstevel@tonic-gate 		 */
25080Sstevel@tonic-gate 		if (thisdg_attrs == NULL) {
25090Sstevel@tonic-gate 			/* sticky options, or none */
25100Sstevel@tonic-gate 			ipp = &icmp->icmp_sticky_ipp;
25110Sstevel@tonic-gate 			sticky = B_TRUE;
25120Sstevel@tonic-gate 		} else {
25130Sstevel@tonic-gate 			/* ancillary data */
25140Sstevel@tonic-gate 			ipp = (ip6_pkt_t *)thisdg_attrs;
25150Sstevel@tonic-gate 			sticky = B_FALSE;
25160Sstevel@tonic-gate 		}
25170Sstevel@tonic-gate 
25180Sstevel@tonic-gate 		switch (name) {
25190Sstevel@tonic-gate 		case IPV6_MULTICAST_IF:
25208348SEric.Yu@Sun.COM 			if (!checkonly) {
25210Sstevel@tonic-gate 				icmp->icmp_multicast_if_index = *i1;
25228348SEric.Yu@Sun.COM 				PASS_OPT_TO_IP(connp);
25238348SEric.Yu@Sun.COM 			}
25240Sstevel@tonic-gate 			break;
25250Sstevel@tonic-gate 		case IPV6_UNICAST_HOPS:
25260Sstevel@tonic-gate 			/* -1 means use default */
25270Sstevel@tonic-gate 			if (*i1 < -1 || *i1 > IPV6_MAX_HOPS) {
25280Sstevel@tonic-gate 				*outlenp = 0;
25290Sstevel@tonic-gate 				return (EINVAL);
25300Sstevel@tonic-gate 			}
25310Sstevel@tonic-gate 			if (!checkonly) {
25320Sstevel@tonic-gate 				if (*i1 == -1) {
2533679Sseb 					icmp->icmp_ttl = ipp->ipp_unicast_hops =
25343448Sdh155122 					    is->is_ipv6_hoplimit;
2535679Sseb 					ipp->ipp_fields &= ~IPPF_UNICAST_HOPS;
25360Sstevel@tonic-gate 					/* Pass modified value to IP. */
25370Sstevel@tonic-gate 					*i1 = ipp->ipp_hoplimit;
25380Sstevel@tonic-gate 				} else {
2539679Sseb 					icmp->icmp_ttl = ipp->ipp_unicast_hops =
25400Sstevel@tonic-gate 					    (uint8_t)*i1;
2541679Sseb 					ipp->ipp_fields |= IPPF_UNICAST_HOPS;
25420Sstevel@tonic-gate 				}
25430Sstevel@tonic-gate 				/* Rebuild the header template */
25445240Snordmark 				error = icmp_build_hdrs(icmp);
25450Sstevel@tonic-gate 				if (error != 0) {
25460Sstevel@tonic-gate 					*outlenp = 0;
25470Sstevel@tonic-gate 					return (error);
25480Sstevel@tonic-gate 				}
25490Sstevel@tonic-gate 			}
25500Sstevel@tonic-gate 			break;
25510Sstevel@tonic-gate 		case IPV6_MULTICAST_HOPS:
25520Sstevel@tonic-gate 			/* -1 means use default */
25530Sstevel@tonic-gate 			if (*i1 < -1 || *i1 > IPV6_MAX_HOPS) {
25540Sstevel@tonic-gate 				*outlenp = 0;
25550Sstevel@tonic-gate 				return (EINVAL);
25560Sstevel@tonic-gate 			}
25570Sstevel@tonic-gate 			if (!checkonly) {
25580Sstevel@tonic-gate 				if (*i1 == -1) {
25590Sstevel@tonic-gate 					icmp->icmp_multicast_ttl =
2560679Sseb 					    ipp->ipp_multicast_hops =
25610Sstevel@tonic-gate 					    IP_DEFAULT_MULTICAST_TTL;
2562679Sseb 					ipp->ipp_fields &= ~IPPF_MULTICAST_HOPS;
25630Sstevel@tonic-gate 					/* Pass modified value to IP. */
2564679Sseb 					*i1 = icmp->icmp_multicast_ttl;
25650Sstevel@tonic-gate 				} else {
25660Sstevel@tonic-gate 					icmp->icmp_multicast_ttl =
2567679Sseb 					    ipp->ipp_multicast_hops =
25680Sstevel@tonic-gate 					    (uint8_t)*i1;
2569679Sseb 					ipp->ipp_fields |= IPPF_MULTICAST_HOPS;
25700Sstevel@tonic-gate 				}
25710Sstevel@tonic-gate 			}
25720Sstevel@tonic-gate 			break;
25730Sstevel@tonic-gate 		case IPV6_MULTICAST_LOOP:
25740Sstevel@tonic-gate 			if (*i1 != 0 && *i1 != 1) {
25750Sstevel@tonic-gate 				*outlenp = 0;
25760Sstevel@tonic-gate 				return (EINVAL);
25770Sstevel@tonic-gate 			}
25788348SEric.Yu@Sun.COM 			if (!checkonly) {
25795240Snordmark 				connp->conn_multicast_loop = *i1;
25808348SEric.Yu@Sun.COM 				PASS_OPT_TO_IP(connp);
25818348SEric.Yu@Sun.COM 			}
25820Sstevel@tonic-gate 			break;
25830Sstevel@tonic-gate 		case IPV6_CHECKSUM:
25840Sstevel@tonic-gate 			/*
25850Sstevel@tonic-gate 			 * Integer offset into the user data of where the
25860Sstevel@tonic-gate 			 * checksum is located.
25870Sstevel@tonic-gate 			 * Offset of -1 disables option.
25880Sstevel@tonic-gate 			 * Does not apply to IPPROTO_ICMPV6.
25890Sstevel@tonic-gate 			 */
25900Sstevel@tonic-gate 			if (icmp->icmp_proto == IPPROTO_ICMPV6 || !sticky) {
25910Sstevel@tonic-gate 				*outlenp = 0;
25920Sstevel@tonic-gate 				return (EINVAL);
25930Sstevel@tonic-gate 			}
25940Sstevel@tonic-gate 			if ((*i1 != -1) && ((*i1 < 0) || (*i1 & 0x1) != 0)) {
25950Sstevel@tonic-gate 				/* Negative or not 16 bit aligned offset */
25960Sstevel@tonic-gate 				*outlenp = 0;
25970Sstevel@tonic-gate 				return (EINVAL);
25980Sstevel@tonic-gate 			}
25990Sstevel@tonic-gate 			if (checkonly)
26000Sstevel@tonic-gate 				break;
26010Sstevel@tonic-gate 
26020Sstevel@tonic-gate 			if (*i1 == -1) {
26030Sstevel@tonic-gate 				icmp->icmp_raw_checksum = 0;
26040Sstevel@tonic-gate 				ipp->ipp_fields &= ~IPPF_RAW_CKSUM;
26050Sstevel@tonic-gate 			} else {
26060Sstevel@tonic-gate 				icmp->icmp_raw_checksum = 1;
26070Sstevel@tonic-gate 				icmp->icmp_checksum_off = *i1;
26080Sstevel@tonic-gate 				ipp->ipp_fields |= IPPF_RAW_CKSUM;
26090Sstevel@tonic-gate 			}
26100Sstevel@tonic-gate 			/* Rebuild the header template */
26115240Snordmark 			error = icmp_build_hdrs(icmp);
26120Sstevel@tonic-gate 			if (error != 0) {
26130Sstevel@tonic-gate 				*outlenp = 0;
26140Sstevel@tonic-gate 				return (error);
26150Sstevel@tonic-gate 			}
26160Sstevel@tonic-gate 			break;
26170Sstevel@tonic-gate 		case IPV6_JOIN_GROUP:
26180Sstevel@tonic-gate 		case IPV6_LEAVE_GROUP:
26190Sstevel@tonic-gate 		case MCAST_JOIN_GROUP:
26200Sstevel@tonic-gate 		case MCAST_LEAVE_GROUP:
26210Sstevel@tonic-gate 		case MCAST_BLOCK_SOURCE:
26220Sstevel@tonic-gate 		case MCAST_UNBLOCK_SOURCE:
26230Sstevel@tonic-gate 		case MCAST_JOIN_SOURCE_GROUP:
26240Sstevel@tonic-gate 		case MCAST_LEAVE_SOURCE_GROUP:
26250Sstevel@tonic-gate 			/*
26260Sstevel@tonic-gate 			 * "soft" error (negative)
26270Sstevel@tonic-gate 			 * option not handled at this level
26280Sstevel@tonic-gate 			 * Note: Do not modify *outlenp
26290Sstevel@tonic-gate 			 */
26300Sstevel@tonic-gate 			return (-EINVAL);
26310Sstevel@tonic-gate 		case IPV6_BOUND_IF:
26328348SEric.Yu@Sun.COM 			if (!checkonly) {
26330Sstevel@tonic-gate 				icmp->icmp_bound_if = *i1;
26348348SEric.Yu@Sun.COM 				PASS_OPT_TO_IP(connp);
26358348SEric.Yu@Sun.COM 			}
26360Sstevel@tonic-gate 			break;
26370Sstevel@tonic-gate 		case IPV6_UNSPEC_SRC:
26388348SEric.Yu@Sun.COM 			if (!checkonly) {
26390Sstevel@tonic-gate 				icmp->icmp_unspec_source = onoff;
26408348SEric.Yu@Sun.COM 				PASS_OPT_TO_IP(connp);
26418348SEric.Yu@Sun.COM 			}
26420Sstevel@tonic-gate 			break;
26430Sstevel@tonic-gate 		case IPV6_RECVTCLASS:
26448348SEric.Yu@Sun.COM 			if (!checkonly) {
26450Sstevel@tonic-gate 				icmp->icmp_ipv6_recvtclass = onoff;
26468348SEric.Yu@Sun.COM 				PASS_OPT_TO_IP(connp);
26478348SEric.Yu@Sun.COM 			}
26480Sstevel@tonic-gate 			break;
26490Sstevel@tonic-gate 		/*
26500Sstevel@tonic-gate 		 * Set boolean switches for ancillary data delivery
26510Sstevel@tonic-gate 		 */
26520Sstevel@tonic-gate 		case IPV6_RECVPKTINFO:
26538348SEric.Yu@Sun.COM 			if (!checkonly) {
26543318Srshoaib 				icmp->icmp_ip_recvpktinfo = onoff;
26558348SEric.Yu@Sun.COM 				PASS_OPT_TO_IP(connp);
26568348SEric.Yu@Sun.COM 			}
26570Sstevel@tonic-gate 			break;
26580Sstevel@tonic-gate 		case IPV6_RECVPATHMTU:
26598348SEric.Yu@Sun.COM 			if (!checkonly) {
26600Sstevel@tonic-gate 				icmp->icmp_ipv6_recvpathmtu = onoff;
26618348SEric.Yu@Sun.COM 				PASS_OPT_TO_IP(connp);
26628348SEric.Yu@Sun.COM 			}
26630Sstevel@tonic-gate 			break;
26640Sstevel@tonic-gate 		case IPV6_RECVHOPLIMIT:
26658348SEric.Yu@Sun.COM 			if (!checkonly) {
26660Sstevel@tonic-gate 				icmp->icmp_ipv6_recvhoplimit = onoff;
26678348SEric.Yu@Sun.COM 				PASS_OPT_TO_IP(connp);
26688348SEric.Yu@Sun.COM 			}
26690Sstevel@tonic-gate 			break;
26700Sstevel@tonic-gate 		case IPV6_RECVHOPOPTS:
26718348SEric.Yu@Sun.COM 			if (!checkonly) {
26720Sstevel@tonic-gate 				icmp->icmp_ipv6_recvhopopts = onoff;
26738348SEric.Yu@Sun.COM 				PASS_OPT_TO_IP(connp);
26748348SEric.Yu@Sun.COM 			}
26750Sstevel@tonic-gate 			break;
26760Sstevel@tonic-gate 		case IPV6_RECVDSTOPTS:
26778348SEric.Yu@Sun.COM 			if (!checkonly) {
26780Sstevel@tonic-gate 				icmp->icmp_ipv6_recvdstopts = onoff;
26798348SEric.Yu@Sun.COM 				PASS_OPT_TO_IP(connp);
26808348SEric.Yu@Sun.COM 			}
26810Sstevel@tonic-gate 			break;
26820Sstevel@tonic-gate 		case _OLD_IPV6_RECVDSTOPTS:
26830Sstevel@tonic-gate 			if (!checkonly)
26840Sstevel@tonic-gate 				icmp->icmp_old_ipv6_recvdstopts = onoff;
26850Sstevel@tonic-gate 			break;
26860Sstevel@tonic-gate 		case IPV6_RECVRTHDRDSTOPTS:
26878348SEric.Yu@Sun.COM 			if (!checkonly) {
26880Sstevel@tonic-gate 				icmp->icmp_ipv6_recvrtdstopts = onoff;
26898348SEric.Yu@Sun.COM 				PASS_OPT_TO_IP(connp);
26908348SEric.Yu@Sun.COM 			}
26910Sstevel@tonic-gate 			break;
26920Sstevel@tonic-gate 		case IPV6_RECVRTHDR:
26938348SEric.Yu@Sun.COM 			if (!checkonly) {
26940Sstevel@tonic-gate 				icmp->icmp_ipv6_recvrthdr = onoff;
26958348SEric.Yu@Sun.COM 				PASS_OPT_TO_IP(connp);
26968348SEric.Yu@Sun.COM 			}
26970Sstevel@tonic-gate 			break;
26980Sstevel@tonic-gate 		/*
26990Sstevel@tonic-gate 		 * Set sticky options or ancillary data.
27000Sstevel@tonic-gate 		 * If sticky options, (re)build any extension headers
27010Sstevel@tonic-gate 		 * that might be needed as a result.
27020Sstevel@tonic-gate 		 */
27030Sstevel@tonic-gate 		case IPV6_PKTINFO:
27040Sstevel@tonic-gate 			/*
27050Sstevel@tonic-gate 			 * The source address and ifindex are verified
27060Sstevel@tonic-gate 			 * in ip_opt_set(). For ancillary data the
27070Sstevel@tonic-gate 			 * source address is checked in ip_wput_v6.
27080Sstevel@tonic-gate 			 */
27098348SEric.Yu@Sun.COM 			if (inlen != 0 && inlen !=
27108348SEric.Yu@Sun.COM 			    sizeof (struct in6_pktinfo)) {
27110Sstevel@tonic-gate 				return (EINVAL);
27128348SEric.Yu@Sun.COM 			}
27130Sstevel@tonic-gate 			if (checkonly)
27140Sstevel@tonic-gate 				break;
27150Sstevel@tonic-gate 
27160Sstevel@tonic-gate 			if (inlen == 0) {
27170Sstevel@tonic-gate 				ipp->ipp_fields &= ~(IPPF_IFINDEX|IPPF_ADDR);
27180Sstevel@tonic-gate 				ipp->ipp_sticky_ignored |=
27190Sstevel@tonic-gate 				    (IPPF_IFINDEX|IPPF_ADDR);
27200Sstevel@tonic-gate 			} else {
27210Sstevel@tonic-gate 				struct in6_pktinfo *pkti;
27220Sstevel@tonic-gate 
27230Sstevel@tonic-gate 				pkti = (struct in6_pktinfo *)invalp;
27240Sstevel@tonic-gate 				ipp->ipp_ifindex = pkti->ipi6_ifindex;
27250Sstevel@tonic-gate 				ipp->ipp_addr = pkti->ipi6_addr;
27260Sstevel@tonic-gate 				if (ipp->ipp_ifindex != 0)
27270Sstevel@tonic-gate 					ipp->ipp_fields |= IPPF_IFINDEX;
27280Sstevel@tonic-gate 				else
27290Sstevel@tonic-gate 					ipp->ipp_fields &= ~IPPF_IFINDEX;
27300Sstevel@tonic-gate 				if (!IN6_IS_ADDR_UNSPECIFIED(
27310Sstevel@tonic-gate 				    &ipp->ipp_addr))
27320Sstevel@tonic-gate 					ipp->ipp_fields |= IPPF_ADDR;
27330Sstevel@tonic-gate 				else
27340Sstevel@tonic-gate 					ipp->ipp_fields &= ~IPPF_ADDR;
27350Sstevel@tonic-gate 			}
27360Sstevel@tonic-gate 			if (sticky) {
27375240Snordmark 				error = icmp_build_hdrs(icmp);
27380Sstevel@tonic-gate 				if (error != 0)
27390Sstevel@tonic-gate 					return (error);
27408348SEric.Yu@Sun.COM 				PASS_OPT_TO_IP(connp);
27410Sstevel@tonic-gate 			}
27420Sstevel@tonic-gate 			break;
27430Sstevel@tonic-gate 		case IPV6_HOPLIMIT:
2744679Sseb 			/* This option can only be used as ancillary data. */
2745679Sseb 			if (sticky)
2746679Sseb 				return (EINVAL);
27470Sstevel@tonic-gate 			if (inlen != 0 && inlen != sizeof (int))
27480Sstevel@tonic-gate 				return (EINVAL);
27490Sstevel@tonic-gate 			if (checkonly)
27500Sstevel@tonic-gate 				break;
27510Sstevel@tonic-gate 
27520Sstevel@tonic-gate 			if (inlen == 0) {
27530Sstevel@tonic-gate 				ipp->ipp_fields &= ~IPPF_HOPLIMIT;
27540Sstevel@tonic-gate 				ipp->ipp_sticky_ignored |= IPPF_HOPLIMIT;
27550Sstevel@tonic-gate 			} else {
27560Sstevel@tonic-gate 				if (*i1 > 255 || *i1 < -1)
27570Sstevel@tonic-gate 					return (EINVAL);
27580Sstevel@tonic-gate 				if (*i1 == -1)
27593448Sdh155122 					ipp->ipp_hoplimit =
27603448Sdh155122 					    is->is_ipv6_hoplimit;
27610Sstevel@tonic-gate 				else
27620Sstevel@tonic-gate 					ipp->ipp_hoplimit = *i1;
27630Sstevel@tonic-gate 				ipp->ipp_fields |= IPPF_HOPLIMIT;
27640Sstevel@tonic-gate 			}
27650Sstevel@tonic-gate 			break;
27660Sstevel@tonic-gate 		case IPV6_TCLASS:
27670Sstevel@tonic-gate 			/*
27680Sstevel@tonic-gate 			 * IPV6_RECVTCLASS accepts -1 as use kernel default
27690Sstevel@tonic-gate 			 * and [0, 255] as the actualy traffic class.
27700Sstevel@tonic-gate 			 */
27718348SEric.Yu@Sun.COM 			if (inlen != 0 && inlen != sizeof (int)) {
27720Sstevel@tonic-gate 				return (EINVAL);
27738348SEric.Yu@Sun.COM 			}
27740Sstevel@tonic-gate 			if (checkonly)
27750Sstevel@tonic-gate 				break;
27760Sstevel@tonic-gate 
27770Sstevel@tonic-gate 			if (inlen == 0) {
27780Sstevel@tonic-gate 				ipp->ipp_fields &= ~IPPF_TCLASS;
27790Sstevel@tonic-gate 				ipp->ipp_sticky_ignored |= IPPF_TCLASS;
27800Sstevel@tonic-gate 			} else {
27810Sstevel@tonic-gate 				if (*i1 >= 256 || *i1 < -1)
27820Sstevel@tonic-gate 					return (EINVAL);
27830Sstevel@tonic-gate 				if (*i1 == -1) {
27840Sstevel@tonic-gate 					ipp->ipp_tclass =
27850Sstevel@tonic-gate 					    IPV6_FLOW_TCLASS(
27860Sstevel@tonic-gate 					    IPV6_DEFAULT_VERS_AND_FLOW);
27870Sstevel@tonic-gate 				} else {
27880Sstevel@tonic-gate 					ipp->ipp_tclass = *i1;
27890Sstevel@tonic-gate 				}
27900Sstevel@tonic-gate 				ipp->ipp_fields |= IPPF_TCLASS;
27910Sstevel@tonic-gate 			}
27920Sstevel@tonic-gate 			if (sticky) {
27935240Snordmark 				error = icmp_build_hdrs(icmp);
27940Sstevel@tonic-gate 				if (error != 0)
27950Sstevel@tonic-gate 					return (error);
27960Sstevel@tonic-gate 			}
27970Sstevel@tonic-gate 			break;
27980Sstevel@tonic-gate 		case IPV6_NEXTHOP:
27990Sstevel@tonic-gate 			/*
28000Sstevel@tonic-gate 			 * IP will verify that the nexthop is reachable
28010Sstevel@tonic-gate 			 * and fail for sticky options.
28020Sstevel@tonic-gate 			 */
28038348SEric.Yu@Sun.COM 			if (inlen != 0 && inlen != sizeof (sin6_t)) {
28040Sstevel@tonic-gate 				return (EINVAL);
28058348SEric.Yu@Sun.COM 			}
28060Sstevel@tonic-gate 			if (checkonly)
28070Sstevel@tonic-gate 				break;
28080Sstevel@tonic-gate 
28090Sstevel@tonic-gate 			if (inlen == 0) {
28100Sstevel@tonic-gate 				ipp->ipp_fields &= ~IPPF_NEXTHOP;
28110Sstevel@tonic-gate 				ipp->ipp_sticky_ignored |= IPPF_NEXTHOP;
28120Sstevel@tonic-gate 			} else {
28130Sstevel@tonic-gate 				sin6_t *sin6 = (sin6_t *)invalp;
28140Sstevel@tonic-gate 
28158348SEric.Yu@Sun.COM 				if (sin6->sin6_family != AF_INET6) {
28160Sstevel@tonic-gate 					return (EAFNOSUPPORT);
28178348SEric.Yu@Sun.COM 				}
28188348SEric.Yu@Sun.COM 				if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
28190Sstevel@tonic-gate 					return (EADDRNOTAVAIL);
28208348SEric.Yu@Sun.COM 				}
28210Sstevel@tonic-gate 				ipp->ipp_nexthop = sin6->sin6_addr;
28220Sstevel@tonic-gate 				if (!IN6_IS_ADDR_UNSPECIFIED(
28230Sstevel@tonic-gate 				    &ipp->ipp_nexthop))
28240Sstevel@tonic-gate 					ipp->ipp_fields |= IPPF_NEXTHOP;
28250Sstevel@tonic-gate 				else
28260Sstevel@tonic-gate 					ipp->ipp_fields &= ~IPPF_NEXTHOP;
28270Sstevel@tonic-gate 			}
28280Sstevel@tonic-gate 			if (sticky) {
28295240Snordmark 				error = icmp_build_hdrs(icmp);
28300Sstevel@tonic-gate 				if (error != 0)
28310Sstevel@tonic-gate 					return (error);
28328348SEric.Yu@Sun.COM 				PASS_OPT_TO_IP(connp);
28330Sstevel@tonic-gate 			}
28340Sstevel@tonic-gate 			break;
28350Sstevel@tonic-gate 		case IPV6_HOPOPTS: {
28360Sstevel@tonic-gate 			ip6_hbh_t *hopts = (ip6_hbh_t *)invalp;
28370Sstevel@tonic-gate 			/*
28380Sstevel@tonic-gate 			 * Sanity checks - minimum size, size a multiple of
28390Sstevel@tonic-gate 			 * eight bytes, and matching size passed in.
28400Sstevel@tonic-gate 			 */
28410Sstevel@tonic-gate 			if (inlen != 0 &&
28428348SEric.Yu@Sun.COM 			    inlen != (8 * (hopts->ip6h_len + 1))) {
28430Sstevel@tonic-gate 				return (EINVAL);
28448348SEric.Yu@Sun.COM 			}
28450Sstevel@tonic-gate 
28460Sstevel@tonic-gate 			if (checkonly)
28470Sstevel@tonic-gate 				break;
28481676Sjpk 			error = optcom_pkt_set(invalp, inlen, sticky,
28491676Sjpk 			    (uchar_t **)&ipp->ipp_hopopts,
28501676Sjpk 			    &ipp->ipp_hopoptslen,
28511676Sjpk 			    sticky ? icmp->icmp_label_len_v6 : 0);
28521676Sjpk 			if (error != 0)
28531676Sjpk 				return (error);
28541676Sjpk 			if (ipp->ipp_hopoptslen == 0) {
28550Sstevel@tonic-gate 				ipp->ipp_fields &= ~IPPF_HOPOPTS;
28560Sstevel@tonic-gate 				ipp->ipp_sticky_ignored |= IPPF_HOPOPTS;
28570Sstevel@tonic-gate 			} else {
28580Sstevel@tonic-gate 				ipp->ipp_fields |= IPPF_HOPOPTS;
28590Sstevel@tonic-gate 			}
28600Sstevel@tonic-gate 			if (sticky) {
28615240Snordmark 				error = icmp_build_hdrs(icmp);
28620Sstevel@tonic-gate 				if (error != 0)
28630Sstevel@tonic-gate 					return (error);
28640Sstevel@tonic-gate 			}
28650Sstevel@tonic-gate 			break;
28660Sstevel@tonic-gate 		}
28670Sstevel@tonic-gate 		case IPV6_RTHDRDSTOPTS: {
28680Sstevel@tonic-gate 			ip6_dest_t *dopts = (ip6_dest_t *)invalp;
28690Sstevel@tonic-gate 
28700Sstevel@tonic-gate 			/*
28710Sstevel@tonic-gate 			 * Sanity checks - minimum size, size a multiple of
28720Sstevel@tonic-gate 			 * eight bytes, and matching size passed in.
28730Sstevel@tonic-gate 			 */
28740Sstevel@tonic-gate 			if (inlen != 0 &&
28750Sstevel@tonic-gate 			    inlen != (8 * (dopts->ip6d_len + 1)))
28760Sstevel@tonic-gate 				return (EINVAL);
28770Sstevel@tonic-gate 
28780Sstevel@tonic-gate 			if (checkonly)
28790Sstevel@tonic-gate 				break;
28800Sstevel@tonic-gate 
28810Sstevel@tonic-gate 			if (inlen == 0) {
28820Sstevel@tonic-gate 				if (sticky &&
28830Sstevel@tonic-gate 				    (ipp->ipp_fields & IPPF_RTDSTOPTS) != 0) {
28840Sstevel@tonic-gate 					kmem_free(ipp->ipp_rtdstopts,
28850Sstevel@tonic-gate 					    ipp->ipp_rtdstoptslen);
28860Sstevel@tonic-gate 					ipp->ipp_rtdstopts = NULL;
28870Sstevel@tonic-gate 					ipp->ipp_rtdstoptslen = 0;
28880Sstevel@tonic-gate 				}
28890Sstevel@tonic-gate 				ipp->ipp_fields &= ~IPPF_RTDSTOPTS;
28900Sstevel@tonic-gate 				ipp->ipp_sticky_ignored |= IPPF_RTDSTOPTS;
28910Sstevel@tonic-gate 			} else {
28921676Sjpk 				error = optcom_pkt_set(invalp, inlen, sticky,
28930Sstevel@tonic-gate 				    (uchar_t **)&ipp->ipp_rtdstopts,
28941676Sjpk 				    &ipp->ipp_rtdstoptslen, 0);
28950Sstevel@tonic-gate 				if (error != 0)
28960Sstevel@tonic-gate 					return (error);
28970Sstevel@tonic-gate 				ipp->ipp_fields |= IPPF_RTDSTOPTS;
28980Sstevel@tonic-gate 			}
28990Sstevel@tonic-gate 			if (sticky) {
29005240Snordmark 				error = icmp_build_hdrs(icmp);
29010Sstevel@tonic-gate 				if (error != 0)
29020Sstevel@tonic-gate 					return (error);
29030Sstevel@tonic-gate 			}
29040Sstevel@tonic-gate 			break;
29050Sstevel@tonic-gate 		}
29060Sstevel@tonic-gate 		case IPV6_DSTOPTS: {
29070Sstevel@tonic-gate 			ip6_dest_t *dopts = (ip6_dest_t *)invalp;
29080Sstevel@tonic-gate 
29090Sstevel@tonic-gate 			/*
29100Sstevel@tonic-gate 			 * Sanity checks - minimum size, size a multiple of
29110Sstevel@tonic-gate 			 * eight bytes, and matching size passed in.
29120Sstevel@tonic-gate 			 */
29130Sstevel@tonic-gate 			if (inlen != 0 &&
29140Sstevel@tonic-gate 			    inlen != (8 * (dopts->ip6d_len + 1)))
29150Sstevel@tonic-gate 				return (EINVAL);
29160Sstevel@tonic-gate 
29170Sstevel@tonic-gate 			if (checkonly)
29180Sstevel@tonic-gate 				break;
29190Sstevel@tonic-gate 
29200Sstevel@tonic-gate 			if (inlen == 0) {
29210Sstevel@tonic-gate 				if (sticky &&
29220Sstevel@tonic-gate 				    (ipp->ipp_fields & IPPF_DSTOPTS) != 0) {
29230Sstevel@tonic-gate 					kmem_free(ipp->ipp_dstopts,
29240Sstevel@tonic-gate 					    ipp->ipp_dstoptslen);
29250Sstevel@tonic-gate 					ipp->ipp_dstopts = NULL;
29260Sstevel@tonic-gate 					ipp->ipp_dstoptslen = 0;
29270Sstevel@tonic-gate 				}
29280Sstevel@tonic-gate 				ipp->ipp_fields &= ~IPPF_DSTOPTS;
29290Sstevel@tonic-gate 				ipp->ipp_sticky_ignored |= IPPF_DSTOPTS;
29300Sstevel@tonic-gate 			} else {
29311676Sjpk 				error = optcom_pkt_set(invalp, inlen, sticky,
29320Sstevel@tonic-gate 				    (uchar_t **)&ipp->ipp_dstopts,
29331676Sjpk 				    &ipp->ipp_dstoptslen, 0);
29340Sstevel@tonic-gate 				if (error != 0)
29350Sstevel@tonic-gate 					return (error);
29360Sstevel@tonic-gate 				ipp->ipp_fields |= IPPF_DSTOPTS;
29370Sstevel@tonic-gate 			}
29380Sstevel@tonic-gate 			if (sticky) {
29395240Snordmark 				error = icmp_build_hdrs(icmp);
29400Sstevel@tonic-gate 				if (error != 0)
29410Sstevel@tonic-gate 					return (error);
29420Sstevel@tonic-gate 			}
29430Sstevel@tonic-gate 			break;
29440Sstevel@tonic-gate 		}
29450Sstevel@tonic-gate 		case IPV6_RTHDR: {
29460Sstevel@tonic-gate 			ip6_rthdr_t *rt = (ip6_rthdr_t *)invalp;
29470Sstevel@tonic-gate 
29480Sstevel@tonic-gate 			/*
29490Sstevel@tonic-gate 			 * Sanity checks - minimum size, size a multiple of
29500Sstevel@tonic-gate 			 * eight bytes, and matching size passed in.
29510Sstevel@tonic-gate 			 */
29520Sstevel@tonic-gate 			if (inlen != 0 &&
29530Sstevel@tonic-gate 			    inlen != (8 * (rt->ip6r_len + 1)))
29540Sstevel@tonic-gate 				return (EINVAL);
29550Sstevel@tonic-gate 
29560Sstevel@tonic-gate 			if (checkonly)
29570Sstevel@tonic-gate 				break;
29580Sstevel@tonic-gate 
29590Sstevel@tonic-gate 			if (inlen == 0) {
29600Sstevel@tonic-gate 				if (sticky &&
29610Sstevel@tonic-gate 				    (ipp->ipp_fields & IPPF_RTHDR) != 0) {
29620Sstevel@tonic-gate 					kmem_free(ipp->ipp_rthdr,
29630Sstevel@tonic-gate 					    ipp->ipp_rthdrlen);
29640Sstevel@tonic-gate 					ipp->ipp_rthdr = NULL;
29650Sstevel@tonic-gate 					ipp->ipp_rthdrlen = 0;
29660Sstevel@tonic-gate 				}
29670Sstevel@tonic-gate 				ipp->ipp_fields &= ~IPPF_RTHDR;
29680Sstevel@tonic-gate 				ipp->ipp_sticky_ignored |= IPPF_RTHDR;
29690Sstevel@tonic-gate 			} else {
29701676Sjpk 				error = optcom_pkt_set(invalp, inlen, sticky,
29710Sstevel@tonic-gate 				    (uchar_t **)&ipp->ipp_rthdr,
29721676Sjpk 				    &ipp->ipp_rthdrlen, 0);
29730Sstevel@tonic-gate 				if (error != 0)
29740Sstevel@tonic-gate 					return (error);
29750Sstevel@tonic-gate 				ipp->ipp_fields |= IPPF_RTHDR;
29760Sstevel@tonic-gate 			}
29770Sstevel@tonic-gate 			if (sticky) {
29785240Snordmark 				error = icmp_build_hdrs(icmp);
29790Sstevel@tonic-gate 				if (error != 0)
29800Sstevel@tonic-gate 					return (error);
29810Sstevel@tonic-gate 			}
29820Sstevel@tonic-gate 			break;
29830Sstevel@tonic-gate 		}
29840Sstevel@tonic-gate 
29850Sstevel@tonic-gate 		case IPV6_DONTFRAG:
29860Sstevel@tonic-gate 			if (checkonly)
29870Sstevel@tonic-gate 				break;
29880Sstevel@tonic-gate 
29890Sstevel@tonic-gate 			if (onoff) {
29900Sstevel@tonic-gate 				ipp->ipp_fields |= IPPF_DONTFRAG;
29910Sstevel@tonic-gate 			} else {
29920Sstevel@tonic-gate 				ipp->ipp_fields &= ~IPPF_DONTFRAG;
29930Sstevel@tonic-gate 			}
29940Sstevel@tonic-gate 			break;
29950Sstevel@tonic-gate 
29960Sstevel@tonic-gate 		case IPV6_USE_MIN_MTU:
29970Sstevel@tonic-gate 			if (inlen != sizeof (int))
29980Sstevel@tonic-gate 				return (EINVAL);
29990Sstevel@tonic-gate 
30000Sstevel@tonic-gate 			if (*i1 < -1 || *i1 > 1)
30010Sstevel@tonic-gate 				return (EINVAL);
30020Sstevel@tonic-gate 
30030Sstevel@tonic-gate 			if (checkonly)
30040Sstevel@tonic-gate 				break;
30050Sstevel@tonic-gate 
30060Sstevel@tonic-gate 			ipp->ipp_fields |= IPPF_USE_MIN_MTU;
30070Sstevel@tonic-gate 			ipp->ipp_use_min_mtu = *i1;
30080Sstevel@tonic-gate 			break;
30090Sstevel@tonic-gate 
30100Sstevel@tonic-gate 		/*
30110Sstevel@tonic-gate 		 * This option can't be set.  Its only returned via
30120Sstevel@tonic-gate 		 * getsockopt() or ancillary data.
30130Sstevel@tonic-gate 		 */
30140Sstevel@tonic-gate 		case IPV6_PATHMTU:
30150Sstevel@tonic-gate 			return (EINVAL);
30160Sstevel@tonic-gate 
30170Sstevel@tonic-gate 		case IPV6_BOUND_PIF:
30180Sstevel@tonic-gate 		case IPV6_SEC_OPT:
30190Sstevel@tonic-gate 		case IPV6_DONTFAILOVER_IF:
30200Sstevel@tonic-gate 		case IPV6_SRC_PREFERENCES:
30210Sstevel@tonic-gate 		case IPV6_V6ONLY:
30220Sstevel@tonic-gate 			/* Handled at IP level */
30230Sstevel@tonic-gate 			return (-EINVAL);
30240Sstevel@tonic-gate 		default:
30250Sstevel@tonic-gate 			*outlenp = 0;
30260Sstevel@tonic-gate 			return (EINVAL);
30270Sstevel@tonic-gate 		}
30280Sstevel@tonic-gate 		break;
30290Sstevel@tonic-gate 	}		/* end IPPROTO_IPV6 */
30300Sstevel@tonic-gate 
30310Sstevel@tonic-gate 	case IPPROTO_ICMPV6:
30320Sstevel@tonic-gate 		/*
30330Sstevel@tonic-gate 		 * Only allow IPv6 option processing on IPv6 sockets.
30340Sstevel@tonic-gate 		 */
30350Sstevel@tonic-gate 		if (icmp->icmp_family != AF_INET6) {
30360Sstevel@tonic-gate 			*outlenp = 0;
30370Sstevel@tonic-gate 			return (ENOPROTOOPT);
30380Sstevel@tonic-gate 		}
30390Sstevel@tonic-gate 		if (icmp->icmp_proto != IPPROTO_ICMPV6) {
30400Sstevel@tonic-gate 			*outlenp = 0;
30410Sstevel@tonic-gate 			return (ENOPROTOOPT);
30420Sstevel@tonic-gate 		}
30430Sstevel@tonic-gate 		switch (name) {
30440Sstevel@tonic-gate 		case ICMP6_FILTER:
30450Sstevel@tonic-gate 			if (!checkonly) {
30460Sstevel@tonic-gate 				if ((inlen != 0) &&
30470Sstevel@tonic-gate 				    (inlen != sizeof (icmp6_filter_t)))
30480Sstevel@tonic-gate 					return (EINVAL);
30490Sstevel@tonic-gate 
30500Sstevel@tonic-gate 				if (inlen == 0) {
30510Sstevel@tonic-gate 					if (icmp->icmp_filter != NULL) {
30520Sstevel@tonic-gate 						kmem_free(icmp->icmp_filter,
30530Sstevel@tonic-gate 						    sizeof (icmp6_filter_t));
30540Sstevel@tonic-gate 						icmp->icmp_filter = NULL;
30550Sstevel@tonic-gate 					}
30560Sstevel@tonic-gate 				} else {
30570Sstevel@tonic-gate 					if (icmp->icmp_filter == NULL) {
30580Sstevel@tonic-gate 						icmp->icmp_filter = kmem_alloc(
30590Sstevel@tonic-gate 						    sizeof (icmp6_filter_t),
30600Sstevel@tonic-gate 						    KM_NOSLEEP);
30610Sstevel@tonic-gate 						if (icmp->icmp_filter == NULL) {
30620Sstevel@tonic-gate 							*outlenp = 0;
30630Sstevel@tonic-gate 							return (ENOBUFS);
30640Sstevel@tonic-gate 						}
30650Sstevel@tonic-gate 					}
30660Sstevel@tonic-gate 					(void) bcopy(invalp, icmp->icmp_filter,
30670Sstevel@tonic-gate 					    inlen);
30680Sstevel@tonic-gate 				}
30690Sstevel@tonic-gate 			}
30700Sstevel@tonic-gate 			break;
30710Sstevel@tonic-gate 
30720Sstevel@tonic-gate 		default:
30730Sstevel@tonic-gate 			*outlenp = 0;
30740Sstevel@tonic-gate 			return (EINVAL);
30750Sstevel@tonic-gate 		}
30760Sstevel@tonic-gate 		break;
30770Sstevel@tonic-gate 	default:
30780Sstevel@tonic-gate 		*outlenp = 0;
30790Sstevel@tonic-gate 		return (EINVAL);
30800Sstevel@tonic-gate 	}
30810Sstevel@tonic-gate 	/*
30820Sstevel@tonic-gate 	 * Common case of OK return with outval same as inval.
30830Sstevel@tonic-gate 	 */
30840Sstevel@tonic-gate 	if (invalp != outvalp) {
30850Sstevel@tonic-gate 		/* don't trust bcopy for identical src/dst */
30860Sstevel@tonic-gate 		(void) bcopy(invalp, outvalp, inlen);
30870Sstevel@tonic-gate 	}
30880Sstevel@tonic-gate 	*outlenp = inlen;
30890Sstevel@tonic-gate 	return (0);
30900Sstevel@tonic-gate }
30918348SEric.Yu@Sun.COM 
30925240Snordmark /* This routine sets socket options. */
30935240Snordmark /* ARGSUSED */
30945240Snordmark int
30958348SEric.Yu@Sun.COM icmp_opt_set(conn_t *connp, uint_t optset_context, int level, int name,
30968348SEric.Yu@Sun.COM     uint_t inlen, uchar_t *invalp, uint_t *outlenp, uchar_t *outvalp,
30978348SEric.Yu@Sun.COM     void *thisdg_attrs, cred_t *cr)
30988348SEric.Yu@Sun.COM {
30998348SEric.Yu@Sun.COM 	boolean_t checkonly;
31008348SEric.Yu@Sun.COM 	int	error;
31018348SEric.Yu@Sun.COM 
31028348SEric.Yu@Sun.COM 	error = 0;
31038348SEric.Yu@Sun.COM 	switch (optset_context) {
31048348SEric.Yu@Sun.COM 	case SETFN_OPTCOM_CHECKONLY:
31058348SEric.Yu@Sun.COM 		checkonly = B_TRUE;
31068348SEric.Yu@Sun.COM 		/*
31078348SEric.Yu@Sun.COM 		 * Note: Implies T_CHECK semantics for T_OPTCOM_REQ
31088348SEric.Yu@Sun.COM 		 * inlen != 0 implies value supplied and
31098348SEric.Yu@Sun.COM 		 * 	we have to "pretend" to set it.
31108348SEric.Yu@Sun.COM 		 * inlen == 0 implies that there is no
31118348SEric.Yu@Sun.COM 		 * 	value part in T_CHECK request and just validation
31128348SEric.Yu@Sun.COM 		 * done elsewhere should be enough, we just return here.
31138348SEric.Yu@Sun.COM 		 */
31148348SEric.Yu@Sun.COM 		if (inlen == 0) {
31158348SEric.Yu@Sun.COM 			*outlenp = 0;
31168348SEric.Yu@Sun.COM 			error = 0;
31178348SEric.Yu@Sun.COM 			goto done;
31188348SEric.Yu@Sun.COM 		}
31198348SEric.Yu@Sun.COM 		break;
31208348SEric.Yu@Sun.COM 	case SETFN_OPTCOM_NEGOTIATE:
31218348SEric.Yu@Sun.COM 		checkonly = B_FALSE;
31228348SEric.Yu@Sun.COM 		break;
31238348SEric.Yu@Sun.COM 	case SETFN_UD_NEGOTIATE:
31248348SEric.Yu@Sun.COM 	case SETFN_CONN_NEGOTIATE:
31258348SEric.Yu@Sun.COM 		checkonly = B_FALSE;
31268348SEric.Yu@Sun.COM 		/*
31278348SEric.Yu@Sun.COM 		 * Negotiating local and "association-related" options
31288348SEric.Yu@Sun.COM 		 * through T_UNITDATA_REQ.
31298348SEric.Yu@Sun.COM 		 *
31308348SEric.Yu@Sun.COM 		 * Following routine can filter out ones we do not
31318348SEric.Yu@Sun.COM 		 * want to be "set" this way.
31328348SEric.Yu@Sun.COM 		 */
31338348SEric.Yu@Sun.COM 		if (!icmp_opt_allow_udr_set(level, name)) {
31348348SEric.Yu@Sun.COM 			*outlenp = 0;
31358348SEric.Yu@Sun.COM 			error = EINVAL;
31368348SEric.Yu@Sun.COM 			goto done;
31378348SEric.Yu@Sun.COM 		}
31388348SEric.Yu@Sun.COM 		break;
31398348SEric.Yu@Sun.COM 	default:
31408348SEric.Yu@Sun.COM 		/*
31418348SEric.Yu@Sun.COM 		 * We should never get here
31428348SEric.Yu@Sun.COM 		 */
31438348SEric.Yu@Sun.COM 		*outlenp = 0;
31448348SEric.Yu@Sun.COM 		error = EINVAL;
31458348SEric.Yu@Sun.COM 		goto done;
31468348SEric.Yu@Sun.COM 	}
31478348SEric.Yu@Sun.COM 
31488348SEric.Yu@Sun.COM 	ASSERT((optset_context != SETFN_OPTCOM_CHECKONLY) ||
31498348SEric.Yu@Sun.COM 	    (optset_context == SETFN_OPTCOM_CHECKONLY && inlen != 0));
31508348SEric.Yu@Sun.COM 	error = icmp_do_opt_set(connp, level, name, inlen, invalp, outlenp,
31518348SEric.Yu@Sun.COM 	    outvalp, cr, thisdg_attrs, checkonly);
31528348SEric.Yu@Sun.COM 
31538348SEric.Yu@Sun.COM done:
31548348SEric.Yu@Sun.COM 	return (error);
31558348SEric.Yu@Sun.COM }
31568348SEric.Yu@Sun.COM 
31578348SEric.Yu@Sun.COM /* This routine sets socket options. */
31588348SEric.Yu@Sun.COM /* ARGSUSED */
31598348SEric.Yu@Sun.COM int
31608348SEric.Yu@Sun.COM icmp_tpi_opt_set(queue_t *q, uint_t optset_context, int level, int name,
31615240Snordmark     uint_t inlen, uchar_t *invalp, uint_t *outlenp, uchar_t *outvalp,
31625240Snordmark     void *thisdg_attrs, cred_t *cr, mblk_t *mblk)
31635240Snordmark {
31648348SEric.Yu@Sun.COM 	conn_t	*connp =  Q_TO_CONN(q);
31655240Snordmark 	icmp_t	*icmp;
31668348SEric.Yu@Sun.COM 	int error;
31678348SEric.Yu@Sun.COM 
31688348SEric.Yu@Sun.COM 	icmp = connp->conn_icmp;
31695240Snordmark 	rw_enter(&icmp->icmp_rwlock, RW_WRITER);
31708348SEric.Yu@Sun.COM 	error = icmp_opt_set(connp, optset_context, level, name, inlen, invalp,
31718348SEric.Yu@Sun.COM 	    outlenp, outvalp, thisdg_attrs, cr);
31725240Snordmark 	rw_exit(&icmp->icmp_rwlock);
31738348SEric.Yu@Sun.COM 	return (error);
31745240Snordmark }
31750Sstevel@tonic-gate 
31760Sstevel@tonic-gate /*
31770Sstevel@tonic-gate  * Update icmp_sticky_hdrs based on icmp_sticky_ipp, icmp_v6src, icmp_ttl,
31780Sstevel@tonic-gate  * icmp_proto, icmp_raw_checksum and icmp_no_tp_cksum.
31790Sstevel@tonic-gate  * The headers include ip6i_t (if needed), ip6_t, and any sticky extension
31800Sstevel@tonic-gate  * headers.
31810Sstevel@tonic-gate  * Returns failure if can't allocate memory.
31820Sstevel@tonic-gate  */
31830Sstevel@tonic-gate static int
31845240Snordmark icmp_build_hdrs(icmp_t *icmp)
31850Sstevel@tonic-gate {
31863448Sdh155122 	icmp_stack_t *is = icmp->icmp_is;
31870Sstevel@tonic-gate 	uchar_t	*hdrs;
31880Sstevel@tonic-gate 	uint_t	hdrs_len;
31890Sstevel@tonic-gate 	ip6_t	*ip6h;
31900Sstevel@tonic-gate 	ip6i_t	*ip6i;
31910Sstevel@tonic-gate 	ip6_pkt_t *ipp = &icmp->icmp_sticky_ipp;
31920Sstevel@tonic-gate 
31935240Snordmark 	ASSERT(RW_WRITE_HELD(&icmp->icmp_rwlock));
31940Sstevel@tonic-gate 	hdrs_len = ip_total_hdrs_len_v6(ipp);
31950Sstevel@tonic-gate 	ASSERT(hdrs_len != 0);
31960Sstevel@tonic-gate 	if (hdrs_len != icmp->icmp_sticky_hdrs_len) {
31970Sstevel@tonic-gate 		/* Need to reallocate */
31980Sstevel@tonic-gate 		if (hdrs_len != 0) {
31990Sstevel@tonic-gate 			hdrs = kmem_alloc(hdrs_len, KM_NOSLEEP);
32000Sstevel@tonic-gate 			if (hdrs == NULL)
32010Sstevel@tonic-gate 				return (ENOMEM);
32020Sstevel@tonic-gate 		} else {
32030Sstevel@tonic-gate 			hdrs = NULL;
32040Sstevel@tonic-gate 		}
32050Sstevel@tonic-gate 		if (icmp->icmp_sticky_hdrs_len != 0) {
32060Sstevel@tonic-gate 			kmem_free(icmp->icmp_sticky_hdrs,
32070Sstevel@tonic-gate 			    icmp->icmp_sticky_hdrs_len);
32080Sstevel@tonic-gate 		}
32090Sstevel@tonic-gate 		icmp->icmp_sticky_hdrs = hdrs;
32100Sstevel@tonic-gate 		icmp->icmp_sticky_hdrs_len = hdrs_len;
32110Sstevel@tonic-gate 	}
32120Sstevel@tonic-gate 	ip_build_hdrs_v6(icmp->icmp_sticky_hdrs,
32130Sstevel@tonic-gate 	    icmp->icmp_sticky_hdrs_len, ipp, icmp->icmp_proto);
32140Sstevel@tonic-gate 
32150Sstevel@tonic-gate 	/* Set header fields not in ipp */
32160Sstevel@tonic-gate 	if (ipp->ipp_fields & IPPF_HAS_IP6I) {
32170Sstevel@tonic-gate 		ip6i = (ip6i_t *)icmp->icmp_sticky_hdrs;
32180Sstevel@tonic-gate 		ip6h = (ip6_t *)&ip6i[1];
32190Sstevel@tonic-gate 
32200Sstevel@tonic-gate 		if (ipp->ipp_fields & IPPF_RAW_CKSUM) {
32210Sstevel@tonic-gate 			ip6i->ip6i_flags |= IP6I_RAW_CHECKSUM;
32220Sstevel@tonic-gate 			ip6i->ip6i_checksum_off = icmp->icmp_checksum_off;
32230Sstevel@tonic-gate 		}
32240Sstevel@tonic-gate 		if (ipp->ipp_fields & IPPF_NO_CKSUM) {
32250Sstevel@tonic-gate 			ip6i->ip6i_flags |= IP6I_NO_ULP_CKSUM;
32260Sstevel@tonic-gate 		}
32270Sstevel@tonic-gate 	} else {
32280Sstevel@tonic-gate 		ip6h = (ip6_t *)icmp->icmp_sticky_hdrs;
32290Sstevel@tonic-gate 	}
32300Sstevel@tonic-gate 
32310Sstevel@tonic-gate 	if (!(ipp->ipp_fields & IPPF_ADDR))
32320Sstevel@tonic-gate 		ip6h->ip6_src = icmp->icmp_v6src;
32330Sstevel@tonic-gate 
32340Sstevel@tonic-gate 	/* Try to get everything in a single mblk */
32350Sstevel@tonic-gate 	if (hdrs_len > icmp->icmp_max_hdr_len) {
32360Sstevel@tonic-gate 		icmp->icmp_max_hdr_len = hdrs_len;
32375240Snordmark 		rw_exit(&icmp->icmp_rwlock);
32388348SEric.Yu@Sun.COM 		(void) proto_set_tx_wroff(icmp->icmp_connp->conn_rq,
32398348SEric.Yu@Sun.COM 		    icmp->icmp_connp,
32405240Snordmark 		    icmp->icmp_max_hdr_len + is->is_wroff_extra);
32415240Snordmark 		rw_enter(&icmp->icmp_rwlock, RW_WRITER);
32420Sstevel@tonic-gate 	}
32430Sstevel@tonic-gate 	return (0);
32440Sstevel@tonic-gate }
32450Sstevel@tonic-gate 
32460Sstevel@tonic-gate /*
32470Sstevel@tonic-gate  * This routine retrieves the value of an ND variable in a icmpparam_t
32480Sstevel@tonic-gate  * structure.  It is called through nd_getset when a user reads the
32490Sstevel@tonic-gate  * variable.
32500Sstevel@tonic-gate  */
32510Sstevel@tonic-gate /* ARGSUSED */
32520Sstevel@tonic-gate static int
32530Sstevel@tonic-gate icmp_param_get(queue_t *q, mblk_t *mp, caddr_t cp, cred_t *cr)
32540Sstevel@tonic-gate {
32550Sstevel@tonic-gate 	icmpparam_t	*icmppa = (icmpparam_t *)cp;
32560Sstevel@tonic-gate 
32570Sstevel@tonic-gate 	(void) mi_mpprintf(mp, "%d", icmppa->icmp_param_value);
32580Sstevel@tonic-gate 	return (0);
32590Sstevel@tonic-gate }
32600Sstevel@tonic-gate 
32610Sstevel@tonic-gate /*
32620Sstevel@tonic-gate  * Walk through the param array specified registering each element with the
32630Sstevel@tonic-gate  * named dispatch (ND) handler.
32640Sstevel@tonic-gate  */
32650Sstevel@tonic-gate static boolean_t
32663448Sdh155122 icmp_param_register(IDP *ndp, icmpparam_t *icmppa, int cnt)
32670Sstevel@tonic-gate {
32680Sstevel@tonic-gate 	for (; cnt-- > 0; icmppa++) {
32690Sstevel@tonic-gate 		if (icmppa->icmp_param_name && icmppa->icmp_param_name[0]) {
32703448Sdh155122 			if (!nd_load(ndp, icmppa->icmp_param_name,
32710Sstevel@tonic-gate 			    icmp_param_get, icmp_param_set,
32720Sstevel@tonic-gate 			    (caddr_t)icmppa)) {
32733448Sdh155122 				nd_free(ndp);
32740Sstevel@tonic-gate 				return (B_FALSE);
32750Sstevel@tonic-gate 			}
32760Sstevel@tonic-gate 		}
32770Sstevel@tonic-gate 	}
32783448Sdh155122 	if (!nd_load(ndp, "icmp_status", icmp_status_report, NULL,
32790Sstevel@tonic-gate 	    NULL)) {
32803448Sdh155122 		nd_free(ndp);
32810Sstevel@tonic-gate 		return (B_FALSE);
32820Sstevel@tonic-gate 	}
32830Sstevel@tonic-gate 	return (B_TRUE);
32840Sstevel@tonic-gate }
32850Sstevel@tonic-gate 
32860Sstevel@tonic-gate /* This routine sets an ND variable in a icmpparam_t structure. */
32870Sstevel@tonic-gate /* ARGSUSED */
32880Sstevel@tonic-gate static int
32890Sstevel@tonic-gate icmp_param_set(queue_t *q, mblk_t *mp, char *value, caddr_t cp, cred_t *cr)
32900Sstevel@tonic-gate {
32910Sstevel@tonic-gate 	long		new_value;
32920Sstevel@tonic-gate 	icmpparam_t	*icmppa = (icmpparam_t *)cp;
32930Sstevel@tonic-gate 
32940Sstevel@tonic-gate 	/*
32950Sstevel@tonic-gate 	 * Fail the request if the new value does not lie within the
32960Sstevel@tonic-gate 	 * required bounds.
32970Sstevel@tonic-gate 	 */
32980Sstevel@tonic-gate 	if (ddi_strtol(value, NULL, 10, &new_value) != 0 ||
32990Sstevel@tonic-gate 	    new_value < icmppa->icmp_param_min ||
33000Sstevel@tonic-gate 	    new_value > icmppa->icmp_param_max) {
33010Sstevel@tonic-gate 		return (EINVAL);
33020Sstevel@tonic-gate 	}
33030Sstevel@tonic-gate 	/* Set the new value */
33040Sstevel@tonic-gate 	icmppa->icmp_param_value = new_value;
33050Sstevel@tonic-gate 	return (0);
33060Sstevel@tonic-gate }
33078348SEric.Yu@Sun.COM static void
33088348SEric.Yu@Sun.COM icmp_queue_fallback(icmp_t *icmp, mblk_t *mp)
33098348SEric.Yu@Sun.COM {
33108348SEric.Yu@Sun.COM 	ASSERT(MUTEX_HELD(&icmp->icmp_recv_lock));
33118348SEric.Yu@Sun.COM 	if (IPCL_IS_NONSTR(icmp->icmp_connp)) {
33128348SEric.Yu@Sun.COM 		/*
33138348SEric.Yu@Sun.COM 		 * fallback has started but messages have not been moved yet
33148348SEric.Yu@Sun.COM 		 */
33158348SEric.Yu@Sun.COM 		if (icmp->icmp_fallback_queue_head == NULL) {
33168348SEric.Yu@Sun.COM 			ASSERT(icmp->icmp_fallback_queue_tail == NULL);
33178348SEric.Yu@Sun.COM 			icmp->icmp_fallback_queue_head = mp;
33188348SEric.Yu@Sun.COM 			icmp->icmp_fallback_queue_tail = mp;
33198348SEric.Yu@Sun.COM 		} else {
33208348SEric.Yu@Sun.COM 			ASSERT(icmp->icmp_fallback_queue_tail != NULL);
33218348SEric.Yu@Sun.COM 			icmp->icmp_fallback_queue_tail->b_next = mp;
33228348SEric.Yu@Sun.COM 			icmp->icmp_fallback_queue_tail = mp;
33238348SEric.Yu@Sun.COM 		}
33248348SEric.Yu@Sun.COM 		mutex_exit(&icmp->icmp_recv_lock);
33258348SEric.Yu@Sun.COM 	} else {
33268348SEric.Yu@Sun.COM 		/*
33278348SEric.Yu@Sun.COM 		 * no more fallbacks possible, ok to drop lock.
33288348SEric.Yu@Sun.COM 		 */
33298348SEric.Yu@Sun.COM 		mutex_exit(&icmp->icmp_recv_lock);
33308348SEric.Yu@Sun.COM 		putnext(icmp->icmp_connp->conn_rq, mp);
33318348SEric.Yu@Sun.COM 	}
33328348SEric.Yu@Sun.COM }
33338348SEric.Yu@Sun.COM 
33345240Snordmark /*ARGSUSED2*/
33350Sstevel@tonic-gate static void
33365240Snordmark icmp_input(void *arg1, mblk_t *mp, void *arg2)
33370Sstevel@tonic-gate {
33385240Snordmark 	conn_t *connp = (conn_t *)arg1;
33390Sstevel@tonic-gate 	struct T_unitdata_ind	*tudi;
33400Sstevel@tonic-gate 	uchar_t			*rptr;
33415240Snordmark 	icmp_t			*icmp;
33425240Snordmark 	icmp_stack_t		*is;
33430Sstevel@tonic-gate 	sin_t			*sin;
33440Sstevel@tonic-gate 	sin6_t			*sin6;
33450Sstevel@tonic-gate 	ip6_t			*ip6h;
33460Sstevel@tonic-gate 	ip6i_t			*ip6i;
33470Sstevel@tonic-gate 	mblk_t			*mp1;
33480Sstevel@tonic-gate 	int			hdr_len;
33490Sstevel@tonic-gate 	ipha_t			*ipha;
33500Sstevel@tonic-gate 	int			udi_size;	/* Size of T_unitdata_ind */
33510Sstevel@tonic-gate 	uint_t			ipvers;
33520Sstevel@tonic-gate 	ip6_pkt_t		ipp;
33530Sstevel@tonic-gate 	uint8_t			nexthdr;
33543318Srshoaib 	ip_pktinfo_t		*pinfo = NULL;
33550Sstevel@tonic-gate 	mblk_t			*options_mp = NULL;
33560Sstevel@tonic-gate 	uint_t			icmp_opt = 0;
33570Sstevel@tonic-gate 	boolean_t		icmp_ipv6_recvhoplimit = B_FALSE;
33581676Sjpk 	uint_t			hopstrip;
33598348SEric.Yu@Sun.COM 	int			error;
33600Sstevel@tonic-gate 
33615240Snordmark 	ASSERT(connp->conn_flags & IPCL_RAWIPCONN);
33625240Snordmark 
33635240Snordmark 	icmp = connp->conn_icmp;
33645240Snordmark 	is = icmp->icmp_is;
33655240Snordmark 	rptr = mp->b_rptr;
33665240Snordmark 	ASSERT(DB_TYPE(mp) == M_DATA || DB_TYPE(mp) == M_CTL);
33675240Snordmark 	ASSERT(OK_32PTR(rptr));
33685240Snordmark 
33695240Snordmark 	/*
33705240Snordmark 	 * IP should have prepended the options data in an M_CTL
33715240Snordmark 	 * Check M_CTL "type" to make sure are not here bcos of
33725240Snordmark 	 * a valid ICMP message
33735240Snordmark 	 */
33745240Snordmark 	if (DB_TYPE(mp) == M_CTL) {
33750Sstevel@tonic-gate 		/*
33765240Snordmark 		 * FIXME: does IP still do this?
33770Sstevel@tonic-gate 		 * IP sends up the IPSEC_IN message for handling IPSEC
33780Sstevel@tonic-gate 		 * policy at the TCP level. We don't need it here.
33790Sstevel@tonic-gate 		 */
33800Sstevel@tonic-gate 		if (*(uint32_t *)(mp->b_rptr) == IPSEC_IN) {
33810Sstevel@tonic-gate 			mp1 = mp->b_cont;
33820Sstevel@tonic-gate 			freeb(mp);
33830Sstevel@tonic-gate 			mp = mp1;
33845240Snordmark 			rptr = mp->b_rptr;
33855240Snordmark 		} else if (MBLKL(mp) == sizeof (ip_pktinfo_t) &&
33865240Snordmark 		    ((ip_pktinfo_t *)mp->b_rptr)->ip_pkt_ulp_type ==
33875240Snordmark 		    IN_PKTINFO) {
33885240Snordmark 			/*
33895240Snordmark 			 * IP_RECVIF or IP_RECVSLLA or IPF_RECVADDR information
33905240Snordmark 			 * has been prepended to the packet by IP. We need to
33915240Snordmark 			 * extract the mblk and adjust the rptr
33925240Snordmark 			 */
33933318Srshoaib 			pinfo = (ip_pktinfo_t *)mp->b_rptr;
33945240Snordmark 			options_mp = mp;
33955240Snordmark 			mp = mp->b_cont;
33965240Snordmark 			rptr = mp->b_rptr;
33975240Snordmark 		} else {
33985240Snordmark 			/*
33995240Snordmark 			 * ICMP messages.
34005240Snordmark 			 */
34018348SEric.Yu@Sun.COM 			icmp_icmp_error(connp, mp);
34020Sstevel@tonic-gate 			return;
34030Sstevel@tonic-gate 		}
34040Sstevel@tonic-gate 	}
34050Sstevel@tonic-gate 
34060Sstevel@tonic-gate 	/*
34070Sstevel@tonic-gate 	 * Discard message if it is misaligned or smaller than the IP header.
34080Sstevel@tonic-gate 	 */
34090Sstevel@tonic-gate 	if (!OK_32PTR(rptr) || (mp->b_wptr - rptr) < sizeof (ipha_t)) {
34100Sstevel@tonic-gate 		freemsg(mp);
34110Sstevel@tonic-gate 		if (options_mp != NULL)
34120Sstevel@tonic-gate 			freeb(options_mp);
34135240Snordmark 		BUMP_MIB(&is->is_rawip_mib, rawipInErrors);
34140Sstevel@tonic-gate 		return;
34150Sstevel@tonic-gate 	}
34160Sstevel@tonic-gate 	ipvers = IPH_HDR_VERSION((ipha_t *)rptr);
34170Sstevel@tonic-gate 
34180Sstevel@tonic-gate 	/* Handle M_DATA messages containing IP packets messages */
34190Sstevel@tonic-gate 	if (ipvers == IPV4_VERSION) {
34200Sstevel@tonic-gate 		/*
34210Sstevel@tonic-gate 		 * Special case where IP attaches
34220Sstevel@tonic-gate 		 * the IRE needs to be handled so that we don't send up
34230Sstevel@tonic-gate 		 * IRE to the user land.
34240Sstevel@tonic-gate 		 */
34250Sstevel@tonic-gate 		ipha = (ipha_t *)rptr;
34260Sstevel@tonic-gate 		hdr_len = IPH_HDR_LENGTH(ipha);
34270Sstevel@tonic-gate 
34280Sstevel@tonic-gate 		if (ipha->ipha_protocol == IPPROTO_TCP) {
34290Sstevel@tonic-gate 			tcph_t *tcph = (tcph_t *)&mp->b_rptr[hdr_len];
34300Sstevel@tonic-gate 
34310Sstevel@tonic-gate 			if (((tcph->th_flags[0] & (TH_SYN|TH_ACK)) ==
34320Sstevel@tonic-gate 			    TH_SYN) && mp->b_cont != NULL) {
34330Sstevel@tonic-gate 				mp1 = mp->b_cont;
34340Sstevel@tonic-gate 				if (mp1->b_datap->db_type == IRE_DB_TYPE) {
34350Sstevel@tonic-gate 					freeb(mp1);
34360Sstevel@tonic-gate 					mp->b_cont = NULL;
34370Sstevel@tonic-gate 				}
34380Sstevel@tonic-gate 			}
34390Sstevel@tonic-gate 		}
34403448Sdh155122 		if (is->is_bsd_compat) {
34410Sstevel@tonic-gate 			ushort_t len;
34420Sstevel@tonic-gate 			len = ntohs(ipha->ipha_length);
34430Sstevel@tonic-gate 
34440Sstevel@tonic-gate 			if (mp->b_datap->db_ref > 1) {
34450Sstevel@tonic-gate 				/*
34460Sstevel@tonic-gate 				 * Allocate a new IP header so that we can
34470Sstevel@tonic-gate 				 * modify ipha_length.
34480Sstevel@tonic-gate 				 */
34490Sstevel@tonic-gate 				mblk_t	*mp1;
34500Sstevel@tonic-gate 
34510Sstevel@tonic-gate 				mp1 = allocb(hdr_len, BPRI_MED);
34520Sstevel@tonic-gate 				if (!mp1) {
34530Sstevel@tonic-gate 					freemsg(mp);
34540Sstevel@tonic-gate 					if (options_mp != NULL)
34550Sstevel@tonic-gate 						freeb(options_mp);
34565240Snordmark 					BUMP_MIB(&is->is_rawip_mib,
34573448Sdh155122 					    rawipInErrors);
34580Sstevel@tonic-gate 					return;
34590Sstevel@tonic-gate 				}
34600Sstevel@tonic-gate 				bcopy(rptr, mp1->b_rptr, hdr_len);
34610Sstevel@tonic-gate 				mp->b_rptr = rptr + hdr_len;
34620Sstevel@tonic-gate 				rptr = mp1->b_rptr;
34630Sstevel@tonic-gate 				ipha = (ipha_t *)rptr;
34640Sstevel@tonic-gate 				mp1->b_cont = mp;
34650Sstevel@tonic-gate 				mp1->b_wptr = rptr + hdr_len;
34660Sstevel@tonic-gate 				mp = mp1;
34670Sstevel@tonic-gate 			}
34680Sstevel@tonic-gate 			len -= hdr_len;
34690Sstevel@tonic-gate 			ipha->ipha_length = htons(len);
34700Sstevel@tonic-gate 		}
34710Sstevel@tonic-gate 	}
34720Sstevel@tonic-gate 
34730Sstevel@tonic-gate 	/*
34740Sstevel@tonic-gate 	 * This is the inbound data path.  Packets are passed upstream as
34750Sstevel@tonic-gate 	 * T_UNITDATA_IND messages with full IP headers still attached.
34760Sstevel@tonic-gate 	 */
34770Sstevel@tonic-gate 	if (icmp->icmp_family == AF_INET) {
34780Sstevel@tonic-gate 		ASSERT(ipvers == IPV4_VERSION);
34790Sstevel@tonic-gate 		udi_size =  sizeof (struct T_unitdata_ind) + sizeof (sin_t);
34805267Snordmark 		if (icmp->icmp_recvif && (pinfo != NULL) &&
34813318Srshoaib 		    (pinfo->ip_pkt_flags & IPF_RECVIF)) {
34820Sstevel@tonic-gate 			udi_size += sizeof (struct T_opthdr) +
34830Sstevel@tonic-gate 			    sizeof (uint_t);
34840Sstevel@tonic-gate 		}
34853318Srshoaib 
34865267Snordmark 		if (icmp->icmp_ip_recvpktinfo && (pinfo != NULL) &&
34873318Srshoaib 		    (pinfo->ip_pkt_flags & IPF_RECVADDR)) {
34883318Srshoaib 			udi_size += sizeof (struct T_opthdr) +
34893318Srshoaib 			    sizeof (struct in_pktinfo);
34903318Srshoaib 		}
34913318Srshoaib 
34921673Sgt145670 		/*
34931673Sgt145670 		 * If SO_TIMESTAMP is set allocate the appropriate sized
34941673Sgt145670 		 * buffer. Since gethrestime() expects a pointer aligned
34951673Sgt145670 		 * argument, we allocate space necessary for extra
34961673Sgt145670 		 * alignment (even though it might not be used).
34971673Sgt145670 		 */
34981673Sgt145670 		if (icmp->icmp_timestamp) {
34991673Sgt145670 			udi_size += sizeof (struct T_opthdr) +
35001673Sgt145670 			    sizeof (timestruc_t) + _POINTER_ALIGNMENT;
35011673Sgt145670 		}
35020Sstevel@tonic-gate 		mp1 = allocb(udi_size, BPRI_MED);
35030Sstevel@tonic-gate 		if (mp1 == NULL) {
35040Sstevel@tonic-gate 			freemsg(mp);
35050Sstevel@tonic-gate 			if (options_mp != NULL)
35060Sstevel@tonic-gate 				freeb(options_mp);
35075240Snordmark 			BUMP_MIB(&is->is_rawip_mib, rawipInErrors);
35080Sstevel@tonic-gate 			return;
35090Sstevel@tonic-gate 		}
35100Sstevel@tonic-gate 		mp1->b_cont = mp;
35110Sstevel@tonic-gate 		mp = mp1;
35120Sstevel@tonic-gate 		tudi = (struct T_unitdata_ind *)mp->b_rptr;
35130Sstevel@tonic-gate 		mp->b_datap->db_type = M_PROTO;
35140Sstevel@tonic-gate 		mp->b_wptr = (uchar_t *)tudi + udi_size;
35150Sstevel@tonic-gate 		tudi->PRIM_type = T_UNITDATA_IND;
35160Sstevel@tonic-gate 		tudi->SRC_length = sizeof (sin_t);
35170Sstevel@tonic-gate 		tudi->SRC_offset = sizeof (struct T_unitdata_ind);
35180Sstevel@tonic-gate 		sin = (sin_t *)&tudi[1];
35190Sstevel@tonic-gate 		*sin = sin_null;
35200Sstevel@tonic-gate 		sin->sin_family = AF_INET;
35210Sstevel@tonic-gate 		sin->sin_addr.s_addr = ipha->ipha_src;
35220Sstevel@tonic-gate 		tudi->OPT_offset =  sizeof (struct T_unitdata_ind) +
35230Sstevel@tonic-gate 		    sizeof (sin_t);
35240Sstevel@tonic-gate 		udi_size -= (sizeof (struct T_unitdata_ind) + sizeof (sin_t));
35250Sstevel@tonic-gate 		tudi->OPT_length = udi_size;
35260Sstevel@tonic-gate 
35270Sstevel@tonic-gate 		/*
35280Sstevel@tonic-gate 		 * Add options if IP_RECVIF is set
35290Sstevel@tonic-gate 		 */
35300Sstevel@tonic-gate 		if (udi_size != 0) {
35310Sstevel@tonic-gate 			char *dstopt;
35320Sstevel@tonic-gate 
35330Sstevel@tonic-gate 			dstopt = (char *)&sin[1];
35345267Snordmark 			if (icmp->icmp_recvif && (pinfo != NULL) &&
35353318Srshoaib 			    (pinfo->ip_pkt_flags & IPF_RECVIF)) {
35360Sstevel@tonic-gate 
35370Sstevel@tonic-gate 				struct T_opthdr *toh;
35380Sstevel@tonic-gate 				uint_t		*dstptr;
35390Sstevel@tonic-gate 
35400Sstevel@tonic-gate 				toh = (struct T_opthdr *)dstopt;
35410Sstevel@tonic-gate 				toh->level = IPPROTO_IP;
35420Sstevel@tonic-gate 				toh->name = IP_RECVIF;
35430Sstevel@tonic-gate 				toh->len = sizeof (struct T_opthdr) +
35445240Snordmark 				    sizeof (uint_t);
35450Sstevel@tonic-gate 				toh->status = 0;
35460Sstevel@tonic-gate 				dstopt += sizeof (struct T_opthdr);
35470Sstevel@tonic-gate 				dstptr = (uint_t *)dstopt;
35483318Srshoaib 				*dstptr = pinfo->ip_pkt_ifindex;
35490Sstevel@tonic-gate 				dstopt += sizeof (uint_t);
35500Sstevel@tonic-gate 				udi_size -= toh->len;
35510Sstevel@tonic-gate 			}
35521673Sgt145670 			if (icmp->icmp_timestamp) {
35531673Sgt145670 				struct	T_opthdr *toh;
35541673Sgt145670 
35551673Sgt145670 				toh = (struct T_opthdr *)dstopt;
35561673Sgt145670 				toh->level = SOL_SOCKET;
35571673Sgt145670 				toh->name = SCM_TIMESTAMP;
35581673Sgt145670 				toh->len = sizeof (struct T_opthdr) +
35591673Sgt145670 				    sizeof (timestruc_t) + _POINTER_ALIGNMENT;
35601673Sgt145670 				toh->status = 0;
35611673Sgt145670 				dstopt += sizeof (struct T_opthdr);
35621673Sgt145670 				/* Align for gethrestime() */
35631673Sgt145670 				dstopt = (char *)P2ROUNDUP((intptr_t)dstopt,
35641673Sgt145670 				    sizeof (intptr_t));
35651673Sgt145670 				gethrestime((timestruc_t *)dstopt);
35663318Srshoaib 				dstopt = (char *)toh + toh->len;
35673318Srshoaib 				udi_size -= toh->len;
35683318Srshoaib 			}
35695267Snordmark 			if (icmp->icmp_ip_recvpktinfo && (pinfo != NULL) &&
35703318Srshoaib 			    (pinfo->ip_pkt_flags & IPF_RECVADDR)) {
35713318Srshoaib 				struct	T_opthdr *toh;
35723318Srshoaib 				struct	in_pktinfo *pktinfop;
35733318Srshoaib 
35743318Srshoaib 				toh = (struct T_opthdr *)dstopt;
35753318Srshoaib 				toh->level = IPPROTO_IP;
35763318Srshoaib 				toh->name = IP_PKTINFO;
35773318Srshoaib 				toh->len = sizeof (struct T_opthdr) +
35783318Srshoaib 				    sizeof (in_pktinfo_t);
35793318Srshoaib 				toh->status = 0;
35803318Srshoaib 				dstopt += sizeof (struct T_opthdr);
35813318Srshoaib 				pktinfop = (struct in_pktinfo *)dstopt;
35823318Srshoaib 				pktinfop->ipi_ifindex = pinfo->ip_pkt_ifindex;
35833318Srshoaib 				pktinfop->ipi_spec_dst =
35843318Srshoaib 				    pinfo->ip_pkt_match_addr;
35853318Srshoaib 
35863318Srshoaib 				pktinfop->ipi_addr.s_addr = ipha->ipha_dst;
35873318Srshoaib 
35883318Srshoaib 				dstopt += sizeof (struct in_pktinfo);
35891673Sgt145670 				udi_size -= toh->len;
35901673Sgt145670 			}
35910Sstevel@tonic-gate 
35920Sstevel@tonic-gate 			/* Consumed all of allocated space */
35930Sstevel@tonic-gate 			ASSERT(udi_size == 0);
35940Sstevel@tonic-gate 		}
35950Sstevel@tonic-gate 
35965267Snordmark 		if (options_mp != NULL)
35975267Snordmark 			freeb(options_mp);
35985267Snordmark 
35995240Snordmark 		BUMP_MIB(&is->is_rawip_mib, rawipInDatagrams);
36008348SEric.Yu@Sun.COM 		goto deliver;
36010Sstevel@tonic-gate 	}
36020Sstevel@tonic-gate 
36030Sstevel@tonic-gate 	/*
36040Sstevel@tonic-gate 	 * We don't need options_mp in the IPv6 path.
36050Sstevel@tonic-gate 	 */
36060Sstevel@tonic-gate 	if (options_mp != NULL) {
36070Sstevel@tonic-gate 		freeb(options_mp);
36080Sstevel@tonic-gate 		options_mp = NULL;
36090Sstevel@tonic-gate 	}
36100Sstevel@tonic-gate 
36110Sstevel@tonic-gate 	/*
36120Sstevel@tonic-gate 	 * Discard message if it is smaller than the IPv6 header
36130Sstevel@tonic-gate 	 * or if the header is malformed.
36140Sstevel@tonic-gate 	 */
36150Sstevel@tonic-gate 	if ((mp->b_wptr - rptr) < sizeof (ip6_t) ||
36160Sstevel@tonic-gate 	    IPH_HDR_VERSION((ipha_t *)rptr) != IPV6_VERSION ||
36170Sstevel@tonic-gate 	    icmp->icmp_family != AF_INET6) {
36180Sstevel@tonic-gate 		freemsg(mp);
36195240Snordmark 		BUMP_MIB(&is->is_rawip_mib, rawipInErrors);
36200Sstevel@tonic-gate 		return;
36210Sstevel@tonic-gate 	}
36220Sstevel@tonic-gate 
36230Sstevel@tonic-gate 	/* Initialize */
36240Sstevel@tonic-gate 	ipp.ipp_fields = 0;
36251676Sjpk 	hopstrip = 0;
36260Sstevel@tonic-gate 
36270Sstevel@tonic-gate 	ip6h = (ip6_t *)rptr;
36280Sstevel@tonic-gate 	/*
36290Sstevel@tonic-gate 	 * Call on ip_find_hdr_v6 which gets the total hdr len
36300Sstevel@tonic-gate 	 * as well as individual lenghts of ext hdrs (and ptrs to
36310Sstevel@tonic-gate 	 * them).
36320Sstevel@tonic-gate 	 */
36330Sstevel@tonic-gate 	if (ip6h->ip6_nxt != icmp->icmp_proto) {
36340Sstevel@tonic-gate 		/* Look for ifindex information */
36350Sstevel@tonic-gate 		if (ip6h->ip6_nxt == IPPROTO_RAW) {
36360Sstevel@tonic-gate 			ip6i = (ip6i_t *)ip6h;
36370Sstevel@tonic-gate 			if (ip6i->ip6i_flags & IP6I_IFINDEX) {
36380Sstevel@tonic-gate 				ASSERT(ip6i->ip6i_ifindex != 0);
36390Sstevel@tonic-gate 				ipp.ipp_fields |= IPPF_IFINDEX;
36400Sstevel@tonic-gate 				ipp.ipp_ifindex = ip6i->ip6i_ifindex;
36410Sstevel@tonic-gate 			}
36420Sstevel@tonic-gate 			rptr = (uchar_t *)&ip6i[1];
36430Sstevel@tonic-gate 			mp->b_rptr = rptr;
36440Sstevel@tonic-gate 			if (rptr == mp->b_wptr) {
36450Sstevel@tonic-gate 				mp1 = mp->b_cont;
36460Sstevel@tonic-gate 				freeb(mp);
36470Sstevel@tonic-gate 				mp = mp1;
36480Sstevel@tonic-gate 				rptr = mp->b_rptr;
36490Sstevel@tonic-gate 			}
36500Sstevel@tonic-gate 			ASSERT(mp->b_wptr - rptr >= IPV6_HDR_LEN);
36510Sstevel@tonic-gate 			ip6h = (ip6_t *)rptr;
36520Sstevel@tonic-gate 		}
36530Sstevel@tonic-gate 		hdr_len = ip_find_hdr_v6(mp, ip6h, &ipp, &nexthdr);
36541676Sjpk 
36551676Sjpk 		/*
36561676Sjpk 		 * We need to lie a bit to the user because users inside
36571676Sjpk 		 * labeled compartments should not see their own labels.  We
36581676Sjpk 		 * assume that in all other respects IP has checked the label,
36591676Sjpk 		 * and that the label is always first among the options.  (If
36601676Sjpk 		 * it's not first, then this code won't see it, and the option
36611676Sjpk 		 * will be passed along to the user.)
36621676Sjpk 		 *
36631676Sjpk 		 * If we had multilevel ICMP sockets, then the following code
36641676Sjpk 		 * should be skipped for them to allow the user to see the
36651676Sjpk 		 * label.
36661676Sjpk 		 *
36671676Sjpk 		 * Alignment restrictions in the definition of IP options
36681676Sjpk 		 * (namely, the requirement that the 4-octet DOI goes on a
36691676Sjpk 		 * 4-octet boundary) mean that we know exactly where the option
36701676Sjpk 		 * should start, but we're lenient for other hosts.
36711676Sjpk 		 *
36721676Sjpk 		 * Note that there are no multilevel ICMP or raw IP sockets
36731676Sjpk 		 * yet, thus nobody ever sees the IP6OPT_LS option.
36741676Sjpk 		 */
36751676Sjpk 		if ((ipp.ipp_fields & IPPF_HOPOPTS) &&
36761676Sjpk 		    ipp.ipp_hopoptslen > 5 && is_system_labeled()) {
36771676Sjpk 			const uchar_t *ucp =
36781676Sjpk 			    (const uchar_t *)ipp.ipp_hopopts + 2;
36791676Sjpk 			int remlen = ipp.ipp_hopoptslen - 2;
36801676Sjpk 
36811676Sjpk 			while (remlen > 0) {
36821676Sjpk 				if (*ucp == IP6OPT_PAD1) {
36831676Sjpk 					remlen--;
36841676Sjpk 					ucp++;
36851676Sjpk 				} else if (*ucp == IP6OPT_PADN) {
36861676Sjpk 					remlen -= ucp[1] + 2;
36871676Sjpk 					ucp += ucp[1] + 2;
36881676Sjpk 				} else if (*ucp == ip6opt_ls) {
36891676Sjpk 					hopstrip = (ucp -
36901676Sjpk 					    (const uchar_t *)ipp.ipp_hopopts) +
36911676Sjpk 					    ucp[1] + 2;
36921676Sjpk 					hopstrip = (hopstrip + 7) & ~7;
36931676Sjpk 					break;
36941676Sjpk 				} else {
36951676Sjpk 					/* label option must be first */
36961676Sjpk 					break;
36971676Sjpk 				}
36981676Sjpk 			}
36991676Sjpk 		}
37000Sstevel@tonic-gate 	} else {
37010Sstevel@tonic-gate 		hdr_len = IPV6_HDR_LEN;
37020Sstevel@tonic-gate 		ip6i = NULL;
37030Sstevel@tonic-gate 		nexthdr = ip6h->ip6_nxt;
37040Sstevel@tonic-gate 	}
37050Sstevel@tonic-gate 	/*
37060Sstevel@tonic-gate 	 * One special case where IP attaches the IRE needs to
37070Sstevel@tonic-gate 	 * be handled so that we don't send up IRE to the user land.
37080Sstevel@tonic-gate 	 */
37090Sstevel@tonic-gate 	if (nexthdr == IPPROTO_TCP) {
37100Sstevel@tonic-gate 		tcph_t *tcph = (tcph_t *)&mp->b_rptr[hdr_len];
37110Sstevel@tonic-gate 
37120Sstevel@tonic-gate 		if (((tcph->th_flags[0] & (TH_SYN|TH_ACK)) == TH_SYN) &&
37130Sstevel@tonic-gate 		    mp->b_cont != NULL) {
37140Sstevel@tonic-gate 			mp1 = mp->b_cont;
37150Sstevel@tonic-gate 			if (mp1->b_datap->db_type == IRE_DB_TYPE) {
37160Sstevel@tonic-gate 				freeb(mp1);
37170Sstevel@tonic-gate 				mp->b_cont = NULL;
37180Sstevel@tonic-gate 			}
37190Sstevel@tonic-gate 		}
37200Sstevel@tonic-gate 	}
37210Sstevel@tonic-gate 	/*
37220Sstevel@tonic-gate 	 * Check a filter for ICMPv6 types if needed.
37230Sstevel@tonic-gate 	 * Verify raw checksums if needed.
37240Sstevel@tonic-gate 	 */
37250Sstevel@tonic-gate 	if (icmp->icmp_filter != NULL || icmp->icmp_raw_checksum) {
37260Sstevel@tonic-gate 		if (icmp->icmp_filter != NULL) {
37270Sstevel@tonic-gate 			int type;
37280Sstevel@tonic-gate 
37290Sstevel@tonic-gate 			/* Assumes that IP has done the pullupmsg */
37300Sstevel@tonic-gate 			type = mp->b_rptr[hdr_len];
37310Sstevel@tonic-gate 
37320Sstevel@tonic-gate 			ASSERT(mp->b_rptr + hdr_len <= mp->b_wptr);
37330Sstevel@tonic-gate 			if (ICMP6_FILTER_WILLBLOCK(type, icmp->icmp_filter)) {
37340Sstevel@tonic-gate 				freemsg(mp);
37350Sstevel@tonic-gate 				return;
37360Sstevel@tonic-gate 			}
37370Sstevel@tonic-gate 		} else {
37380Sstevel@tonic-gate 			/* Checksum */
37390Sstevel@tonic-gate 			uint16_t	*up;
37400Sstevel@tonic-gate 			uint32_t	sum;
37410Sstevel@tonic-gate 			int		remlen;
37420Sstevel@tonic-gate 
37430Sstevel@tonic-gate 			up = (uint16_t *)&ip6h->ip6_src;
37440Sstevel@tonic-gate 
37450Sstevel@tonic-gate 			remlen = msgdsize(mp) - hdr_len;
37460Sstevel@tonic-gate 			sum = htons(icmp->icmp_proto + remlen)
37470Sstevel@tonic-gate 			    + up[0] + up[1] + up[2] + up[3]
37480Sstevel@tonic-gate 			    + up[4] + up[5] + up[6] + up[7]
37490Sstevel@tonic-gate 			    + up[8] + up[9] + up[10] + up[11]
37500Sstevel@tonic-gate 			    + up[12] + up[13] + up[14] + up[15];
37510Sstevel@tonic-gate 			sum = (sum & 0xffff) + (sum >> 16);
37520Sstevel@tonic-gate 			sum = IP_CSUM(mp, hdr_len, sum);
37530Sstevel@tonic-gate 			if (sum != 0) {
37540Sstevel@tonic-gate 				/* IPv6 RAW checksum failed */
37550Sstevel@tonic-gate 				ip0dbg(("icmp_rput: RAW checksum "
37560Sstevel@tonic-gate 				    "failed %x\n", sum));
37570Sstevel@tonic-gate 				freemsg(mp);
37585240Snordmark 				BUMP_MIB(&is->is_rawip_mib,
37593448Sdh155122 				    rawipInCksumErrs);
37600Sstevel@tonic-gate 				return;
37610Sstevel@tonic-gate 			}
37620Sstevel@tonic-gate 		}
37630Sstevel@tonic-gate 	}
37640Sstevel@tonic-gate 	/* Skip all the IPv6 headers per API */
37650Sstevel@tonic-gate 	mp->b_rptr += hdr_len;
37660Sstevel@tonic-gate 
37670Sstevel@tonic-gate 	udi_size = sizeof (struct T_unitdata_ind) + sizeof (sin6_t);
37680Sstevel@tonic-gate 
37690Sstevel@tonic-gate 	/*
37700Sstevel@tonic-gate 	 * We use local variables icmp_opt and icmp_ipv6_recvhoplimit to
37710Sstevel@tonic-gate 	 * maintain state information, instead of relying on icmp_t
37720Sstevel@tonic-gate 	 * structure, since there arent any locks protecting these members
37730Sstevel@tonic-gate 	 * and there is a window where there might be a race between a
37740Sstevel@tonic-gate 	 * thread setting options on the write side and a thread reading
37750Sstevel@tonic-gate 	 * these options on the read size.
37760Sstevel@tonic-gate 	 */
37770Sstevel@tonic-gate 	if (ipp.ipp_fields & (IPPF_HOPOPTS|IPPF_DSTOPTS|IPPF_RTDSTOPTS|
37780Sstevel@tonic-gate 	    IPPF_RTHDR|IPPF_IFINDEX)) {
37790Sstevel@tonic-gate 		if (icmp->icmp_ipv6_recvhopopts &&
37801676Sjpk 		    (ipp.ipp_fields & IPPF_HOPOPTS) &&
37811676Sjpk 		    ipp.ipp_hopoptslen > hopstrip) {
37820Sstevel@tonic-gate 			udi_size += sizeof (struct T_opthdr) +
37831676Sjpk 			    ipp.ipp_hopoptslen - hopstrip;
37840Sstevel@tonic-gate 			icmp_opt |= IPPF_HOPOPTS;
37850Sstevel@tonic-gate 		}
37860Sstevel@tonic-gate 		if ((icmp->icmp_ipv6_recvdstopts ||
37875240Snordmark 		    icmp->icmp_old_ipv6_recvdstopts) &&
37880Sstevel@tonic-gate 		    (ipp.ipp_fields & IPPF_DSTOPTS)) {
37890Sstevel@tonic-gate 			udi_size += sizeof (struct T_opthdr) +
37900Sstevel@tonic-gate 			    ipp.ipp_dstoptslen;
37910Sstevel@tonic-gate 			icmp_opt |= IPPF_DSTOPTS;
37920Sstevel@tonic-gate 		}
37930Sstevel@tonic-gate 		if (((icmp->icmp_ipv6_recvdstopts &&
37940Sstevel@tonic-gate 		    icmp->icmp_ipv6_recvrthdr &&
37950Sstevel@tonic-gate 		    (ipp.ipp_fields & IPPF_RTHDR)) ||
37960Sstevel@tonic-gate 		    icmp->icmp_ipv6_recvrtdstopts) &&
37970Sstevel@tonic-gate 		    (ipp.ipp_fields & IPPF_RTDSTOPTS)) {
37980Sstevel@tonic-gate 			udi_size += sizeof (struct T_opthdr) +
37990Sstevel@tonic-gate 			    ipp.ipp_rtdstoptslen;
38000Sstevel@tonic-gate 			icmp_opt |= IPPF_RTDSTOPTS;
38010Sstevel@tonic-gate 		}
38020Sstevel@tonic-gate 		if (icmp->icmp_ipv6_recvrthdr &&
38030Sstevel@tonic-gate 		    (ipp.ipp_fields & IPPF_RTHDR)) {
38040Sstevel@tonic-gate 			udi_size += sizeof (struct T_opthdr) +
38050Sstevel@tonic-gate 			    ipp.ipp_rthdrlen;
38060Sstevel@tonic-gate 			icmp_opt |= IPPF_RTHDR;
38070Sstevel@tonic-gate 		}
38083318Srshoaib 		if (icmp->icmp_ip_recvpktinfo &&
38090Sstevel@tonic-gate 		    (ipp.ipp_fields & IPPF_IFINDEX)) {
38100Sstevel@tonic-gate 			udi_size += sizeof (struct T_opthdr) +
38110Sstevel@tonic-gate 			    sizeof (struct in6_pktinfo);
38120Sstevel@tonic-gate 			icmp_opt |= IPPF_IFINDEX;
38130Sstevel@tonic-gate 		}
38140Sstevel@tonic-gate 	}
38150Sstevel@tonic-gate 	if (icmp->icmp_ipv6_recvhoplimit) {
38160Sstevel@tonic-gate 		udi_size += sizeof (struct T_opthdr) + sizeof (int);
38170Sstevel@tonic-gate 		icmp_ipv6_recvhoplimit = B_TRUE;
38180Sstevel@tonic-gate 	}
38190Sstevel@tonic-gate 
38200Sstevel@tonic-gate 	if (icmp->icmp_ipv6_recvtclass)
38210Sstevel@tonic-gate 		udi_size += sizeof (struct T_opthdr) + sizeof (int);
38220Sstevel@tonic-gate 
38235401Snordmark 	/*
38245401Snordmark 	 * If SO_TIMESTAMP is set allocate the appropriate sized
38255401Snordmark 	 * buffer. Since gethrestime() expects a pointer aligned
38265401Snordmark 	 * argument, we allocate space necessary for extra
38275401Snordmark 	 * alignment (even though it might not be used).
38285401Snordmark 	 */
38295401Snordmark 	if (icmp->icmp_timestamp) {
38305401Snordmark 		udi_size += sizeof (struct T_opthdr) +
38315401Snordmark 		    sizeof (timestruc_t) + _POINTER_ALIGNMENT;
38325401Snordmark 	}
38335401Snordmark 
38340Sstevel@tonic-gate 	mp1 = allocb(udi_size, BPRI_MED);
38350Sstevel@tonic-gate 	if (mp1 == NULL) {
38360Sstevel@tonic-gate 		freemsg(mp);
38375240Snordmark 		BUMP_MIB(&is->is_rawip_mib, rawipInErrors);
38380Sstevel@tonic-gate 		return;
38390Sstevel@tonic-gate 	}
38400Sstevel@tonic-gate 	mp1->b_cont = mp;
38410Sstevel@tonic-gate 	mp = mp1;
38420Sstevel@tonic-gate 	mp->b_datap->db_type = M_PROTO;
38430Sstevel@tonic-gate 	tudi = (struct T_unitdata_ind *)mp->b_rptr;
38440Sstevel@tonic-gate 	mp->b_wptr = (uchar_t *)tudi + udi_size;
38450Sstevel@tonic-gate 	tudi->PRIM_type = T_UNITDATA_IND;
38460Sstevel@tonic-gate 	tudi->SRC_length = sizeof (sin6_t);
38470Sstevel@tonic-gate 	tudi->SRC_offset = sizeof (struct T_unitdata_ind);
38480Sstevel@tonic-gate 	tudi->OPT_offset = sizeof (struct T_unitdata_ind) + sizeof (sin6_t);
38490Sstevel@tonic-gate 	udi_size -= (sizeof (struct T_unitdata_ind) + sizeof (sin6_t));
38500Sstevel@tonic-gate 	tudi->OPT_length = udi_size;
38510Sstevel@tonic-gate 	sin6 = (sin6_t *)&tudi[1];
38520Sstevel@tonic-gate 	sin6->sin6_port = 0;
38530Sstevel@tonic-gate 	sin6->sin6_family = AF_INET6;
38540Sstevel@tonic-gate 
38550Sstevel@tonic-gate 	sin6->sin6_addr = ip6h->ip6_src;
38560Sstevel@tonic-gate 	/* No sin6_flowinfo per API */
38570Sstevel@tonic-gate 	sin6->sin6_flowinfo = 0;
38580Sstevel@tonic-gate 	/* For link-scope source pass up scope id */
38590Sstevel@tonic-gate 	if ((ipp.ipp_fields & IPPF_IFINDEX) &&
38600Sstevel@tonic-gate 	    IN6_IS_ADDR_LINKSCOPE(&ip6h->ip6_src))
38610Sstevel@tonic-gate 		sin6->sin6_scope_id = ipp.ipp_ifindex;
38620Sstevel@tonic-gate 	else
38630Sstevel@tonic-gate 		sin6->sin6_scope_id = 0;
38640Sstevel@tonic-gate 
38650Sstevel@tonic-gate 	sin6->__sin6_src_id = ip_srcid_find_addr(&ip6h->ip6_dst,
38663448Sdh155122 	    icmp->icmp_zoneid, is->is_netstack);
38670Sstevel@tonic-gate 
38680Sstevel@tonic-gate 	if (udi_size != 0) {
38690Sstevel@tonic-gate 		uchar_t *dstopt;
38700Sstevel@tonic-gate 
38710Sstevel@tonic-gate 		dstopt = (uchar_t *)&sin6[1];
38720Sstevel@tonic-gate 		if (icmp_opt & IPPF_IFINDEX) {
38730Sstevel@tonic-gate 			struct T_opthdr *toh;
38740Sstevel@tonic-gate 			struct in6_pktinfo *pkti;
38750Sstevel@tonic-gate 
38760Sstevel@tonic-gate 			toh = (struct T_opthdr *)dstopt;
38770Sstevel@tonic-gate 			toh->level = IPPROTO_IPV6;
38780Sstevel@tonic-gate 			toh->name = IPV6_PKTINFO;
38790Sstevel@tonic-gate 			toh->len = sizeof (struct T_opthdr) +
38800Sstevel@tonic-gate 			    sizeof (*pkti);
38810Sstevel@tonic-gate 			toh->status = 0;
38820Sstevel@tonic-gate 			dstopt += sizeof (struct T_opthdr);
38830Sstevel@tonic-gate 			pkti = (struct in6_pktinfo *)dstopt;
38840Sstevel@tonic-gate 			pkti->ipi6_addr = ip6h->ip6_dst;
38850Sstevel@tonic-gate 			pkti->ipi6_ifindex = ipp.ipp_ifindex;
38860Sstevel@tonic-gate 			dstopt += sizeof (*pkti);
38870Sstevel@tonic-gate 			udi_size -= toh->len;
38880Sstevel@tonic-gate 		}
38890Sstevel@tonic-gate 		if (icmp_ipv6_recvhoplimit) {
38900Sstevel@tonic-gate 			struct T_opthdr *toh;
38910Sstevel@tonic-gate 
38920Sstevel@tonic-gate 			toh = (struct T_opthdr *)dstopt;
38930Sstevel@tonic-gate 			toh->level = IPPROTO_IPV6;
38940Sstevel@tonic-gate 			toh->name = IPV6_HOPLIMIT;
38950Sstevel@tonic-gate 			toh->len = sizeof (struct T_opthdr) +
38960Sstevel@tonic-gate 			    sizeof (uint_t);
38970Sstevel@tonic-gate 			toh->status = 0;
38980Sstevel@tonic-gate 			dstopt += sizeof (struct T_opthdr);
38990Sstevel@tonic-gate 			*(uint_t *)dstopt = ip6h->ip6_hops;
39000Sstevel@tonic-gate 			dstopt += sizeof (uint_t);
39010Sstevel@tonic-gate 			udi_size -= toh->len;
39020Sstevel@tonic-gate 		}
39030Sstevel@tonic-gate 		if (icmp->icmp_ipv6_recvtclass) {
39040Sstevel@tonic-gate 			struct T_opthdr *toh;
39050Sstevel@tonic-gate 
39060Sstevel@tonic-gate 			toh = (struct T_opthdr *)dstopt;
39070Sstevel@tonic-gate 			toh->level = IPPROTO_IPV6;
39080Sstevel@tonic-gate 			toh->name = IPV6_TCLASS;
39090Sstevel@tonic-gate 			toh->len = sizeof (struct T_opthdr) +
39100Sstevel@tonic-gate 			    sizeof (uint_t);
39110Sstevel@tonic-gate 			toh->status = 0;
39120Sstevel@tonic-gate 			dstopt += sizeof (struct T_opthdr);
39130Sstevel@tonic-gate 			*(uint_t *)dstopt = IPV6_FLOW_TCLASS(ip6h->ip6_flow);
39140Sstevel@tonic-gate 			dstopt += sizeof (uint_t);
39150Sstevel@tonic-gate 			udi_size -= toh->len;
39160Sstevel@tonic-gate 		}
39175401Snordmark 		if (icmp->icmp_timestamp) {
39188348SEric.Yu@Sun.COM 			struct  T_opthdr *toh;
39195401Snordmark 
39205401Snordmark 			toh = (struct T_opthdr *)dstopt;
39215401Snordmark 			toh->level = SOL_SOCKET;
39225401Snordmark 			toh->name = SCM_TIMESTAMP;
39235401Snordmark 			toh->len = sizeof (struct T_opthdr) +
39245401Snordmark 			    sizeof (timestruc_t) + _POINTER_ALIGNMENT;
39255401Snordmark 			toh->status = 0;
39265401Snordmark 			dstopt += sizeof (struct T_opthdr);
39275401Snordmark 			/* Align for gethrestime() */
39285401Snordmark 			dstopt = (uchar_t *)P2ROUNDUP((intptr_t)dstopt,
39295401Snordmark 			    sizeof (intptr_t));
39305401Snordmark 			gethrestime((timestruc_t *)dstopt);
39315401Snordmark 			dstopt = (uchar_t *)toh + toh->len;
39325401Snordmark 			udi_size -= toh->len;
39335401Snordmark 		}
39348348SEric.Yu@Sun.COM 
39350Sstevel@tonic-gate 		if (icmp_opt & IPPF_HOPOPTS) {
39360Sstevel@tonic-gate 			struct T_opthdr *toh;
39370Sstevel@tonic-gate 
39380Sstevel@tonic-gate 			toh = (struct T_opthdr *)dstopt;
39390Sstevel@tonic-gate 			toh->level = IPPROTO_IPV6;
39400Sstevel@tonic-gate 			toh->name = IPV6_HOPOPTS;
39410Sstevel@tonic-gate 			toh->len = sizeof (struct T_opthdr) +
39421676Sjpk 			    ipp.ipp_hopoptslen - hopstrip;
39430Sstevel@tonic-gate 			toh->status = 0;
39440Sstevel@tonic-gate 			dstopt += sizeof (struct T_opthdr);
39451676Sjpk 			bcopy((char *)ipp.ipp_hopopts + hopstrip, dstopt,
39461676Sjpk 			    ipp.ipp_hopoptslen - hopstrip);
39471676Sjpk 			if (hopstrip > 0) {
39481676Sjpk 				/* copy next header value and fake length */
39491676Sjpk 				dstopt[0] = ((uchar_t *)ipp.ipp_hopopts)[0];
39501676Sjpk 				dstopt[1] = ((uchar_t *)ipp.ipp_hopopts)[1] -
39511676Sjpk 				    hopstrip / 8;
39521676Sjpk 			}
39531676Sjpk 			dstopt += ipp.ipp_hopoptslen - hopstrip;
39540Sstevel@tonic-gate 			udi_size -= toh->len;
39550Sstevel@tonic-gate 		}
39560Sstevel@tonic-gate 		if (icmp_opt & IPPF_RTDSTOPTS) {
39570Sstevel@tonic-gate 			struct T_opthdr *toh;
39580Sstevel@tonic-gate 
39590Sstevel@tonic-gate 			toh = (struct T_opthdr *)dstopt;
39600Sstevel@tonic-gate 			toh->level = IPPROTO_IPV6;
39610Sstevel@tonic-gate 			toh->name = IPV6_DSTOPTS;
39620Sstevel@tonic-gate 			toh->len = sizeof (struct T_opthdr) +
39630Sstevel@tonic-gate 			    ipp.ipp_rtdstoptslen;
39640Sstevel@tonic-gate 			toh->status = 0;
39650Sstevel@tonic-gate 			dstopt += sizeof (struct T_opthdr);
39660Sstevel@tonic-gate 			bcopy(ipp.ipp_rtdstopts, dstopt,
39670Sstevel@tonic-gate 			    ipp.ipp_rtdstoptslen);
39680Sstevel@tonic-gate 			dstopt += ipp.ipp_rtdstoptslen;
39690Sstevel@tonic-gate 			udi_size -= toh->len;
39700Sstevel@tonic-gate 		}
39710Sstevel@tonic-gate 		if (icmp_opt & IPPF_RTHDR) {
39720Sstevel@tonic-gate 			struct T_opthdr *toh;
39730Sstevel@tonic-gate 
39740Sstevel@tonic-gate 			toh = (struct T_opthdr *)dstopt;
39750Sstevel@tonic-gate 			toh->level = IPPROTO_IPV6;
39760Sstevel@tonic-gate 			toh->name = IPV6_RTHDR;
39770Sstevel@tonic-gate 			toh->len = sizeof (struct T_opthdr) +
39780Sstevel@tonic-gate 			    ipp.ipp_rthdrlen;
39790Sstevel@tonic-gate 			toh->status = 0;
39800Sstevel@tonic-gate 			dstopt += sizeof (struct T_opthdr);
39810Sstevel@tonic-gate 			bcopy(ipp.ipp_rthdr, dstopt, ipp.ipp_rthdrlen);
39820Sstevel@tonic-gate 			dstopt += ipp.ipp_rthdrlen;
39830Sstevel@tonic-gate 			udi_size -= toh->len;
39840Sstevel@tonic-gate 		}
39850Sstevel@tonic-gate 		if (icmp_opt & IPPF_DSTOPTS) {
39860Sstevel@tonic-gate 			struct T_opthdr *toh;
39870Sstevel@tonic-gate 
39880Sstevel@tonic-gate 			toh = (struct T_opthdr *)dstopt;
39890Sstevel@tonic-gate 			toh->level = IPPROTO_IPV6;
39900Sstevel@tonic-gate 			toh->name = IPV6_DSTOPTS;
39910Sstevel@tonic-gate 			toh->len = sizeof (struct T_opthdr) +
39920Sstevel@tonic-gate 			    ipp.ipp_dstoptslen;
39930Sstevel@tonic-gate 			toh->status = 0;
39940Sstevel@tonic-gate 			dstopt += sizeof (struct T_opthdr);
39950Sstevel@tonic-gate 			bcopy(ipp.ipp_dstopts, dstopt,
39960Sstevel@tonic-gate 			    ipp.ipp_dstoptslen);
39970Sstevel@tonic-gate 			dstopt += ipp.ipp_dstoptslen;
39980Sstevel@tonic-gate 			udi_size -= toh->len;
39990Sstevel@tonic-gate 		}
40000Sstevel@tonic-gate 		/* Consumed all of allocated space */
40010Sstevel@tonic-gate 		ASSERT(udi_size == 0);
40020Sstevel@tonic-gate 	}
40035240Snordmark 	BUMP_MIB(&is->is_rawip_mib, rawipInDatagrams);
40048348SEric.Yu@Sun.COM 
40058348SEric.Yu@Sun.COM deliver:
40068348SEric.Yu@Sun.COM 	if (IPCL_IS_NONSTR(connp)) {
40078348SEric.Yu@Sun.COM 		if ((*connp->conn_upcalls->su_recv)
40088348SEric.Yu@Sun.COM 		    (connp->conn_upper_handle, mp, msgdsize(mp), 0, &error,
40098348SEric.Yu@Sun.COM 		    NULL) < 0) {
40108348SEric.Yu@Sun.COM 			mutex_enter(&icmp->icmp_recv_lock);
40118348SEric.Yu@Sun.COM 			if (error == ENOSPC) {
40128348SEric.Yu@Sun.COM 				/*
40138348SEric.Yu@Sun.COM 				 * let's confirm while holding the lock
40148348SEric.Yu@Sun.COM 				 */
40158348SEric.Yu@Sun.COM 				if ((*connp->conn_upcalls->su_recv)
40168348SEric.Yu@Sun.COM 				    (connp->conn_upper_handle, NULL, 0, 0,
40178348SEric.Yu@Sun.COM 				    &error, NULL) < 0) {
40188348SEric.Yu@Sun.COM 					if (error == ENOSPC) {
40198348SEric.Yu@Sun.COM 						connp->conn_flow_cntrld =
40208348SEric.Yu@Sun.COM 						    B_TRUE;
40218348SEric.Yu@Sun.COM 					} else {
40228348SEric.Yu@Sun.COM 						ASSERT(error == EOPNOTSUPP);
40238348SEric.Yu@Sun.COM 					}
40240Sstevel@tonic-gate 				}
40258348SEric.Yu@Sun.COM 				mutex_exit(&icmp->icmp_recv_lock);
40268348SEric.Yu@Sun.COM 			} else {
40278348SEric.Yu@Sun.COM 				ASSERT(error == EOPNOTSUPP);
40288348SEric.Yu@Sun.COM 				icmp_queue_fallback(icmp, mp);
40290Sstevel@tonic-gate 			}
40300Sstevel@tonic-gate 		}
40318348SEric.Yu@Sun.COM 	} else {
40328348SEric.Yu@Sun.COM 		putnext(connp->conn_rq, mp);
40338348SEric.Yu@Sun.COM 	}
40348348SEric.Yu@Sun.COM 	ASSERT(MUTEX_NOT_HELD(&icmp->icmp_recv_lock));
40350Sstevel@tonic-gate }
40360Sstevel@tonic-gate 
40370Sstevel@tonic-gate /*
40380Sstevel@tonic-gate  * return SNMP stuff in buffer in mpdata
40390Sstevel@tonic-gate  */
40405240Snordmark mblk_t *
40410Sstevel@tonic-gate icmp_snmp_get(queue_t *q, mblk_t *mpctl)
40420Sstevel@tonic-gate {
40430Sstevel@tonic-gate 	mblk_t			*mpdata;
40440Sstevel@tonic-gate 	struct opthdr		*optp;
40455240Snordmark 	conn_t			*connp = Q_TO_CONN(q);
40465240Snordmark 	icmp_stack_t		*is = connp->conn_netstack->netstack_icmp;
40475240Snordmark 	mblk_t			*mp2ctl;
40485240Snordmark 
40495240Snordmark 	/*
40505240Snordmark 	 * make a copy of the original message
40515240Snordmark 	 */
40525240Snordmark 	mp2ctl = copymsg(mpctl);
40530Sstevel@tonic-gate 
40540Sstevel@tonic-gate 	if (mpctl == NULL ||
40550Sstevel@tonic-gate 	    (mpdata = mpctl->b_cont) == NULL) {
40565240Snordmark 		freemsg(mpctl);
40575240Snordmark 		freemsg(mp2ctl);
40580Sstevel@tonic-gate 		return (0);
40590Sstevel@tonic-gate 	}
40600Sstevel@tonic-gate 
40610Sstevel@tonic-gate 	/* fixed length structure for IPv4 and IPv6 counters */
40620Sstevel@tonic-gate 	optp = (struct opthdr *)&mpctl->b_rptr[sizeof (struct T_optmgmt_ack)];
40630Sstevel@tonic-gate 	optp->level = EXPER_RAWIP;
40640Sstevel@tonic-gate 	optp->name = 0;
40655240Snordmark 	(void) snmp_append_data(mpdata, (char *)&is->is_rawip_mib,
40665240Snordmark 	    sizeof (is->is_rawip_mib));
40670Sstevel@tonic-gate 	optp->len = msgdsize(mpdata);
40680Sstevel@tonic-gate 	qreply(q, mpctl);
40690Sstevel@tonic-gate 
40705240Snordmark 	return (mp2ctl);
40710Sstevel@tonic-gate }
40720Sstevel@tonic-gate 
40730Sstevel@tonic-gate /*
40740Sstevel@tonic-gate  * Return 0 if invalid set request, 1 otherwise, including non-rawip requests.
40750Sstevel@tonic-gate  * TODO:  If this ever actually tries to set anything, it needs to be
40760Sstevel@tonic-gate  * to do the appropriate locking.
40770Sstevel@tonic-gate  */
40780Sstevel@tonic-gate /* ARGSUSED */
40795240Snordmark int
40800Sstevel@tonic-gate icmp_snmp_set(queue_t *q, t_scalar_t level, t_scalar_t name,
40810Sstevel@tonic-gate     uchar_t *ptr, int len)
40820Sstevel@tonic-gate {
40830Sstevel@tonic-gate 	switch (level) {
40840Sstevel@tonic-gate 	case EXPER_RAWIP:
40850Sstevel@tonic-gate 		return (0);
40860Sstevel@tonic-gate 	default:
40870Sstevel@tonic-gate 		return (1);
40880Sstevel@tonic-gate 	}
40890Sstevel@tonic-gate }
40900Sstevel@tonic-gate 
40910Sstevel@tonic-gate /* Report for ndd "icmp_status" */
40920Sstevel@tonic-gate /* ARGSUSED */
40930Sstevel@tonic-gate static int
40940Sstevel@tonic-gate icmp_status_report(queue_t *q, mblk_t *mp, caddr_t cp, cred_t *cr)
40950Sstevel@tonic-gate {
40965240Snordmark 	conn_t  *connp;
40975240Snordmark 	ip_stack_t *ipst;
40980Sstevel@tonic-gate 	char	laddrbuf[INET6_ADDRSTRLEN];
40990Sstevel@tonic-gate 	char	faddrbuf[INET6_ADDRSTRLEN];
41005240Snordmark 	int	i;
41010Sstevel@tonic-gate 
41020Sstevel@tonic-gate 	(void) mi_mpprintf(mp,
41030Sstevel@tonic-gate 	    "RAWIP    " MI_COL_HDRPAD_STR
41040Sstevel@tonic-gate 	/*   01234567[89ABCDEF] */
41050Sstevel@tonic-gate 	    "  src addr        dest addr       state");
41060Sstevel@tonic-gate 	/*   xxx.xxx.xxx.xxx xxx.xxx.xxx.xxx UNBOUND */
41070Sstevel@tonic-gate 
41085240Snordmark 	connp = Q_TO_CONN(q);
41095240Snordmark 	ipst = connp->conn_netstack->netstack_ip;
41105240Snordmark 	for (i = 0; i < CONN_G_HASH_SIZE; i++) {
41115240Snordmark 		connf_t *connfp;
41125240Snordmark 		char	*state;
41135240Snordmark 
41145240Snordmark 		connfp = &ipst->ips_ipcl_globalhash_fanout[i];
41155240Snordmark 		connp = NULL;
41165240Snordmark 
41175240Snordmark 		while ((connp = ipcl_get_next_conn(connfp, connp,
41185240Snordmark 		    IPCL_RAWIPCONN)) != NULL) {
41195240Snordmark 			icmp_t  *icmp;
41205240Snordmark 
41215240Snordmark 			mutex_enter(&(connp)->conn_lock);
41225240Snordmark 			icmp = connp->conn_icmp;
41235240Snordmark 
41245240Snordmark 			if (icmp->icmp_state == TS_UNBND)
41255240Snordmark 				state = "UNBOUND";
41265240Snordmark 			else if (icmp->icmp_state == TS_IDLE)
41275240Snordmark 				state = "IDLE";
41285240Snordmark 			else if (icmp->icmp_state == TS_DATA_XFER)
41295240Snordmark 				state = "CONNECTED";
41305240Snordmark 			else
41315240Snordmark 				state = "UnkState";
41325240Snordmark 
41335240Snordmark 			(void) mi_mpprintf(mp, MI_COL_PTRFMT_STR "%s %s %s",
41345240Snordmark 			    (void *)icmp,
41358348SEric.Yu@Sun.COM 			    inet_ntop(AF_INET6, &icmp->icmp_v6dst.sin6_addr,
41368348SEric.Yu@Sun.COM 			    faddrbuf,
41375240Snordmark 			    sizeof (faddrbuf)),
41385240Snordmark 			    inet_ntop(AF_INET6, &icmp->icmp_v6src, laddrbuf,
41395240Snordmark 			    sizeof (laddrbuf)),
41405240Snordmark 			    state);
41415240Snordmark 			mutex_exit(&(connp)->conn_lock);
41425240Snordmark 		}
41430Sstevel@tonic-gate 	}
41440Sstevel@tonic-gate 	return (0);
41450Sstevel@tonic-gate }
41460Sstevel@tonic-gate 
41470Sstevel@tonic-gate /*
41480Sstevel@tonic-gate  * This routine creates a T_UDERROR_IND message and passes it upstream.
41490Sstevel@tonic-gate  * The address and options are copied from the T_UNITDATA_REQ message
41500Sstevel@tonic-gate  * passed in mp.  This message is freed.
41510Sstevel@tonic-gate  */
41520Sstevel@tonic-gate static void
41530Sstevel@tonic-gate icmp_ud_err(queue_t *q, mblk_t *mp, t_scalar_t err)
41540Sstevel@tonic-gate {
41550Sstevel@tonic-gate 	mblk_t	*mp1;
41560Sstevel@tonic-gate 	uchar_t	*rptr = mp->b_rptr;
41570Sstevel@tonic-gate 	struct T_unitdata_req *tudr = (struct T_unitdata_req *)rptr;
41580Sstevel@tonic-gate 
41590Sstevel@tonic-gate 	mp1 = mi_tpi_uderror_ind((char *)&rptr[tudr->DEST_offset],
41600Sstevel@tonic-gate 	    tudr->DEST_length, (char *)&rptr[tudr->OPT_offset],
41610Sstevel@tonic-gate 	    tudr->OPT_length, err);
41620Sstevel@tonic-gate 	if (mp1)
41630Sstevel@tonic-gate 		qreply(q, mp1);
41640Sstevel@tonic-gate 	freemsg(mp);
41650Sstevel@tonic-gate }
41660Sstevel@tonic-gate 
41678348SEric.Yu@Sun.COM 
41688348SEric.Yu@Sun.COM static int
41698348SEric.Yu@Sun.COM rawip_do_unbind(conn_t *connp)
41700Sstevel@tonic-gate {
41718348SEric.Yu@Sun.COM 	icmp_t *icmp = connp->conn_icmp;
41725240Snordmark 
41735240Snordmark 	rw_enter(&icmp->icmp_rwlock, RW_WRITER);
41740Sstevel@tonic-gate 	/* If a bind has not been done, we can't unbind. */
41755240Snordmark 	if (icmp->icmp_state == TS_UNBND || icmp->icmp_pending_op != -1) {
41765240Snordmark 		rw_exit(&icmp->icmp_rwlock);
41778348SEric.Yu@Sun.COM 		return (-TOUTSTATE);
41780Sstevel@tonic-gate 	}
41795240Snordmark 	icmp->icmp_pending_op = T_UNBIND_REQ;
41805240Snordmark 	rw_exit(&icmp->icmp_rwlock);
41815240Snordmark 
41825240Snordmark 	/*
41838348SEric.Yu@Sun.COM 	 * Call ip to unbind
41845240Snordmark 	 */
41858348SEric.Yu@Sun.COM 
41868348SEric.Yu@Sun.COM 	ip_unbind(connp);
41875240Snordmark 
41885240Snordmark 	/*
41895240Snordmark 	 * Once we're unbound from IP, the pending operation may be cleared
41905240Snordmark 	 * here.
41915240Snordmark 	 */
41925240Snordmark 	rw_enter(&icmp->icmp_rwlock, RW_WRITER);
41930Sstevel@tonic-gate 	V6_SET_ZERO(icmp->icmp_v6src);
41940Sstevel@tonic-gate 	V6_SET_ZERO(icmp->icmp_bound_v6src);
41955240Snordmark 	icmp->icmp_pending_op = -1;
41960Sstevel@tonic-gate 	icmp->icmp_state = TS_UNBND;
41975240Snordmark 	if (icmp->icmp_family == AF_INET6)
41985240Snordmark 		(void) icmp_build_hdrs(icmp);
41995240Snordmark 	rw_exit(&icmp->icmp_rwlock);
42008348SEric.Yu@Sun.COM 	return (0);
42018348SEric.Yu@Sun.COM }
42028348SEric.Yu@Sun.COM 
42038348SEric.Yu@Sun.COM /*
42048348SEric.Yu@Sun.COM  * This routine is called by icmp_wput to handle T_UNBIND_REQ messages.
42058348SEric.Yu@Sun.COM  * After some error checking, the message is passed downstream to ip.
42068348SEric.Yu@Sun.COM  */
42078348SEric.Yu@Sun.COM static void
42088348SEric.Yu@Sun.COM icmp_tpi_unbind(queue_t *q, mblk_t *mp)
42098348SEric.Yu@Sun.COM {
42108348SEric.Yu@Sun.COM 	conn_t	*connp = Q_TO_CONN(q);
42118348SEric.Yu@Sun.COM 	int	error;
42128348SEric.Yu@Sun.COM 
42138348SEric.Yu@Sun.COM 	ASSERT(mp->b_cont == NULL);
42148348SEric.Yu@Sun.COM 	error = rawip_do_unbind(connp);
42158348SEric.Yu@Sun.COM 	if (error) {
42168348SEric.Yu@Sun.COM 		if (error < 0) {
42178348SEric.Yu@Sun.COM 			icmp_err_ack(q, mp, -error, 0);
42188348SEric.Yu@Sun.COM 		} else {
42198348SEric.Yu@Sun.COM 			icmp_err_ack(q, mp, 0, error);
42208348SEric.Yu@Sun.COM 		}
42218348SEric.Yu@Sun.COM 		return;
42228348SEric.Yu@Sun.COM 	}
42238348SEric.Yu@Sun.COM 
42248348SEric.Yu@Sun.COM 	/*
42258348SEric.Yu@Sun.COM 	 * Convert mp into a T_OK_ACK
42268348SEric.Yu@Sun.COM 	 */
42278348SEric.Yu@Sun.COM 
42288348SEric.Yu@Sun.COM 	mp = mi_tpi_ok_ack_alloc(mp);
42298348SEric.Yu@Sun.COM 
42308348SEric.Yu@Sun.COM 	/*
42318348SEric.Yu@Sun.COM 	 * should not happen in practice... T_OK_ACK is smaller than the
42328348SEric.Yu@Sun.COM 	 * original message.
42338348SEric.Yu@Sun.COM 	 */
42348348SEric.Yu@Sun.COM 	ASSERT(mp != NULL);
42358348SEric.Yu@Sun.COM 	ASSERT(((struct T_ok_ack *)mp->b_rptr)->PRIM_type == T_OK_ACK);
42365240Snordmark 	qreply(q, mp);
42370Sstevel@tonic-gate }
42380Sstevel@tonic-gate 
42398348SEric.Yu@Sun.COM 
42400Sstevel@tonic-gate /*
42410Sstevel@tonic-gate  * Process IPv4 packets that already include an IP header.
42420Sstevel@tonic-gate  * Used when IP_HDRINCL has been set (implicit for IPPROTO_RAW and
42430Sstevel@tonic-gate  * IPPROTO_IGMP).
42440Sstevel@tonic-gate  */
42458348SEric.Yu@Sun.COM static int
42468348SEric.Yu@Sun.COM icmp_wput_hdrincl(queue_t *q, conn_t *connp, mblk_t *mp, icmp_t *icmp,
42478348SEric.Yu@Sun.COM     ip4_pkt_t *pktinfop)
42480Sstevel@tonic-gate {
42493448Sdh155122 	icmp_stack_t *is = icmp->icmp_is;
42500Sstevel@tonic-gate 	ipha_t	*ipha;
42510Sstevel@tonic-gate 	int	ip_hdr_length;
42520Sstevel@tonic-gate 	int	tp_hdr_len;
42530Sstevel@tonic-gate 	mblk_t	*mp1;
42540Sstevel@tonic-gate 	uint_t	pkt_len;
42553318Srshoaib 	ip_opt_info_t optinfo;
42563318Srshoaib 
42573318Srshoaib 	optinfo.ip_opt_flags = 0;
42583318Srshoaib 	optinfo.ip_opt_ill_index = 0;
42590Sstevel@tonic-gate 	ipha = (ipha_t *)mp->b_rptr;
42600Sstevel@tonic-gate 	ip_hdr_length = IP_SIMPLE_HDR_LENGTH + icmp->icmp_ip_snd_options_len;
42610Sstevel@tonic-gate 	if ((mp->b_wptr - mp->b_rptr) < IP_SIMPLE_HDR_LENGTH) {
42620Sstevel@tonic-gate 		if (!pullupmsg(mp, IP_SIMPLE_HDR_LENGTH)) {
42633448Sdh155122 			ASSERT(icmp != NULL);
42645240Snordmark 			BUMP_MIB(&is->is_rawip_mib, rawipOutErrors);
42650Sstevel@tonic-gate 			freemsg(mp);
42668348SEric.Yu@Sun.COM 			return (0);
42670Sstevel@tonic-gate 		}
42680Sstevel@tonic-gate 		ipha = (ipha_t *)mp->b_rptr;
42690Sstevel@tonic-gate 	}
42700Sstevel@tonic-gate 	ipha->ipha_version_and_hdr_length =
42710Sstevel@tonic-gate 	    (IP_VERSION<<4) | (ip_hdr_length>>2);
42720Sstevel@tonic-gate 
42730Sstevel@tonic-gate 	/*
42740Sstevel@tonic-gate 	 * For the socket of SOCK_RAW type, the checksum is provided in the
42750Sstevel@tonic-gate 	 * pre-built packet. We set the ipha_ident field to IP_HDR_INCLUDED to
42760Sstevel@tonic-gate 	 * tell IP that the application has sent a complete IP header and not
42770Sstevel@tonic-gate 	 * to compute the transport checksum nor change the DF flag.
42780Sstevel@tonic-gate 	 */
42790Sstevel@tonic-gate 	ipha->ipha_ident = IP_HDR_INCLUDED;
42800Sstevel@tonic-gate 	ipha->ipha_hdr_checksum = 0;
42810Sstevel@tonic-gate 	ipha->ipha_fragment_offset_and_flags &= htons(IPH_DF);
42820Sstevel@tonic-gate 	/* Insert options if any */
42830Sstevel@tonic-gate 	if (ip_hdr_length > IP_SIMPLE_HDR_LENGTH) {
42840Sstevel@tonic-gate 		/*
42850Sstevel@tonic-gate 		 * Put the IP header plus any transport header that is
42860Sstevel@tonic-gate 		 * checksumed by ip_wput into the first mblk. (ip_wput assumes
42870Sstevel@tonic-gate 		 * that at least the checksum field is in the first mblk.)
42880Sstevel@tonic-gate 		 */
42890Sstevel@tonic-gate 		switch (ipha->ipha_protocol) {
42900Sstevel@tonic-gate 		case IPPROTO_UDP:
42910Sstevel@tonic-gate 			tp_hdr_len = 8;
42920Sstevel@tonic-gate 			break;
42930Sstevel@tonic-gate 		case IPPROTO_TCP:
42940Sstevel@tonic-gate 			tp_hdr_len = 20;
42950Sstevel@tonic-gate 			break;
42960Sstevel@tonic-gate 		default:
42970Sstevel@tonic-gate 			tp_hdr_len = 0;
42980Sstevel@tonic-gate 			break;
42990Sstevel@tonic-gate 		}
43000Sstevel@tonic-gate 		/*
43010Sstevel@tonic-gate 		 * The code below assumes that IP_SIMPLE_HDR_LENGTH plus
43020Sstevel@tonic-gate 		 * tp_hdr_len bytes will be in a single mblk.
43030Sstevel@tonic-gate 		 */
43040Sstevel@tonic-gate 		if ((mp->b_wptr - mp->b_rptr) < (IP_SIMPLE_HDR_LENGTH +
43050Sstevel@tonic-gate 		    tp_hdr_len)) {
43060Sstevel@tonic-gate 			if (!pullupmsg(mp, IP_SIMPLE_HDR_LENGTH +
43070Sstevel@tonic-gate 			    tp_hdr_len)) {
43085240Snordmark 				BUMP_MIB(&is->is_rawip_mib,
43093448Sdh155122 				    rawipOutErrors);
43100Sstevel@tonic-gate 				freemsg(mp);
43118348SEric.Yu@Sun.COM 				return (0);
43120Sstevel@tonic-gate 			}
43130Sstevel@tonic-gate 			ipha = (ipha_t *)mp->b_rptr;
43140Sstevel@tonic-gate 		}
43150Sstevel@tonic-gate 
43160Sstevel@tonic-gate 		/*
43170Sstevel@tonic-gate 		 * if the length is larger then the max allowed IP packet,
43180Sstevel@tonic-gate 		 * then send an error and abort the processing.
43190Sstevel@tonic-gate 		 */
43200Sstevel@tonic-gate 		pkt_len = ntohs(ipha->ipha_length)
43210Sstevel@tonic-gate 		    + icmp->icmp_ip_snd_options_len;
43220Sstevel@tonic-gate 		if (pkt_len > IP_MAXPACKET) {
43238348SEric.Yu@Sun.COM 			return (EMSGSIZE);
43240Sstevel@tonic-gate 		}
43253448Sdh155122 		if (!(mp1 = allocb(ip_hdr_length + is->is_wroff_extra +
43260Sstevel@tonic-gate 		    tp_hdr_len, BPRI_LO))) {
43278348SEric.Yu@Sun.COM 			return (ENOMEM);
43280Sstevel@tonic-gate 		}
43293448Sdh155122 		mp1->b_rptr += is->is_wroff_extra;
43300Sstevel@tonic-gate 		mp1->b_wptr = mp1->b_rptr + ip_hdr_length;
43310Sstevel@tonic-gate 
43320Sstevel@tonic-gate 		ipha->ipha_length = htons((uint16_t)pkt_len);
43330Sstevel@tonic-gate 		bcopy(ipha, mp1->b_rptr, IP_SIMPLE_HDR_LENGTH);
43340Sstevel@tonic-gate 
43350Sstevel@tonic-gate 		/* Copy transport header if any */
43360Sstevel@tonic-gate 		bcopy(&ipha[1], mp1->b_wptr, tp_hdr_len);
43370Sstevel@tonic-gate 		mp1->b_wptr += tp_hdr_len;
43380Sstevel@tonic-gate 
43390Sstevel@tonic-gate 		/* Add options */
43400Sstevel@tonic-gate 		ipha = (ipha_t *)mp1->b_rptr;
43410Sstevel@tonic-gate 		bcopy(icmp->icmp_ip_snd_options, &ipha[1],
43420Sstevel@tonic-gate 		    icmp->icmp_ip_snd_options_len);
43430Sstevel@tonic-gate 
43440Sstevel@tonic-gate 		/* Drop IP header and transport header from original */
43450Sstevel@tonic-gate 		(void) adjmsg(mp, IP_SIMPLE_HDR_LENGTH + tp_hdr_len);
43460Sstevel@tonic-gate 
43470Sstevel@tonic-gate 		mp1->b_cont = mp;
43480Sstevel@tonic-gate 		mp = mp1;
43490Sstevel@tonic-gate 		/*
43500Sstevel@tonic-gate 		 * Massage source route putting first source
43510Sstevel@tonic-gate 		 * route in ipha_dst.
43520Sstevel@tonic-gate 		 */
43535240Snordmark 		(void) ip_massage_options(ipha, is->is_netstack);
43540Sstevel@tonic-gate 	}
43553318Srshoaib 
43563318Srshoaib 	if (pktinfop != NULL) {
43573318Srshoaib 		/*
43583318Srshoaib 		 * Over write the source address provided in the header
43593318Srshoaib 		 */
43603318Srshoaib 		if (pktinfop->ip4_addr != INADDR_ANY) {
43613318Srshoaib 			ipha->ipha_src = pktinfop->ip4_addr;
43623318Srshoaib 			optinfo.ip_opt_flags = IP_VERIFY_SRC;
43633318Srshoaib 		}
43643318Srshoaib 
43653318Srshoaib 		if (pktinfop->ip4_ill_index != 0) {
43663318Srshoaib 			optinfo.ip_opt_ill_index = pktinfop->ip4_ill_index;
43673318Srshoaib 		}
43683318Srshoaib 	}
43693318Srshoaib 
43705240Snordmark 	mblk_setcred(mp, connp->conn_cred);
43718275SEric Cheng 	ip_output_options(connp, mp, q, IP_WPUT, &optinfo);
43728348SEric.Yu@Sun.COM 	return (0);
43730Sstevel@tonic-gate }
43740Sstevel@tonic-gate 
43758348SEric.Yu@Sun.COM static int
43768348SEric.Yu@Sun.COM icmp_update_label(icmp_t *icmp, mblk_t *mp, ipaddr_t dst)
43771676Sjpk {
43781676Sjpk 	int err;
43791676Sjpk 	uchar_t opt_storage[IP_MAX_OPT_LENGTH];
43805240Snordmark 	icmp_stack_t		*is = icmp->icmp_is;
43815240Snordmark 	conn_t	*connp = icmp->icmp_connp;
43825240Snordmark 
43835240Snordmark 	err = tsol_compute_label(DB_CREDDEF(mp, connp->conn_cred), dst,
43846596Skp158701 	    opt_storage, connp->conn_mac_exempt,
43855240Snordmark 	    is->is_netstack->netstack_ip);
43861676Sjpk 	if (err == 0) {
43871676Sjpk 		err = tsol_update_options(&icmp->icmp_ip_snd_options,
43881676Sjpk 		    &icmp->icmp_ip_snd_options_len, &icmp->icmp_label_len,
43891676Sjpk 		    opt_storage);
43901676Sjpk 	}
43911676Sjpk 	if (err != 0) {
43925240Snordmark 		BUMP_MIB(&is->is_rawip_mib, rawipOutErrors);
43931676Sjpk 		DTRACE_PROBE4(
43941676Sjpk 		    tx__ip__log__drop__updatelabel__icmp,
43958348SEric.Yu@Sun.COM 		    char *, "icmp(1) failed to update options(2) on mp(3)",
43968348SEric.Yu@Sun.COM 		    icmp_t *, icmp, char *, opt_storage, mblk_t *, mp);
43978348SEric.Yu@Sun.COM 		return (err);
43981676Sjpk 	}
43991676Sjpk 	IN6_IPADDR_TO_V4MAPPED(dst, &icmp->icmp_v6lastdst);
44008348SEric.Yu@Sun.COM 	return (0);
44011676Sjpk }
44021676Sjpk 
44030Sstevel@tonic-gate /*
44040Sstevel@tonic-gate  * This routine handles all messages passed downstream.  It either
44050Sstevel@tonic-gate  * consumes the message or passes it downstream; it never queues a
44060Sstevel@tonic-gate  * a message.
44070Sstevel@tonic-gate  */
44080Sstevel@tonic-gate static void
44090Sstevel@tonic-gate icmp_wput(queue_t *q, mblk_t *mp)
44100Sstevel@tonic-gate {
44110Sstevel@tonic-gate 	uchar_t	*rptr = mp->b_rptr;
44120Sstevel@tonic-gate 	ipha_t	*ipha;
44130Sstevel@tonic-gate 	mblk_t	*mp1;
44140Sstevel@tonic-gate #define	tudr ((struct T_unitdata_req *)rptr)
44150Sstevel@tonic-gate 	size_t	ip_len;
44165240Snordmark 	conn_t	*connp = Q_TO_CONN(q);
44175240Snordmark 	icmp_t	*icmp = connp->conn_icmp;
44183448Sdh155122 	icmp_stack_t *is = icmp->icmp_is;
44190Sstevel@tonic-gate 	sin6_t	*sin6;
44200Sstevel@tonic-gate 	sin_t	*sin;
44210Sstevel@tonic-gate 	ipaddr_t	v4dst;
44223318Srshoaib 	ip4_pkt_t	pktinfo;
44233318Srshoaib 	ip4_pkt_t	*pktinfop = &pktinfo;
44248348SEric.Yu@Sun.COM 	ip6_pkt_t	ipp_s;  /* For ancillary data options */
44258348SEric.Yu@Sun.COM 	ip6_pkt_t	*ipp = &ipp_s;
44268348SEric.Yu@Sun.COM 	int error;
44278348SEric.Yu@Sun.COM 
44288348SEric.Yu@Sun.COM 	ipp->ipp_fields = 0;
44298348SEric.Yu@Sun.COM 	ipp->ipp_sticky_ignored = 0;
44300Sstevel@tonic-gate 
44310Sstevel@tonic-gate 	switch (mp->b_datap->db_type) {
44320Sstevel@tonic-gate 	case M_DATA:
44330Sstevel@tonic-gate 		if (icmp->icmp_hdrincl) {
44340Sstevel@tonic-gate 			ASSERT(icmp->icmp_ipversion == IPV4_VERSION);
44351676Sjpk 			ipha = (ipha_t *)mp->b_rptr;
44361676Sjpk 			if (mp->b_wptr - mp->b_rptr < IP_SIMPLE_HDR_LENGTH) {
44371676Sjpk 				if (!pullupmsg(mp, IP_SIMPLE_HDR_LENGTH)) {
44385240Snordmark 					BUMP_MIB(&is->is_rawip_mib,
44393448Sdh155122 					    rawipOutErrors);
44401676Sjpk 					freemsg(mp);
44411676Sjpk 					return;
44421676Sjpk 				}
44431676Sjpk 				ipha = (ipha_t *)mp->b_rptr;
44441676Sjpk 			}
44451676Sjpk 			/*
44461676Sjpk 			 * If this connection was used for v6 (inconceivable!)
44471676Sjpk 			 * or if we have a new destination, then it's time to
44481676Sjpk 			 * figure a new label.
44491676Sjpk 			 */
44501676Sjpk 			if (is_system_labeled() &&
44511676Sjpk 			    (!IN6_IS_ADDR_V4MAPPED(&icmp->icmp_v6lastdst) ||
44521676Sjpk 			    V4_PART_OF_V6(icmp->icmp_v6lastdst) !=
44538348SEric.Yu@Sun.COM 			    ipha->ipha_dst)) {
44548348SEric.Yu@Sun.COM 				error = icmp_update_label(icmp, mp,
44558348SEric.Yu@Sun.COM 				    ipha->ipha_dst);
44568348SEric.Yu@Sun.COM 				if (error != 0) {
44578348SEric.Yu@Sun.COM 					icmp_ud_err(q, mp, error);
44588348SEric.Yu@Sun.COM 					return;
44598348SEric.Yu@Sun.COM 				}
44601676Sjpk 			}
44618348SEric.Yu@Sun.COM 			error = icmp_wput_hdrincl(q, connp, mp, icmp, NULL);
44628348SEric.Yu@Sun.COM 			if (error != 0)
44638348SEric.Yu@Sun.COM 				icmp_ud_err(q, mp, error);
44640Sstevel@tonic-gate 			return;
44650Sstevel@tonic-gate 		}
44660Sstevel@tonic-gate 		freemsg(mp);
44670Sstevel@tonic-gate 		return;
44680Sstevel@tonic-gate 	case M_PROTO:
44690Sstevel@tonic-gate 	case M_PCPROTO:
44700Sstevel@tonic-gate 		ip_len = mp->b_wptr - rptr;
44710Sstevel@tonic-gate 		if (ip_len >= sizeof (struct T_unitdata_req)) {
44720Sstevel@tonic-gate 			/* Expedite valid T_UNITDATA_REQ to below the switch */
44730Sstevel@tonic-gate 			if (((union T_primitives *)rptr)->type
44740Sstevel@tonic-gate 			    == T_UNITDATA_REQ)
44750Sstevel@tonic-gate 				break;
44760Sstevel@tonic-gate 		}
44770Sstevel@tonic-gate 		/* FALLTHRU */
44780Sstevel@tonic-gate 	default:
44790Sstevel@tonic-gate 		icmp_wput_other(q, mp);
44800Sstevel@tonic-gate 		return;
44810Sstevel@tonic-gate 	}
44820Sstevel@tonic-gate 
44830Sstevel@tonic-gate 	/* Handle T_UNITDATA_REQ messages here. */
44840Sstevel@tonic-gate 
44850Sstevel@tonic-gate 	mp1 = mp->b_cont;
44860Sstevel@tonic-gate 	if (mp1 == NULL) {
44875240Snordmark 		BUMP_MIB(&is->is_rawip_mib, rawipOutErrors);
44880Sstevel@tonic-gate 		icmp_ud_err(q, mp, EPROTO);
44890Sstevel@tonic-gate 		return;
44900Sstevel@tonic-gate 	}
44910Sstevel@tonic-gate 
44920Sstevel@tonic-gate 	if ((rptr + tudr->DEST_offset + tudr->DEST_length) > mp->b_wptr) {
44935240Snordmark 		BUMP_MIB(&is->is_rawip_mib, rawipOutErrors);
44940Sstevel@tonic-gate 		icmp_ud_err(q, mp, EADDRNOTAVAIL);
44950Sstevel@tonic-gate 		return;
44960Sstevel@tonic-gate 	}
44970Sstevel@tonic-gate 
44980Sstevel@tonic-gate 	switch (icmp->icmp_family) {
44990Sstevel@tonic-gate 	case AF_INET6:
45000Sstevel@tonic-gate 		sin6 = (sin6_t *)&rptr[tudr->DEST_offset];
45010Sstevel@tonic-gate 		if (!OK_32PTR((char *)sin6) ||
45020Sstevel@tonic-gate 		    tudr->DEST_length != sizeof (sin6_t) ||
45030Sstevel@tonic-gate 		    sin6->sin6_family != AF_INET6) {
45045240Snordmark 			BUMP_MIB(&is->is_rawip_mib, rawipOutErrors);
45050Sstevel@tonic-gate 			icmp_ud_err(q, mp, EADDRNOTAVAIL);
45060Sstevel@tonic-gate 			return;
45070Sstevel@tonic-gate 		}
45080Sstevel@tonic-gate 
45090Sstevel@tonic-gate 		/* No support for mapped addresses on raw sockets */
45100Sstevel@tonic-gate 		if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
45115240Snordmark 			BUMP_MIB(&is->is_rawip_mib, rawipOutErrors);
45120Sstevel@tonic-gate 			icmp_ud_err(q, mp, EADDRNOTAVAIL);
45130Sstevel@tonic-gate 			return;
45140Sstevel@tonic-gate 		}
45150Sstevel@tonic-gate 
45160Sstevel@tonic-gate 		/*
45170Sstevel@tonic-gate 		 * Destination is a native IPv6 address.
45180Sstevel@tonic-gate 		 * Send out an IPv6 format packet.
45190Sstevel@tonic-gate 		 */
45208348SEric.Yu@Sun.COM 		if (tudr->OPT_length != 0) {
45218348SEric.Yu@Sun.COM 			int error;
45228348SEric.Yu@Sun.COM 
45238348SEric.Yu@Sun.COM 			error = 0;
45248348SEric.Yu@Sun.COM 			if (icmp_unitdata_opt_process(q, mp, &error,
45258348SEric.Yu@Sun.COM 			    (void *)ipp) < 0) {
45268348SEric.Yu@Sun.COM 				/* failure */
45278348SEric.Yu@Sun.COM 				BUMP_MIB(&is->is_rawip_mib, rawipOutErrors);
45288348SEric.Yu@Sun.COM 				icmp_ud_err(q, mp, error);
45298348SEric.Yu@Sun.COM 				return;
45308348SEric.Yu@Sun.COM 			}
45318348SEric.Yu@Sun.COM 			ASSERT(error == 0);
45328348SEric.Yu@Sun.COM 		}
45338348SEric.Yu@Sun.COM 
45348348SEric.Yu@Sun.COM 		error = raw_ip_send_data_v6(q, connp, mp1, sin6, ipp);
45358348SEric.Yu@Sun.COM 		goto done;
45360Sstevel@tonic-gate 
45370Sstevel@tonic-gate 	case AF_INET:
45380Sstevel@tonic-gate 		sin = (sin_t *)&rptr[tudr->DEST_offset];
45390Sstevel@tonic-gate 		if (!OK_32PTR((char *)sin) ||
45400Sstevel@tonic-gate 		    tudr->DEST_length != sizeof (sin_t) ||
45410Sstevel@tonic-gate 		    sin->sin_family != AF_INET) {
45425240Snordmark 			BUMP_MIB(&is->is_rawip_mib, rawipOutErrors);
45430Sstevel@tonic-gate 			icmp_ud_err(q, mp, EADDRNOTAVAIL);
45440Sstevel@tonic-gate 			return;
45450Sstevel@tonic-gate 		}
45460Sstevel@tonic-gate 		/* Extract and ipaddr */
45470Sstevel@tonic-gate 		v4dst = sin->sin_addr.s_addr;
45480Sstevel@tonic-gate 		break;
45491676Sjpk 
45501676Sjpk 	default:
45511676Sjpk 		ASSERT(0);
45520Sstevel@tonic-gate 	}
45530Sstevel@tonic-gate 
45543318Srshoaib 	pktinfop->ip4_ill_index = 0;
45553318Srshoaib 	pktinfop->ip4_addr = INADDR_ANY;
45563318Srshoaib 
45570Sstevel@tonic-gate 	/*
45580Sstevel@tonic-gate 	 * If options passed in, feed it for verification and handling
45590Sstevel@tonic-gate 	 */
45600Sstevel@tonic-gate 	if (tudr->OPT_length != 0) {
45610Sstevel@tonic-gate 		int error;
45620Sstevel@tonic-gate 
45633318Srshoaib 		error = 0;
45640Sstevel@tonic-gate 		if (icmp_unitdata_opt_process(q, mp, &error,
45653318Srshoaib 		    (void *)pktinfop) < 0) {
45660Sstevel@tonic-gate 			/* failure */
45675240Snordmark 			BUMP_MIB(&is->is_rawip_mib, rawipOutErrors);
45680Sstevel@tonic-gate 			icmp_ud_err(q, mp, error);
45690Sstevel@tonic-gate 			return;
45700Sstevel@tonic-gate 		}
45713318Srshoaib 		ASSERT(error == 0);
45720Sstevel@tonic-gate 		/*
45730Sstevel@tonic-gate 		 * Note: Success in processing options.
45740Sstevel@tonic-gate 		 * mp option buffer represented by
45750Sstevel@tonic-gate 		 * OPT_length/offset now potentially modified
45760Sstevel@tonic-gate 		 * and contain option setting results
45770Sstevel@tonic-gate 		 */
45788348SEric.Yu@Sun.COM 	}
45798348SEric.Yu@Sun.COM 
45808348SEric.Yu@Sun.COM 	error = raw_ip_send_data_v4(q, connp, mp1, v4dst, pktinfop);
45818348SEric.Yu@Sun.COM done:
45828348SEric.Yu@Sun.COM 	if (error != 0) {
45838348SEric.Yu@Sun.COM 		icmp_ud_err(q, mp, error);
45848348SEric.Yu@Sun.COM 		return;
45858348SEric.Yu@Sun.COM 	} else {
45868348SEric.Yu@Sun.COM 		mp->b_cont = NULL;
45878348SEric.Yu@Sun.COM 		freeb(mp);
45888348SEric.Yu@Sun.COM 	}
45898348SEric.Yu@Sun.COM }
45908348SEric.Yu@Sun.COM 
45918348SEric.Yu@Sun.COM 
45928348SEric.Yu@Sun.COM /* ARGSUSED */
45938348SEric.Yu@Sun.COM static void
45948348SEric.Yu@Sun.COM icmp_wput_fallback(queue_t *q, mblk_t *mp)
45958348SEric.Yu@Sun.COM {
45968348SEric.Yu@Sun.COM #ifdef DEBUG
45978348SEric.Yu@Sun.COM 	cmn_err(CE_CONT, "icmp_wput_fallback: Message during fallback \n");
45988348SEric.Yu@Sun.COM #endif
45998348SEric.Yu@Sun.COM 	freemsg(mp);
46008348SEric.Yu@Sun.COM }
46018348SEric.Yu@Sun.COM 
46028348SEric.Yu@Sun.COM static int
46038348SEric.Yu@Sun.COM raw_ip_send_data_v4(queue_t *q, conn_t *connp, mblk_t *mp, ipaddr_t v4dst,
46048348SEric.Yu@Sun.COM     ip4_pkt_t *pktinfop)
46058348SEric.Yu@Sun.COM {
46068348SEric.Yu@Sun.COM 	ipha_t	*ipha;
46078348SEric.Yu@Sun.COM 	size_t	ip_len;
46088348SEric.Yu@Sun.COM 	icmp_t	*icmp = connp->conn_icmp;
46098348SEric.Yu@Sun.COM 	icmp_stack_t *is = icmp->icmp_is;
46108348SEric.Yu@Sun.COM 	int	ip_hdr_length;
46118348SEric.Yu@Sun.COM 	ip_opt_info_t	optinfo;
46128348SEric.Yu@Sun.COM 
46138348SEric.Yu@Sun.COM 	optinfo.ip_opt_flags = 0;
46148348SEric.Yu@Sun.COM 	optinfo.ip_opt_ill_index = 0;
46158348SEric.Yu@Sun.COM 
46168348SEric.Yu@Sun.COM 	if (icmp->icmp_state == TS_UNBND) {
46178348SEric.Yu@Sun.COM 		/* If a port has not been bound to the stream, fail. */
46188348SEric.Yu@Sun.COM 		BUMP_MIB(&is->is_rawip_mib, rawipOutErrors);
46198348SEric.Yu@Sun.COM 		return (EPROTO);
46200Sstevel@tonic-gate 	}
46210Sstevel@tonic-gate 
46221676Sjpk 	if (v4dst == INADDR_ANY)
46231676Sjpk 		v4dst = htonl(INADDR_LOOPBACK);
46241676Sjpk 
46251676Sjpk 	/* Check if our saved options are valid; update if not */
46261676Sjpk 	if (is_system_labeled() &&
46271676Sjpk 	    (!IN6_IS_ADDR_V4MAPPED(&icmp->icmp_v6lastdst) ||
46288348SEric.Yu@Sun.COM 	    V4_PART_OF_V6(icmp->icmp_v6lastdst) != v4dst)) {
46298348SEric.Yu@Sun.COM 		int error = icmp_update_label(icmp, mp, v4dst);
46308348SEric.Yu@Sun.COM 
46318348SEric.Yu@Sun.COM 		if (error != 0)
46328348SEric.Yu@Sun.COM 			return (error);
46331676Sjpk 	}
46341676Sjpk 
46350Sstevel@tonic-gate 	/* Protocol 255 contains full IP headers */
46368348SEric.Yu@Sun.COM 	if (icmp->icmp_hdrincl)
46378348SEric.Yu@Sun.COM 		return (icmp_wput_hdrincl(q, connp, mp, icmp, pktinfop));
46383318Srshoaib 
46390Sstevel@tonic-gate 	/* Add an IP header */
46400Sstevel@tonic-gate 	ip_hdr_length = IP_SIMPLE_HDR_LENGTH + icmp->icmp_ip_snd_options_len;
46418348SEric.Yu@Sun.COM 	ipha = (ipha_t *)&mp->b_rptr[-ip_hdr_length];
46428348SEric.Yu@Sun.COM 	if ((uchar_t *)ipha < mp->b_datap->db_base ||
46438348SEric.Yu@Sun.COM 	    mp->b_datap->db_ref != 1 ||
46440Sstevel@tonic-gate 	    !OK_32PTR(ipha)) {
46458348SEric.Yu@Sun.COM 		mblk_t	*mp1;
46463448Sdh155122 		if (!(mp1 = allocb(ip_hdr_length + is->is_wroff_extra,
46470Sstevel@tonic-gate 		    BPRI_LO))) {
46485240Snordmark 			BUMP_MIB(&is->is_rawip_mib, rawipOutErrors);
46498348SEric.Yu@Sun.COM 			return (ENOMEM);
46500Sstevel@tonic-gate 		}
46518348SEric.Yu@Sun.COM 		mp1->b_cont = mp;
46520Sstevel@tonic-gate 		ipha = (ipha_t *)mp1->b_datap->db_lim;
46530Sstevel@tonic-gate 		mp1->b_wptr = (uchar_t *)ipha;
46540Sstevel@tonic-gate 		ipha = (ipha_t *)((uchar_t *)ipha - ip_hdr_length);
46558348SEric.Yu@Sun.COM 		mp = mp1;
46560Sstevel@tonic-gate 	}
46570Sstevel@tonic-gate #ifdef	_BIG_ENDIAN
46580Sstevel@tonic-gate 	/* Set version, header length, and tos */
46590Sstevel@tonic-gate 	*(uint16_t *)&ipha->ipha_version_and_hdr_length =
46600Sstevel@tonic-gate 	    ((((IP_VERSION << 4) | (ip_hdr_length>>2)) << 8) |
46615240Snordmark 	    icmp->icmp_type_of_service);
46620Sstevel@tonic-gate 	/* Set ttl and protocol */
46630Sstevel@tonic-gate 	*(uint16_t *)&ipha->ipha_ttl = (icmp->icmp_ttl << 8) | icmp->icmp_proto;
46640Sstevel@tonic-gate #else
46650Sstevel@tonic-gate 	/* Set version, header length, and tos */
46660Sstevel@tonic-gate 	*(uint16_t *)&ipha->ipha_version_and_hdr_length =
46670Sstevel@tonic-gate 	    ((icmp->icmp_type_of_service << 8) |
46685240Snordmark 	    ((IP_VERSION << 4) | (ip_hdr_length>>2)));
46690Sstevel@tonic-gate 	/* Set ttl and protocol */
46700Sstevel@tonic-gate 	*(uint16_t *)&ipha->ipha_ttl = (icmp->icmp_proto << 8) | icmp->icmp_ttl;
46710Sstevel@tonic-gate #endif
46723318Srshoaib 	if (pktinfop->ip4_addr != INADDR_ANY) {
46733318Srshoaib 		ipha->ipha_src = pktinfop->ip4_addr;
46743318Srshoaib 		optinfo.ip_opt_flags = IP_VERIFY_SRC;
46753318Srshoaib 	} else {
46763318Srshoaib 
46773318Srshoaib 		/*
46783318Srshoaib 		 * Copy our address into the packet.  If this is zero,
46793318Srshoaib 		 * ip will fill in the real source address.
46803318Srshoaib 		 */
46813318Srshoaib 		IN6_V4MAPPED_TO_IPADDR(&icmp->icmp_v6src, ipha->ipha_src);
46823318Srshoaib 	}
46833318Srshoaib 
46840Sstevel@tonic-gate 	ipha->ipha_fragment_offset_and_flags = 0;
46850Sstevel@tonic-gate 
46863318Srshoaib 	if (pktinfop->ip4_ill_index != 0) {
46873318Srshoaib 		optinfo.ip_opt_ill_index = pktinfop->ip4_ill_index;
46883318Srshoaib 	}
46893318Srshoaib 
46903318Srshoaib 
46910Sstevel@tonic-gate 	/*
46920Sstevel@tonic-gate 	 * For the socket of SOCK_RAW type, the checksum is provided in the
46930Sstevel@tonic-gate 	 * pre-built packet. We set the ipha_ident field to IP_HDR_INCLUDED to
46940Sstevel@tonic-gate 	 * tell IP that the application has sent a complete IP header and not
46950Sstevel@tonic-gate 	 * to compute the transport checksum nor change the DF flag.
46960Sstevel@tonic-gate 	 */
46970Sstevel@tonic-gate 	ipha->ipha_ident = IP_HDR_INCLUDED;
46980Sstevel@tonic-gate 
46990Sstevel@tonic-gate 	/* Finish common formatting of the packet. */
47008348SEric.Yu@Sun.COM 	mp->b_rptr = (uchar_t *)ipha;
47018348SEric.Yu@Sun.COM 
47028348SEric.Yu@Sun.COM 	ip_len = mp->b_wptr - (uchar_t *)ipha;
47038348SEric.Yu@Sun.COM 	if (mp->b_cont != NULL)
47048348SEric.Yu@Sun.COM 		ip_len += msgdsize(mp->b_cont);
47050Sstevel@tonic-gate 
47060Sstevel@tonic-gate 	/*
47070Sstevel@tonic-gate 	 * Set the length into the IP header.
47080Sstevel@tonic-gate 	 * If the length is greater than the maximum allowed by IP,
47090Sstevel@tonic-gate 	 * then free the message and return. Do not try and send it
47100Sstevel@tonic-gate 	 * as this can cause problems in layers below.
47110Sstevel@tonic-gate 	 */
47120Sstevel@tonic-gate 	if (ip_len > IP_MAXPACKET) {
47135240Snordmark 		BUMP_MIB(&is->is_rawip_mib, rawipOutErrors);
47148348SEric.Yu@Sun.COM 		return (EMSGSIZE);
47150Sstevel@tonic-gate 	}
47160Sstevel@tonic-gate 	ipha->ipha_length = htons((uint16_t)ip_len);
47170Sstevel@tonic-gate 	/*
47188348SEric.Yu@Sun.COM 	 * Copy in the destination address request
47190Sstevel@tonic-gate 	 */
47201676Sjpk 	ipha->ipha_dst = v4dst;
47210Sstevel@tonic-gate 
47220Sstevel@tonic-gate 	/*
47230Sstevel@tonic-gate 	 * Set ttl based on IP_MULTICAST_TTL to match IPv6 logic.
47240Sstevel@tonic-gate 	 */
47250Sstevel@tonic-gate 	if (CLASSD(v4dst))
47260Sstevel@tonic-gate 		ipha->ipha_ttl = icmp->icmp_multicast_ttl;
47270Sstevel@tonic-gate 
47280Sstevel@tonic-gate 	/* Copy in options if any */
47290Sstevel@tonic-gate 	if (ip_hdr_length > IP_SIMPLE_HDR_LENGTH) {
47300Sstevel@tonic-gate 		bcopy(icmp->icmp_ip_snd_options,
47310Sstevel@tonic-gate 		    &ipha[1], icmp->icmp_ip_snd_options_len);
47320Sstevel@tonic-gate 		/*
47330Sstevel@tonic-gate 		 * Massage source route putting first source route in ipha_dst.
47340Sstevel@tonic-gate 		 * Ignore the destination in the T_unitdata_req.
47350Sstevel@tonic-gate 		 */
47365240Snordmark 		(void) ip_massage_options(ipha, is->is_netstack);
47370Sstevel@tonic-gate 	}
47383318Srshoaib 
47395240Snordmark 	BUMP_MIB(&is->is_rawip_mib, rawipOutDatagrams);
47408348SEric.Yu@Sun.COM 	mblk_setcred(mp, connp->conn_cred);
47418348SEric.Yu@Sun.COM 	ip_output_options(connp, mp, q, IP_WPUT, &optinfo);
47428348SEric.Yu@Sun.COM 	return (0);
47430Sstevel@tonic-gate }
47440Sstevel@tonic-gate 
47458348SEric.Yu@Sun.COM static int
47468348SEric.Yu@Sun.COM icmp_update_label_v6(icmp_t *icmp, mblk_t *mp, in6_addr_t *dst)
47471676Sjpk {
47481676Sjpk 	int err;
47491676Sjpk 	uchar_t opt_storage[TSOL_MAX_IPV6_OPTION];
47505240Snordmark 	icmp_stack_t		*is = icmp->icmp_is;
47515240Snordmark 	conn_t	*connp = icmp->icmp_connp;
47525240Snordmark 
47535240Snordmark 	err = tsol_compute_label_v6(DB_CREDDEF(mp, connp->conn_cred), dst,
47546596Skp158701 	    opt_storage, connp->conn_mac_exempt,
47555240Snordmark 	    is->is_netstack->netstack_ip);
47561676Sjpk 	if (err == 0) {
47571676Sjpk 		err = tsol_update_sticky(&icmp->icmp_sticky_ipp,
47581676Sjpk 		    &icmp->icmp_label_len_v6, opt_storage);
47591676Sjpk 	}
47601676Sjpk 	if (err != 0) {
47615240Snordmark 		BUMP_MIB(&is->is_rawip_mib, rawipOutErrors);
47621676Sjpk 		DTRACE_PROBE4(
47631676Sjpk 		    tx__ip__log__drop__updatelabel__icmp6,
47648348SEric.Yu@Sun.COM 		    char *, "icmp(1) failed to update options(2) on mp(3)",
47658348SEric.Yu@Sun.COM 		    icmp_t *, icmp, char *, opt_storage, mblk_t *, mp);
47668348SEric.Yu@Sun.COM 		return (err);
47671676Sjpk 	}
47681676Sjpk 
47691676Sjpk 	icmp->icmp_v6lastdst = *dst;
47708348SEric.Yu@Sun.COM 	return (0);
47711676Sjpk }
47721676Sjpk 
47730Sstevel@tonic-gate /*
47748348SEric.Yu@Sun.COM  * raw_ip_send_data_v6():
47750Sstevel@tonic-gate  * Assumes that icmp_wput did some sanity checking on the destination
47761676Sjpk  * address, but that the label may not yet be correct.
47770Sstevel@tonic-gate  */
47788348SEric.Yu@Sun.COM static int
47798348SEric.Yu@Sun.COM raw_ip_send_data_v6(queue_t *q, conn_t *connp, mblk_t *mp, sin6_t *sin6,
47808348SEric.Yu@Sun.COM     ip6_pkt_t *ipp)
47810Sstevel@tonic-gate {
47820Sstevel@tonic-gate 	ip6_t			*ip6h;
47838348SEric.Yu@Sun.COM 	ip6i_t			*ip6i;	/* mp->b_rptr even if no ip6i_t */
47840Sstevel@tonic-gate 	int			ip_hdr_len = IPV6_HDR_LEN;
47850Sstevel@tonic-gate 	size_t			ip_len;
47868348SEric.Yu@Sun.COM 	icmp_t			*icmp = connp->conn_icmp;
47873448Sdh155122 	icmp_stack_t		*is = icmp->icmp_is;
47880Sstevel@tonic-gate 	ip6_pkt_t		*tipp;
47890Sstevel@tonic-gate 	uint32_t		csum = 0;
47900Sstevel@tonic-gate 	uint_t			ignore = 0;
47910Sstevel@tonic-gate 	uint_t			option_exists = 0, is_sticky = 0;
47920Sstevel@tonic-gate 	uint8_t			*cp;
47930Sstevel@tonic-gate 	uint8_t			*nxthdr_ptr;
47941676Sjpk 	in6_addr_t		ip6_dst;
47950Sstevel@tonic-gate 
47960Sstevel@tonic-gate 	/*
47970Sstevel@tonic-gate 	 * If the local address is a mapped address return
47980Sstevel@tonic-gate 	 * an error.
47990Sstevel@tonic-gate 	 * It would be possible to send an IPv6 packet but the
48000Sstevel@tonic-gate 	 * response would never make it back to the application
48010Sstevel@tonic-gate 	 * since it is bound to a mapped address.
48020Sstevel@tonic-gate 	 */
48030Sstevel@tonic-gate 	if (IN6_IS_ADDR_V4MAPPED(&icmp->icmp_v6src)) {
48045240Snordmark 		BUMP_MIB(&is->is_rawip_mib, rawipOutErrors);
48058348SEric.Yu@Sun.COM 		return (EADDRNOTAVAIL);
48068348SEric.Yu@Sun.COM 	}
48078348SEric.Yu@Sun.COM 
48088348SEric.Yu@Sun.COM 	ignore = ipp->ipp_sticky_ignored;
48090Sstevel@tonic-gate 	if (sin6->sin6_scope_id != 0 &&
48100Sstevel@tonic-gate 	    IN6_IS_ADDR_LINKSCOPE(&sin6->sin6_addr)) {
48110Sstevel@tonic-gate 		/*
48120Sstevel@tonic-gate 		 * IPPF_SCOPE_ID is special.  It's neither a sticky
48130Sstevel@tonic-gate 		 * option nor ancillary data.  It needs to be
48140Sstevel@tonic-gate 		 * explicitly set in options_exists.
48150Sstevel@tonic-gate 		 */
48160Sstevel@tonic-gate 		option_exists |= IPPF_SCOPE_ID;
48170Sstevel@tonic-gate 	}
48180Sstevel@tonic-gate 
48191676Sjpk 	/*
48201676Sjpk 	 * Compute the destination address
48211676Sjpk 	 */
48221676Sjpk 	ip6_dst = sin6->sin6_addr;
48231676Sjpk 	if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr))
48241676Sjpk 		ip6_dst = ipv6_loopback;
48251676Sjpk 
48261676Sjpk 	/*
48271676Sjpk 	 * If we're not going to the same destination as last time, then
48281676Sjpk 	 * recompute the label required.  This is done in a separate routine to
48291676Sjpk 	 * avoid blowing up our stack here.
48301676Sjpk 	 */
48311676Sjpk 	if (is_system_labeled() &&
48328348SEric.Yu@Sun.COM 	    !IN6_ARE_ADDR_EQUAL(&icmp->icmp_v6lastdst, &ip6_dst)) {
48338348SEric.Yu@Sun.COM 		int error = 0;
48348348SEric.Yu@Sun.COM 
48358348SEric.Yu@Sun.COM 		error = icmp_update_label_v6(icmp, mp, &ip6_dst);
48368348SEric.Yu@Sun.COM 		if (error != 0)
48378348SEric.Yu@Sun.COM 			return (error);
48381676Sjpk 	}
48391676Sjpk 
48401676Sjpk 	/*
48411676Sjpk 	 * If there's a security label here, then we ignore any options the
48421676Sjpk 	 * user may try to set.  We keep the peer's label as a hidden sticky
48431676Sjpk 	 * option.
48441676Sjpk 	 */
48451676Sjpk 	if (icmp->icmp_label_len_v6 > 0) {
48461676Sjpk 		ignore &= ~IPPF_HOPOPTS;
48471676Sjpk 		ipp->ipp_fields &= ~IPPF_HOPOPTS;
48481676Sjpk 	}
48491676Sjpk 
48500Sstevel@tonic-gate 	if ((icmp->icmp_sticky_ipp.ipp_fields == 0) &&
48510Sstevel@tonic-gate 	    (ipp->ipp_fields == 0)) {
48520Sstevel@tonic-gate 		/* No sticky options nor ancillary data. */
48530Sstevel@tonic-gate 		goto no_options;
48540Sstevel@tonic-gate 	}
48550Sstevel@tonic-gate 
48560Sstevel@tonic-gate 	/*
48570Sstevel@tonic-gate 	 * Go through the options figuring out where each is going to
48580Sstevel@tonic-gate 	 * come from and build two masks.  The first mask indicates if
48590Sstevel@tonic-gate 	 * the option exists at all.  The second mask indicates if the
48600Sstevel@tonic-gate 	 * option is sticky or ancillary.
48610Sstevel@tonic-gate 	 */
48620Sstevel@tonic-gate 	if (!(ignore & IPPF_HOPOPTS)) {
48630Sstevel@tonic-gate 		if (ipp->ipp_fields & IPPF_HOPOPTS) {
48640Sstevel@tonic-gate 			option_exists |= IPPF_HOPOPTS;
48650Sstevel@tonic-gate 			ip_hdr_len += ipp->ipp_hopoptslen;
48660Sstevel@tonic-gate 		} else if (icmp->icmp_sticky_ipp.ipp_fields & IPPF_HOPOPTS) {
48670Sstevel@tonic-gate 			option_exists |= IPPF_HOPOPTS;
48680Sstevel@tonic-gate 			is_sticky |= IPPF_HOPOPTS;
48690Sstevel@tonic-gate 			ip_hdr_len += icmp->icmp_sticky_ipp.ipp_hopoptslen;
48700Sstevel@tonic-gate 		}
48710Sstevel@tonic-gate 	}
48720Sstevel@tonic-gate 
48730Sstevel@tonic-gate 	if (!(ignore & IPPF_RTHDR)) {
48740Sstevel@tonic-gate 		if (ipp->ipp_fields & IPPF_RTHDR) {
48750Sstevel@tonic-gate 			option_exists |= IPPF_RTHDR;
48760Sstevel@tonic-gate 			ip_hdr_len += ipp->ipp_rthdrlen;
48770Sstevel@tonic-gate 		} else if (icmp->icmp_sticky_ipp.ipp_fields & IPPF_RTHDR) {
48780Sstevel@tonic-gate 			option_exists |= IPPF_RTHDR;
48790Sstevel@tonic-gate 			is_sticky |= IPPF_RTHDR;
48800Sstevel@tonic-gate 			ip_hdr_len += icmp->icmp_sticky_ipp.ipp_rthdrlen;
48810Sstevel@tonic-gate 		}
48820Sstevel@tonic-gate 	}
48830Sstevel@tonic-gate 
48840Sstevel@tonic-gate 	if (!(ignore & IPPF_RTDSTOPTS) && (option_exists & IPPF_RTHDR)) {
48850Sstevel@tonic-gate 		/*
48860Sstevel@tonic-gate 		 * Need to have a router header to use these.
48870Sstevel@tonic-gate 		 */
48880Sstevel@tonic-gate 		if (ipp->ipp_fields & IPPF_RTDSTOPTS) {
48890Sstevel@tonic-gate 			option_exists |= IPPF_RTDSTOPTS;
48900Sstevel@tonic-gate 			ip_hdr_len += ipp->ipp_rtdstoptslen;
48910Sstevel@tonic-gate 		} else if (icmp->icmp_sticky_ipp.ipp_fields & IPPF_RTDSTOPTS) {
48920Sstevel@tonic-gate 			option_exists |= IPPF_RTDSTOPTS;
48930Sstevel@tonic-gate 			is_sticky |= IPPF_RTDSTOPTS;
48940Sstevel@tonic-gate 			ip_hdr_len +=
48950Sstevel@tonic-gate 			    icmp->icmp_sticky_ipp.ipp_rtdstoptslen;
48960Sstevel@tonic-gate 		}
48970Sstevel@tonic-gate 	}
48980Sstevel@tonic-gate 
48990Sstevel@tonic-gate 	if (!(ignore & IPPF_DSTOPTS)) {
49000Sstevel@tonic-gate 		if (ipp->ipp_fields & IPPF_DSTOPTS) {
49010Sstevel@tonic-gate 			option_exists |= IPPF_DSTOPTS;
49020Sstevel@tonic-gate 			ip_hdr_len += ipp->ipp_dstoptslen;
49030Sstevel@tonic-gate 		} else if (icmp->icmp_sticky_ipp.ipp_fields & IPPF_DSTOPTS) {
49040Sstevel@tonic-gate 			option_exists |= IPPF_DSTOPTS;
49050Sstevel@tonic-gate 			is_sticky |= IPPF_DSTOPTS;
49060Sstevel@tonic-gate 			ip_hdr_len += icmp->icmp_sticky_ipp.ipp_dstoptslen;
49070Sstevel@tonic-gate 		}
49080Sstevel@tonic-gate 	}
49090Sstevel@tonic-gate 
49100Sstevel@tonic-gate 	if (!(ignore & IPPF_IFINDEX)) {
49110Sstevel@tonic-gate 		if (ipp->ipp_fields & IPPF_IFINDEX) {
49120Sstevel@tonic-gate 			option_exists |= IPPF_IFINDEX;
49130Sstevel@tonic-gate 		} else if (icmp->icmp_sticky_ipp.ipp_fields & IPPF_IFINDEX) {
49140Sstevel@tonic-gate 			option_exists |= IPPF_IFINDEX;
49150Sstevel@tonic-gate 			is_sticky |= IPPF_IFINDEX;
49160Sstevel@tonic-gate 		}
49170Sstevel@tonic-gate 	}
49180Sstevel@tonic-gate 
49190Sstevel@tonic-gate 	if (!(ignore & IPPF_ADDR)) {
49200Sstevel@tonic-gate 		if (ipp->ipp_fields & IPPF_ADDR) {
49210Sstevel@tonic-gate 			option_exists |= IPPF_ADDR;
49220Sstevel@tonic-gate 		} else if (icmp->icmp_sticky_ipp.ipp_fields & IPPF_ADDR) {
49230Sstevel@tonic-gate 			option_exists |= IPPF_ADDR;
49240Sstevel@tonic-gate 			is_sticky |= IPPF_ADDR;
49250Sstevel@tonic-gate 		}
49260Sstevel@tonic-gate 	}
49270Sstevel@tonic-gate 
49280Sstevel@tonic-gate 	if (!(ignore & IPPF_DONTFRAG)) {
49290Sstevel@tonic-gate 		if (ipp->ipp_fields & IPPF_DONTFRAG) {
49300Sstevel@tonic-gate 			option_exists |= IPPF_DONTFRAG;
49310Sstevel@tonic-gate 		} else if (icmp->icmp_sticky_ipp.ipp_fields & IPPF_DONTFRAG) {
49320Sstevel@tonic-gate 			option_exists |= IPPF_DONTFRAG;
49330Sstevel@tonic-gate 			is_sticky |= IPPF_DONTFRAG;
49340Sstevel@tonic-gate 		}
49350Sstevel@tonic-gate 	}
49360Sstevel@tonic-gate 
49370Sstevel@tonic-gate 	if (!(ignore & IPPF_USE_MIN_MTU)) {
49380Sstevel@tonic-gate 		if (ipp->ipp_fields & IPPF_USE_MIN_MTU) {
49390Sstevel@tonic-gate 			option_exists |= IPPF_USE_MIN_MTU;
49400Sstevel@tonic-gate 		} else if (icmp->icmp_sticky_ipp.ipp_fields &
49410Sstevel@tonic-gate 		    IPPF_USE_MIN_MTU) {
49420Sstevel@tonic-gate 			option_exists |= IPPF_USE_MIN_MTU;
49430Sstevel@tonic-gate 			is_sticky |= IPPF_USE_MIN_MTU;
49440Sstevel@tonic-gate 		}
49450Sstevel@tonic-gate 	}
49460Sstevel@tonic-gate 
49470Sstevel@tonic-gate 	if (!(ignore & IPPF_NEXTHOP)) {
49480Sstevel@tonic-gate 		if (ipp->ipp_fields & IPPF_NEXTHOP) {
49490Sstevel@tonic-gate 			option_exists |= IPPF_NEXTHOP;
49500Sstevel@tonic-gate 		} else if (icmp->icmp_sticky_ipp.ipp_fields & IPPF_NEXTHOP) {
49510Sstevel@tonic-gate 			option_exists |= IPPF_NEXTHOP;
49520Sstevel@tonic-gate 			is_sticky |= IPPF_NEXTHOP;
49530Sstevel@tonic-gate 		}
49540Sstevel@tonic-gate 	}
49550Sstevel@tonic-gate 
4956679Sseb 	if (!(ignore & IPPF_HOPLIMIT) && (ipp->ipp_fields & IPPF_HOPLIMIT))
4957679Sseb 		option_exists |= IPPF_HOPLIMIT;
4958679Sseb 	/* IPV6_HOPLIMIT can never be sticky */
4959679Sseb 	ASSERT(!(icmp->icmp_sticky_ipp.ipp_fields & IPPF_HOPLIMIT));
4960679Sseb 
4961679Sseb 	if (!(ignore & IPPF_UNICAST_HOPS) &&
4962679Sseb 	    (icmp->icmp_sticky_ipp.ipp_fields & IPPF_UNICAST_HOPS)) {
4963679Sseb 		option_exists |= IPPF_UNICAST_HOPS;
4964679Sseb 		is_sticky |= IPPF_UNICAST_HOPS;
4965679Sseb 	}
4966679Sseb 
4967679Sseb 	if (!(ignore & IPPF_MULTICAST_HOPS) &&
4968679Sseb 	    (icmp->icmp_sticky_ipp.ipp_fields & IPPF_MULTICAST_HOPS)) {
4969679Sseb 		option_exists |= IPPF_MULTICAST_HOPS;
4970679Sseb 		is_sticky |= IPPF_MULTICAST_HOPS;
49710Sstevel@tonic-gate 	}
49720Sstevel@tonic-gate 
49730Sstevel@tonic-gate 	if (icmp->icmp_sticky_ipp.ipp_fields & IPPF_NO_CKSUM) {
49740Sstevel@tonic-gate 		/* This is a sticky socket option only */
49750Sstevel@tonic-gate 		option_exists |= IPPF_NO_CKSUM;
49760Sstevel@tonic-gate 		is_sticky |= IPPF_NO_CKSUM;
49770Sstevel@tonic-gate 	}
49780Sstevel@tonic-gate 
49790Sstevel@tonic-gate 	if (icmp->icmp_sticky_ipp.ipp_fields & IPPF_RAW_CKSUM) {
49800Sstevel@tonic-gate 		/* This is a sticky socket option only */
49810Sstevel@tonic-gate 		option_exists |= IPPF_RAW_CKSUM;
49820Sstevel@tonic-gate 		is_sticky |= IPPF_RAW_CKSUM;
49830Sstevel@tonic-gate 	}
49840Sstevel@tonic-gate 
49850Sstevel@tonic-gate 	if (!(ignore & IPPF_TCLASS)) {
49860Sstevel@tonic-gate 		if (ipp->ipp_fields & IPPF_TCLASS) {
49870Sstevel@tonic-gate 			option_exists |= IPPF_TCLASS;
49880Sstevel@tonic-gate 		} else if (icmp->icmp_sticky_ipp.ipp_fields & IPPF_TCLASS) {
49890Sstevel@tonic-gate 			option_exists |= IPPF_TCLASS;
49900Sstevel@tonic-gate 			is_sticky |= IPPF_TCLASS;
49910Sstevel@tonic-gate 		}
49920Sstevel@tonic-gate 	}
49930Sstevel@tonic-gate 
49940Sstevel@tonic-gate no_options:
49950Sstevel@tonic-gate 
49960Sstevel@tonic-gate 	/*
49970Sstevel@tonic-gate 	 * If any options carried in the ip6i_t were specified, we
49980Sstevel@tonic-gate 	 * need to account for the ip6i_t in the data we'll be sending
49990Sstevel@tonic-gate 	 * down.
50000Sstevel@tonic-gate 	 */
50010Sstevel@tonic-gate 	if (option_exists & IPPF_HAS_IP6I)
50020Sstevel@tonic-gate 		ip_hdr_len += sizeof (ip6i_t);
50030Sstevel@tonic-gate 
50040Sstevel@tonic-gate 	/* check/fix buffer config, setup pointers into it */
50058348SEric.Yu@Sun.COM 	ip6h = (ip6_t *)&mp->b_rptr[-ip_hdr_len];
50068348SEric.Yu@Sun.COM 	if ((mp->b_datap->db_ref != 1) ||
50078348SEric.Yu@Sun.COM 	    ((unsigned char *)ip6h < mp->b_datap->db_base) ||
50080Sstevel@tonic-gate 	    !OK_32PTR(ip6h)) {
50098348SEric.Yu@Sun.COM 		mblk_t	*mp1;
50108348SEric.Yu@Sun.COM 
50110Sstevel@tonic-gate 		/* Try to get everything in a single mblk next time */
50120Sstevel@tonic-gate 		if (ip_hdr_len > icmp->icmp_max_hdr_len) {
50130Sstevel@tonic-gate 			icmp->icmp_max_hdr_len = ip_hdr_len;
50148348SEric.Yu@Sun.COM 
50158348SEric.Yu@Sun.COM 			(void) proto_set_tx_wroff(q == NULL ? NULL:RD(q), connp,
50163448Sdh155122 			    icmp->icmp_max_hdr_len + is->is_wroff_extra);
50170Sstevel@tonic-gate 		}
50183448Sdh155122 		mp1 = allocb(ip_hdr_len + is->is_wroff_extra, BPRI_LO);
50190Sstevel@tonic-gate 		if (!mp1) {
50205240Snordmark 			BUMP_MIB(&is->is_rawip_mib, rawipOutErrors);
50218348SEric.Yu@Sun.COM 			return (ENOMEM);
50220Sstevel@tonic-gate 		}
50238348SEric.Yu@Sun.COM 		mp1->b_cont = mp;
50240Sstevel@tonic-gate 		mp1->b_wptr = mp1->b_datap->db_lim;
50250Sstevel@tonic-gate 		ip6h = (ip6_t *)(mp1->b_wptr - ip_hdr_len);
50268348SEric.Yu@Sun.COM 		mp = mp1;
50278348SEric.Yu@Sun.COM 	}
50288348SEric.Yu@Sun.COM 	mp->b_rptr = (unsigned char *)ip6h;
50290Sstevel@tonic-gate 	ip6i = (ip6i_t *)ip6h;
50300Sstevel@tonic-gate 
50310Sstevel@tonic-gate #define	ANCIL_OR_STICKY_PTR(f) ((is_sticky & f) ? &icmp->icmp_sticky_ipp : ipp)
50320Sstevel@tonic-gate 	if (option_exists & IPPF_HAS_IP6I) {
50330Sstevel@tonic-gate 		ip6h = (ip6_t *)&ip6i[1];
50340Sstevel@tonic-gate 		ip6i->ip6i_flags = 0;
50350Sstevel@tonic-gate 		ip6i->ip6i_vcf = IPV6_DEFAULT_VERS_AND_FLOW;
50360Sstevel@tonic-gate 
50370Sstevel@tonic-gate 		/* sin6_scope_id takes precendence over IPPF_IFINDEX */
50380Sstevel@tonic-gate 		if (option_exists & IPPF_SCOPE_ID) {
50390Sstevel@tonic-gate 			ip6i->ip6i_flags |= IP6I_IFINDEX;
50400Sstevel@tonic-gate 			ip6i->ip6i_ifindex = sin6->sin6_scope_id;
50410Sstevel@tonic-gate 		} else if (option_exists & IPPF_IFINDEX) {
50420Sstevel@tonic-gate 			tipp = ANCIL_OR_STICKY_PTR(IPPF_IFINDEX);
50430Sstevel@tonic-gate 			ASSERT(tipp->ipp_ifindex != 0);
50440Sstevel@tonic-gate 			ip6i->ip6i_flags |= IP6I_IFINDEX;
50450Sstevel@tonic-gate 			ip6i->ip6i_ifindex = tipp->ipp_ifindex;
50460Sstevel@tonic-gate 		}
50470Sstevel@tonic-gate 
50480Sstevel@tonic-gate 		if (option_exists & IPPF_RAW_CKSUM) {
50490Sstevel@tonic-gate 			ip6i->ip6i_flags |= IP6I_RAW_CHECKSUM;
50500Sstevel@tonic-gate 			ip6i->ip6i_checksum_off = icmp->icmp_checksum_off;
50510Sstevel@tonic-gate 		}
50520Sstevel@tonic-gate 
50530Sstevel@tonic-gate 		if (option_exists & IPPF_NO_CKSUM) {
50540Sstevel@tonic-gate 			ip6i->ip6i_flags |= IP6I_NO_ULP_CKSUM;
50550Sstevel@tonic-gate 		}
50560Sstevel@tonic-gate 
50570Sstevel@tonic-gate 		if (option_exists & IPPF_ADDR) {
50580Sstevel@tonic-gate 			/*
50590Sstevel@tonic-gate 			 * Enable per-packet source address verification if
50600Sstevel@tonic-gate 			 * IPV6_PKTINFO specified the source address.
50610Sstevel@tonic-gate 			 * ip6_src is set in the transport's _wput function.
50620Sstevel@tonic-gate 			 */
50630Sstevel@tonic-gate 			ip6i->ip6i_flags |= IP6I_VERIFY_SRC;
50640Sstevel@tonic-gate 		}
50650Sstevel@tonic-gate 
50660Sstevel@tonic-gate 		if (option_exists & IPPF_DONTFRAG) {
50670Sstevel@tonic-gate 			ip6i->ip6i_flags |= IP6I_DONTFRAG;
50680Sstevel@tonic-gate 		}
50690Sstevel@tonic-gate 
50700Sstevel@tonic-gate 		if (option_exists & IPPF_USE_MIN_MTU) {
50710Sstevel@tonic-gate 			ip6i->ip6i_flags = IP6I_API_USE_MIN_MTU(
50720Sstevel@tonic-gate 			    ip6i->ip6i_flags, ipp->ipp_use_min_mtu);
50730Sstevel@tonic-gate 		}
50740Sstevel@tonic-gate 
50750Sstevel@tonic-gate 		if (option_exists & IPPF_NEXTHOP) {
50760Sstevel@tonic-gate 			tipp = ANCIL_OR_STICKY_PTR(IPPF_NEXTHOP);
50770Sstevel@tonic-gate 			ASSERT(!IN6_IS_ADDR_UNSPECIFIED(&tipp->ipp_nexthop));
50780Sstevel@tonic-gate 			ip6i->ip6i_flags |= IP6I_NEXTHOP;
50790Sstevel@tonic-gate 			ip6i->ip6i_nexthop = tipp->ipp_nexthop;
50800Sstevel@tonic-gate 		}
50810Sstevel@tonic-gate 
50820Sstevel@tonic-gate 		/*
50830Sstevel@tonic-gate 		 * tell IP this is an ip6i_t private header
50840Sstevel@tonic-gate 		 */
50850Sstevel@tonic-gate 		ip6i->ip6i_nxt = IPPROTO_RAW;
50860Sstevel@tonic-gate 	}
50870Sstevel@tonic-gate 
50880Sstevel@tonic-gate 	/* Initialize IPv6 header */
50890Sstevel@tonic-gate 	ip6h->ip6_vcf = IPV6_DEFAULT_VERS_AND_FLOW;
50900Sstevel@tonic-gate 	bzero(&ip6h->ip6_src, sizeof (ip6h->ip6_src));
50910Sstevel@tonic-gate 
5092679Sseb 	/* Set the hoplimit of the outgoing packet. */
50930Sstevel@tonic-gate 	if (option_exists & IPPF_HOPLIMIT) {
5094679Sseb 		/* IPV6_HOPLIMIT ancillary data overrides all other settings. */
5095679Sseb 		ip6h->ip6_hops = ipp->ipp_hoplimit;
5096679Sseb 		ip6i->ip6i_flags |= IP6I_HOPLIMIT;
5097679Sseb 	} else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) {
50980Sstevel@tonic-gate 		ip6h->ip6_hops = icmp->icmp_multicast_ttl;
5099679Sseb 		if (option_exists & IPPF_MULTICAST_HOPS)
5100679Sseb 			ip6i->ip6i_flags |= IP6I_HOPLIMIT;
51010Sstevel@tonic-gate 	} else {
51020Sstevel@tonic-gate 		ip6h->ip6_hops = icmp->icmp_ttl;
5103679Sseb 		if (option_exists & IPPF_UNICAST_HOPS)
5104679Sseb 			ip6i->ip6i_flags |= IP6I_HOPLIMIT;
51050Sstevel@tonic-gate 	}
51060Sstevel@tonic-gate 
51070Sstevel@tonic-gate 	if (option_exists & IPPF_ADDR) {
51080Sstevel@tonic-gate 		tipp = ANCIL_OR_STICKY_PTR(IPPF_ADDR);
51090Sstevel@tonic-gate 		ASSERT(!IN6_IS_ADDR_UNSPECIFIED(&tipp->ipp_addr));
51100Sstevel@tonic-gate 		ip6h->ip6_src = tipp->ipp_addr;
51110Sstevel@tonic-gate 	} else {
51120Sstevel@tonic-gate 		/*
51130Sstevel@tonic-gate 		 * The source address was not set using IPV6_PKTINFO.
51140Sstevel@tonic-gate 		 * First look at the bound source.
51150Sstevel@tonic-gate 		 * If unspecified fallback to __sin6_src_id.
51160Sstevel@tonic-gate 		 */
51170Sstevel@tonic-gate 		ip6h->ip6_src = icmp->icmp_v6src;
51180Sstevel@tonic-gate 		if (sin6->__sin6_src_id != 0 &&
51190Sstevel@tonic-gate 		    IN6_IS_ADDR_UNSPECIFIED(&ip6h->ip6_src)) {
51200Sstevel@tonic-gate 			ip_srcid_find_id(sin6->__sin6_src_id,
51213448Sdh155122 			    &ip6h->ip6_src, icmp->icmp_zoneid,
51223448Sdh155122 			    is->is_netstack);
51230Sstevel@tonic-gate 		}
51240Sstevel@tonic-gate 	}
51250Sstevel@tonic-gate 
51260Sstevel@tonic-gate 	nxthdr_ptr = (uint8_t *)&ip6h->ip6_nxt;
51270Sstevel@tonic-gate 	cp = (uint8_t *)&ip6h[1];
51280Sstevel@tonic-gate 
51290Sstevel@tonic-gate 	/*
51300Sstevel@tonic-gate 	 * Here's where we have to start stringing together
51310Sstevel@tonic-gate 	 * any extension headers in the right order:
51320Sstevel@tonic-gate 	 * Hop-by-hop, destination, routing, and final destination opts.
51330Sstevel@tonic-gate 	 */
51340Sstevel@tonic-gate 	if (option_exists & IPPF_HOPOPTS) {
51350Sstevel@tonic-gate 		/* Hop-by-hop options */
51360Sstevel@tonic-gate 		ip6_hbh_t *hbh = (ip6_hbh_t *)cp;
51370Sstevel@tonic-gate 		tipp = ANCIL_OR_STICKY_PTR(IPPF_HOPOPTS);
51380Sstevel@tonic-gate 
51390Sstevel@tonic-gate 		*nxthdr_ptr = IPPROTO_HOPOPTS;
51400Sstevel@tonic-gate 		nxthdr_ptr = &hbh->ip6h_nxt;
51410Sstevel@tonic-gate 
51420Sstevel@tonic-gate 		bcopy(tipp->ipp_hopopts, cp, tipp->ipp_hopoptslen);
51430Sstevel@tonic-gate 		cp += tipp->ipp_hopoptslen;
51440Sstevel@tonic-gate 	}
51450Sstevel@tonic-gate 	/*
51460Sstevel@tonic-gate 	 * En-route destination options
51470Sstevel@tonic-gate 	 * Only do them if there's a routing header as well
51480Sstevel@tonic-gate 	 */
51490Sstevel@tonic-gate 	if (option_exists & IPPF_RTDSTOPTS) {
51500Sstevel@tonic-gate 		ip6_dest_t *dst = (ip6_dest_t *)cp;
51510Sstevel@tonic-gate 		tipp = ANCIL_OR_STICKY_PTR(IPPF_RTDSTOPTS);
51520Sstevel@tonic-gate 
51530Sstevel@tonic-gate 		*nxthdr_ptr = IPPROTO_DSTOPTS;
51540Sstevel@tonic-gate 		nxthdr_ptr = &dst->ip6d_nxt;
51550Sstevel@tonic-gate 
51560Sstevel@tonic-gate 		bcopy(tipp->ipp_rtdstopts, cp, tipp->ipp_rtdstoptslen);
51570Sstevel@tonic-gate 		cp += tipp->ipp_rtdstoptslen;
51580Sstevel@tonic-gate 	}
51590Sstevel@tonic-gate 	/*
51600Sstevel@tonic-gate 	 * Routing header next
51610Sstevel@tonic-gate 	 */
51620Sstevel@tonic-gate 	if (option_exists & IPPF_RTHDR) {
51630Sstevel@tonic-gate 		ip6_rthdr_t *rt = (ip6_rthdr_t *)cp;
51640Sstevel@tonic-gate 		tipp = ANCIL_OR_STICKY_PTR(IPPF_RTHDR);
51650Sstevel@tonic-gate 
51660Sstevel@tonic-gate 		*nxthdr_ptr = IPPROTO_ROUTING;
51670Sstevel@tonic-gate 		nxthdr_ptr = &rt->ip6r_nxt;
51680Sstevel@tonic-gate 
51690Sstevel@tonic-gate 		bcopy(tipp->ipp_rthdr, cp, tipp->ipp_rthdrlen);
51700Sstevel@tonic-gate 		cp += tipp->ipp_rthdrlen;
51710Sstevel@tonic-gate 	}
51720Sstevel@tonic-gate 	/*
51730Sstevel@tonic-gate 	 * Do ultimate destination options
51740Sstevel@tonic-gate 	 */
51750Sstevel@tonic-gate 	if (option_exists & IPPF_DSTOPTS) {
51760Sstevel@tonic-gate 		ip6_dest_t *dest = (ip6_dest_t *)cp;
51770Sstevel@tonic-gate 		tipp = ANCIL_OR_STICKY_PTR(IPPF_DSTOPTS);
51780Sstevel@tonic-gate 
51790Sstevel@tonic-gate 		*nxthdr_ptr = IPPROTO_DSTOPTS;
51800Sstevel@tonic-gate 		nxthdr_ptr = &dest->ip6d_nxt;
51810Sstevel@tonic-gate 
51820Sstevel@tonic-gate 		bcopy(tipp->ipp_dstopts, cp, tipp->ipp_dstoptslen);
51830Sstevel@tonic-gate 		cp += tipp->ipp_dstoptslen;
51840Sstevel@tonic-gate 	}
51850Sstevel@tonic-gate 
51860Sstevel@tonic-gate 	/*
51870Sstevel@tonic-gate 	 * Now set the last header pointer to the proto passed in
51880Sstevel@tonic-gate 	 */
51890Sstevel@tonic-gate 	ASSERT((int)(cp - (uint8_t *)ip6i) == ip_hdr_len);
51900Sstevel@tonic-gate 	*nxthdr_ptr = icmp->icmp_proto;
51910Sstevel@tonic-gate 
51920Sstevel@tonic-gate 	/*
51930Sstevel@tonic-gate 	 * Copy in the destination address
51940Sstevel@tonic-gate 	 */
51951676Sjpk 	ip6h->ip6_dst = ip6_dst;
51960Sstevel@tonic-gate 
51970Sstevel@tonic-gate 	ip6h->ip6_vcf =
51985240Snordmark 	    (IPV6_DEFAULT_VERS_AND_FLOW & IPV6_VERS_AND_FLOW_MASK) |
51995240Snordmark 	    (sin6->sin6_flowinfo & ~IPV6_VERS_AND_FLOW_MASK);
52000Sstevel@tonic-gate 
52010Sstevel@tonic-gate 	if (option_exists & IPPF_TCLASS) {
52020Sstevel@tonic-gate 		tipp = ANCIL_OR_STICKY_PTR(IPPF_TCLASS);
52030Sstevel@tonic-gate 		ip6h->ip6_vcf = IPV6_TCLASS_FLOW(ip6h->ip6_vcf,
52040Sstevel@tonic-gate 		    tipp->ipp_tclass);
52050Sstevel@tonic-gate 	}
52060Sstevel@tonic-gate 	if (option_exists & IPPF_RTHDR) {
52070Sstevel@tonic-gate 		ip6_rthdr_t	*rth;
52080Sstevel@tonic-gate 
52090Sstevel@tonic-gate 		/*
52100Sstevel@tonic-gate 		 * Perform any processing needed for source routing.
52110Sstevel@tonic-gate 		 * We know that all extension headers will be in the same mblk
52120Sstevel@tonic-gate 		 * as the IPv6 header.
52130Sstevel@tonic-gate 		 */
52148348SEric.Yu@Sun.COM 		rth = ip_find_rthdr_v6(ip6h, mp->b_wptr);
52150Sstevel@tonic-gate 		if (rth != NULL && rth->ip6r_segleft != 0) {
52160Sstevel@tonic-gate 			if (rth->ip6r_type != IPV6_RTHDR_TYPE_0) {
52170Sstevel@tonic-gate 				/*
52180Sstevel@tonic-gate 				 * Drop packet - only support Type 0 routing.
52190Sstevel@tonic-gate 				 * Notify the application as well.
52200Sstevel@tonic-gate 				 */
52215240Snordmark 				BUMP_MIB(&is->is_rawip_mib,
52223448Sdh155122 				    rawipOutErrors);
52238348SEric.Yu@Sun.COM 				return (EPROTO);
52240Sstevel@tonic-gate 			}
52250Sstevel@tonic-gate 			/*
52260Sstevel@tonic-gate 			 * rth->ip6r_len is twice the number of
52270Sstevel@tonic-gate 			 * addresses in the header
52280Sstevel@tonic-gate 			 */
52290Sstevel@tonic-gate 			if (rth->ip6r_len & 0x1) {
52305240Snordmark 				BUMP_MIB(&is->is_rawip_mib,
52313448Sdh155122 				    rawipOutErrors);
52328348SEric.Yu@Sun.COM 				return (EPROTO);
52330Sstevel@tonic-gate 			}
52340Sstevel@tonic-gate 			/*
52350Sstevel@tonic-gate 			 * Shuffle the routing header and ip6_dst
52360Sstevel@tonic-gate 			 * addresses, and get the checksum difference
52370Sstevel@tonic-gate 			 * between the first hop (in ip6_dst) and
52380Sstevel@tonic-gate 			 * the destination (in the last routing hdr entry).
52390Sstevel@tonic-gate 			 */
52403448Sdh155122 			csum = ip_massage_options_v6(ip6h, rth,
52415240Snordmark 			    is->is_netstack);
52420Sstevel@tonic-gate 			/*
52430Sstevel@tonic-gate 			 * Verify that the first hop isn't a mapped address.
52440Sstevel@tonic-gate 			 * Routers along the path need to do this verification
52450Sstevel@tonic-gate 			 * for subsequent hops.
52460Sstevel@tonic-gate 			 */
52470Sstevel@tonic-gate 			if (IN6_IS_ADDR_V4MAPPED(&ip6h->ip6_dst)) {
52485240Snordmark 				BUMP_MIB(&is->is_rawip_mib,
52493448Sdh155122 				    rawipOutErrors);
52508348SEric.Yu@Sun.COM 				return (EADDRNOTAVAIL);
52510Sstevel@tonic-gate 			}
52520Sstevel@tonic-gate 		}
52530Sstevel@tonic-gate 	}
52540Sstevel@tonic-gate 
52558348SEric.Yu@Sun.COM 	ip_len = mp->b_wptr - (uchar_t *)ip6h - IPV6_HDR_LEN;
52568348SEric.Yu@Sun.COM 	if (mp->b_cont != NULL)
52578348SEric.Yu@Sun.COM 		ip_len += msgdsize(mp->b_cont);
52580Sstevel@tonic-gate 
52590Sstevel@tonic-gate 	/*
52600Sstevel@tonic-gate 	 * Set the length into the IP header.
52610Sstevel@tonic-gate 	 * If the length is greater than the maximum allowed by IP,
52620Sstevel@tonic-gate 	 * then free the message and return. Do not try and send it
52630Sstevel@tonic-gate 	 * as this can cause problems in layers below.
52640Sstevel@tonic-gate 	 */
52650Sstevel@tonic-gate 	if (ip_len > IP_MAXPACKET) {
52665240Snordmark 		BUMP_MIB(&is->is_rawip_mib, rawipOutErrors);
52678348SEric.Yu@Sun.COM 		return (EMSGSIZE);
52680Sstevel@tonic-gate 	}
52690Sstevel@tonic-gate 	if (icmp->icmp_proto == IPPROTO_ICMPV6 || icmp->icmp_raw_checksum) {
52708348SEric.Yu@Sun.COM 		uint_t	cksum_off;	/* From ip6i == mp->b_rptr */
52710Sstevel@tonic-gate 		uint16_t *cksum_ptr;
52720Sstevel@tonic-gate 		uint_t	ext_hdrs_len;
52730Sstevel@tonic-gate 
52740Sstevel@tonic-gate 		/* ICMPv6 must have an offset matching icmp6_cksum offset */
52750Sstevel@tonic-gate 		ASSERT(icmp->icmp_proto != IPPROTO_ICMPV6 ||
52760Sstevel@tonic-gate 		    icmp->icmp_checksum_off == 2);
52770Sstevel@tonic-gate 
52780Sstevel@tonic-gate 		/*
52790Sstevel@tonic-gate 		 * We make it easy for IP to include our pseudo header
52800Sstevel@tonic-gate 		 * by putting our length in uh_checksum, modified (if
52810Sstevel@tonic-gate 		 * we have a routing header) by the checksum difference
52820Sstevel@tonic-gate 		 * between the ultimate destination and first hop addresses.
52830Sstevel@tonic-gate 		 * Note: ICMPv6 must always checksum the packet.
52840Sstevel@tonic-gate 		 */
52850Sstevel@tonic-gate 		cksum_off = ip_hdr_len + icmp->icmp_checksum_off;
52868348SEric.Yu@Sun.COM 		if (cksum_off + sizeof (uint16_t) > mp->b_wptr - mp->b_rptr) {
52878348SEric.Yu@Sun.COM 			if (!pullupmsg(mp, cksum_off + sizeof (uint16_t))) {
52885240Snordmark 				BUMP_MIB(&is->is_rawip_mib,
52893448Sdh155122 				    rawipOutErrors);
52900Sstevel@tonic-gate 				freemsg(mp);
52918348SEric.Yu@Sun.COM 				return (0);
52920Sstevel@tonic-gate 			}
52938348SEric.Yu@Sun.COM 			ip6i = (ip6i_t *)mp->b_rptr;
52940Sstevel@tonic-gate 			if (ip6i->ip6i_nxt == IPPROTO_RAW)
52950Sstevel@tonic-gate 				ip6h = (ip6_t *)&ip6i[1];
52960Sstevel@tonic-gate 			else
52970Sstevel@tonic-gate 				ip6h = (ip6_t *)ip6i;
52980Sstevel@tonic-gate 		}
52990Sstevel@tonic-gate 		/* Add payload length to checksum */
53000Sstevel@tonic-gate 		ext_hdrs_len = ip_hdr_len - IPV6_HDR_LEN -
53010Sstevel@tonic-gate 		    (int)((uchar_t *)ip6h - (uchar_t *)ip6i);
53020Sstevel@tonic-gate 		csum += htons(ip_len - ext_hdrs_len);
53030Sstevel@tonic-gate 
53040Sstevel@tonic-gate 		cksum_ptr = (uint16_t *)((uchar_t *)ip6i + cksum_off);
53050Sstevel@tonic-gate 		csum = (csum & 0xFFFF) + (csum >> 16);
53060Sstevel@tonic-gate 		*cksum_ptr = (uint16_t)csum;
53070Sstevel@tonic-gate 	}
53080Sstevel@tonic-gate 
53090Sstevel@tonic-gate #ifdef _LITTLE_ENDIAN
53100Sstevel@tonic-gate 	ip_len = htons(ip_len);
53110Sstevel@tonic-gate #endif
53120Sstevel@tonic-gate 	ip6h->ip6_plen = (uint16_t)ip_len;
53130Sstevel@tonic-gate 
53140Sstevel@tonic-gate 	/* We're done. Pass the packet to IP */
53155240Snordmark 	BUMP_MIB(&is->is_rawip_mib, rawipOutDatagrams);
53168348SEric.Yu@Sun.COM 	ip_output_v6(icmp->icmp_connp, mp, q, IP_WPUT);
53178348SEric.Yu@Sun.COM 	return (0);
53180Sstevel@tonic-gate }
53190Sstevel@tonic-gate 
53200Sstevel@tonic-gate static void
53210Sstevel@tonic-gate icmp_wput_other(queue_t *q, mblk_t *mp)
53220Sstevel@tonic-gate {
53230Sstevel@tonic-gate 	uchar_t	*rptr = mp->b_rptr;
53240Sstevel@tonic-gate 	struct iocblk *iocp;
53250Sstevel@tonic-gate #define	tudr ((struct T_unitdata_req *)rptr)
53265240Snordmark 	conn_t	*connp = Q_TO_CONN(q);
53275240Snordmark 	icmp_t	*icmp = connp->conn_icmp;
53285240Snordmark 	icmp_stack_t *is = icmp->icmp_is;
53290Sstevel@tonic-gate 	cred_t *cr;
53300Sstevel@tonic-gate 
53315240Snordmark 	cr = DB_CREDDEF(mp, connp->conn_cred);
53320Sstevel@tonic-gate 
53330Sstevel@tonic-gate 	switch (mp->b_datap->db_type) {
53340Sstevel@tonic-gate 	case M_PROTO:
53350Sstevel@tonic-gate 	case M_PCPROTO:
53360Sstevel@tonic-gate 		if (mp->b_wptr - rptr < sizeof (t_scalar_t)) {
53370Sstevel@tonic-gate 			/*
53380Sstevel@tonic-gate 			 * If the message does not contain a PRIM_type,
53390Sstevel@tonic-gate 			 * throw it away.
53400Sstevel@tonic-gate 			 */
53410Sstevel@tonic-gate 			freemsg(mp);
53420Sstevel@tonic-gate 			return;
53430Sstevel@tonic-gate 		}
53440Sstevel@tonic-gate 		switch (((union T_primitives *)rptr)->type) {
53450Sstevel@tonic-gate 		case T_ADDR_REQ:
53460Sstevel@tonic-gate 			icmp_addr_req(q, mp);
53470Sstevel@tonic-gate 			return;
53480Sstevel@tonic-gate 		case O_T_BIND_REQ:
53490Sstevel@tonic-gate 		case T_BIND_REQ:
53508348SEric.Yu@Sun.COM 			icmp_tpi_bind(q, mp);
53510Sstevel@tonic-gate 			return;
53520Sstevel@tonic-gate 		case T_CONN_REQ:
53538348SEric.Yu@Sun.COM 			icmp_tpi_connect(q, mp);
53540Sstevel@tonic-gate 			return;
53550Sstevel@tonic-gate 		case T_CAPABILITY_REQ:
53560Sstevel@tonic-gate 			icmp_capability_req(q, mp);
53570Sstevel@tonic-gate 			return;
53580Sstevel@tonic-gate 		case T_INFO_REQ:
53590Sstevel@tonic-gate 			icmp_info_req(q, mp);
53600Sstevel@tonic-gate 			return;
53610Sstevel@tonic-gate 		case T_UNITDATA_REQ:
53620Sstevel@tonic-gate 			/*
53630Sstevel@tonic-gate 			 * If a T_UNITDATA_REQ gets here, the address must
53640Sstevel@tonic-gate 			 * be bad.  Valid T_UNITDATA_REQs are found above
53650Sstevel@tonic-gate 			 * and break to below this switch.
53660Sstevel@tonic-gate 			 */
53670Sstevel@tonic-gate 			icmp_ud_err(q, mp, EADDRNOTAVAIL);
53680Sstevel@tonic-gate 			return;
53690Sstevel@tonic-gate 		case T_UNBIND_REQ:
53708348SEric.Yu@Sun.COM 			icmp_tpi_unbind(q, mp);
53710Sstevel@tonic-gate 			return;
53720Sstevel@tonic-gate 
53730Sstevel@tonic-gate 		case T_SVR4_OPTMGMT_REQ:
53745240Snordmark 			if (!snmpcom_req(q, mp, icmp_snmp_set, ip_snmp_get,
53755240Snordmark 			    cr)) {
53760Sstevel@tonic-gate 				/* Only IP can return anything meaningful */
53770Sstevel@tonic-gate 				(void) svr4_optcom_req(q, mp, cr,
53785240Snordmark 				    &icmp_opt_obj, B_TRUE);
53795240Snordmark 			}
53800Sstevel@tonic-gate 			return;
53810Sstevel@tonic-gate 
53820Sstevel@tonic-gate 		case T_OPTMGMT_REQ:
53830Sstevel@tonic-gate 			/* Only IP can return anything meaningful */
53845240Snordmark 			(void) tpi_optcom_req(q, mp, cr, &icmp_opt_obj, B_TRUE);
53850Sstevel@tonic-gate 			return;
53860Sstevel@tonic-gate 
53870Sstevel@tonic-gate 		case T_DISCON_REQ:
53888348SEric.Yu@Sun.COM 			icmp_tpi_disconnect(q, mp);
53890Sstevel@tonic-gate 			return;
53900Sstevel@tonic-gate 
53910Sstevel@tonic-gate 		/* The following TPI message is not supported by icmp. */
53920Sstevel@tonic-gate 		case O_T_CONN_RES:
53930Sstevel@tonic-gate 		case T_CONN_RES:
53940Sstevel@tonic-gate 			icmp_err_ack(q, mp, TNOTSUPPORT, 0);
53950Sstevel@tonic-gate 			return;
53960Sstevel@tonic-gate 
53970Sstevel@tonic-gate 		/* The following 3 TPI requests are illegal for icmp. */
53980Sstevel@tonic-gate 		case T_DATA_REQ:
53990Sstevel@tonic-gate 		case T_EXDATA_REQ:
54000Sstevel@tonic-gate 		case T_ORDREL_REQ:
54010Sstevel@tonic-gate 			freemsg(mp);
54020Sstevel@tonic-gate 			(void) putctl1(RD(q), M_ERROR, EPROTO);
54030Sstevel@tonic-gate 			return;
54040Sstevel@tonic-gate 		default:
54050Sstevel@tonic-gate 			break;
54060Sstevel@tonic-gate 		}
54070Sstevel@tonic-gate 		break;
54080Sstevel@tonic-gate 	case M_IOCTL:
54090Sstevel@tonic-gate 		iocp = (struct iocblk *)mp->b_rptr;
54100Sstevel@tonic-gate 		switch (iocp->ioc_cmd) {
54110Sstevel@tonic-gate 		case TI_GETPEERNAME:
54120Sstevel@tonic-gate 			if (icmp->icmp_state != TS_DATA_XFER) {
54130Sstevel@tonic-gate 				/*
54140Sstevel@tonic-gate 				 * If a default destination address has not
54150Sstevel@tonic-gate 				 * been associated with the stream, then we
54160Sstevel@tonic-gate 				 * don't know the peer's name.
54170Sstevel@tonic-gate 				 */
54180Sstevel@tonic-gate 				iocp->ioc_error = ENOTCONN;
54195240Snordmark 		err_ret:;
54200Sstevel@tonic-gate 				iocp->ioc_count = 0;
54210Sstevel@tonic-gate 				mp->b_datap->db_type = M_IOCACK;
54220Sstevel@tonic-gate 				qreply(q, mp);
54230Sstevel@tonic-gate 				return;
54240Sstevel@tonic-gate 			}
54250Sstevel@tonic-gate 			/* FALLTHRU */
54260Sstevel@tonic-gate 		case TI_GETMYNAME:
54270Sstevel@tonic-gate 			/*
54280Sstevel@tonic-gate 			 * For TI_GETPEERNAME and TI_GETMYNAME, we first
54290Sstevel@tonic-gate 			 * need to copyin the user's strbuf structure.
54300Sstevel@tonic-gate 			 * Processing will continue in the M_IOCDATA case
54310Sstevel@tonic-gate 			 * below.
54320Sstevel@tonic-gate 			 */
54330Sstevel@tonic-gate 			mi_copyin(q, mp, NULL,
54340Sstevel@tonic-gate 			    SIZEOF_STRUCT(strbuf, iocp->ioc_flag));
54350Sstevel@tonic-gate 			return;
54360Sstevel@tonic-gate 		case ND_SET:
54370Sstevel@tonic-gate 			/* nd_getset performs the necessary error checking */
54380Sstevel@tonic-gate 		case ND_GET:
54395240Snordmark 			if (nd_getset(q, is->is_nd, mp)) {
54400Sstevel@tonic-gate 				qreply(q, mp);
54410Sstevel@tonic-gate 				return;
54420Sstevel@tonic-gate 			}
54430Sstevel@tonic-gate 			break;
54448348SEric.Yu@Sun.COM 		case _SIOCSOCKFALLBACK:
54458348SEric.Yu@Sun.COM 			/*
54468348SEric.Yu@Sun.COM 			 * socket is falling back to be a
54478348SEric.Yu@Sun.COM 			 * streams socket. Nothing  to do
54488348SEric.Yu@Sun.COM 			 */
54498348SEric.Yu@Sun.COM 			iocp->ioc_count = 0;
54508348SEric.Yu@Sun.COM 			iocp->ioc_rval = 0;
54518348SEric.Yu@Sun.COM 			qreply(q, mp);
54528348SEric.Yu@Sun.COM 			return;
54530Sstevel@tonic-gate 		default:
54540Sstevel@tonic-gate 			break;
54550Sstevel@tonic-gate 		}
54560Sstevel@tonic-gate 		break;
54570Sstevel@tonic-gate 	case M_IOCDATA:
54580Sstevel@tonic-gate 		icmp_wput_iocdata(q, mp);
54590Sstevel@tonic-gate 		return;
54600Sstevel@tonic-gate 	default:
54610Sstevel@tonic-gate 		break;
54620Sstevel@tonic-gate 	}
54635240Snordmark 	ip_wput(q, mp);
54640Sstevel@tonic-gate }
54650Sstevel@tonic-gate 
54660Sstevel@tonic-gate /*
54670Sstevel@tonic-gate  * icmp_wput_iocdata is called by icmp_wput_slow to handle all M_IOCDATA
54680Sstevel@tonic-gate  * messages.
54690Sstevel@tonic-gate  */
54700Sstevel@tonic-gate static void
54710Sstevel@tonic-gate icmp_wput_iocdata(queue_t *q, mblk_t *mp)
54720Sstevel@tonic-gate {
54730Sstevel@tonic-gate 	mblk_t	*mp1;
54740Sstevel@tonic-gate 	STRUCT_HANDLE(strbuf, sb);
54750Sstevel@tonic-gate 	icmp_t	*icmp;
54768348SEric.Yu@Sun.COM 	uint_t	addrlen;
54778348SEric.Yu@Sun.COM 	uint_t	error;
54780Sstevel@tonic-gate 
54790Sstevel@tonic-gate 	/* Make sure it is one of ours. */
54800Sstevel@tonic-gate 	switch (((struct iocblk *)mp->b_rptr)->ioc_cmd) {
54810Sstevel@tonic-gate 	case TI_GETMYNAME:
54820Sstevel@tonic-gate 	case TI_GETPEERNAME:
54830Sstevel@tonic-gate 		break;
54840Sstevel@tonic-gate 	default:
54855240Snordmark 		icmp = Q_TO_ICMP(q);
54865240Snordmark 		ip_output(icmp->icmp_connp, mp, q, IP_WPUT);
54870Sstevel@tonic-gate 		return;
54880Sstevel@tonic-gate 	}
54890Sstevel@tonic-gate 	switch (mi_copy_state(q, mp, &mp1)) {
54900Sstevel@tonic-gate 	case -1:
54910Sstevel@tonic-gate 		return;
54920Sstevel@tonic-gate 	case MI_COPY_CASE(MI_COPY_IN, 1):
54930Sstevel@tonic-gate 		break;
54940Sstevel@tonic-gate 	case MI_COPY_CASE(MI_COPY_OUT, 1):
54950Sstevel@tonic-gate 		/*
54960Sstevel@tonic-gate 		 * The address has been copied out, so now
54970Sstevel@tonic-gate 		 * copyout the strbuf.
54980Sstevel@tonic-gate 		 */
54990Sstevel@tonic-gate 		mi_copyout(q, mp);
55000Sstevel@tonic-gate 		return;
55010Sstevel@tonic-gate 	case MI_COPY_CASE(MI_COPY_OUT, 2):
55020Sstevel@tonic-gate 		/*
55030Sstevel@tonic-gate 		 * The address and strbuf have been copied out.
55040Sstevel@tonic-gate 		 * We're done, so just acknowledge the original
55050Sstevel@tonic-gate 		 * M_IOCTL.
55060Sstevel@tonic-gate 		 */
55070Sstevel@tonic-gate 		mi_copy_done(q, mp, 0);
55080Sstevel@tonic-gate 		return;
55090Sstevel@tonic-gate 	default:
55100Sstevel@tonic-gate 		/*
55110Sstevel@tonic-gate 		 * Something strange has happened, so acknowledge
55120Sstevel@tonic-gate 		 * the original M_IOCTL with an EPROTO error.
55130Sstevel@tonic-gate 		 */
55140Sstevel@tonic-gate 		mi_copy_done(q, mp, EPROTO);
55150Sstevel@tonic-gate 		return;
55160Sstevel@tonic-gate 	}
55170Sstevel@tonic-gate 	/*
55180Sstevel@tonic-gate 	 * Now we have the strbuf structure for TI_GETMYNAME
55190Sstevel@tonic-gate 	 * and TI_GETPEERNAME.  Next we copyout the requested
55200Sstevel@tonic-gate 	 * address and then we'll copyout the strbuf.
55210Sstevel@tonic-gate 	 */
55220Sstevel@tonic-gate 	STRUCT_SET_HANDLE(sb, ((struct iocblk *)mp->b_rptr)->ioc_flag,
55230Sstevel@tonic-gate 	    (void *)mp1->b_rptr);
55245240Snordmark 	icmp = Q_TO_ICMP(q);
55250Sstevel@tonic-gate 	if (icmp->icmp_family == AF_INET)
55260Sstevel@tonic-gate 		addrlen = sizeof (sin_t);
55270Sstevel@tonic-gate 	else
55280Sstevel@tonic-gate 		addrlen = sizeof (sin6_t);
55290Sstevel@tonic-gate 
55300Sstevel@tonic-gate 	if (STRUCT_FGET(sb, maxlen) < addrlen) {
55310Sstevel@tonic-gate 		mi_copy_done(q, mp, EINVAL);
55320Sstevel@tonic-gate 		return;
55330Sstevel@tonic-gate 	}
55348348SEric.Yu@Sun.COM 
55358348SEric.Yu@Sun.COM 	mp1 = mi_copyout_alloc(q, mp, STRUCT_FGETP(sb, buf), addrlen, B_TRUE);
55368348SEric.Yu@Sun.COM 
55378348SEric.Yu@Sun.COM 	if (mp1 == NULL)
55388348SEric.Yu@Sun.COM 		return;
55398348SEric.Yu@Sun.COM 
55408348SEric.Yu@Sun.COM 	rw_enter(&icmp->icmp_rwlock, RW_READER);
55410Sstevel@tonic-gate 	switch (((struct iocblk *)mp->b_rptr)->ioc_cmd) {
55420Sstevel@tonic-gate 	case TI_GETMYNAME:
55438348SEric.Yu@Sun.COM 		error = rawip_do_getsockname(icmp, (void *)mp1->b_rptr,
55448348SEric.Yu@Sun.COM 		    &addrlen);
55450Sstevel@tonic-gate 		break;
55460Sstevel@tonic-gate 	case TI_GETPEERNAME:
55478348SEric.Yu@Sun.COM 		error = rawip_do_getpeername(icmp, (void *)mp1->b_rptr,
55488348SEric.Yu@Sun.COM 		    &addrlen);
55490Sstevel@tonic-gate 		break;
55508348SEric.Yu@Sun.COM 	}
55518348SEric.Yu@Sun.COM 	rw_exit(&icmp->icmp_rwlock);
55528348SEric.Yu@Sun.COM 
55538348SEric.Yu@Sun.COM 	if (error != 0) {
55548348SEric.Yu@Sun.COM 		mi_copy_done(q, mp, error);
55550Sstevel@tonic-gate 	} else {
55568348SEric.Yu@Sun.COM 		mp1->b_wptr += addrlen;
55578348SEric.Yu@Sun.COM 		STRUCT_FSET(sb, len, addrlen);
55588348SEric.Yu@Sun.COM 
55598348SEric.Yu@Sun.COM 		/* Copy out the address */
55608348SEric.Yu@Sun.COM 		mi_copyout(q, mp);
55618348SEric.Yu@Sun.COM 	}
55620Sstevel@tonic-gate }
55630Sstevel@tonic-gate 
55640Sstevel@tonic-gate static int
55650Sstevel@tonic-gate icmp_unitdata_opt_process(queue_t *q, mblk_t *mp, int *errorp,
55660Sstevel@tonic-gate     void *thisdg_attrs)
55670Sstevel@tonic-gate {
55685240Snordmark 	conn_t	*connp = Q_TO_CONN(q);
55690Sstevel@tonic-gate 	struct T_unitdata_req *udreqp;
55700Sstevel@tonic-gate 	int is_absreq_failure;
55710Sstevel@tonic-gate 	cred_t *cr;
55720Sstevel@tonic-gate 
55730Sstevel@tonic-gate 	udreqp = (struct T_unitdata_req *)mp->b_rptr;
55740Sstevel@tonic-gate 	*errorp = 0;
55750Sstevel@tonic-gate 
55765240Snordmark 	cr = DB_CREDDEF(mp, connp->conn_cred);
55770Sstevel@tonic-gate 
55780Sstevel@tonic-gate 	*errorp = tpi_optcom_buf(q, mp, &udreqp->OPT_length,
55790Sstevel@tonic-gate 	    udreqp->OPT_offset, cr, &icmp_opt_obj,
55800Sstevel@tonic-gate 	    thisdg_attrs, &is_absreq_failure);
55810Sstevel@tonic-gate 
55820Sstevel@tonic-gate 	if (*errorp != 0) {
55830Sstevel@tonic-gate 		/*
55840Sstevel@tonic-gate 		 * Note: No special action needed in this
55850Sstevel@tonic-gate 		 * module for "is_absreq_failure"
55860Sstevel@tonic-gate 		 */
55870Sstevel@tonic-gate 		return (-1);		/* failure */
55880Sstevel@tonic-gate 	}
55890Sstevel@tonic-gate 	ASSERT(is_absreq_failure == 0);
55900Sstevel@tonic-gate 	return (0);	/* success */
55910Sstevel@tonic-gate }
55920Sstevel@tonic-gate 
55930Sstevel@tonic-gate void
55948348SEric.Yu@Sun.COM icmp_ddi_g_init(void)
55950Sstevel@tonic-gate {
55965381Smeem 	icmp_max_optsize = optcom_max_optsize(icmp_opt_obj.odb_opt_des_arr,
55975240Snordmark 	    icmp_opt_obj.odb_opt_arr_cnt);
55980Sstevel@tonic-gate 
55993448Sdh155122 	/*
56003448Sdh155122 	 * We want to be informed each time a stack is created or
56013448Sdh155122 	 * destroyed in the kernel, so we can maintain the
56023448Sdh155122 	 * set of icmp_stack_t's.
56033448Sdh155122 	 */
56043448Sdh155122 	netstack_register(NS_ICMP, rawip_stack_init, NULL, rawip_stack_fini);
56050Sstevel@tonic-gate }
56060Sstevel@tonic-gate 
56070Sstevel@tonic-gate void
56088348SEric.Yu@Sun.COM icmp_ddi_g_destroy(void)
56090Sstevel@tonic-gate {
56103448Sdh155122 	netstack_unregister(NS_ICMP);
56110Sstevel@tonic-gate }
56120Sstevel@tonic-gate 
56138348SEric.Yu@Sun.COM #define	INET_NAME	"ip"
56148348SEric.Yu@Sun.COM 
56153448Sdh155122 /*
56163448Sdh155122  * Initialize the ICMP stack instance.
56173448Sdh155122  */
56183448Sdh155122 static void *
56193448Sdh155122 rawip_stack_init(netstackid_t stackid, netstack_t *ns)
56203448Sdh155122 {
56213448Sdh155122 	icmp_stack_t	*is;
56223448Sdh155122 	icmpparam_t	*pa;
56238348SEric.Yu@Sun.COM 	int		error = 0;
56248348SEric.Yu@Sun.COM 	major_t		major;
56253448Sdh155122 
56263448Sdh155122 	is = (icmp_stack_t *)kmem_zalloc(sizeof (*is), KM_SLEEP);
56273448Sdh155122 	is->is_netstack = ns;
56283448Sdh155122 
56293448Sdh155122 	pa = (icmpparam_t *)kmem_alloc(sizeof (icmp_param_arr), KM_SLEEP);
56303448Sdh155122 	is->is_param_arr = pa;
56313448Sdh155122 	bcopy(icmp_param_arr, is->is_param_arr, sizeof (icmp_param_arr));
56323448Sdh155122 
56333448Sdh155122 	(void) icmp_param_register(&is->is_nd,
56343448Sdh155122 	    is->is_param_arr, A_CNT(icmp_param_arr));
56353448Sdh155122 	is->is_ksp = rawip_kstat_init(stackid);
56368348SEric.Yu@Sun.COM 
56378348SEric.Yu@Sun.COM 	major = mod_name_to_major(INET_NAME);
56388348SEric.Yu@Sun.COM 	error = ldi_ident_from_major(major, &is->is_ldi_ident);
56398348SEric.Yu@Sun.COM 	ASSERT(error == 0);
56403448Sdh155122 	return (is);
56413448Sdh155122 }
56423448Sdh155122 
56433448Sdh155122 /*
56443448Sdh155122  * Free the ICMP stack instance.
56453448Sdh155122  */
56460Sstevel@tonic-gate static void
56473448Sdh155122 rawip_stack_fini(netstackid_t stackid, void *arg)
56483448Sdh155122 {
56493448Sdh155122 	icmp_stack_t *is = (icmp_stack_t *)arg;
56503448Sdh155122 
56513448Sdh155122 	nd_free(&is->is_nd);
56523448Sdh155122 	kmem_free(is->is_param_arr, sizeof (icmp_param_arr));
56533448Sdh155122 	is->is_param_arr = NULL;
56543448Sdh155122 
56553448Sdh155122 	rawip_kstat_fini(stackid, is->is_ksp);
56563448Sdh155122 	is->is_ksp = NULL;
56578348SEric.Yu@Sun.COM 	ldi_ident_release(is->is_ldi_ident);
56583448Sdh155122 	kmem_free(is, sizeof (*is));
56593448Sdh155122 }
56603448Sdh155122 
56613448Sdh155122 static void *
56623448Sdh155122 rawip_kstat_init(netstackid_t stackid) {
56633448Sdh155122 	kstat_t	*ksp;
56640Sstevel@tonic-gate 
56650Sstevel@tonic-gate 	rawip_named_kstat_t template = {
56660Sstevel@tonic-gate 		{ "inDatagrams",	KSTAT_DATA_UINT32, 0 },
56670Sstevel@tonic-gate 		{ "inCksumErrs",	KSTAT_DATA_UINT32, 0 },
56680Sstevel@tonic-gate 		{ "inErrors",		KSTAT_DATA_UINT32, 0 },
56690Sstevel@tonic-gate 		{ "outDatagrams",	KSTAT_DATA_UINT32, 0 },
56700Sstevel@tonic-gate 		{ "outErrors",		KSTAT_DATA_UINT32, 0 },
56710Sstevel@tonic-gate 	};
56720Sstevel@tonic-gate 
56733448Sdh155122 	ksp = kstat_create_netstack("icmp", 0, "rawip", "mib2",
56740Sstevel@tonic-gate 					KSTAT_TYPE_NAMED,
56750Sstevel@tonic-gate 					NUM_OF_FIELDS(rawip_named_kstat_t),
56763448Sdh155122 					0, stackid);
56773448Sdh155122 	if (ksp == NULL || ksp->ks_data == NULL)
56783448Sdh155122 		return (NULL);
56793448Sdh155122 
56803448Sdh155122 	bcopy(&template, ksp->ks_data, sizeof (template));
56813448Sdh155122 	ksp->ks_update = rawip_kstat_update;
56823448Sdh155122 	ksp->ks_private = (void *)(uintptr_t)stackid;
56833448Sdh155122 
56843448Sdh155122 	kstat_install(ksp);
56853448Sdh155122 	return (ksp);
56860Sstevel@tonic-gate }
56870Sstevel@tonic-gate 
56880Sstevel@tonic-gate static void
56893448Sdh155122 rawip_kstat_fini(netstackid_t stackid, kstat_t *ksp)
56903448Sdh155122 {
56913448Sdh155122 	if (ksp != NULL) {
56923448Sdh155122 		ASSERT(stackid == (netstackid_t)(uintptr_t)ksp->ks_private);
56933448Sdh155122 		kstat_delete_netstack(ksp, stackid);
56940Sstevel@tonic-gate 	}
56950Sstevel@tonic-gate }
56960Sstevel@tonic-gate 
56970Sstevel@tonic-gate static int
56983448Sdh155122 rawip_kstat_update(kstat_t *ksp, int rw)
56993448Sdh155122 {
57000Sstevel@tonic-gate 	rawip_named_kstat_t *rawipkp;
57013448Sdh155122 	netstackid_t	stackid = (netstackid_t)(uintptr_t)ksp->ks_private;
57023448Sdh155122 	netstack_t	*ns;
57033448Sdh155122 	icmp_stack_t	*is;
57043448Sdh155122 
57053448Sdh155122 	if ((ksp == NULL) || (ksp->ks_data == NULL))
57060Sstevel@tonic-gate 		return (EIO);
57070Sstevel@tonic-gate 
57080Sstevel@tonic-gate 	if (rw == KSTAT_WRITE)
57090Sstevel@tonic-gate 		return (EACCES);
57100Sstevel@tonic-gate 
57113448Sdh155122 	rawipkp = (rawip_named_kstat_t *)ksp->ks_data;
57123448Sdh155122 
57133448Sdh155122 	ns = netstack_find_by_stackid(stackid);
57143448Sdh155122 	if (ns == NULL)
57153448Sdh155122 		return (-1);
57163448Sdh155122 	is = ns->netstack_icmp;
57173448Sdh155122 	if (is == NULL) {
57183448Sdh155122 		netstack_rele(ns);
57193448Sdh155122 		return (-1);
57203448Sdh155122 	}
57213448Sdh155122 	rawipkp->inDatagrams.value.ui32 =  is->is_rawip_mib.rawipInDatagrams;
57223448Sdh155122 	rawipkp->inCksumErrs.value.ui32 =  is->is_rawip_mib.rawipInCksumErrs;
57233448Sdh155122 	rawipkp->inErrors.value.ui32 =	   is->is_rawip_mib.rawipInErrors;
57243448Sdh155122 	rawipkp->outDatagrams.value.ui32 = is->is_rawip_mib.rawipOutDatagrams;
57253448Sdh155122 	rawipkp->outErrors.value.ui32 =	   is->is_rawip_mib.rawipOutErrors;
57263448Sdh155122 	netstack_rele(ns);
57270Sstevel@tonic-gate 	return (0);
57280Sstevel@tonic-gate }
57298348SEric.Yu@Sun.COM 
57308348SEric.Yu@Sun.COM /* ARGSUSED */
57318348SEric.Yu@Sun.COM int
57328348SEric.Yu@Sun.COM rawip_accept(sock_lower_handle_t lproto_handle,
57338348SEric.Yu@Sun.COM     sock_lower_handle_t eproto_handle, sock_upper_handle_t sock_handle,
57348348SEric.Yu@Sun.COM     cred_t *cr)
57358348SEric.Yu@Sun.COM {
57368348SEric.Yu@Sun.COM 	return (EOPNOTSUPP);
57378348SEric.Yu@Sun.COM }
57388348SEric.Yu@Sun.COM 
57398348SEric.Yu@Sun.COM /* ARGSUSED */
57408348SEric.Yu@Sun.COM int
57418348SEric.Yu@Sun.COM rawip_bind(sock_lower_handle_t proto_handle, struct sockaddr *sa,
57428348SEric.Yu@Sun.COM     socklen_t len, cred_t *cr)
57438348SEric.Yu@Sun.COM {
57448348SEric.Yu@Sun.COM 	conn_t  *connp = (conn_t *)proto_handle;
57458348SEric.Yu@Sun.COM 	int error;
57468348SEric.Yu@Sun.COM 
57478348SEric.Yu@Sun.COM 	/* Binding to a NULL address really means unbind */
57488348SEric.Yu@Sun.COM 	if (sa == NULL)
57498348SEric.Yu@Sun.COM 		error = rawip_do_unbind(connp);
57508348SEric.Yu@Sun.COM 	else
57518348SEric.Yu@Sun.COM 		error = rawip_do_bind(connp, sa, len);
57528348SEric.Yu@Sun.COM 
57538348SEric.Yu@Sun.COM 	if (error < 0) {
57548348SEric.Yu@Sun.COM 		if (error == -TOUTSTATE)
57558348SEric.Yu@Sun.COM 			error = EINVAL;
57568348SEric.Yu@Sun.COM 		else
57578348SEric.Yu@Sun.COM 			error = proto_tlitosyserr(-error);
57588348SEric.Yu@Sun.COM 	}
57598348SEric.Yu@Sun.COM 	return (error);
57608348SEric.Yu@Sun.COM }
57618348SEric.Yu@Sun.COM 
57628348SEric.Yu@Sun.COM static int
57638348SEric.Yu@Sun.COM rawip_implicit_bind(conn_t *connp)
57648348SEric.Yu@Sun.COM {
57658348SEric.Yu@Sun.COM 	sin6_t sin6addr;
57668348SEric.Yu@Sun.COM 	sin_t *sin;
57678348SEric.Yu@Sun.COM 	sin6_t *sin6;
57688348SEric.Yu@Sun.COM 	socklen_t len;
57698348SEric.Yu@Sun.COM 	int error;
57708348SEric.Yu@Sun.COM 
57718348SEric.Yu@Sun.COM 	if (connp->conn_icmp->icmp_family == AF_INET) {
57728348SEric.Yu@Sun.COM 		len = sizeof (struct sockaddr_in);
57738348SEric.Yu@Sun.COM 		sin = (sin_t *)&sin6addr;
57748348SEric.Yu@Sun.COM 		*sin = sin_null;
57758348SEric.Yu@Sun.COM 		sin->sin_family = AF_INET;
57768348SEric.Yu@Sun.COM 		sin->sin_addr.s_addr = INADDR_ANY;
57778348SEric.Yu@Sun.COM 	} else {
57788348SEric.Yu@Sun.COM 		ASSERT(connp->conn_icmp->icmp_family == AF_INET6);
57798348SEric.Yu@Sun.COM 		len = sizeof (sin6_t);
57808348SEric.Yu@Sun.COM 		sin6 = (sin6_t *)&sin6addr;
57818348SEric.Yu@Sun.COM 		*sin6 = sin6_null;
57828348SEric.Yu@Sun.COM 		sin6->sin6_family = AF_INET6;
57838348SEric.Yu@Sun.COM 		V6_SET_ZERO(sin6->sin6_addr);
57848348SEric.Yu@Sun.COM 	}
57858348SEric.Yu@Sun.COM 
57868348SEric.Yu@Sun.COM 	error = rawip_do_bind(connp, (struct sockaddr *)&sin6addr, len);
57878348SEric.Yu@Sun.COM 
57888348SEric.Yu@Sun.COM 	return ((error < 0) ? proto_tlitosyserr(-error) : error);
57898348SEric.Yu@Sun.COM }
57908348SEric.Yu@Sun.COM 
57918348SEric.Yu@Sun.COM static int
57928348SEric.Yu@Sun.COM rawip_unbind(conn_t *connp)
57938348SEric.Yu@Sun.COM {
57948348SEric.Yu@Sun.COM 	int error;
57958348SEric.Yu@Sun.COM 
57968348SEric.Yu@Sun.COM 	error = rawip_do_unbind(connp);
57978348SEric.Yu@Sun.COM 	if (error < 0) {
57988348SEric.Yu@Sun.COM 		error = proto_tlitosyserr(-error);
57998348SEric.Yu@Sun.COM 	}
58008348SEric.Yu@Sun.COM 	return (error);
58018348SEric.Yu@Sun.COM }
58028348SEric.Yu@Sun.COM 
58038348SEric.Yu@Sun.COM /* ARGSUSED */
58048348SEric.Yu@Sun.COM int
58058348SEric.Yu@Sun.COM rawip_listen(sock_lower_handle_t proto_handle, int backlog, cred_t *cr)
58068348SEric.Yu@Sun.COM {
58078348SEric.Yu@Sun.COM 	return (EOPNOTSUPP);
58088348SEric.Yu@Sun.COM }
58098348SEric.Yu@Sun.COM 
58108348SEric.Yu@Sun.COM /* ARGSUSED */
58118348SEric.Yu@Sun.COM int
58128348SEric.Yu@Sun.COM rawip_connect(sock_lower_handle_t proto_handle, const struct sockaddr *sa,
58138348SEric.Yu@Sun.COM     socklen_t len, sock_connid_t *id, cred_t *cr)
58148348SEric.Yu@Sun.COM {
58158348SEric.Yu@Sun.COM 	conn_t	*connp = (conn_t *)proto_handle;
58168348SEric.Yu@Sun.COM 	icmp_t *icmp = connp->conn_icmp;
58178348SEric.Yu@Sun.COM 	int	error;
58188348SEric.Yu@Sun.COM 	boolean_t did_bind = B_FALSE;
58198348SEric.Yu@Sun.COM 
58208348SEric.Yu@Sun.COM 	if (sa == NULL) {
58218348SEric.Yu@Sun.COM 		/*
58228348SEric.Yu@Sun.COM 		 * Disconnect
58238348SEric.Yu@Sun.COM 		 * Make sure we are connected
58248348SEric.Yu@Sun.COM 		 */
58258348SEric.Yu@Sun.COM 		if (icmp->icmp_state != TS_DATA_XFER)
58268348SEric.Yu@Sun.COM 			return (EINVAL);
58278348SEric.Yu@Sun.COM 
58288348SEric.Yu@Sun.COM 		error = icmp_disconnect(connp);
58298348SEric.Yu@Sun.COM 		return (error);
58308348SEric.Yu@Sun.COM 	}
58318348SEric.Yu@Sun.COM 
58328348SEric.Yu@Sun.COM 	error = proto_verify_ip_addr(icmp->icmp_family, sa, len);
58338348SEric.Yu@Sun.COM 	if (error != 0)
58348348SEric.Yu@Sun.COM 		return (error);
58358348SEric.Yu@Sun.COM 
58368348SEric.Yu@Sun.COM 	/* do an implicit bind if necessary */
58378348SEric.Yu@Sun.COM 	if (icmp->icmp_state == TS_UNBND) {
58388348SEric.Yu@Sun.COM 		error = rawip_implicit_bind(connp);
58398348SEric.Yu@Sun.COM 		/*
58408348SEric.Yu@Sun.COM 		 * We could be racing with an actual bind, in which case
58418348SEric.Yu@Sun.COM 		 * we would see EPROTO. We cross our fingers and try
58428348SEric.Yu@Sun.COM 		 * to connect.
58438348SEric.Yu@Sun.COM 		 */
58448348SEric.Yu@Sun.COM 		if (!(error == 0 || error == EPROTO))
58458348SEric.Yu@Sun.COM 			return (error);
58468348SEric.Yu@Sun.COM 		did_bind = B_TRUE;
58478348SEric.Yu@Sun.COM 	}
58488348SEric.Yu@Sun.COM 
58498348SEric.Yu@Sun.COM 	/*
58508348SEric.Yu@Sun.COM 	 * set SO_DGRAM_ERRIND
58518348SEric.Yu@Sun.COM 	 */
58528348SEric.Yu@Sun.COM 	icmp->icmp_dgram_errind = B_TRUE;
58538348SEric.Yu@Sun.COM 
58548348SEric.Yu@Sun.COM 	error = rawip_do_connect(connp, sa, len);
58558348SEric.Yu@Sun.COM 
58568348SEric.Yu@Sun.COM 	if (error != 0 && did_bind) {
58578348SEric.Yu@Sun.COM 		int unbind_err;
58588348SEric.Yu@Sun.COM 
58598348SEric.Yu@Sun.COM 		unbind_err = rawip_unbind(connp);
58608348SEric.Yu@Sun.COM 		ASSERT(unbind_err == 0);
58618348SEric.Yu@Sun.COM 	}
58628348SEric.Yu@Sun.COM 
58638348SEric.Yu@Sun.COM 	if (error == 0) {
58648348SEric.Yu@Sun.COM 		*id = 0;
58658348SEric.Yu@Sun.COM 		(*connp->conn_upcalls->su_connected)
58668348SEric.Yu@Sun.COM 		    (connp->conn_upper_handle, 0, NULL, -1);
58678348SEric.Yu@Sun.COM 	} else if (error < 0) {
58688348SEric.Yu@Sun.COM 		error = proto_tlitosyserr(-error);
58698348SEric.Yu@Sun.COM 	}
58708348SEric.Yu@Sun.COM 	return (error);
58718348SEric.Yu@Sun.COM }
58728348SEric.Yu@Sun.COM 
58738348SEric.Yu@Sun.COM /* ARGSUSED */
58748348SEric.Yu@Sun.COM void
58758348SEric.Yu@Sun.COM rawip_fallback(sock_lower_handle_t proto_handle, queue_t *q,
58768348SEric.Yu@Sun.COM     boolean_t direct_sockfs, so_proto_quiesced_cb_t quiesced_cb)
58778348SEric.Yu@Sun.COM {
58788348SEric.Yu@Sun.COM 	conn_t  *connp = (conn_t *)proto_handle;
58798348SEric.Yu@Sun.COM 	icmp_t	*icmp;
58808348SEric.Yu@Sun.COM 	struct T_capability_ack tca;
58818348SEric.Yu@Sun.COM 	struct sockaddr_in6 laddr, faddr;
58828348SEric.Yu@Sun.COM 	socklen_t laddrlen, faddrlen;
58838348SEric.Yu@Sun.COM 	short opts;
58848348SEric.Yu@Sun.COM 	struct stroptions *stropt;
58858348SEric.Yu@Sun.COM 	mblk_t *stropt_mp;
58868348SEric.Yu@Sun.COM 	int error;
58878348SEric.Yu@Sun.COM 
58888348SEric.Yu@Sun.COM 	icmp = connp->conn_icmp;
58898348SEric.Yu@Sun.COM 
58908348SEric.Yu@Sun.COM 	stropt_mp = allocb_wait(sizeof (*stropt), BPRI_HI, STR_NOSIG, NULL);
58918348SEric.Yu@Sun.COM 
58928348SEric.Yu@Sun.COM 	/*
58938348SEric.Yu@Sun.COM 	 * setup the fallback stream that was allocated
58948348SEric.Yu@Sun.COM 	 */
58958348SEric.Yu@Sun.COM 	connp->conn_dev = (dev_t)RD(q)->q_ptr;
58968348SEric.Yu@Sun.COM 	connp->conn_minor_arena = WR(q)->q_ptr;
58978348SEric.Yu@Sun.COM 
58988348SEric.Yu@Sun.COM 	RD(q)->q_ptr = WR(q)->q_ptr = connp;
58998348SEric.Yu@Sun.COM 
59008348SEric.Yu@Sun.COM 	WR(q)->q_qinfo = &icmpwinit;
59018348SEric.Yu@Sun.COM 
59028348SEric.Yu@Sun.COM 	connp->conn_rq = RD(q);
59038348SEric.Yu@Sun.COM 	connp->conn_wq = WR(q);
59048348SEric.Yu@Sun.COM 
59058348SEric.Yu@Sun.COM 	/* Notify stream head about options before sending up data */
59068348SEric.Yu@Sun.COM 	stropt_mp->b_datap->db_type = M_SETOPTS;
59078348SEric.Yu@Sun.COM 	stropt_mp->b_wptr += sizeof (*stropt);
59088348SEric.Yu@Sun.COM 	stropt = (struct stroptions *)stropt_mp->b_rptr;
59098348SEric.Yu@Sun.COM 	stropt->so_flags = SO_WROFF | SO_HIWAT;
59108348SEric.Yu@Sun.COM 	stropt->so_wroff =
59118348SEric.Yu@Sun.COM 	    (ushort_t)(icmp->icmp_max_hdr_len + icmp->icmp_is->is_wroff_extra);
59128348SEric.Yu@Sun.COM 	stropt->so_hiwat = icmp->icmp_recv_hiwat;
59138348SEric.Yu@Sun.COM 	putnext(RD(q), stropt_mp);
59148348SEric.Yu@Sun.COM 
59158348SEric.Yu@Sun.COM 	/*
59168348SEric.Yu@Sun.COM 	 * free helper stream
59178348SEric.Yu@Sun.COM 	 */
5918*8477SRao.Shoaib@Sun.COM 	ip_free_helper_stream(connp);
59198348SEric.Yu@Sun.COM 
59208348SEric.Yu@Sun.COM 	/*
59218348SEric.Yu@Sun.COM 	 * Collect the information needed to sync with the sonode
59228348SEric.Yu@Sun.COM 	 */
59238348SEric.Yu@Sun.COM 	icmp_do_capability_ack(icmp, &tca, TC1_INFO);
59248348SEric.Yu@Sun.COM 
59258348SEric.Yu@Sun.COM 	laddrlen = faddrlen = sizeof (sin6_t);
59268348SEric.Yu@Sun.COM 	(void) rawip_getsockname((sock_lower_handle_t)connp,
59278348SEric.Yu@Sun.COM 	    (struct sockaddr *)&laddr, &laddrlen, NULL);
59288348SEric.Yu@Sun.COM 	error = rawip_getpeername((sock_lower_handle_t)connp,
59298348SEric.Yu@Sun.COM 	    (struct sockaddr *)&faddr, &faddrlen, NULL);
59308348SEric.Yu@Sun.COM 	if (error != 0)
59318348SEric.Yu@Sun.COM 		faddrlen = 0;
59328348SEric.Yu@Sun.COM 	opts = 0;
59338348SEric.Yu@Sun.COM 	if (icmp->icmp_dgram_errind)
59348348SEric.Yu@Sun.COM 		opts |= SO_DGRAM_ERRIND;
59358348SEric.Yu@Sun.COM 	if (icmp->icmp_dontroute)
59368348SEric.Yu@Sun.COM 		opts |= SO_DONTROUTE;
59378348SEric.Yu@Sun.COM 
59388348SEric.Yu@Sun.COM 	/*
59398348SEric.Yu@Sun.COM 	 * Once we grab the drain lock, no data will be send up
59408348SEric.Yu@Sun.COM 	 * to the socket. So we notify the socket that the endpoint
59418348SEric.Yu@Sun.COM 	 * is quiescent and it's therefore safe move data from
59428348SEric.Yu@Sun.COM 	 * the socket to the stream head.
59438348SEric.Yu@Sun.COM 	 */
59448348SEric.Yu@Sun.COM 	(*quiesced_cb)(connp->conn_upper_handle, q, &tca,
59458348SEric.Yu@Sun.COM 	    (struct sockaddr *)&laddr, laddrlen,
59468348SEric.Yu@Sun.COM 	    (struct sockaddr *)&faddr, faddrlen, opts);
59478348SEric.Yu@Sun.COM 
59488348SEric.Yu@Sun.COM 	/*
59498348SEric.Yu@Sun.COM 	 * push up any packets that were queued in icmp_t
59508348SEric.Yu@Sun.COM 	 */
59518348SEric.Yu@Sun.COM 
59528348SEric.Yu@Sun.COM 	mutex_enter(&icmp->icmp_recv_lock);
59538348SEric.Yu@Sun.COM 	while (icmp->icmp_fallback_queue_head != NULL) {
59548348SEric.Yu@Sun.COM 		mblk_t	*mp;
59558348SEric.Yu@Sun.COM 
59568348SEric.Yu@Sun.COM 		mp = icmp->icmp_fallback_queue_head;
59578348SEric.Yu@Sun.COM 		icmp->icmp_fallback_queue_head = mp->b_next;
59588348SEric.Yu@Sun.COM 		mp->b_next = NULL;
59598348SEric.Yu@Sun.COM 		mutex_exit(&icmp->icmp_recv_lock);
59608348SEric.Yu@Sun.COM 		putnext(RD(q), mp);
59618348SEric.Yu@Sun.COM 		mutex_enter(&icmp->icmp_recv_lock);
59628348SEric.Yu@Sun.COM 	}
59638348SEric.Yu@Sun.COM 	icmp->icmp_fallback_queue_tail = icmp->icmp_fallback_queue_head;
59648348SEric.Yu@Sun.COM 	/*
59658348SEric.Yu@Sun.COM 	 * No longer a streams less socket
59668348SEric.Yu@Sun.COM 	 */
59678348SEric.Yu@Sun.COM 	connp->conn_flags &= ~IPCL_NONSTR;
59688348SEric.Yu@Sun.COM 	mutex_exit(&icmp->icmp_recv_lock);
59698348SEric.Yu@Sun.COM 	ASSERT(icmp->icmp_fallback_queue_head == NULL &&
59708348SEric.Yu@Sun.COM 	    icmp->icmp_fallback_queue_tail == NULL);
59718348SEric.Yu@Sun.COM 
59728348SEric.Yu@Sun.COM 	ASSERT(connp->conn_ref >= 1);
59738348SEric.Yu@Sun.COM }
59748348SEric.Yu@Sun.COM 
59758348SEric.Yu@Sun.COM /* ARGSUSED */
59768348SEric.Yu@Sun.COM sock_lower_handle_t
59778348SEric.Yu@Sun.COM rawip_create(int family, int type, int proto, sock_downcalls_t **sock_downcalls,
59788348SEric.Yu@Sun.COM     uint_t *smodep, int *errorp, int flags, cred_t *credp)
59798348SEric.Yu@Sun.COM {
59808348SEric.Yu@Sun.COM 	conn_t *connp;
59818348SEric.Yu@Sun.COM 
59828348SEric.Yu@Sun.COM 	if (type != SOCK_RAW || (family != AF_INET && family != AF_INET6)) {
59838348SEric.Yu@Sun.COM 		*errorp = EPROTONOSUPPORT;
59848348SEric.Yu@Sun.COM 		return (NULL);
59858348SEric.Yu@Sun.COM 	}
59868348SEric.Yu@Sun.COM 
59878348SEric.Yu@Sun.COM 	connp = icmp_open(family, credp, errorp, flags);
59888348SEric.Yu@Sun.COM 	if (connp != NULL) {
59898348SEric.Yu@Sun.COM 		icmp_stack_t *is;
59908348SEric.Yu@Sun.COM 
59918348SEric.Yu@Sun.COM 		is = connp->conn_icmp->icmp_is;
59928348SEric.Yu@Sun.COM 		connp->conn_flags |= IPCL_NONSTR;
59938348SEric.Yu@Sun.COM 
59948348SEric.Yu@Sun.COM 		if (connp->conn_icmp->icmp_family == AF_INET6) {
59958348SEric.Yu@Sun.COM 			/* Build initial header template for transmit */
59968348SEric.Yu@Sun.COM 			rw_enter(&connp->conn_icmp->icmp_rwlock, RW_WRITER);
59978348SEric.Yu@Sun.COM 			if ((*errorp =
59988348SEric.Yu@Sun.COM 			    icmp_build_hdrs(connp->conn_icmp)) != 0) {
59998348SEric.Yu@Sun.COM 				rw_exit(&connp->conn_icmp->icmp_rwlock);
60008348SEric.Yu@Sun.COM 				ipcl_conn_destroy(connp);
60018348SEric.Yu@Sun.COM 				return (NULL);
60028348SEric.Yu@Sun.COM 			}
60038348SEric.Yu@Sun.COM 			rw_exit(&connp->conn_icmp->icmp_rwlock);
60048348SEric.Yu@Sun.COM 		}
60058348SEric.Yu@Sun.COM 
60068348SEric.Yu@Sun.COM 		connp->conn_icmp->icmp_recv_hiwat = is->is_recv_hiwat;
60078348SEric.Yu@Sun.COM 		connp->conn_icmp->icmp_xmit_hiwat = is->is_xmit_hiwat;
60088348SEric.Yu@Sun.COM 
60098348SEric.Yu@Sun.COM 		if ((*errorp = ip_create_helper_stream(connp,
60108348SEric.Yu@Sun.COM 		    is->is_ldi_ident)) != 0) {
60118348SEric.Yu@Sun.COM 			cmn_err(CE_CONT, "create of IP helper stream failed\n");
60128348SEric.Yu@Sun.COM 			(void) rawip_do_close(connp);
60138348SEric.Yu@Sun.COM 			return (NULL);
60148348SEric.Yu@Sun.COM 		}
60158348SEric.Yu@Sun.COM 
60168348SEric.Yu@Sun.COM 		mutex_enter(&connp->conn_lock);
60178348SEric.Yu@Sun.COM 		connp->conn_state_flags &= ~CONN_INCIPIENT;
60188348SEric.Yu@Sun.COM 		mutex_exit(&connp->conn_lock);
60198348SEric.Yu@Sun.COM 		*sock_downcalls = &sock_rawip_downcalls;
60208348SEric.Yu@Sun.COM 		*smodep = SM_ATOMIC;
60218348SEric.Yu@Sun.COM 	} else {
60228348SEric.Yu@Sun.COM 		ASSERT(*errorp != 0);
60238348SEric.Yu@Sun.COM 	}
60248348SEric.Yu@Sun.COM 
60258348SEric.Yu@Sun.COM 	return ((sock_lower_handle_t)connp);
60268348SEric.Yu@Sun.COM }
60278348SEric.Yu@Sun.COM 
60288348SEric.Yu@Sun.COM /* ARGSUSED */
60298348SEric.Yu@Sun.COM void
60308348SEric.Yu@Sun.COM rawip_activate(sock_lower_handle_t proto_handle,
60318348SEric.Yu@Sun.COM     sock_upper_handle_t sock_handle, sock_upcalls_t *sock_upcalls, int flags,
60328348SEric.Yu@Sun.COM     cred_t *cr)
60338348SEric.Yu@Sun.COM {
60348348SEric.Yu@Sun.COM 	conn_t 			*connp = (conn_t *)proto_handle;
60358348SEric.Yu@Sun.COM 	icmp_stack_t 		*is = connp->conn_icmp->icmp_is;
60368348SEric.Yu@Sun.COM 	struct sock_proto_props sopp;
60378348SEric.Yu@Sun.COM 
60388348SEric.Yu@Sun.COM 	connp->conn_upcalls = sock_upcalls;
60398348SEric.Yu@Sun.COM 	connp->conn_upper_handle = sock_handle;
60408348SEric.Yu@Sun.COM 
60418348SEric.Yu@Sun.COM 	sopp.sopp_flags = SOCKOPT_WROFF | SOCKOPT_RCVHIWAT | SOCKOPT_RCVLOWAT |
60428348SEric.Yu@Sun.COM 	    SOCKOPT_MAXBLK | SOCKOPT_MAXPSZ | SOCKOPT_MINPSZ;
60438348SEric.Yu@Sun.COM 	sopp.sopp_wroff = connp->conn_icmp->icmp_max_hdr_len +
60448348SEric.Yu@Sun.COM 	    is->is_wroff_extra;
60458348SEric.Yu@Sun.COM 	sopp.sopp_rxhiwat = is->is_recv_hiwat;
60468348SEric.Yu@Sun.COM 	sopp.sopp_rxlowat = icmp_mod_info.mi_lowat;
60478348SEric.Yu@Sun.COM 	sopp.sopp_maxblk = INFPSZ;
60488348SEric.Yu@Sun.COM 	sopp.sopp_maxpsz = IP_MAXPACKET;
60498348SEric.Yu@Sun.COM 	sopp.sopp_minpsz = (icmp_mod_info.mi_minpsz == 1) ? 0 :
60508348SEric.Yu@Sun.COM 	    icmp_mod_info.mi_minpsz;
60518348SEric.Yu@Sun.COM 
60528348SEric.Yu@Sun.COM 	(*connp->conn_upcalls->su_set_proto_props)
60538348SEric.Yu@Sun.COM 	    (connp->conn_upper_handle, &sopp);
60548348SEric.Yu@Sun.COM }
60558348SEric.Yu@Sun.COM 
60568348SEric.Yu@Sun.COM static int
60578348SEric.Yu@Sun.COM rawip_do_getsockname(icmp_t *icmp, struct sockaddr *sa, uint_t *salenp)
60588348SEric.Yu@Sun.COM {
60598348SEric.Yu@Sun.COM 	sin_t	*sin = (sin_t *)sa;
60608348SEric.Yu@Sun.COM 	sin6_t	*sin6 = (sin6_t *)sa;
60618348SEric.Yu@Sun.COM 
60628348SEric.Yu@Sun.COM 	ASSERT(icmp != NULL);
60638348SEric.Yu@Sun.COM 	ASSERT(RW_LOCK_HELD(&icmp->icmp_rwlock));
60648348SEric.Yu@Sun.COM 
60658348SEric.Yu@Sun.COM 	switch (icmp->icmp_family) {
60668348SEric.Yu@Sun.COM 	case AF_INET:
60678348SEric.Yu@Sun.COM 		ASSERT(icmp->icmp_ipversion == IPV4_VERSION);
60688348SEric.Yu@Sun.COM 		if (*salenp < sizeof (sin_t))
60698348SEric.Yu@Sun.COM 			return (EINVAL);
60708348SEric.Yu@Sun.COM 
60718348SEric.Yu@Sun.COM 		*salenp = sizeof (sin_t);
60728348SEric.Yu@Sun.COM 		*sin = sin_null;
60738348SEric.Yu@Sun.COM 		sin->sin_family = AF_INET;
60748348SEric.Yu@Sun.COM 		if (icmp->icmp_state == TS_UNBND) {
60758348SEric.Yu@Sun.COM 			break;
60768348SEric.Yu@Sun.COM 		}
60778348SEric.Yu@Sun.COM 
60788348SEric.Yu@Sun.COM 		if (!IN6_IS_ADDR_V4MAPPED_ANY(&icmp->icmp_v6src) &&
60798348SEric.Yu@Sun.COM 		    !IN6_IS_ADDR_UNSPECIFIED(&icmp->icmp_v6src)) {
60808348SEric.Yu@Sun.COM 			sin->sin_addr.s_addr = V4_PART_OF_V6(icmp->icmp_v6src);
60818348SEric.Yu@Sun.COM 		} else {
60828348SEric.Yu@Sun.COM 			/*
60838348SEric.Yu@Sun.COM 			 * INADDR_ANY
60848348SEric.Yu@Sun.COM 			 * icmp_v6src is not set, we might be bound to
60858348SEric.Yu@Sun.COM 			 * broadcast/multicast. Use icmp_bound_v6src as
60868348SEric.Yu@Sun.COM 			 * local address instead (that could
60878348SEric.Yu@Sun.COM 			 * also still be INADDR_ANY)
60888348SEric.Yu@Sun.COM 			 */
60898348SEric.Yu@Sun.COM 			sin->sin_addr.s_addr =
60908348SEric.Yu@Sun.COM 			    V4_PART_OF_V6(icmp->icmp_bound_v6src);
60918348SEric.Yu@Sun.COM 		}
60928348SEric.Yu@Sun.COM 		break;
60938348SEric.Yu@Sun.COM 	case AF_INET6:
60948348SEric.Yu@Sun.COM 
60958348SEric.Yu@Sun.COM 		if (*salenp < sizeof (sin6_t))
60968348SEric.Yu@Sun.COM 			return (EINVAL);
60978348SEric.Yu@Sun.COM 
60988348SEric.Yu@Sun.COM 		*salenp = sizeof (sin6_t);
60998348SEric.Yu@Sun.COM 		*sin6 = sin6_null;
61008348SEric.Yu@Sun.COM 		sin6->sin6_family = AF_INET6;
61018348SEric.Yu@Sun.COM 		if (icmp->icmp_state == TS_UNBND) {
61028348SEric.Yu@Sun.COM 			break;
61038348SEric.Yu@Sun.COM 		}
61048348SEric.Yu@Sun.COM 		if (!IN6_IS_ADDR_UNSPECIFIED(&icmp->icmp_v6src)) {
61058348SEric.Yu@Sun.COM 			sin6->sin6_addr = icmp->icmp_v6src;
61068348SEric.Yu@Sun.COM 		} else {
61078348SEric.Yu@Sun.COM 			/*
61088348SEric.Yu@Sun.COM 			 * UNSPECIFIED
61098348SEric.Yu@Sun.COM 			 * icmp_v6src is not set, we might be bound to
61108348SEric.Yu@Sun.COM 			 * broadcast/multicast. Use icmp_bound_v6src as
61118348SEric.Yu@Sun.COM 			 * local address instead (that could
61128348SEric.Yu@Sun.COM 			 * also still be UNSPECIFIED)
61138348SEric.Yu@Sun.COM 			 */
61148348SEric.Yu@Sun.COM 
61158348SEric.Yu@Sun.COM 			sin6->sin6_addr = icmp->icmp_bound_v6src;
61168348SEric.Yu@Sun.COM 		}
61178348SEric.Yu@Sun.COM 		break;
61188348SEric.Yu@Sun.COM 	}
61198348SEric.Yu@Sun.COM 	return (0);
61208348SEric.Yu@Sun.COM }
61218348SEric.Yu@Sun.COM 
61228348SEric.Yu@Sun.COM static int
61238348SEric.Yu@Sun.COM rawip_do_getpeername(icmp_t *icmp, struct sockaddr *sa, uint_t *salenp)
61248348SEric.Yu@Sun.COM {
61258348SEric.Yu@Sun.COM 	sin_t   *sin = (sin_t *)sa;
61268348SEric.Yu@Sun.COM 	sin6_t  *sin6 = (sin6_t *)sa;
61278348SEric.Yu@Sun.COM 
61288348SEric.Yu@Sun.COM 	ASSERT(icmp != NULL);
61298348SEric.Yu@Sun.COM 	ASSERT(RW_LOCK_HELD(&icmp->icmp_rwlock));
61308348SEric.Yu@Sun.COM 
61318348SEric.Yu@Sun.COM 	if (icmp->icmp_state != TS_DATA_XFER)
61328348SEric.Yu@Sun.COM 		return (ENOTCONN);
61338348SEric.Yu@Sun.COM 
61348348SEric.Yu@Sun.COM 	sa->sa_family = icmp->icmp_family;
61358348SEric.Yu@Sun.COM 	switch (icmp->icmp_family) {
61368348SEric.Yu@Sun.COM 	case AF_INET:
61378348SEric.Yu@Sun.COM 		ASSERT(icmp->icmp_ipversion == IPV4_VERSION);
61388348SEric.Yu@Sun.COM 
61398348SEric.Yu@Sun.COM 		if (*salenp < sizeof (sin_t))
61408348SEric.Yu@Sun.COM 			return (EINVAL);
61418348SEric.Yu@Sun.COM 
61428348SEric.Yu@Sun.COM 		*salenp = sizeof (sin_t);
61438348SEric.Yu@Sun.COM 		*sin = sin_null;
61448348SEric.Yu@Sun.COM 		sin->sin_family = AF_INET;
61458348SEric.Yu@Sun.COM 		sin->sin_addr.s_addr =
61468348SEric.Yu@Sun.COM 		    V4_PART_OF_V6(icmp->icmp_v6dst.sin6_addr);
61478348SEric.Yu@Sun.COM 		break;
61488348SEric.Yu@Sun.COM 	case AF_INET6:
61498348SEric.Yu@Sun.COM 		if (*salenp < sizeof (sin6_t))
61508348SEric.Yu@Sun.COM 			return (EINVAL);
61518348SEric.Yu@Sun.COM 
61528348SEric.Yu@Sun.COM 		*salenp = sizeof (sin6_t);
61538348SEric.Yu@Sun.COM 		*sin6 = sin6_null;
61548348SEric.Yu@Sun.COM 		*sin6 = icmp->icmp_v6dst;
61558348SEric.Yu@Sun.COM 		break;
61568348SEric.Yu@Sun.COM 	}
61578348SEric.Yu@Sun.COM 	return (0);
61588348SEric.Yu@Sun.COM }
61598348SEric.Yu@Sun.COM 
61608348SEric.Yu@Sun.COM /* ARGSUSED */
61618348SEric.Yu@Sun.COM int
61628348SEric.Yu@Sun.COM rawip_getpeername(sock_lower_handle_t proto_handle, struct sockaddr *sa,
61638348SEric.Yu@Sun.COM     socklen_t *salenp, cred_t *cr)
61648348SEric.Yu@Sun.COM {
61658348SEric.Yu@Sun.COM 	conn_t  *connp = (conn_t *)proto_handle;
61668348SEric.Yu@Sun.COM 	icmp_t  *icmp = connp->conn_icmp;
61678348SEric.Yu@Sun.COM 	int	error;
61688348SEric.Yu@Sun.COM 
61698348SEric.Yu@Sun.COM 	ASSERT(icmp != NULL);
61708348SEric.Yu@Sun.COM 
61718348SEric.Yu@Sun.COM 	rw_enter(&icmp->icmp_rwlock, RW_READER);
61728348SEric.Yu@Sun.COM 
61738348SEric.Yu@Sun.COM 	error = rawip_do_getpeername(icmp, sa, salenp);
61748348SEric.Yu@Sun.COM 
61758348SEric.Yu@Sun.COM 	rw_exit(&icmp->icmp_rwlock);
61768348SEric.Yu@Sun.COM 
61778348SEric.Yu@Sun.COM 	return (error);
61788348SEric.Yu@Sun.COM }
61798348SEric.Yu@Sun.COM 
61808348SEric.Yu@Sun.COM /* ARGSUSED */
61818348SEric.Yu@Sun.COM int
61828348SEric.Yu@Sun.COM rawip_getsockname(sock_lower_handle_t proto_handle, struct sockaddr *sa,
61838348SEric.Yu@Sun.COM     socklen_t *salenp, cred_t *cr)
61848348SEric.Yu@Sun.COM {
61858348SEric.Yu@Sun.COM 	conn_t  *connp = (conn_t *)proto_handle;
61868348SEric.Yu@Sun.COM 	icmp_t	*icmp = connp->conn_icmp;
61878348SEric.Yu@Sun.COM 	int	error;
61888348SEric.Yu@Sun.COM 
61898348SEric.Yu@Sun.COM 	ASSERT(icmp != NULL);
61908348SEric.Yu@Sun.COM 	rw_enter(&icmp->icmp_rwlock, RW_READER);
61918348SEric.Yu@Sun.COM 
61928348SEric.Yu@Sun.COM 	error = rawip_do_getsockname(icmp, sa, salenp);
61938348SEric.Yu@Sun.COM 
61948348SEric.Yu@Sun.COM 	rw_exit(&icmp->icmp_rwlock);
61958348SEric.Yu@Sun.COM 
61968348SEric.Yu@Sun.COM 	return (error);
61978348SEric.Yu@Sun.COM }
61988348SEric.Yu@Sun.COM 
61998348SEric.Yu@Sun.COM int
62008348SEric.Yu@Sun.COM rawip_setsockopt(sock_lower_handle_t proto_handle, int level, int option_name,
62018348SEric.Yu@Sun.COM     const void *optvalp, socklen_t optlen, cred_t *cr)
62028348SEric.Yu@Sun.COM {
62038348SEric.Yu@Sun.COM 	conn_t	*connp = (conn_t *)proto_handle;
62048348SEric.Yu@Sun.COM 	icmp_t *icmp = connp->conn_icmp;
62058348SEric.Yu@Sun.COM 	int error;
62068348SEric.Yu@Sun.COM 
62078348SEric.Yu@Sun.COM 	error = proto_opt_check(level, option_name, optlen, NULL,
62088348SEric.Yu@Sun.COM 	    icmp_opt_obj.odb_opt_des_arr,
62098348SEric.Yu@Sun.COM 	    icmp_opt_obj.odb_opt_arr_cnt,
62108348SEric.Yu@Sun.COM 	    icmp_opt_obj.odb_topmost_tpiprovider,
62118348SEric.Yu@Sun.COM 	    B_TRUE, B_FALSE, cr);
62128348SEric.Yu@Sun.COM 
62138348SEric.Yu@Sun.COM 	if (error != 0) {
62148348SEric.Yu@Sun.COM 		/*
62158348SEric.Yu@Sun.COM 		 * option not recognized
62168348SEric.Yu@Sun.COM 		 */
62178348SEric.Yu@Sun.COM 		if (error < 0) {
62188348SEric.Yu@Sun.COM 			error = proto_tlitosyserr(-error);
62198348SEric.Yu@Sun.COM 		}
62208348SEric.Yu@Sun.COM 		return (error);
62218348SEric.Yu@Sun.COM 	}
62228348SEric.Yu@Sun.COM 
62238348SEric.Yu@Sun.COM 	rw_enter(&icmp->icmp_rwlock, RW_WRITER);
62248348SEric.Yu@Sun.COM 	error = icmp_opt_set(connp, SETFN_OPTCOM_NEGOTIATE, level,
62258348SEric.Yu@Sun.COM 	    option_name, optlen, (uchar_t *)optvalp, (uint_t *)&optlen,
62268348SEric.Yu@Sun.COM 	    (uchar_t *)optvalp, NULL, cr);
62278348SEric.Yu@Sun.COM 	rw_exit(&icmp->icmp_rwlock);
62288348SEric.Yu@Sun.COM 
62298348SEric.Yu@Sun.COM 	if (error < 0) {
62308348SEric.Yu@Sun.COM 		/*
62318348SEric.Yu@Sun.COM 		 * Pass on to ip
62328348SEric.Yu@Sun.COM 		 */
62338348SEric.Yu@Sun.COM 		error = ip_set_options(connp, level, option_name, optvalp,
62348348SEric.Yu@Sun.COM 		    optlen, cr);
62358348SEric.Yu@Sun.COM 	}
62368348SEric.Yu@Sun.COM 
62378348SEric.Yu@Sun.COM 	ASSERT(error >= 0);
62388348SEric.Yu@Sun.COM 
62398348SEric.Yu@Sun.COM 	return (error);
62408348SEric.Yu@Sun.COM }
62418348SEric.Yu@Sun.COM 
62428348SEric.Yu@Sun.COM int
62438348SEric.Yu@Sun.COM rawip_getsockopt(sock_lower_handle_t proto_handle, int level, int option_name,
62448348SEric.Yu@Sun.COM     void *optvalp, socklen_t *optlen, cred_t *cr)
62458348SEric.Yu@Sun.COM {
62468348SEric.Yu@Sun.COM 	int		error;
62478348SEric.Yu@Sun.COM 	conn_t		*connp = (conn_t *)proto_handle;
62488348SEric.Yu@Sun.COM 	icmp_t		*icmp = connp->conn_icmp;
62498348SEric.Yu@Sun.COM 	t_uscalar_t	max_optbuf_len;
62508348SEric.Yu@Sun.COM 	void		*optvalp_buf;
62518348SEric.Yu@Sun.COM 	int		len;
62528348SEric.Yu@Sun.COM 
62538348SEric.Yu@Sun.COM 	error = proto_opt_check(level, option_name, *optlen, &max_optbuf_len,
62548348SEric.Yu@Sun.COM 	    icmp_opt_obj.odb_opt_des_arr,
62558348SEric.Yu@Sun.COM 	    icmp_opt_obj.odb_opt_arr_cnt,
62568348SEric.Yu@Sun.COM 	    icmp_opt_obj.odb_topmost_tpiprovider,
62578348SEric.Yu@Sun.COM 	    B_FALSE, B_TRUE, cr);
62588348SEric.Yu@Sun.COM 
62598348SEric.Yu@Sun.COM 	if (error != 0) {
62608348SEric.Yu@Sun.COM 		if (error < 0) {
62618348SEric.Yu@Sun.COM 			error = proto_tlitosyserr(-error);
62628348SEric.Yu@Sun.COM 		}
62638348SEric.Yu@Sun.COM 		return (error);
62648348SEric.Yu@Sun.COM 	}
62658348SEric.Yu@Sun.COM 
62668348SEric.Yu@Sun.COM 	optvalp_buf = kmem_alloc(max_optbuf_len, KM_SLEEP);
62678348SEric.Yu@Sun.COM 	rw_enter(&icmp->icmp_rwlock, RW_READER);
62688348SEric.Yu@Sun.COM 	len = icmp_opt_get(connp, level, option_name, optvalp_buf);
62698348SEric.Yu@Sun.COM 	rw_exit(&icmp->icmp_rwlock);
62708348SEric.Yu@Sun.COM 
62718348SEric.Yu@Sun.COM 	if (len < 0) {
62728348SEric.Yu@Sun.COM 		/*
62738348SEric.Yu@Sun.COM 		 * Pass on to IP
62748348SEric.Yu@Sun.COM 		 */
62758348SEric.Yu@Sun.COM 		kmem_free(optvalp_buf, max_optbuf_len);
62768348SEric.Yu@Sun.COM 		return (ip_get_options(connp, level, option_name, optvalp,
62778348SEric.Yu@Sun.COM 		    optlen, cr));
62788348SEric.Yu@Sun.COM 	} else {
62798348SEric.Yu@Sun.COM 		/*
62808348SEric.Yu@Sun.COM 		 * update optlen and copy option value
62818348SEric.Yu@Sun.COM 		 */
62828348SEric.Yu@Sun.COM 		t_uscalar_t size = MIN(len, *optlen);
62838348SEric.Yu@Sun.COM 		bcopy(optvalp_buf, optvalp, size);
62848348SEric.Yu@Sun.COM 		bcopy(&size, optlen, sizeof (size));
62858348SEric.Yu@Sun.COM 
62868348SEric.Yu@Sun.COM 		kmem_free(optvalp_buf, max_optbuf_len);
62878348SEric.Yu@Sun.COM 		return (0);
62888348SEric.Yu@Sun.COM 	}
62898348SEric.Yu@Sun.COM }
62908348SEric.Yu@Sun.COM 
62918348SEric.Yu@Sun.COM /* ARGSUSED */
62928348SEric.Yu@Sun.COM int
62938348SEric.Yu@Sun.COM rawip_close(sock_lower_handle_t proto_handle, int flags, cred_t *cr)
62948348SEric.Yu@Sun.COM {
62958348SEric.Yu@Sun.COM 	conn_t	*connp = (conn_t *)proto_handle;
62968348SEric.Yu@Sun.COM 	(void) rawip_do_close(connp);
62978348SEric.Yu@Sun.COM 	return (0);
62988348SEric.Yu@Sun.COM }
62998348SEric.Yu@Sun.COM 
63008348SEric.Yu@Sun.COM /* ARGSUSED */
63018348SEric.Yu@Sun.COM int
63028348SEric.Yu@Sun.COM rawip_shutdown(sock_lower_handle_t proto_handle, int how, cred_t *cr)
63038348SEric.Yu@Sun.COM {
63048348SEric.Yu@Sun.COM 	conn_t  *connp = (conn_t *)proto_handle;
63058348SEric.Yu@Sun.COM 
63068348SEric.Yu@Sun.COM 	/* shut down the send side */
63078348SEric.Yu@Sun.COM 	if (how != SHUT_RD)
63088348SEric.Yu@Sun.COM 		(*connp->conn_upcalls->su_opctl)(connp->conn_upper_handle,
63098348SEric.Yu@Sun.COM 		    SOCK_OPCTL_SHUT_SEND, 0);
63108348SEric.Yu@Sun.COM 	/* shut down the recv side */
63118348SEric.Yu@Sun.COM 	if (how != SHUT_WR)
63128348SEric.Yu@Sun.COM 		(*connp->conn_upcalls->su_opctl)(connp->conn_upper_handle,
63138348SEric.Yu@Sun.COM 		    SOCK_OPCTL_SHUT_RECV, 0);
63148348SEric.Yu@Sun.COM 	return (0);
63158348SEric.Yu@Sun.COM }
63168348SEric.Yu@Sun.COM 
63178348SEric.Yu@Sun.COM void
63188348SEric.Yu@Sun.COM rawip_clr_flowctrl(sock_lower_handle_t proto_handle)
63198348SEric.Yu@Sun.COM {
63208348SEric.Yu@Sun.COM 	conn_t  *connp = (conn_t *)proto_handle;
63218348SEric.Yu@Sun.COM 	icmp_t	*icmp = connp->conn_icmp;
63228348SEric.Yu@Sun.COM 
63238348SEric.Yu@Sun.COM 	mutex_enter(&icmp->icmp_recv_lock);
63248348SEric.Yu@Sun.COM 	connp->conn_flow_cntrld = B_FALSE;
63258348SEric.Yu@Sun.COM 	mutex_exit(&icmp->icmp_recv_lock);
63268348SEric.Yu@Sun.COM }
63278348SEric.Yu@Sun.COM 
63288348SEric.Yu@Sun.COM int
63298348SEric.Yu@Sun.COM rawip_ioctl(sock_lower_handle_t proto_handle, int cmd, intptr_t arg,
63308348SEric.Yu@Sun.COM     int mode, int32_t *rvalp, cred_t *cr)
63318348SEric.Yu@Sun.COM {
63328348SEric.Yu@Sun.COM 	conn_t  	*connp = (conn_t *)proto_handle;
63338348SEric.Yu@Sun.COM 	int		error;
63348348SEric.Yu@Sun.COM 
63358348SEric.Yu@Sun.COM 	switch (cmd) {
63368348SEric.Yu@Sun.COM 	case ND_SET:
63378348SEric.Yu@Sun.COM 	case ND_GET:
63388348SEric.Yu@Sun.COM 	case _SIOCSOCKFALLBACK:
63398348SEric.Yu@Sun.COM 	case TI_GETPEERNAME:
63408348SEric.Yu@Sun.COM 	case TI_GETMYNAME:
63418348SEric.Yu@Sun.COM #ifdef DEBUG
63428348SEric.Yu@Sun.COM 		cmn_err(CE_CONT, "icmp_ioctl cmd 0x%x on non streams"
63438348SEric.Yu@Sun.COM 		    " socket", cmd);
63448348SEric.Yu@Sun.COM #endif
63458348SEric.Yu@Sun.COM 		error = EINVAL;
63468348SEric.Yu@Sun.COM 		break;
63478348SEric.Yu@Sun.COM 	default:
63488348SEric.Yu@Sun.COM 		/*
63498348SEric.Yu@Sun.COM 		 * Pass on to IP using helper stream
63508348SEric.Yu@Sun.COM 		 */
63518444SRao.Shoaib@Sun.COM 		error = ldi_ioctl(connp->conn_helper_info->iphs_handle,
63528348SEric.Yu@Sun.COM 		    cmd, arg, mode, cr, rvalp);
63538348SEric.Yu@Sun.COM 		break;
63548348SEric.Yu@Sun.COM 	}
63558348SEric.Yu@Sun.COM 	return (error);
63568348SEric.Yu@Sun.COM }
63578348SEric.Yu@Sun.COM 
63588348SEric.Yu@Sun.COM /* ARGSUSED */
63598348SEric.Yu@Sun.COM int
63608348SEric.Yu@Sun.COM rawip_send(sock_lower_handle_t proto_handle, mblk_t *mp, struct nmsghdr *msg,
63618348SEric.Yu@Sun.COM     cred_t *cr)
63628348SEric.Yu@Sun.COM {
63638348SEric.Yu@Sun.COM 	conn_t *connp = (conn_t *)proto_handle;
63648348SEric.Yu@Sun.COM 	icmp_t	*icmp = connp->conn_icmp;
63658348SEric.Yu@Sun.COM 	icmp_stack_t *is = icmp->icmp_is;
63668348SEric.Yu@Sun.COM 	int error = 0;
63678348SEric.Yu@Sun.COM 	boolean_t bypass_dgram_errind = B_FALSE;
63688348SEric.Yu@Sun.COM 
63698348SEric.Yu@Sun.COM 	ASSERT(DB_TYPE(mp) == M_DATA);
63708348SEric.Yu@Sun.COM 
63718348SEric.Yu@Sun.COM 	if (is_system_labeled())
63728348SEric.Yu@Sun.COM 		msg_setcredpid(mp, cr, curproc->p_pid);
63738348SEric.Yu@Sun.COM 
63748348SEric.Yu@Sun.COM 	/* do an implicit bind if necessary */
63758348SEric.Yu@Sun.COM 	if (icmp->icmp_state == TS_UNBND) {
63768348SEric.Yu@Sun.COM 		error = rawip_implicit_bind(connp);
63778348SEric.Yu@Sun.COM 		/*
63788348SEric.Yu@Sun.COM 		 * We could be racing with an actual bind, in which case
63798348SEric.Yu@Sun.COM 		 * we would see EPROTO. We cross our fingers and try
63808348SEric.Yu@Sun.COM 		 * to connect.
63818348SEric.Yu@Sun.COM 		 */
63828348SEric.Yu@Sun.COM 		if (!(error == 0 || error == EPROTO)) {
63838348SEric.Yu@Sun.COM 			freemsg(mp);
63848348SEric.Yu@Sun.COM 			return (error);
63858348SEric.Yu@Sun.COM 		}
63868348SEric.Yu@Sun.COM 	}
63878348SEric.Yu@Sun.COM 
63888348SEric.Yu@Sun.COM 	rw_enter(&icmp->icmp_rwlock, RW_WRITER);
63898348SEric.Yu@Sun.COM 
63908348SEric.Yu@Sun.COM 	if (msg->msg_name != NULL && icmp->icmp_state == TS_DATA_XFER) {
63918348SEric.Yu@Sun.COM 		error = EISCONN;
63928348SEric.Yu@Sun.COM 		goto done_lock;
63938348SEric.Yu@Sun.COM 	}
63948348SEric.Yu@Sun.COM 
63958348SEric.Yu@Sun.COM 	switch (icmp->icmp_family) {
63968348SEric.Yu@Sun.COM 	case AF_INET6: {
63978348SEric.Yu@Sun.COM 		sin6_t	*sin6;
63988348SEric.Yu@Sun.COM 		ip6_pkt_t	ipp_s;	/* For ancillary data options */
63998348SEric.Yu@Sun.COM 		ip6_pkt_t	*ipp = &ipp_s;
64008348SEric.Yu@Sun.COM 
64018348SEric.Yu@Sun.COM 		sin6 = (sin6_t *)msg->msg_name;
64028348SEric.Yu@Sun.COM 		if (sin6 != NULL) {
64038348SEric.Yu@Sun.COM 			error = proto_verify_ip_addr(icmp->icmp_family,
64048348SEric.Yu@Sun.COM 			    (struct sockaddr *)msg->msg_name, msg->msg_namelen);
64058348SEric.Yu@Sun.COM 			if (error != 0) {
64068348SEric.Yu@Sun.COM 				bypass_dgram_errind = B_TRUE;
64078348SEric.Yu@Sun.COM 				goto done_lock;
64088348SEric.Yu@Sun.COM 			}
64098348SEric.Yu@Sun.COM 			if (icmp->icmp_delayed_error != 0) {
64108348SEric.Yu@Sun.COM 				sin6_t  *sin1 = (sin6_t *)msg->msg_name;
64118348SEric.Yu@Sun.COM 				sin6_t  *sin2 = (sin6_t *)
64128348SEric.Yu@Sun.COM 				    &icmp->icmp_delayed_addr;
64138348SEric.Yu@Sun.COM 
64148348SEric.Yu@Sun.COM 				error = icmp->icmp_delayed_error;
64158348SEric.Yu@Sun.COM 				icmp->icmp_delayed_error = 0;
64168348SEric.Yu@Sun.COM 
64178348SEric.Yu@Sun.COM 				/* Compare IP address and port */
64188348SEric.Yu@Sun.COM 
64198348SEric.Yu@Sun.COM 				if (sin1->sin6_port == sin2->sin6_port &&
64208348SEric.Yu@Sun.COM 				    IN6_ARE_ADDR_EQUAL(&sin1->sin6_addr,
64218348SEric.Yu@Sun.COM 				    &sin2->sin6_addr)) {
64228348SEric.Yu@Sun.COM 					goto done_lock;
64238348SEric.Yu@Sun.COM 				}
64248348SEric.Yu@Sun.COM 			}
64258348SEric.Yu@Sun.COM 		} else {
64268348SEric.Yu@Sun.COM 			/*
64278348SEric.Yu@Sun.COM 			 * Use connected address
64288348SEric.Yu@Sun.COM 			 */
64298348SEric.Yu@Sun.COM 			if (icmp->icmp_state != TS_DATA_XFER) {
64308348SEric.Yu@Sun.COM 				BUMP_MIB(&is->is_rawip_mib, rawipOutErrors);
64318348SEric.Yu@Sun.COM 				error = EDESTADDRREQ;
64328348SEric.Yu@Sun.COM 				bypass_dgram_errind = B_TRUE;
64338348SEric.Yu@Sun.COM 				goto done_lock;
64348348SEric.Yu@Sun.COM 			}
64358348SEric.Yu@Sun.COM 			sin6 = &icmp->icmp_v6dst;
64368348SEric.Yu@Sun.COM 		}
64378348SEric.Yu@Sun.COM 
64388348SEric.Yu@Sun.COM 		/* No support for mapped addresses on raw sockets */
64398348SEric.Yu@Sun.COM 		if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
64408348SEric.Yu@Sun.COM 			BUMP_MIB(&is->is_rawip_mib, rawipOutErrors);
64418348SEric.Yu@Sun.COM 			error = EADDRNOTAVAIL;
64428348SEric.Yu@Sun.COM 			goto done_lock;
64438348SEric.Yu@Sun.COM 		}
64448348SEric.Yu@Sun.COM 
64458348SEric.Yu@Sun.COM 		ipp->ipp_fields = 0;
64468348SEric.Yu@Sun.COM 		ipp->ipp_sticky_ignored = 0;
64478348SEric.Yu@Sun.COM 
64488348SEric.Yu@Sun.COM 		/*
64498348SEric.Yu@Sun.COM 		 * If options passed in, feed it for verification and handling
64508348SEric.Yu@Sun.COM 		 */
64518348SEric.Yu@Sun.COM 		if (msg->msg_controllen != 0) {
64528348SEric.Yu@Sun.COM 			error = process_auxiliary_options(connp,
64538348SEric.Yu@Sun.COM 			    msg->msg_control, msg->msg_controllen,
64548348SEric.Yu@Sun.COM 			    ipp, &icmp_opt_obj, icmp_opt_set);
64558348SEric.Yu@Sun.COM 			if (error != 0) {
64568348SEric.Yu@Sun.COM 				goto done_lock;
64578348SEric.Yu@Sun.COM 			}
64588348SEric.Yu@Sun.COM 		}
64598348SEric.Yu@Sun.COM 
64608348SEric.Yu@Sun.COM 		rw_exit(&icmp->icmp_rwlock);
64618348SEric.Yu@Sun.COM 
64628348SEric.Yu@Sun.COM 		/*
64638348SEric.Yu@Sun.COM 		 * Destination is a native IPv6 address.
64648348SEric.Yu@Sun.COM 		 * Send out an IPv6 format packet.
64658348SEric.Yu@Sun.COM 		 */
64668348SEric.Yu@Sun.COM 
64678348SEric.Yu@Sun.COM 		error = raw_ip_send_data_v6(connp->conn_wq, connp, mp, sin6,
64688348SEric.Yu@Sun.COM 		    ipp);
64698348SEric.Yu@Sun.COM 	}
64708348SEric.Yu@Sun.COM 		break;
64718348SEric.Yu@Sun.COM 	case AF_INET: {
64728348SEric.Yu@Sun.COM 		sin_t	*sin;
64738348SEric.Yu@Sun.COM 		ip4_pkt_t pktinfo;
64748348SEric.Yu@Sun.COM 		ip4_pkt_t *pktinfop = &pktinfo;
64758348SEric.Yu@Sun.COM 		ipaddr_t	v4dst;
64768348SEric.Yu@Sun.COM 
64778348SEric.Yu@Sun.COM 		sin = (sin_t *)msg->msg_name;
64788348SEric.Yu@Sun.COM 		if (sin != NULL) {
64798348SEric.Yu@Sun.COM 			error = proto_verify_ip_addr(icmp->icmp_family,
64808348SEric.Yu@Sun.COM 			    (struct sockaddr *)msg->msg_name, msg->msg_namelen);
64818348SEric.Yu@Sun.COM 			if (error != 0) {
64828348SEric.Yu@Sun.COM 				bypass_dgram_errind = B_TRUE;
64838348SEric.Yu@Sun.COM 				goto done_lock;
64848348SEric.Yu@Sun.COM 			}
64858348SEric.Yu@Sun.COM 			v4dst = sin->sin_addr.s_addr;
64868348SEric.Yu@Sun.COM 			if (icmp->icmp_delayed_error != 0) {
64878348SEric.Yu@Sun.COM 				sin_t *sin1 = (sin_t *)msg->msg_name;
64888348SEric.Yu@Sun.COM 				sin_t *sin2 = (sin_t *)&icmp->icmp_delayed_addr;
64898348SEric.Yu@Sun.COM 
64908348SEric.Yu@Sun.COM 				error = icmp->icmp_delayed_error;
64918348SEric.Yu@Sun.COM 				icmp->icmp_delayed_error = 0;
64928348SEric.Yu@Sun.COM 
64938348SEric.Yu@Sun.COM 				/* Compare IP address and port */
64948348SEric.Yu@Sun.COM 				if (sin1->sin_port == sin2->sin_port &&
64958348SEric.Yu@Sun.COM 				    sin1->sin_addr.s_addr ==
64968348SEric.Yu@Sun.COM 				    sin2->sin_addr.s_addr) {
64978348SEric.Yu@Sun.COM 					goto done_lock;
64988348SEric.Yu@Sun.COM 				}
64998348SEric.Yu@Sun.COM 
65008348SEric.Yu@Sun.COM 			}
65018348SEric.Yu@Sun.COM 		} else {
65028348SEric.Yu@Sun.COM 			/*
65038348SEric.Yu@Sun.COM 			 * Use connected address
65048348SEric.Yu@Sun.COM 			 */
65058348SEric.Yu@Sun.COM 			if (icmp->icmp_state != TS_DATA_XFER) {
65068348SEric.Yu@Sun.COM 				BUMP_MIB(&is->is_rawip_mib, rawipOutErrors);
65078348SEric.Yu@Sun.COM 				error = EDESTADDRREQ;
65088348SEric.Yu@Sun.COM 				bypass_dgram_errind = B_TRUE;
65098348SEric.Yu@Sun.COM 				goto done_lock;
65108348SEric.Yu@Sun.COM 			}
65118348SEric.Yu@Sun.COM 			v4dst = V4_PART_OF_V6(icmp->icmp_v6dst.sin6_addr);
65128348SEric.Yu@Sun.COM 		}
65138348SEric.Yu@Sun.COM 
65148348SEric.Yu@Sun.COM 
65158348SEric.Yu@Sun.COM 		pktinfop->ip4_ill_index = 0;
65168348SEric.Yu@Sun.COM 		pktinfop->ip4_addr = INADDR_ANY;
65178348SEric.Yu@Sun.COM 
65188348SEric.Yu@Sun.COM 		/*
65198348SEric.Yu@Sun.COM 		 * If options passed in, feed it for verification and handling
65208348SEric.Yu@Sun.COM 		 */
65218348SEric.Yu@Sun.COM 		if (msg->msg_controllen != 0) {
65228348SEric.Yu@Sun.COM 			error = process_auxiliary_options(connp,
65238348SEric.Yu@Sun.COM 			    msg->msg_control, msg->msg_controllen,
65248348SEric.Yu@Sun.COM 			    pktinfop, &icmp_opt_obj, icmp_opt_set);
65258348SEric.Yu@Sun.COM 			if (error != 0) {
65268348SEric.Yu@Sun.COM 				goto done_lock;
65278348SEric.Yu@Sun.COM 			}
65288348SEric.Yu@Sun.COM 		}
65298348SEric.Yu@Sun.COM 		rw_exit(&icmp->icmp_rwlock);
65308348SEric.Yu@Sun.COM 
65318348SEric.Yu@Sun.COM 		error = raw_ip_send_data_v4(connp->conn_wq, connp, mp,
65328348SEric.Yu@Sun.COM 		    v4dst, pktinfop);
65338348SEric.Yu@Sun.COM 		break;
65348348SEric.Yu@Sun.COM 	}
65358348SEric.Yu@Sun.COM 
65368348SEric.Yu@Sun.COM 	default:
65378348SEric.Yu@Sun.COM 		ASSERT(0);
65388348SEric.Yu@Sun.COM 	}
65398348SEric.Yu@Sun.COM 
65408348SEric.Yu@Sun.COM 	goto done;
65418348SEric.Yu@Sun.COM 
65428348SEric.Yu@Sun.COM done_lock:
65438348SEric.Yu@Sun.COM 	rw_exit(&icmp->icmp_rwlock);
65448348SEric.Yu@Sun.COM 	if (error != 0) {
65458348SEric.Yu@Sun.COM 		ASSERT(mp != NULL);
65468348SEric.Yu@Sun.COM 		freemsg(mp);
65478348SEric.Yu@Sun.COM 	}
65488348SEric.Yu@Sun.COM done:
65498348SEric.Yu@Sun.COM 	if (bypass_dgram_errind)
65508348SEric.Yu@Sun.COM 		return (error);
65518348SEric.Yu@Sun.COM 	return (icmp->icmp_dgram_errind ? error : 0);
65528348SEric.Yu@Sun.COM }
65538348SEric.Yu@Sun.COM 
65548348SEric.Yu@Sun.COM sock_downcalls_t sock_rawip_downcalls = {
65558348SEric.Yu@Sun.COM 	rawip_activate,
65568348SEric.Yu@Sun.COM 	rawip_accept,
65578348SEric.Yu@Sun.COM 	rawip_bind,
65588348SEric.Yu@Sun.COM 	rawip_listen,
65598348SEric.Yu@Sun.COM 	rawip_connect,
65608348SEric.Yu@Sun.COM 	rawip_getpeername,
65618348SEric.Yu@Sun.COM 	rawip_getsockname,
65628348SEric.Yu@Sun.COM 	rawip_getsockopt,
65638348SEric.Yu@Sun.COM 	rawip_setsockopt,
65648348SEric.Yu@Sun.COM 	rawip_send,
65658348SEric.Yu@Sun.COM 	NULL,
65668348SEric.Yu@Sun.COM 	NULL,
65678348SEric.Yu@Sun.COM 	NULL,
65688348SEric.Yu@Sun.COM 	rawip_shutdown,
65698348SEric.Yu@Sun.COM 	rawip_clr_flowctrl,
65708348SEric.Yu@Sun.COM 	rawip_ioctl,
65718348SEric.Yu@Sun.COM 	rawip_close
65728348SEric.Yu@Sun.COM };
6573