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); 18527258ee5SArnaldo Carvalho de Melo timeo = sock_sndtimeo(sk, noblock); 1867c657876SArnaldo Carvalho de Melo 1877c657876SArnaldo Carvalho de Melo /* 1887c657876SArnaldo Carvalho de Melo * We have to use sk_stream_wait_connect here to set sk_write_pending, 1897c657876SArnaldo Carvalho de Melo * so that the trick in dccp_rcv_request_sent_state_process. 1907c657876SArnaldo Carvalho de Melo */ 1917c657876SArnaldo Carvalho de Melo /* Wait for a connection to finish. */ 1927c657876SArnaldo Carvalho de Melo if ((1 << sk->sk_state) & ~(DCCPF_OPEN | DCCPF_PARTOPEN | DCCPF_CLOSING)) 1937c657876SArnaldo Carvalho de Melo if ((rc = sk_stream_wait_connect(sk, &timeo)) != 0) 19427258ee5SArnaldo Carvalho de Melo goto out_release; 1957c657876SArnaldo Carvalho de Melo 1967c657876SArnaldo Carvalho de Melo size = sk->sk_prot->max_header + len; 1977c657876SArnaldo Carvalho de Melo release_sock(sk); 1987c657876SArnaldo Carvalho de Melo skb = sock_alloc_send_skb(sk, size, noblock, &rc); 1997c657876SArnaldo Carvalho de Melo lock_sock(sk); 2007c657876SArnaldo Carvalho de Melo if (skb == NULL) 2017c657876SArnaldo Carvalho de Melo goto out_release; 2027c657876SArnaldo Carvalho de Melo 2037c657876SArnaldo Carvalho de Melo skb_reserve(skb, sk->sk_prot->max_header); 2047c657876SArnaldo Carvalho de Melo rc = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len); 20527258ee5SArnaldo Carvalho de Melo if (rc != 0) 20627258ee5SArnaldo Carvalho de Melo goto out_discard; 2077c657876SArnaldo Carvalho de Melo 20827258ee5SArnaldo Carvalho de Melo rc = dccp_write_xmit(sk, skb, len); 2097c657876SArnaldo Carvalho de Melo out_release: 2107c657876SArnaldo Carvalho de Melo release_sock(sk); 2117c657876SArnaldo Carvalho de Melo return rc ? : len; 21227258ee5SArnaldo Carvalho de Melo out_discard: 21327258ee5SArnaldo Carvalho de Melo kfree_skb(skb); 2147c657876SArnaldo Carvalho de Melo goto out_release; 2157c657876SArnaldo Carvalho de Melo } 2167c657876SArnaldo Carvalho de Melo 2177c657876SArnaldo Carvalho de Melo EXPORT_SYMBOL(dccp_sendmsg); 2187c657876SArnaldo Carvalho de Melo 2197c657876SArnaldo Carvalho de Melo int dccp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, 2207c657876SArnaldo Carvalho de Melo size_t len, int nonblock, int flags, int *addr_len) 2217c657876SArnaldo Carvalho de Melo { 2227c657876SArnaldo Carvalho de Melo const struct dccp_hdr *dh; 2237c657876SArnaldo Carvalho de Melo int copied = 0; 2247c657876SArnaldo Carvalho de Melo unsigned long used; 2257c657876SArnaldo Carvalho de Melo int err; 2267c657876SArnaldo Carvalho de Melo int target; /* Read at least this many bytes */ 2277c657876SArnaldo Carvalho de Melo long timeo; 2287c657876SArnaldo Carvalho de Melo 2297c657876SArnaldo Carvalho de Melo lock_sock(sk); 2307c657876SArnaldo Carvalho de Melo 2317c657876SArnaldo Carvalho de Melo err = -ENOTCONN; 2327c657876SArnaldo Carvalho de Melo if (sk->sk_state == DCCP_LISTEN) 2337c657876SArnaldo Carvalho de Melo goto out; 2347c657876SArnaldo Carvalho de Melo 2357c657876SArnaldo Carvalho de Melo timeo = sock_rcvtimeo(sk, nonblock); 2367c657876SArnaldo Carvalho de Melo 2377c657876SArnaldo Carvalho de Melo /* Urgent data needs to be handled specially. */ 2387c657876SArnaldo Carvalho de Melo if (flags & MSG_OOB) 2397c657876SArnaldo Carvalho de Melo goto recv_urg; 2407c657876SArnaldo Carvalho de Melo 2417c657876SArnaldo Carvalho de Melo /* FIXME */ 2427c657876SArnaldo Carvalho de Melo #if 0 2437c657876SArnaldo Carvalho de Melo seq = &tp->copied_seq; 2447c657876SArnaldo Carvalho de Melo if (flags & MSG_PEEK) { 2457c657876SArnaldo Carvalho de Melo peek_seq = tp->copied_seq; 2467c657876SArnaldo Carvalho de Melo seq = &peek_seq; 2477c657876SArnaldo Carvalho de Melo } 2487c657876SArnaldo Carvalho de Melo #endif 2497c657876SArnaldo Carvalho de Melo 2507c657876SArnaldo Carvalho de Melo target = sock_rcvlowat(sk, flags & MSG_WAITALL, len); 2517c657876SArnaldo Carvalho de Melo 2527c657876SArnaldo Carvalho de Melo do { 2537c657876SArnaldo Carvalho de Melo struct sk_buff *skb; 2547c657876SArnaldo Carvalho de Melo u32 offset; 2557c657876SArnaldo Carvalho de Melo 2567c657876SArnaldo Carvalho de Melo /* FIXME */ 2577c657876SArnaldo Carvalho de Melo #if 0 2587c657876SArnaldo Carvalho de Melo /* Are we at urgent data? Stop if we have read anything or have SIGURG pending. */ 2597c657876SArnaldo Carvalho de Melo if (tp->urg_data && tp->urg_seq == *seq) { 2607c657876SArnaldo Carvalho de Melo if (copied) 2617c657876SArnaldo Carvalho de Melo break; 2627c657876SArnaldo Carvalho de Melo if (signal_pending(current)) { 2637c657876SArnaldo Carvalho de Melo copied = timeo ? sock_intr_errno(timeo) : -EAGAIN; 2647c657876SArnaldo Carvalho de Melo break; 2657c657876SArnaldo Carvalho de Melo } 2667c657876SArnaldo Carvalho de Melo } 2677c657876SArnaldo Carvalho de Melo #endif 2687c657876SArnaldo Carvalho de Melo 2697c657876SArnaldo Carvalho de Melo /* Next get a buffer. */ 2707c657876SArnaldo Carvalho de Melo 2717c657876SArnaldo Carvalho de Melo skb = skb_peek(&sk->sk_receive_queue); 2727c657876SArnaldo Carvalho de Melo do { 2737c657876SArnaldo Carvalho de Melo if (!skb) 2747c657876SArnaldo Carvalho de Melo break; 2757c657876SArnaldo Carvalho de Melo 2767c657876SArnaldo Carvalho de Melo offset = 0; 2777c657876SArnaldo Carvalho de Melo dh = dccp_hdr(skb); 2787c657876SArnaldo Carvalho de Melo 2797c657876SArnaldo Carvalho de Melo if (dh->dccph_type == DCCP_PKT_DATA || 2807c657876SArnaldo Carvalho de Melo dh->dccph_type == DCCP_PKT_DATAACK) 2817c657876SArnaldo Carvalho de Melo goto found_ok_skb; 2827c657876SArnaldo Carvalho de Melo 2837c657876SArnaldo Carvalho de Melo if (dh->dccph_type == DCCP_PKT_RESET || 2847c657876SArnaldo Carvalho de Melo dh->dccph_type == DCCP_PKT_CLOSE) { 2857c657876SArnaldo Carvalho de Melo dccp_pr_debug("found fin ok!\n"); 2867c657876SArnaldo Carvalho de Melo goto found_fin_ok; 2877c657876SArnaldo Carvalho de Melo } 2887c657876SArnaldo Carvalho de Melo dccp_pr_debug("packet_type=%s\n", dccp_packet_name(dh->dccph_type)); 2897c657876SArnaldo Carvalho de Melo BUG_TRAP(flags & MSG_PEEK); 2907c657876SArnaldo Carvalho de Melo skb = skb->next; 2917c657876SArnaldo Carvalho de Melo } while (skb != (struct sk_buff *)&sk->sk_receive_queue); 2927c657876SArnaldo Carvalho de Melo 2937c657876SArnaldo Carvalho de Melo /* Well, if we have backlog, try to process it now yet. */ 2947c657876SArnaldo Carvalho de Melo if (copied >= target && !sk->sk_backlog.tail) 2957c657876SArnaldo Carvalho de Melo break; 2967c657876SArnaldo Carvalho de Melo 2977c657876SArnaldo Carvalho de Melo if (copied) { 2987c657876SArnaldo Carvalho de Melo if (sk->sk_err || 2997c657876SArnaldo Carvalho de Melo sk->sk_state == DCCP_CLOSED || 3007c657876SArnaldo Carvalho de Melo (sk->sk_shutdown & RCV_SHUTDOWN) || 3017c657876SArnaldo Carvalho de Melo !timeo || 3027c657876SArnaldo Carvalho de Melo signal_pending(current) || 3037c657876SArnaldo Carvalho de Melo (flags & MSG_PEEK)) 3047c657876SArnaldo Carvalho de Melo break; 3057c657876SArnaldo Carvalho de Melo } else { 3067c657876SArnaldo Carvalho de Melo if (sock_flag(sk, SOCK_DONE)) 3077c657876SArnaldo Carvalho de Melo break; 3087c657876SArnaldo Carvalho de Melo 3097c657876SArnaldo Carvalho de Melo if (sk->sk_err) { 3107c657876SArnaldo Carvalho de Melo copied = sock_error(sk); 3117c657876SArnaldo Carvalho de Melo break; 3127c657876SArnaldo Carvalho de Melo } 3137c657876SArnaldo Carvalho de Melo 3147c657876SArnaldo Carvalho de Melo if (sk->sk_shutdown & RCV_SHUTDOWN) 3157c657876SArnaldo Carvalho de Melo break; 3167c657876SArnaldo Carvalho de Melo 3177c657876SArnaldo Carvalho de Melo if (sk->sk_state == DCCP_CLOSED) { 3187c657876SArnaldo Carvalho de Melo if (!sock_flag(sk, SOCK_DONE)) { 3197c657876SArnaldo Carvalho de Melo /* This occurs when user tries to read 3207c657876SArnaldo Carvalho de Melo * from never connected socket. 3217c657876SArnaldo Carvalho de Melo */ 3227c657876SArnaldo Carvalho de Melo copied = -ENOTCONN; 3237c657876SArnaldo Carvalho de Melo break; 3247c657876SArnaldo Carvalho de Melo } 3257c657876SArnaldo Carvalho de Melo break; 3267c657876SArnaldo Carvalho de Melo } 3277c657876SArnaldo Carvalho de Melo 3287c657876SArnaldo Carvalho de Melo if (!timeo) { 3297c657876SArnaldo Carvalho de Melo copied = -EAGAIN; 3307c657876SArnaldo Carvalho de Melo break; 3317c657876SArnaldo Carvalho de Melo } 3327c657876SArnaldo Carvalho de Melo 3337c657876SArnaldo Carvalho de Melo if (signal_pending(current)) { 3347c657876SArnaldo Carvalho de Melo copied = sock_intr_errno(timeo); 3357c657876SArnaldo Carvalho de Melo break; 3367c657876SArnaldo Carvalho de Melo } 3377c657876SArnaldo Carvalho de Melo } 3387c657876SArnaldo Carvalho de Melo 3397c657876SArnaldo Carvalho de Melo /* FIXME: cleanup_rbuf(sk, copied); */ 3407c657876SArnaldo Carvalho de Melo 3417c657876SArnaldo Carvalho de Melo if (copied >= target) { 3427c657876SArnaldo Carvalho de Melo /* Do not sleep, just process backlog. */ 3437c657876SArnaldo Carvalho de Melo release_sock(sk); 3447c657876SArnaldo Carvalho de Melo lock_sock(sk); 3457c657876SArnaldo Carvalho de Melo } else 3467c657876SArnaldo Carvalho de Melo sk_wait_data(sk, &timeo); 3477c657876SArnaldo Carvalho de Melo 3487c657876SArnaldo Carvalho de Melo continue; 3497c657876SArnaldo Carvalho de Melo 3507c657876SArnaldo Carvalho de Melo found_ok_skb: 3517c657876SArnaldo Carvalho de Melo /* Ok so how much can we use? */ 3527c657876SArnaldo Carvalho de Melo used = skb->len - offset; 3537c657876SArnaldo Carvalho de Melo if (len < used) 3547c657876SArnaldo Carvalho de Melo used = len; 3557c657876SArnaldo Carvalho de Melo 3567c657876SArnaldo Carvalho de Melo if (!(flags & MSG_TRUNC)) { 3577c657876SArnaldo Carvalho de Melo err = skb_copy_datagram_iovec(skb, offset, 3587c657876SArnaldo Carvalho de Melo msg->msg_iov, used); 3597c657876SArnaldo Carvalho de Melo if (err) { 3607c657876SArnaldo Carvalho de Melo /* Exception. Bailout! */ 3617c657876SArnaldo Carvalho de Melo if (!copied) 3627c657876SArnaldo Carvalho de Melo copied = -EFAULT; 3637c657876SArnaldo Carvalho de Melo break; 3647c657876SArnaldo Carvalho de Melo } 3657c657876SArnaldo Carvalho de Melo } 3667c657876SArnaldo Carvalho de Melo 3677c657876SArnaldo Carvalho de Melo copied += used; 3687c657876SArnaldo Carvalho de Melo len -= used; 3697c657876SArnaldo Carvalho de Melo 3707c657876SArnaldo Carvalho de Melo /* FIXME: tcp_rcv_space_adjust(sk); */ 3717c657876SArnaldo Carvalho de Melo 3727c657876SArnaldo Carvalho de Melo //skip_copy: 3737c657876SArnaldo Carvalho de Melo if (used + offset < skb->len) 3747c657876SArnaldo Carvalho de Melo continue; 3757c657876SArnaldo Carvalho de Melo 3767c657876SArnaldo Carvalho de Melo if (!(flags & MSG_PEEK)) 3777c657876SArnaldo Carvalho de Melo sk_eat_skb(sk, skb); 3787c657876SArnaldo Carvalho de Melo continue; 3797c657876SArnaldo Carvalho de Melo found_fin_ok: 3807c657876SArnaldo Carvalho de Melo if (!(flags & MSG_PEEK)) 3817c657876SArnaldo Carvalho de Melo sk_eat_skb(sk, skb); 3827c657876SArnaldo Carvalho de Melo break; 3837c657876SArnaldo Carvalho de Melo 3847c657876SArnaldo Carvalho de Melo } while (len > 0); 3857c657876SArnaldo Carvalho de Melo 3867c657876SArnaldo Carvalho de Melo /* According to UNIX98, msg_name/msg_namelen are ignored 3877c657876SArnaldo Carvalho de Melo * on connected socket. I was just happy when found this 8) --ANK 3887c657876SArnaldo Carvalho de Melo */ 3897c657876SArnaldo Carvalho de Melo 3907c657876SArnaldo Carvalho de Melo /* Clean up data we have read: This will do ACK frames. */ 3917c657876SArnaldo Carvalho de Melo /* FIXME: cleanup_rbuf(sk, copied); */ 3927c657876SArnaldo Carvalho de Melo 3937c657876SArnaldo Carvalho de Melo release_sock(sk); 3947c657876SArnaldo Carvalho de Melo return copied; 3957c657876SArnaldo Carvalho de Melo 3967c657876SArnaldo Carvalho de Melo out: 3977c657876SArnaldo Carvalho de Melo release_sock(sk); 3987c657876SArnaldo Carvalho de Melo return err; 3997c657876SArnaldo Carvalho de Melo 4007c657876SArnaldo Carvalho de Melo recv_urg: 4017c657876SArnaldo Carvalho de Melo /* FIXME: err = tcp_recv_urg(sk, timeo, msg, len, flags, addr_len); */ 4027c657876SArnaldo Carvalho de Melo goto out; 4037c657876SArnaldo Carvalho de Melo } 4047c657876SArnaldo Carvalho de Melo 4057c657876SArnaldo Carvalho de Melo static int inet_dccp_listen(struct socket *sock, int backlog) 4067c657876SArnaldo Carvalho de Melo { 4077c657876SArnaldo Carvalho de Melo struct sock *sk = sock->sk; 4087c657876SArnaldo Carvalho de Melo unsigned char old_state; 4097c657876SArnaldo Carvalho de Melo int err; 4107c657876SArnaldo Carvalho de Melo 4117c657876SArnaldo Carvalho de Melo lock_sock(sk); 4127c657876SArnaldo Carvalho de Melo 4137c657876SArnaldo Carvalho de Melo err = -EINVAL; 4147c657876SArnaldo Carvalho de Melo if (sock->state != SS_UNCONNECTED || sock->type != SOCK_DCCP) 4157c657876SArnaldo Carvalho de Melo goto out; 4167c657876SArnaldo Carvalho de Melo 4177c657876SArnaldo Carvalho de Melo old_state = sk->sk_state; 4187c657876SArnaldo Carvalho de Melo if (!((1 << old_state) & (DCCPF_CLOSED | DCCPF_LISTEN))) 4197c657876SArnaldo Carvalho de Melo goto out; 4207c657876SArnaldo Carvalho de Melo 4217c657876SArnaldo Carvalho de Melo /* Really, if the socket is already in listen state 4227c657876SArnaldo Carvalho de Melo * we can only allow the backlog to be adjusted. 4237c657876SArnaldo Carvalho de Melo */ 4247c657876SArnaldo Carvalho de Melo if (old_state != DCCP_LISTEN) { 4257c657876SArnaldo Carvalho de Melo /* 4267c657876SArnaldo Carvalho de Melo * FIXME: here it probably should be sk->sk_prot->listen_start 4277c657876SArnaldo Carvalho de Melo * see tcp_listen_start 4287c657876SArnaldo Carvalho de Melo */ 4297c657876SArnaldo Carvalho de Melo err = dccp_listen_start(sk); 4307c657876SArnaldo Carvalho de Melo if (err) 4317c657876SArnaldo Carvalho de Melo goto out; 4327c657876SArnaldo Carvalho de Melo } 4337c657876SArnaldo Carvalho de Melo sk->sk_max_ack_backlog = backlog; 4347c657876SArnaldo Carvalho de Melo err = 0; 4357c657876SArnaldo Carvalho de Melo 4367c657876SArnaldo Carvalho de Melo out: 4377c657876SArnaldo Carvalho de Melo release_sock(sk); 4387c657876SArnaldo Carvalho de Melo return err; 4397c657876SArnaldo Carvalho de Melo } 4407c657876SArnaldo Carvalho de Melo 4417c657876SArnaldo Carvalho de Melo static const unsigned char dccp_new_state[] = { 4427c657876SArnaldo Carvalho de Melo /* current state: new state: action: */ 4437c657876SArnaldo Carvalho de Melo [0] = DCCP_CLOSED, 4447c657876SArnaldo Carvalho de Melo [DCCP_OPEN] = DCCP_CLOSING | DCCP_ACTION_FIN, 4457c657876SArnaldo Carvalho de Melo [DCCP_REQUESTING] = DCCP_CLOSED, 4467c657876SArnaldo Carvalho de Melo [DCCP_PARTOPEN] = DCCP_CLOSING | DCCP_ACTION_FIN, 4477c657876SArnaldo Carvalho de Melo [DCCP_LISTEN] = DCCP_CLOSED, 4487c657876SArnaldo Carvalho de Melo [DCCP_RESPOND] = DCCP_CLOSED, 4497c657876SArnaldo Carvalho de Melo [DCCP_CLOSING] = DCCP_CLOSED, 4507c657876SArnaldo Carvalho de Melo [DCCP_TIME_WAIT] = DCCP_CLOSED, 4517c657876SArnaldo Carvalho de Melo [DCCP_CLOSED] = DCCP_CLOSED, 4527c657876SArnaldo Carvalho de Melo }; 4537c657876SArnaldo Carvalho de Melo 4547c657876SArnaldo Carvalho de Melo static int dccp_close_state(struct sock *sk) 4557c657876SArnaldo Carvalho de Melo { 4567c657876SArnaldo Carvalho de Melo const int next = dccp_new_state[sk->sk_state]; 4577c657876SArnaldo Carvalho de Melo const int ns = next & DCCP_STATE_MASK; 4587c657876SArnaldo Carvalho de Melo 4597c657876SArnaldo Carvalho de Melo if (ns != sk->sk_state) 4607c657876SArnaldo Carvalho de Melo dccp_set_state(sk, ns); 4617c657876SArnaldo Carvalho de Melo 4627c657876SArnaldo Carvalho de Melo return next & DCCP_ACTION_FIN; 4637c657876SArnaldo Carvalho de Melo } 4647c657876SArnaldo Carvalho de Melo 4657c657876SArnaldo Carvalho de Melo void dccp_close(struct sock *sk, long timeout) 4667c657876SArnaldo Carvalho de Melo { 4677c657876SArnaldo Carvalho de Melo struct sk_buff *skb; 4687c657876SArnaldo Carvalho de Melo 4697c657876SArnaldo Carvalho de Melo lock_sock(sk); 4707c657876SArnaldo Carvalho de Melo 4717c657876SArnaldo Carvalho de Melo sk->sk_shutdown = SHUTDOWN_MASK; 4727c657876SArnaldo Carvalho de Melo 4737c657876SArnaldo Carvalho de Melo if (sk->sk_state == DCCP_LISTEN) { 4747c657876SArnaldo Carvalho de Melo dccp_set_state(sk, DCCP_CLOSED); 4757c657876SArnaldo Carvalho de Melo 4767c657876SArnaldo Carvalho de Melo /* Special case. */ 4777c657876SArnaldo Carvalho de Melo inet_csk_listen_stop(sk); 4787c657876SArnaldo Carvalho de Melo 4797c657876SArnaldo Carvalho de Melo goto adjudge_to_death; 4807c657876SArnaldo Carvalho de Melo } 4817c657876SArnaldo Carvalho de Melo 4827c657876SArnaldo Carvalho de Melo /* 4837c657876SArnaldo Carvalho de Melo * We need to flush the recv. buffs. We do this only on the 4847c657876SArnaldo Carvalho de Melo * descriptor close, not protocol-sourced closes, because the 4857c657876SArnaldo Carvalho de Melo *reader process may not have drained the data yet! 4867c657876SArnaldo Carvalho de Melo */ 4877c657876SArnaldo Carvalho de Melo /* FIXME: check for unread data */ 4887c657876SArnaldo Carvalho de Melo while ((skb = __skb_dequeue(&sk->sk_receive_queue)) != NULL) { 4897c657876SArnaldo Carvalho de Melo __kfree_skb(skb); 4907c657876SArnaldo Carvalho de Melo } 4917c657876SArnaldo Carvalho de Melo 4927c657876SArnaldo Carvalho de Melo if (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime) { 4937c657876SArnaldo Carvalho de Melo /* Check zero linger _after_ checking for unread data. */ 4947c657876SArnaldo Carvalho de Melo sk->sk_prot->disconnect(sk, 0); 4957c657876SArnaldo Carvalho de Melo } else if (dccp_close_state(sk)) { 4967c657876SArnaldo Carvalho de Melo dccp_send_close(sk); 4977c657876SArnaldo Carvalho de Melo } 4987c657876SArnaldo Carvalho de Melo 4997c657876SArnaldo Carvalho de Melo sk_stream_wait_close(sk, timeout); 5007c657876SArnaldo Carvalho de Melo 5017c657876SArnaldo Carvalho de Melo adjudge_to_death: 5027c657876SArnaldo Carvalho de Melo release_sock(sk); 5037c657876SArnaldo Carvalho de Melo /* 5047c657876SArnaldo Carvalho de Melo * Now socket is owned by kernel and we acquire BH lock 5057c657876SArnaldo Carvalho de Melo * to finish close. No need to check for user refs. 5067c657876SArnaldo Carvalho de Melo */ 5077c657876SArnaldo Carvalho de Melo local_bh_disable(); 5087c657876SArnaldo Carvalho de Melo bh_lock_sock(sk); 5097c657876SArnaldo Carvalho de Melo BUG_TRAP(!sock_owned_by_user(sk)); 5107c657876SArnaldo Carvalho de Melo 5117c657876SArnaldo Carvalho de Melo sock_hold(sk); 5127c657876SArnaldo Carvalho de Melo sock_orphan(sk); 5137c657876SArnaldo Carvalho de Melo 5147c657876SArnaldo Carvalho de Melo if (sk->sk_state != DCCP_CLOSED) 5157c657876SArnaldo Carvalho de Melo dccp_set_state(sk, DCCP_CLOSED); 5167c657876SArnaldo Carvalho de Melo 5177c657876SArnaldo Carvalho de Melo atomic_inc(&dccp_orphan_count); 5187c657876SArnaldo Carvalho de Melo if (sk->sk_state == DCCP_CLOSED) 5197c657876SArnaldo Carvalho de Melo inet_csk_destroy_sock(sk); 5207c657876SArnaldo Carvalho de Melo 5217c657876SArnaldo Carvalho de Melo /* Otherwise, socket is reprieved until protocol close. */ 5227c657876SArnaldo Carvalho de Melo 5237c657876SArnaldo Carvalho de Melo bh_unlock_sock(sk); 5247c657876SArnaldo Carvalho de Melo local_bh_enable(); 5257c657876SArnaldo Carvalho de Melo sock_put(sk); 5267c657876SArnaldo Carvalho de Melo } 5277c657876SArnaldo Carvalho de Melo 5287c657876SArnaldo Carvalho de Melo void dccp_shutdown(struct sock *sk, int how) 5297c657876SArnaldo Carvalho de Melo { 5307c657876SArnaldo Carvalho de Melo dccp_pr_debug("entry\n"); 5317c657876SArnaldo Carvalho de Melo } 5327c657876SArnaldo Carvalho de Melo 5337c657876SArnaldo Carvalho de Melo struct proto_ops inet_dccp_ops = { 5347c657876SArnaldo Carvalho de Melo .family = PF_INET, 5357c657876SArnaldo Carvalho de Melo .owner = THIS_MODULE, 5367c657876SArnaldo Carvalho de Melo .release = inet_release, 5377c657876SArnaldo Carvalho de Melo .bind = inet_bind, 5387c657876SArnaldo Carvalho de Melo .connect = inet_stream_connect, 5397c657876SArnaldo Carvalho de Melo .socketpair = sock_no_socketpair, 5407c657876SArnaldo Carvalho de Melo .accept = inet_accept, 5417c657876SArnaldo Carvalho de Melo .getname = inet_getname, 5427c657876SArnaldo Carvalho de Melo .poll = sock_no_poll, 5437c657876SArnaldo Carvalho de Melo .ioctl = inet_ioctl, 5447c657876SArnaldo Carvalho de Melo .listen = inet_dccp_listen, /* FIXME: work on inet_listen to rename it to sock_common_listen */ 5457c657876SArnaldo Carvalho de Melo .shutdown = inet_shutdown, 5467c657876SArnaldo Carvalho de Melo .setsockopt = sock_common_setsockopt, 5477c657876SArnaldo Carvalho de Melo .getsockopt = sock_common_getsockopt, 5487c657876SArnaldo Carvalho de Melo .sendmsg = inet_sendmsg, 5497c657876SArnaldo Carvalho de Melo .recvmsg = sock_common_recvmsg, 5507c657876SArnaldo Carvalho de Melo .mmap = sock_no_mmap, 5517c657876SArnaldo Carvalho de Melo .sendpage = sock_no_sendpage, 5527c657876SArnaldo Carvalho de Melo }; 5537c657876SArnaldo Carvalho de Melo 5547c657876SArnaldo Carvalho de Melo extern struct net_proto_family inet_family_ops; 5557c657876SArnaldo Carvalho de Melo 5567c657876SArnaldo Carvalho de Melo static struct inet_protosw dccp_v4_protosw = { 5577c657876SArnaldo Carvalho de Melo .type = SOCK_DCCP, 5587c657876SArnaldo Carvalho de Melo .protocol = IPPROTO_DCCP, 5597c657876SArnaldo Carvalho de Melo .prot = &dccp_v4_prot, 5607c657876SArnaldo Carvalho de Melo .ops = &inet_dccp_ops, 5617c657876SArnaldo Carvalho de Melo .capability = -1, 5627c657876SArnaldo Carvalho de Melo .no_check = 0, 5637c657876SArnaldo Carvalho de Melo .flags = 0, 5647c657876SArnaldo Carvalho de Melo }; 5657c657876SArnaldo Carvalho de Melo 5667c657876SArnaldo Carvalho de Melo /* 5677c657876SArnaldo Carvalho de Melo * This is the global socket data structure used for responding to 5687c657876SArnaldo Carvalho de Melo * the Out-of-the-blue (OOTB) packets. A control sock will be created 5697c657876SArnaldo Carvalho de Melo * for this socket at the initialization time. 5707c657876SArnaldo Carvalho de Melo */ 5717c657876SArnaldo Carvalho de Melo struct socket *dccp_ctl_socket; 5727c657876SArnaldo Carvalho de Melo 5737c657876SArnaldo Carvalho de Melo static char dccp_ctl_socket_err_msg[] __initdata = 5747c657876SArnaldo Carvalho de Melo KERN_ERR "DCCP: Failed to create the control socket.\n"; 5757c657876SArnaldo Carvalho de Melo 5767c657876SArnaldo Carvalho de Melo static int __init dccp_ctl_sock_init(void) 5777c657876SArnaldo Carvalho de Melo { 5787c657876SArnaldo Carvalho de Melo int rc = sock_create_kern(PF_INET, SOCK_DCCP, IPPROTO_DCCP, 5797c657876SArnaldo Carvalho de Melo &dccp_ctl_socket); 5807c657876SArnaldo Carvalho de Melo if (rc < 0) 5817c657876SArnaldo Carvalho de Melo printk(dccp_ctl_socket_err_msg); 5827c657876SArnaldo Carvalho de Melo else { 5837c657876SArnaldo Carvalho de Melo dccp_ctl_socket->sk->sk_allocation = GFP_ATOMIC; 5847c657876SArnaldo Carvalho de Melo inet_sk(dccp_ctl_socket->sk)->uc_ttl = -1; 5857c657876SArnaldo Carvalho de Melo 5867c657876SArnaldo Carvalho de Melo /* Unhash it so that IP input processing does not even 5877c657876SArnaldo Carvalho de Melo * see it, we do not wish this socket to see incoming 5887c657876SArnaldo Carvalho de Melo * packets. 5897c657876SArnaldo Carvalho de Melo */ 5907c657876SArnaldo Carvalho de Melo dccp_ctl_socket->sk->sk_prot->unhash(dccp_ctl_socket->sk); 5917c657876SArnaldo Carvalho de Melo } 5927c657876SArnaldo Carvalho de Melo 5937c657876SArnaldo Carvalho de Melo return rc; 5947c657876SArnaldo Carvalho de Melo } 5957c657876SArnaldo Carvalho de Melo 5967c657876SArnaldo Carvalho de Melo static void __exit dccp_ctl_sock_exit(void) 5977c657876SArnaldo Carvalho de Melo { 5987c657876SArnaldo Carvalho de Melo if (dccp_ctl_socket != NULL) 5997c657876SArnaldo Carvalho de Melo sock_release(dccp_ctl_socket); 6007c657876SArnaldo Carvalho de Melo } 6017c657876SArnaldo Carvalho de Melo 6027c657876SArnaldo Carvalho de Melo static int __init init_dccp_v4_mibs(void) 6037c657876SArnaldo Carvalho de Melo { 6047c657876SArnaldo Carvalho de Melo int rc = -ENOMEM; 6057c657876SArnaldo Carvalho de Melo 6067c657876SArnaldo Carvalho de Melo dccp_statistics[0] = alloc_percpu(struct dccp_mib); 6077c657876SArnaldo Carvalho de Melo if (dccp_statistics[0] == NULL) 6087c657876SArnaldo Carvalho de Melo goto out; 6097c657876SArnaldo Carvalho de Melo 6107c657876SArnaldo Carvalho de Melo dccp_statistics[1] = alloc_percpu(struct dccp_mib); 6117c657876SArnaldo Carvalho de Melo if (dccp_statistics[1] == NULL) 6127c657876SArnaldo Carvalho de Melo goto out_free_one; 6137c657876SArnaldo Carvalho de Melo 6147c657876SArnaldo Carvalho de Melo rc = 0; 6157c657876SArnaldo Carvalho de Melo out: 6167c657876SArnaldo Carvalho de Melo return rc; 6177c657876SArnaldo Carvalho de Melo out_free_one: 6187c657876SArnaldo Carvalho de Melo free_percpu(dccp_statistics[0]); 6197c657876SArnaldo Carvalho de Melo dccp_statistics[0] = NULL; 6207c657876SArnaldo Carvalho de Melo goto out; 6217c657876SArnaldo Carvalho de Melo 6227c657876SArnaldo Carvalho de Melo } 6237c657876SArnaldo Carvalho de Melo 6247c657876SArnaldo Carvalho de Melo static int thash_entries; 6257c657876SArnaldo Carvalho de Melo module_param(thash_entries, int, 0444); 6267c657876SArnaldo Carvalho de Melo MODULE_PARM_DESC(thash_entries, "Number of ehash buckets"); 6277c657876SArnaldo Carvalho de Melo 6287c657876SArnaldo Carvalho de Melo int dccp_debug; 6297c657876SArnaldo Carvalho de Melo module_param(dccp_debug, int, 0444); 6307c657876SArnaldo Carvalho de Melo MODULE_PARM_DESC(dccp_debug, "Enable debug messages"); 6317c657876SArnaldo Carvalho de Melo 6327c657876SArnaldo Carvalho de Melo static int __init dccp_init(void) 6337c657876SArnaldo Carvalho de Melo { 6347c657876SArnaldo Carvalho de Melo unsigned long goal; 6357c657876SArnaldo Carvalho de Melo int ehash_order, bhash_order, i; 6367c657876SArnaldo Carvalho de Melo int rc = proto_register(&dccp_v4_prot, 1); 6377c657876SArnaldo Carvalho de Melo 6387c657876SArnaldo Carvalho de Melo if (rc) 6397c657876SArnaldo Carvalho de Melo goto out; 6407c657876SArnaldo Carvalho de Melo 6417c657876SArnaldo Carvalho de Melo dccp_hashinfo.bind_bucket_cachep = kmem_cache_create("dccp_bind_bucket", 6427c657876SArnaldo Carvalho de Melo sizeof(struct inet_bind_bucket), 6437c657876SArnaldo Carvalho de Melo 0, SLAB_HWCACHE_ALIGN, 6447c657876SArnaldo Carvalho de Melo NULL, NULL); 6457c657876SArnaldo Carvalho de Melo if (!dccp_hashinfo.bind_bucket_cachep) 6467c657876SArnaldo Carvalho de Melo goto out_proto_unregister; 6477c657876SArnaldo Carvalho de Melo 6487c657876SArnaldo Carvalho de Melo /* 6497c657876SArnaldo Carvalho de Melo * Size and allocate the main established and bind bucket 6507c657876SArnaldo Carvalho de Melo * hash tables. 6517c657876SArnaldo Carvalho de Melo * 6527c657876SArnaldo Carvalho de Melo * The methodology is similar to that of the buffer cache. 6537c657876SArnaldo Carvalho de Melo */ 6547c657876SArnaldo Carvalho de Melo if (num_physpages >= (128 * 1024)) 6557c657876SArnaldo Carvalho de Melo goal = num_physpages >> (21 - PAGE_SHIFT); 6567c657876SArnaldo Carvalho de Melo else 6577c657876SArnaldo Carvalho de Melo goal = num_physpages >> (23 - PAGE_SHIFT); 6587c657876SArnaldo Carvalho de Melo 6597c657876SArnaldo Carvalho de Melo if (thash_entries) 6607c657876SArnaldo Carvalho de Melo goal = (thash_entries * sizeof(struct inet_ehash_bucket)) >> PAGE_SHIFT; 6617c657876SArnaldo Carvalho de Melo for (ehash_order = 0; (1UL << ehash_order) < goal; ehash_order++) 6627c657876SArnaldo Carvalho de Melo ; 6637c657876SArnaldo Carvalho de Melo do { 6647c657876SArnaldo Carvalho de Melo dccp_hashinfo.ehash_size = (1UL << ehash_order) * PAGE_SIZE / 6657c657876SArnaldo Carvalho de Melo sizeof(struct inet_ehash_bucket); 6667c657876SArnaldo Carvalho de Melo dccp_hashinfo.ehash_size >>= 1; 6677c657876SArnaldo Carvalho de Melo while (dccp_hashinfo.ehash_size & (dccp_hashinfo.ehash_size - 1)) 6687c657876SArnaldo Carvalho de Melo dccp_hashinfo.ehash_size--; 6697c657876SArnaldo Carvalho de Melo dccp_hashinfo.ehash = (struct inet_ehash_bucket *) 6707c657876SArnaldo Carvalho de Melo __get_free_pages(GFP_ATOMIC, ehash_order); 6717c657876SArnaldo Carvalho de Melo } while (!dccp_hashinfo.ehash && --ehash_order > 0); 6727c657876SArnaldo Carvalho de Melo 6737c657876SArnaldo Carvalho de Melo if (!dccp_hashinfo.ehash) { 6747c657876SArnaldo Carvalho de Melo printk(KERN_CRIT "Failed to allocate DCCP " 6757c657876SArnaldo Carvalho de Melo "established hash table\n"); 6767c657876SArnaldo Carvalho de Melo goto out_free_bind_bucket_cachep; 6777c657876SArnaldo Carvalho de Melo } 6787c657876SArnaldo Carvalho de Melo 6797c657876SArnaldo Carvalho de Melo for (i = 0; i < (dccp_hashinfo.ehash_size << 1); i++) { 6807c657876SArnaldo Carvalho de Melo rwlock_init(&dccp_hashinfo.ehash[i].lock); 6817c657876SArnaldo Carvalho de Melo INIT_HLIST_HEAD(&dccp_hashinfo.ehash[i].chain); 6827c657876SArnaldo Carvalho de Melo } 6837c657876SArnaldo Carvalho de Melo 6847c657876SArnaldo Carvalho de Melo bhash_order = ehash_order; 6857c657876SArnaldo Carvalho de Melo 6867c657876SArnaldo Carvalho de Melo do { 6877c657876SArnaldo Carvalho de Melo dccp_hashinfo.bhash_size = (1UL << bhash_order) * PAGE_SIZE / 6887c657876SArnaldo Carvalho de Melo sizeof(struct inet_bind_hashbucket); 6897c657876SArnaldo Carvalho de Melo if ((dccp_hashinfo.bhash_size > (64 * 1024)) && bhash_order > 0) 6907c657876SArnaldo Carvalho de Melo continue; 6917c657876SArnaldo Carvalho de Melo dccp_hashinfo.bhash = (struct inet_bind_hashbucket *) 6927c657876SArnaldo Carvalho de Melo __get_free_pages(GFP_ATOMIC, bhash_order); 6937c657876SArnaldo Carvalho de Melo } while (!dccp_hashinfo.bhash && --bhash_order >= 0); 6947c657876SArnaldo Carvalho de Melo 6957c657876SArnaldo Carvalho de Melo if (!dccp_hashinfo.bhash) { 6967c657876SArnaldo Carvalho de Melo printk(KERN_CRIT "Failed to allocate DCCP bind hash table\n"); 6977c657876SArnaldo Carvalho de Melo goto out_free_dccp_ehash; 6987c657876SArnaldo Carvalho de Melo } 6997c657876SArnaldo Carvalho de Melo 7007c657876SArnaldo Carvalho de Melo for (i = 0; i < dccp_hashinfo.bhash_size; i++) { 7017c657876SArnaldo Carvalho de Melo spin_lock_init(&dccp_hashinfo.bhash[i].lock); 7027c657876SArnaldo Carvalho de Melo INIT_HLIST_HEAD(&dccp_hashinfo.bhash[i].chain); 7037c657876SArnaldo Carvalho de Melo } 7047c657876SArnaldo Carvalho de Melo 7057c657876SArnaldo Carvalho de Melo if (init_dccp_v4_mibs()) 7067c657876SArnaldo Carvalho de Melo goto out_free_dccp_bhash; 7077c657876SArnaldo Carvalho de Melo 7087c657876SArnaldo Carvalho de Melo rc = -EAGAIN; 7097c657876SArnaldo Carvalho de Melo if (inet_add_protocol(&dccp_protocol, IPPROTO_DCCP)) 7107c657876SArnaldo Carvalho de Melo goto out_free_dccp_v4_mibs; 7117c657876SArnaldo Carvalho de Melo 7127c657876SArnaldo Carvalho de Melo inet_register_protosw(&dccp_v4_protosw); 7137c657876SArnaldo Carvalho de Melo 7147c657876SArnaldo Carvalho de Melo rc = dccp_ctl_sock_init(); 7157c657876SArnaldo Carvalho de Melo if (rc) 7167c657876SArnaldo Carvalho de Melo goto out_unregister_protosw; 7177c657876SArnaldo Carvalho de Melo out: 7187c657876SArnaldo Carvalho de Melo return rc; 7197c657876SArnaldo Carvalho de Melo out_unregister_protosw: 7207c657876SArnaldo Carvalho de Melo inet_unregister_protosw(&dccp_v4_protosw); 7217c657876SArnaldo Carvalho de Melo inet_del_protocol(&dccp_protocol, IPPROTO_DCCP); 7227c657876SArnaldo Carvalho de Melo out_free_dccp_v4_mibs: 7237c657876SArnaldo Carvalho de Melo free_percpu(dccp_statistics[0]); 7247c657876SArnaldo Carvalho de Melo free_percpu(dccp_statistics[1]); 7257c657876SArnaldo Carvalho de Melo dccp_statistics[0] = dccp_statistics[1] = NULL; 7267c657876SArnaldo Carvalho de Melo out_free_dccp_bhash: 7277c657876SArnaldo Carvalho de Melo free_pages((unsigned long)dccp_hashinfo.bhash, bhash_order); 7287c657876SArnaldo Carvalho de Melo dccp_hashinfo.bhash = NULL; 7297c657876SArnaldo Carvalho de Melo out_free_dccp_ehash: 7307c657876SArnaldo Carvalho de Melo free_pages((unsigned long)dccp_hashinfo.ehash, ehash_order); 7317c657876SArnaldo Carvalho de Melo dccp_hashinfo.ehash = NULL; 7327c657876SArnaldo Carvalho de Melo out_free_bind_bucket_cachep: 7337c657876SArnaldo Carvalho de Melo kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep); 7347c657876SArnaldo Carvalho de Melo dccp_hashinfo.bind_bucket_cachep = NULL; 7357c657876SArnaldo Carvalho de Melo out_proto_unregister: 7367c657876SArnaldo Carvalho de Melo proto_unregister(&dccp_v4_prot); 7377c657876SArnaldo Carvalho de Melo goto out; 7387c657876SArnaldo Carvalho de Melo } 7397c657876SArnaldo Carvalho de Melo 7407c657876SArnaldo Carvalho de Melo static const char dccp_del_proto_err_msg[] __exitdata = 7417c657876SArnaldo Carvalho de Melo KERN_ERR "can't remove dccp net_protocol\n"; 7427c657876SArnaldo Carvalho de Melo 7437c657876SArnaldo Carvalho de Melo static void __exit dccp_fini(void) 7447c657876SArnaldo Carvalho de Melo { 7457c657876SArnaldo Carvalho de Melo dccp_ctl_sock_exit(); 7467c657876SArnaldo Carvalho de Melo 7477c657876SArnaldo Carvalho de Melo inet_unregister_protosw(&dccp_v4_protosw); 7487c657876SArnaldo Carvalho de Melo 7497c657876SArnaldo Carvalho de Melo if (inet_del_protocol(&dccp_protocol, IPPROTO_DCCP) < 0) 7507c657876SArnaldo Carvalho de Melo printk(dccp_del_proto_err_msg); 7517c657876SArnaldo Carvalho de Melo 7527c657876SArnaldo Carvalho de Melo /* Free the control endpoint. */ 7537c657876SArnaldo Carvalho de Melo sock_release(dccp_ctl_socket); 7547c657876SArnaldo Carvalho de Melo 7557c657876SArnaldo Carvalho de Melo proto_unregister(&dccp_v4_prot); 7567c657876SArnaldo Carvalho de Melo 7577c657876SArnaldo Carvalho de Melo kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep); 7587c657876SArnaldo Carvalho de Melo } 7597c657876SArnaldo Carvalho de Melo 7607c657876SArnaldo Carvalho de Melo module_init(dccp_init); 7617c657876SArnaldo Carvalho de Melo module_exit(dccp_fini); 7627c657876SArnaldo Carvalho de Melo 763bb97d31fSArnaldo Carvalho de Melo /* 764bb97d31fSArnaldo Carvalho de Melo * __stringify doesn't likes enums, so use SOCK_DCCP (6) and IPPROTO_DCCP (33) 765bb97d31fSArnaldo Carvalho de Melo * values directly, Also cover the case where the protocol is not specified, 766bb97d31fSArnaldo Carvalho de Melo * i.e. net-pf-PF_INET-proto-0-type-SOCK_DCCP 767bb97d31fSArnaldo Carvalho de Melo */ 768bb97d31fSArnaldo Carvalho de Melo MODULE_ALIAS("net-pf-" __stringify(PF_INET) "-proto-33-type-6"); 769bb97d31fSArnaldo Carvalho de Melo MODULE_ALIAS("net-pf-" __stringify(PF_INET) "-proto-0-type-6"); 7707c657876SArnaldo Carvalho de Melo MODULE_LICENSE("GPL"); 7717c657876SArnaldo Carvalho de Melo MODULE_AUTHOR("Arnaldo Carvalho de Melo <acme@conectiva.com.br>"); 7727c657876SArnaldo Carvalho de Melo MODULE_DESCRIPTION("DCCP - Datagram Congestion Controlled Protocol"); 773