diff options
-rw-r--r-- | include/linux/sock_diag.h | 23 | ||||
-rw-r--r-- | net/ipv4/inet_diag.c | 102 |
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__ | ||
3 | struct sk_buff; | ||
4 | struct nlmsghdr; | ||
5 | |||
6 | struct sock_diag_req { | ||
7 | __u8 sdiag_family; | ||
8 | __u8 sdiag_protocol; | ||
9 | }; | ||
10 | |||
11 | struct sock_diag_handler { | ||
12 | __u8 family; | ||
13 | int (*dump)(struct sk_buff *skb, struct nlmsghdr *nlh); | ||
14 | }; | ||
15 | |||
16 | int sock_diag_register(struct sock_diag_handler *h); | ||
17 | void sock_diag_unregister(struct sock_diag_handler *h); | ||
18 | |||
19 | void sock_diag_register_inet_compat(int (*fn)(struct sk_buff *skb, struct nlmsghdr *nlh)); | ||
20 | void sock_diag_unregister_inet_compat(int (*fn)(struct sk_buff *skb, struct nlmsghdr *nlh)); | ||
21 | |||
22 | extern 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 | ||
37 | static const struct inet_diag_handler **inet_diag_table; | 38 | static 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 | ||
891 | static 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 | |||
905 | static struct sock_diag_handler inet_diag_handler = { | ||
906 | .family = AF_INET, | ||
907 | .dump = inet_diag_handler_dump, | ||
908 | }; | ||
909 | |||
910 | static struct sock_diag_handler inet6_diag_handler = { | ||
911 | .family = AF_INET6, | ||
912 | .dump = inet_diag_handler_dump, | ||
913 | }; | ||
914 | |||
915 | static struct sock_diag_handler *sock_diag_handlers[AF_MAX]; | ||
916 | static DEFINE_MUTEX(sock_diag_table_mutex); | ||
917 | |||
918 | int 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 | |||
935 | void 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 | |||
948 | static 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 | |||
954 | static inline void sock_diag_unlock_handler(struct sock_diag_handler *h) | ||
955 | { | ||
956 | mutex_unlock(&sock_diag_table_mutex); | ||
957 | } | ||
958 | |||
890 | static int __sock_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) | 959 | static 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 | ||
895 | static int sock_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) | 978 | static 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 | |||
965 | out: | 1056 | out: |
966 | return err; | 1057 | return err; |
1058 | |||
1059 | out_free_inet: | ||
1060 | sock_diag_unregister(&inet_diag_handler); | ||
1061 | out_free_nl: | ||
1062 | netlink_kernel_release(sdiagnl); | ||
967 | out_free_table: | 1063 | out_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 | ||
972 | static void __exit inet_diag_exit(void) | 1068 | static 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 | } |