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, 150a1d3a355SArnaldo Carvalho de Melo char __user *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, 161a1d3a355SArnaldo Carvalho de Melo char __user *optval, int __user *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); 209*20472af9SArnaldo Carvalho de Melo /* 210*20472af9SArnaldo Carvalho de Melo * XXX we don't use sk_write_queue, so just discard the packet. 211*20472af9SArnaldo Carvalho de Melo * Current plan however is to _use_ sk_write_queue with 212*20472af9SArnaldo Carvalho de Melo * an algorith similar to tcp_sendmsg, where the main difference 213*20472af9SArnaldo Carvalho de Melo * is that in DCCP we have to respect packet boundaries, so 214*20472af9SArnaldo Carvalho de Melo * no coalescing of skbs. 215*20472af9SArnaldo Carvalho de Melo * 216*20472af9SArnaldo Carvalho de Melo * This bug was _quickly_ found & fixed by just looking at an OSTRA 217*20472af9SArnaldo Carvalho de Melo * generated callgraph 8) -acme 218*20472af9SArnaldo Carvalho de Melo */ 219*20472af9SArnaldo Carvalho de Melo if (rc != 0) 220*20472af9SArnaldo Carvalho de Melo goto out_discard; 2217c657876SArnaldo Carvalho de Melo out_release: 2227c657876SArnaldo Carvalho de Melo release_sock(sk); 2237c657876SArnaldo Carvalho de Melo return rc ? : len; 22427258ee5SArnaldo Carvalho de Melo out_discard: 22527258ee5SArnaldo Carvalho de Melo kfree_skb(skb); 2267c657876SArnaldo Carvalho de Melo goto out_release; 2277c657876SArnaldo Carvalho de Melo } 2287c657876SArnaldo Carvalho de Melo 2297c657876SArnaldo Carvalho de Melo int dccp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, 2307c657876SArnaldo Carvalho de Melo size_t len, int nonblock, int flags, int *addr_len) 2317c657876SArnaldo Carvalho de Melo { 2327c657876SArnaldo Carvalho de Melo const struct dccp_hdr *dh; 2337c657876SArnaldo Carvalho de Melo long timeo; 2347c657876SArnaldo Carvalho de Melo 2357c657876SArnaldo Carvalho de Melo lock_sock(sk); 2367c657876SArnaldo Carvalho de Melo 237531669a0SArnaldo Carvalho de Melo if (sk->sk_state == DCCP_LISTEN) { 238531669a0SArnaldo Carvalho de Melo len = -ENOTCONN; 2397c657876SArnaldo Carvalho de Melo goto out; 240531669a0SArnaldo Carvalho de Melo } 2417c657876SArnaldo Carvalho de Melo 2427c657876SArnaldo Carvalho de Melo timeo = sock_rcvtimeo(sk, nonblock); 2437c657876SArnaldo Carvalho de Melo 2447c657876SArnaldo Carvalho de Melo do { 245531669a0SArnaldo Carvalho de Melo struct sk_buff *skb = skb_peek(&sk->sk_receive_queue); 2467c657876SArnaldo Carvalho de Melo 247531669a0SArnaldo Carvalho de Melo if (skb == NULL) 248531669a0SArnaldo Carvalho de Melo goto verify_sock_status; 2497c657876SArnaldo Carvalho de Melo 2507c657876SArnaldo Carvalho de Melo dh = dccp_hdr(skb); 2517c657876SArnaldo Carvalho de Melo 2527c657876SArnaldo Carvalho de Melo if (dh->dccph_type == DCCP_PKT_DATA || 2537c657876SArnaldo Carvalho de Melo dh->dccph_type == DCCP_PKT_DATAACK) 2547c657876SArnaldo Carvalho de Melo goto found_ok_skb; 2557c657876SArnaldo Carvalho de Melo 2567c657876SArnaldo Carvalho de Melo if (dh->dccph_type == DCCP_PKT_RESET || 2577c657876SArnaldo Carvalho de Melo dh->dccph_type == DCCP_PKT_CLOSE) { 2587c657876SArnaldo Carvalho de Melo dccp_pr_debug("found fin ok!\n"); 259531669a0SArnaldo Carvalho de Melo len = 0; 2607c657876SArnaldo Carvalho de Melo goto found_fin_ok; 2617c657876SArnaldo Carvalho de Melo } 2627690af3fSArnaldo Carvalho de Melo dccp_pr_debug("packet_type=%s\n", 2637690af3fSArnaldo Carvalho de Melo dccp_packet_name(dh->dccph_type)); 264531669a0SArnaldo Carvalho de Melo sk_eat_skb(sk, skb); 265531669a0SArnaldo Carvalho de Melo verify_sock_status: 266531669a0SArnaldo Carvalho de Melo if (sock_flag(sk, SOCK_DONE)) { 267531669a0SArnaldo Carvalho de Melo len = 0; 2687c657876SArnaldo Carvalho de Melo break; 2697c657876SArnaldo Carvalho de Melo } 2707c657876SArnaldo Carvalho de Melo 271531669a0SArnaldo Carvalho de Melo if (sk->sk_err) { 272531669a0SArnaldo Carvalho de Melo len = sock_error(sk); 2737c657876SArnaldo Carvalho de Melo break; 274531669a0SArnaldo Carvalho de Melo } 275531669a0SArnaldo Carvalho de Melo 276531669a0SArnaldo Carvalho de Melo if (sk->sk_shutdown & RCV_SHUTDOWN) { 277531669a0SArnaldo Carvalho de Melo len = 0; 278531669a0SArnaldo Carvalho de Melo break; 279531669a0SArnaldo Carvalho de Melo } 2807c657876SArnaldo Carvalho de Melo 2817c657876SArnaldo Carvalho de Melo if (sk->sk_state == DCCP_CLOSED) { 2827c657876SArnaldo Carvalho de Melo if (!sock_flag(sk, SOCK_DONE)) { 2837c657876SArnaldo Carvalho de Melo /* This occurs when user tries to read 2847c657876SArnaldo Carvalho de Melo * from never connected socket. 2857c657876SArnaldo Carvalho de Melo */ 286531669a0SArnaldo Carvalho de Melo len = -ENOTCONN; 2877c657876SArnaldo Carvalho de Melo break; 2887c657876SArnaldo Carvalho de Melo } 289531669a0SArnaldo Carvalho de Melo len = 0; 2907c657876SArnaldo Carvalho de Melo break; 2917c657876SArnaldo Carvalho de Melo } 2927c657876SArnaldo Carvalho de Melo 2937c657876SArnaldo Carvalho de Melo if (!timeo) { 294531669a0SArnaldo Carvalho de Melo len = -EAGAIN; 2957c657876SArnaldo Carvalho de Melo break; 2967c657876SArnaldo Carvalho de Melo } 2977c657876SArnaldo Carvalho de Melo 2987c657876SArnaldo Carvalho de Melo if (signal_pending(current)) { 299531669a0SArnaldo Carvalho de Melo len = sock_intr_errno(timeo); 3007c657876SArnaldo Carvalho de Melo break; 3017c657876SArnaldo Carvalho de Melo } 3027c657876SArnaldo Carvalho de Melo 3037c657876SArnaldo Carvalho de Melo sk_wait_data(sk, &timeo); 3047c657876SArnaldo Carvalho de Melo continue; 3057c657876SArnaldo Carvalho de Melo found_ok_skb: 306531669a0SArnaldo Carvalho de Melo if (len > skb->len) 307531669a0SArnaldo Carvalho de Melo len = skb->len; 308531669a0SArnaldo Carvalho de Melo else if (len < skb->len) 309531669a0SArnaldo Carvalho de Melo msg->msg_flags |= MSG_TRUNC; 3107c657876SArnaldo Carvalho de Melo 311531669a0SArnaldo Carvalho de Melo if (skb_copy_datagram_iovec(skb, 0, msg->msg_iov, len)) { 3127c657876SArnaldo Carvalho de Melo /* Exception. Bailout! */ 313531669a0SArnaldo Carvalho de Melo len = -EFAULT; 3147c657876SArnaldo Carvalho de Melo break; 3157c657876SArnaldo Carvalho de Melo } 3167c657876SArnaldo Carvalho de Melo found_fin_ok: 3177c657876SArnaldo Carvalho de Melo if (!(flags & MSG_PEEK)) 3187c657876SArnaldo Carvalho de Melo sk_eat_skb(sk, skb); 3197c657876SArnaldo Carvalho de Melo break; 320531669a0SArnaldo Carvalho de Melo } while (1); 3217c657876SArnaldo Carvalho de Melo out: 3227c657876SArnaldo Carvalho de Melo release_sock(sk); 323531669a0SArnaldo Carvalho de Melo return len; 3247c657876SArnaldo Carvalho de Melo } 3257c657876SArnaldo Carvalho de Melo 3267c657876SArnaldo Carvalho de Melo static int inet_dccp_listen(struct socket *sock, int backlog) 3277c657876SArnaldo Carvalho de Melo { 3287c657876SArnaldo Carvalho de Melo struct sock *sk = sock->sk; 3297c657876SArnaldo Carvalho de Melo unsigned char old_state; 3307c657876SArnaldo Carvalho de Melo int err; 3317c657876SArnaldo Carvalho de Melo 3327c657876SArnaldo Carvalho de Melo lock_sock(sk); 3337c657876SArnaldo Carvalho de Melo 3347c657876SArnaldo Carvalho de Melo err = -EINVAL; 3357c657876SArnaldo Carvalho de Melo if (sock->state != SS_UNCONNECTED || sock->type != SOCK_DCCP) 3367c657876SArnaldo Carvalho de Melo goto out; 3377c657876SArnaldo Carvalho de Melo 3387c657876SArnaldo Carvalho de Melo old_state = sk->sk_state; 3397c657876SArnaldo Carvalho de Melo if (!((1 << old_state) & (DCCPF_CLOSED | DCCPF_LISTEN))) 3407c657876SArnaldo Carvalho de Melo goto out; 3417c657876SArnaldo Carvalho de Melo 3427c657876SArnaldo Carvalho de Melo /* Really, if the socket is already in listen state 3437c657876SArnaldo Carvalho de Melo * we can only allow the backlog to be adjusted. 3447c657876SArnaldo Carvalho de Melo */ 3457c657876SArnaldo Carvalho de Melo if (old_state != DCCP_LISTEN) { 3467c657876SArnaldo Carvalho de Melo /* 3477c657876SArnaldo Carvalho de Melo * FIXME: here it probably should be sk->sk_prot->listen_start 3487c657876SArnaldo Carvalho de Melo * see tcp_listen_start 3497c657876SArnaldo Carvalho de Melo */ 3507c657876SArnaldo Carvalho de Melo err = dccp_listen_start(sk); 3517c657876SArnaldo Carvalho de Melo if (err) 3527c657876SArnaldo Carvalho de Melo goto out; 3537c657876SArnaldo Carvalho de Melo } 3547c657876SArnaldo Carvalho de Melo sk->sk_max_ack_backlog = backlog; 3557c657876SArnaldo Carvalho de Melo err = 0; 3567c657876SArnaldo Carvalho de Melo 3577c657876SArnaldo Carvalho de Melo out: 3587c657876SArnaldo Carvalho de Melo release_sock(sk); 3597c657876SArnaldo Carvalho de Melo return err; 3607c657876SArnaldo Carvalho de Melo } 3617c657876SArnaldo Carvalho de Melo 3627c657876SArnaldo Carvalho de Melo static const unsigned char dccp_new_state[] = { 3637c657876SArnaldo Carvalho de Melo /* current state: new state: action: */ 3647c657876SArnaldo Carvalho de Melo [0] = DCCP_CLOSED, 3657c657876SArnaldo Carvalho de Melo [DCCP_OPEN] = DCCP_CLOSING | DCCP_ACTION_FIN, 3667c657876SArnaldo Carvalho de Melo [DCCP_REQUESTING] = DCCP_CLOSED, 3677c657876SArnaldo Carvalho de Melo [DCCP_PARTOPEN] = DCCP_CLOSING | DCCP_ACTION_FIN, 3687c657876SArnaldo Carvalho de Melo [DCCP_LISTEN] = DCCP_CLOSED, 3697c657876SArnaldo Carvalho de Melo [DCCP_RESPOND] = DCCP_CLOSED, 3707c657876SArnaldo Carvalho de Melo [DCCP_CLOSING] = DCCP_CLOSED, 3717c657876SArnaldo Carvalho de Melo [DCCP_TIME_WAIT] = DCCP_CLOSED, 3727c657876SArnaldo Carvalho de Melo [DCCP_CLOSED] = DCCP_CLOSED, 3737c657876SArnaldo Carvalho de Melo }; 3747c657876SArnaldo Carvalho de Melo 3757c657876SArnaldo Carvalho de Melo static int dccp_close_state(struct sock *sk) 3767c657876SArnaldo Carvalho de Melo { 3777c657876SArnaldo Carvalho de Melo const int next = dccp_new_state[sk->sk_state]; 3787c657876SArnaldo Carvalho de Melo const int ns = next & DCCP_STATE_MASK; 3797c657876SArnaldo Carvalho de Melo 3807c657876SArnaldo Carvalho de Melo if (ns != sk->sk_state) 3817c657876SArnaldo Carvalho de Melo dccp_set_state(sk, ns); 3827c657876SArnaldo Carvalho de Melo 3837c657876SArnaldo Carvalho de Melo return next & DCCP_ACTION_FIN; 3847c657876SArnaldo Carvalho de Melo } 3857c657876SArnaldo Carvalho de Melo 3867c657876SArnaldo Carvalho de Melo void dccp_close(struct sock *sk, long timeout) 3877c657876SArnaldo Carvalho de Melo { 3887c657876SArnaldo Carvalho de Melo struct sk_buff *skb; 3897c657876SArnaldo Carvalho de Melo 3907c657876SArnaldo Carvalho de Melo lock_sock(sk); 3917c657876SArnaldo Carvalho de Melo 3927c657876SArnaldo Carvalho de Melo sk->sk_shutdown = SHUTDOWN_MASK; 3937c657876SArnaldo Carvalho de Melo 3947c657876SArnaldo Carvalho de Melo if (sk->sk_state == DCCP_LISTEN) { 3957c657876SArnaldo Carvalho de Melo dccp_set_state(sk, DCCP_CLOSED); 3967c657876SArnaldo Carvalho de Melo 3977c657876SArnaldo Carvalho de Melo /* Special case. */ 3987c657876SArnaldo Carvalho de Melo inet_csk_listen_stop(sk); 3997c657876SArnaldo Carvalho de Melo 4007c657876SArnaldo Carvalho de Melo goto adjudge_to_death; 4017c657876SArnaldo Carvalho de Melo } 4027c657876SArnaldo Carvalho de Melo 4037c657876SArnaldo Carvalho de Melo /* 4047c657876SArnaldo Carvalho de Melo * We need to flush the recv. buffs. We do this only on the 4057c657876SArnaldo Carvalho de Melo * descriptor close, not protocol-sourced closes, because the 4067c657876SArnaldo Carvalho de Melo *reader process may not have drained the data yet! 4077c657876SArnaldo Carvalho de Melo */ 4087c657876SArnaldo Carvalho de Melo /* FIXME: check for unread data */ 4097c657876SArnaldo Carvalho de Melo while ((skb = __skb_dequeue(&sk->sk_receive_queue)) != NULL) { 4107c657876SArnaldo Carvalho de Melo __kfree_skb(skb); 4117c657876SArnaldo Carvalho de Melo } 4127c657876SArnaldo Carvalho de Melo 4137c657876SArnaldo Carvalho de Melo if (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime) { 4147c657876SArnaldo Carvalho de Melo /* Check zero linger _after_ checking for unread data. */ 4157c657876SArnaldo Carvalho de Melo sk->sk_prot->disconnect(sk, 0); 4167c657876SArnaldo Carvalho de Melo } else if (dccp_close_state(sk)) { 4177ad07e7cSArnaldo Carvalho de Melo dccp_send_close(sk, 1); 4187c657876SArnaldo Carvalho de Melo } 4197c657876SArnaldo Carvalho de Melo 4207c657876SArnaldo Carvalho de Melo sk_stream_wait_close(sk, timeout); 4217c657876SArnaldo Carvalho de Melo 4227c657876SArnaldo Carvalho de Melo adjudge_to_death: 4237ad07e7cSArnaldo Carvalho de Melo /* 4247ad07e7cSArnaldo Carvalho de Melo * It is the last release_sock in its life. It will remove backlog. 4257ad07e7cSArnaldo Carvalho de Melo */ 4267c657876SArnaldo Carvalho de Melo release_sock(sk); 4277c657876SArnaldo Carvalho de Melo /* 4287c657876SArnaldo Carvalho de Melo * Now socket is owned by kernel and we acquire BH lock 4297c657876SArnaldo Carvalho de Melo * to finish close. No need to check for user refs. 4307c657876SArnaldo Carvalho de Melo */ 4317c657876SArnaldo Carvalho de Melo local_bh_disable(); 4327c657876SArnaldo Carvalho de Melo bh_lock_sock(sk); 4337c657876SArnaldo Carvalho de Melo BUG_TRAP(!sock_owned_by_user(sk)); 4347c657876SArnaldo Carvalho de Melo 4357c657876SArnaldo Carvalho de Melo sock_hold(sk); 4367c657876SArnaldo Carvalho de Melo sock_orphan(sk); 4377c657876SArnaldo Carvalho de Melo 4387ad07e7cSArnaldo Carvalho de Melo /* 4397ad07e7cSArnaldo Carvalho de Melo * The last release_sock may have processed the CLOSE or RESET 4407ad07e7cSArnaldo Carvalho de Melo * packet moving sock to CLOSED state, if not we have to fire 4417ad07e7cSArnaldo Carvalho de Melo * the CLOSE/CLOSEREQ retransmission timer, see "8.3. Termination" 4427ad07e7cSArnaldo Carvalho de Melo * in draft-ietf-dccp-spec-11. -acme 4437ad07e7cSArnaldo Carvalho de Melo */ 4447ad07e7cSArnaldo Carvalho de Melo if (sk->sk_state == DCCP_CLOSING) { 4457ad07e7cSArnaldo Carvalho de Melo /* FIXME: should start at 2 * RTT */ 4467ad07e7cSArnaldo Carvalho de Melo /* Timer for repeating the CLOSE/CLOSEREQ until an answer. */ 4477ad07e7cSArnaldo Carvalho de Melo inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, 4487ad07e7cSArnaldo Carvalho de Melo inet_csk(sk)->icsk_rto, 4497ad07e7cSArnaldo Carvalho de Melo DCCP_RTO_MAX); 4507ad07e7cSArnaldo Carvalho de Melo #if 0 4517ad07e7cSArnaldo Carvalho de Melo /* Yeah, we should use sk->sk_prot->orphan_count, etc */ 4527c657876SArnaldo Carvalho de Melo dccp_set_state(sk, DCCP_CLOSED); 4537ad07e7cSArnaldo Carvalho de Melo #endif 4547ad07e7cSArnaldo Carvalho de Melo } 4557c657876SArnaldo Carvalho de Melo 4567ad07e7cSArnaldo Carvalho de Melo atomic_inc(sk->sk_prot->orphan_count); 4577c657876SArnaldo Carvalho de Melo if (sk->sk_state == DCCP_CLOSED) 4587c657876SArnaldo Carvalho de Melo inet_csk_destroy_sock(sk); 4597c657876SArnaldo Carvalho de Melo 4607c657876SArnaldo Carvalho de Melo /* Otherwise, socket is reprieved until protocol close. */ 4617c657876SArnaldo Carvalho de Melo 4627c657876SArnaldo Carvalho de Melo bh_unlock_sock(sk); 4637c657876SArnaldo Carvalho de Melo local_bh_enable(); 4647c657876SArnaldo Carvalho de Melo sock_put(sk); 4657c657876SArnaldo Carvalho de Melo } 4667c657876SArnaldo Carvalho de Melo 4677c657876SArnaldo Carvalho de Melo void dccp_shutdown(struct sock *sk, int how) 4687c657876SArnaldo Carvalho de Melo { 4697c657876SArnaldo Carvalho de Melo dccp_pr_debug("entry\n"); 4707c657876SArnaldo Carvalho de Melo } 4717c657876SArnaldo Carvalho de Melo 472a1d3a355SArnaldo Carvalho de Melo static struct proto_ops inet_dccp_ops = { 4737c657876SArnaldo Carvalho de Melo .family = PF_INET, 4747c657876SArnaldo Carvalho de Melo .owner = THIS_MODULE, 4757c657876SArnaldo Carvalho de Melo .release = inet_release, 4767c657876SArnaldo Carvalho de Melo .bind = inet_bind, 4777c657876SArnaldo Carvalho de Melo .connect = inet_stream_connect, 4787c657876SArnaldo Carvalho de Melo .socketpair = sock_no_socketpair, 4797c657876SArnaldo Carvalho de Melo .accept = inet_accept, 4807c657876SArnaldo Carvalho de Melo .getname = inet_getname, 4817c657876SArnaldo Carvalho de Melo .poll = sock_no_poll, 4827c657876SArnaldo Carvalho de Melo .ioctl = inet_ioctl, 4837690af3fSArnaldo Carvalho de Melo /* FIXME: work on inet_listen to rename it to sock_common_listen */ 4847690af3fSArnaldo Carvalho de Melo .listen = inet_dccp_listen, 4857c657876SArnaldo Carvalho de Melo .shutdown = inet_shutdown, 4867c657876SArnaldo Carvalho de Melo .setsockopt = sock_common_setsockopt, 4877c657876SArnaldo Carvalho de Melo .getsockopt = sock_common_getsockopt, 4887c657876SArnaldo Carvalho de Melo .sendmsg = inet_sendmsg, 4897c657876SArnaldo Carvalho de Melo .recvmsg = sock_common_recvmsg, 4907c657876SArnaldo Carvalho de Melo .mmap = sock_no_mmap, 4917c657876SArnaldo Carvalho de Melo .sendpage = sock_no_sendpage, 4927c657876SArnaldo Carvalho de Melo }; 4937c657876SArnaldo Carvalho de Melo 4947c657876SArnaldo Carvalho de Melo extern struct net_proto_family inet_family_ops; 4957c657876SArnaldo Carvalho de Melo 4967c657876SArnaldo Carvalho de Melo static struct inet_protosw dccp_v4_protosw = { 4977c657876SArnaldo Carvalho de Melo .type = SOCK_DCCP, 4987c657876SArnaldo Carvalho de Melo .protocol = IPPROTO_DCCP, 4997c657876SArnaldo Carvalho de Melo .prot = &dccp_v4_prot, 5007c657876SArnaldo Carvalho de Melo .ops = &inet_dccp_ops, 5017c657876SArnaldo Carvalho de Melo .capability = -1, 5027c657876SArnaldo Carvalho de Melo .no_check = 0, 5037c657876SArnaldo Carvalho de Melo .flags = 0, 5047c657876SArnaldo Carvalho de Melo }; 5057c657876SArnaldo Carvalho de Melo 5067c657876SArnaldo Carvalho de Melo /* 5077c657876SArnaldo Carvalho de Melo * This is the global socket data structure used for responding to 5087c657876SArnaldo Carvalho de Melo * the Out-of-the-blue (OOTB) packets. A control sock will be created 5097c657876SArnaldo Carvalho de Melo * for this socket at the initialization time. 5107c657876SArnaldo Carvalho de Melo */ 5117c657876SArnaldo Carvalho de Melo struct socket *dccp_ctl_socket; 5127c657876SArnaldo Carvalho de Melo 5137c657876SArnaldo Carvalho de Melo static char dccp_ctl_socket_err_msg[] __initdata = 5147c657876SArnaldo Carvalho de Melo KERN_ERR "DCCP: Failed to create the control socket.\n"; 5157c657876SArnaldo Carvalho de Melo 5167c657876SArnaldo Carvalho de Melo static int __init dccp_ctl_sock_init(void) 5177c657876SArnaldo Carvalho de Melo { 5187c657876SArnaldo Carvalho de Melo int rc = sock_create_kern(PF_INET, SOCK_DCCP, IPPROTO_DCCP, 5197c657876SArnaldo Carvalho de Melo &dccp_ctl_socket); 5207c657876SArnaldo Carvalho de Melo if (rc < 0) 5217c657876SArnaldo Carvalho de Melo printk(dccp_ctl_socket_err_msg); 5227c657876SArnaldo Carvalho de Melo else { 5237c657876SArnaldo Carvalho de Melo dccp_ctl_socket->sk->sk_allocation = GFP_ATOMIC; 5247c657876SArnaldo Carvalho de Melo inet_sk(dccp_ctl_socket->sk)->uc_ttl = -1; 5257c657876SArnaldo Carvalho de Melo 5267c657876SArnaldo Carvalho de Melo /* Unhash it so that IP input processing does not even 5277c657876SArnaldo Carvalho de Melo * see it, we do not wish this socket to see incoming 5287c657876SArnaldo Carvalho de Melo * packets. 5297c657876SArnaldo Carvalho de Melo */ 5307c657876SArnaldo Carvalho de Melo dccp_ctl_socket->sk->sk_prot->unhash(dccp_ctl_socket->sk); 5317c657876SArnaldo Carvalho de Melo } 5327c657876SArnaldo Carvalho de Melo 5337c657876SArnaldo Carvalho de Melo return rc; 5347c657876SArnaldo Carvalho de Melo } 5357c657876SArnaldo Carvalho de Melo 536725ba8eeSArnaldo Carvalho de Melo #ifdef CONFIG_IP_DCCP_UNLOAD_HACK 537725ba8eeSArnaldo Carvalho de Melo void dccp_ctl_sock_exit(void) 5387c657876SArnaldo Carvalho de Melo { 5395480855bSArnaldo Carvalho de Melo if (dccp_ctl_socket != NULL) { 5407c657876SArnaldo Carvalho de Melo sock_release(dccp_ctl_socket); 5415480855bSArnaldo Carvalho de Melo dccp_ctl_socket = NULL; 5425480855bSArnaldo Carvalho de Melo } 5437c657876SArnaldo Carvalho de Melo } 5447c657876SArnaldo Carvalho de Melo 545725ba8eeSArnaldo Carvalho de Melo EXPORT_SYMBOL_GPL(dccp_ctl_sock_exit); 546725ba8eeSArnaldo Carvalho de Melo #endif 547725ba8eeSArnaldo Carvalho de Melo 5487c657876SArnaldo Carvalho de Melo static int __init init_dccp_v4_mibs(void) 5497c657876SArnaldo Carvalho de Melo { 5507c657876SArnaldo Carvalho de Melo int rc = -ENOMEM; 5517c657876SArnaldo Carvalho de Melo 5527c657876SArnaldo Carvalho de Melo dccp_statistics[0] = alloc_percpu(struct dccp_mib); 5537c657876SArnaldo Carvalho de Melo if (dccp_statistics[0] == NULL) 5547c657876SArnaldo Carvalho de Melo goto out; 5557c657876SArnaldo Carvalho de Melo 5567c657876SArnaldo Carvalho de Melo dccp_statistics[1] = alloc_percpu(struct dccp_mib); 5577c657876SArnaldo Carvalho de Melo if (dccp_statistics[1] == NULL) 5587c657876SArnaldo Carvalho de Melo goto out_free_one; 5597c657876SArnaldo Carvalho de Melo 5607c657876SArnaldo Carvalho de Melo rc = 0; 5617c657876SArnaldo Carvalho de Melo out: 5627c657876SArnaldo Carvalho de Melo return rc; 5637c657876SArnaldo Carvalho de Melo out_free_one: 5647c657876SArnaldo Carvalho de Melo free_percpu(dccp_statistics[0]); 5657c657876SArnaldo Carvalho de Melo dccp_statistics[0] = NULL; 5667c657876SArnaldo Carvalho de Melo goto out; 5677c657876SArnaldo Carvalho de Melo 5687c657876SArnaldo Carvalho de Melo } 5697c657876SArnaldo Carvalho de Melo 5707c657876SArnaldo Carvalho de Melo static int thash_entries; 5717c657876SArnaldo Carvalho de Melo module_param(thash_entries, int, 0444); 5727c657876SArnaldo Carvalho de Melo MODULE_PARM_DESC(thash_entries, "Number of ehash buckets"); 5737c657876SArnaldo Carvalho de Melo 574a1d3a355SArnaldo Carvalho de Melo #ifdef CONFIG_IP_DCCP_DEBUG 5757c657876SArnaldo Carvalho de Melo int dccp_debug; 5767c657876SArnaldo Carvalho de Melo module_param(dccp_debug, int, 0444); 5777c657876SArnaldo Carvalho de Melo MODULE_PARM_DESC(dccp_debug, "Enable debug messages"); 578a1d3a355SArnaldo Carvalho de Melo #endif 5797c657876SArnaldo Carvalho de Melo 5807c657876SArnaldo Carvalho de Melo static int __init dccp_init(void) 5817c657876SArnaldo Carvalho de Melo { 5827c657876SArnaldo Carvalho de Melo unsigned long goal; 5837c657876SArnaldo Carvalho de Melo int ehash_order, bhash_order, i; 5847c657876SArnaldo Carvalho de Melo int rc = proto_register(&dccp_v4_prot, 1); 5857c657876SArnaldo Carvalho de Melo 5867c657876SArnaldo Carvalho de Melo if (rc) 5877c657876SArnaldo Carvalho de Melo goto out; 5887c657876SArnaldo Carvalho de Melo 5897690af3fSArnaldo Carvalho de Melo dccp_hashinfo.bind_bucket_cachep = 5907690af3fSArnaldo Carvalho de Melo kmem_cache_create("dccp_bind_bucket", 5917690af3fSArnaldo Carvalho de Melo sizeof(struct inet_bind_bucket), 0, 5927690af3fSArnaldo Carvalho de Melo SLAB_HWCACHE_ALIGN, NULL, NULL); 5937c657876SArnaldo Carvalho de Melo if (!dccp_hashinfo.bind_bucket_cachep) 5947c657876SArnaldo Carvalho de Melo goto out_proto_unregister; 5957c657876SArnaldo Carvalho de Melo 5967c657876SArnaldo Carvalho de Melo /* 5977c657876SArnaldo Carvalho de Melo * Size and allocate the main established and bind bucket 5987c657876SArnaldo Carvalho de Melo * hash tables. 5997c657876SArnaldo Carvalho de Melo * 6007c657876SArnaldo Carvalho de Melo * The methodology is similar to that of the buffer cache. 6017c657876SArnaldo Carvalho de Melo */ 6027c657876SArnaldo Carvalho de Melo if (num_physpages >= (128 * 1024)) 6037c657876SArnaldo Carvalho de Melo goal = num_physpages >> (21 - PAGE_SHIFT); 6047c657876SArnaldo Carvalho de Melo else 6057c657876SArnaldo Carvalho de Melo goal = num_physpages >> (23 - PAGE_SHIFT); 6067c657876SArnaldo Carvalho de Melo 6077c657876SArnaldo Carvalho de Melo if (thash_entries) 6087690af3fSArnaldo Carvalho de Melo goal = (thash_entries * 6097690af3fSArnaldo Carvalho de Melo sizeof(struct inet_ehash_bucket)) >> PAGE_SHIFT; 6107c657876SArnaldo Carvalho de Melo for (ehash_order = 0; (1UL << ehash_order) < goal; ehash_order++) 6117c657876SArnaldo Carvalho de Melo ; 6127c657876SArnaldo Carvalho de Melo do { 6137c657876SArnaldo Carvalho de Melo dccp_hashinfo.ehash_size = (1UL << ehash_order) * PAGE_SIZE / 6147c657876SArnaldo Carvalho de Melo sizeof(struct inet_ehash_bucket); 6157c657876SArnaldo Carvalho de Melo dccp_hashinfo.ehash_size >>= 1; 6167690af3fSArnaldo Carvalho de Melo while (dccp_hashinfo.ehash_size & 6177690af3fSArnaldo Carvalho de Melo (dccp_hashinfo.ehash_size - 1)) 6187c657876SArnaldo Carvalho de Melo dccp_hashinfo.ehash_size--; 6197c657876SArnaldo Carvalho de Melo dccp_hashinfo.ehash = (struct inet_ehash_bucket *) 6207c657876SArnaldo Carvalho de Melo __get_free_pages(GFP_ATOMIC, ehash_order); 6217c657876SArnaldo Carvalho de Melo } while (!dccp_hashinfo.ehash && --ehash_order > 0); 6227c657876SArnaldo Carvalho de Melo 6237c657876SArnaldo Carvalho de Melo if (!dccp_hashinfo.ehash) { 6247c657876SArnaldo Carvalho de Melo printk(KERN_CRIT "Failed to allocate DCCP " 6257c657876SArnaldo Carvalho de Melo "established hash table\n"); 6267c657876SArnaldo Carvalho de Melo goto out_free_bind_bucket_cachep; 6277c657876SArnaldo Carvalho de Melo } 6287c657876SArnaldo Carvalho de Melo 6297c657876SArnaldo Carvalho de Melo for (i = 0; i < (dccp_hashinfo.ehash_size << 1); i++) { 6307c657876SArnaldo Carvalho de Melo rwlock_init(&dccp_hashinfo.ehash[i].lock); 6317c657876SArnaldo Carvalho de Melo INIT_HLIST_HEAD(&dccp_hashinfo.ehash[i].chain); 6327c657876SArnaldo Carvalho de Melo } 6337c657876SArnaldo Carvalho de Melo 6347c657876SArnaldo Carvalho de Melo bhash_order = ehash_order; 6357c657876SArnaldo Carvalho de Melo 6367c657876SArnaldo Carvalho de Melo do { 6377c657876SArnaldo Carvalho de Melo dccp_hashinfo.bhash_size = (1UL << bhash_order) * PAGE_SIZE / 6387c657876SArnaldo Carvalho de Melo sizeof(struct inet_bind_hashbucket); 6397690af3fSArnaldo Carvalho de Melo if ((dccp_hashinfo.bhash_size > (64 * 1024)) && 6407690af3fSArnaldo Carvalho de Melo bhash_order > 0) 6417c657876SArnaldo Carvalho de Melo continue; 6427c657876SArnaldo Carvalho de Melo dccp_hashinfo.bhash = (struct inet_bind_hashbucket *) 6437c657876SArnaldo Carvalho de Melo __get_free_pages(GFP_ATOMIC, bhash_order); 6447c657876SArnaldo Carvalho de Melo } while (!dccp_hashinfo.bhash && --bhash_order >= 0); 6457c657876SArnaldo Carvalho de Melo 6467c657876SArnaldo Carvalho de Melo if (!dccp_hashinfo.bhash) { 6477c657876SArnaldo Carvalho de Melo printk(KERN_CRIT "Failed to allocate DCCP bind hash table\n"); 6487c657876SArnaldo Carvalho de Melo goto out_free_dccp_ehash; 6497c657876SArnaldo Carvalho de Melo } 6507c657876SArnaldo Carvalho de Melo 6517c657876SArnaldo Carvalho de Melo for (i = 0; i < dccp_hashinfo.bhash_size; i++) { 6527c657876SArnaldo Carvalho de Melo spin_lock_init(&dccp_hashinfo.bhash[i].lock); 6537c657876SArnaldo Carvalho de Melo INIT_HLIST_HEAD(&dccp_hashinfo.bhash[i].chain); 6547c657876SArnaldo Carvalho de Melo } 6557c657876SArnaldo Carvalho de Melo 6567c657876SArnaldo Carvalho de Melo if (init_dccp_v4_mibs()) 6577c657876SArnaldo Carvalho de Melo goto out_free_dccp_bhash; 6587c657876SArnaldo Carvalho de Melo 6597c657876SArnaldo Carvalho de Melo rc = -EAGAIN; 6607c657876SArnaldo Carvalho de Melo if (inet_add_protocol(&dccp_protocol, IPPROTO_DCCP)) 6617c657876SArnaldo Carvalho de Melo goto out_free_dccp_v4_mibs; 6627c657876SArnaldo Carvalho de Melo 6637c657876SArnaldo Carvalho de Melo inet_register_protosw(&dccp_v4_protosw); 6647c657876SArnaldo Carvalho de Melo 6657c657876SArnaldo Carvalho de Melo rc = dccp_ctl_sock_init(); 6667c657876SArnaldo Carvalho de Melo if (rc) 6677c657876SArnaldo Carvalho de Melo goto out_unregister_protosw; 6687c657876SArnaldo Carvalho de Melo out: 6697c657876SArnaldo Carvalho de Melo return rc; 6707c657876SArnaldo Carvalho de Melo out_unregister_protosw: 6717c657876SArnaldo Carvalho de Melo inet_unregister_protosw(&dccp_v4_protosw); 6727c657876SArnaldo Carvalho de Melo inet_del_protocol(&dccp_protocol, IPPROTO_DCCP); 6737c657876SArnaldo Carvalho de Melo out_free_dccp_v4_mibs: 6747c657876SArnaldo Carvalho de Melo free_percpu(dccp_statistics[0]); 6757c657876SArnaldo Carvalho de Melo free_percpu(dccp_statistics[1]); 6767c657876SArnaldo Carvalho de Melo dccp_statistics[0] = dccp_statistics[1] = NULL; 6777c657876SArnaldo Carvalho de Melo out_free_dccp_bhash: 6787c657876SArnaldo Carvalho de Melo free_pages((unsigned long)dccp_hashinfo.bhash, bhash_order); 6797c657876SArnaldo Carvalho de Melo dccp_hashinfo.bhash = NULL; 6807c657876SArnaldo Carvalho de Melo out_free_dccp_ehash: 6817c657876SArnaldo Carvalho de Melo free_pages((unsigned long)dccp_hashinfo.ehash, ehash_order); 6827c657876SArnaldo Carvalho de Melo dccp_hashinfo.ehash = NULL; 6837c657876SArnaldo Carvalho de Melo out_free_bind_bucket_cachep: 6847c657876SArnaldo Carvalho de Melo kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep); 6857c657876SArnaldo Carvalho de Melo dccp_hashinfo.bind_bucket_cachep = NULL; 6867c657876SArnaldo Carvalho de Melo out_proto_unregister: 6877c657876SArnaldo Carvalho de Melo proto_unregister(&dccp_v4_prot); 6887c657876SArnaldo Carvalho de Melo goto out; 6897c657876SArnaldo Carvalho de Melo } 6907c657876SArnaldo Carvalho de Melo 6917c657876SArnaldo Carvalho de Melo static const char dccp_del_proto_err_msg[] __exitdata = 6927c657876SArnaldo Carvalho de Melo KERN_ERR "can't remove dccp net_protocol\n"; 6937c657876SArnaldo Carvalho de Melo 6947c657876SArnaldo Carvalho de Melo static void __exit dccp_fini(void) 6957c657876SArnaldo Carvalho de Melo { 6967c657876SArnaldo Carvalho de Melo inet_unregister_protosw(&dccp_v4_protosw); 6977c657876SArnaldo Carvalho de Melo 6987c657876SArnaldo Carvalho de Melo if (inet_del_protocol(&dccp_protocol, IPPROTO_DCCP) < 0) 6997c657876SArnaldo Carvalho de Melo printk(dccp_del_proto_err_msg); 7007c657876SArnaldo Carvalho de Melo 701725ba8eeSArnaldo Carvalho de Melo free_percpu(dccp_statistics[0]); 702725ba8eeSArnaldo Carvalho de Melo free_percpu(dccp_statistics[1]); 703725ba8eeSArnaldo Carvalho de Melo free_pages((unsigned long)dccp_hashinfo.bhash, 704725ba8eeSArnaldo Carvalho de Melo get_order(dccp_hashinfo.bhash_size * 705725ba8eeSArnaldo Carvalho de Melo sizeof(struct inet_bind_hashbucket))); 706725ba8eeSArnaldo Carvalho de Melo free_pages((unsigned long)dccp_hashinfo.ehash, 707725ba8eeSArnaldo Carvalho de Melo get_order(dccp_hashinfo.ehash_size * 708725ba8eeSArnaldo Carvalho de Melo sizeof(struct inet_ehash_bucket))); 7097c657876SArnaldo Carvalho de Melo kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep); 710725ba8eeSArnaldo Carvalho de Melo proto_unregister(&dccp_v4_prot); 7117c657876SArnaldo Carvalho de Melo } 7127c657876SArnaldo Carvalho de Melo 7137c657876SArnaldo Carvalho de Melo module_init(dccp_init); 7147c657876SArnaldo Carvalho de Melo module_exit(dccp_fini); 7157c657876SArnaldo Carvalho de Melo 716bb97d31fSArnaldo Carvalho de Melo /* 717bb97d31fSArnaldo Carvalho de Melo * __stringify doesn't likes enums, so use SOCK_DCCP (6) and IPPROTO_DCCP (33) 718bb97d31fSArnaldo Carvalho de Melo * values directly, Also cover the case where the protocol is not specified, 719bb97d31fSArnaldo Carvalho de Melo * i.e. net-pf-PF_INET-proto-0-type-SOCK_DCCP 720bb97d31fSArnaldo Carvalho de Melo */ 721bb97d31fSArnaldo Carvalho de Melo MODULE_ALIAS("net-pf-" __stringify(PF_INET) "-proto-33-type-6"); 722bb97d31fSArnaldo Carvalho de Melo MODULE_ALIAS("net-pf-" __stringify(PF_INET) "-proto-0-type-6"); 7237c657876SArnaldo Carvalho de Melo MODULE_LICENSE("GPL"); 7247c657876SArnaldo Carvalho de Melo MODULE_AUTHOR("Arnaldo Carvalho de Melo <acme@conectiva.com.br>"); 7257c657876SArnaldo Carvalho de Melo MODULE_DESCRIPTION("DCCP - Datagram Congestion Controlled Protocol"); 726