Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[TCPDIAG]: Introduce inet_diag_{register,unregister}

Next changeset will rename tcp_diag to inet_diag and move the tcp_diag code out
of it and into a new tcp_diag.c, similar to the net/dccp/diag.c introduced in
this changeset, completing the transition to a generic inet_diag
infrastructure.

Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Arnaldo Carvalho de Melo and committed by
David S. Miller
4f5736c4 5324a040

+186 -45
+19
include/linux/tcp_diag.h
··· 5 5 #define TCPDIAG_GETSOCK 18 6 6 #define DCCPDIAG_GETSOCK 19 7 7 8 + #define INET_DIAG_GETSOCK_MAX 24 9 + 8 10 /* Socket identity */ 9 11 struct tcpdiag_sockid 10 12 { ··· 126 124 __u32 tcpv_rtt; 127 125 __u32 tcpv_minrtt; 128 126 }; 127 + 128 + #ifdef __KERNEL__ 129 + struct sock; 130 + struct inet_hashinfo; 131 + 132 + struct inet_diag_handler { 133 + struct inet_hashinfo *idiag_hashinfo; 134 + void (*idiag_get_info)(struct sock *sk, 135 + struct tcpdiagmsg *r, 136 + void *info); 137 + __u16 idiag_info_size; 138 + __u16 idiag_type; 139 + }; 140 + 141 + extern int inet_diag_register(const struct inet_diag_handler *handler); 142 + extern void inet_diag_unregister(const struct inet_diag_handler *handler); 143 + #endif /* __KERNEL__ */ 129 144 130 145 #endif /* _TCP_DIAG_H_ */
+5
net/dccp/Kconfig
··· 19 19 20 20 If in doubt, say N. 21 21 22 + config IP_DCCP_DIAG 23 + depends on IP_DCCP && IP_TCPDIAG 24 + def_tristate y if (IP_DCCP = y && IP_TCPDIAG = y) 25 + def_tristate m 26 + 22 27 source "net/dccp/ccids/Kconfig" 23 28 24 29 endmenu
+4
net/dccp/Makefile
··· 3 3 dccp-y := ccid.o input.o ipv4.o minisocks.o options.o output.o proto.o \ 4 4 timer.o packet_history.o 5 5 6 + obj-$(CONFIG_IP_DCCP_DIAG) += dccp_diag.o 7 + 6 8 obj-y += ccids/ 9 + 10 + dccp_diag-y := diag.o
+47
net/dccp/diag.c
··· 1 + /* 2 + * net/dccp/diag.c 3 + * 4 + * An implementation of the DCCP protocol 5 + * Arnaldo Carvalho de Melo <acme@mandriva.com> 6 + * 7 + * This program is free software; you can redistribute it and/or modify it 8 + * under the terms of the GNU General Public License version 2 as 9 + * published by the Free Software Foundation. 10 + */ 11 + 12 + #include <linux/config.h> 13 + 14 + #include <linux/module.h> 15 + #include <linux/tcp_diag.h> 16 + 17 + #include "dccp.h" 18 + 19 + static void dccp_diag_get_info(struct sock *sk, struct tcpdiagmsg *r, 20 + void *_info) 21 + { 22 + r->tcpdiag_rqueue = r->tcpdiag_wqueue = 0; 23 + } 24 + 25 + static struct inet_diag_handler dccp_diag_handler = { 26 + .idiag_hashinfo = &dccp_hashinfo, 27 + .idiag_get_info = dccp_diag_get_info, 28 + .idiag_type = DCCPDIAG_GETSOCK, 29 + .idiag_info_size = 0, 30 + }; 31 + 32 + static int __init dccp_diag_init(void) 33 + { 34 + return inet_diag_register(&dccp_diag_handler); 35 + } 36 + 37 + static void __exit dccp_diag_fini(void) 38 + { 39 + inet_diag_unregister(&dccp_diag_handler); 40 + } 41 + 42 + module_init(dccp_diag_init); 43 + module_exit(dccp_diag_fini); 44 + 45 + MODULE_LICENSE("GPL"); 46 + MODULE_AUTHOR("Arnaldo Carvalho de Melo <acme@mandriva.com>"); 47 + MODULE_DESCRIPTION("DCCP inet_diag handler");
-3
net/ipv4/Kconfig
··· 423 423 424 424 If unsure, say Y. 425 425 426 - config IP_TCPDIAG_DCCP 427 - def_bool (IP_TCPDIAG=y && IP_DCCP=y) || (IP_TCPDIAG=m && IP_DCCP) 428 - 429 426 config TCP_CONG_ADVANCED 430 427 bool "TCP: advanced congestion control" 431 428 ---help---
+111 -42
net/ipv4/tcp_diag.c
··· 34 34 35 35 #include <linux/tcp_diag.h> 36 36 37 + static const struct inet_diag_handler **inet_diag_table; 38 + 37 39 struct tcpdiag_entry 38 40 { 39 41 u32 *saddr; ··· 63 61 const struct inet_connection_sock *icsk = inet_csk(sk); 64 62 struct tcpdiagmsg *r; 65 63 struct nlmsghdr *nlh; 66 - struct tcp_info *info = NULL; 64 + void *info = NULL; 67 65 struct tcpdiag_meminfo *minfo = NULL; 68 66 unsigned char *b = skb->tail; 67 + const struct inet_diag_handler *handler; 68 + 69 + handler = inet_diag_table[unlh->nlmsg_type]; 70 + BUG_ON(handler == NULL); 69 71 70 72 nlh = NLMSG_PUT(skb, pid, seq, unlh->nlmsg_type, sizeof(*r)); 71 73 nlh->nlmsg_flags = nlmsg_flags; 74 + 72 75 r = NLMSG_DATA(nlh); 73 76 if (sk->sk_state != TCP_TIME_WAIT) { 74 77 if (ext & (1<<(TCPDIAG_MEMINFO-1))) 75 78 minfo = TCPDIAG_PUT(skb, TCPDIAG_MEMINFO, sizeof(*minfo)); 76 79 if (ext & (1<<(TCPDIAG_INFO-1))) 77 - info = TCPDIAG_PUT(skb, TCPDIAG_INFO, sizeof(*info)); 80 + info = TCPDIAG_PUT(skb, TCPDIAG_INFO, 81 + handler->idiag_info_size); 78 82 79 83 if ((ext & (1 << (TCPDIAG_CONG - 1))) && icsk->icsk_ca_ops) { 80 84 size_t len = strlen(icsk->icsk_ca_ops->name); ··· 163 155 r->tcpdiag_expires = 0; 164 156 } 165 157 #undef EXPIRES_IN_MS 166 - /* 167 - * Ahem... for now we'll have some knowledge about TCP -acme 168 - * But this is just one of two small exceptions, both in this 169 - * function, so lets close our eyes for some 15 lines or so... 8) 170 - * -acme 171 - */ 172 - if (sk->sk_protocol == IPPROTO_TCP) { 173 - const struct tcp_sock *tp = tcp_sk(sk); 174 - 175 - r->tcpdiag_rqueue = tp->rcv_nxt - tp->copied_seq; 176 - r->tcpdiag_wqueue = tp->write_seq - tp->snd_una; 177 - } else 178 - r->tcpdiag_rqueue = r->tcpdiag_wqueue = 0; 179 158 180 159 r->tcpdiag_uid = sock_i_uid(sk); 181 160 r->tcpdiag_inode = sock_i_ino(sk); ··· 174 179 minfo->tcpdiag_tmem = atomic_read(&sk->sk_wmem_alloc); 175 180 } 176 181 177 - /* Ahem... for now we'll have some knowledge about TCP -acme */ 178 - if (info) { 179 - if (sk->sk_protocol == IPPROTO_TCP) 180 - tcp_get_info(sk, info); 181 - else 182 - memset(info, 0, sizeof(*info)); 183 - } 182 + handler->idiag_get_info(sk, r, info); 184 183 185 184 if (sk->sk_state < TCP_TIME_WAIT && 186 185 icsk->icsk_ca_ops && icsk->icsk_ca_ops->get_info) ··· 195 206 struct sock *sk; 196 207 struct tcpdiagreq *req = NLMSG_DATA(nlh); 197 208 struct sk_buff *rep; 198 - struct inet_hashinfo *hashinfo = &tcp_hashinfo; 199 - #ifdef CONFIG_IP_TCPDIAG_DCCP 200 - if (nlh->nlmsg_type == DCCPDIAG_GETSOCK) 201 - hashinfo = &dccp_hashinfo; 202 - #endif 209 + struct inet_hashinfo *hashinfo; 210 + const struct inet_diag_handler *handler; 211 + 212 + handler = inet_diag_table[nlh->nlmsg_type]; 213 + BUG_ON(handler == NULL); 214 + hashinfo = handler->idiag_hashinfo; 215 + 203 216 if (req->tcpdiag_family == AF_INET) { 204 217 sk = inet_lookup(hashinfo, req->id.tcpdiag_dst[0], 205 218 req->id.tcpdiag_dport, req->id.tcpdiag_src[0], ··· 232 241 goto out; 233 242 234 243 err = -ENOMEM; 235 - rep = alloc_skb(NLMSG_SPACE(sizeof(struct tcpdiagmsg)+ 236 - sizeof(struct tcpdiag_meminfo)+ 237 - sizeof(struct tcp_info)+64), GFP_KERNEL); 244 + rep = alloc_skb(NLMSG_SPACE((sizeof(struct tcpdiagmsg) + 245 + sizeof(struct tcpdiag_meminfo) + 246 + handler->idiag_info_size + 64)), 247 + GFP_KERNEL); 238 248 if (!rep) 239 249 goto out; 240 250 ··· 595 603 int i, num; 596 604 int s_i, s_num; 597 605 struct tcpdiagreq *r = NLMSG_DATA(cb->nlh); 606 + const struct inet_diag_handler *handler; 598 607 struct inet_hashinfo *hashinfo; 599 608 609 + handler = inet_diag_table[cb->nlh->nlmsg_type]; 610 + BUG_ON(handler == NULL); 611 + hashinfo = handler->idiag_hashinfo; 612 + 600 613 s_i = cb->args[1]; 601 614 s_num = num = cb->args[2]; 602 - hashinfo = &tcp_hashinfo; 603 - #ifdef CONFIG_IP_TCPDIAG_DCCP 604 - if (cb->nlh->nlmsg_type == DCCPDIAG_GETSOCK) 605 - hashinfo = &dccp_hashinfo; 606 - #endif 615 + 607 616 if (cb->args[0] == 0) { 608 617 if (!(r->tcpdiag_states&(TCPF_LISTEN|TCPF_SYN_RECV))) 609 618 goto skip_listen_ht; ··· 738 745 if (!(nlh->nlmsg_flags&NLM_F_REQUEST)) 739 746 return 0; 740 747 741 - if (nlh->nlmsg_type != TCPDIAG_GETSOCK 742 - #ifdef CONFIG_IP_TCPDIAG_DCCP 743 - && nlh->nlmsg_type != DCCPDIAG_GETSOCK 744 - #endif 745 - ) 748 + if (nlh->nlmsg_type >= INET_DIAG_GETSOCK_MAX) 746 749 goto err_inval; 750 + 751 + if (inet_diag_table[nlh->nlmsg_type] == NULL) 752 + return -ENOENT; 747 753 748 754 if (NLMSG_LENGTH(sizeof(struct tcpdiagreq)) > skb->len) 749 755 goto err_inval; ··· 795 803 } 796 804 } 797 805 806 + static void tcp_diag_get_info(struct sock *sk, struct tcpdiagmsg *r, 807 + void *_info) 808 + { 809 + const struct tcp_sock *tp = tcp_sk(sk); 810 + struct tcp_info *info = _info; 811 + 812 + r->tcpdiag_rqueue = tp->rcv_nxt - tp->copied_seq; 813 + r->tcpdiag_wqueue = tp->write_seq - tp->snd_una; 814 + if (info != NULL) 815 + tcp_get_info(sk, info); 816 + } 817 + 818 + static struct inet_diag_handler tcp_diag_handler = { 819 + .idiag_hashinfo = &tcp_hashinfo, 820 + .idiag_get_info = tcp_diag_get_info, 821 + .idiag_type = TCPDIAG_GETSOCK, 822 + .idiag_info_size = sizeof(struct tcp_info), 823 + }; 824 + 825 + static DEFINE_SPINLOCK(inet_diag_register_lock); 826 + 827 + int inet_diag_register(const struct inet_diag_handler *h) 828 + { 829 + const __u16 type = h->idiag_type; 830 + int err = -EINVAL; 831 + 832 + if (type >= INET_DIAG_GETSOCK_MAX) 833 + goto out; 834 + 835 + spin_lock(&inet_diag_register_lock); 836 + err = -EEXIST; 837 + if (inet_diag_table[type] == NULL) { 838 + inet_diag_table[type] = h; 839 + err = 0; 840 + } 841 + spin_unlock(&inet_diag_register_lock); 842 + out: 843 + return err; 844 + } 845 + EXPORT_SYMBOL_GPL(inet_diag_register); 846 + 847 + void inet_diag_unregister(const struct inet_diag_handler *h) 848 + { 849 + const __u16 type = h->idiag_type; 850 + 851 + if (type >= INET_DIAG_GETSOCK_MAX) 852 + return; 853 + 854 + spin_lock(&inet_diag_register_lock); 855 + inet_diag_table[type] = NULL; 856 + spin_unlock(&inet_diag_register_lock); 857 + 858 + synchronize_rcu(); 859 + } 860 + EXPORT_SYMBOL_GPL(inet_diag_unregister); 861 + 798 862 static int __init tcpdiag_init(void) 799 863 { 864 + const int inet_diag_table_size = (INET_DIAG_GETSOCK_MAX * 865 + sizeof(struct inet_diag_handler *)); 866 + int err = -ENOMEM; 867 + 868 + inet_diag_table = kmalloc(inet_diag_table_size, GFP_KERNEL); 869 + if (!inet_diag_table) 870 + goto out; 871 + 872 + memset(inet_diag_table, 0, inet_diag_table_size); 873 + 800 874 tcpnl = netlink_kernel_create(NETLINK_TCPDIAG, tcpdiag_rcv, 801 875 THIS_MODULE); 802 876 if (tcpnl == NULL) 803 - return -ENOMEM; 804 - return 0; 877 + goto out_free_table; 878 + 879 + err = inet_diag_register(&tcp_diag_handler); 880 + if (err) 881 + goto out_sock_release; 882 + out: 883 + return err; 884 + out_sock_release: 885 + sock_release(tcpnl->sk_socket); 886 + out_free_table: 887 + kfree(inet_diag_table); 888 + goto out; 805 889 } 806 890 807 891 static void __exit tcpdiag_exit(void) 808 892 { 809 893 sock_release(tcpnl->sk_socket); 894 + kfree(inet_diag_table); 810 895 } 811 896 812 897 module_init(tcpdiag_init);