17c657876SArnaldo Carvalho de Melo /* 27c657876SArnaldo Carvalho de Melo * net/dccp/proto.c 37c657876SArnaldo Carvalho de Melo * 47c657876SArnaldo Carvalho de Melo * An implementation of the DCCP protocol 57c657876SArnaldo Carvalho de Melo * Arnaldo Carvalho de Melo <acme@conectiva.com.br> 67c657876SArnaldo Carvalho de Melo * 77c657876SArnaldo Carvalho de Melo * This program is free software; you can redistribute it and/or modify it 87c657876SArnaldo Carvalho de Melo * under the terms of the GNU General Public License version 2 as 97c657876SArnaldo Carvalho de Melo * published by the Free Software Foundation. 107c657876SArnaldo Carvalho de Melo */ 117c657876SArnaldo Carvalho de Melo 127c657876SArnaldo Carvalho de Melo #include <linux/config.h> 137c657876SArnaldo Carvalho de Melo #include <linux/dccp.h> 147c657876SArnaldo Carvalho de Melo #include <linux/module.h> 157c657876SArnaldo Carvalho de Melo #include <linux/types.h> 167c657876SArnaldo Carvalho de Melo #include <linux/sched.h> 177c657876SArnaldo Carvalho de Melo #include <linux/kernel.h> 187c657876SArnaldo Carvalho de Melo #include <linux/skbuff.h> 197c657876SArnaldo Carvalho de Melo #include <linux/netdevice.h> 207c657876SArnaldo Carvalho de Melo #include <linux/in.h> 217c657876SArnaldo Carvalho de Melo #include <linux/if_arp.h> 227c657876SArnaldo Carvalho de Melo #include <linux/init.h> 237c657876SArnaldo Carvalho de Melo #include <linux/random.h> 247c657876SArnaldo Carvalho de Melo #include <net/checksum.h> 257c657876SArnaldo Carvalho de Melo 267c657876SArnaldo Carvalho de Melo #include <net/inet_common.h> 277c657876SArnaldo Carvalho de Melo #include <net/ip.h> 287c657876SArnaldo Carvalho de Melo #include <net/protocol.h> 297c657876SArnaldo Carvalho de Melo #include <net/sock.h> 307c657876SArnaldo Carvalho de Melo #include <net/xfrm.h> 317c657876SArnaldo Carvalho de Melo 327c657876SArnaldo Carvalho de Melo #include <asm/semaphore.h> 337c657876SArnaldo Carvalho de Melo #include <linux/spinlock.h> 347c657876SArnaldo Carvalho de Melo #include <linux/timer.h> 357c657876SArnaldo Carvalho de Melo #include <linux/delay.h> 367c657876SArnaldo Carvalho de Melo #include <linux/poll.h> 377c657876SArnaldo Carvalho de Melo #include <linux/dccp.h> 387c657876SArnaldo Carvalho de Melo 397c657876SArnaldo Carvalho de Melo #include "ccid.h" 407c657876SArnaldo Carvalho de Melo #include "dccp.h" 417c657876SArnaldo Carvalho de Melo 427c657876SArnaldo Carvalho de Melo DEFINE_SNMP_STAT(struct dccp_mib, dccp_statistics); 437c657876SArnaldo Carvalho de Melo 447c657876SArnaldo Carvalho de Melo atomic_t dccp_orphan_count = ATOMIC_INIT(0); 457c657876SArnaldo Carvalho de Melo 467c657876SArnaldo Carvalho de Melo static struct net_protocol dccp_protocol = { 477c657876SArnaldo Carvalho de Melo .handler = dccp_v4_rcv, 487c657876SArnaldo Carvalho de Melo .err_handler = dccp_v4_err, 497c657876SArnaldo Carvalho de Melo }; 507c657876SArnaldo Carvalho de Melo 517c657876SArnaldo Carvalho de Melo const char *dccp_packet_name(const int type) 527c657876SArnaldo Carvalho de Melo { 537c657876SArnaldo Carvalho de Melo static const char *dccp_packet_names[] = { 547c657876SArnaldo Carvalho de Melo [DCCP_PKT_REQUEST] = "REQUEST", 557c657876SArnaldo Carvalho de Melo [DCCP_PKT_RESPONSE] = "RESPONSE", 567c657876SArnaldo Carvalho de Melo [DCCP_PKT_DATA] = "DATA", 577c657876SArnaldo Carvalho de Melo [DCCP_PKT_ACK] = "ACK", 587c657876SArnaldo Carvalho de Melo [DCCP_PKT_DATAACK] = "DATAACK", 597c657876SArnaldo Carvalho de Melo [DCCP_PKT_CLOSEREQ] = "CLOSEREQ", 607c657876SArnaldo Carvalho de Melo [DCCP_PKT_CLOSE] = "CLOSE", 617c657876SArnaldo Carvalho de Melo [DCCP_PKT_RESET] = "RESET", 627c657876SArnaldo Carvalho de Melo [DCCP_PKT_SYNC] = "SYNC", 637c657876SArnaldo Carvalho de Melo [DCCP_PKT_SYNCACK] = "SYNCACK", 647c657876SArnaldo Carvalho de Melo }; 657c657876SArnaldo Carvalho de Melo 667c657876SArnaldo Carvalho de Melo if (type >= DCCP_NR_PKT_TYPES) 677c657876SArnaldo Carvalho de Melo return "INVALID"; 687c657876SArnaldo Carvalho de Melo else 697c657876SArnaldo Carvalho de Melo return dccp_packet_names[type]; 707c657876SArnaldo Carvalho de Melo } 717c657876SArnaldo Carvalho de Melo 727c657876SArnaldo Carvalho de Melo EXPORT_SYMBOL_GPL(dccp_packet_name); 737c657876SArnaldo Carvalho de Melo 747c657876SArnaldo Carvalho de Melo const char *dccp_state_name(const int state) 757c657876SArnaldo Carvalho de Melo { 767c657876SArnaldo Carvalho de Melo static char *dccp_state_names[] = { 777c657876SArnaldo Carvalho de Melo [DCCP_OPEN] = "OPEN", 787c657876SArnaldo Carvalho de Melo [DCCP_REQUESTING] = "REQUESTING", 797c657876SArnaldo Carvalho de Melo [DCCP_PARTOPEN] = "PARTOPEN", 807c657876SArnaldo Carvalho de Melo [DCCP_LISTEN] = "LISTEN", 817c657876SArnaldo Carvalho de Melo [DCCP_RESPOND] = "RESPOND", 827c657876SArnaldo Carvalho de Melo [DCCP_CLOSING] = "CLOSING", 837c657876SArnaldo Carvalho de Melo [DCCP_TIME_WAIT] = "TIME_WAIT", 847c657876SArnaldo Carvalho de Melo [DCCP_CLOSED] = "CLOSED", 857c657876SArnaldo Carvalho de Melo }; 867c657876SArnaldo Carvalho de Melo 877c657876SArnaldo Carvalho de Melo if (state >= DCCP_MAX_STATES) 887c657876SArnaldo Carvalho de Melo return "INVALID STATE!"; 897c657876SArnaldo Carvalho de Melo else 907c657876SArnaldo Carvalho de Melo return dccp_state_names[state]; 917c657876SArnaldo Carvalho de Melo } 927c657876SArnaldo Carvalho de Melo 937c657876SArnaldo Carvalho de Melo EXPORT_SYMBOL_GPL(dccp_state_name); 947c657876SArnaldo Carvalho de Melo 957c657876SArnaldo Carvalho de Melo static inline int dccp_listen_start(struct sock *sk) 967c657876SArnaldo Carvalho de Melo { 977c657876SArnaldo Carvalho de Melo dccp_sk(sk)->dccps_role = DCCP_ROLE_LISTEN; 987c657876SArnaldo Carvalho de Melo return inet_csk_listen_start(sk, TCP_SYNQ_HSIZE); 997c657876SArnaldo Carvalho de Melo } 1007c657876SArnaldo Carvalho de Melo 1017c657876SArnaldo Carvalho de Melo int dccp_disconnect(struct sock *sk, int flags) 1027c657876SArnaldo Carvalho de Melo { 1037c657876SArnaldo Carvalho de Melo struct inet_connection_sock *icsk = inet_csk(sk); 1047c657876SArnaldo Carvalho de Melo struct inet_sock *inet = inet_sk(sk); 1057c657876SArnaldo Carvalho de Melo int err = 0; 1067c657876SArnaldo Carvalho de Melo const int old_state = sk->sk_state; 1077c657876SArnaldo Carvalho de Melo 1087c657876SArnaldo Carvalho de Melo if (old_state != DCCP_CLOSED) 1097c657876SArnaldo Carvalho de Melo dccp_set_state(sk, DCCP_CLOSED); 1107c657876SArnaldo Carvalho de Melo 1117c657876SArnaldo Carvalho de Melo /* ABORT function of RFC793 */ 1127c657876SArnaldo Carvalho de Melo if (old_state == DCCP_LISTEN) { 1137c657876SArnaldo Carvalho de Melo inet_csk_listen_stop(sk); 1147c657876SArnaldo Carvalho de Melo /* FIXME: do the active reset thing */ 1157c657876SArnaldo Carvalho de Melo } else if (old_state == DCCP_REQUESTING) 1167c657876SArnaldo Carvalho de Melo sk->sk_err = ECONNRESET; 1177c657876SArnaldo Carvalho de Melo 1187c657876SArnaldo Carvalho de Melo dccp_clear_xmit_timers(sk); 1197c657876SArnaldo Carvalho de Melo __skb_queue_purge(&sk->sk_receive_queue); 1207c657876SArnaldo Carvalho de Melo if (sk->sk_send_head != NULL) { 1217c657876SArnaldo Carvalho de Melo __kfree_skb(sk->sk_send_head); 1227c657876SArnaldo Carvalho de Melo sk->sk_send_head = NULL; 1237c657876SArnaldo Carvalho de Melo } 1247c657876SArnaldo Carvalho de Melo 1257c657876SArnaldo Carvalho de Melo inet->dport = 0; 1267c657876SArnaldo Carvalho de Melo 1277c657876SArnaldo Carvalho de Melo if (!(sk->sk_userlocks & SOCK_BINDADDR_LOCK)) 1287c657876SArnaldo Carvalho de Melo inet_reset_saddr(sk); 1297c657876SArnaldo Carvalho de Melo 1307c657876SArnaldo Carvalho de Melo sk->sk_shutdown = 0; 1317c657876SArnaldo Carvalho de Melo sock_reset_flag(sk, SOCK_DONE); 1327c657876SArnaldo Carvalho de Melo 1337c657876SArnaldo Carvalho de Melo icsk->icsk_backoff = 0; 1347c657876SArnaldo Carvalho de Melo inet_csk_delack_init(sk); 1357c657876SArnaldo Carvalho de Melo __sk_dst_reset(sk); 1367c657876SArnaldo Carvalho de Melo 1377c657876SArnaldo Carvalho de Melo BUG_TRAP(!inet->num || icsk->icsk_bind_hash); 1387c657876SArnaldo Carvalho de Melo 1397c657876SArnaldo Carvalho de Melo sk->sk_error_report(sk); 1407c657876SArnaldo Carvalho de Melo return err; 1417c657876SArnaldo Carvalho de Melo } 1427c657876SArnaldo Carvalho de Melo 1437c657876SArnaldo Carvalho de Melo int dccp_ioctl(struct sock *sk, int cmd, unsigned long arg) 1447c657876SArnaldo Carvalho de Melo { 1457c657876SArnaldo Carvalho de Melo dccp_pr_debug("entry\n"); 1467c657876SArnaldo Carvalho de Melo return -ENOIOCTLCMD; 1477c657876SArnaldo Carvalho de Melo } 1487c657876SArnaldo Carvalho de Melo 1497c657876SArnaldo Carvalho de Melo int dccp_setsockopt(struct sock *sk, int level, int optname, 1507c657876SArnaldo Carvalho de Melo char *optval, int optlen) 1517c657876SArnaldo Carvalho de Melo { 1527c657876SArnaldo Carvalho de Melo dccp_pr_debug("entry\n"); 1537c657876SArnaldo Carvalho de Melo 1547c657876SArnaldo Carvalho de Melo if (level != SOL_DCCP) 1557c657876SArnaldo Carvalho de Melo return ip_setsockopt(sk, level, optname, optval, optlen); 1567c657876SArnaldo Carvalho de Melo 1577c657876SArnaldo Carvalho de Melo return -EOPNOTSUPP; 1587c657876SArnaldo Carvalho de Melo } 1597c657876SArnaldo Carvalho de Melo 1607c657876SArnaldo Carvalho de Melo int dccp_getsockopt(struct sock *sk, int level, int optname, 1617c657876SArnaldo Carvalho de Melo char *optval, int *optlen) 1627c657876SArnaldo Carvalho de Melo { 1637c657876SArnaldo Carvalho de Melo dccp_pr_debug("entry\n"); 1647c657876SArnaldo Carvalho de Melo 1657c657876SArnaldo Carvalho de Melo if (level != SOL_DCCP) 1667c657876SArnaldo Carvalho de Melo return ip_getsockopt(sk, level, optname, optval, optlen); 1677c657876SArnaldo Carvalho de Melo 1687c657876SArnaldo Carvalho de Melo return -EOPNOTSUPP; 1697c657876SArnaldo Carvalho de Melo } 1707c657876SArnaldo Carvalho de Melo 1717c657876SArnaldo Carvalho de Melo int dccp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, 1727c657876SArnaldo Carvalho de Melo size_t len) 1737c657876SArnaldo Carvalho de Melo { 1747c657876SArnaldo Carvalho de Melo const struct dccp_sock *dp = dccp_sk(sk); 1757c657876SArnaldo Carvalho de Melo const int flags = msg->msg_flags; 1767c657876SArnaldo Carvalho de Melo const int noblock = flags & MSG_DONTWAIT; 1777c657876SArnaldo Carvalho de Melo struct sk_buff *skb; 1787c657876SArnaldo Carvalho de Melo int rc, size; 1797c657876SArnaldo Carvalho de Melo long timeo; 1807c657876SArnaldo Carvalho de Melo 1817c657876SArnaldo Carvalho de Melo if (len > dp->dccps_mss_cache) 1827c657876SArnaldo Carvalho de Melo return -EMSGSIZE; 1837c657876SArnaldo Carvalho de Melo 1847c657876SArnaldo Carvalho de Melo lock_sock(sk); 1857c657876SArnaldo Carvalho de Melo 1867c657876SArnaldo Carvalho de Melo timeo = sock_sndtimeo(sk, flags & MSG_DONTWAIT); 1877c657876SArnaldo Carvalho de Melo 1887c657876SArnaldo Carvalho de Melo /* 1897c657876SArnaldo Carvalho de Melo * We have to use sk_stream_wait_connect here to set sk_write_pending, 1907c657876SArnaldo Carvalho de Melo * so that the trick in dccp_rcv_request_sent_state_process. 1917c657876SArnaldo Carvalho de Melo */ 1927c657876SArnaldo Carvalho de Melo /* Wait for a connection to finish. */ 1937c657876SArnaldo Carvalho de Melo if ((1 << sk->sk_state) & ~(DCCPF_OPEN | DCCPF_PARTOPEN | DCCPF_CLOSING)) 1947c657876SArnaldo Carvalho de Melo if ((rc = sk_stream_wait_connect(sk, &timeo)) != 0) 1957c657876SArnaldo Carvalho de Melo goto out_err; 1967c657876SArnaldo Carvalho de Melo 1977c657876SArnaldo Carvalho de Melo size = sk->sk_prot->max_header + len; 1987c657876SArnaldo Carvalho de Melo release_sock(sk); 1997c657876SArnaldo Carvalho de Melo skb = sock_alloc_send_skb(sk, size, noblock, &rc); 2007c657876SArnaldo Carvalho de Melo lock_sock(sk); 2017c657876SArnaldo Carvalho de Melo 2027c657876SArnaldo Carvalho de Melo if (skb == NULL) 2037c657876SArnaldo Carvalho de Melo goto out_release; 2047c657876SArnaldo Carvalho de Melo 2057c657876SArnaldo Carvalho de Melo skb_reserve(skb, sk->sk_prot->max_header); 2067c657876SArnaldo Carvalho de Melo rc = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len); 2077c657876SArnaldo Carvalho de Melo if (rc == 0) { 2087c657876SArnaldo Carvalho de Melo struct dccp_skb_cb *dcb = DCCP_SKB_CB(skb); 2097c657876SArnaldo Carvalho de Melo const struct dccp_ackpkts *ap = dp->dccps_hc_rx_ackpkts; 2107c657876SArnaldo Carvalho de Melo long delay; 2117c657876SArnaldo Carvalho de Melo 2127c657876SArnaldo Carvalho de Melo /* 2137c657876SArnaldo Carvalho de Melo * XXX: This is just to match the Waikato tree CA interaction 2147c657876SArnaldo Carvalho de Melo * points, after the CCID3 code is stable and I have a better 2157c657876SArnaldo Carvalho de Melo * understanding of behaviour I'll change this to look more like 2167c657876SArnaldo Carvalho de Melo * TCP. 2177c657876SArnaldo Carvalho de Melo */ 2187c657876SArnaldo Carvalho de Melo while (1) { 2197c657876SArnaldo Carvalho de Melo rc = ccid_hc_tx_send_packet(dp->dccps_hc_tx_ccid, sk, 2207c657876SArnaldo Carvalho de Melo skb, len, &delay); 2217c657876SArnaldo Carvalho de Melo if (rc == 0) 2227c657876SArnaldo Carvalho de Melo break; 2237c657876SArnaldo Carvalho de Melo if (rc != -EAGAIN) 2247c657876SArnaldo Carvalho de Melo goto out_discard; 2257c657876SArnaldo Carvalho de Melo if (delay > timeo) 2267c657876SArnaldo Carvalho de Melo goto out_discard; 2277c657876SArnaldo Carvalho de Melo release_sock(sk); 2287c657876SArnaldo Carvalho de Melo delay = schedule_timeout(delay); 2297c657876SArnaldo Carvalho de Melo lock_sock(sk); 2307c657876SArnaldo Carvalho de Melo timeo -= delay; 2317c657876SArnaldo Carvalho de Melo if (signal_pending(current)) 2327c657876SArnaldo Carvalho de Melo goto out_interrupted; 2337c657876SArnaldo Carvalho de Melo rc = -EPIPE; 2347c657876SArnaldo Carvalho de Melo if (!(sk->sk_state == DCCP_PARTOPEN || sk->sk_state == DCCP_OPEN)) 2357c657876SArnaldo Carvalho de Melo goto out_discard; 2367c657876SArnaldo Carvalho de Melo } 2377c657876SArnaldo Carvalho de Melo 2387c657876SArnaldo Carvalho de Melo if (sk->sk_state == DCCP_PARTOPEN) { 2397c657876SArnaldo Carvalho de Melo /* See 8.1.5. Handshake Completion */ 2407c657876SArnaldo Carvalho de Melo inet_csk_schedule_ack(sk); 2417c657876SArnaldo Carvalho de Melo inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK, inet_csk(sk)->icsk_rto, TCP_RTO_MAX); 2427c657876SArnaldo Carvalho de Melo dcb->dccpd_type = DCCP_PKT_DATAACK; 2437c657876SArnaldo Carvalho de Melo /* FIXME: we really should have a dccps_ack_pending or use icsk */ 2447c657876SArnaldo Carvalho de Melo } else if (inet_csk_ack_scheduled(sk) || 2457c657876SArnaldo Carvalho de Melo (dp->dccps_options.dccpo_send_ack_vector && 2467c657876SArnaldo Carvalho de Melo ap->dccpap_buf_ackno != DCCP_MAX_SEQNO + 1 && 2477c657876SArnaldo Carvalho de Melo ap->dccpap_ack_seqno == DCCP_MAX_SEQNO + 1)) 2487c657876SArnaldo Carvalho de Melo dcb->dccpd_type = DCCP_PKT_DATAACK; 2497c657876SArnaldo Carvalho de Melo else 2507c657876SArnaldo Carvalho de Melo dcb->dccpd_type = DCCP_PKT_DATA; 2517c657876SArnaldo Carvalho de Melo dccp_transmit_skb(sk, skb); 2527c657876SArnaldo Carvalho de Melo ccid_hc_tx_packet_sent(dp->dccps_hc_tx_ccid, sk, 0, len); 2537c657876SArnaldo Carvalho de Melo } else { 2547c657876SArnaldo Carvalho de Melo out_discard: 2557c657876SArnaldo Carvalho de Melo kfree_skb(skb); 2567c657876SArnaldo Carvalho de Melo } 2577c657876SArnaldo Carvalho de Melo out_release: 2587c657876SArnaldo Carvalho de Melo release_sock(sk); 2597c657876SArnaldo Carvalho de Melo return rc ? : len; 2607c657876SArnaldo Carvalho de Melo out_err: 2617c657876SArnaldo Carvalho de Melo rc = sk_stream_error(sk, flags, rc); 2627c657876SArnaldo Carvalho de Melo goto out_release; 2637c657876SArnaldo Carvalho de Melo out_interrupted: 2647c657876SArnaldo Carvalho de Melo rc = sock_intr_errno(timeo); 2657c657876SArnaldo Carvalho de Melo goto out_discard; 2667c657876SArnaldo Carvalho de Melo } 2677c657876SArnaldo Carvalho de Melo 2687c657876SArnaldo Carvalho de Melo EXPORT_SYMBOL(dccp_sendmsg); 2697c657876SArnaldo Carvalho de Melo 2707c657876SArnaldo Carvalho de Melo int dccp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, 2717c657876SArnaldo Carvalho de Melo size_t len, int nonblock, int flags, int *addr_len) 2727c657876SArnaldo Carvalho de Melo { 2737c657876SArnaldo Carvalho de Melo const struct dccp_hdr *dh; 2747c657876SArnaldo Carvalho de Melo int copied = 0; 2757c657876SArnaldo Carvalho de Melo unsigned long used; 2767c657876SArnaldo Carvalho de Melo int err; 2777c657876SArnaldo Carvalho de Melo int target; /* Read at least this many bytes */ 2787c657876SArnaldo Carvalho de Melo long timeo; 2797c657876SArnaldo Carvalho de Melo 2807c657876SArnaldo Carvalho de Melo lock_sock(sk); 2817c657876SArnaldo Carvalho de Melo 2827c657876SArnaldo Carvalho de Melo err = -ENOTCONN; 2837c657876SArnaldo Carvalho de Melo if (sk->sk_state == DCCP_LISTEN) 2847c657876SArnaldo Carvalho de Melo goto out; 2857c657876SArnaldo Carvalho de Melo 2867c657876SArnaldo Carvalho de Melo timeo = sock_rcvtimeo(sk, nonblock); 2877c657876SArnaldo Carvalho de Melo 2887c657876SArnaldo Carvalho de Melo /* Urgent data needs to be handled specially. */ 2897c657876SArnaldo Carvalho de Melo if (flags & MSG_OOB) 2907c657876SArnaldo Carvalho de Melo goto recv_urg; 2917c657876SArnaldo Carvalho de Melo 2927c657876SArnaldo Carvalho de Melo /* FIXME */ 2937c657876SArnaldo Carvalho de Melo #if 0 2947c657876SArnaldo Carvalho de Melo seq = &tp->copied_seq; 2957c657876SArnaldo Carvalho de Melo if (flags & MSG_PEEK) { 2967c657876SArnaldo Carvalho de Melo peek_seq = tp->copied_seq; 2977c657876SArnaldo Carvalho de Melo seq = &peek_seq; 2987c657876SArnaldo Carvalho de Melo } 2997c657876SArnaldo Carvalho de Melo #endif 3007c657876SArnaldo Carvalho de Melo 3017c657876SArnaldo Carvalho de Melo target = sock_rcvlowat(sk, flags & MSG_WAITALL, len); 3027c657876SArnaldo Carvalho de Melo 3037c657876SArnaldo Carvalho de Melo do { 3047c657876SArnaldo Carvalho de Melo struct sk_buff *skb; 3057c657876SArnaldo Carvalho de Melo u32 offset; 3067c657876SArnaldo Carvalho de Melo 3077c657876SArnaldo Carvalho de Melo /* FIXME */ 3087c657876SArnaldo Carvalho de Melo #if 0 3097c657876SArnaldo Carvalho de Melo /* Are we at urgent data? Stop if we have read anything or have SIGURG pending. */ 3107c657876SArnaldo Carvalho de Melo if (tp->urg_data && tp->urg_seq == *seq) { 3117c657876SArnaldo Carvalho de Melo if (copied) 3127c657876SArnaldo Carvalho de Melo break; 3137c657876SArnaldo Carvalho de Melo if (signal_pending(current)) { 3147c657876SArnaldo Carvalho de Melo copied = timeo ? sock_intr_errno(timeo) : -EAGAIN; 3157c657876SArnaldo Carvalho de Melo break; 3167c657876SArnaldo Carvalho de Melo } 3177c657876SArnaldo Carvalho de Melo } 3187c657876SArnaldo Carvalho de Melo #endif 3197c657876SArnaldo Carvalho de Melo 3207c657876SArnaldo Carvalho de Melo /* Next get a buffer. */ 3217c657876SArnaldo Carvalho de Melo 3227c657876SArnaldo Carvalho de Melo skb = skb_peek(&sk->sk_receive_queue); 3237c657876SArnaldo Carvalho de Melo do { 3247c657876SArnaldo Carvalho de Melo if (!skb) 3257c657876SArnaldo Carvalho de Melo break; 3267c657876SArnaldo Carvalho de Melo 3277c657876SArnaldo Carvalho de Melo offset = 0; 3287c657876SArnaldo Carvalho de Melo dh = dccp_hdr(skb); 3297c657876SArnaldo Carvalho de Melo 3307c657876SArnaldo Carvalho de Melo if (dh->dccph_type == DCCP_PKT_DATA || 3317c657876SArnaldo Carvalho de Melo dh->dccph_type == DCCP_PKT_DATAACK) 3327c657876SArnaldo Carvalho de Melo goto found_ok_skb; 3337c657876SArnaldo Carvalho de Melo 3347c657876SArnaldo Carvalho de Melo if (dh->dccph_type == DCCP_PKT_RESET || 3357c657876SArnaldo Carvalho de Melo dh->dccph_type == DCCP_PKT_CLOSE) { 3367c657876SArnaldo Carvalho de Melo dccp_pr_debug("found fin ok!\n"); 3377c657876SArnaldo Carvalho de Melo goto found_fin_ok; 3387c657876SArnaldo Carvalho de Melo } 3397c657876SArnaldo Carvalho de Melo dccp_pr_debug("packet_type=%s\n", dccp_packet_name(dh->dccph_type)); 3407c657876SArnaldo Carvalho de Melo BUG_TRAP(flags & MSG_PEEK); 3417c657876SArnaldo Carvalho de Melo skb = skb->next; 3427c657876SArnaldo Carvalho de Melo } while (skb != (struct sk_buff *)&sk->sk_receive_queue); 3437c657876SArnaldo Carvalho de Melo 3447c657876SArnaldo Carvalho de Melo /* Well, if we have backlog, try to process it now yet. */ 3457c657876SArnaldo Carvalho de Melo if (copied >= target && !sk->sk_backlog.tail) 3467c657876SArnaldo Carvalho de Melo break; 3477c657876SArnaldo Carvalho de Melo 3487c657876SArnaldo Carvalho de Melo if (copied) { 3497c657876SArnaldo Carvalho de Melo if (sk->sk_err || 3507c657876SArnaldo Carvalho de Melo sk->sk_state == DCCP_CLOSED || 3517c657876SArnaldo Carvalho de Melo (sk->sk_shutdown & RCV_SHUTDOWN) || 3527c657876SArnaldo Carvalho de Melo !timeo || 3537c657876SArnaldo Carvalho de Melo signal_pending(current) || 3547c657876SArnaldo Carvalho de Melo (flags & MSG_PEEK)) 3557c657876SArnaldo Carvalho de Melo break; 3567c657876SArnaldo Carvalho de Melo } else { 3577c657876SArnaldo Carvalho de Melo if (sock_flag(sk, SOCK_DONE)) 3587c657876SArnaldo Carvalho de Melo break; 3597c657876SArnaldo Carvalho de Melo 3607c657876SArnaldo Carvalho de Melo if (sk->sk_err) { 3617c657876SArnaldo Carvalho de Melo copied = sock_error(sk); 3627c657876SArnaldo Carvalho de Melo break; 3637c657876SArnaldo Carvalho de Melo } 3647c657876SArnaldo Carvalho de Melo 3657c657876SArnaldo Carvalho de Melo if (sk->sk_shutdown & RCV_SHUTDOWN) 3667c657876SArnaldo Carvalho de Melo break; 3677c657876SArnaldo Carvalho de Melo 3687c657876SArnaldo Carvalho de Melo if (sk->sk_state == DCCP_CLOSED) { 3697c657876SArnaldo Carvalho de Melo if (!sock_flag(sk, SOCK_DONE)) { 3707c657876SArnaldo Carvalho de Melo /* This occurs when user tries to read 3717c657876SArnaldo Carvalho de Melo * from never connected socket. 3727c657876SArnaldo Carvalho de Melo */ 3737c657876SArnaldo Carvalho de Melo copied = -ENOTCONN; 3747c657876SArnaldo Carvalho de Melo break; 3757c657876SArnaldo Carvalho de Melo } 3767c657876SArnaldo Carvalho de Melo break; 3777c657876SArnaldo Carvalho de Melo } 3787c657876SArnaldo Carvalho de Melo 3797c657876SArnaldo Carvalho de Melo if (!timeo) { 3807c657876SArnaldo Carvalho de Melo copied = -EAGAIN; 3817c657876SArnaldo Carvalho de Melo break; 3827c657876SArnaldo Carvalho de Melo } 3837c657876SArnaldo Carvalho de Melo 3847c657876SArnaldo Carvalho de Melo if (signal_pending(current)) { 3857c657876SArnaldo Carvalho de Melo copied = sock_intr_errno(timeo); 3867c657876SArnaldo Carvalho de Melo break; 3877c657876SArnaldo Carvalho de Melo } 3887c657876SArnaldo Carvalho de Melo } 3897c657876SArnaldo Carvalho de Melo 3907c657876SArnaldo Carvalho de Melo /* FIXME: cleanup_rbuf(sk, copied); */ 3917c657876SArnaldo Carvalho de Melo 3927c657876SArnaldo Carvalho de Melo if (copied >= target) { 3937c657876SArnaldo Carvalho de Melo /* Do not sleep, just process backlog. */ 3947c657876SArnaldo Carvalho de Melo release_sock(sk); 3957c657876SArnaldo Carvalho de Melo lock_sock(sk); 3967c657876SArnaldo Carvalho de Melo } else 3977c657876SArnaldo Carvalho de Melo sk_wait_data(sk, &timeo); 3987c657876SArnaldo Carvalho de Melo 3997c657876SArnaldo Carvalho de Melo continue; 4007c657876SArnaldo Carvalho de Melo 4017c657876SArnaldo Carvalho de Melo found_ok_skb: 4027c657876SArnaldo Carvalho de Melo /* Ok so how much can we use? */ 4037c657876SArnaldo Carvalho de Melo used = skb->len - offset; 4047c657876SArnaldo Carvalho de Melo if (len < used) 4057c657876SArnaldo Carvalho de Melo used = len; 4067c657876SArnaldo Carvalho de Melo 4077c657876SArnaldo Carvalho de Melo if (!(flags & MSG_TRUNC)) { 4087c657876SArnaldo Carvalho de Melo err = skb_copy_datagram_iovec(skb, offset, 4097c657876SArnaldo Carvalho de Melo msg->msg_iov, used); 4107c657876SArnaldo Carvalho de Melo if (err) { 4117c657876SArnaldo Carvalho de Melo /* Exception. Bailout! */ 4127c657876SArnaldo Carvalho de Melo if (!copied) 4137c657876SArnaldo Carvalho de Melo copied = -EFAULT; 4147c657876SArnaldo Carvalho de Melo break; 4157c657876SArnaldo Carvalho de Melo } 4167c657876SArnaldo Carvalho de Melo } 4177c657876SArnaldo Carvalho de Melo 4187c657876SArnaldo Carvalho de Melo copied += used; 4197c657876SArnaldo Carvalho de Melo len -= used; 4207c657876SArnaldo Carvalho de Melo 4217c657876SArnaldo Carvalho de Melo /* FIXME: tcp_rcv_space_adjust(sk); */ 4227c657876SArnaldo Carvalho de Melo 4237c657876SArnaldo Carvalho de Melo //skip_copy: 4247c657876SArnaldo Carvalho de Melo if (used + offset < skb->len) 4257c657876SArnaldo Carvalho de Melo continue; 4267c657876SArnaldo Carvalho de Melo 4277c657876SArnaldo Carvalho de Melo if (!(flags & MSG_PEEK)) 4287c657876SArnaldo Carvalho de Melo sk_eat_skb(sk, skb); 4297c657876SArnaldo Carvalho de Melo continue; 4307c657876SArnaldo Carvalho de Melo found_fin_ok: 4317c657876SArnaldo Carvalho de Melo if (!(flags & MSG_PEEK)) 4327c657876SArnaldo Carvalho de Melo sk_eat_skb(sk, skb); 4337c657876SArnaldo Carvalho de Melo break; 4347c657876SArnaldo Carvalho de Melo 4357c657876SArnaldo Carvalho de Melo } while (len > 0); 4367c657876SArnaldo Carvalho de Melo 4377c657876SArnaldo Carvalho de Melo /* According to UNIX98, msg_name/msg_namelen are ignored 4387c657876SArnaldo Carvalho de Melo * on connected socket. I was just happy when found this 8) --ANK 4397c657876SArnaldo Carvalho de Melo */ 4407c657876SArnaldo Carvalho de Melo 4417c657876SArnaldo Carvalho de Melo /* Clean up data we have read: This will do ACK frames. */ 4427c657876SArnaldo Carvalho de Melo /* FIXME: cleanup_rbuf(sk, copied); */ 4437c657876SArnaldo Carvalho de Melo 4447c657876SArnaldo Carvalho de Melo release_sock(sk); 4457c657876SArnaldo Carvalho de Melo return copied; 4467c657876SArnaldo Carvalho de Melo 4477c657876SArnaldo Carvalho de Melo out: 4487c657876SArnaldo Carvalho de Melo release_sock(sk); 4497c657876SArnaldo Carvalho de Melo return err; 4507c657876SArnaldo Carvalho de Melo 4517c657876SArnaldo Carvalho de Melo recv_urg: 4527c657876SArnaldo Carvalho de Melo /* FIXME: err = tcp_recv_urg(sk, timeo, msg, len, flags, addr_len); */ 4537c657876SArnaldo Carvalho de Melo goto out; 4547c657876SArnaldo Carvalho de Melo } 4557c657876SArnaldo Carvalho de Melo 4567c657876SArnaldo Carvalho de Melo static int inet_dccp_listen(struct socket *sock, int backlog) 4577c657876SArnaldo Carvalho de Melo { 4587c657876SArnaldo Carvalho de Melo struct sock *sk = sock->sk; 4597c657876SArnaldo Carvalho de Melo unsigned char old_state; 4607c657876SArnaldo Carvalho de Melo int err; 4617c657876SArnaldo Carvalho de Melo 4627c657876SArnaldo Carvalho de Melo lock_sock(sk); 4637c657876SArnaldo Carvalho de Melo 4647c657876SArnaldo Carvalho de Melo err = -EINVAL; 4657c657876SArnaldo Carvalho de Melo if (sock->state != SS_UNCONNECTED || sock->type != SOCK_DCCP) 4667c657876SArnaldo Carvalho de Melo goto out; 4677c657876SArnaldo Carvalho de Melo 4687c657876SArnaldo Carvalho de Melo old_state = sk->sk_state; 4697c657876SArnaldo Carvalho de Melo if (!((1 << old_state) & (DCCPF_CLOSED | DCCPF_LISTEN))) 4707c657876SArnaldo Carvalho de Melo goto out; 4717c657876SArnaldo Carvalho de Melo 4727c657876SArnaldo Carvalho de Melo /* Really, if the socket is already in listen state 4737c657876SArnaldo Carvalho de Melo * we can only allow the backlog to be adjusted. 4747c657876SArnaldo Carvalho de Melo */ 4757c657876SArnaldo Carvalho de Melo if (old_state != DCCP_LISTEN) { 4767c657876SArnaldo Carvalho de Melo /* 4777c657876SArnaldo Carvalho de Melo * FIXME: here it probably should be sk->sk_prot->listen_start 4787c657876SArnaldo Carvalho de Melo * see tcp_listen_start 4797c657876SArnaldo Carvalho de Melo */ 4807c657876SArnaldo Carvalho de Melo err = dccp_listen_start(sk); 4817c657876SArnaldo Carvalho de Melo if (err) 4827c657876SArnaldo Carvalho de Melo goto out; 4837c657876SArnaldo Carvalho de Melo } 4847c657876SArnaldo Carvalho de Melo sk->sk_max_ack_backlog = backlog; 4857c657876SArnaldo Carvalho de Melo err = 0; 4867c657876SArnaldo Carvalho de Melo 4877c657876SArnaldo Carvalho de Melo out: 4887c657876SArnaldo Carvalho de Melo release_sock(sk); 4897c657876SArnaldo Carvalho de Melo return err; 4907c657876SArnaldo Carvalho de Melo } 4917c657876SArnaldo Carvalho de Melo 4927c657876SArnaldo Carvalho de Melo static const unsigned char dccp_new_state[] = { 4937c657876SArnaldo Carvalho de Melo /* current state: new state: action: */ 4947c657876SArnaldo Carvalho de Melo [0] = DCCP_CLOSED, 4957c657876SArnaldo Carvalho de Melo [DCCP_OPEN] = DCCP_CLOSING | DCCP_ACTION_FIN, 4967c657876SArnaldo Carvalho de Melo [DCCP_REQUESTING] = DCCP_CLOSED, 4977c657876SArnaldo Carvalho de Melo [DCCP_PARTOPEN] = DCCP_CLOSING | DCCP_ACTION_FIN, 4987c657876SArnaldo Carvalho de Melo [DCCP_LISTEN] = DCCP_CLOSED, 4997c657876SArnaldo Carvalho de Melo [DCCP_RESPOND] = DCCP_CLOSED, 5007c657876SArnaldo Carvalho de Melo [DCCP_CLOSING] = DCCP_CLOSED, 5017c657876SArnaldo Carvalho de Melo [DCCP_TIME_WAIT] = DCCP_CLOSED, 5027c657876SArnaldo Carvalho de Melo [DCCP_CLOSED] = DCCP_CLOSED, 5037c657876SArnaldo Carvalho de Melo }; 5047c657876SArnaldo Carvalho de Melo 5057c657876SArnaldo Carvalho de Melo static int dccp_close_state(struct sock *sk) 5067c657876SArnaldo Carvalho de Melo { 5077c657876SArnaldo Carvalho de Melo const int next = dccp_new_state[sk->sk_state]; 5087c657876SArnaldo Carvalho de Melo const int ns = next & DCCP_STATE_MASK; 5097c657876SArnaldo Carvalho de Melo 5107c657876SArnaldo Carvalho de Melo if (ns != sk->sk_state) 5117c657876SArnaldo Carvalho de Melo dccp_set_state(sk, ns); 5127c657876SArnaldo Carvalho de Melo 5137c657876SArnaldo Carvalho de Melo return next & DCCP_ACTION_FIN; 5147c657876SArnaldo Carvalho de Melo } 5157c657876SArnaldo Carvalho de Melo 5167c657876SArnaldo Carvalho de Melo void dccp_close(struct sock *sk, long timeout) 5177c657876SArnaldo Carvalho de Melo { 5187c657876SArnaldo Carvalho de Melo struct sk_buff *skb; 5197c657876SArnaldo Carvalho de Melo 5207c657876SArnaldo Carvalho de Melo lock_sock(sk); 5217c657876SArnaldo Carvalho de Melo 5227c657876SArnaldo Carvalho de Melo sk->sk_shutdown = SHUTDOWN_MASK; 5237c657876SArnaldo Carvalho de Melo 5247c657876SArnaldo Carvalho de Melo if (sk->sk_state == DCCP_LISTEN) { 5257c657876SArnaldo Carvalho de Melo dccp_set_state(sk, DCCP_CLOSED); 5267c657876SArnaldo Carvalho de Melo 5277c657876SArnaldo Carvalho de Melo /* Special case. */ 5287c657876SArnaldo Carvalho de Melo inet_csk_listen_stop(sk); 5297c657876SArnaldo Carvalho de Melo 5307c657876SArnaldo Carvalho de Melo goto adjudge_to_death; 5317c657876SArnaldo Carvalho de Melo } 5327c657876SArnaldo Carvalho de Melo 5337c657876SArnaldo Carvalho de Melo /* 5347c657876SArnaldo Carvalho de Melo * We need to flush the recv. buffs. We do this only on the 5357c657876SArnaldo Carvalho de Melo * descriptor close, not protocol-sourced closes, because the 5367c657876SArnaldo Carvalho de Melo *reader process may not have drained the data yet! 5377c657876SArnaldo Carvalho de Melo */ 5387c657876SArnaldo Carvalho de Melo /* FIXME: check for unread data */ 5397c657876SArnaldo Carvalho de Melo while ((skb = __skb_dequeue(&sk->sk_receive_queue)) != NULL) { 5407c657876SArnaldo Carvalho de Melo __kfree_skb(skb); 5417c657876SArnaldo Carvalho de Melo } 5427c657876SArnaldo Carvalho de Melo 5437c657876SArnaldo Carvalho de Melo if (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime) { 5447c657876SArnaldo Carvalho de Melo /* Check zero linger _after_ checking for unread data. */ 5457c657876SArnaldo Carvalho de Melo sk->sk_prot->disconnect(sk, 0); 5467c657876SArnaldo Carvalho de Melo } else if (dccp_close_state(sk)) { 5477c657876SArnaldo Carvalho de Melo dccp_send_close(sk); 5487c657876SArnaldo Carvalho de Melo } 5497c657876SArnaldo Carvalho de Melo 5507c657876SArnaldo Carvalho de Melo sk_stream_wait_close(sk, timeout); 5517c657876SArnaldo Carvalho de Melo 5527c657876SArnaldo Carvalho de Melo adjudge_to_death: 5537c657876SArnaldo Carvalho de Melo release_sock(sk); 5547c657876SArnaldo Carvalho de Melo /* 5557c657876SArnaldo Carvalho de Melo * Now socket is owned by kernel and we acquire BH lock 5567c657876SArnaldo Carvalho de Melo * to finish close. No need to check for user refs. 5577c657876SArnaldo Carvalho de Melo */ 5587c657876SArnaldo Carvalho de Melo local_bh_disable(); 5597c657876SArnaldo Carvalho de Melo bh_lock_sock(sk); 5607c657876SArnaldo Carvalho de Melo BUG_TRAP(!sock_owned_by_user(sk)); 5617c657876SArnaldo Carvalho de Melo 5627c657876SArnaldo Carvalho de Melo sock_hold(sk); 5637c657876SArnaldo Carvalho de Melo sock_orphan(sk); 5647c657876SArnaldo Carvalho de Melo 5657c657876SArnaldo Carvalho de Melo if (sk->sk_state != DCCP_CLOSED) 5667c657876SArnaldo Carvalho de Melo dccp_set_state(sk, DCCP_CLOSED); 5677c657876SArnaldo Carvalho de Melo 5687c657876SArnaldo Carvalho de Melo atomic_inc(&dccp_orphan_count); 5697c657876SArnaldo Carvalho de Melo if (sk->sk_state == DCCP_CLOSED) 5707c657876SArnaldo Carvalho de Melo inet_csk_destroy_sock(sk); 5717c657876SArnaldo Carvalho de Melo 5727c657876SArnaldo Carvalho de Melo /* Otherwise, socket is reprieved until protocol close. */ 5737c657876SArnaldo Carvalho de Melo 5747c657876SArnaldo Carvalho de Melo bh_unlock_sock(sk); 5757c657876SArnaldo Carvalho de Melo local_bh_enable(); 5767c657876SArnaldo Carvalho de Melo sock_put(sk); 5777c657876SArnaldo Carvalho de Melo } 5787c657876SArnaldo Carvalho de Melo 5797c657876SArnaldo Carvalho de Melo void dccp_shutdown(struct sock *sk, int how) 5807c657876SArnaldo Carvalho de Melo { 5817c657876SArnaldo Carvalho de Melo dccp_pr_debug("entry\n"); 5827c657876SArnaldo Carvalho de Melo } 5837c657876SArnaldo Carvalho de Melo 5847c657876SArnaldo Carvalho de Melo struct proto_ops inet_dccp_ops = { 5857c657876SArnaldo Carvalho de Melo .family = PF_INET, 5867c657876SArnaldo Carvalho de Melo .owner = THIS_MODULE, 5877c657876SArnaldo Carvalho de Melo .release = inet_release, 5887c657876SArnaldo Carvalho de Melo .bind = inet_bind, 5897c657876SArnaldo Carvalho de Melo .connect = inet_stream_connect, 5907c657876SArnaldo Carvalho de Melo .socketpair = sock_no_socketpair, 5917c657876SArnaldo Carvalho de Melo .accept = inet_accept, 5927c657876SArnaldo Carvalho de Melo .getname = inet_getname, 5937c657876SArnaldo Carvalho de Melo .poll = sock_no_poll, 5947c657876SArnaldo Carvalho de Melo .ioctl = inet_ioctl, 5957c657876SArnaldo Carvalho de Melo .listen = inet_dccp_listen, /* FIXME: work on inet_listen to rename it to sock_common_listen */ 5967c657876SArnaldo Carvalho de Melo .shutdown = inet_shutdown, 5977c657876SArnaldo Carvalho de Melo .setsockopt = sock_common_setsockopt, 5987c657876SArnaldo Carvalho de Melo .getsockopt = sock_common_getsockopt, 5997c657876SArnaldo Carvalho de Melo .sendmsg = inet_sendmsg, 6007c657876SArnaldo Carvalho de Melo .recvmsg = sock_common_recvmsg, 6017c657876SArnaldo Carvalho de Melo .mmap = sock_no_mmap, 6027c657876SArnaldo Carvalho de Melo .sendpage = sock_no_sendpage, 6037c657876SArnaldo Carvalho de Melo }; 6047c657876SArnaldo Carvalho de Melo 6057c657876SArnaldo Carvalho de Melo extern struct net_proto_family inet_family_ops; 6067c657876SArnaldo Carvalho de Melo 6077c657876SArnaldo Carvalho de Melo static struct inet_protosw dccp_v4_protosw = { 6087c657876SArnaldo Carvalho de Melo .type = SOCK_DCCP, 6097c657876SArnaldo Carvalho de Melo .protocol = IPPROTO_DCCP, 6107c657876SArnaldo Carvalho de Melo .prot = &dccp_v4_prot, 6117c657876SArnaldo Carvalho de Melo .ops = &inet_dccp_ops, 6127c657876SArnaldo Carvalho de Melo .capability = -1, 6137c657876SArnaldo Carvalho de Melo .no_check = 0, 6147c657876SArnaldo Carvalho de Melo .flags = 0, 6157c657876SArnaldo Carvalho de Melo }; 6167c657876SArnaldo Carvalho de Melo 6177c657876SArnaldo Carvalho de Melo /* 6187c657876SArnaldo Carvalho de Melo * This is the global socket data structure used for responding to 6197c657876SArnaldo Carvalho de Melo * the Out-of-the-blue (OOTB) packets. A control sock will be created 6207c657876SArnaldo Carvalho de Melo * for this socket at the initialization time. 6217c657876SArnaldo Carvalho de Melo */ 6227c657876SArnaldo Carvalho de Melo struct socket *dccp_ctl_socket; 6237c657876SArnaldo Carvalho de Melo 6247c657876SArnaldo Carvalho de Melo static char dccp_ctl_socket_err_msg[] __initdata = 6257c657876SArnaldo Carvalho de Melo KERN_ERR "DCCP: Failed to create the control socket.\n"; 6267c657876SArnaldo Carvalho de Melo 6277c657876SArnaldo Carvalho de Melo static int __init dccp_ctl_sock_init(void) 6287c657876SArnaldo Carvalho de Melo { 6297c657876SArnaldo Carvalho de Melo int rc = sock_create_kern(PF_INET, SOCK_DCCP, IPPROTO_DCCP, 6307c657876SArnaldo Carvalho de Melo &dccp_ctl_socket); 6317c657876SArnaldo Carvalho de Melo if (rc < 0) 6327c657876SArnaldo Carvalho de Melo printk(dccp_ctl_socket_err_msg); 6337c657876SArnaldo Carvalho de Melo else { 6347c657876SArnaldo Carvalho de Melo dccp_ctl_socket->sk->sk_allocation = GFP_ATOMIC; 6357c657876SArnaldo Carvalho de Melo inet_sk(dccp_ctl_socket->sk)->uc_ttl = -1; 6367c657876SArnaldo Carvalho de Melo 6377c657876SArnaldo Carvalho de Melo /* Unhash it so that IP input processing does not even 6387c657876SArnaldo Carvalho de Melo * see it, we do not wish this socket to see incoming 6397c657876SArnaldo Carvalho de Melo * packets. 6407c657876SArnaldo Carvalho de Melo */ 6417c657876SArnaldo Carvalho de Melo dccp_ctl_socket->sk->sk_prot->unhash(dccp_ctl_socket->sk); 6427c657876SArnaldo Carvalho de Melo } 6437c657876SArnaldo Carvalho de Melo 6447c657876SArnaldo Carvalho de Melo return rc; 6457c657876SArnaldo Carvalho de Melo } 6467c657876SArnaldo Carvalho de Melo 6477c657876SArnaldo Carvalho de Melo static void __exit dccp_ctl_sock_exit(void) 6487c657876SArnaldo Carvalho de Melo { 6497c657876SArnaldo Carvalho de Melo if (dccp_ctl_socket != NULL) 6507c657876SArnaldo Carvalho de Melo sock_release(dccp_ctl_socket); 6517c657876SArnaldo Carvalho de Melo } 6527c657876SArnaldo Carvalho de Melo 6537c657876SArnaldo Carvalho de Melo static int __init init_dccp_v4_mibs(void) 6547c657876SArnaldo Carvalho de Melo { 6557c657876SArnaldo Carvalho de Melo int rc = -ENOMEM; 6567c657876SArnaldo Carvalho de Melo 6577c657876SArnaldo Carvalho de Melo dccp_statistics[0] = alloc_percpu(struct dccp_mib); 6587c657876SArnaldo Carvalho de Melo if (dccp_statistics[0] == NULL) 6597c657876SArnaldo Carvalho de Melo goto out; 6607c657876SArnaldo Carvalho de Melo 6617c657876SArnaldo Carvalho de Melo dccp_statistics[1] = alloc_percpu(struct dccp_mib); 6627c657876SArnaldo Carvalho de Melo if (dccp_statistics[1] == NULL) 6637c657876SArnaldo Carvalho de Melo goto out_free_one; 6647c657876SArnaldo Carvalho de Melo 6657c657876SArnaldo Carvalho de Melo rc = 0; 6667c657876SArnaldo Carvalho de Melo out: 6677c657876SArnaldo Carvalho de Melo return rc; 6687c657876SArnaldo Carvalho de Melo out_free_one: 6697c657876SArnaldo Carvalho de Melo free_percpu(dccp_statistics[0]); 6707c657876SArnaldo Carvalho de Melo dccp_statistics[0] = NULL; 6717c657876SArnaldo Carvalho de Melo goto out; 6727c657876SArnaldo Carvalho de Melo 6737c657876SArnaldo Carvalho de Melo } 6747c657876SArnaldo Carvalho de Melo 6757c657876SArnaldo Carvalho de Melo static int thash_entries; 6767c657876SArnaldo Carvalho de Melo module_param(thash_entries, int, 0444); 6777c657876SArnaldo Carvalho de Melo MODULE_PARM_DESC(thash_entries, "Number of ehash buckets"); 6787c657876SArnaldo Carvalho de Melo 6797c657876SArnaldo Carvalho de Melo int dccp_debug; 6807c657876SArnaldo Carvalho de Melo module_param(dccp_debug, int, 0444); 6817c657876SArnaldo Carvalho de Melo MODULE_PARM_DESC(dccp_debug, "Enable debug messages"); 6827c657876SArnaldo Carvalho de Melo 6837c657876SArnaldo Carvalho de Melo static int __init dccp_init(void) 6847c657876SArnaldo Carvalho de Melo { 6857c657876SArnaldo Carvalho de Melo unsigned long goal; 6867c657876SArnaldo Carvalho de Melo int ehash_order, bhash_order, i; 6877c657876SArnaldo Carvalho de Melo int rc = proto_register(&dccp_v4_prot, 1); 6887c657876SArnaldo Carvalho de Melo 6897c657876SArnaldo Carvalho de Melo if (rc) 6907c657876SArnaldo Carvalho de Melo goto out; 6917c657876SArnaldo Carvalho de Melo 6927c657876SArnaldo Carvalho de Melo dccp_hashinfo.bind_bucket_cachep = kmem_cache_create("dccp_bind_bucket", 6937c657876SArnaldo Carvalho de Melo sizeof(struct inet_bind_bucket), 6947c657876SArnaldo Carvalho de Melo 0, SLAB_HWCACHE_ALIGN, 6957c657876SArnaldo Carvalho de Melo NULL, NULL); 6967c657876SArnaldo Carvalho de Melo if (!dccp_hashinfo.bind_bucket_cachep) 6977c657876SArnaldo Carvalho de Melo goto out_proto_unregister; 6987c657876SArnaldo Carvalho de Melo 6997c657876SArnaldo Carvalho de Melo /* 7007c657876SArnaldo Carvalho de Melo * Size and allocate the main established and bind bucket 7017c657876SArnaldo Carvalho de Melo * hash tables. 7027c657876SArnaldo Carvalho de Melo * 7037c657876SArnaldo Carvalho de Melo * The methodology is similar to that of the buffer cache. 7047c657876SArnaldo Carvalho de Melo */ 7057c657876SArnaldo Carvalho de Melo if (num_physpages >= (128 * 1024)) 7067c657876SArnaldo Carvalho de Melo goal = num_physpages >> (21 - PAGE_SHIFT); 7077c657876SArnaldo Carvalho de Melo else 7087c657876SArnaldo Carvalho de Melo goal = num_physpages >> (23 - PAGE_SHIFT); 7097c657876SArnaldo Carvalho de Melo 7107c657876SArnaldo Carvalho de Melo if (thash_entries) 7117c657876SArnaldo Carvalho de Melo goal = (thash_entries * sizeof(struct inet_ehash_bucket)) >> PAGE_SHIFT; 7127c657876SArnaldo Carvalho de Melo for (ehash_order = 0; (1UL << ehash_order) < goal; ehash_order++) 7137c657876SArnaldo Carvalho de Melo ; 7147c657876SArnaldo Carvalho de Melo do { 7157c657876SArnaldo Carvalho de Melo dccp_hashinfo.ehash_size = (1UL << ehash_order) * PAGE_SIZE / 7167c657876SArnaldo Carvalho de Melo sizeof(struct inet_ehash_bucket); 7177c657876SArnaldo Carvalho de Melo dccp_hashinfo.ehash_size >>= 1; 7187c657876SArnaldo Carvalho de Melo while (dccp_hashinfo.ehash_size & (dccp_hashinfo.ehash_size - 1)) 7197c657876SArnaldo Carvalho de Melo dccp_hashinfo.ehash_size--; 7207c657876SArnaldo Carvalho de Melo dccp_hashinfo.ehash = (struct inet_ehash_bucket *) 7217c657876SArnaldo Carvalho de Melo __get_free_pages(GFP_ATOMIC, ehash_order); 7227c657876SArnaldo Carvalho de Melo } while (!dccp_hashinfo.ehash && --ehash_order > 0); 7237c657876SArnaldo Carvalho de Melo 7247c657876SArnaldo Carvalho de Melo if (!dccp_hashinfo.ehash) { 7257c657876SArnaldo Carvalho de Melo printk(KERN_CRIT "Failed to allocate DCCP " 7267c657876SArnaldo Carvalho de Melo "established hash table\n"); 7277c657876SArnaldo Carvalho de Melo goto out_free_bind_bucket_cachep; 7287c657876SArnaldo Carvalho de Melo } 7297c657876SArnaldo Carvalho de Melo 7307c657876SArnaldo Carvalho de Melo for (i = 0; i < (dccp_hashinfo.ehash_size << 1); i++) { 7317c657876SArnaldo Carvalho de Melo rwlock_init(&dccp_hashinfo.ehash[i].lock); 7327c657876SArnaldo Carvalho de Melo INIT_HLIST_HEAD(&dccp_hashinfo.ehash[i].chain); 7337c657876SArnaldo Carvalho de Melo } 7347c657876SArnaldo Carvalho de Melo 7357c657876SArnaldo Carvalho de Melo bhash_order = ehash_order; 7367c657876SArnaldo Carvalho de Melo 7377c657876SArnaldo Carvalho de Melo do { 7387c657876SArnaldo Carvalho de Melo dccp_hashinfo.bhash_size = (1UL << bhash_order) * PAGE_SIZE / 7397c657876SArnaldo Carvalho de Melo sizeof(struct inet_bind_hashbucket); 7407c657876SArnaldo Carvalho de Melo if ((dccp_hashinfo.bhash_size > (64 * 1024)) && bhash_order > 0) 7417c657876SArnaldo Carvalho de Melo continue; 7427c657876SArnaldo Carvalho de Melo dccp_hashinfo.bhash = (struct inet_bind_hashbucket *) 7437c657876SArnaldo Carvalho de Melo __get_free_pages(GFP_ATOMIC, bhash_order); 7447c657876SArnaldo Carvalho de Melo } while (!dccp_hashinfo.bhash && --bhash_order >= 0); 7457c657876SArnaldo Carvalho de Melo 7467c657876SArnaldo Carvalho de Melo if (!dccp_hashinfo.bhash) { 7477c657876SArnaldo Carvalho de Melo printk(KERN_CRIT "Failed to allocate DCCP bind hash table\n"); 7487c657876SArnaldo Carvalho de Melo goto out_free_dccp_ehash; 7497c657876SArnaldo Carvalho de Melo } 7507c657876SArnaldo Carvalho de Melo 7517c657876SArnaldo Carvalho de Melo for (i = 0; i < dccp_hashinfo.bhash_size; i++) { 7527c657876SArnaldo Carvalho de Melo spin_lock_init(&dccp_hashinfo.bhash[i].lock); 7537c657876SArnaldo Carvalho de Melo INIT_HLIST_HEAD(&dccp_hashinfo.bhash[i].chain); 7547c657876SArnaldo Carvalho de Melo } 7557c657876SArnaldo Carvalho de Melo 7567c657876SArnaldo Carvalho de Melo if (init_dccp_v4_mibs()) 7577c657876SArnaldo Carvalho de Melo goto out_free_dccp_bhash; 7587c657876SArnaldo Carvalho de Melo 7597c657876SArnaldo Carvalho de Melo rc = -EAGAIN; 7607c657876SArnaldo Carvalho de Melo if (inet_add_protocol(&dccp_protocol, IPPROTO_DCCP)) 7617c657876SArnaldo Carvalho de Melo goto out_free_dccp_v4_mibs; 7627c657876SArnaldo Carvalho de Melo 7637c657876SArnaldo Carvalho de Melo inet_register_protosw(&dccp_v4_protosw); 7647c657876SArnaldo Carvalho de Melo 7657c657876SArnaldo Carvalho de Melo rc = dccp_ctl_sock_init(); 7667c657876SArnaldo Carvalho de Melo if (rc) 7677c657876SArnaldo Carvalho de Melo goto out_unregister_protosw; 7687c657876SArnaldo Carvalho de Melo out: 7697c657876SArnaldo Carvalho de Melo return rc; 7707c657876SArnaldo Carvalho de Melo out_unregister_protosw: 7717c657876SArnaldo Carvalho de Melo inet_unregister_protosw(&dccp_v4_protosw); 7727c657876SArnaldo Carvalho de Melo inet_del_protocol(&dccp_protocol, IPPROTO_DCCP); 7737c657876SArnaldo Carvalho de Melo out_free_dccp_v4_mibs: 7747c657876SArnaldo Carvalho de Melo free_percpu(dccp_statistics[0]); 7757c657876SArnaldo Carvalho de Melo free_percpu(dccp_statistics[1]); 7767c657876SArnaldo Carvalho de Melo dccp_statistics[0] = dccp_statistics[1] = NULL; 7777c657876SArnaldo Carvalho de Melo out_free_dccp_bhash: 7787c657876SArnaldo Carvalho de Melo free_pages((unsigned long)dccp_hashinfo.bhash, bhash_order); 7797c657876SArnaldo Carvalho de Melo dccp_hashinfo.bhash = NULL; 7807c657876SArnaldo Carvalho de Melo out_free_dccp_ehash: 7817c657876SArnaldo Carvalho de Melo free_pages((unsigned long)dccp_hashinfo.ehash, ehash_order); 7827c657876SArnaldo Carvalho de Melo dccp_hashinfo.ehash = NULL; 7837c657876SArnaldo Carvalho de Melo out_free_bind_bucket_cachep: 7847c657876SArnaldo Carvalho de Melo kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep); 7857c657876SArnaldo Carvalho de Melo dccp_hashinfo.bind_bucket_cachep = NULL; 7867c657876SArnaldo Carvalho de Melo out_proto_unregister: 7877c657876SArnaldo Carvalho de Melo proto_unregister(&dccp_v4_prot); 7887c657876SArnaldo Carvalho de Melo goto out; 7897c657876SArnaldo Carvalho de Melo } 7907c657876SArnaldo Carvalho de Melo 7917c657876SArnaldo Carvalho de Melo static const char dccp_del_proto_err_msg[] __exitdata = 7927c657876SArnaldo Carvalho de Melo KERN_ERR "can't remove dccp net_protocol\n"; 7937c657876SArnaldo Carvalho de Melo 7947c657876SArnaldo Carvalho de Melo static void __exit dccp_fini(void) 7957c657876SArnaldo Carvalho de Melo { 7967c657876SArnaldo Carvalho de Melo dccp_ctl_sock_exit(); 7977c657876SArnaldo Carvalho de Melo 7987c657876SArnaldo Carvalho de Melo inet_unregister_protosw(&dccp_v4_protosw); 7997c657876SArnaldo Carvalho de Melo 8007c657876SArnaldo Carvalho de Melo if (inet_del_protocol(&dccp_protocol, IPPROTO_DCCP) < 0) 8017c657876SArnaldo Carvalho de Melo printk(dccp_del_proto_err_msg); 8027c657876SArnaldo Carvalho de Melo 8037c657876SArnaldo Carvalho de Melo /* Free the control endpoint. */ 8047c657876SArnaldo Carvalho de Melo sock_release(dccp_ctl_socket); 8057c657876SArnaldo Carvalho de Melo 8067c657876SArnaldo Carvalho de Melo proto_unregister(&dccp_v4_prot); 8077c657876SArnaldo Carvalho de Melo 8087c657876SArnaldo Carvalho de Melo kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep); 8097c657876SArnaldo Carvalho de Melo } 8107c657876SArnaldo Carvalho de Melo 8117c657876SArnaldo Carvalho de Melo module_init(dccp_init); 8127c657876SArnaldo Carvalho de Melo module_exit(dccp_fini); 8137c657876SArnaldo Carvalho de Melo 814bb97d31fSArnaldo Carvalho de Melo /* 815bb97d31fSArnaldo Carvalho de Melo * __stringify doesn't likes enums, so use SOCK_DCCP (6) and IPPROTO_DCCP (33) 816bb97d31fSArnaldo Carvalho de Melo * values directly, Also cover the case where the protocol is not specified, 817bb97d31fSArnaldo Carvalho de Melo * i.e. net-pf-PF_INET-proto-0-type-SOCK_DCCP 818bb97d31fSArnaldo Carvalho de Melo */ 819bb97d31fSArnaldo Carvalho de Melo MODULE_ALIAS("net-pf-" __stringify(PF_INET) "-proto-33-type-6"); 820bb97d31fSArnaldo Carvalho de Melo MODULE_ALIAS("net-pf-" __stringify(PF_INET) "-proto-0-type-6"); 8217c657876SArnaldo Carvalho de Melo MODULE_LICENSE("GPL"); 8227c657876SArnaldo Carvalho de Melo MODULE_AUTHOR("Arnaldo Carvalho de Melo <acme@conectiva.com.br>"); 8237c657876SArnaldo Carvalho de Melo MODULE_DESCRIPTION("DCCP - Datagram Congestion Controlled Protocol"); 824