152b7c59bSPavel Emelyanov /* 252b7c59bSPavel Emelyanov * udp_diag.c Module for monitoring UDP transport protocols sockets. 352b7c59bSPavel Emelyanov * 452b7c59bSPavel Emelyanov * Authors: Pavel Emelyanov, <xemul@parallels.com> 552b7c59bSPavel Emelyanov * 652b7c59bSPavel Emelyanov * This program is free software; you can redistribute it and/or 752b7c59bSPavel Emelyanov * modify it under the terms of the GNU General Public License 852b7c59bSPavel Emelyanov * as published by the Free Software Foundation; either version 952b7c59bSPavel Emelyanov * 2 of the License, or (at your option) any later version. 1052b7c59bSPavel Emelyanov */ 1152b7c59bSPavel Emelyanov 1252b7c59bSPavel Emelyanov 1352b7c59bSPavel Emelyanov #include <linux/module.h> 1452b7c59bSPavel Emelyanov #include <linux/inet_diag.h> 1552b7c59bSPavel Emelyanov #include <linux/udp.h> 1652b7c59bSPavel Emelyanov #include <net/udp.h> 1752b7c59bSPavel Emelyanov #include <net/udplite.h> 1852b7c59bSPavel Emelyanov #include <linux/sock_diag.h> 1952b7c59bSPavel Emelyanov 20b6d640c2SPavel Emelyanov static int sk_diag_dump(struct sock *sk, struct sk_buff *skb, 21c8991362SPavel Emelyanov struct netlink_callback *cb, struct inet_diag_req_v2 *req, 22b6d640c2SPavel Emelyanov struct nlattr *bc) 23b6d640c2SPavel Emelyanov { 24b6d640c2SPavel Emelyanov if (!inet_diag_bc_sk(bc, sk)) 25b6d640c2SPavel Emelyanov return 0; 26b6d640c2SPavel Emelyanov 27d06ca956SEric W. Biederman return inet_sk_diag_fill(sk, NULL, skb, req, 28d06ca956SEric W. Biederman sk_user_ns(NETLINK_CB(cb->skb).ssk), 2915e47304SEric W. Biederman NETLINK_CB(cb->skb).portid, 30b6d640c2SPavel Emelyanov cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh); 31b6d640c2SPavel Emelyanov } 32b6d640c2SPavel Emelyanov 3352b7c59bSPavel Emelyanov static int udp_dump_one(struct udp_table *tbl, struct sk_buff *in_skb, 34c8991362SPavel Emelyanov const struct nlmsghdr *nlh, struct inet_diag_req_v2 *req) 3552b7c59bSPavel Emelyanov { 36a925aa00SPavel Emelyanov int err = -EINVAL; 37a925aa00SPavel Emelyanov struct sock *sk; 38a925aa00SPavel Emelyanov struct sk_buff *rep; 3951d7cccfSAndrey Vagin struct net *net = sock_net(in_skb->sk); 40a925aa00SPavel Emelyanov 41a925aa00SPavel Emelyanov if (req->sdiag_family == AF_INET) 4251d7cccfSAndrey Vagin sk = __udp4_lib_lookup(net, 43a925aa00SPavel Emelyanov req->id.idiag_src[0], req->id.idiag_sport, 44a925aa00SPavel Emelyanov req->id.idiag_dst[0], req->id.idiag_dport, 45a925aa00SPavel Emelyanov req->id.idiag_if, tbl); 4686e62ad6SPavel Emelyanov #if IS_ENABLED(CONFIG_IPV6) 47a925aa00SPavel Emelyanov else if (req->sdiag_family == AF_INET6) 4851d7cccfSAndrey Vagin sk = __udp6_lib_lookup(net, 49a925aa00SPavel Emelyanov (struct in6_addr *)req->id.idiag_src, 50a925aa00SPavel Emelyanov req->id.idiag_sport, 51a925aa00SPavel Emelyanov (struct in6_addr *)req->id.idiag_dst, 52a925aa00SPavel Emelyanov req->id.idiag_dport, 53a925aa00SPavel Emelyanov req->id.idiag_if, tbl); 5486e62ad6SPavel Emelyanov #endif 55a925aa00SPavel Emelyanov else 56a925aa00SPavel Emelyanov goto out_nosk; 57a925aa00SPavel Emelyanov 58a925aa00SPavel Emelyanov err = -ENOENT; 59a925aa00SPavel Emelyanov if (sk == NULL) 60a925aa00SPavel Emelyanov goto out_nosk; 61a925aa00SPavel Emelyanov 62f65c1b53SPavel Emelyanov err = sock_diag_check_cookie(sk, req->id.idiag_cookie); 63a925aa00SPavel Emelyanov if (err) 64a925aa00SPavel Emelyanov goto out; 65a925aa00SPavel Emelyanov 66a925aa00SPavel Emelyanov err = -ENOMEM; 67a925aa00SPavel Emelyanov rep = alloc_skb(NLMSG_SPACE((sizeof(struct inet_diag_msg) + 68a925aa00SPavel Emelyanov sizeof(struct inet_diag_meminfo) + 69a925aa00SPavel Emelyanov 64)), GFP_KERNEL); 70a925aa00SPavel Emelyanov if (!rep) 71a925aa00SPavel Emelyanov goto out; 72a925aa00SPavel Emelyanov 73a925aa00SPavel Emelyanov err = inet_sk_diag_fill(sk, NULL, rep, req, 74d06ca956SEric W. Biederman sk_user_ns(NETLINK_CB(in_skb).ssk), 7515e47304SEric W. Biederman NETLINK_CB(in_skb).portid, 76a925aa00SPavel Emelyanov nlh->nlmsg_seq, 0, nlh); 77a925aa00SPavel Emelyanov if (err < 0) { 78a925aa00SPavel Emelyanov WARN_ON(err == -EMSGSIZE); 79a925aa00SPavel Emelyanov kfree_skb(rep); 80a925aa00SPavel Emelyanov goto out; 81a925aa00SPavel Emelyanov } 8215e47304SEric W. Biederman err = netlink_unicast(net->diag_nlsk, rep, NETLINK_CB(in_skb).portid, 83a925aa00SPavel Emelyanov MSG_DONTWAIT); 84a925aa00SPavel Emelyanov if (err > 0) 85a925aa00SPavel Emelyanov err = 0; 86a925aa00SPavel Emelyanov out: 87a925aa00SPavel Emelyanov if (sk) 88a925aa00SPavel Emelyanov sock_put(sk); 89a925aa00SPavel Emelyanov out_nosk: 90a925aa00SPavel Emelyanov return err; 9152b7c59bSPavel Emelyanov } 9252b7c59bSPavel Emelyanov 9352b7c59bSPavel Emelyanov static void udp_dump(struct udp_table *table, struct sk_buff *skb, struct netlink_callback *cb, 94c8991362SPavel Emelyanov struct inet_diag_req_v2 *r, struct nlattr *bc) 9552b7c59bSPavel Emelyanov { 96b6d640c2SPavel Emelyanov int num, s_num, slot, s_slot; 9751d7cccfSAndrey Vagin struct net *net = sock_net(skb->sk); 98b6d640c2SPavel Emelyanov 99b6d640c2SPavel Emelyanov s_slot = cb->args[0]; 100b6d640c2SPavel Emelyanov num = s_num = cb->args[1]; 101b6d640c2SPavel Emelyanov 102b6d640c2SPavel Emelyanov for (slot = s_slot; slot <= table->mask; num = s_num = 0, slot++) { 103b6d640c2SPavel Emelyanov struct sock *sk; 104b6d640c2SPavel Emelyanov struct hlist_nulls_node *node; 105b6d640c2SPavel Emelyanov struct udp_hslot *hslot = &table->hash[slot]; 106b6d640c2SPavel Emelyanov 107b6d640c2SPavel Emelyanov if (hlist_nulls_empty(&hslot->head)) 108b6d640c2SPavel Emelyanov continue; 109b6d640c2SPavel Emelyanov 110b6d640c2SPavel Emelyanov spin_lock_bh(&hslot->lock); 111b6d640c2SPavel Emelyanov sk_nulls_for_each(sk, node, &hslot->head) { 112b6d640c2SPavel Emelyanov struct inet_sock *inet = inet_sk(sk); 113b6d640c2SPavel Emelyanov 11451d7cccfSAndrey Vagin if (!net_eq(sock_net(sk), net)) 11551d7cccfSAndrey Vagin continue; 116b6d640c2SPavel Emelyanov if (num < s_num) 117b6d640c2SPavel Emelyanov goto next; 118b6d640c2SPavel Emelyanov if (!(r->idiag_states & (1 << sk->sk_state))) 119b6d640c2SPavel Emelyanov goto next; 120b6d640c2SPavel Emelyanov if (r->sdiag_family != AF_UNSPEC && 121b6d640c2SPavel Emelyanov sk->sk_family != r->sdiag_family) 122b6d640c2SPavel Emelyanov goto next; 123b6d640c2SPavel Emelyanov if (r->id.idiag_sport != inet->inet_sport && 124b6d640c2SPavel Emelyanov r->id.idiag_sport) 125b6d640c2SPavel Emelyanov goto next; 126b6d640c2SPavel Emelyanov if (r->id.idiag_dport != inet->inet_dport && 127b6d640c2SPavel Emelyanov r->id.idiag_dport) 128b6d640c2SPavel Emelyanov goto next; 129b6d640c2SPavel Emelyanov 130b6d640c2SPavel Emelyanov if (sk_diag_dump(sk, skb, cb, r, bc) < 0) { 131b6d640c2SPavel Emelyanov spin_unlock_bh(&hslot->lock); 132b6d640c2SPavel Emelyanov goto done; 133b6d640c2SPavel Emelyanov } 134b6d640c2SPavel Emelyanov next: 135b6d640c2SPavel Emelyanov num++; 136b6d640c2SPavel Emelyanov } 137b6d640c2SPavel Emelyanov spin_unlock_bh(&hslot->lock); 138b6d640c2SPavel Emelyanov } 139b6d640c2SPavel Emelyanov done: 140b6d640c2SPavel Emelyanov cb->args[0] = slot; 141b6d640c2SPavel Emelyanov cb->args[1] = num; 14252b7c59bSPavel Emelyanov } 14352b7c59bSPavel Emelyanov 14452b7c59bSPavel Emelyanov static void udp_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, 145c8991362SPavel Emelyanov struct inet_diag_req_v2 *r, struct nlattr *bc) 14652b7c59bSPavel Emelyanov { 14752b7c59bSPavel Emelyanov udp_dump(&udp_table, skb, cb, r, bc); 14852b7c59bSPavel Emelyanov } 14952b7c59bSPavel Emelyanov 15052b7c59bSPavel Emelyanov static int udp_diag_dump_one(struct sk_buff *in_skb, const struct nlmsghdr *nlh, 151c8991362SPavel Emelyanov struct inet_diag_req_v2 *req) 15252b7c59bSPavel Emelyanov { 15352b7c59bSPavel Emelyanov return udp_dump_one(&udp_table, in_skb, nlh, req); 15452b7c59bSPavel Emelyanov } 15552b7c59bSPavel Emelyanov 15662ad6fcdSShan Wei static void udp_diag_get_info(struct sock *sk, struct inet_diag_msg *r, 15762ad6fcdSShan Wei void *info) 15862ad6fcdSShan Wei { 15962ad6fcdSShan Wei r->idiag_rqueue = sk_rmem_alloc_get(sk); 16062ad6fcdSShan Wei r->idiag_wqueue = sk_wmem_alloc_get(sk); 16162ad6fcdSShan Wei } 16262ad6fcdSShan Wei 16352b7c59bSPavel Emelyanov static const struct inet_diag_handler udp_diag_handler = { 16452b7c59bSPavel Emelyanov .dump = udp_diag_dump, 16552b7c59bSPavel Emelyanov .dump_one = udp_diag_dump_one, 16662ad6fcdSShan Wei .idiag_get_info = udp_diag_get_info, 16752b7c59bSPavel Emelyanov .idiag_type = IPPROTO_UDP, 16852b7c59bSPavel Emelyanov }; 16952b7c59bSPavel Emelyanov 17052b7c59bSPavel Emelyanov static void udplite_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, 171c8991362SPavel Emelyanov struct inet_diag_req_v2 *r, struct nlattr *bc) 17252b7c59bSPavel Emelyanov { 17352b7c59bSPavel Emelyanov udp_dump(&udplite_table, skb, cb, r, bc); 17452b7c59bSPavel Emelyanov } 17552b7c59bSPavel Emelyanov 17652b7c59bSPavel Emelyanov static int udplite_diag_dump_one(struct sk_buff *in_skb, const struct nlmsghdr *nlh, 177c8991362SPavel Emelyanov struct inet_diag_req_v2 *req) 17852b7c59bSPavel Emelyanov { 17952b7c59bSPavel Emelyanov return udp_dump_one(&udplite_table, in_skb, nlh, req); 18052b7c59bSPavel Emelyanov } 18152b7c59bSPavel Emelyanov 18252b7c59bSPavel Emelyanov static const struct inet_diag_handler udplite_diag_handler = { 18352b7c59bSPavel Emelyanov .dump = udplite_diag_dump, 18452b7c59bSPavel Emelyanov .dump_one = udplite_diag_dump_one, 18562ad6fcdSShan Wei .idiag_get_info = udp_diag_get_info, 18652b7c59bSPavel Emelyanov .idiag_type = IPPROTO_UDPLITE, 18752b7c59bSPavel Emelyanov }; 18852b7c59bSPavel Emelyanov 18952b7c59bSPavel Emelyanov static int __init udp_diag_init(void) 19052b7c59bSPavel Emelyanov { 19152b7c59bSPavel Emelyanov int err; 19252b7c59bSPavel Emelyanov 19352b7c59bSPavel Emelyanov err = inet_diag_register(&udp_diag_handler); 19452b7c59bSPavel Emelyanov if (err) 19552b7c59bSPavel Emelyanov goto out; 19652b7c59bSPavel Emelyanov err = inet_diag_register(&udplite_diag_handler); 19752b7c59bSPavel Emelyanov if (err) 19852b7c59bSPavel Emelyanov goto out_lite; 19952b7c59bSPavel Emelyanov out: 20052b7c59bSPavel Emelyanov return err; 20152b7c59bSPavel Emelyanov out_lite: 20252b7c59bSPavel Emelyanov inet_diag_unregister(&udp_diag_handler); 20352b7c59bSPavel Emelyanov goto out; 20452b7c59bSPavel Emelyanov } 20552b7c59bSPavel Emelyanov 20652b7c59bSPavel Emelyanov static void __exit udp_diag_exit(void) 20752b7c59bSPavel Emelyanov { 20852b7c59bSPavel Emelyanov inet_diag_unregister(&udplite_diag_handler); 20952b7c59bSPavel Emelyanov inet_diag_unregister(&udp_diag_handler); 21052b7c59bSPavel Emelyanov } 21152b7c59bSPavel Emelyanov 21252b7c59bSPavel Emelyanov module_init(udp_diag_init); 21352b7c59bSPavel Emelyanov module_exit(udp_diag_exit); 21452b7c59bSPavel Emelyanov MODULE_LICENSE("GPL"); 215aec8dc62SPavel Emelyanov MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 2-17 /* AF_INET - IPPROTO_UDP */); 216aec8dc62SPavel Emelyanov MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 2-136 /* AF_INET - IPPROTO_UDPLITE */); 217