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 143*331968bdSArnaldo Carvalho de Melo /* 144*331968bdSArnaldo Carvalho de Melo * Wait for a DCCP event. 145*331968bdSArnaldo Carvalho de Melo * 146*331968bdSArnaldo Carvalho de Melo * Note that we don't need to lock the socket, as the upper poll layers 147*331968bdSArnaldo Carvalho de Melo * take care of normal races (between the test and the event) and we don't 148*331968bdSArnaldo Carvalho de Melo * go look at any of the socket buffers directly. 149*331968bdSArnaldo Carvalho de Melo */ 150*331968bdSArnaldo Carvalho de Melo static unsigned int dccp_poll(struct file *file, struct socket *sock, 151*331968bdSArnaldo Carvalho de Melo poll_table *wait) 152*331968bdSArnaldo Carvalho de Melo { 153*331968bdSArnaldo Carvalho de Melo unsigned int mask; 154*331968bdSArnaldo Carvalho de Melo struct sock *sk = sock->sk; 155*331968bdSArnaldo Carvalho de Melo 156*331968bdSArnaldo Carvalho de Melo poll_wait(file, sk->sk_sleep, wait); 157*331968bdSArnaldo Carvalho de Melo if (sk->sk_state == DCCP_LISTEN) 158*331968bdSArnaldo Carvalho de Melo return inet_csk_listen_poll(sk); 159*331968bdSArnaldo Carvalho de Melo 160*331968bdSArnaldo Carvalho de Melo /* Socket is not locked. We are protected from async events 161*331968bdSArnaldo Carvalho de Melo by poll logic and correct handling of state changes 162*331968bdSArnaldo Carvalho de Melo made by another threads is impossible in any case. 163*331968bdSArnaldo Carvalho de Melo */ 164*331968bdSArnaldo Carvalho de Melo 165*331968bdSArnaldo Carvalho de Melo mask = 0; 166*331968bdSArnaldo Carvalho de Melo if (sk->sk_err) 167*331968bdSArnaldo Carvalho de Melo mask = POLLERR; 168*331968bdSArnaldo Carvalho de Melo 169*331968bdSArnaldo Carvalho de Melo if (sk->sk_shutdown == SHUTDOWN_MASK || sk->sk_state == DCCP_CLOSED) 170*331968bdSArnaldo Carvalho de Melo mask |= POLLHUP; 171*331968bdSArnaldo Carvalho de Melo if (sk->sk_shutdown & RCV_SHUTDOWN) 172*331968bdSArnaldo Carvalho de Melo mask |= POLLIN | POLLRDNORM; 173*331968bdSArnaldo Carvalho de Melo 174*331968bdSArnaldo Carvalho de Melo /* Connected? */ 175*331968bdSArnaldo Carvalho de Melo if ((1 << sk->sk_state) & ~(DCCPF_REQUESTING | DCCPF_RESPOND)) { 176*331968bdSArnaldo Carvalho de Melo if (atomic_read(&sk->sk_rmem_alloc) > 0) 177*331968bdSArnaldo Carvalho de Melo mask |= POLLIN | POLLRDNORM; 178*331968bdSArnaldo Carvalho de Melo 179*331968bdSArnaldo Carvalho de Melo if (!(sk->sk_shutdown & SEND_SHUTDOWN)) { 180*331968bdSArnaldo Carvalho de Melo if (sk_stream_wspace(sk) >= sk_stream_min_wspace(sk)) { 181*331968bdSArnaldo Carvalho de Melo mask |= POLLOUT | POLLWRNORM; 182*331968bdSArnaldo Carvalho de Melo } else { /* send SIGIO later */ 183*331968bdSArnaldo Carvalho de Melo set_bit(SOCK_ASYNC_NOSPACE, 184*331968bdSArnaldo Carvalho de Melo &sk->sk_socket->flags); 185*331968bdSArnaldo Carvalho de Melo set_bit(SOCK_NOSPACE, &sk->sk_socket->flags); 186*331968bdSArnaldo Carvalho de Melo 187*331968bdSArnaldo Carvalho de Melo /* Race breaker. If space is freed after 188*331968bdSArnaldo Carvalho de Melo * wspace test but before the flags are set, 189*331968bdSArnaldo Carvalho de Melo * IO signal will be lost. 190*331968bdSArnaldo Carvalho de Melo */ 191*331968bdSArnaldo Carvalho de Melo if (sk_stream_wspace(sk) >= sk_stream_min_wspace(sk)) 192*331968bdSArnaldo Carvalho de Melo mask |= POLLOUT | POLLWRNORM; 193*331968bdSArnaldo Carvalho de Melo } 194*331968bdSArnaldo Carvalho de Melo } 195*331968bdSArnaldo Carvalho de Melo } 196*331968bdSArnaldo Carvalho de Melo return mask; 197*331968bdSArnaldo Carvalho de Melo } 198*331968bdSArnaldo Carvalho de Melo 1997c657876SArnaldo Carvalho de Melo int dccp_ioctl(struct sock *sk, int cmd, unsigned long arg) 2007c657876SArnaldo Carvalho de Melo { 2017c657876SArnaldo Carvalho de Melo dccp_pr_debug("entry\n"); 2027c657876SArnaldo Carvalho de Melo return -ENOIOCTLCMD; 2037c657876SArnaldo Carvalho de Melo } 2047c657876SArnaldo Carvalho de Melo 2057c657876SArnaldo Carvalho de Melo int dccp_setsockopt(struct sock *sk, int level, int optname, 206a1d3a355SArnaldo Carvalho de Melo char __user *optval, int optlen) 2077c657876SArnaldo Carvalho de Melo { 2087c657876SArnaldo Carvalho de Melo dccp_pr_debug("entry\n"); 2097c657876SArnaldo Carvalho de Melo 2107c657876SArnaldo Carvalho de Melo if (level != SOL_DCCP) 2117c657876SArnaldo Carvalho de Melo return ip_setsockopt(sk, level, optname, optval, optlen); 2127c657876SArnaldo Carvalho de Melo 2137c657876SArnaldo Carvalho de Melo return -EOPNOTSUPP; 2147c657876SArnaldo Carvalho de Melo } 2157c657876SArnaldo Carvalho de Melo 2167c657876SArnaldo Carvalho de Melo int dccp_getsockopt(struct sock *sk, int level, int optname, 217a1d3a355SArnaldo Carvalho de Melo char __user *optval, int __user *optlen) 2187c657876SArnaldo Carvalho de Melo { 2197c657876SArnaldo Carvalho de Melo dccp_pr_debug("entry\n"); 2207c657876SArnaldo Carvalho de Melo 2217c657876SArnaldo Carvalho de Melo if (level != SOL_DCCP) 2227c657876SArnaldo Carvalho de Melo return ip_getsockopt(sk, level, optname, optval, optlen); 2237c657876SArnaldo Carvalho de Melo 2247c657876SArnaldo Carvalho de Melo return -EOPNOTSUPP; 2257c657876SArnaldo Carvalho de Melo } 2267c657876SArnaldo Carvalho de Melo 2277c657876SArnaldo Carvalho de Melo int dccp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, 2287c657876SArnaldo Carvalho de Melo size_t len) 2297c657876SArnaldo Carvalho de Melo { 2307c657876SArnaldo Carvalho de Melo const struct dccp_sock *dp = dccp_sk(sk); 2317c657876SArnaldo Carvalho de Melo const int flags = msg->msg_flags; 2327c657876SArnaldo Carvalho de Melo const int noblock = flags & MSG_DONTWAIT; 2337c657876SArnaldo Carvalho de Melo struct sk_buff *skb; 2347c657876SArnaldo Carvalho de Melo int rc, size; 2357c657876SArnaldo Carvalho de Melo long timeo; 2367c657876SArnaldo Carvalho de Melo 2377c657876SArnaldo Carvalho de Melo if (len > dp->dccps_mss_cache) 2387c657876SArnaldo Carvalho de Melo return -EMSGSIZE; 2397c657876SArnaldo Carvalho de Melo 2407c657876SArnaldo Carvalho de Melo lock_sock(sk); 24127258ee5SArnaldo Carvalho de Melo timeo = sock_sndtimeo(sk, noblock); 2427c657876SArnaldo Carvalho de Melo 2437c657876SArnaldo Carvalho de Melo /* 2447c657876SArnaldo Carvalho de Melo * We have to use sk_stream_wait_connect here to set sk_write_pending, 2457c657876SArnaldo Carvalho de Melo * so that the trick in dccp_rcv_request_sent_state_process. 2467c657876SArnaldo Carvalho de Melo */ 2477c657876SArnaldo Carvalho de Melo /* Wait for a connection to finish. */ 2487c657876SArnaldo Carvalho de Melo if ((1 << sk->sk_state) & ~(DCCPF_OPEN | DCCPF_PARTOPEN | DCCPF_CLOSING)) 2497c657876SArnaldo Carvalho de Melo if ((rc = sk_stream_wait_connect(sk, &timeo)) != 0) 25027258ee5SArnaldo Carvalho de Melo goto out_release; 2517c657876SArnaldo Carvalho de Melo 2527c657876SArnaldo Carvalho de Melo size = sk->sk_prot->max_header + len; 2537c657876SArnaldo Carvalho de Melo release_sock(sk); 2547c657876SArnaldo Carvalho de Melo skb = sock_alloc_send_skb(sk, size, noblock, &rc); 2557c657876SArnaldo Carvalho de Melo lock_sock(sk); 2567c657876SArnaldo Carvalho de Melo if (skb == NULL) 2577c657876SArnaldo Carvalho de Melo goto out_release; 2587c657876SArnaldo Carvalho de Melo 2597c657876SArnaldo Carvalho de Melo skb_reserve(skb, sk->sk_prot->max_header); 2607c657876SArnaldo Carvalho de Melo rc = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len); 26127258ee5SArnaldo Carvalho de Melo if (rc != 0) 26227258ee5SArnaldo Carvalho de Melo goto out_discard; 2637c657876SArnaldo Carvalho de Melo 26427258ee5SArnaldo Carvalho de Melo rc = dccp_write_xmit(sk, skb, len); 26520472af9SArnaldo Carvalho de Melo /* 26620472af9SArnaldo Carvalho de Melo * XXX we don't use sk_write_queue, so just discard the packet. 26720472af9SArnaldo Carvalho de Melo * Current plan however is to _use_ sk_write_queue with 26820472af9SArnaldo Carvalho de Melo * an algorith similar to tcp_sendmsg, where the main difference 26920472af9SArnaldo Carvalho de Melo * is that in DCCP we have to respect packet boundaries, so 27020472af9SArnaldo Carvalho de Melo * no coalescing of skbs. 27120472af9SArnaldo Carvalho de Melo * 27220472af9SArnaldo Carvalho de Melo * This bug was _quickly_ found & fixed by just looking at an OSTRA 27320472af9SArnaldo Carvalho de Melo * generated callgraph 8) -acme 27420472af9SArnaldo Carvalho de Melo */ 27520472af9SArnaldo Carvalho de Melo if (rc != 0) 27620472af9SArnaldo Carvalho de Melo goto out_discard; 2777c657876SArnaldo Carvalho de Melo out_release: 2787c657876SArnaldo Carvalho de Melo release_sock(sk); 2797c657876SArnaldo Carvalho de Melo return rc ? : len; 28027258ee5SArnaldo Carvalho de Melo out_discard: 28127258ee5SArnaldo Carvalho de Melo kfree_skb(skb); 2827c657876SArnaldo Carvalho de Melo goto out_release; 2837c657876SArnaldo Carvalho de Melo } 2847c657876SArnaldo Carvalho de Melo 2857c657876SArnaldo Carvalho de Melo int dccp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, 2867c657876SArnaldo Carvalho de Melo size_t len, int nonblock, int flags, int *addr_len) 2877c657876SArnaldo Carvalho de Melo { 2887c657876SArnaldo Carvalho de Melo const struct dccp_hdr *dh; 2897c657876SArnaldo Carvalho de Melo long timeo; 2907c657876SArnaldo Carvalho de Melo 2917c657876SArnaldo Carvalho de Melo lock_sock(sk); 2927c657876SArnaldo Carvalho de Melo 293531669a0SArnaldo Carvalho de Melo if (sk->sk_state == DCCP_LISTEN) { 294531669a0SArnaldo Carvalho de Melo len = -ENOTCONN; 2957c657876SArnaldo Carvalho de Melo goto out; 296531669a0SArnaldo Carvalho de Melo } 2977c657876SArnaldo Carvalho de Melo 2987c657876SArnaldo Carvalho de Melo timeo = sock_rcvtimeo(sk, nonblock); 2997c657876SArnaldo Carvalho de Melo 3007c657876SArnaldo Carvalho de Melo do { 301531669a0SArnaldo Carvalho de Melo struct sk_buff *skb = skb_peek(&sk->sk_receive_queue); 3027c657876SArnaldo Carvalho de Melo 303531669a0SArnaldo Carvalho de Melo if (skb == NULL) 304531669a0SArnaldo Carvalho de Melo goto verify_sock_status; 3057c657876SArnaldo Carvalho de Melo 3067c657876SArnaldo Carvalho de Melo dh = dccp_hdr(skb); 3077c657876SArnaldo Carvalho de Melo 3087c657876SArnaldo Carvalho de Melo if (dh->dccph_type == DCCP_PKT_DATA || 3097c657876SArnaldo Carvalho de Melo dh->dccph_type == DCCP_PKT_DATAACK) 3107c657876SArnaldo Carvalho de Melo goto found_ok_skb; 3117c657876SArnaldo Carvalho de Melo 3127c657876SArnaldo Carvalho de Melo if (dh->dccph_type == DCCP_PKT_RESET || 3137c657876SArnaldo Carvalho de Melo dh->dccph_type == DCCP_PKT_CLOSE) { 3147c657876SArnaldo Carvalho de Melo dccp_pr_debug("found fin ok!\n"); 315531669a0SArnaldo Carvalho de Melo len = 0; 3167c657876SArnaldo Carvalho de Melo goto found_fin_ok; 3177c657876SArnaldo Carvalho de Melo } 3187690af3fSArnaldo Carvalho de Melo dccp_pr_debug("packet_type=%s\n", 3197690af3fSArnaldo Carvalho de Melo dccp_packet_name(dh->dccph_type)); 320531669a0SArnaldo Carvalho de Melo sk_eat_skb(sk, skb); 321531669a0SArnaldo Carvalho de Melo verify_sock_status: 322531669a0SArnaldo Carvalho de Melo if (sock_flag(sk, SOCK_DONE)) { 323531669a0SArnaldo Carvalho de Melo len = 0; 3247c657876SArnaldo Carvalho de Melo break; 3257c657876SArnaldo Carvalho de Melo } 3267c657876SArnaldo Carvalho de Melo 327531669a0SArnaldo Carvalho de Melo if (sk->sk_err) { 328531669a0SArnaldo Carvalho de Melo len = sock_error(sk); 3297c657876SArnaldo Carvalho de Melo break; 330531669a0SArnaldo Carvalho de Melo } 331531669a0SArnaldo Carvalho de Melo 332531669a0SArnaldo Carvalho de Melo if (sk->sk_shutdown & RCV_SHUTDOWN) { 333531669a0SArnaldo Carvalho de Melo len = 0; 334531669a0SArnaldo Carvalho de Melo break; 335531669a0SArnaldo Carvalho de Melo } 3367c657876SArnaldo Carvalho de Melo 3377c657876SArnaldo Carvalho de Melo if (sk->sk_state == DCCP_CLOSED) { 3387c657876SArnaldo Carvalho de Melo if (!sock_flag(sk, SOCK_DONE)) { 3397c657876SArnaldo Carvalho de Melo /* This occurs when user tries to read 3407c657876SArnaldo Carvalho de Melo * from never connected socket. 3417c657876SArnaldo Carvalho de Melo */ 342531669a0SArnaldo Carvalho de Melo len = -ENOTCONN; 3437c657876SArnaldo Carvalho de Melo break; 3447c657876SArnaldo Carvalho de Melo } 345531669a0SArnaldo Carvalho de Melo len = 0; 3467c657876SArnaldo Carvalho de Melo break; 3477c657876SArnaldo Carvalho de Melo } 3487c657876SArnaldo Carvalho de Melo 3497c657876SArnaldo Carvalho de Melo if (!timeo) { 350531669a0SArnaldo Carvalho de Melo len = -EAGAIN; 3517c657876SArnaldo Carvalho de Melo break; 3527c657876SArnaldo Carvalho de Melo } 3537c657876SArnaldo Carvalho de Melo 3547c657876SArnaldo Carvalho de Melo if (signal_pending(current)) { 355531669a0SArnaldo Carvalho de Melo len = sock_intr_errno(timeo); 3567c657876SArnaldo Carvalho de Melo break; 3577c657876SArnaldo Carvalho de Melo } 3587c657876SArnaldo Carvalho de Melo 3597c657876SArnaldo Carvalho de Melo sk_wait_data(sk, &timeo); 3607c657876SArnaldo Carvalho de Melo continue; 3617c657876SArnaldo Carvalho de Melo found_ok_skb: 362531669a0SArnaldo Carvalho de Melo if (len > skb->len) 363531669a0SArnaldo Carvalho de Melo len = skb->len; 364531669a0SArnaldo Carvalho de Melo else if (len < skb->len) 365531669a0SArnaldo Carvalho de Melo msg->msg_flags |= MSG_TRUNC; 3667c657876SArnaldo Carvalho de Melo 367531669a0SArnaldo Carvalho de Melo if (skb_copy_datagram_iovec(skb, 0, msg->msg_iov, len)) { 3687c657876SArnaldo Carvalho de Melo /* Exception. Bailout! */ 369531669a0SArnaldo Carvalho de Melo len = -EFAULT; 3707c657876SArnaldo Carvalho de Melo break; 3717c657876SArnaldo Carvalho de Melo } 3727c657876SArnaldo Carvalho de Melo found_fin_ok: 3737c657876SArnaldo Carvalho de Melo if (!(flags & MSG_PEEK)) 3747c657876SArnaldo Carvalho de Melo sk_eat_skb(sk, skb); 3757c657876SArnaldo Carvalho de Melo break; 376531669a0SArnaldo Carvalho de Melo } while (1); 3777c657876SArnaldo Carvalho de Melo out: 3787c657876SArnaldo Carvalho de Melo release_sock(sk); 379531669a0SArnaldo Carvalho de Melo return len; 3807c657876SArnaldo Carvalho de Melo } 3817c657876SArnaldo Carvalho de Melo 3827c657876SArnaldo Carvalho de Melo static int inet_dccp_listen(struct socket *sock, int backlog) 3837c657876SArnaldo Carvalho de Melo { 3847c657876SArnaldo Carvalho de Melo struct sock *sk = sock->sk; 3857c657876SArnaldo Carvalho de Melo unsigned char old_state; 3867c657876SArnaldo Carvalho de Melo int err; 3877c657876SArnaldo Carvalho de Melo 3887c657876SArnaldo Carvalho de Melo lock_sock(sk); 3897c657876SArnaldo Carvalho de Melo 3907c657876SArnaldo Carvalho de Melo err = -EINVAL; 3917c657876SArnaldo Carvalho de Melo if (sock->state != SS_UNCONNECTED || sock->type != SOCK_DCCP) 3927c657876SArnaldo Carvalho de Melo goto out; 3937c657876SArnaldo Carvalho de Melo 3947c657876SArnaldo Carvalho de Melo old_state = sk->sk_state; 3957c657876SArnaldo Carvalho de Melo if (!((1 << old_state) & (DCCPF_CLOSED | DCCPF_LISTEN))) 3967c657876SArnaldo Carvalho de Melo goto out; 3977c657876SArnaldo Carvalho de Melo 3987c657876SArnaldo Carvalho de Melo /* Really, if the socket is already in listen state 3997c657876SArnaldo Carvalho de Melo * we can only allow the backlog to be adjusted. 4007c657876SArnaldo Carvalho de Melo */ 4017c657876SArnaldo Carvalho de Melo if (old_state != DCCP_LISTEN) { 4027c657876SArnaldo Carvalho de Melo /* 4037c657876SArnaldo Carvalho de Melo * FIXME: here it probably should be sk->sk_prot->listen_start 4047c657876SArnaldo Carvalho de Melo * see tcp_listen_start 4057c657876SArnaldo Carvalho de Melo */ 4067c657876SArnaldo Carvalho de Melo err = dccp_listen_start(sk); 4077c657876SArnaldo Carvalho de Melo if (err) 4087c657876SArnaldo Carvalho de Melo goto out; 4097c657876SArnaldo Carvalho de Melo } 4107c657876SArnaldo Carvalho de Melo sk->sk_max_ack_backlog = backlog; 4117c657876SArnaldo Carvalho de Melo err = 0; 4127c657876SArnaldo Carvalho de Melo 4137c657876SArnaldo Carvalho de Melo out: 4147c657876SArnaldo Carvalho de Melo release_sock(sk); 4157c657876SArnaldo Carvalho de Melo return err; 4167c657876SArnaldo Carvalho de Melo } 4177c657876SArnaldo Carvalho de Melo 4187c657876SArnaldo Carvalho de Melo static const unsigned char dccp_new_state[] = { 4197c657876SArnaldo Carvalho de Melo /* current state: new state: action: */ 4207c657876SArnaldo Carvalho de Melo [0] = DCCP_CLOSED, 4217c657876SArnaldo Carvalho de Melo [DCCP_OPEN] = DCCP_CLOSING | DCCP_ACTION_FIN, 4227c657876SArnaldo Carvalho de Melo [DCCP_REQUESTING] = DCCP_CLOSED, 4237c657876SArnaldo Carvalho de Melo [DCCP_PARTOPEN] = DCCP_CLOSING | DCCP_ACTION_FIN, 4247c657876SArnaldo Carvalho de Melo [DCCP_LISTEN] = DCCP_CLOSED, 4257c657876SArnaldo Carvalho de Melo [DCCP_RESPOND] = DCCP_CLOSED, 4267c657876SArnaldo Carvalho de Melo [DCCP_CLOSING] = DCCP_CLOSED, 4277c657876SArnaldo Carvalho de Melo [DCCP_TIME_WAIT] = DCCP_CLOSED, 4287c657876SArnaldo Carvalho de Melo [DCCP_CLOSED] = DCCP_CLOSED, 4297c657876SArnaldo Carvalho de Melo }; 4307c657876SArnaldo Carvalho de Melo 4317c657876SArnaldo Carvalho de Melo static int dccp_close_state(struct sock *sk) 4327c657876SArnaldo Carvalho de Melo { 4337c657876SArnaldo Carvalho de Melo const int next = dccp_new_state[sk->sk_state]; 4347c657876SArnaldo Carvalho de Melo const int ns = next & DCCP_STATE_MASK; 4357c657876SArnaldo Carvalho de Melo 4367c657876SArnaldo Carvalho de Melo if (ns != sk->sk_state) 4377c657876SArnaldo Carvalho de Melo dccp_set_state(sk, ns); 4387c657876SArnaldo Carvalho de Melo 4397c657876SArnaldo Carvalho de Melo return next & DCCP_ACTION_FIN; 4407c657876SArnaldo Carvalho de Melo } 4417c657876SArnaldo Carvalho de Melo 4427c657876SArnaldo Carvalho de Melo void dccp_close(struct sock *sk, long timeout) 4437c657876SArnaldo Carvalho de Melo { 4447c657876SArnaldo Carvalho de Melo struct sk_buff *skb; 4457c657876SArnaldo Carvalho de Melo 4467c657876SArnaldo Carvalho de Melo lock_sock(sk); 4477c657876SArnaldo Carvalho de Melo 4487c657876SArnaldo Carvalho de Melo sk->sk_shutdown = SHUTDOWN_MASK; 4497c657876SArnaldo Carvalho de Melo 4507c657876SArnaldo Carvalho de Melo if (sk->sk_state == DCCP_LISTEN) { 4517c657876SArnaldo Carvalho de Melo dccp_set_state(sk, DCCP_CLOSED); 4527c657876SArnaldo Carvalho de Melo 4537c657876SArnaldo Carvalho de Melo /* Special case. */ 4547c657876SArnaldo Carvalho de Melo inet_csk_listen_stop(sk); 4557c657876SArnaldo Carvalho de Melo 4567c657876SArnaldo Carvalho de Melo goto adjudge_to_death; 4577c657876SArnaldo Carvalho de Melo } 4587c657876SArnaldo Carvalho de Melo 4597c657876SArnaldo Carvalho de Melo /* 4607c657876SArnaldo Carvalho de Melo * We need to flush the recv. buffs. We do this only on the 4617c657876SArnaldo Carvalho de Melo * descriptor close, not protocol-sourced closes, because the 4627c657876SArnaldo Carvalho de Melo *reader process may not have drained the data yet! 4637c657876SArnaldo Carvalho de Melo */ 4647c657876SArnaldo Carvalho de Melo /* FIXME: check for unread data */ 4657c657876SArnaldo Carvalho de Melo while ((skb = __skb_dequeue(&sk->sk_receive_queue)) != NULL) { 4667c657876SArnaldo Carvalho de Melo __kfree_skb(skb); 4677c657876SArnaldo Carvalho de Melo } 4687c657876SArnaldo Carvalho de Melo 4697c657876SArnaldo Carvalho de Melo if (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime) { 4707c657876SArnaldo Carvalho de Melo /* Check zero linger _after_ checking for unread data. */ 4717c657876SArnaldo Carvalho de Melo sk->sk_prot->disconnect(sk, 0); 4727c657876SArnaldo Carvalho de Melo } else if (dccp_close_state(sk)) { 4737ad07e7cSArnaldo Carvalho de Melo dccp_send_close(sk, 1); 4747c657876SArnaldo Carvalho de Melo } 4757c657876SArnaldo Carvalho de Melo 4767c657876SArnaldo Carvalho de Melo sk_stream_wait_close(sk, timeout); 4777c657876SArnaldo Carvalho de Melo 4787c657876SArnaldo Carvalho de Melo adjudge_to_death: 4797ad07e7cSArnaldo Carvalho de Melo /* 4807ad07e7cSArnaldo Carvalho de Melo * It is the last release_sock in its life. It will remove backlog. 4817ad07e7cSArnaldo Carvalho de Melo */ 4827c657876SArnaldo Carvalho de Melo release_sock(sk); 4837c657876SArnaldo Carvalho de Melo /* 4847c657876SArnaldo Carvalho de Melo * Now socket is owned by kernel and we acquire BH lock 4857c657876SArnaldo Carvalho de Melo * to finish close. No need to check for user refs. 4867c657876SArnaldo Carvalho de Melo */ 4877c657876SArnaldo Carvalho de Melo local_bh_disable(); 4887c657876SArnaldo Carvalho de Melo bh_lock_sock(sk); 4897c657876SArnaldo Carvalho de Melo BUG_TRAP(!sock_owned_by_user(sk)); 4907c657876SArnaldo Carvalho de Melo 4917c657876SArnaldo Carvalho de Melo sock_hold(sk); 4927c657876SArnaldo Carvalho de Melo sock_orphan(sk); 4937c657876SArnaldo Carvalho de Melo 4947ad07e7cSArnaldo Carvalho de Melo /* 4957ad07e7cSArnaldo Carvalho de Melo * The last release_sock may have processed the CLOSE or RESET 4967ad07e7cSArnaldo Carvalho de Melo * packet moving sock to CLOSED state, if not we have to fire 4977ad07e7cSArnaldo Carvalho de Melo * the CLOSE/CLOSEREQ retransmission timer, see "8.3. Termination" 4987ad07e7cSArnaldo Carvalho de Melo * in draft-ietf-dccp-spec-11. -acme 4997ad07e7cSArnaldo Carvalho de Melo */ 5007ad07e7cSArnaldo Carvalho de Melo if (sk->sk_state == DCCP_CLOSING) { 5017ad07e7cSArnaldo Carvalho de Melo /* FIXME: should start at 2 * RTT */ 5027ad07e7cSArnaldo Carvalho de Melo /* Timer for repeating the CLOSE/CLOSEREQ until an answer. */ 5037ad07e7cSArnaldo Carvalho de Melo inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, 5047ad07e7cSArnaldo Carvalho de Melo inet_csk(sk)->icsk_rto, 5057ad07e7cSArnaldo Carvalho de Melo DCCP_RTO_MAX); 5067ad07e7cSArnaldo Carvalho de Melo #if 0 5077ad07e7cSArnaldo Carvalho de Melo /* Yeah, we should use sk->sk_prot->orphan_count, etc */ 5087c657876SArnaldo Carvalho de Melo dccp_set_state(sk, DCCP_CLOSED); 5097ad07e7cSArnaldo Carvalho de Melo #endif 5107ad07e7cSArnaldo Carvalho de Melo } 5117c657876SArnaldo Carvalho de Melo 5127ad07e7cSArnaldo Carvalho de Melo atomic_inc(sk->sk_prot->orphan_count); 5137c657876SArnaldo Carvalho de Melo if (sk->sk_state == DCCP_CLOSED) 5147c657876SArnaldo Carvalho de Melo inet_csk_destroy_sock(sk); 5157c657876SArnaldo Carvalho de Melo 5167c657876SArnaldo Carvalho de Melo /* Otherwise, socket is reprieved until protocol close. */ 5177c657876SArnaldo Carvalho de Melo 5187c657876SArnaldo Carvalho de Melo bh_unlock_sock(sk); 5197c657876SArnaldo Carvalho de Melo local_bh_enable(); 5207c657876SArnaldo Carvalho de Melo sock_put(sk); 5217c657876SArnaldo Carvalho de Melo } 5227c657876SArnaldo Carvalho de Melo 5237c657876SArnaldo Carvalho de Melo void dccp_shutdown(struct sock *sk, int how) 5247c657876SArnaldo Carvalho de Melo { 5257c657876SArnaldo Carvalho de Melo dccp_pr_debug("entry\n"); 5267c657876SArnaldo Carvalho de Melo } 5277c657876SArnaldo Carvalho de Melo 528a1d3a355SArnaldo Carvalho de Melo static struct proto_ops inet_dccp_ops = { 5297c657876SArnaldo Carvalho de Melo .family = PF_INET, 5307c657876SArnaldo Carvalho de Melo .owner = THIS_MODULE, 5317c657876SArnaldo Carvalho de Melo .release = inet_release, 5327c657876SArnaldo Carvalho de Melo .bind = inet_bind, 5337c657876SArnaldo Carvalho de Melo .connect = inet_stream_connect, 5347c657876SArnaldo Carvalho de Melo .socketpair = sock_no_socketpair, 5357c657876SArnaldo Carvalho de Melo .accept = inet_accept, 5367c657876SArnaldo Carvalho de Melo .getname = inet_getname, 537*331968bdSArnaldo Carvalho de Melo /* FIXME: work on tcp_poll to rename it to inet_csk_poll */ 538*331968bdSArnaldo Carvalho de Melo .poll = dccp_poll, 5397c657876SArnaldo Carvalho de Melo .ioctl = inet_ioctl, 5407690af3fSArnaldo Carvalho de Melo /* FIXME: work on inet_listen to rename it to sock_common_listen */ 5417690af3fSArnaldo Carvalho de Melo .listen = inet_dccp_listen, 5427c657876SArnaldo Carvalho de Melo .shutdown = inet_shutdown, 5437c657876SArnaldo Carvalho de Melo .setsockopt = sock_common_setsockopt, 5447c657876SArnaldo Carvalho de Melo .getsockopt = sock_common_getsockopt, 5457c657876SArnaldo Carvalho de Melo .sendmsg = inet_sendmsg, 5467c657876SArnaldo Carvalho de Melo .recvmsg = sock_common_recvmsg, 5477c657876SArnaldo Carvalho de Melo .mmap = sock_no_mmap, 5487c657876SArnaldo Carvalho de Melo .sendpage = sock_no_sendpage, 5497c657876SArnaldo Carvalho de Melo }; 5507c657876SArnaldo Carvalho de Melo 5517c657876SArnaldo Carvalho de Melo extern struct net_proto_family inet_family_ops; 5527c657876SArnaldo Carvalho de Melo 5537c657876SArnaldo Carvalho de Melo static struct inet_protosw dccp_v4_protosw = { 5547c657876SArnaldo Carvalho de Melo .type = SOCK_DCCP, 5557c657876SArnaldo Carvalho de Melo .protocol = IPPROTO_DCCP, 5567c657876SArnaldo Carvalho de Melo .prot = &dccp_v4_prot, 5577c657876SArnaldo Carvalho de Melo .ops = &inet_dccp_ops, 5587c657876SArnaldo Carvalho de Melo .capability = -1, 5597c657876SArnaldo Carvalho de Melo .no_check = 0, 5607c657876SArnaldo Carvalho de Melo .flags = 0, 5617c657876SArnaldo Carvalho de Melo }; 5627c657876SArnaldo Carvalho de Melo 5637c657876SArnaldo Carvalho de Melo /* 5647c657876SArnaldo Carvalho de Melo * This is the global socket data structure used for responding to 5657c657876SArnaldo Carvalho de Melo * the Out-of-the-blue (OOTB) packets. A control sock will be created 5667c657876SArnaldo Carvalho de Melo * for this socket at the initialization time. 5677c657876SArnaldo Carvalho de Melo */ 5687c657876SArnaldo Carvalho de Melo struct socket *dccp_ctl_socket; 5697c657876SArnaldo Carvalho de Melo 5707c657876SArnaldo Carvalho de Melo static char dccp_ctl_socket_err_msg[] __initdata = 5717c657876SArnaldo Carvalho de Melo KERN_ERR "DCCP: Failed to create the control socket.\n"; 5727c657876SArnaldo Carvalho de Melo 5737c657876SArnaldo Carvalho de Melo static int __init dccp_ctl_sock_init(void) 5747c657876SArnaldo Carvalho de Melo { 5757c657876SArnaldo Carvalho de Melo int rc = sock_create_kern(PF_INET, SOCK_DCCP, IPPROTO_DCCP, 5767c657876SArnaldo Carvalho de Melo &dccp_ctl_socket); 5777c657876SArnaldo Carvalho de Melo if (rc < 0) 5787c657876SArnaldo Carvalho de Melo printk(dccp_ctl_socket_err_msg); 5797c657876SArnaldo Carvalho de Melo else { 5807c657876SArnaldo Carvalho de Melo dccp_ctl_socket->sk->sk_allocation = GFP_ATOMIC; 5817c657876SArnaldo Carvalho de Melo inet_sk(dccp_ctl_socket->sk)->uc_ttl = -1; 5827c657876SArnaldo Carvalho de Melo 5837c657876SArnaldo Carvalho de Melo /* Unhash it so that IP input processing does not even 5847c657876SArnaldo Carvalho de Melo * see it, we do not wish this socket to see incoming 5857c657876SArnaldo Carvalho de Melo * packets. 5867c657876SArnaldo Carvalho de Melo */ 5877c657876SArnaldo Carvalho de Melo dccp_ctl_socket->sk->sk_prot->unhash(dccp_ctl_socket->sk); 5887c657876SArnaldo Carvalho de Melo } 5897c657876SArnaldo Carvalho de Melo 5907c657876SArnaldo Carvalho de Melo return rc; 5917c657876SArnaldo Carvalho de Melo } 5927c657876SArnaldo Carvalho de Melo 593725ba8eeSArnaldo Carvalho de Melo #ifdef CONFIG_IP_DCCP_UNLOAD_HACK 594725ba8eeSArnaldo Carvalho de Melo void dccp_ctl_sock_exit(void) 5957c657876SArnaldo Carvalho de Melo { 5965480855bSArnaldo Carvalho de Melo if (dccp_ctl_socket != NULL) { 5977c657876SArnaldo Carvalho de Melo sock_release(dccp_ctl_socket); 5985480855bSArnaldo Carvalho de Melo dccp_ctl_socket = NULL; 5995480855bSArnaldo Carvalho de Melo } 6007c657876SArnaldo Carvalho de Melo } 6017c657876SArnaldo Carvalho de Melo 602725ba8eeSArnaldo Carvalho de Melo EXPORT_SYMBOL_GPL(dccp_ctl_sock_exit); 603725ba8eeSArnaldo Carvalho de Melo #endif 604725ba8eeSArnaldo Carvalho de Melo 6057c657876SArnaldo Carvalho de Melo static int __init init_dccp_v4_mibs(void) 6067c657876SArnaldo Carvalho de Melo { 6077c657876SArnaldo Carvalho de Melo int rc = -ENOMEM; 6087c657876SArnaldo Carvalho de Melo 6097c657876SArnaldo Carvalho de Melo dccp_statistics[0] = alloc_percpu(struct dccp_mib); 6107c657876SArnaldo Carvalho de Melo if (dccp_statistics[0] == NULL) 6117c657876SArnaldo Carvalho de Melo goto out; 6127c657876SArnaldo Carvalho de Melo 6137c657876SArnaldo Carvalho de Melo dccp_statistics[1] = alloc_percpu(struct dccp_mib); 6147c657876SArnaldo Carvalho de Melo if (dccp_statistics[1] == NULL) 6157c657876SArnaldo Carvalho de Melo goto out_free_one; 6167c657876SArnaldo Carvalho de Melo 6177c657876SArnaldo Carvalho de Melo rc = 0; 6187c657876SArnaldo Carvalho de Melo out: 6197c657876SArnaldo Carvalho de Melo return rc; 6207c657876SArnaldo Carvalho de Melo out_free_one: 6217c657876SArnaldo Carvalho de Melo free_percpu(dccp_statistics[0]); 6227c657876SArnaldo Carvalho de Melo dccp_statistics[0] = NULL; 6237c657876SArnaldo Carvalho de Melo goto out; 6247c657876SArnaldo Carvalho de Melo 6257c657876SArnaldo Carvalho de Melo } 6267c657876SArnaldo Carvalho de Melo 6277c657876SArnaldo Carvalho de Melo static int thash_entries; 6287c657876SArnaldo Carvalho de Melo module_param(thash_entries, int, 0444); 6297c657876SArnaldo Carvalho de Melo MODULE_PARM_DESC(thash_entries, "Number of ehash buckets"); 6307c657876SArnaldo Carvalho de Melo 631a1d3a355SArnaldo Carvalho de Melo #ifdef CONFIG_IP_DCCP_DEBUG 6327c657876SArnaldo Carvalho de Melo int dccp_debug; 6337c657876SArnaldo Carvalho de Melo module_param(dccp_debug, int, 0444); 6347c657876SArnaldo Carvalho de Melo MODULE_PARM_DESC(dccp_debug, "Enable debug messages"); 635a1d3a355SArnaldo Carvalho de Melo #endif 6367c657876SArnaldo Carvalho de Melo 6377c657876SArnaldo Carvalho de Melo static int __init dccp_init(void) 6387c657876SArnaldo Carvalho de Melo { 6397c657876SArnaldo Carvalho de Melo unsigned long goal; 6407c657876SArnaldo Carvalho de Melo int ehash_order, bhash_order, i; 6417c657876SArnaldo Carvalho de Melo int rc = proto_register(&dccp_v4_prot, 1); 6427c657876SArnaldo Carvalho de Melo 6437c657876SArnaldo Carvalho de Melo if (rc) 6447c657876SArnaldo Carvalho de Melo goto out; 6457c657876SArnaldo Carvalho de Melo 6467690af3fSArnaldo Carvalho de Melo dccp_hashinfo.bind_bucket_cachep = 6477690af3fSArnaldo Carvalho de Melo kmem_cache_create("dccp_bind_bucket", 6487690af3fSArnaldo Carvalho de Melo sizeof(struct inet_bind_bucket), 0, 6497690af3fSArnaldo Carvalho de Melo SLAB_HWCACHE_ALIGN, NULL, NULL); 6507c657876SArnaldo Carvalho de Melo if (!dccp_hashinfo.bind_bucket_cachep) 6517c657876SArnaldo Carvalho de Melo goto out_proto_unregister; 6527c657876SArnaldo Carvalho de Melo 6537c657876SArnaldo Carvalho de Melo /* 6547c657876SArnaldo Carvalho de Melo * Size and allocate the main established and bind bucket 6557c657876SArnaldo Carvalho de Melo * hash tables. 6567c657876SArnaldo Carvalho de Melo * 6577c657876SArnaldo Carvalho de Melo * The methodology is similar to that of the buffer cache. 6587c657876SArnaldo Carvalho de Melo */ 6597c657876SArnaldo Carvalho de Melo if (num_physpages >= (128 * 1024)) 6607c657876SArnaldo Carvalho de Melo goal = num_physpages >> (21 - PAGE_SHIFT); 6617c657876SArnaldo Carvalho de Melo else 6627c657876SArnaldo Carvalho de Melo goal = num_physpages >> (23 - PAGE_SHIFT); 6637c657876SArnaldo Carvalho de Melo 6647c657876SArnaldo Carvalho de Melo if (thash_entries) 6657690af3fSArnaldo Carvalho de Melo goal = (thash_entries * 6667690af3fSArnaldo Carvalho de Melo sizeof(struct inet_ehash_bucket)) >> PAGE_SHIFT; 6677c657876SArnaldo Carvalho de Melo for (ehash_order = 0; (1UL << ehash_order) < goal; ehash_order++) 6687c657876SArnaldo Carvalho de Melo ; 6697c657876SArnaldo Carvalho de Melo do { 6707c657876SArnaldo Carvalho de Melo dccp_hashinfo.ehash_size = (1UL << ehash_order) * PAGE_SIZE / 6717c657876SArnaldo Carvalho de Melo sizeof(struct inet_ehash_bucket); 6727c657876SArnaldo Carvalho de Melo dccp_hashinfo.ehash_size >>= 1; 6737690af3fSArnaldo Carvalho de Melo while (dccp_hashinfo.ehash_size & 6747690af3fSArnaldo Carvalho de Melo (dccp_hashinfo.ehash_size - 1)) 6757c657876SArnaldo Carvalho de Melo dccp_hashinfo.ehash_size--; 6767c657876SArnaldo Carvalho de Melo dccp_hashinfo.ehash = (struct inet_ehash_bucket *) 6777c657876SArnaldo Carvalho de Melo __get_free_pages(GFP_ATOMIC, ehash_order); 6787c657876SArnaldo Carvalho de Melo } while (!dccp_hashinfo.ehash && --ehash_order > 0); 6797c657876SArnaldo Carvalho de Melo 6807c657876SArnaldo Carvalho de Melo if (!dccp_hashinfo.ehash) { 6817c657876SArnaldo Carvalho de Melo printk(KERN_CRIT "Failed to allocate DCCP " 6827c657876SArnaldo Carvalho de Melo "established hash table\n"); 6837c657876SArnaldo Carvalho de Melo goto out_free_bind_bucket_cachep; 6847c657876SArnaldo Carvalho de Melo } 6857c657876SArnaldo Carvalho de Melo 6867c657876SArnaldo Carvalho de Melo for (i = 0; i < (dccp_hashinfo.ehash_size << 1); i++) { 6877c657876SArnaldo Carvalho de Melo rwlock_init(&dccp_hashinfo.ehash[i].lock); 6887c657876SArnaldo Carvalho de Melo INIT_HLIST_HEAD(&dccp_hashinfo.ehash[i].chain); 6897c657876SArnaldo Carvalho de Melo } 6907c657876SArnaldo Carvalho de Melo 6917c657876SArnaldo Carvalho de Melo bhash_order = ehash_order; 6927c657876SArnaldo Carvalho de Melo 6937c657876SArnaldo Carvalho de Melo do { 6947c657876SArnaldo Carvalho de Melo dccp_hashinfo.bhash_size = (1UL << bhash_order) * PAGE_SIZE / 6957c657876SArnaldo Carvalho de Melo sizeof(struct inet_bind_hashbucket); 6967690af3fSArnaldo Carvalho de Melo if ((dccp_hashinfo.bhash_size > (64 * 1024)) && 6977690af3fSArnaldo Carvalho de Melo bhash_order > 0) 6987c657876SArnaldo Carvalho de Melo continue; 6997c657876SArnaldo Carvalho de Melo dccp_hashinfo.bhash = (struct inet_bind_hashbucket *) 7007c657876SArnaldo Carvalho de Melo __get_free_pages(GFP_ATOMIC, bhash_order); 7017c657876SArnaldo Carvalho de Melo } while (!dccp_hashinfo.bhash && --bhash_order >= 0); 7027c657876SArnaldo Carvalho de Melo 7037c657876SArnaldo Carvalho de Melo if (!dccp_hashinfo.bhash) { 7047c657876SArnaldo Carvalho de Melo printk(KERN_CRIT "Failed to allocate DCCP bind hash table\n"); 7057c657876SArnaldo Carvalho de Melo goto out_free_dccp_ehash; 7067c657876SArnaldo Carvalho de Melo } 7077c657876SArnaldo Carvalho de Melo 7087c657876SArnaldo Carvalho de Melo for (i = 0; i < dccp_hashinfo.bhash_size; i++) { 7097c657876SArnaldo Carvalho de Melo spin_lock_init(&dccp_hashinfo.bhash[i].lock); 7107c657876SArnaldo Carvalho de Melo INIT_HLIST_HEAD(&dccp_hashinfo.bhash[i].chain); 7117c657876SArnaldo Carvalho de Melo } 7127c657876SArnaldo Carvalho de Melo 7137c657876SArnaldo Carvalho de Melo if (init_dccp_v4_mibs()) 7147c657876SArnaldo Carvalho de Melo goto out_free_dccp_bhash; 7157c657876SArnaldo Carvalho de Melo 7167c657876SArnaldo Carvalho de Melo rc = -EAGAIN; 7177c657876SArnaldo Carvalho de Melo if (inet_add_protocol(&dccp_protocol, IPPROTO_DCCP)) 7187c657876SArnaldo Carvalho de Melo goto out_free_dccp_v4_mibs; 7197c657876SArnaldo Carvalho de Melo 7207c657876SArnaldo Carvalho de Melo inet_register_protosw(&dccp_v4_protosw); 7217c657876SArnaldo Carvalho de Melo 7227c657876SArnaldo Carvalho de Melo rc = dccp_ctl_sock_init(); 7237c657876SArnaldo Carvalho de Melo if (rc) 7247c657876SArnaldo Carvalho de Melo goto out_unregister_protosw; 7257c657876SArnaldo Carvalho de Melo out: 7267c657876SArnaldo Carvalho de Melo return rc; 7277c657876SArnaldo Carvalho de Melo out_unregister_protosw: 7287c657876SArnaldo Carvalho de Melo inet_unregister_protosw(&dccp_v4_protosw); 7297c657876SArnaldo Carvalho de Melo inet_del_protocol(&dccp_protocol, IPPROTO_DCCP); 7307c657876SArnaldo Carvalho de Melo out_free_dccp_v4_mibs: 7317c657876SArnaldo Carvalho de Melo free_percpu(dccp_statistics[0]); 7327c657876SArnaldo Carvalho de Melo free_percpu(dccp_statistics[1]); 7337c657876SArnaldo Carvalho de Melo dccp_statistics[0] = dccp_statistics[1] = NULL; 7347c657876SArnaldo Carvalho de Melo out_free_dccp_bhash: 7357c657876SArnaldo Carvalho de Melo free_pages((unsigned long)dccp_hashinfo.bhash, bhash_order); 7367c657876SArnaldo Carvalho de Melo dccp_hashinfo.bhash = NULL; 7377c657876SArnaldo Carvalho de Melo out_free_dccp_ehash: 7387c657876SArnaldo Carvalho de Melo free_pages((unsigned long)dccp_hashinfo.ehash, ehash_order); 7397c657876SArnaldo Carvalho de Melo dccp_hashinfo.ehash = NULL; 7407c657876SArnaldo Carvalho de Melo out_free_bind_bucket_cachep: 7417c657876SArnaldo Carvalho de Melo kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep); 7427c657876SArnaldo Carvalho de Melo dccp_hashinfo.bind_bucket_cachep = NULL; 7437c657876SArnaldo Carvalho de Melo out_proto_unregister: 7447c657876SArnaldo Carvalho de Melo proto_unregister(&dccp_v4_prot); 7457c657876SArnaldo Carvalho de Melo goto out; 7467c657876SArnaldo Carvalho de Melo } 7477c657876SArnaldo Carvalho de Melo 7487c657876SArnaldo Carvalho de Melo static const char dccp_del_proto_err_msg[] __exitdata = 7497c657876SArnaldo Carvalho de Melo KERN_ERR "can't remove dccp net_protocol\n"; 7507c657876SArnaldo Carvalho de Melo 7517c657876SArnaldo Carvalho de Melo static void __exit dccp_fini(void) 7527c657876SArnaldo Carvalho de Melo { 7537c657876SArnaldo Carvalho de Melo inet_unregister_protosw(&dccp_v4_protosw); 7547c657876SArnaldo Carvalho de Melo 7557c657876SArnaldo Carvalho de Melo if (inet_del_protocol(&dccp_protocol, IPPROTO_DCCP) < 0) 7567c657876SArnaldo Carvalho de Melo printk(dccp_del_proto_err_msg); 7577c657876SArnaldo Carvalho de Melo 758725ba8eeSArnaldo Carvalho de Melo free_percpu(dccp_statistics[0]); 759725ba8eeSArnaldo Carvalho de Melo free_percpu(dccp_statistics[1]); 760725ba8eeSArnaldo Carvalho de Melo free_pages((unsigned long)dccp_hashinfo.bhash, 761725ba8eeSArnaldo Carvalho de Melo get_order(dccp_hashinfo.bhash_size * 762725ba8eeSArnaldo Carvalho de Melo sizeof(struct inet_bind_hashbucket))); 763725ba8eeSArnaldo Carvalho de Melo free_pages((unsigned long)dccp_hashinfo.ehash, 764725ba8eeSArnaldo Carvalho de Melo get_order(dccp_hashinfo.ehash_size * 765725ba8eeSArnaldo Carvalho de Melo sizeof(struct inet_ehash_bucket))); 7667c657876SArnaldo Carvalho de Melo kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep); 767725ba8eeSArnaldo Carvalho de Melo proto_unregister(&dccp_v4_prot); 7687c657876SArnaldo Carvalho de Melo } 7697c657876SArnaldo Carvalho de Melo 7707c657876SArnaldo Carvalho de Melo module_init(dccp_init); 7717c657876SArnaldo Carvalho de Melo module_exit(dccp_fini); 7727c657876SArnaldo Carvalho de Melo 773bb97d31fSArnaldo Carvalho de Melo /* 774bb97d31fSArnaldo Carvalho de Melo * __stringify doesn't likes enums, so use SOCK_DCCP (6) and IPPROTO_DCCP (33) 775bb97d31fSArnaldo Carvalho de Melo * values directly, Also cover the case where the protocol is not specified, 776bb97d31fSArnaldo Carvalho de Melo * i.e. net-pf-PF_INET-proto-0-type-SOCK_DCCP 777bb97d31fSArnaldo Carvalho de Melo */ 778bb97d31fSArnaldo Carvalho de Melo MODULE_ALIAS("net-pf-" __stringify(PF_INET) "-proto-33-type-6"); 779bb97d31fSArnaldo Carvalho de Melo MODULE_ALIAS("net-pf-" __stringify(PF_INET) "-proto-0-type-6"); 7807c657876SArnaldo Carvalho de Melo MODULE_LICENSE("GPL"); 7817c657876SArnaldo Carvalho de Melo MODULE_AUTHOR("Arnaldo Carvalho de Melo <acme@conectiva.com.br>"); 7827c657876SArnaldo Carvalho de Melo MODULE_DESCRIPTION("DCCP - Datagram Congestion Controlled Protocol"); 783