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); 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 int dccp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, 2187c657876SArnaldo Carvalho de Melo size_t len, int nonblock, int flags, int *addr_len) 2197c657876SArnaldo Carvalho de Melo { 2207c657876SArnaldo Carvalho de Melo const struct dccp_hdr *dh; 2217c657876SArnaldo Carvalho de Melo long timeo; 2227c657876SArnaldo Carvalho de Melo 2237c657876SArnaldo Carvalho de Melo lock_sock(sk); 2247c657876SArnaldo Carvalho de Melo 225531669a0SArnaldo Carvalho de Melo if (sk->sk_state == DCCP_LISTEN) { 226531669a0SArnaldo Carvalho de Melo len = -ENOTCONN; 2277c657876SArnaldo Carvalho de Melo goto out; 228531669a0SArnaldo Carvalho de Melo } 2297c657876SArnaldo Carvalho de Melo 2307c657876SArnaldo Carvalho de Melo timeo = sock_rcvtimeo(sk, nonblock); 2317c657876SArnaldo Carvalho de Melo 2327c657876SArnaldo Carvalho de Melo do { 233531669a0SArnaldo Carvalho de Melo struct sk_buff *skb = skb_peek(&sk->sk_receive_queue); 2347c657876SArnaldo Carvalho de Melo 235531669a0SArnaldo Carvalho de Melo if (skb == NULL) 236531669a0SArnaldo Carvalho de Melo goto verify_sock_status; 2377c657876SArnaldo Carvalho de Melo 2387c657876SArnaldo Carvalho de Melo dh = dccp_hdr(skb); 2397c657876SArnaldo Carvalho de Melo 2407c657876SArnaldo Carvalho de Melo if (dh->dccph_type == DCCP_PKT_DATA || 2417c657876SArnaldo Carvalho de Melo dh->dccph_type == DCCP_PKT_DATAACK) 2427c657876SArnaldo Carvalho de Melo goto found_ok_skb; 2437c657876SArnaldo Carvalho de Melo 2447c657876SArnaldo Carvalho de Melo if (dh->dccph_type == DCCP_PKT_RESET || 2457c657876SArnaldo Carvalho de Melo dh->dccph_type == DCCP_PKT_CLOSE) { 2467c657876SArnaldo Carvalho de Melo dccp_pr_debug("found fin ok!\n"); 247531669a0SArnaldo Carvalho de Melo len = 0; 2487c657876SArnaldo Carvalho de Melo goto found_fin_ok; 2497c657876SArnaldo Carvalho de Melo } 2507690af3fSArnaldo Carvalho de Melo dccp_pr_debug("packet_type=%s\n", 2517690af3fSArnaldo Carvalho de Melo dccp_packet_name(dh->dccph_type)); 252531669a0SArnaldo Carvalho de Melo sk_eat_skb(sk, skb); 253531669a0SArnaldo Carvalho de Melo verify_sock_status: 254531669a0SArnaldo Carvalho de Melo if (sock_flag(sk, SOCK_DONE)) { 255531669a0SArnaldo Carvalho de Melo len = 0; 2567c657876SArnaldo Carvalho de Melo break; 2577c657876SArnaldo Carvalho de Melo } 2587c657876SArnaldo Carvalho de Melo 259531669a0SArnaldo Carvalho de Melo if (sk->sk_err) { 260531669a0SArnaldo Carvalho de Melo len = sock_error(sk); 2617c657876SArnaldo Carvalho de Melo break; 262531669a0SArnaldo Carvalho de Melo } 263531669a0SArnaldo Carvalho de Melo 264531669a0SArnaldo Carvalho de Melo if (sk->sk_shutdown & RCV_SHUTDOWN) { 265531669a0SArnaldo Carvalho de Melo len = 0; 266531669a0SArnaldo Carvalho de Melo break; 267531669a0SArnaldo Carvalho de Melo } 2687c657876SArnaldo Carvalho de Melo 2697c657876SArnaldo Carvalho de Melo if (sk->sk_state == DCCP_CLOSED) { 2707c657876SArnaldo Carvalho de Melo if (!sock_flag(sk, SOCK_DONE)) { 2717c657876SArnaldo Carvalho de Melo /* This occurs when user tries to read 2727c657876SArnaldo Carvalho de Melo * from never connected socket. 2737c657876SArnaldo Carvalho de Melo */ 274531669a0SArnaldo Carvalho de Melo len = -ENOTCONN; 2757c657876SArnaldo Carvalho de Melo break; 2767c657876SArnaldo Carvalho de Melo } 277531669a0SArnaldo Carvalho de Melo len = 0; 2787c657876SArnaldo Carvalho de Melo break; 2797c657876SArnaldo Carvalho de Melo } 2807c657876SArnaldo Carvalho de Melo 2817c657876SArnaldo Carvalho de Melo if (!timeo) { 282531669a0SArnaldo Carvalho de Melo len = -EAGAIN; 2837c657876SArnaldo Carvalho de Melo break; 2847c657876SArnaldo Carvalho de Melo } 2857c657876SArnaldo Carvalho de Melo 2867c657876SArnaldo Carvalho de Melo if (signal_pending(current)) { 287531669a0SArnaldo Carvalho de Melo len = sock_intr_errno(timeo); 2887c657876SArnaldo Carvalho de Melo break; 2897c657876SArnaldo Carvalho de Melo } 2907c657876SArnaldo Carvalho de Melo 2917c657876SArnaldo Carvalho de Melo sk_wait_data(sk, &timeo); 2927c657876SArnaldo Carvalho de Melo continue; 2937c657876SArnaldo Carvalho de Melo found_ok_skb: 294531669a0SArnaldo Carvalho de Melo if (len > skb->len) 295531669a0SArnaldo Carvalho de Melo len = skb->len; 296531669a0SArnaldo Carvalho de Melo else if (len < skb->len) 297531669a0SArnaldo Carvalho de Melo msg->msg_flags |= MSG_TRUNC; 2987c657876SArnaldo Carvalho de Melo 299531669a0SArnaldo Carvalho de Melo if (skb_copy_datagram_iovec(skb, 0, msg->msg_iov, len)) { 3007c657876SArnaldo Carvalho de Melo /* Exception. Bailout! */ 301531669a0SArnaldo Carvalho de Melo len = -EFAULT; 3027c657876SArnaldo Carvalho de Melo break; 3037c657876SArnaldo Carvalho de Melo } 3047c657876SArnaldo Carvalho de Melo found_fin_ok: 3057c657876SArnaldo Carvalho de Melo if (!(flags & MSG_PEEK)) 3067c657876SArnaldo Carvalho de Melo sk_eat_skb(sk, skb); 3077c657876SArnaldo Carvalho de Melo break; 308531669a0SArnaldo Carvalho de Melo } while (1); 3097c657876SArnaldo Carvalho de Melo out: 3107c657876SArnaldo Carvalho de Melo release_sock(sk); 311531669a0SArnaldo Carvalho de Melo return len; 3127c657876SArnaldo Carvalho de Melo } 3137c657876SArnaldo Carvalho de Melo 3147c657876SArnaldo Carvalho de Melo static int inet_dccp_listen(struct socket *sock, int backlog) 3157c657876SArnaldo Carvalho de Melo { 3167c657876SArnaldo Carvalho de Melo struct sock *sk = sock->sk; 3177c657876SArnaldo Carvalho de Melo unsigned char old_state; 3187c657876SArnaldo Carvalho de Melo int err; 3197c657876SArnaldo Carvalho de Melo 3207c657876SArnaldo Carvalho de Melo lock_sock(sk); 3217c657876SArnaldo Carvalho de Melo 3227c657876SArnaldo Carvalho de Melo err = -EINVAL; 3237c657876SArnaldo Carvalho de Melo if (sock->state != SS_UNCONNECTED || sock->type != SOCK_DCCP) 3247c657876SArnaldo Carvalho de Melo goto out; 3257c657876SArnaldo Carvalho de Melo 3267c657876SArnaldo Carvalho de Melo old_state = sk->sk_state; 3277c657876SArnaldo Carvalho de Melo if (!((1 << old_state) & (DCCPF_CLOSED | DCCPF_LISTEN))) 3287c657876SArnaldo Carvalho de Melo goto out; 3297c657876SArnaldo Carvalho de Melo 3307c657876SArnaldo Carvalho de Melo /* Really, if the socket is already in listen state 3317c657876SArnaldo Carvalho de Melo * we can only allow the backlog to be adjusted. 3327c657876SArnaldo Carvalho de Melo */ 3337c657876SArnaldo Carvalho de Melo if (old_state != DCCP_LISTEN) { 3347c657876SArnaldo Carvalho de Melo /* 3357c657876SArnaldo Carvalho de Melo * FIXME: here it probably should be sk->sk_prot->listen_start 3367c657876SArnaldo Carvalho de Melo * see tcp_listen_start 3377c657876SArnaldo Carvalho de Melo */ 3387c657876SArnaldo Carvalho de Melo err = dccp_listen_start(sk); 3397c657876SArnaldo Carvalho de Melo if (err) 3407c657876SArnaldo Carvalho de Melo goto out; 3417c657876SArnaldo Carvalho de Melo } 3427c657876SArnaldo Carvalho de Melo sk->sk_max_ack_backlog = backlog; 3437c657876SArnaldo Carvalho de Melo err = 0; 3447c657876SArnaldo Carvalho de Melo 3457c657876SArnaldo Carvalho de Melo out: 3467c657876SArnaldo Carvalho de Melo release_sock(sk); 3477c657876SArnaldo Carvalho de Melo return err; 3487c657876SArnaldo Carvalho de Melo } 3497c657876SArnaldo Carvalho de Melo 3507c657876SArnaldo Carvalho de Melo static const unsigned char dccp_new_state[] = { 3517c657876SArnaldo Carvalho de Melo /* current state: new state: action: */ 3527c657876SArnaldo Carvalho de Melo [0] = DCCP_CLOSED, 3537c657876SArnaldo Carvalho de Melo [DCCP_OPEN] = DCCP_CLOSING | DCCP_ACTION_FIN, 3547c657876SArnaldo Carvalho de Melo [DCCP_REQUESTING] = DCCP_CLOSED, 3557c657876SArnaldo Carvalho de Melo [DCCP_PARTOPEN] = DCCP_CLOSING | DCCP_ACTION_FIN, 3567c657876SArnaldo Carvalho de Melo [DCCP_LISTEN] = DCCP_CLOSED, 3577c657876SArnaldo Carvalho de Melo [DCCP_RESPOND] = DCCP_CLOSED, 3587c657876SArnaldo Carvalho de Melo [DCCP_CLOSING] = DCCP_CLOSED, 3597c657876SArnaldo Carvalho de Melo [DCCP_TIME_WAIT] = DCCP_CLOSED, 3607c657876SArnaldo Carvalho de Melo [DCCP_CLOSED] = DCCP_CLOSED, 3617c657876SArnaldo Carvalho de Melo }; 3627c657876SArnaldo Carvalho de Melo 3637c657876SArnaldo Carvalho de Melo static int dccp_close_state(struct sock *sk) 3647c657876SArnaldo Carvalho de Melo { 3657c657876SArnaldo Carvalho de Melo const int next = dccp_new_state[sk->sk_state]; 3667c657876SArnaldo Carvalho de Melo const int ns = next & DCCP_STATE_MASK; 3677c657876SArnaldo Carvalho de Melo 3687c657876SArnaldo Carvalho de Melo if (ns != sk->sk_state) 3697c657876SArnaldo Carvalho de Melo dccp_set_state(sk, ns); 3707c657876SArnaldo Carvalho de Melo 3717c657876SArnaldo Carvalho de Melo return next & DCCP_ACTION_FIN; 3727c657876SArnaldo Carvalho de Melo } 3737c657876SArnaldo Carvalho de Melo 3747c657876SArnaldo Carvalho de Melo void dccp_close(struct sock *sk, long timeout) 3757c657876SArnaldo Carvalho de Melo { 3767c657876SArnaldo Carvalho de Melo struct sk_buff *skb; 3777c657876SArnaldo Carvalho de Melo 3787c657876SArnaldo Carvalho de Melo lock_sock(sk); 3797c657876SArnaldo Carvalho de Melo 3807c657876SArnaldo Carvalho de Melo sk->sk_shutdown = SHUTDOWN_MASK; 3817c657876SArnaldo Carvalho de Melo 3827c657876SArnaldo Carvalho de Melo if (sk->sk_state == DCCP_LISTEN) { 3837c657876SArnaldo Carvalho de Melo dccp_set_state(sk, DCCP_CLOSED); 3847c657876SArnaldo Carvalho de Melo 3857c657876SArnaldo Carvalho de Melo /* Special case. */ 3867c657876SArnaldo Carvalho de Melo inet_csk_listen_stop(sk); 3877c657876SArnaldo Carvalho de Melo 3887c657876SArnaldo Carvalho de Melo goto adjudge_to_death; 3897c657876SArnaldo Carvalho de Melo } 3907c657876SArnaldo Carvalho de Melo 3917c657876SArnaldo Carvalho de Melo /* 3927c657876SArnaldo Carvalho de Melo * We need to flush the recv. buffs. We do this only on the 3937c657876SArnaldo Carvalho de Melo * descriptor close, not protocol-sourced closes, because the 3947c657876SArnaldo Carvalho de Melo *reader process may not have drained the data yet! 3957c657876SArnaldo Carvalho de Melo */ 3967c657876SArnaldo Carvalho de Melo /* FIXME: check for unread data */ 3977c657876SArnaldo Carvalho de Melo while ((skb = __skb_dequeue(&sk->sk_receive_queue)) != NULL) { 3987c657876SArnaldo Carvalho de Melo __kfree_skb(skb); 3997c657876SArnaldo Carvalho de Melo } 4007c657876SArnaldo Carvalho de Melo 4017c657876SArnaldo Carvalho de Melo if (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime) { 4027c657876SArnaldo Carvalho de Melo /* Check zero linger _after_ checking for unread data. */ 4037c657876SArnaldo Carvalho de Melo sk->sk_prot->disconnect(sk, 0); 4047c657876SArnaldo Carvalho de Melo } else if (dccp_close_state(sk)) { 4057c657876SArnaldo Carvalho de Melo dccp_send_close(sk); 4067c657876SArnaldo Carvalho de Melo } 4077c657876SArnaldo Carvalho de Melo 4087c657876SArnaldo Carvalho de Melo sk_stream_wait_close(sk, timeout); 4097c657876SArnaldo Carvalho de Melo 4107c657876SArnaldo Carvalho de Melo adjudge_to_death: 4117c657876SArnaldo Carvalho de Melo release_sock(sk); 4127c657876SArnaldo Carvalho de Melo /* 4137c657876SArnaldo Carvalho de Melo * Now socket is owned by kernel and we acquire BH lock 4147c657876SArnaldo Carvalho de Melo * to finish close. No need to check for user refs. 4157c657876SArnaldo Carvalho de Melo */ 4167c657876SArnaldo Carvalho de Melo local_bh_disable(); 4177c657876SArnaldo Carvalho de Melo bh_lock_sock(sk); 4187c657876SArnaldo Carvalho de Melo BUG_TRAP(!sock_owned_by_user(sk)); 4197c657876SArnaldo Carvalho de Melo 4207c657876SArnaldo Carvalho de Melo sock_hold(sk); 4217c657876SArnaldo Carvalho de Melo sock_orphan(sk); 4227c657876SArnaldo Carvalho de Melo 4237c657876SArnaldo Carvalho de Melo if (sk->sk_state != DCCP_CLOSED) 4247c657876SArnaldo Carvalho de Melo dccp_set_state(sk, DCCP_CLOSED); 4257c657876SArnaldo Carvalho de Melo 4267c657876SArnaldo Carvalho de Melo atomic_inc(&dccp_orphan_count); 4277c657876SArnaldo Carvalho de Melo if (sk->sk_state == DCCP_CLOSED) 4287c657876SArnaldo Carvalho de Melo inet_csk_destroy_sock(sk); 4297c657876SArnaldo Carvalho de Melo 4307c657876SArnaldo Carvalho de Melo /* Otherwise, socket is reprieved until protocol close. */ 4317c657876SArnaldo Carvalho de Melo 4327c657876SArnaldo Carvalho de Melo bh_unlock_sock(sk); 4337c657876SArnaldo Carvalho de Melo local_bh_enable(); 4347c657876SArnaldo Carvalho de Melo sock_put(sk); 4357c657876SArnaldo Carvalho de Melo } 4367c657876SArnaldo Carvalho de Melo 4377c657876SArnaldo Carvalho de Melo void dccp_shutdown(struct sock *sk, int how) 4387c657876SArnaldo Carvalho de Melo { 4397c657876SArnaldo Carvalho de Melo dccp_pr_debug("entry\n"); 4407c657876SArnaldo Carvalho de Melo } 4417c657876SArnaldo Carvalho de Melo 442a1d3a355SArnaldo Carvalho de Melo static struct proto_ops inet_dccp_ops = { 4437c657876SArnaldo Carvalho de Melo .family = PF_INET, 4447c657876SArnaldo Carvalho de Melo .owner = THIS_MODULE, 4457c657876SArnaldo Carvalho de Melo .release = inet_release, 4467c657876SArnaldo Carvalho de Melo .bind = inet_bind, 4477c657876SArnaldo Carvalho de Melo .connect = inet_stream_connect, 4487c657876SArnaldo Carvalho de Melo .socketpair = sock_no_socketpair, 4497c657876SArnaldo Carvalho de Melo .accept = inet_accept, 4507c657876SArnaldo Carvalho de Melo .getname = inet_getname, 4517c657876SArnaldo Carvalho de Melo .poll = sock_no_poll, 4527c657876SArnaldo Carvalho de Melo .ioctl = inet_ioctl, 4537690af3fSArnaldo Carvalho de Melo /* FIXME: work on inet_listen to rename it to sock_common_listen */ 4547690af3fSArnaldo Carvalho de Melo .listen = inet_dccp_listen, 4557c657876SArnaldo Carvalho de Melo .shutdown = inet_shutdown, 4567c657876SArnaldo Carvalho de Melo .setsockopt = sock_common_setsockopt, 4577c657876SArnaldo Carvalho de Melo .getsockopt = sock_common_getsockopt, 4587c657876SArnaldo Carvalho de Melo .sendmsg = inet_sendmsg, 4597c657876SArnaldo Carvalho de Melo .recvmsg = sock_common_recvmsg, 4607c657876SArnaldo Carvalho de Melo .mmap = sock_no_mmap, 4617c657876SArnaldo Carvalho de Melo .sendpage = sock_no_sendpage, 4627c657876SArnaldo Carvalho de Melo }; 4637c657876SArnaldo Carvalho de Melo 4647c657876SArnaldo Carvalho de Melo extern struct net_proto_family inet_family_ops; 4657c657876SArnaldo Carvalho de Melo 4667c657876SArnaldo Carvalho de Melo static struct inet_protosw dccp_v4_protosw = { 4677c657876SArnaldo Carvalho de Melo .type = SOCK_DCCP, 4687c657876SArnaldo Carvalho de Melo .protocol = IPPROTO_DCCP, 4697c657876SArnaldo Carvalho de Melo .prot = &dccp_v4_prot, 4707c657876SArnaldo Carvalho de Melo .ops = &inet_dccp_ops, 4717c657876SArnaldo Carvalho de Melo .capability = -1, 4727c657876SArnaldo Carvalho de Melo .no_check = 0, 4737c657876SArnaldo Carvalho de Melo .flags = 0, 4747c657876SArnaldo Carvalho de Melo }; 4757c657876SArnaldo Carvalho de Melo 4767c657876SArnaldo Carvalho de Melo /* 4777c657876SArnaldo Carvalho de Melo * This is the global socket data structure used for responding to 4787c657876SArnaldo Carvalho de Melo * the Out-of-the-blue (OOTB) packets. A control sock will be created 4797c657876SArnaldo Carvalho de Melo * for this socket at the initialization time. 4807c657876SArnaldo Carvalho de Melo */ 4817c657876SArnaldo Carvalho de Melo struct socket *dccp_ctl_socket; 4827c657876SArnaldo Carvalho de Melo 4837c657876SArnaldo Carvalho de Melo static char dccp_ctl_socket_err_msg[] __initdata = 4847c657876SArnaldo Carvalho de Melo KERN_ERR "DCCP: Failed to create the control socket.\n"; 4857c657876SArnaldo Carvalho de Melo 4867c657876SArnaldo Carvalho de Melo static int __init dccp_ctl_sock_init(void) 4877c657876SArnaldo Carvalho de Melo { 4887c657876SArnaldo Carvalho de Melo int rc = sock_create_kern(PF_INET, SOCK_DCCP, IPPROTO_DCCP, 4897c657876SArnaldo Carvalho de Melo &dccp_ctl_socket); 4907c657876SArnaldo Carvalho de Melo if (rc < 0) 4917c657876SArnaldo Carvalho de Melo printk(dccp_ctl_socket_err_msg); 4927c657876SArnaldo Carvalho de Melo else { 4937c657876SArnaldo Carvalho de Melo dccp_ctl_socket->sk->sk_allocation = GFP_ATOMIC; 4947c657876SArnaldo Carvalho de Melo inet_sk(dccp_ctl_socket->sk)->uc_ttl = -1; 4957c657876SArnaldo Carvalho de Melo 4967c657876SArnaldo Carvalho de Melo /* Unhash it so that IP input processing does not even 4977c657876SArnaldo Carvalho de Melo * see it, we do not wish this socket to see incoming 4987c657876SArnaldo Carvalho de Melo * packets. 4997c657876SArnaldo Carvalho de Melo */ 5007c657876SArnaldo Carvalho de Melo dccp_ctl_socket->sk->sk_prot->unhash(dccp_ctl_socket->sk); 5017c657876SArnaldo Carvalho de Melo } 5027c657876SArnaldo Carvalho de Melo 5037c657876SArnaldo Carvalho de Melo return rc; 5047c657876SArnaldo Carvalho de Melo } 5057c657876SArnaldo Carvalho de Melo 506725ba8eeSArnaldo Carvalho de Melo #ifdef CONFIG_IP_DCCP_UNLOAD_HACK 507725ba8eeSArnaldo Carvalho de Melo void dccp_ctl_sock_exit(void) 5087c657876SArnaldo Carvalho de Melo { 5097c657876SArnaldo Carvalho de Melo if (dccp_ctl_socket != NULL) 5107c657876SArnaldo Carvalho de Melo sock_release(dccp_ctl_socket); 5117c657876SArnaldo Carvalho de Melo } 5127c657876SArnaldo Carvalho de Melo 513725ba8eeSArnaldo Carvalho de Melo EXPORT_SYMBOL_GPL(dccp_ctl_sock_exit); 514725ba8eeSArnaldo Carvalho de Melo #endif 515725ba8eeSArnaldo Carvalho de Melo 5167c657876SArnaldo Carvalho de Melo static int __init init_dccp_v4_mibs(void) 5177c657876SArnaldo Carvalho de Melo { 5187c657876SArnaldo Carvalho de Melo int rc = -ENOMEM; 5197c657876SArnaldo Carvalho de Melo 5207c657876SArnaldo Carvalho de Melo dccp_statistics[0] = alloc_percpu(struct dccp_mib); 5217c657876SArnaldo Carvalho de Melo if (dccp_statistics[0] == NULL) 5227c657876SArnaldo Carvalho de Melo goto out; 5237c657876SArnaldo Carvalho de Melo 5247c657876SArnaldo Carvalho de Melo dccp_statistics[1] = alloc_percpu(struct dccp_mib); 5257c657876SArnaldo Carvalho de Melo if (dccp_statistics[1] == NULL) 5267c657876SArnaldo Carvalho de Melo goto out_free_one; 5277c657876SArnaldo Carvalho de Melo 5287c657876SArnaldo Carvalho de Melo rc = 0; 5297c657876SArnaldo Carvalho de Melo out: 5307c657876SArnaldo Carvalho de Melo return rc; 5317c657876SArnaldo Carvalho de Melo out_free_one: 5327c657876SArnaldo Carvalho de Melo free_percpu(dccp_statistics[0]); 5337c657876SArnaldo Carvalho de Melo dccp_statistics[0] = NULL; 5347c657876SArnaldo Carvalho de Melo goto out; 5357c657876SArnaldo Carvalho de Melo 5367c657876SArnaldo Carvalho de Melo } 5377c657876SArnaldo Carvalho de Melo 5387c657876SArnaldo Carvalho de Melo static int thash_entries; 5397c657876SArnaldo Carvalho de Melo module_param(thash_entries, int, 0444); 5407c657876SArnaldo Carvalho de Melo MODULE_PARM_DESC(thash_entries, "Number of ehash buckets"); 5417c657876SArnaldo Carvalho de Melo 542a1d3a355SArnaldo Carvalho de Melo #ifdef CONFIG_IP_DCCP_DEBUG 5437c657876SArnaldo Carvalho de Melo int dccp_debug; 5447c657876SArnaldo Carvalho de Melo module_param(dccp_debug, int, 0444); 5457c657876SArnaldo Carvalho de Melo MODULE_PARM_DESC(dccp_debug, "Enable debug messages"); 546a1d3a355SArnaldo Carvalho de Melo #endif 5477c657876SArnaldo Carvalho de Melo 5487c657876SArnaldo Carvalho de Melo static int __init dccp_init(void) 5497c657876SArnaldo Carvalho de Melo { 5507c657876SArnaldo Carvalho de Melo unsigned long goal; 5517c657876SArnaldo Carvalho de Melo int ehash_order, bhash_order, i; 5527c657876SArnaldo Carvalho de Melo int rc = proto_register(&dccp_v4_prot, 1); 5537c657876SArnaldo Carvalho de Melo 5547c657876SArnaldo Carvalho de Melo if (rc) 5557c657876SArnaldo Carvalho de Melo goto out; 5567c657876SArnaldo Carvalho de Melo 5577690af3fSArnaldo Carvalho de Melo dccp_hashinfo.bind_bucket_cachep = 5587690af3fSArnaldo Carvalho de Melo kmem_cache_create("dccp_bind_bucket", 5597690af3fSArnaldo Carvalho de Melo sizeof(struct inet_bind_bucket), 0, 5607690af3fSArnaldo Carvalho de Melo SLAB_HWCACHE_ALIGN, NULL, NULL); 5617c657876SArnaldo Carvalho de Melo if (!dccp_hashinfo.bind_bucket_cachep) 5627c657876SArnaldo Carvalho de Melo goto out_proto_unregister; 5637c657876SArnaldo Carvalho de Melo 5647c657876SArnaldo Carvalho de Melo /* 5657c657876SArnaldo Carvalho de Melo * Size and allocate the main established and bind bucket 5667c657876SArnaldo Carvalho de Melo * hash tables. 5677c657876SArnaldo Carvalho de Melo * 5687c657876SArnaldo Carvalho de Melo * The methodology is similar to that of the buffer cache. 5697c657876SArnaldo Carvalho de Melo */ 5707c657876SArnaldo Carvalho de Melo if (num_physpages >= (128 * 1024)) 5717c657876SArnaldo Carvalho de Melo goal = num_physpages >> (21 - PAGE_SHIFT); 5727c657876SArnaldo Carvalho de Melo else 5737c657876SArnaldo Carvalho de Melo goal = num_physpages >> (23 - PAGE_SHIFT); 5747c657876SArnaldo Carvalho de Melo 5757c657876SArnaldo Carvalho de Melo if (thash_entries) 5767690af3fSArnaldo Carvalho de Melo goal = (thash_entries * 5777690af3fSArnaldo Carvalho de Melo sizeof(struct inet_ehash_bucket)) >> PAGE_SHIFT; 5787c657876SArnaldo Carvalho de Melo for (ehash_order = 0; (1UL << ehash_order) < goal; ehash_order++) 5797c657876SArnaldo Carvalho de Melo ; 5807c657876SArnaldo Carvalho de Melo do { 5817c657876SArnaldo Carvalho de Melo dccp_hashinfo.ehash_size = (1UL << ehash_order) * PAGE_SIZE / 5827c657876SArnaldo Carvalho de Melo sizeof(struct inet_ehash_bucket); 5837c657876SArnaldo Carvalho de Melo dccp_hashinfo.ehash_size >>= 1; 5847690af3fSArnaldo Carvalho de Melo while (dccp_hashinfo.ehash_size & 5857690af3fSArnaldo Carvalho de Melo (dccp_hashinfo.ehash_size - 1)) 5867c657876SArnaldo Carvalho de Melo dccp_hashinfo.ehash_size--; 5877c657876SArnaldo Carvalho de Melo dccp_hashinfo.ehash = (struct inet_ehash_bucket *) 5887c657876SArnaldo Carvalho de Melo __get_free_pages(GFP_ATOMIC, ehash_order); 5897c657876SArnaldo Carvalho de Melo } while (!dccp_hashinfo.ehash && --ehash_order > 0); 5907c657876SArnaldo Carvalho de Melo 5917c657876SArnaldo Carvalho de Melo if (!dccp_hashinfo.ehash) { 5927c657876SArnaldo Carvalho de Melo printk(KERN_CRIT "Failed to allocate DCCP " 5937c657876SArnaldo Carvalho de Melo "established hash table\n"); 5947c657876SArnaldo Carvalho de Melo goto out_free_bind_bucket_cachep; 5957c657876SArnaldo Carvalho de Melo } 5967c657876SArnaldo Carvalho de Melo 5977c657876SArnaldo Carvalho de Melo for (i = 0; i < (dccp_hashinfo.ehash_size << 1); i++) { 5987c657876SArnaldo Carvalho de Melo rwlock_init(&dccp_hashinfo.ehash[i].lock); 5997c657876SArnaldo Carvalho de Melo INIT_HLIST_HEAD(&dccp_hashinfo.ehash[i].chain); 6007c657876SArnaldo Carvalho de Melo } 6017c657876SArnaldo Carvalho de Melo 6027c657876SArnaldo Carvalho de Melo bhash_order = ehash_order; 6037c657876SArnaldo Carvalho de Melo 6047c657876SArnaldo Carvalho de Melo do { 6057c657876SArnaldo Carvalho de Melo dccp_hashinfo.bhash_size = (1UL << bhash_order) * PAGE_SIZE / 6067c657876SArnaldo Carvalho de Melo sizeof(struct inet_bind_hashbucket); 6077690af3fSArnaldo Carvalho de Melo if ((dccp_hashinfo.bhash_size > (64 * 1024)) && 6087690af3fSArnaldo Carvalho de Melo bhash_order > 0) 6097c657876SArnaldo Carvalho de Melo continue; 6107c657876SArnaldo Carvalho de Melo dccp_hashinfo.bhash = (struct inet_bind_hashbucket *) 6117c657876SArnaldo Carvalho de Melo __get_free_pages(GFP_ATOMIC, bhash_order); 6127c657876SArnaldo Carvalho de Melo } while (!dccp_hashinfo.bhash && --bhash_order >= 0); 6137c657876SArnaldo Carvalho de Melo 6147c657876SArnaldo Carvalho de Melo if (!dccp_hashinfo.bhash) { 6157c657876SArnaldo Carvalho de Melo printk(KERN_CRIT "Failed to allocate DCCP bind hash table\n"); 6167c657876SArnaldo Carvalho de Melo goto out_free_dccp_ehash; 6177c657876SArnaldo Carvalho de Melo } 6187c657876SArnaldo Carvalho de Melo 6197c657876SArnaldo Carvalho de Melo for (i = 0; i < dccp_hashinfo.bhash_size; i++) { 6207c657876SArnaldo Carvalho de Melo spin_lock_init(&dccp_hashinfo.bhash[i].lock); 6217c657876SArnaldo Carvalho de Melo INIT_HLIST_HEAD(&dccp_hashinfo.bhash[i].chain); 6227c657876SArnaldo Carvalho de Melo } 6237c657876SArnaldo Carvalho de Melo 6247c657876SArnaldo Carvalho de Melo if (init_dccp_v4_mibs()) 6257c657876SArnaldo Carvalho de Melo goto out_free_dccp_bhash; 6267c657876SArnaldo Carvalho de Melo 6277c657876SArnaldo Carvalho de Melo rc = -EAGAIN; 6287c657876SArnaldo Carvalho de Melo if (inet_add_protocol(&dccp_protocol, IPPROTO_DCCP)) 6297c657876SArnaldo Carvalho de Melo goto out_free_dccp_v4_mibs; 6307c657876SArnaldo Carvalho de Melo 6317c657876SArnaldo Carvalho de Melo inet_register_protosw(&dccp_v4_protosw); 6327c657876SArnaldo Carvalho de Melo 6337c657876SArnaldo Carvalho de Melo rc = dccp_ctl_sock_init(); 6347c657876SArnaldo Carvalho de Melo if (rc) 6357c657876SArnaldo Carvalho de Melo goto out_unregister_protosw; 6367c657876SArnaldo Carvalho de Melo out: 6377c657876SArnaldo Carvalho de Melo return rc; 6387c657876SArnaldo Carvalho de Melo out_unregister_protosw: 6397c657876SArnaldo Carvalho de Melo inet_unregister_protosw(&dccp_v4_protosw); 6407c657876SArnaldo Carvalho de Melo inet_del_protocol(&dccp_protocol, IPPROTO_DCCP); 6417c657876SArnaldo Carvalho de Melo out_free_dccp_v4_mibs: 6427c657876SArnaldo Carvalho de Melo free_percpu(dccp_statistics[0]); 6437c657876SArnaldo Carvalho de Melo free_percpu(dccp_statistics[1]); 6447c657876SArnaldo Carvalho de Melo dccp_statistics[0] = dccp_statistics[1] = NULL; 6457c657876SArnaldo Carvalho de Melo out_free_dccp_bhash: 6467c657876SArnaldo Carvalho de Melo free_pages((unsigned long)dccp_hashinfo.bhash, bhash_order); 6477c657876SArnaldo Carvalho de Melo dccp_hashinfo.bhash = NULL; 6487c657876SArnaldo Carvalho de Melo out_free_dccp_ehash: 6497c657876SArnaldo Carvalho de Melo free_pages((unsigned long)dccp_hashinfo.ehash, ehash_order); 6507c657876SArnaldo Carvalho de Melo dccp_hashinfo.ehash = NULL; 6517c657876SArnaldo Carvalho de Melo out_free_bind_bucket_cachep: 6527c657876SArnaldo Carvalho de Melo kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep); 6537c657876SArnaldo Carvalho de Melo dccp_hashinfo.bind_bucket_cachep = NULL; 6547c657876SArnaldo Carvalho de Melo out_proto_unregister: 6557c657876SArnaldo Carvalho de Melo proto_unregister(&dccp_v4_prot); 6567c657876SArnaldo Carvalho de Melo goto out; 6577c657876SArnaldo Carvalho de Melo } 6587c657876SArnaldo Carvalho de Melo 6597c657876SArnaldo Carvalho de Melo static const char dccp_del_proto_err_msg[] __exitdata = 6607c657876SArnaldo Carvalho de Melo KERN_ERR "can't remove dccp net_protocol\n"; 6617c657876SArnaldo Carvalho de Melo 6627c657876SArnaldo Carvalho de Melo static void __exit dccp_fini(void) 6637c657876SArnaldo Carvalho de Melo { 6647c657876SArnaldo Carvalho de Melo inet_unregister_protosw(&dccp_v4_protosw); 6657c657876SArnaldo Carvalho de Melo 6667c657876SArnaldo Carvalho de Melo if (inet_del_protocol(&dccp_protocol, IPPROTO_DCCP) < 0) 6677c657876SArnaldo Carvalho de Melo printk(dccp_del_proto_err_msg); 6687c657876SArnaldo Carvalho de Melo 669725ba8eeSArnaldo Carvalho de Melo free_percpu(dccp_statistics[0]); 670725ba8eeSArnaldo Carvalho de Melo free_percpu(dccp_statistics[1]); 671725ba8eeSArnaldo Carvalho de Melo free_pages((unsigned long)dccp_hashinfo.bhash, 672725ba8eeSArnaldo Carvalho de Melo get_order(dccp_hashinfo.bhash_size * 673725ba8eeSArnaldo Carvalho de Melo sizeof(struct inet_bind_hashbucket))); 674725ba8eeSArnaldo Carvalho de Melo free_pages((unsigned long)dccp_hashinfo.ehash, 675725ba8eeSArnaldo Carvalho de Melo get_order(dccp_hashinfo.ehash_size * 676725ba8eeSArnaldo Carvalho de Melo sizeof(struct inet_ehash_bucket))); 6777c657876SArnaldo Carvalho de Melo kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep); 678725ba8eeSArnaldo Carvalho de Melo proto_unregister(&dccp_v4_prot); 6797c657876SArnaldo Carvalho de Melo } 6807c657876SArnaldo Carvalho de Melo 6817c657876SArnaldo Carvalho de Melo module_init(dccp_init); 6827c657876SArnaldo Carvalho de Melo module_exit(dccp_fini); 6837c657876SArnaldo Carvalho de Melo 684bb97d31fSArnaldo Carvalho de Melo /* 685bb97d31fSArnaldo Carvalho de Melo * __stringify doesn't likes enums, so use SOCK_DCCP (6) and IPPROTO_DCCP (33) 686bb97d31fSArnaldo Carvalho de Melo * values directly, Also cover the case where the protocol is not specified, 687bb97d31fSArnaldo Carvalho de Melo * i.e. net-pf-PF_INET-proto-0-type-SOCK_DCCP 688bb97d31fSArnaldo Carvalho de Melo */ 689bb97d31fSArnaldo Carvalho de Melo MODULE_ALIAS("net-pf-" __stringify(PF_INET) "-proto-33-type-6"); 690bb97d31fSArnaldo Carvalho de Melo MODULE_ALIAS("net-pf-" __stringify(PF_INET) "-proto-0-type-6"); 6917c657876SArnaldo Carvalho de Melo MODULE_LICENSE("GPL"); 6927c657876SArnaldo Carvalho de Melo MODULE_AUTHOR("Arnaldo Carvalho de Melo <acme@conectiva.com.br>"); 6937c657876SArnaldo Carvalho de Melo MODULE_DESCRIPTION("DCCP - Datagram Congestion Controlled Protocol"); 694