162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * net/dccp/proto.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * An implementation of the DCCP protocol 662306a36Sopenharmony_ci * Arnaldo Carvalho de Melo <acme@conectiva.com.br> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/dccp.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/types.h> 1262306a36Sopenharmony_ci#include <linux/sched.h> 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/skbuff.h> 1562306a36Sopenharmony_ci#include <linux/netdevice.h> 1662306a36Sopenharmony_ci#include <linux/in.h> 1762306a36Sopenharmony_ci#include <linux/if_arp.h> 1862306a36Sopenharmony_ci#include <linux/init.h> 1962306a36Sopenharmony_ci#include <linux/random.h> 2062306a36Sopenharmony_ci#include <linux/slab.h> 2162306a36Sopenharmony_ci#include <net/checksum.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include <net/inet_sock.h> 2462306a36Sopenharmony_ci#include <net/inet_common.h> 2562306a36Sopenharmony_ci#include <net/sock.h> 2662306a36Sopenharmony_ci#include <net/xfrm.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include <asm/ioctls.h> 2962306a36Sopenharmony_ci#include <linux/spinlock.h> 3062306a36Sopenharmony_ci#include <linux/timer.h> 3162306a36Sopenharmony_ci#include <linux/delay.h> 3262306a36Sopenharmony_ci#include <linux/poll.h> 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#include "ccid.h" 3562306a36Sopenharmony_ci#include "dccp.h" 3662306a36Sopenharmony_ci#include "feat.h" 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define CREATE_TRACE_POINTS 3962306a36Sopenharmony_ci#include "trace.h" 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ciDEFINE_SNMP_STAT(struct dccp_mib, dccp_statistics) __read_mostly; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_statistics); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ciDEFINE_PER_CPU(unsigned int, dccp_orphan_count); 4662306a36Sopenharmony_ciEXPORT_PER_CPU_SYMBOL_GPL(dccp_orphan_count); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistruct inet_hashinfo dccp_hashinfo; 4962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_hashinfo); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* the maximum queue length for tx in packets. 0 is no limit */ 5262306a36Sopenharmony_ciint sysctl_dccp_tx_qlen __read_mostly = 5; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci#ifdef CONFIG_IP_DCCP_DEBUG 5562306a36Sopenharmony_cistatic const char *dccp_state_name(const int state) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci static const char *const dccp_state_names[] = { 5862306a36Sopenharmony_ci [DCCP_OPEN] = "OPEN", 5962306a36Sopenharmony_ci [DCCP_REQUESTING] = "REQUESTING", 6062306a36Sopenharmony_ci [DCCP_PARTOPEN] = "PARTOPEN", 6162306a36Sopenharmony_ci [DCCP_LISTEN] = "LISTEN", 6262306a36Sopenharmony_ci [DCCP_RESPOND] = "RESPOND", 6362306a36Sopenharmony_ci [DCCP_CLOSING] = "CLOSING", 6462306a36Sopenharmony_ci [DCCP_ACTIVE_CLOSEREQ] = "CLOSEREQ", 6562306a36Sopenharmony_ci [DCCP_PASSIVE_CLOSE] = "PASSIVE_CLOSE", 6662306a36Sopenharmony_ci [DCCP_PASSIVE_CLOSEREQ] = "PASSIVE_CLOSEREQ", 6762306a36Sopenharmony_ci [DCCP_TIME_WAIT] = "TIME_WAIT", 6862306a36Sopenharmony_ci [DCCP_CLOSED] = "CLOSED", 6962306a36Sopenharmony_ci }; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci if (state >= DCCP_MAX_STATES) 7262306a36Sopenharmony_ci return "INVALID STATE!"; 7362306a36Sopenharmony_ci else 7462306a36Sopenharmony_ci return dccp_state_names[state]; 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci#endif 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_civoid dccp_set_state(struct sock *sk, const int state) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci const int oldstate = sk->sk_state; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci dccp_pr_debug("%s(%p) %s --> %s\n", dccp_role(sk), sk, 8362306a36Sopenharmony_ci dccp_state_name(oldstate), dccp_state_name(state)); 8462306a36Sopenharmony_ci WARN_ON(state == oldstate); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci switch (state) { 8762306a36Sopenharmony_ci case DCCP_OPEN: 8862306a36Sopenharmony_ci if (oldstate != DCCP_OPEN) 8962306a36Sopenharmony_ci DCCP_INC_STATS(DCCP_MIB_CURRESTAB); 9062306a36Sopenharmony_ci /* Client retransmits all Confirm options until entering OPEN */ 9162306a36Sopenharmony_ci if (oldstate == DCCP_PARTOPEN) 9262306a36Sopenharmony_ci dccp_feat_list_purge(&dccp_sk(sk)->dccps_featneg); 9362306a36Sopenharmony_ci break; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci case DCCP_CLOSED: 9662306a36Sopenharmony_ci if (oldstate == DCCP_OPEN || oldstate == DCCP_ACTIVE_CLOSEREQ || 9762306a36Sopenharmony_ci oldstate == DCCP_CLOSING) 9862306a36Sopenharmony_ci DCCP_INC_STATS(DCCP_MIB_ESTABRESETS); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci sk->sk_prot->unhash(sk); 10162306a36Sopenharmony_ci if (inet_csk(sk)->icsk_bind_hash != NULL && 10262306a36Sopenharmony_ci !(sk->sk_userlocks & SOCK_BINDPORT_LOCK)) 10362306a36Sopenharmony_ci inet_put_port(sk); 10462306a36Sopenharmony_ci fallthrough; 10562306a36Sopenharmony_ci default: 10662306a36Sopenharmony_ci if (oldstate == DCCP_OPEN) 10762306a36Sopenharmony_ci DCCP_DEC_STATS(DCCP_MIB_CURRESTAB); 10862306a36Sopenharmony_ci } 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci /* Change state AFTER socket is unhashed to avoid closed 11162306a36Sopenharmony_ci * socket sitting in hash tables. 11262306a36Sopenharmony_ci */ 11362306a36Sopenharmony_ci inet_sk_set_state(sk, state); 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_set_state); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic void dccp_finish_passive_close(struct sock *sk) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci switch (sk->sk_state) { 12162306a36Sopenharmony_ci case DCCP_PASSIVE_CLOSE: 12262306a36Sopenharmony_ci /* Node (client or server) has received Close packet. */ 12362306a36Sopenharmony_ci dccp_send_reset(sk, DCCP_RESET_CODE_CLOSED); 12462306a36Sopenharmony_ci dccp_set_state(sk, DCCP_CLOSED); 12562306a36Sopenharmony_ci break; 12662306a36Sopenharmony_ci case DCCP_PASSIVE_CLOSEREQ: 12762306a36Sopenharmony_ci /* 12862306a36Sopenharmony_ci * Client received CloseReq. We set the `active' flag so that 12962306a36Sopenharmony_ci * dccp_send_close() retransmits the Close as per RFC 4340, 8.3. 13062306a36Sopenharmony_ci */ 13162306a36Sopenharmony_ci dccp_send_close(sk, 1); 13262306a36Sopenharmony_ci dccp_set_state(sk, DCCP_CLOSING); 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_civoid dccp_done(struct sock *sk) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci dccp_set_state(sk, DCCP_CLOSED); 13962306a36Sopenharmony_ci dccp_clear_xmit_timers(sk); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci sk->sk_shutdown = SHUTDOWN_MASK; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (!sock_flag(sk, SOCK_DEAD)) 14462306a36Sopenharmony_ci sk->sk_state_change(sk); 14562306a36Sopenharmony_ci else 14662306a36Sopenharmony_ci inet_csk_destroy_sock(sk); 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_done); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ciconst char *dccp_packet_name(const int type) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci static const char *const dccp_packet_names[] = { 15462306a36Sopenharmony_ci [DCCP_PKT_REQUEST] = "REQUEST", 15562306a36Sopenharmony_ci [DCCP_PKT_RESPONSE] = "RESPONSE", 15662306a36Sopenharmony_ci [DCCP_PKT_DATA] = "DATA", 15762306a36Sopenharmony_ci [DCCP_PKT_ACK] = "ACK", 15862306a36Sopenharmony_ci [DCCP_PKT_DATAACK] = "DATAACK", 15962306a36Sopenharmony_ci [DCCP_PKT_CLOSEREQ] = "CLOSEREQ", 16062306a36Sopenharmony_ci [DCCP_PKT_CLOSE] = "CLOSE", 16162306a36Sopenharmony_ci [DCCP_PKT_RESET] = "RESET", 16262306a36Sopenharmony_ci [DCCP_PKT_SYNC] = "SYNC", 16362306a36Sopenharmony_ci [DCCP_PKT_SYNCACK] = "SYNCACK", 16462306a36Sopenharmony_ci }; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci if (type >= DCCP_NR_PKT_TYPES) 16762306a36Sopenharmony_ci return "INVALID"; 16862306a36Sopenharmony_ci else 16962306a36Sopenharmony_ci return dccp_packet_names[type]; 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_packet_name); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_civoid dccp_destruct_common(struct sock *sk) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci struct dccp_sock *dp = dccp_sk(sk); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk); 17962306a36Sopenharmony_ci dp->dccps_hc_tx_ccid = NULL; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_destruct_common); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic void dccp_sk_destruct(struct sock *sk) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci dccp_destruct_common(sk); 18662306a36Sopenharmony_ci inet_sock_destruct(sk); 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ciint dccp_init_sock(struct sock *sk, const __u8 ctl_sock_initialized) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci struct dccp_sock *dp = dccp_sk(sk); 19262306a36Sopenharmony_ci struct inet_connection_sock *icsk = inet_csk(sk); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci pr_warn_once("DCCP is deprecated and scheduled to be removed in 2025, " 19562306a36Sopenharmony_ci "please contact the netdev mailing list\n"); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci icsk->icsk_rto = DCCP_TIMEOUT_INIT; 19862306a36Sopenharmony_ci icsk->icsk_syn_retries = sysctl_dccp_request_retries; 19962306a36Sopenharmony_ci sk->sk_state = DCCP_CLOSED; 20062306a36Sopenharmony_ci sk->sk_write_space = dccp_write_space; 20162306a36Sopenharmony_ci sk->sk_destruct = dccp_sk_destruct; 20262306a36Sopenharmony_ci icsk->icsk_sync_mss = dccp_sync_mss; 20362306a36Sopenharmony_ci dp->dccps_mss_cache = 536; 20462306a36Sopenharmony_ci dp->dccps_rate_last = jiffies; 20562306a36Sopenharmony_ci dp->dccps_role = DCCP_ROLE_UNDEFINED; 20662306a36Sopenharmony_ci dp->dccps_service = DCCP_SERVICE_CODE_IS_ABSENT; 20762306a36Sopenharmony_ci dp->dccps_tx_qlen = sysctl_dccp_tx_qlen; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci dccp_init_xmit_timers(sk); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci INIT_LIST_HEAD(&dp->dccps_featneg); 21262306a36Sopenharmony_ci /* control socket doesn't need feat nego */ 21362306a36Sopenharmony_ci if (likely(ctl_sock_initialized)) 21462306a36Sopenharmony_ci return dccp_feat_init(sk); 21562306a36Sopenharmony_ci return 0; 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_init_sock); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_civoid dccp_destroy_sock(struct sock *sk) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci struct dccp_sock *dp = dccp_sk(sk); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci __skb_queue_purge(&sk->sk_write_queue); 22562306a36Sopenharmony_ci if (sk->sk_send_head != NULL) { 22662306a36Sopenharmony_ci kfree_skb(sk->sk_send_head); 22762306a36Sopenharmony_ci sk->sk_send_head = NULL; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci /* Clean up a referenced DCCP bind bucket. */ 23162306a36Sopenharmony_ci if (inet_csk(sk)->icsk_bind_hash != NULL) 23262306a36Sopenharmony_ci inet_put_port(sk); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci kfree(dp->dccps_service_list); 23562306a36Sopenharmony_ci dp->dccps_service_list = NULL; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci if (dp->dccps_hc_rx_ackvec != NULL) { 23862306a36Sopenharmony_ci dccp_ackvec_free(dp->dccps_hc_rx_ackvec); 23962306a36Sopenharmony_ci dp->dccps_hc_rx_ackvec = NULL; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk); 24262306a36Sopenharmony_ci dp->dccps_hc_rx_ccid = NULL; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci /* clean up feature negotiation state */ 24562306a36Sopenharmony_ci dccp_feat_list_purge(&dp->dccps_featneg); 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_destroy_sock); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic inline int dccp_need_reset(int state) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci return state != DCCP_CLOSED && state != DCCP_LISTEN && 25362306a36Sopenharmony_ci state != DCCP_REQUESTING; 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ciint dccp_disconnect(struct sock *sk, int flags) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci struct inet_connection_sock *icsk = inet_csk(sk); 25962306a36Sopenharmony_ci struct inet_sock *inet = inet_sk(sk); 26062306a36Sopenharmony_ci struct dccp_sock *dp = dccp_sk(sk); 26162306a36Sopenharmony_ci const int old_state = sk->sk_state; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (old_state != DCCP_CLOSED) 26462306a36Sopenharmony_ci dccp_set_state(sk, DCCP_CLOSED); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci /* 26762306a36Sopenharmony_ci * This corresponds to the ABORT function of RFC793, sec. 3.8 26862306a36Sopenharmony_ci * TCP uses a RST segment, DCCP a Reset packet with Code 2, "Aborted". 26962306a36Sopenharmony_ci */ 27062306a36Sopenharmony_ci if (old_state == DCCP_LISTEN) { 27162306a36Sopenharmony_ci inet_csk_listen_stop(sk); 27262306a36Sopenharmony_ci } else if (dccp_need_reset(old_state)) { 27362306a36Sopenharmony_ci dccp_send_reset(sk, DCCP_RESET_CODE_ABORTED); 27462306a36Sopenharmony_ci sk->sk_err = ECONNRESET; 27562306a36Sopenharmony_ci } else if (old_state == DCCP_REQUESTING) 27662306a36Sopenharmony_ci sk->sk_err = ECONNRESET; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci dccp_clear_xmit_timers(sk); 27962306a36Sopenharmony_ci ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk); 28062306a36Sopenharmony_ci dp->dccps_hc_rx_ccid = NULL; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci __skb_queue_purge(&sk->sk_receive_queue); 28362306a36Sopenharmony_ci __skb_queue_purge(&sk->sk_write_queue); 28462306a36Sopenharmony_ci if (sk->sk_send_head != NULL) { 28562306a36Sopenharmony_ci __kfree_skb(sk->sk_send_head); 28662306a36Sopenharmony_ci sk->sk_send_head = NULL; 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci inet->inet_dport = 0; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci inet_bhash2_reset_saddr(sk); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci sk->sk_shutdown = 0; 29462306a36Sopenharmony_ci sock_reset_flag(sk, SOCK_DONE); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci icsk->icsk_backoff = 0; 29762306a36Sopenharmony_ci inet_csk_delack_init(sk); 29862306a36Sopenharmony_ci __sk_dst_reset(sk); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci WARN_ON(inet->inet_num && !icsk->icsk_bind_hash); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci sk_error_report(sk); 30362306a36Sopenharmony_ci return 0; 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_disconnect); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci/* 30962306a36Sopenharmony_ci * Wait for a DCCP event. 31062306a36Sopenharmony_ci * 31162306a36Sopenharmony_ci * Note that we don't need to lock the socket, as the upper poll layers 31262306a36Sopenharmony_ci * take care of normal races (between the test and the event) and we don't 31362306a36Sopenharmony_ci * go look at any of the socket buffers directly. 31462306a36Sopenharmony_ci */ 31562306a36Sopenharmony_ci__poll_t dccp_poll(struct file *file, struct socket *sock, 31662306a36Sopenharmony_ci poll_table *wait) 31762306a36Sopenharmony_ci{ 31862306a36Sopenharmony_ci struct sock *sk = sock->sk; 31962306a36Sopenharmony_ci __poll_t mask; 32062306a36Sopenharmony_ci u8 shutdown; 32162306a36Sopenharmony_ci int state; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci sock_poll_wait(file, sock, wait); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci state = inet_sk_state_load(sk); 32662306a36Sopenharmony_ci if (state == DCCP_LISTEN) 32762306a36Sopenharmony_ci return inet_csk_listen_poll(sk); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci /* Socket is not locked. We are protected from async events 33062306a36Sopenharmony_ci by poll logic and correct handling of state changes 33162306a36Sopenharmony_ci made by another threads is impossible in any case. 33262306a36Sopenharmony_ci */ 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci mask = 0; 33562306a36Sopenharmony_ci if (READ_ONCE(sk->sk_err)) 33662306a36Sopenharmony_ci mask = EPOLLERR; 33762306a36Sopenharmony_ci shutdown = READ_ONCE(sk->sk_shutdown); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci if (shutdown == SHUTDOWN_MASK || state == DCCP_CLOSED) 34062306a36Sopenharmony_ci mask |= EPOLLHUP; 34162306a36Sopenharmony_ci if (shutdown & RCV_SHUTDOWN) 34262306a36Sopenharmony_ci mask |= EPOLLIN | EPOLLRDNORM | EPOLLRDHUP; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci /* Connected? */ 34562306a36Sopenharmony_ci if ((1 << state) & ~(DCCPF_REQUESTING | DCCPF_RESPOND)) { 34662306a36Sopenharmony_ci if (atomic_read(&sk->sk_rmem_alloc) > 0) 34762306a36Sopenharmony_ci mask |= EPOLLIN | EPOLLRDNORM; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci if (!(shutdown & SEND_SHUTDOWN)) { 35062306a36Sopenharmony_ci if (sk_stream_is_writeable(sk)) { 35162306a36Sopenharmony_ci mask |= EPOLLOUT | EPOLLWRNORM; 35262306a36Sopenharmony_ci } else { /* send SIGIO later */ 35362306a36Sopenharmony_ci sk_set_bit(SOCKWQ_ASYNC_NOSPACE, sk); 35462306a36Sopenharmony_ci set_bit(SOCK_NOSPACE, &sk->sk_socket->flags); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci /* Race breaker. If space is freed after 35762306a36Sopenharmony_ci * wspace test but before the flags are set, 35862306a36Sopenharmony_ci * IO signal will be lost. 35962306a36Sopenharmony_ci */ 36062306a36Sopenharmony_ci if (sk_stream_is_writeable(sk)) 36162306a36Sopenharmony_ci mask |= EPOLLOUT | EPOLLWRNORM; 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci return mask; 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_poll); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ciint dccp_ioctl(struct sock *sk, int cmd, int *karg) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci int rc = -ENOTCONN; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci lock_sock(sk); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci if (sk->sk_state == DCCP_LISTEN) 37662306a36Sopenharmony_ci goto out; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci switch (cmd) { 37962306a36Sopenharmony_ci case SIOCOUTQ: { 38062306a36Sopenharmony_ci *karg = sk_wmem_alloc_get(sk); 38162306a36Sopenharmony_ci /* Using sk_wmem_alloc here because sk_wmem_queued is not used by DCCP and 38262306a36Sopenharmony_ci * always 0, comparably to UDP. 38362306a36Sopenharmony_ci */ 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci rc = 0; 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci break; 38862306a36Sopenharmony_ci case SIOCINQ: { 38962306a36Sopenharmony_ci struct sk_buff *skb; 39062306a36Sopenharmony_ci *karg = 0; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci skb = skb_peek(&sk->sk_receive_queue); 39362306a36Sopenharmony_ci if (skb != NULL) { 39462306a36Sopenharmony_ci /* 39562306a36Sopenharmony_ci * We will only return the amount of this packet since 39662306a36Sopenharmony_ci * that is all that will be read. 39762306a36Sopenharmony_ci */ 39862306a36Sopenharmony_ci *karg = skb->len; 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci rc = 0; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci break; 40362306a36Sopenharmony_ci default: 40462306a36Sopenharmony_ci rc = -ENOIOCTLCMD; 40562306a36Sopenharmony_ci break; 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ciout: 40862306a36Sopenharmony_ci release_sock(sk); 40962306a36Sopenharmony_ci return rc; 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_ioctl); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_cistatic int dccp_setsockopt_service(struct sock *sk, const __be32 service, 41562306a36Sopenharmony_ci sockptr_t optval, unsigned int optlen) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci struct dccp_sock *dp = dccp_sk(sk); 41862306a36Sopenharmony_ci struct dccp_service_list *sl = NULL; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci if (service == DCCP_SERVICE_INVALID_VALUE || 42162306a36Sopenharmony_ci optlen > DCCP_SERVICE_LIST_MAX_LEN * sizeof(u32)) 42262306a36Sopenharmony_ci return -EINVAL; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci if (optlen > sizeof(service)) { 42562306a36Sopenharmony_ci sl = kmalloc(optlen, GFP_KERNEL); 42662306a36Sopenharmony_ci if (sl == NULL) 42762306a36Sopenharmony_ci return -ENOMEM; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci sl->dccpsl_nr = optlen / sizeof(u32) - 1; 43062306a36Sopenharmony_ci if (copy_from_sockptr_offset(sl->dccpsl_list, optval, 43162306a36Sopenharmony_ci sizeof(service), optlen - sizeof(service)) || 43262306a36Sopenharmony_ci dccp_list_has_service(sl, DCCP_SERVICE_INVALID_VALUE)) { 43362306a36Sopenharmony_ci kfree(sl); 43462306a36Sopenharmony_ci return -EFAULT; 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci lock_sock(sk); 43962306a36Sopenharmony_ci dp->dccps_service = service; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci kfree(dp->dccps_service_list); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci dp->dccps_service_list = sl; 44462306a36Sopenharmony_ci release_sock(sk); 44562306a36Sopenharmony_ci return 0; 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_cistatic int dccp_setsockopt_cscov(struct sock *sk, int cscov, bool rx) 44962306a36Sopenharmony_ci{ 45062306a36Sopenharmony_ci u8 *list, len; 45162306a36Sopenharmony_ci int i, rc; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci if (cscov < 0 || cscov > 15) 45462306a36Sopenharmony_ci return -EINVAL; 45562306a36Sopenharmony_ci /* 45662306a36Sopenharmony_ci * Populate a list of permissible values, in the range cscov...15. This 45762306a36Sopenharmony_ci * is necessary since feature negotiation of single values only works if 45862306a36Sopenharmony_ci * both sides incidentally choose the same value. Since the list starts 45962306a36Sopenharmony_ci * lowest-value first, negotiation will pick the smallest shared value. 46062306a36Sopenharmony_ci */ 46162306a36Sopenharmony_ci if (cscov == 0) 46262306a36Sopenharmony_ci return 0; 46362306a36Sopenharmony_ci len = 16 - cscov; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci list = kmalloc(len, GFP_KERNEL); 46662306a36Sopenharmony_ci if (list == NULL) 46762306a36Sopenharmony_ci return -ENOBUFS; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci for (i = 0; i < len; i++) 47062306a36Sopenharmony_ci list[i] = cscov++; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci rc = dccp_feat_register_sp(sk, DCCPF_MIN_CSUM_COVER, rx, list, len); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci if (rc == 0) { 47562306a36Sopenharmony_ci if (rx) 47662306a36Sopenharmony_ci dccp_sk(sk)->dccps_pcrlen = cscov; 47762306a36Sopenharmony_ci else 47862306a36Sopenharmony_ci dccp_sk(sk)->dccps_pcslen = cscov; 47962306a36Sopenharmony_ci } 48062306a36Sopenharmony_ci kfree(list); 48162306a36Sopenharmony_ci return rc; 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_cistatic int dccp_setsockopt_ccid(struct sock *sk, int type, 48562306a36Sopenharmony_ci sockptr_t optval, unsigned int optlen) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci u8 *val; 48862306a36Sopenharmony_ci int rc = 0; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci if (optlen < 1 || optlen > DCCP_FEAT_MAX_SP_VALS) 49162306a36Sopenharmony_ci return -EINVAL; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci val = memdup_sockptr(optval, optlen); 49462306a36Sopenharmony_ci if (IS_ERR(val)) 49562306a36Sopenharmony_ci return PTR_ERR(val); 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci lock_sock(sk); 49862306a36Sopenharmony_ci if (type == DCCP_SOCKOPT_TX_CCID || type == DCCP_SOCKOPT_CCID) 49962306a36Sopenharmony_ci rc = dccp_feat_register_sp(sk, DCCPF_CCID, 1, val, optlen); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci if (!rc && (type == DCCP_SOCKOPT_RX_CCID || type == DCCP_SOCKOPT_CCID)) 50262306a36Sopenharmony_ci rc = dccp_feat_register_sp(sk, DCCPF_CCID, 0, val, optlen); 50362306a36Sopenharmony_ci release_sock(sk); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci kfree(val); 50662306a36Sopenharmony_ci return rc; 50762306a36Sopenharmony_ci} 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_cistatic int do_dccp_setsockopt(struct sock *sk, int level, int optname, 51062306a36Sopenharmony_ci sockptr_t optval, unsigned int optlen) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci struct dccp_sock *dp = dccp_sk(sk); 51362306a36Sopenharmony_ci int val, err = 0; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci switch (optname) { 51662306a36Sopenharmony_ci case DCCP_SOCKOPT_PACKET_SIZE: 51762306a36Sopenharmony_ci DCCP_WARN("sockopt(PACKET_SIZE) is deprecated: fix your app\n"); 51862306a36Sopenharmony_ci return 0; 51962306a36Sopenharmony_ci case DCCP_SOCKOPT_CHANGE_L: 52062306a36Sopenharmony_ci case DCCP_SOCKOPT_CHANGE_R: 52162306a36Sopenharmony_ci DCCP_WARN("sockopt(CHANGE_L/R) is deprecated: fix your app\n"); 52262306a36Sopenharmony_ci return 0; 52362306a36Sopenharmony_ci case DCCP_SOCKOPT_CCID: 52462306a36Sopenharmony_ci case DCCP_SOCKOPT_RX_CCID: 52562306a36Sopenharmony_ci case DCCP_SOCKOPT_TX_CCID: 52662306a36Sopenharmony_ci return dccp_setsockopt_ccid(sk, optname, optval, optlen); 52762306a36Sopenharmony_ci } 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci if (optlen < (int)sizeof(int)) 53062306a36Sopenharmony_ci return -EINVAL; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci if (copy_from_sockptr(&val, optval, sizeof(int))) 53362306a36Sopenharmony_ci return -EFAULT; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci if (optname == DCCP_SOCKOPT_SERVICE) 53662306a36Sopenharmony_ci return dccp_setsockopt_service(sk, val, optval, optlen); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci lock_sock(sk); 53962306a36Sopenharmony_ci switch (optname) { 54062306a36Sopenharmony_ci case DCCP_SOCKOPT_SERVER_TIMEWAIT: 54162306a36Sopenharmony_ci if (dp->dccps_role != DCCP_ROLE_SERVER) 54262306a36Sopenharmony_ci err = -EOPNOTSUPP; 54362306a36Sopenharmony_ci else 54462306a36Sopenharmony_ci dp->dccps_server_timewait = (val != 0); 54562306a36Sopenharmony_ci break; 54662306a36Sopenharmony_ci case DCCP_SOCKOPT_SEND_CSCOV: 54762306a36Sopenharmony_ci err = dccp_setsockopt_cscov(sk, val, false); 54862306a36Sopenharmony_ci break; 54962306a36Sopenharmony_ci case DCCP_SOCKOPT_RECV_CSCOV: 55062306a36Sopenharmony_ci err = dccp_setsockopt_cscov(sk, val, true); 55162306a36Sopenharmony_ci break; 55262306a36Sopenharmony_ci case DCCP_SOCKOPT_QPOLICY_ID: 55362306a36Sopenharmony_ci if (sk->sk_state != DCCP_CLOSED) 55462306a36Sopenharmony_ci err = -EISCONN; 55562306a36Sopenharmony_ci else if (val < 0 || val >= DCCPQ_POLICY_MAX) 55662306a36Sopenharmony_ci err = -EINVAL; 55762306a36Sopenharmony_ci else 55862306a36Sopenharmony_ci dp->dccps_qpolicy = val; 55962306a36Sopenharmony_ci break; 56062306a36Sopenharmony_ci case DCCP_SOCKOPT_QPOLICY_TXQLEN: 56162306a36Sopenharmony_ci if (val < 0) 56262306a36Sopenharmony_ci err = -EINVAL; 56362306a36Sopenharmony_ci else 56462306a36Sopenharmony_ci dp->dccps_tx_qlen = val; 56562306a36Sopenharmony_ci break; 56662306a36Sopenharmony_ci default: 56762306a36Sopenharmony_ci err = -ENOPROTOOPT; 56862306a36Sopenharmony_ci break; 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci release_sock(sk); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci return err; 57362306a36Sopenharmony_ci} 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ciint dccp_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval, 57662306a36Sopenharmony_ci unsigned int optlen) 57762306a36Sopenharmony_ci{ 57862306a36Sopenharmony_ci if (level != SOL_DCCP) 57962306a36Sopenharmony_ci return inet_csk(sk)->icsk_af_ops->setsockopt(sk, level, 58062306a36Sopenharmony_ci optname, optval, 58162306a36Sopenharmony_ci optlen); 58262306a36Sopenharmony_ci return do_dccp_setsockopt(sk, level, optname, optval, optlen); 58362306a36Sopenharmony_ci} 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_setsockopt); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_cistatic int dccp_getsockopt_service(struct sock *sk, int len, 58862306a36Sopenharmony_ci __be32 __user *optval, 58962306a36Sopenharmony_ci int __user *optlen) 59062306a36Sopenharmony_ci{ 59162306a36Sopenharmony_ci const struct dccp_sock *dp = dccp_sk(sk); 59262306a36Sopenharmony_ci const struct dccp_service_list *sl; 59362306a36Sopenharmony_ci int err = -ENOENT, slen = 0, total_len = sizeof(u32); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci lock_sock(sk); 59662306a36Sopenharmony_ci if ((sl = dp->dccps_service_list) != NULL) { 59762306a36Sopenharmony_ci slen = sl->dccpsl_nr * sizeof(u32); 59862306a36Sopenharmony_ci total_len += slen; 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci err = -EINVAL; 60262306a36Sopenharmony_ci if (total_len > len) 60362306a36Sopenharmony_ci goto out; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci err = 0; 60662306a36Sopenharmony_ci if (put_user(total_len, optlen) || 60762306a36Sopenharmony_ci put_user(dp->dccps_service, optval) || 60862306a36Sopenharmony_ci (sl != NULL && copy_to_user(optval + 1, sl->dccpsl_list, slen))) 60962306a36Sopenharmony_ci err = -EFAULT; 61062306a36Sopenharmony_ciout: 61162306a36Sopenharmony_ci release_sock(sk); 61262306a36Sopenharmony_ci return err; 61362306a36Sopenharmony_ci} 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_cistatic int do_dccp_getsockopt(struct sock *sk, int level, int optname, 61662306a36Sopenharmony_ci char __user *optval, int __user *optlen) 61762306a36Sopenharmony_ci{ 61862306a36Sopenharmony_ci struct dccp_sock *dp; 61962306a36Sopenharmony_ci int val, len; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci if (get_user(len, optlen)) 62262306a36Sopenharmony_ci return -EFAULT; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci if (len < (int)sizeof(int)) 62562306a36Sopenharmony_ci return -EINVAL; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci dp = dccp_sk(sk); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci switch (optname) { 63062306a36Sopenharmony_ci case DCCP_SOCKOPT_PACKET_SIZE: 63162306a36Sopenharmony_ci DCCP_WARN("sockopt(PACKET_SIZE) is deprecated: fix your app\n"); 63262306a36Sopenharmony_ci return 0; 63362306a36Sopenharmony_ci case DCCP_SOCKOPT_SERVICE: 63462306a36Sopenharmony_ci return dccp_getsockopt_service(sk, len, 63562306a36Sopenharmony_ci (__be32 __user *)optval, optlen); 63662306a36Sopenharmony_ci case DCCP_SOCKOPT_GET_CUR_MPS: 63762306a36Sopenharmony_ci val = READ_ONCE(dp->dccps_mss_cache); 63862306a36Sopenharmony_ci break; 63962306a36Sopenharmony_ci case DCCP_SOCKOPT_AVAILABLE_CCIDS: 64062306a36Sopenharmony_ci return ccid_getsockopt_builtin_ccids(sk, len, optval, optlen); 64162306a36Sopenharmony_ci case DCCP_SOCKOPT_TX_CCID: 64262306a36Sopenharmony_ci val = ccid_get_current_tx_ccid(dp); 64362306a36Sopenharmony_ci if (val < 0) 64462306a36Sopenharmony_ci return -ENOPROTOOPT; 64562306a36Sopenharmony_ci break; 64662306a36Sopenharmony_ci case DCCP_SOCKOPT_RX_CCID: 64762306a36Sopenharmony_ci val = ccid_get_current_rx_ccid(dp); 64862306a36Sopenharmony_ci if (val < 0) 64962306a36Sopenharmony_ci return -ENOPROTOOPT; 65062306a36Sopenharmony_ci break; 65162306a36Sopenharmony_ci case DCCP_SOCKOPT_SERVER_TIMEWAIT: 65262306a36Sopenharmony_ci val = dp->dccps_server_timewait; 65362306a36Sopenharmony_ci break; 65462306a36Sopenharmony_ci case DCCP_SOCKOPT_SEND_CSCOV: 65562306a36Sopenharmony_ci val = dp->dccps_pcslen; 65662306a36Sopenharmony_ci break; 65762306a36Sopenharmony_ci case DCCP_SOCKOPT_RECV_CSCOV: 65862306a36Sopenharmony_ci val = dp->dccps_pcrlen; 65962306a36Sopenharmony_ci break; 66062306a36Sopenharmony_ci case DCCP_SOCKOPT_QPOLICY_ID: 66162306a36Sopenharmony_ci val = dp->dccps_qpolicy; 66262306a36Sopenharmony_ci break; 66362306a36Sopenharmony_ci case DCCP_SOCKOPT_QPOLICY_TXQLEN: 66462306a36Sopenharmony_ci val = dp->dccps_tx_qlen; 66562306a36Sopenharmony_ci break; 66662306a36Sopenharmony_ci case 128 ... 191: 66762306a36Sopenharmony_ci return ccid_hc_rx_getsockopt(dp->dccps_hc_rx_ccid, sk, optname, 66862306a36Sopenharmony_ci len, (u32 __user *)optval, optlen); 66962306a36Sopenharmony_ci case 192 ... 255: 67062306a36Sopenharmony_ci return ccid_hc_tx_getsockopt(dp->dccps_hc_tx_ccid, sk, optname, 67162306a36Sopenharmony_ci len, (u32 __user *)optval, optlen); 67262306a36Sopenharmony_ci default: 67362306a36Sopenharmony_ci return -ENOPROTOOPT; 67462306a36Sopenharmony_ci } 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci len = sizeof(val); 67762306a36Sopenharmony_ci if (put_user(len, optlen) || copy_to_user(optval, &val, len)) 67862306a36Sopenharmony_ci return -EFAULT; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci return 0; 68162306a36Sopenharmony_ci} 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ciint dccp_getsockopt(struct sock *sk, int level, int optname, 68462306a36Sopenharmony_ci char __user *optval, int __user *optlen) 68562306a36Sopenharmony_ci{ 68662306a36Sopenharmony_ci if (level != SOL_DCCP) 68762306a36Sopenharmony_ci return inet_csk(sk)->icsk_af_ops->getsockopt(sk, level, 68862306a36Sopenharmony_ci optname, optval, 68962306a36Sopenharmony_ci optlen); 69062306a36Sopenharmony_ci return do_dccp_getsockopt(sk, level, optname, optval, optlen); 69162306a36Sopenharmony_ci} 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_getsockopt); 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_cistatic int dccp_msghdr_parse(struct msghdr *msg, struct sk_buff *skb) 69662306a36Sopenharmony_ci{ 69762306a36Sopenharmony_ci struct cmsghdr *cmsg; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci /* 70062306a36Sopenharmony_ci * Assign an (opaque) qpolicy priority value to skb->priority. 70162306a36Sopenharmony_ci * 70262306a36Sopenharmony_ci * We are overloading this skb field for use with the qpolicy subystem. 70362306a36Sopenharmony_ci * The skb->priority is normally used for the SO_PRIORITY option, which 70462306a36Sopenharmony_ci * is initialised from sk_priority. Since the assignment of sk_priority 70562306a36Sopenharmony_ci * to skb->priority happens later (on layer 3), we overload this field 70662306a36Sopenharmony_ci * for use with queueing priorities as long as the skb is on layer 4. 70762306a36Sopenharmony_ci * The default priority value (if nothing is set) is 0. 70862306a36Sopenharmony_ci */ 70962306a36Sopenharmony_ci skb->priority = 0; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci for_each_cmsghdr(cmsg, msg) { 71262306a36Sopenharmony_ci if (!CMSG_OK(msg, cmsg)) 71362306a36Sopenharmony_ci return -EINVAL; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci if (cmsg->cmsg_level != SOL_DCCP) 71662306a36Sopenharmony_ci continue; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci if (cmsg->cmsg_type <= DCCP_SCM_QPOLICY_MAX && 71962306a36Sopenharmony_ci !dccp_qpolicy_param_ok(skb->sk, cmsg->cmsg_type)) 72062306a36Sopenharmony_ci return -EINVAL; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci switch (cmsg->cmsg_type) { 72362306a36Sopenharmony_ci case DCCP_SCM_PRIORITY: 72462306a36Sopenharmony_ci if (cmsg->cmsg_len != CMSG_LEN(sizeof(__u32))) 72562306a36Sopenharmony_ci return -EINVAL; 72662306a36Sopenharmony_ci skb->priority = *(__u32 *)CMSG_DATA(cmsg); 72762306a36Sopenharmony_ci break; 72862306a36Sopenharmony_ci default: 72962306a36Sopenharmony_ci return -EINVAL; 73062306a36Sopenharmony_ci } 73162306a36Sopenharmony_ci } 73262306a36Sopenharmony_ci return 0; 73362306a36Sopenharmony_ci} 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ciint dccp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) 73662306a36Sopenharmony_ci{ 73762306a36Sopenharmony_ci const struct dccp_sock *dp = dccp_sk(sk); 73862306a36Sopenharmony_ci const int flags = msg->msg_flags; 73962306a36Sopenharmony_ci const int noblock = flags & MSG_DONTWAIT; 74062306a36Sopenharmony_ci struct sk_buff *skb; 74162306a36Sopenharmony_ci int rc, size; 74262306a36Sopenharmony_ci long timeo; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci trace_dccp_probe(sk, len); 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci if (len > READ_ONCE(dp->dccps_mss_cache)) 74762306a36Sopenharmony_ci return -EMSGSIZE; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci lock_sock(sk); 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci timeo = sock_sndtimeo(sk, noblock); 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci /* 75462306a36Sopenharmony_ci * We have to use sk_stream_wait_connect here to set sk_write_pending, 75562306a36Sopenharmony_ci * so that the trick in dccp_rcv_request_sent_state_process. 75662306a36Sopenharmony_ci */ 75762306a36Sopenharmony_ci /* Wait for a connection to finish. */ 75862306a36Sopenharmony_ci if ((1 << sk->sk_state) & ~(DCCPF_OPEN | DCCPF_PARTOPEN)) 75962306a36Sopenharmony_ci if ((rc = sk_stream_wait_connect(sk, &timeo)) != 0) 76062306a36Sopenharmony_ci goto out_release; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci size = sk->sk_prot->max_header + len; 76362306a36Sopenharmony_ci release_sock(sk); 76462306a36Sopenharmony_ci skb = sock_alloc_send_skb(sk, size, noblock, &rc); 76562306a36Sopenharmony_ci lock_sock(sk); 76662306a36Sopenharmony_ci if (skb == NULL) 76762306a36Sopenharmony_ci goto out_release; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci if (dccp_qpolicy_full(sk)) { 77062306a36Sopenharmony_ci rc = -EAGAIN; 77162306a36Sopenharmony_ci goto out_discard; 77262306a36Sopenharmony_ci } 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci if (sk->sk_state == DCCP_CLOSED) { 77562306a36Sopenharmony_ci rc = -ENOTCONN; 77662306a36Sopenharmony_ci goto out_discard; 77762306a36Sopenharmony_ci } 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci /* We need to check dccps_mss_cache after socket is locked. */ 78062306a36Sopenharmony_ci if (len > dp->dccps_mss_cache) { 78162306a36Sopenharmony_ci rc = -EMSGSIZE; 78262306a36Sopenharmony_ci goto out_discard; 78362306a36Sopenharmony_ci } 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci skb_reserve(skb, sk->sk_prot->max_header); 78662306a36Sopenharmony_ci rc = memcpy_from_msg(skb_put(skb, len), msg, len); 78762306a36Sopenharmony_ci if (rc != 0) 78862306a36Sopenharmony_ci goto out_discard; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci rc = dccp_msghdr_parse(msg, skb); 79162306a36Sopenharmony_ci if (rc != 0) 79262306a36Sopenharmony_ci goto out_discard; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci dccp_qpolicy_push(sk, skb); 79562306a36Sopenharmony_ci /* 79662306a36Sopenharmony_ci * The xmit_timer is set if the TX CCID is rate-based and will expire 79762306a36Sopenharmony_ci * when congestion control permits to release further packets into the 79862306a36Sopenharmony_ci * network. Window-based CCIDs do not use this timer. 79962306a36Sopenharmony_ci */ 80062306a36Sopenharmony_ci if (!timer_pending(&dp->dccps_xmit_timer)) 80162306a36Sopenharmony_ci dccp_write_xmit(sk); 80262306a36Sopenharmony_ciout_release: 80362306a36Sopenharmony_ci release_sock(sk); 80462306a36Sopenharmony_ci return rc ? : len; 80562306a36Sopenharmony_ciout_discard: 80662306a36Sopenharmony_ci kfree_skb(skb); 80762306a36Sopenharmony_ci goto out_release; 80862306a36Sopenharmony_ci} 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_sendmsg); 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ciint dccp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int flags, 81362306a36Sopenharmony_ci int *addr_len) 81462306a36Sopenharmony_ci{ 81562306a36Sopenharmony_ci const struct dccp_hdr *dh; 81662306a36Sopenharmony_ci long timeo; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci lock_sock(sk); 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci if (sk->sk_state == DCCP_LISTEN) { 82162306a36Sopenharmony_ci len = -ENOTCONN; 82262306a36Sopenharmony_ci goto out; 82362306a36Sopenharmony_ci } 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT); 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci do { 82862306a36Sopenharmony_ci struct sk_buff *skb = skb_peek(&sk->sk_receive_queue); 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci if (skb == NULL) 83162306a36Sopenharmony_ci goto verify_sock_status; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci dh = dccp_hdr(skb); 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci switch (dh->dccph_type) { 83662306a36Sopenharmony_ci case DCCP_PKT_DATA: 83762306a36Sopenharmony_ci case DCCP_PKT_DATAACK: 83862306a36Sopenharmony_ci goto found_ok_skb; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci case DCCP_PKT_CLOSE: 84162306a36Sopenharmony_ci case DCCP_PKT_CLOSEREQ: 84262306a36Sopenharmony_ci if (!(flags & MSG_PEEK)) 84362306a36Sopenharmony_ci dccp_finish_passive_close(sk); 84462306a36Sopenharmony_ci fallthrough; 84562306a36Sopenharmony_ci case DCCP_PKT_RESET: 84662306a36Sopenharmony_ci dccp_pr_debug("found fin (%s) ok!\n", 84762306a36Sopenharmony_ci dccp_packet_name(dh->dccph_type)); 84862306a36Sopenharmony_ci len = 0; 84962306a36Sopenharmony_ci goto found_fin_ok; 85062306a36Sopenharmony_ci default: 85162306a36Sopenharmony_ci dccp_pr_debug("packet_type=%s\n", 85262306a36Sopenharmony_ci dccp_packet_name(dh->dccph_type)); 85362306a36Sopenharmony_ci sk_eat_skb(sk, skb); 85462306a36Sopenharmony_ci } 85562306a36Sopenharmony_civerify_sock_status: 85662306a36Sopenharmony_ci if (sock_flag(sk, SOCK_DONE)) { 85762306a36Sopenharmony_ci len = 0; 85862306a36Sopenharmony_ci break; 85962306a36Sopenharmony_ci } 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci if (sk->sk_err) { 86262306a36Sopenharmony_ci len = sock_error(sk); 86362306a36Sopenharmony_ci break; 86462306a36Sopenharmony_ci } 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci if (sk->sk_shutdown & RCV_SHUTDOWN) { 86762306a36Sopenharmony_ci len = 0; 86862306a36Sopenharmony_ci break; 86962306a36Sopenharmony_ci } 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci if (sk->sk_state == DCCP_CLOSED) { 87262306a36Sopenharmony_ci if (!sock_flag(sk, SOCK_DONE)) { 87362306a36Sopenharmony_ci /* This occurs when user tries to read 87462306a36Sopenharmony_ci * from never connected socket. 87562306a36Sopenharmony_ci */ 87662306a36Sopenharmony_ci len = -ENOTCONN; 87762306a36Sopenharmony_ci break; 87862306a36Sopenharmony_ci } 87962306a36Sopenharmony_ci len = 0; 88062306a36Sopenharmony_ci break; 88162306a36Sopenharmony_ci } 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci if (!timeo) { 88462306a36Sopenharmony_ci len = -EAGAIN; 88562306a36Sopenharmony_ci break; 88662306a36Sopenharmony_ci } 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci if (signal_pending(current)) { 88962306a36Sopenharmony_ci len = sock_intr_errno(timeo); 89062306a36Sopenharmony_ci break; 89162306a36Sopenharmony_ci } 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci sk_wait_data(sk, &timeo, NULL); 89462306a36Sopenharmony_ci continue; 89562306a36Sopenharmony_ci found_ok_skb: 89662306a36Sopenharmony_ci if (len > skb->len) 89762306a36Sopenharmony_ci len = skb->len; 89862306a36Sopenharmony_ci else if (len < skb->len) 89962306a36Sopenharmony_ci msg->msg_flags |= MSG_TRUNC; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci if (skb_copy_datagram_msg(skb, 0, msg, len)) { 90262306a36Sopenharmony_ci /* Exception. Bailout! */ 90362306a36Sopenharmony_ci len = -EFAULT; 90462306a36Sopenharmony_ci break; 90562306a36Sopenharmony_ci } 90662306a36Sopenharmony_ci if (flags & MSG_TRUNC) 90762306a36Sopenharmony_ci len = skb->len; 90862306a36Sopenharmony_ci found_fin_ok: 90962306a36Sopenharmony_ci if (!(flags & MSG_PEEK)) 91062306a36Sopenharmony_ci sk_eat_skb(sk, skb); 91162306a36Sopenharmony_ci break; 91262306a36Sopenharmony_ci } while (1); 91362306a36Sopenharmony_ciout: 91462306a36Sopenharmony_ci release_sock(sk); 91562306a36Sopenharmony_ci return len; 91662306a36Sopenharmony_ci} 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_recvmsg); 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ciint inet_dccp_listen(struct socket *sock, int backlog) 92162306a36Sopenharmony_ci{ 92262306a36Sopenharmony_ci struct sock *sk = sock->sk; 92362306a36Sopenharmony_ci unsigned char old_state; 92462306a36Sopenharmony_ci int err; 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci lock_sock(sk); 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci err = -EINVAL; 92962306a36Sopenharmony_ci if (sock->state != SS_UNCONNECTED || sock->type != SOCK_DCCP) 93062306a36Sopenharmony_ci goto out; 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci old_state = sk->sk_state; 93362306a36Sopenharmony_ci if (!((1 << old_state) & (DCCPF_CLOSED | DCCPF_LISTEN))) 93462306a36Sopenharmony_ci goto out; 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci WRITE_ONCE(sk->sk_max_ack_backlog, backlog); 93762306a36Sopenharmony_ci /* Really, if the socket is already in listen state 93862306a36Sopenharmony_ci * we can only allow the backlog to be adjusted. 93962306a36Sopenharmony_ci */ 94062306a36Sopenharmony_ci if (old_state != DCCP_LISTEN) { 94162306a36Sopenharmony_ci struct dccp_sock *dp = dccp_sk(sk); 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci dp->dccps_role = DCCP_ROLE_LISTEN; 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci /* do not start to listen if feature negotiation setup fails */ 94662306a36Sopenharmony_ci if (dccp_feat_finalise_settings(dp)) { 94762306a36Sopenharmony_ci err = -EPROTO; 94862306a36Sopenharmony_ci goto out; 94962306a36Sopenharmony_ci } 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci err = inet_csk_listen_start(sk); 95262306a36Sopenharmony_ci if (err) 95362306a36Sopenharmony_ci goto out; 95462306a36Sopenharmony_ci } 95562306a36Sopenharmony_ci err = 0; 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ciout: 95862306a36Sopenharmony_ci release_sock(sk); 95962306a36Sopenharmony_ci return err; 96062306a36Sopenharmony_ci} 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(inet_dccp_listen); 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_cistatic void dccp_terminate_connection(struct sock *sk) 96562306a36Sopenharmony_ci{ 96662306a36Sopenharmony_ci u8 next_state = DCCP_CLOSED; 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci switch (sk->sk_state) { 96962306a36Sopenharmony_ci case DCCP_PASSIVE_CLOSE: 97062306a36Sopenharmony_ci case DCCP_PASSIVE_CLOSEREQ: 97162306a36Sopenharmony_ci dccp_finish_passive_close(sk); 97262306a36Sopenharmony_ci break; 97362306a36Sopenharmony_ci case DCCP_PARTOPEN: 97462306a36Sopenharmony_ci dccp_pr_debug("Stop PARTOPEN timer (%p)\n", sk); 97562306a36Sopenharmony_ci inet_csk_clear_xmit_timer(sk, ICSK_TIME_DACK); 97662306a36Sopenharmony_ci fallthrough; 97762306a36Sopenharmony_ci case DCCP_OPEN: 97862306a36Sopenharmony_ci dccp_send_close(sk, 1); 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci if (dccp_sk(sk)->dccps_role == DCCP_ROLE_SERVER && 98162306a36Sopenharmony_ci !dccp_sk(sk)->dccps_server_timewait) 98262306a36Sopenharmony_ci next_state = DCCP_ACTIVE_CLOSEREQ; 98362306a36Sopenharmony_ci else 98462306a36Sopenharmony_ci next_state = DCCP_CLOSING; 98562306a36Sopenharmony_ci fallthrough; 98662306a36Sopenharmony_ci default: 98762306a36Sopenharmony_ci dccp_set_state(sk, next_state); 98862306a36Sopenharmony_ci } 98962306a36Sopenharmony_ci} 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_civoid dccp_close(struct sock *sk, long timeout) 99262306a36Sopenharmony_ci{ 99362306a36Sopenharmony_ci struct dccp_sock *dp = dccp_sk(sk); 99462306a36Sopenharmony_ci struct sk_buff *skb; 99562306a36Sopenharmony_ci u32 data_was_unread = 0; 99662306a36Sopenharmony_ci int state; 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci lock_sock(sk); 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci sk->sk_shutdown = SHUTDOWN_MASK; 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci if (sk->sk_state == DCCP_LISTEN) { 100362306a36Sopenharmony_ci dccp_set_state(sk, DCCP_CLOSED); 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci /* Special case. */ 100662306a36Sopenharmony_ci inet_csk_listen_stop(sk); 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci goto adjudge_to_death; 100962306a36Sopenharmony_ci } 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci sk_stop_timer(sk, &dp->dccps_xmit_timer); 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci /* 101462306a36Sopenharmony_ci * We need to flush the recv. buffs. We do this only on the 101562306a36Sopenharmony_ci * descriptor close, not protocol-sourced closes, because the 101662306a36Sopenharmony_ci *reader process may not have drained the data yet! 101762306a36Sopenharmony_ci */ 101862306a36Sopenharmony_ci while ((skb = __skb_dequeue(&sk->sk_receive_queue)) != NULL) { 101962306a36Sopenharmony_ci data_was_unread += skb->len; 102062306a36Sopenharmony_ci __kfree_skb(skb); 102162306a36Sopenharmony_ci } 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci /* If socket has been already reset kill it. */ 102462306a36Sopenharmony_ci if (sk->sk_state == DCCP_CLOSED) 102562306a36Sopenharmony_ci goto adjudge_to_death; 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci if (data_was_unread) { 102862306a36Sopenharmony_ci /* Unread data was tossed, send an appropriate Reset Code */ 102962306a36Sopenharmony_ci DCCP_WARN("ABORT with %u bytes unread\n", data_was_unread); 103062306a36Sopenharmony_ci dccp_send_reset(sk, DCCP_RESET_CODE_ABORTED); 103162306a36Sopenharmony_ci dccp_set_state(sk, DCCP_CLOSED); 103262306a36Sopenharmony_ci } else if (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime) { 103362306a36Sopenharmony_ci /* Check zero linger _after_ checking for unread data. */ 103462306a36Sopenharmony_ci sk->sk_prot->disconnect(sk, 0); 103562306a36Sopenharmony_ci } else if (sk->sk_state != DCCP_CLOSED) { 103662306a36Sopenharmony_ci /* 103762306a36Sopenharmony_ci * Normal connection termination. May need to wait if there are 103862306a36Sopenharmony_ci * still packets in the TX queue that are delayed by the CCID. 103962306a36Sopenharmony_ci */ 104062306a36Sopenharmony_ci dccp_flush_write_queue(sk, &timeout); 104162306a36Sopenharmony_ci dccp_terminate_connection(sk); 104262306a36Sopenharmony_ci } 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci /* 104562306a36Sopenharmony_ci * Flush write queue. This may be necessary in several cases: 104662306a36Sopenharmony_ci * - we have been closed by the peer but still have application data; 104762306a36Sopenharmony_ci * - abortive termination (unread data or zero linger time), 104862306a36Sopenharmony_ci * - normal termination but queue could not be flushed within time limit 104962306a36Sopenharmony_ci */ 105062306a36Sopenharmony_ci __skb_queue_purge(&sk->sk_write_queue); 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci sk_stream_wait_close(sk, timeout); 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ciadjudge_to_death: 105562306a36Sopenharmony_ci state = sk->sk_state; 105662306a36Sopenharmony_ci sock_hold(sk); 105762306a36Sopenharmony_ci sock_orphan(sk); 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci /* 106062306a36Sopenharmony_ci * It is the last release_sock in its life. It will remove backlog. 106162306a36Sopenharmony_ci */ 106262306a36Sopenharmony_ci release_sock(sk); 106362306a36Sopenharmony_ci /* 106462306a36Sopenharmony_ci * Now socket is owned by kernel and we acquire BH lock 106562306a36Sopenharmony_ci * to finish close. No need to check for user refs. 106662306a36Sopenharmony_ci */ 106762306a36Sopenharmony_ci local_bh_disable(); 106862306a36Sopenharmony_ci bh_lock_sock(sk); 106962306a36Sopenharmony_ci WARN_ON(sock_owned_by_user(sk)); 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci this_cpu_inc(dccp_orphan_count); 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci /* Have we already been destroyed by a softirq or backlog? */ 107462306a36Sopenharmony_ci if (state != DCCP_CLOSED && sk->sk_state == DCCP_CLOSED) 107562306a36Sopenharmony_ci goto out; 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci if (sk->sk_state == DCCP_CLOSED) 107862306a36Sopenharmony_ci inet_csk_destroy_sock(sk); 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci /* Otherwise, socket is reprieved until protocol close. */ 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ciout: 108362306a36Sopenharmony_ci bh_unlock_sock(sk); 108462306a36Sopenharmony_ci local_bh_enable(); 108562306a36Sopenharmony_ci sock_put(sk); 108662306a36Sopenharmony_ci} 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_close); 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_civoid dccp_shutdown(struct sock *sk, int how) 109162306a36Sopenharmony_ci{ 109262306a36Sopenharmony_ci dccp_pr_debug("called shutdown(%x)\n", how); 109362306a36Sopenharmony_ci} 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_shutdown); 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_cistatic inline int __init dccp_mib_init(void) 109862306a36Sopenharmony_ci{ 109962306a36Sopenharmony_ci dccp_statistics = alloc_percpu(struct dccp_mib); 110062306a36Sopenharmony_ci if (!dccp_statistics) 110162306a36Sopenharmony_ci return -ENOMEM; 110262306a36Sopenharmony_ci return 0; 110362306a36Sopenharmony_ci} 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_cistatic inline void dccp_mib_exit(void) 110662306a36Sopenharmony_ci{ 110762306a36Sopenharmony_ci free_percpu(dccp_statistics); 110862306a36Sopenharmony_ci} 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_cistatic int thash_entries; 111162306a36Sopenharmony_cimodule_param(thash_entries, int, 0444); 111262306a36Sopenharmony_ciMODULE_PARM_DESC(thash_entries, "Number of ehash buckets"); 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci#ifdef CONFIG_IP_DCCP_DEBUG 111562306a36Sopenharmony_cibool dccp_debug; 111662306a36Sopenharmony_cimodule_param(dccp_debug, bool, 0644); 111762306a36Sopenharmony_ciMODULE_PARM_DESC(dccp_debug, "Enable debug messages"); 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dccp_debug); 112062306a36Sopenharmony_ci#endif 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_cistatic int __init dccp_init(void) 112362306a36Sopenharmony_ci{ 112462306a36Sopenharmony_ci unsigned long goal; 112562306a36Sopenharmony_ci unsigned long nr_pages = totalram_pages(); 112662306a36Sopenharmony_ci int ehash_order, bhash_order, i; 112762306a36Sopenharmony_ci int rc; 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(struct dccp_skb_cb) > 113062306a36Sopenharmony_ci sizeof_field(struct sk_buff, cb)); 113162306a36Sopenharmony_ci rc = inet_hashinfo2_init_mod(&dccp_hashinfo); 113262306a36Sopenharmony_ci if (rc) 113362306a36Sopenharmony_ci goto out_fail; 113462306a36Sopenharmony_ci rc = -ENOBUFS; 113562306a36Sopenharmony_ci dccp_hashinfo.bind_bucket_cachep = 113662306a36Sopenharmony_ci kmem_cache_create("dccp_bind_bucket", 113762306a36Sopenharmony_ci sizeof(struct inet_bind_bucket), 0, 113862306a36Sopenharmony_ci SLAB_HWCACHE_ALIGN | SLAB_ACCOUNT, NULL); 113962306a36Sopenharmony_ci if (!dccp_hashinfo.bind_bucket_cachep) 114062306a36Sopenharmony_ci goto out_free_hashinfo2; 114162306a36Sopenharmony_ci dccp_hashinfo.bind2_bucket_cachep = 114262306a36Sopenharmony_ci kmem_cache_create("dccp_bind2_bucket", 114362306a36Sopenharmony_ci sizeof(struct inet_bind2_bucket), 0, 114462306a36Sopenharmony_ci SLAB_HWCACHE_ALIGN | SLAB_ACCOUNT, NULL); 114562306a36Sopenharmony_ci if (!dccp_hashinfo.bind2_bucket_cachep) 114662306a36Sopenharmony_ci goto out_free_bind_bucket_cachep; 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci /* 114962306a36Sopenharmony_ci * Size and allocate the main established and bind bucket 115062306a36Sopenharmony_ci * hash tables. 115162306a36Sopenharmony_ci * 115262306a36Sopenharmony_ci * The methodology is similar to that of the buffer cache. 115362306a36Sopenharmony_ci */ 115462306a36Sopenharmony_ci if (nr_pages >= (128 * 1024)) 115562306a36Sopenharmony_ci goal = nr_pages >> (21 - PAGE_SHIFT); 115662306a36Sopenharmony_ci else 115762306a36Sopenharmony_ci goal = nr_pages >> (23 - PAGE_SHIFT); 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci if (thash_entries) 116062306a36Sopenharmony_ci goal = (thash_entries * 116162306a36Sopenharmony_ci sizeof(struct inet_ehash_bucket)) >> PAGE_SHIFT; 116262306a36Sopenharmony_ci for (ehash_order = 0; (1UL << ehash_order) < goal; ehash_order++) 116362306a36Sopenharmony_ci ; 116462306a36Sopenharmony_ci do { 116562306a36Sopenharmony_ci unsigned long hash_size = (1UL << ehash_order) * PAGE_SIZE / 116662306a36Sopenharmony_ci sizeof(struct inet_ehash_bucket); 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci while (hash_size & (hash_size - 1)) 116962306a36Sopenharmony_ci hash_size--; 117062306a36Sopenharmony_ci dccp_hashinfo.ehash_mask = hash_size - 1; 117162306a36Sopenharmony_ci dccp_hashinfo.ehash = (struct inet_ehash_bucket *) 117262306a36Sopenharmony_ci __get_free_pages(GFP_ATOMIC|__GFP_NOWARN, ehash_order); 117362306a36Sopenharmony_ci } while (!dccp_hashinfo.ehash && --ehash_order > 0); 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci if (!dccp_hashinfo.ehash) { 117662306a36Sopenharmony_ci DCCP_CRIT("Failed to allocate DCCP established hash table"); 117762306a36Sopenharmony_ci goto out_free_bind2_bucket_cachep; 117862306a36Sopenharmony_ci } 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci for (i = 0; i <= dccp_hashinfo.ehash_mask; i++) 118162306a36Sopenharmony_ci INIT_HLIST_NULLS_HEAD(&dccp_hashinfo.ehash[i].chain, i); 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci if (inet_ehash_locks_alloc(&dccp_hashinfo)) 118462306a36Sopenharmony_ci goto out_free_dccp_ehash; 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci bhash_order = ehash_order; 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci do { 118962306a36Sopenharmony_ci dccp_hashinfo.bhash_size = (1UL << bhash_order) * PAGE_SIZE / 119062306a36Sopenharmony_ci sizeof(struct inet_bind_hashbucket); 119162306a36Sopenharmony_ci if ((dccp_hashinfo.bhash_size > (64 * 1024)) && 119262306a36Sopenharmony_ci bhash_order > 0) 119362306a36Sopenharmony_ci continue; 119462306a36Sopenharmony_ci dccp_hashinfo.bhash = (struct inet_bind_hashbucket *) 119562306a36Sopenharmony_ci __get_free_pages(GFP_ATOMIC|__GFP_NOWARN, bhash_order); 119662306a36Sopenharmony_ci } while (!dccp_hashinfo.bhash && --bhash_order >= 0); 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci if (!dccp_hashinfo.bhash) { 119962306a36Sopenharmony_ci DCCP_CRIT("Failed to allocate DCCP bind hash table"); 120062306a36Sopenharmony_ci goto out_free_dccp_locks; 120162306a36Sopenharmony_ci } 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci dccp_hashinfo.bhash2 = (struct inet_bind_hashbucket *) 120462306a36Sopenharmony_ci __get_free_pages(GFP_ATOMIC | __GFP_NOWARN, bhash_order); 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci if (!dccp_hashinfo.bhash2) { 120762306a36Sopenharmony_ci DCCP_CRIT("Failed to allocate DCCP bind2 hash table"); 120862306a36Sopenharmony_ci goto out_free_dccp_bhash; 120962306a36Sopenharmony_ci } 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci for (i = 0; i < dccp_hashinfo.bhash_size; i++) { 121262306a36Sopenharmony_ci spin_lock_init(&dccp_hashinfo.bhash[i].lock); 121362306a36Sopenharmony_ci INIT_HLIST_HEAD(&dccp_hashinfo.bhash[i].chain); 121462306a36Sopenharmony_ci spin_lock_init(&dccp_hashinfo.bhash2[i].lock); 121562306a36Sopenharmony_ci INIT_HLIST_HEAD(&dccp_hashinfo.bhash2[i].chain); 121662306a36Sopenharmony_ci } 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci dccp_hashinfo.pernet = false; 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci rc = dccp_mib_init(); 122162306a36Sopenharmony_ci if (rc) 122262306a36Sopenharmony_ci goto out_free_dccp_bhash2; 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci rc = dccp_ackvec_init(); 122562306a36Sopenharmony_ci if (rc) 122662306a36Sopenharmony_ci goto out_free_dccp_mib; 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci rc = dccp_sysctl_init(); 122962306a36Sopenharmony_ci if (rc) 123062306a36Sopenharmony_ci goto out_ackvec_exit; 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci rc = ccid_initialize_builtins(); 123362306a36Sopenharmony_ci if (rc) 123462306a36Sopenharmony_ci goto out_sysctl_exit; 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci dccp_timestamping_init(); 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci return 0; 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ciout_sysctl_exit: 124162306a36Sopenharmony_ci dccp_sysctl_exit(); 124262306a36Sopenharmony_ciout_ackvec_exit: 124362306a36Sopenharmony_ci dccp_ackvec_exit(); 124462306a36Sopenharmony_ciout_free_dccp_mib: 124562306a36Sopenharmony_ci dccp_mib_exit(); 124662306a36Sopenharmony_ciout_free_dccp_bhash2: 124762306a36Sopenharmony_ci free_pages((unsigned long)dccp_hashinfo.bhash2, bhash_order); 124862306a36Sopenharmony_ciout_free_dccp_bhash: 124962306a36Sopenharmony_ci free_pages((unsigned long)dccp_hashinfo.bhash, bhash_order); 125062306a36Sopenharmony_ciout_free_dccp_locks: 125162306a36Sopenharmony_ci inet_ehash_locks_free(&dccp_hashinfo); 125262306a36Sopenharmony_ciout_free_dccp_ehash: 125362306a36Sopenharmony_ci free_pages((unsigned long)dccp_hashinfo.ehash, ehash_order); 125462306a36Sopenharmony_ciout_free_bind2_bucket_cachep: 125562306a36Sopenharmony_ci kmem_cache_destroy(dccp_hashinfo.bind2_bucket_cachep); 125662306a36Sopenharmony_ciout_free_bind_bucket_cachep: 125762306a36Sopenharmony_ci kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep); 125862306a36Sopenharmony_ciout_free_hashinfo2: 125962306a36Sopenharmony_ci inet_hashinfo2_free_mod(&dccp_hashinfo); 126062306a36Sopenharmony_ciout_fail: 126162306a36Sopenharmony_ci dccp_hashinfo.bhash = NULL; 126262306a36Sopenharmony_ci dccp_hashinfo.bhash2 = NULL; 126362306a36Sopenharmony_ci dccp_hashinfo.ehash = NULL; 126462306a36Sopenharmony_ci dccp_hashinfo.bind_bucket_cachep = NULL; 126562306a36Sopenharmony_ci dccp_hashinfo.bind2_bucket_cachep = NULL; 126662306a36Sopenharmony_ci return rc; 126762306a36Sopenharmony_ci} 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_cistatic void __exit dccp_fini(void) 127062306a36Sopenharmony_ci{ 127162306a36Sopenharmony_ci int bhash_order = get_order(dccp_hashinfo.bhash_size * 127262306a36Sopenharmony_ci sizeof(struct inet_bind_hashbucket)); 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci ccid_cleanup_builtins(); 127562306a36Sopenharmony_ci dccp_mib_exit(); 127662306a36Sopenharmony_ci free_pages((unsigned long)dccp_hashinfo.bhash, bhash_order); 127762306a36Sopenharmony_ci free_pages((unsigned long)dccp_hashinfo.bhash2, bhash_order); 127862306a36Sopenharmony_ci free_pages((unsigned long)dccp_hashinfo.ehash, 127962306a36Sopenharmony_ci get_order((dccp_hashinfo.ehash_mask + 1) * 128062306a36Sopenharmony_ci sizeof(struct inet_ehash_bucket))); 128162306a36Sopenharmony_ci inet_ehash_locks_free(&dccp_hashinfo); 128262306a36Sopenharmony_ci kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep); 128362306a36Sopenharmony_ci dccp_ackvec_exit(); 128462306a36Sopenharmony_ci dccp_sysctl_exit(); 128562306a36Sopenharmony_ci inet_hashinfo2_free_mod(&dccp_hashinfo); 128662306a36Sopenharmony_ci} 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_cimodule_init(dccp_init); 128962306a36Sopenharmony_cimodule_exit(dccp_fini); 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 129262306a36Sopenharmony_ciMODULE_AUTHOR("Arnaldo Carvalho de Melo <acme@conectiva.com.br>"); 129362306a36Sopenharmony_ciMODULE_DESCRIPTION("DCCP - Datagram Congestion Controlled Protocol"); 1294