From 52b7c59bc34c1eb73c46e023c9c01231e1cb637a Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Fri, 9 Dec 2011 06:23:51 +0000 Subject: udp_diag: Basic skeleton Introduce the transport level diag handler module for UDP (and UDP-lite) sockets and register (empty for now) callbacks in the inet_diag module. Signed-off-by: Pavel Emelyanov Signed-off-by: David S. Miller --- net/ipv4/udp_diag.c | 95 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 net/ipv4/udp_diag.c (limited to 'net/ipv4/udp_diag.c') diff --git a/net/ipv4/udp_diag.c b/net/ipv4/udp_diag.c new file mode 100644 index 000000000000..75e4e4ada545 --- /dev/null +++ b/net/ipv4/udp_diag.c @@ -0,0 +1,95 @@ +/* + * udp_diag.c Module for monitoring UDP transport protocols sockets. + * + * Authors: Pavel Emelyanov, + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + + +#include +#include +#include +#include +#include +#include +#include + +static int udp_dump_one(struct udp_table *tbl, struct sk_buff *in_skb, + const struct nlmsghdr *nlh, struct inet_diag_req *req) +{ + return 0; +} + +static void udp_dump(struct udp_table *table, struct sk_buff *skb, struct netlink_callback *cb, + struct inet_diag_req *r, struct nlattr *bc) +{ +} + +static void udp_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, + struct inet_diag_req *r, struct nlattr *bc) +{ + udp_dump(&udp_table, skb, cb, r, bc); +} + +static int udp_diag_dump_one(struct sk_buff *in_skb, const struct nlmsghdr *nlh, + struct inet_diag_req *req) +{ + return udp_dump_one(&udp_table, in_skb, nlh, req); +} + +static const struct inet_diag_handler udp_diag_handler = { + .dump = udp_diag_dump, + .dump_one = udp_diag_dump_one, + .idiag_type = IPPROTO_UDP, +}; + +static void udplite_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, + struct inet_diag_req *r, struct nlattr *bc) +{ + udp_dump(&udplite_table, skb, cb, r, bc); +} + +static int udplite_diag_dump_one(struct sk_buff *in_skb, const struct nlmsghdr *nlh, + struct inet_diag_req *req) +{ + return udp_dump_one(&udplite_table, in_skb, nlh, req); +} + +static const struct inet_diag_handler udplite_diag_handler = { + .dump = udplite_diag_dump, + .dump_one = udplite_diag_dump_one, + .idiag_type = IPPROTO_UDPLITE, +}; + +static int __init udp_diag_init(void) +{ + int err; + + err = inet_diag_register(&udp_diag_handler); + if (err) + goto out; + err = inet_diag_register(&udplite_diag_handler); + if (err) + goto out_lite; +out: + return err; +out_lite: + inet_diag_unregister(&udp_diag_handler); + goto out; +} + +static void __exit udp_diag_exit(void) +{ + inet_diag_unregister(&udplite_diag_handler); + inet_diag_unregister(&udp_diag_handler); +} + +module_init(udp_diag_init); +module_exit(udp_diag_exit); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 17); +MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 136); -- cgit v1.2.3 From a925aa00a55e3b72bd38bfdd3d6f97c0d900c949 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Fri, 9 Dec 2011 06:24:06 +0000 Subject: udp_diag: Implement the get_exact dumping functionality Do the same as TCP does -- lookup a socket in the given udp_table, check cookie, fill the reply message with existing inet socket dumping helper and send one back. Signed-off-by: Pavel Emelyanov Signed-off-by: David S. Miller --- net/ipv4/udp_diag.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) (limited to 'net/ipv4/udp_diag.c') diff --git a/net/ipv4/udp_diag.c b/net/ipv4/udp_diag.c index 75e4e4ada545..caa164dcd30f 100644 --- a/net/ipv4/udp_diag.c +++ b/net/ipv4/udp_diag.c @@ -21,7 +21,57 @@ static int udp_dump_one(struct udp_table *tbl, struct sk_buff *in_skb, const struct nlmsghdr *nlh, struct inet_diag_req *req) { - return 0; + int err = -EINVAL; + struct sock *sk; + struct sk_buff *rep; + + if (req->sdiag_family == AF_INET) + sk = __udp4_lib_lookup(&init_net, + req->id.idiag_src[0], req->id.idiag_sport, + req->id.idiag_dst[0], req->id.idiag_dport, + req->id.idiag_if, tbl); + else if (req->sdiag_family == AF_INET6) + sk = __udp6_lib_lookup(&init_net, + (struct in6_addr *)req->id.idiag_src, + req->id.idiag_sport, + (struct in6_addr *)req->id.idiag_dst, + req->id.idiag_dport, + req->id.idiag_if, tbl); + else + goto out_nosk; + + err = -ENOENT; + if (sk == NULL) + goto out_nosk; + + err = inet_diag_check_cookie(sk, req); + if (err) + goto out; + + err = -ENOMEM; + rep = alloc_skb(NLMSG_SPACE((sizeof(struct inet_diag_msg) + + sizeof(struct inet_diag_meminfo) + + 64)), GFP_KERNEL); + if (!rep) + goto out; + + err = inet_sk_diag_fill(sk, NULL, rep, req, + NETLINK_CB(in_skb).pid, + nlh->nlmsg_seq, 0, nlh); + if (err < 0) { + WARN_ON(err == -EMSGSIZE); + kfree_skb(rep); + goto out; + } + err = netlink_unicast(sock_diag_nlsk, rep, NETLINK_CB(in_skb).pid, + MSG_DONTWAIT); + if (err > 0) + err = 0; +out: + if (sk) + sock_put(sk); +out_nosk: + return err; } static void udp_dump(struct udp_table *table, struct sk_buff *skb, struct netlink_callback *cb, -- cgit v1.2.3 From b6d640c2286d16b15a21a4475c896cdce6a0bbe0 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Fri, 9 Dec 2011 06:24:21 +0000 Subject: udp_diag: Implement the dump-all functionality Do the same as TCP does -- iterate the given udp_table, filter sockets with bytecode and dump sockets into reply message. The same filtering as for TCP applies, though only some of the state bits really matter. Signed-off-by: Pavel Emelyanov Signed-off-by: David S. Miller --- net/ipv4/udp_diag.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) (limited to 'net/ipv4/udp_diag.c') diff --git a/net/ipv4/udp_diag.c b/net/ipv4/udp_diag.c index caa164dcd30f..65063444a119 100644 --- a/net/ipv4/udp_diag.c +++ b/net/ipv4/udp_diag.c @@ -18,6 +18,17 @@ #include #include +static int sk_diag_dump(struct sock *sk, struct sk_buff *skb, + struct netlink_callback *cb, struct inet_diag_req *req, + struct nlattr *bc) +{ + if (!inet_diag_bc_sk(bc, sk)) + return 0; + + return inet_sk_diag_fill(sk, NULL, skb, req, NETLINK_CB(cb->skb).pid, + cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh); +} + static int udp_dump_one(struct udp_table *tbl, struct sk_buff *in_skb, const struct nlmsghdr *nlh, struct inet_diag_req *req) { @@ -77,6 +88,49 @@ out_nosk: static void udp_dump(struct udp_table *table, struct sk_buff *skb, struct netlink_callback *cb, struct inet_diag_req *r, struct nlattr *bc) { + int num, s_num, slot, s_slot; + + s_slot = cb->args[0]; + num = s_num = cb->args[1]; + + for (slot = s_slot; slot <= table->mask; num = s_num = 0, slot++) { + struct sock *sk; + struct hlist_nulls_node *node; + struct udp_hslot *hslot = &table->hash[slot]; + + if (hlist_nulls_empty(&hslot->head)) + continue; + + spin_lock_bh(&hslot->lock); + sk_nulls_for_each(sk, node, &hslot->head) { + struct inet_sock *inet = inet_sk(sk); + + if (num < s_num) + goto next; + if (!(r->idiag_states & (1 << sk->sk_state))) + goto next; + if (r->sdiag_family != AF_UNSPEC && + sk->sk_family != r->sdiag_family) + goto next; + if (r->id.idiag_sport != inet->inet_sport && + r->id.idiag_sport) + goto next; + if (r->id.idiag_dport != inet->inet_dport && + r->id.idiag_dport) + goto next; + + if (sk_diag_dump(sk, skb, cb, r, bc) < 0) { + spin_unlock_bh(&hslot->lock); + goto done; + } +next: + num++; + } + spin_unlock_bh(&hslot->lock); + } +done: + cb->args[0] = slot; + cb->args[1] = num; } static void udp_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, -- cgit v1.2.3 From 86e62ad6b2a49064a078d7f22fa81afdaecc1187 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Fri, 9 Dec 2011 23:35:07 +0000 Subject: udp_diag: Fix the !ipv6 case Wrap the udp6 lookup into the proper ifdef-s. Signed-off-by: Pavel Emelyanov Signed-off-by: David S. Miller --- net/ipv4/udp_diag.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'net/ipv4/udp_diag.c') diff --git a/net/ipv4/udp_diag.c b/net/ipv4/udp_diag.c index 65063444a119..27910c133019 100644 --- a/net/ipv4/udp_diag.c +++ b/net/ipv4/udp_diag.c @@ -41,6 +41,7 @@ static int udp_dump_one(struct udp_table *tbl, struct sk_buff *in_skb, req->id.idiag_src[0], req->id.idiag_sport, req->id.idiag_dst[0], req->id.idiag_dport, req->id.idiag_if, tbl); +#if IS_ENABLED(CONFIG_IPV6) else if (req->sdiag_family == AF_INET6) sk = __udp6_lib_lookup(&init_net, (struct in6_addr *)req->id.idiag_src, @@ -48,6 +49,7 @@ static int udp_dump_one(struct udp_table *tbl, struct sk_buff *in_skb, (struct in6_addr *)req->id.idiag_dst, req->id.idiag_dport, req->id.idiag_if, tbl); +#endif else goto out_nosk; -- cgit v1.2.3 From aec8dc62f66199aef153d86e1f90d9c1d14696e3 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Thu, 15 Dec 2011 02:43:27 +0000 Subject: sock_diag: Fix module netlink aliases I've made a mistake when fixing the sock_/inet_diag aliases :( 1. The sock_diag layer should request the family-based alias, not just the IPPROTO_IP one; 2. The inet_diag layer should request for AF_INET+protocol alias, not just the protocol one. Thus fix this. Signed-off-by: Pavel Emelyanov Signed-off-by: David S. Miller --- net/core/sock_diag.c | 4 ++-- net/dccp/diag.c | 2 +- net/ipv4/inet_diag.c | 7 ++++--- net/ipv4/tcp_diag.c | 2 +- net/ipv4/udp_diag.c | 4 ++-- 5 files changed, 10 insertions(+), 9 deletions(-) (limited to 'net/ipv4/udp_diag.c') diff --git a/net/core/sock_diag.c b/net/core/sock_diag.c index 9c27bcda59be..cee96f368108 100644 --- a/net/core/sock_diag.c +++ b/net/core/sock_diag.c @@ -64,7 +64,7 @@ static inline struct sock_diag_handler *sock_diag_lock_handler(int family) { if (sock_diag_handlers[family] == NULL) request_module("net-pf-%d-proto-%d-type-%d", PF_NETLINK, - NETLINK_SOCK_DIAG, IPPROTO_IP); + NETLINK_SOCK_DIAG, family); mutex_lock(&sock_diag_table_mutex); return sock_diag_handlers[family]; @@ -103,7 +103,7 @@ static int sock_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) case DCCPDIAG_GETSOCK: if (inet_rcv_compat == NULL) request_module("net-pf-%d-proto-%d-type-%d", PF_NETLINK, - NETLINK_SOCK_DIAG, IPPROTO_IP); + NETLINK_SOCK_DIAG, AF_INET); mutex_lock(&sock_diag_table_mutex); if (inet_rcv_compat != NULL) diff --git a/net/dccp/diag.c b/net/dccp/diag.c index e29214d193d6..8f1625753377 100644 --- a/net/dccp/diag.c +++ b/net/dccp/diag.c @@ -83,4 +83,4 @@ module_exit(dccp_diag_fini); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Arnaldo Carvalho de Melo "); MODULE_DESCRIPTION("DCCP inet_diag handler"); -MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 33); +MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 2-33 /* AF_INET - IPPROTO_DCCP */); diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index 575e28c57cc9..fa27313765f3 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c @@ -54,8 +54,8 @@ static DEFINE_MUTEX(inet_diag_table_mutex); static const struct inet_diag_handler *inet_diag_lock_handler(int proto) { if (!inet_diag_table[proto]) - request_module("net-pf-%d-proto-%d-type-%d", PF_NETLINK, - NETLINK_SOCK_DIAG, proto); + request_module("net-pf-%d-proto-%d-type-%d-%d", PF_NETLINK, + NETLINK_SOCK_DIAG, AF_INET, proto); mutex_lock(&inet_diag_table_mutex); if (!inet_diag_table[proto]) @@ -1087,4 +1087,5 @@ static void __exit inet_diag_exit(void) module_init(inet_diag_init); module_exit(inet_diag_exit); MODULE_LICENSE("GPL"); -MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 0); +MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 2 /* AF_INET */); +MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 10 /* AF_INET6 */); diff --git a/net/ipv4/tcp_diag.c b/net/ipv4/tcp_diag.c index 6334b1f71f2d..8cd357a8be79 100644 --- a/net/ipv4/tcp_diag.c +++ b/net/ipv4/tcp_diag.c @@ -66,4 +66,4 @@ static void __exit tcp_diag_exit(void) module_init(tcp_diag_init); module_exit(tcp_diag_exit); MODULE_LICENSE("GPL"); -MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 6); +MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 2-6 /* AF_INET - IPPROTO_TCP */); diff --git a/net/ipv4/udp_diag.c b/net/ipv4/udp_diag.c index 27910c133019..fe9db8675acb 100644 --- a/net/ipv4/udp_diag.c +++ b/net/ipv4/udp_diag.c @@ -197,5 +197,5 @@ static void __exit udp_diag_exit(void) module_init(udp_diag_init); module_exit(udp_diag_exit); MODULE_LICENSE("GPL"); -MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 17); -MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 136); +MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 2-17 /* AF_INET - IPPROTO_UDP */); +MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 2-136 /* AF_INET - IPPROTO_UDPLITE */); -- cgit v1.2.3 From f65c1b534b99aef1809b893387b295963821549f Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Thu, 15 Dec 2011 02:43:44 +0000 Subject: sock_diag: Generalize requests cookies managements The sk address is used as a cookie between dump/get_exact calls. It will be required for unix socket sdumping, so move it from inet_diag to sock_diag. Signed-off-by: Pavel Emelyanov Signed-off-by: David S. Miller --- include/linux/inet_diag.h | 1 - include/linux/sock_diag.h | 3 +++ net/core/sock_diag.c | 19 +++++++++++++++++++ net/ipv4/inet_diag.c | 23 ++++------------------- net/ipv4/udp_diag.c | 2 +- 5 files changed, 27 insertions(+), 21 deletions(-) (limited to 'net/ipv4/udp_diag.c') diff --git a/include/linux/inet_diag.h b/include/linux/inet_diag.h index a27e62178d43..afa5d5c74169 100644 --- a/include/linux/inet_diag.h +++ b/include/linux/inet_diag.h @@ -168,7 +168,6 @@ int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo, struct inet_diag_req *req); int inet_diag_bc_sk(const struct nlattr *_bc, struct sock *sk); -int inet_diag_check_cookie(struct sock *sk, struct inet_diag_req *req); extern int inet_diag_register(const struct inet_diag_handler *handler); extern void inet_diag_unregister(const struct inet_diag_handler *handler); diff --git a/include/linux/sock_diag.h b/include/linux/sock_diag.h index 7999778ad08d..379d5dccf8e1 100644 --- a/include/linux/sock_diag.h +++ b/include/linux/sock_diag.h @@ -22,5 +22,8 @@ void sock_diag_unregister(struct sock_diag_handler *h); void sock_diag_register_inet_compat(int (*fn)(struct sk_buff *skb, struct nlmsghdr *nlh)); void sock_diag_unregister_inet_compat(int (*fn)(struct sk_buff *skb, struct nlmsghdr *nlh)); +int sock_diag_check_cookie(void *sk, __u32 *cookie); +void sock_diag_save_cookie(void *sk, __u32 *cookie); + extern struct sock *sock_diag_nlsk; #endif diff --git a/net/core/sock_diag.c b/net/core/sock_diag.c index cee96f368108..711bdefe7753 100644 --- a/net/core/sock_diag.c +++ b/net/core/sock_diag.c @@ -12,6 +12,25 @@ static struct sock_diag_handler *sock_diag_handlers[AF_MAX]; static int (*inet_rcv_compat)(struct sk_buff *skb, struct nlmsghdr *nlh); static DEFINE_MUTEX(sock_diag_table_mutex); +int sock_diag_check_cookie(void *sk, __u32 *cookie) +{ + if ((cookie[0] != INET_DIAG_NOCOOKIE || + cookie[1] != INET_DIAG_NOCOOKIE) && + ((u32)(unsigned long)sk != cookie[0] || + (u32)((((unsigned long)sk) >> 31) >> 1) != cookie[1])) + return -ESTALE; + else + return 0; +} +EXPORT_SYMBOL_GPL(sock_diag_check_cookie); + +void sock_diag_save_cookie(void *sk, __u32 *cookie) +{ + cookie[0] = (u32)(unsigned long)sk; + cookie[1] = (u32)(((unsigned long)sk >> 31) >> 1); +} +EXPORT_SYMBOL_GPL(sock_diag_save_cookie); + void sock_diag_register_inet_compat(int (*fn)(struct sk_buff *skb, struct nlmsghdr *nlh)) { mutex_lock(&sock_diag_table_mutex); diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index fa27313765f3..fb2e47ff59f7 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c @@ -102,8 +102,7 @@ int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk, r->idiag_retrans = 0; r->id.idiag_if = sk->sk_bound_dev_if; - r->id.idiag_cookie[0] = (u32)(unsigned long)sk; - r->id.idiag_cookie[1] = (u32)(((unsigned long)sk >> 31) >> 1); + sock_diag_save_cookie(sk, r->id.idiag_cookie); r->id.idiag_sport = inet->inet_sport; r->id.idiag_dport = inet->inet_dport; @@ -221,8 +220,7 @@ static int inet_twsk_diag_fill(struct inet_timewait_sock *tw, r->idiag_family = tw->tw_family; r->idiag_retrans = 0; r->id.idiag_if = tw->tw_bound_dev_if; - r->id.idiag_cookie[0] = (u32)(unsigned long)tw; - r->id.idiag_cookie[1] = (u32)(((unsigned long)tw >> 31) >> 1); + sock_diag_save_cookie(tw, r->id.idiag_cookie); r->id.idiag_sport = tw->tw_sport; r->id.idiag_dport = tw->tw_dport; r->id.idiag_src[0] = tw->tw_rcv_saddr; @@ -261,18 +259,6 @@ static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, return inet_csk_diag_fill(sk, skb, r, pid, seq, nlmsg_flags, unlh); } -int inet_diag_check_cookie(struct sock *sk, struct inet_diag_req *req) -{ - if ((req->id.idiag_cookie[0] != INET_DIAG_NOCOOKIE || - req->id.idiag_cookie[1] != INET_DIAG_NOCOOKIE) && - ((u32)(unsigned long)sk != req->id.idiag_cookie[0] || - (u32)((((unsigned long)sk) >> 31) >> 1) != req->id.idiag_cookie[1])) - return -ESTALE; - else - return 0; -} -EXPORT_SYMBOL_GPL(inet_diag_check_cookie); - int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *in_skb, const struct nlmsghdr *nlh, struct inet_diag_req *req) { @@ -304,7 +290,7 @@ int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *in_s if (sk == NULL) goto out_nosk; - err = inet_diag_check_cookie(sk, req); + err = sock_diag_check_cookie(sk, req->id.idiag_cookie); if (err) goto out; @@ -617,8 +603,7 @@ static int inet_diag_fill_req(struct sk_buff *skb, struct sock *sk, r->idiag_retrans = req->retrans; r->id.idiag_if = sk->sk_bound_dev_if; - r->id.idiag_cookie[0] = (u32)(unsigned long)req; - r->id.idiag_cookie[1] = (u32)(((unsigned long)req >> 31) >> 1); + sock_diag_save_cookie(req, r->id.idiag_cookie); tmo = req->expires - jiffies; if (tmo < 0) diff --git a/net/ipv4/udp_diag.c b/net/ipv4/udp_diag.c index fe9db8675acb..69f8a7ca63dd 100644 --- a/net/ipv4/udp_diag.c +++ b/net/ipv4/udp_diag.c @@ -57,7 +57,7 @@ static int udp_dump_one(struct udp_table *tbl, struct sk_buff *in_skb, if (sk == NULL) goto out_nosk; - err = inet_diag_check_cookie(sk, req); + err = sock_diag_check_cookie(sk, req->id.idiag_cookie); if (err) goto out; -- cgit v1.2.3