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