aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/sock_diag.h23
-rw-r--r--net/ipv4/inet_diag.c102
2 files changed, 123 insertions, 2 deletions
diff --git a/include/linux/sock_diag.h b/include/linux/sock_diag.h
new file mode 100644
index 000000000000..ba4933b1213b
--- /dev/null
+++ b/include/linux/sock_diag.h
@@ -0,0 +1,23 @@
1#ifndef __SOCK_DIAG_H__
2#define __SOCK_DIAG_H__
3struct sk_buff;
4struct nlmsghdr;
5
6struct sock_diag_req {
7 __u8 sdiag_family;
8 __u8 sdiag_protocol;
9};
10
11struct sock_diag_handler {
12 __u8 family;
13 int (*dump)(struct sk_buff *skb, struct nlmsghdr *nlh);
14};
15
16int sock_diag_register(struct sock_diag_handler *h);
17void sock_diag_unregister(struct sock_diag_handler *h);
18
19void sock_diag_register_inet_compat(int (*fn)(struct sk_buff *skb, struct nlmsghdr *nlh));
20void sock_diag_unregister_inet_compat(int (*fn)(struct sk_buff *skb, struct nlmsghdr *nlh));
21
22extern struct sock *sock_diag_nlsk;
23#endif
diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c
index 58caecc343b1..877875ea3d71 100644
--- a/net/ipv4/inet_diag.c
+++ b/net/ipv4/inet_diag.c
@@ -33,6 +33,7 @@
33#include <linux/stddef.h> 33#include <linux/stddef.h>
34 34
35#include <linux/inet_diag.h> 35#include <linux/inet_diag.h>
36#include <linux/sock_diag.h>
36 37
37static const struct inet_diag_handler **inet_diag_table; 38static const struct inet_diag_handler **inet_diag_table;
38 39
@@ -887,9 +888,91 @@ static int inet_diag_rcv_msg_compat(struct sk_buff *skb, struct nlmsghdr *nlh)
887 return inet_diag_get_exact(skb, nlh); 888 return inet_diag_get_exact(skb, nlh);
888} 889}
889 890
891static int inet_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h)
892{
893 int hdrlen = sizeof(struct inet_diag_req);
894
895 if (nlmsg_len(h) < hdrlen)
896 return -EINVAL;
897
898 if (h->nlmsg_flags & NLM_F_DUMP) {
899 return -EAFNOSUPPORT;
900 }
901
902 return -EAFNOSUPPORT;
903}
904
905static struct sock_diag_handler inet_diag_handler = {
906 .family = AF_INET,
907 .dump = inet_diag_handler_dump,
908};
909
910static struct sock_diag_handler inet6_diag_handler = {
911 .family = AF_INET6,
912 .dump = inet_diag_handler_dump,
913};
914
915static struct sock_diag_handler *sock_diag_handlers[AF_MAX];
916static DEFINE_MUTEX(sock_diag_table_mutex);
917
918int sock_diag_register(struct sock_diag_handler *hndl)
919{
920 int err = 0;
921
922 if (hndl->family > AF_MAX)
923 return -EINVAL;
924
925 mutex_lock(&sock_diag_table_mutex);
926 if (sock_diag_handlers[hndl->family])
927 err = -EBUSY;
928 else
929 sock_diag_handlers[hndl->family] = hndl;
930 mutex_unlock(&sock_diag_table_mutex);
931
932 return err;
933}
934
935void sock_diag_unregister(struct sock_diag_handler *hnld)
936{
937 int family = hnld->family;
938
939 if (family > AF_MAX)
940 return;
941
942 mutex_lock(&sock_diag_table_mutex);
943 BUG_ON(sock_diag_handlers[family] != hnld);
944 sock_diag_handlers[family] = NULL;
945 mutex_unlock(&sock_diag_table_mutex);
946}
947
948static inline struct sock_diag_handler *sock_diag_lock_handler(int family)
949{
950 mutex_lock(&sock_diag_table_mutex);
951 return sock_diag_handlers[family];
952}
953
954static inline void sock_diag_unlock_handler(struct sock_diag_handler *h)
955{
956 mutex_unlock(&sock_diag_table_mutex);
957}
958
890static int __sock_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) 959static int __sock_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
891{ 960{
892 return -EOPNOTSUPP; 961 int err;
962 struct sock_diag_req *req = NLMSG_DATA(nlh);
963 struct sock_diag_handler *hndl;
964
965 if (nlmsg_len(nlh) < sizeof(*req))
966 return -EINVAL;
967
968 hndl = sock_diag_lock_handler(req->sdiag_family);
969 if (hndl == NULL)
970 err = -ENOENT;
971 else
972 err = hndl->dump(skb, nlh);
973 sock_diag_unlock_handler(hndl);
974
975 return err;
893} 976}
894 977
895static int sock_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) 978static int sock_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
@@ -961,9 +1044,22 @@ static int __init inet_diag_init(void)
961 sock_diag_rcv, NULL, THIS_MODULE); 1044 sock_diag_rcv, NULL, THIS_MODULE);
962 if (sdiagnl == NULL) 1045 if (sdiagnl == NULL)
963 goto out_free_table; 1046 goto out_free_table;
964 err = 0; 1047
1048 err = sock_diag_register(&inet_diag_handler);
1049 if (err)
1050 goto out_free_nl;
1051
1052 err = sock_diag_register(&inet6_diag_handler);
1053 if (err)
1054 goto out_free_inet;
1055
965out: 1056out:
966 return err; 1057 return err;
1058
1059out_free_inet:
1060 sock_diag_unregister(&inet_diag_handler);
1061out_free_nl:
1062 netlink_kernel_release(sdiagnl);
967out_free_table: 1063out_free_table:
968 kfree(inet_diag_table); 1064 kfree(inet_diag_table);
969 goto out; 1065 goto out;
@@ -971,6 +1067,8 @@ out_free_table:
971 1067
972static void __exit inet_diag_exit(void) 1068static void __exit inet_diag_exit(void)
973{ 1069{
1070 sock_diag_unregister(&inet6_diag_handler);
1071 sock_diag_unregister(&inet_diag_handler);
974 netlink_kernel_release(sdiagnl); 1072 netlink_kernel_release(sdiagnl);
975 kfree(inet_diag_table); 1073 kfree(inet_diag_table);
976} 1074}