18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * net/dccp/proto.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * An implementation of the DCCP protocol 68c2ecf20Sopenharmony_ci * Arnaldo Carvalho de Melo <acme@conectiva.com.br> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/dccp.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/types.h> 128c2ecf20Sopenharmony_ci#include <linux/sched.h> 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 158c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 168c2ecf20Sopenharmony_ci#include <linux/in.h> 178c2ecf20Sopenharmony_ci#include <linux/if_arp.h> 188c2ecf20Sopenharmony_ci#include <linux/init.h> 198c2ecf20Sopenharmony_ci#include <linux/random.h> 208c2ecf20Sopenharmony_ci#include <linux/slab.h> 218c2ecf20Sopenharmony_ci#include <net/checksum.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include <net/inet_sock.h> 248c2ecf20Sopenharmony_ci#include <net/inet_common.h> 258c2ecf20Sopenharmony_ci#include <net/sock.h> 268c2ecf20Sopenharmony_ci#include <net/xfrm.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include <asm/ioctls.h> 298c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 308c2ecf20Sopenharmony_ci#include <linux/timer.h> 318c2ecf20Sopenharmony_ci#include <linux/delay.h> 328c2ecf20Sopenharmony_ci#include <linux/poll.h> 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#include "ccid.h" 358c2ecf20Sopenharmony_ci#include "dccp.h" 368c2ecf20Sopenharmony_ci#include "feat.h" 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define CREATE_TRACE_POINTS 398c2ecf20Sopenharmony_ci#include "trace.h" 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ciDEFINE_SNMP_STAT(struct dccp_mib, dccp_statistics) __read_mostly; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_statistics); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ciDEFINE_PER_CPU(unsigned int, dccp_orphan_count); 468c2ecf20Sopenharmony_ciEXPORT_PER_CPU_SYMBOL_GPL(dccp_orphan_count); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistruct inet_hashinfo dccp_hashinfo; 498c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_hashinfo); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* the maximum queue length for tx in packets. 0 is no limit */ 528c2ecf20Sopenharmony_ciint sysctl_dccp_tx_qlen __read_mostly = 5; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_DCCP_DEBUG 558c2ecf20Sopenharmony_cistatic const char *dccp_state_name(const int state) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci static const char *const dccp_state_names[] = { 588c2ecf20Sopenharmony_ci [DCCP_OPEN] = "OPEN", 598c2ecf20Sopenharmony_ci [DCCP_REQUESTING] = "REQUESTING", 608c2ecf20Sopenharmony_ci [DCCP_PARTOPEN] = "PARTOPEN", 618c2ecf20Sopenharmony_ci [DCCP_LISTEN] = "LISTEN", 628c2ecf20Sopenharmony_ci [DCCP_RESPOND] = "RESPOND", 638c2ecf20Sopenharmony_ci [DCCP_CLOSING] = "CLOSING", 648c2ecf20Sopenharmony_ci [DCCP_ACTIVE_CLOSEREQ] = "CLOSEREQ", 658c2ecf20Sopenharmony_ci [DCCP_PASSIVE_CLOSE] = "PASSIVE_CLOSE", 668c2ecf20Sopenharmony_ci [DCCP_PASSIVE_CLOSEREQ] = "PASSIVE_CLOSEREQ", 678c2ecf20Sopenharmony_ci [DCCP_TIME_WAIT] = "TIME_WAIT", 688c2ecf20Sopenharmony_ci [DCCP_CLOSED] = "CLOSED", 698c2ecf20Sopenharmony_ci }; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci if (state >= DCCP_MAX_STATES) 728c2ecf20Sopenharmony_ci return "INVALID STATE!"; 738c2ecf20Sopenharmony_ci else 748c2ecf20Sopenharmony_ci return dccp_state_names[state]; 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci#endif 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_civoid dccp_set_state(struct sock *sk, const int state) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci const int oldstate = sk->sk_state; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci dccp_pr_debug("%s(%p) %s --> %s\n", dccp_role(sk), sk, 838c2ecf20Sopenharmony_ci dccp_state_name(oldstate), dccp_state_name(state)); 848c2ecf20Sopenharmony_ci WARN_ON(state == oldstate); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci switch (state) { 878c2ecf20Sopenharmony_ci case DCCP_OPEN: 888c2ecf20Sopenharmony_ci if (oldstate != DCCP_OPEN) 898c2ecf20Sopenharmony_ci DCCP_INC_STATS(DCCP_MIB_CURRESTAB); 908c2ecf20Sopenharmony_ci /* Client retransmits all Confirm options until entering OPEN */ 918c2ecf20Sopenharmony_ci if (oldstate == DCCP_PARTOPEN) 928c2ecf20Sopenharmony_ci dccp_feat_list_purge(&dccp_sk(sk)->dccps_featneg); 938c2ecf20Sopenharmony_ci break; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci case DCCP_CLOSED: 968c2ecf20Sopenharmony_ci if (oldstate == DCCP_OPEN || oldstate == DCCP_ACTIVE_CLOSEREQ || 978c2ecf20Sopenharmony_ci oldstate == DCCP_CLOSING) 988c2ecf20Sopenharmony_ci DCCP_INC_STATS(DCCP_MIB_ESTABRESETS); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci sk->sk_prot->unhash(sk); 1018c2ecf20Sopenharmony_ci if (inet_csk(sk)->icsk_bind_hash != NULL && 1028c2ecf20Sopenharmony_ci !(sk->sk_userlocks & SOCK_BINDPORT_LOCK)) 1038c2ecf20Sopenharmony_ci inet_put_port(sk); 1048c2ecf20Sopenharmony_ci fallthrough; 1058c2ecf20Sopenharmony_ci default: 1068c2ecf20Sopenharmony_ci if (oldstate == DCCP_OPEN) 1078c2ecf20Sopenharmony_ci DCCP_DEC_STATS(DCCP_MIB_CURRESTAB); 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci /* Change state AFTER socket is unhashed to avoid closed 1118c2ecf20Sopenharmony_ci * socket sitting in hash tables. 1128c2ecf20Sopenharmony_ci */ 1138c2ecf20Sopenharmony_ci inet_sk_set_state(sk, state); 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_set_state); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic void dccp_finish_passive_close(struct sock *sk) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci switch (sk->sk_state) { 1218c2ecf20Sopenharmony_ci case DCCP_PASSIVE_CLOSE: 1228c2ecf20Sopenharmony_ci /* Node (client or server) has received Close packet. */ 1238c2ecf20Sopenharmony_ci dccp_send_reset(sk, DCCP_RESET_CODE_CLOSED); 1248c2ecf20Sopenharmony_ci dccp_set_state(sk, DCCP_CLOSED); 1258c2ecf20Sopenharmony_ci break; 1268c2ecf20Sopenharmony_ci case DCCP_PASSIVE_CLOSEREQ: 1278c2ecf20Sopenharmony_ci /* 1288c2ecf20Sopenharmony_ci * Client received CloseReq. We set the `active' flag so that 1298c2ecf20Sopenharmony_ci * dccp_send_close() retransmits the Close as per RFC 4340, 8.3. 1308c2ecf20Sopenharmony_ci */ 1318c2ecf20Sopenharmony_ci dccp_send_close(sk, 1); 1328c2ecf20Sopenharmony_ci dccp_set_state(sk, DCCP_CLOSING); 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_civoid dccp_done(struct sock *sk) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci dccp_set_state(sk, DCCP_CLOSED); 1398c2ecf20Sopenharmony_ci dccp_clear_xmit_timers(sk); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci sk->sk_shutdown = SHUTDOWN_MASK; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (!sock_flag(sk, SOCK_DEAD)) 1448c2ecf20Sopenharmony_ci sk->sk_state_change(sk); 1458c2ecf20Sopenharmony_ci else 1468c2ecf20Sopenharmony_ci inet_csk_destroy_sock(sk); 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_done); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ciconst char *dccp_packet_name(const int type) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci static const char *const dccp_packet_names[] = { 1548c2ecf20Sopenharmony_ci [DCCP_PKT_REQUEST] = "REQUEST", 1558c2ecf20Sopenharmony_ci [DCCP_PKT_RESPONSE] = "RESPONSE", 1568c2ecf20Sopenharmony_ci [DCCP_PKT_DATA] = "DATA", 1578c2ecf20Sopenharmony_ci [DCCP_PKT_ACK] = "ACK", 1588c2ecf20Sopenharmony_ci [DCCP_PKT_DATAACK] = "DATAACK", 1598c2ecf20Sopenharmony_ci [DCCP_PKT_CLOSEREQ] = "CLOSEREQ", 1608c2ecf20Sopenharmony_ci [DCCP_PKT_CLOSE] = "CLOSE", 1618c2ecf20Sopenharmony_ci [DCCP_PKT_RESET] = "RESET", 1628c2ecf20Sopenharmony_ci [DCCP_PKT_SYNC] = "SYNC", 1638c2ecf20Sopenharmony_ci [DCCP_PKT_SYNCACK] = "SYNCACK", 1648c2ecf20Sopenharmony_ci }; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci if (type >= DCCP_NR_PKT_TYPES) 1678c2ecf20Sopenharmony_ci return "INVALID"; 1688c2ecf20Sopenharmony_ci else 1698c2ecf20Sopenharmony_ci return dccp_packet_names[type]; 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_packet_name); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_civoid dccp_destruct_common(struct sock *sk) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci struct dccp_sock *dp = dccp_sk(sk); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk); 1798c2ecf20Sopenharmony_ci dp->dccps_hc_tx_ccid = NULL; 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_destruct_common); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic void dccp_sk_destruct(struct sock *sk) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci dccp_destruct_common(sk); 1868c2ecf20Sopenharmony_ci inet_sock_destruct(sk); 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ciint dccp_init_sock(struct sock *sk, const __u8 ctl_sock_initialized) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci struct dccp_sock *dp = dccp_sk(sk); 1928c2ecf20Sopenharmony_ci struct inet_connection_sock *icsk = inet_csk(sk); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci icsk->icsk_rto = DCCP_TIMEOUT_INIT; 1958c2ecf20Sopenharmony_ci icsk->icsk_syn_retries = sysctl_dccp_request_retries; 1968c2ecf20Sopenharmony_ci sk->sk_state = DCCP_CLOSED; 1978c2ecf20Sopenharmony_ci sk->sk_write_space = dccp_write_space; 1988c2ecf20Sopenharmony_ci sk->sk_destruct = dccp_sk_destruct; 1998c2ecf20Sopenharmony_ci icsk->icsk_sync_mss = dccp_sync_mss; 2008c2ecf20Sopenharmony_ci dp->dccps_mss_cache = 536; 2018c2ecf20Sopenharmony_ci dp->dccps_rate_last = jiffies; 2028c2ecf20Sopenharmony_ci dp->dccps_role = DCCP_ROLE_UNDEFINED; 2038c2ecf20Sopenharmony_ci dp->dccps_service = DCCP_SERVICE_CODE_IS_ABSENT; 2048c2ecf20Sopenharmony_ci dp->dccps_tx_qlen = sysctl_dccp_tx_qlen; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci dccp_init_xmit_timers(sk); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dp->dccps_featneg); 2098c2ecf20Sopenharmony_ci /* control socket doesn't need feat nego */ 2108c2ecf20Sopenharmony_ci if (likely(ctl_sock_initialized)) 2118c2ecf20Sopenharmony_ci return dccp_feat_init(sk); 2128c2ecf20Sopenharmony_ci return 0; 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_init_sock); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_civoid dccp_destroy_sock(struct sock *sk) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci struct dccp_sock *dp = dccp_sk(sk); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci __skb_queue_purge(&sk->sk_write_queue); 2228c2ecf20Sopenharmony_ci if (sk->sk_send_head != NULL) { 2238c2ecf20Sopenharmony_ci kfree_skb(sk->sk_send_head); 2248c2ecf20Sopenharmony_ci sk->sk_send_head = NULL; 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci /* Clean up a referenced DCCP bind bucket. */ 2288c2ecf20Sopenharmony_ci if (inet_csk(sk)->icsk_bind_hash != NULL) 2298c2ecf20Sopenharmony_ci inet_put_port(sk); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci kfree(dp->dccps_service_list); 2328c2ecf20Sopenharmony_ci dp->dccps_service_list = NULL; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci if (dp->dccps_hc_rx_ackvec != NULL) { 2358c2ecf20Sopenharmony_ci dccp_ackvec_free(dp->dccps_hc_rx_ackvec); 2368c2ecf20Sopenharmony_ci dp->dccps_hc_rx_ackvec = NULL; 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk); 2398c2ecf20Sopenharmony_ci dp->dccps_hc_rx_ccid = NULL; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci /* clean up feature negotiation state */ 2428c2ecf20Sopenharmony_ci dccp_feat_list_purge(&dp->dccps_featneg); 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_destroy_sock); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic inline int dccp_listen_start(struct sock *sk, int backlog) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci struct dccp_sock *dp = dccp_sk(sk); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci dp->dccps_role = DCCP_ROLE_LISTEN; 2528c2ecf20Sopenharmony_ci /* do not start to listen if feature negotiation setup fails */ 2538c2ecf20Sopenharmony_ci if (dccp_feat_finalise_settings(dp)) 2548c2ecf20Sopenharmony_ci return -EPROTO; 2558c2ecf20Sopenharmony_ci return inet_csk_listen_start(sk, backlog); 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic inline int dccp_need_reset(int state) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci return state != DCCP_CLOSED && state != DCCP_LISTEN && 2618c2ecf20Sopenharmony_ci state != DCCP_REQUESTING; 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ciint dccp_disconnect(struct sock *sk, int flags) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci struct inet_connection_sock *icsk = inet_csk(sk); 2678c2ecf20Sopenharmony_ci struct inet_sock *inet = inet_sk(sk); 2688c2ecf20Sopenharmony_ci struct dccp_sock *dp = dccp_sk(sk); 2698c2ecf20Sopenharmony_ci const int old_state = sk->sk_state; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci if (old_state != DCCP_CLOSED) 2728c2ecf20Sopenharmony_ci dccp_set_state(sk, DCCP_CLOSED); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci /* 2758c2ecf20Sopenharmony_ci * This corresponds to the ABORT function of RFC793, sec. 3.8 2768c2ecf20Sopenharmony_ci * TCP uses a RST segment, DCCP a Reset packet with Code 2, "Aborted". 2778c2ecf20Sopenharmony_ci */ 2788c2ecf20Sopenharmony_ci if (old_state == DCCP_LISTEN) { 2798c2ecf20Sopenharmony_ci inet_csk_listen_stop(sk); 2808c2ecf20Sopenharmony_ci } else if (dccp_need_reset(old_state)) { 2818c2ecf20Sopenharmony_ci dccp_send_reset(sk, DCCP_RESET_CODE_ABORTED); 2828c2ecf20Sopenharmony_ci sk->sk_err = ECONNRESET; 2838c2ecf20Sopenharmony_ci } else if (old_state == DCCP_REQUESTING) 2848c2ecf20Sopenharmony_ci sk->sk_err = ECONNRESET; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci dccp_clear_xmit_timers(sk); 2878c2ecf20Sopenharmony_ci ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk); 2888c2ecf20Sopenharmony_ci dp->dccps_hc_rx_ccid = NULL; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci __skb_queue_purge(&sk->sk_receive_queue); 2918c2ecf20Sopenharmony_ci __skb_queue_purge(&sk->sk_write_queue); 2928c2ecf20Sopenharmony_ci if (sk->sk_send_head != NULL) { 2938c2ecf20Sopenharmony_ci __kfree_skb(sk->sk_send_head); 2948c2ecf20Sopenharmony_ci sk->sk_send_head = NULL; 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci inet->inet_dport = 0; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci if (!(sk->sk_userlocks & SOCK_BINDADDR_LOCK)) 3008c2ecf20Sopenharmony_ci inet_reset_saddr(sk); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci sk->sk_shutdown = 0; 3038c2ecf20Sopenharmony_ci sock_reset_flag(sk, SOCK_DONE); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci icsk->icsk_backoff = 0; 3068c2ecf20Sopenharmony_ci inet_csk_delack_init(sk); 3078c2ecf20Sopenharmony_ci __sk_dst_reset(sk); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci WARN_ON(inet->inet_num && !icsk->icsk_bind_hash); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci sk->sk_error_report(sk); 3128c2ecf20Sopenharmony_ci return 0; 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_disconnect); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci/* 3188c2ecf20Sopenharmony_ci * Wait for a DCCP event. 3198c2ecf20Sopenharmony_ci * 3208c2ecf20Sopenharmony_ci * Note that we don't need to lock the socket, as the upper poll layers 3218c2ecf20Sopenharmony_ci * take care of normal races (between the test and the event) and we don't 3228c2ecf20Sopenharmony_ci * go look at any of the socket buffers directly. 3238c2ecf20Sopenharmony_ci */ 3248c2ecf20Sopenharmony_ci__poll_t dccp_poll(struct file *file, struct socket *sock, 3258c2ecf20Sopenharmony_ci poll_table *wait) 3268c2ecf20Sopenharmony_ci{ 3278c2ecf20Sopenharmony_ci struct sock *sk = sock->sk; 3288c2ecf20Sopenharmony_ci __poll_t mask; 3298c2ecf20Sopenharmony_ci u8 shutdown; 3308c2ecf20Sopenharmony_ci int state; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci sock_poll_wait(file, sock, wait); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci state = inet_sk_state_load(sk); 3358c2ecf20Sopenharmony_ci if (state == DCCP_LISTEN) 3368c2ecf20Sopenharmony_ci return inet_csk_listen_poll(sk); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci /* Socket is not locked. We are protected from async events 3398c2ecf20Sopenharmony_ci by poll logic and correct handling of state changes 3408c2ecf20Sopenharmony_ci made by another threads is impossible in any case. 3418c2ecf20Sopenharmony_ci */ 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci mask = 0; 3448c2ecf20Sopenharmony_ci if (READ_ONCE(sk->sk_err)) 3458c2ecf20Sopenharmony_ci mask = EPOLLERR; 3468c2ecf20Sopenharmony_ci shutdown = READ_ONCE(sk->sk_shutdown); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci if (shutdown == SHUTDOWN_MASK || state == DCCP_CLOSED) 3498c2ecf20Sopenharmony_ci mask |= EPOLLHUP; 3508c2ecf20Sopenharmony_ci if (shutdown & RCV_SHUTDOWN) 3518c2ecf20Sopenharmony_ci mask |= EPOLLIN | EPOLLRDNORM | EPOLLRDHUP; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci /* Connected? */ 3548c2ecf20Sopenharmony_ci if ((1 << state) & ~(DCCPF_REQUESTING | DCCPF_RESPOND)) { 3558c2ecf20Sopenharmony_ci if (atomic_read(&sk->sk_rmem_alloc) > 0) 3568c2ecf20Sopenharmony_ci mask |= EPOLLIN | EPOLLRDNORM; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci if (!(shutdown & SEND_SHUTDOWN)) { 3598c2ecf20Sopenharmony_ci if (sk_stream_is_writeable(sk)) { 3608c2ecf20Sopenharmony_ci mask |= EPOLLOUT | EPOLLWRNORM; 3618c2ecf20Sopenharmony_ci } else { /* send SIGIO later */ 3628c2ecf20Sopenharmony_ci sk_set_bit(SOCKWQ_ASYNC_NOSPACE, sk); 3638c2ecf20Sopenharmony_ci set_bit(SOCK_NOSPACE, &sk->sk_socket->flags); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci /* Race breaker. If space is freed after 3668c2ecf20Sopenharmony_ci * wspace test but before the flags are set, 3678c2ecf20Sopenharmony_ci * IO signal will be lost. 3688c2ecf20Sopenharmony_ci */ 3698c2ecf20Sopenharmony_ci if (sk_stream_is_writeable(sk)) 3708c2ecf20Sopenharmony_ci mask |= EPOLLOUT | EPOLLWRNORM; 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci return mask; 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_poll); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ciint dccp_ioctl(struct sock *sk, int cmd, unsigned long arg) 3798c2ecf20Sopenharmony_ci{ 3808c2ecf20Sopenharmony_ci int rc = -ENOTCONN; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci lock_sock(sk); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci if (sk->sk_state == DCCP_LISTEN) 3858c2ecf20Sopenharmony_ci goto out; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci switch (cmd) { 3888c2ecf20Sopenharmony_ci case SIOCOUTQ: { 3898c2ecf20Sopenharmony_ci int amount = sk_wmem_alloc_get(sk); 3908c2ecf20Sopenharmony_ci /* Using sk_wmem_alloc here because sk_wmem_queued is not used by DCCP and 3918c2ecf20Sopenharmony_ci * always 0, comparably to UDP. 3928c2ecf20Sopenharmony_ci */ 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci rc = put_user(amount, (int __user *)arg); 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci break; 3978c2ecf20Sopenharmony_ci case SIOCINQ: { 3988c2ecf20Sopenharmony_ci struct sk_buff *skb; 3998c2ecf20Sopenharmony_ci unsigned long amount = 0; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci skb = skb_peek(&sk->sk_receive_queue); 4028c2ecf20Sopenharmony_ci if (skb != NULL) { 4038c2ecf20Sopenharmony_ci /* 4048c2ecf20Sopenharmony_ci * We will only return the amount of this packet since 4058c2ecf20Sopenharmony_ci * that is all that will be read. 4068c2ecf20Sopenharmony_ci */ 4078c2ecf20Sopenharmony_ci amount = skb->len; 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci rc = put_user(amount, (int __user *)arg); 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci break; 4128c2ecf20Sopenharmony_ci default: 4138c2ecf20Sopenharmony_ci rc = -ENOIOCTLCMD; 4148c2ecf20Sopenharmony_ci break; 4158c2ecf20Sopenharmony_ci } 4168c2ecf20Sopenharmony_ciout: 4178c2ecf20Sopenharmony_ci release_sock(sk); 4188c2ecf20Sopenharmony_ci return rc; 4198c2ecf20Sopenharmony_ci} 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_ioctl); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_cistatic int dccp_setsockopt_service(struct sock *sk, const __be32 service, 4248c2ecf20Sopenharmony_ci sockptr_t optval, unsigned int optlen) 4258c2ecf20Sopenharmony_ci{ 4268c2ecf20Sopenharmony_ci struct dccp_sock *dp = dccp_sk(sk); 4278c2ecf20Sopenharmony_ci struct dccp_service_list *sl = NULL; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci if (service == DCCP_SERVICE_INVALID_VALUE || 4308c2ecf20Sopenharmony_ci optlen > DCCP_SERVICE_LIST_MAX_LEN * sizeof(u32)) 4318c2ecf20Sopenharmony_ci return -EINVAL; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci if (optlen > sizeof(service)) { 4348c2ecf20Sopenharmony_ci sl = kmalloc(optlen, GFP_KERNEL); 4358c2ecf20Sopenharmony_ci if (sl == NULL) 4368c2ecf20Sopenharmony_ci return -ENOMEM; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci sl->dccpsl_nr = optlen / sizeof(u32) - 1; 4398c2ecf20Sopenharmony_ci if (copy_from_sockptr_offset(sl->dccpsl_list, optval, 4408c2ecf20Sopenharmony_ci sizeof(service), optlen - sizeof(service)) || 4418c2ecf20Sopenharmony_ci dccp_list_has_service(sl, DCCP_SERVICE_INVALID_VALUE)) { 4428c2ecf20Sopenharmony_ci kfree(sl); 4438c2ecf20Sopenharmony_ci return -EFAULT; 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci lock_sock(sk); 4488c2ecf20Sopenharmony_ci dp->dccps_service = service; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci kfree(dp->dccps_service_list); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci dp->dccps_service_list = sl; 4538c2ecf20Sopenharmony_ci release_sock(sk); 4548c2ecf20Sopenharmony_ci return 0; 4558c2ecf20Sopenharmony_ci} 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_cistatic int dccp_setsockopt_cscov(struct sock *sk, int cscov, bool rx) 4588c2ecf20Sopenharmony_ci{ 4598c2ecf20Sopenharmony_ci u8 *list, len; 4608c2ecf20Sopenharmony_ci int i, rc; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci if (cscov < 0 || cscov > 15) 4638c2ecf20Sopenharmony_ci return -EINVAL; 4648c2ecf20Sopenharmony_ci /* 4658c2ecf20Sopenharmony_ci * Populate a list of permissible values, in the range cscov...15. This 4668c2ecf20Sopenharmony_ci * is necessary since feature negotiation of single values only works if 4678c2ecf20Sopenharmony_ci * both sides incidentally choose the same value. Since the list starts 4688c2ecf20Sopenharmony_ci * lowest-value first, negotiation will pick the smallest shared value. 4698c2ecf20Sopenharmony_ci */ 4708c2ecf20Sopenharmony_ci if (cscov == 0) 4718c2ecf20Sopenharmony_ci return 0; 4728c2ecf20Sopenharmony_ci len = 16 - cscov; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci list = kmalloc(len, GFP_KERNEL); 4758c2ecf20Sopenharmony_ci if (list == NULL) 4768c2ecf20Sopenharmony_ci return -ENOBUFS; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) 4798c2ecf20Sopenharmony_ci list[i] = cscov++; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci rc = dccp_feat_register_sp(sk, DCCPF_MIN_CSUM_COVER, rx, list, len); 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci if (rc == 0) { 4848c2ecf20Sopenharmony_ci if (rx) 4858c2ecf20Sopenharmony_ci dccp_sk(sk)->dccps_pcrlen = cscov; 4868c2ecf20Sopenharmony_ci else 4878c2ecf20Sopenharmony_ci dccp_sk(sk)->dccps_pcslen = cscov; 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci kfree(list); 4908c2ecf20Sopenharmony_ci return rc; 4918c2ecf20Sopenharmony_ci} 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_cistatic int dccp_setsockopt_ccid(struct sock *sk, int type, 4948c2ecf20Sopenharmony_ci sockptr_t optval, unsigned int optlen) 4958c2ecf20Sopenharmony_ci{ 4968c2ecf20Sopenharmony_ci u8 *val; 4978c2ecf20Sopenharmony_ci int rc = 0; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci if (optlen < 1 || optlen > DCCP_FEAT_MAX_SP_VALS) 5008c2ecf20Sopenharmony_ci return -EINVAL; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci val = memdup_sockptr(optval, optlen); 5038c2ecf20Sopenharmony_ci if (IS_ERR(val)) 5048c2ecf20Sopenharmony_ci return PTR_ERR(val); 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci lock_sock(sk); 5078c2ecf20Sopenharmony_ci if (type == DCCP_SOCKOPT_TX_CCID || type == DCCP_SOCKOPT_CCID) 5088c2ecf20Sopenharmony_ci rc = dccp_feat_register_sp(sk, DCCPF_CCID, 1, val, optlen); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci if (!rc && (type == DCCP_SOCKOPT_RX_CCID || type == DCCP_SOCKOPT_CCID)) 5118c2ecf20Sopenharmony_ci rc = dccp_feat_register_sp(sk, DCCPF_CCID, 0, val, optlen); 5128c2ecf20Sopenharmony_ci release_sock(sk); 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci kfree(val); 5158c2ecf20Sopenharmony_ci return rc; 5168c2ecf20Sopenharmony_ci} 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_cistatic int do_dccp_setsockopt(struct sock *sk, int level, int optname, 5198c2ecf20Sopenharmony_ci sockptr_t optval, unsigned int optlen) 5208c2ecf20Sopenharmony_ci{ 5218c2ecf20Sopenharmony_ci struct dccp_sock *dp = dccp_sk(sk); 5228c2ecf20Sopenharmony_ci int val, err = 0; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci switch (optname) { 5258c2ecf20Sopenharmony_ci case DCCP_SOCKOPT_PACKET_SIZE: 5268c2ecf20Sopenharmony_ci DCCP_WARN("sockopt(PACKET_SIZE) is deprecated: fix your app\n"); 5278c2ecf20Sopenharmony_ci return 0; 5288c2ecf20Sopenharmony_ci case DCCP_SOCKOPT_CHANGE_L: 5298c2ecf20Sopenharmony_ci case DCCP_SOCKOPT_CHANGE_R: 5308c2ecf20Sopenharmony_ci DCCP_WARN("sockopt(CHANGE_L/R) is deprecated: fix your app\n"); 5318c2ecf20Sopenharmony_ci return 0; 5328c2ecf20Sopenharmony_ci case DCCP_SOCKOPT_CCID: 5338c2ecf20Sopenharmony_ci case DCCP_SOCKOPT_RX_CCID: 5348c2ecf20Sopenharmony_ci case DCCP_SOCKOPT_TX_CCID: 5358c2ecf20Sopenharmony_ci return dccp_setsockopt_ccid(sk, optname, optval, optlen); 5368c2ecf20Sopenharmony_ci } 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci if (optlen < (int)sizeof(int)) 5398c2ecf20Sopenharmony_ci return -EINVAL; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci if (copy_from_sockptr(&val, optval, sizeof(int))) 5428c2ecf20Sopenharmony_ci return -EFAULT; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci if (optname == DCCP_SOCKOPT_SERVICE) 5458c2ecf20Sopenharmony_ci return dccp_setsockopt_service(sk, val, optval, optlen); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci lock_sock(sk); 5488c2ecf20Sopenharmony_ci switch (optname) { 5498c2ecf20Sopenharmony_ci case DCCP_SOCKOPT_SERVER_TIMEWAIT: 5508c2ecf20Sopenharmony_ci if (dp->dccps_role != DCCP_ROLE_SERVER) 5518c2ecf20Sopenharmony_ci err = -EOPNOTSUPP; 5528c2ecf20Sopenharmony_ci else 5538c2ecf20Sopenharmony_ci dp->dccps_server_timewait = (val != 0); 5548c2ecf20Sopenharmony_ci break; 5558c2ecf20Sopenharmony_ci case DCCP_SOCKOPT_SEND_CSCOV: 5568c2ecf20Sopenharmony_ci err = dccp_setsockopt_cscov(sk, val, false); 5578c2ecf20Sopenharmony_ci break; 5588c2ecf20Sopenharmony_ci case DCCP_SOCKOPT_RECV_CSCOV: 5598c2ecf20Sopenharmony_ci err = dccp_setsockopt_cscov(sk, val, true); 5608c2ecf20Sopenharmony_ci break; 5618c2ecf20Sopenharmony_ci case DCCP_SOCKOPT_QPOLICY_ID: 5628c2ecf20Sopenharmony_ci if (sk->sk_state != DCCP_CLOSED) 5638c2ecf20Sopenharmony_ci err = -EISCONN; 5648c2ecf20Sopenharmony_ci else if (val < 0 || val >= DCCPQ_POLICY_MAX) 5658c2ecf20Sopenharmony_ci err = -EINVAL; 5668c2ecf20Sopenharmony_ci else 5678c2ecf20Sopenharmony_ci dp->dccps_qpolicy = val; 5688c2ecf20Sopenharmony_ci break; 5698c2ecf20Sopenharmony_ci case DCCP_SOCKOPT_QPOLICY_TXQLEN: 5708c2ecf20Sopenharmony_ci if (val < 0) 5718c2ecf20Sopenharmony_ci err = -EINVAL; 5728c2ecf20Sopenharmony_ci else 5738c2ecf20Sopenharmony_ci dp->dccps_tx_qlen = val; 5748c2ecf20Sopenharmony_ci break; 5758c2ecf20Sopenharmony_ci default: 5768c2ecf20Sopenharmony_ci err = -ENOPROTOOPT; 5778c2ecf20Sopenharmony_ci break; 5788c2ecf20Sopenharmony_ci } 5798c2ecf20Sopenharmony_ci release_sock(sk); 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci return err; 5828c2ecf20Sopenharmony_ci} 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ciint dccp_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval, 5858c2ecf20Sopenharmony_ci unsigned int optlen) 5868c2ecf20Sopenharmony_ci{ 5878c2ecf20Sopenharmony_ci if (level != SOL_DCCP) 5888c2ecf20Sopenharmony_ci return inet_csk(sk)->icsk_af_ops->setsockopt(sk, level, 5898c2ecf20Sopenharmony_ci optname, optval, 5908c2ecf20Sopenharmony_ci optlen); 5918c2ecf20Sopenharmony_ci return do_dccp_setsockopt(sk, level, optname, optval, optlen); 5928c2ecf20Sopenharmony_ci} 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_setsockopt); 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_cistatic int dccp_getsockopt_service(struct sock *sk, int len, 5978c2ecf20Sopenharmony_ci __be32 __user *optval, 5988c2ecf20Sopenharmony_ci int __user *optlen) 5998c2ecf20Sopenharmony_ci{ 6008c2ecf20Sopenharmony_ci const struct dccp_sock *dp = dccp_sk(sk); 6018c2ecf20Sopenharmony_ci const struct dccp_service_list *sl; 6028c2ecf20Sopenharmony_ci int err = -ENOENT, slen = 0, total_len = sizeof(u32); 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci lock_sock(sk); 6058c2ecf20Sopenharmony_ci if ((sl = dp->dccps_service_list) != NULL) { 6068c2ecf20Sopenharmony_ci slen = sl->dccpsl_nr * sizeof(u32); 6078c2ecf20Sopenharmony_ci total_len += slen; 6088c2ecf20Sopenharmony_ci } 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci err = -EINVAL; 6118c2ecf20Sopenharmony_ci if (total_len > len) 6128c2ecf20Sopenharmony_ci goto out; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci err = 0; 6158c2ecf20Sopenharmony_ci if (put_user(total_len, optlen) || 6168c2ecf20Sopenharmony_ci put_user(dp->dccps_service, optval) || 6178c2ecf20Sopenharmony_ci (sl != NULL && copy_to_user(optval + 1, sl->dccpsl_list, slen))) 6188c2ecf20Sopenharmony_ci err = -EFAULT; 6198c2ecf20Sopenharmony_ciout: 6208c2ecf20Sopenharmony_ci release_sock(sk); 6218c2ecf20Sopenharmony_ci return err; 6228c2ecf20Sopenharmony_ci} 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_cistatic int do_dccp_getsockopt(struct sock *sk, int level, int optname, 6258c2ecf20Sopenharmony_ci char __user *optval, int __user *optlen) 6268c2ecf20Sopenharmony_ci{ 6278c2ecf20Sopenharmony_ci struct dccp_sock *dp; 6288c2ecf20Sopenharmony_ci int val, len; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci if (get_user(len, optlen)) 6318c2ecf20Sopenharmony_ci return -EFAULT; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci if (len < (int)sizeof(int)) 6348c2ecf20Sopenharmony_ci return -EINVAL; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci dp = dccp_sk(sk); 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci switch (optname) { 6398c2ecf20Sopenharmony_ci case DCCP_SOCKOPT_PACKET_SIZE: 6408c2ecf20Sopenharmony_ci DCCP_WARN("sockopt(PACKET_SIZE) is deprecated: fix your app\n"); 6418c2ecf20Sopenharmony_ci return 0; 6428c2ecf20Sopenharmony_ci case DCCP_SOCKOPT_SERVICE: 6438c2ecf20Sopenharmony_ci return dccp_getsockopt_service(sk, len, 6448c2ecf20Sopenharmony_ci (__be32 __user *)optval, optlen); 6458c2ecf20Sopenharmony_ci case DCCP_SOCKOPT_GET_CUR_MPS: 6468c2ecf20Sopenharmony_ci val = READ_ONCE(dp->dccps_mss_cache); 6478c2ecf20Sopenharmony_ci break; 6488c2ecf20Sopenharmony_ci case DCCP_SOCKOPT_AVAILABLE_CCIDS: 6498c2ecf20Sopenharmony_ci return ccid_getsockopt_builtin_ccids(sk, len, optval, optlen); 6508c2ecf20Sopenharmony_ci case DCCP_SOCKOPT_TX_CCID: 6518c2ecf20Sopenharmony_ci val = ccid_get_current_tx_ccid(dp); 6528c2ecf20Sopenharmony_ci if (val < 0) 6538c2ecf20Sopenharmony_ci return -ENOPROTOOPT; 6548c2ecf20Sopenharmony_ci break; 6558c2ecf20Sopenharmony_ci case DCCP_SOCKOPT_RX_CCID: 6568c2ecf20Sopenharmony_ci val = ccid_get_current_rx_ccid(dp); 6578c2ecf20Sopenharmony_ci if (val < 0) 6588c2ecf20Sopenharmony_ci return -ENOPROTOOPT; 6598c2ecf20Sopenharmony_ci break; 6608c2ecf20Sopenharmony_ci case DCCP_SOCKOPT_SERVER_TIMEWAIT: 6618c2ecf20Sopenharmony_ci val = dp->dccps_server_timewait; 6628c2ecf20Sopenharmony_ci break; 6638c2ecf20Sopenharmony_ci case DCCP_SOCKOPT_SEND_CSCOV: 6648c2ecf20Sopenharmony_ci val = dp->dccps_pcslen; 6658c2ecf20Sopenharmony_ci break; 6668c2ecf20Sopenharmony_ci case DCCP_SOCKOPT_RECV_CSCOV: 6678c2ecf20Sopenharmony_ci val = dp->dccps_pcrlen; 6688c2ecf20Sopenharmony_ci break; 6698c2ecf20Sopenharmony_ci case DCCP_SOCKOPT_QPOLICY_ID: 6708c2ecf20Sopenharmony_ci val = dp->dccps_qpolicy; 6718c2ecf20Sopenharmony_ci break; 6728c2ecf20Sopenharmony_ci case DCCP_SOCKOPT_QPOLICY_TXQLEN: 6738c2ecf20Sopenharmony_ci val = dp->dccps_tx_qlen; 6748c2ecf20Sopenharmony_ci break; 6758c2ecf20Sopenharmony_ci case 128 ... 191: 6768c2ecf20Sopenharmony_ci return ccid_hc_rx_getsockopt(dp->dccps_hc_rx_ccid, sk, optname, 6778c2ecf20Sopenharmony_ci len, (u32 __user *)optval, optlen); 6788c2ecf20Sopenharmony_ci case 192 ... 255: 6798c2ecf20Sopenharmony_ci return ccid_hc_tx_getsockopt(dp->dccps_hc_tx_ccid, sk, optname, 6808c2ecf20Sopenharmony_ci len, (u32 __user *)optval, optlen); 6818c2ecf20Sopenharmony_ci default: 6828c2ecf20Sopenharmony_ci return -ENOPROTOOPT; 6838c2ecf20Sopenharmony_ci } 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci len = sizeof(val); 6868c2ecf20Sopenharmony_ci if (put_user(len, optlen) || copy_to_user(optval, &val, len)) 6878c2ecf20Sopenharmony_ci return -EFAULT; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci return 0; 6908c2ecf20Sopenharmony_ci} 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ciint dccp_getsockopt(struct sock *sk, int level, int optname, 6938c2ecf20Sopenharmony_ci char __user *optval, int __user *optlen) 6948c2ecf20Sopenharmony_ci{ 6958c2ecf20Sopenharmony_ci if (level != SOL_DCCP) 6968c2ecf20Sopenharmony_ci return inet_csk(sk)->icsk_af_ops->getsockopt(sk, level, 6978c2ecf20Sopenharmony_ci optname, optval, 6988c2ecf20Sopenharmony_ci optlen); 6998c2ecf20Sopenharmony_ci return do_dccp_getsockopt(sk, level, optname, optval, optlen); 7008c2ecf20Sopenharmony_ci} 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_getsockopt); 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_cistatic int dccp_msghdr_parse(struct msghdr *msg, struct sk_buff *skb) 7058c2ecf20Sopenharmony_ci{ 7068c2ecf20Sopenharmony_ci struct cmsghdr *cmsg; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci /* 7098c2ecf20Sopenharmony_ci * Assign an (opaque) qpolicy priority value to skb->priority. 7108c2ecf20Sopenharmony_ci * 7118c2ecf20Sopenharmony_ci * We are overloading this skb field for use with the qpolicy subystem. 7128c2ecf20Sopenharmony_ci * The skb->priority is normally used for the SO_PRIORITY option, which 7138c2ecf20Sopenharmony_ci * is initialised from sk_priority. Since the assignment of sk_priority 7148c2ecf20Sopenharmony_ci * to skb->priority happens later (on layer 3), we overload this field 7158c2ecf20Sopenharmony_ci * for use with queueing priorities as long as the skb is on layer 4. 7168c2ecf20Sopenharmony_ci * The default priority value (if nothing is set) is 0. 7178c2ecf20Sopenharmony_ci */ 7188c2ecf20Sopenharmony_ci skb->priority = 0; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci for_each_cmsghdr(cmsg, msg) { 7218c2ecf20Sopenharmony_ci if (!CMSG_OK(msg, cmsg)) 7228c2ecf20Sopenharmony_ci return -EINVAL; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci if (cmsg->cmsg_level != SOL_DCCP) 7258c2ecf20Sopenharmony_ci continue; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci if (cmsg->cmsg_type <= DCCP_SCM_QPOLICY_MAX && 7288c2ecf20Sopenharmony_ci !dccp_qpolicy_param_ok(skb->sk, cmsg->cmsg_type)) 7298c2ecf20Sopenharmony_ci return -EINVAL; 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci switch (cmsg->cmsg_type) { 7328c2ecf20Sopenharmony_ci case DCCP_SCM_PRIORITY: 7338c2ecf20Sopenharmony_ci if (cmsg->cmsg_len != CMSG_LEN(sizeof(__u32))) 7348c2ecf20Sopenharmony_ci return -EINVAL; 7358c2ecf20Sopenharmony_ci skb->priority = *(__u32 *)CMSG_DATA(cmsg); 7368c2ecf20Sopenharmony_ci break; 7378c2ecf20Sopenharmony_ci default: 7388c2ecf20Sopenharmony_ci return -EINVAL; 7398c2ecf20Sopenharmony_ci } 7408c2ecf20Sopenharmony_ci } 7418c2ecf20Sopenharmony_ci return 0; 7428c2ecf20Sopenharmony_ci} 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ciint dccp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) 7458c2ecf20Sopenharmony_ci{ 7468c2ecf20Sopenharmony_ci const struct dccp_sock *dp = dccp_sk(sk); 7478c2ecf20Sopenharmony_ci const int flags = msg->msg_flags; 7488c2ecf20Sopenharmony_ci const int noblock = flags & MSG_DONTWAIT; 7498c2ecf20Sopenharmony_ci struct sk_buff *skb; 7508c2ecf20Sopenharmony_ci int rc, size; 7518c2ecf20Sopenharmony_ci long timeo; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci trace_dccp_probe(sk, len); 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci if (len > READ_ONCE(dp->dccps_mss_cache)) 7568c2ecf20Sopenharmony_ci return -EMSGSIZE; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci lock_sock(sk); 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci timeo = sock_sndtimeo(sk, noblock); 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci /* 7638c2ecf20Sopenharmony_ci * We have to use sk_stream_wait_connect here to set sk_write_pending, 7648c2ecf20Sopenharmony_ci * so that the trick in dccp_rcv_request_sent_state_process. 7658c2ecf20Sopenharmony_ci */ 7668c2ecf20Sopenharmony_ci /* Wait for a connection to finish. */ 7678c2ecf20Sopenharmony_ci if ((1 << sk->sk_state) & ~(DCCPF_OPEN | DCCPF_PARTOPEN)) 7688c2ecf20Sopenharmony_ci if ((rc = sk_stream_wait_connect(sk, &timeo)) != 0) 7698c2ecf20Sopenharmony_ci goto out_release; 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci size = sk->sk_prot->max_header + len; 7728c2ecf20Sopenharmony_ci release_sock(sk); 7738c2ecf20Sopenharmony_ci skb = sock_alloc_send_skb(sk, size, noblock, &rc); 7748c2ecf20Sopenharmony_ci lock_sock(sk); 7758c2ecf20Sopenharmony_ci if (skb == NULL) 7768c2ecf20Sopenharmony_ci goto out_release; 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci if (dccp_qpolicy_full(sk)) { 7798c2ecf20Sopenharmony_ci rc = -EAGAIN; 7808c2ecf20Sopenharmony_ci goto out_discard; 7818c2ecf20Sopenharmony_ci } 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci if (sk->sk_state == DCCP_CLOSED) { 7848c2ecf20Sopenharmony_ci rc = -ENOTCONN; 7858c2ecf20Sopenharmony_ci goto out_discard; 7868c2ecf20Sopenharmony_ci } 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci /* We need to check dccps_mss_cache after socket is locked. */ 7898c2ecf20Sopenharmony_ci if (len > dp->dccps_mss_cache) { 7908c2ecf20Sopenharmony_ci rc = -EMSGSIZE; 7918c2ecf20Sopenharmony_ci goto out_discard; 7928c2ecf20Sopenharmony_ci } 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci skb_reserve(skb, sk->sk_prot->max_header); 7958c2ecf20Sopenharmony_ci rc = memcpy_from_msg(skb_put(skb, len), msg, len); 7968c2ecf20Sopenharmony_ci if (rc != 0) 7978c2ecf20Sopenharmony_ci goto out_discard; 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci rc = dccp_msghdr_parse(msg, skb); 8008c2ecf20Sopenharmony_ci if (rc != 0) 8018c2ecf20Sopenharmony_ci goto out_discard; 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci dccp_qpolicy_push(sk, skb); 8048c2ecf20Sopenharmony_ci /* 8058c2ecf20Sopenharmony_ci * The xmit_timer is set if the TX CCID is rate-based and will expire 8068c2ecf20Sopenharmony_ci * when congestion control permits to release further packets into the 8078c2ecf20Sopenharmony_ci * network. Window-based CCIDs do not use this timer. 8088c2ecf20Sopenharmony_ci */ 8098c2ecf20Sopenharmony_ci if (!timer_pending(&dp->dccps_xmit_timer)) 8108c2ecf20Sopenharmony_ci dccp_write_xmit(sk); 8118c2ecf20Sopenharmony_ciout_release: 8128c2ecf20Sopenharmony_ci release_sock(sk); 8138c2ecf20Sopenharmony_ci return rc ? : len; 8148c2ecf20Sopenharmony_ciout_discard: 8158c2ecf20Sopenharmony_ci kfree_skb(skb); 8168c2ecf20Sopenharmony_ci goto out_release; 8178c2ecf20Sopenharmony_ci} 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_sendmsg); 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ciint dccp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int nonblock, 8228c2ecf20Sopenharmony_ci int flags, int *addr_len) 8238c2ecf20Sopenharmony_ci{ 8248c2ecf20Sopenharmony_ci const struct dccp_hdr *dh; 8258c2ecf20Sopenharmony_ci long timeo; 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci lock_sock(sk); 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci if (sk->sk_state == DCCP_LISTEN) { 8308c2ecf20Sopenharmony_ci len = -ENOTCONN; 8318c2ecf20Sopenharmony_ci goto out; 8328c2ecf20Sopenharmony_ci } 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci timeo = sock_rcvtimeo(sk, nonblock); 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci do { 8378c2ecf20Sopenharmony_ci struct sk_buff *skb = skb_peek(&sk->sk_receive_queue); 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci if (skb == NULL) 8408c2ecf20Sopenharmony_ci goto verify_sock_status; 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci dh = dccp_hdr(skb); 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci switch (dh->dccph_type) { 8458c2ecf20Sopenharmony_ci case DCCP_PKT_DATA: 8468c2ecf20Sopenharmony_ci case DCCP_PKT_DATAACK: 8478c2ecf20Sopenharmony_ci goto found_ok_skb; 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci case DCCP_PKT_CLOSE: 8508c2ecf20Sopenharmony_ci case DCCP_PKT_CLOSEREQ: 8518c2ecf20Sopenharmony_ci if (!(flags & MSG_PEEK)) 8528c2ecf20Sopenharmony_ci dccp_finish_passive_close(sk); 8538c2ecf20Sopenharmony_ci fallthrough; 8548c2ecf20Sopenharmony_ci case DCCP_PKT_RESET: 8558c2ecf20Sopenharmony_ci dccp_pr_debug("found fin (%s) ok!\n", 8568c2ecf20Sopenharmony_ci dccp_packet_name(dh->dccph_type)); 8578c2ecf20Sopenharmony_ci len = 0; 8588c2ecf20Sopenharmony_ci goto found_fin_ok; 8598c2ecf20Sopenharmony_ci default: 8608c2ecf20Sopenharmony_ci dccp_pr_debug("packet_type=%s\n", 8618c2ecf20Sopenharmony_ci dccp_packet_name(dh->dccph_type)); 8628c2ecf20Sopenharmony_ci sk_eat_skb(sk, skb); 8638c2ecf20Sopenharmony_ci } 8648c2ecf20Sopenharmony_civerify_sock_status: 8658c2ecf20Sopenharmony_ci if (sock_flag(sk, SOCK_DONE)) { 8668c2ecf20Sopenharmony_ci len = 0; 8678c2ecf20Sopenharmony_ci break; 8688c2ecf20Sopenharmony_ci } 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci if (sk->sk_err) { 8718c2ecf20Sopenharmony_ci len = sock_error(sk); 8728c2ecf20Sopenharmony_ci break; 8738c2ecf20Sopenharmony_ci } 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci if (sk->sk_shutdown & RCV_SHUTDOWN) { 8768c2ecf20Sopenharmony_ci len = 0; 8778c2ecf20Sopenharmony_ci break; 8788c2ecf20Sopenharmony_ci } 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci if (sk->sk_state == DCCP_CLOSED) { 8818c2ecf20Sopenharmony_ci if (!sock_flag(sk, SOCK_DONE)) { 8828c2ecf20Sopenharmony_ci /* This occurs when user tries to read 8838c2ecf20Sopenharmony_ci * from never connected socket. 8848c2ecf20Sopenharmony_ci */ 8858c2ecf20Sopenharmony_ci len = -ENOTCONN; 8868c2ecf20Sopenharmony_ci break; 8878c2ecf20Sopenharmony_ci } 8888c2ecf20Sopenharmony_ci len = 0; 8898c2ecf20Sopenharmony_ci break; 8908c2ecf20Sopenharmony_ci } 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci if (!timeo) { 8938c2ecf20Sopenharmony_ci len = -EAGAIN; 8948c2ecf20Sopenharmony_ci break; 8958c2ecf20Sopenharmony_ci } 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci if (signal_pending(current)) { 8988c2ecf20Sopenharmony_ci len = sock_intr_errno(timeo); 8998c2ecf20Sopenharmony_ci break; 9008c2ecf20Sopenharmony_ci } 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci sk_wait_data(sk, &timeo, NULL); 9038c2ecf20Sopenharmony_ci continue; 9048c2ecf20Sopenharmony_ci found_ok_skb: 9058c2ecf20Sopenharmony_ci if (len > skb->len) 9068c2ecf20Sopenharmony_ci len = skb->len; 9078c2ecf20Sopenharmony_ci else if (len < skb->len) 9088c2ecf20Sopenharmony_ci msg->msg_flags |= MSG_TRUNC; 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci if (skb_copy_datagram_msg(skb, 0, msg, len)) { 9118c2ecf20Sopenharmony_ci /* Exception. Bailout! */ 9128c2ecf20Sopenharmony_ci len = -EFAULT; 9138c2ecf20Sopenharmony_ci break; 9148c2ecf20Sopenharmony_ci } 9158c2ecf20Sopenharmony_ci if (flags & MSG_TRUNC) 9168c2ecf20Sopenharmony_ci len = skb->len; 9178c2ecf20Sopenharmony_ci found_fin_ok: 9188c2ecf20Sopenharmony_ci if (!(flags & MSG_PEEK)) 9198c2ecf20Sopenharmony_ci sk_eat_skb(sk, skb); 9208c2ecf20Sopenharmony_ci break; 9218c2ecf20Sopenharmony_ci } while (1); 9228c2ecf20Sopenharmony_ciout: 9238c2ecf20Sopenharmony_ci release_sock(sk); 9248c2ecf20Sopenharmony_ci return len; 9258c2ecf20Sopenharmony_ci} 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_recvmsg); 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ciint inet_dccp_listen(struct socket *sock, int backlog) 9308c2ecf20Sopenharmony_ci{ 9318c2ecf20Sopenharmony_ci struct sock *sk = sock->sk; 9328c2ecf20Sopenharmony_ci unsigned char old_state; 9338c2ecf20Sopenharmony_ci int err; 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci lock_sock(sk); 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci err = -EINVAL; 9388c2ecf20Sopenharmony_ci if (sock->state != SS_UNCONNECTED || sock->type != SOCK_DCCP) 9398c2ecf20Sopenharmony_ci goto out; 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci old_state = sk->sk_state; 9428c2ecf20Sopenharmony_ci if (!((1 << old_state) & (DCCPF_CLOSED | DCCPF_LISTEN))) 9438c2ecf20Sopenharmony_ci goto out; 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci WRITE_ONCE(sk->sk_max_ack_backlog, backlog); 9468c2ecf20Sopenharmony_ci /* Really, if the socket is already in listen state 9478c2ecf20Sopenharmony_ci * we can only allow the backlog to be adjusted. 9488c2ecf20Sopenharmony_ci */ 9498c2ecf20Sopenharmony_ci if (old_state != DCCP_LISTEN) { 9508c2ecf20Sopenharmony_ci /* 9518c2ecf20Sopenharmony_ci * FIXME: here it probably should be sk->sk_prot->listen_start 9528c2ecf20Sopenharmony_ci * see tcp_listen_start 9538c2ecf20Sopenharmony_ci */ 9548c2ecf20Sopenharmony_ci err = dccp_listen_start(sk, backlog); 9558c2ecf20Sopenharmony_ci if (err) 9568c2ecf20Sopenharmony_ci goto out; 9578c2ecf20Sopenharmony_ci } 9588c2ecf20Sopenharmony_ci err = 0; 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ciout: 9618c2ecf20Sopenharmony_ci release_sock(sk); 9628c2ecf20Sopenharmony_ci return err; 9638c2ecf20Sopenharmony_ci} 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(inet_dccp_listen); 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_cistatic void dccp_terminate_connection(struct sock *sk) 9688c2ecf20Sopenharmony_ci{ 9698c2ecf20Sopenharmony_ci u8 next_state = DCCP_CLOSED; 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci switch (sk->sk_state) { 9728c2ecf20Sopenharmony_ci case DCCP_PASSIVE_CLOSE: 9738c2ecf20Sopenharmony_ci case DCCP_PASSIVE_CLOSEREQ: 9748c2ecf20Sopenharmony_ci dccp_finish_passive_close(sk); 9758c2ecf20Sopenharmony_ci break; 9768c2ecf20Sopenharmony_ci case DCCP_PARTOPEN: 9778c2ecf20Sopenharmony_ci dccp_pr_debug("Stop PARTOPEN timer (%p)\n", sk); 9788c2ecf20Sopenharmony_ci inet_csk_clear_xmit_timer(sk, ICSK_TIME_DACK); 9798c2ecf20Sopenharmony_ci fallthrough; 9808c2ecf20Sopenharmony_ci case DCCP_OPEN: 9818c2ecf20Sopenharmony_ci dccp_send_close(sk, 1); 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci if (dccp_sk(sk)->dccps_role == DCCP_ROLE_SERVER && 9848c2ecf20Sopenharmony_ci !dccp_sk(sk)->dccps_server_timewait) 9858c2ecf20Sopenharmony_ci next_state = DCCP_ACTIVE_CLOSEREQ; 9868c2ecf20Sopenharmony_ci else 9878c2ecf20Sopenharmony_ci next_state = DCCP_CLOSING; 9888c2ecf20Sopenharmony_ci fallthrough; 9898c2ecf20Sopenharmony_ci default: 9908c2ecf20Sopenharmony_ci dccp_set_state(sk, next_state); 9918c2ecf20Sopenharmony_ci } 9928c2ecf20Sopenharmony_ci} 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_civoid dccp_close(struct sock *sk, long timeout) 9958c2ecf20Sopenharmony_ci{ 9968c2ecf20Sopenharmony_ci struct dccp_sock *dp = dccp_sk(sk); 9978c2ecf20Sopenharmony_ci struct sk_buff *skb; 9988c2ecf20Sopenharmony_ci u32 data_was_unread = 0; 9998c2ecf20Sopenharmony_ci int state; 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci lock_sock(sk); 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci sk->sk_shutdown = SHUTDOWN_MASK; 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci if (sk->sk_state == DCCP_LISTEN) { 10068c2ecf20Sopenharmony_ci dccp_set_state(sk, DCCP_CLOSED); 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci /* Special case. */ 10098c2ecf20Sopenharmony_ci inet_csk_listen_stop(sk); 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci goto adjudge_to_death; 10128c2ecf20Sopenharmony_ci } 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci sk_stop_timer(sk, &dp->dccps_xmit_timer); 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci /* 10178c2ecf20Sopenharmony_ci * We need to flush the recv. buffs. We do this only on the 10188c2ecf20Sopenharmony_ci * descriptor close, not protocol-sourced closes, because the 10198c2ecf20Sopenharmony_ci *reader process may not have drained the data yet! 10208c2ecf20Sopenharmony_ci */ 10218c2ecf20Sopenharmony_ci while ((skb = __skb_dequeue(&sk->sk_receive_queue)) != NULL) { 10228c2ecf20Sopenharmony_ci data_was_unread += skb->len; 10238c2ecf20Sopenharmony_ci __kfree_skb(skb); 10248c2ecf20Sopenharmony_ci } 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci /* If socket has been already reset kill it. */ 10278c2ecf20Sopenharmony_ci if (sk->sk_state == DCCP_CLOSED) 10288c2ecf20Sopenharmony_ci goto adjudge_to_death; 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci if (data_was_unread) { 10318c2ecf20Sopenharmony_ci /* Unread data was tossed, send an appropriate Reset Code */ 10328c2ecf20Sopenharmony_ci DCCP_WARN("ABORT with %u bytes unread\n", data_was_unread); 10338c2ecf20Sopenharmony_ci dccp_send_reset(sk, DCCP_RESET_CODE_ABORTED); 10348c2ecf20Sopenharmony_ci dccp_set_state(sk, DCCP_CLOSED); 10358c2ecf20Sopenharmony_ci } else if (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime) { 10368c2ecf20Sopenharmony_ci /* Check zero linger _after_ checking for unread data. */ 10378c2ecf20Sopenharmony_ci sk->sk_prot->disconnect(sk, 0); 10388c2ecf20Sopenharmony_ci } else if (sk->sk_state != DCCP_CLOSED) { 10398c2ecf20Sopenharmony_ci /* 10408c2ecf20Sopenharmony_ci * Normal connection termination. May need to wait if there are 10418c2ecf20Sopenharmony_ci * still packets in the TX queue that are delayed by the CCID. 10428c2ecf20Sopenharmony_ci */ 10438c2ecf20Sopenharmony_ci dccp_flush_write_queue(sk, &timeout); 10448c2ecf20Sopenharmony_ci dccp_terminate_connection(sk); 10458c2ecf20Sopenharmony_ci } 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci /* 10488c2ecf20Sopenharmony_ci * Flush write queue. This may be necessary in several cases: 10498c2ecf20Sopenharmony_ci * - we have been closed by the peer but still have application data; 10508c2ecf20Sopenharmony_ci * - abortive termination (unread data or zero linger time), 10518c2ecf20Sopenharmony_ci * - normal termination but queue could not be flushed within time limit 10528c2ecf20Sopenharmony_ci */ 10538c2ecf20Sopenharmony_ci __skb_queue_purge(&sk->sk_write_queue); 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci sk_stream_wait_close(sk, timeout); 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ciadjudge_to_death: 10588c2ecf20Sopenharmony_ci state = sk->sk_state; 10598c2ecf20Sopenharmony_ci sock_hold(sk); 10608c2ecf20Sopenharmony_ci sock_orphan(sk); 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci /* 10638c2ecf20Sopenharmony_ci * It is the last release_sock in its life. It will remove backlog. 10648c2ecf20Sopenharmony_ci */ 10658c2ecf20Sopenharmony_ci release_sock(sk); 10668c2ecf20Sopenharmony_ci /* 10678c2ecf20Sopenharmony_ci * Now socket is owned by kernel and we acquire BH lock 10688c2ecf20Sopenharmony_ci * to finish close. No need to check for user refs. 10698c2ecf20Sopenharmony_ci */ 10708c2ecf20Sopenharmony_ci local_bh_disable(); 10718c2ecf20Sopenharmony_ci bh_lock_sock(sk); 10728c2ecf20Sopenharmony_ci WARN_ON(sock_owned_by_user(sk)); 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci this_cpu_inc(dccp_orphan_count); 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci /* Have we already been destroyed by a softirq or backlog? */ 10778c2ecf20Sopenharmony_ci if (state != DCCP_CLOSED && sk->sk_state == DCCP_CLOSED) 10788c2ecf20Sopenharmony_ci goto out; 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci if (sk->sk_state == DCCP_CLOSED) 10818c2ecf20Sopenharmony_ci inet_csk_destroy_sock(sk); 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci /* Otherwise, socket is reprieved until protocol close. */ 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ciout: 10868c2ecf20Sopenharmony_ci bh_unlock_sock(sk); 10878c2ecf20Sopenharmony_ci local_bh_enable(); 10888c2ecf20Sopenharmony_ci sock_put(sk); 10898c2ecf20Sopenharmony_ci} 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_close); 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_civoid dccp_shutdown(struct sock *sk, int how) 10948c2ecf20Sopenharmony_ci{ 10958c2ecf20Sopenharmony_ci dccp_pr_debug("called shutdown(%x)\n", how); 10968c2ecf20Sopenharmony_ci} 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_shutdown); 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_cistatic inline int __init dccp_mib_init(void) 11018c2ecf20Sopenharmony_ci{ 11028c2ecf20Sopenharmony_ci dccp_statistics = alloc_percpu(struct dccp_mib); 11038c2ecf20Sopenharmony_ci if (!dccp_statistics) 11048c2ecf20Sopenharmony_ci return -ENOMEM; 11058c2ecf20Sopenharmony_ci return 0; 11068c2ecf20Sopenharmony_ci} 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_cistatic inline void dccp_mib_exit(void) 11098c2ecf20Sopenharmony_ci{ 11108c2ecf20Sopenharmony_ci free_percpu(dccp_statistics); 11118c2ecf20Sopenharmony_ci} 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_cistatic int thash_entries; 11148c2ecf20Sopenharmony_cimodule_param(thash_entries, int, 0444); 11158c2ecf20Sopenharmony_ciMODULE_PARM_DESC(thash_entries, "Number of ehash buckets"); 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_DCCP_DEBUG 11188c2ecf20Sopenharmony_cibool dccp_debug; 11198c2ecf20Sopenharmony_cimodule_param(dccp_debug, bool, 0644); 11208c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dccp_debug, "Enable debug messages"); 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_debug); 11238c2ecf20Sopenharmony_ci#endif 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_cistatic int __init dccp_init(void) 11268c2ecf20Sopenharmony_ci{ 11278c2ecf20Sopenharmony_ci unsigned long goal; 11288c2ecf20Sopenharmony_ci unsigned long nr_pages = totalram_pages(); 11298c2ecf20Sopenharmony_ci int ehash_order, bhash_order, i; 11308c2ecf20Sopenharmony_ci int rc; 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(struct dccp_skb_cb) > 11338c2ecf20Sopenharmony_ci sizeof_field(struct sk_buff, cb)); 11348c2ecf20Sopenharmony_ci inet_hashinfo_init(&dccp_hashinfo); 11358c2ecf20Sopenharmony_ci rc = inet_hashinfo2_init_mod(&dccp_hashinfo); 11368c2ecf20Sopenharmony_ci if (rc) 11378c2ecf20Sopenharmony_ci goto out_fail; 11388c2ecf20Sopenharmony_ci rc = -ENOBUFS; 11398c2ecf20Sopenharmony_ci dccp_hashinfo.bind_bucket_cachep = 11408c2ecf20Sopenharmony_ci kmem_cache_create("dccp_bind_bucket", 11418c2ecf20Sopenharmony_ci sizeof(struct inet_bind_bucket), 0, 11428c2ecf20Sopenharmony_ci SLAB_HWCACHE_ALIGN, NULL); 11438c2ecf20Sopenharmony_ci if (!dccp_hashinfo.bind_bucket_cachep) 11448c2ecf20Sopenharmony_ci goto out_free_hashinfo2; 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci /* 11478c2ecf20Sopenharmony_ci * Size and allocate the main established and bind bucket 11488c2ecf20Sopenharmony_ci * hash tables. 11498c2ecf20Sopenharmony_ci * 11508c2ecf20Sopenharmony_ci * The methodology is similar to that of the buffer cache. 11518c2ecf20Sopenharmony_ci */ 11528c2ecf20Sopenharmony_ci if (nr_pages >= (128 * 1024)) 11538c2ecf20Sopenharmony_ci goal = nr_pages >> (21 - PAGE_SHIFT); 11548c2ecf20Sopenharmony_ci else 11558c2ecf20Sopenharmony_ci goal = nr_pages >> (23 - PAGE_SHIFT); 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci if (thash_entries) 11588c2ecf20Sopenharmony_ci goal = (thash_entries * 11598c2ecf20Sopenharmony_ci sizeof(struct inet_ehash_bucket)) >> PAGE_SHIFT; 11608c2ecf20Sopenharmony_ci for (ehash_order = 0; (1UL << ehash_order) < goal; ehash_order++) 11618c2ecf20Sopenharmony_ci ; 11628c2ecf20Sopenharmony_ci do { 11638c2ecf20Sopenharmony_ci unsigned long hash_size = (1UL << ehash_order) * PAGE_SIZE / 11648c2ecf20Sopenharmony_ci sizeof(struct inet_ehash_bucket); 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci while (hash_size & (hash_size - 1)) 11678c2ecf20Sopenharmony_ci hash_size--; 11688c2ecf20Sopenharmony_ci dccp_hashinfo.ehash_mask = hash_size - 1; 11698c2ecf20Sopenharmony_ci dccp_hashinfo.ehash = (struct inet_ehash_bucket *) 11708c2ecf20Sopenharmony_ci __get_free_pages(GFP_ATOMIC|__GFP_NOWARN, ehash_order); 11718c2ecf20Sopenharmony_ci } while (!dccp_hashinfo.ehash && --ehash_order > 0); 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci if (!dccp_hashinfo.ehash) { 11748c2ecf20Sopenharmony_ci DCCP_CRIT("Failed to allocate DCCP established hash table"); 11758c2ecf20Sopenharmony_ci goto out_free_bind_bucket_cachep; 11768c2ecf20Sopenharmony_ci } 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci for (i = 0; i <= dccp_hashinfo.ehash_mask; i++) 11798c2ecf20Sopenharmony_ci INIT_HLIST_NULLS_HEAD(&dccp_hashinfo.ehash[i].chain, i); 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci if (inet_ehash_locks_alloc(&dccp_hashinfo)) 11828c2ecf20Sopenharmony_ci goto out_free_dccp_ehash; 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci bhash_order = ehash_order; 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci do { 11878c2ecf20Sopenharmony_ci dccp_hashinfo.bhash_size = (1UL << bhash_order) * PAGE_SIZE / 11888c2ecf20Sopenharmony_ci sizeof(struct inet_bind_hashbucket); 11898c2ecf20Sopenharmony_ci if ((dccp_hashinfo.bhash_size > (64 * 1024)) && 11908c2ecf20Sopenharmony_ci bhash_order > 0) 11918c2ecf20Sopenharmony_ci continue; 11928c2ecf20Sopenharmony_ci dccp_hashinfo.bhash = (struct inet_bind_hashbucket *) 11938c2ecf20Sopenharmony_ci __get_free_pages(GFP_ATOMIC|__GFP_NOWARN, bhash_order); 11948c2ecf20Sopenharmony_ci } while (!dccp_hashinfo.bhash && --bhash_order >= 0); 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci if (!dccp_hashinfo.bhash) { 11978c2ecf20Sopenharmony_ci DCCP_CRIT("Failed to allocate DCCP bind hash table"); 11988c2ecf20Sopenharmony_ci goto out_free_dccp_locks; 11998c2ecf20Sopenharmony_ci } 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci for (i = 0; i < dccp_hashinfo.bhash_size; i++) { 12028c2ecf20Sopenharmony_ci spin_lock_init(&dccp_hashinfo.bhash[i].lock); 12038c2ecf20Sopenharmony_ci INIT_HLIST_HEAD(&dccp_hashinfo.bhash[i].chain); 12048c2ecf20Sopenharmony_ci } 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci rc = dccp_mib_init(); 12078c2ecf20Sopenharmony_ci if (rc) 12088c2ecf20Sopenharmony_ci goto out_free_dccp_bhash; 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci rc = dccp_ackvec_init(); 12118c2ecf20Sopenharmony_ci if (rc) 12128c2ecf20Sopenharmony_ci goto out_free_dccp_mib; 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci rc = dccp_sysctl_init(); 12158c2ecf20Sopenharmony_ci if (rc) 12168c2ecf20Sopenharmony_ci goto out_ackvec_exit; 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci rc = ccid_initialize_builtins(); 12198c2ecf20Sopenharmony_ci if (rc) 12208c2ecf20Sopenharmony_ci goto out_sysctl_exit; 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci dccp_timestamping_init(); 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci return 0; 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ciout_sysctl_exit: 12278c2ecf20Sopenharmony_ci dccp_sysctl_exit(); 12288c2ecf20Sopenharmony_ciout_ackvec_exit: 12298c2ecf20Sopenharmony_ci dccp_ackvec_exit(); 12308c2ecf20Sopenharmony_ciout_free_dccp_mib: 12318c2ecf20Sopenharmony_ci dccp_mib_exit(); 12328c2ecf20Sopenharmony_ciout_free_dccp_bhash: 12338c2ecf20Sopenharmony_ci free_pages((unsigned long)dccp_hashinfo.bhash, bhash_order); 12348c2ecf20Sopenharmony_ciout_free_dccp_locks: 12358c2ecf20Sopenharmony_ci inet_ehash_locks_free(&dccp_hashinfo); 12368c2ecf20Sopenharmony_ciout_free_dccp_ehash: 12378c2ecf20Sopenharmony_ci free_pages((unsigned long)dccp_hashinfo.ehash, ehash_order); 12388c2ecf20Sopenharmony_ciout_free_bind_bucket_cachep: 12398c2ecf20Sopenharmony_ci kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep); 12408c2ecf20Sopenharmony_ciout_free_hashinfo2: 12418c2ecf20Sopenharmony_ci inet_hashinfo2_free_mod(&dccp_hashinfo); 12428c2ecf20Sopenharmony_ciout_fail: 12438c2ecf20Sopenharmony_ci dccp_hashinfo.bhash = NULL; 12448c2ecf20Sopenharmony_ci dccp_hashinfo.ehash = NULL; 12458c2ecf20Sopenharmony_ci dccp_hashinfo.bind_bucket_cachep = NULL; 12468c2ecf20Sopenharmony_ci return rc; 12478c2ecf20Sopenharmony_ci} 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_cistatic void __exit dccp_fini(void) 12508c2ecf20Sopenharmony_ci{ 12518c2ecf20Sopenharmony_ci ccid_cleanup_builtins(); 12528c2ecf20Sopenharmony_ci dccp_mib_exit(); 12538c2ecf20Sopenharmony_ci free_pages((unsigned long)dccp_hashinfo.bhash, 12548c2ecf20Sopenharmony_ci get_order(dccp_hashinfo.bhash_size * 12558c2ecf20Sopenharmony_ci sizeof(struct inet_bind_hashbucket))); 12568c2ecf20Sopenharmony_ci free_pages((unsigned long)dccp_hashinfo.ehash, 12578c2ecf20Sopenharmony_ci get_order((dccp_hashinfo.ehash_mask + 1) * 12588c2ecf20Sopenharmony_ci sizeof(struct inet_ehash_bucket))); 12598c2ecf20Sopenharmony_ci inet_ehash_locks_free(&dccp_hashinfo); 12608c2ecf20Sopenharmony_ci kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep); 12618c2ecf20Sopenharmony_ci dccp_ackvec_exit(); 12628c2ecf20Sopenharmony_ci dccp_sysctl_exit(); 12638c2ecf20Sopenharmony_ci inet_hashinfo2_free_mod(&dccp_hashinfo); 12648c2ecf20Sopenharmony_ci} 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_cimodule_init(dccp_init); 12678c2ecf20Sopenharmony_cimodule_exit(dccp_fini); 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 12708c2ecf20Sopenharmony_ciMODULE_AUTHOR("Arnaldo Carvalho de Melo <acme@conectiva.com.br>"); 12718c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("DCCP - Datagram Congestion Controlled Protocol"); 1272