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 /* 22bfcb55b8SRao Shoaib * Copyright 2009 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/kmem.h> 447c478bd9Sstevel@tonic-gate #include <sys/policy.h> 457c478bd9Sstevel@tonic-gate #include <sys/ucred.h> 467c478bd9Sstevel@tonic-gate #include <sys/zone.h> 477c478bd9Sstevel@tonic-gate 487c478bd9Sstevel@tonic-gate #include <sys/socket.h> 490f1702c5SYu Xiangning #include <sys/socketvar.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> 710f1702c5SYu Xiangning #include <inet/proto_set.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> 81da14cebeSEric Cheng #include <sys/squeue_impl.h> 82b127ac41SPhilip Kirk #include <inet/ipnet.h> 83*e11c3f44Smeem #include <sys/ethernet.h> 847c478bd9Sstevel@tonic-gate 857c478bd9Sstevel@tonic-gate /* 86ff550d0eSmasputra * The ipsec_info.h header file is here since it has the definition for the 877c478bd9Sstevel@tonic-gate * M_CTL message types used by IP to convey information to the ULP. The 8845916cd2Sjpk * ipsec_info.h needs the pfkeyv2.h, hence the latter's presence. 897c478bd9Sstevel@tonic-gate */ 907c478bd9Sstevel@tonic-gate #include <net/pfkeyv2.h> 917c478bd9Sstevel@tonic-gate #include <inet/ipsec_info.h> 927c478bd9Sstevel@tonic-gate 9345916cd2Sjpk #include <sys/tsol/label.h> 9445916cd2Sjpk #include <sys/tsol/tnet.h> 9545916cd2Sjpk #include <rpc/pmap_prot.h> 9645916cd2Sjpk 977c478bd9Sstevel@tonic-gate /* 987c478bd9Sstevel@tonic-gate * Synchronization notes: 997c478bd9Sstevel@tonic-gate * 100fc80c0dfSnordmark * UDP is MT and uses the usual kernel synchronization primitives. There are 2 101fc80c0dfSnordmark * locks, the fanout lock (uf_lock) and the udp endpoint lock udp_rwlock. 102fc80c0dfSnordmark * We also use conn_lock when updating things that affect the IP classifier 103fc80c0dfSnordmark * lookup. 104fc80c0dfSnordmark * The lock order is udp_rwlock -> uf_lock and is udp_rwlock -> conn_lock. 1057c478bd9Sstevel@tonic-gate * 106fc80c0dfSnordmark * The fanout lock uf_lock: 107ff550d0eSmasputra * When a UDP endpoint is bound to a local port, it is inserted into 1087c478bd9Sstevel@tonic-gate * a bind hash list. The list consists of an array of udp_fanout_t buckets. 1097c478bd9Sstevel@tonic-gate * The size of the array is controlled by the udp_bind_fanout_size variable. 1107c478bd9Sstevel@tonic-gate * This variable can be changed in /etc/system if the default value is 111ff550d0eSmasputra * not large enough. Each bind hash bucket is protected by a per bucket 112ff550d0eSmasputra * lock. It protects the udp_bind_hash and udp_ptpbhn fields in the udp_t 113fc80c0dfSnordmark * structure and a few other fields in the udp_t. A UDP endpoint is removed 114fc80c0dfSnordmark * from the bind hash list only when it is being unbound or being closed. 115fc80c0dfSnordmark * The per bucket lock also protects a UDP endpoint's state changes. 116ff550d0eSmasputra * 117fc80c0dfSnordmark * The udp_rwlock: 118fc80c0dfSnordmark * This protects most of the other fields in the udp_t. The exact list of 119fc80c0dfSnordmark * fields which are protected by each of the above locks is documented in 120fc80c0dfSnordmark * the udp_t structure definition. 121ff550d0eSmasputra * 122fc80c0dfSnordmark * Plumbing notes: 123fc80c0dfSnordmark * UDP is always a device driver. For compatibility with mibopen() code 124fc80c0dfSnordmark * it is possible to I_PUSH "udp", but that results in pushing a passthrough 125fc80c0dfSnordmark * dummy module. 126ff550d0eSmasputra * 127fc80c0dfSnordmark * The above implies that we don't support any intermediate module to 128ff550d0eSmasputra * reside in between /dev/ip and udp -- in fact, we never supported such 129ff550d0eSmasputra * scenario in the past as the inter-layer communication semantics have 130fc80c0dfSnordmark * always been private. 1317c478bd9Sstevel@tonic-gate */ 1327c478bd9Sstevel@tonic-gate 133f4b3ec61Sdh /* For /etc/system control */ 1347c478bd9Sstevel@tonic-gate uint_t udp_bind_fanout_size = UDP_BIND_FANOUT_SIZE; 1357c478bd9Sstevel@tonic-gate 1367c478bd9Sstevel@tonic-gate #define NDD_TOO_QUICK_MSG \ 13745916cd2Sjpk "ndd get info rate too high for non-privileged users, try again " \ 1387c478bd9Sstevel@tonic-gate "later.\n" 1397c478bd9Sstevel@tonic-gate #define NDD_OUT_OF_BUF_MSG "<< Out of buffer >>\n" 1407c478bd9Sstevel@tonic-gate 14145916cd2Sjpk /* Option processing attrs */ 14245916cd2Sjpk typedef struct udpattrs_s { 14319a30e1aSrshoaib union { 14419a30e1aSrshoaib ip6_pkt_t *udpattr_ipp6; /* For V6 */ 14519a30e1aSrshoaib ip4_pkt_t *udpattr_ipp4; /* For V4 */ 14619a30e1aSrshoaib } udpattr_ippu; 14719a30e1aSrshoaib #define udpattr_ipp6 udpattr_ippu.udpattr_ipp6 14819a30e1aSrshoaib #define udpattr_ipp4 udpattr_ippu.udpattr_ipp4 14945916cd2Sjpk mblk_t *udpattr_mb; 15045916cd2Sjpk boolean_t udpattr_credset; 15145916cd2Sjpk } udpattrs_t; 15245916cd2Sjpk 1537c478bd9Sstevel@tonic-gate static void udp_addr_req(queue_t *q, mblk_t *mp); 1540f1702c5SYu Xiangning static void udp_tpi_bind(queue_t *q, mblk_t *mp); 1557c478bd9Sstevel@tonic-gate static void udp_bind_hash_insert(udp_fanout_t *uf, udp_t *udp); 1567c478bd9Sstevel@tonic-gate static void udp_bind_hash_remove(udp_t *udp, boolean_t caller_holds_lock); 157fc80c0dfSnordmark static int udp_build_hdrs(udp_t *udp); 1587c478bd9Sstevel@tonic-gate static void udp_capability_req(queue_t *q, mblk_t *mp); 1590f1702c5SYu Xiangning static int udp_tpi_close(queue_t *q, int flags); 1600f1702c5SYu Xiangning static void udp_tpi_connect(queue_t *q, mblk_t *mp); 1610f1702c5SYu Xiangning static void udp_tpi_disconnect(queue_t *q, mblk_t *mp); 1627c478bd9Sstevel@tonic-gate static void udp_err_ack(queue_t *q, mblk_t *mp, t_scalar_t t_error, 1637c478bd9Sstevel@tonic-gate int sys_error); 1647c478bd9Sstevel@tonic-gate static void udp_err_ack_prim(queue_t *q, mblk_t *mp, int primitive, 1657c478bd9Sstevel@tonic-gate t_scalar_t tlierr, int unixerr); 1667c478bd9Sstevel@tonic-gate static int udp_extra_priv_ports_get(queue_t *q, mblk_t *mp, caddr_t cp, 1677c478bd9Sstevel@tonic-gate cred_t *cr); 1687c478bd9Sstevel@tonic-gate static int udp_extra_priv_ports_add(queue_t *q, mblk_t *mp, 1697c478bd9Sstevel@tonic-gate char *value, caddr_t cp, cred_t *cr); 1707c478bd9Sstevel@tonic-gate static int udp_extra_priv_ports_del(queue_t *q, mblk_t *mp, 1717c478bd9Sstevel@tonic-gate char *value, caddr_t cp, cred_t *cr); 1720f1702c5SYu Xiangning static void udp_icmp_error(conn_t *, mblk_t *); 1730f1702c5SYu Xiangning static void udp_icmp_error_ipv6(conn_t *, mblk_t *); 1747c478bd9Sstevel@tonic-gate static void udp_info_req(queue_t *q, mblk_t *mp); 175fc80c0dfSnordmark static void udp_input(void *, mblk_t *, void *); 1767c478bd9Sstevel@tonic-gate static mblk_t *udp_ip_bind_mp(udp_t *udp, t_scalar_t bind_prim, 1777c478bd9Sstevel@tonic-gate t_scalar_t addr_length); 178fc80c0dfSnordmark static void udp_lrput(queue_t *, mblk_t *); 179fc80c0dfSnordmark static void udp_lwput(queue_t *, mblk_t *); 1807c478bd9Sstevel@tonic-gate static int udp_open(queue_t *q, dev_t *devp, int flag, int sflag, 181fc80c0dfSnordmark cred_t *credp, boolean_t isv6); 182fc80c0dfSnordmark static int udp_openv4(queue_t *q, dev_t *devp, int flag, int sflag, 183fc80c0dfSnordmark cred_t *credp); 184fc80c0dfSnordmark static int udp_openv6(queue_t *q, dev_t *devp, int flag, int sflag, 1857c478bd9Sstevel@tonic-gate cred_t *credp); 1867c478bd9Sstevel@tonic-gate static int udp_unitdata_opt_process(queue_t *q, mblk_t *mp, 18745916cd2Sjpk int *errorp, udpattrs_t *udpattrs); 1887c478bd9Sstevel@tonic-gate static boolean_t udp_opt_allow_udr_set(t_scalar_t level, t_scalar_t name); 1897c478bd9Sstevel@tonic-gate static int udp_param_get(queue_t *q, mblk_t *mp, caddr_t cp, cred_t *cr); 190f4b3ec61Sdh static boolean_t udp_param_register(IDP *ndp, udpparam_t *udppa, int cnt); 1917c478bd9Sstevel@tonic-gate static int udp_param_set(queue_t *q, mblk_t *mp, char *value, caddr_t cp, 1927c478bd9Sstevel@tonic-gate cred_t *cr); 1937c478bd9Sstevel@tonic-gate static void udp_report_item(mblk_t *mp, udp_t *udp); 194ff550d0eSmasputra static int udp_rinfop(queue_t *q, infod_t *dp); 195ff550d0eSmasputra static int udp_rrw(queue_t *q, struiod_t *dp); 1967c478bd9Sstevel@tonic-gate static int udp_status_report(queue_t *q, mblk_t *mp, caddr_t cp, 1977c478bd9Sstevel@tonic-gate cred_t *cr); 198da14cebeSEric Cheng static void udp_send_data(udp_t *udp, queue_t *q, mblk_t *mp, 199da14cebeSEric Cheng ipha_t *ipha); 200ff550d0eSmasputra static void udp_ud_err(queue_t *q, mblk_t *mp, uchar_t *destaddr, 201ff550d0eSmasputra t_scalar_t destlen, t_scalar_t err); 2020f1702c5SYu Xiangning static void udp_tpi_unbind(queue_t *q, mblk_t *mp); 20345916cd2Sjpk static in_port_t udp_update_next_port(udp_t *udp, in_port_t port, 20445916cd2Sjpk boolean_t random); 205437220cdSdanmcd static mblk_t *udp_output_v4(conn_t *, mblk_t *, ipaddr_t, uint16_t, uint_t, 2060f1702c5SYu Xiangning int *, boolean_t, struct nmsghdr *, cred_t *, pid_t); 207ff550d0eSmasputra static mblk_t *udp_output_v6(conn_t *connp, mblk_t *mp, sin6_t *sin6, 2080f1702c5SYu Xiangning int *error, struct nmsghdr *msg, cred_t *cr, pid_t pid); 2097c478bd9Sstevel@tonic-gate static void udp_wput_other(queue_t *q, mblk_t *mp); 2107c478bd9Sstevel@tonic-gate static void udp_wput_iocdata(queue_t *q, mblk_t *mp); 2110f1702c5SYu Xiangning static void udp_wput_fallback(queue_t *q, mblk_t *mp); 212ff550d0eSmasputra static size_t udp_set_rcv_hiwat(udp_t *udp, size_t size); 2137c478bd9Sstevel@tonic-gate 214f4b3ec61Sdh static void *udp_stack_init(netstackid_t stackid, netstack_t *ns); 215f4b3ec61Sdh static void udp_stack_fini(netstackid_t stackid, void *arg); 216f4b3ec61Sdh 217f4b3ec61Sdh static void *udp_kstat_init(netstackid_t stackid); 218f4b3ec61Sdh static void udp_kstat_fini(netstackid_t stackid, kstat_t *ksp); 219f4b3ec61Sdh static void *udp_kstat2_init(netstackid_t, udp_stat_t *); 220f4b3ec61Sdh static void udp_kstat2_fini(netstackid_t, kstat_t *); 2217c478bd9Sstevel@tonic-gate static int udp_kstat_update(kstat_t *kp, int rw); 222ff550d0eSmasputra 223ff550d0eSmasputra static void udp_rcv_enqueue(queue_t *q, udp_t *udp, mblk_t *mp, 224ff550d0eSmasputra uint_t pkt_len); 225ff550d0eSmasputra static void udp_rcv_drain(queue_t *q, udp_t *udp, boolean_t closing); 226fc80c0dfSnordmark static void udp_xmit(queue_t *, mblk_t *, ire_t *ire, conn_t *, zoneid_t); 227ff550d0eSmasputra 2280f1702c5SYu Xiangning static int udp_send_connected(conn_t *, mblk_t *, struct nmsghdr *, 2290f1702c5SYu Xiangning cred_t *, pid_t); 2300f1702c5SYu Xiangning 2310f1702c5SYu Xiangning /* Common routine for TPI and socket module */ 2320f1702c5SYu Xiangning static conn_t *udp_do_open(cred_t *, boolean_t, int); 2330f1702c5SYu Xiangning static void udp_do_close(conn_t *); 2340f1702c5SYu Xiangning static int udp_do_bind(conn_t *, struct sockaddr *, socklen_t, cred_t *, 2350f1702c5SYu Xiangning boolean_t); 2360f1702c5SYu Xiangning static int udp_do_unbind(conn_t *); 2370f1702c5SYu Xiangning static int udp_do_getsockname(udp_t *, struct sockaddr *, uint_t *); 2380f1702c5SYu Xiangning static int udp_do_getpeername(udp_t *, struct sockaddr *, uint_t *); 2390f1702c5SYu Xiangning 2400f1702c5SYu Xiangning int udp_getsockname(sock_lower_handle_t, 2410f1702c5SYu Xiangning struct sockaddr *, socklen_t *, cred_t *); 2420f1702c5SYu Xiangning int udp_getpeername(sock_lower_handle_t, 2430f1702c5SYu Xiangning struct sockaddr *, socklen_t *, cred_t *); 2440f1702c5SYu Xiangning static int udp_do_connect(conn_t *, const struct sockaddr *, socklen_t); 2450f1702c5SYu Xiangning static int udp_post_ip_bind_connect(udp_t *, mblk_t *, int); 2460f1702c5SYu Xiangning 247ff550d0eSmasputra #define UDP_RECV_HIWATER (56 * 1024) 248ff550d0eSmasputra #define UDP_RECV_LOWATER 128 249ff550d0eSmasputra #define UDP_XMIT_HIWATER (56 * 1024) 250ff550d0eSmasputra #define UDP_XMIT_LOWATER 1024 251ff550d0eSmasputra 2528e4b770fSLu Huafeng /* 2538e4b770fSLu Huafeng * The following is defined in tcp.c 2548e4b770fSLu Huafeng */ 2558e4b770fSLu Huafeng extern int (*cl_inet_connect2)(netstackid_t stack_id, 2568e4b770fSLu Huafeng uint8_t protocol, boolean_t is_outgoing, 2578e4b770fSLu Huafeng sa_family_t addr_family, 2588e4b770fSLu Huafeng uint8_t *laddrp, in_port_t lport, 2598e4b770fSLu Huafeng uint8_t *faddrp, in_port_t fport, void *args); 2608e4b770fSLu Huafeng 2618e4b770fSLu Huafeng /* 2628e4b770fSLu Huafeng * Checks if the given destination addr/port is allowed out. 2638e4b770fSLu Huafeng * If allowed, registers the (dest_addr/port, node_ID) mapping at Cluster. 2648e4b770fSLu Huafeng * Called for each connect() and for sendto()/sendmsg() to a different 2658e4b770fSLu Huafeng * destination. 2668e4b770fSLu Huafeng * For connect(), called in udp_connect(). 2678e4b770fSLu Huafeng * For sendto()/sendmsg(), called in udp_output_v{4,6}(). 2688e4b770fSLu Huafeng * 2698e4b770fSLu Huafeng * This macro assumes that the cl_inet_connect2 hook is not NULL. 2708e4b770fSLu Huafeng * Please check this before calling this macro. 2718e4b770fSLu Huafeng * 2728e4b770fSLu Huafeng * void 2738e4b770fSLu Huafeng * CL_INET_UDP_CONNECT(conn_t cp, udp_t *udp, boolean_t is_outgoing, 2748e4b770fSLu Huafeng * in6_addr_t *faddrp, in_port_t (or uint16_t) fport, int err); 2758e4b770fSLu Huafeng */ 2768e4b770fSLu Huafeng #define CL_INET_UDP_CONNECT(cp, udp, is_outgoing, faddrp, fport, err) { \ 2778e4b770fSLu Huafeng (err) = 0; \ 2788e4b770fSLu Huafeng /* \ 2798e4b770fSLu Huafeng * Running in cluster mode - check and register active \ 2808e4b770fSLu Huafeng * "connection" information \ 2818e4b770fSLu Huafeng */ \ 2828e4b770fSLu Huafeng if ((udp)->udp_ipversion == IPV4_VERSION) \ 2838e4b770fSLu Huafeng (err) = (*cl_inet_connect2)( \ 2848e4b770fSLu Huafeng (cp)->conn_netstack->netstack_stackid, \ 2858e4b770fSLu Huafeng IPPROTO_UDP, is_outgoing, AF_INET, \ 2868e4b770fSLu Huafeng (uint8_t *)&((udp)->udp_v6src._S6_un._S6_u32[3]), \ 2878e4b770fSLu Huafeng (udp)->udp_port, \ 2888e4b770fSLu Huafeng (uint8_t *)&((faddrp)->_S6_un._S6_u32[3]), \ 2898e4b770fSLu Huafeng (in_port_t)(fport), NULL); \ 2908e4b770fSLu Huafeng else \ 2918e4b770fSLu Huafeng (err) = (*cl_inet_connect2)( \ 2928e4b770fSLu Huafeng (cp)->conn_netstack->netstack_stackid, \ 2938e4b770fSLu Huafeng IPPROTO_UDP, is_outgoing, AF_INET6, \ 2948e4b770fSLu Huafeng (uint8_t *)&((udp)->udp_v6src), (udp)->udp_port, \ 2958e4b770fSLu Huafeng (uint8_t *)(faddrp), (in_port_t)(fport), NULL); \ 2968e4b770fSLu Huafeng } 2978e4b770fSLu Huafeng 298fc80c0dfSnordmark static struct module_info udp_mod_info = { 299ff550d0eSmasputra UDP_MOD_ID, UDP_MOD_NAME, 1, INFPSZ, UDP_RECV_HIWATER, UDP_RECV_LOWATER 300ff550d0eSmasputra }; 3017c478bd9Sstevel@tonic-gate 302fc80c0dfSnordmark /* 303fc80c0dfSnordmark * Entry points for UDP as a device. 304fc80c0dfSnordmark * We have separate open functions for the /dev/udp and /dev/udp6 devices. 305fc80c0dfSnordmark */ 306fc80c0dfSnordmark static struct qinit udp_rinitv4 = { 3070f1702c5SYu Xiangning NULL, NULL, udp_openv4, udp_tpi_close, NULL, 308fc80c0dfSnordmark &udp_mod_info, NULL, udp_rrw, udp_rinfop, STRUIOT_STANDARD 309fc80c0dfSnordmark }; 310fc80c0dfSnordmark 311fc80c0dfSnordmark static struct qinit udp_rinitv6 = { 3120f1702c5SYu Xiangning NULL, NULL, udp_openv6, udp_tpi_close, NULL, 313fc80c0dfSnordmark &udp_mod_info, NULL, udp_rrw, udp_rinfop, STRUIOT_STANDARD 314ff550d0eSmasputra }; 3157c478bd9Sstevel@tonic-gate 316ff550d0eSmasputra static struct qinit udp_winit = { 317a9737be2Snordmark (pfi_t)udp_wput, (pfi_t)ip_wsrv, NULL, NULL, NULL, 318fc80c0dfSnordmark &udp_mod_info, NULL, NULL, NULL, STRUIOT_NONE 319fc80c0dfSnordmark }; 320fc80c0dfSnordmark 3210f1702c5SYu Xiangning /* UDP entry point during fallback */ 3220f1702c5SYu Xiangning struct qinit udp_fallback_sock_winit = { 3230f1702c5SYu Xiangning (pfi_t)udp_wput_fallback, NULL, NULL, NULL, NULL, &udp_mod_info 3240f1702c5SYu Xiangning }; 3250f1702c5SYu Xiangning 326fc80c0dfSnordmark /* 327fc80c0dfSnordmark * UDP needs to handle I_LINK and I_PLINK since ifconfig 328fc80c0dfSnordmark * likes to use it as a place to hang the various streams. 329fc80c0dfSnordmark */ 330fc80c0dfSnordmark static struct qinit udp_lrinit = { 3310f1702c5SYu Xiangning (pfi_t)udp_lrput, NULL, udp_openv4, udp_tpi_close, NULL, 332fc80c0dfSnordmark &udp_mod_info 3337c478bd9Sstevel@tonic-gate }; 3347c478bd9Sstevel@tonic-gate 335fc80c0dfSnordmark static struct qinit udp_lwinit = { 3360f1702c5SYu Xiangning (pfi_t)udp_lwput, NULL, udp_openv4, udp_tpi_close, NULL, 337fc80c0dfSnordmark &udp_mod_info 3387c478bd9Sstevel@tonic-gate }; 3397c478bd9Sstevel@tonic-gate 340fc80c0dfSnordmark /* For AF_INET aka /dev/udp */ 341fc80c0dfSnordmark struct streamtab udpinfov4 = { 342fc80c0dfSnordmark &udp_rinitv4, &udp_winit, &udp_lrinit, &udp_lwinit 3437c478bd9Sstevel@tonic-gate }; 3447c478bd9Sstevel@tonic-gate 345fc80c0dfSnordmark /* For AF_INET6 aka /dev/udp6 */ 346fc80c0dfSnordmark struct streamtab udpinfov6 = { 347fc80c0dfSnordmark &udp_rinitv6, &udp_winit, &udp_lrinit, &udp_lwinit 3487c478bd9Sstevel@tonic-gate }; 3497c478bd9Sstevel@tonic-gate 3507c478bd9Sstevel@tonic-gate static sin_t sin_null; /* Zero address for quick clears */ 3517c478bd9Sstevel@tonic-gate static sin6_t sin6_null; /* Zero address for quick clears */ 3527c478bd9Sstevel@tonic-gate 353ff550d0eSmasputra #define UDP_MAXPACKET_IPV4 (IP_MAXPACKET - UDPH_SIZE - IP_SIMPLE_HDR_LENGTH) 3547c478bd9Sstevel@tonic-gate 3557c478bd9Sstevel@tonic-gate /* Default structure copied into T_INFO_ACK messages */ 3567c478bd9Sstevel@tonic-gate static struct T_info_ack udp_g_t_info_ack_ipv4 = { 3577c478bd9Sstevel@tonic-gate T_INFO_ACK, 3587c478bd9Sstevel@tonic-gate UDP_MAXPACKET_IPV4, /* TSDU_size. Excl. headers */ 3597c478bd9Sstevel@tonic-gate T_INVALID, /* ETSU_size. udp does not support expedited data. */ 3607c478bd9Sstevel@tonic-gate T_INVALID, /* CDATA_size. udp does not support connect data. */ 3617c478bd9Sstevel@tonic-gate T_INVALID, /* DDATA_size. udp does not support disconnect data. */ 3627c478bd9Sstevel@tonic-gate sizeof (sin_t), /* ADDR_size. */ 3637c478bd9Sstevel@tonic-gate 0, /* OPT_size - not initialized here */ 3647c478bd9Sstevel@tonic-gate UDP_MAXPACKET_IPV4, /* TIDU_size. Excl. headers */ 3657c478bd9Sstevel@tonic-gate T_CLTS, /* SERV_type. udp supports connection-less. */ 3667c478bd9Sstevel@tonic-gate TS_UNBND, /* CURRENT_state. This is set from udp_state. */ 3677c478bd9Sstevel@tonic-gate (XPG4_1|SENDZERO) /* PROVIDER_flag */ 3687c478bd9Sstevel@tonic-gate }; 3697c478bd9Sstevel@tonic-gate 370ff550d0eSmasputra #define UDP_MAXPACKET_IPV6 (IP_MAXPACKET - UDPH_SIZE - IPV6_HDR_LEN) 371ff550d0eSmasputra 3727c478bd9Sstevel@tonic-gate static struct T_info_ack udp_g_t_info_ack_ipv6 = { 3737c478bd9Sstevel@tonic-gate T_INFO_ACK, 3747c478bd9Sstevel@tonic-gate UDP_MAXPACKET_IPV6, /* TSDU_size. Excl. headers */ 3757c478bd9Sstevel@tonic-gate T_INVALID, /* ETSU_size. udp does not support expedited data. */ 3767c478bd9Sstevel@tonic-gate T_INVALID, /* CDATA_size. udp does not support connect data. */ 3777c478bd9Sstevel@tonic-gate T_INVALID, /* DDATA_size. udp does not support disconnect data. */ 3787c478bd9Sstevel@tonic-gate sizeof (sin6_t), /* ADDR_size. */ 3797c478bd9Sstevel@tonic-gate 0, /* OPT_size - not initialized here */ 3807c478bd9Sstevel@tonic-gate UDP_MAXPACKET_IPV6, /* TIDU_size. Excl. headers */ 3817c478bd9Sstevel@tonic-gate T_CLTS, /* SERV_type. udp supports connection-less. */ 3827c478bd9Sstevel@tonic-gate TS_UNBND, /* CURRENT_state. This is set from udp_state. */ 3837c478bd9Sstevel@tonic-gate (XPG4_1|SENDZERO) /* PROVIDER_flag */ 3847c478bd9Sstevel@tonic-gate }; 3857c478bd9Sstevel@tonic-gate 3867c478bd9Sstevel@tonic-gate /* largest UDP port number */ 3877c478bd9Sstevel@tonic-gate #define UDP_MAX_PORT 65535 3887c478bd9Sstevel@tonic-gate 3897c478bd9Sstevel@tonic-gate /* 390f4b3ec61Sdh * Table of ND variables supported by udp. These are loaded into us_nd 3917c478bd9Sstevel@tonic-gate * in udp_open. 3927c478bd9Sstevel@tonic-gate * All of these are alterable, within the min/max values given, at run time. 3937c478bd9Sstevel@tonic-gate */ 394ff550d0eSmasputra /* BEGIN CSTYLED */ 395ff550d0eSmasputra udpparam_t udp_param_arr[] = { 396ff550d0eSmasputra /*min max value name */ 397ff550d0eSmasputra { 0L, 256, 32, "udp_wroff_extra" }, 398ff550d0eSmasputra { 1L, 255, 255, "udp_ipv4_ttl" }, 399ff550d0eSmasputra { 0, IPV6_MAX_HOPS, IPV6_DEFAULT_HOPS, "udp_ipv6_hoplimit"}, 400ff550d0eSmasputra { 1024, (32 * 1024), 1024, "udp_smallest_nonpriv_port" }, 401ff550d0eSmasputra { 0, 1, 1, "udp_do_checksum" }, 402ff550d0eSmasputra { 1024, UDP_MAX_PORT, (32 * 1024), "udp_smallest_anon_port" }, 403ff550d0eSmasputra { 1024, UDP_MAX_PORT, UDP_MAX_PORT, "udp_largest_anon_port" }, 404ff550d0eSmasputra { UDP_XMIT_LOWATER, (1<<30), UDP_XMIT_HIWATER, "udp_xmit_hiwat"}, 405ff550d0eSmasputra { 0, (1<<30), UDP_XMIT_LOWATER, "udp_xmit_lowat"}, 406ff550d0eSmasputra { UDP_RECV_LOWATER, (1<<30), UDP_RECV_HIWATER, "udp_recv_hiwat"}, 407ff550d0eSmasputra { 65536, (1<<30), 2*1024*1024, "udp_max_buf"}, 408ff550d0eSmasputra { 100, 60000, 1000, "udp_ndd_get_info_interval"}, 4097c478bd9Sstevel@tonic-gate }; 410ff550d0eSmasputra /* END CSTYLED */ 4117c478bd9Sstevel@tonic-gate 412f4b3ec61Sdh /* Setable in /etc/system */ 4137c478bd9Sstevel@tonic-gate /* If set to 0, pick ephemeral port sequentially; otherwise randomly. */ 4147c478bd9Sstevel@tonic-gate uint32_t udp_random_anon_port = 1; 4157c478bd9Sstevel@tonic-gate 4167c478bd9Sstevel@tonic-gate /* 4177c478bd9Sstevel@tonic-gate * Hook functions to enable cluster networking. 4187c478bd9Sstevel@tonic-gate * On non-clustered systems these vectors must always be NULL 4197c478bd9Sstevel@tonic-gate */ 4207c478bd9Sstevel@tonic-gate 4218e4b770fSLu Huafeng void (*cl_inet_bind)(netstackid_t stack_id, uchar_t protocol, 4228e4b770fSLu Huafeng sa_family_t addr_family, uint8_t *laddrp, in_port_t lport, 4238e4b770fSLu Huafeng void *args) = NULL; 4248e4b770fSLu Huafeng void (*cl_inet_unbind)(netstackid_t stack_id, uint8_t protocol, 4258e4b770fSLu Huafeng sa_family_t addr_family, uint8_t *laddrp, in_port_t lport, 4268e4b770fSLu Huafeng void *args) = NULL; 427ff550d0eSmasputra 428ff550d0eSmasputra typedef union T_primitives *t_primp_t; 429ff550d0eSmasputra 4307c478bd9Sstevel@tonic-gate /* 43145916cd2Sjpk * Return the next anonymous port in the privileged port range for 4327c478bd9Sstevel@tonic-gate * bind checking. 43345916cd2Sjpk * 43445916cd2Sjpk * Trusted Extension (TX) notes: TX allows administrator to mark or 43545916cd2Sjpk * reserve ports as Multilevel ports (MLP). MLP has special function 43645916cd2Sjpk * on TX systems. Once a port is made MLP, it's not available as 43745916cd2Sjpk * ordinary port. This creates "holes" in the port name space. It 43845916cd2Sjpk * may be necessary to skip the "holes" find a suitable anon port. 4397c478bd9Sstevel@tonic-gate */ 4407c478bd9Sstevel@tonic-gate static in_port_t 44145916cd2Sjpk udp_get_next_priv_port(udp_t *udp) 4427c478bd9Sstevel@tonic-gate { 4437c478bd9Sstevel@tonic-gate static in_port_t next_priv_port = IPPORT_RESERVED - 1; 44445916cd2Sjpk in_port_t nextport; 44545916cd2Sjpk boolean_t restart = B_FALSE; 446f4b3ec61Sdh udp_stack_t *us = udp->udp_us; 4477c478bd9Sstevel@tonic-gate 44845916cd2Sjpk retry: 449f4b3ec61Sdh if (next_priv_port < us->us_min_anonpriv_port || 45045916cd2Sjpk next_priv_port >= IPPORT_RESERVED) { 4517c478bd9Sstevel@tonic-gate next_priv_port = IPPORT_RESERVED - 1; 45245916cd2Sjpk if (restart) 45345916cd2Sjpk return (0); 45445916cd2Sjpk restart = B_TRUE; 45545916cd2Sjpk } 45645916cd2Sjpk 45745916cd2Sjpk if (is_system_labeled() && 45845916cd2Sjpk (nextport = tsol_next_port(crgetzone(udp->udp_connp->conn_cred), 45945916cd2Sjpk next_priv_port, IPPROTO_UDP, B_FALSE)) != 0) { 46045916cd2Sjpk next_priv_port = nextport; 46145916cd2Sjpk goto retry; 4627c478bd9Sstevel@tonic-gate } 46345916cd2Sjpk 4647c478bd9Sstevel@tonic-gate return (next_priv_port--); 4657c478bd9Sstevel@tonic-gate } 4667c478bd9Sstevel@tonic-gate 4677c478bd9Sstevel@tonic-gate /* UDP bind hash report triggered via the Named Dispatch mechanism. */ 4687c478bd9Sstevel@tonic-gate /* ARGSUSED */ 4697c478bd9Sstevel@tonic-gate static int 4707c478bd9Sstevel@tonic-gate udp_bind_hash_report(queue_t *q, mblk_t *mp, caddr_t cp, cred_t *cr) 4717c478bd9Sstevel@tonic-gate { 4727c478bd9Sstevel@tonic-gate udp_fanout_t *udpf; 4737c478bd9Sstevel@tonic-gate int i; 4747c478bd9Sstevel@tonic-gate zoneid_t zoneid; 475ff550d0eSmasputra conn_t *connp; 476ff550d0eSmasputra udp_t *udp; 477f4b3ec61Sdh udp_stack_t *us; 478ff550d0eSmasputra 479ff550d0eSmasputra connp = Q_TO_CONN(q); 480ff550d0eSmasputra udp = connp->conn_udp; 481f4b3ec61Sdh us = udp->udp_us; 4827c478bd9Sstevel@tonic-gate 4837c478bd9Sstevel@tonic-gate /* Refer to comments in udp_status_report(). */ 484f4b3ec61Sdh if (cr == NULL || secpolicy_ip_config(cr, B_TRUE) != 0) { 485f4b3ec61Sdh if (ddi_get_lbolt() - us->us_last_ndd_get_info_time < 486f4b3ec61Sdh drv_usectohz(us->us_ndd_get_info_interval * 1000)) { 4877c478bd9Sstevel@tonic-gate (void) mi_mpprintf(mp, NDD_TOO_QUICK_MSG); 4887c478bd9Sstevel@tonic-gate return (0); 4897c478bd9Sstevel@tonic-gate } 4907c478bd9Sstevel@tonic-gate } 4917c478bd9Sstevel@tonic-gate if ((mp->b_cont = allocb(ND_MAX_BUF_LEN, BPRI_HI)) == NULL) { 4927c478bd9Sstevel@tonic-gate /* The following may work even if we cannot get a large buf. */ 4937c478bd9Sstevel@tonic-gate (void) mi_mpprintf(mp, NDD_OUT_OF_BUF_MSG); 4947c478bd9Sstevel@tonic-gate return (0); 4957c478bd9Sstevel@tonic-gate } 4967c478bd9Sstevel@tonic-gate 4977c478bd9Sstevel@tonic-gate (void) mi_mpprintf(mp, 4987c478bd9Sstevel@tonic-gate "UDP " MI_COL_HDRPAD_STR 4997c478bd9Sstevel@tonic-gate /* 12345678[89ABCDEF] */ 5007c478bd9Sstevel@tonic-gate " zone lport src addr dest addr port state"); 5017c478bd9Sstevel@tonic-gate /* 1234 12345 xxx.xxx.xxx.xxx xxx.xxx.xxx.xxx 12345 UNBOUND */ 5027c478bd9Sstevel@tonic-gate 503ff550d0eSmasputra zoneid = connp->conn_zoneid; 5047c478bd9Sstevel@tonic-gate 505f4b3ec61Sdh for (i = 0; i < us->us_bind_fanout_size; i++) { 506f4b3ec61Sdh udpf = &us->us_bind_fanout[i]; 5077c478bd9Sstevel@tonic-gate mutex_enter(&udpf->uf_lock); 5087c478bd9Sstevel@tonic-gate 5097c478bd9Sstevel@tonic-gate /* Print the hash index. */ 5107c478bd9Sstevel@tonic-gate udp = udpf->uf_udp; 5117c478bd9Sstevel@tonic-gate if (zoneid != GLOBAL_ZONEID) { 5127c478bd9Sstevel@tonic-gate /* skip to first entry in this zone; might be none */ 5137c478bd9Sstevel@tonic-gate while (udp != NULL && 514ff550d0eSmasputra udp->udp_connp->conn_zoneid != zoneid) 5157c478bd9Sstevel@tonic-gate udp = udp->udp_bind_hash; 5167c478bd9Sstevel@tonic-gate } 5177c478bd9Sstevel@tonic-gate if (udp != NULL) { 5187c478bd9Sstevel@tonic-gate uint_t print_len, buf_len; 5197c478bd9Sstevel@tonic-gate 5207c478bd9Sstevel@tonic-gate buf_len = mp->b_cont->b_datap->db_lim - 5217c478bd9Sstevel@tonic-gate mp->b_cont->b_wptr; 5227c478bd9Sstevel@tonic-gate print_len = snprintf((char *)mp->b_cont->b_wptr, 5237c478bd9Sstevel@tonic-gate buf_len, "%d\n", i); 5247c478bd9Sstevel@tonic-gate if (print_len < buf_len) { 5257c478bd9Sstevel@tonic-gate mp->b_cont->b_wptr += print_len; 5267c478bd9Sstevel@tonic-gate } else { 5277c478bd9Sstevel@tonic-gate mp->b_cont->b_wptr += buf_len; 5287c478bd9Sstevel@tonic-gate } 5297c478bd9Sstevel@tonic-gate for (; udp != NULL; udp = udp->udp_bind_hash) { 5307c478bd9Sstevel@tonic-gate if (zoneid == GLOBAL_ZONEID || 531ff550d0eSmasputra zoneid == udp->udp_connp->conn_zoneid) 5327c478bd9Sstevel@tonic-gate udp_report_item(mp->b_cont, udp); 5337c478bd9Sstevel@tonic-gate } 5347c478bd9Sstevel@tonic-gate } 5357c478bd9Sstevel@tonic-gate mutex_exit(&udpf->uf_lock); 5367c478bd9Sstevel@tonic-gate } 537f4b3ec61Sdh us->us_last_ndd_get_info_time = ddi_get_lbolt(); 5387c478bd9Sstevel@tonic-gate return (0); 5397c478bd9Sstevel@tonic-gate } 5407c478bd9Sstevel@tonic-gate 5417c478bd9Sstevel@tonic-gate /* 5427c478bd9Sstevel@tonic-gate * Hash list removal routine for udp_t structures. 5437c478bd9Sstevel@tonic-gate */ 5447c478bd9Sstevel@tonic-gate static void 5457c478bd9Sstevel@tonic-gate udp_bind_hash_remove(udp_t *udp, boolean_t caller_holds_lock) 5467c478bd9Sstevel@tonic-gate { 5477c478bd9Sstevel@tonic-gate udp_t *udpnext; 5487c478bd9Sstevel@tonic-gate kmutex_t *lockp; 549f4b3ec61Sdh udp_stack_t *us = udp->udp_us; 5507c478bd9Sstevel@tonic-gate 5517c478bd9Sstevel@tonic-gate if (udp->udp_ptpbhn == NULL) 5527c478bd9Sstevel@tonic-gate return; 5537c478bd9Sstevel@tonic-gate 5547c478bd9Sstevel@tonic-gate /* 5557c478bd9Sstevel@tonic-gate * Extract the lock pointer in case there are concurrent 5567c478bd9Sstevel@tonic-gate * hash_remove's for this instance. 5577c478bd9Sstevel@tonic-gate */ 5587c478bd9Sstevel@tonic-gate ASSERT(udp->udp_port != 0); 5597c478bd9Sstevel@tonic-gate if (!caller_holds_lock) { 560f4b3ec61Sdh lockp = &us->us_bind_fanout[UDP_BIND_HASH(udp->udp_port, 561437220cdSdanmcd us->us_bind_fanout_size)].uf_lock; 5627c478bd9Sstevel@tonic-gate ASSERT(lockp != NULL); 5637c478bd9Sstevel@tonic-gate mutex_enter(lockp); 5647c478bd9Sstevel@tonic-gate } 5657c478bd9Sstevel@tonic-gate if (udp->udp_ptpbhn != NULL) { 5667c478bd9Sstevel@tonic-gate udpnext = udp->udp_bind_hash; 5677c478bd9Sstevel@tonic-gate if (udpnext != NULL) { 5687c478bd9Sstevel@tonic-gate udpnext->udp_ptpbhn = udp->udp_ptpbhn; 5697c478bd9Sstevel@tonic-gate udp->udp_bind_hash = NULL; 5707c478bd9Sstevel@tonic-gate } 5717c478bd9Sstevel@tonic-gate *udp->udp_ptpbhn = udpnext; 5727c478bd9Sstevel@tonic-gate udp->udp_ptpbhn = NULL; 5737c478bd9Sstevel@tonic-gate } 5747c478bd9Sstevel@tonic-gate if (!caller_holds_lock) { 5757c478bd9Sstevel@tonic-gate mutex_exit(lockp); 5767c478bd9Sstevel@tonic-gate } 5777c478bd9Sstevel@tonic-gate } 5787c478bd9Sstevel@tonic-gate 5797c478bd9Sstevel@tonic-gate static void 5807c478bd9Sstevel@tonic-gate udp_bind_hash_insert(udp_fanout_t *uf, udp_t *udp) 5817c478bd9Sstevel@tonic-gate { 5827c478bd9Sstevel@tonic-gate udp_t **udpp; 5837c478bd9Sstevel@tonic-gate udp_t *udpnext; 5847c478bd9Sstevel@tonic-gate 5857c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&uf->uf_lock)); 586fc80c0dfSnordmark ASSERT(udp->udp_ptpbhn == NULL); 5877c478bd9Sstevel@tonic-gate udpp = &uf->uf_udp; 5887c478bd9Sstevel@tonic-gate udpnext = udpp[0]; 5897c478bd9Sstevel@tonic-gate if (udpnext != NULL) { 5907c478bd9Sstevel@tonic-gate /* 5917c478bd9Sstevel@tonic-gate * If the new udp bound to the INADDR_ANY address 5927c478bd9Sstevel@tonic-gate * and the first one in the list is not bound to 5937c478bd9Sstevel@tonic-gate * INADDR_ANY we skip all entries until we find the 5947c478bd9Sstevel@tonic-gate * first one bound to INADDR_ANY. 5957c478bd9Sstevel@tonic-gate * This makes sure that applications binding to a 5967c478bd9Sstevel@tonic-gate * specific address get preference over those binding to 5977c478bd9Sstevel@tonic-gate * INADDR_ANY. 5987c478bd9Sstevel@tonic-gate */ 5997c478bd9Sstevel@tonic-gate if (V6_OR_V4_INADDR_ANY(udp->udp_bound_v6src) && 6007c478bd9Sstevel@tonic-gate !V6_OR_V4_INADDR_ANY(udpnext->udp_bound_v6src)) { 6017c478bd9Sstevel@tonic-gate while ((udpnext = udpp[0]) != NULL && 6027c478bd9Sstevel@tonic-gate !V6_OR_V4_INADDR_ANY( 6037c478bd9Sstevel@tonic-gate udpnext->udp_bound_v6src)) { 6047c478bd9Sstevel@tonic-gate udpp = &(udpnext->udp_bind_hash); 6057c478bd9Sstevel@tonic-gate } 6067c478bd9Sstevel@tonic-gate if (udpnext != NULL) 6077c478bd9Sstevel@tonic-gate udpnext->udp_ptpbhn = &udp->udp_bind_hash; 6087c478bd9Sstevel@tonic-gate } else { 6097c478bd9Sstevel@tonic-gate udpnext->udp_ptpbhn = &udp->udp_bind_hash; 6107c478bd9Sstevel@tonic-gate } 6117c478bd9Sstevel@tonic-gate } 6127c478bd9Sstevel@tonic-gate udp->udp_bind_hash = udpnext; 6137c478bd9Sstevel@tonic-gate udp->udp_ptpbhn = udpp; 6147c478bd9Sstevel@tonic-gate udpp[0] = udp; 6157c478bd9Sstevel@tonic-gate } 6167c478bd9Sstevel@tonic-gate 6177c478bd9Sstevel@tonic-gate /* 6187c478bd9Sstevel@tonic-gate * This routine is called to handle each O_T_BIND_REQ/T_BIND_REQ message 6197c478bd9Sstevel@tonic-gate * passed to udp_wput. 6207c478bd9Sstevel@tonic-gate * It associates a port number and local address with the stream. 6217c478bd9Sstevel@tonic-gate * The O_T_BIND_REQ/T_BIND_REQ is passed downstream to ip with the UDP 6227c478bd9Sstevel@tonic-gate * protocol type (IPPROTO_UDP) placed in the message following the address. 6237c478bd9Sstevel@tonic-gate * A T_BIND_ACK message is passed upstream when ip acknowledges the request. 6247c478bd9Sstevel@tonic-gate * (Called as writer.) 6257c478bd9Sstevel@tonic-gate * 6267c478bd9Sstevel@tonic-gate * Note that UDP over IPv4 and IPv6 sockets can use the same port number 6277c478bd9Sstevel@tonic-gate * without setting SO_REUSEADDR. This is needed so that they 6287c478bd9Sstevel@tonic-gate * can be viewed as two independent transport protocols. 6297c478bd9Sstevel@tonic-gate * However, anonymouns ports are allocated from the same range to avoid 630f4b3ec61Sdh * duplicating the us->us_next_port_to_try. 6317c478bd9Sstevel@tonic-gate */ 6327c478bd9Sstevel@tonic-gate static void 6330f1702c5SYu Xiangning udp_tpi_bind(queue_t *q, mblk_t *mp) 6347c478bd9Sstevel@tonic-gate { 6357c478bd9Sstevel@tonic-gate sin_t *sin; 6367c478bd9Sstevel@tonic-gate sin6_t *sin6; 6377c478bd9Sstevel@tonic-gate mblk_t *mp1; 6387c478bd9Sstevel@tonic-gate struct T_bind_req *tbr; 639ff550d0eSmasputra conn_t *connp; 640ff550d0eSmasputra udp_t *udp; 6410f1702c5SYu Xiangning int error; 6420f1702c5SYu Xiangning struct sockaddr *sa; 6437c478bd9Sstevel@tonic-gate 644ff550d0eSmasputra connp = Q_TO_CONN(q); 645ff550d0eSmasputra udp = connp->conn_udp; 6467c478bd9Sstevel@tonic-gate if ((mp->b_wptr - mp->b_rptr) < sizeof (*tbr)) { 6477c478bd9Sstevel@tonic-gate (void) mi_strlog(q, 1, SL_ERROR|SL_TRACE, 6487c478bd9Sstevel@tonic-gate "udp_bind: bad req, len %u", 6497c478bd9Sstevel@tonic-gate (uint_t)(mp->b_wptr - mp->b_rptr)); 6507c478bd9Sstevel@tonic-gate udp_err_ack(q, mp, TPROTO, 0); 6517c478bd9Sstevel@tonic-gate return; 6527c478bd9Sstevel@tonic-gate } 6537c478bd9Sstevel@tonic-gate if (udp->udp_state != TS_UNBND) { 6547c478bd9Sstevel@tonic-gate (void) mi_strlog(q, 1, SL_ERROR|SL_TRACE, 6557c478bd9Sstevel@tonic-gate "udp_bind: bad state, %u", udp->udp_state); 6567c478bd9Sstevel@tonic-gate udp_err_ack(q, mp, TOUTSTATE, 0); 6577c478bd9Sstevel@tonic-gate return; 6587c478bd9Sstevel@tonic-gate } 6597c478bd9Sstevel@tonic-gate /* 6607c478bd9Sstevel@tonic-gate * Reallocate the message to make sure we have enough room for an 6617c478bd9Sstevel@tonic-gate * address and the protocol type. 6627c478bd9Sstevel@tonic-gate */ 6637c478bd9Sstevel@tonic-gate mp1 = reallocb(mp, sizeof (struct T_bind_ack) + sizeof (sin6_t) + 1, 1); 6647c478bd9Sstevel@tonic-gate if (!mp1) { 6657c478bd9Sstevel@tonic-gate udp_err_ack(q, mp, TSYSERR, ENOMEM); 6667c478bd9Sstevel@tonic-gate return; 6677c478bd9Sstevel@tonic-gate } 6687c478bd9Sstevel@tonic-gate 6697c478bd9Sstevel@tonic-gate mp = mp1; 6700f1702c5SYu Xiangning 6710f1702c5SYu Xiangning /* Reset the message type in preparation for shipping it back. */ 6720f1702c5SYu Xiangning DB_TYPE(mp) = M_PCPROTO; 6730f1702c5SYu Xiangning 6747c478bd9Sstevel@tonic-gate tbr = (struct T_bind_req *)mp->b_rptr; 6757c478bd9Sstevel@tonic-gate switch (tbr->ADDR_length) { 6767c478bd9Sstevel@tonic-gate case 0: /* Request for a generic port */ 6777c478bd9Sstevel@tonic-gate tbr->ADDR_offset = sizeof (struct T_bind_req); 6787c478bd9Sstevel@tonic-gate if (udp->udp_family == AF_INET) { 6797c478bd9Sstevel@tonic-gate tbr->ADDR_length = sizeof (sin_t); 6807c478bd9Sstevel@tonic-gate sin = (sin_t *)&tbr[1]; 6817c478bd9Sstevel@tonic-gate *sin = sin_null; 6827c478bd9Sstevel@tonic-gate sin->sin_family = AF_INET; 6837c478bd9Sstevel@tonic-gate mp->b_wptr = (uchar_t *)&sin[1]; 6840f1702c5SYu Xiangning sa = (struct sockaddr *)sin; 6857c478bd9Sstevel@tonic-gate } else { 6867c478bd9Sstevel@tonic-gate ASSERT(udp->udp_family == AF_INET6); 6877c478bd9Sstevel@tonic-gate tbr->ADDR_length = sizeof (sin6_t); 6887c478bd9Sstevel@tonic-gate sin6 = (sin6_t *)&tbr[1]; 6897c478bd9Sstevel@tonic-gate *sin6 = sin6_null; 6907c478bd9Sstevel@tonic-gate sin6->sin6_family = AF_INET6; 6917c478bd9Sstevel@tonic-gate mp->b_wptr = (uchar_t *)&sin6[1]; 6920f1702c5SYu Xiangning sa = (struct sockaddr *)sin6; 6937c478bd9Sstevel@tonic-gate } 6947c478bd9Sstevel@tonic-gate break; 6957c478bd9Sstevel@tonic-gate 6967c478bd9Sstevel@tonic-gate case sizeof (sin_t): /* Complete IPv4 address */ 6970f1702c5SYu Xiangning sa = (struct sockaddr *)mi_offset_param(mp, tbr->ADDR_offset, 6987c478bd9Sstevel@tonic-gate sizeof (sin_t)); 6990f1702c5SYu Xiangning if (sa == NULL || !OK_32PTR((char *)sa)) { 7007c478bd9Sstevel@tonic-gate udp_err_ack(q, mp, TSYSERR, EINVAL); 7017c478bd9Sstevel@tonic-gate return; 7027c478bd9Sstevel@tonic-gate } 7037c478bd9Sstevel@tonic-gate if (udp->udp_family != AF_INET || 7040f1702c5SYu Xiangning sa->sa_family != AF_INET) { 7057c478bd9Sstevel@tonic-gate udp_err_ack(q, mp, TSYSERR, EAFNOSUPPORT); 7067c478bd9Sstevel@tonic-gate return; 7077c478bd9Sstevel@tonic-gate } 7087c478bd9Sstevel@tonic-gate break; 7097c478bd9Sstevel@tonic-gate 7107c478bd9Sstevel@tonic-gate case sizeof (sin6_t): /* complete IPv6 address */ 7110f1702c5SYu Xiangning sa = (struct sockaddr *)mi_offset_param(mp, tbr->ADDR_offset, 7127c478bd9Sstevel@tonic-gate sizeof (sin6_t)); 7130f1702c5SYu Xiangning if (sa == NULL || !OK_32PTR((char *)sa)) { 7147c478bd9Sstevel@tonic-gate udp_err_ack(q, mp, TSYSERR, EINVAL); 7157c478bd9Sstevel@tonic-gate return; 7167c478bd9Sstevel@tonic-gate } 7177c478bd9Sstevel@tonic-gate if (udp->udp_family != AF_INET6 || 7180f1702c5SYu Xiangning sa->sa_family != AF_INET6) { 7197c478bd9Sstevel@tonic-gate udp_err_ack(q, mp, TSYSERR, EAFNOSUPPORT); 7207c478bd9Sstevel@tonic-gate return; 7217c478bd9Sstevel@tonic-gate } 7227c478bd9Sstevel@tonic-gate break; 7237c478bd9Sstevel@tonic-gate 7247c478bd9Sstevel@tonic-gate default: /* Invalid request */ 7257c478bd9Sstevel@tonic-gate (void) mi_strlog(q, 1, SL_ERROR|SL_TRACE, 7267c478bd9Sstevel@tonic-gate "udp_bind: bad ADDR_length length %u", tbr->ADDR_length); 7277c478bd9Sstevel@tonic-gate udp_err_ack(q, mp, TBADADDR, 0); 7287c478bd9Sstevel@tonic-gate return; 7297c478bd9Sstevel@tonic-gate } 7307c478bd9Sstevel@tonic-gate 7317c478bd9Sstevel@tonic-gate 7320f1702c5SYu Xiangning cred_t *cr = DB_CREDDEF(mp, connp->conn_cred); 7330f1702c5SYu Xiangning error = udp_do_bind(connp, sa, tbr->ADDR_length, cr, 7340f1702c5SYu Xiangning tbr->PRIM_type != O_T_BIND_REQ); 7357c478bd9Sstevel@tonic-gate 7360f1702c5SYu Xiangning if (error != 0) { 7370f1702c5SYu Xiangning if (error > 0) { 7380f1702c5SYu Xiangning udp_err_ack(q, mp, TSYSERR, error); 7397c478bd9Sstevel@tonic-gate } else { 7400f1702c5SYu Xiangning udp_err_ack(q, mp, -error, 0); 7417c478bd9Sstevel@tonic-gate } 7427c478bd9Sstevel@tonic-gate } else { 7430f1702c5SYu Xiangning tbr->PRIM_type = T_BIND_ACK; 7440f1702c5SYu Xiangning qreply(q, mp); 7450f1702c5SYu Xiangning } 7460f1702c5SYu Xiangning } 7477c478bd9Sstevel@tonic-gate 7480f1702c5SYu Xiangning /* 7490f1702c5SYu Xiangning * This routine handles each T_CONN_REQ message passed to udp. It 7500f1702c5SYu Xiangning * associates a default destination address with the stream. 7510f1702c5SYu Xiangning * 7520f1702c5SYu Xiangning * This routine sends down a T_BIND_REQ to IP with the following mblks: 7530f1702c5SYu Xiangning * T_BIND_REQ - specifying local and remote address/port 7540f1702c5SYu Xiangning * IRE_DB_REQ_TYPE - to get an IRE back containing ire_type and src 7550f1702c5SYu Xiangning * T_OK_ACK - for the T_CONN_REQ 7560f1702c5SYu Xiangning * T_CONN_CON - to keep the TPI user happy 7570f1702c5SYu Xiangning * 7580f1702c5SYu Xiangning * The connect completes in udp_do_connect. 7590f1702c5SYu Xiangning * When a T_BIND_ACK is received information is extracted from the IRE 7600f1702c5SYu Xiangning * and the two appended messages are sent to the TPI user. 7610f1702c5SYu Xiangning * Should udp_bind_result receive T_ERROR_ACK for the T_BIND_REQ it will 7620f1702c5SYu Xiangning * convert it to an error ack for the appropriate primitive. 7630f1702c5SYu Xiangning */ 7640f1702c5SYu Xiangning static void 7650f1702c5SYu Xiangning udp_tpi_connect(queue_t *q, mblk_t *mp) 7660f1702c5SYu Xiangning { 7670f1702c5SYu Xiangning mblk_t *mp1; 7680f1702c5SYu Xiangning udp_t *udp; 7690f1702c5SYu Xiangning conn_t *connp = Q_TO_CONN(q); 7700f1702c5SYu Xiangning int error; 7710f1702c5SYu Xiangning socklen_t len; 7720f1702c5SYu Xiangning struct sockaddr *sa; 7730f1702c5SYu Xiangning struct T_conn_req *tcr; 7747c478bd9Sstevel@tonic-gate 7750f1702c5SYu Xiangning udp = connp->conn_udp; 7760f1702c5SYu Xiangning tcr = (struct T_conn_req *)mp->b_rptr; 7777c478bd9Sstevel@tonic-gate 7780f1702c5SYu Xiangning /* A bit of sanity checking */ 7790f1702c5SYu Xiangning if ((mp->b_wptr - mp->b_rptr) < sizeof (struct T_conn_req)) { 7800f1702c5SYu Xiangning udp_err_ack(q, mp, TPROTO, 0); 7810f1702c5SYu Xiangning return; 7827c478bd9Sstevel@tonic-gate } 7837c478bd9Sstevel@tonic-gate 7840f1702c5SYu Xiangning if (tcr->OPT_length != 0) { 7850f1702c5SYu Xiangning udp_err_ack(q, mp, TBADOPT, 0); 78645916cd2Sjpk return; 78745916cd2Sjpk } 78845916cd2Sjpk 789fc80c0dfSnordmark /* 7900f1702c5SYu Xiangning * Determine packet type based on type of address passed in 7910f1702c5SYu Xiangning * the request should contain an IPv4 or IPv6 address. 7920f1702c5SYu Xiangning * Make sure that address family matches the type of 7930f1702c5SYu Xiangning * family of the the address passed down 794fc80c0dfSnordmark */ 7950f1702c5SYu Xiangning len = tcr->DEST_length; 7960f1702c5SYu Xiangning switch (tcr->DEST_length) { 7970f1702c5SYu Xiangning default: 7980f1702c5SYu Xiangning udp_err_ack(q, mp, TBADADDR, 0); 7990f1702c5SYu Xiangning return; 8000f1702c5SYu Xiangning 8010f1702c5SYu Xiangning case sizeof (sin_t): 8020f1702c5SYu Xiangning sa = (struct sockaddr *)mi_offset_param(mp, tcr->DEST_offset, 8030f1702c5SYu Xiangning sizeof (sin_t)); 8040f1702c5SYu Xiangning break; 8050f1702c5SYu Xiangning 8060f1702c5SYu Xiangning case sizeof (sin6_t): 8070f1702c5SYu Xiangning sa = (struct sockaddr *)mi_offset_param(mp, tcr->DEST_offset, 8080f1702c5SYu Xiangning sizeof (sin6_t)); 8090f1702c5SYu Xiangning break; 8100f1702c5SYu Xiangning } 8110f1702c5SYu Xiangning 8120f1702c5SYu Xiangning error = proto_verify_ip_addr(udp->udp_family, sa, len); 8130f1702c5SYu Xiangning if (error != 0) { 8140f1702c5SYu Xiangning udp_err_ack(q, mp, TSYSERR, error); 815fc80c0dfSnordmark return; 816fc80c0dfSnordmark } 8170f1702c5SYu Xiangning 8187c478bd9Sstevel@tonic-gate /* 8190f1702c5SYu Xiangning * We have to send a connection confirmation to 8200f1702c5SYu Xiangning * keep TLI happy. 8217c478bd9Sstevel@tonic-gate */ 8227c478bd9Sstevel@tonic-gate if (udp->udp_family == AF_INET) { 8230f1702c5SYu Xiangning mp1 = mi_tpi_conn_con(NULL, (char *)sa, 8240f1702c5SYu Xiangning sizeof (sin_t), NULL, 0); 8257c478bd9Sstevel@tonic-gate } else { 8260f1702c5SYu Xiangning mp1 = mi_tpi_conn_con(NULL, (char *)sa, 8270f1702c5SYu Xiangning sizeof (sin6_t), NULL, 0); 8280f1702c5SYu Xiangning } 8290f1702c5SYu Xiangning if (mp1 == NULL) { 8300f1702c5SYu Xiangning udp_err_ack(q, mp, TSYSERR, ENOMEM); 8310f1702c5SYu Xiangning return; 8327c478bd9Sstevel@tonic-gate } 8337c478bd9Sstevel@tonic-gate 8347c478bd9Sstevel@tonic-gate /* 8350f1702c5SYu Xiangning * ok_ack for T_CONN_REQ 8367c478bd9Sstevel@tonic-gate */ 8370f1702c5SYu Xiangning mp = mi_tpi_ok_ack_alloc(mp); 8380f1702c5SYu Xiangning if (mp == NULL) { 8390f1702c5SYu Xiangning /* Unable to reuse the T_CONN_REQ for the ack. */ 8400f1702c5SYu Xiangning freemsg(mp1); 8410f1702c5SYu Xiangning udp_err_ack_prim(q, mp1, T_CONN_REQ, TSYSERR, ENOMEM); 8420f1702c5SYu Xiangning return; 8430f1702c5SYu Xiangning } 8447c478bd9Sstevel@tonic-gate 8450f1702c5SYu Xiangning error = udp_do_connect(connp, sa, len); 8460f1702c5SYu Xiangning if (error != 0) { 8470f1702c5SYu Xiangning freeb(mp1); 8480f1702c5SYu Xiangning if (error < 0) 8490f1702c5SYu Xiangning udp_err_ack(q, mp, -error, 0); 8500f1702c5SYu Xiangning else 8510f1702c5SYu Xiangning udp_err_ack(q, mp, TSYSERR, error); 8520f1702c5SYu Xiangning } else { 8530f1702c5SYu Xiangning putnext(connp->conn_rq, mp); 8540f1702c5SYu Xiangning putnext(connp->conn_rq, mp1); 8550f1702c5SYu Xiangning } 8560f1702c5SYu Xiangning } 8570f1702c5SYu Xiangning 8580f1702c5SYu Xiangning static int 8590f1702c5SYu Xiangning udp_tpi_close(queue_t *q, int flags) 8600f1702c5SYu Xiangning { 8610f1702c5SYu Xiangning conn_t *connp; 8620f1702c5SYu Xiangning 8630f1702c5SYu Xiangning if (flags & SO_FALLBACK) { 864f4b3ec61Sdh /* 8650f1702c5SYu Xiangning * stream is being closed while in fallback 8660f1702c5SYu Xiangning * simply free the resources that were allocated 867f4b3ec61Sdh */ 8680f1702c5SYu Xiangning inet_minor_free(WR(q)->q_ptr, (dev_t)(RD(q)->q_ptr)); 8690f1702c5SYu Xiangning qprocsoff(q); 8700f1702c5SYu Xiangning goto done; 8717c478bd9Sstevel@tonic-gate } 8727c478bd9Sstevel@tonic-gate 8730f1702c5SYu Xiangning connp = Q_TO_CONN(q); 8740f1702c5SYu Xiangning udp_do_close(connp); 8750f1702c5SYu Xiangning done: 8760f1702c5SYu Xiangning q->q_ptr = WR(q)->q_ptr = NULL; 8770f1702c5SYu Xiangning return (0); 8780f1702c5SYu Xiangning } 87945916cd2Sjpk 8800f1702c5SYu Xiangning /* 8810f1702c5SYu Xiangning * Called in the close path to quiesce the conn 8820f1702c5SYu Xiangning */ 8830f1702c5SYu Xiangning void 8840f1702c5SYu Xiangning udp_quiesce_conn(conn_t *connp) 8850f1702c5SYu Xiangning { 8860f1702c5SYu Xiangning udp_t *udp = connp->conn_udp; 8877c478bd9Sstevel@tonic-gate 8880f1702c5SYu Xiangning if (cl_inet_unbind != NULL && udp->udp_state == TS_IDLE) { 8897c478bd9Sstevel@tonic-gate /* 8900f1702c5SYu Xiangning * Running in cluster mode - register unbind information 8917c478bd9Sstevel@tonic-gate */ 8920f1702c5SYu Xiangning if (udp->udp_ipversion == IPV4_VERSION) { 8938e4b770fSLu Huafeng (*cl_inet_unbind)( 8948e4b770fSLu Huafeng connp->conn_netstack->netstack_stackid, 8958e4b770fSLu Huafeng IPPROTO_UDP, AF_INET, 8960f1702c5SYu Xiangning (uint8_t *)(&(V4_PART_OF_V6(udp->udp_v6src))), 8978e4b770fSLu Huafeng (in_port_t)udp->udp_port, NULL); 8980f1702c5SYu Xiangning } else { 8998e4b770fSLu Huafeng (*cl_inet_unbind)( 9008e4b770fSLu Huafeng connp->conn_netstack->netstack_stackid, 9018e4b770fSLu Huafeng IPPROTO_UDP, AF_INET6, 9020f1702c5SYu Xiangning (uint8_t *)(&(udp->udp_v6src)), 9038e4b770fSLu Huafeng (in_port_t)udp->udp_port, NULL); 9040f1702c5SYu Xiangning } 9050f1702c5SYu Xiangning } 90645916cd2Sjpk 9070f1702c5SYu Xiangning udp_bind_hash_remove(udp, B_FALSE); 9087c478bd9Sstevel@tonic-gate 9090f1702c5SYu Xiangning } 9107c478bd9Sstevel@tonic-gate 9110f1702c5SYu Xiangning void 9120f1702c5SYu Xiangning udp_close_free(conn_t *connp) 9130f1702c5SYu Xiangning { 9140f1702c5SYu Xiangning udp_t *udp = connp->conn_udp; 9157c478bd9Sstevel@tonic-gate 9160f1702c5SYu Xiangning /* If there are any options associated with the stream, free them. */ 9170f1702c5SYu Xiangning if (udp->udp_ip_snd_options != NULL) { 9180f1702c5SYu Xiangning mi_free((char *)udp->udp_ip_snd_options); 9190f1702c5SYu Xiangning udp->udp_ip_snd_options = NULL; 9200f1702c5SYu Xiangning udp->udp_ip_snd_options_len = 0; 9210f1702c5SYu Xiangning } 9227c478bd9Sstevel@tonic-gate 9230f1702c5SYu Xiangning if (udp->udp_ip_rcv_options != NULL) { 9240f1702c5SYu Xiangning mi_free((char *)udp->udp_ip_rcv_options); 9250f1702c5SYu Xiangning udp->udp_ip_rcv_options = NULL; 9260f1702c5SYu Xiangning udp->udp_ip_rcv_options_len = 0; 9270f1702c5SYu Xiangning } 9287c478bd9Sstevel@tonic-gate 9290f1702c5SYu Xiangning /* Free memory associated with sticky options */ 9300f1702c5SYu Xiangning if (udp->udp_sticky_hdrs_len != 0) { 9310f1702c5SYu Xiangning kmem_free(udp->udp_sticky_hdrs, 9320f1702c5SYu Xiangning udp->udp_sticky_hdrs_len); 9330f1702c5SYu Xiangning udp->udp_sticky_hdrs = NULL; 9340f1702c5SYu Xiangning udp->udp_sticky_hdrs_len = 0; 9357c478bd9Sstevel@tonic-gate } 9367c478bd9Sstevel@tonic-gate 9370f1702c5SYu Xiangning ip6_pkt_free(&udp->udp_sticky_ipp); 9380f1702c5SYu Xiangning 9397c478bd9Sstevel@tonic-gate /* 9400f1702c5SYu Xiangning * Clear any fields which the kmem_cache constructor clears. 9410f1702c5SYu Xiangning * Only udp_connp needs to be preserved. 9420f1702c5SYu Xiangning * TBD: We should make this more efficient to avoid clearing 9430f1702c5SYu Xiangning * everything. 9447c478bd9Sstevel@tonic-gate */ 9450f1702c5SYu Xiangning ASSERT(udp->udp_connp == connp); 9460f1702c5SYu Xiangning bzero(udp, sizeof (udp_t)); 9470f1702c5SYu Xiangning udp->udp_connp = connp; 9480f1702c5SYu Xiangning } 9497c478bd9Sstevel@tonic-gate 9500f1702c5SYu Xiangning static int 9510f1702c5SYu Xiangning udp_do_disconnect(conn_t *connp) 9520f1702c5SYu Xiangning { 9530f1702c5SYu Xiangning udp_t *udp; 9540f1702c5SYu Xiangning mblk_t *ire_mp; 9550f1702c5SYu Xiangning udp_fanout_t *udpf; 9560f1702c5SYu Xiangning udp_stack_t *us; 9570f1702c5SYu Xiangning int error; 9587c478bd9Sstevel@tonic-gate 9590f1702c5SYu Xiangning udp = connp->conn_udp; 9600f1702c5SYu Xiangning us = udp->udp_us; 9610f1702c5SYu Xiangning rw_enter(&udp->udp_rwlock, RW_WRITER); 9620f1702c5SYu Xiangning if (udp->udp_state != TS_DATA_XFER || udp->udp_pending_op != -1) { 9630f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 9640f1702c5SYu Xiangning return (-TOUTSTATE); 9650f1702c5SYu Xiangning } 9660f1702c5SYu Xiangning udp->udp_pending_op = T_DISCON_REQ; 9670f1702c5SYu Xiangning udpf = &us->us_bind_fanout[UDP_BIND_HASH(udp->udp_port, 9680f1702c5SYu Xiangning us->us_bind_fanout_size)]; 9690f1702c5SYu Xiangning mutex_enter(&udpf->uf_lock); 9700f1702c5SYu Xiangning udp->udp_v6src = udp->udp_bound_v6src; 9710f1702c5SYu Xiangning udp->udp_state = TS_IDLE; 9720f1702c5SYu Xiangning mutex_exit(&udpf->uf_lock); 9730f1702c5SYu Xiangning 9740f1702c5SYu Xiangning if (udp->udp_family == AF_INET6) { 9757c478bd9Sstevel@tonic-gate /* Rebuild the header template */ 976fc80c0dfSnordmark error = udp_build_hdrs(udp); 9777c478bd9Sstevel@tonic-gate if (error != 0) { 978fc80c0dfSnordmark udp->udp_pending_op = -1; 979fc80c0dfSnordmark rw_exit(&udp->udp_rwlock); 9800f1702c5SYu Xiangning return (error); 9817c478bd9Sstevel@tonic-gate } 9827c478bd9Sstevel@tonic-gate } 9830f1702c5SYu Xiangning 9840f1702c5SYu Xiangning ire_mp = allocb(sizeof (ire_t), BPRI_HI); 9850f1702c5SYu Xiangning if (ire_mp == NULL) { 9860f1702c5SYu Xiangning mutex_enter(&udpf->uf_lock); 9870f1702c5SYu Xiangning udp->udp_pending_op = -1; 9880f1702c5SYu Xiangning mutex_exit(&udpf->uf_lock); 9890f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 9900f1702c5SYu Xiangning return (ENOMEM); 9910f1702c5SYu Xiangning } 9920f1702c5SYu Xiangning 993fc80c0dfSnordmark rw_exit(&udp->udp_rwlock); 9947c478bd9Sstevel@tonic-gate 9950f1702c5SYu Xiangning if (udp->udp_family == AF_INET6) { 9960f1702c5SYu Xiangning error = ip_proto_bind_laddr_v6(connp, &ire_mp, IPPROTO_UDP, 9970f1702c5SYu Xiangning &udp->udp_bound_v6src, udp->udp_port, B_TRUE); 9980f1702c5SYu Xiangning } else { 9990f1702c5SYu Xiangning error = ip_proto_bind_laddr_v4(connp, &ire_mp, IPPROTO_UDP, 10000f1702c5SYu Xiangning V4_PART_OF_V6(udp->udp_bound_v6src), udp->udp_port, B_TRUE); 10010f1702c5SYu Xiangning } 10020f1702c5SYu Xiangning 10030f1702c5SYu Xiangning return (udp_post_ip_bind_connect(udp, ire_mp, error)); 10040f1702c5SYu Xiangning } 10050f1702c5SYu Xiangning 10060f1702c5SYu Xiangning 10070f1702c5SYu Xiangning static void 10080f1702c5SYu Xiangning udp_tpi_disconnect(queue_t *q, mblk_t *mp) 10090f1702c5SYu Xiangning { 10100f1702c5SYu Xiangning conn_t *connp = Q_TO_CONN(q); 10110f1702c5SYu Xiangning int error; 10127c478bd9Sstevel@tonic-gate 10130f1702c5SYu Xiangning /* 10140f1702c5SYu Xiangning * Allocate the largest primitive we need to send back 10150f1702c5SYu Xiangning * T_error_ack is > than T_ok_ack 10160f1702c5SYu Xiangning */ 10170f1702c5SYu Xiangning mp = reallocb(mp, sizeof (struct T_error_ack), 1); 10180f1702c5SYu Xiangning if (mp == NULL) { 10190f1702c5SYu Xiangning /* Unable to reuse the T_DISCON_REQ for the ack. */ 10200f1702c5SYu Xiangning udp_err_ack_prim(q, mp, T_DISCON_REQ, TSYSERR, ENOMEM); 10210f1702c5SYu Xiangning return; 10227c478bd9Sstevel@tonic-gate } 10237c478bd9Sstevel@tonic-gate 10240f1702c5SYu Xiangning error = udp_do_disconnect(connp); 102545916cd2Sjpk 10260f1702c5SYu Xiangning if (error != 0) { 10270f1702c5SYu Xiangning if (error < 0) { 10280f1702c5SYu Xiangning udp_err_ack(q, mp, -error, 0); 10290f1702c5SYu Xiangning } else { 10300f1702c5SYu Xiangning udp_err_ack(q, mp, TSYSERR, error); 103145916cd2Sjpk } 10320f1702c5SYu Xiangning } else { 10330f1702c5SYu Xiangning mp = mi_tpi_ok_ack_alloc(mp); 10340f1702c5SYu Xiangning ASSERT(mp != NULL); 10350f1702c5SYu Xiangning qreply(q, mp); 10360f1702c5SYu Xiangning } 10370f1702c5SYu Xiangning } 103845916cd2Sjpk 10390f1702c5SYu Xiangning int 10400f1702c5SYu Xiangning udp_disconnect(conn_t *connp) 10410f1702c5SYu Xiangning { 10420f1702c5SYu Xiangning int error; 10430f1702c5SYu Xiangning udp_t *udp = connp->conn_udp; 104445916cd2Sjpk 10450f1702c5SYu Xiangning udp->udp_dgram_errind = B_FALSE; 104645916cd2Sjpk 10470f1702c5SYu Xiangning error = udp_do_disconnect(connp); 104845916cd2Sjpk 10490f1702c5SYu Xiangning if (error < 0) 10500f1702c5SYu Xiangning error = proto_tlitosyserr(-error); 1051ff550d0eSmasputra 10520f1702c5SYu Xiangning return (error); 1053ff550d0eSmasputra } 1054ff550d0eSmasputra 10550f1702c5SYu Xiangning /* This routine creates a T_ERROR_ACK message and passes it upstream. */ 10560f1702c5SYu Xiangning static void 10570f1702c5SYu Xiangning udp_err_ack(queue_t *q, mblk_t *mp, t_scalar_t t_error, int sys_error) 1058ff550d0eSmasputra { 10590f1702c5SYu Xiangning if ((mp = mi_tpi_err_ack_alloc(mp, t_error, sys_error)) != NULL) 10600f1702c5SYu Xiangning qreply(q, mp); 10617c478bd9Sstevel@tonic-gate } 10627c478bd9Sstevel@tonic-gate 10630f1702c5SYu Xiangning /* Shorthand to generate and send TPI error acks to our client */ 10647c478bd9Sstevel@tonic-gate static void 10650f1702c5SYu Xiangning udp_err_ack_prim(queue_t *q, mblk_t *mp, int primitive, t_scalar_t t_error, 10660f1702c5SYu Xiangning int sys_error) 10677c478bd9Sstevel@tonic-gate { 10680f1702c5SYu Xiangning struct T_error_ack *teackp; 10697c478bd9Sstevel@tonic-gate 10700f1702c5SYu Xiangning if ((mp = tpi_ack_alloc(mp, sizeof (struct T_error_ack), 10710f1702c5SYu Xiangning M_PCPROTO, T_ERROR_ACK)) != NULL) { 10720f1702c5SYu Xiangning teackp = (struct T_error_ack *)mp->b_rptr; 10730f1702c5SYu Xiangning teackp->ERROR_prim = primitive; 10740f1702c5SYu Xiangning teackp->TLI_error = t_error; 10750f1702c5SYu Xiangning teackp->UNIX_error = sys_error; 10760f1702c5SYu Xiangning qreply(q, mp); 10777c478bd9Sstevel@tonic-gate } 10780f1702c5SYu Xiangning } 10797c478bd9Sstevel@tonic-gate 10800f1702c5SYu Xiangning /*ARGSUSED*/ 10810f1702c5SYu Xiangning static int 10820f1702c5SYu Xiangning udp_extra_priv_ports_get(queue_t *q, mblk_t *mp, caddr_t cp, cred_t *cr) 10830f1702c5SYu Xiangning { 10840f1702c5SYu Xiangning int i; 10850f1702c5SYu Xiangning udp_t *udp = Q_TO_UDP(q); 10860f1702c5SYu Xiangning udp_stack_t *us = udp->udp_us; 10870f1702c5SYu Xiangning 10880f1702c5SYu Xiangning for (i = 0; i < us->us_num_epriv_ports; i++) { 10890f1702c5SYu Xiangning if (us->us_epriv_ports[i] != 0) 10900f1702c5SYu Xiangning (void) mi_mpprintf(mp, "%d ", us->us_epriv_ports[i]); 10917c478bd9Sstevel@tonic-gate } 10920f1702c5SYu Xiangning return (0); 10930f1702c5SYu Xiangning } 10940f1702c5SYu Xiangning 10950f1702c5SYu Xiangning /* ARGSUSED */ 10960f1702c5SYu Xiangning static int 10970f1702c5SYu Xiangning udp_extra_priv_ports_add(queue_t *q, mblk_t *mp, char *value, caddr_t cp, 10980f1702c5SYu Xiangning cred_t *cr) 10990f1702c5SYu Xiangning { 11000f1702c5SYu Xiangning long new_value; 11010f1702c5SYu Xiangning int i; 11020f1702c5SYu Xiangning udp_t *udp = Q_TO_UDP(q); 11030f1702c5SYu Xiangning udp_stack_t *us = udp->udp_us; 11047c478bd9Sstevel@tonic-gate 11057c478bd9Sstevel@tonic-gate /* 11060f1702c5SYu Xiangning * Fail the request if the new value does not lie within the 11070f1702c5SYu Xiangning * port number limits. 11087c478bd9Sstevel@tonic-gate */ 11090f1702c5SYu Xiangning if (ddi_strtol(value, NULL, 10, &new_value) != 0 || 11100f1702c5SYu Xiangning new_value <= 0 || new_value >= 65536) { 11110f1702c5SYu Xiangning return (EINVAL); 11120f1702c5SYu Xiangning } 11137c478bd9Sstevel@tonic-gate 11140f1702c5SYu Xiangning /* Check if the value is already in the list */ 11150f1702c5SYu Xiangning for (i = 0; i < us->us_num_epriv_ports; i++) { 11160f1702c5SYu Xiangning if (new_value == us->us_epriv_ports[i]) { 11170f1702c5SYu Xiangning return (EEXIST); 11187c478bd9Sstevel@tonic-gate } 11197c478bd9Sstevel@tonic-gate } 11200f1702c5SYu Xiangning /* Find an empty slot */ 11210f1702c5SYu Xiangning for (i = 0; i < us->us_num_epriv_ports; i++) { 11220f1702c5SYu Xiangning if (us->us_epriv_ports[i] == 0) 11230f1702c5SYu Xiangning break; 11240f1702c5SYu Xiangning } 11250f1702c5SYu Xiangning if (i == us->us_num_epriv_ports) { 11260f1702c5SYu Xiangning return (EOVERFLOW); 11277c478bd9Sstevel@tonic-gate } 11287c478bd9Sstevel@tonic-gate 11290f1702c5SYu Xiangning /* Set the new value */ 11300f1702c5SYu Xiangning us->us_epriv_ports[i] = (in_port_t)new_value; 11310f1702c5SYu Xiangning return (0); 11320f1702c5SYu Xiangning } 11330f1702c5SYu Xiangning 11340f1702c5SYu Xiangning /* ARGSUSED */ 11350f1702c5SYu Xiangning static int 11360f1702c5SYu Xiangning udp_extra_priv_ports_del(queue_t *q, mblk_t *mp, char *value, caddr_t cp, 11370f1702c5SYu Xiangning cred_t *cr) 11380f1702c5SYu Xiangning { 11390f1702c5SYu Xiangning long new_value; 11400f1702c5SYu Xiangning int i; 11410f1702c5SYu Xiangning udp_t *udp = Q_TO_UDP(q); 11420f1702c5SYu Xiangning udp_stack_t *us = udp->udp_us; 1143fc80c0dfSnordmark 1144fc80c0dfSnordmark /* 11450f1702c5SYu Xiangning * Fail the request if the new value does not lie within the 11460f1702c5SYu Xiangning * port number limits. 11470f1702c5SYu Xiangning */ 11480f1702c5SYu Xiangning if (ddi_strtol(value, NULL, 10, &new_value) != 0 || 11490f1702c5SYu Xiangning new_value <= 0 || new_value >= 65536) { 11500f1702c5SYu Xiangning return (EINVAL); 1151fc80c0dfSnordmark } 1152fc80c0dfSnordmark 11530f1702c5SYu Xiangning /* Check that the value is already in the list */ 11540f1702c5SYu Xiangning for (i = 0; i < us->us_num_epriv_ports; i++) { 11550f1702c5SYu Xiangning if (us->us_epriv_ports[i] == new_value) 11560f1702c5SYu Xiangning break; 11570f1702c5SYu Xiangning } 11580f1702c5SYu Xiangning if (i == us->us_num_epriv_ports) { 11590f1702c5SYu Xiangning return (ESRCH); 1160fc80c0dfSnordmark } 1161fc80c0dfSnordmark 11620f1702c5SYu Xiangning /* Clear the value */ 11630f1702c5SYu Xiangning us->us_epriv_ports[i] = 0; 11640f1702c5SYu Xiangning return (0); 11650f1702c5SYu Xiangning } 1166fc80c0dfSnordmark 11670f1702c5SYu Xiangning /* At minimum we need 4 bytes of UDP header */ 11680f1702c5SYu Xiangning #define ICMP_MIN_UDP_HDR 4 1169fc80c0dfSnordmark 11700f1702c5SYu Xiangning /* 11710f1702c5SYu Xiangning * udp_icmp_error is called by udp_input to process ICMP msgs. passed up by IP. 11720f1702c5SYu Xiangning * Generates the appropriate T_UDERROR_IND for permanent (non-transient) errors. 11730f1702c5SYu Xiangning * Assumes that IP has pulled up everything up to and including the ICMP header. 11740f1702c5SYu Xiangning */ 11750f1702c5SYu Xiangning static void 11760f1702c5SYu Xiangning udp_icmp_error(conn_t *connp, mblk_t *mp) 11770f1702c5SYu Xiangning { 11780f1702c5SYu Xiangning icmph_t *icmph; 11790f1702c5SYu Xiangning ipha_t *ipha; 11800f1702c5SYu Xiangning int iph_hdr_length; 11810f1702c5SYu Xiangning udpha_t *udpha; 11820f1702c5SYu Xiangning sin_t sin; 11830f1702c5SYu Xiangning sin6_t sin6; 11840f1702c5SYu Xiangning mblk_t *mp1; 11850f1702c5SYu Xiangning int error = 0; 11860f1702c5SYu Xiangning udp_t *udp = connp->conn_udp; 11877c478bd9Sstevel@tonic-gate 11880f1702c5SYu Xiangning mp1 = NULL; 11890f1702c5SYu Xiangning ipha = (ipha_t *)mp->b_rptr; 11907c478bd9Sstevel@tonic-gate 11910f1702c5SYu Xiangning ASSERT(OK_32PTR(mp->b_rptr)); 11920f1702c5SYu Xiangning 11930f1702c5SYu Xiangning if (IPH_HDR_VERSION(ipha) != IPV4_VERSION) { 11940f1702c5SYu Xiangning ASSERT(IPH_HDR_VERSION(ipha) == IPV6_VERSION); 11950f1702c5SYu Xiangning udp_icmp_error_ipv6(connp, mp); 11967c478bd9Sstevel@tonic-gate return; 11977c478bd9Sstevel@tonic-gate } 11980f1702c5SYu Xiangning ASSERT(IPH_HDR_VERSION(ipha) == IPV4_VERSION); 11997c478bd9Sstevel@tonic-gate 12000f1702c5SYu Xiangning /* Skip past the outer IP and ICMP headers */ 12010f1702c5SYu Xiangning iph_hdr_length = IPH_HDR_LENGTH(ipha); 12020f1702c5SYu Xiangning icmph = (icmph_t *)&mp->b_rptr[iph_hdr_length]; 12030f1702c5SYu Xiangning ipha = (ipha_t *)&icmph[1]; 12040f1702c5SYu Xiangning 12050f1702c5SYu Xiangning /* Skip past the inner IP and find the ULP header */ 12060f1702c5SYu Xiangning iph_hdr_length = IPH_HDR_LENGTH(ipha); 12070f1702c5SYu Xiangning udpha = (udpha_t *)((char *)ipha + iph_hdr_length); 12080f1702c5SYu Xiangning 12090f1702c5SYu Xiangning switch (icmph->icmph_type) { 12100f1702c5SYu Xiangning case ICMP_DEST_UNREACHABLE: 12110f1702c5SYu Xiangning switch (icmph->icmph_code) { 12120f1702c5SYu Xiangning case ICMP_FRAGMENTATION_NEEDED: 12130f1702c5SYu Xiangning /* 12140f1702c5SYu Xiangning * IP has already adjusted the path MTU. 12150f1702c5SYu Xiangning */ 12160f1702c5SYu Xiangning break; 12170f1702c5SYu Xiangning case ICMP_PORT_UNREACHABLE: 12180f1702c5SYu Xiangning case ICMP_PROTOCOL_UNREACHABLE: 12190f1702c5SYu Xiangning error = ECONNREFUSED; 12200f1702c5SYu Xiangning break; 12210f1702c5SYu Xiangning default: 12220f1702c5SYu Xiangning /* Transient errors */ 12230f1702c5SYu Xiangning break; 12240f1702c5SYu Xiangning } 12250f1702c5SYu Xiangning break; 12260f1702c5SYu Xiangning default: 12270f1702c5SYu Xiangning /* Transient errors */ 12280f1702c5SYu Xiangning break; 12290f1702c5SYu Xiangning } 12300f1702c5SYu Xiangning if (error == 0) { 12310f1702c5SYu Xiangning freemsg(mp); 12327c478bd9Sstevel@tonic-gate return; 12337c478bd9Sstevel@tonic-gate } 12347c478bd9Sstevel@tonic-gate 12357c478bd9Sstevel@tonic-gate /* 12360f1702c5SYu Xiangning * Deliver T_UDERROR_IND when the application has asked for it. 12370f1702c5SYu Xiangning * The socket layer enables this automatically when connected. 12387c478bd9Sstevel@tonic-gate */ 12390f1702c5SYu Xiangning if (!udp->udp_dgram_errind) { 12400f1702c5SYu Xiangning freemsg(mp); 1241fc80c0dfSnordmark return; 12427c478bd9Sstevel@tonic-gate } 12437c478bd9Sstevel@tonic-gate 12447c478bd9Sstevel@tonic-gate 12450f1702c5SYu Xiangning switch (udp->udp_family) { 12460f1702c5SYu Xiangning case AF_INET: 12470f1702c5SYu Xiangning sin = sin_null; 12480f1702c5SYu Xiangning sin.sin_family = AF_INET; 12490f1702c5SYu Xiangning sin.sin_addr.s_addr = ipha->ipha_dst; 12500f1702c5SYu Xiangning sin.sin_port = udpha->uha_dst_port; 12510f1702c5SYu Xiangning if (IPCL_IS_NONSTR(connp)) { 12520f1702c5SYu Xiangning rw_enter(&udp->udp_rwlock, RW_WRITER); 12530f1702c5SYu Xiangning if (udp->udp_state == TS_DATA_XFER) { 12540f1702c5SYu Xiangning if (sin.sin_port == udp->udp_dstport && 12550f1702c5SYu Xiangning sin.sin_addr.s_addr == 12560f1702c5SYu Xiangning V4_PART_OF_V6(udp->udp_v6dst)) { 12570f1702c5SYu Xiangning 12580f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 12590f1702c5SYu Xiangning (*connp->conn_upcalls->su_set_error) 12600f1702c5SYu Xiangning (connp->conn_upper_handle, error); 12610f1702c5SYu Xiangning goto done; 12620f1702c5SYu Xiangning } 12630f1702c5SYu Xiangning } else { 12640f1702c5SYu Xiangning udp->udp_delayed_error = error; 12650f1702c5SYu Xiangning *((sin_t *)&udp->udp_delayed_addr) = sin; 12660f1702c5SYu Xiangning } 12670f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 12680f1702c5SYu Xiangning } else { 12690f1702c5SYu Xiangning mp1 = mi_tpi_uderror_ind((char *)&sin, sizeof (sin_t), 12700f1702c5SYu Xiangning NULL, 0, error); 12710f1702c5SYu Xiangning } 12720f1702c5SYu Xiangning break; 12730f1702c5SYu Xiangning case AF_INET6: 12740f1702c5SYu Xiangning sin6 = sin6_null; 12750f1702c5SYu Xiangning sin6.sin6_family = AF_INET6; 12760f1702c5SYu Xiangning IN6_IPADDR_TO_V4MAPPED(ipha->ipha_dst, &sin6.sin6_addr); 12770f1702c5SYu Xiangning sin6.sin6_port = udpha->uha_dst_port; 12780f1702c5SYu Xiangning if (IPCL_IS_NONSTR(connp)) { 12790f1702c5SYu Xiangning rw_enter(&udp->udp_rwlock, RW_WRITER); 12800f1702c5SYu Xiangning if (udp->udp_state == TS_DATA_XFER) { 12810f1702c5SYu Xiangning if (sin6.sin6_port == udp->udp_dstport && 12820f1702c5SYu Xiangning IN6_ARE_ADDR_EQUAL(&sin6.sin6_addr, 12830f1702c5SYu Xiangning &udp->udp_v6dst)) { 12840f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 12850f1702c5SYu Xiangning (*connp->conn_upcalls->su_set_error) 12860f1702c5SYu Xiangning (connp->conn_upper_handle, error); 12870f1702c5SYu Xiangning goto done; 12880f1702c5SYu Xiangning } 12890f1702c5SYu Xiangning } else { 12900f1702c5SYu Xiangning udp->udp_delayed_error = error; 12910f1702c5SYu Xiangning *((sin6_t *)&udp->udp_delayed_addr) = sin6; 12920f1702c5SYu Xiangning } 12930f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 12940f1702c5SYu Xiangning } else { 1295ff550d0eSmasputra 12960f1702c5SYu Xiangning mp1 = mi_tpi_uderror_ind((char *)&sin6, sizeof (sin6_t), 12970f1702c5SYu Xiangning NULL, 0, error); 12980f1702c5SYu Xiangning } 12990f1702c5SYu Xiangning break; 13000f1702c5SYu Xiangning } 1301ff550d0eSmasputra if (mp1 != NULL) 13020f1702c5SYu Xiangning putnext(connp->conn_rq, mp1); 13030f1702c5SYu Xiangning done: 13040f1702c5SYu Xiangning freemsg(mp); 13057c478bd9Sstevel@tonic-gate } 13067c478bd9Sstevel@tonic-gate 13070f1702c5SYu Xiangning /* 13080f1702c5SYu Xiangning * udp_icmp_error_ipv6 is called by udp_icmp_error to process ICMP for IPv6. 13090f1702c5SYu Xiangning * Generates the appropriate T_UDERROR_IND for permanent (non-transient) errors. 13100f1702c5SYu Xiangning * Assumes that IP has pulled up all the extension headers as well as the 13110f1702c5SYu Xiangning * ICMPv6 header. 13120f1702c5SYu Xiangning */ 13130f1702c5SYu Xiangning static void 13140f1702c5SYu Xiangning udp_icmp_error_ipv6(conn_t *connp, mblk_t *mp) 13157c478bd9Sstevel@tonic-gate { 13160f1702c5SYu Xiangning icmp6_t *icmp6; 13170f1702c5SYu Xiangning ip6_t *ip6h, *outer_ip6h; 13180f1702c5SYu Xiangning uint16_t iph_hdr_length; 13190f1702c5SYu Xiangning uint8_t *nexthdrp; 13200f1702c5SYu Xiangning udpha_t *udpha; 13210f1702c5SYu Xiangning sin6_t sin6; 13220f1702c5SYu Xiangning mblk_t *mp1; 13230f1702c5SYu Xiangning int error = 0; 13240f1702c5SYu Xiangning udp_t *udp = connp->conn_udp; 13250f1702c5SYu Xiangning udp_stack_t *us = udp->udp_us; 1326ff550d0eSmasputra 13270f1702c5SYu Xiangning outer_ip6h = (ip6_t *)mp->b_rptr; 13280f1702c5SYu Xiangning if (outer_ip6h->ip6_nxt != IPPROTO_ICMPV6) 13290f1702c5SYu Xiangning iph_hdr_length = ip_hdr_length_v6(mp, outer_ip6h); 13300f1702c5SYu Xiangning else 13310f1702c5SYu Xiangning iph_hdr_length = IPV6_HDR_LEN; 13320f1702c5SYu Xiangning icmp6 = (icmp6_t *)&mp->b_rptr[iph_hdr_length]; 13330f1702c5SYu Xiangning ip6h = (ip6_t *)&icmp6[1]; 13340f1702c5SYu Xiangning if (!ip_hdr_length_nexthdr_v6(mp, ip6h, &iph_hdr_length, &nexthdrp)) { 13350f1702c5SYu Xiangning freemsg(mp); 13360f1702c5SYu Xiangning return; 13370f1702c5SYu Xiangning } 13380f1702c5SYu Xiangning udpha = (udpha_t *)((char *)ip6h + iph_hdr_length); 13397c478bd9Sstevel@tonic-gate 13400f1702c5SYu Xiangning switch (icmp6->icmp6_type) { 13410f1702c5SYu Xiangning case ICMP6_DST_UNREACH: 13420f1702c5SYu Xiangning switch (icmp6->icmp6_code) { 13430f1702c5SYu Xiangning case ICMP6_DST_UNREACH_NOPORT: 13440f1702c5SYu Xiangning error = ECONNREFUSED; 13450f1702c5SYu Xiangning break; 13460f1702c5SYu Xiangning case ICMP6_DST_UNREACH_ADMIN: 13470f1702c5SYu Xiangning case ICMP6_DST_UNREACH_NOROUTE: 13480f1702c5SYu Xiangning case ICMP6_DST_UNREACH_BEYONDSCOPE: 13490f1702c5SYu Xiangning case ICMP6_DST_UNREACH_ADDR: 13500f1702c5SYu Xiangning /* Transient errors */ 13510f1702c5SYu Xiangning break; 13520f1702c5SYu Xiangning default: 13530f1702c5SYu Xiangning break; 13540f1702c5SYu Xiangning } 13550f1702c5SYu Xiangning break; 13560f1702c5SYu Xiangning case ICMP6_PACKET_TOO_BIG: { 13570f1702c5SYu Xiangning struct T_unitdata_ind *tudi; 13580f1702c5SYu Xiangning struct T_opthdr *toh; 13590f1702c5SYu Xiangning size_t udi_size; 13600f1702c5SYu Xiangning mblk_t *newmp; 13610f1702c5SYu Xiangning t_scalar_t opt_length = sizeof (struct T_opthdr) + 13620f1702c5SYu Xiangning sizeof (struct ip6_mtuinfo); 13630f1702c5SYu Xiangning sin6_t *sin6; 13640f1702c5SYu Xiangning struct ip6_mtuinfo *mtuinfo; 13657c478bd9Sstevel@tonic-gate 13660f1702c5SYu Xiangning /* 13670f1702c5SYu Xiangning * If the application has requested to receive path mtu 13680f1702c5SYu Xiangning * information, send up an empty message containing an 13690f1702c5SYu Xiangning * IPV6_PATHMTU ancillary data item. 13700f1702c5SYu Xiangning */ 13710f1702c5SYu Xiangning if (!udp->udp_ipv6_recvpathmtu) 13720f1702c5SYu Xiangning break; 1373ff550d0eSmasputra 13740f1702c5SYu Xiangning udi_size = sizeof (struct T_unitdata_ind) + sizeof (sin6_t) + 13750f1702c5SYu Xiangning opt_length; 13760f1702c5SYu Xiangning if ((newmp = allocb(udi_size, BPRI_MED)) == NULL) { 13770f1702c5SYu Xiangning BUMP_MIB(&us->us_udp_mib, udpInErrors); 13780f1702c5SYu Xiangning break; 13790f1702c5SYu Xiangning } 1380c62a497dSgeorges 13810f1702c5SYu Xiangning /* 13820f1702c5SYu Xiangning * newmp->b_cont is left to NULL on purpose. This is an 13830f1702c5SYu Xiangning * empty message containing only ancillary data. 13840f1702c5SYu Xiangning */ 13850f1702c5SYu Xiangning newmp->b_datap->db_type = M_PROTO; 13860f1702c5SYu Xiangning tudi = (struct T_unitdata_ind *)newmp->b_rptr; 13870f1702c5SYu Xiangning newmp->b_wptr = (uchar_t *)tudi + udi_size; 13880f1702c5SYu Xiangning tudi->PRIM_type = T_UNITDATA_IND; 13890f1702c5SYu Xiangning tudi->SRC_length = sizeof (sin6_t); 13900f1702c5SYu Xiangning tudi->SRC_offset = sizeof (struct T_unitdata_ind); 13910f1702c5SYu Xiangning tudi->OPT_offset = tudi->SRC_offset + sizeof (sin6_t); 13920f1702c5SYu Xiangning tudi->OPT_length = opt_length; 1393fc80c0dfSnordmark 13940f1702c5SYu Xiangning sin6 = (sin6_t *)&tudi[1]; 13950f1702c5SYu Xiangning bzero(sin6, sizeof (sin6_t)); 13960f1702c5SYu Xiangning sin6->sin6_family = AF_INET6; 13970f1702c5SYu Xiangning sin6->sin6_addr = udp->udp_v6dst; 1398ff550d0eSmasputra 13990f1702c5SYu Xiangning toh = (struct T_opthdr *)&sin6[1]; 14000f1702c5SYu Xiangning toh->level = IPPROTO_IPV6; 14010f1702c5SYu Xiangning toh->name = IPV6_PATHMTU; 14020f1702c5SYu Xiangning toh->len = opt_length; 14030f1702c5SYu Xiangning toh->status = 0; 1404ff550d0eSmasputra 14050f1702c5SYu Xiangning mtuinfo = (struct ip6_mtuinfo *)&toh[1]; 14060f1702c5SYu Xiangning bzero(mtuinfo, sizeof (struct ip6_mtuinfo)); 14070f1702c5SYu Xiangning mtuinfo->ip6m_addr.sin6_family = AF_INET6; 14080f1702c5SYu Xiangning mtuinfo->ip6m_addr.sin6_addr = ip6h->ip6_dst; 14090f1702c5SYu Xiangning mtuinfo->ip6m_mtu = icmp6->icmp6_mtu; 14107c478bd9Sstevel@tonic-gate /* 14110f1702c5SYu Xiangning * We've consumed everything we need from the original 14120f1702c5SYu Xiangning * message. Free it, then send our empty message. 14137c478bd9Sstevel@tonic-gate */ 14140f1702c5SYu Xiangning freemsg(mp); 14150f1702c5SYu Xiangning if (!IPCL_IS_NONSTR(connp)) { 14160f1702c5SYu Xiangning putnext(connp->conn_rq, newmp); 14177c478bd9Sstevel@tonic-gate } else { 14180f1702c5SYu Xiangning (*connp->conn_upcalls->su_recv) 14190f1702c5SYu Xiangning (connp->conn_upper_handle, newmp, 0, 0, &error, 14200f1702c5SYu Xiangning NULL); 14217c478bd9Sstevel@tonic-gate } 14220f1702c5SYu Xiangning return; 14237c478bd9Sstevel@tonic-gate } 14240f1702c5SYu Xiangning case ICMP6_TIME_EXCEEDED: 14250f1702c5SYu Xiangning /* Transient errors */ 14260f1702c5SYu Xiangning break; 14270f1702c5SYu Xiangning case ICMP6_PARAM_PROB: 14280f1702c5SYu Xiangning /* If this corresponds to an ICMP_PROTOCOL_UNREACHABLE */ 14290f1702c5SYu Xiangning if (icmp6->icmp6_code == ICMP6_PARAMPROB_NEXTHEADER && 14300f1702c5SYu Xiangning (uchar_t *)ip6h + icmp6->icmp6_pptr == 14310f1702c5SYu Xiangning (uchar_t *)nexthdrp) { 14320f1702c5SYu Xiangning error = ECONNREFUSED; 14330f1702c5SYu Xiangning break; 14340f1702c5SYu Xiangning } 14350f1702c5SYu Xiangning break; 1436ff550d0eSmasputra } 14370f1702c5SYu Xiangning if (error == 0) { 14380f1702c5SYu Xiangning freemsg(mp); 14390f1702c5SYu Xiangning return; 1440ff550d0eSmasputra } 14417c478bd9Sstevel@tonic-gate 14420f1702c5SYu Xiangning /* 14430f1702c5SYu Xiangning * Deliver T_UDERROR_IND when the application has asked for it. 14440f1702c5SYu Xiangning * The socket layer enables this automatically when connected. 14450f1702c5SYu Xiangning */ 14460f1702c5SYu Xiangning if (!udp->udp_dgram_errind) { 14470f1702c5SYu Xiangning freemsg(mp); 14480f1702c5SYu Xiangning return; 14497c478bd9Sstevel@tonic-gate } 1450ff550d0eSmasputra 14510f1702c5SYu Xiangning sin6 = sin6_null; 14520f1702c5SYu Xiangning sin6.sin6_family = AF_INET6; 14530f1702c5SYu Xiangning sin6.sin6_addr = ip6h->ip6_dst; 14540f1702c5SYu Xiangning sin6.sin6_port = udpha->uha_dst_port; 14550f1702c5SYu Xiangning sin6.sin6_flowinfo = ip6h->ip6_vcf & ~IPV6_VERS_AND_FLOW_MASK; 1456e845e33dSnordmark 14570f1702c5SYu Xiangning if (IPCL_IS_NONSTR(connp)) { 14580f1702c5SYu Xiangning rw_enter(&udp->udp_rwlock, RW_WRITER); 14590f1702c5SYu Xiangning if (udp->udp_state == TS_DATA_XFER) { 14600f1702c5SYu Xiangning if (sin6.sin6_port == udp->udp_dstport && 14610f1702c5SYu Xiangning IN6_ARE_ADDR_EQUAL(&sin6.sin6_addr, 14620f1702c5SYu Xiangning &udp->udp_v6dst)) { 14630f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 14640f1702c5SYu Xiangning (*connp->conn_upcalls->su_set_error) 14650f1702c5SYu Xiangning (connp->conn_upper_handle, error); 14660f1702c5SYu Xiangning goto done; 14670f1702c5SYu Xiangning } 14680f1702c5SYu Xiangning } else { 14690f1702c5SYu Xiangning udp->udp_delayed_error = error; 14700f1702c5SYu Xiangning *((sin6_t *)&udp->udp_delayed_addr) = sin6; 14710f1702c5SYu Xiangning } 14720f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 14730f1702c5SYu Xiangning } else { 14740f1702c5SYu Xiangning mp1 = mi_tpi_uderror_ind((char *)&sin6, sizeof (sin6_t), 14750f1702c5SYu Xiangning NULL, 0, error); 14760f1702c5SYu Xiangning if (mp1 != NULL) 14770f1702c5SYu Xiangning putnext(connp->conn_rq, mp1); 14780f1702c5SYu Xiangning } 14790f1702c5SYu Xiangning 14800f1702c5SYu Xiangning done: 14810f1702c5SYu Xiangning freemsg(mp); 14827c478bd9Sstevel@tonic-gate } 14837c478bd9Sstevel@tonic-gate 14847c478bd9Sstevel@tonic-gate /* 14850f1702c5SYu Xiangning * This routine responds to T_ADDR_REQ messages. It is called by udp_wput. 14860f1702c5SYu Xiangning * The local address is filled in if endpoint is bound. The remote address 14870f1702c5SYu Xiangning * is filled in if remote address has been precified ("connected endpoint") 14880f1702c5SYu Xiangning * (The concept of connected CLTS sockets is alien to published TPI 14890f1702c5SYu Xiangning * but we support it anyway). 14907c478bd9Sstevel@tonic-gate */ 14917c478bd9Sstevel@tonic-gate static void 14920f1702c5SYu Xiangning udp_addr_req(queue_t *q, mblk_t *mp) 14937c478bd9Sstevel@tonic-gate { 14940f1702c5SYu Xiangning sin_t *sin; 14950f1702c5SYu Xiangning sin6_t *sin6; 14960f1702c5SYu Xiangning mblk_t *ackmp; 14970f1702c5SYu Xiangning struct T_addr_ack *taa; 14980f1702c5SYu Xiangning udp_t *udp = Q_TO_UDP(q); 14997c478bd9Sstevel@tonic-gate 15000f1702c5SYu Xiangning /* Make it large enough for worst case */ 15010f1702c5SYu Xiangning ackmp = reallocb(mp, sizeof (struct T_addr_ack) + 15020f1702c5SYu Xiangning 2 * sizeof (sin6_t), 1); 15030f1702c5SYu Xiangning if (ackmp == NULL) { 15040f1702c5SYu Xiangning udp_err_ack(q, mp, TSYSERR, ENOMEM); 15057c478bd9Sstevel@tonic-gate return; 15067c478bd9Sstevel@tonic-gate } 15070f1702c5SYu Xiangning taa = (struct T_addr_ack *)ackmp->b_rptr; 15080f1702c5SYu Xiangning 15090f1702c5SYu Xiangning bzero(taa, sizeof (struct T_addr_ack)); 15100f1702c5SYu Xiangning ackmp->b_wptr = (uchar_t *)&taa[1]; 15117c478bd9Sstevel@tonic-gate 15120f1702c5SYu Xiangning taa->PRIM_type = T_ADDR_ACK; 15130f1702c5SYu Xiangning ackmp->b_datap->db_type = M_PCPROTO; 15140f1702c5SYu Xiangning rw_enter(&udp->udp_rwlock, RW_READER); 15157c478bd9Sstevel@tonic-gate /* 15160f1702c5SYu Xiangning * Note: Following code assumes 32 bit alignment of basic 15170f1702c5SYu Xiangning * data structures like sin_t and struct T_addr_ack. 15187c478bd9Sstevel@tonic-gate */ 15190f1702c5SYu Xiangning if (udp->udp_state != TS_UNBND) { 15200f1702c5SYu Xiangning /* 15210f1702c5SYu Xiangning * Fill in local address first 15220f1702c5SYu Xiangning */ 15230f1702c5SYu Xiangning taa->LOCADDR_offset = sizeof (*taa); 15240f1702c5SYu Xiangning if (udp->udp_family == AF_INET) { 15250f1702c5SYu Xiangning taa->LOCADDR_length = sizeof (sin_t); 15260f1702c5SYu Xiangning sin = (sin_t *)&taa[1]; 15270f1702c5SYu Xiangning /* Fill zeroes and then initialize non-zero fields */ 15280f1702c5SYu Xiangning *sin = sin_null; 15290f1702c5SYu Xiangning sin->sin_family = AF_INET; 15300f1702c5SYu Xiangning if (!IN6_IS_ADDR_V4MAPPED_ANY(&udp->udp_v6src) && 15310f1702c5SYu Xiangning !IN6_IS_ADDR_UNSPECIFIED(&udp->udp_v6src)) { 15320f1702c5SYu Xiangning IN6_V4MAPPED_TO_IPADDR(&udp->udp_v6src, 15330f1702c5SYu Xiangning sin->sin_addr.s_addr); 15340f1702c5SYu Xiangning } else { 15350f1702c5SYu Xiangning /* 15360f1702c5SYu Xiangning * INADDR_ANY 15370f1702c5SYu Xiangning * udp_v6src is not set, we might be bound to 15380f1702c5SYu Xiangning * broadcast/multicast. Use udp_bound_v6src as 15390f1702c5SYu Xiangning * local address instead (that could 15400f1702c5SYu Xiangning * also still be INADDR_ANY) 15410f1702c5SYu Xiangning */ 15420f1702c5SYu Xiangning IN6_V4MAPPED_TO_IPADDR(&udp->udp_bound_v6src, 15430f1702c5SYu Xiangning sin->sin_addr.s_addr); 15440f1702c5SYu Xiangning } 15450f1702c5SYu Xiangning sin->sin_port = udp->udp_port; 15460f1702c5SYu Xiangning ackmp->b_wptr = (uchar_t *)&sin[1]; 15470f1702c5SYu Xiangning if (udp->udp_state == TS_DATA_XFER) { 15480f1702c5SYu Xiangning /* 15490f1702c5SYu Xiangning * connected, fill remote address too 15500f1702c5SYu Xiangning */ 15510f1702c5SYu Xiangning taa->REMADDR_length = sizeof (sin_t); 15520f1702c5SYu Xiangning /* assumed 32-bit alignment */ 15530f1702c5SYu Xiangning taa->REMADDR_offset = taa->LOCADDR_offset + 15540f1702c5SYu Xiangning taa->LOCADDR_length; 15557c478bd9Sstevel@tonic-gate 15560f1702c5SYu Xiangning sin = (sin_t *)(ackmp->b_rptr + 15570f1702c5SYu Xiangning taa->REMADDR_offset); 15580f1702c5SYu Xiangning /* initialize */ 15590f1702c5SYu Xiangning *sin = sin_null; 15600f1702c5SYu Xiangning sin->sin_family = AF_INET; 15610f1702c5SYu Xiangning sin->sin_addr.s_addr = 15620f1702c5SYu Xiangning V4_PART_OF_V6(udp->udp_v6dst); 15630f1702c5SYu Xiangning sin->sin_port = udp->udp_dstport; 15640f1702c5SYu Xiangning ackmp->b_wptr = (uchar_t *)&sin[1]; 15650f1702c5SYu Xiangning } 15660f1702c5SYu Xiangning } else { 15670f1702c5SYu Xiangning taa->LOCADDR_length = sizeof (sin6_t); 15680f1702c5SYu Xiangning sin6 = (sin6_t *)&taa[1]; 15690f1702c5SYu Xiangning /* Fill zeroes and then initialize non-zero fields */ 15700f1702c5SYu Xiangning *sin6 = sin6_null; 15710f1702c5SYu Xiangning sin6->sin6_family = AF_INET6; 15720f1702c5SYu Xiangning if (!IN6_IS_ADDR_UNSPECIFIED(&udp->udp_v6src)) { 15730f1702c5SYu Xiangning sin6->sin6_addr = udp->udp_v6src; 15740f1702c5SYu Xiangning } else { 15750f1702c5SYu Xiangning /* 15760f1702c5SYu Xiangning * UNSPECIFIED 15770f1702c5SYu Xiangning * udp_v6src is not set, we might be bound to 15780f1702c5SYu Xiangning * broadcast/multicast. Use udp_bound_v6src as 15790f1702c5SYu Xiangning * local address instead (that could 15800f1702c5SYu Xiangning * also still be UNSPECIFIED) 15810f1702c5SYu Xiangning */ 15820f1702c5SYu Xiangning sin6->sin6_addr = 15830f1702c5SYu Xiangning udp->udp_bound_v6src; 15840f1702c5SYu Xiangning } 15850f1702c5SYu Xiangning sin6->sin6_port = udp->udp_port; 15860f1702c5SYu Xiangning ackmp->b_wptr = (uchar_t *)&sin6[1]; 15870f1702c5SYu Xiangning if (udp->udp_state == TS_DATA_XFER) { 15880f1702c5SYu Xiangning /* 15890f1702c5SYu Xiangning * connected, fill remote address too 15900f1702c5SYu Xiangning */ 15910f1702c5SYu Xiangning taa->REMADDR_length = sizeof (sin6_t); 15920f1702c5SYu Xiangning /* assumed 32-bit alignment */ 15930f1702c5SYu Xiangning taa->REMADDR_offset = taa->LOCADDR_offset + 15940f1702c5SYu Xiangning taa->LOCADDR_length; 15957c478bd9Sstevel@tonic-gate 15960f1702c5SYu Xiangning sin6 = (sin6_t *)(ackmp->b_rptr + 15970f1702c5SYu Xiangning taa->REMADDR_offset); 15980f1702c5SYu Xiangning /* initialize */ 15990f1702c5SYu Xiangning *sin6 = sin6_null; 16000f1702c5SYu Xiangning sin6->sin6_family = AF_INET6; 16010f1702c5SYu Xiangning sin6->sin6_addr = udp->udp_v6dst; 16020f1702c5SYu Xiangning sin6->sin6_port = udp->udp_dstport; 16030f1702c5SYu Xiangning ackmp->b_wptr = (uchar_t *)&sin6[1]; 16040f1702c5SYu Xiangning } 16050f1702c5SYu Xiangning ackmp->b_wptr = (uchar_t *)&sin6[1]; 16067c478bd9Sstevel@tonic-gate } 16077c478bd9Sstevel@tonic-gate } 1608fc80c0dfSnordmark rw_exit(&udp->udp_rwlock); 16090f1702c5SYu Xiangning ASSERT(ackmp->b_wptr <= ackmp->b_datap->db_lim); 16100f1702c5SYu Xiangning qreply(q, ackmp); 16117c478bd9Sstevel@tonic-gate } 16127c478bd9Sstevel@tonic-gate 16137c478bd9Sstevel@tonic-gate static void 16140f1702c5SYu Xiangning udp_copy_info(struct T_info_ack *tap, udp_t *udp) 16157c478bd9Sstevel@tonic-gate { 16160f1702c5SYu Xiangning if (udp->udp_family == AF_INET) { 16170f1702c5SYu Xiangning *tap = udp_g_t_info_ack_ipv4; 16180f1702c5SYu Xiangning } else { 16190f1702c5SYu Xiangning *tap = udp_g_t_info_ack_ipv6; 16207c478bd9Sstevel@tonic-gate } 16210f1702c5SYu Xiangning tap->CURRENT_state = udp->udp_state; 16220f1702c5SYu Xiangning tap->OPT_size = udp_max_optsize; 16237c478bd9Sstevel@tonic-gate } 16247c478bd9Sstevel@tonic-gate 16250f1702c5SYu Xiangning static void 16260f1702c5SYu Xiangning udp_do_capability_ack(udp_t *udp, struct T_capability_ack *tcap, 16270f1702c5SYu Xiangning t_uscalar_t cap_bits1) 16287c478bd9Sstevel@tonic-gate { 16290f1702c5SYu Xiangning tcap->CAP_bits1 = 0; 16307c478bd9Sstevel@tonic-gate 16310f1702c5SYu Xiangning if (cap_bits1 & TC1_INFO) { 16320f1702c5SYu Xiangning udp_copy_info(&tcap->INFO_ack, udp); 16330f1702c5SYu Xiangning tcap->CAP_bits1 |= TC1_INFO; 16347c478bd9Sstevel@tonic-gate } 16357c478bd9Sstevel@tonic-gate } 16367c478bd9Sstevel@tonic-gate 16370f1702c5SYu Xiangning /* 16380f1702c5SYu Xiangning * This routine responds to T_CAPABILITY_REQ messages. It is called by 16390f1702c5SYu Xiangning * udp_wput. Much of the T_CAPABILITY_ACK information is copied from 16400f1702c5SYu Xiangning * udp_g_t_info_ack. The current state of the stream is copied from 16410f1702c5SYu Xiangning * udp_state. 16420f1702c5SYu Xiangning */ 16430f1702c5SYu Xiangning static void 16440f1702c5SYu Xiangning udp_capability_req(queue_t *q, mblk_t *mp) 16457c478bd9Sstevel@tonic-gate { 16460f1702c5SYu Xiangning t_uscalar_t cap_bits1; 16470f1702c5SYu Xiangning struct T_capability_ack *tcap; 16480f1702c5SYu Xiangning udp_t *udp = Q_TO_UDP(q); 16497c478bd9Sstevel@tonic-gate 16500f1702c5SYu Xiangning cap_bits1 = ((struct T_capability_req *)mp->b_rptr)->CAP_bits1; 16517c478bd9Sstevel@tonic-gate 16520f1702c5SYu Xiangning mp = tpi_ack_alloc(mp, sizeof (struct T_capability_ack), 16530f1702c5SYu Xiangning mp->b_datap->db_type, T_CAPABILITY_ACK); 16540f1702c5SYu Xiangning if (!mp) 16550f1702c5SYu Xiangning return; 16567c478bd9Sstevel@tonic-gate 16570f1702c5SYu Xiangning tcap = (struct T_capability_ack *)mp->b_rptr; 16580f1702c5SYu Xiangning udp_do_capability_ack(udp, tcap, cap_bits1); 16597c478bd9Sstevel@tonic-gate 16600f1702c5SYu Xiangning qreply(q, mp); 16617c478bd9Sstevel@tonic-gate } 16627c478bd9Sstevel@tonic-gate 16637c478bd9Sstevel@tonic-gate /* 16640f1702c5SYu Xiangning * This routine responds to T_INFO_REQ messages. It is called by udp_wput. 16650f1702c5SYu Xiangning * Most of the T_INFO_ACK information is copied from udp_g_t_info_ack. 16660f1702c5SYu Xiangning * The current state of the stream is copied from udp_state. 16677c478bd9Sstevel@tonic-gate */ 16687c478bd9Sstevel@tonic-gate static void 16690f1702c5SYu Xiangning udp_info_req(queue_t *q, mblk_t *mp) 16707c478bd9Sstevel@tonic-gate { 16710f1702c5SYu Xiangning udp_t *udp = Q_TO_UDP(q); 16727c478bd9Sstevel@tonic-gate 16730f1702c5SYu Xiangning /* Create a T_INFO_ACK message. */ 16740f1702c5SYu Xiangning mp = tpi_ack_alloc(mp, sizeof (struct T_info_ack), M_PCPROTO, 16750f1702c5SYu Xiangning T_INFO_ACK); 16760f1702c5SYu Xiangning if (!mp) 16777c478bd9Sstevel@tonic-gate return; 16780f1702c5SYu Xiangning udp_copy_info((struct T_info_ack *)mp->b_rptr, udp); 16790f1702c5SYu Xiangning qreply(q, mp); 16800f1702c5SYu Xiangning } 16817c478bd9Sstevel@tonic-gate 16820f1702c5SYu Xiangning /* 16830f1702c5SYu Xiangning * IP recognizes seven kinds of bind requests: 16840f1702c5SYu Xiangning * 16850f1702c5SYu Xiangning * - A zero-length address binds only to the protocol number. 16860f1702c5SYu Xiangning * 16870f1702c5SYu Xiangning * - A 4-byte address is treated as a request to 16880f1702c5SYu Xiangning * validate that the address is a valid local IPv4 16890f1702c5SYu Xiangning * address, appropriate for an application to bind to. 16900f1702c5SYu Xiangning * IP does the verification, but does not make any note 16910f1702c5SYu Xiangning * of the address at this time. 16920f1702c5SYu Xiangning * 16930f1702c5SYu Xiangning * - A 16-byte address contains is treated as a request 16940f1702c5SYu Xiangning * to validate a local IPv6 address, as the 4-byte 16950f1702c5SYu Xiangning * address case above. 16960f1702c5SYu Xiangning * 16970f1702c5SYu Xiangning * - A 16-byte sockaddr_in to validate the local IPv4 address and also 16980f1702c5SYu Xiangning * use it for the inbound fanout of packets. 16990f1702c5SYu Xiangning * 17000f1702c5SYu Xiangning * - A 24-byte sockaddr_in6 to validate the local IPv6 address and also 17010f1702c5SYu Xiangning * use it for the inbound fanout of packets. 17020f1702c5SYu Xiangning * 17030f1702c5SYu Xiangning * - A 12-byte address (ipa_conn_t) containing complete IPv4 fanout 17040f1702c5SYu Xiangning * information consisting of local and remote addresses 17050f1702c5SYu Xiangning * and ports. In this case, the addresses are both 17060f1702c5SYu Xiangning * validated as appropriate for this operation, and, if 17070f1702c5SYu Xiangning * so, the information is retained for use in the 17080f1702c5SYu Xiangning * inbound fanout. 17090f1702c5SYu Xiangning * 17100f1702c5SYu Xiangning * - A 36-byte address address (ipa6_conn_t) containing complete IPv6 17110f1702c5SYu Xiangning * fanout information, like the 12-byte case above. 17120f1702c5SYu Xiangning * 17130f1702c5SYu Xiangning * IP will also fill in the IRE request mblk with information 17140f1702c5SYu Xiangning * regarding our peer. In all cases, we notify IP of our protocol 17150f1702c5SYu Xiangning * type by appending a single protocol byte to the bind request. 17160f1702c5SYu Xiangning */ 17170f1702c5SYu Xiangning static mblk_t * 17180f1702c5SYu Xiangning udp_ip_bind_mp(udp_t *udp, t_scalar_t bind_prim, t_scalar_t addr_length) 17190f1702c5SYu Xiangning { 17200f1702c5SYu Xiangning char *cp; 17210f1702c5SYu Xiangning mblk_t *mp; 17220f1702c5SYu Xiangning struct T_bind_req *tbr; 17230f1702c5SYu Xiangning ipa_conn_t *ac; 17240f1702c5SYu Xiangning ipa6_conn_t *ac6; 17250f1702c5SYu Xiangning sin_t *sin; 17260f1702c5SYu Xiangning sin6_t *sin6; 17277c478bd9Sstevel@tonic-gate 17280f1702c5SYu Xiangning ASSERT(bind_prim == O_T_BIND_REQ || bind_prim == T_BIND_REQ); 17290f1702c5SYu Xiangning ASSERT(RW_LOCK_HELD(&udp->udp_rwlock)); 17300f1702c5SYu Xiangning mp = allocb(sizeof (*tbr) + addr_length + 1, BPRI_HI); 17310f1702c5SYu Xiangning if (!mp) 17320f1702c5SYu Xiangning return (mp); 17330f1702c5SYu Xiangning mp->b_datap->db_type = M_PROTO; 17340f1702c5SYu Xiangning tbr = (struct T_bind_req *)mp->b_rptr; 17350f1702c5SYu Xiangning tbr->PRIM_type = bind_prim; 17360f1702c5SYu Xiangning tbr->ADDR_offset = sizeof (*tbr); 17370f1702c5SYu Xiangning tbr->CONIND_number = 0; 17380f1702c5SYu Xiangning tbr->ADDR_length = addr_length; 17390f1702c5SYu Xiangning cp = (char *)&tbr[1]; 17400f1702c5SYu Xiangning switch (addr_length) { 17410f1702c5SYu Xiangning case sizeof (ipa_conn_t): 17420f1702c5SYu Xiangning ASSERT(udp->udp_family == AF_INET); 17430f1702c5SYu Xiangning /* Append a request for an IRE */ 17440f1702c5SYu Xiangning mp->b_cont = allocb(sizeof (ire_t), BPRI_HI); 17450f1702c5SYu Xiangning if (!mp->b_cont) { 17460f1702c5SYu Xiangning freemsg(mp); 17470f1702c5SYu Xiangning return (NULL); 17487c478bd9Sstevel@tonic-gate } 17490f1702c5SYu Xiangning mp->b_cont->b_wptr += sizeof (ire_t); 17500f1702c5SYu Xiangning mp->b_cont->b_datap->db_type = IRE_DB_REQ_TYPE; 17510f1702c5SYu Xiangning 17520f1702c5SYu Xiangning /* cp known to be 32 bit aligned */ 17530f1702c5SYu Xiangning ac = (ipa_conn_t *)cp; 17540f1702c5SYu Xiangning ac->ac_laddr = V4_PART_OF_V6(udp->udp_v6src); 17550f1702c5SYu Xiangning ac->ac_faddr = V4_PART_OF_V6(udp->udp_v6dst); 17560f1702c5SYu Xiangning ac->ac_fport = udp->udp_dstport; 17570f1702c5SYu Xiangning ac->ac_lport = udp->udp_port; 17587c478bd9Sstevel@tonic-gate break; 17590f1702c5SYu Xiangning 17600f1702c5SYu Xiangning case sizeof (ipa6_conn_t): 17610f1702c5SYu Xiangning ASSERT(udp->udp_family == AF_INET6); 17620f1702c5SYu Xiangning /* Append a request for an IRE */ 17630f1702c5SYu Xiangning mp->b_cont = allocb(sizeof (ire_t), BPRI_HI); 17640f1702c5SYu Xiangning if (!mp->b_cont) { 17650f1702c5SYu Xiangning freemsg(mp); 17660f1702c5SYu Xiangning return (NULL); 17670f1702c5SYu Xiangning } 17680f1702c5SYu Xiangning mp->b_cont->b_wptr += sizeof (ire_t); 17690f1702c5SYu Xiangning mp->b_cont->b_datap->db_type = IRE_DB_REQ_TYPE; 17700f1702c5SYu Xiangning 17710f1702c5SYu Xiangning /* cp known to be 32 bit aligned */ 17720f1702c5SYu Xiangning ac6 = (ipa6_conn_t *)cp; 17730f1702c5SYu Xiangning ac6->ac6_laddr = udp->udp_v6src; 17740f1702c5SYu Xiangning ac6->ac6_faddr = udp->udp_v6dst; 17750f1702c5SYu Xiangning ac6->ac6_fport = udp->udp_dstport; 17760f1702c5SYu Xiangning ac6->ac6_lport = udp->udp_port; 17777c478bd9Sstevel@tonic-gate break; 17787c478bd9Sstevel@tonic-gate 17790f1702c5SYu Xiangning case sizeof (sin_t): 17800f1702c5SYu Xiangning ASSERT(udp->udp_family == AF_INET); 17810f1702c5SYu Xiangning /* Append a request for an IRE */ 17820f1702c5SYu Xiangning mp->b_cont = allocb(sizeof (ire_t), BPRI_HI); 17830f1702c5SYu Xiangning if (!mp->b_cont) { 17840f1702c5SYu Xiangning freemsg(mp); 17850f1702c5SYu Xiangning return (NULL); 17860f1702c5SYu Xiangning } 17870f1702c5SYu Xiangning mp->b_cont->b_wptr += sizeof (ire_t); 17880f1702c5SYu Xiangning mp->b_cont->b_datap->db_type = IRE_DB_REQ_TYPE; 1789fc80c0dfSnordmark 17900f1702c5SYu Xiangning sin = (sin_t *)cp; 17910f1702c5SYu Xiangning *sin = sin_null; 17920f1702c5SYu Xiangning sin->sin_family = AF_INET; 17930f1702c5SYu Xiangning sin->sin_addr.s_addr = V4_PART_OF_V6(udp->udp_bound_v6src); 17940f1702c5SYu Xiangning sin->sin_port = udp->udp_port; 17957c478bd9Sstevel@tonic-gate break; 17967c478bd9Sstevel@tonic-gate 17970f1702c5SYu Xiangning case sizeof (sin6_t): 17980f1702c5SYu Xiangning ASSERT(udp->udp_family == AF_INET6); 17990f1702c5SYu Xiangning /* Append a request for an IRE */ 18000f1702c5SYu Xiangning mp->b_cont = allocb(sizeof (ire_t), BPRI_HI); 18010f1702c5SYu Xiangning if (!mp->b_cont) { 18020f1702c5SYu Xiangning freemsg(mp); 18030f1702c5SYu Xiangning return (NULL); 18040f1702c5SYu Xiangning } 18050f1702c5SYu Xiangning mp->b_cont->b_wptr += sizeof (ire_t); 18060f1702c5SYu Xiangning mp->b_cont->b_datap->db_type = IRE_DB_REQ_TYPE; 18070f1702c5SYu Xiangning 18080f1702c5SYu Xiangning sin6 = (sin6_t *)cp; 18090f1702c5SYu Xiangning *sin6 = sin6_null; 18100f1702c5SYu Xiangning sin6->sin6_family = AF_INET6; 18110f1702c5SYu Xiangning sin6->sin6_addr = udp->udp_bound_v6src; 18120f1702c5SYu Xiangning sin6->sin6_port = udp->udp_port; 18137c478bd9Sstevel@tonic-gate break; 18147c478bd9Sstevel@tonic-gate } 18150f1702c5SYu Xiangning /* Add protocol number to end */ 18160f1702c5SYu Xiangning cp[addr_length] = (char)IPPROTO_UDP; 18170f1702c5SYu Xiangning mp->b_wptr = (uchar_t *)&cp[addr_length + 1]; 18180f1702c5SYu Xiangning return (mp); 18190f1702c5SYu Xiangning } 18200f1702c5SYu Xiangning 18210f1702c5SYu Xiangning /* For /dev/udp aka AF_INET open */ 18220f1702c5SYu Xiangning static int 18230f1702c5SYu Xiangning udp_openv4(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp) 18240f1702c5SYu Xiangning { 18250f1702c5SYu Xiangning return (udp_open(q, devp, flag, sflag, credp, B_FALSE)); 18260f1702c5SYu Xiangning } 18270f1702c5SYu Xiangning 18280f1702c5SYu Xiangning /* For /dev/udp6 aka AF_INET6 open */ 18290f1702c5SYu Xiangning static int 18300f1702c5SYu Xiangning udp_openv6(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp) 18310f1702c5SYu Xiangning { 18320f1702c5SYu Xiangning return (udp_open(q, devp, flag, sflag, credp, B_TRUE)); 18337c478bd9Sstevel@tonic-gate } 18347c478bd9Sstevel@tonic-gate 18357c478bd9Sstevel@tonic-gate /* 18360f1702c5SYu Xiangning * This is the open routine for udp. It allocates a udp_t structure for 18370f1702c5SYu Xiangning * the stream and, on the first open of the module, creates an ND table. 18387c478bd9Sstevel@tonic-gate */ 18390f1702c5SYu Xiangning /*ARGSUSED2*/ 18400f1702c5SYu Xiangning static int 18410f1702c5SYu Xiangning udp_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp, 18420f1702c5SYu Xiangning boolean_t isv6) 18437c478bd9Sstevel@tonic-gate { 18440f1702c5SYu Xiangning int error; 18450f1702c5SYu Xiangning udp_t *udp; 18460f1702c5SYu Xiangning conn_t *connp; 18470f1702c5SYu Xiangning dev_t conn_dev; 18480f1702c5SYu Xiangning udp_stack_t *us; 18490f1702c5SYu Xiangning vmem_t *minor_arena; 18507c478bd9Sstevel@tonic-gate 18510f1702c5SYu Xiangning TRACE_1(TR_FAC_UDP, TR_UDP_OPEN, "udp_open: q %p", q); 18527c478bd9Sstevel@tonic-gate 18530f1702c5SYu Xiangning /* If the stream is already open, return immediately. */ 18540f1702c5SYu Xiangning if (q->q_ptr != NULL) 18550f1702c5SYu Xiangning return (0); 18567c478bd9Sstevel@tonic-gate 18570f1702c5SYu Xiangning if (sflag == MODOPEN) 18580f1702c5SYu Xiangning return (EINVAL); 18597c478bd9Sstevel@tonic-gate 18600f1702c5SYu Xiangning if ((ip_minor_arena_la != NULL) && (flag & SO_SOCKSTR) && 18610f1702c5SYu Xiangning ((conn_dev = inet_minor_alloc(ip_minor_arena_la)) != 0)) { 18620f1702c5SYu Xiangning minor_arena = ip_minor_arena_la; 18630f1702c5SYu Xiangning } else { 18647c478bd9Sstevel@tonic-gate /* 18650f1702c5SYu Xiangning * Either minor numbers in the large arena were exhausted 18660f1702c5SYu Xiangning * or a non socket application is doing the open. 18670f1702c5SYu Xiangning * Try to allocate from the small arena. 18687c478bd9Sstevel@tonic-gate */ 18690f1702c5SYu Xiangning if ((conn_dev = inet_minor_alloc(ip_minor_arena_sa)) == 0) 18700f1702c5SYu Xiangning return (EBUSY); 18717c478bd9Sstevel@tonic-gate 18720f1702c5SYu Xiangning minor_arena = ip_minor_arena_sa; 18730f1702c5SYu Xiangning } 18747c478bd9Sstevel@tonic-gate 18750f1702c5SYu Xiangning if (flag & SO_FALLBACK) { 18767c478bd9Sstevel@tonic-gate /* 18770f1702c5SYu Xiangning * Non streams socket needs a stream to fallback to 18787c478bd9Sstevel@tonic-gate */ 18790f1702c5SYu Xiangning RD(q)->q_ptr = (void *)conn_dev; 18800f1702c5SYu Xiangning WR(q)->q_qinfo = &udp_fallback_sock_winit; 18810f1702c5SYu Xiangning WR(q)->q_ptr = (void *)minor_arena; 18820f1702c5SYu Xiangning qprocson(q); 18830f1702c5SYu Xiangning return (0); 18847c478bd9Sstevel@tonic-gate } 18850f1702c5SYu Xiangning 18860f1702c5SYu Xiangning connp = udp_do_open(credp, isv6, KM_SLEEP); 18870f1702c5SYu Xiangning if (connp == NULL) { 18880f1702c5SYu Xiangning inet_minor_free(minor_arena, conn_dev); 18890f1702c5SYu Xiangning return (ENOMEM); 18907c478bd9Sstevel@tonic-gate } 18910f1702c5SYu Xiangning udp = connp->conn_udp; 18920f1702c5SYu Xiangning us = udp->udp_us; 18930f1702c5SYu Xiangning 18940f1702c5SYu Xiangning *devp = makedevice(getemajor(*devp), (minor_t)conn_dev); 18950f1702c5SYu Xiangning connp->conn_dev = conn_dev; 18960f1702c5SYu Xiangning connp->conn_minor_arena = minor_arena; 18977c478bd9Sstevel@tonic-gate 1898fc80c0dfSnordmark /* 18990f1702c5SYu Xiangning * Initialize the udp_t structure for this stream. 1900fc80c0dfSnordmark */ 19010f1702c5SYu Xiangning q->q_ptr = connp; 19020f1702c5SYu Xiangning WR(q)->q_ptr = connp; 19030f1702c5SYu Xiangning connp->conn_rq = q; 19040f1702c5SYu Xiangning connp->conn_wq = WR(q); 19050f1702c5SYu Xiangning 19060f1702c5SYu Xiangning rw_enter(&udp->udp_rwlock, RW_WRITER); 19070f1702c5SYu Xiangning ASSERT(connp->conn_ulp == IPPROTO_UDP); 19080f1702c5SYu Xiangning ASSERT(connp->conn_udp == udp); 19090f1702c5SYu Xiangning ASSERT(udp->udp_connp == connp); 19100f1702c5SYu Xiangning 19110f1702c5SYu Xiangning if (flag & SO_SOCKSTR) { 19120f1702c5SYu Xiangning connp->conn_flags |= IPCL_SOCKET; 19130f1702c5SYu Xiangning udp->udp_issocket = B_TRUE; 19140f1702c5SYu Xiangning udp->udp_direct_sockfs = B_TRUE; 1915fc80c0dfSnordmark } 1916fc80c0dfSnordmark 19170f1702c5SYu Xiangning q->q_hiwat = us->us_recv_hiwat; 19180f1702c5SYu Xiangning WR(q)->q_hiwat = us->us_xmit_hiwat; 19190f1702c5SYu Xiangning WR(q)->q_lowat = us->us_xmit_lowat; 19207c478bd9Sstevel@tonic-gate 19210f1702c5SYu Xiangning qprocson(q); 19220f1702c5SYu Xiangning 19230f1702c5SYu Xiangning if (udp->udp_family == AF_INET6) { 19240f1702c5SYu Xiangning /* Build initial header template for transmit */ 19250f1702c5SYu Xiangning if ((error = udp_build_hdrs(udp)) != 0) { 19260f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 19270f1702c5SYu Xiangning qprocsoff(q); 19280f1702c5SYu Xiangning inet_minor_free(minor_arena, conn_dev); 19290f1702c5SYu Xiangning ipcl_conn_destroy(connp); 19300f1702c5SYu Xiangning return (error); 19310f1702c5SYu Xiangning } 19320f1702c5SYu Xiangning } 19330f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 19340f1702c5SYu Xiangning 19350f1702c5SYu Xiangning /* Set the Stream head write offset and high watermark. */ 19360f1702c5SYu Xiangning (void) proto_set_tx_wroff(q, connp, 19370f1702c5SYu Xiangning udp->udp_max_hdr_len + us->us_wroff_extra); 19380f1702c5SYu Xiangning /* XXX udp_set_rcv_hiwat() doesn't hold the lock, is it a bug??? */ 19390f1702c5SYu Xiangning (void) proto_set_rx_hiwat(q, connp, udp_set_rcv_hiwat(udp, q->q_hiwat)); 19400f1702c5SYu Xiangning 19410f1702c5SYu Xiangning mutex_enter(&connp->conn_lock); 19420f1702c5SYu Xiangning connp->conn_state_flags &= ~CONN_INCIPIENT; 19430f1702c5SYu Xiangning mutex_exit(&connp->conn_lock); 19440f1702c5SYu Xiangning return (0); 19457c478bd9Sstevel@tonic-gate } 19467c478bd9Sstevel@tonic-gate 19477c478bd9Sstevel@tonic-gate /* 19480f1702c5SYu Xiangning * Which UDP options OK to set through T_UNITDATA_REQ... 19497c478bd9Sstevel@tonic-gate */ 19500f1702c5SYu Xiangning /* ARGSUSED */ 19510f1702c5SYu Xiangning static boolean_t 19520f1702c5SYu Xiangning udp_opt_allow_udr_set(t_scalar_t level, t_scalar_t name) 19537c478bd9Sstevel@tonic-gate { 19540f1702c5SYu Xiangning return (B_TRUE); 19550f1702c5SYu Xiangning } 19567c478bd9Sstevel@tonic-gate 19570f1702c5SYu Xiangning /* 19580f1702c5SYu Xiangning * This routine gets default values of certain options whose default 19590f1702c5SYu Xiangning * values are maintained by protcol specific code 19600f1702c5SYu Xiangning */ 19610f1702c5SYu Xiangning /* ARGSUSED */ 19620f1702c5SYu Xiangning int 19630f1702c5SYu Xiangning udp_opt_default(queue_t *q, t_scalar_t level, t_scalar_t name, uchar_t *ptr) 19640f1702c5SYu Xiangning { 19650f1702c5SYu Xiangning udp_t *udp = Q_TO_UDP(q); 19660f1702c5SYu Xiangning udp_stack_t *us = udp->udp_us; 19670f1702c5SYu Xiangning int *i1 = (int *)ptr; 19680f1702c5SYu Xiangning 19690f1702c5SYu Xiangning switch (level) { 19700f1702c5SYu Xiangning case IPPROTO_IP: 19710f1702c5SYu Xiangning switch (name) { 19720f1702c5SYu Xiangning case IP_MULTICAST_TTL: 19730f1702c5SYu Xiangning *ptr = (uchar_t)IP_DEFAULT_MULTICAST_TTL; 19740f1702c5SYu Xiangning return (sizeof (uchar_t)); 19750f1702c5SYu Xiangning case IP_MULTICAST_LOOP: 19760f1702c5SYu Xiangning *ptr = (uchar_t)IP_DEFAULT_MULTICAST_LOOP; 19770f1702c5SYu Xiangning return (sizeof (uchar_t)); 19780f1702c5SYu Xiangning } 19790f1702c5SYu Xiangning break; 19800f1702c5SYu Xiangning case IPPROTO_IPV6: 19810f1702c5SYu Xiangning switch (name) { 19820f1702c5SYu Xiangning case IPV6_MULTICAST_HOPS: 19830f1702c5SYu Xiangning *i1 = IP_DEFAULT_MULTICAST_TTL; 19840f1702c5SYu Xiangning return (sizeof (int)); 19850f1702c5SYu Xiangning case IPV6_MULTICAST_LOOP: 19860f1702c5SYu Xiangning *i1 = IP_DEFAULT_MULTICAST_LOOP; 19870f1702c5SYu Xiangning return (sizeof (int)); 19880f1702c5SYu Xiangning case IPV6_UNICAST_HOPS: 19890f1702c5SYu Xiangning *i1 = us->us_ipv6_hoplimit; 19900f1702c5SYu Xiangning return (sizeof (int)); 19910f1702c5SYu Xiangning } 19920f1702c5SYu Xiangning break; 19937c478bd9Sstevel@tonic-gate } 19940f1702c5SYu Xiangning return (-1); 19950f1702c5SYu Xiangning } 19967c478bd9Sstevel@tonic-gate 19970f1702c5SYu Xiangning /* 19980f1702c5SYu Xiangning * This routine retrieves the current status of socket options. 19990f1702c5SYu Xiangning * It returns the size of the option retrieved. 20000f1702c5SYu Xiangning */ 20010f1702c5SYu Xiangning static int 20020f1702c5SYu Xiangning udp_opt_get(conn_t *connp, int level, int name, uchar_t *ptr) 20030f1702c5SYu Xiangning { 20040f1702c5SYu Xiangning udp_t *udp = connp->conn_udp; 20050f1702c5SYu Xiangning udp_stack_t *us = udp->udp_us; 20060f1702c5SYu Xiangning int *i1 = (int *)ptr; 20070f1702c5SYu Xiangning ip6_pkt_t *ipp = &udp->udp_sticky_ipp; 20080f1702c5SYu Xiangning int len; 20090f1702c5SYu Xiangning 20100f1702c5SYu Xiangning ASSERT(RW_READ_HELD(&udp->udp_rwlock)); 20110f1702c5SYu Xiangning switch (level) { 20120f1702c5SYu Xiangning case SOL_SOCKET: 20130f1702c5SYu Xiangning switch (name) { 20140f1702c5SYu Xiangning case SO_DEBUG: 20150f1702c5SYu Xiangning *i1 = udp->udp_debug; 20160f1702c5SYu Xiangning break; /* goto sizeof (int) option return */ 20170f1702c5SYu Xiangning case SO_REUSEADDR: 20180f1702c5SYu Xiangning *i1 = udp->udp_reuseaddr; 20190f1702c5SYu Xiangning break; /* goto sizeof (int) option return */ 20200f1702c5SYu Xiangning case SO_TYPE: 20210f1702c5SYu Xiangning *i1 = SOCK_DGRAM; 20220f1702c5SYu Xiangning break; /* goto sizeof (int) option return */ 20237c478bd9Sstevel@tonic-gate 20247c478bd9Sstevel@tonic-gate /* 20250f1702c5SYu Xiangning * The following three items are available here, 20260f1702c5SYu Xiangning * but are only meaningful to IP. 20277c478bd9Sstevel@tonic-gate */ 20280f1702c5SYu Xiangning case SO_DONTROUTE: 20290f1702c5SYu Xiangning *i1 = udp->udp_dontroute; 20300f1702c5SYu Xiangning break; /* goto sizeof (int) option return */ 20310f1702c5SYu Xiangning case SO_USELOOPBACK: 20320f1702c5SYu Xiangning *i1 = udp->udp_useloopback; 20330f1702c5SYu Xiangning break; /* goto sizeof (int) option return */ 20340f1702c5SYu Xiangning case SO_BROADCAST: 20350f1702c5SYu Xiangning *i1 = udp->udp_broadcast; 20360f1702c5SYu Xiangning break; /* goto sizeof (int) option return */ 20370f1702c5SYu Xiangning 20380f1702c5SYu Xiangning case SO_SNDBUF: 20390f1702c5SYu Xiangning *i1 = udp->udp_xmit_hiwat; 20400f1702c5SYu Xiangning break; /* goto sizeof (int) option return */ 20410f1702c5SYu Xiangning case SO_RCVBUF: 20420f1702c5SYu Xiangning *i1 = udp->udp_rcv_disply_hiwat; 20430f1702c5SYu Xiangning break; /* goto sizeof (int) option return */ 20440f1702c5SYu Xiangning case SO_DGRAM_ERRIND: 20450f1702c5SYu Xiangning *i1 = udp->udp_dgram_errind; 20460f1702c5SYu Xiangning break; /* goto sizeof (int) option return */ 20470f1702c5SYu Xiangning case SO_RECVUCRED: 20480f1702c5SYu Xiangning *i1 = udp->udp_recvucred; 20490f1702c5SYu Xiangning break; /* goto sizeof (int) option return */ 20500f1702c5SYu Xiangning case SO_TIMESTAMP: 20510f1702c5SYu Xiangning *i1 = udp->udp_timestamp; 20520f1702c5SYu Xiangning break; /* goto sizeof (int) option return */ 20530f1702c5SYu Xiangning case SO_ANON_MLP: 20540f1702c5SYu Xiangning *i1 = connp->conn_anon_mlp; 20550f1702c5SYu Xiangning break; /* goto sizeof (int) option return */ 20560f1702c5SYu Xiangning case SO_MAC_EXEMPT: 20570f1702c5SYu Xiangning *i1 = connp->conn_mac_exempt; 20580f1702c5SYu Xiangning break; /* goto sizeof (int) option return */ 20590f1702c5SYu Xiangning case SO_ALLZONES: 20600f1702c5SYu Xiangning *i1 = connp->conn_allzones; 20610f1702c5SYu Xiangning break; /* goto sizeof (int) option return */ 20620f1702c5SYu Xiangning case SO_EXCLBIND: 20630f1702c5SYu Xiangning *i1 = udp->udp_exclbind ? SO_EXCLBIND : 0; 20640f1702c5SYu Xiangning break; 20650f1702c5SYu Xiangning case SO_PROTOTYPE: 20660f1702c5SYu Xiangning *i1 = IPPROTO_UDP; 20670f1702c5SYu Xiangning break; 20680f1702c5SYu Xiangning case SO_DOMAIN: 20690f1702c5SYu Xiangning *i1 = udp->udp_family; 20700f1702c5SYu Xiangning break; 20710f1702c5SYu Xiangning default: 20720f1702c5SYu Xiangning return (-1); 20730f1702c5SYu Xiangning } 20740f1702c5SYu Xiangning break; 20750f1702c5SYu Xiangning case IPPROTO_IP: 20760f1702c5SYu Xiangning if (udp->udp_family != AF_INET) 20770f1702c5SYu Xiangning return (-1); 20780f1702c5SYu Xiangning switch (name) { 20790f1702c5SYu Xiangning case IP_OPTIONS: 20800f1702c5SYu Xiangning case T_IP_OPTIONS: 20810f1702c5SYu Xiangning len = udp->udp_ip_rcv_options_len - udp->udp_label_len; 20820f1702c5SYu Xiangning if (len > 0) { 20830f1702c5SYu Xiangning bcopy(udp->udp_ip_rcv_options + 20840f1702c5SYu Xiangning udp->udp_label_len, ptr, len); 20850f1702c5SYu Xiangning } 20860f1702c5SYu Xiangning return (len); 20870f1702c5SYu Xiangning case IP_TOS: 20880f1702c5SYu Xiangning case T_IP_TOS: 20890f1702c5SYu Xiangning *i1 = (int)udp->udp_type_of_service; 20900f1702c5SYu Xiangning break; /* goto sizeof (int) option return */ 20910f1702c5SYu Xiangning case IP_TTL: 20920f1702c5SYu Xiangning *i1 = (int)udp->udp_ttl; 20930f1702c5SYu Xiangning break; /* goto sizeof (int) option return */ 20940f1702c5SYu Xiangning case IP_DHCPINIT_IF: 20950f1702c5SYu Xiangning return (-EINVAL); 20960f1702c5SYu Xiangning case IP_NEXTHOP: 20970f1702c5SYu Xiangning case IP_RECVPKTINFO: 20980f1702c5SYu Xiangning /* 20990f1702c5SYu Xiangning * This also handles IP_PKTINFO. 21000f1702c5SYu Xiangning * IP_PKTINFO and IP_RECVPKTINFO have the same value. 21010f1702c5SYu Xiangning * Differentiation is based on the size of the argument 21020f1702c5SYu Xiangning * passed in. 21030f1702c5SYu Xiangning * This option is handled in IP which will return an 21040f1702c5SYu Xiangning * error for IP_PKTINFO as it's not supported as a 21050f1702c5SYu Xiangning * sticky option. 21060f1702c5SYu Xiangning */ 21070f1702c5SYu Xiangning return (-EINVAL); 21080f1702c5SYu Xiangning case IP_MULTICAST_IF: 21090f1702c5SYu Xiangning /* 0 address if not set */ 21100f1702c5SYu Xiangning *(ipaddr_t *)ptr = udp->udp_multicast_if_addr; 21110f1702c5SYu Xiangning return (sizeof (ipaddr_t)); 21120f1702c5SYu Xiangning case IP_MULTICAST_TTL: 21130f1702c5SYu Xiangning *(uchar_t *)ptr = udp->udp_multicast_ttl; 21140f1702c5SYu Xiangning return (sizeof (uchar_t)); 21150f1702c5SYu Xiangning case IP_MULTICAST_LOOP: 21160f1702c5SYu Xiangning *ptr = connp->conn_multicast_loop; 21170f1702c5SYu Xiangning return (sizeof (uint8_t)); 21180f1702c5SYu Xiangning case IP_RECVOPTS: 21190f1702c5SYu Xiangning *i1 = udp->udp_recvopts; 21200f1702c5SYu Xiangning break; /* goto sizeof (int) option return */ 21210f1702c5SYu Xiangning case IP_RECVDSTADDR: 21220f1702c5SYu Xiangning *i1 = udp->udp_recvdstaddr; 21230f1702c5SYu Xiangning break; /* goto sizeof (int) option return */ 21240f1702c5SYu Xiangning case IP_RECVIF: 21250f1702c5SYu Xiangning *i1 = udp->udp_recvif; 21260f1702c5SYu Xiangning break; /* goto sizeof (int) option return */ 21270f1702c5SYu Xiangning case IP_RECVSLLA: 21280f1702c5SYu Xiangning *i1 = udp->udp_recvslla; 21290f1702c5SYu Xiangning break; /* goto sizeof (int) option return */ 21300f1702c5SYu Xiangning case IP_RECVTTL: 21310f1702c5SYu Xiangning *i1 = udp->udp_recvttl; 21320f1702c5SYu Xiangning break; /* goto sizeof (int) option return */ 21330f1702c5SYu Xiangning case IP_ADD_MEMBERSHIP: 21340f1702c5SYu Xiangning case IP_DROP_MEMBERSHIP: 21350f1702c5SYu Xiangning case IP_BLOCK_SOURCE: 21360f1702c5SYu Xiangning case IP_UNBLOCK_SOURCE: 21370f1702c5SYu Xiangning case IP_ADD_SOURCE_MEMBERSHIP: 21380f1702c5SYu Xiangning case IP_DROP_SOURCE_MEMBERSHIP: 21390f1702c5SYu Xiangning case MCAST_JOIN_GROUP: 21400f1702c5SYu Xiangning case MCAST_LEAVE_GROUP: 21410f1702c5SYu Xiangning case MCAST_BLOCK_SOURCE: 21420f1702c5SYu Xiangning case MCAST_UNBLOCK_SOURCE: 21430f1702c5SYu Xiangning case MCAST_JOIN_SOURCE_GROUP: 21440f1702c5SYu Xiangning case MCAST_LEAVE_SOURCE_GROUP: 21450f1702c5SYu Xiangning /* cannot "get" the value for these */ 21460f1702c5SYu Xiangning return (-1); 21470f1702c5SYu Xiangning case IP_BOUND_IF: 21480f1702c5SYu Xiangning /* Zero if not set */ 21490f1702c5SYu Xiangning *i1 = udp->udp_bound_if; 21500f1702c5SYu Xiangning break; /* goto sizeof (int) option return */ 21510f1702c5SYu Xiangning case IP_UNSPEC_SRC: 21520f1702c5SYu Xiangning *i1 = udp->udp_unspec_source; 21530f1702c5SYu Xiangning break; /* goto sizeof (int) option return */ 21540f1702c5SYu Xiangning case IP_BROADCAST_TTL: 21550f1702c5SYu Xiangning *(uchar_t *)ptr = connp->conn_broadcast_ttl; 21560f1702c5SYu Xiangning return (sizeof (uchar_t)); 21570f1702c5SYu Xiangning default: 21580f1702c5SYu Xiangning return (-1); 21590f1702c5SYu Xiangning } 21600f1702c5SYu Xiangning break; 21610f1702c5SYu Xiangning case IPPROTO_IPV6: 21620f1702c5SYu Xiangning if (udp->udp_family != AF_INET6) 21630f1702c5SYu Xiangning return (-1); 21640f1702c5SYu Xiangning switch (name) { 21650f1702c5SYu Xiangning case IPV6_UNICAST_HOPS: 21660f1702c5SYu Xiangning *i1 = (unsigned int)udp->udp_ttl; 21670f1702c5SYu Xiangning break; /* goto sizeof (int) option return */ 21680f1702c5SYu Xiangning case IPV6_MULTICAST_IF: 21690f1702c5SYu Xiangning /* 0 index if not set */ 21700f1702c5SYu Xiangning *i1 = udp->udp_multicast_if_index; 21710f1702c5SYu Xiangning break; /* goto sizeof (int) option return */ 21720f1702c5SYu Xiangning case IPV6_MULTICAST_HOPS: 21730f1702c5SYu Xiangning *i1 = udp->udp_multicast_ttl; 21740f1702c5SYu Xiangning break; /* goto sizeof (int) option return */ 21750f1702c5SYu Xiangning case IPV6_MULTICAST_LOOP: 21760f1702c5SYu Xiangning *i1 = connp->conn_multicast_loop; 21770f1702c5SYu Xiangning break; /* goto sizeof (int) option return */ 21780f1702c5SYu Xiangning case IPV6_JOIN_GROUP: 21790f1702c5SYu Xiangning case IPV6_LEAVE_GROUP: 21800f1702c5SYu Xiangning case MCAST_JOIN_GROUP: 21810f1702c5SYu Xiangning case MCAST_LEAVE_GROUP: 21820f1702c5SYu Xiangning case MCAST_BLOCK_SOURCE: 21830f1702c5SYu Xiangning case MCAST_UNBLOCK_SOURCE: 21840f1702c5SYu Xiangning case MCAST_JOIN_SOURCE_GROUP: 21850f1702c5SYu Xiangning case MCAST_LEAVE_SOURCE_GROUP: 21860f1702c5SYu Xiangning /* cannot "get" the value for these */ 21870f1702c5SYu Xiangning return (-1); 21880f1702c5SYu Xiangning case IPV6_BOUND_IF: 21890f1702c5SYu Xiangning /* Zero if not set */ 21900f1702c5SYu Xiangning *i1 = udp->udp_bound_if; 21910f1702c5SYu Xiangning break; /* goto sizeof (int) option return */ 21920f1702c5SYu Xiangning case IPV6_UNSPEC_SRC: 21930f1702c5SYu Xiangning *i1 = udp->udp_unspec_source; 21940f1702c5SYu Xiangning break; /* goto sizeof (int) option return */ 21950f1702c5SYu Xiangning case IPV6_RECVPKTINFO: 21960f1702c5SYu Xiangning *i1 = udp->udp_ip_recvpktinfo; 21970f1702c5SYu Xiangning break; /* goto sizeof (int) option return */ 21980f1702c5SYu Xiangning case IPV6_RECVTCLASS: 21990f1702c5SYu Xiangning *i1 = udp->udp_ipv6_recvtclass; 22000f1702c5SYu Xiangning break; /* goto sizeof (int) option return */ 22010f1702c5SYu Xiangning case IPV6_RECVPATHMTU: 22020f1702c5SYu Xiangning *i1 = udp->udp_ipv6_recvpathmtu; 22030f1702c5SYu Xiangning break; /* goto sizeof (int) option return */ 22040f1702c5SYu Xiangning case IPV6_RECVHOPLIMIT: 22050f1702c5SYu Xiangning *i1 = udp->udp_ipv6_recvhoplimit; 22060f1702c5SYu Xiangning break; /* goto sizeof (int) option return */ 22070f1702c5SYu Xiangning case IPV6_RECVHOPOPTS: 22080f1702c5SYu Xiangning *i1 = udp->udp_ipv6_recvhopopts; 22090f1702c5SYu Xiangning break; /* goto sizeof (int) option return */ 22100f1702c5SYu Xiangning case IPV6_RECVDSTOPTS: 22110f1702c5SYu Xiangning *i1 = udp->udp_ipv6_recvdstopts; 22120f1702c5SYu Xiangning break; /* goto sizeof (int) option return */ 22130f1702c5SYu Xiangning case _OLD_IPV6_RECVDSTOPTS: 22140f1702c5SYu Xiangning *i1 = udp->udp_old_ipv6_recvdstopts; 22150f1702c5SYu Xiangning break; /* goto sizeof (int) option return */ 22160f1702c5SYu Xiangning case IPV6_RECVRTHDRDSTOPTS: 22170f1702c5SYu Xiangning *i1 = udp->udp_ipv6_recvrthdrdstopts; 22180f1702c5SYu Xiangning break; /* goto sizeof (int) option return */ 22190f1702c5SYu Xiangning case IPV6_RECVRTHDR: 22200f1702c5SYu Xiangning *i1 = udp->udp_ipv6_recvrthdr; 22210f1702c5SYu Xiangning break; /* goto sizeof (int) option return */ 22220f1702c5SYu Xiangning case IPV6_PKTINFO: { 22230f1702c5SYu Xiangning /* XXX assumes that caller has room for max size! */ 22240f1702c5SYu Xiangning struct in6_pktinfo *pkti; 22250f1702c5SYu Xiangning 22260f1702c5SYu Xiangning pkti = (struct in6_pktinfo *)ptr; 22270f1702c5SYu Xiangning if (ipp->ipp_fields & IPPF_IFINDEX) 22280f1702c5SYu Xiangning pkti->ipi6_ifindex = ipp->ipp_ifindex; 22290f1702c5SYu Xiangning else 22300f1702c5SYu Xiangning pkti->ipi6_ifindex = 0; 22310f1702c5SYu Xiangning if (ipp->ipp_fields & IPPF_ADDR) 22320f1702c5SYu Xiangning pkti->ipi6_addr = ipp->ipp_addr; 22330f1702c5SYu Xiangning else 22340f1702c5SYu Xiangning pkti->ipi6_addr = ipv6_all_zeros; 22350f1702c5SYu Xiangning return (sizeof (struct in6_pktinfo)); 22360f1702c5SYu Xiangning } 22370f1702c5SYu Xiangning case IPV6_TCLASS: 22380f1702c5SYu Xiangning if (ipp->ipp_fields & IPPF_TCLASS) 22390f1702c5SYu Xiangning *i1 = ipp->ipp_tclass; 22400f1702c5SYu Xiangning else 22410f1702c5SYu Xiangning *i1 = IPV6_FLOW_TCLASS( 22420f1702c5SYu Xiangning IPV6_DEFAULT_VERS_AND_FLOW); 22430f1702c5SYu Xiangning break; /* goto sizeof (int) option return */ 22440f1702c5SYu Xiangning case IPV6_NEXTHOP: { 22450f1702c5SYu Xiangning sin6_t *sin6 = (sin6_t *)ptr; 22460f1702c5SYu Xiangning 22470f1702c5SYu Xiangning if (!(ipp->ipp_fields & IPPF_NEXTHOP)) 22480f1702c5SYu Xiangning return (0); 22490f1702c5SYu Xiangning *sin6 = sin6_null; 22500f1702c5SYu Xiangning sin6->sin6_family = AF_INET6; 22510f1702c5SYu Xiangning sin6->sin6_addr = ipp->ipp_nexthop; 22520f1702c5SYu Xiangning return (sizeof (sin6_t)); 22530f1702c5SYu Xiangning } 22540f1702c5SYu Xiangning case IPV6_HOPOPTS: 22550f1702c5SYu Xiangning if (!(ipp->ipp_fields & IPPF_HOPOPTS)) 22560f1702c5SYu Xiangning return (0); 22570f1702c5SYu Xiangning if (ipp->ipp_hopoptslen <= udp->udp_label_len_v6) 22580f1702c5SYu Xiangning return (0); 22590f1702c5SYu Xiangning /* 22600f1702c5SYu Xiangning * The cipso/label option is added by kernel. 22610f1702c5SYu Xiangning * User is not usually aware of this option. 22620f1702c5SYu Xiangning * We copy out the hbh opt after the label option. 22630f1702c5SYu Xiangning */ 22640f1702c5SYu Xiangning bcopy((char *)ipp->ipp_hopopts + udp->udp_label_len_v6, 22650f1702c5SYu Xiangning ptr, ipp->ipp_hopoptslen - udp->udp_label_len_v6); 22660f1702c5SYu Xiangning if (udp->udp_label_len_v6 > 0) { 22670f1702c5SYu Xiangning ptr[0] = ((char *)ipp->ipp_hopopts)[0]; 22680f1702c5SYu Xiangning ptr[1] = (ipp->ipp_hopoptslen - 22690f1702c5SYu Xiangning udp->udp_label_len_v6 + 7) / 8 - 1; 22700f1702c5SYu Xiangning } 22710f1702c5SYu Xiangning return (ipp->ipp_hopoptslen - udp->udp_label_len_v6); 22720f1702c5SYu Xiangning case IPV6_RTHDRDSTOPTS: 22730f1702c5SYu Xiangning if (!(ipp->ipp_fields & IPPF_RTDSTOPTS)) 22740f1702c5SYu Xiangning return (0); 22750f1702c5SYu Xiangning bcopy(ipp->ipp_rtdstopts, ptr, ipp->ipp_rtdstoptslen); 22760f1702c5SYu Xiangning return (ipp->ipp_rtdstoptslen); 22770f1702c5SYu Xiangning case IPV6_RTHDR: 22780f1702c5SYu Xiangning if (!(ipp->ipp_fields & IPPF_RTHDR)) 22790f1702c5SYu Xiangning return (0); 22800f1702c5SYu Xiangning bcopy(ipp->ipp_rthdr, ptr, ipp->ipp_rthdrlen); 22810f1702c5SYu Xiangning return (ipp->ipp_rthdrlen); 22820f1702c5SYu Xiangning case IPV6_DSTOPTS: 22830f1702c5SYu Xiangning if (!(ipp->ipp_fields & IPPF_DSTOPTS)) 22840f1702c5SYu Xiangning return (0); 22850f1702c5SYu Xiangning bcopy(ipp->ipp_dstopts, ptr, ipp->ipp_dstoptslen); 22860f1702c5SYu Xiangning return (ipp->ipp_dstoptslen); 22870f1702c5SYu Xiangning case IPV6_PATHMTU: 22880f1702c5SYu Xiangning return (ip_fill_mtuinfo(&udp->udp_v6dst, 22890f1702c5SYu Xiangning udp->udp_dstport, (struct ip6_mtuinfo *)ptr, 22900f1702c5SYu Xiangning us->us_netstack)); 22910f1702c5SYu Xiangning default: 22920f1702c5SYu Xiangning return (-1); 22930f1702c5SYu Xiangning } 22940f1702c5SYu Xiangning break; 22950f1702c5SYu Xiangning case IPPROTO_UDP: 22960f1702c5SYu Xiangning switch (name) { 22970f1702c5SYu Xiangning case UDP_ANONPRIVBIND: 22980f1702c5SYu Xiangning *i1 = udp->udp_anon_priv_bind; 22990f1702c5SYu Xiangning break; 23000f1702c5SYu Xiangning case UDP_EXCLBIND: 23010f1702c5SYu Xiangning *i1 = udp->udp_exclbind ? UDP_EXCLBIND : 0; 23020f1702c5SYu Xiangning break; 23030f1702c5SYu Xiangning case UDP_RCVHDR: 23040f1702c5SYu Xiangning *i1 = udp->udp_rcvhdr ? 1 : 0; 23050f1702c5SYu Xiangning break; 23060f1702c5SYu Xiangning case UDP_NAT_T_ENDPOINT: 23070f1702c5SYu Xiangning *i1 = udp->udp_nat_t_endpoint; 23080f1702c5SYu Xiangning break; 23090f1702c5SYu Xiangning default: 23100f1702c5SYu Xiangning return (-1); 23110f1702c5SYu Xiangning } 23120f1702c5SYu Xiangning break; 23130f1702c5SYu Xiangning default: 23140f1702c5SYu Xiangning return (-1); 23150f1702c5SYu Xiangning } 23160f1702c5SYu Xiangning return (sizeof (int)); 23170f1702c5SYu Xiangning } 23180f1702c5SYu Xiangning 23190f1702c5SYu Xiangning int 23200f1702c5SYu Xiangning udp_tpi_opt_get(queue_t *q, t_scalar_t level, t_scalar_t name, uchar_t *ptr) 23210f1702c5SYu Xiangning { 23220f1702c5SYu Xiangning udp_t *udp; 23230f1702c5SYu Xiangning int err; 23240f1702c5SYu Xiangning 23250f1702c5SYu Xiangning udp = Q_TO_UDP(q); 23260f1702c5SYu Xiangning 23270f1702c5SYu Xiangning rw_enter(&udp->udp_rwlock, RW_READER); 23280f1702c5SYu Xiangning err = udp_opt_get(Q_TO_CONN(q), level, name, ptr); 23290f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 23300f1702c5SYu Xiangning return (err); 23310f1702c5SYu Xiangning } 23320f1702c5SYu Xiangning 23330f1702c5SYu Xiangning /* 23340f1702c5SYu Xiangning * This routine sets socket options. 23350f1702c5SYu Xiangning */ 23360f1702c5SYu Xiangning /* ARGSUSED */ 23370f1702c5SYu Xiangning static int 23380f1702c5SYu Xiangning udp_do_opt_set(conn_t *connp, int level, int name, uint_t inlen, 23390f1702c5SYu Xiangning uchar_t *invalp, uint_t *outlenp, uchar_t *outvalp, cred_t *cr, 23400f1702c5SYu Xiangning void *thisdg_attrs, boolean_t checkonly) 23410f1702c5SYu Xiangning { 23420f1702c5SYu Xiangning udpattrs_t *attrs = thisdg_attrs; 23430f1702c5SYu Xiangning int *i1 = (int *)invalp; 23440f1702c5SYu Xiangning boolean_t onoff = (*i1 == 0) ? 0 : 1; 23450f1702c5SYu Xiangning udp_t *udp = connp->conn_udp; 23460f1702c5SYu Xiangning udp_stack_t *us = udp->udp_us; 23470f1702c5SYu Xiangning int error; 23480f1702c5SYu Xiangning uint_t newlen; 23490f1702c5SYu Xiangning size_t sth_wroff; 23500f1702c5SYu Xiangning 23510f1702c5SYu Xiangning ASSERT(RW_WRITE_HELD(&udp->udp_rwlock)); 23520f1702c5SYu Xiangning /* 23530f1702c5SYu Xiangning * For fixed length options, no sanity check 23540f1702c5SYu Xiangning * of passed in length is done. It is assumed *_optcom_req() 23550f1702c5SYu Xiangning * routines do the right thing. 23560f1702c5SYu Xiangning */ 23570f1702c5SYu Xiangning switch (level) { 23580f1702c5SYu Xiangning case SOL_SOCKET: 23590f1702c5SYu Xiangning switch (name) { 23600f1702c5SYu Xiangning case SO_REUSEADDR: 23610f1702c5SYu Xiangning if (!checkonly) { 23620f1702c5SYu Xiangning udp->udp_reuseaddr = onoff; 23630f1702c5SYu Xiangning PASS_OPT_TO_IP(connp); 23640f1702c5SYu Xiangning } 23650f1702c5SYu Xiangning break; 23660f1702c5SYu Xiangning case SO_DEBUG: 23670f1702c5SYu Xiangning if (!checkonly) 23680f1702c5SYu Xiangning udp->udp_debug = onoff; 23690f1702c5SYu Xiangning break; 23700f1702c5SYu Xiangning /* 23710f1702c5SYu Xiangning * The following three items are available here, 23720f1702c5SYu Xiangning * but are only meaningful to IP. 23730f1702c5SYu Xiangning */ 23740f1702c5SYu Xiangning case SO_DONTROUTE: 23750f1702c5SYu Xiangning if (!checkonly) { 23760f1702c5SYu Xiangning udp->udp_dontroute = onoff; 23770f1702c5SYu Xiangning PASS_OPT_TO_IP(connp); 23780f1702c5SYu Xiangning } 23790f1702c5SYu Xiangning break; 23800f1702c5SYu Xiangning case SO_USELOOPBACK: 23810f1702c5SYu Xiangning if (!checkonly) { 23820f1702c5SYu Xiangning udp->udp_useloopback = onoff; 23830f1702c5SYu Xiangning PASS_OPT_TO_IP(connp); 23840f1702c5SYu Xiangning } 23850f1702c5SYu Xiangning break; 23860f1702c5SYu Xiangning case SO_BROADCAST: 23870f1702c5SYu Xiangning if (!checkonly) { 23880f1702c5SYu Xiangning udp->udp_broadcast = onoff; 23890f1702c5SYu Xiangning PASS_OPT_TO_IP(connp); 23900f1702c5SYu Xiangning } 23910f1702c5SYu Xiangning break; 23920f1702c5SYu Xiangning 23930f1702c5SYu Xiangning case SO_SNDBUF: 23940f1702c5SYu Xiangning if (*i1 > us->us_max_buf) { 23950f1702c5SYu Xiangning *outlenp = 0; 23960f1702c5SYu Xiangning return (ENOBUFS); 23970f1702c5SYu Xiangning } 23980f1702c5SYu Xiangning if (!checkonly) { 23990f1702c5SYu Xiangning udp->udp_xmit_hiwat = *i1; 24000f1702c5SYu Xiangning connp->conn_wq->q_hiwat = *i1; 24010f1702c5SYu Xiangning } 24020f1702c5SYu Xiangning break; 24030f1702c5SYu Xiangning case SO_RCVBUF: 24040f1702c5SYu Xiangning if (*i1 > us->us_max_buf) { 24050f1702c5SYu Xiangning *outlenp = 0; 24060f1702c5SYu Xiangning return (ENOBUFS); 24070f1702c5SYu Xiangning } 24080f1702c5SYu Xiangning if (!checkonly) { 24090f1702c5SYu Xiangning int size; 24100f1702c5SYu Xiangning 24110f1702c5SYu Xiangning udp->udp_rcv_disply_hiwat = *i1; 24120f1702c5SYu Xiangning size = udp_set_rcv_hiwat(udp, *i1); 24130f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 24140f1702c5SYu Xiangning (void) proto_set_rx_hiwat(connp->conn_rq, connp, 24150f1702c5SYu Xiangning size); 24160f1702c5SYu Xiangning rw_enter(&udp->udp_rwlock, RW_WRITER); 24170f1702c5SYu Xiangning } 24180f1702c5SYu Xiangning break; 24190f1702c5SYu Xiangning case SO_DGRAM_ERRIND: 24200f1702c5SYu Xiangning if (!checkonly) 24210f1702c5SYu Xiangning udp->udp_dgram_errind = onoff; 24220f1702c5SYu Xiangning break; 24230f1702c5SYu Xiangning case SO_RECVUCRED: 24240f1702c5SYu Xiangning if (!checkonly) 24250f1702c5SYu Xiangning udp->udp_recvucred = onoff; 24260f1702c5SYu Xiangning break; 24270f1702c5SYu Xiangning case SO_ALLZONES: 24280f1702c5SYu Xiangning /* 24290f1702c5SYu Xiangning * "soft" error (negative) 24300f1702c5SYu Xiangning * option not handled at this level 24310f1702c5SYu Xiangning * Do not modify *outlenp. 24320f1702c5SYu Xiangning */ 24330f1702c5SYu Xiangning return (-EINVAL); 24340f1702c5SYu Xiangning case SO_TIMESTAMP: 24350f1702c5SYu Xiangning if (!checkonly) 24360f1702c5SYu Xiangning udp->udp_timestamp = onoff; 24370f1702c5SYu Xiangning break; 24380f1702c5SYu Xiangning case SO_ANON_MLP: 24390f1702c5SYu Xiangning if (!checkonly) { 24400f1702c5SYu Xiangning connp->conn_anon_mlp = onoff; 24410f1702c5SYu Xiangning PASS_OPT_TO_IP(connp); 24420f1702c5SYu Xiangning } 24430f1702c5SYu Xiangning break; 24440f1702c5SYu Xiangning case SO_MAC_EXEMPT: 24450f1702c5SYu Xiangning if (secpolicy_net_mac_aware(cr) != 0 || 24460f1702c5SYu Xiangning udp->udp_state != TS_UNBND) 24470f1702c5SYu Xiangning return (EACCES); 24480f1702c5SYu Xiangning if (!checkonly) { 24490f1702c5SYu Xiangning connp->conn_mac_exempt = onoff; 24500f1702c5SYu Xiangning PASS_OPT_TO_IP(connp); 24510f1702c5SYu Xiangning } 24520f1702c5SYu Xiangning break; 24530f1702c5SYu Xiangning case SCM_UCRED: { 24540f1702c5SYu Xiangning struct ucred_s *ucr; 24550f1702c5SYu Xiangning cred_t *cr, *newcr; 24560f1702c5SYu Xiangning ts_label_t *tsl; 24570f1702c5SYu Xiangning 24580f1702c5SYu Xiangning /* 24590f1702c5SYu Xiangning * Only sockets that have proper privileges and are 24600f1702c5SYu Xiangning * bound to MLPs will have any other value here, so 24610f1702c5SYu Xiangning * this implicitly tests for privilege to set label. 24620f1702c5SYu Xiangning */ 24630f1702c5SYu Xiangning if (connp->conn_mlp_type == mlptSingle) 24640f1702c5SYu Xiangning break; 24650f1702c5SYu Xiangning ucr = (struct ucred_s *)invalp; 24660f1702c5SYu Xiangning if (inlen != ucredsize || 24670f1702c5SYu Xiangning ucr->uc_labeloff < sizeof (*ucr) || 24680f1702c5SYu Xiangning ucr->uc_labeloff + sizeof (bslabel_t) > inlen) 24690f1702c5SYu Xiangning return (EINVAL); 24700f1702c5SYu Xiangning if (!checkonly) { 24710f1702c5SYu Xiangning mblk_t *mb; 24720f1702c5SYu Xiangning 24730f1702c5SYu Xiangning if (attrs == NULL || 24740f1702c5SYu Xiangning (mb = attrs->udpattr_mb) == NULL) 24750f1702c5SYu Xiangning return (EINVAL); 24760f1702c5SYu Xiangning if ((cr = DB_CRED(mb)) == NULL) 24770f1702c5SYu Xiangning cr = udp->udp_connp->conn_cred; 24780f1702c5SYu Xiangning ASSERT(cr != NULL); 24790f1702c5SYu Xiangning if ((tsl = crgetlabel(cr)) == NULL) 24800f1702c5SYu Xiangning return (EINVAL); 24810f1702c5SYu Xiangning newcr = copycred_from_bslabel(cr, UCLABEL(ucr), 24820f1702c5SYu Xiangning tsl->tsl_doi, KM_NOSLEEP); 24830f1702c5SYu Xiangning if (newcr == NULL) 24840f1702c5SYu Xiangning return (ENOSR); 24850f1702c5SYu Xiangning mblk_setcred(mb, newcr); 24860f1702c5SYu Xiangning attrs->udpattr_credset = B_TRUE; 24870f1702c5SYu Xiangning crfree(newcr); 24880f1702c5SYu Xiangning } 24890f1702c5SYu Xiangning break; 24900f1702c5SYu Xiangning } 24910f1702c5SYu Xiangning case SO_EXCLBIND: 24920f1702c5SYu Xiangning if (!checkonly) 24930f1702c5SYu Xiangning udp->udp_exclbind = onoff; 24940f1702c5SYu Xiangning break; 24950f1702c5SYu Xiangning default: 24960f1702c5SYu Xiangning *outlenp = 0; 24970f1702c5SYu Xiangning return (EINVAL); 24980f1702c5SYu Xiangning } 24990f1702c5SYu Xiangning break; 25000f1702c5SYu Xiangning case IPPROTO_IP: 25010f1702c5SYu Xiangning if (udp->udp_family != AF_INET) { 25020f1702c5SYu Xiangning *outlenp = 0; 25030f1702c5SYu Xiangning return (ENOPROTOOPT); 25040f1702c5SYu Xiangning } 25050f1702c5SYu Xiangning switch (name) { 25060f1702c5SYu Xiangning case IP_OPTIONS: 25070f1702c5SYu Xiangning case T_IP_OPTIONS: 25080f1702c5SYu Xiangning /* Save options for use by IP. */ 25090f1702c5SYu Xiangning newlen = inlen + udp->udp_label_len; 25100f1702c5SYu Xiangning if ((inlen & 0x3) || newlen > IP_MAX_OPT_LENGTH) { 25110f1702c5SYu Xiangning *outlenp = 0; 25120f1702c5SYu Xiangning return (EINVAL); 25130f1702c5SYu Xiangning } 25140f1702c5SYu Xiangning if (checkonly) 25150f1702c5SYu Xiangning break; 25160f1702c5SYu Xiangning 25170f1702c5SYu Xiangning /* 25180f1702c5SYu Xiangning * Update the stored options taking into account 25190f1702c5SYu Xiangning * any CIPSO option which we should not overwrite. 25200f1702c5SYu Xiangning */ 25210f1702c5SYu Xiangning if (!tsol_option_set(&udp->udp_ip_snd_options, 25220f1702c5SYu Xiangning &udp->udp_ip_snd_options_len, 25230f1702c5SYu Xiangning udp->udp_label_len, invalp, inlen)) { 25240f1702c5SYu Xiangning *outlenp = 0; 25250f1702c5SYu Xiangning return (ENOMEM); 25260f1702c5SYu Xiangning } 25270f1702c5SYu Xiangning 25280f1702c5SYu Xiangning udp->udp_max_hdr_len = IP_SIMPLE_HDR_LENGTH + 25290f1702c5SYu Xiangning UDPH_SIZE + udp->udp_ip_snd_options_len; 25300f1702c5SYu Xiangning sth_wroff = udp->udp_max_hdr_len + us->us_wroff_extra; 25310f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 25320f1702c5SYu Xiangning (void) proto_set_tx_wroff(connp->conn_rq, connp, 25330f1702c5SYu Xiangning sth_wroff); 25340f1702c5SYu Xiangning rw_enter(&udp->udp_rwlock, RW_WRITER); 25350f1702c5SYu Xiangning break; 25360f1702c5SYu Xiangning 25370f1702c5SYu Xiangning case IP_TTL: 25380f1702c5SYu Xiangning if (!checkonly) { 25390f1702c5SYu Xiangning udp->udp_ttl = (uchar_t)*i1; 25400f1702c5SYu Xiangning } 25410f1702c5SYu Xiangning break; 25420f1702c5SYu Xiangning case IP_TOS: 25430f1702c5SYu Xiangning case T_IP_TOS: 25440f1702c5SYu Xiangning if (!checkonly) { 25450f1702c5SYu Xiangning udp->udp_type_of_service = (uchar_t)*i1; 25460f1702c5SYu Xiangning } 25470f1702c5SYu Xiangning break; 25480f1702c5SYu Xiangning case IP_MULTICAST_IF: { 25490f1702c5SYu Xiangning /* 25500f1702c5SYu Xiangning * TODO should check OPTMGMT reply and undo this if 25510f1702c5SYu Xiangning * there is an error. 25520f1702c5SYu Xiangning */ 25530f1702c5SYu Xiangning struct in_addr *inap = (struct in_addr *)invalp; 25540f1702c5SYu Xiangning if (!checkonly) { 25550f1702c5SYu Xiangning udp->udp_multicast_if_addr = 25560f1702c5SYu Xiangning inap->s_addr; 25570f1702c5SYu Xiangning PASS_OPT_TO_IP(connp); 25580f1702c5SYu Xiangning } 25590f1702c5SYu Xiangning break; 25600f1702c5SYu Xiangning } 25610f1702c5SYu Xiangning case IP_MULTICAST_TTL: 25620f1702c5SYu Xiangning if (!checkonly) 25630f1702c5SYu Xiangning udp->udp_multicast_ttl = *invalp; 25640f1702c5SYu Xiangning break; 25650f1702c5SYu Xiangning case IP_MULTICAST_LOOP: 25660f1702c5SYu Xiangning if (!checkonly) { 25670f1702c5SYu Xiangning connp->conn_multicast_loop = *invalp; 25680f1702c5SYu Xiangning PASS_OPT_TO_IP(connp); 25690f1702c5SYu Xiangning } 25700f1702c5SYu Xiangning break; 25710f1702c5SYu Xiangning case IP_RECVOPTS: 25720f1702c5SYu Xiangning if (!checkonly) 25730f1702c5SYu Xiangning udp->udp_recvopts = onoff; 25740f1702c5SYu Xiangning break; 25750f1702c5SYu Xiangning case IP_RECVDSTADDR: 25760f1702c5SYu Xiangning if (!checkonly) 25770f1702c5SYu Xiangning udp->udp_recvdstaddr = onoff; 25780f1702c5SYu Xiangning break; 25790f1702c5SYu Xiangning case IP_RECVIF: 25800f1702c5SYu Xiangning if (!checkonly) { 25810f1702c5SYu Xiangning udp->udp_recvif = onoff; 25820f1702c5SYu Xiangning PASS_OPT_TO_IP(connp); 25830f1702c5SYu Xiangning } 25840f1702c5SYu Xiangning break; 25850f1702c5SYu Xiangning case IP_RECVSLLA: 25860f1702c5SYu Xiangning if (!checkonly) { 25870f1702c5SYu Xiangning udp->udp_recvslla = onoff; 25880f1702c5SYu Xiangning PASS_OPT_TO_IP(connp); 25890f1702c5SYu Xiangning } 25900f1702c5SYu Xiangning break; 25910f1702c5SYu Xiangning case IP_RECVTTL: 25920f1702c5SYu Xiangning if (!checkonly) 25930f1702c5SYu Xiangning udp->udp_recvttl = onoff; 25940f1702c5SYu Xiangning break; 25950f1702c5SYu Xiangning case IP_PKTINFO: { 25960f1702c5SYu Xiangning /* 25970f1702c5SYu Xiangning * This also handles IP_RECVPKTINFO. 25980f1702c5SYu Xiangning * IP_PKTINFO and IP_RECVPKTINFO have same value. 25990f1702c5SYu Xiangning * Differentiation is based on the size of the 26000f1702c5SYu Xiangning * argument passed in. 26010f1702c5SYu Xiangning */ 26020f1702c5SYu Xiangning struct in_pktinfo *pktinfop; 26030f1702c5SYu Xiangning ip4_pkt_t *attr_pktinfop; 26040f1702c5SYu Xiangning 26050f1702c5SYu Xiangning if (checkonly) 26060f1702c5SYu Xiangning break; 26070f1702c5SYu Xiangning 26080f1702c5SYu Xiangning if (inlen == sizeof (int)) { 26090f1702c5SYu Xiangning /* 26100f1702c5SYu Xiangning * This is IP_RECVPKTINFO option. 26110f1702c5SYu Xiangning * Keep a local copy of whether this option is 26120f1702c5SYu Xiangning * set or not and pass it down to IP for 26130f1702c5SYu Xiangning * processing. 26147c478bd9Sstevel@tonic-gate */ 26150f1702c5SYu Xiangning 26160f1702c5SYu Xiangning udp->udp_ip_recvpktinfo = onoff; 26170f1702c5SYu Xiangning return (-EINVAL); 26180f1702c5SYu Xiangning } 26190f1702c5SYu Xiangning 26200f1702c5SYu Xiangning if (attrs == NULL || 26210f1702c5SYu Xiangning (attr_pktinfop = attrs->udpattr_ipp4) == NULL) { 26220f1702c5SYu Xiangning /* 26230f1702c5SYu Xiangning * sticky option or no buffer to return 26240f1702c5SYu Xiangning * the results. 26250f1702c5SYu Xiangning */ 26260f1702c5SYu Xiangning return (EINVAL); 26270f1702c5SYu Xiangning } 26280f1702c5SYu Xiangning 26290f1702c5SYu Xiangning if (inlen != sizeof (struct in_pktinfo)) 26300f1702c5SYu Xiangning return (EINVAL); 26310f1702c5SYu Xiangning 26320f1702c5SYu Xiangning pktinfop = (struct in_pktinfo *)invalp; 26330f1702c5SYu Xiangning 26340f1702c5SYu Xiangning /* 26350f1702c5SYu Xiangning * At least one of the values should be specified 26360f1702c5SYu Xiangning */ 26370f1702c5SYu Xiangning if (pktinfop->ipi_ifindex == 0 && 26380f1702c5SYu Xiangning pktinfop->ipi_spec_dst.s_addr == INADDR_ANY) { 26390f1702c5SYu Xiangning return (EINVAL); 26400f1702c5SYu Xiangning } 26410f1702c5SYu Xiangning 26420f1702c5SYu Xiangning attr_pktinfop->ip4_addr = pktinfop->ipi_spec_dst.s_addr; 26430f1702c5SYu Xiangning attr_pktinfop->ip4_ill_index = pktinfop->ipi_ifindex; 26440f1702c5SYu Xiangning 26450f1702c5SYu Xiangning break; 26460f1702c5SYu Xiangning } 26470f1702c5SYu Xiangning case IP_ADD_MEMBERSHIP: 26480f1702c5SYu Xiangning case IP_DROP_MEMBERSHIP: 26490f1702c5SYu Xiangning case IP_BLOCK_SOURCE: 26500f1702c5SYu Xiangning case IP_UNBLOCK_SOURCE: 26510f1702c5SYu Xiangning case IP_ADD_SOURCE_MEMBERSHIP: 26520f1702c5SYu Xiangning case IP_DROP_SOURCE_MEMBERSHIP: 26530f1702c5SYu Xiangning case MCAST_JOIN_GROUP: 26540f1702c5SYu Xiangning case MCAST_LEAVE_GROUP: 26550f1702c5SYu Xiangning case MCAST_BLOCK_SOURCE: 26560f1702c5SYu Xiangning case MCAST_UNBLOCK_SOURCE: 26570f1702c5SYu Xiangning case MCAST_JOIN_SOURCE_GROUP: 26580f1702c5SYu Xiangning case MCAST_LEAVE_SOURCE_GROUP: 26590f1702c5SYu Xiangning case IP_SEC_OPT: 26600f1702c5SYu Xiangning case IP_NEXTHOP: 26610f1702c5SYu Xiangning case IP_DHCPINIT_IF: 26620f1702c5SYu Xiangning /* 26630f1702c5SYu Xiangning * "soft" error (negative) 26640f1702c5SYu Xiangning * option not handled at this level 26650f1702c5SYu Xiangning * Do not modify *outlenp. 26660f1702c5SYu Xiangning */ 26670f1702c5SYu Xiangning return (-EINVAL); 26680f1702c5SYu Xiangning case IP_BOUND_IF: 26690f1702c5SYu Xiangning if (!checkonly) { 26700f1702c5SYu Xiangning udp->udp_bound_if = *i1; 26710f1702c5SYu Xiangning PASS_OPT_TO_IP(connp); 26720f1702c5SYu Xiangning } 26730f1702c5SYu Xiangning break; 26740f1702c5SYu Xiangning case IP_UNSPEC_SRC: 26750f1702c5SYu Xiangning if (!checkonly) { 26760f1702c5SYu Xiangning udp->udp_unspec_source = onoff; 26770f1702c5SYu Xiangning PASS_OPT_TO_IP(connp); 26780f1702c5SYu Xiangning } 26790f1702c5SYu Xiangning break; 26800f1702c5SYu Xiangning case IP_BROADCAST_TTL: 26810f1702c5SYu Xiangning if (!checkonly) 26820f1702c5SYu Xiangning connp->conn_broadcast_ttl = *invalp; 26830f1702c5SYu Xiangning break; 26840f1702c5SYu Xiangning default: 26850f1702c5SYu Xiangning *outlenp = 0; 26860f1702c5SYu Xiangning return (EINVAL); 26870f1702c5SYu Xiangning } 26880f1702c5SYu Xiangning break; 26890f1702c5SYu Xiangning case IPPROTO_IPV6: { 26900f1702c5SYu Xiangning ip6_pkt_t *ipp; 26910f1702c5SYu Xiangning boolean_t sticky; 26920f1702c5SYu Xiangning 26930f1702c5SYu Xiangning if (udp->udp_family != AF_INET6) { 26940f1702c5SYu Xiangning *outlenp = 0; 26950f1702c5SYu Xiangning return (ENOPROTOOPT); 26960f1702c5SYu Xiangning } 26970f1702c5SYu Xiangning /* 26980f1702c5SYu Xiangning * Deal with both sticky options and ancillary data 26990f1702c5SYu Xiangning */ 27000f1702c5SYu Xiangning sticky = B_FALSE; 27010f1702c5SYu Xiangning if (attrs == NULL || (ipp = attrs->udpattr_ipp6) == 27020f1702c5SYu Xiangning NULL) { 27030f1702c5SYu Xiangning /* sticky options, or none */ 27040f1702c5SYu Xiangning ipp = &udp->udp_sticky_ipp; 27050f1702c5SYu Xiangning sticky = B_TRUE; 27060f1702c5SYu Xiangning } 27070f1702c5SYu Xiangning 27080f1702c5SYu Xiangning switch (name) { 27090f1702c5SYu Xiangning case IPV6_MULTICAST_IF: 27100f1702c5SYu Xiangning if (!checkonly) { 27110f1702c5SYu Xiangning udp->udp_multicast_if_index = *i1; 27120f1702c5SYu Xiangning PASS_OPT_TO_IP(connp); 27130f1702c5SYu Xiangning } 27140f1702c5SYu Xiangning break; 27150f1702c5SYu Xiangning case IPV6_UNICAST_HOPS: 27160f1702c5SYu Xiangning /* -1 means use default */ 27170f1702c5SYu Xiangning if (*i1 < -1 || *i1 > IPV6_MAX_HOPS) { 27180f1702c5SYu Xiangning *outlenp = 0; 27190f1702c5SYu Xiangning return (EINVAL); 27200f1702c5SYu Xiangning } 27210f1702c5SYu Xiangning if (!checkonly) { 27220f1702c5SYu Xiangning if (*i1 == -1) { 27230f1702c5SYu Xiangning udp->udp_ttl = ipp->ipp_unicast_hops = 27240f1702c5SYu Xiangning us->us_ipv6_hoplimit; 27250f1702c5SYu Xiangning ipp->ipp_fields &= ~IPPF_UNICAST_HOPS; 27260f1702c5SYu Xiangning /* Pass modified value to IP. */ 27270f1702c5SYu Xiangning *i1 = udp->udp_ttl; 27280f1702c5SYu Xiangning } else { 27290f1702c5SYu Xiangning udp->udp_ttl = ipp->ipp_unicast_hops = 27300f1702c5SYu Xiangning (uint8_t)*i1; 27310f1702c5SYu Xiangning ipp->ipp_fields |= IPPF_UNICAST_HOPS; 27320f1702c5SYu Xiangning } 27330f1702c5SYu Xiangning /* Rebuild the header template */ 27340f1702c5SYu Xiangning error = udp_build_hdrs(udp); 27350f1702c5SYu Xiangning if (error != 0) { 27360f1702c5SYu Xiangning *outlenp = 0; 27370f1702c5SYu Xiangning return (error); 27380f1702c5SYu Xiangning } 27390f1702c5SYu Xiangning } 27400f1702c5SYu Xiangning break; 27410f1702c5SYu Xiangning case IPV6_MULTICAST_HOPS: 27420f1702c5SYu Xiangning /* -1 means use default */ 27430f1702c5SYu Xiangning if (*i1 < -1 || *i1 > IPV6_MAX_HOPS) { 27440f1702c5SYu Xiangning *outlenp = 0; 27450f1702c5SYu Xiangning return (EINVAL); 27460f1702c5SYu Xiangning } 27470f1702c5SYu Xiangning if (!checkonly) { 27480f1702c5SYu Xiangning if (*i1 == -1) { 27490f1702c5SYu Xiangning udp->udp_multicast_ttl = 27500f1702c5SYu Xiangning ipp->ipp_multicast_hops = 27510f1702c5SYu Xiangning IP_DEFAULT_MULTICAST_TTL; 27520f1702c5SYu Xiangning ipp->ipp_fields &= ~IPPF_MULTICAST_HOPS; 27530f1702c5SYu Xiangning /* Pass modified value to IP. */ 27540f1702c5SYu Xiangning *i1 = udp->udp_multicast_ttl; 27550f1702c5SYu Xiangning } else { 27560f1702c5SYu Xiangning udp->udp_multicast_ttl = 27570f1702c5SYu Xiangning ipp->ipp_multicast_hops = 27580f1702c5SYu Xiangning (uint8_t)*i1; 27590f1702c5SYu Xiangning ipp->ipp_fields |= IPPF_MULTICAST_HOPS; 27600f1702c5SYu Xiangning } 27610f1702c5SYu Xiangning } 27620f1702c5SYu Xiangning break; 27630f1702c5SYu Xiangning case IPV6_MULTICAST_LOOP: 27640f1702c5SYu Xiangning if (*i1 != 0 && *i1 != 1) { 27650f1702c5SYu Xiangning *outlenp = 0; 27660f1702c5SYu Xiangning return (EINVAL); 27670f1702c5SYu Xiangning } 27680f1702c5SYu Xiangning if (!checkonly) { 27690f1702c5SYu Xiangning connp->conn_multicast_loop = *i1; 27700f1702c5SYu Xiangning PASS_OPT_TO_IP(connp); 27710f1702c5SYu Xiangning } 27720f1702c5SYu Xiangning break; 27730f1702c5SYu Xiangning case IPV6_JOIN_GROUP: 27740f1702c5SYu Xiangning case IPV6_LEAVE_GROUP: 27750f1702c5SYu Xiangning case MCAST_JOIN_GROUP: 27760f1702c5SYu Xiangning case MCAST_LEAVE_GROUP: 27770f1702c5SYu Xiangning case MCAST_BLOCK_SOURCE: 27780f1702c5SYu Xiangning case MCAST_UNBLOCK_SOURCE: 27790f1702c5SYu Xiangning case MCAST_JOIN_SOURCE_GROUP: 27800f1702c5SYu Xiangning case MCAST_LEAVE_SOURCE_GROUP: 27810f1702c5SYu Xiangning /* 27820f1702c5SYu Xiangning * "soft" error (negative) 27830f1702c5SYu Xiangning * option not handled at this level 27840f1702c5SYu Xiangning * Note: Do not modify *outlenp 27850f1702c5SYu Xiangning */ 27860f1702c5SYu Xiangning return (-EINVAL); 27870f1702c5SYu Xiangning case IPV6_BOUND_IF: 27880f1702c5SYu Xiangning if (!checkonly) { 27890f1702c5SYu Xiangning udp->udp_bound_if = *i1; 27900f1702c5SYu Xiangning PASS_OPT_TO_IP(connp); 27910f1702c5SYu Xiangning } 27920f1702c5SYu Xiangning break; 27930f1702c5SYu Xiangning case IPV6_UNSPEC_SRC: 27940f1702c5SYu Xiangning if (!checkonly) { 27950f1702c5SYu Xiangning udp->udp_unspec_source = onoff; 27960f1702c5SYu Xiangning PASS_OPT_TO_IP(connp); 27970f1702c5SYu Xiangning } 27980f1702c5SYu Xiangning break; 27990f1702c5SYu Xiangning /* 28000f1702c5SYu Xiangning * Set boolean switches for ancillary data delivery 28010f1702c5SYu Xiangning */ 28020f1702c5SYu Xiangning case IPV6_RECVPKTINFO: 28030f1702c5SYu Xiangning if (!checkonly) { 28040f1702c5SYu Xiangning udp->udp_ip_recvpktinfo = onoff; 28050f1702c5SYu Xiangning PASS_OPT_TO_IP(connp); 28060f1702c5SYu Xiangning } 28070f1702c5SYu Xiangning break; 28080f1702c5SYu Xiangning case IPV6_RECVTCLASS: 28090f1702c5SYu Xiangning if (!checkonly) { 28100f1702c5SYu Xiangning udp->udp_ipv6_recvtclass = onoff; 28110f1702c5SYu Xiangning PASS_OPT_TO_IP(connp); 28120f1702c5SYu Xiangning } 28130f1702c5SYu Xiangning break; 28140f1702c5SYu Xiangning case IPV6_RECVPATHMTU: 28150f1702c5SYu Xiangning if (!checkonly) { 28160f1702c5SYu Xiangning udp->udp_ipv6_recvpathmtu = onoff; 28170f1702c5SYu Xiangning PASS_OPT_TO_IP(connp); 28180f1702c5SYu Xiangning } 28190f1702c5SYu Xiangning break; 28200f1702c5SYu Xiangning case IPV6_RECVHOPLIMIT: 28210f1702c5SYu Xiangning if (!checkonly) { 28220f1702c5SYu Xiangning udp->udp_ipv6_recvhoplimit = onoff; 28230f1702c5SYu Xiangning PASS_OPT_TO_IP(connp); 28240f1702c5SYu Xiangning } 28250f1702c5SYu Xiangning break; 28260f1702c5SYu Xiangning case IPV6_RECVHOPOPTS: 28270f1702c5SYu Xiangning if (!checkonly) { 28280f1702c5SYu Xiangning udp->udp_ipv6_recvhopopts = onoff; 28290f1702c5SYu Xiangning PASS_OPT_TO_IP(connp); 28300f1702c5SYu Xiangning } 28310f1702c5SYu Xiangning break; 28320f1702c5SYu Xiangning case IPV6_RECVDSTOPTS: 28330f1702c5SYu Xiangning if (!checkonly) { 28340f1702c5SYu Xiangning udp->udp_ipv6_recvdstopts = onoff; 28350f1702c5SYu Xiangning PASS_OPT_TO_IP(connp); 28360f1702c5SYu Xiangning } 28370f1702c5SYu Xiangning break; 28380f1702c5SYu Xiangning case _OLD_IPV6_RECVDSTOPTS: 28390f1702c5SYu Xiangning if (!checkonly) 28400f1702c5SYu Xiangning udp->udp_old_ipv6_recvdstopts = onoff; 28410f1702c5SYu Xiangning break; 28420f1702c5SYu Xiangning case IPV6_RECVRTHDRDSTOPTS: 28430f1702c5SYu Xiangning if (!checkonly) { 28440f1702c5SYu Xiangning udp->udp_ipv6_recvrthdrdstopts = onoff; 28450f1702c5SYu Xiangning PASS_OPT_TO_IP(connp); 28460f1702c5SYu Xiangning } 28470f1702c5SYu Xiangning break; 28480f1702c5SYu Xiangning case IPV6_RECVRTHDR: 28490f1702c5SYu Xiangning if (!checkonly) { 28500f1702c5SYu Xiangning udp->udp_ipv6_recvrthdr = onoff; 28510f1702c5SYu Xiangning PASS_OPT_TO_IP(connp); 28527c478bd9Sstevel@tonic-gate } 28530f1702c5SYu Xiangning break; 28540f1702c5SYu Xiangning /* 28550f1702c5SYu Xiangning * Set sticky options or ancillary data. 28560f1702c5SYu Xiangning * If sticky options, (re)build any extension headers 28570f1702c5SYu Xiangning * that might be needed as a result. 28580f1702c5SYu Xiangning */ 28590f1702c5SYu Xiangning case IPV6_PKTINFO: 28600f1702c5SYu Xiangning /* 28610f1702c5SYu Xiangning * The source address and ifindex are verified 28620f1702c5SYu Xiangning * in ip_opt_set(). For ancillary data the 28630f1702c5SYu Xiangning * source address is checked in ip_wput_v6. 28640f1702c5SYu Xiangning */ 28650f1702c5SYu Xiangning if (inlen != 0 && inlen != sizeof (struct in6_pktinfo)) 28660f1702c5SYu Xiangning return (EINVAL); 28670f1702c5SYu Xiangning if (checkonly) 28680f1702c5SYu Xiangning break; 28690f1702c5SYu Xiangning 28700f1702c5SYu Xiangning if (inlen == 0) { 28710f1702c5SYu Xiangning ipp->ipp_fields &= ~(IPPF_IFINDEX|IPPF_ADDR); 28720f1702c5SYu Xiangning ipp->ipp_sticky_ignored |= 28730f1702c5SYu Xiangning (IPPF_IFINDEX|IPPF_ADDR); 28740f1702c5SYu Xiangning } else { 28750f1702c5SYu Xiangning struct in6_pktinfo *pkti; 28760f1702c5SYu Xiangning 28770f1702c5SYu Xiangning pkti = (struct in6_pktinfo *)invalp; 28780f1702c5SYu Xiangning ipp->ipp_ifindex = pkti->ipi6_ifindex; 28790f1702c5SYu Xiangning ipp->ipp_addr = pkti->ipi6_addr; 28800f1702c5SYu Xiangning if (ipp->ipp_ifindex != 0) 28810f1702c5SYu Xiangning ipp->ipp_fields |= IPPF_IFINDEX; 28820f1702c5SYu Xiangning else 28830f1702c5SYu Xiangning ipp->ipp_fields &= ~IPPF_IFINDEX; 28840f1702c5SYu Xiangning if (!IN6_IS_ADDR_UNSPECIFIED( 28850f1702c5SYu Xiangning &ipp->ipp_addr)) 28860f1702c5SYu Xiangning ipp->ipp_fields |= IPPF_ADDR; 28870f1702c5SYu Xiangning else 28880f1702c5SYu Xiangning ipp->ipp_fields &= ~IPPF_ADDR; 28890f1702c5SYu Xiangning } 28900f1702c5SYu Xiangning if (sticky) { 28910f1702c5SYu Xiangning error = udp_build_hdrs(udp); 28920f1702c5SYu Xiangning if (error != 0) 28930f1702c5SYu Xiangning return (error); 28940f1702c5SYu Xiangning PASS_OPT_TO_IP(connp); 28950f1702c5SYu Xiangning } 28960f1702c5SYu Xiangning break; 28970f1702c5SYu Xiangning case IPV6_HOPLIMIT: 28980f1702c5SYu Xiangning if (sticky) 28990f1702c5SYu Xiangning return (EINVAL); 29000f1702c5SYu Xiangning if (inlen != 0 && inlen != sizeof (int)) 29010f1702c5SYu Xiangning return (EINVAL); 29020f1702c5SYu Xiangning if (checkonly) 29030f1702c5SYu Xiangning break; 29040f1702c5SYu Xiangning 29050f1702c5SYu Xiangning if (inlen == 0) { 29060f1702c5SYu Xiangning ipp->ipp_fields &= ~IPPF_HOPLIMIT; 29070f1702c5SYu Xiangning ipp->ipp_sticky_ignored |= IPPF_HOPLIMIT; 29080f1702c5SYu Xiangning } else { 29090f1702c5SYu Xiangning if (*i1 > 255 || *i1 < -1) 29100f1702c5SYu Xiangning return (EINVAL); 29110f1702c5SYu Xiangning if (*i1 == -1) 29120f1702c5SYu Xiangning ipp->ipp_hoplimit = 29130f1702c5SYu Xiangning us->us_ipv6_hoplimit; 29140f1702c5SYu Xiangning else 29150f1702c5SYu Xiangning ipp->ipp_hoplimit = *i1; 29160f1702c5SYu Xiangning ipp->ipp_fields |= IPPF_HOPLIMIT; 29170f1702c5SYu Xiangning } 29180f1702c5SYu Xiangning break; 29190f1702c5SYu Xiangning case IPV6_TCLASS: 29200f1702c5SYu Xiangning if (inlen != 0 && inlen != sizeof (int)) 29210f1702c5SYu Xiangning return (EINVAL); 29220f1702c5SYu Xiangning if (checkonly) 29230f1702c5SYu Xiangning break; 29240f1702c5SYu Xiangning 29250f1702c5SYu Xiangning if (inlen == 0) { 29260f1702c5SYu Xiangning ipp->ipp_fields &= ~IPPF_TCLASS; 29270f1702c5SYu Xiangning ipp->ipp_sticky_ignored |= IPPF_TCLASS; 29280f1702c5SYu Xiangning } else { 29290f1702c5SYu Xiangning if (*i1 > 255 || *i1 < -1) 29300f1702c5SYu Xiangning return (EINVAL); 29310f1702c5SYu Xiangning if (*i1 == -1) 29320f1702c5SYu Xiangning ipp->ipp_tclass = 0; 29330f1702c5SYu Xiangning else 29340f1702c5SYu Xiangning ipp->ipp_tclass = *i1; 29350f1702c5SYu Xiangning ipp->ipp_fields |= IPPF_TCLASS; 29360f1702c5SYu Xiangning } 29370f1702c5SYu Xiangning if (sticky) { 29380f1702c5SYu Xiangning error = udp_build_hdrs(udp); 29390f1702c5SYu Xiangning if (error != 0) 29400f1702c5SYu Xiangning return (error); 29410f1702c5SYu Xiangning } 29420f1702c5SYu Xiangning break; 29430f1702c5SYu Xiangning case IPV6_NEXTHOP: 29440f1702c5SYu Xiangning /* 29450f1702c5SYu Xiangning * IP will verify that the nexthop is reachable 29460f1702c5SYu Xiangning * and fail for sticky options. 29470f1702c5SYu Xiangning */ 29480f1702c5SYu Xiangning if (inlen != 0 && inlen != sizeof (sin6_t)) 29490f1702c5SYu Xiangning return (EINVAL); 29500f1702c5SYu Xiangning if (checkonly) 29510f1702c5SYu Xiangning break; 29520f1702c5SYu Xiangning 29530f1702c5SYu Xiangning if (inlen == 0) { 29540f1702c5SYu Xiangning ipp->ipp_fields &= ~IPPF_NEXTHOP; 29550f1702c5SYu Xiangning ipp->ipp_sticky_ignored |= IPPF_NEXTHOP; 29560f1702c5SYu Xiangning } else { 29570f1702c5SYu Xiangning sin6_t *sin6 = (sin6_t *)invalp; 29580f1702c5SYu Xiangning 29590f1702c5SYu Xiangning if (sin6->sin6_family != AF_INET6) { 29600f1702c5SYu Xiangning return (EAFNOSUPPORT); 29610f1702c5SYu Xiangning } 29620f1702c5SYu Xiangning if (IN6_IS_ADDR_V4MAPPED( 29630f1702c5SYu Xiangning &sin6->sin6_addr)) 29640f1702c5SYu Xiangning return (EADDRNOTAVAIL); 29650f1702c5SYu Xiangning ipp->ipp_nexthop = sin6->sin6_addr; 29660f1702c5SYu Xiangning if (!IN6_IS_ADDR_UNSPECIFIED( 29670f1702c5SYu Xiangning &ipp->ipp_nexthop)) 29680f1702c5SYu Xiangning ipp->ipp_fields |= IPPF_NEXTHOP; 29690f1702c5SYu Xiangning else 29700f1702c5SYu Xiangning ipp->ipp_fields &= ~IPPF_NEXTHOP; 29710f1702c5SYu Xiangning } 29720f1702c5SYu Xiangning if (sticky) { 29730f1702c5SYu Xiangning error = udp_build_hdrs(udp); 29740f1702c5SYu Xiangning if (error != 0) 29750f1702c5SYu Xiangning return (error); 29760f1702c5SYu Xiangning PASS_OPT_TO_IP(connp); 29770f1702c5SYu Xiangning } 29780f1702c5SYu Xiangning break; 29790f1702c5SYu Xiangning case IPV6_HOPOPTS: { 29800f1702c5SYu Xiangning ip6_hbh_t *hopts = (ip6_hbh_t *)invalp; 29810f1702c5SYu Xiangning /* 29820f1702c5SYu Xiangning * Sanity checks - minimum size, size a multiple of 29830f1702c5SYu Xiangning * eight bytes, and matching size passed in. 29840f1702c5SYu Xiangning */ 29850f1702c5SYu Xiangning if (inlen != 0 && 29860f1702c5SYu Xiangning inlen != (8 * (hopts->ip6h_len + 1))) 29870f1702c5SYu Xiangning return (EINVAL); 29880f1702c5SYu Xiangning 29890f1702c5SYu Xiangning if (checkonly) 29900f1702c5SYu Xiangning break; 29910f1702c5SYu Xiangning 29920f1702c5SYu Xiangning error = optcom_pkt_set(invalp, inlen, sticky, 29930f1702c5SYu Xiangning (uchar_t **)&ipp->ipp_hopopts, 29940f1702c5SYu Xiangning &ipp->ipp_hopoptslen, 29950f1702c5SYu Xiangning sticky ? udp->udp_label_len_v6 : 0); 29960f1702c5SYu Xiangning if (error != 0) 29970f1702c5SYu Xiangning return (error); 29980f1702c5SYu Xiangning if (ipp->ipp_hopoptslen == 0) { 29990f1702c5SYu Xiangning ipp->ipp_fields &= ~IPPF_HOPOPTS; 30000f1702c5SYu Xiangning ipp->ipp_sticky_ignored |= IPPF_HOPOPTS; 30010f1702c5SYu Xiangning } else { 30020f1702c5SYu Xiangning ipp->ipp_fields |= IPPF_HOPOPTS; 30030f1702c5SYu Xiangning } 30040f1702c5SYu Xiangning if (sticky) { 30050f1702c5SYu Xiangning error = udp_build_hdrs(udp); 30060f1702c5SYu Xiangning if (error != 0) 30070f1702c5SYu Xiangning return (error); 30080f1702c5SYu Xiangning } 30090f1702c5SYu Xiangning break; 30100f1702c5SYu Xiangning } 30110f1702c5SYu Xiangning case IPV6_RTHDRDSTOPTS: { 30120f1702c5SYu Xiangning ip6_dest_t *dopts = (ip6_dest_t *)invalp; 30130f1702c5SYu Xiangning 30140f1702c5SYu Xiangning /* 30150f1702c5SYu Xiangning * Sanity checks - minimum size, size a multiple of 30160f1702c5SYu Xiangning * eight bytes, and matching size passed in. 30170f1702c5SYu Xiangning */ 30180f1702c5SYu Xiangning if (inlen != 0 && 30190f1702c5SYu Xiangning inlen != (8 * (dopts->ip6d_len + 1))) 30200f1702c5SYu Xiangning return (EINVAL); 30210f1702c5SYu Xiangning 30220f1702c5SYu Xiangning if (checkonly) 30230f1702c5SYu Xiangning break; 30240f1702c5SYu Xiangning 30250f1702c5SYu Xiangning if (inlen == 0) { 30260f1702c5SYu Xiangning if (sticky && 30270f1702c5SYu Xiangning (ipp->ipp_fields & IPPF_RTDSTOPTS) != 0) { 30280f1702c5SYu Xiangning kmem_free(ipp->ipp_rtdstopts, 30290f1702c5SYu Xiangning ipp->ipp_rtdstoptslen); 30300f1702c5SYu Xiangning ipp->ipp_rtdstopts = NULL; 30310f1702c5SYu Xiangning ipp->ipp_rtdstoptslen = 0; 30320f1702c5SYu Xiangning } 30330f1702c5SYu Xiangning 30340f1702c5SYu Xiangning ipp->ipp_fields &= ~IPPF_RTDSTOPTS; 30350f1702c5SYu Xiangning ipp->ipp_sticky_ignored |= IPPF_RTDSTOPTS; 30360f1702c5SYu Xiangning } else { 30370f1702c5SYu Xiangning error = optcom_pkt_set(invalp, inlen, sticky, 30380f1702c5SYu Xiangning (uchar_t **)&ipp->ipp_rtdstopts, 30390f1702c5SYu Xiangning &ipp->ipp_rtdstoptslen, 0); 30400f1702c5SYu Xiangning if (error != 0) 30410f1702c5SYu Xiangning return (error); 30420f1702c5SYu Xiangning ipp->ipp_fields |= IPPF_RTDSTOPTS; 30430f1702c5SYu Xiangning } 30440f1702c5SYu Xiangning if (sticky) { 30450f1702c5SYu Xiangning error = udp_build_hdrs(udp); 30460f1702c5SYu Xiangning if (error != 0) 30470f1702c5SYu Xiangning return (error); 30480f1702c5SYu Xiangning } 30490f1702c5SYu Xiangning break; 30500f1702c5SYu Xiangning } 30510f1702c5SYu Xiangning case IPV6_DSTOPTS: { 30520f1702c5SYu Xiangning ip6_dest_t *dopts = (ip6_dest_t *)invalp; 30530f1702c5SYu Xiangning 30540f1702c5SYu Xiangning /* 30550f1702c5SYu Xiangning * Sanity checks - minimum size, size a multiple of 30560f1702c5SYu Xiangning * eight bytes, and matching size passed in. 30570f1702c5SYu Xiangning */ 30580f1702c5SYu Xiangning if (inlen != 0 && 30590f1702c5SYu Xiangning inlen != (8 * (dopts->ip6d_len + 1))) 30600f1702c5SYu Xiangning return (EINVAL); 30610f1702c5SYu Xiangning 30620f1702c5SYu Xiangning if (checkonly) 30630f1702c5SYu Xiangning break; 30640f1702c5SYu Xiangning 30650f1702c5SYu Xiangning if (inlen == 0) { 30660f1702c5SYu Xiangning if (sticky && 30670f1702c5SYu Xiangning (ipp->ipp_fields & IPPF_DSTOPTS) != 0) { 30680f1702c5SYu Xiangning kmem_free(ipp->ipp_dstopts, 30690f1702c5SYu Xiangning ipp->ipp_dstoptslen); 30700f1702c5SYu Xiangning ipp->ipp_dstopts = NULL; 30710f1702c5SYu Xiangning ipp->ipp_dstoptslen = 0; 30720f1702c5SYu Xiangning } 30730f1702c5SYu Xiangning ipp->ipp_fields &= ~IPPF_DSTOPTS; 30740f1702c5SYu Xiangning ipp->ipp_sticky_ignored |= IPPF_DSTOPTS; 30750f1702c5SYu Xiangning } else { 30760f1702c5SYu Xiangning error = optcom_pkt_set(invalp, inlen, sticky, 30770f1702c5SYu Xiangning (uchar_t **)&ipp->ipp_dstopts, 30780f1702c5SYu Xiangning &ipp->ipp_dstoptslen, 0); 30790f1702c5SYu Xiangning if (error != 0) 30800f1702c5SYu Xiangning return (error); 30810f1702c5SYu Xiangning ipp->ipp_fields |= IPPF_DSTOPTS; 30820f1702c5SYu Xiangning } 30830f1702c5SYu Xiangning if (sticky) { 30840f1702c5SYu Xiangning error = udp_build_hdrs(udp); 30850f1702c5SYu Xiangning if (error != 0) 30860f1702c5SYu Xiangning return (error); 30870f1702c5SYu Xiangning } 30880f1702c5SYu Xiangning break; 30890f1702c5SYu Xiangning } 30900f1702c5SYu Xiangning case IPV6_RTHDR: { 30910f1702c5SYu Xiangning ip6_rthdr_t *rt = (ip6_rthdr_t *)invalp; 30920f1702c5SYu Xiangning 30930f1702c5SYu Xiangning /* 30940f1702c5SYu Xiangning * Sanity checks - minimum size, size a multiple of 30950f1702c5SYu Xiangning * eight bytes, and matching size passed in. 30960f1702c5SYu Xiangning */ 30970f1702c5SYu Xiangning if (inlen != 0 && 30980f1702c5SYu Xiangning inlen != (8 * (rt->ip6r_len + 1))) 30990f1702c5SYu Xiangning return (EINVAL); 31000f1702c5SYu Xiangning 31010f1702c5SYu Xiangning if (checkonly) 31020f1702c5SYu Xiangning break; 31037c478bd9Sstevel@tonic-gate 31040f1702c5SYu Xiangning if (inlen == 0) { 31050f1702c5SYu Xiangning if (sticky && 31060f1702c5SYu Xiangning (ipp->ipp_fields & IPPF_RTHDR) != 0) { 31070f1702c5SYu Xiangning kmem_free(ipp->ipp_rthdr, 31080f1702c5SYu Xiangning ipp->ipp_rthdrlen); 31090f1702c5SYu Xiangning ipp->ipp_rthdr = NULL; 31100f1702c5SYu Xiangning ipp->ipp_rthdrlen = 0; 31110f1702c5SYu Xiangning } 31120f1702c5SYu Xiangning ipp->ipp_fields &= ~IPPF_RTHDR; 31130f1702c5SYu Xiangning ipp->ipp_sticky_ignored |= IPPF_RTHDR; 31140f1702c5SYu Xiangning } else { 31150f1702c5SYu Xiangning error = optcom_pkt_set(invalp, inlen, sticky, 31160f1702c5SYu Xiangning (uchar_t **)&ipp->ipp_rthdr, 31170f1702c5SYu Xiangning &ipp->ipp_rthdrlen, 0); 31180f1702c5SYu Xiangning if (error != 0) 31190f1702c5SYu Xiangning return (error); 31200f1702c5SYu Xiangning ipp->ipp_fields |= IPPF_RTHDR; 31217c478bd9Sstevel@tonic-gate } 31220f1702c5SYu Xiangning if (sticky) { 31230f1702c5SYu Xiangning error = udp_build_hdrs(udp); 31240f1702c5SYu Xiangning if (error != 0) 31250f1702c5SYu Xiangning return (error); 31260f1702c5SYu Xiangning } 31270f1702c5SYu Xiangning break; 31280f1702c5SYu Xiangning } 31290f1702c5SYu Xiangning 31300f1702c5SYu Xiangning case IPV6_DONTFRAG: 31310f1702c5SYu Xiangning if (checkonly) 31320f1702c5SYu Xiangning break; 31330f1702c5SYu Xiangning 31340f1702c5SYu Xiangning if (onoff) { 31350f1702c5SYu Xiangning ipp->ipp_fields |= IPPF_DONTFRAG; 31367c478bd9Sstevel@tonic-gate } else { 31370f1702c5SYu Xiangning ipp->ipp_fields &= ~IPPF_DONTFRAG; 31387c478bd9Sstevel@tonic-gate } 31390f1702c5SYu Xiangning break; 31407c478bd9Sstevel@tonic-gate 31410f1702c5SYu Xiangning case IPV6_USE_MIN_MTU: 31420f1702c5SYu Xiangning if (inlen != sizeof (int)) 31430f1702c5SYu Xiangning return (EINVAL); 31440f1702c5SYu Xiangning 31450f1702c5SYu Xiangning if (*i1 < -1 || *i1 > 1) 31460f1702c5SYu Xiangning return (EINVAL); 31470f1702c5SYu Xiangning 31480f1702c5SYu Xiangning if (checkonly) 31490f1702c5SYu Xiangning break; 31500f1702c5SYu Xiangning 31510f1702c5SYu Xiangning ipp->ipp_fields |= IPPF_USE_MIN_MTU; 31520f1702c5SYu Xiangning ipp->ipp_use_min_mtu = *i1; 31530f1702c5SYu Xiangning break; 31540f1702c5SYu Xiangning 31550f1702c5SYu Xiangning case IPV6_SEC_OPT: 31560f1702c5SYu Xiangning case IPV6_SRC_PREFERENCES: 31570f1702c5SYu Xiangning case IPV6_V6ONLY: 31580f1702c5SYu Xiangning /* Handled at the IP level */ 31590f1702c5SYu Xiangning return (-EINVAL); 31600f1702c5SYu Xiangning default: 31610f1702c5SYu Xiangning *outlenp = 0; 31620f1702c5SYu Xiangning return (EINVAL); 31630f1702c5SYu Xiangning } 31640f1702c5SYu Xiangning break; 31650f1702c5SYu Xiangning } /* end IPPROTO_IPV6 */ 31660f1702c5SYu Xiangning case IPPROTO_UDP: 31670f1702c5SYu Xiangning switch (name) { 31680f1702c5SYu Xiangning case UDP_ANONPRIVBIND: 31690f1702c5SYu Xiangning if ((error = secpolicy_net_privaddr(cr, 0, 31700f1702c5SYu Xiangning IPPROTO_UDP)) != 0) { 31710f1702c5SYu Xiangning *outlenp = 0; 31720f1702c5SYu Xiangning return (error); 31737c478bd9Sstevel@tonic-gate } 31740f1702c5SYu Xiangning if (!checkonly) { 31750f1702c5SYu Xiangning udp->udp_anon_priv_bind = onoff; 31760f1702c5SYu Xiangning } 31770f1702c5SYu Xiangning break; 31780f1702c5SYu Xiangning case UDP_EXCLBIND: 31790f1702c5SYu Xiangning if (!checkonly) 31800f1702c5SYu Xiangning udp->udp_exclbind = onoff; 31810f1702c5SYu Xiangning break; 31820f1702c5SYu Xiangning case UDP_RCVHDR: 31830f1702c5SYu Xiangning if (!checkonly) 31840f1702c5SYu Xiangning udp->udp_rcvhdr = onoff; 31850f1702c5SYu Xiangning break; 31860f1702c5SYu Xiangning case UDP_NAT_T_ENDPOINT: 31870f1702c5SYu Xiangning if ((error = secpolicy_ip_config(cr, B_FALSE)) != 0) { 31880f1702c5SYu Xiangning *outlenp = 0; 31890f1702c5SYu Xiangning return (error); 31900f1702c5SYu Xiangning } 31910f1702c5SYu Xiangning 31920f1702c5SYu Xiangning /* 31930f1702c5SYu Xiangning * Use udp_family instead so we can avoid ambiguitites 31940f1702c5SYu Xiangning * with AF_INET6 sockets that may switch from IPv4 31950f1702c5SYu Xiangning * to IPv6. 31960f1702c5SYu Xiangning */ 31970f1702c5SYu Xiangning if (udp->udp_family != AF_INET) { 31980f1702c5SYu Xiangning *outlenp = 0; 31990f1702c5SYu Xiangning return (EAFNOSUPPORT); 32000f1702c5SYu Xiangning } 32010f1702c5SYu Xiangning 32020f1702c5SYu Xiangning if (!checkonly) { 32030f1702c5SYu Xiangning int size; 32040f1702c5SYu Xiangning 32050f1702c5SYu Xiangning udp->udp_nat_t_endpoint = onoff; 32060f1702c5SYu Xiangning 32070f1702c5SYu Xiangning udp->udp_max_hdr_len = IP_SIMPLE_HDR_LENGTH + 32080f1702c5SYu Xiangning UDPH_SIZE + udp->udp_ip_snd_options_len; 32090f1702c5SYu Xiangning 32100f1702c5SYu Xiangning /* Also, adjust wroff */ 32110f1702c5SYu Xiangning if (onoff) { 32120f1702c5SYu Xiangning udp->udp_max_hdr_len += 32130f1702c5SYu Xiangning sizeof (uint32_t); 32140f1702c5SYu Xiangning } 32150f1702c5SYu Xiangning size = udp->udp_max_hdr_len + 32160f1702c5SYu Xiangning us->us_wroff_extra; 32170f1702c5SYu Xiangning (void) proto_set_tx_wroff(connp->conn_rq, connp, 32180f1702c5SYu Xiangning size); 32190f1702c5SYu Xiangning } 32200f1702c5SYu Xiangning break; 32210f1702c5SYu Xiangning default: 32220f1702c5SYu Xiangning *outlenp = 0; 32230f1702c5SYu Xiangning return (EINVAL); 32247c478bd9Sstevel@tonic-gate } 32250f1702c5SYu Xiangning break; 32260f1702c5SYu Xiangning default: 32270f1702c5SYu Xiangning *outlenp = 0; 32280f1702c5SYu Xiangning return (EINVAL); 32297c478bd9Sstevel@tonic-gate } 32300f1702c5SYu Xiangning /* 32310f1702c5SYu Xiangning * Common case of OK return with outval same as inval. 32320f1702c5SYu Xiangning */ 32330f1702c5SYu Xiangning if (invalp != outvalp) { 32340f1702c5SYu Xiangning /* don't trust bcopy for identical src/dst */ 32350f1702c5SYu Xiangning (void) bcopy(invalp, outvalp, inlen); 32367c478bd9Sstevel@tonic-gate } 32370f1702c5SYu Xiangning *outlenp = inlen; 32380f1702c5SYu Xiangning return (0); 32397c478bd9Sstevel@tonic-gate } 32407c478bd9Sstevel@tonic-gate 32410f1702c5SYu Xiangning int 32420f1702c5SYu Xiangning udp_opt_set(conn_t *connp, uint_t optset_context, int level, int name, 32430f1702c5SYu Xiangning uint_t inlen, uchar_t *invalp, uint_t *outlenp, uchar_t *outvalp, 32440f1702c5SYu Xiangning void *thisdg_attrs, cred_t *cr) 32457c478bd9Sstevel@tonic-gate { 32460f1702c5SYu Xiangning int error; 32470f1702c5SYu Xiangning boolean_t checkonly; 32487c478bd9Sstevel@tonic-gate 32490f1702c5SYu Xiangning error = 0; 32500f1702c5SYu Xiangning switch (optset_context) { 32510f1702c5SYu Xiangning case SETFN_OPTCOM_CHECKONLY: 32520f1702c5SYu Xiangning checkonly = B_TRUE; 32530f1702c5SYu Xiangning /* 32540f1702c5SYu Xiangning * Note: Implies T_CHECK semantics for T_OPTCOM_REQ 32550f1702c5SYu Xiangning * inlen != 0 implies value supplied and 32560f1702c5SYu Xiangning * we have to "pretend" to set it. 32570f1702c5SYu Xiangning * inlen == 0 implies that there is no 32580f1702c5SYu Xiangning * value part in T_CHECK request and just validation 32590f1702c5SYu Xiangning * done elsewhere should be enough, we just return here. 32600f1702c5SYu Xiangning */ 32610f1702c5SYu Xiangning if (inlen == 0) { 32620f1702c5SYu Xiangning *outlenp = 0; 32630f1702c5SYu Xiangning goto done; 32640f1702c5SYu Xiangning } 32650f1702c5SYu Xiangning break; 32660f1702c5SYu Xiangning case SETFN_OPTCOM_NEGOTIATE: 32670f1702c5SYu Xiangning checkonly = B_FALSE; 32680f1702c5SYu Xiangning break; 32690f1702c5SYu Xiangning case SETFN_UD_NEGOTIATE: 32700f1702c5SYu Xiangning case SETFN_CONN_NEGOTIATE: 32710f1702c5SYu Xiangning checkonly = B_FALSE; 32720f1702c5SYu Xiangning /* 32730f1702c5SYu Xiangning * Negotiating local and "association-related" options 32740f1702c5SYu Xiangning * through T_UNITDATA_REQ. 32750f1702c5SYu Xiangning * 32760f1702c5SYu Xiangning * Following routine can filter out ones we do not 32770f1702c5SYu Xiangning * want to be "set" this way. 32780f1702c5SYu Xiangning */ 32790f1702c5SYu Xiangning if (!udp_opt_allow_udr_set(level, name)) { 32800f1702c5SYu Xiangning *outlenp = 0; 32810f1702c5SYu Xiangning error = EINVAL; 32820f1702c5SYu Xiangning goto done; 32830f1702c5SYu Xiangning } 32840f1702c5SYu Xiangning break; 32850f1702c5SYu Xiangning default: 32860f1702c5SYu Xiangning /* 32870f1702c5SYu Xiangning * We should never get here 32880f1702c5SYu Xiangning */ 32890f1702c5SYu Xiangning *outlenp = 0; 32900f1702c5SYu Xiangning error = EINVAL; 32910f1702c5SYu Xiangning goto done; 32927c478bd9Sstevel@tonic-gate } 32937c478bd9Sstevel@tonic-gate 32940f1702c5SYu Xiangning ASSERT((optset_context != SETFN_OPTCOM_CHECKONLY) || 32950f1702c5SYu Xiangning (optset_context == SETFN_OPTCOM_CHECKONLY && inlen != 0)); 32960f1702c5SYu Xiangning 32970f1702c5SYu Xiangning error = udp_do_opt_set(connp, level, name, inlen, invalp, outlenp, 32980f1702c5SYu Xiangning outvalp, cr, thisdg_attrs, checkonly); 32990f1702c5SYu Xiangning done: 33000f1702c5SYu Xiangning return (error); 33017c478bd9Sstevel@tonic-gate } 33027c478bd9Sstevel@tonic-gate 33030f1702c5SYu Xiangning /* ARGSUSED */ 33040f1702c5SYu Xiangning int 33050f1702c5SYu Xiangning udp_tpi_opt_set(queue_t *q, uint_t optset_context, int level, int name, 33060f1702c5SYu Xiangning uint_t inlen, uchar_t *invalp, uint_t *outlenp, uchar_t *outvalp, 33070f1702c5SYu Xiangning void *thisdg_attrs, cred_t *cr, mblk_t *mblk) 33087c478bd9Sstevel@tonic-gate { 33090f1702c5SYu Xiangning conn_t *connp = Q_TO_CONN(q); 33100f1702c5SYu Xiangning int error; 33110f1702c5SYu Xiangning udp_t *udp = connp->conn_udp; 33127c478bd9Sstevel@tonic-gate 33130f1702c5SYu Xiangning rw_enter(&udp->udp_rwlock, RW_WRITER); 33140f1702c5SYu Xiangning error = udp_opt_set(connp, optset_context, level, name, inlen, invalp, 33150f1702c5SYu Xiangning outlenp, outvalp, thisdg_attrs, cr); 33160f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 33170f1702c5SYu Xiangning return (error); 33187c478bd9Sstevel@tonic-gate } 33197c478bd9Sstevel@tonic-gate 33207c478bd9Sstevel@tonic-gate /* 33210f1702c5SYu Xiangning * Update udp_sticky_hdrs based on udp_sticky_ipp, udp_v6src, and udp_ttl. 33220f1702c5SYu Xiangning * The headers include ip6i_t (if needed), ip6_t, any sticky extension 33230f1702c5SYu Xiangning * headers, and the udp header. 33240f1702c5SYu Xiangning * Returns failure if can't allocate memory. 33257c478bd9Sstevel@tonic-gate */ 33260f1702c5SYu Xiangning static int 33270f1702c5SYu Xiangning udp_build_hdrs(udp_t *udp) 33287c478bd9Sstevel@tonic-gate { 33290f1702c5SYu Xiangning udp_stack_t *us = udp->udp_us; 33300f1702c5SYu Xiangning uchar_t *hdrs; 33310f1702c5SYu Xiangning uint_t hdrs_len; 33320f1702c5SYu Xiangning ip6_t *ip6h; 33330f1702c5SYu Xiangning ip6i_t *ip6i; 33340f1702c5SYu Xiangning udpha_t *udpha; 33350f1702c5SYu Xiangning ip6_pkt_t *ipp = &udp->udp_sticky_ipp; 33360f1702c5SYu Xiangning size_t sth_wroff; 33370f1702c5SYu Xiangning conn_t *connp = udp->udp_connp; 33387c478bd9Sstevel@tonic-gate 33390f1702c5SYu Xiangning ASSERT(RW_WRITE_HELD(&udp->udp_rwlock)); 33400f1702c5SYu Xiangning ASSERT(connp != NULL); 33417c478bd9Sstevel@tonic-gate 33420f1702c5SYu Xiangning hdrs_len = ip_total_hdrs_len_v6(ipp) + UDPH_SIZE; 33430f1702c5SYu Xiangning ASSERT(hdrs_len != 0); 33440f1702c5SYu Xiangning if (hdrs_len != udp->udp_sticky_hdrs_len) { 33450f1702c5SYu Xiangning /* Need to reallocate */ 33460f1702c5SYu Xiangning hdrs = kmem_alloc(hdrs_len, KM_NOSLEEP); 33470f1702c5SYu Xiangning if (hdrs == NULL) 33480f1702c5SYu Xiangning return (ENOMEM); 33497c478bd9Sstevel@tonic-gate 33500f1702c5SYu Xiangning if (udp->udp_sticky_hdrs_len != 0) { 33510f1702c5SYu Xiangning kmem_free(udp->udp_sticky_hdrs, 33520f1702c5SYu Xiangning udp->udp_sticky_hdrs_len); 33537c478bd9Sstevel@tonic-gate } 33540f1702c5SYu Xiangning udp->udp_sticky_hdrs = hdrs; 33550f1702c5SYu Xiangning udp->udp_sticky_hdrs_len = hdrs_len; 33560f1702c5SYu Xiangning } 33570f1702c5SYu Xiangning ip_build_hdrs_v6(udp->udp_sticky_hdrs, 33580f1702c5SYu Xiangning udp->udp_sticky_hdrs_len - UDPH_SIZE, ipp, IPPROTO_UDP); 33597c478bd9Sstevel@tonic-gate 33600f1702c5SYu Xiangning /* Set header fields not in ipp */ 33610f1702c5SYu Xiangning if (ipp->ipp_fields & IPPF_HAS_IP6I) { 33620f1702c5SYu Xiangning ip6i = (ip6i_t *)udp->udp_sticky_hdrs; 33630f1702c5SYu Xiangning ip6h = (ip6_t *)&ip6i[1]; 33640f1702c5SYu Xiangning } else { 33650f1702c5SYu Xiangning ip6h = (ip6_t *)udp->udp_sticky_hdrs; 33660f1702c5SYu Xiangning } 33677c478bd9Sstevel@tonic-gate 33680f1702c5SYu Xiangning if (!(ipp->ipp_fields & IPPF_ADDR)) 33690f1702c5SYu Xiangning ip6h->ip6_src = udp->udp_v6src; 33707c478bd9Sstevel@tonic-gate 33710f1702c5SYu Xiangning udpha = (udpha_t *)(udp->udp_sticky_hdrs + hdrs_len - UDPH_SIZE); 33720f1702c5SYu Xiangning udpha->uha_src_port = udp->udp_port; 33737c478bd9Sstevel@tonic-gate 33740f1702c5SYu Xiangning /* Try to get everything in a single mblk */ 33750f1702c5SYu Xiangning if (hdrs_len > udp->udp_max_hdr_len) { 33760f1702c5SYu Xiangning udp->udp_max_hdr_len = hdrs_len; 33770f1702c5SYu Xiangning sth_wroff = udp->udp_max_hdr_len + us->us_wroff_extra; 33780f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 33790f1702c5SYu Xiangning (void) proto_set_tx_wroff(udp->udp_connp->conn_rq, 33800f1702c5SYu Xiangning udp->udp_connp, sth_wroff); 33810f1702c5SYu Xiangning rw_enter(&udp->udp_rwlock, RW_WRITER); 33827c478bd9Sstevel@tonic-gate } 33830f1702c5SYu Xiangning return (0); 33847c478bd9Sstevel@tonic-gate } 33857c478bd9Sstevel@tonic-gate 33860f1702c5SYu Xiangning /* 33870f1702c5SYu Xiangning * This routine retrieves the value of an ND variable in a udpparam_t 33880f1702c5SYu Xiangning * structure. It is called through nd_getset when a user reads the 33890f1702c5SYu Xiangning * variable. 33900f1702c5SYu Xiangning */ 33910f1702c5SYu Xiangning /* ARGSUSED */ 3392fc80c0dfSnordmark static int 33930f1702c5SYu Xiangning udp_param_get(queue_t *q, mblk_t *mp, caddr_t cp, cred_t *cr) 3394fc80c0dfSnordmark { 33950f1702c5SYu Xiangning udpparam_t *udppa = (udpparam_t *)cp; 33960f1702c5SYu Xiangning 33970f1702c5SYu Xiangning (void) mi_mpprintf(mp, "%d", udppa->udp_param_value); 33980f1702c5SYu Xiangning return (0); 3399fc80c0dfSnordmark } 3400fc80c0dfSnordmark 34010f1702c5SYu Xiangning /* 34020f1702c5SYu Xiangning * Walk through the param array specified registering each element with the 34030f1702c5SYu Xiangning * named dispatch (ND) handler. 34040f1702c5SYu Xiangning */ 34050f1702c5SYu Xiangning static boolean_t 34060f1702c5SYu Xiangning udp_param_register(IDP *ndp, udpparam_t *udppa, int cnt) 34070f1702c5SYu Xiangning { 34080f1702c5SYu Xiangning for (; cnt-- > 0; udppa++) { 34090f1702c5SYu Xiangning if (udppa->udp_param_name && udppa->udp_param_name[0]) { 34100f1702c5SYu Xiangning if (!nd_load(ndp, udppa->udp_param_name, 34110f1702c5SYu Xiangning udp_param_get, udp_param_set, 34120f1702c5SYu Xiangning (caddr_t)udppa)) { 34130f1702c5SYu Xiangning nd_free(ndp); 34140f1702c5SYu Xiangning return (B_FALSE); 34150f1702c5SYu Xiangning } 34160f1702c5SYu Xiangning } 34170f1702c5SYu Xiangning } 34180f1702c5SYu Xiangning if (!nd_load(ndp, "udp_extra_priv_ports", 34190f1702c5SYu Xiangning udp_extra_priv_ports_get, NULL, NULL)) { 34200f1702c5SYu Xiangning nd_free(ndp); 34210f1702c5SYu Xiangning return (B_FALSE); 34220f1702c5SYu Xiangning } 34230f1702c5SYu Xiangning if (!nd_load(ndp, "udp_extra_priv_ports_add", 34240f1702c5SYu Xiangning NULL, udp_extra_priv_ports_add, NULL)) { 34250f1702c5SYu Xiangning nd_free(ndp); 34260f1702c5SYu Xiangning return (B_FALSE); 34270f1702c5SYu Xiangning } 34280f1702c5SYu Xiangning if (!nd_load(ndp, "udp_extra_priv_ports_del", 34290f1702c5SYu Xiangning NULL, udp_extra_priv_ports_del, NULL)) { 34300f1702c5SYu Xiangning nd_free(ndp); 34310f1702c5SYu Xiangning return (B_FALSE); 34320f1702c5SYu Xiangning } 34330f1702c5SYu Xiangning if (!nd_load(ndp, "udp_status", udp_status_report, NULL, 34340f1702c5SYu Xiangning NULL)) { 34350f1702c5SYu Xiangning nd_free(ndp); 34360f1702c5SYu Xiangning return (B_FALSE); 34370f1702c5SYu Xiangning } 34380f1702c5SYu Xiangning if (!nd_load(ndp, "udp_bind_hash", udp_bind_hash_report, NULL, 34390f1702c5SYu Xiangning NULL)) { 34400f1702c5SYu Xiangning nd_free(ndp); 34410f1702c5SYu Xiangning return (B_FALSE); 34420f1702c5SYu Xiangning } 34430f1702c5SYu Xiangning return (B_TRUE); 34440f1702c5SYu Xiangning } 34450f1702c5SYu Xiangning 34460f1702c5SYu Xiangning /* This routine sets an ND variable in a udpparam_t structure. */ 34470f1702c5SYu Xiangning /* ARGSUSED */ 3448fc80c0dfSnordmark static int 34490f1702c5SYu Xiangning udp_param_set(queue_t *q, mblk_t *mp, char *value, caddr_t cp, cred_t *cr) 3450fc80c0dfSnordmark { 34510f1702c5SYu Xiangning long new_value; 34520f1702c5SYu Xiangning udpparam_t *udppa = (udpparam_t *)cp; 34530f1702c5SYu Xiangning 34540f1702c5SYu Xiangning /* 34550f1702c5SYu Xiangning * Fail the request if the new value does not lie within the 34560f1702c5SYu Xiangning * required bounds. 34570f1702c5SYu Xiangning */ 34580f1702c5SYu Xiangning if (ddi_strtol(value, NULL, 10, &new_value) != 0 || 34590f1702c5SYu Xiangning new_value < udppa->udp_param_min || 34600f1702c5SYu Xiangning new_value > udppa->udp_param_max) { 34610f1702c5SYu Xiangning return (EINVAL); 34620f1702c5SYu Xiangning } 34630f1702c5SYu Xiangning 34640f1702c5SYu Xiangning /* Set the new value */ 34650f1702c5SYu Xiangning udppa->udp_param_value = new_value; 34660f1702c5SYu Xiangning return (0); 3467fc80c0dfSnordmark } 3468fc80c0dfSnordmark 34697c478bd9Sstevel@tonic-gate /* 34700f1702c5SYu Xiangning * Copy hop-by-hop option from ipp->ipp_hopopts to the buffer provided (with 34710f1702c5SYu Xiangning * T_opthdr) and return the number of bytes copied. 'dbuf' may be NULL to 34720f1702c5SYu Xiangning * just count the length needed for allocation. If 'dbuf' is non-NULL, 34730f1702c5SYu Xiangning * then it's assumed to be allocated to be large enough. 34740f1702c5SYu Xiangning * 34750f1702c5SYu Xiangning * Returns zero if trimming of the security option causes all options to go 34760f1702c5SYu Xiangning * away. 34777c478bd9Sstevel@tonic-gate */ 34780f1702c5SYu Xiangning static size_t 34790f1702c5SYu Xiangning copy_hop_opts(const ip6_pkt_t *ipp, uchar_t *dbuf) 34807c478bd9Sstevel@tonic-gate { 34810f1702c5SYu Xiangning struct T_opthdr *toh; 34820f1702c5SYu Xiangning size_t hol = ipp->ipp_hopoptslen; 34830f1702c5SYu Xiangning ip6_hbh_t *dstopt = NULL; 34840f1702c5SYu Xiangning const ip6_hbh_t *srcopt = ipp->ipp_hopopts; 34850f1702c5SYu Xiangning size_t tlen, olen, plen; 34860f1702c5SYu Xiangning boolean_t deleting; 34870f1702c5SYu Xiangning const struct ip6_opt *sopt, *lastpad; 34880f1702c5SYu Xiangning struct ip6_opt *dopt; 34897c478bd9Sstevel@tonic-gate 34900f1702c5SYu Xiangning if ((toh = (struct T_opthdr *)dbuf) != NULL) { 34910f1702c5SYu Xiangning toh->level = IPPROTO_IPV6; 34920f1702c5SYu Xiangning toh->name = IPV6_HOPOPTS; 34930f1702c5SYu Xiangning toh->status = 0; 34940f1702c5SYu Xiangning dstopt = (ip6_hbh_t *)(toh + 1); 34950f1702c5SYu Xiangning } 34967c478bd9Sstevel@tonic-gate 34970f1702c5SYu Xiangning /* 34980f1702c5SYu Xiangning * If labeling is enabled, then skip the label option 34990f1702c5SYu Xiangning * but get other options if there are any. 35000f1702c5SYu Xiangning */ 35010f1702c5SYu Xiangning if (is_system_labeled()) { 35020f1702c5SYu Xiangning dopt = NULL; 35030f1702c5SYu Xiangning if (dstopt != NULL) { 35040f1702c5SYu Xiangning /* will fill in ip6h_len later */ 35050f1702c5SYu Xiangning dstopt->ip6h_nxt = srcopt->ip6h_nxt; 35060f1702c5SYu Xiangning dopt = (struct ip6_opt *)(dstopt + 1); 35070f1702c5SYu Xiangning } 35080f1702c5SYu Xiangning sopt = (const struct ip6_opt *)(srcopt + 1); 35090f1702c5SYu Xiangning hol -= sizeof (*srcopt); 35100f1702c5SYu Xiangning tlen = sizeof (*dstopt); 35110f1702c5SYu Xiangning lastpad = NULL; 35120f1702c5SYu Xiangning deleting = B_FALSE; 35130f1702c5SYu Xiangning /* 35140f1702c5SYu Xiangning * This loop finds the first (lastpad pointer) of any number of 35150f1702c5SYu Xiangning * pads that preceeds the security option, then treats the 35160f1702c5SYu Xiangning * security option as though it were a pad, and then finds the 35170f1702c5SYu Xiangning * next non-pad option (or end of list). 35180f1702c5SYu Xiangning * 35190f1702c5SYu Xiangning * It then treats the entire block as one big pad. To preserve 35200f1702c5SYu Xiangning * alignment of any options that follow, or just the end of the 35210f1702c5SYu Xiangning * list, it computes a minimal new padding size that keeps the 35220f1702c5SYu Xiangning * same alignment for the next option. 35230f1702c5SYu Xiangning * 35240f1702c5SYu Xiangning * If it encounters just a sequence of pads with no security 35250f1702c5SYu Xiangning * option, those are copied as-is rather than collapsed. 35260f1702c5SYu Xiangning * 35270f1702c5SYu Xiangning * Note that to handle the end of list case, the code makes one 35280f1702c5SYu Xiangning * loop with 'hol' set to zero. 35290f1702c5SYu Xiangning */ 35300f1702c5SYu Xiangning for (;;) { 35310f1702c5SYu Xiangning if (hol > 0) { 35320f1702c5SYu Xiangning if (sopt->ip6o_type == IP6OPT_PAD1) { 35330f1702c5SYu Xiangning if (lastpad == NULL) 35340f1702c5SYu Xiangning lastpad = sopt; 35350f1702c5SYu Xiangning sopt = (const struct ip6_opt *) 35360f1702c5SYu Xiangning &sopt->ip6o_len; 35370f1702c5SYu Xiangning hol--; 35380f1702c5SYu Xiangning continue; 35390f1702c5SYu Xiangning } 35400f1702c5SYu Xiangning olen = sopt->ip6o_len + sizeof (*sopt); 35410f1702c5SYu Xiangning if (olen > hol) 35420f1702c5SYu Xiangning olen = hol; 35430f1702c5SYu Xiangning if (sopt->ip6o_type == IP6OPT_PADN || 35440f1702c5SYu Xiangning sopt->ip6o_type == ip6opt_ls) { 35450f1702c5SYu Xiangning if (sopt->ip6o_type == ip6opt_ls) 35460f1702c5SYu Xiangning deleting = B_TRUE; 35470f1702c5SYu Xiangning if (lastpad == NULL) 35480f1702c5SYu Xiangning lastpad = sopt; 35490f1702c5SYu Xiangning sopt = (const struct ip6_opt *) 35500f1702c5SYu Xiangning ((const char *)sopt + olen); 35510f1702c5SYu Xiangning hol -= olen; 35520f1702c5SYu Xiangning continue; 35530f1702c5SYu Xiangning } 35540f1702c5SYu Xiangning } else { 35550f1702c5SYu Xiangning /* if nothing was copied at all, then delete */ 35560f1702c5SYu Xiangning if (tlen == sizeof (*dstopt)) 35570f1702c5SYu Xiangning return (0); 35580f1702c5SYu Xiangning /* last pass; pick up any trailing padding */ 35590f1702c5SYu Xiangning olen = 0; 35600f1702c5SYu Xiangning } 35610f1702c5SYu Xiangning if (deleting) { 35620f1702c5SYu Xiangning /* 35630f1702c5SYu Xiangning * compute aligning effect of deleted material 35640f1702c5SYu Xiangning * to reproduce with pad. 35650f1702c5SYu Xiangning */ 35660f1702c5SYu Xiangning plen = ((const char *)sopt - 35670f1702c5SYu Xiangning (const char *)lastpad) & 7; 35680f1702c5SYu Xiangning tlen += plen; 35690f1702c5SYu Xiangning if (dopt != NULL) { 35700f1702c5SYu Xiangning if (plen == 1) { 35710f1702c5SYu Xiangning dopt->ip6o_type = IP6OPT_PAD1; 35720f1702c5SYu Xiangning } else if (plen > 1) { 35730f1702c5SYu Xiangning plen -= sizeof (*dopt); 35740f1702c5SYu Xiangning dopt->ip6o_type = IP6OPT_PADN; 35750f1702c5SYu Xiangning dopt->ip6o_len = plen; 35760f1702c5SYu Xiangning if (plen > 0) 35770f1702c5SYu Xiangning bzero(dopt + 1, plen); 35780f1702c5SYu Xiangning } 35790f1702c5SYu Xiangning dopt = (struct ip6_opt *) 35800f1702c5SYu Xiangning ((char *)dopt + plen); 35810f1702c5SYu Xiangning } 35820f1702c5SYu Xiangning deleting = B_FALSE; 35830f1702c5SYu Xiangning lastpad = NULL; 35840f1702c5SYu Xiangning } 35850f1702c5SYu Xiangning /* if there's uncopied padding, then copy that now */ 35860f1702c5SYu Xiangning if (lastpad != NULL) { 35870f1702c5SYu Xiangning olen += (const char *)sopt - 35880f1702c5SYu Xiangning (const char *)lastpad; 35890f1702c5SYu Xiangning sopt = lastpad; 35900f1702c5SYu Xiangning lastpad = NULL; 35910f1702c5SYu Xiangning } 35920f1702c5SYu Xiangning if (dopt != NULL && olen > 0) { 35930f1702c5SYu Xiangning bcopy(sopt, dopt, olen); 35940f1702c5SYu Xiangning dopt = (struct ip6_opt *)((char *)dopt + olen); 35950f1702c5SYu Xiangning } 35960f1702c5SYu Xiangning if (hol == 0) 35970f1702c5SYu Xiangning break; 35980f1702c5SYu Xiangning tlen += olen; 35990f1702c5SYu Xiangning sopt = (const struct ip6_opt *) 36000f1702c5SYu Xiangning ((const char *)sopt + olen); 36010f1702c5SYu Xiangning hol -= olen; 36020f1702c5SYu Xiangning } 36030f1702c5SYu Xiangning /* go back and patch up the length value, rounded upward */ 36040f1702c5SYu Xiangning if (dstopt != NULL) 36050f1702c5SYu Xiangning dstopt->ip6h_len = (tlen - 1) >> 3; 36060f1702c5SYu Xiangning } else { 36070f1702c5SYu Xiangning tlen = hol; 36080f1702c5SYu Xiangning if (dstopt != NULL) 36090f1702c5SYu Xiangning bcopy(srcopt, dstopt, hol); 36100f1702c5SYu Xiangning } 36117c478bd9Sstevel@tonic-gate 36120f1702c5SYu Xiangning tlen += sizeof (*toh); 36130f1702c5SYu Xiangning if (toh != NULL) 36140f1702c5SYu Xiangning toh->len = tlen; 36157c478bd9Sstevel@tonic-gate 36160f1702c5SYu Xiangning return (tlen); 36170f1702c5SYu Xiangning } 3618f4b3ec61Sdh 36190f1702c5SYu Xiangning /* 36200f1702c5SYu Xiangning * Update udp_rcv_opt_len from the packet. 36210f1702c5SYu Xiangning * Called when options received, and when no options received but 36220f1702c5SYu Xiangning * udp_ip_recv_opt_len has previously recorded options. 36230f1702c5SYu Xiangning */ 36240f1702c5SYu Xiangning static void 36250f1702c5SYu Xiangning udp_save_ip_rcv_opt(udp_t *udp, void *opt, int opt_len) 36260f1702c5SYu Xiangning { 36270f1702c5SYu Xiangning /* Save the options if any */ 36280f1702c5SYu Xiangning if (opt_len > 0) { 36290f1702c5SYu Xiangning if (opt_len > udp->udp_ip_rcv_options_len) { 36300f1702c5SYu Xiangning /* Need to allocate larger buffer */ 36310f1702c5SYu Xiangning if (udp->udp_ip_rcv_options_len != 0) 36320f1702c5SYu Xiangning mi_free((char *)udp->udp_ip_rcv_options); 36330f1702c5SYu Xiangning udp->udp_ip_rcv_options_len = 0; 36340f1702c5SYu Xiangning udp->udp_ip_rcv_options = 36350f1702c5SYu Xiangning (uchar_t *)mi_alloc(opt_len, BPRI_HI); 36360f1702c5SYu Xiangning if (udp->udp_ip_rcv_options != NULL) 36370f1702c5SYu Xiangning udp->udp_ip_rcv_options_len = opt_len; 36380f1702c5SYu Xiangning } 36390f1702c5SYu Xiangning if (udp->udp_ip_rcv_options_len != 0) { 36400f1702c5SYu Xiangning bcopy(opt, udp->udp_ip_rcv_options, opt_len); 36410f1702c5SYu Xiangning /* Adjust length if we are resusing the space */ 36420f1702c5SYu Xiangning udp->udp_ip_rcv_options_len = opt_len; 36430f1702c5SYu Xiangning } 36440f1702c5SYu Xiangning } else if (udp->udp_ip_rcv_options_len != 0) { 36450f1702c5SYu Xiangning /* Clear out previously recorded options */ 36460f1702c5SYu Xiangning mi_free((char *)udp->udp_ip_rcv_options); 36470f1702c5SYu Xiangning udp->udp_ip_rcv_options = NULL; 36480f1702c5SYu Xiangning udp->udp_ip_rcv_options_len = 0; 36490f1702c5SYu Xiangning } 36500f1702c5SYu Xiangning } 3651f4b3ec61Sdh 36520f1702c5SYu Xiangning static void 36530f1702c5SYu Xiangning udp_queue_fallback(udp_t *udp, mblk_t *mp) 36540f1702c5SYu Xiangning { 36550f1702c5SYu Xiangning ASSERT(MUTEX_HELD(&udp->udp_recv_lock)); 36560f1702c5SYu Xiangning if (IPCL_IS_NONSTR(udp->udp_connp)) { 3657aa92d85bSgt /* 36580f1702c5SYu Xiangning * fallback has started but messages have not been moved yet 3659aa92d85bSgt */ 36600f1702c5SYu Xiangning if (udp->udp_fallback_queue_head == NULL) { 36610f1702c5SYu Xiangning ASSERT(udp->udp_fallback_queue_tail == NULL); 36620f1702c5SYu Xiangning udp->udp_fallback_queue_head = mp; 36630f1702c5SYu Xiangning udp->udp_fallback_queue_tail = mp; 36640f1702c5SYu Xiangning } else { 36650f1702c5SYu Xiangning ASSERT(udp->udp_fallback_queue_tail != NULL); 36660f1702c5SYu Xiangning udp->udp_fallback_queue_tail->b_next = mp; 36670f1702c5SYu Xiangning udp->udp_fallback_queue_tail = mp; 3668aa92d85bSgt } 36690f1702c5SYu Xiangning mutex_exit(&udp->udp_recv_lock); 36700f1702c5SYu Xiangning } else { 36710f1702c5SYu Xiangning /* 36720f1702c5SYu Xiangning * no more fallbacks possible, ok to drop lock. 36730f1702c5SYu Xiangning */ 36740f1702c5SYu Xiangning mutex_exit(&udp->udp_recv_lock); 36750f1702c5SYu Xiangning putnext(udp->udp_connp->conn_rq, mp); 3676fc80c0dfSnordmark } 36770f1702c5SYu Xiangning } 36780f1702c5SYu Xiangning 36790f1702c5SYu Xiangning /* ARGSUSED2 */ 36800f1702c5SYu Xiangning static void 36810f1702c5SYu Xiangning udp_input(void *arg1, mblk_t *mp, void *arg2) 36820f1702c5SYu Xiangning { 36830f1702c5SYu Xiangning conn_t *connp = (conn_t *)arg1; 36840f1702c5SYu Xiangning struct T_unitdata_ind *tudi; 36850f1702c5SYu Xiangning uchar_t *rptr; /* Pointer to IP header */ 36860f1702c5SYu Xiangning int hdr_length; /* Length of IP+UDP headers */ 36870f1702c5SYu Xiangning int opt_len; 36880f1702c5SYu Xiangning int udi_size; /* Size of T_unitdata_ind */ 36890f1702c5SYu Xiangning int mp_len; 36900f1702c5SYu Xiangning udp_t *udp; 36910f1702c5SYu Xiangning udpha_t *udpha; 36920f1702c5SYu Xiangning int ipversion; 36930f1702c5SYu Xiangning ip6_pkt_t ipp; 36940f1702c5SYu Xiangning ip6_t *ip6h; 36950f1702c5SYu Xiangning ip6i_t *ip6i; 36960f1702c5SYu Xiangning mblk_t *mp1; 36970f1702c5SYu Xiangning mblk_t *options_mp = NULL; 36980f1702c5SYu Xiangning ip_pktinfo_t *pinfo = NULL; 36990f1702c5SYu Xiangning cred_t *cr = NULL; 37000f1702c5SYu Xiangning pid_t cpid; 37010f1702c5SYu Xiangning uint32_t udp_ip_rcv_options_len; 37020f1702c5SYu Xiangning udp_bits_t udp_bits; 37030f1702c5SYu Xiangning cred_t *rcr = connp->conn_cred; 37040f1702c5SYu Xiangning udp_stack_t *us; 3705aa92d85bSgt 37060f1702c5SYu Xiangning ASSERT(connp->conn_flags & IPCL_UDPCONN); 3707ff550d0eSmasputra 3708fc80c0dfSnordmark udp = connp->conn_udp; 37090f1702c5SYu Xiangning us = udp->udp_us; 37100f1702c5SYu Xiangning rptr = mp->b_rptr; 37110f1702c5SYu Xiangning ASSERT(DB_TYPE(mp) == M_DATA || DB_TYPE(mp) == M_CTL); 37120f1702c5SYu Xiangning ASSERT(OK_32PTR(rptr)); 3713ff550d0eSmasputra 3714ff550d0eSmasputra /* 37150f1702c5SYu Xiangning * IP should have prepended the options data in an M_CTL 37160f1702c5SYu Xiangning * Check M_CTL "type" to make sure are not here bcos of 37170f1702c5SYu Xiangning * a valid ICMP message 37187c478bd9Sstevel@tonic-gate */ 37190f1702c5SYu Xiangning if (DB_TYPE(mp) == M_CTL) { 37200f1702c5SYu Xiangning if (MBLKL(mp) == sizeof (ip_pktinfo_t) && 37210f1702c5SYu Xiangning ((ip_pktinfo_t *)mp->b_rptr)->ip_pkt_ulp_type == 37220f1702c5SYu Xiangning IN_PKTINFO) { 37230f1702c5SYu Xiangning /* 37240f1702c5SYu Xiangning * IP_RECVIF or IP_RECVSLLA or IPF_RECVADDR information 37250f1702c5SYu Xiangning * has been prepended to the packet by IP. We need to 37260f1702c5SYu Xiangning * extract the mblk and adjust the rptr 37270f1702c5SYu Xiangning */ 37280f1702c5SYu Xiangning pinfo = (ip_pktinfo_t *)mp->b_rptr; 37290f1702c5SYu Xiangning options_mp = mp; 37300f1702c5SYu Xiangning mp = mp->b_cont; 37310f1702c5SYu Xiangning rptr = mp->b_rptr; 37320f1702c5SYu Xiangning UDP_STAT(us, udp_in_pktinfo); 37330f1702c5SYu Xiangning } else { 37340f1702c5SYu Xiangning /* 37350f1702c5SYu Xiangning * ICMP messages. 37360f1702c5SYu Xiangning */ 37370f1702c5SYu Xiangning udp_icmp_error(connp, mp); 37380f1702c5SYu Xiangning return; 37390f1702c5SYu Xiangning } 37407c478bd9Sstevel@tonic-gate } 37417c478bd9Sstevel@tonic-gate 37420f1702c5SYu Xiangning mp_len = msgdsize(mp); 374345916cd2Sjpk /* 37440f1702c5SYu Xiangning * This is the inbound data path. 37450f1702c5SYu Xiangning * First, we check to make sure the IP version number is correct, 37460f1702c5SYu Xiangning * and then pull the IP and UDP headers into the first mblk. 374745916cd2Sjpk */ 37487c478bd9Sstevel@tonic-gate 37490f1702c5SYu Xiangning /* Initialize regardless if ipversion is IPv4 or IPv6 */ 37500f1702c5SYu Xiangning ipp.ipp_fields = 0; 3751fc80c0dfSnordmark 37520f1702c5SYu Xiangning ipversion = IPH_HDR_VERSION(rptr); 3753fc80c0dfSnordmark 37540f1702c5SYu Xiangning rw_enter(&udp->udp_rwlock, RW_READER); 37550f1702c5SYu Xiangning udp_ip_rcv_options_len = udp->udp_ip_rcv_options_len; 37560f1702c5SYu Xiangning udp_bits = udp->udp_bits; 37570f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 3758fc80c0dfSnordmark 37590f1702c5SYu Xiangning switch (ipversion) { 37600f1702c5SYu Xiangning case IPV4_VERSION: 37610f1702c5SYu Xiangning ASSERT(MBLKL(mp) >= sizeof (ipha_t)); 37620f1702c5SYu Xiangning ASSERT(((ipha_t *)rptr)->ipha_protocol == IPPROTO_UDP); 37630f1702c5SYu Xiangning hdr_length = IPH_HDR_LENGTH(rptr) + UDPH_SIZE; 37640f1702c5SYu Xiangning opt_len = hdr_length - (IP_SIMPLE_HDR_LENGTH + UDPH_SIZE); 37650f1702c5SYu Xiangning if ((opt_len > 0 || udp_ip_rcv_options_len > 0) && 37660f1702c5SYu Xiangning udp->udp_family == AF_INET) { 37670f1702c5SYu Xiangning /* 37680f1702c5SYu Xiangning * Record/update udp_ip_rcv_options with the lock 37690f1702c5SYu Xiangning * held. Not needed for AF_INET6 sockets 37700f1702c5SYu Xiangning * since they don't support a getsockopt of IP_OPTIONS. 37710f1702c5SYu Xiangning */ 37720f1702c5SYu Xiangning rw_enter(&udp->udp_rwlock, RW_WRITER); 37730f1702c5SYu Xiangning udp_save_ip_rcv_opt(udp, rptr + IP_SIMPLE_HDR_LENGTH, 37740f1702c5SYu Xiangning opt_len); 3775fc80c0dfSnordmark rw_exit(&udp->udp_rwlock); 37767c478bd9Sstevel@tonic-gate } 37770f1702c5SYu Xiangning /* Handle IPV6_RECVPKTINFO even for IPv4 packet. */ 37780f1702c5SYu Xiangning if ((udp->udp_family == AF_INET6) && (pinfo != NULL) && 37790f1702c5SYu Xiangning udp->udp_ip_recvpktinfo) { 37800f1702c5SYu Xiangning if (pinfo->ip_pkt_flags & IPF_RECVIF) { 37810f1702c5SYu Xiangning ipp.ipp_fields |= IPPF_IFINDEX; 37820f1702c5SYu Xiangning ipp.ipp_ifindex = pinfo->ip_pkt_ifindex; 37830f1702c5SYu Xiangning } 37840f1702c5SYu Xiangning } 37850f1702c5SYu Xiangning break; 37860f1702c5SYu Xiangning case IPV6_VERSION: 37870f1702c5SYu Xiangning /* 37880f1702c5SYu Xiangning * IPv6 packets can only be received by applications 37890f1702c5SYu Xiangning * that are prepared to receive IPv6 addresses. 37900f1702c5SYu Xiangning * The IP fanout must ensure this. 37910f1702c5SYu Xiangning */ 37920f1702c5SYu Xiangning ASSERT(udp->udp_family == AF_INET6); 37937c478bd9Sstevel@tonic-gate 37940f1702c5SYu Xiangning ip6h = (ip6_t *)rptr; 37950f1702c5SYu Xiangning ASSERT((uchar_t *)&ip6h[1] <= mp->b_wptr); 37967c478bd9Sstevel@tonic-gate 37970f1702c5SYu Xiangning if (ip6h->ip6_nxt != IPPROTO_UDP) { 37980f1702c5SYu Xiangning uint8_t nexthdrp; 37990f1702c5SYu Xiangning /* Look for ifindex information */ 38000f1702c5SYu Xiangning if (ip6h->ip6_nxt == IPPROTO_RAW) { 38010f1702c5SYu Xiangning ip6i = (ip6i_t *)ip6h; 38020f1702c5SYu Xiangning if ((uchar_t *)&ip6i[1] > mp->b_wptr) 38030f1702c5SYu Xiangning goto tossit; 38047c478bd9Sstevel@tonic-gate 38050f1702c5SYu Xiangning if (ip6i->ip6i_flags & IP6I_IFINDEX) { 38060f1702c5SYu Xiangning ASSERT(ip6i->ip6i_ifindex != 0); 38070f1702c5SYu Xiangning ipp.ipp_fields |= IPPF_IFINDEX; 38080f1702c5SYu Xiangning ipp.ipp_ifindex = ip6i->ip6i_ifindex; 38090f1702c5SYu Xiangning } 38100f1702c5SYu Xiangning rptr = (uchar_t *)&ip6i[1]; 38110f1702c5SYu Xiangning mp->b_rptr = rptr; 38120f1702c5SYu Xiangning if (rptr == mp->b_wptr) { 38130f1702c5SYu Xiangning mp1 = mp->b_cont; 38140f1702c5SYu Xiangning freeb(mp); 38150f1702c5SYu Xiangning mp = mp1; 38160f1702c5SYu Xiangning rptr = mp->b_rptr; 38170f1702c5SYu Xiangning } 38180f1702c5SYu Xiangning if (MBLKL(mp) < (IPV6_HDR_LEN + UDPH_SIZE)) 38190f1702c5SYu Xiangning goto tossit; 38200f1702c5SYu Xiangning ip6h = (ip6_t *)rptr; 38210f1702c5SYu Xiangning mp_len = msgdsize(mp); 38220f1702c5SYu Xiangning } 38230f1702c5SYu Xiangning /* 38240f1702c5SYu Xiangning * Find any potentially interesting extension headers 38250f1702c5SYu Xiangning * as well as the length of the IPv6 + extension 38260f1702c5SYu Xiangning * headers. 38270f1702c5SYu Xiangning */ 38280f1702c5SYu Xiangning hdr_length = ip_find_hdr_v6(mp, ip6h, &ipp, &nexthdrp) + 38290f1702c5SYu Xiangning UDPH_SIZE; 38300f1702c5SYu Xiangning ASSERT(nexthdrp == IPPROTO_UDP); 38310f1702c5SYu Xiangning } else { 38320f1702c5SYu Xiangning hdr_length = IPV6_HDR_LEN + UDPH_SIZE; 38330f1702c5SYu Xiangning ip6i = NULL; 38347c478bd9Sstevel@tonic-gate } 38357c478bd9Sstevel@tonic-gate break; 38360f1702c5SYu Xiangning default: 38370f1702c5SYu Xiangning ASSERT(0); 38380f1702c5SYu Xiangning } 38390f1702c5SYu Xiangning 38400f1702c5SYu Xiangning /* 38410f1702c5SYu Xiangning * IP inspected the UDP header thus all of it must be in the mblk. 38420f1702c5SYu Xiangning * UDP length check is performed for IPv6 packets and IPv4 packets 38430f1702c5SYu Xiangning * to check if the size of the packet as specified 38440f1702c5SYu Xiangning * by the header is the same as the physical size of the packet. 38450f1702c5SYu Xiangning * FIXME? Didn't IP already check this? 38460f1702c5SYu Xiangning */ 38470f1702c5SYu Xiangning udpha = (udpha_t *)(rptr + (hdr_length - UDPH_SIZE)); 38480f1702c5SYu Xiangning if ((MBLKL(mp) < hdr_length) || 38490f1702c5SYu Xiangning (mp_len != (ntohs(udpha->uha_length) + hdr_length - UDPH_SIZE))) { 38500f1702c5SYu Xiangning goto tossit; 38517c478bd9Sstevel@tonic-gate } 38527c478bd9Sstevel@tonic-gate 3853ff550d0eSmasputra 38540f1702c5SYu Xiangning /* Walk past the headers unless IP_RECVHDR was set. */ 38550f1702c5SYu Xiangning if (!udp_bits.udpb_rcvhdr) { 38560f1702c5SYu Xiangning mp->b_rptr = rptr + hdr_length; 38570f1702c5SYu Xiangning mp_len -= hdr_length; 38580f1702c5SYu Xiangning } 38597c478bd9Sstevel@tonic-gate 38600f1702c5SYu Xiangning /* 38610f1702c5SYu Xiangning * This is the inbound data path. Packets are passed upstream as 38620f1702c5SYu Xiangning * T_UNITDATA_IND messages with full IP headers still attached. 38630f1702c5SYu Xiangning */ 38640f1702c5SYu Xiangning if (udp->udp_family == AF_INET) { 38650f1702c5SYu Xiangning sin_t *sin; 38660f1702c5SYu Xiangning 38670f1702c5SYu Xiangning ASSERT(IPH_HDR_VERSION((ipha_t *)rptr) == IPV4_VERSION); 38687c478bd9Sstevel@tonic-gate 38697c478bd9Sstevel@tonic-gate /* 38700f1702c5SYu Xiangning * Normally only send up the source address. 38710f1702c5SYu Xiangning * If IP_RECVDSTADDR is set we include the destination IP 38720f1702c5SYu Xiangning * address as an option. With IP_RECVOPTS we include all 38730f1702c5SYu Xiangning * the IP options. 38747c478bd9Sstevel@tonic-gate */ 38750f1702c5SYu Xiangning udi_size = sizeof (struct T_unitdata_ind) + sizeof (sin_t); 38760f1702c5SYu Xiangning if (udp_bits.udpb_recvdstaddr) { 38770f1702c5SYu Xiangning udi_size += sizeof (struct T_opthdr) + 38780f1702c5SYu Xiangning sizeof (struct in_addr); 38790f1702c5SYu Xiangning UDP_STAT(us, udp_in_recvdstaddr); 38807c478bd9Sstevel@tonic-gate } 38810f1702c5SYu Xiangning 38820f1702c5SYu Xiangning if (udp_bits.udpb_ip_recvpktinfo && (pinfo != NULL) && 38830f1702c5SYu Xiangning (pinfo->ip_pkt_flags & IPF_RECVADDR)) { 38840f1702c5SYu Xiangning udi_size += sizeof (struct T_opthdr) + 38850f1702c5SYu Xiangning sizeof (struct in_pktinfo); 38860f1702c5SYu Xiangning UDP_STAT(us, udp_ip_rcvpktinfo); 38870f1702c5SYu Xiangning } 38880f1702c5SYu Xiangning 38890f1702c5SYu Xiangning if ((udp_bits.udpb_recvopts) && opt_len > 0) { 38900f1702c5SYu Xiangning udi_size += sizeof (struct T_opthdr) + opt_len; 38910f1702c5SYu Xiangning UDP_STAT(us, udp_in_recvopts); 38927c478bd9Sstevel@tonic-gate } 38937c478bd9Sstevel@tonic-gate 38940f1702c5SYu Xiangning /* 38950f1702c5SYu Xiangning * If the IP_RECVSLLA or the IP_RECVIF is set then allocate 38960f1702c5SYu Xiangning * space accordingly 38970f1702c5SYu Xiangning */ 38980f1702c5SYu Xiangning if ((udp_bits.udpb_recvif) && (pinfo != NULL) && 38990f1702c5SYu Xiangning (pinfo->ip_pkt_flags & IPF_RECVIF)) { 39000f1702c5SYu Xiangning udi_size += sizeof (struct T_opthdr) + sizeof (uint_t); 39010f1702c5SYu Xiangning UDP_STAT(us, udp_in_recvif); 39027c478bd9Sstevel@tonic-gate } 39037c478bd9Sstevel@tonic-gate 39040f1702c5SYu Xiangning if ((udp_bits.udpb_recvslla) && (pinfo != NULL) && 39050f1702c5SYu Xiangning (pinfo->ip_pkt_flags & IPF_RECVSLLA)) { 39060f1702c5SYu Xiangning udi_size += sizeof (struct T_opthdr) + 39070f1702c5SYu Xiangning sizeof (struct sockaddr_dl); 39080f1702c5SYu Xiangning UDP_STAT(us, udp_in_recvslla); 39097c478bd9Sstevel@tonic-gate } 39100f1702c5SYu Xiangning 39110f1702c5SYu Xiangning if ((udp_bits.udpb_recvucred) && 39120f1702c5SYu Xiangning (cr = DB_CRED(mp)) != NULL) { 39130f1702c5SYu Xiangning udi_size += sizeof (struct T_opthdr) + ucredsize; 39140f1702c5SYu Xiangning cpid = DB_CPID(mp); 39150f1702c5SYu Xiangning UDP_STAT(us, udp_in_recvucred); 39160f1702c5SYu Xiangning } 39170f1702c5SYu Xiangning 39180f1702c5SYu Xiangning /* 39190f1702c5SYu Xiangning * If SO_TIMESTAMP is set allocate the appropriate sized 39200f1702c5SYu Xiangning * buffer. Since gethrestime() expects a pointer aligned 39210f1702c5SYu Xiangning * argument, we allocate space necessary for extra 39220f1702c5SYu Xiangning * alignment (even though it might not be used). 39230f1702c5SYu Xiangning */ 39240f1702c5SYu Xiangning if (udp_bits.udpb_timestamp) { 39250f1702c5SYu Xiangning udi_size += sizeof (struct T_opthdr) + 39260f1702c5SYu Xiangning sizeof (timestruc_t) + _POINTER_ALIGNMENT; 39270f1702c5SYu Xiangning UDP_STAT(us, udp_in_timestamp); 39280f1702c5SYu Xiangning } 39290f1702c5SYu Xiangning 39300f1702c5SYu Xiangning /* 39310f1702c5SYu Xiangning * If IP_RECVTTL is set allocate the appropriate sized buffer 39320f1702c5SYu Xiangning */ 39330f1702c5SYu Xiangning if (udp_bits.udpb_recvttl) { 39340f1702c5SYu Xiangning udi_size += sizeof (struct T_opthdr) + sizeof (uint8_t); 39350f1702c5SYu Xiangning UDP_STAT(us, udp_in_recvttl); 39360f1702c5SYu Xiangning } 39370f1702c5SYu Xiangning 39380f1702c5SYu Xiangning /* Allocate a message block for the T_UNITDATA_IND structure. */ 39390f1702c5SYu Xiangning mp1 = allocb(udi_size, BPRI_MED); 39400f1702c5SYu Xiangning if (mp1 == NULL) { 39410f1702c5SYu Xiangning freemsg(mp); 39420f1702c5SYu Xiangning if (options_mp != NULL) 39430f1702c5SYu Xiangning freeb(options_mp); 39440f1702c5SYu Xiangning BUMP_MIB(&us->us_udp_mib, udpInErrors); 39450f1702c5SYu Xiangning return; 39460f1702c5SYu Xiangning } 39470f1702c5SYu Xiangning mp1->b_cont = mp; 39480f1702c5SYu Xiangning mp = mp1; 39490f1702c5SYu Xiangning mp->b_datap->db_type = M_PROTO; 39500f1702c5SYu Xiangning tudi = (struct T_unitdata_ind *)mp->b_rptr; 39510f1702c5SYu Xiangning mp->b_wptr = (uchar_t *)tudi + udi_size; 39520f1702c5SYu Xiangning tudi->PRIM_type = T_UNITDATA_IND; 39530f1702c5SYu Xiangning tudi->SRC_length = sizeof (sin_t); 39540f1702c5SYu Xiangning tudi->SRC_offset = sizeof (struct T_unitdata_ind); 39550f1702c5SYu Xiangning tudi->OPT_offset = sizeof (struct T_unitdata_ind) + 39560f1702c5SYu Xiangning sizeof (sin_t); 39570f1702c5SYu Xiangning udi_size -= (sizeof (struct T_unitdata_ind) + sizeof (sin_t)); 39580f1702c5SYu Xiangning tudi->OPT_length = udi_size; 39590f1702c5SYu Xiangning sin = (sin_t *)&tudi[1]; 39600f1702c5SYu Xiangning sin->sin_addr.s_addr = ((ipha_t *)rptr)->ipha_src; 39610f1702c5SYu Xiangning sin->sin_port = udpha->uha_src_port; 39620f1702c5SYu Xiangning sin->sin_family = udp->udp_family; 39630f1702c5SYu Xiangning *(uint32_t *)&sin->sin_zero[0] = 0; 39640f1702c5SYu Xiangning *(uint32_t *)&sin->sin_zero[4] = 0; 39650f1702c5SYu Xiangning 39660f1702c5SYu Xiangning /* 39670f1702c5SYu Xiangning * Add options if IP_RECVDSTADDR, IP_RECVIF, IP_RECVSLLA or 39680f1702c5SYu Xiangning * IP_RECVTTL has been set. 39690f1702c5SYu Xiangning */ 39700f1702c5SYu Xiangning if (udi_size != 0) { 397145916cd2Sjpk /* 39720f1702c5SYu Xiangning * Copy in destination address before options to avoid 39730f1702c5SYu Xiangning * any padding issues. 397445916cd2Sjpk */ 39750f1702c5SYu Xiangning char *dstopt; 39760f1702c5SYu Xiangning 39770f1702c5SYu Xiangning dstopt = (char *)&sin[1]; 39780f1702c5SYu Xiangning if (udp_bits.udpb_recvdstaddr) { 39790f1702c5SYu Xiangning struct T_opthdr *toh; 39800f1702c5SYu Xiangning ipaddr_t *dstptr; 39810f1702c5SYu Xiangning 39820f1702c5SYu Xiangning toh = (struct T_opthdr *)dstopt; 39830f1702c5SYu Xiangning toh->level = IPPROTO_IP; 39840f1702c5SYu Xiangning toh->name = IP_RECVDSTADDR; 39850f1702c5SYu Xiangning toh->len = sizeof (struct T_opthdr) + 39860f1702c5SYu Xiangning sizeof (ipaddr_t); 39870f1702c5SYu Xiangning toh->status = 0; 39880f1702c5SYu Xiangning dstopt += sizeof (struct T_opthdr); 39890f1702c5SYu Xiangning dstptr = (ipaddr_t *)dstopt; 39900f1702c5SYu Xiangning *dstptr = ((ipha_t *)rptr)->ipha_dst; 39910f1702c5SYu Xiangning dstopt += sizeof (ipaddr_t); 39920f1702c5SYu Xiangning udi_size -= toh->len; 39930f1702c5SYu Xiangning } 39940f1702c5SYu Xiangning 39950f1702c5SYu Xiangning if (udp_bits.udpb_recvopts && opt_len > 0) { 39960f1702c5SYu Xiangning struct T_opthdr *toh; 39970f1702c5SYu Xiangning 39980f1702c5SYu Xiangning toh = (struct T_opthdr *)dstopt; 39990f1702c5SYu Xiangning toh->level = IPPROTO_IP; 40000f1702c5SYu Xiangning toh->name = IP_RECVOPTS; 40010f1702c5SYu Xiangning toh->len = sizeof (struct T_opthdr) + opt_len; 40020f1702c5SYu Xiangning toh->status = 0; 40030f1702c5SYu Xiangning dstopt += sizeof (struct T_opthdr); 40040f1702c5SYu Xiangning bcopy(rptr + IP_SIMPLE_HDR_LENGTH, dstopt, 40050f1702c5SYu Xiangning opt_len); 40060f1702c5SYu Xiangning dstopt += opt_len; 40070f1702c5SYu Xiangning udi_size -= toh->len; 40080f1702c5SYu Xiangning } 40090f1702c5SYu Xiangning 40100f1702c5SYu Xiangning if ((udp_bits.udpb_ip_recvpktinfo) && (pinfo != NULL) && 40110f1702c5SYu Xiangning (pinfo->ip_pkt_flags & IPF_RECVADDR)) { 40120f1702c5SYu Xiangning struct T_opthdr *toh; 40130f1702c5SYu Xiangning struct in_pktinfo *pktinfop; 40140f1702c5SYu Xiangning 40150f1702c5SYu Xiangning toh = (struct T_opthdr *)dstopt; 40160f1702c5SYu Xiangning toh->level = IPPROTO_IP; 40170f1702c5SYu Xiangning toh->name = IP_PKTINFO; 40180f1702c5SYu Xiangning toh->len = sizeof (struct T_opthdr) + 40190f1702c5SYu Xiangning sizeof (*pktinfop); 40200f1702c5SYu Xiangning toh->status = 0; 40210f1702c5SYu Xiangning dstopt += sizeof (struct T_opthdr); 40220f1702c5SYu Xiangning pktinfop = (struct in_pktinfo *)dstopt; 40230f1702c5SYu Xiangning pktinfop->ipi_ifindex = pinfo->ip_pkt_ifindex; 40240f1702c5SYu Xiangning pktinfop->ipi_spec_dst = 40250f1702c5SYu Xiangning pinfo->ip_pkt_match_addr; 40260f1702c5SYu Xiangning pktinfop->ipi_addr.s_addr = 40270f1702c5SYu Xiangning ((ipha_t *)rptr)->ipha_dst; 40280f1702c5SYu Xiangning 40290f1702c5SYu Xiangning dstopt += sizeof (struct in_pktinfo); 40300f1702c5SYu Xiangning udi_size -= toh->len; 40310f1702c5SYu Xiangning } 40320f1702c5SYu Xiangning 40330f1702c5SYu Xiangning if ((udp_bits.udpb_recvslla) && (pinfo != NULL) && 40340f1702c5SYu Xiangning (pinfo->ip_pkt_flags & IPF_RECVSLLA)) { 40350f1702c5SYu Xiangning 40360f1702c5SYu Xiangning struct T_opthdr *toh; 40370f1702c5SYu Xiangning struct sockaddr_dl *dstptr; 40380f1702c5SYu Xiangning 40390f1702c5SYu Xiangning toh = (struct T_opthdr *)dstopt; 40400f1702c5SYu Xiangning toh->level = IPPROTO_IP; 40410f1702c5SYu Xiangning toh->name = IP_RECVSLLA; 40420f1702c5SYu Xiangning toh->len = sizeof (struct T_opthdr) + 40430f1702c5SYu Xiangning sizeof (struct sockaddr_dl); 40440f1702c5SYu Xiangning toh->status = 0; 40450f1702c5SYu Xiangning dstopt += sizeof (struct T_opthdr); 40460f1702c5SYu Xiangning dstptr = (struct sockaddr_dl *)dstopt; 40470f1702c5SYu Xiangning bcopy(&pinfo->ip_pkt_slla, dstptr, 40480f1702c5SYu Xiangning sizeof (struct sockaddr_dl)); 40490f1702c5SYu Xiangning dstopt += sizeof (struct sockaddr_dl); 40500f1702c5SYu Xiangning udi_size -= toh->len; 40510f1702c5SYu Xiangning } 40520f1702c5SYu Xiangning 40530f1702c5SYu Xiangning if ((udp_bits.udpb_recvif) && (pinfo != NULL) && 40540f1702c5SYu Xiangning (pinfo->ip_pkt_flags & IPF_RECVIF)) { 40550f1702c5SYu Xiangning 40560f1702c5SYu Xiangning struct T_opthdr *toh; 40570f1702c5SYu Xiangning uint_t *dstptr; 40580f1702c5SYu Xiangning 40590f1702c5SYu Xiangning toh = (struct T_opthdr *)dstopt; 40600f1702c5SYu Xiangning toh->level = IPPROTO_IP; 40610f1702c5SYu Xiangning toh->name = IP_RECVIF; 40620f1702c5SYu Xiangning toh->len = sizeof (struct T_opthdr) + 40630f1702c5SYu Xiangning sizeof (uint_t); 40640f1702c5SYu Xiangning toh->status = 0; 40650f1702c5SYu Xiangning dstopt += sizeof (struct T_opthdr); 40660f1702c5SYu Xiangning dstptr = (uint_t *)dstopt; 40670f1702c5SYu Xiangning *dstptr = pinfo->ip_pkt_ifindex; 40680f1702c5SYu Xiangning dstopt += sizeof (uint_t); 40690f1702c5SYu Xiangning udi_size -= toh->len; 407045916cd2Sjpk } 40717c478bd9Sstevel@tonic-gate 40720f1702c5SYu Xiangning if (cr != NULL) { 40730f1702c5SYu Xiangning struct T_opthdr *toh; 4074fc80c0dfSnordmark 40750f1702c5SYu Xiangning toh = (struct T_opthdr *)dstopt; 40760f1702c5SYu Xiangning toh->level = SOL_SOCKET; 40770f1702c5SYu Xiangning toh->name = SCM_UCRED; 40780f1702c5SYu Xiangning toh->len = sizeof (struct T_opthdr) + ucredsize; 40790f1702c5SYu Xiangning toh->status = 0; 40800f1702c5SYu Xiangning dstopt += sizeof (struct T_opthdr); 40810f1702c5SYu Xiangning (void) cred2ucred(cr, cpid, dstopt, rcr); 40820f1702c5SYu Xiangning dstopt += ucredsize; 40830f1702c5SYu Xiangning udi_size -= toh->len; 40840f1702c5SYu Xiangning } 4085fc80c0dfSnordmark 40860f1702c5SYu Xiangning if (udp_bits.udpb_timestamp) { 40870f1702c5SYu Xiangning struct T_opthdr *toh; 4088fc80c0dfSnordmark 40890f1702c5SYu Xiangning toh = (struct T_opthdr *)dstopt; 40900f1702c5SYu Xiangning toh->level = SOL_SOCKET; 40910f1702c5SYu Xiangning toh->name = SCM_TIMESTAMP; 40920f1702c5SYu Xiangning toh->len = sizeof (struct T_opthdr) + 40930f1702c5SYu Xiangning sizeof (timestruc_t) + _POINTER_ALIGNMENT; 40940f1702c5SYu Xiangning toh->status = 0; 40950f1702c5SYu Xiangning dstopt += sizeof (struct T_opthdr); 40960f1702c5SYu Xiangning /* Align for gethrestime() */ 40970f1702c5SYu Xiangning dstopt = (char *)P2ROUNDUP((intptr_t)dstopt, 40980f1702c5SYu Xiangning sizeof (intptr_t)); 40990f1702c5SYu Xiangning gethrestime((timestruc_t *)dstopt); 41000f1702c5SYu Xiangning dstopt = (char *)toh + toh->len; 41010f1702c5SYu Xiangning udi_size -= toh->len; 41020f1702c5SYu Xiangning } 4103ff550d0eSmasputra 41040f1702c5SYu Xiangning /* 41050f1702c5SYu Xiangning * CAUTION: 41060f1702c5SYu Xiangning * Due to aligment issues 41070f1702c5SYu Xiangning * Processing of IP_RECVTTL option 41080f1702c5SYu Xiangning * should always be the last. Adding 41090f1702c5SYu Xiangning * any option processing after this will 41100f1702c5SYu Xiangning * cause alignment panic. 41110f1702c5SYu Xiangning */ 41120f1702c5SYu Xiangning if (udp_bits.udpb_recvttl) { 41130f1702c5SYu Xiangning struct T_opthdr *toh; 41140f1702c5SYu Xiangning uint8_t *dstptr; 41157c478bd9Sstevel@tonic-gate 41160f1702c5SYu Xiangning toh = (struct T_opthdr *)dstopt; 41170f1702c5SYu Xiangning toh->level = IPPROTO_IP; 41180f1702c5SYu Xiangning toh->name = IP_RECVTTL; 41190f1702c5SYu Xiangning toh->len = sizeof (struct T_opthdr) + 41200f1702c5SYu Xiangning sizeof (uint8_t); 41210f1702c5SYu Xiangning toh->status = 0; 41220f1702c5SYu Xiangning dstopt += sizeof (struct T_opthdr); 41230f1702c5SYu Xiangning dstptr = (uint8_t *)dstopt; 41240f1702c5SYu Xiangning *dstptr = ((ipha_t *)rptr)->ipha_ttl; 41250f1702c5SYu Xiangning dstopt += sizeof (uint8_t); 41260f1702c5SYu Xiangning udi_size -= toh->len; 41270f1702c5SYu Xiangning } 41280f1702c5SYu Xiangning 41290f1702c5SYu Xiangning /* Consumed all of allocated space */ 41300f1702c5SYu Xiangning ASSERT(udi_size == 0); 41317c478bd9Sstevel@tonic-gate } 41320f1702c5SYu Xiangning } else { 41330f1702c5SYu Xiangning sin6_t *sin6; 41340f1702c5SYu Xiangning 41357c478bd9Sstevel@tonic-gate /* 41360f1702c5SYu Xiangning * Handle both IPv4 and IPv6 packets for IPv6 sockets. 41377c478bd9Sstevel@tonic-gate * 41380f1702c5SYu Xiangning * Normally we only send up the address. If receiving of any 41390f1702c5SYu Xiangning * optional receive side information is enabled, we also send 41400f1702c5SYu Xiangning * that up as options. 41417c478bd9Sstevel@tonic-gate */ 41420f1702c5SYu Xiangning udi_size = sizeof (struct T_unitdata_ind) + sizeof (sin6_t); 41437c478bd9Sstevel@tonic-gate 41440f1702c5SYu Xiangning if (ipp.ipp_fields & (IPPF_HOPOPTS|IPPF_DSTOPTS|IPPF_RTDSTOPTS| 41450f1702c5SYu Xiangning IPPF_RTHDR|IPPF_IFINDEX)) { 41460f1702c5SYu Xiangning if ((udp_bits.udpb_ipv6_recvhopopts) && 41470f1702c5SYu Xiangning (ipp.ipp_fields & IPPF_HOPOPTS)) { 41480f1702c5SYu Xiangning size_t hlen; 41497c478bd9Sstevel@tonic-gate 41500f1702c5SYu Xiangning UDP_STAT(us, udp_in_recvhopopts); 41510f1702c5SYu Xiangning hlen = copy_hop_opts(&ipp, NULL); 41520f1702c5SYu Xiangning if (hlen == 0) 41530f1702c5SYu Xiangning ipp.ipp_fields &= ~IPPF_HOPOPTS; 41540f1702c5SYu Xiangning udi_size += hlen; 41557c478bd9Sstevel@tonic-gate } 41560f1702c5SYu Xiangning if (((udp_bits.udpb_ipv6_recvdstopts) || 41570f1702c5SYu Xiangning udp_bits.udpb_old_ipv6_recvdstopts) && 41580f1702c5SYu Xiangning (ipp.ipp_fields & IPPF_DSTOPTS)) { 41590f1702c5SYu Xiangning udi_size += sizeof (struct T_opthdr) + 41600f1702c5SYu Xiangning ipp.ipp_dstoptslen; 41610f1702c5SYu Xiangning UDP_STAT(us, udp_in_recvdstopts); 41627c478bd9Sstevel@tonic-gate } 41630f1702c5SYu Xiangning if ((((udp_bits.udpb_ipv6_recvdstopts) && 41640f1702c5SYu Xiangning udp_bits.udpb_ipv6_recvrthdr && 41650f1702c5SYu Xiangning (ipp.ipp_fields & IPPF_RTHDR)) || 41660f1702c5SYu Xiangning (udp_bits.udpb_ipv6_recvrthdrdstopts)) && 41670f1702c5SYu Xiangning (ipp.ipp_fields & IPPF_RTDSTOPTS)) { 41680f1702c5SYu Xiangning udi_size += sizeof (struct T_opthdr) + 41690f1702c5SYu Xiangning ipp.ipp_rtdstoptslen; 41700f1702c5SYu Xiangning UDP_STAT(us, udp_in_recvrtdstopts); 41717c478bd9Sstevel@tonic-gate } 41720f1702c5SYu Xiangning if ((udp_bits.udpb_ipv6_recvrthdr) && 41730f1702c5SYu Xiangning (ipp.ipp_fields & IPPF_RTHDR)) { 41740f1702c5SYu Xiangning udi_size += sizeof (struct T_opthdr) + 41750f1702c5SYu Xiangning ipp.ipp_rthdrlen; 41760f1702c5SYu Xiangning UDP_STAT(us, udp_in_recvrthdr); 41777c478bd9Sstevel@tonic-gate } 41780f1702c5SYu Xiangning if ((udp_bits.udpb_ip_recvpktinfo) && 41790f1702c5SYu Xiangning (ipp.ipp_fields & IPPF_IFINDEX)) { 41800f1702c5SYu Xiangning udi_size += sizeof (struct T_opthdr) + 41810f1702c5SYu Xiangning sizeof (struct in6_pktinfo); 41820f1702c5SYu Xiangning UDP_STAT(us, udp_in_recvpktinfo); 41830f1702c5SYu Xiangning } 41840f1702c5SYu Xiangning 41850f1702c5SYu Xiangning } 41860f1702c5SYu Xiangning if ((udp_bits.udpb_recvucred) && 41870f1702c5SYu Xiangning (cr = DB_CRED(mp)) != NULL) { 41880f1702c5SYu Xiangning udi_size += sizeof (struct T_opthdr) + ucredsize; 41890f1702c5SYu Xiangning cpid = DB_CPID(mp); 41900f1702c5SYu Xiangning UDP_STAT(us, udp_in_recvucred); 41910f1702c5SYu Xiangning } 41920f1702c5SYu Xiangning 41930f1702c5SYu Xiangning /* 41940f1702c5SYu Xiangning * If SO_TIMESTAMP is set allocate the appropriate sized 41950f1702c5SYu Xiangning * buffer. Since gethrestime() expects a pointer aligned 41960f1702c5SYu Xiangning * argument, we allocate space necessary for extra 41970f1702c5SYu Xiangning * alignment (even though it might not be used). 41980f1702c5SYu Xiangning */ 41990f1702c5SYu Xiangning if (udp_bits.udpb_timestamp) { 42000f1702c5SYu Xiangning udi_size += sizeof (struct T_opthdr) + 42010f1702c5SYu Xiangning sizeof (timestruc_t) + _POINTER_ALIGNMENT; 42020f1702c5SYu Xiangning UDP_STAT(us, udp_in_timestamp); 42030f1702c5SYu Xiangning } 420445916cd2Sjpk 42050f1702c5SYu Xiangning if (udp_bits.udpb_ipv6_recvhoplimit) { 42060f1702c5SYu Xiangning udi_size += sizeof (struct T_opthdr) + sizeof (int); 42070f1702c5SYu Xiangning UDP_STAT(us, udp_in_recvhoplimit); 42080f1702c5SYu Xiangning } 420945916cd2Sjpk 42100f1702c5SYu Xiangning if (udp_bits.udpb_ipv6_recvtclass) { 42110f1702c5SYu Xiangning udi_size += sizeof (struct T_opthdr) + sizeof (int); 42120f1702c5SYu Xiangning UDP_STAT(us, udp_in_recvtclass); 421345916cd2Sjpk } 42140f1702c5SYu Xiangning 42150f1702c5SYu Xiangning mp1 = allocb(udi_size, BPRI_MED); 42160f1702c5SYu Xiangning if (mp1 == NULL) { 42170f1702c5SYu Xiangning freemsg(mp); 42180f1702c5SYu Xiangning if (options_mp != NULL) 42190f1702c5SYu Xiangning freeb(options_mp); 42200f1702c5SYu Xiangning BUMP_MIB(&us->us_udp_mib, udpInErrors); 42210f1702c5SYu Xiangning return; 42227c478bd9Sstevel@tonic-gate } 42230f1702c5SYu Xiangning mp1->b_cont = mp; 42240f1702c5SYu Xiangning mp = mp1; 42250f1702c5SYu Xiangning mp->b_datap->db_type = M_PROTO; 42260f1702c5SYu Xiangning tudi = (struct T_unitdata_ind *)mp->b_rptr; 42270f1702c5SYu Xiangning mp->b_wptr = (uchar_t *)tudi + udi_size; 42280f1702c5SYu Xiangning tudi->PRIM_type = T_UNITDATA_IND; 42290f1702c5SYu Xiangning tudi->SRC_length = sizeof (sin6_t); 42300f1702c5SYu Xiangning tudi->SRC_offset = sizeof (struct T_unitdata_ind); 42310f1702c5SYu Xiangning tudi->OPT_offset = sizeof (struct T_unitdata_ind) + 42320f1702c5SYu Xiangning sizeof (sin6_t); 42330f1702c5SYu Xiangning udi_size -= (sizeof (struct T_unitdata_ind) + sizeof (sin6_t)); 42340f1702c5SYu Xiangning tudi->OPT_length = udi_size; 42350f1702c5SYu Xiangning sin6 = (sin6_t *)&tudi[1]; 42360f1702c5SYu Xiangning if (ipversion == IPV4_VERSION) { 42370f1702c5SYu Xiangning in6_addr_t v6dst; 42380f1702c5SYu Xiangning 42390f1702c5SYu Xiangning IN6_IPADDR_TO_V4MAPPED(((ipha_t *)rptr)->ipha_src, 42400f1702c5SYu Xiangning &sin6->sin6_addr); 42410f1702c5SYu Xiangning IN6_IPADDR_TO_V4MAPPED(((ipha_t *)rptr)->ipha_dst, 42420f1702c5SYu Xiangning &v6dst); 42430f1702c5SYu Xiangning sin6->sin6_flowinfo = 0; 42440f1702c5SYu Xiangning sin6->sin6_scope_id = 0; 42450f1702c5SYu Xiangning sin6->__sin6_src_id = ip_srcid_find_addr(&v6dst, 42460f1702c5SYu Xiangning connp->conn_zoneid, us->us_netstack); 42470f1702c5SYu Xiangning } else { 42480f1702c5SYu Xiangning sin6->sin6_addr = ip6h->ip6_src; 42490f1702c5SYu Xiangning /* No sin6_flowinfo per API */ 42500f1702c5SYu Xiangning sin6->sin6_flowinfo = 0; 42510f1702c5SYu Xiangning /* For link-scope source pass up scope id */ 42520f1702c5SYu Xiangning if ((ipp.ipp_fields & IPPF_IFINDEX) && 42530f1702c5SYu Xiangning IN6_IS_ADDR_LINKSCOPE(&ip6h->ip6_src)) 42540f1702c5SYu Xiangning sin6->sin6_scope_id = ipp.ipp_ifindex; 42550f1702c5SYu Xiangning else 42560f1702c5SYu Xiangning sin6->sin6_scope_id = 0; 42570f1702c5SYu Xiangning sin6->__sin6_src_id = ip_srcid_find_addr( 42580f1702c5SYu Xiangning &ip6h->ip6_dst, connp->conn_zoneid, 42590f1702c5SYu Xiangning us->us_netstack); 42607c478bd9Sstevel@tonic-gate } 42610f1702c5SYu Xiangning sin6->sin6_port = udpha->uha_src_port; 42620f1702c5SYu Xiangning sin6->sin6_family = udp->udp_family; 42630f1702c5SYu Xiangning 42640f1702c5SYu Xiangning if (udi_size != 0) { 42650f1702c5SYu Xiangning uchar_t *dstopt; 42660f1702c5SYu Xiangning 42670f1702c5SYu Xiangning dstopt = (uchar_t *)&sin6[1]; 42680f1702c5SYu Xiangning if ((udp_bits.udpb_ip_recvpktinfo) && 42690f1702c5SYu Xiangning (ipp.ipp_fields & IPPF_IFINDEX)) { 42700f1702c5SYu Xiangning struct T_opthdr *toh; 42710f1702c5SYu Xiangning struct in6_pktinfo *pkti; 42720f1702c5SYu Xiangning 42730f1702c5SYu Xiangning toh = (struct T_opthdr *)dstopt; 42740f1702c5SYu Xiangning toh->level = IPPROTO_IPV6; 42750f1702c5SYu Xiangning toh->name = IPV6_PKTINFO; 42760f1702c5SYu Xiangning toh->len = sizeof (struct T_opthdr) + 42770f1702c5SYu Xiangning sizeof (*pkti); 42780f1702c5SYu Xiangning toh->status = 0; 42790f1702c5SYu Xiangning dstopt += sizeof (struct T_opthdr); 42800f1702c5SYu Xiangning pkti = (struct in6_pktinfo *)dstopt; 42810f1702c5SYu Xiangning if (ipversion == IPV6_VERSION) 42820f1702c5SYu Xiangning pkti->ipi6_addr = ip6h->ip6_dst; 42830f1702c5SYu Xiangning else 42840f1702c5SYu Xiangning IN6_IPADDR_TO_V4MAPPED( 42850f1702c5SYu Xiangning ((ipha_t *)rptr)->ipha_dst, 42860f1702c5SYu Xiangning &pkti->ipi6_addr); 42870f1702c5SYu Xiangning pkti->ipi6_ifindex = ipp.ipp_ifindex; 42880f1702c5SYu Xiangning dstopt += sizeof (*pkti); 42890f1702c5SYu Xiangning udi_size -= toh->len; 42907c478bd9Sstevel@tonic-gate } 42910f1702c5SYu Xiangning if (udp_bits.udpb_ipv6_recvhoplimit) { 42920f1702c5SYu Xiangning struct T_opthdr *toh; 42937c478bd9Sstevel@tonic-gate 42940f1702c5SYu Xiangning toh = (struct T_opthdr *)dstopt; 42950f1702c5SYu Xiangning toh->level = IPPROTO_IPV6; 42960f1702c5SYu Xiangning toh->name = IPV6_HOPLIMIT; 42970f1702c5SYu Xiangning toh->len = sizeof (struct T_opthdr) + 42980f1702c5SYu Xiangning sizeof (uint_t); 42990f1702c5SYu Xiangning toh->status = 0; 43000f1702c5SYu Xiangning dstopt += sizeof (struct T_opthdr); 43010f1702c5SYu Xiangning if (ipversion == IPV6_VERSION) 43020f1702c5SYu Xiangning *(uint_t *)dstopt = ip6h->ip6_hops; 43030f1702c5SYu Xiangning else 43040f1702c5SYu Xiangning *(uint_t *)dstopt = 43050f1702c5SYu Xiangning ((ipha_t *)rptr)->ipha_ttl; 43060f1702c5SYu Xiangning dstopt += sizeof (uint_t); 43070f1702c5SYu Xiangning udi_size -= toh->len; 43087c478bd9Sstevel@tonic-gate } 43090f1702c5SYu Xiangning if (udp_bits.udpb_ipv6_recvtclass) { 43100f1702c5SYu Xiangning struct T_opthdr *toh; 431145916cd2Sjpk 43120f1702c5SYu Xiangning toh = (struct T_opthdr *)dstopt; 43130f1702c5SYu Xiangning toh->level = IPPROTO_IPV6; 43140f1702c5SYu Xiangning toh->name = IPV6_TCLASS; 43150f1702c5SYu Xiangning toh->len = sizeof (struct T_opthdr) + 43160f1702c5SYu Xiangning sizeof (uint_t); 43170f1702c5SYu Xiangning toh->status = 0; 43180f1702c5SYu Xiangning dstopt += sizeof (struct T_opthdr); 43190f1702c5SYu Xiangning if (ipversion == IPV6_VERSION) { 43200f1702c5SYu Xiangning *(uint_t *)dstopt = 43210f1702c5SYu Xiangning IPV6_FLOW_TCLASS(ip6h->ip6_flow); 43220f1702c5SYu Xiangning } else { 43230f1702c5SYu Xiangning ipha_t *ipha = (ipha_t *)rptr; 43240f1702c5SYu Xiangning *(uint_t *)dstopt = 43250f1702c5SYu Xiangning ipha->ipha_type_of_service; 43260f1702c5SYu Xiangning } 43270f1702c5SYu Xiangning dstopt += sizeof (uint_t); 43280f1702c5SYu Xiangning udi_size -= toh->len; 43290f1702c5SYu Xiangning } 43300f1702c5SYu Xiangning if ((udp_bits.udpb_ipv6_recvhopopts) && 43310f1702c5SYu Xiangning (ipp.ipp_fields & IPPF_HOPOPTS)) { 43320f1702c5SYu Xiangning size_t hlen; 433345916cd2Sjpk 43340f1702c5SYu Xiangning hlen = copy_hop_opts(&ipp, dstopt); 43350f1702c5SYu Xiangning dstopt += hlen; 43360f1702c5SYu Xiangning udi_size -= hlen; 43377c478bd9Sstevel@tonic-gate } 43380f1702c5SYu Xiangning if ((udp_bits.udpb_ipv6_recvdstopts) && 43390f1702c5SYu Xiangning (udp_bits.udpb_ipv6_recvrthdr) && 43400f1702c5SYu Xiangning (ipp.ipp_fields & IPPF_RTHDR) && 43410f1702c5SYu Xiangning (ipp.ipp_fields & IPPF_RTDSTOPTS)) { 43420f1702c5SYu Xiangning struct T_opthdr *toh; 43430f1702c5SYu Xiangning 43440f1702c5SYu Xiangning toh = (struct T_opthdr *)dstopt; 43450f1702c5SYu Xiangning toh->level = IPPROTO_IPV6; 43460f1702c5SYu Xiangning toh->name = IPV6_DSTOPTS; 43470f1702c5SYu Xiangning toh->len = sizeof (struct T_opthdr) + 43480f1702c5SYu Xiangning ipp.ipp_rtdstoptslen; 43490f1702c5SYu Xiangning toh->status = 0; 43500f1702c5SYu Xiangning dstopt += sizeof (struct T_opthdr); 43510f1702c5SYu Xiangning bcopy(ipp.ipp_rtdstopts, dstopt, 43520f1702c5SYu Xiangning ipp.ipp_rtdstoptslen); 43530f1702c5SYu Xiangning dstopt += ipp.ipp_rtdstoptslen; 43540f1702c5SYu Xiangning udi_size -= toh->len; 43550f1702c5SYu Xiangning } 43560f1702c5SYu Xiangning if ((udp_bits.udpb_ipv6_recvrthdr) && 43570f1702c5SYu Xiangning (ipp.ipp_fields & IPPF_RTHDR)) { 43580f1702c5SYu Xiangning struct T_opthdr *toh; 43590f1702c5SYu Xiangning 43600f1702c5SYu Xiangning toh = (struct T_opthdr *)dstopt; 43610f1702c5SYu Xiangning toh->level = IPPROTO_IPV6; 43620f1702c5SYu Xiangning toh->name = IPV6_RTHDR; 43630f1702c5SYu Xiangning toh->len = sizeof (struct T_opthdr) + 43640f1702c5SYu Xiangning ipp.ipp_rthdrlen; 43650f1702c5SYu Xiangning toh->status = 0; 43660f1702c5SYu Xiangning dstopt += sizeof (struct T_opthdr); 43670f1702c5SYu Xiangning bcopy(ipp.ipp_rthdr, dstopt, ipp.ipp_rthdrlen); 43680f1702c5SYu Xiangning dstopt += ipp.ipp_rthdrlen; 43690f1702c5SYu Xiangning udi_size -= toh->len; 43700f1702c5SYu Xiangning } 43710f1702c5SYu Xiangning if ((udp_bits.udpb_ipv6_recvdstopts) && 43720f1702c5SYu Xiangning (ipp.ipp_fields & IPPF_DSTOPTS)) { 43730f1702c5SYu Xiangning struct T_opthdr *toh; 43740f1702c5SYu Xiangning 43750f1702c5SYu Xiangning toh = (struct T_opthdr *)dstopt; 43760f1702c5SYu Xiangning toh->level = IPPROTO_IPV6; 43770f1702c5SYu Xiangning toh->name = IPV6_DSTOPTS; 43780f1702c5SYu Xiangning toh->len = sizeof (struct T_opthdr) + 43790f1702c5SYu Xiangning ipp.ipp_dstoptslen; 43800f1702c5SYu Xiangning toh->status = 0; 43810f1702c5SYu Xiangning dstopt += sizeof (struct T_opthdr); 43820f1702c5SYu Xiangning bcopy(ipp.ipp_dstopts, dstopt, 43830f1702c5SYu Xiangning ipp.ipp_dstoptslen); 43840f1702c5SYu Xiangning dstopt += ipp.ipp_dstoptslen; 43850f1702c5SYu Xiangning udi_size -= toh->len; 43860f1702c5SYu Xiangning } 43870f1702c5SYu Xiangning if (cr != NULL) { 43880f1702c5SYu Xiangning struct T_opthdr *toh; 43890f1702c5SYu Xiangning 43900f1702c5SYu Xiangning toh = (struct T_opthdr *)dstopt; 43910f1702c5SYu Xiangning toh->level = SOL_SOCKET; 43920f1702c5SYu Xiangning toh->name = SCM_UCRED; 43930f1702c5SYu Xiangning toh->len = sizeof (struct T_opthdr) + ucredsize; 43940f1702c5SYu Xiangning toh->status = 0; 43950f1702c5SYu Xiangning (void) cred2ucred(cr, cpid, &toh[1], rcr); 43960f1702c5SYu Xiangning dstopt += toh->len; 43970f1702c5SYu Xiangning udi_size -= toh->len; 43987c478bd9Sstevel@tonic-gate } 43990f1702c5SYu Xiangning if (udp_bits.udpb_timestamp) { 44000f1702c5SYu Xiangning struct T_opthdr *toh; 44010f1702c5SYu Xiangning 44020f1702c5SYu Xiangning toh = (struct T_opthdr *)dstopt; 44030f1702c5SYu Xiangning toh->level = SOL_SOCKET; 44040f1702c5SYu Xiangning toh->name = SCM_TIMESTAMP; 44050f1702c5SYu Xiangning toh->len = sizeof (struct T_opthdr) + 44060f1702c5SYu Xiangning sizeof (timestruc_t) + _POINTER_ALIGNMENT; 44070f1702c5SYu Xiangning toh->status = 0; 44080f1702c5SYu Xiangning dstopt += sizeof (struct T_opthdr); 44090f1702c5SYu Xiangning /* Align for gethrestime() */ 44100f1702c5SYu Xiangning dstopt = (uchar_t *)P2ROUNDUP((intptr_t)dstopt, 44110f1702c5SYu Xiangning sizeof (intptr_t)); 44120f1702c5SYu Xiangning gethrestime((timestruc_t *)dstopt); 44130f1702c5SYu Xiangning dstopt = (uchar_t *)toh + toh->len; 44140f1702c5SYu Xiangning udi_size -= toh->len; 44157c478bd9Sstevel@tonic-gate } 441619a30e1aSrshoaib 44170f1702c5SYu Xiangning /* Consumed all of allocated space */ 44180f1702c5SYu Xiangning ASSERT(udi_size == 0); 44190f1702c5SYu Xiangning } 44200f1702c5SYu Xiangning #undef sin6 44210f1702c5SYu Xiangning /* No IP_RECVDSTADDR for IPv6. */ 44220f1702c5SYu Xiangning } 442319a30e1aSrshoaib 44240f1702c5SYu Xiangning BUMP_MIB(&us->us_udp_mib, udpHCInDatagrams); 44250f1702c5SYu Xiangning if (options_mp != NULL) 44260f1702c5SYu Xiangning freeb(options_mp); 442719a30e1aSrshoaib 44280f1702c5SYu Xiangning if (IPCL_IS_NONSTR(connp)) { 44290f1702c5SYu Xiangning int error; 443019a30e1aSrshoaib 44310f1702c5SYu Xiangning if ((*connp->conn_upcalls->su_recv) 44320f1702c5SYu Xiangning (connp->conn_upper_handle, mp, msgdsize(mp), 0, &error, 44330f1702c5SYu Xiangning NULL) < 0) { 44340f1702c5SYu Xiangning mutex_enter(&udp->udp_recv_lock); 44350f1702c5SYu Xiangning if (error == ENOSPC) { 443619a30e1aSrshoaib /* 44370f1702c5SYu Xiangning * let's confirm while holding the lock 443819a30e1aSrshoaib */ 44390f1702c5SYu Xiangning if ((*connp->conn_upcalls->su_recv) 44400f1702c5SYu Xiangning (connp->conn_upper_handle, NULL, 0, 0, 44410f1702c5SYu Xiangning &error, NULL) < 0) { 44420f1702c5SYu Xiangning if (error == ENOSPC) { 44430f1702c5SYu Xiangning connp->conn_flow_cntrld = 44440f1702c5SYu Xiangning B_TRUE; 44450f1702c5SYu Xiangning } else { 44460f1702c5SYu Xiangning ASSERT(error == EOPNOTSUPP); 44470f1702c5SYu Xiangning } 44480f1702c5SYu Xiangning } 44490f1702c5SYu Xiangning mutex_exit(&udp->udp_recv_lock); 44500f1702c5SYu Xiangning } else { 44510f1702c5SYu Xiangning ASSERT(error == EOPNOTSUPP); 44520f1702c5SYu Xiangning udp_queue_fallback(udp, mp); 445319a30e1aSrshoaib } 44540f1702c5SYu Xiangning } 44550f1702c5SYu Xiangning } else { 44560f1702c5SYu Xiangning putnext(connp->conn_rq, mp); 44570f1702c5SYu Xiangning } 44580f1702c5SYu Xiangning ASSERT(MUTEX_NOT_HELD(&udp->udp_recv_lock)); 44590f1702c5SYu Xiangning return; 446019a30e1aSrshoaib 44610f1702c5SYu Xiangning tossit: 44620f1702c5SYu Xiangning freemsg(mp); 44630f1702c5SYu Xiangning if (options_mp != NULL) 44640f1702c5SYu Xiangning freeb(options_mp); 44650f1702c5SYu Xiangning BUMP_MIB(&us->us_udp_mib, udpInErrors); 44660f1702c5SYu Xiangning } 446719a30e1aSrshoaib 44680f1702c5SYu Xiangning /* 44690f1702c5SYu Xiangning * return SNMP stuff in buffer in mpdata. We don't hold any lock and report 44700f1702c5SYu Xiangning * information that can be changing beneath us. 44710f1702c5SYu Xiangning */ 44720f1702c5SYu Xiangning mblk_t * 44730f1702c5SYu Xiangning udp_snmp_get(queue_t *q, mblk_t *mpctl) 44740f1702c5SYu Xiangning { 44750f1702c5SYu Xiangning mblk_t *mpdata; 44760f1702c5SYu Xiangning mblk_t *mp_conn_ctl; 44770f1702c5SYu Xiangning mblk_t *mp_attr_ctl; 44780f1702c5SYu Xiangning mblk_t *mp6_conn_ctl; 44790f1702c5SYu Xiangning mblk_t *mp6_attr_ctl; 44800f1702c5SYu Xiangning mblk_t *mp_conn_tail; 44810f1702c5SYu Xiangning mblk_t *mp_attr_tail; 44820f1702c5SYu Xiangning mblk_t *mp6_conn_tail; 44830f1702c5SYu Xiangning mblk_t *mp6_attr_tail; 44840f1702c5SYu Xiangning struct opthdr *optp; 44850f1702c5SYu Xiangning mib2_udpEntry_t ude; 44860f1702c5SYu Xiangning mib2_udp6Entry_t ude6; 44870f1702c5SYu Xiangning mib2_transportMLPEntry_t mlp; 44880f1702c5SYu Xiangning int state; 44890f1702c5SYu Xiangning zoneid_t zoneid; 44900f1702c5SYu Xiangning int i; 44910f1702c5SYu Xiangning connf_t *connfp; 44920f1702c5SYu Xiangning conn_t *connp = Q_TO_CONN(q); 44930f1702c5SYu Xiangning int v4_conn_idx; 44940f1702c5SYu Xiangning int v6_conn_idx; 44950f1702c5SYu Xiangning boolean_t needattr; 44960f1702c5SYu Xiangning udp_t *udp; 44970f1702c5SYu Xiangning ip_stack_t *ipst = connp->conn_netstack->netstack_ip; 44980f1702c5SYu Xiangning udp_stack_t *us = connp->conn_netstack->netstack_udp; 44990f1702c5SYu Xiangning mblk_t *mp2ctl; 450019a30e1aSrshoaib 45010f1702c5SYu Xiangning /* 45020f1702c5SYu Xiangning * make a copy of the original message 45030f1702c5SYu Xiangning */ 45040f1702c5SYu Xiangning mp2ctl = copymsg(mpctl); 450519a30e1aSrshoaib 45060f1702c5SYu Xiangning mp_conn_ctl = mp_attr_ctl = mp6_conn_ctl = NULL; 45070f1702c5SYu Xiangning if (mpctl == NULL || 45080f1702c5SYu Xiangning (mpdata = mpctl->b_cont) == NULL || 45090f1702c5SYu Xiangning (mp_conn_ctl = copymsg(mpctl)) == NULL || 45100f1702c5SYu Xiangning (mp_attr_ctl = copymsg(mpctl)) == NULL || 45110f1702c5SYu Xiangning (mp6_conn_ctl = copymsg(mpctl)) == NULL || 45120f1702c5SYu Xiangning (mp6_attr_ctl = copymsg(mpctl)) == NULL) { 45130f1702c5SYu Xiangning freemsg(mp_conn_ctl); 45140f1702c5SYu Xiangning freemsg(mp_attr_ctl); 45150f1702c5SYu Xiangning freemsg(mp6_conn_ctl); 45160f1702c5SYu Xiangning freemsg(mpctl); 45170f1702c5SYu Xiangning freemsg(mp2ctl); 45180f1702c5SYu Xiangning return (0); 45190f1702c5SYu Xiangning } 452019a30e1aSrshoaib 45210f1702c5SYu Xiangning zoneid = connp->conn_zoneid; 45227c478bd9Sstevel@tonic-gate 45230f1702c5SYu Xiangning /* fixed length structure for IPv4 and IPv6 counters */ 45240f1702c5SYu Xiangning SET_MIB(us->us_udp_mib.udpEntrySize, sizeof (mib2_udpEntry_t)); 45250f1702c5SYu Xiangning SET_MIB(us->us_udp_mib.udp6EntrySize, sizeof (mib2_udp6Entry_t)); 45260f1702c5SYu Xiangning /* synchronize 64- and 32-bit counters */ 45270f1702c5SYu Xiangning SYNC32_MIB(&us->us_udp_mib, udpInDatagrams, udpHCInDatagrams); 45280f1702c5SYu Xiangning SYNC32_MIB(&us->us_udp_mib, udpOutDatagrams, udpHCOutDatagrams); 45290f1702c5SYu Xiangning 45300f1702c5SYu Xiangning optp = (struct opthdr *)&mpctl->b_rptr[sizeof (struct T_optmgmt_ack)]; 45310f1702c5SYu Xiangning optp->level = MIB2_UDP; 45320f1702c5SYu Xiangning optp->name = 0; 45330f1702c5SYu Xiangning (void) snmp_append_data(mpdata, (char *)&us->us_udp_mib, 45340f1702c5SYu Xiangning sizeof (us->us_udp_mib)); 45350f1702c5SYu Xiangning optp->len = msgdsize(mpdata); 45360f1702c5SYu Xiangning qreply(q, mpctl); 45370f1702c5SYu Xiangning 45380f1702c5SYu Xiangning mp_conn_tail = mp_attr_tail = mp6_conn_tail = mp6_attr_tail = NULL; 45390f1702c5SYu Xiangning v4_conn_idx = v6_conn_idx = 0; 45400f1702c5SYu Xiangning 45410f1702c5SYu Xiangning for (i = 0; i < CONN_G_HASH_SIZE; i++) { 45420f1702c5SYu Xiangning connfp = &ipst->ips_ipcl_globalhash_fanout[i]; 45430f1702c5SYu Xiangning connp = NULL; 45440f1702c5SYu Xiangning 45450f1702c5SYu Xiangning while ((connp = ipcl_get_next_conn(connfp, connp, 45460f1702c5SYu Xiangning IPCL_UDPCONN))) { 45470f1702c5SYu Xiangning udp = connp->conn_udp; 45480f1702c5SYu Xiangning if (zoneid != connp->conn_zoneid) 45490f1702c5SYu Xiangning continue; 45507c478bd9Sstevel@tonic-gate 45510f1702c5SYu Xiangning /* 45520f1702c5SYu Xiangning * Note that the port numbers are sent in 45530f1702c5SYu Xiangning * host byte order 45540f1702c5SYu Xiangning */ 45550f1702c5SYu Xiangning 45560f1702c5SYu Xiangning if (udp->udp_state == TS_UNBND) 45570f1702c5SYu Xiangning state = MIB2_UDP_unbound; 45580f1702c5SYu Xiangning else if (udp->udp_state == TS_IDLE) 45590f1702c5SYu Xiangning state = MIB2_UDP_idle; 45600f1702c5SYu Xiangning else if (udp->udp_state == TS_DATA_XFER) 45610f1702c5SYu Xiangning state = MIB2_UDP_connected; 45620f1702c5SYu Xiangning else 45630f1702c5SYu Xiangning state = MIB2_UDP_unknown; 45640f1702c5SYu Xiangning 45650f1702c5SYu Xiangning needattr = B_FALSE; 45660f1702c5SYu Xiangning bzero(&mlp, sizeof (mlp)); 45670f1702c5SYu Xiangning if (connp->conn_mlp_type != mlptSingle) { 45680f1702c5SYu Xiangning if (connp->conn_mlp_type == mlptShared || 45690f1702c5SYu Xiangning connp->conn_mlp_type == mlptBoth) 45700f1702c5SYu Xiangning mlp.tme_flags |= MIB2_TMEF_SHARED; 45710f1702c5SYu Xiangning if (connp->conn_mlp_type == mlptPrivate || 45720f1702c5SYu Xiangning connp->conn_mlp_type == mlptBoth) 45730f1702c5SYu Xiangning mlp.tme_flags |= MIB2_TMEF_PRIVATE; 45740f1702c5SYu Xiangning needattr = B_TRUE; 45757c478bd9Sstevel@tonic-gate } 45760f1702c5SYu Xiangning 45770f1702c5SYu Xiangning /* 45780f1702c5SYu Xiangning * Create an IPv4 table entry for IPv4 entries and also 45790f1702c5SYu Xiangning * any IPv6 entries which are bound to in6addr_any 45800f1702c5SYu Xiangning * (i.e. anything a IPv4 peer could connect/send to). 45810f1702c5SYu Xiangning */ 45820f1702c5SYu Xiangning if (udp->udp_ipversion == IPV4_VERSION || 45830f1702c5SYu Xiangning (udp->udp_state <= TS_IDLE && 45840f1702c5SYu Xiangning IN6_IS_ADDR_UNSPECIFIED(&udp->udp_v6src))) { 45850f1702c5SYu Xiangning ude.udpEntryInfo.ue_state = state; 45860f1702c5SYu Xiangning /* 45870f1702c5SYu Xiangning * If in6addr_any this will set it to 45880f1702c5SYu Xiangning * INADDR_ANY 45890f1702c5SYu Xiangning */ 45900f1702c5SYu Xiangning ude.udpLocalAddress = 45910f1702c5SYu Xiangning V4_PART_OF_V6(udp->udp_v6src); 45920f1702c5SYu Xiangning ude.udpLocalPort = ntohs(udp->udp_port); 45930f1702c5SYu Xiangning if (udp->udp_state == TS_DATA_XFER) { 45940f1702c5SYu Xiangning /* 45950f1702c5SYu Xiangning * Can potentially get here for 45960f1702c5SYu Xiangning * v6 socket if another process 45970f1702c5SYu Xiangning * (say, ping) has just done a 45980f1702c5SYu Xiangning * sendto(), changing the state 45990f1702c5SYu Xiangning * from the TS_IDLE above to 46000f1702c5SYu Xiangning * TS_DATA_XFER by the time we hit 46010f1702c5SYu Xiangning * this part of the code. 46020f1702c5SYu Xiangning */ 46030f1702c5SYu Xiangning ude.udpEntryInfo.ue_RemoteAddress = 46040f1702c5SYu Xiangning V4_PART_OF_V6(udp->udp_v6dst); 46050f1702c5SYu Xiangning ude.udpEntryInfo.ue_RemotePort = 46060f1702c5SYu Xiangning ntohs(udp->udp_dstport); 46077c478bd9Sstevel@tonic-gate } else { 46080f1702c5SYu Xiangning ude.udpEntryInfo.ue_RemoteAddress = 0; 46090f1702c5SYu Xiangning ude.udpEntryInfo.ue_RemotePort = 0; 46107c478bd9Sstevel@tonic-gate } 46110f1702c5SYu Xiangning 46120f1702c5SYu Xiangning /* 46130f1702c5SYu Xiangning * We make the assumption that all udp_t 46140f1702c5SYu Xiangning * structs will be created within an address 46150f1702c5SYu Xiangning * region no larger than 32-bits. 46160f1702c5SYu Xiangning */ 46170f1702c5SYu Xiangning ude.udpInstance = (uint32_t)(uintptr_t)udp; 46180f1702c5SYu Xiangning ude.udpCreationProcess = 46190f1702c5SYu Xiangning (udp->udp_open_pid < 0) ? 46200f1702c5SYu Xiangning MIB2_UNKNOWN_PROCESS : 46210f1702c5SYu Xiangning udp->udp_open_pid; 46220f1702c5SYu Xiangning ude.udpCreationTime = udp->udp_open_time; 46230f1702c5SYu Xiangning 46240f1702c5SYu Xiangning (void) snmp_append_data2(mp_conn_ctl->b_cont, 46250f1702c5SYu Xiangning &mp_conn_tail, (char *)&ude, sizeof (ude)); 46260f1702c5SYu Xiangning mlp.tme_connidx = v4_conn_idx++; 46270f1702c5SYu Xiangning if (needattr) 46280f1702c5SYu Xiangning (void) snmp_append_data2( 46290f1702c5SYu Xiangning mp_attr_ctl->b_cont, &mp_attr_tail, 46300f1702c5SYu Xiangning (char *)&mlp, sizeof (mlp)); 46317c478bd9Sstevel@tonic-gate } 46320f1702c5SYu Xiangning if (udp->udp_ipversion == IPV6_VERSION) { 46330f1702c5SYu Xiangning ude6.udp6EntryInfo.ue_state = state; 46340f1702c5SYu Xiangning ude6.udp6LocalAddress = udp->udp_v6src; 46350f1702c5SYu Xiangning ude6.udp6LocalPort = ntohs(udp->udp_port); 46360f1702c5SYu Xiangning ude6.udp6IfIndex = udp->udp_bound_if; 46370f1702c5SYu Xiangning if (udp->udp_state == TS_DATA_XFER) { 46380f1702c5SYu Xiangning ude6.udp6EntryInfo.ue_RemoteAddress = 46390f1702c5SYu Xiangning udp->udp_v6dst; 46400f1702c5SYu Xiangning ude6.udp6EntryInfo.ue_RemotePort = 46410f1702c5SYu Xiangning ntohs(udp->udp_dstport); 46427c478bd9Sstevel@tonic-gate } else { 46430f1702c5SYu Xiangning ude6.udp6EntryInfo.ue_RemoteAddress = 46440f1702c5SYu Xiangning sin6_null.sin6_addr; 46450f1702c5SYu Xiangning ude6.udp6EntryInfo.ue_RemotePort = 0; 46467c478bd9Sstevel@tonic-gate } 46470f1702c5SYu Xiangning /* 46480f1702c5SYu Xiangning * We make the assumption that all udp_t 46490f1702c5SYu Xiangning * structs will be created within an address 46500f1702c5SYu Xiangning * region no larger than 32-bits. 46510f1702c5SYu Xiangning */ 46520f1702c5SYu Xiangning ude6.udp6Instance = (uint32_t)(uintptr_t)udp; 46530f1702c5SYu Xiangning ude6.udp6CreationProcess = 46540f1702c5SYu Xiangning (udp->udp_open_pid < 0) ? 46550f1702c5SYu Xiangning MIB2_UNKNOWN_PROCESS : 46560f1702c5SYu Xiangning udp->udp_open_pid; 46570f1702c5SYu Xiangning ude6.udp6CreationTime = udp->udp_open_time; 46580f1702c5SYu Xiangning 46590f1702c5SYu Xiangning (void) snmp_append_data2(mp6_conn_ctl->b_cont, 46600f1702c5SYu Xiangning &mp6_conn_tail, (char *)&ude6, 46610f1702c5SYu Xiangning sizeof (ude6)); 46620f1702c5SYu Xiangning mlp.tme_connidx = v6_conn_idx++; 46630f1702c5SYu Xiangning if (needattr) 46640f1702c5SYu Xiangning (void) snmp_append_data2( 46650f1702c5SYu Xiangning mp6_attr_ctl->b_cont, 46660f1702c5SYu Xiangning &mp6_attr_tail, (char *)&mlp, 46670f1702c5SYu Xiangning sizeof (mlp)); 46687c478bd9Sstevel@tonic-gate } 46690f1702c5SYu Xiangning } 46700f1702c5SYu Xiangning } 46710f1702c5SYu Xiangning 46720f1702c5SYu Xiangning /* IPv4 UDP endpoints */ 46730f1702c5SYu Xiangning optp = (struct opthdr *)&mp_conn_ctl->b_rptr[ 46740f1702c5SYu Xiangning sizeof (struct T_optmgmt_ack)]; 46750f1702c5SYu Xiangning optp->level = MIB2_UDP; 46760f1702c5SYu Xiangning optp->name = MIB2_UDP_ENTRY; 46770f1702c5SYu Xiangning optp->len = msgdsize(mp_conn_ctl->b_cont); 46780f1702c5SYu Xiangning qreply(q, mp_conn_ctl); 46797c478bd9Sstevel@tonic-gate 46800f1702c5SYu Xiangning /* table of MLP attributes... */ 46810f1702c5SYu Xiangning optp = (struct opthdr *)&mp_attr_ctl->b_rptr[ 46820f1702c5SYu Xiangning sizeof (struct T_optmgmt_ack)]; 46830f1702c5SYu Xiangning optp->level = MIB2_UDP; 46840f1702c5SYu Xiangning optp->name = EXPER_XPORT_MLP; 46850f1702c5SYu Xiangning optp->len = msgdsize(mp_attr_ctl->b_cont); 46860f1702c5SYu Xiangning if (optp->len == 0) 46870f1702c5SYu Xiangning freemsg(mp_attr_ctl); 46880f1702c5SYu Xiangning else 46890f1702c5SYu Xiangning qreply(q, mp_attr_ctl); 46907c478bd9Sstevel@tonic-gate 46910f1702c5SYu Xiangning /* IPv6 UDP endpoints */ 46920f1702c5SYu Xiangning optp = (struct opthdr *)&mp6_conn_ctl->b_rptr[ 46930f1702c5SYu Xiangning sizeof (struct T_optmgmt_ack)]; 46940f1702c5SYu Xiangning optp->level = MIB2_UDP6; 46950f1702c5SYu Xiangning optp->name = MIB2_UDP6_ENTRY; 46960f1702c5SYu Xiangning optp->len = msgdsize(mp6_conn_ctl->b_cont); 46970f1702c5SYu Xiangning qreply(q, mp6_conn_ctl); 46987c478bd9Sstevel@tonic-gate 46990f1702c5SYu Xiangning /* table of MLP attributes... */ 47000f1702c5SYu Xiangning optp = (struct opthdr *)&mp6_attr_ctl->b_rptr[ 47010f1702c5SYu Xiangning sizeof (struct T_optmgmt_ack)]; 47020f1702c5SYu Xiangning optp->level = MIB2_UDP6; 47030f1702c5SYu Xiangning optp->name = EXPER_XPORT_MLP; 47040f1702c5SYu Xiangning optp->len = msgdsize(mp6_attr_ctl->b_cont); 47050f1702c5SYu Xiangning if (optp->len == 0) 47060f1702c5SYu Xiangning freemsg(mp6_attr_ctl); 47070f1702c5SYu Xiangning else 47080f1702c5SYu Xiangning qreply(q, mp6_attr_ctl); 47097c478bd9Sstevel@tonic-gate 47100f1702c5SYu Xiangning return (mp2ctl); 47110f1702c5SYu Xiangning } 47120f1702c5SYu Xiangning 47130f1702c5SYu Xiangning /* 47140f1702c5SYu Xiangning * Return 0 if invalid set request, 1 otherwise, including non-udp requests. 47150f1702c5SYu Xiangning * NOTE: Per MIB-II, UDP has no writable data. 47160f1702c5SYu Xiangning * TODO: If this ever actually tries to set anything, it needs to be 47170f1702c5SYu Xiangning * to do the appropriate locking. 47180f1702c5SYu Xiangning */ 47190f1702c5SYu Xiangning /* ARGSUSED */ 47200f1702c5SYu Xiangning int 47210f1702c5SYu Xiangning udp_snmp_set(queue_t *q, t_scalar_t level, t_scalar_t name, 47220f1702c5SYu Xiangning uchar_t *ptr, int len) 47230f1702c5SYu Xiangning { 47240f1702c5SYu Xiangning switch (level) { 47250f1702c5SYu Xiangning case MIB2_UDP: 47260f1702c5SYu Xiangning return (0); 47270f1702c5SYu Xiangning default: 47280f1702c5SYu Xiangning return (1); 47290f1702c5SYu Xiangning } 47300f1702c5SYu Xiangning } 47310f1702c5SYu Xiangning 47320f1702c5SYu Xiangning static void 47330f1702c5SYu Xiangning udp_report_item(mblk_t *mp, udp_t *udp) 47340f1702c5SYu Xiangning { 47350f1702c5SYu Xiangning char *state; 47360f1702c5SYu Xiangning char addrbuf1[INET6_ADDRSTRLEN]; 47370f1702c5SYu Xiangning char addrbuf2[INET6_ADDRSTRLEN]; 47380f1702c5SYu Xiangning uint_t print_len, buf_len; 47390f1702c5SYu Xiangning 47400f1702c5SYu Xiangning buf_len = mp->b_datap->db_lim - mp->b_wptr; 47410f1702c5SYu Xiangning ASSERT(buf_len >= 0); 47420f1702c5SYu Xiangning if (buf_len == 0) 47430f1702c5SYu Xiangning return; 47440f1702c5SYu Xiangning 47450f1702c5SYu Xiangning if (udp->udp_state == TS_UNBND) 47460f1702c5SYu Xiangning state = "UNBOUND"; 47470f1702c5SYu Xiangning else if (udp->udp_state == TS_IDLE) 47480f1702c5SYu Xiangning state = "IDLE"; 47490f1702c5SYu Xiangning else if (udp->udp_state == TS_DATA_XFER) 47500f1702c5SYu Xiangning state = "CONNECTED"; 47510f1702c5SYu Xiangning else 47520f1702c5SYu Xiangning state = "UnkState"; 47530f1702c5SYu Xiangning print_len = snprintf((char *)mp->b_wptr, buf_len, 47540f1702c5SYu Xiangning MI_COL_PTRFMT_STR "%4d %5u %s %s %5u %s\n", 47550f1702c5SYu Xiangning (void *)udp, udp->udp_connp->conn_zoneid, ntohs(udp->udp_port), 47560f1702c5SYu Xiangning inet_ntop(AF_INET6, &udp->udp_v6src, addrbuf1, sizeof (addrbuf1)), 47570f1702c5SYu Xiangning inet_ntop(AF_INET6, &udp->udp_v6dst, addrbuf2, sizeof (addrbuf2)), 47580f1702c5SYu Xiangning ntohs(udp->udp_dstport), state); 47590f1702c5SYu Xiangning if (print_len < buf_len) { 47600f1702c5SYu Xiangning mp->b_wptr += print_len; 47610f1702c5SYu Xiangning } else { 47620f1702c5SYu Xiangning mp->b_wptr += buf_len; 47630f1702c5SYu Xiangning } 47640f1702c5SYu Xiangning } 47650f1702c5SYu Xiangning 47660f1702c5SYu Xiangning /* Report for ndd "udp_status" */ 47670f1702c5SYu Xiangning /* ARGSUSED */ 47680f1702c5SYu Xiangning static int 47690f1702c5SYu Xiangning udp_status_report(queue_t *q, mblk_t *mp, caddr_t cp, cred_t *cr) 47700f1702c5SYu Xiangning { 47710f1702c5SYu Xiangning zoneid_t zoneid; 47720f1702c5SYu Xiangning connf_t *connfp; 47730f1702c5SYu Xiangning conn_t *connp = Q_TO_CONN(q); 47740f1702c5SYu Xiangning udp_t *udp = connp->conn_udp; 47750f1702c5SYu Xiangning int i; 47760f1702c5SYu Xiangning udp_stack_t *us = udp->udp_us; 47770f1702c5SYu Xiangning ip_stack_t *ipst = connp->conn_netstack->netstack_ip; 47780f1702c5SYu Xiangning 47790f1702c5SYu Xiangning /* 47800f1702c5SYu Xiangning * Because of the ndd constraint, at most we can have 64K buffer 47810f1702c5SYu Xiangning * to put in all UDP info. So to be more efficient, just 47820f1702c5SYu Xiangning * allocate a 64K buffer here, assuming we need that large buffer. 47830f1702c5SYu Xiangning * This may be a problem as any user can read udp_status. Therefore 47840f1702c5SYu Xiangning * we limit the rate of doing this using us_ndd_get_info_interval. 47850f1702c5SYu Xiangning * This should be OK as normal users should not do this too often. 47860f1702c5SYu Xiangning */ 47870f1702c5SYu Xiangning if (cr == NULL || secpolicy_ip_config(cr, B_TRUE) != 0) { 47880f1702c5SYu Xiangning if (ddi_get_lbolt() - us->us_last_ndd_get_info_time < 47890f1702c5SYu Xiangning drv_usectohz(us->us_ndd_get_info_interval * 1000)) { 47900f1702c5SYu Xiangning (void) mi_mpprintf(mp, NDD_TOO_QUICK_MSG); 47910f1702c5SYu Xiangning return (0); 47920f1702c5SYu Xiangning } 47930f1702c5SYu Xiangning } 47940f1702c5SYu Xiangning if ((mp->b_cont = allocb(ND_MAX_BUF_LEN, BPRI_HI)) == NULL) { 47950f1702c5SYu Xiangning /* The following may work even if we cannot get a large buf. */ 47960f1702c5SYu Xiangning (void) mi_mpprintf(mp, NDD_OUT_OF_BUF_MSG); 47970f1702c5SYu Xiangning return (0); 47980f1702c5SYu Xiangning } 47990f1702c5SYu Xiangning (void) mi_mpprintf(mp, 48000f1702c5SYu Xiangning "UDP " MI_COL_HDRPAD_STR 48010f1702c5SYu Xiangning /* 12345678[89ABCDEF] */ 48020f1702c5SYu Xiangning " zone lport src addr dest addr port state"); 48030f1702c5SYu Xiangning /* 1234 12345 xxx.xxx.xxx.xxx xxx.xxx.xxx.xxx 12345 UNBOUND */ 48040f1702c5SYu Xiangning 48050f1702c5SYu Xiangning zoneid = connp->conn_zoneid; 48060f1702c5SYu Xiangning 48070f1702c5SYu Xiangning for (i = 0; i < CONN_G_HASH_SIZE; i++) { 48080f1702c5SYu Xiangning connfp = &ipst->ips_ipcl_globalhash_fanout[i]; 48090f1702c5SYu Xiangning connp = NULL; 48100f1702c5SYu Xiangning 48110f1702c5SYu Xiangning while ((connp = ipcl_get_next_conn(connfp, connp, 48120f1702c5SYu Xiangning IPCL_UDPCONN))) { 48130f1702c5SYu Xiangning udp = connp->conn_udp; 48140f1702c5SYu Xiangning if (zoneid != GLOBAL_ZONEID && 48150f1702c5SYu Xiangning zoneid != connp->conn_zoneid) 48160f1702c5SYu Xiangning continue; 48170f1702c5SYu Xiangning 48180f1702c5SYu Xiangning udp_report_item(mp->b_cont, udp); 48190f1702c5SYu Xiangning } 48200f1702c5SYu Xiangning } 48210f1702c5SYu Xiangning us->us_last_ndd_get_info_time = ddi_get_lbolt(); 48220f1702c5SYu Xiangning return (0); 48230f1702c5SYu Xiangning } 48240f1702c5SYu Xiangning 48250f1702c5SYu Xiangning /* 48260f1702c5SYu Xiangning * This routine creates a T_UDERROR_IND message and passes it upstream. 48270f1702c5SYu Xiangning * The address and options are copied from the T_UNITDATA_REQ message 48280f1702c5SYu Xiangning * passed in mp. This message is freed. 48290f1702c5SYu Xiangning */ 48300f1702c5SYu Xiangning static void 48310f1702c5SYu Xiangning udp_ud_err(queue_t *q, mblk_t *mp, uchar_t *destaddr, t_scalar_t destlen, 48320f1702c5SYu Xiangning t_scalar_t err) 48330f1702c5SYu Xiangning { 48340f1702c5SYu Xiangning struct T_unitdata_req *tudr; 48350f1702c5SYu Xiangning mblk_t *mp1; 48360f1702c5SYu Xiangning uchar_t *optaddr; 48370f1702c5SYu Xiangning t_scalar_t optlen; 48380f1702c5SYu Xiangning 48390f1702c5SYu Xiangning if (DB_TYPE(mp) == M_DATA) { 48400f1702c5SYu Xiangning ASSERT(destaddr != NULL && destlen != 0); 48410f1702c5SYu Xiangning optaddr = NULL; 48420f1702c5SYu Xiangning optlen = 0; 48430f1702c5SYu Xiangning } else { 48440f1702c5SYu Xiangning if ((mp->b_wptr < mp->b_rptr) || 48450f1702c5SYu Xiangning (MBLKL(mp)) < sizeof (struct T_unitdata_req)) { 48460f1702c5SYu Xiangning goto done; 48470f1702c5SYu Xiangning } 48480f1702c5SYu Xiangning tudr = (struct T_unitdata_req *)mp->b_rptr; 48490f1702c5SYu Xiangning destaddr = mp->b_rptr + tudr->DEST_offset; 48500f1702c5SYu Xiangning if (destaddr < mp->b_rptr || destaddr >= mp->b_wptr || 48510f1702c5SYu Xiangning destaddr + tudr->DEST_length < mp->b_rptr || 48520f1702c5SYu Xiangning destaddr + tudr->DEST_length > mp->b_wptr) { 48530f1702c5SYu Xiangning goto done; 48540f1702c5SYu Xiangning } 48550f1702c5SYu Xiangning optaddr = mp->b_rptr + tudr->OPT_offset; 48560f1702c5SYu Xiangning if (optaddr < mp->b_rptr || optaddr >= mp->b_wptr || 48570f1702c5SYu Xiangning optaddr + tudr->OPT_length < mp->b_rptr || 48580f1702c5SYu Xiangning optaddr + tudr->OPT_length > mp->b_wptr) { 48590f1702c5SYu Xiangning goto done; 48600f1702c5SYu Xiangning } 48610f1702c5SYu Xiangning destlen = tudr->DEST_length; 48620f1702c5SYu Xiangning optlen = tudr->OPT_length; 48630f1702c5SYu Xiangning } 48647c478bd9Sstevel@tonic-gate 48650f1702c5SYu Xiangning mp1 = mi_tpi_uderror_ind((char *)destaddr, destlen, 48660f1702c5SYu Xiangning (char *)optaddr, optlen, err); 48670f1702c5SYu Xiangning if (mp1 != NULL) 48680f1702c5SYu Xiangning qreply(q, mp1); 48697c478bd9Sstevel@tonic-gate 48700f1702c5SYu Xiangning done: 48710f1702c5SYu Xiangning freemsg(mp); 48720f1702c5SYu Xiangning } 48737c478bd9Sstevel@tonic-gate 48740f1702c5SYu Xiangning /* 48750f1702c5SYu Xiangning * This routine removes a port number association from a stream. It 48760f1702c5SYu Xiangning * is called by udp_wput to handle T_UNBIND_REQ messages. 48770f1702c5SYu Xiangning */ 48780f1702c5SYu Xiangning static void 48790f1702c5SYu Xiangning udp_tpi_unbind(queue_t *q, mblk_t *mp) 48800f1702c5SYu Xiangning { 48810f1702c5SYu Xiangning conn_t *connp = Q_TO_CONN(q); 48820f1702c5SYu Xiangning int error; 48837c478bd9Sstevel@tonic-gate 48840f1702c5SYu Xiangning error = udp_do_unbind(connp); 48850f1702c5SYu Xiangning if (error) { 48860f1702c5SYu Xiangning if (error < 0) 48870f1702c5SYu Xiangning udp_err_ack(q, mp, -error, 0); 48880f1702c5SYu Xiangning else 48890f1702c5SYu Xiangning udp_err_ack(q, mp, TSYSERR, error); 48900f1702c5SYu Xiangning return; 48910f1702c5SYu Xiangning } 48920f1702c5SYu Xiangning 48930f1702c5SYu Xiangning mp = mi_tpi_ok_ack_alloc(mp); 48940f1702c5SYu Xiangning ASSERT(mp != NULL); 48950f1702c5SYu Xiangning ASSERT(((struct T_ok_ack *)mp->b_rptr)->PRIM_type == T_OK_ACK); 48960f1702c5SYu Xiangning qreply(q, mp); 48970f1702c5SYu Xiangning } 48980f1702c5SYu Xiangning 48990f1702c5SYu Xiangning /* 49000f1702c5SYu Xiangning * Don't let port fall into the privileged range. 49010f1702c5SYu Xiangning * Since the extra privileged ports can be arbitrary we also 49020f1702c5SYu Xiangning * ensure that we exclude those from consideration. 49030f1702c5SYu Xiangning * us->us_epriv_ports is not sorted thus we loop over it until 49040f1702c5SYu Xiangning * there are no changes. 49050f1702c5SYu Xiangning */ 49060f1702c5SYu Xiangning static in_port_t 49070f1702c5SYu Xiangning udp_update_next_port(udp_t *udp, in_port_t port, boolean_t random) 49080f1702c5SYu Xiangning { 49090f1702c5SYu Xiangning int i; 49100f1702c5SYu Xiangning in_port_t nextport; 49110f1702c5SYu Xiangning boolean_t restart = B_FALSE; 49120f1702c5SYu Xiangning udp_stack_t *us = udp->udp_us; 49130f1702c5SYu Xiangning 49140f1702c5SYu Xiangning if (random && udp_random_anon_port != 0) { 49150f1702c5SYu Xiangning (void) random_get_pseudo_bytes((uint8_t *)&port, 49160f1702c5SYu Xiangning sizeof (in_port_t)); 49170f1702c5SYu Xiangning /* 49180f1702c5SYu Xiangning * Unless changed by a sys admin, the smallest anon port 49190f1702c5SYu Xiangning * is 32768 and the largest anon port is 65535. It is 49200f1702c5SYu Xiangning * very likely (50%) for the random port to be smaller 49210f1702c5SYu Xiangning * than the smallest anon port. When that happens, 49220f1702c5SYu Xiangning * add port % (anon port range) to the smallest anon 49230f1702c5SYu Xiangning * port to get the random port. It should fall into the 49240f1702c5SYu Xiangning * valid anon port range. 49250f1702c5SYu Xiangning */ 49260f1702c5SYu Xiangning if (port < us->us_smallest_anon_port) { 49270f1702c5SYu Xiangning port = us->us_smallest_anon_port + 49280f1702c5SYu Xiangning port % (us->us_largest_anon_port - 49290f1702c5SYu Xiangning us->us_smallest_anon_port); 49307c478bd9Sstevel@tonic-gate } 49310f1702c5SYu Xiangning } 49320f1702c5SYu Xiangning 49330f1702c5SYu Xiangning retry: 49340f1702c5SYu Xiangning if (port < us->us_smallest_anon_port) 49350f1702c5SYu Xiangning port = us->us_smallest_anon_port; 49360f1702c5SYu Xiangning 49370f1702c5SYu Xiangning if (port > us->us_largest_anon_port) { 49380f1702c5SYu Xiangning port = us->us_smallest_anon_port; 49390f1702c5SYu Xiangning if (restart) 49400f1702c5SYu Xiangning return (0); 49410f1702c5SYu Xiangning restart = B_TRUE; 49420f1702c5SYu Xiangning } 49430f1702c5SYu Xiangning 49440f1702c5SYu Xiangning if (port < us->us_smallest_nonpriv_port) 49450f1702c5SYu Xiangning port = us->us_smallest_nonpriv_port; 49467c478bd9Sstevel@tonic-gate 49470f1702c5SYu Xiangning for (i = 0; i < us->us_num_epriv_ports; i++) { 49480f1702c5SYu Xiangning if (port == us->us_epriv_ports[i]) { 49490f1702c5SYu Xiangning port++; 49507c478bd9Sstevel@tonic-gate /* 49510f1702c5SYu Xiangning * Make sure that the port is in the 49520f1702c5SYu Xiangning * valid range. 49537c478bd9Sstevel@tonic-gate */ 49540f1702c5SYu Xiangning goto retry; 49550f1702c5SYu Xiangning } 49560f1702c5SYu Xiangning } 49577c478bd9Sstevel@tonic-gate 49580f1702c5SYu Xiangning if (is_system_labeled() && 49590f1702c5SYu Xiangning (nextport = tsol_next_port(crgetzone(udp->udp_connp->conn_cred), 49600f1702c5SYu Xiangning port, IPPROTO_UDP, B_TRUE)) != 0) { 49610f1702c5SYu Xiangning port = nextport; 49620f1702c5SYu Xiangning goto retry; 49630f1702c5SYu Xiangning } 49647c478bd9Sstevel@tonic-gate 49650f1702c5SYu Xiangning return (port); 49660f1702c5SYu Xiangning } 4967ff550d0eSmasputra 49680f1702c5SYu Xiangning static int 49698e4b770fSLu Huafeng udp_update_label(queue_t *wq, mblk_t *mp, ipaddr_t dst, 49708e4b770fSLu Huafeng boolean_t *update_lastdst) 49710f1702c5SYu Xiangning { 49720f1702c5SYu Xiangning int err; 49730f1702c5SYu Xiangning uchar_t opt_storage[IP_MAX_OPT_LENGTH]; 49740f1702c5SYu Xiangning udp_t *udp = Q_TO_UDP(wq); 49750f1702c5SYu Xiangning udp_stack_t *us = udp->udp_us; 49767c478bd9Sstevel@tonic-gate 49770f1702c5SYu Xiangning err = tsol_compute_label(DB_CREDDEF(mp, udp->udp_connp->conn_cred), dst, 49780f1702c5SYu Xiangning opt_storage, udp->udp_connp->conn_mac_exempt, 49790f1702c5SYu Xiangning us->us_netstack->netstack_ip); 49800f1702c5SYu Xiangning if (err == 0) { 49810f1702c5SYu Xiangning err = tsol_update_options(&udp->udp_ip_snd_options, 49820f1702c5SYu Xiangning &udp->udp_ip_snd_options_len, &udp->udp_label_len, 49830f1702c5SYu Xiangning opt_storage); 49840f1702c5SYu Xiangning } 49850f1702c5SYu Xiangning if (err != 0) { 49860f1702c5SYu Xiangning DTRACE_PROBE4( 49870f1702c5SYu Xiangning tx__ip__log__info__updatelabel__udp, 49880f1702c5SYu Xiangning char *, "queue(1) failed to update options(2) on mp(3)", 49890f1702c5SYu Xiangning queue_t *, wq, char *, opt_storage, mblk_t *, mp); 49900f1702c5SYu Xiangning } else { 49918e4b770fSLu Huafeng *update_lastdst = B_TRUE; 49920f1702c5SYu Xiangning } 49930f1702c5SYu Xiangning return (err); 49940f1702c5SYu Xiangning } 49957c478bd9Sstevel@tonic-gate 49960f1702c5SYu Xiangning static mblk_t * 49970f1702c5SYu Xiangning udp_output_v4(conn_t *connp, mblk_t *mp, ipaddr_t v4dst, uint16_t port, 49980f1702c5SYu Xiangning uint_t srcid, int *error, boolean_t insert_spi, struct nmsghdr *msg, 49990f1702c5SYu Xiangning cred_t *cr, pid_t pid) 50000f1702c5SYu Xiangning { 50010f1702c5SYu Xiangning udp_t *udp = connp->conn_udp; 50020f1702c5SYu Xiangning mblk_t *mp1 = mp; 50030f1702c5SYu Xiangning mblk_t *mp2; 50040f1702c5SYu Xiangning ipha_t *ipha; 50050f1702c5SYu Xiangning int ip_hdr_length; 50060f1702c5SYu Xiangning uint32_t ip_len; 50070f1702c5SYu Xiangning udpha_t *udpha; 50080f1702c5SYu Xiangning boolean_t lock_held = B_FALSE; 50090f1702c5SYu Xiangning in_port_t uha_src_port; 50100f1702c5SYu Xiangning udpattrs_t attrs; 50110f1702c5SYu Xiangning uchar_t ip_snd_opt[IP_MAX_OPT_LENGTH]; 50120f1702c5SYu Xiangning uint32_t ip_snd_opt_len = 0; 50130f1702c5SYu Xiangning ip4_pkt_t pktinfo; 50140f1702c5SYu Xiangning ip4_pkt_t *pktinfop = &pktinfo; 50150f1702c5SYu Xiangning ip_opt_info_t optinfo; 50160f1702c5SYu Xiangning ip_stack_t *ipst = connp->conn_netstack->netstack_ip; 50170f1702c5SYu Xiangning udp_stack_t *us = udp->udp_us; 50180f1702c5SYu Xiangning ipsec_stack_t *ipss = ipst->ips_netstack->netstack_ipsec; 50190f1702c5SYu Xiangning queue_t *q = connp->conn_wq; 50200f1702c5SYu Xiangning ire_t *ire; 50218e4b770fSLu Huafeng in6_addr_t v6dst; 50228e4b770fSLu Huafeng boolean_t update_lastdst = B_FALSE; 50237c478bd9Sstevel@tonic-gate 50240f1702c5SYu Xiangning *error = 0; 50250f1702c5SYu Xiangning pktinfop->ip4_ill_index = 0; 50260f1702c5SYu Xiangning pktinfop->ip4_addr = INADDR_ANY; 50270f1702c5SYu Xiangning optinfo.ip_opt_flags = 0; 50280f1702c5SYu Xiangning optinfo.ip_opt_ill_index = 0; 50290f1702c5SYu Xiangning 50300f1702c5SYu Xiangning if (v4dst == INADDR_ANY) 50310f1702c5SYu Xiangning v4dst = htonl(INADDR_LOOPBACK); 50327c478bd9Sstevel@tonic-gate 50330f1702c5SYu Xiangning /* 50340f1702c5SYu Xiangning * If options passed in, feed it for verification and handling 50350f1702c5SYu Xiangning */ 50360f1702c5SYu Xiangning attrs.udpattr_credset = B_FALSE; 50370f1702c5SYu Xiangning if (IPCL_IS_NONSTR(connp)) { 50380f1702c5SYu Xiangning if (msg->msg_controllen != 0) { 50390f1702c5SYu Xiangning attrs.udpattr_ipp4 = pktinfop; 50400f1702c5SYu Xiangning attrs.udpattr_mb = mp; 50417c478bd9Sstevel@tonic-gate 50420f1702c5SYu Xiangning rw_enter(&udp->udp_rwlock, RW_WRITER); 50430f1702c5SYu Xiangning *error = process_auxiliary_options(connp, 50440f1702c5SYu Xiangning msg->msg_control, msg->msg_controllen, 50450f1702c5SYu Xiangning &attrs, &udp_opt_obj, udp_opt_set); 50460f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 50470f1702c5SYu Xiangning if (*error) 50480f1702c5SYu Xiangning goto done; 50490f1702c5SYu Xiangning } 50500f1702c5SYu Xiangning } else { 50510f1702c5SYu Xiangning if (DB_TYPE(mp) != M_DATA) { 50520f1702c5SYu Xiangning mp1 = mp->b_cont; 50530f1702c5SYu Xiangning if (((struct T_unitdata_req *) 50540f1702c5SYu Xiangning mp->b_rptr)->OPT_length != 0) { 50550f1702c5SYu Xiangning attrs.udpattr_ipp4 = pktinfop; 50560f1702c5SYu Xiangning attrs.udpattr_mb = mp; 50570f1702c5SYu Xiangning if (udp_unitdata_opt_process(q, mp, error, 50580f1702c5SYu Xiangning &attrs) < 0) 50590f1702c5SYu Xiangning goto done; 50600f1702c5SYu Xiangning /* 50610f1702c5SYu Xiangning * Note: success in processing options. 50620f1702c5SYu Xiangning * mp option buffer represented by 50630f1702c5SYu Xiangning * OPT_length/offset now potentially modified 50640f1702c5SYu Xiangning * and contain option setting results 50650f1702c5SYu Xiangning */ 50660f1702c5SYu Xiangning ASSERT(*error == 0); 50677c478bd9Sstevel@tonic-gate } 50687c478bd9Sstevel@tonic-gate } 50690f1702c5SYu Xiangning } 50707c478bd9Sstevel@tonic-gate 50710f1702c5SYu Xiangning /* mp1 points to the M_DATA mblk carrying the packet */ 50720f1702c5SYu Xiangning ASSERT(mp1 != NULL && DB_TYPE(mp1) == M_DATA); 50737c478bd9Sstevel@tonic-gate 50740f1702c5SYu Xiangning /* 50750f1702c5SYu Xiangning * Determine whether we need to mark the mblk with the user's 50760f1702c5SYu Xiangning * credentials. 50770f1702c5SYu Xiangning */ 50780f1702c5SYu Xiangning ire = connp->conn_ire_cache; 50790f1702c5SYu Xiangning if (is_system_labeled() || CLASSD(v4dst) || (ire == NULL) || 50800f1702c5SYu Xiangning (ire->ire_addr != v4dst) || 50810f1702c5SYu Xiangning (ire->ire_type & (IRE_BROADCAST | IRE_LOCAL | IRE_LOOPBACK))) { 50820f1702c5SYu Xiangning if (cr != NULL && DB_CRED(mp) == NULL) 50830f1702c5SYu Xiangning msg_setcredpid(mp, cr, pid); 50840f1702c5SYu Xiangning } 50857c478bd9Sstevel@tonic-gate 50860f1702c5SYu Xiangning rw_enter(&udp->udp_rwlock, RW_READER); 50870f1702c5SYu Xiangning lock_held = B_TRUE; 50888e4b770fSLu Huafeng 50898e4b770fSLu Huafeng /* 50908e4b770fSLu Huafeng * Cluster and TSOL note: 50918e4b770fSLu Huafeng * udp.udp_v6lastdst is shared by Cluster and TSOL 50928e4b770fSLu Huafeng * udp.udp_lastdstport is used by Cluster 50938e4b770fSLu Huafeng * 50948e4b770fSLu Huafeng * Both Cluster and TSOL need to update the dest addr and/or port. 50958e4b770fSLu Huafeng * Updating is done after both Cluster and TSOL checks, protected 50968e4b770fSLu Huafeng * by conn_lock. 50978e4b770fSLu Huafeng */ 50988e4b770fSLu Huafeng mutex_enter(&connp->conn_lock); 50998e4b770fSLu Huafeng 51008e4b770fSLu Huafeng if (cl_inet_connect2 != NULL && 51018e4b770fSLu Huafeng (!IN6_IS_ADDR_V4MAPPED(&udp->udp_v6lastdst) || 51028e4b770fSLu Huafeng V4_PART_OF_V6(udp->udp_v6lastdst) != v4dst || 51038e4b770fSLu Huafeng udp->udp_lastdstport != port)) { 51048e4b770fSLu Huafeng mutex_exit(&connp->conn_lock); 51058e4b770fSLu Huafeng *error = 0; 51068e4b770fSLu Huafeng IN6_IPADDR_TO_V4MAPPED(v4dst, &v6dst); 51078e4b770fSLu Huafeng CL_INET_UDP_CONNECT(connp, udp, B_TRUE, &v6dst, port, *error); 51088e4b770fSLu Huafeng if (*error != 0) { 51098e4b770fSLu Huafeng *error = EHOSTUNREACH; 51108e4b770fSLu Huafeng goto done; 51118e4b770fSLu Huafeng } 51128e4b770fSLu Huafeng update_lastdst = B_TRUE; 51138e4b770fSLu Huafeng mutex_enter(&connp->conn_lock); 51148e4b770fSLu Huafeng } 51158e4b770fSLu Huafeng 51160f1702c5SYu Xiangning /* 51170f1702c5SYu Xiangning * Check if our saved options are valid; update if not. 51180f1702c5SYu Xiangning * TSOL Note: Since we are not in WRITER mode, UDP packets 51190f1702c5SYu Xiangning * to different destination may require different labels, 51200f1702c5SYu Xiangning * or worse, UDP packets to same IP address may require 51210f1702c5SYu Xiangning * different labels due to use of shared all-zones address. 51220f1702c5SYu Xiangning * We use conn_lock to ensure that lastdst, ip_snd_options, 51230f1702c5SYu Xiangning * and ip_snd_options_len are consistent for the current 51240f1702c5SYu Xiangning * destination and are updated atomically. 51250f1702c5SYu Xiangning */ 51260f1702c5SYu Xiangning if (is_system_labeled()) { 51270f1702c5SYu Xiangning /* Using UDP MLP requires SCM_UCRED from user */ 51280f1702c5SYu Xiangning if (connp->conn_mlp_type != mlptSingle && 51290f1702c5SYu Xiangning !attrs.udpattr_credset) { 51300f1702c5SYu Xiangning mutex_exit(&connp->conn_lock); 51310f1702c5SYu Xiangning DTRACE_PROBE4( 51320f1702c5SYu Xiangning tx__ip__log__info__output__udp, 51330f1702c5SYu Xiangning char *, "MLP mp(1) lacks SCM_UCRED attr(2) on q(3)", 51340f1702c5SYu Xiangning mblk_t *, mp1, udpattrs_t *, &attrs, queue_t *, q); 51350f1702c5SYu Xiangning *error = ECONNREFUSED; 51360f1702c5SYu Xiangning goto done; 51370f1702c5SYu Xiangning } 51380f1702c5SYu Xiangning /* 51390f1702c5SYu Xiangning * update label option for this UDP socket if 51400f1702c5SYu Xiangning * - the destination has changed, or 51410f1702c5SYu Xiangning * - the UDP socket is MLP 51420f1702c5SYu Xiangning */ 51430f1702c5SYu Xiangning if ((!IN6_IS_ADDR_V4MAPPED(&udp->udp_v6lastdst) || 51440f1702c5SYu Xiangning V4_PART_OF_V6(udp->udp_v6lastdst) != v4dst || 51450f1702c5SYu Xiangning connp->conn_mlp_type != mlptSingle) && 51468e4b770fSLu Huafeng (*error = udp_update_label(q, mp, v4dst, &update_lastdst)) 51478e4b770fSLu Huafeng != 0) { 51480f1702c5SYu Xiangning mutex_exit(&connp->conn_lock); 51490f1702c5SYu Xiangning goto done; 51500f1702c5SYu Xiangning } 51510f1702c5SYu Xiangning } 51528e4b770fSLu Huafeng if (update_lastdst) { 51538e4b770fSLu Huafeng IN6_IPADDR_TO_V4MAPPED(v4dst, &udp->udp_v6lastdst); 51548e4b770fSLu Huafeng udp->udp_lastdstport = port; 51558e4b770fSLu Huafeng } 51560f1702c5SYu Xiangning if (udp->udp_ip_snd_options_len > 0) { 51570f1702c5SYu Xiangning ip_snd_opt_len = udp->udp_ip_snd_options_len; 51580f1702c5SYu Xiangning bcopy(udp->udp_ip_snd_options, ip_snd_opt, ip_snd_opt_len); 51590f1702c5SYu Xiangning } 51600f1702c5SYu Xiangning mutex_exit(&connp->conn_lock); 51617c478bd9Sstevel@tonic-gate 51620f1702c5SYu Xiangning /* Add an IP header */ 51630f1702c5SYu Xiangning ip_hdr_length = IP_SIMPLE_HDR_LENGTH + UDPH_SIZE + ip_snd_opt_len + 51640f1702c5SYu Xiangning (insert_spi ? sizeof (uint32_t) : 0); 51650f1702c5SYu Xiangning ipha = (ipha_t *)&mp1->b_rptr[-ip_hdr_length]; 51660f1702c5SYu Xiangning if (DB_REF(mp1) != 1 || (uchar_t *)ipha < DB_BASE(mp1) || 51670f1702c5SYu Xiangning !OK_32PTR(ipha)) { 51680f1702c5SYu Xiangning mp2 = allocb(ip_hdr_length + us->us_wroff_extra, BPRI_LO); 51690f1702c5SYu Xiangning if (mp2 == NULL) { 51700f1702c5SYu Xiangning TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_END, 51710f1702c5SYu Xiangning "udp_wput_end: q %p (%S)", q, "allocbfail2"); 51720f1702c5SYu Xiangning *error = ENOMEM; 51730f1702c5SYu Xiangning goto done; 51740f1702c5SYu Xiangning } 51750f1702c5SYu Xiangning mp2->b_wptr = DB_LIM(mp2); 51760f1702c5SYu Xiangning mp2->b_cont = mp1; 51770f1702c5SYu Xiangning mp1 = mp2; 51780f1702c5SYu Xiangning if (DB_TYPE(mp) != M_DATA) 51790f1702c5SYu Xiangning mp->b_cont = mp1; 51800f1702c5SYu Xiangning else 51810f1702c5SYu Xiangning mp = mp1; 51827c478bd9Sstevel@tonic-gate 51830f1702c5SYu Xiangning ipha = (ipha_t *)(mp1->b_wptr - ip_hdr_length); 51840f1702c5SYu Xiangning } 51850f1702c5SYu Xiangning ip_hdr_length -= (UDPH_SIZE + (insert_spi ? sizeof (uint32_t) : 0)); 51860f1702c5SYu Xiangning #ifdef _BIG_ENDIAN 51870f1702c5SYu Xiangning /* Set version, header length, and tos */ 51880f1702c5SYu Xiangning *(uint16_t *)&ipha->ipha_version_and_hdr_length = 51890f1702c5SYu Xiangning ((((IP_VERSION << 4) | (ip_hdr_length>>2)) << 8) | 51900f1702c5SYu Xiangning udp->udp_type_of_service); 51910f1702c5SYu Xiangning /* Set ttl and protocol */ 51920f1702c5SYu Xiangning *(uint16_t *)&ipha->ipha_ttl = (udp->udp_ttl << 8) | IPPROTO_UDP; 51930f1702c5SYu Xiangning #else 51940f1702c5SYu Xiangning /* Set version, header length, and tos */ 51950f1702c5SYu Xiangning *(uint16_t *)&ipha->ipha_version_and_hdr_length = 51960f1702c5SYu Xiangning ((udp->udp_type_of_service << 8) | 51970f1702c5SYu Xiangning ((IP_VERSION << 4) | (ip_hdr_length>>2))); 51980f1702c5SYu Xiangning /* Set ttl and protocol */ 51990f1702c5SYu Xiangning *(uint16_t *)&ipha->ipha_ttl = (IPPROTO_UDP << 8) | udp->udp_ttl; 52000f1702c5SYu Xiangning #endif 52010f1702c5SYu Xiangning if (pktinfop->ip4_addr != INADDR_ANY) { 52020f1702c5SYu Xiangning ipha->ipha_src = pktinfop->ip4_addr; 52030f1702c5SYu Xiangning optinfo.ip_opt_flags = IP_VERIFY_SRC; 52040f1702c5SYu Xiangning } else { 52050f1702c5SYu Xiangning /* 52060f1702c5SYu Xiangning * Copy our address into the packet. If this is zero, 52070f1702c5SYu Xiangning * first look at __sin6_src_id for a hint. If we leave the 52080f1702c5SYu Xiangning * source as INADDR_ANY then ip will fill in the real source 52090f1702c5SYu Xiangning * address. 52100f1702c5SYu Xiangning */ 52110f1702c5SYu Xiangning IN6_V4MAPPED_TO_IPADDR(&udp->udp_v6src, ipha->ipha_src); 52120f1702c5SYu Xiangning if (srcid != 0 && ipha->ipha_src == INADDR_ANY) { 52130f1702c5SYu Xiangning in6_addr_t v6src; 52147c478bd9Sstevel@tonic-gate 52150f1702c5SYu Xiangning ip_srcid_find_id(srcid, &v6src, connp->conn_zoneid, 52160f1702c5SYu Xiangning us->us_netstack); 52170f1702c5SYu Xiangning IN6_V4MAPPED_TO_IPADDR(&v6src, ipha->ipha_src); 52187c478bd9Sstevel@tonic-gate } 52190f1702c5SYu Xiangning } 52200f1702c5SYu Xiangning uha_src_port = udp->udp_port; 52210f1702c5SYu Xiangning if (ip_hdr_length == IP_SIMPLE_HDR_LENGTH) { 52220f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 52230f1702c5SYu Xiangning lock_held = B_FALSE; 52240f1702c5SYu Xiangning } 5225437220cdSdanmcd 52260f1702c5SYu Xiangning if (pktinfop->ip4_ill_index != 0) { 52270f1702c5SYu Xiangning optinfo.ip_opt_ill_index = pktinfop->ip4_ill_index; 52280f1702c5SYu Xiangning } 5229437220cdSdanmcd 52300f1702c5SYu Xiangning ipha->ipha_fragment_offset_and_flags = 0; 52310f1702c5SYu Xiangning ipha->ipha_ident = 0; 5232437220cdSdanmcd 52330f1702c5SYu Xiangning mp1->b_rptr = (uchar_t *)ipha; 5234437220cdSdanmcd 52350f1702c5SYu Xiangning ASSERT((uintptr_t)(mp1->b_wptr - (uchar_t *)ipha) <= 52360f1702c5SYu Xiangning (uintptr_t)UINT_MAX); 52370f1702c5SYu Xiangning 52380f1702c5SYu Xiangning /* Determine length of packet */ 52390f1702c5SYu Xiangning ip_len = (uint32_t)(mp1->b_wptr - (uchar_t *)ipha); 52400f1702c5SYu Xiangning if ((mp2 = mp1->b_cont) != NULL) { 52410f1702c5SYu Xiangning do { 52420f1702c5SYu Xiangning ASSERT((uintptr_t)MBLKL(mp2) <= (uintptr_t)UINT_MAX); 52430f1702c5SYu Xiangning ip_len += (uint32_t)MBLKL(mp2); 52440f1702c5SYu Xiangning } while ((mp2 = mp2->b_cont) != NULL); 52457c478bd9Sstevel@tonic-gate } 52467c478bd9Sstevel@tonic-gate /* 52470f1702c5SYu Xiangning * If the size of the packet is greater than the maximum allowed by 52480f1702c5SYu Xiangning * ip, return an error. Passing this down could cause panics because 52490f1702c5SYu Xiangning * the size will have wrapped and be inconsistent with the msg size. 52507c478bd9Sstevel@tonic-gate */ 52510f1702c5SYu Xiangning if (ip_len > IP_MAXPACKET) { 52520f1702c5SYu Xiangning TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_END, 52530f1702c5SYu Xiangning "udp_wput_end: q %p (%S)", q, "IP length exceeded"); 52540f1702c5SYu Xiangning *error = EMSGSIZE; 52550f1702c5SYu Xiangning goto done; 52567c478bd9Sstevel@tonic-gate } 52570f1702c5SYu Xiangning ipha->ipha_length = htons((uint16_t)ip_len); 52580f1702c5SYu Xiangning ip_len -= ip_hdr_length; 52590f1702c5SYu Xiangning ip_len = htons((uint16_t)ip_len); 52600f1702c5SYu Xiangning udpha = (udpha_t *)(((uchar_t *)ipha) + ip_hdr_length); 52617c478bd9Sstevel@tonic-gate 52620f1702c5SYu Xiangning /* Insert all-0s SPI now. */ 52630f1702c5SYu Xiangning if (insert_spi) 52640f1702c5SYu Xiangning *((uint32_t *)(udpha + 1)) = 0; 5265fc80c0dfSnordmark 52660f1702c5SYu Xiangning /* 52670f1702c5SYu Xiangning * Copy in the destination address 52680f1702c5SYu Xiangning */ 52690f1702c5SYu Xiangning ipha->ipha_dst = v4dst; 5270fc80c0dfSnordmark 52710f1702c5SYu Xiangning /* 52720f1702c5SYu Xiangning * Set ttl based on IP_MULTICAST_TTL to match IPv6 logic. 52730f1702c5SYu Xiangning */ 52740f1702c5SYu Xiangning if (CLASSD(v4dst)) 52750f1702c5SYu Xiangning ipha->ipha_ttl = udp->udp_multicast_ttl; 5276fc80c0dfSnordmark 52770f1702c5SYu Xiangning udpha->uha_dst_port = port; 52780f1702c5SYu Xiangning udpha->uha_src_port = uha_src_port; 52797c478bd9Sstevel@tonic-gate 52800f1702c5SYu Xiangning if (ip_snd_opt_len > 0) { 52810f1702c5SYu Xiangning uint32_t cksum; 52827c478bd9Sstevel@tonic-gate 52830f1702c5SYu Xiangning bcopy(ip_snd_opt, &ipha[1], ip_snd_opt_len); 52840f1702c5SYu Xiangning lock_held = B_FALSE; 52850f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 52860f1702c5SYu Xiangning /* 52870f1702c5SYu Xiangning * Massage source route putting first source route in ipha_dst. 52880f1702c5SYu Xiangning * Ignore the destination in T_unitdata_req. 52890f1702c5SYu Xiangning * Create a checksum adjustment for a source route, if any. 52900f1702c5SYu Xiangning */ 52910f1702c5SYu Xiangning cksum = ip_massage_options(ipha, us->us_netstack); 52920f1702c5SYu Xiangning cksum = (cksum & 0xFFFF) + (cksum >> 16); 52930f1702c5SYu Xiangning cksum -= ((ipha->ipha_dst >> 16) & 0xFFFF) + 52940f1702c5SYu Xiangning (ipha->ipha_dst & 0xFFFF); 52950f1702c5SYu Xiangning if ((int)cksum < 0) 52960f1702c5SYu Xiangning cksum--; 52970f1702c5SYu Xiangning cksum = (cksum & 0xFFFF) + (cksum >> 16); 52980f1702c5SYu Xiangning /* 52990f1702c5SYu Xiangning * IP does the checksum if uha_checksum is non-zero, 53000f1702c5SYu Xiangning * We make it easy for IP to include our pseudo header 53010f1702c5SYu Xiangning * by putting our length in uha_checksum. 53020f1702c5SYu Xiangning */ 53030f1702c5SYu Xiangning cksum += ip_len; 53040f1702c5SYu Xiangning cksum = (cksum & 0xFFFF) + (cksum >> 16); 53050f1702c5SYu Xiangning /* There might be a carry. */ 53060f1702c5SYu Xiangning cksum = (cksum & 0xFFFF) + (cksum >> 16); 53070f1702c5SYu Xiangning #ifdef _LITTLE_ENDIAN 53080f1702c5SYu Xiangning if (us->us_do_checksum) 53090f1702c5SYu Xiangning ip_len = (cksum << 16) | ip_len; 53100f1702c5SYu Xiangning #else 53110f1702c5SYu Xiangning if (us->us_do_checksum) 53120f1702c5SYu Xiangning ip_len = (ip_len << 16) | cksum; 53130f1702c5SYu Xiangning else 53140f1702c5SYu Xiangning ip_len <<= 16; 53150f1702c5SYu Xiangning #endif 53160f1702c5SYu Xiangning } else { 53170f1702c5SYu Xiangning /* 53180f1702c5SYu Xiangning * IP does the checksum if uha_checksum is non-zero, 53190f1702c5SYu Xiangning * We make it easy for IP to include our pseudo header 53200f1702c5SYu Xiangning * by putting our length in uha_checksum. 53210f1702c5SYu Xiangning */ 53220f1702c5SYu Xiangning if (us->us_do_checksum) 53230f1702c5SYu Xiangning ip_len |= (ip_len << 16); 53240f1702c5SYu Xiangning #ifndef _LITTLE_ENDIAN 53250f1702c5SYu Xiangning else 53260f1702c5SYu Xiangning ip_len <<= 16; 53270f1702c5SYu Xiangning #endif 53287c478bd9Sstevel@tonic-gate } 53290f1702c5SYu Xiangning ASSERT(!lock_held); 53300f1702c5SYu Xiangning /* Set UDP length and checksum */ 53310f1702c5SYu Xiangning *((uint32_t *)&udpha->uha_length) = ip_len; 53320f1702c5SYu Xiangning if (DB_CRED(mp) != NULL) 53330f1702c5SYu Xiangning mblk_setcred(mp1, DB_CRED(mp)); 53347c478bd9Sstevel@tonic-gate 53350f1702c5SYu Xiangning if (DB_TYPE(mp) != M_DATA) { 53360f1702c5SYu Xiangning ASSERT(mp != mp1); 53370f1702c5SYu Xiangning freeb(mp); 53387c478bd9Sstevel@tonic-gate } 53397c478bd9Sstevel@tonic-gate 53400f1702c5SYu Xiangning /* mp has been consumed and we'll return success */ 53410f1702c5SYu Xiangning ASSERT(*error == 0); 53420f1702c5SYu Xiangning mp = NULL; 53437c478bd9Sstevel@tonic-gate 53440f1702c5SYu Xiangning /* We're done. Pass the packet to ip. */ 53450f1702c5SYu Xiangning BUMP_MIB(&us->us_udp_mib, udpHCOutDatagrams); 53460f1702c5SYu Xiangning TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_END, 53470f1702c5SYu Xiangning "udp_wput_end: q %p (%S)", q, "end"); 53487c478bd9Sstevel@tonic-gate 53490f1702c5SYu Xiangning if ((connp->conn_flags & IPCL_CHECK_POLICY) != 0 || 53500f1702c5SYu Xiangning CONN_OUTBOUND_POLICY_PRESENT(connp, ipss) || 53510f1702c5SYu Xiangning connp->conn_dontroute || 53520f1702c5SYu Xiangning connp->conn_outgoing_ill != NULL || optinfo.ip_opt_flags != 0 || 53530f1702c5SYu Xiangning optinfo.ip_opt_ill_index != 0 || 53540f1702c5SYu Xiangning ipha->ipha_version_and_hdr_length != IP_SIMPLE_HDR_VERSION || 53550f1702c5SYu Xiangning IPP_ENABLED(IPP_LOCAL_OUT, ipst) || 53560f1702c5SYu Xiangning ipst->ips_ip_g_mrouter != NULL) { 53570f1702c5SYu Xiangning UDP_STAT(us, udp_ip_send); 53580f1702c5SYu Xiangning ip_output_options(connp, mp1, connp->conn_wq, IP_WPUT, 53590f1702c5SYu Xiangning &optinfo); 53600f1702c5SYu Xiangning } else { 53610f1702c5SYu Xiangning udp_send_data(udp, connp->conn_wq, mp1, ipha); 53620f1702c5SYu Xiangning } 53630f1702c5SYu Xiangning 53640f1702c5SYu Xiangning done: 53650f1702c5SYu Xiangning if (lock_held) 5366fc80c0dfSnordmark rw_exit(&udp->udp_rwlock); 53670f1702c5SYu Xiangning if (*error != 0) { 53680f1702c5SYu Xiangning ASSERT(mp != NULL); 53690f1702c5SYu Xiangning BUMP_MIB(&us->us_udp_mib, udpOutErrors); 53707c478bd9Sstevel@tonic-gate } 53710f1702c5SYu Xiangning return (mp); 53727c478bd9Sstevel@tonic-gate } 53737c478bd9Sstevel@tonic-gate 53740f1702c5SYu Xiangning static void 53750f1702c5SYu Xiangning udp_send_data(udp_t *udp, queue_t *q, mblk_t *mp, ipha_t *ipha) 53767c478bd9Sstevel@tonic-gate { 53770f1702c5SYu Xiangning conn_t *connp = udp->udp_connp; 53780f1702c5SYu Xiangning ipaddr_t src, dst; 53790f1702c5SYu Xiangning ire_t *ire; 53800f1702c5SYu Xiangning ipif_t *ipif = NULL; 53810f1702c5SYu Xiangning mblk_t *ire_fp_mp; 53820f1702c5SYu Xiangning boolean_t retry_caching; 53830f1702c5SYu Xiangning udp_stack_t *us = udp->udp_us; 53840f1702c5SYu Xiangning ip_stack_t *ipst = connp->conn_netstack->netstack_ip; 53857c478bd9Sstevel@tonic-gate 53860f1702c5SYu Xiangning dst = ipha->ipha_dst; 53870f1702c5SYu Xiangning src = ipha->ipha_src; 53880f1702c5SYu Xiangning ASSERT(ipha->ipha_ident == 0); 53897c478bd9Sstevel@tonic-gate 53900f1702c5SYu Xiangning if (CLASSD(dst)) { 53910f1702c5SYu Xiangning int err; 53920f1702c5SYu Xiangning 53930f1702c5SYu Xiangning ipif = conn_get_held_ipif(connp, 53940f1702c5SYu Xiangning &connp->conn_multicast_ipif, &err); 53950f1702c5SYu Xiangning 53960f1702c5SYu Xiangning if (ipif == NULL || ipif->ipif_isv6 || 53970f1702c5SYu Xiangning (ipif->ipif_ill->ill_phyint->phyint_flags & 53980f1702c5SYu Xiangning PHYI_LOOPBACK)) { 53990f1702c5SYu Xiangning if (ipif != NULL) 54000f1702c5SYu Xiangning ipif_refrele(ipif); 54010f1702c5SYu Xiangning UDP_STAT(us, udp_ip_send); 54020f1702c5SYu Xiangning ip_output(connp, mp, q, IP_WPUT); 54030f1702c5SYu Xiangning return; 54040f1702c5SYu Xiangning } 54057c478bd9Sstevel@tonic-gate } 54067c478bd9Sstevel@tonic-gate 54070f1702c5SYu Xiangning retry_caching = B_FALSE; 54080f1702c5SYu Xiangning mutex_enter(&connp->conn_lock); 54090f1702c5SYu Xiangning ire = connp->conn_ire_cache; 54100f1702c5SYu Xiangning ASSERT(!(connp->conn_state_flags & CONN_INCIPIENT)); 54117c478bd9Sstevel@tonic-gate 54120f1702c5SYu Xiangning if (ire == NULL || ire->ire_addr != dst || 54130f1702c5SYu Xiangning (ire->ire_marks & IRE_MARK_CONDEMNED)) { 54140f1702c5SYu Xiangning retry_caching = B_TRUE; 54150f1702c5SYu Xiangning } else if (CLASSD(dst) && (ire->ire_type & IRE_CACHE)) { 54160f1702c5SYu Xiangning ill_t *stq_ill = (ill_t *)ire->ire_stq->q_ptr; 54170f1702c5SYu Xiangning 54180f1702c5SYu Xiangning ASSERT(ipif != NULL); 5419*e11c3f44Smeem if (!IS_ON_SAME_LAN(stq_ill, ipif->ipif_ill)) 54200f1702c5SYu Xiangning retry_caching = B_TRUE; 54217c478bd9Sstevel@tonic-gate } 54227c478bd9Sstevel@tonic-gate 54230f1702c5SYu Xiangning if (!retry_caching) { 54240f1702c5SYu Xiangning ASSERT(ire != NULL); 54250f1702c5SYu Xiangning IRE_REFHOLD(ire); 54260f1702c5SYu Xiangning mutex_exit(&connp->conn_lock); 54270f1702c5SYu Xiangning } else { 54280f1702c5SYu Xiangning boolean_t cached = B_FALSE; 54297c478bd9Sstevel@tonic-gate 54300f1702c5SYu Xiangning connp->conn_ire_cache = NULL; 54310f1702c5SYu Xiangning mutex_exit(&connp->conn_lock); 543245916cd2Sjpk 54330f1702c5SYu Xiangning /* Release the old ire */ 54340f1702c5SYu Xiangning if (ire != NULL) { 54350f1702c5SYu Xiangning IRE_REFRELE_NOTR(ire); 54360f1702c5SYu Xiangning ire = NULL; 54370f1702c5SYu Xiangning } 543845916cd2Sjpk 54390f1702c5SYu Xiangning if (CLASSD(dst)) { 54400f1702c5SYu Xiangning ASSERT(ipif != NULL); 54410f1702c5SYu Xiangning ire = ire_ctable_lookup(dst, 0, 0, ipif, 54420f1702c5SYu Xiangning connp->conn_zoneid, MBLK_GETLABEL(mp), 5443*e11c3f44Smeem MATCH_IRE_ILL, ipst); 54440f1702c5SYu Xiangning } else { 54450f1702c5SYu Xiangning ASSERT(ipif == NULL); 54460f1702c5SYu Xiangning ire = ire_cache_lookup(dst, connp->conn_zoneid, 54470f1702c5SYu Xiangning MBLK_GETLABEL(mp), ipst); 544845916cd2Sjpk } 54490f1702c5SYu Xiangning 54500f1702c5SYu Xiangning if (ire == NULL) { 54510f1702c5SYu Xiangning if (ipif != NULL) 54520f1702c5SYu Xiangning ipif_refrele(ipif); 54530f1702c5SYu Xiangning UDP_STAT(us, udp_ire_null); 54540f1702c5SYu Xiangning ip_output(connp, mp, q, IP_WPUT); 54550f1702c5SYu Xiangning return; 54560f1702c5SYu Xiangning } 54570f1702c5SYu Xiangning IRE_REFHOLD_NOTR(ire); 54580f1702c5SYu Xiangning 54590f1702c5SYu Xiangning mutex_enter(&connp->conn_lock); 54600f1702c5SYu Xiangning if (CONN_CACHE_IRE(connp) && connp->conn_ire_cache == NULL && 54610f1702c5SYu Xiangning !(ire->ire_marks & IRE_MARK_CONDEMNED)) { 54620f1702c5SYu Xiangning irb_t *irb = ire->ire_bucket; 54630f1702c5SYu Xiangning 54640f1702c5SYu Xiangning /* 54650f1702c5SYu Xiangning * IRE's created for non-connection oriented transports 54660f1702c5SYu Xiangning * are normally initialized with IRE_MARK_TEMPORARY set 54670f1702c5SYu Xiangning * in the ire_marks. These IRE's are preferentially 54680f1702c5SYu Xiangning * reaped when the hash chain length in the cache 54690f1702c5SYu Xiangning * bucket exceeds the maximum value specified in 54700f1702c5SYu Xiangning * ip[6]_ire_max_bucket_cnt. This can severely affect 54710f1702c5SYu Xiangning * UDP performance if IRE cache entries that we need 54720f1702c5SYu Xiangning * to reuse are continually removed. To remedy this, 54730f1702c5SYu Xiangning * when we cache the IRE in the conn_t, we remove the 54740f1702c5SYu Xiangning * IRE_MARK_TEMPORARY bit from the ire_marks if it was 54750f1702c5SYu Xiangning * set. 54760f1702c5SYu Xiangning */ 54770f1702c5SYu Xiangning if (ire->ire_marks & IRE_MARK_TEMPORARY) { 54780f1702c5SYu Xiangning rw_enter(&irb->irb_lock, RW_WRITER); 54790f1702c5SYu Xiangning if (ire->ire_marks & IRE_MARK_TEMPORARY) { 54800f1702c5SYu Xiangning ire->ire_marks &= ~IRE_MARK_TEMPORARY; 54810f1702c5SYu Xiangning irb->irb_tmp_ire_cnt--; 548245916cd2Sjpk } 54830f1702c5SYu Xiangning rw_exit(&irb->irb_lock); 548445916cd2Sjpk } 54850f1702c5SYu Xiangning connp->conn_ire_cache = ire; 54860f1702c5SYu Xiangning cached = B_TRUE; 548745916cd2Sjpk } 54880f1702c5SYu Xiangning mutex_exit(&connp->conn_lock); 54890f1702c5SYu Xiangning 54900f1702c5SYu Xiangning /* 54910f1702c5SYu Xiangning * We can continue to use the ire but since it was not 54920f1702c5SYu Xiangning * cached, we should drop the extra reference. 54930f1702c5SYu Xiangning */ 54940f1702c5SYu Xiangning if (!cached) 54950f1702c5SYu Xiangning IRE_REFRELE_NOTR(ire); 54960f1702c5SYu Xiangning } 54970f1702c5SYu Xiangning ASSERT(ire != NULL && ire->ire_ipversion == IPV4_VERSION); 54980f1702c5SYu Xiangning ASSERT(!CLASSD(dst) || ipif != NULL); 54990f1702c5SYu Xiangning 55000f1702c5SYu Xiangning /* 55010f1702c5SYu Xiangning * Check if we can take the fast-path. 55020f1702c5SYu Xiangning * Note that "incomplete" ire's (where the link-layer for next hop 55030f1702c5SYu Xiangning * is not resolved, or where the fast-path header in nce_fp_mp is not 55040f1702c5SYu Xiangning * available yet) are sent down the legacy (slow) path 55050f1702c5SYu Xiangning */ 55060f1702c5SYu Xiangning if ((ire->ire_type & (IRE_BROADCAST|IRE_LOCAL|IRE_LOOPBACK)) || 55070f1702c5SYu Xiangning (ire->ire_flags & RTF_MULTIRT) || (ire->ire_stq == NULL) || 55080f1702c5SYu Xiangning (ire->ire_max_frag < ntohs(ipha->ipha_length)) || 55090f1702c5SYu Xiangning ((ire->ire_nce == NULL) || 55100f1702c5SYu Xiangning ((ire_fp_mp = ire->ire_nce->nce_fp_mp) == NULL)) || 55110f1702c5SYu Xiangning connp->conn_nexthop_set || (MBLKL(ire_fp_mp) > MBLKHEAD(mp))) { 55120f1702c5SYu Xiangning if (ipif != NULL) 55130f1702c5SYu Xiangning ipif_refrele(ipif); 55140f1702c5SYu Xiangning UDP_STAT(us, udp_ip_ire_send); 55150f1702c5SYu Xiangning IRE_REFRELE(ire); 55160f1702c5SYu Xiangning ip_output(connp, mp, q, IP_WPUT); 55170f1702c5SYu Xiangning return; 551845916cd2Sjpk } 551945916cd2Sjpk 55200f1702c5SYu Xiangning if (src == INADDR_ANY && !connp->conn_unspec_src) { 55210f1702c5SYu Xiangning if (CLASSD(dst) && !(ire->ire_flags & RTF_SETSRC)) 55220f1702c5SYu Xiangning ipha->ipha_src = ipif->ipif_src_addr; 55230f1702c5SYu Xiangning else 55240f1702c5SYu Xiangning ipha->ipha_src = ire->ire_src_addr; 55250f1702c5SYu Xiangning } 552645916cd2Sjpk 55270f1702c5SYu Xiangning if (ipif != NULL) 55280f1702c5SYu Xiangning ipif_refrele(ipif); 55290f1702c5SYu Xiangning 55300f1702c5SYu Xiangning udp_xmit(connp->conn_wq, mp, ire, connp, connp->conn_zoneid); 553145916cd2Sjpk } 553245916cd2Sjpk 55337c478bd9Sstevel@tonic-gate static void 55340f1702c5SYu Xiangning udp_xmit(queue_t *q, mblk_t *mp, ire_t *ire, conn_t *connp, zoneid_t zoneid) 55357c478bd9Sstevel@tonic-gate { 55360f1702c5SYu Xiangning ipaddr_t src, dst; 55370f1702c5SYu Xiangning ill_t *ill; 55380f1702c5SYu Xiangning mblk_t *ire_fp_mp; 55390f1702c5SYu Xiangning uint_t ire_fp_mp_len; 55400f1702c5SYu Xiangning uint16_t *up; 55410f1702c5SYu Xiangning uint32_t cksum, hcksum_txflags; 55420f1702c5SYu Xiangning queue_t *dev_q; 55430f1702c5SYu Xiangning udp_t *udp = connp->conn_udp; 55440f1702c5SYu Xiangning ipha_t *ipha = (ipha_t *)mp->b_rptr; 55450f1702c5SYu Xiangning udp_stack_t *us = udp->udp_us; 55460f1702c5SYu Xiangning ip_stack_t *ipst = connp->conn_netstack->netstack_ip; 55470f1702c5SYu Xiangning boolean_t ll_multicast = B_FALSE; 55480f1702c5SYu Xiangning 55490f1702c5SYu Xiangning dev_q = ire->ire_stq->q_next; 55500f1702c5SYu Xiangning ASSERT(dev_q != NULL); 55510f1702c5SYu Xiangning 55520f1702c5SYu Xiangning ill = ire_to_ill(ire); 55530f1702c5SYu Xiangning ASSERT(ill != NULL); 55540f1702c5SYu Xiangning 55550f1702c5SYu Xiangning /* is queue flow controlled? */ 55560f1702c5SYu Xiangning if (q->q_first != NULL || connp->conn_draining || 55570f1702c5SYu Xiangning DEV_Q_FLOW_BLOCKED(dev_q)) { 55580f1702c5SYu Xiangning BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsHCOutRequests); 55590f1702c5SYu Xiangning BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsOutDiscards); 55600f1702c5SYu Xiangning 55610f1702c5SYu Xiangning if (ipst->ips_ip_output_queue) 55620f1702c5SYu Xiangning (void) putq(connp->conn_wq, mp); 55630f1702c5SYu Xiangning else 55640f1702c5SYu Xiangning freemsg(mp); 55650f1702c5SYu Xiangning ire_refrele(ire); 55660f1702c5SYu Xiangning return; 55670f1702c5SYu Xiangning } 55680f1702c5SYu Xiangning 55690f1702c5SYu Xiangning ire_fp_mp = ire->ire_nce->nce_fp_mp; 55700f1702c5SYu Xiangning ire_fp_mp_len = MBLKL(ire_fp_mp); 55710f1702c5SYu Xiangning ASSERT(MBLKHEAD(mp) >= ire_fp_mp_len); 55720f1702c5SYu Xiangning 55730f1702c5SYu Xiangning dst = ipha->ipha_dst; 55740f1702c5SYu Xiangning src = ipha->ipha_src; 55750f1702c5SYu Xiangning 55760f1702c5SYu Xiangning 55770f1702c5SYu Xiangning BUMP_MIB(ill->ill_ip_mib, ipIfStatsHCOutRequests); 55780f1702c5SYu Xiangning 55790f1702c5SYu Xiangning ipha->ipha_ident = (uint16_t)atomic_add_32_nv(&ire->ire_ident, 1); 55800f1702c5SYu Xiangning #ifndef _BIG_ENDIAN 55810f1702c5SYu Xiangning ipha->ipha_ident = (ipha->ipha_ident << 8) | (ipha->ipha_ident >> 8); 55820f1702c5SYu Xiangning #endif 55830f1702c5SYu Xiangning 55840f1702c5SYu Xiangning if (ILL_HCKSUM_CAPABLE(ill) && dohwcksum) { 55850f1702c5SYu Xiangning ASSERT(ill->ill_hcksum_capab != NULL); 55860f1702c5SYu Xiangning hcksum_txflags = ill->ill_hcksum_capab->ill_hcksum_txflags; 55870f1702c5SYu Xiangning } else { 55880f1702c5SYu Xiangning hcksum_txflags = 0; 55890f1702c5SYu Xiangning } 55900f1702c5SYu Xiangning 55910f1702c5SYu Xiangning /* pseudo-header checksum (do it in parts for IP header checksum) */ 55920f1702c5SYu Xiangning cksum = (dst >> 16) + (dst & 0xFFFF) + (src >> 16) + (src & 0xFFFF); 55930f1702c5SYu Xiangning 55940f1702c5SYu Xiangning ASSERT(ipha->ipha_version_and_hdr_length == IP_SIMPLE_HDR_VERSION); 55950f1702c5SYu Xiangning up = IPH_UDPH_CHECKSUMP(ipha, IP_SIMPLE_HDR_LENGTH); 55960f1702c5SYu Xiangning if (*up != 0) { 55970f1702c5SYu Xiangning IP_CKSUM_XMIT_FAST(ire->ire_ipversion, hcksum_txflags, 55980f1702c5SYu Xiangning mp, ipha, up, IPPROTO_UDP, IP_SIMPLE_HDR_LENGTH, 55990f1702c5SYu Xiangning ntohs(ipha->ipha_length), cksum); 56000f1702c5SYu Xiangning 56010f1702c5SYu Xiangning /* Software checksum? */ 56020f1702c5SYu Xiangning if (DB_CKSUMFLAGS(mp) == 0) { 56030f1702c5SYu Xiangning UDP_STAT(us, udp_out_sw_cksum); 56040f1702c5SYu Xiangning UDP_STAT_UPDATE(us, udp_out_sw_cksum_bytes, 56050f1702c5SYu Xiangning ntohs(ipha->ipha_length) - IP_SIMPLE_HDR_LENGTH); 5606fc80c0dfSnordmark } 56070f1702c5SYu Xiangning } 56080f1702c5SYu Xiangning 56090f1702c5SYu Xiangning if (!CLASSD(dst)) { 56100f1702c5SYu Xiangning ipha->ipha_fragment_offset_and_flags |= 56110f1702c5SYu Xiangning (uint32_t)htons(ire->ire_frag_flag); 56120f1702c5SYu Xiangning } 56130f1702c5SYu Xiangning 56140f1702c5SYu Xiangning /* Calculate IP header checksum if hardware isn't capable */ 56150f1702c5SYu Xiangning if (!(DB_CKSUMFLAGS(mp) & HCK_IPV4_HDRCKSUM)) { 56160f1702c5SYu Xiangning IP_HDR_CKSUM(ipha, cksum, ((uint32_t *)ipha)[0], 56170f1702c5SYu Xiangning ((uint16_t *)ipha)[4]); 56180f1702c5SYu Xiangning } 56190f1702c5SYu Xiangning 56200f1702c5SYu Xiangning if (CLASSD(dst)) { 5621*e11c3f44Smeem if (ilm_lookup_ill(ill, dst, ALL_ZONES) != NULL) { 56220f1702c5SYu Xiangning ip_multicast_loopback(q, ill, mp, 56230f1702c5SYu Xiangning connp->conn_multicast_loop ? 0 : 56240f1702c5SYu Xiangning IP_FF_NO_MCAST_LOOP, zoneid); 56250f1702c5SYu Xiangning } 56260f1702c5SYu Xiangning 56270f1702c5SYu Xiangning /* If multicast TTL is 0 then we are done */ 56280f1702c5SYu Xiangning if (ipha->ipha_ttl == 0) { 56290f1702c5SYu Xiangning freemsg(mp); 56300f1702c5SYu Xiangning ire_refrele(ire); 56310f1702c5SYu Xiangning return; 56320f1702c5SYu Xiangning } 56330f1702c5SYu Xiangning ll_multicast = B_TRUE; 56340f1702c5SYu Xiangning } 56350f1702c5SYu Xiangning 56360f1702c5SYu Xiangning ASSERT(DB_TYPE(ire_fp_mp) == M_DATA); 56370f1702c5SYu Xiangning mp->b_rptr = (uchar_t *)ipha - ire_fp_mp_len; 56380f1702c5SYu Xiangning bcopy(ire_fp_mp->b_rptr, mp->b_rptr, ire_fp_mp_len); 56390f1702c5SYu Xiangning 56400f1702c5SYu Xiangning UPDATE_OB_PKT_COUNT(ire); 56410f1702c5SYu Xiangning ire->ire_last_used_time = lbolt; 56420f1702c5SYu Xiangning 56430f1702c5SYu Xiangning BUMP_MIB(ill->ill_ip_mib, ipIfStatsHCOutTransmits); 56440f1702c5SYu Xiangning UPDATE_MIB(ill->ill_ip_mib, ipIfStatsHCOutOctets, 56450f1702c5SYu Xiangning ntohs(ipha->ipha_length)); 56460f1702c5SYu Xiangning 56470f1702c5SYu Xiangning DTRACE_PROBE4(ip4__physical__out__start, 56480f1702c5SYu Xiangning ill_t *, NULL, ill_t *, ill, ipha_t *, ipha, mblk_t *, mp); 56490f1702c5SYu Xiangning FW_HOOKS(ipst->ips_ip4_physical_out_event, 56500f1702c5SYu Xiangning ipst->ips_ipv4firewall_physical_out, NULL, ill, ipha, mp, mp, 56510f1702c5SYu Xiangning ll_multicast, ipst); 56520f1702c5SYu Xiangning DTRACE_PROBE1(ip4__physical__out__end, mblk_t *, mp); 56530f1702c5SYu Xiangning if (ipst->ips_ipobs_enabled && mp != NULL) { 56540f1702c5SYu Xiangning zoneid_t szone; 56550f1702c5SYu Xiangning 56560f1702c5SYu Xiangning szone = ip_get_zoneid_v4(ipha->ipha_src, mp, 56570f1702c5SYu Xiangning ipst, ALL_ZONES); 56580f1702c5SYu Xiangning ipobs_hook(mp, IPOBS_HOOK_OUTBOUND, szone, 56590f1702c5SYu Xiangning ALL_ZONES, ill, IPV4_VERSION, ire_fp_mp_len, ipst); 56600f1702c5SYu Xiangning } 56610f1702c5SYu Xiangning 56620f1702c5SYu Xiangning if (mp != NULL) { 56630f1702c5SYu Xiangning DTRACE_IP7(send, mblk_t *, mp, conn_t *, NULL, 56640f1702c5SYu Xiangning void_ip_t *, ipha, __dtrace_ipsr_ill_t *, ill, 56650f1702c5SYu Xiangning ipha_t *, ipha, ip6_t *, NULL, int, 0); 56660f1702c5SYu Xiangning 56670f1702c5SYu Xiangning if (ILL_DIRECT_CAPABLE(ill)) { 56680f1702c5SYu Xiangning ill_dld_direct_t *idd = &ill->ill_dld_capab->idc_direct; 56690f1702c5SYu Xiangning 56700f1702c5SYu Xiangning (void) idd->idd_tx_df(idd->idd_tx_dh, mp, 56710f1702c5SYu Xiangning (uintptr_t)connp, 0); 56720f1702c5SYu Xiangning } else { 56730f1702c5SYu Xiangning putnext(ire->ire_stq, mp); 5674fc80c0dfSnordmark } 5675fc80c0dfSnordmark } 56760f1702c5SYu Xiangning IRE_REFRELE(ire); 5677fc80c0dfSnordmark } 5678fc80c0dfSnordmark 56790f1702c5SYu Xiangning static boolean_t 56808e4b770fSLu Huafeng udp_update_label_v6(queue_t *wq, mblk_t *mp, in6_addr_t *dst, 56818e4b770fSLu Huafeng boolean_t *update_lastdst) 5682fc80c0dfSnordmark { 56830f1702c5SYu Xiangning udp_t *udp = Q_TO_UDP(wq); 56840f1702c5SYu Xiangning int err; 56850f1702c5SYu Xiangning uchar_t opt_storage[TSOL_MAX_IPV6_OPTION]; 56860f1702c5SYu Xiangning udp_stack_t *us = udp->udp_us; 56870f1702c5SYu Xiangning 56880f1702c5SYu Xiangning err = tsol_compute_label_v6(DB_CREDDEF(mp, udp->udp_connp->conn_cred), 56890f1702c5SYu Xiangning dst, opt_storage, udp->udp_connp->conn_mac_exempt, 56900f1702c5SYu Xiangning us->us_netstack->netstack_ip); 56910f1702c5SYu Xiangning if (err == 0) { 56920f1702c5SYu Xiangning err = tsol_update_sticky(&udp->udp_sticky_ipp, 56930f1702c5SYu Xiangning &udp->udp_label_len_v6, opt_storage); 56940f1702c5SYu Xiangning } 56950f1702c5SYu Xiangning if (err != 0) { 56960f1702c5SYu Xiangning DTRACE_PROBE4( 56970f1702c5SYu Xiangning tx__ip__log__drop__updatelabel__udp6, 56980f1702c5SYu Xiangning char *, "queue(1) failed to update options(2) on mp(3)", 56990f1702c5SYu Xiangning queue_t *, wq, char *, opt_storage, mblk_t *, mp); 57000f1702c5SYu Xiangning } else { 57018e4b770fSLu Huafeng *update_lastdst = B_TRUE; 57020f1702c5SYu Xiangning } 57030f1702c5SYu Xiangning return (err); 57040f1702c5SYu Xiangning } 57050f1702c5SYu Xiangning 57060f1702c5SYu Xiangning static int 57070f1702c5SYu Xiangning udp_send_connected(conn_t *connp, mblk_t *mp, struct nmsghdr *msg, cred_t *cr, 57080f1702c5SYu Xiangning pid_t pid) 57090f1702c5SYu Xiangning { 57100f1702c5SYu Xiangning udp_t *udp = connp->conn_udp; 57110f1702c5SYu Xiangning udp_stack_t *us = udp->udp_us; 57120f1702c5SYu Xiangning ipaddr_t v4dst; 57130f1702c5SYu Xiangning in_port_t dstport; 57140f1702c5SYu Xiangning boolean_t mapped_addr; 57150f1702c5SYu Xiangning struct sockaddr_storage ss; 57160f1702c5SYu Xiangning sin_t *sin; 57170f1702c5SYu Xiangning sin6_t *sin6; 57180f1702c5SYu Xiangning struct sockaddr *addr; 57190f1702c5SYu Xiangning socklen_t addrlen; 57200f1702c5SYu Xiangning int error; 57210f1702c5SYu Xiangning boolean_t insert_spi = udp->udp_nat_t_endpoint; 57227c478bd9Sstevel@tonic-gate 57230f1702c5SYu Xiangning /* M_DATA for connected socket */ 57247c478bd9Sstevel@tonic-gate 57250f1702c5SYu Xiangning ASSERT(udp->udp_issocket || IPCL_IS_NONSTR(connp)); 57260f1702c5SYu Xiangning UDP_DBGSTAT(us, udp_data_conn); 57277c478bd9Sstevel@tonic-gate 57280f1702c5SYu Xiangning mutex_enter(&connp->conn_lock); 57290f1702c5SYu Xiangning if (udp->udp_state != TS_DATA_XFER) { 57300f1702c5SYu Xiangning mutex_exit(&connp->conn_lock); 57310f1702c5SYu Xiangning BUMP_MIB(&us->us_udp_mib, udpOutErrors); 57320f1702c5SYu Xiangning UDP_STAT(us, udp_out_err_notconn); 57330f1702c5SYu Xiangning freemsg(mp); 57340f1702c5SYu Xiangning TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_END, 57350f1702c5SYu Xiangning "udp_wput_end: connp %p (%S)", connp, 57360f1702c5SYu Xiangning "not-connected; address required"); 57370f1702c5SYu Xiangning return (EDESTADDRREQ); 57387c478bd9Sstevel@tonic-gate } 57397c478bd9Sstevel@tonic-gate 57400f1702c5SYu Xiangning mapped_addr = IN6_IS_ADDR_V4MAPPED(&udp->udp_v6dst); 57410f1702c5SYu Xiangning if (mapped_addr) 57420f1702c5SYu Xiangning IN6_V4MAPPED_TO_IPADDR(&udp->udp_v6dst, v4dst); 5743fc80c0dfSnordmark 57440f1702c5SYu Xiangning /* Initialize addr and addrlen as if they're passed in */ 57450f1702c5SYu Xiangning if (udp->udp_family == AF_INET) { 57460f1702c5SYu Xiangning sin = (sin_t *)&ss; 57470f1702c5SYu Xiangning sin->sin_family = AF_INET; 57480f1702c5SYu Xiangning dstport = sin->sin_port = udp->udp_dstport; 57490f1702c5SYu Xiangning ASSERT(mapped_addr); 57500f1702c5SYu Xiangning sin->sin_addr.s_addr = v4dst; 57510f1702c5SYu Xiangning addr = (struct sockaddr *)sin; 57520f1702c5SYu Xiangning addrlen = sizeof (*sin); 57530f1702c5SYu Xiangning } else { 57540f1702c5SYu Xiangning sin6 = (sin6_t *)&ss; 57550f1702c5SYu Xiangning sin6->sin6_family = AF_INET6; 57560f1702c5SYu Xiangning dstport = sin6->sin6_port = udp->udp_dstport; 57570f1702c5SYu Xiangning sin6->sin6_flowinfo = udp->udp_flowinfo; 57580f1702c5SYu Xiangning sin6->sin6_addr = udp->udp_v6dst; 57590f1702c5SYu Xiangning sin6->sin6_scope_id = 0; 57600f1702c5SYu Xiangning sin6->__sin6_src_id = 0; 57610f1702c5SYu Xiangning addr = (struct sockaddr *)sin6; 57620f1702c5SYu Xiangning addrlen = sizeof (*sin6); 57630f1702c5SYu Xiangning } 57640f1702c5SYu Xiangning mutex_exit(&connp->conn_lock); 5765fc80c0dfSnordmark 57660f1702c5SYu Xiangning if (mapped_addr) { 57677c478bd9Sstevel@tonic-gate /* 57680f1702c5SYu Xiangning * Handle both AF_INET and AF_INET6; the latter 57690f1702c5SYu Xiangning * for IPV4 mapped destination addresses. Note 57700f1702c5SYu Xiangning * here that both addr and addrlen point to the 57710f1702c5SYu Xiangning * corresponding struct depending on the address 57720f1702c5SYu Xiangning * family of the socket. 57737c478bd9Sstevel@tonic-gate */ 57740f1702c5SYu Xiangning mp = udp_output_v4(connp, mp, v4dst, dstport, 0, &error, 57750f1702c5SYu Xiangning insert_spi, msg, cr, pid); 57760f1702c5SYu Xiangning } else { 57770f1702c5SYu Xiangning mp = udp_output_v6(connp, mp, sin6, &error, msg, cr, pid); 57780f1702c5SYu Xiangning } 57790f1702c5SYu Xiangning if (error == 0) { 57800f1702c5SYu Xiangning ASSERT(mp == NULL); 57810f1702c5SYu Xiangning return (0); 57827c478bd9Sstevel@tonic-gate } 57837c478bd9Sstevel@tonic-gate 57840f1702c5SYu Xiangning UDP_STAT(us, udp_out_err_output); 57850f1702c5SYu Xiangning ASSERT(mp != NULL); 57860f1702c5SYu Xiangning if (IPCL_IS_NONSTR(connp)) { 57870f1702c5SYu Xiangning freemsg(mp); 57880f1702c5SYu Xiangning return (error); 57890f1702c5SYu Xiangning } else { 57900f1702c5SYu Xiangning /* mp is freed by the following routine */ 57910f1702c5SYu Xiangning udp_ud_err(connp->conn_wq, mp, (uchar_t *)addr, 57920f1702c5SYu Xiangning (t_scalar_t)addrlen, (t_scalar_t)error); 57930f1702c5SYu Xiangning return (0); 57947c478bd9Sstevel@tonic-gate } 57950f1702c5SYu Xiangning } 57960f1702c5SYu Xiangning 57970f1702c5SYu Xiangning /* ARGSUSED */ 57980f1702c5SYu Xiangning static int 57990f1702c5SYu Xiangning udp_send_not_connected(conn_t *connp, mblk_t *mp, struct sockaddr *addr, 58000f1702c5SYu Xiangning socklen_t addrlen, struct nmsghdr *msg, cred_t *cr, pid_t pid) 58010f1702c5SYu Xiangning { 58027c478bd9Sstevel@tonic-gate 58030f1702c5SYu Xiangning udp_t *udp = connp->conn_udp; 58040f1702c5SYu Xiangning boolean_t insert_spi = udp->udp_nat_t_endpoint; 58050f1702c5SYu Xiangning int error = 0; 58060f1702c5SYu Xiangning sin6_t *sin6; 58070f1702c5SYu Xiangning sin_t *sin; 58080f1702c5SYu Xiangning uint_t srcid; 58090f1702c5SYu Xiangning uint16_t port; 58100f1702c5SYu Xiangning ipaddr_t v4dst; 5811fc80c0dfSnordmark 58127c478bd9Sstevel@tonic-gate 58130f1702c5SYu Xiangning ASSERT(addr != NULL); 58147c478bd9Sstevel@tonic-gate 58150f1702c5SYu Xiangning switch (udp->udp_family) { 58160f1702c5SYu Xiangning case AF_INET6: 58170f1702c5SYu Xiangning sin6 = (sin6_t *)addr; 58180f1702c5SYu Xiangning if (!IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { 58190f1702c5SYu Xiangning /* 58200f1702c5SYu Xiangning * Destination is a non-IPv4-compatible IPv6 address. 58210f1702c5SYu Xiangning * Send out an IPv6 format packet. 58220f1702c5SYu Xiangning */ 58230f1702c5SYu Xiangning mp = udp_output_v6(connp, mp, sin6, &error, msg, cr, 58240f1702c5SYu Xiangning pid); 58250f1702c5SYu Xiangning if (error != 0) 58260f1702c5SYu Xiangning goto ud_error; 58277c478bd9Sstevel@tonic-gate 58280f1702c5SYu Xiangning return (0); 58290f1702c5SYu Xiangning } 58307c478bd9Sstevel@tonic-gate /* 58310f1702c5SYu Xiangning * If the local address is not zero or a mapped address 58320f1702c5SYu Xiangning * return an error. It would be possible to send an IPv4 58330f1702c5SYu Xiangning * packet but the response would never make it back to the 58340f1702c5SYu Xiangning * application since it is bound to a non-mapped address. 58357c478bd9Sstevel@tonic-gate */ 58360f1702c5SYu Xiangning if (!IN6_IS_ADDR_V4MAPPED(&udp->udp_v6src) && 58370f1702c5SYu Xiangning !IN6_IS_ADDR_UNSPECIFIED(&udp->udp_v6src)) { 58380f1702c5SYu Xiangning error = EADDRNOTAVAIL; 58390f1702c5SYu Xiangning goto ud_error; 58407c478bd9Sstevel@tonic-gate } 58410f1702c5SYu Xiangning /* Send IPv4 packet without modifying udp_ipversion */ 58420f1702c5SYu Xiangning /* Extract port and ipaddr */ 58430f1702c5SYu Xiangning port = sin6->sin6_port; 58440f1702c5SYu Xiangning IN6_V4MAPPED_TO_IPADDR(&sin6->sin6_addr, v4dst); 58450f1702c5SYu Xiangning srcid = sin6->__sin6_src_id; 58460f1702c5SYu Xiangning break; 58470f1702c5SYu Xiangning 58480f1702c5SYu Xiangning case AF_INET: 58490f1702c5SYu Xiangning sin = (sin_t *)addr; 58500f1702c5SYu Xiangning /* Extract port and ipaddr */ 58510f1702c5SYu Xiangning port = sin->sin_port; 58520f1702c5SYu Xiangning v4dst = sin->sin_addr.s_addr; 58530f1702c5SYu Xiangning srcid = 0; 58540f1702c5SYu Xiangning break; 58550f1702c5SYu Xiangning } 58567c478bd9Sstevel@tonic-gate 58570f1702c5SYu Xiangning mp = udp_output_v4(connp, mp, v4dst, port, srcid, &error, insert_spi, 58580f1702c5SYu Xiangning msg, cr, pid); 5859fc80c0dfSnordmark 58600f1702c5SYu Xiangning if (error == 0) { 58610f1702c5SYu Xiangning ASSERT(mp == NULL); 58620f1702c5SYu Xiangning return (0); 58630f1702c5SYu Xiangning } 586419a30e1aSrshoaib 58650f1702c5SYu Xiangning ud_error: 58660f1702c5SYu Xiangning ASSERT(mp != NULL); 58677c478bd9Sstevel@tonic-gate 58680f1702c5SYu Xiangning return (error); 58690f1702c5SYu Xiangning } 58707c478bd9Sstevel@tonic-gate 58710f1702c5SYu Xiangning /* 58720f1702c5SYu Xiangning * This routine handles all messages passed downstream. It either 58730f1702c5SYu Xiangning * consumes the message or passes it downstream; it never queues a 58740f1702c5SYu Xiangning * a message. 58750f1702c5SYu Xiangning * 58760f1702c5SYu Xiangning * Also entry point for sockfs when udp is in "direct sockfs" mode. This mode 58770f1702c5SYu Xiangning * is valid when we are directly beneath the stream head, and thus sockfs 58780f1702c5SYu Xiangning * is able to bypass STREAMS and directly call us, passing along the sockaddr 58790f1702c5SYu Xiangning * structure without the cumbersome T_UNITDATA_REQ interface for the case of 58800f1702c5SYu Xiangning * connected endpoints. 58810f1702c5SYu Xiangning */ 58820f1702c5SYu Xiangning void 58830f1702c5SYu Xiangning udp_wput(queue_t *q, mblk_t *mp) 58840f1702c5SYu Xiangning { 58850f1702c5SYu Xiangning conn_t *connp = Q_TO_CONN(q); 58860f1702c5SYu Xiangning udp_t *udp = connp->conn_udp; 58870f1702c5SYu Xiangning int error = 0; 58880f1702c5SYu Xiangning struct sockaddr *addr; 58890f1702c5SYu Xiangning socklen_t addrlen; 58900f1702c5SYu Xiangning udp_stack_t *us = udp->udp_us; 5891d6287788Srshoaib 58920f1702c5SYu Xiangning TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_START, 58930f1702c5SYu Xiangning "udp_wput_start: queue %p mp %p", q, mp); 5894d6287788Srshoaib 58950f1702c5SYu Xiangning /* 58960f1702c5SYu Xiangning * We directly handle several cases here: T_UNITDATA_REQ message 58970f1702c5SYu Xiangning * coming down as M_PROTO/M_PCPROTO and M_DATA messages for connected 58980f1702c5SYu Xiangning * socket. 58990f1702c5SYu Xiangning */ 59000f1702c5SYu Xiangning switch (DB_TYPE(mp)) { 59010f1702c5SYu Xiangning case M_DATA: 5902d6287788Srshoaib /* 59030f1702c5SYu Xiangning * Quick check for error cases. Checks will be done again 59040f1702c5SYu Xiangning * under the lock later on 5905d6287788Srshoaib */ 59060f1702c5SYu Xiangning if (!udp->udp_direct_sockfs || udp->udp_state != TS_DATA_XFER) { 59070f1702c5SYu Xiangning /* Not connected; address is required */ 59080f1702c5SYu Xiangning BUMP_MIB(&us->us_udp_mib, udpOutErrors); 59090f1702c5SYu Xiangning UDP_STAT(us, udp_out_err_notconn); 59107c478bd9Sstevel@tonic-gate freemsg(mp); 59110f1702c5SYu Xiangning TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_END, 59120f1702c5SYu Xiangning "udp_wput_end: connp %p (%S)", connp, 59130f1702c5SYu Xiangning "not-connected; address required"); 59147c478bd9Sstevel@tonic-gate return; 59157c478bd9Sstevel@tonic-gate } 59160f1702c5SYu Xiangning (void) udp_send_connected(connp, mp, NULL, NULL, -1); 59170f1702c5SYu Xiangning return; 59187c478bd9Sstevel@tonic-gate 59190f1702c5SYu Xiangning case M_PROTO: 59200f1702c5SYu Xiangning case M_PCPROTO: { 59210f1702c5SYu Xiangning struct T_unitdata_req *tudr; 59227c478bd9Sstevel@tonic-gate 59230f1702c5SYu Xiangning ASSERT((uintptr_t)MBLKL(mp) <= (uintptr_t)INT_MAX); 59240f1702c5SYu Xiangning tudr = (struct T_unitdata_req *)mp->b_rptr; 59257c478bd9Sstevel@tonic-gate 59260f1702c5SYu Xiangning /* Handle valid T_UNITDATA_REQ here */ 59270f1702c5SYu Xiangning if (MBLKL(mp) >= sizeof (*tudr) && 59280f1702c5SYu Xiangning ((t_primp_t)mp->b_rptr)->type == T_UNITDATA_REQ) { 59290f1702c5SYu Xiangning if (mp->b_cont == NULL) { 59300f1702c5SYu Xiangning TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_END, 59310f1702c5SYu Xiangning "udp_wput_end: q %p (%S)", q, "badaddr"); 59320f1702c5SYu Xiangning error = EPROTO; 59330f1702c5SYu Xiangning goto ud_error; 5934fc80c0dfSnordmark } 5935fc80c0dfSnordmark 59360f1702c5SYu Xiangning if (!MBLKIN(mp, 0, tudr->DEST_offset + 59370f1702c5SYu Xiangning tudr->DEST_length)) { 59380f1702c5SYu Xiangning TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_END, 59390f1702c5SYu Xiangning "udp_wput_end: q %p (%S)", q, "badaddr"); 59400f1702c5SYu Xiangning error = EADDRNOTAVAIL; 59410f1702c5SYu Xiangning goto ud_error; 59427c478bd9Sstevel@tonic-gate } 59430f1702c5SYu Xiangning /* 59440f1702c5SYu Xiangning * If a port has not been bound to the stream, fail. 59450f1702c5SYu Xiangning * This is not a problem when sockfs is directly 59460f1702c5SYu Xiangning * above us, because it will ensure that the socket 59470f1702c5SYu Xiangning * is first bound before allowing data to be sent. 59480f1702c5SYu Xiangning */ 59490f1702c5SYu Xiangning if (udp->udp_state == TS_UNBND) { 59500f1702c5SYu Xiangning TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_END, 59510f1702c5SYu Xiangning "udp_wput_end: q %p (%S)", q, "outstate"); 59520f1702c5SYu Xiangning error = EPROTO; 59530f1702c5SYu Xiangning goto ud_error; 595419a30e1aSrshoaib } 59550f1702c5SYu Xiangning addr = (struct sockaddr *) 59560f1702c5SYu Xiangning &mp->b_rptr[tudr->DEST_offset]; 59570f1702c5SYu Xiangning addrlen = tudr->DEST_length; 59580f1702c5SYu Xiangning if (tudr->OPT_length != 0) 59590f1702c5SYu Xiangning UDP_STAT(us, udp_out_opt); 59600f1702c5SYu Xiangning break; 59610f1702c5SYu Xiangning } 59620f1702c5SYu Xiangning /* FALLTHRU */ 59630f1702c5SYu Xiangning } 59640f1702c5SYu Xiangning default: 59650f1702c5SYu Xiangning udp_wput_other(q, mp); 59660f1702c5SYu Xiangning return; 59670f1702c5SYu Xiangning } 59680f1702c5SYu Xiangning ASSERT(addr != NULL); 596919a30e1aSrshoaib 59700f1702c5SYu Xiangning error = udp_send_not_connected(connp, mp, addr, addrlen, NULL, NULL, 59710f1702c5SYu Xiangning -1); 59720f1702c5SYu Xiangning if (error != 0) { 59730f1702c5SYu Xiangning ud_error: 59740f1702c5SYu Xiangning UDP_STAT(us, udp_out_err_output); 59750f1702c5SYu Xiangning ASSERT(mp != NULL); 59760f1702c5SYu Xiangning /* mp is freed by the following routine */ 59770f1702c5SYu Xiangning udp_ud_err(q, mp, (uchar_t *)addr, (t_scalar_t)addrlen, 59780f1702c5SYu Xiangning (t_scalar_t)error); 59790f1702c5SYu Xiangning } 59800f1702c5SYu Xiangning } 59817c478bd9Sstevel@tonic-gate 59820f1702c5SYu Xiangning /* ARGSUSED */ 59830f1702c5SYu Xiangning static void 59840f1702c5SYu Xiangning udp_wput_fallback(queue_t *wq, mblk_t *mp) 59850f1702c5SYu Xiangning { 59860f1702c5SYu Xiangning #ifdef DEBUG 59870f1702c5SYu Xiangning cmn_err(CE_CONT, "udp_wput_fallback: Message in fallback \n"); 59880f1702c5SYu Xiangning #endif 59890f1702c5SYu Xiangning freemsg(mp); 59900f1702c5SYu Xiangning } 59917c478bd9Sstevel@tonic-gate 59927c478bd9Sstevel@tonic-gate 59930f1702c5SYu Xiangning /* 59940f1702c5SYu Xiangning * udp_output_v6(): 59950f1702c5SYu Xiangning * Assumes that udp_wput did some sanity checking on the destination 59960f1702c5SYu Xiangning * address. 59970f1702c5SYu Xiangning */ 59980f1702c5SYu Xiangning static mblk_t * 59990f1702c5SYu Xiangning udp_output_v6(conn_t *connp, mblk_t *mp, sin6_t *sin6, int *error, 60000f1702c5SYu Xiangning struct nmsghdr *msg, cred_t *cr, pid_t pid) 60010f1702c5SYu Xiangning { 60020f1702c5SYu Xiangning ip6_t *ip6h; 60030f1702c5SYu Xiangning ip6i_t *ip6i; /* mp1->b_rptr even if no ip6i_t */ 60040f1702c5SYu Xiangning mblk_t *mp1 = mp; 60050f1702c5SYu Xiangning mblk_t *mp2; 60060f1702c5SYu Xiangning int udp_ip_hdr_len = IPV6_HDR_LEN + UDPH_SIZE; 60070f1702c5SYu Xiangning size_t ip_len; 60080f1702c5SYu Xiangning udpha_t *udph; 60090f1702c5SYu Xiangning udp_t *udp = connp->conn_udp; 60100f1702c5SYu Xiangning udp_stack_t *us = udp->udp_us; 60110f1702c5SYu Xiangning queue_t *q = connp->conn_wq; 60120f1702c5SYu Xiangning ip6_pkt_t ipp_s; /* For ancillary data options */ 60130f1702c5SYu Xiangning ip6_pkt_t *ipp = &ipp_s; 60140f1702c5SYu Xiangning ip6_pkt_t *tipp; /* temporary ipp */ 60150f1702c5SYu Xiangning uint32_t csum = 0; 60160f1702c5SYu Xiangning uint_t ignore = 0; 60170f1702c5SYu Xiangning uint_t option_exists = 0, is_sticky = 0; 60180f1702c5SYu Xiangning uint8_t *cp; 60190f1702c5SYu Xiangning uint8_t *nxthdr_ptr; 60200f1702c5SYu Xiangning in6_addr_t ip6_dst; 60218e4b770fSLu Huafeng in_port_t port; 60220f1702c5SYu Xiangning udpattrs_t attrs; 60230f1702c5SYu Xiangning boolean_t opt_present; 60240f1702c5SYu Xiangning ip6_hbh_t *hopoptsptr = NULL; 60250f1702c5SYu Xiangning uint_t hopoptslen = 0; 60260f1702c5SYu Xiangning boolean_t is_ancillary = B_FALSE; 60270f1702c5SYu Xiangning size_t sth_wroff = 0; 60280f1702c5SYu Xiangning ire_t *ire; 60298e4b770fSLu Huafeng boolean_t update_lastdst = B_FALSE; 60307c478bd9Sstevel@tonic-gate 60310f1702c5SYu Xiangning *error = 0; 60327c478bd9Sstevel@tonic-gate 60330f1702c5SYu Xiangning /* 60340f1702c5SYu Xiangning * If the local address is a mapped address return 60350f1702c5SYu Xiangning * an error. 60360f1702c5SYu Xiangning * It would be possible to send an IPv6 packet but the 60370f1702c5SYu Xiangning * response would never make it back to the application 60380f1702c5SYu Xiangning * since it is bound to a mapped address. 60390f1702c5SYu Xiangning */ 60400f1702c5SYu Xiangning if (IN6_IS_ADDR_V4MAPPED(&udp->udp_v6src)) { 60410f1702c5SYu Xiangning *error = EADDRNOTAVAIL; 60420f1702c5SYu Xiangning goto done; 60430f1702c5SYu Xiangning } 60447c478bd9Sstevel@tonic-gate 60450f1702c5SYu Xiangning ipp->ipp_fields = 0; 60460f1702c5SYu Xiangning ipp->ipp_sticky_ignored = 0; 60477c478bd9Sstevel@tonic-gate 60480f1702c5SYu Xiangning /* 60490f1702c5SYu Xiangning * If TPI options passed in, feed it for verification and handling 60500f1702c5SYu Xiangning */ 60510f1702c5SYu Xiangning attrs.udpattr_credset = B_FALSE; 60520f1702c5SYu Xiangning opt_present = B_FALSE; 60530f1702c5SYu Xiangning if (IPCL_IS_NONSTR(connp)) { 60540f1702c5SYu Xiangning if (msg->msg_controllen != 0) { 60550f1702c5SYu Xiangning attrs.udpattr_ipp6 = ipp; 60560f1702c5SYu Xiangning attrs.udpattr_mb = mp; 6057e4f35dbaSgt 60580f1702c5SYu Xiangning rw_enter(&udp->udp_rwlock, RW_WRITER); 60590f1702c5SYu Xiangning *error = process_auxiliary_options(connp, 60600f1702c5SYu Xiangning msg->msg_control, msg->msg_controllen, 60610f1702c5SYu Xiangning &attrs, &udp_opt_obj, udp_opt_set); 60620f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 60630f1702c5SYu Xiangning if (*error) 60640f1702c5SYu Xiangning goto done; 60650f1702c5SYu Xiangning ASSERT(*error == 0); 60660f1702c5SYu Xiangning opt_present = B_TRUE; 60670f1702c5SYu Xiangning } 60680f1702c5SYu Xiangning } else { 60690f1702c5SYu Xiangning if (DB_TYPE(mp) != M_DATA) { 60700f1702c5SYu Xiangning mp1 = mp->b_cont; 60710f1702c5SYu Xiangning if (((struct T_unitdata_req *) 60720f1702c5SYu Xiangning mp->b_rptr)->OPT_length != 0) { 60730f1702c5SYu Xiangning attrs.udpattr_ipp6 = ipp; 60740f1702c5SYu Xiangning attrs.udpattr_mb = mp; 60750f1702c5SYu Xiangning if (udp_unitdata_opt_process(q, mp, error, 60760f1702c5SYu Xiangning &attrs) < 0) { 60770f1702c5SYu Xiangning goto done; 60780f1702c5SYu Xiangning } 60790f1702c5SYu Xiangning ASSERT(*error == 0); 60800f1702c5SYu Xiangning opt_present = B_TRUE; 6081d6287788Srshoaib } 60820f1702c5SYu Xiangning } 60830f1702c5SYu Xiangning } 6084d6287788Srshoaib 60850f1702c5SYu Xiangning /* 60860f1702c5SYu Xiangning * Determine whether we need to mark the mblk with the user's 60870f1702c5SYu Xiangning * credentials. 60880f1702c5SYu Xiangning */ 60890f1702c5SYu Xiangning ire = connp->conn_ire_cache; 60900f1702c5SYu Xiangning if (is_system_labeled() || IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr) || 60910f1702c5SYu Xiangning (ire == NULL) || 60920f1702c5SYu Xiangning (!IN6_ARE_ADDR_EQUAL(&ire->ire_addr_v6, &sin6->sin6_addr)) || 60930f1702c5SYu Xiangning (ire->ire_type & (IRE_LOCAL | IRE_LOOPBACK))) { 60940f1702c5SYu Xiangning if (cr != NULL && DB_CRED(mp) == NULL) 60950f1702c5SYu Xiangning msg_setcredpid(mp, cr, pid); 60960f1702c5SYu Xiangning } 6097d6287788Srshoaib 60980f1702c5SYu Xiangning rw_enter(&udp->udp_rwlock, RW_READER); 60990f1702c5SYu Xiangning ignore = ipp->ipp_sticky_ignored; 61007c478bd9Sstevel@tonic-gate 61010f1702c5SYu Xiangning /* mp1 points to the M_DATA mblk carrying the packet */ 61020f1702c5SYu Xiangning ASSERT(mp1 != NULL && DB_TYPE(mp1) == M_DATA); 61037c478bd9Sstevel@tonic-gate 61040f1702c5SYu Xiangning if (sin6->sin6_scope_id != 0 && 61050f1702c5SYu Xiangning IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { 61067c478bd9Sstevel@tonic-gate /* 61070f1702c5SYu Xiangning * IPPF_SCOPE_ID is special. It's neither a sticky 61080f1702c5SYu Xiangning * option nor ancillary data. It needs to be 61090f1702c5SYu Xiangning * explicitly set in options_exists. 61107c478bd9Sstevel@tonic-gate */ 61110f1702c5SYu Xiangning option_exists |= IPPF_SCOPE_ID; 61120f1702c5SYu Xiangning } 61137c478bd9Sstevel@tonic-gate 61140f1702c5SYu Xiangning /* 61150f1702c5SYu Xiangning * Compute the destination address 61160f1702c5SYu Xiangning */ 61170f1702c5SYu Xiangning ip6_dst = sin6->sin6_addr; 61180f1702c5SYu Xiangning if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) 61190f1702c5SYu Xiangning ip6_dst = ipv6_loopback; 612045916cd2Sjpk 61218e4b770fSLu Huafeng port = sin6->sin6_port; 61228e4b770fSLu Huafeng 61238e4b770fSLu Huafeng /* 61248e4b770fSLu Huafeng * Cluster and TSOL notes, Cluster check: 61258e4b770fSLu Huafeng * see comments in udp_output_v4(). 61268e4b770fSLu Huafeng */ 61278e4b770fSLu Huafeng mutex_enter(&connp->conn_lock); 61288e4b770fSLu Huafeng 61298e4b770fSLu Huafeng if (cl_inet_connect2 != NULL && 61308e4b770fSLu Huafeng (!IN6_ARE_ADDR_EQUAL(&ip6_dst, &udp->udp_v6lastdst) || 61318e4b770fSLu Huafeng port != udp->udp_lastdstport)) { 61328e4b770fSLu Huafeng mutex_exit(&connp->conn_lock); 61338e4b770fSLu Huafeng *error = 0; 61348e4b770fSLu Huafeng CL_INET_UDP_CONNECT(connp, udp, B_TRUE, &ip6_dst, port, *error); 61358e4b770fSLu Huafeng if (*error != 0) { 61368e4b770fSLu Huafeng *error = EHOSTUNREACH; 61378e4b770fSLu Huafeng rw_exit(&udp->udp_rwlock); 61388e4b770fSLu Huafeng goto done; 61398e4b770fSLu Huafeng } 61408e4b770fSLu Huafeng update_lastdst = B_TRUE; 61418e4b770fSLu Huafeng mutex_enter(&connp->conn_lock); 61428e4b770fSLu Huafeng } 61438e4b770fSLu Huafeng 61440f1702c5SYu Xiangning /* 61450f1702c5SYu Xiangning * If we're not going to the same destination as last time, then 61460f1702c5SYu Xiangning * recompute the label required. This is done in a separate routine to 61470f1702c5SYu Xiangning * avoid blowing up our stack here. 61480f1702c5SYu Xiangning * 61490f1702c5SYu Xiangning * TSOL Note: Since we are not in WRITER mode, UDP packets 61500f1702c5SYu Xiangning * to different destination may require different labels, 61510f1702c5SYu Xiangning * or worse, UDP packets to same IP address may require 61520f1702c5SYu Xiangning * different labels due to use of shared all-zones address. 61530f1702c5SYu Xiangning * We use conn_lock to ensure that lastdst, sticky ipp_hopopts, 61540f1702c5SYu Xiangning * and sticky ipp_hopoptslen are consistent for the current 61550f1702c5SYu Xiangning * destination and are updated atomically. 61560f1702c5SYu Xiangning */ 61570f1702c5SYu Xiangning if (is_system_labeled()) { 61580f1702c5SYu Xiangning /* Using UDP MLP requires SCM_UCRED from user */ 61590f1702c5SYu Xiangning if (connp->conn_mlp_type != mlptSingle && 61600f1702c5SYu Xiangning !attrs.udpattr_credset) { 61610f1702c5SYu Xiangning DTRACE_PROBE4( 61620f1702c5SYu Xiangning tx__ip__log__info__output__udp6, 61630f1702c5SYu Xiangning char *, "MLP mp(1) lacks SCM_UCRED attr(2) on q(3)", 61640f1702c5SYu Xiangning mblk_t *, mp1, udpattrs_t *, &attrs, queue_t *, q); 61650f1702c5SYu Xiangning *error = ECONNREFUSED; 61660f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 61670f1702c5SYu Xiangning mutex_exit(&connp->conn_lock); 61680f1702c5SYu Xiangning goto done; 61690f1702c5SYu Xiangning } 61700f1702c5SYu Xiangning /* 61710f1702c5SYu Xiangning * update label option for this UDP socket if 61720f1702c5SYu Xiangning * - the destination has changed, or 61730f1702c5SYu Xiangning * - the UDP socket is MLP 61740f1702c5SYu Xiangning */ 61750f1702c5SYu Xiangning if ((opt_present || 61760f1702c5SYu Xiangning !IN6_ARE_ADDR_EQUAL(&udp->udp_v6lastdst, &ip6_dst) || 61770f1702c5SYu Xiangning connp->conn_mlp_type != mlptSingle) && 61788e4b770fSLu Huafeng (*error = udp_update_label_v6(q, mp, &ip6_dst, 61798e4b770fSLu Huafeng &update_lastdst)) != 0) { 61800f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 61810f1702c5SYu Xiangning mutex_exit(&connp->conn_lock); 61820f1702c5SYu Xiangning goto done; 61830f1702c5SYu Xiangning } 61840f1702c5SYu Xiangning } 61850f1702c5SYu Xiangning 61868e4b770fSLu Huafeng if (update_lastdst) { 61878e4b770fSLu Huafeng udp->udp_v6lastdst = ip6_dst; 61888e4b770fSLu Huafeng udp->udp_lastdstport = port; 61898e4b770fSLu Huafeng } 61908e4b770fSLu Huafeng 61910f1702c5SYu Xiangning /* 61920f1702c5SYu Xiangning * If there's a security label here, then we ignore any options the 61930f1702c5SYu Xiangning * user may try to set. We keep the peer's label as a hidden sticky 61940f1702c5SYu Xiangning * option. We make a private copy of this label before releasing the 61950f1702c5SYu Xiangning * lock so that label is kept consistent with the destination addr. 61960f1702c5SYu Xiangning */ 61970f1702c5SYu Xiangning if (udp->udp_label_len_v6 > 0) { 61980f1702c5SYu Xiangning ignore &= ~IPPF_HOPOPTS; 61990f1702c5SYu Xiangning ipp->ipp_fields &= ~IPPF_HOPOPTS; 62000f1702c5SYu Xiangning } 62017c478bd9Sstevel@tonic-gate 62020f1702c5SYu Xiangning if ((udp->udp_sticky_ipp.ipp_fields == 0) && (ipp->ipp_fields == 0)) { 62030f1702c5SYu Xiangning /* No sticky options nor ancillary data. */ 62040f1702c5SYu Xiangning mutex_exit(&connp->conn_lock); 62050f1702c5SYu Xiangning goto no_options; 62060f1702c5SYu Xiangning } 62077c478bd9Sstevel@tonic-gate 62080f1702c5SYu Xiangning /* 62090f1702c5SYu Xiangning * Go through the options figuring out where each is going to 62100f1702c5SYu Xiangning * come from and build two masks. The first mask indicates if 62110f1702c5SYu Xiangning * the option exists at all. The second mask indicates if the 62120f1702c5SYu Xiangning * option is sticky or ancillary. 62130f1702c5SYu Xiangning */ 62140f1702c5SYu Xiangning if (!(ignore & IPPF_HOPOPTS)) { 62150f1702c5SYu Xiangning if (ipp->ipp_fields & IPPF_HOPOPTS) { 62160f1702c5SYu Xiangning option_exists |= IPPF_HOPOPTS; 62170f1702c5SYu Xiangning udp_ip_hdr_len += ipp->ipp_hopoptslen; 62180f1702c5SYu Xiangning } else if (udp->udp_sticky_ipp.ipp_fields & IPPF_HOPOPTS) { 62190f1702c5SYu Xiangning option_exists |= IPPF_HOPOPTS; 62200f1702c5SYu Xiangning is_sticky |= IPPF_HOPOPTS; 62210f1702c5SYu Xiangning ASSERT(udp->udp_sticky_ipp.ipp_hopoptslen != 0); 62220f1702c5SYu Xiangning hopoptsptr = kmem_alloc( 62230f1702c5SYu Xiangning udp->udp_sticky_ipp.ipp_hopoptslen, KM_NOSLEEP); 62240f1702c5SYu Xiangning if (hopoptsptr == NULL) { 62250f1702c5SYu Xiangning *error = ENOMEM; 62260f1702c5SYu Xiangning mutex_exit(&connp->conn_lock); 62270f1702c5SYu Xiangning goto done; 62280f1702c5SYu Xiangning } 62290f1702c5SYu Xiangning hopoptslen = udp->udp_sticky_ipp.ipp_hopoptslen; 62300f1702c5SYu Xiangning bcopy(udp->udp_sticky_ipp.ipp_hopopts, hopoptsptr, 62310f1702c5SYu Xiangning hopoptslen); 62320f1702c5SYu Xiangning udp_ip_hdr_len += hopoptslen; 6233ae43f94fSnordmark } 62340f1702c5SYu Xiangning } 62350f1702c5SYu Xiangning mutex_exit(&connp->conn_lock); 6236ae43f94fSnordmark 62370f1702c5SYu Xiangning if (!(ignore & IPPF_RTHDR)) { 62380f1702c5SYu Xiangning if (ipp->ipp_fields & IPPF_RTHDR) { 62390f1702c5SYu Xiangning option_exists |= IPPF_RTHDR; 62400f1702c5SYu Xiangning udp_ip_hdr_len += ipp->ipp_rthdrlen; 62410f1702c5SYu Xiangning } else if (udp->udp_sticky_ipp.ipp_fields & IPPF_RTHDR) { 62420f1702c5SYu Xiangning option_exists |= IPPF_RTHDR; 62430f1702c5SYu Xiangning is_sticky |= IPPF_RTHDR; 62440f1702c5SYu Xiangning udp_ip_hdr_len += udp->udp_sticky_ipp.ipp_rthdrlen; 6245ff550d0eSmasputra } 62460f1702c5SYu Xiangning } 62477c478bd9Sstevel@tonic-gate 62480f1702c5SYu Xiangning if (!(ignore & IPPF_RTDSTOPTS) && (option_exists & IPPF_RTHDR)) { 62490f1702c5SYu Xiangning if (ipp->ipp_fields & IPPF_RTDSTOPTS) { 62500f1702c5SYu Xiangning option_exists |= IPPF_RTDSTOPTS; 62510f1702c5SYu Xiangning udp_ip_hdr_len += ipp->ipp_rtdstoptslen; 62520f1702c5SYu Xiangning } else if (udp->udp_sticky_ipp.ipp_fields & IPPF_RTDSTOPTS) { 62530f1702c5SYu Xiangning option_exists |= IPPF_RTDSTOPTS; 62540f1702c5SYu Xiangning is_sticky |= IPPF_RTDSTOPTS; 62550f1702c5SYu Xiangning udp_ip_hdr_len += udp->udp_sticky_ipp.ipp_rtdstoptslen; 6256ff550d0eSmasputra } 62570f1702c5SYu Xiangning } 62587c478bd9Sstevel@tonic-gate 62590f1702c5SYu Xiangning if (!(ignore & IPPF_DSTOPTS)) { 62600f1702c5SYu Xiangning if (ipp->ipp_fields & IPPF_DSTOPTS) { 62610f1702c5SYu Xiangning option_exists |= IPPF_DSTOPTS; 62620f1702c5SYu Xiangning udp_ip_hdr_len += ipp->ipp_dstoptslen; 62630f1702c5SYu Xiangning } else if (udp->udp_sticky_ipp.ipp_fields & IPPF_DSTOPTS) { 62640f1702c5SYu Xiangning option_exists |= IPPF_DSTOPTS; 62650f1702c5SYu Xiangning is_sticky |= IPPF_DSTOPTS; 62660f1702c5SYu Xiangning udp_ip_hdr_len += udp->udp_sticky_ipp.ipp_dstoptslen; 62677c478bd9Sstevel@tonic-gate } 62680f1702c5SYu Xiangning } 62697c478bd9Sstevel@tonic-gate 62700f1702c5SYu Xiangning if (!(ignore & IPPF_IFINDEX)) { 62710f1702c5SYu Xiangning if (ipp->ipp_fields & IPPF_IFINDEX) { 62720f1702c5SYu Xiangning option_exists |= IPPF_IFINDEX; 62730f1702c5SYu Xiangning } else if (udp->udp_sticky_ipp.ipp_fields & IPPF_IFINDEX) { 62740f1702c5SYu Xiangning option_exists |= IPPF_IFINDEX; 62750f1702c5SYu Xiangning is_sticky |= IPPF_IFINDEX; 62767c478bd9Sstevel@tonic-gate } 62770f1702c5SYu Xiangning } 62787c478bd9Sstevel@tonic-gate 62790f1702c5SYu Xiangning if (!(ignore & IPPF_ADDR)) { 62800f1702c5SYu Xiangning if (ipp->ipp_fields & IPPF_ADDR) { 62810f1702c5SYu Xiangning option_exists |= IPPF_ADDR; 62820f1702c5SYu Xiangning } else if (udp->udp_sticky_ipp.ipp_fields & IPPF_ADDR) { 62830f1702c5SYu Xiangning option_exists |= IPPF_ADDR; 62840f1702c5SYu Xiangning is_sticky |= IPPF_ADDR; 62850f1702c5SYu Xiangning } 62860f1702c5SYu Xiangning } 62877c478bd9Sstevel@tonic-gate 62880f1702c5SYu Xiangning if (!(ignore & IPPF_DONTFRAG)) { 62890f1702c5SYu Xiangning if (ipp->ipp_fields & IPPF_DONTFRAG) { 62900f1702c5SYu Xiangning option_exists |= IPPF_DONTFRAG; 62910f1702c5SYu Xiangning } else if (udp->udp_sticky_ipp.ipp_fields & IPPF_DONTFRAG) { 62920f1702c5SYu Xiangning option_exists |= IPPF_DONTFRAG; 62930f1702c5SYu Xiangning is_sticky |= IPPF_DONTFRAG; 62940f1702c5SYu Xiangning } 62950f1702c5SYu Xiangning } 62967c478bd9Sstevel@tonic-gate 62970f1702c5SYu Xiangning if (!(ignore & IPPF_USE_MIN_MTU)) { 62980f1702c5SYu Xiangning if (ipp->ipp_fields & IPPF_USE_MIN_MTU) { 62990f1702c5SYu Xiangning option_exists |= IPPF_USE_MIN_MTU; 63000f1702c5SYu Xiangning } else if (udp->udp_sticky_ipp.ipp_fields & 63010f1702c5SYu Xiangning IPPF_USE_MIN_MTU) { 63020f1702c5SYu Xiangning option_exists |= IPPF_USE_MIN_MTU; 63030f1702c5SYu Xiangning is_sticky |= IPPF_USE_MIN_MTU; 63040f1702c5SYu Xiangning } 63050f1702c5SYu Xiangning } 63067c478bd9Sstevel@tonic-gate 63070f1702c5SYu Xiangning if (!(ignore & IPPF_HOPLIMIT) && (ipp->ipp_fields & IPPF_HOPLIMIT)) 63080f1702c5SYu Xiangning option_exists |= IPPF_HOPLIMIT; 63090f1702c5SYu Xiangning /* IPV6_HOPLIMIT can never be sticky */ 63100f1702c5SYu Xiangning ASSERT(!(udp->udp_sticky_ipp.ipp_fields & IPPF_HOPLIMIT)); 63117c478bd9Sstevel@tonic-gate 63120f1702c5SYu Xiangning if (!(ignore & IPPF_UNICAST_HOPS) && 63130f1702c5SYu Xiangning (udp->udp_sticky_ipp.ipp_fields & IPPF_UNICAST_HOPS)) { 63140f1702c5SYu Xiangning option_exists |= IPPF_UNICAST_HOPS; 63150f1702c5SYu Xiangning is_sticky |= IPPF_UNICAST_HOPS; 63160f1702c5SYu Xiangning } 63177c478bd9Sstevel@tonic-gate 63180f1702c5SYu Xiangning if (!(ignore & IPPF_MULTICAST_HOPS) && 63190f1702c5SYu Xiangning (udp->udp_sticky_ipp.ipp_fields & IPPF_MULTICAST_HOPS)) { 63200f1702c5SYu Xiangning option_exists |= IPPF_MULTICAST_HOPS; 63210f1702c5SYu Xiangning is_sticky |= IPPF_MULTICAST_HOPS; 63220f1702c5SYu Xiangning } 63237c478bd9Sstevel@tonic-gate 63240f1702c5SYu Xiangning if (!(ignore & IPPF_TCLASS)) { 63250f1702c5SYu Xiangning if (ipp->ipp_fields & IPPF_TCLASS) { 63260f1702c5SYu Xiangning option_exists |= IPPF_TCLASS; 63270f1702c5SYu Xiangning } else if (udp->udp_sticky_ipp.ipp_fields & IPPF_TCLASS) { 63280f1702c5SYu Xiangning option_exists |= IPPF_TCLASS; 63290f1702c5SYu Xiangning is_sticky |= IPPF_TCLASS; 63300f1702c5SYu Xiangning } 63310f1702c5SYu Xiangning } 63327c478bd9Sstevel@tonic-gate 63330f1702c5SYu Xiangning if (!(ignore & IPPF_NEXTHOP) && 63340f1702c5SYu Xiangning (udp->udp_sticky_ipp.ipp_fields & IPPF_NEXTHOP)) { 63350f1702c5SYu Xiangning option_exists |= IPPF_NEXTHOP; 63360f1702c5SYu Xiangning is_sticky |= IPPF_NEXTHOP; 63370f1702c5SYu Xiangning } 63387c478bd9Sstevel@tonic-gate 63390f1702c5SYu Xiangning no_options: 6340ae43f94fSnordmark 63410f1702c5SYu Xiangning /* 63420f1702c5SYu Xiangning * If any options carried in the ip6i_t were specified, we 63430f1702c5SYu Xiangning * need to account for the ip6i_t in the data we'll be sending 63440f1702c5SYu Xiangning * down. 63450f1702c5SYu Xiangning */ 63460f1702c5SYu Xiangning if (option_exists & IPPF_HAS_IP6I) 63470f1702c5SYu Xiangning udp_ip_hdr_len += sizeof (ip6i_t); 6348ae43f94fSnordmark 63490f1702c5SYu Xiangning /* check/fix buffer config, setup pointers into it */ 63500f1702c5SYu Xiangning ip6h = (ip6_t *)&mp1->b_rptr[-udp_ip_hdr_len]; 63510f1702c5SYu Xiangning if (DB_REF(mp1) != 1 || ((unsigned char *)ip6h < DB_BASE(mp1)) || 63520f1702c5SYu Xiangning !OK_32PTR(ip6h)) { 63530f1702c5SYu Xiangning 63540f1702c5SYu Xiangning /* Try to get everything in a single mblk next time */ 63550f1702c5SYu Xiangning if (udp_ip_hdr_len > udp->udp_max_hdr_len) { 63560f1702c5SYu Xiangning udp->udp_max_hdr_len = udp_ip_hdr_len; 63570f1702c5SYu Xiangning sth_wroff = udp->udp_max_hdr_len + us->us_wroff_extra; 63587c478bd9Sstevel@tonic-gate } 63597c478bd9Sstevel@tonic-gate 63600f1702c5SYu Xiangning mp2 = allocb(udp_ip_hdr_len + us->us_wroff_extra, BPRI_LO); 63610f1702c5SYu Xiangning if (mp2 == NULL) { 63620f1702c5SYu Xiangning *error = ENOMEM; 63630f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 63640f1702c5SYu Xiangning goto done; 63650f1702c5SYu Xiangning } 63660f1702c5SYu Xiangning mp2->b_wptr = DB_LIM(mp2); 63670f1702c5SYu Xiangning mp2->b_cont = mp1; 63680f1702c5SYu Xiangning mp1 = mp2; 63690f1702c5SYu Xiangning if (DB_TYPE(mp) != M_DATA) 63700f1702c5SYu Xiangning mp->b_cont = mp1; 63710f1702c5SYu Xiangning else 63720f1702c5SYu Xiangning mp = mp1; 6373ff550d0eSmasputra 63740f1702c5SYu Xiangning ip6h = (ip6_t *)(mp1->b_wptr - udp_ip_hdr_len); 6375ff550d0eSmasputra } 63760f1702c5SYu Xiangning mp1->b_rptr = (unsigned char *)ip6h; 63770f1702c5SYu Xiangning ip6i = (ip6i_t *)ip6h; 6378ff550d0eSmasputra 63790f1702c5SYu Xiangning #define ANCIL_OR_STICKY_PTR(f) ((is_sticky & f) ? &udp->udp_sticky_ipp : ipp) 63800f1702c5SYu Xiangning if (option_exists & IPPF_HAS_IP6I) { 63810f1702c5SYu Xiangning ip6h = (ip6_t *)&ip6i[1]; 63820f1702c5SYu Xiangning ip6i->ip6i_flags = 0; 63830f1702c5SYu Xiangning ip6i->ip6i_vcf = IPV6_DEFAULT_VERS_AND_FLOW; 63847c478bd9Sstevel@tonic-gate 63850f1702c5SYu Xiangning /* sin6_scope_id takes precendence over IPPF_IFINDEX */ 63860f1702c5SYu Xiangning if (option_exists & IPPF_SCOPE_ID) { 63870f1702c5SYu Xiangning ip6i->ip6i_flags |= IP6I_IFINDEX; 63880f1702c5SYu Xiangning ip6i->ip6i_ifindex = sin6->sin6_scope_id; 63890f1702c5SYu Xiangning } else if (option_exists & IPPF_IFINDEX) { 63900f1702c5SYu Xiangning tipp = ANCIL_OR_STICKY_PTR(IPPF_IFINDEX); 63910f1702c5SYu Xiangning ASSERT(tipp->ipp_ifindex != 0); 63920f1702c5SYu Xiangning ip6i->ip6i_flags |= IP6I_IFINDEX; 63930f1702c5SYu Xiangning ip6i->ip6i_ifindex = tipp->ipp_ifindex; 63940f1702c5SYu Xiangning } 63957c478bd9Sstevel@tonic-gate 63960f1702c5SYu Xiangning if (option_exists & IPPF_ADDR) { 63970f1702c5SYu Xiangning /* 63980f1702c5SYu Xiangning * Enable per-packet source address verification if 63990f1702c5SYu Xiangning * IPV6_PKTINFO specified the source address. 64000f1702c5SYu Xiangning * ip6_src is set in the transport's _wput function. 64010f1702c5SYu Xiangning */ 64020f1702c5SYu Xiangning ip6i->ip6i_flags |= IP6I_VERIFY_SRC; 64037c478bd9Sstevel@tonic-gate } 64047c478bd9Sstevel@tonic-gate 64050f1702c5SYu Xiangning if (option_exists & IPPF_DONTFRAG) { 64060f1702c5SYu Xiangning ip6i->ip6i_flags |= IP6I_DONTFRAG; 64070f1702c5SYu Xiangning } 64087c478bd9Sstevel@tonic-gate 64090f1702c5SYu Xiangning if (option_exists & IPPF_USE_MIN_MTU) { 64100f1702c5SYu Xiangning ip6i->ip6i_flags = IP6I_API_USE_MIN_MTU( 64110f1702c5SYu Xiangning ip6i->ip6i_flags, ipp->ipp_use_min_mtu); 64120f1702c5SYu Xiangning } 64137c478bd9Sstevel@tonic-gate 64140f1702c5SYu Xiangning if (option_exists & IPPF_NEXTHOP) { 64150f1702c5SYu Xiangning tipp = ANCIL_OR_STICKY_PTR(IPPF_NEXTHOP); 64160f1702c5SYu Xiangning ASSERT(!IN6_IS_ADDR_UNSPECIFIED(&tipp->ipp_nexthop)); 64170f1702c5SYu Xiangning ip6i->ip6i_flags |= IP6I_NEXTHOP; 64180f1702c5SYu Xiangning ip6i->ip6i_nexthop = tipp->ipp_nexthop; 64197c478bd9Sstevel@tonic-gate } 64200f1702c5SYu Xiangning 64210f1702c5SYu Xiangning /* 64220f1702c5SYu Xiangning * tell IP this is an ip6i_t private header 64230f1702c5SYu Xiangning */ 64240f1702c5SYu Xiangning ip6i->ip6i_nxt = IPPROTO_RAW; 64257c478bd9Sstevel@tonic-gate } 64267c478bd9Sstevel@tonic-gate 64270f1702c5SYu Xiangning /* Initialize IPv6 header */ 64280f1702c5SYu Xiangning ip6h->ip6_vcf = IPV6_DEFAULT_VERS_AND_FLOW; 64290f1702c5SYu Xiangning bzero(&ip6h->ip6_src, sizeof (ip6h->ip6_src)); 64307c478bd9Sstevel@tonic-gate 64310f1702c5SYu Xiangning /* Set the hoplimit of the outgoing packet. */ 64320f1702c5SYu Xiangning if (option_exists & IPPF_HOPLIMIT) { 64330f1702c5SYu Xiangning /* IPV6_HOPLIMIT ancillary data overrides all other settings. */ 64340f1702c5SYu Xiangning ip6h->ip6_hops = ipp->ipp_hoplimit; 64350f1702c5SYu Xiangning ip6i->ip6i_flags |= IP6I_HOPLIMIT; 64360f1702c5SYu Xiangning } else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) { 64370f1702c5SYu Xiangning ip6h->ip6_hops = udp->udp_multicast_ttl; 64380f1702c5SYu Xiangning if (option_exists & IPPF_MULTICAST_HOPS) 64390f1702c5SYu Xiangning ip6i->ip6i_flags |= IP6I_HOPLIMIT; 64400f1702c5SYu Xiangning } else { 64410f1702c5SYu Xiangning ip6h->ip6_hops = udp->udp_ttl; 64420f1702c5SYu Xiangning if (option_exists & IPPF_UNICAST_HOPS) 64430f1702c5SYu Xiangning ip6i->ip6i_flags |= IP6I_HOPLIMIT; 64440f1702c5SYu Xiangning } 64457c478bd9Sstevel@tonic-gate 64460f1702c5SYu Xiangning if (option_exists & IPPF_ADDR) { 64470f1702c5SYu Xiangning tipp = ANCIL_OR_STICKY_PTR(IPPF_ADDR); 64480f1702c5SYu Xiangning ASSERT(!IN6_IS_ADDR_UNSPECIFIED(&tipp->ipp_addr)); 64490f1702c5SYu Xiangning ip6h->ip6_src = tipp->ipp_addr; 64500f1702c5SYu Xiangning } else { 64517c478bd9Sstevel@tonic-gate /* 64520f1702c5SYu Xiangning * The source address was not set using IPV6_PKTINFO. 64530f1702c5SYu Xiangning * First look at the bound source. 64540f1702c5SYu Xiangning * If unspecified fallback to __sin6_src_id. 64557c478bd9Sstevel@tonic-gate */ 64560f1702c5SYu Xiangning ip6h->ip6_src = udp->udp_v6src; 64570f1702c5SYu Xiangning if (sin6->__sin6_src_id != 0 && 64580f1702c5SYu Xiangning IN6_IS_ADDR_UNSPECIFIED(&ip6h->ip6_src)) { 64590f1702c5SYu Xiangning ip_srcid_find_id(sin6->__sin6_src_id, 64600f1702c5SYu Xiangning &ip6h->ip6_src, connp->conn_zoneid, 64610f1702c5SYu Xiangning us->us_netstack); 64627c478bd9Sstevel@tonic-gate } 64637c478bd9Sstevel@tonic-gate } 64640f1702c5SYu Xiangning 64650f1702c5SYu Xiangning nxthdr_ptr = (uint8_t *)&ip6h->ip6_nxt; 64660f1702c5SYu Xiangning cp = (uint8_t *)&ip6h[1]; 64670f1702c5SYu Xiangning 64687c478bd9Sstevel@tonic-gate /* 64690f1702c5SYu Xiangning * Here's where we have to start stringing together 64700f1702c5SYu Xiangning * any extension headers in the right order: 64710f1702c5SYu Xiangning * Hop-by-hop, destination, routing, and final destination opts. 64727c478bd9Sstevel@tonic-gate */ 64730f1702c5SYu Xiangning if (option_exists & IPPF_HOPOPTS) { 64740f1702c5SYu Xiangning /* Hop-by-hop options */ 64750f1702c5SYu Xiangning ip6_hbh_t *hbh = (ip6_hbh_t *)cp; 64760f1702c5SYu Xiangning tipp = ANCIL_OR_STICKY_PTR(IPPF_HOPOPTS); 64770f1702c5SYu Xiangning if (hopoptslen == 0) { 64780f1702c5SYu Xiangning hopoptsptr = tipp->ipp_hopopts; 64790f1702c5SYu Xiangning hopoptslen = tipp->ipp_hopoptslen; 64800f1702c5SYu Xiangning is_ancillary = B_TRUE; 64817c478bd9Sstevel@tonic-gate } 6482fc80c0dfSnordmark 64830f1702c5SYu Xiangning *nxthdr_ptr = IPPROTO_HOPOPTS; 64840f1702c5SYu Xiangning nxthdr_ptr = &hbh->ip6h_nxt; 6485fc80c0dfSnordmark 64860f1702c5SYu Xiangning bcopy(hopoptsptr, cp, hopoptslen); 64870f1702c5SYu Xiangning cp += hopoptslen; 6488fc80c0dfSnordmark 64890f1702c5SYu Xiangning if (hopoptsptr != NULL && !is_ancillary) { 64900f1702c5SYu Xiangning kmem_free(hopoptsptr, hopoptslen); 64910f1702c5SYu Xiangning hopoptsptr = NULL; 64920f1702c5SYu Xiangning hopoptslen = 0; 64930f1702c5SYu Xiangning } 64940f1702c5SYu Xiangning } 6495fc80c0dfSnordmark /* 64960f1702c5SYu Xiangning * En-route destination options 64970f1702c5SYu Xiangning * Only do them if there's a routing header as well 6498fc80c0dfSnordmark */ 64990f1702c5SYu Xiangning if (option_exists & IPPF_RTDSTOPTS) { 65000f1702c5SYu Xiangning ip6_dest_t *dst = (ip6_dest_t *)cp; 65010f1702c5SYu Xiangning tipp = ANCIL_OR_STICKY_PTR(IPPF_RTDSTOPTS); 6502fc80c0dfSnordmark 65030f1702c5SYu Xiangning *nxthdr_ptr = IPPROTO_DSTOPTS; 65040f1702c5SYu Xiangning nxthdr_ptr = &dst->ip6d_nxt; 6505fc80c0dfSnordmark 65060f1702c5SYu Xiangning bcopy(tipp->ipp_rtdstopts, cp, tipp->ipp_rtdstoptslen); 65070f1702c5SYu Xiangning cp += tipp->ipp_rtdstoptslen; 6508fc80c0dfSnordmark } 6509fc80c0dfSnordmark /* 65100f1702c5SYu Xiangning * Routing header next 6511fc80c0dfSnordmark */ 65120f1702c5SYu Xiangning if (option_exists & IPPF_RTHDR) { 65130f1702c5SYu Xiangning ip6_rthdr_t *rt = (ip6_rthdr_t *)cp; 65140f1702c5SYu Xiangning tipp = ANCIL_OR_STICKY_PTR(IPPF_RTHDR); 65157c478bd9Sstevel@tonic-gate 65160f1702c5SYu Xiangning *nxthdr_ptr = IPPROTO_ROUTING; 65170f1702c5SYu Xiangning nxthdr_ptr = &rt->ip6r_nxt; 651845916cd2Sjpk 65190f1702c5SYu Xiangning bcopy(tipp->ipp_rthdr, cp, tipp->ipp_rthdrlen); 65200f1702c5SYu Xiangning cp += tipp->ipp_rthdrlen; 65210f1702c5SYu Xiangning } 65220f1702c5SYu Xiangning /* 65230f1702c5SYu Xiangning * Do ultimate destination options 65240f1702c5SYu Xiangning */ 65250f1702c5SYu Xiangning if (option_exists & IPPF_DSTOPTS) { 65260f1702c5SYu Xiangning ip6_dest_t *dest = (ip6_dest_t *)cp; 65270f1702c5SYu Xiangning tipp = ANCIL_OR_STICKY_PTR(IPPF_DSTOPTS); 65287c478bd9Sstevel@tonic-gate 65290f1702c5SYu Xiangning *nxthdr_ptr = IPPROTO_DSTOPTS; 65300f1702c5SYu Xiangning nxthdr_ptr = &dest->ip6d_nxt; 65317c478bd9Sstevel@tonic-gate 65320f1702c5SYu Xiangning bcopy(tipp->ipp_dstopts, cp, tipp->ipp_dstoptslen); 65330f1702c5SYu Xiangning cp += tipp->ipp_dstoptslen; 65340f1702c5SYu Xiangning } 65350f1702c5SYu Xiangning /* 65360f1702c5SYu Xiangning * Now set the last header pointer to the proto passed in 65370f1702c5SYu Xiangning */ 65380f1702c5SYu Xiangning ASSERT((int)(cp - (uint8_t *)ip6i) == (udp_ip_hdr_len - UDPH_SIZE)); 65390f1702c5SYu Xiangning *nxthdr_ptr = IPPROTO_UDP; 65407c478bd9Sstevel@tonic-gate 65410f1702c5SYu Xiangning /* Update UDP header */ 65420f1702c5SYu Xiangning udph = (udpha_t *)((uchar_t *)ip6i + udp_ip_hdr_len - UDPH_SIZE); 65430f1702c5SYu Xiangning udph->uha_dst_port = sin6->sin6_port; 65440f1702c5SYu Xiangning udph->uha_src_port = udp->udp_port; 65457c478bd9Sstevel@tonic-gate 65460f1702c5SYu Xiangning /* 65470f1702c5SYu Xiangning * Copy in the destination address 65480f1702c5SYu Xiangning */ 65490f1702c5SYu Xiangning ip6h->ip6_dst = ip6_dst; 655045916cd2Sjpk 65510f1702c5SYu Xiangning ip6h->ip6_vcf = 65520f1702c5SYu Xiangning (IPV6_DEFAULT_VERS_AND_FLOW & IPV6_VERS_AND_FLOW_MASK) | 65530f1702c5SYu Xiangning (sin6->sin6_flowinfo & ~IPV6_VERS_AND_FLOW_MASK); 65543173664eSapersson 65550f1702c5SYu Xiangning if (option_exists & IPPF_TCLASS) { 65560f1702c5SYu Xiangning tipp = ANCIL_OR_STICKY_PTR(IPPF_TCLASS); 65570f1702c5SYu Xiangning ip6h->ip6_vcf = IPV6_TCLASS_FLOW(ip6h->ip6_vcf, 65580f1702c5SYu Xiangning tipp->ipp_tclass); 65590f1702c5SYu Xiangning } 65600f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 65613173664eSapersson 65620f1702c5SYu Xiangning if (option_exists & IPPF_RTHDR) { 65630f1702c5SYu Xiangning ip6_rthdr_t *rth; 65640f1702c5SYu Xiangning 65650f1702c5SYu Xiangning /* 65660f1702c5SYu Xiangning * Perform any processing needed for source routing. 65670f1702c5SYu Xiangning * We know that all extension headers will be in the same mblk 65680f1702c5SYu Xiangning * as the IPv6 header. 65690f1702c5SYu Xiangning */ 65700f1702c5SYu Xiangning rth = ip_find_rthdr_v6(ip6h, mp1->b_wptr); 65710f1702c5SYu Xiangning if (rth != NULL && rth->ip6r_segleft != 0) { 65720f1702c5SYu Xiangning if (rth->ip6r_type != IPV6_RTHDR_TYPE_0) { 65733173664eSapersson /* 65740f1702c5SYu Xiangning * Drop packet - only support Type 0 routing. 65750f1702c5SYu Xiangning * Notify the application as well. 65763173664eSapersson */ 65770f1702c5SYu Xiangning *error = EPROTO; 65780f1702c5SYu Xiangning goto done; 65790f1702c5SYu Xiangning } 65803173664eSapersson 65810f1702c5SYu Xiangning /* 65820f1702c5SYu Xiangning * rth->ip6r_len is twice the number of 65830f1702c5SYu Xiangning * addresses in the header. Thus it must be even. 65840f1702c5SYu Xiangning */ 65850f1702c5SYu Xiangning if (rth->ip6r_len & 0x1) { 65860f1702c5SYu Xiangning *error = EPROTO; 65870f1702c5SYu Xiangning goto done; 65880f1702c5SYu Xiangning } 65890f1702c5SYu Xiangning /* 65900f1702c5SYu Xiangning * Shuffle the routing header and ip6_dst 65910f1702c5SYu Xiangning * addresses, and get the checksum difference 65920f1702c5SYu Xiangning * between the first hop (in ip6_dst) and 65930f1702c5SYu Xiangning * the destination (in the last routing hdr entry). 65940f1702c5SYu Xiangning */ 65950f1702c5SYu Xiangning csum = ip_massage_options_v6(ip6h, rth, 65960f1702c5SYu Xiangning us->us_netstack); 65970f1702c5SYu Xiangning /* 65980f1702c5SYu Xiangning * Verify that the first hop isn't a mapped address. 65990f1702c5SYu Xiangning * Routers along the path need to do this verification 66000f1702c5SYu Xiangning * for subsequent hops. 66010f1702c5SYu Xiangning */ 66020f1702c5SYu Xiangning if (IN6_IS_ADDR_V4MAPPED(&ip6h->ip6_dst)) { 66030f1702c5SYu Xiangning *error = EADDRNOTAVAIL; 66040f1702c5SYu Xiangning goto done; 66057c478bd9Sstevel@tonic-gate } 66060f1702c5SYu Xiangning 66070f1702c5SYu Xiangning cp += (rth->ip6r_len + 1)*8; 66087c478bd9Sstevel@tonic-gate } 66097c478bd9Sstevel@tonic-gate } 66107c478bd9Sstevel@tonic-gate 66110f1702c5SYu Xiangning /* count up length of UDP packet */ 66120f1702c5SYu Xiangning ip_len = (mp1->b_wptr - (unsigned char *)ip6h) - IPV6_HDR_LEN; 66130f1702c5SYu Xiangning if ((mp2 = mp1->b_cont) != NULL) { 66140f1702c5SYu Xiangning do { 66150f1702c5SYu Xiangning ASSERT((uintptr_t)MBLKL(mp2) <= (uintptr_t)UINT_MAX); 66160f1702c5SYu Xiangning ip_len += (uint32_t)MBLKL(mp2); 66170f1702c5SYu Xiangning } while ((mp2 = mp2->b_cont) != NULL); 66180f1702c5SYu Xiangning } 66197c478bd9Sstevel@tonic-gate 66200f1702c5SYu Xiangning /* 66210f1702c5SYu Xiangning * If the size of the packet is greater than the maximum allowed by 66220f1702c5SYu Xiangning * ip, return an error. Passing this down could cause panics because 66230f1702c5SYu Xiangning * the size will have wrapped and be inconsistent with the msg size. 66240f1702c5SYu Xiangning */ 66250f1702c5SYu Xiangning if (ip_len > IP_MAXPACKET) { 66260f1702c5SYu Xiangning *error = EMSGSIZE; 66270f1702c5SYu Xiangning goto done; 66280f1702c5SYu Xiangning } 662945916cd2Sjpk 66300f1702c5SYu Xiangning /* Store the UDP length. Subtract length of extension hdrs */ 66310f1702c5SYu Xiangning udph->uha_length = htons(ip_len + IPV6_HDR_LEN - 66320f1702c5SYu Xiangning (int)((uchar_t *)udph - (uchar_t *)ip6h)); 66337c478bd9Sstevel@tonic-gate 66340f1702c5SYu Xiangning /* 66350f1702c5SYu Xiangning * We make it easy for IP to include our pseudo header 66360f1702c5SYu Xiangning * by putting our length in uh_checksum, modified (if 66370f1702c5SYu Xiangning * we have a routing header) by the checksum difference 66380f1702c5SYu Xiangning * between the ultimate destination and first hop addresses. 66390f1702c5SYu Xiangning * Note: UDP over IPv6 must always checksum the packet. 66400f1702c5SYu Xiangning */ 66410f1702c5SYu Xiangning csum += udph->uha_length; 66420f1702c5SYu Xiangning csum = (csum & 0xFFFF) + (csum >> 16); 66430f1702c5SYu Xiangning udph->uha_checksum = (uint16_t)csum; 664445916cd2Sjpk 66450f1702c5SYu Xiangning #ifdef _LITTLE_ENDIAN 66460f1702c5SYu Xiangning ip_len = htons(ip_len); 66470f1702c5SYu Xiangning #endif 66480f1702c5SYu Xiangning ip6h->ip6_plen = ip_len; 66490f1702c5SYu Xiangning if (DB_CRED(mp) != NULL) 66500f1702c5SYu Xiangning mblk_setcred(mp1, DB_CRED(mp)); 66517c478bd9Sstevel@tonic-gate 66520f1702c5SYu Xiangning if (DB_TYPE(mp) != M_DATA) { 66530f1702c5SYu Xiangning ASSERT(mp != mp1); 66540f1702c5SYu Xiangning freeb(mp); 66557c478bd9Sstevel@tonic-gate } 66567c478bd9Sstevel@tonic-gate 66570f1702c5SYu Xiangning /* mp has been consumed and we'll return success */ 66580f1702c5SYu Xiangning ASSERT(*error == 0); 66590f1702c5SYu Xiangning mp = NULL; 66607c478bd9Sstevel@tonic-gate 66610f1702c5SYu Xiangning /* We're done. Pass the packet to IP */ 66620f1702c5SYu Xiangning BUMP_MIB(&us->us_udp_mib, udpHCOutDatagrams); 66630f1702c5SYu Xiangning ip_output_v6(connp, mp1, q, IP_WPUT); 66647c478bd9Sstevel@tonic-gate 66650f1702c5SYu Xiangning done: 66660f1702c5SYu Xiangning if (sth_wroff != 0) { 66670f1702c5SYu Xiangning (void) proto_set_tx_wroff(RD(q), connp, 66680f1702c5SYu Xiangning udp->udp_max_hdr_len + us->us_wroff_extra); 66697c478bd9Sstevel@tonic-gate } 66700f1702c5SYu Xiangning if (hopoptsptr != NULL && !is_ancillary) { 66710f1702c5SYu Xiangning kmem_free(hopoptsptr, hopoptslen); 66720f1702c5SYu Xiangning hopoptsptr = NULL; 66737c478bd9Sstevel@tonic-gate } 66740f1702c5SYu Xiangning if (*error != 0) { 66750f1702c5SYu Xiangning ASSERT(mp != NULL); 66760f1702c5SYu Xiangning BUMP_MIB(&us->us_udp_mib, udpOutErrors); 66777c478bd9Sstevel@tonic-gate } 66780f1702c5SYu Xiangning return (mp); 66790f1702c5SYu Xiangning } 66807c478bd9Sstevel@tonic-gate 66817c478bd9Sstevel@tonic-gate 66820f1702c5SYu Xiangning static int 66830f1702c5SYu Xiangning i_udp_getpeername(udp_t *udp, struct sockaddr *sa, uint_t *salenp) 66840f1702c5SYu Xiangning { 66850f1702c5SYu Xiangning sin_t *sin = (sin_t *)sa; 66860f1702c5SYu Xiangning sin6_t *sin6 = (sin6_t *)sa; 66877c478bd9Sstevel@tonic-gate 66880f1702c5SYu Xiangning ASSERT(RW_LOCK_HELD(&udp->udp_rwlock)); 66897c478bd9Sstevel@tonic-gate 66900f1702c5SYu Xiangning if (udp->udp_state != TS_DATA_XFER) 66910f1702c5SYu Xiangning return (ENOTCONN); 66920f1702c5SYu Xiangning 66930f1702c5SYu Xiangning switch (udp->udp_family) { 66940f1702c5SYu Xiangning case AF_INET: 66950f1702c5SYu Xiangning ASSERT(udp->udp_ipversion == IPV4_VERSION); 66960f1702c5SYu Xiangning 66970f1702c5SYu Xiangning if (*salenp < sizeof (sin_t)) 66980f1702c5SYu Xiangning return (EINVAL); 66990f1702c5SYu Xiangning 67000f1702c5SYu Xiangning *salenp = sizeof (sin_t); 67010f1702c5SYu Xiangning *sin = sin_null; 67020f1702c5SYu Xiangning sin->sin_family = AF_INET; 67030f1702c5SYu Xiangning sin->sin_port = udp->udp_dstport; 67040f1702c5SYu Xiangning sin->sin_addr.s_addr = V4_PART_OF_V6(udp->udp_v6dst); 67050f1702c5SYu Xiangning break; 67060f1702c5SYu Xiangning 67070f1702c5SYu Xiangning case AF_INET6: 67080f1702c5SYu Xiangning if (*salenp < sizeof (sin6_t)) 67090f1702c5SYu Xiangning return (EINVAL); 67100f1702c5SYu Xiangning 67110f1702c5SYu Xiangning *salenp = sizeof (sin6_t); 67120f1702c5SYu Xiangning *sin6 = sin6_null; 67130f1702c5SYu Xiangning sin6->sin6_family = AF_INET6; 67140f1702c5SYu Xiangning sin6->sin6_port = udp->udp_dstport; 67150f1702c5SYu Xiangning sin6->sin6_addr = udp->udp_v6dst; 67160f1702c5SYu Xiangning sin6->sin6_flowinfo = udp->udp_flowinfo; 67170f1702c5SYu Xiangning break; 67187c478bd9Sstevel@tonic-gate } 67190f1702c5SYu Xiangning 67207c478bd9Sstevel@tonic-gate return (0); 67217c478bd9Sstevel@tonic-gate } 67227c478bd9Sstevel@tonic-gate 67230f1702c5SYu Xiangning static int 67240f1702c5SYu Xiangning udp_getmyname(udp_t *udp, struct sockaddr *sa, uint_t *salenp) 67257c478bd9Sstevel@tonic-gate { 67260f1702c5SYu Xiangning sin_t *sin = (sin_t *)sa; 67270f1702c5SYu Xiangning sin6_t *sin6 = (sin6_t *)sa; 67287c478bd9Sstevel@tonic-gate 67290f1702c5SYu Xiangning ASSERT(RW_LOCK_HELD(&udp->udp_rwlock)); 6730ff550d0eSmasputra 67310f1702c5SYu Xiangning switch (udp->udp_family) { 67320f1702c5SYu Xiangning case AF_INET: 67330f1702c5SYu Xiangning ASSERT(udp->udp_ipversion == IPV4_VERSION); 67347c478bd9Sstevel@tonic-gate 67350f1702c5SYu Xiangning if (*salenp < sizeof (sin_t)) 67360f1702c5SYu Xiangning return (EINVAL); 67377c478bd9Sstevel@tonic-gate 67380f1702c5SYu Xiangning *salenp = sizeof (sin_t); 67390f1702c5SYu Xiangning *sin = sin_null; 67400f1702c5SYu Xiangning sin->sin_family = AF_INET; 67410f1702c5SYu Xiangning sin->sin_port = udp->udp_port; 67427c478bd9Sstevel@tonic-gate 67437c478bd9Sstevel@tonic-gate /* 67440f1702c5SYu Xiangning * If udp_v6src is unspecified, we might be bound to broadcast 67450f1702c5SYu Xiangning * / multicast. Use udp_bound_v6src as local address instead 67460f1702c5SYu Xiangning * (that could also still be unspecified). 67477c478bd9Sstevel@tonic-gate */ 67480f1702c5SYu Xiangning if (!IN6_IS_ADDR_V4MAPPED_ANY(&udp->udp_v6src) && 67490f1702c5SYu Xiangning !IN6_IS_ADDR_UNSPECIFIED(&udp->udp_v6src)) { 67500f1702c5SYu Xiangning sin->sin_addr.s_addr = V4_PART_OF_V6(udp->udp_v6src); 67517c478bd9Sstevel@tonic-gate } else { 67520f1702c5SYu Xiangning sin->sin_addr.s_addr = 67530f1702c5SYu Xiangning V4_PART_OF_V6(udp->udp_bound_v6src); 67547c478bd9Sstevel@tonic-gate } 67550f1702c5SYu Xiangning break; 6756fc80c0dfSnordmark 67570f1702c5SYu Xiangning case AF_INET6: 67580f1702c5SYu Xiangning if (*salenp < sizeof (sin6_t)) 67590f1702c5SYu Xiangning return (EINVAL); 6760fc80c0dfSnordmark 67610f1702c5SYu Xiangning *salenp = sizeof (sin6_t); 67620f1702c5SYu Xiangning *sin6 = sin6_null; 67630f1702c5SYu Xiangning sin6->sin6_family = AF_INET6; 67640f1702c5SYu Xiangning sin6->sin6_port = udp->udp_port; 67650f1702c5SYu Xiangning sin6->sin6_flowinfo = udp->udp_flowinfo; 6766fc80c0dfSnordmark 67670f1702c5SYu Xiangning /* 67680f1702c5SYu Xiangning * If udp_v6src is unspecified, we might be bound to broadcast 67690f1702c5SYu Xiangning * / multicast. Use udp_bound_v6src as local address instead 67700f1702c5SYu Xiangning * (that could also still be unspecified). 67710f1702c5SYu Xiangning */ 67720f1702c5SYu Xiangning if (!IN6_IS_ADDR_UNSPECIFIED(&udp->udp_v6src)) 67730f1702c5SYu Xiangning sin6->sin6_addr = udp->udp_v6src; 67740f1702c5SYu Xiangning else 67750f1702c5SYu Xiangning sin6->sin6_addr = udp->udp_bound_v6src; 67760f1702c5SYu Xiangning break; 67770f1702c5SYu Xiangning } 6778fc80c0dfSnordmark 67790f1702c5SYu Xiangning return (0); 67807c478bd9Sstevel@tonic-gate } 67817c478bd9Sstevel@tonic-gate 67827c478bd9Sstevel@tonic-gate /* 67830f1702c5SYu Xiangning * Handle special out-of-band ioctl requests (see PSARC/2008/265). 67847c478bd9Sstevel@tonic-gate */ 67850f1702c5SYu Xiangning static void 67860f1702c5SYu Xiangning udp_wput_cmdblk(queue_t *q, mblk_t *mp) 67877c478bd9Sstevel@tonic-gate { 67880f1702c5SYu Xiangning void *data; 67890f1702c5SYu Xiangning mblk_t *datamp = mp->b_cont; 67900f1702c5SYu Xiangning udp_t *udp = Q_TO_UDP(q); 67910f1702c5SYu Xiangning cmdblk_t *cmdp = (cmdblk_t *)mp->b_rptr; 67927c478bd9Sstevel@tonic-gate 67930f1702c5SYu Xiangning if (datamp == NULL || MBLKL(datamp) < cmdp->cb_len) { 67940f1702c5SYu Xiangning cmdp->cb_error = EPROTO; 67950f1702c5SYu Xiangning qreply(q, mp); 67960f1702c5SYu Xiangning return; 67977c478bd9Sstevel@tonic-gate } 67980f1702c5SYu Xiangning data = datamp->b_rptr; 679945916cd2Sjpk 68000f1702c5SYu Xiangning rw_enter(&udp->udp_rwlock, RW_READER); 68010f1702c5SYu Xiangning switch (cmdp->cb_cmd) { 68020f1702c5SYu Xiangning case TI_GETPEERNAME: 68030f1702c5SYu Xiangning cmdp->cb_error = i_udp_getpeername(udp, data, &cmdp->cb_len); 68040f1702c5SYu Xiangning break; 68050f1702c5SYu Xiangning case TI_GETMYNAME: 68060f1702c5SYu Xiangning cmdp->cb_error = udp_getmyname(udp, data, &cmdp->cb_len); 68070f1702c5SYu Xiangning break; 68080f1702c5SYu Xiangning default: 68090f1702c5SYu Xiangning cmdp->cb_error = EINVAL; 68100f1702c5SYu Xiangning break; 681145916cd2Sjpk } 68120f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 681345916cd2Sjpk 68140f1702c5SYu Xiangning qreply(q, mp); 68157c478bd9Sstevel@tonic-gate } 68167c478bd9Sstevel@tonic-gate 68170f1702c5SYu Xiangning static void 68180f1702c5SYu Xiangning udp_disable_direct_sockfs(udp_t *udp) 681945916cd2Sjpk { 68200f1702c5SYu Xiangning udp->udp_issocket = B_FALSE; 68210f1702c5SYu Xiangning if (udp->udp_direct_sockfs) { 68220f1702c5SYu Xiangning /* 68230f1702c5SYu Xiangning * Disable read-side synchronous stream interface and 68240f1702c5SYu Xiangning * drain any queued data. 68250f1702c5SYu Xiangning */ 68260f1702c5SYu Xiangning udp_rcv_drain(udp->udp_connp->conn_rq, udp, B_FALSE); 68270f1702c5SYu Xiangning ASSERT(!udp->udp_direct_sockfs); 68280f1702c5SYu Xiangning UDP_STAT(udp->udp_us, udp_sock_fallback); 682945916cd2Sjpk } 683045916cd2Sjpk } 683145916cd2Sjpk 68320f1702c5SYu Xiangning static void 68330f1702c5SYu Xiangning udp_wput_other(queue_t *q, mblk_t *mp) 68347c478bd9Sstevel@tonic-gate { 68350f1702c5SYu Xiangning uchar_t *rptr = mp->b_rptr; 68360f1702c5SYu Xiangning struct datab *db; 68370f1702c5SYu Xiangning struct iocblk *iocp; 68380f1702c5SYu Xiangning cred_t *cr; 68390f1702c5SYu Xiangning conn_t *connp = Q_TO_CONN(q); 6840ff550d0eSmasputra udp_t *udp = connp->conn_udp; 68410f1702c5SYu Xiangning udp_stack_t *us; 684245916cd2Sjpk 68430f1702c5SYu Xiangning TRACE_1(TR_FAC_UDP, TR_UDP_WPUT_OTHER_START, 68440f1702c5SYu Xiangning "udp_wput_other_start: q %p", q); 68457c478bd9Sstevel@tonic-gate 68460f1702c5SYu Xiangning us = udp->udp_us; 68470f1702c5SYu Xiangning db = mp->b_datap; 684845916cd2Sjpk 68490f1702c5SYu Xiangning cr = DB_CREDDEF(mp, connp->conn_cred); 68507c478bd9Sstevel@tonic-gate 68510f1702c5SYu Xiangning switch (db->db_type) { 68520f1702c5SYu Xiangning case M_CMD: 68530f1702c5SYu Xiangning udp_wput_cmdblk(q, mp); 68540f1702c5SYu Xiangning return; 68557c478bd9Sstevel@tonic-gate 68560f1702c5SYu Xiangning case M_PROTO: 68570f1702c5SYu Xiangning case M_PCPROTO: 68580f1702c5SYu Xiangning if (mp->b_wptr - rptr < sizeof (t_scalar_t)) { 68590f1702c5SYu Xiangning freemsg(mp); 68600f1702c5SYu Xiangning TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END, 68610f1702c5SYu Xiangning "udp_wput_other_end: q %p (%S)", q, "protoshort"); 68620f1702c5SYu Xiangning return; 686319a30e1aSrshoaib } 68640f1702c5SYu Xiangning switch (((t_primp_t)rptr)->type) { 68650f1702c5SYu Xiangning case T_ADDR_REQ: 68660f1702c5SYu Xiangning udp_addr_req(q, mp); 68670f1702c5SYu Xiangning TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END, 68680f1702c5SYu Xiangning "udp_wput_other_end: q %p (%S)", q, "addrreq"); 68690f1702c5SYu Xiangning return; 68700f1702c5SYu Xiangning case O_T_BIND_REQ: 68710f1702c5SYu Xiangning case T_BIND_REQ: 68720f1702c5SYu Xiangning udp_tpi_bind(q, mp); 68730f1702c5SYu Xiangning TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END, 68740f1702c5SYu Xiangning "udp_wput_other_end: q %p (%S)", q, "bindreq"); 68750f1702c5SYu Xiangning return; 68760f1702c5SYu Xiangning case T_CONN_REQ: 68770f1702c5SYu Xiangning udp_tpi_connect(q, mp); 68780f1702c5SYu Xiangning TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END, 68790f1702c5SYu Xiangning "udp_wput_other_end: q %p (%S)", q, "connreq"); 68800f1702c5SYu Xiangning return; 68810f1702c5SYu Xiangning case T_CAPABILITY_REQ: 68820f1702c5SYu Xiangning udp_capability_req(q, mp); 68830f1702c5SYu Xiangning TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END, 68840f1702c5SYu Xiangning "udp_wput_other_end: q %p (%S)", q, "capabreq"); 68850f1702c5SYu Xiangning return; 68860f1702c5SYu Xiangning case T_INFO_REQ: 68870f1702c5SYu Xiangning udp_info_req(q, mp); 68880f1702c5SYu Xiangning TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END, 68890f1702c5SYu Xiangning "udp_wput_other_end: q %p (%S)", q, "inforeq"); 68900f1702c5SYu Xiangning return; 68910f1702c5SYu Xiangning case T_UNITDATA_REQ: 68920f1702c5SYu Xiangning /* 68930f1702c5SYu Xiangning * If a T_UNITDATA_REQ gets here, the address must 68940f1702c5SYu Xiangning * be bad. Valid T_UNITDATA_REQs are handled 68950f1702c5SYu Xiangning * in udp_wput. 68960f1702c5SYu Xiangning */ 68970f1702c5SYu Xiangning udp_ud_err(q, mp, NULL, 0, EADDRNOTAVAIL); 68980f1702c5SYu Xiangning TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END, 68990f1702c5SYu Xiangning "udp_wput_other_end: q %p (%S)", q, "unitdatareq"); 69000f1702c5SYu Xiangning return; 69010f1702c5SYu Xiangning case T_UNBIND_REQ: 69020f1702c5SYu Xiangning udp_tpi_unbind(q, mp); 69030f1702c5SYu Xiangning TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END, 69040f1702c5SYu Xiangning "udp_wput_other_end: q %p (%S)", q, "unbindreq"); 69050f1702c5SYu Xiangning return; 69060f1702c5SYu Xiangning case T_SVR4_OPTMGMT_REQ: 69070f1702c5SYu Xiangning if (!snmpcom_req(q, mp, udp_snmp_set, ip_snmp_get, 69080f1702c5SYu Xiangning cr)) { 69090f1702c5SYu Xiangning (void) svr4_optcom_req(q, 69100f1702c5SYu Xiangning mp, cr, &udp_opt_obj, B_TRUE); 69110f1702c5SYu Xiangning } 69120f1702c5SYu Xiangning TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END, 69130f1702c5SYu Xiangning "udp_wput_other_end: q %p (%S)", q, "optmgmtreq"); 69140f1702c5SYu Xiangning return; 691519a30e1aSrshoaib 69160f1702c5SYu Xiangning case T_OPTMGMT_REQ: 69170f1702c5SYu Xiangning (void) tpi_optcom_req(q, mp, cr, &udp_opt_obj, B_TRUE); 69180f1702c5SYu Xiangning TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END, 69190f1702c5SYu Xiangning "udp_wput_other_end: q %p (%S)", q, "optmgmtreq"); 69200f1702c5SYu Xiangning return; 69217c478bd9Sstevel@tonic-gate 69220f1702c5SYu Xiangning case T_DISCON_REQ: 69230f1702c5SYu Xiangning udp_tpi_disconnect(q, mp); 69240f1702c5SYu Xiangning TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END, 69250f1702c5SYu Xiangning "udp_wput_other_end: q %p (%S)", q, "disconreq"); 69260f1702c5SYu Xiangning return; 69277c478bd9Sstevel@tonic-gate 69280f1702c5SYu Xiangning /* The following TPI message is not supported by udp. */ 69290f1702c5SYu Xiangning case O_T_CONN_RES: 69300f1702c5SYu Xiangning case T_CONN_RES: 69310f1702c5SYu Xiangning udp_err_ack(q, mp, TNOTSUPPORT, 0); 69320f1702c5SYu Xiangning TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END, 69330f1702c5SYu Xiangning "udp_wput_other_end: q %p (%S)", q, 69340f1702c5SYu Xiangning "connres/disconreq"); 69350f1702c5SYu Xiangning return; 69367c478bd9Sstevel@tonic-gate 69370f1702c5SYu Xiangning /* The following 3 TPI messages are illegal for udp. */ 69380f1702c5SYu Xiangning case T_DATA_REQ: 69390f1702c5SYu Xiangning case T_EXDATA_REQ: 69400f1702c5SYu Xiangning case T_ORDREL_REQ: 69410f1702c5SYu Xiangning udp_err_ack(q, mp, TNOTSUPPORT, 0); 69420f1702c5SYu Xiangning TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END, 69430f1702c5SYu Xiangning "udp_wput_other_end: q %p (%S)", q, 69440f1702c5SYu Xiangning "data/exdata/ordrel"); 69450f1702c5SYu Xiangning return; 69460f1702c5SYu Xiangning default: 69470f1702c5SYu Xiangning break; 69480f1702c5SYu Xiangning } 69490f1702c5SYu Xiangning break; 69500f1702c5SYu Xiangning case M_FLUSH: 69510f1702c5SYu Xiangning if (*rptr & FLUSHW) 69520f1702c5SYu Xiangning flushq(q, FLUSHDATA); 69530f1702c5SYu Xiangning break; 69540f1702c5SYu Xiangning case M_IOCTL: 69550f1702c5SYu Xiangning iocp = (struct iocblk *)mp->b_rptr; 69560f1702c5SYu Xiangning switch (iocp->ioc_cmd) { 69570f1702c5SYu Xiangning case TI_GETPEERNAME: 69580f1702c5SYu Xiangning if (udp->udp_state != TS_DATA_XFER) { 69590f1702c5SYu Xiangning /* 69600f1702c5SYu Xiangning * If a default destination address has not 69610f1702c5SYu Xiangning * been associated with the stream, then we 69620f1702c5SYu Xiangning * don't know the peer's name. 69630f1702c5SYu Xiangning */ 69640f1702c5SYu Xiangning iocp->ioc_error = ENOTCONN; 69650f1702c5SYu Xiangning iocp->ioc_count = 0; 69660f1702c5SYu Xiangning mp->b_datap->db_type = M_IOCACK; 69670f1702c5SYu Xiangning qreply(q, mp); 69680f1702c5SYu Xiangning TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END, 69690f1702c5SYu Xiangning "udp_wput_other_end: q %p (%S)", q, 69700f1702c5SYu Xiangning "getpeername"); 69710f1702c5SYu Xiangning return; 69720f1702c5SYu Xiangning } 69730f1702c5SYu Xiangning /* FALLTHRU */ 69740f1702c5SYu Xiangning case TI_GETMYNAME: { 69750f1702c5SYu Xiangning /* 69760f1702c5SYu Xiangning * For TI_GETPEERNAME and TI_GETMYNAME, we first 69770f1702c5SYu Xiangning * need to copyin the user's strbuf structure. 69780f1702c5SYu Xiangning * Processing will continue in the M_IOCDATA case 69790f1702c5SYu Xiangning * below. 69800f1702c5SYu Xiangning */ 69810f1702c5SYu Xiangning mi_copyin(q, mp, NULL, 69820f1702c5SYu Xiangning SIZEOF_STRUCT(strbuf, iocp->ioc_flag)); 69830f1702c5SYu Xiangning TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END, 69840f1702c5SYu Xiangning "udp_wput_other_end: q %p (%S)", q, "getmyname"); 69850f1702c5SYu Xiangning return; 69860f1702c5SYu Xiangning } 69870f1702c5SYu Xiangning case ND_SET: 69880f1702c5SYu Xiangning /* nd_getset performs the necessary checking */ 69890f1702c5SYu Xiangning case ND_GET: 69900f1702c5SYu Xiangning if (nd_getset(q, us->us_nd, mp)) { 69910f1702c5SYu Xiangning qreply(q, mp); 69920f1702c5SYu Xiangning TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END, 69930f1702c5SYu Xiangning "udp_wput_other_end: q %p (%S)", q, "get"); 69940f1702c5SYu Xiangning return; 69950f1702c5SYu Xiangning } 69960f1702c5SYu Xiangning break; 69970f1702c5SYu Xiangning case _SIOCSOCKFALLBACK: 69980f1702c5SYu Xiangning /* 69990f1702c5SYu Xiangning * Either sockmod is about to be popped and the 70000f1702c5SYu Xiangning * socket would now be treated as a plain stream, 70010f1702c5SYu Xiangning * or a module is about to be pushed so we could 70020f1702c5SYu Xiangning * no longer use read-side synchronous stream. 70030f1702c5SYu Xiangning * Drain any queued data and disable direct sockfs 70040f1702c5SYu Xiangning * interface from now on. 70050f1702c5SYu Xiangning */ 70060f1702c5SYu Xiangning if (!udp->udp_issocket) { 70070f1702c5SYu Xiangning DB_TYPE(mp) = M_IOCNAK; 70080f1702c5SYu Xiangning iocp->ioc_error = EINVAL; 70090f1702c5SYu Xiangning } else { 70100f1702c5SYu Xiangning udp_disable_direct_sockfs(udp); 70117c478bd9Sstevel@tonic-gate 70120f1702c5SYu Xiangning DB_TYPE(mp) = M_IOCACK; 70130f1702c5SYu Xiangning iocp->ioc_error = 0; 70140f1702c5SYu Xiangning } 70150f1702c5SYu Xiangning iocp->ioc_count = 0; 70160f1702c5SYu Xiangning iocp->ioc_rval = 0; 70170f1702c5SYu Xiangning qreply(q, mp); 70180f1702c5SYu Xiangning return; 70190f1702c5SYu Xiangning default: 70200f1702c5SYu Xiangning break; 70210f1702c5SYu Xiangning } 70220f1702c5SYu Xiangning break; 70230f1702c5SYu Xiangning case M_IOCDATA: 70240f1702c5SYu Xiangning udp_wput_iocdata(q, mp); 70250f1702c5SYu Xiangning TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END, 70260f1702c5SYu Xiangning "udp_wput_other_end: q %p (%S)", q, "iocdata"); 70270f1702c5SYu Xiangning return; 70280f1702c5SYu Xiangning default: 70290f1702c5SYu Xiangning /* Unrecognized messages are passed through without change. */ 70300f1702c5SYu Xiangning break; 70317c478bd9Sstevel@tonic-gate } 70320f1702c5SYu Xiangning TRACE_2(TR_FAC_UDP, TR_UDP_WPUT_OTHER_END, 70330f1702c5SYu Xiangning "udp_wput_other_end: q %p (%S)", q, "end"); 70340f1702c5SYu Xiangning ip_output(connp, mp, q, IP_WPUT); 70350f1702c5SYu Xiangning } 70367c478bd9Sstevel@tonic-gate 70370f1702c5SYu Xiangning /* 70380f1702c5SYu Xiangning * udp_wput_iocdata is called by udp_wput_other to handle all M_IOCDATA 70390f1702c5SYu Xiangning * messages. 70400f1702c5SYu Xiangning */ 70410f1702c5SYu Xiangning static void 70420f1702c5SYu Xiangning udp_wput_iocdata(queue_t *q, mblk_t *mp) 70430f1702c5SYu Xiangning { 70440f1702c5SYu Xiangning mblk_t *mp1; 70450f1702c5SYu Xiangning struct iocblk *iocp = (struct iocblk *)mp->b_rptr; 70460f1702c5SYu Xiangning STRUCT_HANDLE(strbuf, sb); 70470f1702c5SYu Xiangning udp_t *udp = Q_TO_UDP(q); 70480f1702c5SYu Xiangning int error; 70490f1702c5SYu Xiangning uint_t addrlen; 70507c478bd9Sstevel@tonic-gate 70510f1702c5SYu Xiangning /* Make sure it is one of ours. */ 70520f1702c5SYu Xiangning switch (iocp->ioc_cmd) { 70530f1702c5SYu Xiangning case TI_GETMYNAME: 70540f1702c5SYu Xiangning case TI_GETPEERNAME: 70550f1702c5SYu Xiangning break; 70560f1702c5SYu Xiangning default: 70570f1702c5SYu Xiangning ip_output(udp->udp_connp, mp, q, IP_WPUT); 70580f1702c5SYu Xiangning return; 70590f1702c5SYu Xiangning } 70607c478bd9Sstevel@tonic-gate 70610f1702c5SYu Xiangning switch (mi_copy_state(q, mp, &mp1)) { 70620f1702c5SYu Xiangning case -1: 70630f1702c5SYu Xiangning return; 70640f1702c5SYu Xiangning case MI_COPY_CASE(MI_COPY_IN, 1): 70650f1702c5SYu Xiangning break; 70660f1702c5SYu Xiangning case MI_COPY_CASE(MI_COPY_OUT, 1): 70677c478bd9Sstevel@tonic-gate /* 70680f1702c5SYu Xiangning * The address has been copied out, so now 70690f1702c5SYu Xiangning * copyout the strbuf. 70707c478bd9Sstevel@tonic-gate */ 70710f1702c5SYu Xiangning mi_copyout(q, mp); 70720f1702c5SYu Xiangning return; 70730f1702c5SYu Xiangning case MI_COPY_CASE(MI_COPY_OUT, 2): 70747c478bd9Sstevel@tonic-gate /* 70750f1702c5SYu Xiangning * The address and strbuf have been copied out. 70760f1702c5SYu Xiangning * We're done, so just acknowledge the original 70770f1702c5SYu Xiangning * M_IOCTL. 70787c478bd9Sstevel@tonic-gate */ 70790f1702c5SYu Xiangning mi_copy_done(q, mp, 0); 70800f1702c5SYu Xiangning return; 70810f1702c5SYu Xiangning default: 70827c478bd9Sstevel@tonic-gate /* 70830f1702c5SYu Xiangning * Something strange has happened, so acknowledge 70840f1702c5SYu Xiangning * the original M_IOCTL with an EPROTO error. 70857c478bd9Sstevel@tonic-gate */ 70860f1702c5SYu Xiangning mi_copy_done(q, mp, EPROTO); 70870f1702c5SYu Xiangning return; 70887c478bd9Sstevel@tonic-gate } 70897c478bd9Sstevel@tonic-gate 70900f1702c5SYu Xiangning /* 70910f1702c5SYu Xiangning * Now we have the strbuf structure for TI_GETMYNAME 70920f1702c5SYu Xiangning * and TI_GETPEERNAME. Next we copyout the requested 70930f1702c5SYu Xiangning * address and then we'll copyout the strbuf. 70940f1702c5SYu Xiangning */ 70950f1702c5SYu Xiangning STRUCT_SET_HANDLE(sb, iocp->ioc_flag, (void *)mp1->b_rptr); 70960f1702c5SYu Xiangning addrlen = udp->udp_family == AF_INET ? sizeof (sin_t) : sizeof (sin6_t); 70970f1702c5SYu Xiangning if (STRUCT_FGET(sb, maxlen) < addrlen) { 70980f1702c5SYu Xiangning mi_copy_done(q, mp, EINVAL); 70990f1702c5SYu Xiangning return; 7100ff550d0eSmasputra } 7101ff550d0eSmasputra 71020f1702c5SYu Xiangning mp1 = mi_copyout_alloc(q, mp, STRUCT_FGETP(sb, buf), addrlen, B_TRUE); 71037c478bd9Sstevel@tonic-gate 71040f1702c5SYu Xiangning if (mp1 == NULL) 71050f1702c5SYu Xiangning return; 7106ff550d0eSmasputra 71070f1702c5SYu Xiangning rw_enter(&udp->udp_rwlock, RW_READER); 71080f1702c5SYu Xiangning switch (iocp->ioc_cmd) { 71090f1702c5SYu Xiangning case TI_GETMYNAME: 71100f1702c5SYu Xiangning error = udp_do_getsockname(udp, (void *)mp1->b_rptr, &addrlen); 71110f1702c5SYu Xiangning break; 71120f1702c5SYu Xiangning case TI_GETPEERNAME: 71130f1702c5SYu Xiangning error = udp_do_getpeername(udp, (void *)mp1->b_rptr, &addrlen); 71140f1702c5SYu Xiangning break; 7115ff550d0eSmasputra } 71160f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 7117ff550d0eSmasputra 71180f1702c5SYu Xiangning if (error != 0) { 71190f1702c5SYu Xiangning mi_copy_done(q, mp, error); 71200f1702c5SYu Xiangning } else { 71210f1702c5SYu Xiangning mp1->b_wptr += addrlen; 71220f1702c5SYu Xiangning STRUCT_FSET(sb, len, addrlen); 71230f1702c5SYu Xiangning 71240f1702c5SYu Xiangning /* Copy out the address */ 71250f1702c5SYu Xiangning mi_copyout(q, mp); 7126ff550d0eSmasputra } 7127ff550d0eSmasputra } 7128ff550d0eSmasputra 71290f1702c5SYu Xiangning static int 71300f1702c5SYu Xiangning udp_unitdata_opt_process(queue_t *q, mblk_t *mp, int *errorp, 71310f1702c5SYu Xiangning udpattrs_t *udpattrs) 7132ff550d0eSmasputra { 71330f1702c5SYu Xiangning struct T_unitdata_req *udreqp; 71340f1702c5SYu Xiangning int is_absreq_failure; 71350f1702c5SYu Xiangning cred_t *cr; 71360f1702c5SYu Xiangning conn_t *connp = Q_TO_CONN(q); 7137ff550d0eSmasputra 71380f1702c5SYu Xiangning ASSERT(((t_primp_t)mp->b_rptr)->type); 7139ff550d0eSmasputra 71400f1702c5SYu Xiangning cr = DB_CREDDEF(mp, connp->conn_cred); 7141ff550d0eSmasputra 71420f1702c5SYu Xiangning udreqp = (struct T_unitdata_req *)mp->b_rptr; 7143ff550d0eSmasputra 71440f1702c5SYu Xiangning *errorp = tpi_optcom_buf(q, mp, &udreqp->OPT_length, 71450f1702c5SYu Xiangning udreqp->OPT_offset, cr, &udp_opt_obj, 71460f1702c5SYu Xiangning udpattrs, &is_absreq_failure); 7147ff550d0eSmasputra 71480f1702c5SYu Xiangning if (*errorp != 0) { 71490f1702c5SYu Xiangning /* 71500f1702c5SYu Xiangning * Note: No special action needed in this 71510f1702c5SYu Xiangning * module for "is_absreq_failure" 71520f1702c5SYu Xiangning */ 71530f1702c5SYu Xiangning return (-1); /* failure */ 7154ff550d0eSmasputra } 71550f1702c5SYu Xiangning ASSERT(is_absreq_failure == 0); 71560f1702c5SYu Xiangning return (0); /* success */ 71570f1702c5SYu Xiangning } 7158ff550d0eSmasputra 71590f1702c5SYu Xiangning void 71600f1702c5SYu Xiangning udp_ddi_g_init(void) 71610f1702c5SYu Xiangning { 71620f1702c5SYu Xiangning udp_max_optsize = optcom_max_optsize(udp_opt_obj.odb_opt_des_arr, 71630f1702c5SYu Xiangning udp_opt_obj.odb_opt_arr_cnt); 7164ff550d0eSmasputra 71650f1702c5SYu Xiangning /* 71660f1702c5SYu Xiangning * We want to be informed each time a stack is created or 71670f1702c5SYu Xiangning * destroyed in the kernel, so we can maintain the 71680f1702c5SYu Xiangning * set of udp_stack_t's. 71690f1702c5SYu Xiangning */ 71700f1702c5SYu Xiangning netstack_register(NS_UDP, udp_stack_init, NULL, udp_stack_fini); 71710f1702c5SYu Xiangning } 7172ff550d0eSmasputra 71730f1702c5SYu Xiangning void 71740f1702c5SYu Xiangning udp_ddi_g_destroy(void) 71750f1702c5SYu Xiangning { 71760f1702c5SYu Xiangning netstack_unregister(NS_UDP); 71770f1702c5SYu Xiangning } 7178ff550d0eSmasputra 71790f1702c5SYu Xiangning #define INET_NAME "ip" 7180ff550d0eSmasputra 71810f1702c5SYu Xiangning /* 71820f1702c5SYu Xiangning * Initialize the UDP stack instance. 71830f1702c5SYu Xiangning */ 71840f1702c5SYu Xiangning static void * 71850f1702c5SYu Xiangning udp_stack_init(netstackid_t stackid, netstack_t *ns) 71860f1702c5SYu Xiangning { 71870f1702c5SYu Xiangning udp_stack_t *us; 71880f1702c5SYu Xiangning udpparam_t *pa; 71890f1702c5SYu Xiangning int i; 71900f1702c5SYu Xiangning int error = 0; 71910f1702c5SYu Xiangning major_t major; 71929a09d68dSja 71930f1702c5SYu Xiangning us = (udp_stack_t *)kmem_zalloc(sizeof (*us), KM_SLEEP); 71940f1702c5SYu Xiangning us->us_netstack = ns; 7195ff550d0eSmasputra 71960f1702c5SYu Xiangning us->us_num_epriv_ports = UDP_NUM_EPRIV_PORTS; 71970f1702c5SYu Xiangning us->us_epriv_ports[0] = 2049; 71980f1702c5SYu Xiangning us->us_epriv_ports[1] = 4045; 7199ff550d0eSmasputra 7200c793af95Ssangeeta /* 72010f1702c5SYu Xiangning * The smallest anonymous port in the priviledged port range which UDP 72020f1702c5SYu Xiangning * looks for free port. Use in the option UDP_ANONPRIVBIND. 7203c793af95Ssangeeta */ 72040f1702c5SYu Xiangning us->us_min_anonpriv_port = 512; 7205ff550d0eSmasputra 72060f1702c5SYu Xiangning us->us_bind_fanout_size = udp_bind_fanout_size; 72070f1702c5SYu Xiangning 72080f1702c5SYu Xiangning /* Roundup variable that might have been modified in /etc/system */ 72090f1702c5SYu Xiangning if (us->us_bind_fanout_size & (us->us_bind_fanout_size - 1)) { 72100f1702c5SYu Xiangning /* Not a power of two. Round up to nearest power of two */ 72110f1702c5SYu Xiangning for (i = 0; i < 31; i++) { 72120f1702c5SYu Xiangning if (us->us_bind_fanout_size < (1 << i)) 72130f1702c5SYu Xiangning break; 72140f1702c5SYu Xiangning } 72150f1702c5SYu Xiangning us->us_bind_fanout_size = 1 << i; 72160f1702c5SYu Xiangning } 72170f1702c5SYu Xiangning us->us_bind_fanout = kmem_zalloc(us->us_bind_fanout_size * 72180f1702c5SYu Xiangning sizeof (udp_fanout_t), KM_SLEEP); 72190f1702c5SYu Xiangning for (i = 0; i < us->us_bind_fanout_size; i++) { 72200f1702c5SYu Xiangning mutex_init(&us->us_bind_fanout[i].uf_lock, NULL, MUTEX_DEFAULT, 72210f1702c5SYu Xiangning NULL); 7222fc80c0dfSnordmark } 7223ff550d0eSmasputra 72240f1702c5SYu Xiangning pa = (udpparam_t *)kmem_alloc(sizeof (udp_param_arr), KM_SLEEP); 7225fc80c0dfSnordmark 72260f1702c5SYu Xiangning us->us_param_arr = pa; 72270f1702c5SYu Xiangning bcopy(udp_param_arr, us->us_param_arr, sizeof (udp_param_arr)); 72280f1702c5SYu Xiangning 72290f1702c5SYu Xiangning (void) udp_param_register(&us->us_nd, 72300f1702c5SYu Xiangning us->us_param_arr, A_CNT(udp_param_arr)); 72310f1702c5SYu Xiangning 72320f1702c5SYu Xiangning us->us_kstat = udp_kstat2_init(stackid, &us->us_statistics); 72330f1702c5SYu Xiangning us->us_mibkp = udp_kstat_init(stackid); 72340f1702c5SYu Xiangning 72350f1702c5SYu Xiangning major = mod_name_to_major(INET_NAME); 72360f1702c5SYu Xiangning error = ldi_ident_from_major(major, &us->us_ldi_ident); 72370f1702c5SYu Xiangning ASSERT(error == 0); 72380f1702c5SYu Xiangning return (us); 7239fc80c0dfSnordmark } 7240fc80c0dfSnordmark 72410f1702c5SYu Xiangning /* 72420f1702c5SYu Xiangning * Free the UDP stack instance. 72430f1702c5SYu Xiangning */ 7244fc80c0dfSnordmark static void 72450f1702c5SYu Xiangning udp_stack_fini(netstackid_t stackid, void *arg) 7246fc80c0dfSnordmark { 72470f1702c5SYu Xiangning udp_stack_t *us = (udp_stack_t *)arg; 72480f1702c5SYu Xiangning int i; 72493173664eSapersson 72500f1702c5SYu Xiangning for (i = 0; i < us->us_bind_fanout_size; i++) { 72510f1702c5SYu Xiangning mutex_destroy(&us->us_bind_fanout[i].uf_lock); 72520f1702c5SYu Xiangning } 7253fc80c0dfSnordmark 72540f1702c5SYu Xiangning kmem_free(us->us_bind_fanout, us->us_bind_fanout_size * 72550f1702c5SYu Xiangning sizeof (udp_fanout_t)); 7256fc80c0dfSnordmark 72570f1702c5SYu Xiangning us->us_bind_fanout = NULL; 7258ff550d0eSmasputra 72590f1702c5SYu Xiangning nd_free(&us->us_nd); 72600f1702c5SYu Xiangning kmem_free(us->us_param_arr, sizeof (udp_param_arr)); 72610f1702c5SYu Xiangning us->us_param_arr = NULL; 7262fc80c0dfSnordmark 72630f1702c5SYu Xiangning udp_kstat_fini(stackid, us->us_mibkp); 72640f1702c5SYu Xiangning us->us_mibkp = NULL; 7265fc80c0dfSnordmark 72660f1702c5SYu Xiangning udp_kstat2_fini(stackid, us->us_kstat); 72670f1702c5SYu Xiangning us->us_kstat = NULL; 72680f1702c5SYu Xiangning bzero(&us->us_statistics, sizeof (us->us_statistics)); 7269fc80c0dfSnordmark 72700f1702c5SYu Xiangning ldi_ident_release(us->us_ldi_ident); 72710f1702c5SYu Xiangning kmem_free(us, sizeof (*us)); 72720f1702c5SYu Xiangning } 7273fc80c0dfSnordmark 72740f1702c5SYu Xiangning static void * 72750f1702c5SYu Xiangning udp_kstat2_init(netstackid_t stackid, udp_stat_t *us_statisticsp) 72760f1702c5SYu Xiangning { 72770f1702c5SYu Xiangning kstat_t *ksp; 72780f1702c5SYu Xiangning 72790f1702c5SYu Xiangning udp_stat_t template = { 72800f1702c5SYu Xiangning { "udp_ip_send", KSTAT_DATA_UINT64 }, 72810f1702c5SYu Xiangning { "udp_ip_ire_send", KSTAT_DATA_UINT64 }, 72820f1702c5SYu Xiangning { "udp_ire_null", KSTAT_DATA_UINT64 }, 72830f1702c5SYu Xiangning { "udp_drain", KSTAT_DATA_UINT64 }, 72840f1702c5SYu Xiangning { "udp_sock_fallback", KSTAT_DATA_UINT64 }, 72850f1702c5SYu Xiangning { "udp_rrw_busy", KSTAT_DATA_UINT64 }, 72860f1702c5SYu Xiangning { "udp_rrw_msgcnt", KSTAT_DATA_UINT64 }, 72870f1702c5SYu Xiangning { "udp_out_sw_cksum", KSTAT_DATA_UINT64 }, 72880f1702c5SYu Xiangning { "udp_out_sw_cksum_bytes", KSTAT_DATA_UINT64 }, 72890f1702c5SYu Xiangning { "udp_out_opt", KSTAT_DATA_UINT64 }, 72900f1702c5SYu Xiangning { "udp_out_err_notconn", KSTAT_DATA_UINT64 }, 72910f1702c5SYu Xiangning { "udp_out_err_output", KSTAT_DATA_UINT64 }, 72920f1702c5SYu Xiangning { "udp_out_err_tudr", KSTAT_DATA_UINT64 }, 72930f1702c5SYu Xiangning { "udp_in_pktinfo", KSTAT_DATA_UINT64 }, 72940f1702c5SYu Xiangning { "udp_in_recvdstaddr", KSTAT_DATA_UINT64 }, 72950f1702c5SYu Xiangning { "udp_in_recvopts", KSTAT_DATA_UINT64 }, 72960f1702c5SYu Xiangning { "udp_in_recvif", KSTAT_DATA_UINT64 }, 72970f1702c5SYu Xiangning { "udp_in_recvslla", KSTAT_DATA_UINT64 }, 72980f1702c5SYu Xiangning { "udp_in_recvucred", KSTAT_DATA_UINT64 }, 72990f1702c5SYu Xiangning { "udp_in_recvttl", KSTAT_DATA_UINT64 }, 73000f1702c5SYu Xiangning { "udp_in_recvhopopts", KSTAT_DATA_UINT64 }, 73010f1702c5SYu Xiangning { "udp_in_recvhoplimit", KSTAT_DATA_UINT64 }, 73020f1702c5SYu Xiangning { "udp_in_recvdstopts", KSTAT_DATA_UINT64 }, 73030f1702c5SYu Xiangning { "udp_in_recvrtdstopts", KSTAT_DATA_UINT64 }, 73040f1702c5SYu Xiangning { "udp_in_recvrthdr", KSTAT_DATA_UINT64 }, 73050f1702c5SYu Xiangning { "udp_in_recvpktinfo", KSTAT_DATA_UINT64 }, 73060f1702c5SYu Xiangning { "udp_in_recvtclass", KSTAT_DATA_UINT64 }, 73070f1702c5SYu Xiangning { "udp_in_timestamp", KSTAT_DATA_UINT64 }, 73080f1702c5SYu Xiangning #ifdef DEBUG 73090f1702c5SYu Xiangning { "udp_data_conn", KSTAT_DATA_UINT64 }, 73100f1702c5SYu Xiangning { "udp_data_notconn", KSTAT_DATA_UINT64 }, 7311ff550d0eSmasputra #endif 73120f1702c5SYu Xiangning }; 7313ff550d0eSmasputra 73140f1702c5SYu Xiangning ksp = kstat_create_netstack(UDP_MOD_NAME, 0, "udpstat", "net", 73150f1702c5SYu Xiangning KSTAT_TYPE_NAMED, sizeof (template) / sizeof (kstat_named_t), 73160f1702c5SYu Xiangning KSTAT_FLAG_VIRTUAL, stackid); 7317ff550d0eSmasputra 73180f1702c5SYu Xiangning if (ksp == NULL) 73190f1702c5SYu Xiangning return (NULL); 7320ff550d0eSmasputra 73210f1702c5SYu Xiangning bcopy(&template, us_statisticsp, sizeof (template)); 73220f1702c5SYu Xiangning ksp->ks_data = (void *)us_statisticsp; 73230f1702c5SYu Xiangning ksp->ks_private = (void *)(uintptr_t)stackid; 7324ff550d0eSmasputra 73250f1702c5SYu Xiangning kstat_install(ksp); 73260f1702c5SYu Xiangning return (ksp); 73270f1702c5SYu Xiangning } 7328ff550d0eSmasputra 73290f1702c5SYu Xiangning static void 73300f1702c5SYu Xiangning udp_kstat2_fini(netstackid_t stackid, kstat_t *ksp) 73310f1702c5SYu Xiangning { 73320f1702c5SYu Xiangning if (ksp != NULL) { 73330f1702c5SYu Xiangning ASSERT(stackid == (netstackid_t)(uintptr_t)ksp->ks_private); 73340f1702c5SYu Xiangning kstat_delete_netstack(ksp, stackid); 7335a1165259Sblu } 73360f1702c5SYu Xiangning } 7337ff550d0eSmasputra 73380f1702c5SYu Xiangning static void * 73390f1702c5SYu Xiangning udp_kstat_init(netstackid_t stackid) 73400f1702c5SYu Xiangning { 73410f1702c5SYu Xiangning kstat_t *ksp; 7342ff550d0eSmasputra 73430f1702c5SYu Xiangning udp_named_kstat_t template = { 73440f1702c5SYu Xiangning { "inDatagrams", KSTAT_DATA_UINT64, 0 }, 73450f1702c5SYu Xiangning { "inErrors", KSTAT_DATA_UINT32, 0 }, 73460f1702c5SYu Xiangning { "outDatagrams", KSTAT_DATA_UINT64, 0 }, 73470f1702c5SYu Xiangning { "entrySize", KSTAT_DATA_INT32, 0 }, 73480f1702c5SYu Xiangning { "entry6Size", KSTAT_DATA_INT32, 0 }, 73490f1702c5SYu Xiangning { "outErrors", KSTAT_DATA_UINT32, 0 }, 73500f1702c5SYu Xiangning }; 7351ff550d0eSmasputra 73520f1702c5SYu Xiangning ksp = kstat_create_netstack(UDP_MOD_NAME, 0, UDP_MOD_NAME, "mib2", 73530f1702c5SYu Xiangning KSTAT_TYPE_NAMED, 73540f1702c5SYu Xiangning NUM_OF_FIELDS(udp_named_kstat_t), 0, stackid); 7355ff550d0eSmasputra 73560f1702c5SYu Xiangning if (ksp == NULL || ksp->ks_data == NULL) 73570f1702c5SYu Xiangning return (NULL); 73580f1702c5SYu Xiangning 73590f1702c5SYu Xiangning template.entrySize.value.ui32 = sizeof (mib2_udpEntry_t); 73600f1702c5SYu Xiangning template.entry6Size.value.ui32 = sizeof (mib2_udp6Entry_t); 73610f1702c5SYu Xiangning 73620f1702c5SYu Xiangning bcopy(&template, ksp->ks_data, sizeof (template)); 73630f1702c5SYu Xiangning ksp->ks_update = udp_kstat_update; 73640f1702c5SYu Xiangning ksp->ks_private = (void *)(uintptr_t)stackid; 73650f1702c5SYu Xiangning 73660f1702c5SYu Xiangning kstat_install(ksp); 73670f1702c5SYu Xiangning return (ksp); 73680f1702c5SYu Xiangning } 73690f1702c5SYu Xiangning 73700f1702c5SYu Xiangning static void 73710f1702c5SYu Xiangning udp_kstat_fini(netstackid_t stackid, kstat_t *ksp) 73720f1702c5SYu Xiangning { 73730f1702c5SYu Xiangning if (ksp != NULL) { 73740f1702c5SYu Xiangning ASSERT(stackid == (netstackid_t)(uintptr_t)ksp->ks_private); 73750f1702c5SYu Xiangning kstat_delete_netstack(ksp, stackid); 7376ff550d0eSmasputra } 73770f1702c5SYu Xiangning } 7378ff550d0eSmasputra 73790f1702c5SYu Xiangning static int 73800f1702c5SYu Xiangning udp_kstat_update(kstat_t *kp, int rw) 73810f1702c5SYu Xiangning { 73820f1702c5SYu Xiangning udp_named_kstat_t *udpkp; 73830f1702c5SYu Xiangning netstackid_t stackid = (netstackid_t)(uintptr_t)kp->ks_private; 73840f1702c5SYu Xiangning netstack_t *ns; 73850f1702c5SYu Xiangning udp_stack_t *us; 7386ff550d0eSmasputra 73870f1702c5SYu Xiangning if ((kp == NULL) || (kp->ks_data == NULL)) 73880f1702c5SYu Xiangning return (EIO); 7389ff550d0eSmasputra 73900f1702c5SYu Xiangning if (rw == KSTAT_WRITE) 73910f1702c5SYu Xiangning return (EACCES); 73923173664eSapersson 73930f1702c5SYu Xiangning ns = netstack_find_by_stackid(stackid); 73940f1702c5SYu Xiangning if (ns == NULL) 73950f1702c5SYu Xiangning return (-1); 73960f1702c5SYu Xiangning us = ns->netstack_udp; 73970f1702c5SYu Xiangning if (us == NULL) { 73980f1702c5SYu Xiangning netstack_rele(ns); 73990f1702c5SYu Xiangning return (-1); 74000f1702c5SYu Xiangning } 74010f1702c5SYu Xiangning udpkp = (udp_named_kstat_t *)kp->ks_data; 74020f1702c5SYu Xiangning 74030f1702c5SYu Xiangning udpkp->inDatagrams.value.ui64 = us->us_udp_mib.udpHCInDatagrams; 74040f1702c5SYu Xiangning udpkp->inErrors.value.ui32 = us->us_udp_mib.udpInErrors; 74050f1702c5SYu Xiangning udpkp->outDatagrams.value.ui64 = us->us_udp_mib.udpHCOutDatagrams; 74060f1702c5SYu Xiangning udpkp->outErrors.value.ui32 = us->us_udp_mib.udpOutErrors; 74070f1702c5SYu Xiangning netstack_rele(ns); 74080f1702c5SYu Xiangning return (0); 74090f1702c5SYu Xiangning } 7410da14cebeSEric Cheng 74110f1702c5SYu Xiangning /* 74120f1702c5SYu Xiangning * Read-side synchronous stream info entry point, called as a 74130f1702c5SYu Xiangning * result of handling certain STREAMS ioctl operations. 74140f1702c5SYu Xiangning */ 74150f1702c5SYu Xiangning static int 74160f1702c5SYu Xiangning udp_rinfop(queue_t *q, infod_t *dp) 74170f1702c5SYu Xiangning { 74180f1702c5SYu Xiangning mblk_t *mp; 74190f1702c5SYu Xiangning uint_t cmd = dp->d_cmd; 74200f1702c5SYu Xiangning int res = 0; 74210f1702c5SYu Xiangning int error = 0; 74220f1702c5SYu Xiangning udp_t *udp = Q_TO_UDP(q); 74230f1702c5SYu Xiangning struct stdata *stp = STREAM(q); 74240f1702c5SYu Xiangning 74250f1702c5SYu Xiangning mutex_enter(&udp->udp_drain_lock); 74260f1702c5SYu Xiangning /* If shutdown on read has happened, return nothing */ 74270f1702c5SYu Xiangning mutex_enter(&stp->sd_lock); 74280f1702c5SYu Xiangning if (stp->sd_flag & STREOF) { 74290f1702c5SYu Xiangning mutex_exit(&stp->sd_lock); 74300f1702c5SYu Xiangning goto done; 7431da14cebeSEric Cheng } 74320f1702c5SYu Xiangning mutex_exit(&stp->sd_lock); 7433da14cebeSEric Cheng 74340f1702c5SYu Xiangning if ((mp = udp->udp_rcv_list_head) == NULL) 74350f1702c5SYu Xiangning goto done; 7436da14cebeSEric Cheng 74370f1702c5SYu Xiangning ASSERT(DB_TYPE(mp) != M_DATA && mp->b_cont != NULL); 7438da14cebeSEric Cheng 74390f1702c5SYu Xiangning if (cmd & INFOD_COUNT) { 74400f1702c5SYu Xiangning /* 74410f1702c5SYu Xiangning * Return the number of messages. 74420f1702c5SYu Xiangning */ 74430f1702c5SYu Xiangning dp->d_count += udp->udp_rcv_msgcnt; 74440f1702c5SYu Xiangning res |= INFOD_COUNT; 74450f1702c5SYu Xiangning } 74460f1702c5SYu Xiangning if (cmd & INFOD_BYTES) { 74470f1702c5SYu Xiangning /* 74480f1702c5SYu Xiangning * Return size of all data messages. 74490f1702c5SYu Xiangning */ 74500f1702c5SYu Xiangning dp->d_bytes += udp->udp_rcv_cnt; 74510f1702c5SYu Xiangning res |= INFOD_BYTES; 74520f1702c5SYu Xiangning } 74530f1702c5SYu Xiangning if (cmd & INFOD_FIRSTBYTES) { 74540f1702c5SYu Xiangning /* 74550f1702c5SYu Xiangning * Return size of first data message. 74560f1702c5SYu Xiangning */ 74570f1702c5SYu Xiangning dp->d_bytes = msgdsize(mp); 74580f1702c5SYu Xiangning res |= INFOD_FIRSTBYTES; 74590f1702c5SYu Xiangning dp->d_cmd &= ~INFOD_FIRSTBYTES; 74600f1702c5SYu Xiangning } 74610f1702c5SYu Xiangning if (cmd & INFOD_COPYOUT) { 74620f1702c5SYu Xiangning mblk_t *mp1 = mp->b_cont; 74630f1702c5SYu Xiangning int n; 74640f1702c5SYu Xiangning /* 74650f1702c5SYu Xiangning * Return data contents of first message. 74660f1702c5SYu Xiangning */ 74670f1702c5SYu Xiangning ASSERT(DB_TYPE(mp1) == M_DATA); 74680f1702c5SYu Xiangning while (mp1 != NULL && dp->d_uiop->uio_resid > 0) { 74690f1702c5SYu Xiangning n = MIN(dp->d_uiop->uio_resid, MBLKL(mp1)); 74700f1702c5SYu Xiangning if (n != 0 && (error = uiomove((char *)mp1->b_rptr, n, 74710f1702c5SYu Xiangning UIO_READ, dp->d_uiop)) != 0) { 74720f1702c5SYu Xiangning goto done; 74730f1702c5SYu Xiangning } 74740f1702c5SYu Xiangning mp1 = mp1->b_cont; 747510e6dadfSbrendan } 74760f1702c5SYu Xiangning res |= INFOD_COPYOUT; 74770f1702c5SYu Xiangning dp->d_cmd &= ~INFOD_COPYOUT; 7478ff550d0eSmasputra } 74790f1702c5SYu Xiangning done: 74800f1702c5SYu Xiangning mutex_exit(&udp->udp_drain_lock); 74817c478bd9Sstevel@tonic-gate 74820f1702c5SYu Xiangning dp->d_res |= res; 748345916cd2Sjpk 74840f1702c5SYu Xiangning return (error); 748545916cd2Sjpk } 748645916cd2Sjpk 74870f1702c5SYu Xiangning /* 74880f1702c5SYu Xiangning * Read-side synchronous stream entry point. This is called as a result 74890f1702c5SYu Xiangning * of recv/read operation done at sockfs, and is guaranteed to execute 74900f1702c5SYu Xiangning * outside of the interrupt thread context. It returns a single datagram 74910f1702c5SYu Xiangning * (b_cont chain of T_UNITDATA_IND plus data) to the upper layer. 74920f1702c5SYu Xiangning */ 74930f1702c5SYu Xiangning static int 74940f1702c5SYu Xiangning udp_rrw(queue_t *q, struiod_t *dp) 7495fc80c0dfSnordmark { 74960f1702c5SYu Xiangning mblk_t *mp; 74970f1702c5SYu Xiangning udp_t *udp = Q_TO_UDP(q); 74980f1702c5SYu Xiangning udp_stack_t *us = udp->udp_us; 7499fc80c0dfSnordmark 75000f1702c5SYu Xiangning /* 75010f1702c5SYu Xiangning * Dequeue datagram from the head of the list and return 75020f1702c5SYu Xiangning * it to caller; also ensure that RSLEEP sd_wakeq flag is 75030f1702c5SYu Xiangning * set/cleared depending on whether or not there's data 75040f1702c5SYu Xiangning * remaining in the list. 75050f1702c5SYu Xiangning */ 75060f1702c5SYu Xiangning mutex_enter(&udp->udp_drain_lock); 75070f1702c5SYu Xiangning if (!udp->udp_direct_sockfs) { 75080f1702c5SYu Xiangning mutex_exit(&udp->udp_drain_lock); 75090f1702c5SYu Xiangning UDP_STAT(us, udp_rrw_busy); 75100f1702c5SYu Xiangning return (EBUSY); 7511fc80c0dfSnordmark } 75120f1702c5SYu Xiangning if ((mp = udp->udp_rcv_list_head) != NULL) { 75130f1702c5SYu Xiangning uint_t size = msgdsize(mp); 7514fc80c0dfSnordmark 75150f1702c5SYu Xiangning /* Last datagram in the list? */ 75160f1702c5SYu Xiangning if ((udp->udp_rcv_list_head = mp->b_next) == NULL) 75170f1702c5SYu Xiangning udp->udp_rcv_list_tail = NULL; 75180f1702c5SYu Xiangning mp->b_next = NULL; 7519fc80c0dfSnordmark 75200f1702c5SYu Xiangning udp->udp_rcv_cnt -= size; 75210f1702c5SYu Xiangning udp->udp_rcv_msgcnt--; 75220f1702c5SYu Xiangning UDP_STAT(us, udp_rrw_msgcnt); 7523fc80c0dfSnordmark 75240f1702c5SYu Xiangning /* No longer flow-controlling? */ 75250f1702c5SYu Xiangning if (udp->udp_rcv_cnt < udp->udp_rcv_hiwat && 75260f1702c5SYu Xiangning udp->udp_rcv_msgcnt < udp->udp_rcv_hiwat) 75270f1702c5SYu Xiangning udp->udp_drain_qfull = B_FALSE; 75280f1702c5SYu Xiangning } 75290f1702c5SYu Xiangning if (udp->udp_rcv_list_head == NULL) { 7530fc80c0dfSnordmark /* 75310f1702c5SYu Xiangning * Either we just dequeued the last datagram or 75320f1702c5SYu Xiangning * we get here from sockfs and have nothing to 75330f1702c5SYu Xiangning * return; in this case clear RSLEEP. 7534fc80c0dfSnordmark */ 75350f1702c5SYu Xiangning ASSERT(udp->udp_rcv_cnt == 0); 75360f1702c5SYu Xiangning ASSERT(udp->udp_rcv_msgcnt == 0); 75370f1702c5SYu Xiangning ASSERT(udp->udp_rcv_list_tail == NULL); 75380f1702c5SYu Xiangning STR_WAKEUP_CLEAR(STREAM(q)); 7539fc80c0dfSnordmark } else { 75400f1702c5SYu Xiangning /* 75410f1702c5SYu Xiangning * More data follows; we need udp_rrw() to be 75420f1702c5SYu Xiangning * called in future to pick up the rest. 75430f1702c5SYu Xiangning */ 75440f1702c5SYu Xiangning STR_WAKEUP_SET(STREAM(q)); 7545fc80c0dfSnordmark } 75460f1702c5SYu Xiangning mutex_exit(&udp->udp_drain_lock); 75470f1702c5SYu Xiangning dp->d_mp = mp; 75480f1702c5SYu Xiangning return (0); 75490f1702c5SYu Xiangning } 75500f1702c5SYu Xiangning 75510f1702c5SYu Xiangning /* 75520f1702c5SYu Xiangning * Enqueue a completely-built T_UNITDATA_IND message into the receive 75530f1702c5SYu Xiangning * list; this is typically executed within the interrupt thread context 75540f1702c5SYu Xiangning * and so we do things as quickly as possible. 75550f1702c5SYu Xiangning */ 75560f1702c5SYu Xiangning static void 75570f1702c5SYu Xiangning udp_rcv_enqueue(queue_t *q, udp_t *udp, mblk_t *mp, uint_t pkt_len) 75580f1702c5SYu Xiangning { 75590f1702c5SYu Xiangning ASSERT(q == RD(q)); 75600f1702c5SYu Xiangning ASSERT(pkt_len == msgdsize(mp)); 75610f1702c5SYu Xiangning ASSERT(mp->b_next == NULL && mp->b_cont != NULL); 75620f1702c5SYu Xiangning ASSERT(DB_TYPE(mp) == M_PROTO && DB_TYPE(mp->b_cont) == M_DATA); 75630f1702c5SYu Xiangning ASSERT(MBLKL(mp) >= sizeof (struct T_unitdata_ind)); 75640f1702c5SYu Xiangning 75650f1702c5SYu Xiangning mutex_enter(&udp->udp_drain_lock); 75660f1702c5SYu Xiangning /* 75670f1702c5SYu Xiangning * Wake up and signal the receiving app; it is okay to do this 75680f1702c5SYu Xiangning * before enqueueing the mp because we are holding the drain lock. 75690f1702c5SYu Xiangning * One of the advantages of synchronous stream is the ability for 75700f1702c5SYu Xiangning * us to find out when the application performs a read on the 75710f1702c5SYu Xiangning * socket by way of udp_rrw() entry point being called. We need 75720f1702c5SYu Xiangning * to generate SIGPOLL/SIGIO for each received data in the case 75730f1702c5SYu Xiangning * of asynchronous socket just as in the strrput() case. However, 75740f1702c5SYu Xiangning * we only wake the application up when necessary, i.e. during the 75750f1702c5SYu Xiangning * first enqueue. When udp_rrw() is called, we send up a single 75760f1702c5SYu Xiangning * datagram upstream and call STR_WAKEUP_SET() again when there 75770f1702c5SYu Xiangning * are still data remaining in our receive queue. 75780f1702c5SYu Xiangning */ 75790f1702c5SYu Xiangning STR_WAKEUP_SENDSIG(STREAM(q), udp->udp_rcv_list_head); 75800f1702c5SYu Xiangning if (udp->udp_rcv_list_head == NULL) 75810f1702c5SYu Xiangning udp->udp_rcv_list_head = mp; 75820f1702c5SYu Xiangning else 75830f1702c5SYu Xiangning udp->udp_rcv_list_tail->b_next = mp; 75840f1702c5SYu Xiangning udp->udp_rcv_list_tail = mp; 75850f1702c5SYu Xiangning udp->udp_rcv_cnt += pkt_len; 75860f1702c5SYu Xiangning udp->udp_rcv_msgcnt++; 7587fc80c0dfSnordmark 75880f1702c5SYu Xiangning /* Need to flow-control? */ 75890f1702c5SYu Xiangning if (udp->udp_rcv_cnt >= udp->udp_rcv_hiwat || 75900f1702c5SYu Xiangning udp->udp_rcv_msgcnt >= udp->udp_rcv_hiwat) 75910f1702c5SYu Xiangning udp->udp_drain_qfull = B_TRUE; 75920f1702c5SYu Xiangning 75930f1702c5SYu Xiangning mutex_exit(&udp->udp_drain_lock); 7594fc80c0dfSnordmark } 7595fc80c0dfSnordmark 75967c478bd9Sstevel@tonic-gate /* 75970f1702c5SYu Xiangning * Drain the contents of receive list to the module upstream; we do 75980f1702c5SYu Xiangning * this during close or when we fallback to the slow mode due to 75990f1702c5SYu Xiangning * sockmod being popped or a module being pushed on top of us. 76007c478bd9Sstevel@tonic-gate */ 76010f1702c5SYu Xiangning static void 76020f1702c5SYu Xiangning udp_rcv_drain(queue_t *q, udp_t *udp, boolean_t closing) 76037c478bd9Sstevel@tonic-gate { 76040f1702c5SYu Xiangning mblk_t *mp; 7605f4b3ec61Sdh udp_stack_t *us = udp->udp_us; 7606ff550d0eSmasputra 76070f1702c5SYu Xiangning mutex_enter(&udp->udp_drain_lock); 7608ff550d0eSmasputra /* 76090f1702c5SYu Xiangning * There is no race with a concurrent udp_input() sending 76100f1702c5SYu Xiangning * up packets using putnext() after we have cleared the 76110f1702c5SYu Xiangning * udp_direct_sockfs flag but before we have completed 76120f1702c5SYu Xiangning * sending up the packets in udp_rcv_list, since we are 76130f1702c5SYu Xiangning * either a writer or we have quiesced the conn. 7614ff550d0eSmasputra */ 76150f1702c5SYu Xiangning udp->udp_direct_sockfs = B_FALSE; 76160f1702c5SYu Xiangning mutex_exit(&udp->udp_drain_lock); 7617ff550d0eSmasputra 76180f1702c5SYu Xiangning if (udp->udp_rcv_list_head != NULL) 76190f1702c5SYu Xiangning UDP_STAT(us, udp_drain); 7620ff550d0eSmasputra 76210f1702c5SYu Xiangning /* 76220f1702c5SYu Xiangning * Send up everything via putnext(); note here that we 76230f1702c5SYu Xiangning * don't need the udp_drain_lock to protect us since 76240f1702c5SYu Xiangning * nothing can enter udp_rrw() and that we currently 76250f1702c5SYu Xiangning * have exclusive access to this udp. 76260f1702c5SYu Xiangning */ 76270f1702c5SYu Xiangning while ((mp = udp->udp_rcv_list_head) != NULL) { 76280f1702c5SYu Xiangning udp->udp_rcv_list_head = mp->b_next; 76290f1702c5SYu Xiangning mp->b_next = NULL; 76300f1702c5SYu Xiangning udp->udp_rcv_cnt -= msgdsize(mp); 76310f1702c5SYu Xiangning udp->udp_rcv_msgcnt--; 76320f1702c5SYu Xiangning if (closing) { 76330f1702c5SYu Xiangning freemsg(mp); 76340f1702c5SYu Xiangning } else { 76350f1702c5SYu Xiangning ASSERT(q == RD(q)); 76360f1702c5SYu Xiangning putnext(q, mp); 7637ff550d0eSmasputra } 7638ff550d0eSmasputra } 76390f1702c5SYu Xiangning ASSERT(udp->udp_rcv_cnt == 0); 76400f1702c5SYu Xiangning ASSERT(udp->udp_rcv_msgcnt == 0); 76410f1702c5SYu Xiangning ASSERT(udp->udp_rcv_list_head == NULL); 76420f1702c5SYu Xiangning udp->udp_rcv_list_tail = NULL; 76430f1702c5SYu Xiangning udp->udp_drain_qfull = B_FALSE; 7644ff550d0eSmasputra } 7645ff550d0eSmasputra 76460f1702c5SYu Xiangning static size_t 76470f1702c5SYu Xiangning udp_set_rcv_hiwat(udp_t *udp, size_t size) 7648ff550d0eSmasputra { 76490f1702c5SYu Xiangning udp_stack_t *us = udp->udp_us; 7650ff550d0eSmasputra 76510f1702c5SYu Xiangning /* We add a bit of extra buffering */ 76520f1702c5SYu Xiangning size += size >> 1; 76530f1702c5SYu Xiangning if (size > us->us_max_buf) 76540f1702c5SYu Xiangning size = us->us_max_buf; 7655ff550d0eSmasputra 76560f1702c5SYu Xiangning udp->udp_rcv_hiwat = size; 76570f1702c5SYu Xiangning return (size); 76580f1702c5SYu Xiangning } 76597c478bd9Sstevel@tonic-gate 76600f1702c5SYu Xiangning /* 76610f1702c5SYu Xiangning * For the lower queue so that UDP can be a dummy mux. 76620f1702c5SYu Xiangning * Nobody should be sending 76630f1702c5SYu Xiangning * packets up this stream 76640f1702c5SYu Xiangning */ 76650f1702c5SYu Xiangning static void 76660f1702c5SYu Xiangning udp_lrput(queue_t *q, mblk_t *mp) 76670f1702c5SYu Xiangning { 76680f1702c5SYu Xiangning mblk_t *mp1; 76697c478bd9Sstevel@tonic-gate 76700f1702c5SYu Xiangning switch (mp->b_datap->db_type) { 76710f1702c5SYu Xiangning case M_FLUSH: 76720f1702c5SYu Xiangning /* Turn around */ 76730f1702c5SYu Xiangning if (*mp->b_rptr & FLUSHW) { 76740f1702c5SYu Xiangning *mp->b_rptr &= ~FLUSHR; 76750f1702c5SYu Xiangning qreply(q, mp); 76760f1702c5SYu Xiangning return; 76777c478bd9Sstevel@tonic-gate } 76780f1702c5SYu Xiangning break; 76797c478bd9Sstevel@tonic-gate } 76800f1702c5SYu Xiangning /* Could receive messages that passed through ar_rput */ 76810f1702c5SYu Xiangning for (mp1 = mp; mp1; mp1 = mp1->b_cont) 76820f1702c5SYu Xiangning mp1->b_prev = mp1->b_next = NULL; 76830f1702c5SYu Xiangning freemsg(mp); 76840f1702c5SYu Xiangning } 768545916cd2Sjpk 76860f1702c5SYu Xiangning /* 76870f1702c5SYu Xiangning * For the lower queue so that UDP can be a dummy mux. 76880f1702c5SYu Xiangning * Nobody should be sending packets down this stream. 76890f1702c5SYu Xiangning */ 76900f1702c5SYu Xiangning /* ARGSUSED */ 76910f1702c5SYu Xiangning void 76920f1702c5SYu Xiangning udp_lwput(queue_t *q, mblk_t *mp) 76930f1702c5SYu Xiangning { 76940f1702c5SYu Xiangning freemsg(mp); 76950f1702c5SYu Xiangning } 76967c478bd9Sstevel@tonic-gate 76970f1702c5SYu Xiangning /* 76980f1702c5SYu Xiangning * Below routines for UDP socket module. 76990f1702c5SYu Xiangning */ 77007c478bd9Sstevel@tonic-gate 77010f1702c5SYu Xiangning static conn_t * 77020f1702c5SYu Xiangning udp_do_open(cred_t *credp, boolean_t isv6, int flags) 77030f1702c5SYu Xiangning { 77040f1702c5SYu Xiangning udp_t *udp; 77050f1702c5SYu Xiangning conn_t *connp; 77060f1702c5SYu Xiangning zoneid_t zoneid; 77070f1702c5SYu Xiangning netstack_t *ns; 77080f1702c5SYu Xiangning udp_stack_t *us; 770945916cd2Sjpk 77100f1702c5SYu Xiangning ns = netstack_find_by_cred(credp); 77110f1702c5SYu Xiangning ASSERT(ns != NULL); 77120f1702c5SYu Xiangning us = ns->netstack_udp; 77130f1702c5SYu Xiangning ASSERT(us != NULL); 771445916cd2Sjpk 771545916cd2Sjpk /* 77160f1702c5SYu Xiangning * For exclusive stacks we set the zoneid to zero 77170f1702c5SYu Xiangning * to make UDP operate as if in the global zone. 771845916cd2Sjpk */ 77190f1702c5SYu Xiangning if (ns->netstack_stackid != GLOBAL_NETSTACKID) 77200f1702c5SYu Xiangning zoneid = GLOBAL_ZONEID; 77210f1702c5SYu Xiangning else 77220f1702c5SYu Xiangning zoneid = crgetzoneid(credp); 772345916cd2Sjpk 77240f1702c5SYu Xiangning ASSERT(flags == KM_SLEEP || flags == KM_NOSLEEP); 77250f1702c5SYu Xiangning 77260f1702c5SYu Xiangning connp = ipcl_conn_create(IPCL_UDPCONN, flags, ns); 77270f1702c5SYu Xiangning if (connp == NULL) { 77280f1702c5SYu Xiangning netstack_rele(ns); 77290f1702c5SYu Xiangning return (NULL); 77307c478bd9Sstevel@tonic-gate } 77310f1702c5SYu Xiangning udp = connp->conn_udp; 77327c478bd9Sstevel@tonic-gate 77337c478bd9Sstevel@tonic-gate /* 77340f1702c5SYu Xiangning * ipcl_conn_create did a netstack_hold. Undo the hold that was 77350f1702c5SYu Xiangning * done by netstack_find_by_cred() 77367c478bd9Sstevel@tonic-gate */ 77370f1702c5SYu Xiangning netstack_rele(ns); 77387c478bd9Sstevel@tonic-gate 77390f1702c5SYu Xiangning rw_enter(&udp->udp_rwlock, RW_WRITER); 77400f1702c5SYu Xiangning ASSERT(connp->conn_ulp == IPPROTO_UDP); 77410f1702c5SYu Xiangning ASSERT(connp->conn_udp == udp); 77420f1702c5SYu Xiangning ASSERT(udp->udp_connp == connp); 77437c478bd9Sstevel@tonic-gate 77440f1702c5SYu Xiangning /* Set the initial state of the stream and the privilege status. */ 77450f1702c5SYu Xiangning udp->udp_state = TS_UNBND; 77460f1702c5SYu Xiangning if (isv6) { 77470f1702c5SYu Xiangning udp->udp_family = AF_INET6; 77480f1702c5SYu Xiangning udp->udp_ipversion = IPV6_VERSION; 77490f1702c5SYu Xiangning udp->udp_max_hdr_len = IPV6_HDR_LEN + UDPH_SIZE; 77500f1702c5SYu Xiangning udp->udp_ttl = us->us_ipv6_hoplimit; 77510f1702c5SYu Xiangning connp->conn_af_isv6 = B_TRUE; 77520f1702c5SYu Xiangning connp->conn_flags |= IPCL_ISV6; 77530f1702c5SYu Xiangning } else { 77540f1702c5SYu Xiangning udp->udp_family = AF_INET; 77550f1702c5SYu Xiangning udp->udp_ipversion = IPV4_VERSION; 77560f1702c5SYu Xiangning udp->udp_max_hdr_len = IP_SIMPLE_HDR_LENGTH + UDPH_SIZE; 77570f1702c5SYu Xiangning udp->udp_ttl = us->us_ipv4_ttl; 77580f1702c5SYu Xiangning connp->conn_af_isv6 = B_FALSE; 77590f1702c5SYu Xiangning connp->conn_flags &= ~IPCL_ISV6; 77607c478bd9Sstevel@tonic-gate } 77617c478bd9Sstevel@tonic-gate 77620f1702c5SYu Xiangning udp->udp_multicast_ttl = IP_DEFAULT_MULTICAST_TTL; 77630f1702c5SYu Xiangning udp->udp_pending_op = -1; 77640f1702c5SYu Xiangning connp->conn_multicast_loop = IP_DEFAULT_MULTICAST_LOOP; 77650f1702c5SYu Xiangning connp->conn_zoneid = zoneid; 77667c478bd9Sstevel@tonic-gate 77670f1702c5SYu Xiangning udp->udp_open_time = lbolt64; 77680f1702c5SYu Xiangning udp->udp_open_pid = curproc->p_pid; 77697c478bd9Sstevel@tonic-gate 77700f1702c5SYu Xiangning /* 77710f1702c5SYu Xiangning * If the caller has the process-wide flag set, then default to MAC 77720f1702c5SYu Xiangning * exempt mode. This allows read-down to unlabeled hosts. 77730f1702c5SYu Xiangning */ 77740f1702c5SYu Xiangning if (getpflags(NET_MAC_AWARE, credp) != 0) 77750f1702c5SYu Xiangning connp->conn_mac_exempt = B_TRUE; 77767c478bd9Sstevel@tonic-gate 77770f1702c5SYu Xiangning connp->conn_ulp_labeled = is_system_labeled(); 77787c478bd9Sstevel@tonic-gate 77790f1702c5SYu Xiangning udp->udp_us = us; 7780b3d0fa4fSseb 77810f1702c5SYu Xiangning connp->conn_recv = udp_input; 77820f1702c5SYu Xiangning crhold(credp); 77830f1702c5SYu Xiangning connp->conn_cred = credp; 7784b3d0fa4fSseb 77850f1702c5SYu Xiangning *((sin6_t *)&udp->udp_delayed_addr) = sin6_null; 77867c478bd9Sstevel@tonic-gate 77870f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 77887c478bd9Sstevel@tonic-gate 77890f1702c5SYu Xiangning return (connp); 77900f1702c5SYu Xiangning } 77910f1702c5SYu Xiangning 77920f1702c5SYu Xiangning /* ARGSUSED */ 77930f1702c5SYu Xiangning sock_lower_handle_t 77940f1702c5SYu Xiangning udp_create(int family, int type, int proto, sock_downcalls_t **sock_downcalls, 77950f1702c5SYu Xiangning uint_t *smodep, int *errorp, int flags, cred_t *credp) 77960f1702c5SYu Xiangning { 77970f1702c5SYu Xiangning udp_t *udp = NULL; 77980f1702c5SYu Xiangning udp_stack_t *us; 77990f1702c5SYu Xiangning conn_t *connp; 78000f1702c5SYu Xiangning boolean_t isv6; 78010f1702c5SYu Xiangning 78020f1702c5SYu Xiangning if (type != SOCK_DGRAM || (family != AF_INET && family != AF_INET6) || 78030f1702c5SYu Xiangning (proto != 0 && proto != IPPROTO_UDP)) { 78040f1702c5SYu Xiangning *errorp = EPROTONOSUPPORT; 78050f1702c5SYu Xiangning return (NULL); 78064c10bc16Spwernau } 78074c10bc16Spwernau 78080f1702c5SYu Xiangning if (family == AF_INET6) 78090f1702c5SYu Xiangning isv6 = B_TRUE; 78100f1702c5SYu Xiangning else 78110f1702c5SYu Xiangning isv6 = B_FALSE; 78127c478bd9Sstevel@tonic-gate 78130f1702c5SYu Xiangning connp = udp_do_open(credp, isv6, flags); 78140f1702c5SYu Xiangning if (connp == NULL) { 78150f1702c5SYu Xiangning *errorp = ENOMEM; 78160f1702c5SYu Xiangning return (NULL); 78170f1702c5SYu Xiangning } 78187c478bd9Sstevel@tonic-gate 78190f1702c5SYu Xiangning udp = connp->conn_udp; 78200f1702c5SYu Xiangning ASSERT(udp != NULL); 78210f1702c5SYu Xiangning us = udp->udp_us; 78220f1702c5SYu Xiangning ASSERT(us != NULL); 7823fc80c0dfSnordmark 78240f1702c5SYu Xiangning connp->conn_flags |= IPCL_NONSTR | IPCL_SOCKET; 7825fc80c0dfSnordmark 78260f1702c5SYu Xiangning /* Set flow control */ 78270f1702c5SYu Xiangning rw_enter(&udp->udp_rwlock, RW_WRITER); 78280f1702c5SYu Xiangning (void) udp_set_rcv_hiwat(udp, us->us_recv_hiwat); 78290f1702c5SYu Xiangning udp->udp_rcv_disply_hiwat = us->us_recv_hiwat; 78300f1702c5SYu Xiangning udp->udp_rcv_lowat = udp_mod_info.mi_lowat; 78310f1702c5SYu Xiangning udp->udp_xmit_hiwat = us->us_xmit_hiwat; 78320f1702c5SYu Xiangning udp->udp_xmit_lowat = us->us_xmit_lowat; 78330f1702c5SYu Xiangning 78340f1702c5SYu Xiangning if (udp->udp_family == AF_INET6) { 78350f1702c5SYu Xiangning /* Build initial header template for transmit */ 78360f1702c5SYu Xiangning if ((*errorp = udp_build_hdrs(udp)) != 0) { 7837fc80c0dfSnordmark rw_exit(&udp->udp_rwlock); 78380f1702c5SYu Xiangning ipcl_conn_destroy(connp); 78390f1702c5SYu Xiangning return (NULL); 78407c478bd9Sstevel@tonic-gate } 78410f1702c5SYu Xiangning } 78420f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 7843ff550d0eSmasputra 78440f1702c5SYu Xiangning connp->conn_flow_cntrld = B_FALSE; 78450f1702c5SYu Xiangning 78460f1702c5SYu Xiangning ASSERT(us->us_ldi_ident != NULL); 78470f1702c5SYu Xiangning 78480f1702c5SYu Xiangning if ((*errorp = ip_create_helper_stream(connp, us->us_ldi_ident)) != 0) { 784919a8a986SRao Shoaib ip1dbg(("udp_create: create of IP helper stream failed\n")); 78500f1702c5SYu Xiangning udp_do_close(connp); 78510f1702c5SYu Xiangning return (NULL); 78527c478bd9Sstevel@tonic-gate } 78537c478bd9Sstevel@tonic-gate 78540f1702c5SYu Xiangning /* Set the send flow control */ 78550f1702c5SYu Xiangning connp->conn_wq->q_hiwat = us->us_xmit_hiwat; 78560f1702c5SYu Xiangning connp->conn_wq->q_lowat = us->us_xmit_lowat; 78577c478bd9Sstevel@tonic-gate 78580f1702c5SYu Xiangning mutex_enter(&connp->conn_lock); 78590f1702c5SYu Xiangning connp->conn_state_flags &= ~CONN_INCIPIENT; 78600f1702c5SYu Xiangning mutex_exit(&connp->conn_lock); 78617c478bd9Sstevel@tonic-gate 78620f1702c5SYu Xiangning *errorp = 0; 78630f1702c5SYu Xiangning *smodep = SM_ATOMIC; 78640f1702c5SYu Xiangning *sock_downcalls = &sock_udp_downcalls; 78650f1702c5SYu Xiangning return ((sock_lower_handle_t)connp); 78660f1702c5SYu Xiangning } 78677c478bd9Sstevel@tonic-gate 78680f1702c5SYu Xiangning /* ARGSUSED */ 78690f1702c5SYu Xiangning void 78700f1702c5SYu Xiangning udp_activate(sock_lower_handle_t proto_handle, sock_upper_handle_t sock_handle, 78710f1702c5SYu Xiangning sock_upcalls_t *sock_upcalls, int flags, cred_t *cr) 78720f1702c5SYu Xiangning { 78730f1702c5SYu Xiangning conn_t *connp = (conn_t *)proto_handle; 78740f1702c5SYu Xiangning udp_t *udp = connp->conn_udp; 78750f1702c5SYu Xiangning udp_stack_t *us = udp->udp_us; 78760f1702c5SYu Xiangning struct sock_proto_props sopp; 78770f1702c5SYu Xiangning 78780f1702c5SYu Xiangning connp->conn_upcalls = sock_upcalls; 78790f1702c5SYu Xiangning connp->conn_upper_handle = sock_handle; 78800f1702c5SYu Xiangning 78810f1702c5SYu Xiangning sopp.sopp_flags = SOCKOPT_WROFF | SOCKOPT_RCVHIWAT | 78820f1702c5SYu Xiangning SOCKOPT_MAXBLK | SOCKOPT_MAXPSZ | SOCKOPT_MINPSZ; 78830f1702c5SYu Xiangning sopp.sopp_wroff = udp->udp_max_hdr_len + us->us_wroff_extra; 78840f1702c5SYu Xiangning sopp.sopp_maxblk = INFPSZ; 78850f1702c5SYu Xiangning sopp.sopp_rxhiwat = udp->udp_rcv_hiwat; 78860f1702c5SYu Xiangning sopp.sopp_maxaddrlen = sizeof (sin6_t); 78870f1702c5SYu Xiangning sopp.sopp_maxpsz = 78880f1702c5SYu Xiangning (udp->udp_family == AF_INET) ? UDP_MAXPACKET_IPV4 : 78890f1702c5SYu Xiangning UDP_MAXPACKET_IPV6; 78900f1702c5SYu Xiangning sopp.sopp_minpsz = (udp_mod_info.mi_minpsz == 1) ? 0 : 78910f1702c5SYu Xiangning udp_mod_info.mi_minpsz; 78920f1702c5SYu Xiangning 78930f1702c5SYu Xiangning (*connp->conn_upcalls->su_set_proto_props)(connp->conn_upper_handle, 78940f1702c5SYu Xiangning &sopp); 78950f1702c5SYu Xiangning } 78967c478bd9Sstevel@tonic-gate 78970f1702c5SYu Xiangning static void 78980f1702c5SYu Xiangning udp_do_close(conn_t *connp) 78990f1702c5SYu Xiangning { 79000f1702c5SYu Xiangning udp_t *udp; 79017c478bd9Sstevel@tonic-gate 79020f1702c5SYu Xiangning ASSERT(connp != NULL && IPCL_IS_UDP(connp)); 79030f1702c5SYu Xiangning udp = connp->conn_udp; 79040f1702c5SYu Xiangning 79050f1702c5SYu Xiangning udp_quiesce_conn(connp); 79060f1702c5SYu Xiangning ip_quiesce_conn(connp); 79077c478bd9Sstevel@tonic-gate 79080f1702c5SYu Xiangning if (!IPCL_IS_NONSTR(connp)) { 79097c478bd9Sstevel@tonic-gate /* 79100f1702c5SYu Xiangning * Disable read-side synchronous stream 79110f1702c5SYu Xiangning * interface and drain any queued data. 79127c478bd9Sstevel@tonic-gate */ 79130f1702c5SYu Xiangning ASSERT(connp->conn_wq != NULL); 79140f1702c5SYu Xiangning udp_rcv_drain(connp->conn_wq, udp, B_TRUE); 79150f1702c5SYu Xiangning ASSERT(!udp->udp_direct_sockfs); 79160f1702c5SYu Xiangning 79170f1702c5SYu Xiangning ASSERT(connp->conn_rq != NULL); 79180f1702c5SYu Xiangning qprocsoff(connp->conn_rq); 79197c478bd9Sstevel@tonic-gate } 79207c478bd9Sstevel@tonic-gate 79210f1702c5SYu Xiangning ASSERT(udp->udp_rcv_cnt == 0); 79220f1702c5SYu Xiangning ASSERT(udp->udp_rcv_msgcnt == 0); 79230f1702c5SYu Xiangning ASSERT(udp->udp_rcv_list_head == NULL); 79240f1702c5SYu Xiangning ASSERT(udp->udp_rcv_list_tail == NULL); 79257c478bd9Sstevel@tonic-gate 79260f1702c5SYu Xiangning udp_close_free(connp); 79270f1702c5SYu Xiangning 79280f1702c5SYu Xiangning /* 79290f1702c5SYu Xiangning * Now we are truly single threaded on this stream, and can 79300f1702c5SYu Xiangning * delete the things hanging off the connp, and finally the connp. 79310f1702c5SYu Xiangning * We removed this connp from the fanout list, it cannot be 79320f1702c5SYu Xiangning * accessed thru the fanouts, and we already waited for the 79330f1702c5SYu Xiangning * conn_ref to drop to 0. We are already in close, so 79340f1702c5SYu Xiangning * there cannot be any other thread from the top. qprocsoff 79350f1702c5SYu Xiangning * has completed, and service has completed or won't run in 79360f1702c5SYu Xiangning * future. 79370f1702c5SYu Xiangning */ 79380f1702c5SYu Xiangning ASSERT(connp->conn_ref == 1); 79390f1702c5SYu Xiangning if (!IPCL_IS_NONSTR(connp)) { 79400f1702c5SYu Xiangning inet_minor_free(connp->conn_minor_arena, connp->conn_dev); 79417c478bd9Sstevel@tonic-gate } else { 7942bfcb55b8SRao Shoaib ip_free_helper_stream(connp); 79437c478bd9Sstevel@tonic-gate } 79447c478bd9Sstevel@tonic-gate 79450f1702c5SYu Xiangning connp->conn_ref--; 79460f1702c5SYu Xiangning ipcl_conn_destroy(connp); 79470f1702c5SYu Xiangning } 79480f1702c5SYu Xiangning 79490f1702c5SYu Xiangning /* ARGSUSED */ 79500f1702c5SYu Xiangning int 79510f1702c5SYu Xiangning udp_close(sock_lower_handle_t proto_handle, int flags, cred_t *cr) 79520f1702c5SYu Xiangning { 79530f1702c5SYu Xiangning conn_t *connp = (conn_t *)proto_handle; 79540f1702c5SYu Xiangning 79550f1702c5SYu Xiangning udp_do_close(connp); 79560f1702c5SYu Xiangning return (0); 79570f1702c5SYu Xiangning } 79580f1702c5SYu Xiangning 79590f1702c5SYu Xiangning static int 79600f1702c5SYu Xiangning udp_do_bind(conn_t *connp, struct sockaddr *sa, socklen_t len, cred_t *cr, 79610f1702c5SYu Xiangning boolean_t bind_to_req_port_only) 79620f1702c5SYu Xiangning { 79630f1702c5SYu Xiangning sin_t *sin; 79640f1702c5SYu Xiangning sin6_t *sin6; 79650f1702c5SYu Xiangning sin6_t sin6addr; 79660f1702c5SYu Xiangning in_port_t port; /* Host byte order */ 79670f1702c5SYu Xiangning in_port_t requested_port; /* Host byte order */ 79680f1702c5SYu Xiangning int count; 79690f1702c5SYu Xiangning in6_addr_t v6src; 79700f1702c5SYu Xiangning int loopmax; 79710f1702c5SYu Xiangning udp_fanout_t *udpf; 79720f1702c5SYu Xiangning in_port_t lport; /* Network byte order */ 79730f1702c5SYu Xiangning zoneid_t zoneid; 79740f1702c5SYu Xiangning udp_t *udp; 79750f1702c5SYu Xiangning boolean_t is_inaddr_any; 79760f1702c5SYu Xiangning mlp_type_t addrtype, mlptype; 79770f1702c5SYu Xiangning udp_stack_t *us; 79780f1702c5SYu Xiangning int error = 0; 79790f1702c5SYu Xiangning mblk_t *mp = NULL; 79800f1702c5SYu Xiangning 79810f1702c5SYu Xiangning udp = connp->conn_udp; 79820f1702c5SYu Xiangning us = udp->udp_us; 79830f1702c5SYu Xiangning 79840f1702c5SYu Xiangning if (udp->udp_state != TS_UNBND) { 79850f1702c5SYu Xiangning (void) strlog(UDP_MOD_ID, 0, 1, SL_ERROR|SL_TRACE, 79860f1702c5SYu Xiangning "udp_bind: bad state, %u", udp->udp_state); 79870f1702c5SYu Xiangning return (-TOUTSTATE); 79887c478bd9Sstevel@tonic-gate } 79897c478bd9Sstevel@tonic-gate 79900f1702c5SYu Xiangning switch (len) { 79910f1702c5SYu Xiangning case 0: 79920f1702c5SYu Xiangning if (udp->udp_family == AF_INET) { 79930f1702c5SYu Xiangning sin = (sin_t *)&sin6addr; 79940f1702c5SYu Xiangning *sin = sin_null; 79950f1702c5SYu Xiangning sin->sin_family = AF_INET; 79960f1702c5SYu Xiangning sin->sin_addr.s_addr = INADDR_ANY; 79970f1702c5SYu Xiangning udp->udp_ipversion = IPV4_VERSION; 79980f1702c5SYu Xiangning } else { 79990f1702c5SYu Xiangning ASSERT(udp->udp_family == AF_INET6); 80000f1702c5SYu Xiangning sin6 = (sin6_t *)&sin6addr; 80010f1702c5SYu Xiangning *sin6 = sin6_null; 80020f1702c5SYu Xiangning sin6->sin6_family = AF_INET6; 80030f1702c5SYu Xiangning V6_SET_ZERO(sin6->sin6_addr); 80040f1702c5SYu Xiangning udp->udp_ipversion = IPV6_VERSION; 80050f1702c5SYu Xiangning } 80060f1702c5SYu Xiangning port = 0; 80070f1702c5SYu Xiangning break; 80087c478bd9Sstevel@tonic-gate 80090f1702c5SYu Xiangning case sizeof (sin_t): /* Complete IPv4 address */ 80100f1702c5SYu Xiangning sin = (sin_t *)sa; 80110f1702c5SYu Xiangning 80120f1702c5SYu Xiangning if (sin == NULL || !OK_32PTR((char *)sin)) 80130f1702c5SYu Xiangning return (EINVAL); 80140f1702c5SYu Xiangning 80150f1702c5SYu Xiangning if (udp->udp_family != AF_INET || 80160f1702c5SYu Xiangning sin->sin_family != AF_INET) { 80170f1702c5SYu Xiangning return (EAFNOSUPPORT); 8018dc3879f9Sjarrett } 80190f1702c5SYu Xiangning port = ntohs(sin->sin_port); 80200f1702c5SYu Xiangning break; 80217c478bd9Sstevel@tonic-gate 80220f1702c5SYu Xiangning case sizeof (sin6_t): /* complete IPv6 address */ 80230f1702c5SYu Xiangning sin6 = (sin6_t *)sa; 80247c478bd9Sstevel@tonic-gate 80250f1702c5SYu Xiangning if (sin6 == NULL || !OK_32PTR((char *)sin6)) 80260f1702c5SYu Xiangning return (EINVAL); 8027dc3879f9Sjarrett 80280f1702c5SYu Xiangning if (udp->udp_family != AF_INET6 || 80290f1702c5SYu Xiangning sin6->sin6_family != AF_INET6) { 80300f1702c5SYu Xiangning return (EAFNOSUPPORT); 8031dc3879f9Sjarrett } 80320f1702c5SYu Xiangning port = ntohs(sin6->sin6_port); 80330f1702c5SYu Xiangning break; 80340f1702c5SYu Xiangning 80350f1702c5SYu Xiangning default: /* Invalid request */ 80360f1702c5SYu Xiangning (void) strlog(UDP_MOD_ID, 0, 1, SL_ERROR|SL_TRACE, 80370f1702c5SYu Xiangning "udp_bind: bad ADDR_length length %u", len); 80380f1702c5SYu Xiangning return (-TBADADDR); 80397c478bd9Sstevel@tonic-gate } 80407c478bd9Sstevel@tonic-gate 80410f1702c5SYu Xiangning requested_port = port; 80427c478bd9Sstevel@tonic-gate 80430f1702c5SYu Xiangning if (requested_port == 0 || !bind_to_req_port_only) 80440f1702c5SYu Xiangning bind_to_req_port_only = B_FALSE; 80450f1702c5SYu Xiangning else /* T_BIND_REQ and requested_port != 0 */ 80460f1702c5SYu Xiangning bind_to_req_port_only = B_TRUE; 80477c478bd9Sstevel@tonic-gate 80480f1702c5SYu Xiangning if (requested_port == 0) { 80490f1702c5SYu Xiangning /* 80500f1702c5SYu Xiangning * If the application passed in zero for the port number, it 80510f1702c5SYu Xiangning * doesn't care which port number we bind to. Get one in the 80520f1702c5SYu Xiangning * valid range. 80530f1702c5SYu Xiangning */ 80540f1702c5SYu Xiangning if (udp->udp_anon_priv_bind) { 80550f1702c5SYu Xiangning port = udp_get_next_priv_port(udp); 80560f1702c5SYu Xiangning } else { 80570f1702c5SYu Xiangning port = udp_update_next_port(udp, 80580f1702c5SYu Xiangning us->us_next_port_to_try, B_TRUE); 80590f1702c5SYu Xiangning } 80600f1702c5SYu Xiangning } else { 80610f1702c5SYu Xiangning /* 80620f1702c5SYu Xiangning * If the port is in the well-known privileged range, 80630f1702c5SYu Xiangning * make sure the caller was privileged. 80640f1702c5SYu Xiangning */ 80650f1702c5SYu Xiangning int i; 80660f1702c5SYu Xiangning boolean_t priv = B_FALSE; 80677c478bd9Sstevel@tonic-gate 80680f1702c5SYu Xiangning if (port < us->us_smallest_nonpriv_port) { 80690f1702c5SYu Xiangning priv = B_TRUE; 80700f1702c5SYu Xiangning } else { 80710f1702c5SYu Xiangning for (i = 0; i < us->us_num_epriv_ports; i++) { 80720f1702c5SYu Xiangning if (port == us->us_epriv_ports[i]) { 80730f1702c5SYu Xiangning priv = B_TRUE; 80740f1702c5SYu Xiangning break; 80750f1702c5SYu Xiangning } 80760f1702c5SYu Xiangning } 80770f1702c5SYu Xiangning } 80780f1702c5SYu Xiangning 80790f1702c5SYu Xiangning if (priv) { 80800f1702c5SYu Xiangning if (secpolicy_net_privaddr(cr, port, IPPROTO_UDP) != 0) 80810f1702c5SYu Xiangning return (-TACCES); 80820f1702c5SYu Xiangning } 80837c478bd9Sstevel@tonic-gate } 80847c478bd9Sstevel@tonic-gate 80850f1702c5SYu Xiangning if (port == 0) 80860f1702c5SYu Xiangning return (-TNOADDR); 80877c478bd9Sstevel@tonic-gate 80880f1702c5SYu Xiangning /* 80890f1702c5SYu Xiangning * The state must be TS_UNBND. TPI mandates that users must send 80900f1702c5SYu Xiangning * TPI primitives only 1 at a time and wait for the response before 80910f1702c5SYu Xiangning * sending the next primitive. 80920f1702c5SYu Xiangning */ 80930f1702c5SYu Xiangning rw_enter(&udp->udp_rwlock, RW_WRITER); 80940f1702c5SYu Xiangning if (udp->udp_state != TS_UNBND || udp->udp_pending_op != -1) { 80950f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 80960f1702c5SYu Xiangning (void) strlog(UDP_MOD_ID, 0, 1, SL_ERROR|SL_TRACE, 80970f1702c5SYu Xiangning "udp_bind: bad state, %u", udp->udp_state); 80980f1702c5SYu Xiangning return (-TOUTSTATE); 80997c478bd9Sstevel@tonic-gate } 81000f1702c5SYu Xiangning /* XXX how to remove the T_BIND_REQ? Should set it before calling */ 81010f1702c5SYu Xiangning udp->udp_pending_op = T_BIND_REQ; 81027c478bd9Sstevel@tonic-gate /* 81030f1702c5SYu Xiangning * Copy the source address into our udp structure. This address 81040f1702c5SYu Xiangning * may still be zero; if so, IP will fill in the correct address 81050f1702c5SYu Xiangning * each time an outbound packet is passed to it. Since the udp is 81060f1702c5SYu Xiangning * not yet in the bind hash list, we don't grab the uf_lock to 81070f1702c5SYu Xiangning * change udp_ipversion 81087c478bd9Sstevel@tonic-gate */ 81090f1702c5SYu Xiangning if (udp->udp_family == AF_INET) { 81100f1702c5SYu Xiangning ASSERT(sin != NULL); 81110f1702c5SYu Xiangning ASSERT(udp->udp_ipversion == IPV4_VERSION); 81120f1702c5SYu Xiangning udp->udp_max_hdr_len = IP_SIMPLE_HDR_LENGTH + UDPH_SIZE + 81130f1702c5SYu Xiangning udp->udp_ip_snd_options_len; 81140f1702c5SYu Xiangning IN6_IPADDR_TO_V4MAPPED(sin->sin_addr.s_addr, &v6src); 81150f1702c5SYu Xiangning } else { 81160f1702c5SYu Xiangning ASSERT(sin6 != NULL); 81170f1702c5SYu Xiangning v6src = sin6->sin6_addr; 81180f1702c5SYu Xiangning if (IN6_IS_ADDR_V4MAPPED(&v6src)) { 81190f1702c5SYu Xiangning /* 81200f1702c5SYu Xiangning * no need to hold the uf_lock to set the udp_ipversion 81210f1702c5SYu Xiangning * since we are not yet in the fanout list 81220f1702c5SYu Xiangning */ 81230f1702c5SYu Xiangning udp->udp_ipversion = IPV4_VERSION; 81240f1702c5SYu Xiangning udp->udp_max_hdr_len = IP_SIMPLE_HDR_LENGTH + 81250f1702c5SYu Xiangning UDPH_SIZE + udp->udp_ip_snd_options_len; 81260f1702c5SYu Xiangning } else { 81270f1702c5SYu Xiangning udp->udp_ipversion = IPV6_VERSION; 81280f1702c5SYu Xiangning udp->udp_max_hdr_len = udp->udp_sticky_hdrs_len; 81290f1702c5SYu Xiangning } 81300f1702c5SYu Xiangning } 81317c478bd9Sstevel@tonic-gate 81327c478bd9Sstevel@tonic-gate /* 81330f1702c5SYu Xiangning * If udp_reuseaddr is not set, then we have to make sure that 81340f1702c5SYu Xiangning * the IP address and port number the application requested 81350f1702c5SYu Xiangning * (or we selected for the application) is not being used by 81360f1702c5SYu Xiangning * another stream. If another stream is already using the 81370f1702c5SYu Xiangning * requested IP address and port, the behavior depends on 81380f1702c5SYu Xiangning * "bind_to_req_port_only". If set the bind fails; otherwise we 81390f1702c5SYu Xiangning * search for any an unused port to bind to the the stream. 81400f1702c5SYu Xiangning * 81410f1702c5SYu Xiangning * As per the BSD semantics, as modified by the Deering multicast 81420f1702c5SYu Xiangning * changes, if udp_reuseaddr is set, then we allow multiple binds 81430f1702c5SYu Xiangning * to the same port independent of the local IP address. 81440f1702c5SYu Xiangning * 81450f1702c5SYu Xiangning * This is slightly different than in SunOS 4.X which did not 81460f1702c5SYu Xiangning * support IP multicast. Note that the change implemented by the 81470f1702c5SYu Xiangning * Deering multicast code effects all binds - not only binding 81480f1702c5SYu Xiangning * to IP multicast addresses. 81490f1702c5SYu Xiangning * 81500f1702c5SYu Xiangning * Note that when binding to port zero we ignore SO_REUSEADDR in 81510f1702c5SYu Xiangning * order to guarantee a unique port. 81527c478bd9Sstevel@tonic-gate */ 81537c478bd9Sstevel@tonic-gate 81540f1702c5SYu Xiangning count = 0; 81550f1702c5SYu Xiangning if (udp->udp_anon_priv_bind) { 81560f1702c5SYu Xiangning /* 81570f1702c5SYu Xiangning * loopmax = (IPPORT_RESERVED-1) - 81580f1702c5SYu Xiangning * us->us_min_anonpriv_port + 1 81590f1702c5SYu Xiangning */ 81600f1702c5SYu Xiangning loopmax = IPPORT_RESERVED - us->us_min_anonpriv_port; 81610f1702c5SYu Xiangning } else { 81620f1702c5SYu Xiangning loopmax = us->us_largest_anon_port - 81630f1702c5SYu Xiangning us->us_smallest_anon_port + 1; 81647c478bd9Sstevel@tonic-gate } 81657c478bd9Sstevel@tonic-gate 81660f1702c5SYu Xiangning is_inaddr_any = V6_OR_V4_INADDR_ANY(v6src); 81670f1702c5SYu Xiangning zoneid = connp->conn_zoneid; 81680f1702c5SYu Xiangning 81690f1702c5SYu Xiangning for (;;) { 81700f1702c5SYu Xiangning udp_t *udp1; 81710f1702c5SYu Xiangning boolean_t found_exclbind = B_FALSE; 81727c478bd9Sstevel@tonic-gate 81737c478bd9Sstevel@tonic-gate /* 81740f1702c5SYu Xiangning * Walk through the list of udp streams bound to 81750f1702c5SYu Xiangning * requested port with the same IP address. 81767c478bd9Sstevel@tonic-gate */ 81770f1702c5SYu Xiangning lport = htons(port); 81780f1702c5SYu Xiangning udpf = &us->us_bind_fanout[UDP_BIND_HASH(lport, 81790f1702c5SYu Xiangning us->us_bind_fanout_size)]; 81800f1702c5SYu Xiangning mutex_enter(&udpf->uf_lock); 81810f1702c5SYu Xiangning for (udp1 = udpf->uf_udp; udp1 != NULL; 81820f1702c5SYu Xiangning udp1 = udp1->udp_bind_hash) { 81830f1702c5SYu Xiangning if (lport != udp1->udp_port) 81840f1702c5SYu Xiangning continue; 81850f1702c5SYu Xiangning 81860f1702c5SYu Xiangning /* 81870f1702c5SYu Xiangning * On a labeled system, we must treat bindings to ports 81880f1702c5SYu Xiangning * on shared IP addresses by sockets with MAC exemption 81890f1702c5SYu Xiangning * privilege as being in all zones, as there's 81900f1702c5SYu Xiangning * otherwise no way to identify the right receiver. 81910f1702c5SYu Xiangning */ 81920f1702c5SYu Xiangning if (!(IPCL_ZONE_MATCH(udp1->udp_connp, zoneid) || 81930f1702c5SYu Xiangning IPCL_ZONE_MATCH(connp, 81940f1702c5SYu Xiangning udp1->udp_connp->conn_zoneid)) && 81950f1702c5SYu Xiangning !connp->conn_mac_exempt && \ 81960f1702c5SYu Xiangning !udp1->udp_connp->conn_mac_exempt) 81970f1702c5SYu Xiangning continue; 81980f1702c5SYu Xiangning 81990f1702c5SYu Xiangning /* 82000f1702c5SYu Xiangning * If UDP_EXCLBIND is set for either the bound or 82010f1702c5SYu Xiangning * binding endpoint, the semantics of bind 82020f1702c5SYu Xiangning * is changed according to the following chart. 82030f1702c5SYu Xiangning * 82040f1702c5SYu Xiangning * spec = specified address (v4 or v6) 82050f1702c5SYu Xiangning * unspec = unspecified address (v4 or v6) 82060f1702c5SYu Xiangning * A = specified addresses are different for endpoints 82070f1702c5SYu Xiangning * 82080f1702c5SYu Xiangning * bound bind to allowed? 82090f1702c5SYu Xiangning * ------------------------------------- 82100f1702c5SYu Xiangning * unspec unspec no 82110f1702c5SYu Xiangning * unspec spec no 82120f1702c5SYu Xiangning * spec unspec no 82130f1702c5SYu Xiangning * spec spec yes if A 82140f1702c5SYu Xiangning * 82150f1702c5SYu Xiangning * For labeled systems, SO_MAC_EXEMPT behaves the same 82160f1702c5SYu Xiangning * as UDP_EXCLBIND, except that zoneid is ignored. 82170f1702c5SYu Xiangning */ 82180f1702c5SYu Xiangning if (udp1->udp_exclbind || udp->udp_exclbind || 82190f1702c5SYu Xiangning udp1->udp_connp->conn_mac_exempt || 82200f1702c5SYu Xiangning connp->conn_mac_exempt) { 82210f1702c5SYu Xiangning if (V6_OR_V4_INADDR_ANY( 82220f1702c5SYu Xiangning udp1->udp_bound_v6src) || 82230f1702c5SYu Xiangning is_inaddr_any || 82240f1702c5SYu Xiangning IN6_ARE_ADDR_EQUAL(&udp1->udp_bound_v6src, 82250f1702c5SYu Xiangning &v6src)) { 82260f1702c5SYu Xiangning found_exclbind = B_TRUE; 82270f1702c5SYu Xiangning break; 82280f1702c5SYu Xiangning } 82290f1702c5SYu Xiangning continue; 82300f1702c5SYu Xiangning } 82310f1702c5SYu Xiangning 82320f1702c5SYu Xiangning /* 82330f1702c5SYu Xiangning * Check ipversion to allow IPv4 and IPv6 sockets to 82340f1702c5SYu Xiangning * have disjoint port number spaces. 82350f1702c5SYu Xiangning */ 82360f1702c5SYu Xiangning if (udp->udp_ipversion != udp1->udp_ipversion) { 82370f1702c5SYu Xiangning 82387c478bd9Sstevel@tonic-gate /* 82390f1702c5SYu Xiangning * On the first time through the loop, if the 82400f1702c5SYu Xiangning * the user intentionally specified a 82410f1702c5SYu Xiangning * particular port number, then ignore any 82420f1702c5SYu Xiangning * bindings of the other protocol that may 82430f1702c5SYu Xiangning * conflict. This allows the user to bind IPv6 82440f1702c5SYu Xiangning * alone and get both v4 and v6, or bind both 82450f1702c5SYu Xiangning * both and get each seperately. On subsequent 82460f1702c5SYu Xiangning * times through the loop, we're checking a 82470f1702c5SYu Xiangning * port that we chose (not the user) and thus 82480f1702c5SYu Xiangning * we do not allow casual duplicate bindings. 82497c478bd9Sstevel@tonic-gate */ 82500f1702c5SYu Xiangning if (count == 0 && requested_port != 0) 82510f1702c5SYu Xiangning continue; 82527c478bd9Sstevel@tonic-gate } 82537c478bd9Sstevel@tonic-gate 82547c478bd9Sstevel@tonic-gate /* 82550f1702c5SYu Xiangning * No difference depending on SO_REUSEADDR. 82560f1702c5SYu Xiangning * 82570f1702c5SYu Xiangning * If existing port is bound to a 82580f1702c5SYu Xiangning * non-wildcard IP address and 82590f1702c5SYu Xiangning * the requesting stream is bound to 82600f1702c5SYu Xiangning * a distinct different IP addresses 82610f1702c5SYu Xiangning * (non-wildcard, also), keep going. 82627c478bd9Sstevel@tonic-gate */ 82630f1702c5SYu Xiangning if (!is_inaddr_any && 82640f1702c5SYu Xiangning !V6_OR_V4_INADDR_ANY(udp1->udp_bound_v6src) && 82650f1702c5SYu Xiangning !IN6_ARE_ADDR_EQUAL(&udp1->udp_bound_v6src, 82660f1702c5SYu Xiangning &v6src)) { 82670f1702c5SYu Xiangning continue; 82687c478bd9Sstevel@tonic-gate } 82690f1702c5SYu Xiangning break; 82700f1702c5SYu Xiangning } 82710f1702c5SYu Xiangning 82720f1702c5SYu Xiangning if (!found_exclbind && 82730f1702c5SYu Xiangning (udp->udp_reuseaddr && requested_port != 0)) { 82740f1702c5SYu Xiangning break; 82750f1702c5SYu Xiangning } 82760f1702c5SYu Xiangning 82770f1702c5SYu Xiangning if (udp1 == NULL) { 82787c478bd9Sstevel@tonic-gate /* 82790f1702c5SYu Xiangning * No other stream has this IP address 82800f1702c5SYu Xiangning * and port number. We can use it. 82817c478bd9Sstevel@tonic-gate */ 82820f1702c5SYu Xiangning break; 82830f1702c5SYu Xiangning } 82840f1702c5SYu Xiangning mutex_exit(&udpf->uf_lock); 82850f1702c5SYu Xiangning if (bind_to_req_port_only) { 82867c478bd9Sstevel@tonic-gate /* 82870f1702c5SYu Xiangning * We get here only when requested port 82880f1702c5SYu Xiangning * is bound (and only first of the for() 82890f1702c5SYu Xiangning * loop iteration). 82900f1702c5SYu Xiangning * 82910f1702c5SYu Xiangning * The semantics of this bind request 82920f1702c5SYu Xiangning * require it to fail so we return from 82930f1702c5SYu Xiangning * the routine (and exit the loop). 82940f1702c5SYu Xiangning * 82957c478bd9Sstevel@tonic-gate */ 82960f1702c5SYu Xiangning udp->udp_pending_op = -1; 82970f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 82980f1702c5SYu Xiangning return (-TADDRBUSY); 82990f1702c5SYu Xiangning } 83007c478bd9Sstevel@tonic-gate 83010f1702c5SYu Xiangning if (udp->udp_anon_priv_bind) { 83020f1702c5SYu Xiangning port = udp_get_next_priv_port(udp); 83030f1702c5SYu Xiangning } else { 83040f1702c5SYu Xiangning if ((count == 0) && (requested_port != 0)) { 83050f1702c5SYu Xiangning /* 83060f1702c5SYu Xiangning * If the application wants us to find 83070f1702c5SYu Xiangning * a port, get one to start with. Set 83080f1702c5SYu Xiangning * requested_port to 0, so that we will 83090f1702c5SYu Xiangning * update us->us_next_port_to_try below. 83100f1702c5SYu Xiangning */ 83110f1702c5SYu Xiangning port = udp_update_next_port(udp, 83120f1702c5SYu Xiangning us->us_next_port_to_try, B_TRUE); 83130f1702c5SYu Xiangning requested_port = 0; 83140f1702c5SYu Xiangning } else { 83150f1702c5SYu Xiangning port = udp_update_next_port(udp, port + 1, 83160f1702c5SYu Xiangning B_FALSE); 83170f1702c5SYu Xiangning } 83187c478bd9Sstevel@tonic-gate } 83197c478bd9Sstevel@tonic-gate 83200f1702c5SYu Xiangning if (port == 0 || ++count >= loopmax) { 83210f1702c5SYu Xiangning /* 83220f1702c5SYu Xiangning * We've tried every possible port number and 83230f1702c5SYu Xiangning * there are none available, so send an error 83240f1702c5SYu Xiangning * to the user. 83250f1702c5SYu Xiangning */ 83260f1702c5SYu Xiangning udp->udp_pending_op = -1; 83270f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 83280f1702c5SYu Xiangning return (-TNOADDR); 83290f1702c5SYu Xiangning } 83307c478bd9Sstevel@tonic-gate } 83317c478bd9Sstevel@tonic-gate 83327c478bd9Sstevel@tonic-gate /* 83330f1702c5SYu Xiangning * Copy the source address into our udp structure. This address 83340f1702c5SYu Xiangning * may still be zero; if so, ip will fill in the correct address 83350f1702c5SYu Xiangning * each time an outbound packet is passed to it. 83360f1702c5SYu Xiangning * If we are binding to a broadcast or multicast address then 83370f1702c5SYu Xiangning * udp_post_ip_bind_connect will clear the source address 83380f1702c5SYu Xiangning * when udp_do_bind success. 83397c478bd9Sstevel@tonic-gate */ 83400f1702c5SYu Xiangning udp->udp_v6src = udp->udp_bound_v6src = v6src; 83410f1702c5SYu Xiangning udp->udp_port = lport; 83427c478bd9Sstevel@tonic-gate /* 83430f1702c5SYu Xiangning * Now reset the the next anonymous port if the application requested 83440f1702c5SYu Xiangning * an anonymous port, or we handed out the next anonymous port. 83457c478bd9Sstevel@tonic-gate */ 83460f1702c5SYu Xiangning if ((requested_port == 0) && (!udp->udp_anon_priv_bind)) { 83470f1702c5SYu Xiangning us->us_next_port_to_try = port + 1; 83480f1702c5SYu Xiangning } 83497c478bd9Sstevel@tonic-gate 83500f1702c5SYu Xiangning /* Initialize the O_T_BIND_REQ/T_BIND_REQ for ip. */ 83510f1702c5SYu Xiangning if (udp->udp_family == AF_INET) { 83520f1702c5SYu Xiangning sin->sin_port = udp->udp_port; 83530f1702c5SYu Xiangning } else { 83540f1702c5SYu Xiangning sin6->sin6_port = udp->udp_port; 83550f1702c5SYu Xiangning /* Rebuild the header template */ 83560f1702c5SYu Xiangning error = udp_build_hdrs(udp); 83570f1702c5SYu Xiangning if (error != 0) { 83580f1702c5SYu Xiangning udp->udp_pending_op = -1; 83590f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 83600f1702c5SYu Xiangning mutex_exit(&udpf->uf_lock); 83610f1702c5SYu Xiangning return (error); 83620f1702c5SYu Xiangning } 83630f1702c5SYu Xiangning } 83640f1702c5SYu Xiangning udp->udp_state = TS_IDLE; 83650f1702c5SYu Xiangning udp_bind_hash_insert(udpf, udp); 83660f1702c5SYu Xiangning mutex_exit(&udpf->uf_lock); 83670f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 83680f1702c5SYu Xiangning 83690f1702c5SYu Xiangning if (cl_inet_bind) { 83700f1702c5SYu Xiangning /* 83710f1702c5SYu Xiangning * Running in cluster mode - register bind information 83720f1702c5SYu Xiangning */ 83730f1702c5SYu Xiangning if (udp->udp_ipversion == IPV4_VERSION) { 83748e4b770fSLu Huafeng (*cl_inet_bind)(connp->conn_netstack->netstack_stackid, 83758e4b770fSLu Huafeng IPPROTO_UDP, AF_INET, 83760f1702c5SYu Xiangning (uint8_t *)(&V4_PART_OF_V6(udp->udp_v6src)), 83778e4b770fSLu Huafeng (in_port_t)udp->udp_port, NULL); 83780f1702c5SYu Xiangning } else { 83798e4b770fSLu Huafeng (*cl_inet_bind)(connp->conn_netstack->netstack_stackid, 83808e4b770fSLu Huafeng IPPROTO_UDP, AF_INET6, 83810f1702c5SYu Xiangning (uint8_t *)&(udp->udp_v6src), 83828e4b770fSLu Huafeng (in_port_t)udp->udp_port, NULL); 83830f1702c5SYu Xiangning } 8384ff550d0eSmasputra } 8385ff550d0eSmasputra 83860f1702c5SYu Xiangning connp->conn_anon_port = (is_system_labeled() && requested_port == 0); 83870f1702c5SYu Xiangning if (is_system_labeled() && (!connp->conn_anon_port || 83880f1702c5SYu Xiangning connp->conn_anon_mlp)) { 83890f1702c5SYu Xiangning uint16_t mlpport; 83900f1702c5SYu Xiangning cred_t *cr = connp->conn_cred; 83910f1702c5SYu Xiangning zone_t *zone; 83927c478bd9Sstevel@tonic-gate 83930f1702c5SYu Xiangning zone = crgetzone(cr); 83940f1702c5SYu Xiangning connp->conn_mlp_type = udp->udp_recvucred ? mlptBoth : 83950f1702c5SYu Xiangning mlptSingle; 83960f1702c5SYu Xiangning addrtype = tsol_mlp_addr_type(zone->zone_id, IPV6_VERSION, 83970f1702c5SYu Xiangning &v6src, us->us_netstack->netstack_ip); 83980f1702c5SYu Xiangning if (addrtype == mlptSingle) { 83990f1702c5SYu Xiangning rw_enter(&udp->udp_rwlock, RW_WRITER); 84000f1702c5SYu Xiangning udp->udp_pending_op = -1; 84010f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 84020f1702c5SYu Xiangning connp->conn_anon_port = B_FALSE; 84030f1702c5SYu Xiangning connp->conn_mlp_type = mlptSingle; 84040f1702c5SYu Xiangning return (-TNOADDR); 84050f1702c5SYu Xiangning } 84060f1702c5SYu Xiangning mlpport = connp->conn_anon_port ? PMAPPORT : port; 84070f1702c5SYu Xiangning mlptype = tsol_mlp_port_type(zone, IPPROTO_UDP, mlpport, 84080f1702c5SYu Xiangning addrtype); 84090f1702c5SYu Xiangning if (mlptype != mlptSingle && 84100f1702c5SYu Xiangning (connp->conn_mlp_type == mlptSingle || 84110f1702c5SYu Xiangning secpolicy_net_bindmlp(cr) != 0)) { 84120f1702c5SYu Xiangning if (udp->udp_debug) { 84130f1702c5SYu Xiangning (void) strlog(UDP_MOD_ID, 0, 1, 84140f1702c5SYu Xiangning SL_ERROR|SL_TRACE, 84150f1702c5SYu Xiangning "udp_bind: no priv for multilevel port %d", 84160f1702c5SYu Xiangning mlpport); 84170f1702c5SYu Xiangning } 84180f1702c5SYu Xiangning rw_enter(&udp->udp_rwlock, RW_WRITER); 84190f1702c5SYu Xiangning udp->udp_pending_op = -1; 84200f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 84210f1702c5SYu Xiangning connp->conn_anon_port = B_FALSE; 84220f1702c5SYu Xiangning connp->conn_mlp_type = mlptSingle; 84230f1702c5SYu Xiangning return (-TACCES); 84240f1702c5SYu Xiangning } 8425ff550d0eSmasputra 84260f1702c5SYu Xiangning /* 84270f1702c5SYu Xiangning * If we're specifically binding a shared IP address and the 84280f1702c5SYu Xiangning * port is MLP on shared addresses, then check to see if this 84290f1702c5SYu Xiangning * zone actually owns the MLP. Reject if not. 84300f1702c5SYu Xiangning */ 84310f1702c5SYu Xiangning if (mlptype == mlptShared && addrtype == mlptShared) { 84320f1702c5SYu Xiangning /* 84330f1702c5SYu Xiangning * No need to handle exclusive-stack zones since 84340f1702c5SYu Xiangning * ALL_ZONES only applies to the shared stack. 84350f1702c5SYu Xiangning */ 84360f1702c5SYu Xiangning zoneid_t mlpzone; 84370f1702c5SYu Xiangning 84380f1702c5SYu Xiangning mlpzone = tsol_mlp_findzone(IPPROTO_UDP, 84390f1702c5SYu Xiangning htons(mlpport)); 84400f1702c5SYu Xiangning if (connp->conn_zoneid != mlpzone) { 84410f1702c5SYu Xiangning if (udp->udp_debug) { 84420f1702c5SYu Xiangning (void) strlog(UDP_MOD_ID, 0, 1, 84430f1702c5SYu Xiangning SL_ERROR|SL_TRACE, 84440f1702c5SYu Xiangning "udp_bind: attempt to bind port " 84450f1702c5SYu Xiangning "%d on shared addr in zone %d " 84460f1702c5SYu Xiangning "(should be %d)", 84470f1702c5SYu Xiangning mlpport, connp->conn_zoneid, 84480f1702c5SYu Xiangning mlpzone); 84490f1702c5SYu Xiangning } 84500f1702c5SYu Xiangning rw_enter(&udp->udp_rwlock, RW_WRITER); 84510f1702c5SYu Xiangning udp->udp_pending_op = -1; 84520f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 84530f1702c5SYu Xiangning connp->conn_anon_port = B_FALSE; 84540f1702c5SYu Xiangning connp->conn_mlp_type = mlptSingle; 84550f1702c5SYu Xiangning return (-TACCES); 84560f1702c5SYu Xiangning } 84570f1702c5SYu Xiangning } 84580f1702c5SYu Xiangning if (connp->conn_anon_port) { 84590f1702c5SYu Xiangning error = tsol_mlp_anon(zone, mlptype, connp->conn_ulp, 84600f1702c5SYu Xiangning port, B_TRUE); 84610f1702c5SYu Xiangning if (error != 0) { 84620f1702c5SYu Xiangning if (udp->udp_debug) { 84630f1702c5SYu Xiangning (void) strlog(UDP_MOD_ID, 0, 1, 84640f1702c5SYu Xiangning SL_ERROR|SL_TRACE, 84650f1702c5SYu Xiangning "udp_bind: cannot establish anon " 84660f1702c5SYu Xiangning "MLP for port %d", port); 84670f1702c5SYu Xiangning } 84680f1702c5SYu Xiangning rw_enter(&udp->udp_rwlock, RW_WRITER); 84690f1702c5SYu Xiangning udp->udp_pending_op = -1; 84700f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 84710f1702c5SYu Xiangning connp->conn_anon_port = B_FALSE; 84720f1702c5SYu Xiangning connp->conn_mlp_type = mlptSingle; 84730f1702c5SYu Xiangning return (-TACCES); 84740f1702c5SYu Xiangning } 84750f1702c5SYu Xiangning } 84760f1702c5SYu Xiangning connp->conn_mlp_type = mlptype; 8477fc80c0dfSnordmark } 84780f1702c5SYu Xiangning 84790f1702c5SYu Xiangning if (!V6_OR_V4_INADDR_ANY(udp->udp_v6src)) { 84800f1702c5SYu Xiangning /* 84810f1702c5SYu Xiangning * Append a request for an IRE if udp_v6src not 84820f1702c5SYu Xiangning * zero (IPv4 - INADDR_ANY, or IPv6 - all-zeroes address). 84830f1702c5SYu Xiangning */ 84840f1702c5SYu Xiangning mp = allocb(sizeof (ire_t), BPRI_HI); 84850f1702c5SYu Xiangning if (!mp) { 84860f1702c5SYu Xiangning rw_enter(&udp->udp_rwlock, RW_WRITER); 84870f1702c5SYu Xiangning udp->udp_pending_op = -1; 84880f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 84890f1702c5SYu Xiangning return (ENOMEM); 84900f1702c5SYu Xiangning } 84910f1702c5SYu Xiangning mp->b_wptr += sizeof (ire_t); 84920f1702c5SYu Xiangning mp->b_datap->db_type = IRE_DB_REQ_TYPE; 8493dc3879f9Sjarrett } 84940f1702c5SYu Xiangning if (udp->udp_family == AF_INET6) { 84950f1702c5SYu Xiangning ASSERT(udp->udp_connp->conn_af_isv6); 84960f1702c5SYu Xiangning error = ip_proto_bind_laddr_v6(connp, &mp, IPPROTO_UDP, 84970f1702c5SYu Xiangning &udp->udp_bound_v6src, udp->udp_port, B_TRUE); 84980f1702c5SYu Xiangning } else { 84990f1702c5SYu Xiangning ASSERT(!udp->udp_connp->conn_af_isv6); 85000f1702c5SYu Xiangning error = ip_proto_bind_laddr_v4(connp, &mp, IPPROTO_UDP, 85010f1702c5SYu Xiangning V4_PART_OF_V6(udp->udp_bound_v6src), udp->udp_port, 85020f1702c5SYu Xiangning B_TRUE); 8503ff550d0eSmasputra } 85047c478bd9Sstevel@tonic-gate 85050f1702c5SYu Xiangning (void) udp_post_ip_bind_connect(udp, mp, error); 85060f1702c5SYu Xiangning return (error); 85070f1702c5SYu Xiangning } 8508ca9327a6Smeem 85090f1702c5SYu Xiangning int 85100f1702c5SYu Xiangning udp_bind(sock_lower_handle_t proto_handle, struct sockaddr *sa, 85110f1702c5SYu Xiangning socklen_t len, cred_t *cr) 8512ca9327a6Smeem { 85130f1702c5SYu Xiangning int error; 85140f1702c5SYu Xiangning conn_t *connp; 8515ca9327a6Smeem 85160f1702c5SYu Xiangning connp = (conn_t *)proto_handle; 8517ca9327a6Smeem 85180f1702c5SYu Xiangning if (sa == NULL) 85190f1702c5SYu Xiangning error = udp_do_unbind(connp); 85200f1702c5SYu Xiangning else 85210f1702c5SYu Xiangning error = udp_do_bind(connp, sa, len, cr, B_TRUE); 8522ca9327a6Smeem 85230f1702c5SYu Xiangning if (error < 0) { 85240f1702c5SYu Xiangning if (error == -TOUTSTATE) 85250f1702c5SYu Xiangning error = EINVAL; 85260f1702c5SYu Xiangning else 85270f1702c5SYu Xiangning error = proto_tlitosyserr(-error); 8528ca9327a6Smeem } 8529ca9327a6Smeem 85300f1702c5SYu Xiangning return (error); 8531ca9327a6Smeem } 8532ca9327a6Smeem 8533ca9327a6Smeem static int 85340f1702c5SYu Xiangning udp_implicit_bind(conn_t *connp, cred_t *cr) 8535ca9327a6Smeem { 85360f1702c5SYu Xiangning int error; 8537ca9327a6Smeem 85380f1702c5SYu Xiangning error = udp_do_bind(connp, NULL, 0, cr, B_FALSE); 85390f1702c5SYu Xiangning return ((error < 0) ? proto_tlitosyserr(-error) : error); 8540ca9327a6Smeem } 8541ca9327a6Smeem 8542ca9327a6Smeem /* 85430f1702c5SYu Xiangning * This routine removes a port number association from a stream. It 85440f1702c5SYu Xiangning * is called by udp_unbind and udp_tpi_unbind. 8545ca9327a6Smeem */ 85460f1702c5SYu Xiangning static int 85470f1702c5SYu Xiangning udp_do_unbind(conn_t *connp) 8548ca9327a6Smeem { 85490f1702c5SYu Xiangning udp_t *udp = connp->conn_udp; 85500f1702c5SYu Xiangning udp_fanout_t *udpf; 85510f1702c5SYu Xiangning udp_stack_t *us = udp->udp_us; 8552ca9327a6Smeem 85530f1702c5SYu Xiangning if (cl_inet_unbind != NULL) { 85540f1702c5SYu Xiangning /* 85550f1702c5SYu Xiangning * Running in cluster mode - register unbind information 85560f1702c5SYu Xiangning */ 85570f1702c5SYu Xiangning if (udp->udp_ipversion == IPV4_VERSION) { 85588e4b770fSLu Huafeng (*cl_inet_unbind)( 85598e4b770fSLu Huafeng connp->conn_netstack->netstack_stackid, 85608e4b770fSLu Huafeng IPPROTO_UDP, AF_INET, 85610f1702c5SYu Xiangning (uint8_t *)(&V4_PART_OF_V6(udp->udp_v6src)), 85628e4b770fSLu Huafeng (in_port_t)udp->udp_port, NULL); 85630f1702c5SYu Xiangning } else { 85648e4b770fSLu Huafeng (*cl_inet_unbind)( 85658e4b770fSLu Huafeng connp->conn_netstack->netstack_stackid, 85668e4b770fSLu Huafeng IPPROTO_UDP, AF_INET6, 85670f1702c5SYu Xiangning (uint8_t *)&(udp->udp_v6src), 85688e4b770fSLu Huafeng (in_port_t)udp->udp_port, NULL); 85690f1702c5SYu Xiangning } 8570ca9327a6Smeem } 8571ca9327a6Smeem 85720f1702c5SYu Xiangning rw_enter(&udp->udp_rwlock, RW_WRITER); 85730f1702c5SYu Xiangning if (udp->udp_state == TS_UNBND || udp->udp_pending_op != -1) { 85740f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 85750f1702c5SYu Xiangning return (-TOUTSTATE); 8576ca9327a6Smeem } 85770f1702c5SYu Xiangning udp->udp_pending_op = T_UNBIND_REQ; 8578ca9327a6Smeem rw_exit(&udp->udp_rwlock); 8579ca9327a6Smeem 85800f1702c5SYu Xiangning /* 85810f1702c5SYu Xiangning * Pass the unbind to IP; T_UNBIND_REQ is larger than T_OK_ACK 85820f1702c5SYu Xiangning * and therefore ip_unbind must never return NULL. 85830f1702c5SYu Xiangning */ 85840f1702c5SYu Xiangning ip_unbind(connp); 8585ca9327a6Smeem 85860f1702c5SYu Xiangning /* 85870f1702c5SYu Xiangning * Once we're unbound from IP, the pending operation may be cleared 85880f1702c5SYu Xiangning * here. 85890f1702c5SYu Xiangning */ 85900f1702c5SYu Xiangning rw_enter(&udp->udp_rwlock, RW_WRITER); 85910f1702c5SYu Xiangning udpf = &us->us_bind_fanout[UDP_BIND_HASH(udp->udp_port, 85920f1702c5SYu Xiangning us->us_bind_fanout_size)]; 85937c478bd9Sstevel@tonic-gate 85940f1702c5SYu Xiangning mutex_enter(&udpf->uf_lock); 85950f1702c5SYu Xiangning udp_bind_hash_remove(udp, B_TRUE); 85960f1702c5SYu Xiangning V6_SET_ZERO(udp->udp_v6src); 85970f1702c5SYu Xiangning V6_SET_ZERO(udp->udp_bound_v6src); 85980f1702c5SYu Xiangning udp->udp_port = 0; 85990f1702c5SYu Xiangning mutex_exit(&udpf->uf_lock); 86007c478bd9Sstevel@tonic-gate 86010f1702c5SYu Xiangning udp->udp_pending_op = -1; 86020f1702c5SYu Xiangning udp->udp_state = TS_UNBND; 86030f1702c5SYu Xiangning if (udp->udp_family == AF_INET6) 86040f1702c5SYu Xiangning (void) udp_build_hdrs(udp); 86050f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 86067c478bd9Sstevel@tonic-gate 86070f1702c5SYu Xiangning return (0); 86080f1702c5SYu Xiangning } 86090f1702c5SYu Xiangning 86100f1702c5SYu Xiangning static int 86110f1702c5SYu Xiangning udp_post_ip_bind_connect(udp_t *udp, mblk_t *ire_mp, int error) 86120f1702c5SYu Xiangning { 86130f1702c5SYu Xiangning ire_t *ire; 86140f1702c5SYu Xiangning udp_fanout_t *udpf; 86150f1702c5SYu Xiangning udp_stack_t *us = udp->udp_us; 86160f1702c5SYu Xiangning 86170f1702c5SYu Xiangning ASSERT(udp->udp_pending_op != -1); 86180f1702c5SYu Xiangning rw_enter(&udp->udp_rwlock, RW_WRITER); 86190f1702c5SYu Xiangning if (error == 0) { 86200f1702c5SYu Xiangning /* For udp_do_connect() success */ 86210f1702c5SYu Xiangning /* udp_do_bind() success will do nothing in here */ 86220f1702c5SYu Xiangning /* 86230f1702c5SYu Xiangning * If a broadcast/multicast address was bound, set 86240f1702c5SYu Xiangning * the source address to 0. 86250f1702c5SYu Xiangning * This ensures no datagrams with broadcast address 86260f1702c5SYu Xiangning * as source address are emitted (which would violate 86270f1702c5SYu Xiangning * RFC1122 - Hosts requirements) 86280f1702c5SYu Xiangning * 86290f1702c5SYu Xiangning * Note that when connecting the returned IRE is 86300f1702c5SYu Xiangning * for the destination address and we only perform 86310f1702c5SYu Xiangning * the broadcast check for the source address (it 86320f1702c5SYu Xiangning * is OK to connect to a broadcast/multicast address.) 86330f1702c5SYu Xiangning */ 86340f1702c5SYu Xiangning if (ire_mp != NULL && ire_mp->b_datap->db_type == IRE_DB_TYPE) { 86350f1702c5SYu Xiangning ire = (ire_t *)ire_mp->b_rptr; 86367c478bd9Sstevel@tonic-gate 8637ff550d0eSmasputra /* 86380f1702c5SYu Xiangning * Note: we get IRE_BROADCAST for IPv6 to "mark" a 86390f1702c5SYu Xiangning * multicast local address. 8640ff550d0eSmasputra */ 86410f1702c5SYu Xiangning udpf = &us->us_bind_fanout[UDP_BIND_HASH(udp->udp_port, 86420f1702c5SYu Xiangning us->us_bind_fanout_size)]; 86430f1702c5SYu Xiangning if (ire->ire_type == IRE_BROADCAST && 86440f1702c5SYu Xiangning udp->udp_state != TS_DATA_XFER) { 86450f1702c5SYu Xiangning ASSERT(udp->udp_pending_op == T_BIND_REQ || 86460f1702c5SYu Xiangning udp->udp_pending_op == O_T_BIND_REQ); 86470f1702c5SYu Xiangning /* 86480f1702c5SYu Xiangning * This was just a local bind to a broadcast 86490f1702c5SYu Xiangning * addr. 86500f1702c5SYu Xiangning */ 86510f1702c5SYu Xiangning mutex_enter(&udpf->uf_lock); 86520f1702c5SYu Xiangning V6_SET_ZERO(udp->udp_v6src); 86530f1702c5SYu Xiangning mutex_exit(&udpf->uf_lock); 86540f1702c5SYu Xiangning if (udp->udp_family == AF_INET6) 86550f1702c5SYu Xiangning (void) udp_build_hdrs(udp); 86560f1702c5SYu Xiangning } else if (V6_OR_V4_INADDR_ANY(udp->udp_v6src)) { 86570f1702c5SYu Xiangning if (udp->udp_family == AF_INET6) 86580f1702c5SYu Xiangning (void) udp_build_hdrs(udp); 8659ff550d0eSmasputra } 86607c478bd9Sstevel@tonic-gate } 86610f1702c5SYu Xiangning } else { 86620f1702c5SYu Xiangning udpf = &us->us_bind_fanout[UDP_BIND_HASH(udp->udp_port, 86630f1702c5SYu Xiangning us->us_bind_fanout_size)]; 86640f1702c5SYu Xiangning mutex_enter(&udpf->uf_lock); 86650f1702c5SYu Xiangning 86660f1702c5SYu Xiangning if (udp->udp_state == TS_DATA_XFER) { 86670f1702c5SYu Xiangning /* Connect failed */ 86680f1702c5SYu Xiangning /* Revert back to the bound source */ 86690f1702c5SYu Xiangning udp->udp_v6src = udp->udp_bound_v6src; 86700f1702c5SYu Xiangning udp->udp_state = TS_IDLE; 86710f1702c5SYu Xiangning } else { 86720f1702c5SYu Xiangning /* For udp_do_bind() failed */ 86730f1702c5SYu Xiangning V6_SET_ZERO(udp->udp_v6src); 86740f1702c5SYu Xiangning V6_SET_ZERO(udp->udp_bound_v6src); 86750f1702c5SYu Xiangning udp->udp_state = TS_UNBND; 86760f1702c5SYu Xiangning udp_bind_hash_remove(udp, B_TRUE); 86770f1702c5SYu Xiangning udp->udp_port = 0; 86780f1702c5SYu Xiangning } 86790f1702c5SYu Xiangning mutex_exit(&udpf->uf_lock); 86800f1702c5SYu Xiangning if (udp->udp_family == AF_INET6) 86810f1702c5SYu Xiangning (void) udp_build_hdrs(udp); 86827c478bd9Sstevel@tonic-gate } 86830f1702c5SYu Xiangning udp->udp_pending_op = -1; 86840f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 86850f1702c5SYu Xiangning if (ire_mp != NULL) 86860f1702c5SYu Xiangning freeb(ire_mp); 86870f1702c5SYu Xiangning return (error); 8688ff550d0eSmasputra } 8689ff550d0eSmasputra 86907c478bd9Sstevel@tonic-gate /* 86910f1702c5SYu Xiangning * It associates a default destination address with the stream. 86927c478bd9Sstevel@tonic-gate */ 86930f1702c5SYu Xiangning static int 86940f1702c5SYu Xiangning udp_do_connect(conn_t *connp, const struct sockaddr *sa, socklen_t len) 86957c478bd9Sstevel@tonic-gate { 86960f1702c5SYu Xiangning sin6_t *sin6; 86970f1702c5SYu Xiangning sin_t *sin; 86980f1702c5SYu Xiangning in6_addr_t v6dst; 86990f1702c5SYu Xiangning ipaddr_t v4dst; 87000f1702c5SYu Xiangning uint16_t dstport; 87010f1702c5SYu Xiangning uint32_t flowinfo; 87020f1702c5SYu Xiangning mblk_t *ire_mp; 87030f1702c5SYu Xiangning udp_fanout_t *udpf; 87040f1702c5SYu Xiangning udp_t *udp, *udp1; 87050f1702c5SYu Xiangning ushort_t ipversion; 87060f1702c5SYu Xiangning udp_stack_t *us; 87070f1702c5SYu Xiangning int error; 87087c478bd9Sstevel@tonic-gate 87090f1702c5SYu Xiangning udp = connp->conn_udp; 87100f1702c5SYu Xiangning us = udp->udp_us; 87110f1702c5SYu Xiangning 87120f1702c5SYu Xiangning /* 87130f1702c5SYu Xiangning * Address has been verified by the caller 87140f1702c5SYu Xiangning */ 87150f1702c5SYu Xiangning switch (len) { 87167c478bd9Sstevel@tonic-gate default: 87170f1702c5SYu Xiangning /* 87180f1702c5SYu Xiangning * Should never happen 87190f1702c5SYu Xiangning */ 87200f1702c5SYu Xiangning return (EINVAL); 87210f1702c5SYu Xiangning 87220f1702c5SYu Xiangning case sizeof (sin_t): 87230f1702c5SYu Xiangning sin = (sin_t *)sa; 87240f1702c5SYu Xiangning v4dst = sin->sin_addr.s_addr; 87250f1702c5SYu Xiangning dstport = sin->sin_port; 87260f1702c5SYu Xiangning IN6_IPADDR_TO_V4MAPPED(v4dst, &v6dst); 87270f1702c5SYu Xiangning ASSERT(udp->udp_ipversion == IPV4_VERSION); 87280f1702c5SYu Xiangning ipversion = IPV4_VERSION; 87290f1702c5SYu Xiangning break; 87300f1702c5SYu Xiangning 87310f1702c5SYu Xiangning case sizeof (sin6_t): 87320f1702c5SYu Xiangning sin6 = (sin6_t *)sa; 87330f1702c5SYu Xiangning v6dst = sin6->sin6_addr; 87340f1702c5SYu Xiangning dstport = sin6->sin6_port; 87350f1702c5SYu Xiangning if (IN6_IS_ADDR_V4MAPPED(&v6dst)) { 87360f1702c5SYu Xiangning IN6_V4MAPPED_TO_IPADDR(&v6dst, v4dst); 87370f1702c5SYu Xiangning ipversion = IPV4_VERSION; 87380f1702c5SYu Xiangning flowinfo = 0; 87390f1702c5SYu Xiangning } else { 87400f1702c5SYu Xiangning ipversion = IPV6_VERSION; 87410f1702c5SYu Xiangning flowinfo = sin6->sin6_flowinfo; 87420f1702c5SYu Xiangning } 87430f1702c5SYu Xiangning break; 87440f1702c5SYu Xiangning } 87450f1702c5SYu Xiangning 87460f1702c5SYu Xiangning if (dstport == 0) 87470f1702c5SYu Xiangning return (-TBADADDR); 87480f1702c5SYu Xiangning 87490f1702c5SYu Xiangning rw_enter(&udp->udp_rwlock, RW_WRITER); 87500f1702c5SYu Xiangning 87510f1702c5SYu Xiangning /* 87520f1702c5SYu Xiangning * This UDP must have bound to a port already before doing a connect. 87530f1702c5SYu Xiangning * TPI mandates that users must send TPI primitives only 1 at a time 87540f1702c5SYu Xiangning * and wait for the response before sending the next primitive. 87550f1702c5SYu Xiangning */ 87560f1702c5SYu Xiangning if (udp->udp_state == TS_UNBND || udp->udp_pending_op != -1) { 87570f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 87580f1702c5SYu Xiangning (void) strlog(UDP_MOD_ID, 0, 1, SL_ERROR|SL_TRACE, 87590f1702c5SYu Xiangning "udp_connect: bad state, %u", udp->udp_state); 87600f1702c5SYu Xiangning return (-TOUTSTATE); 87610f1702c5SYu Xiangning } 87620f1702c5SYu Xiangning udp->udp_pending_op = T_CONN_REQ; 87630f1702c5SYu Xiangning ASSERT(udp->udp_port != 0 && udp->udp_ptpbhn != NULL); 87640f1702c5SYu Xiangning 87650f1702c5SYu Xiangning if (ipversion == IPV4_VERSION) { 87660f1702c5SYu Xiangning udp->udp_max_hdr_len = IP_SIMPLE_HDR_LENGTH + UDPH_SIZE + 87670f1702c5SYu Xiangning udp->udp_ip_snd_options_len; 87680f1702c5SYu Xiangning } else { 87690f1702c5SYu Xiangning udp->udp_max_hdr_len = udp->udp_sticky_hdrs_len; 87700f1702c5SYu Xiangning } 87710f1702c5SYu Xiangning 87720f1702c5SYu Xiangning udpf = &us->us_bind_fanout[UDP_BIND_HASH(udp->udp_port, 87730f1702c5SYu Xiangning us->us_bind_fanout_size)]; 87740f1702c5SYu Xiangning 87750f1702c5SYu Xiangning mutex_enter(&udpf->uf_lock); 87760f1702c5SYu Xiangning if (udp->udp_state == TS_DATA_XFER) { 87770f1702c5SYu Xiangning /* Already connected - clear out state */ 87780f1702c5SYu Xiangning udp->udp_v6src = udp->udp_bound_v6src; 87790f1702c5SYu Xiangning udp->udp_state = TS_IDLE; 87807c478bd9Sstevel@tonic-gate } 8781ff550d0eSmasputra 87820f1702c5SYu Xiangning /* 87830f1702c5SYu Xiangning * Create a default IP header with no IP options. 87840f1702c5SYu Xiangning */ 87850f1702c5SYu Xiangning udp->udp_dstport = dstport; 87860f1702c5SYu Xiangning udp->udp_ipversion = ipversion; 87870f1702c5SYu Xiangning if (ipversion == IPV4_VERSION) { 87880f1702c5SYu Xiangning /* 87890f1702c5SYu Xiangning * Interpret a zero destination to mean loopback. 87900f1702c5SYu Xiangning * Update the T_CONN_REQ (sin/sin6) since it is used to 87910f1702c5SYu Xiangning * generate the T_CONN_CON. 87920f1702c5SYu Xiangning */ 87930f1702c5SYu Xiangning if (v4dst == INADDR_ANY) { 87940f1702c5SYu Xiangning v4dst = htonl(INADDR_LOOPBACK); 87950f1702c5SYu Xiangning IN6_IPADDR_TO_V4MAPPED(v4dst, &v6dst); 87960f1702c5SYu Xiangning if (udp->udp_family == AF_INET) { 87970f1702c5SYu Xiangning sin->sin_addr.s_addr = v4dst; 87980f1702c5SYu Xiangning } else { 87990f1702c5SYu Xiangning sin6->sin6_addr = v6dst; 88000f1702c5SYu Xiangning } 88010f1702c5SYu Xiangning } 88020f1702c5SYu Xiangning udp->udp_v6dst = v6dst; 88030f1702c5SYu Xiangning udp->udp_flowinfo = 0; 88040f1702c5SYu Xiangning 88057c478bd9Sstevel@tonic-gate /* 88060f1702c5SYu Xiangning * If the destination address is multicast and 88070f1702c5SYu Xiangning * an outgoing multicast interface has been set, 88080f1702c5SYu Xiangning * use the address of that interface as our 88090f1702c5SYu Xiangning * source address if no source address has been set. 88107c478bd9Sstevel@tonic-gate */ 88110f1702c5SYu Xiangning if (V4_PART_OF_V6(udp->udp_v6src) == INADDR_ANY && 88120f1702c5SYu Xiangning CLASSD(v4dst) && 88130f1702c5SYu Xiangning udp->udp_multicast_if_addr != INADDR_ANY) { 88140f1702c5SYu Xiangning IN6_IPADDR_TO_V4MAPPED(udp->udp_multicast_if_addr, 88150f1702c5SYu Xiangning &udp->udp_v6src); 88160f1702c5SYu Xiangning } 88170f1702c5SYu Xiangning } else { 88180f1702c5SYu Xiangning ASSERT(udp->udp_ipversion == IPV6_VERSION); 88197c478bd9Sstevel@tonic-gate /* 88200f1702c5SYu Xiangning * Interpret a zero destination to mean loopback. 88210f1702c5SYu Xiangning * Update the T_CONN_REQ (sin/sin6) since it is used to 88220f1702c5SYu Xiangning * generate the T_CONN_CON. 88237c478bd9Sstevel@tonic-gate */ 88240f1702c5SYu Xiangning if (IN6_IS_ADDR_UNSPECIFIED(&v6dst)) { 88250f1702c5SYu Xiangning v6dst = ipv6_loopback; 88260f1702c5SYu Xiangning sin6->sin6_addr = v6dst; 88270f1702c5SYu Xiangning } 88280f1702c5SYu Xiangning udp->udp_v6dst = v6dst; 88290f1702c5SYu Xiangning udp->udp_flowinfo = flowinfo; 88307c478bd9Sstevel@tonic-gate /* 88310f1702c5SYu Xiangning * If the destination address is multicast and 88320f1702c5SYu Xiangning * an outgoing multicast interface has been set, 88330f1702c5SYu Xiangning * then the ip bind logic will pick the correct source 88340f1702c5SYu Xiangning * address (i.e. matching the outgoing multicast interface). 88357c478bd9Sstevel@tonic-gate */ 88367c478bd9Sstevel@tonic-gate } 88377c478bd9Sstevel@tonic-gate 88387c478bd9Sstevel@tonic-gate /* 88390f1702c5SYu Xiangning * Verify that the src/port/dst/port is unique for all 88400f1702c5SYu Xiangning * connections in TS_DATA_XFER 88417c478bd9Sstevel@tonic-gate */ 88420f1702c5SYu Xiangning for (udp1 = udpf->uf_udp; udp1 != NULL; udp1 = udp1->udp_bind_hash) { 88430f1702c5SYu Xiangning if (udp1->udp_state != TS_DATA_XFER) 88440f1702c5SYu Xiangning continue; 88450f1702c5SYu Xiangning if (udp->udp_port != udp1->udp_port || 88460f1702c5SYu Xiangning udp->udp_ipversion != udp1->udp_ipversion || 88470f1702c5SYu Xiangning dstport != udp1->udp_dstport || 88480f1702c5SYu Xiangning !IN6_ARE_ADDR_EQUAL(&udp->udp_v6src, &udp1->udp_v6src) || 88490f1702c5SYu Xiangning !IN6_ARE_ADDR_EQUAL(&v6dst, &udp1->udp_v6dst) || 88500f1702c5SYu Xiangning !(IPCL_ZONE_MATCH(udp->udp_connp, 88510f1702c5SYu Xiangning udp1->udp_connp->conn_zoneid) || 88520f1702c5SYu Xiangning IPCL_ZONE_MATCH(udp1->udp_connp, 88530f1702c5SYu Xiangning udp->udp_connp->conn_zoneid))) 88540f1702c5SYu Xiangning continue; 88550f1702c5SYu Xiangning mutex_exit(&udpf->uf_lock); 88560f1702c5SYu Xiangning udp->udp_pending_op = -1; 88570f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 88580f1702c5SYu Xiangning return (-TBADADDR); 88597c478bd9Sstevel@tonic-gate } 88608e4b770fSLu Huafeng 88618e4b770fSLu Huafeng if (cl_inet_connect2 != NULL) { 88628e4b770fSLu Huafeng CL_INET_UDP_CONNECT(connp, udp, B_TRUE, &v6dst, dstport, error); 88638e4b770fSLu Huafeng if (error != 0) { 88648e4b770fSLu Huafeng mutex_exit(&udpf->uf_lock); 88658e4b770fSLu Huafeng udp->udp_pending_op = -1; 88668e4b770fSLu Huafeng rw_exit(&udp->udp_rwlock); 88678e4b770fSLu Huafeng return (-TBADADDR); 88688e4b770fSLu Huafeng } 88698e4b770fSLu Huafeng } 88708e4b770fSLu Huafeng 88710f1702c5SYu Xiangning udp->udp_state = TS_DATA_XFER; 88720f1702c5SYu Xiangning mutex_exit(&udpf->uf_lock); 8873ca9327a6Smeem 88740f1702c5SYu Xiangning ire_mp = allocb(sizeof (ire_t), BPRI_HI); 88750f1702c5SYu Xiangning if (ire_mp == NULL) { 88760f1702c5SYu Xiangning mutex_enter(&udpf->uf_lock); 88770f1702c5SYu Xiangning udp->udp_state = TS_IDLE; 88780f1702c5SYu Xiangning udp->udp_pending_op = -1; 88790f1702c5SYu Xiangning mutex_exit(&udpf->uf_lock); 88800f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 88810f1702c5SYu Xiangning return (ENOMEM); 88827c478bd9Sstevel@tonic-gate } 88830f1702c5SYu Xiangning 8884ca9327a6Smeem rw_exit(&udp->udp_rwlock); 88857c478bd9Sstevel@tonic-gate 88860f1702c5SYu Xiangning ire_mp->b_wptr += sizeof (ire_t); 88870f1702c5SYu Xiangning ire_mp->b_datap->db_type = IRE_DB_REQ_TYPE; 88887c478bd9Sstevel@tonic-gate 88890f1702c5SYu Xiangning if (udp->udp_family == AF_INET) { 88900f1702c5SYu Xiangning error = ip_proto_bind_connected_v4(connp, &ire_mp, IPPROTO_UDP, 88910f1702c5SYu Xiangning &V4_PART_OF_V6(udp->udp_v6src), udp->udp_port, 88920f1702c5SYu Xiangning V4_PART_OF_V6(udp->udp_v6dst), udp->udp_dstport, 88930f1702c5SYu Xiangning B_TRUE, B_TRUE); 88940f1702c5SYu Xiangning } else { 88950f1702c5SYu Xiangning error = ip_proto_bind_connected_v6(connp, &ire_mp, IPPROTO_UDP, 88960f1702c5SYu Xiangning &udp->udp_v6src, udp->udp_port, &udp->udp_v6dst, 88970f1702c5SYu Xiangning &udp->udp_sticky_ipp, udp->udp_dstport, B_TRUE, B_TRUE); 88987c478bd9Sstevel@tonic-gate } 88990f1702c5SYu Xiangning 89000f1702c5SYu Xiangning return (udp_post_ip_bind_connect(udp, ire_mp, error)); 89017c478bd9Sstevel@tonic-gate } 89027c478bd9Sstevel@tonic-gate 89030f1702c5SYu Xiangning /* ARGSUSED */ 89047c478bd9Sstevel@tonic-gate static int 89050f1702c5SYu Xiangning udp_connect(sock_lower_handle_t proto_handle, const struct sockaddr *sa, 89060f1702c5SYu Xiangning socklen_t len, sock_connid_t *id, cred_t *cr) 89077c478bd9Sstevel@tonic-gate { 89080f1702c5SYu Xiangning conn_t *connp = (conn_t *)proto_handle; 89090f1702c5SYu Xiangning udp_t *udp = connp->conn_udp; 89100f1702c5SYu Xiangning int error; 89110f1702c5SYu Xiangning boolean_t did_bind = B_FALSE; 89127c478bd9Sstevel@tonic-gate 89130f1702c5SYu Xiangning if (sa == NULL) { 89140f1702c5SYu Xiangning /* 89150f1702c5SYu Xiangning * Disconnect 89160f1702c5SYu Xiangning * Make sure we are connected 89170f1702c5SYu Xiangning */ 89180f1702c5SYu Xiangning if (udp->udp_state != TS_DATA_XFER) 89190f1702c5SYu Xiangning return (EINVAL); 89207c478bd9Sstevel@tonic-gate 89210f1702c5SYu Xiangning error = udp_disconnect(connp); 89220f1702c5SYu Xiangning return (error); 89230f1702c5SYu Xiangning } 89247c478bd9Sstevel@tonic-gate 89250f1702c5SYu Xiangning error = proto_verify_ip_addr(udp->udp_family, sa, len); 89260f1702c5SYu Xiangning if (error != 0) 89270f1702c5SYu Xiangning goto done; 89287c478bd9Sstevel@tonic-gate 89290f1702c5SYu Xiangning /* do an implicit bind if necessary */ 89300f1702c5SYu Xiangning if (udp->udp_state == TS_UNBND) { 89310f1702c5SYu Xiangning error = udp_implicit_bind(connp, cr); 89320f1702c5SYu Xiangning /* 89330f1702c5SYu Xiangning * We could be racing with an actual bind, in which case 89340f1702c5SYu Xiangning * we would see EPROTO. We cross our fingers and try 89350f1702c5SYu Xiangning * to connect. 89360f1702c5SYu Xiangning */ 89370f1702c5SYu Xiangning if (!(error == 0 || error == EPROTO)) 89380f1702c5SYu Xiangning goto done; 89390f1702c5SYu Xiangning did_bind = B_TRUE; 89400f1702c5SYu Xiangning } 89410f1702c5SYu Xiangning /* 89420f1702c5SYu Xiangning * set SO_DGRAM_ERRIND 89430f1702c5SYu Xiangning */ 89440f1702c5SYu Xiangning udp->udp_dgram_errind = B_TRUE; 89457c478bd9Sstevel@tonic-gate 89460f1702c5SYu Xiangning error = udp_do_connect(connp, sa, len); 89470f1702c5SYu Xiangning 89480f1702c5SYu Xiangning if (error != 0 && did_bind) { 89490f1702c5SYu Xiangning int unbind_err; 89500f1702c5SYu Xiangning 89510f1702c5SYu Xiangning unbind_err = udp_do_unbind(connp); 89520f1702c5SYu Xiangning ASSERT(unbind_err == 0); 89530f1702c5SYu Xiangning } 89540f1702c5SYu Xiangning 89550f1702c5SYu Xiangning if (error == 0) { 89560f1702c5SYu Xiangning *id = 0; 89570f1702c5SYu Xiangning (*connp->conn_upcalls->su_connected) 89580f1702c5SYu Xiangning (connp->conn_upper_handle, 0, NULL, -1); 89590f1702c5SYu Xiangning } else if (error < 0) { 89600f1702c5SYu Xiangning error = proto_tlitosyserr(-error); 89610f1702c5SYu Xiangning } 89620f1702c5SYu Xiangning 89630f1702c5SYu Xiangning done: 89640f1702c5SYu Xiangning if (error != 0 && udp->udp_state == TS_DATA_XFER) { 89657c478bd9Sstevel@tonic-gate /* 89660f1702c5SYu Xiangning * No need to hold locks to set state 89670f1702c5SYu Xiangning * after connect failure socket state is undefined 89680f1702c5SYu Xiangning * We set the state only to imitate old sockfs behavior 89697c478bd9Sstevel@tonic-gate */ 89700f1702c5SYu Xiangning udp->udp_state = TS_IDLE; 89717c478bd9Sstevel@tonic-gate } 89720f1702c5SYu Xiangning return (error); 89737c478bd9Sstevel@tonic-gate } 89747c478bd9Sstevel@tonic-gate 89750f1702c5SYu Xiangning /* ARGSUSED */ 89760f1702c5SYu Xiangning int 89770f1702c5SYu Xiangning udp_send(sock_lower_handle_t proto_handle, mblk_t *mp, struct nmsghdr *msg, 89780f1702c5SYu Xiangning cred_t *cr) 89797c478bd9Sstevel@tonic-gate { 89800f1702c5SYu Xiangning conn_t *connp = (conn_t *)proto_handle; 89810f1702c5SYu Xiangning udp_t *udp = connp->conn_udp; 89820f1702c5SYu Xiangning udp_stack_t *us = udp->udp_us; 89830f1702c5SYu Xiangning int error = 0; 89840f1702c5SYu Xiangning 89850f1702c5SYu Xiangning ASSERT(DB_TYPE(mp) == M_DATA); 89867c478bd9Sstevel@tonic-gate 8987f4b3ec61Sdh /* 89880f1702c5SYu Xiangning * If the socket is connected and no change in destination 8989f4b3ec61Sdh */ 89900f1702c5SYu Xiangning if (msg->msg_namelen == 0) { 89910f1702c5SYu Xiangning error = udp_send_connected(connp, mp, msg, cr, curproc->p_pid); 89920f1702c5SYu Xiangning if (error == EDESTADDRREQ) 89930f1702c5SYu Xiangning return (error); 89940f1702c5SYu Xiangning else 89950f1702c5SYu Xiangning return (udp->udp_dgram_errind ? error : 0); 89960f1702c5SYu Xiangning } 8997f4b3ec61Sdh 89980f1702c5SYu Xiangning /* 89990f1702c5SYu Xiangning * Do an implicit bind if necessary. 90000f1702c5SYu Xiangning */ 90010f1702c5SYu Xiangning if (udp->udp_state == TS_UNBND) { 90020f1702c5SYu Xiangning error = udp_implicit_bind(connp, cr); 90030f1702c5SYu Xiangning /* 90040f1702c5SYu Xiangning * We could be racing with an actual bind, in which case 90050f1702c5SYu Xiangning * we would see EPROTO. We cross our fingers and try 90060f1702c5SYu Xiangning * to send. 90070f1702c5SYu Xiangning */ 90080f1702c5SYu Xiangning if (!(error == 0 || error == EPROTO)) { 90090f1702c5SYu Xiangning freemsg(mp); 90100f1702c5SYu Xiangning return (error); 90110f1702c5SYu Xiangning } 90120f1702c5SYu Xiangning } 9013f4b3ec61Sdh 90140f1702c5SYu Xiangning rw_enter(&udp->udp_rwlock, RW_WRITER); 9015f4b3ec61Sdh 90160f1702c5SYu Xiangning if (msg->msg_name != NULL && udp->udp_state == TS_DATA_XFER) { 90170f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 90180f1702c5SYu Xiangning freemsg(mp); 90190f1702c5SYu Xiangning return (EISCONN); 90200f1702c5SYu Xiangning } 9021f4b3ec61Sdh 9022f4b3ec61Sdh 90230f1702c5SYu Xiangning if (udp->udp_delayed_error != 0) { 90240f1702c5SYu Xiangning boolean_t match; 9025f4b3ec61Sdh 90260f1702c5SYu Xiangning error = udp->udp_delayed_error; 90270f1702c5SYu Xiangning match = B_FALSE; 90280f1702c5SYu Xiangning udp->udp_delayed_error = 0; 90290f1702c5SYu Xiangning switch (udp->udp_family) { 90300f1702c5SYu Xiangning case AF_INET: { 90310f1702c5SYu Xiangning /* Compare just IP address and port */ 90320f1702c5SYu Xiangning sin_t *sin1 = (sin_t *)msg->msg_name; 90330f1702c5SYu Xiangning sin_t *sin2 = (sin_t *)&udp->udp_delayed_addr; 9034f4b3ec61Sdh 90350f1702c5SYu Xiangning if (msg->msg_namelen == sizeof (sin_t) && 90360f1702c5SYu Xiangning sin1->sin_port == sin2->sin_port && 90370f1702c5SYu Xiangning sin1->sin_addr.s_addr == sin2->sin_addr.s_addr) 90380f1702c5SYu Xiangning match = B_TRUE; 90390f1702c5SYu Xiangning 90400f1702c5SYu Xiangning break; 90417c478bd9Sstevel@tonic-gate } 90420f1702c5SYu Xiangning case AF_INET6: { 90430f1702c5SYu Xiangning sin6_t *sin1 = (sin6_t *)msg->msg_name; 90440f1702c5SYu Xiangning sin6_t *sin2 = (sin6_t *)&udp->udp_delayed_addr; 9045ff550d0eSmasputra 90460f1702c5SYu Xiangning if (msg->msg_namelen == sizeof (sin6_t) && 90470f1702c5SYu Xiangning sin1->sin6_port == sin2->sin6_port && 90480f1702c5SYu Xiangning IN6_ARE_ADDR_EQUAL(&sin1->sin6_addr, 90490f1702c5SYu Xiangning &sin2->sin6_addr)) 90500f1702c5SYu Xiangning match = B_TRUE; 90510f1702c5SYu Xiangning break; 90520f1702c5SYu Xiangning } 90530f1702c5SYu Xiangning default: 90540f1702c5SYu Xiangning ASSERT(0); 90550f1702c5SYu Xiangning } 9056ff550d0eSmasputra 90570f1702c5SYu Xiangning *((sin6_t *)&udp->udp_delayed_addr) = sin6_null; 9058f4b3ec61Sdh 90590f1702c5SYu Xiangning if (match) { 90600f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 90610f1702c5SYu Xiangning freemsg(mp); 90620f1702c5SYu Xiangning return (error); 90630f1702c5SYu Xiangning } 90640f1702c5SYu Xiangning } 9065f4b3ec61Sdh 90660f1702c5SYu Xiangning error = proto_verify_ip_addr(udp->udp_family, 90670f1702c5SYu Xiangning (struct sockaddr *)msg->msg_name, msg->msg_namelen); 90680f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 90690f1702c5SYu Xiangning 90700f1702c5SYu Xiangning if (error != 0) { 90710f1702c5SYu Xiangning freemsg(mp); 90720f1702c5SYu Xiangning return (error); 90730f1702c5SYu Xiangning } 90740f1702c5SYu Xiangning 90750f1702c5SYu Xiangning error = udp_send_not_connected(connp, mp, 90760f1702c5SYu Xiangning (struct sockaddr *)msg->msg_name, msg->msg_namelen, msg, cr, 90770f1702c5SYu Xiangning curproc->p_pid); 90780f1702c5SYu Xiangning if (error != 0) { 90790f1702c5SYu Xiangning UDP_STAT(us, udp_out_err_output); 90800f1702c5SYu Xiangning freemsg(mp); 90810f1702c5SYu Xiangning } 90820f1702c5SYu Xiangning return (udp->udp_dgram_errind ? error : 0); 90837c478bd9Sstevel@tonic-gate } 90847c478bd9Sstevel@tonic-gate 90850f1702c5SYu Xiangning void 90860f1702c5SYu Xiangning udp_fallback(sock_lower_handle_t proto_handle, queue_t *q, 90870f1702c5SYu Xiangning boolean_t direct_sockfs, so_proto_quiesced_cb_t quiesced_cb) 90887c478bd9Sstevel@tonic-gate { 90890f1702c5SYu Xiangning conn_t *connp = (conn_t *)proto_handle; 90900f1702c5SYu Xiangning udp_t *udp; 90910f1702c5SYu Xiangning struct T_capability_ack tca; 90920f1702c5SYu Xiangning struct sockaddr_in6 laddr, faddr; 90930f1702c5SYu Xiangning socklen_t laddrlen, faddrlen; 90940f1702c5SYu Xiangning short opts; 90950f1702c5SYu Xiangning struct stroptions *stropt; 90960f1702c5SYu Xiangning mblk_t *stropt_mp; 90970f1702c5SYu Xiangning int error; 90987c478bd9Sstevel@tonic-gate 90990f1702c5SYu Xiangning udp = connp->conn_udp; 9100ff550d0eSmasputra 91010f1702c5SYu Xiangning stropt_mp = allocb_wait(sizeof (*stropt), BPRI_HI, STR_NOSIG, NULL); 9102ff550d0eSmasputra 91030f1702c5SYu Xiangning /* 91040f1702c5SYu Xiangning * setup the fallback stream that was allocated 91050f1702c5SYu Xiangning */ 91060f1702c5SYu Xiangning connp->conn_dev = (dev_t)RD(q)->q_ptr; 91070f1702c5SYu Xiangning connp->conn_minor_arena = WR(q)->q_ptr; 91087c478bd9Sstevel@tonic-gate 91090f1702c5SYu Xiangning RD(q)->q_ptr = WR(q)->q_ptr = connp; 9110f4b3ec61Sdh 91110f1702c5SYu Xiangning WR(q)->q_qinfo = &udp_winit; 9112f4b3ec61Sdh 91130f1702c5SYu Xiangning connp->conn_rq = RD(q); 91140f1702c5SYu Xiangning connp->conn_wq = WR(q); 9115f4b3ec61Sdh 91160f1702c5SYu Xiangning /* Notify stream head about options before sending up data */ 91170f1702c5SYu Xiangning stropt_mp->b_datap->db_type = M_SETOPTS; 91180f1702c5SYu Xiangning stropt_mp->b_wptr += sizeof (*stropt); 91190f1702c5SYu Xiangning stropt = (struct stroptions *)stropt_mp->b_rptr; 91200f1702c5SYu Xiangning stropt->so_flags = SO_WROFF | SO_HIWAT; 91210f1702c5SYu Xiangning stropt->so_wroff = 91220f1702c5SYu Xiangning (ushort_t)(udp->udp_max_hdr_len + udp->udp_us->us_wroff_extra); 91230f1702c5SYu Xiangning stropt->so_hiwat = udp->udp_rcv_disply_hiwat; 91240f1702c5SYu Xiangning putnext(RD(q), stropt_mp); 9125f4b3ec61Sdh 91260f1702c5SYu Xiangning /* 91270f1702c5SYu Xiangning * Free the helper stream 91280f1702c5SYu Xiangning */ 9129bfcb55b8SRao Shoaib ip_free_helper_stream(connp); 9130f4b3ec61Sdh 91310f1702c5SYu Xiangning if (!direct_sockfs) 91320f1702c5SYu Xiangning udp_disable_direct_sockfs(udp); 9133f4b3ec61Sdh 91340f1702c5SYu Xiangning /* 91350f1702c5SYu Xiangning * Collect the information needed to sync with the sonode 91360f1702c5SYu Xiangning */ 91370f1702c5SYu Xiangning udp_do_capability_ack(udp, &tca, TC1_INFO); 91380f1702c5SYu Xiangning 91390f1702c5SYu Xiangning laddrlen = faddrlen = sizeof (sin6_t); 91400f1702c5SYu Xiangning (void) udp_getsockname((sock_lower_handle_t)connp, 91410f1702c5SYu Xiangning (struct sockaddr *)&laddr, &laddrlen, NULL); 91420f1702c5SYu Xiangning error = udp_getpeername((sock_lower_handle_t)connp, 91430f1702c5SYu Xiangning (struct sockaddr *)&faddr, &faddrlen, NULL); 91440f1702c5SYu Xiangning if (error != 0) 91450f1702c5SYu Xiangning faddrlen = 0; 91460f1702c5SYu Xiangning 91470f1702c5SYu Xiangning opts = 0; 91480f1702c5SYu Xiangning if (udp->udp_dgram_errind) 91490f1702c5SYu Xiangning opts |= SO_DGRAM_ERRIND; 91500f1702c5SYu Xiangning if (udp->udp_dontroute) 91510f1702c5SYu Xiangning opts |= SO_DONTROUTE; 9152f4b3ec61Sdh 91530f1702c5SYu Xiangning /* 91540f1702c5SYu Xiangning * Once we grab the drain lock, no data will be send up 91550f1702c5SYu Xiangning * to the socket. So we notify the socket that the endpoint 91560f1702c5SYu Xiangning * is quiescent and it's therefore safe move data from 91570f1702c5SYu Xiangning * the socket to the stream head. 91580f1702c5SYu Xiangning */ 91590f1702c5SYu Xiangning (*quiesced_cb)(connp->conn_upper_handle, q, &tca, 91600f1702c5SYu Xiangning (struct sockaddr *)&laddr, laddrlen, 91610f1702c5SYu Xiangning (struct sockaddr *)&faddr, faddrlen, opts); 9162f4b3ec61Sdh 91630f1702c5SYu Xiangning /* 91640f1702c5SYu Xiangning * push up any packets that were queued in udp_t 91650f1702c5SYu Xiangning */ 91667c478bd9Sstevel@tonic-gate 91670f1702c5SYu Xiangning mutex_enter(&udp->udp_recv_lock); 91680f1702c5SYu Xiangning while (udp->udp_fallback_queue_head != NULL) { 91690f1702c5SYu Xiangning mblk_t *mp; 91700f1702c5SYu Xiangning mp = udp->udp_fallback_queue_head; 91710f1702c5SYu Xiangning udp->udp_fallback_queue_head = mp->b_next; 91720f1702c5SYu Xiangning mutex_exit(&udp->udp_recv_lock); 91730f1702c5SYu Xiangning mp->b_next = NULL; 91740f1702c5SYu Xiangning putnext(RD(q), mp); 91750f1702c5SYu Xiangning mutex_enter(&udp->udp_recv_lock); 9176f4b3ec61Sdh } 91770f1702c5SYu Xiangning udp->udp_fallback_queue_tail = udp->udp_fallback_queue_head; 91780f1702c5SYu Xiangning /* 91790f1702c5SYu Xiangning * No longer a streams less socket 91800f1702c5SYu Xiangning */ 91810f1702c5SYu Xiangning connp->conn_flags &= ~IPCL_NONSTR; 91820f1702c5SYu Xiangning mutex_exit(&udp->udp_recv_lock); 91830f1702c5SYu Xiangning 91840f1702c5SYu Xiangning ASSERT(connp->conn_ref >= 1); 9185f4b3ec61Sdh } 9186f4b3ec61Sdh 91870f1702c5SYu Xiangning static int 91880f1702c5SYu Xiangning udp_do_getpeername(udp_t *udp, struct sockaddr *sa, uint_t *salenp) 9189f4b3ec61Sdh { 91900f1702c5SYu Xiangning sin_t *sin = (sin_t *)sa; 91910f1702c5SYu Xiangning sin6_t *sin6 = (sin6_t *)sa; 91927c478bd9Sstevel@tonic-gate 91930f1702c5SYu Xiangning ASSERT(RW_LOCK_HELD(&udp->udp_rwlock)); 91940f1702c5SYu Xiangning ASSERT(udp != NULL); 9195ff550d0eSmasputra 91960f1702c5SYu Xiangning if (udp->udp_state != TS_DATA_XFER) 91970f1702c5SYu Xiangning return (ENOTCONN); 91987c478bd9Sstevel@tonic-gate 91990f1702c5SYu Xiangning switch (udp->udp_family) { 92000f1702c5SYu Xiangning case AF_INET: 92010f1702c5SYu Xiangning ASSERT(udp->udp_ipversion == IPV4_VERSION); 92027c478bd9Sstevel@tonic-gate 92030f1702c5SYu Xiangning if (*salenp < sizeof (sin_t)) 92040f1702c5SYu Xiangning return (EINVAL); 92057c478bd9Sstevel@tonic-gate 92060f1702c5SYu Xiangning *salenp = sizeof (sin_t); 92070f1702c5SYu Xiangning *sin = sin_null; 92080f1702c5SYu Xiangning sin->sin_family = AF_INET; 92090f1702c5SYu Xiangning sin->sin_port = udp->udp_dstport; 92100f1702c5SYu Xiangning sin->sin_addr.s_addr = V4_PART_OF_V6(udp->udp_v6dst); 92110f1702c5SYu Xiangning break; 92120f1702c5SYu Xiangning case AF_INET6: 92130f1702c5SYu Xiangning if (*salenp < sizeof (sin6_t)) 92140f1702c5SYu Xiangning return (EINVAL); 92157c478bd9Sstevel@tonic-gate 92160f1702c5SYu Xiangning *salenp = sizeof (sin6_t); 92170f1702c5SYu Xiangning *sin6 = sin6_null; 92180f1702c5SYu Xiangning sin6->sin6_family = AF_INET6; 92190f1702c5SYu Xiangning sin6->sin6_port = udp->udp_dstport; 92200f1702c5SYu Xiangning sin6->sin6_addr = udp->udp_v6dst; 92210f1702c5SYu Xiangning sin6->sin6_flowinfo = udp->udp_flowinfo; 92220f1702c5SYu Xiangning break; 92237c478bd9Sstevel@tonic-gate } 92240f1702c5SYu Xiangning 92250f1702c5SYu Xiangning return (0); 92267c478bd9Sstevel@tonic-gate } 92277c478bd9Sstevel@tonic-gate 92280f1702c5SYu Xiangning /* ARGSUSED */ 92290f1702c5SYu Xiangning int 92300f1702c5SYu Xiangning udp_getpeername(sock_lower_handle_t proto_handle, struct sockaddr *sa, 92310f1702c5SYu Xiangning socklen_t *salenp, cred_t *cr) 92327c478bd9Sstevel@tonic-gate { 92330f1702c5SYu Xiangning conn_t *connp = (conn_t *)proto_handle; 92340f1702c5SYu Xiangning udp_t *udp = connp->conn_udp; 92350f1702c5SYu Xiangning int error; 92367c478bd9Sstevel@tonic-gate 92370f1702c5SYu Xiangning ASSERT(udp != NULL); 92387c478bd9Sstevel@tonic-gate 92390f1702c5SYu Xiangning rw_enter(&udp->udp_rwlock, RW_READER); 92407c478bd9Sstevel@tonic-gate 92410f1702c5SYu Xiangning error = udp_do_getpeername(udp, sa, salenp); 92427c478bd9Sstevel@tonic-gate 92430f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 92440f1702c5SYu Xiangning 92450f1702c5SYu Xiangning return (error); 92467c478bd9Sstevel@tonic-gate } 92477c478bd9Sstevel@tonic-gate 9248ff550d0eSmasputra static int 92490f1702c5SYu Xiangning udp_do_getsockname(udp_t *udp, struct sockaddr *sa, uint_t *salenp) 9250ff550d0eSmasputra { 92510f1702c5SYu Xiangning sin_t *sin = (sin_t *)sa; 92520f1702c5SYu Xiangning sin6_t *sin6 = (sin6_t *)sa; 9253ff550d0eSmasputra 92540f1702c5SYu Xiangning ASSERT(udp != NULL); 92550f1702c5SYu Xiangning ASSERT(RW_LOCK_HELD(&udp->udp_rwlock)); 9256ff550d0eSmasputra 92570f1702c5SYu Xiangning switch (udp->udp_family) { 92580f1702c5SYu Xiangning case AF_INET: 92590f1702c5SYu Xiangning ASSERT(udp->udp_ipversion == IPV4_VERSION); 9260ff550d0eSmasputra 92610f1702c5SYu Xiangning if (*salenp < sizeof (sin_t)) 92620f1702c5SYu Xiangning return (EINVAL); 9263ff550d0eSmasputra 92640f1702c5SYu Xiangning *salenp = sizeof (sin_t); 92650f1702c5SYu Xiangning *sin = sin_null; 92660f1702c5SYu Xiangning sin->sin_family = AF_INET; 92670f1702c5SYu Xiangning if (udp->udp_state == TS_UNBND) { 92680f1702c5SYu Xiangning break; 92690f1702c5SYu Xiangning } 92700f1702c5SYu Xiangning sin->sin_port = udp->udp_port; 92710f1702c5SYu Xiangning 92720f1702c5SYu Xiangning if (!IN6_IS_ADDR_V4MAPPED_ANY(&udp->udp_v6src) && 92730f1702c5SYu Xiangning !IN6_IS_ADDR_UNSPECIFIED(&udp->udp_v6src)) { 92740f1702c5SYu Xiangning sin->sin_addr.s_addr = V4_PART_OF_V6(udp->udp_v6src); 92750f1702c5SYu Xiangning } else { 92760f1702c5SYu Xiangning /* 92770f1702c5SYu Xiangning * INADDR_ANY 92780f1702c5SYu Xiangning * udp_v6src is not set, we might be bound to 92790f1702c5SYu Xiangning * broadcast/multicast. Use udp_bound_v6src as 92800f1702c5SYu Xiangning * local address instead (that could 92810f1702c5SYu Xiangning * also still be INADDR_ANY) 92820f1702c5SYu Xiangning */ 92830f1702c5SYu Xiangning sin->sin_addr.s_addr = 92840f1702c5SYu Xiangning V4_PART_OF_V6(udp->udp_bound_v6src); 92850f1702c5SYu Xiangning } 92860f1702c5SYu Xiangning break; 92870f1702c5SYu Xiangning 92880f1702c5SYu Xiangning case AF_INET6: 92890f1702c5SYu Xiangning if (*salenp < sizeof (sin6_t)) 92900f1702c5SYu Xiangning return (EINVAL); 92910f1702c5SYu Xiangning 92920f1702c5SYu Xiangning *salenp = sizeof (sin6_t); 92930f1702c5SYu Xiangning *sin6 = sin6_null; 92940f1702c5SYu Xiangning sin6->sin6_family = AF_INET6; 92950f1702c5SYu Xiangning if (udp->udp_state == TS_UNBND) { 92960f1702c5SYu Xiangning break; 92970f1702c5SYu Xiangning } 92980f1702c5SYu Xiangning sin6->sin6_port = udp->udp_port; 92990f1702c5SYu Xiangning 93000f1702c5SYu Xiangning if (!IN6_IS_ADDR_UNSPECIFIED(&udp->udp_v6src)) { 93010f1702c5SYu Xiangning sin6->sin6_addr = udp->udp_v6src; 93020f1702c5SYu Xiangning } else { 93030f1702c5SYu Xiangning /* 93040f1702c5SYu Xiangning * UNSPECIFIED 93050f1702c5SYu Xiangning * udp_v6src is not set, we might be bound to 93060f1702c5SYu Xiangning * broadcast/multicast. Use udp_bound_v6src as 93070f1702c5SYu Xiangning * local address instead (that could 93080f1702c5SYu Xiangning * also still be UNSPECIFIED) 93090f1702c5SYu Xiangning */ 93100f1702c5SYu Xiangning sin6->sin6_addr = udp->udp_bound_v6src; 9311ff550d0eSmasputra } 9312ff550d0eSmasputra } 93130f1702c5SYu Xiangning return (0); 93140f1702c5SYu Xiangning } 9315ff550d0eSmasputra 93160f1702c5SYu Xiangning /* ARGSUSED */ 93170f1702c5SYu Xiangning int 93180f1702c5SYu Xiangning udp_getsockname(sock_lower_handle_t proto_handle, struct sockaddr *sa, 93190f1702c5SYu Xiangning socklen_t *salenp, cred_t *cr) 93200f1702c5SYu Xiangning { 93210f1702c5SYu Xiangning conn_t *connp = (conn_t *)proto_handle; 93220f1702c5SYu Xiangning udp_t *udp = connp->conn_udp; 93230f1702c5SYu Xiangning int error; 93240f1702c5SYu Xiangning 93250f1702c5SYu Xiangning ASSERT(udp != NULL); 93260f1702c5SYu Xiangning rw_enter(&udp->udp_rwlock, RW_READER); 93270f1702c5SYu Xiangning 93280f1702c5SYu Xiangning error = udp_do_getsockname(udp, sa, salenp); 93290f1702c5SYu Xiangning 93300f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 9331ff550d0eSmasputra 9332ff550d0eSmasputra return (error); 9333ff550d0eSmasputra } 9334ff550d0eSmasputra 93350f1702c5SYu Xiangning int 93360f1702c5SYu Xiangning udp_getsockopt(sock_lower_handle_t proto_handle, int level, int option_name, 93370f1702c5SYu Xiangning void *optvalp, socklen_t *optlen, cred_t *cr) 9338ff550d0eSmasputra { 93390f1702c5SYu Xiangning conn_t *connp = (conn_t *)proto_handle; 93400f1702c5SYu Xiangning udp_t *udp = connp->conn_udp; 93410f1702c5SYu Xiangning int error; 93420f1702c5SYu Xiangning t_uscalar_t max_optbuf_len; 93430f1702c5SYu Xiangning void *optvalp_buf; 93440f1702c5SYu Xiangning int len; 93450f1702c5SYu Xiangning 93460f1702c5SYu Xiangning error = proto_opt_check(level, option_name, *optlen, &max_optbuf_len, 93470f1702c5SYu Xiangning udp_opt_obj.odb_opt_des_arr, 93480f1702c5SYu Xiangning udp_opt_obj.odb_opt_arr_cnt, 93490f1702c5SYu Xiangning udp_opt_obj.odb_topmost_tpiprovider, 93500f1702c5SYu Xiangning B_FALSE, B_TRUE, cr); 93510f1702c5SYu Xiangning if (error != 0) { 93520f1702c5SYu Xiangning if (error < 0) 93530f1702c5SYu Xiangning error = proto_tlitosyserr(-error); 93540f1702c5SYu Xiangning return (error); 9355ff550d0eSmasputra } 9356ff550d0eSmasputra 93570f1702c5SYu Xiangning optvalp_buf = kmem_alloc(max_optbuf_len, KM_SLEEP); 93580f1702c5SYu Xiangning rw_enter(&udp->udp_rwlock, RW_READER); 93590f1702c5SYu Xiangning len = udp_opt_get(connp, level, option_name, optvalp_buf); 93600f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 9361ff550d0eSmasputra 93620f1702c5SYu Xiangning if (len < 0) { 9363ff550d0eSmasputra /* 93640f1702c5SYu Xiangning * Pass on to IP 9365ff550d0eSmasputra */ 93660f1702c5SYu Xiangning kmem_free(optvalp_buf, max_optbuf_len); 93670f1702c5SYu Xiangning return (ip_get_options(connp, level, option_name, 93680f1702c5SYu Xiangning optvalp, optlen, cr)); 9369ff550d0eSmasputra } else { 9370ff550d0eSmasputra /* 93710f1702c5SYu Xiangning * update optlen and copy option value 9372ff550d0eSmasputra */ 93730f1702c5SYu Xiangning t_uscalar_t size = MIN(len, *optlen); 93740f1702c5SYu Xiangning bcopy(optvalp_buf, optvalp, size); 93750f1702c5SYu Xiangning bcopy(&size, optlen, sizeof (size)); 9376ff550d0eSmasputra 93770f1702c5SYu Xiangning kmem_free(optvalp_buf, max_optbuf_len); 93780f1702c5SYu Xiangning return (0); 93790f1702c5SYu Xiangning } 9380ff550d0eSmasputra } 9381ff550d0eSmasputra 93820f1702c5SYu Xiangning int 93830f1702c5SYu Xiangning udp_setsockopt(sock_lower_handle_t proto_handle, int level, int option_name, 93840f1702c5SYu Xiangning const void *optvalp, socklen_t optlen, cred_t *cr) 9385ff550d0eSmasputra { 93860f1702c5SYu Xiangning conn_t *connp = (conn_t *)proto_handle; 93870f1702c5SYu Xiangning udp_t *udp = connp->conn_udp; 93880f1702c5SYu Xiangning int error; 9389ff550d0eSmasputra 93900f1702c5SYu Xiangning error = proto_opt_check(level, option_name, optlen, NULL, 93910f1702c5SYu Xiangning udp_opt_obj.odb_opt_des_arr, 93920f1702c5SYu Xiangning udp_opt_obj.odb_opt_arr_cnt, 93930f1702c5SYu Xiangning udp_opt_obj.odb_topmost_tpiprovider, 93940f1702c5SYu Xiangning B_TRUE, B_FALSE, cr); 9395ff550d0eSmasputra 93960f1702c5SYu Xiangning if (error != 0) { 93970f1702c5SYu Xiangning if (error < 0) 93980f1702c5SYu Xiangning error = proto_tlitosyserr(-error); 93990f1702c5SYu Xiangning return (error); 94000f1702c5SYu Xiangning } 9401ff550d0eSmasputra 94020f1702c5SYu Xiangning rw_enter(&udp->udp_rwlock, RW_WRITER); 94030f1702c5SYu Xiangning error = udp_opt_set(connp, SETFN_OPTCOM_NEGOTIATE, level, option_name, 94040f1702c5SYu Xiangning optlen, (uchar_t *)optvalp, (uint_t *)&optlen, (uchar_t *)optvalp, 94050f1702c5SYu Xiangning NULL, cr); 94060f1702c5SYu Xiangning rw_exit(&udp->udp_rwlock); 9407ff550d0eSmasputra 94080f1702c5SYu Xiangning if (error < 0) { 94090f1702c5SYu Xiangning /* 94100f1702c5SYu Xiangning * Pass on to ip 94110f1702c5SYu Xiangning */ 94120f1702c5SYu Xiangning error = ip_set_options(connp, level, option_name, optvalp, 94130f1702c5SYu Xiangning optlen, cr); 9414ff550d0eSmasputra } 94150f1702c5SYu Xiangning 94160f1702c5SYu Xiangning return (error); 9417ff550d0eSmasputra } 9418ff550d0eSmasputra 94190f1702c5SYu Xiangning void 94200f1702c5SYu Xiangning udp_clr_flowctrl(sock_lower_handle_t proto_handle) 9421ff550d0eSmasputra { 94220f1702c5SYu Xiangning conn_t *connp = (conn_t *)proto_handle; 94230f1702c5SYu Xiangning udp_t *udp = connp->conn_udp; 9424f4b3ec61Sdh 94250f1702c5SYu Xiangning mutex_enter(&udp->udp_recv_lock); 94260f1702c5SYu Xiangning connp->conn_flow_cntrld = B_FALSE; 94270f1702c5SYu Xiangning mutex_exit(&udp->udp_recv_lock); 94280f1702c5SYu Xiangning } 9429ff550d0eSmasputra 94300f1702c5SYu Xiangning /* ARGSUSED */ 94310f1702c5SYu Xiangning int 94320f1702c5SYu Xiangning udp_shutdown(sock_lower_handle_t proto_handle, int how, cred_t *cr) 94330f1702c5SYu Xiangning { 94340f1702c5SYu Xiangning conn_t *connp = (conn_t *)proto_handle; 94350f1702c5SYu Xiangning 94360f1702c5SYu Xiangning /* shut down the send side */ 94370f1702c5SYu Xiangning if (how != SHUT_RD) 94380f1702c5SYu Xiangning (*connp->conn_upcalls->su_opctl)(connp->conn_upper_handle, 94390f1702c5SYu Xiangning SOCK_OPCTL_SHUT_SEND, 0); 94400f1702c5SYu Xiangning /* shut down the recv side */ 94410f1702c5SYu Xiangning if (how != SHUT_WR) 94420f1702c5SYu Xiangning (*connp->conn_upcalls->su_opctl)(connp->conn_upper_handle, 94430f1702c5SYu Xiangning SOCK_OPCTL_SHUT_RECV, 0); 94440f1702c5SYu Xiangning return (0); 9445ff550d0eSmasputra } 9446fc80c0dfSnordmark 94470f1702c5SYu Xiangning int 94480f1702c5SYu Xiangning udp_ioctl(sock_lower_handle_t proto_handle, int cmd, intptr_t arg, 94490f1702c5SYu Xiangning int mode, int32_t *rvalp, cred_t *cr) 9450fc80c0dfSnordmark { 94510f1702c5SYu Xiangning conn_t *connp = (conn_t *)proto_handle; 94520f1702c5SYu Xiangning int error; 9453fc80c0dfSnordmark 94540f1702c5SYu Xiangning switch (cmd) { 94550f1702c5SYu Xiangning case ND_SET: 94560f1702c5SYu Xiangning case ND_GET: 94570f1702c5SYu Xiangning case _SIOCSOCKFALLBACK: 94580f1702c5SYu Xiangning case TI_GETPEERNAME: 94590f1702c5SYu Xiangning case TI_GETMYNAME: 94600f1702c5SYu Xiangning ip1dbg(("udp_ioctl: cmd 0x%x on non streams socket", 94610f1702c5SYu Xiangning cmd)); 94620f1702c5SYu Xiangning error = EINVAL; 94630f1702c5SYu Xiangning break; 94640f1702c5SYu Xiangning default: 94650f1702c5SYu Xiangning /* 94660f1702c5SYu Xiangning * Pass on to IP using helper stream 94670f1702c5SYu Xiangning */ 946819a8a986SRao Shoaib error = ldi_ioctl(connp->conn_helper_info->iphs_handle, 94690f1702c5SYu Xiangning cmd, arg, mode, cr, rvalp); 94700f1702c5SYu Xiangning break; 9471fc80c0dfSnordmark } 94720f1702c5SYu Xiangning return (error); 9473fc80c0dfSnordmark } 9474fc80c0dfSnordmark 9475fc80c0dfSnordmark /* ARGSUSED */ 94760f1702c5SYu Xiangning int 94770f1702c5SYu Xiangning udp_accept(sock_lower_handle_t lproto_handle, 94780f1702c5SYu Xiangning sock_lower_handle_t eproto_handle, sock_upper_handle_t sock_handle, 94790f1702c5SYu Xiangning cred_t *cr) 9480fc80c0dfSnordmark { 94810f1702c5SYu Xiangning return (EOPNOTSUPP); 94820f1702c5SYu Xiangning } 94830f1702c5SYu Xiangning 94840f1702c5SYu Xiangning /* ARGSUSED */ 94850f1702c5SYu Xiangning int 94860f1702c5SYu Xiangning udp_listen(sock_lower_handle_t proto_handle, int backlog, cred_t *cr) 94870f1702c5SYu Xiangning { 94880f1702c5SYu Xiangning return (EOPNOTSUPP); 9489fc80c0dfSnordmark } 94900f1702c5SYu Xiangning 94910f1702c5SYu Xiangning sock_downcalls_t sock_udp_downcalls = { 94920f1702c5SYu Xiangning udp_activate, /* sd_activate */ 94930f1702c5SYu Xiangning udp_accept, /* sd_accept */ 94940f1702c5SYu Xiangning udp_bind, /* sd_bind */ 94950f1702c5SYu Xiangning udp_listen, /* sd_listen */ 94960f1702c5SYu Xiangning udp_connect, /* sd_connect */ 94970f1702c5SYu Xiangning udp_getpeername, /* sd_getpeername */ 94980f1702c5SYu Xiangning udp_getsockname, /* sd_getsockname */ 94990f1702c5SYu Xiangning udp_getsockopt, /* sd_getsockopt */ 95000f1702c5SYu Xiangning udp_setsockopt, /* sd_setsockopt */ 95010f1702c5SYu Xiangning udp_send, /* sd_send */ 95020f1702c5SYu Xiangning NULL, /* sd_send_uio */ 95030f1702c5SYu Xiangning NULL, /* sd_recv_uio */ 95040f1702c5SYu Xiangning NULL, /* sd_poll */ 95050f1702c5SYu Xiangning udp_shutdown, /* sd_shutdown */ 95060f1702c5SYu Xiangning udp_clr_flowctrl, /* sd_setflowctrl */ 95070f1702c5SYu Xiangning udp_ioctl, /* sd_ioctl */ 95080f1702c5SYu Xiangning udp_close /* sd_close */ 95090f1702c5SYu Xiangning }; 9510