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/inet_diag.h> 1952b7c59bSPavel Emelyanov #include <linux/sock_diag.h> 2052b7c59bSPavel Emelyanov 21b6d640c2SPavel Emelyanov static int sk_diag_dump(struct sock *sk, struct sk_buff *skb, 22b6d640c2SPavel Emelyanov struct netlink_callback *cb, struct inet_diag_req *req, 23b6d640c2SPavel Emelyanov struct nlattr *bc) 24b6d640c2SPavel Emelyanov { 25b6d640c2SPavel Emelyanov if (!inet_diag_bc_sk(bc, sk)) 26b6d640c2SPavel Emelyanov return 0; 27b6d640c2SPavel Emelyanov 28b6d640c2SPavel Emelyanov return inet_sk_diag_fill(sk, NULL, skb, req, NETLINK_CB(cb->skb).pid, 29b6d640c2SPavel Emelyanov cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh); 30b6d640c2SPavel Emelyanov } 31b6d640c2SPavel Emelyanov 3252b7c59bSPavel Emelyanov static int udp_dump_one(struct udp_table *tbl, struct sk_buff *in_skb, 3352b7c59bSPavel Emelyanov const struct nlmsghdr *nlh, struct inet_diag_req *req) 3452b7c59bSPavel Emelyanov { 35a925aa00SPavel Emelyanov int err = -EINVAL; 36a925aa00SPavel Emelyanov struct sock *sk; 37a925aa00SPavel Emelyanov struct sk_buff *rep; 38a925aa00SPavel Emelyanov 39a925aa00SPavel Emelyanov if (req->sdiag_family == AF_INET) 40a925aa00SPavel Emelyanov sk = __udp4_lib_lookup(&init_net, 41a925aa00SPavel Emelyanov req->id.idiag_src[0], req->id.idiag_sport, 42a925aa00SPavel Emelyanov req->id.idiag_dst[0], req->id.idiag_dport, 43a925aa00SPavel Emelyanov req->id.idiag_if, tbl); 4486e62ad6SPavel Emelyanov #if IS_ENABLED(CONFIG_IPV6) 45a925aa00SPavel Emelyanov else if (req->sdiag_family == AF_INET6) 46a925aa00SPavel Emelyanov sk = __udp6_lib_lookup(&init_net, 47a925aa00SPavel Emelyanov (struct in6_addr *)req->id.idiag_src, 48a925aa00SPavel Emelyanov req->id.idiag_sport, 49a925aa00SPavel Emelyanov (struct in6_addr *)req->id.idiag_dst, 50a925aa00SPavel Emelyanov req->id.idiag_dport, 51a925aa00SPavel Emelyanov req->id.idiag_if, tbl); 5286e62ad6SPavel Emelyanov #endif 53a925aa00SPavel Emelyanov else 54a925aa00SPavel Emelyanov goto out_nosk; 55a925aa00SPavel Emelyanov 56a925aa00SPavel Emelyanov err = -ENOENT; 57a925aa00SPavel Emelyanov if (sk == NULL) 58a925aa00SPavel Emelyanov goto out_nosk; 59a925aa00SPavel Emelyanov 60f65c1b53SPavel Emelyanov err = sock_diag_check_cookie(sk, req->id.idiag_cookie); 61a925aa00SPavel Emelyanov if (err) 62a925aa00SPavel Emelyanov goto out; 63a925aa00SPavel Emelyanov 64a925aa00SPavel Emelyanov err = -ENOMEM; 65a925aa00SPavel Emelyanov rep = alloc_skb(NLMSG_SPACE((sizeof(struct inet_diag_msg) + 66a925aa00SPavel Emelyanov sizeof(struct inet_diag_meminfo) + 67a925aa00SPavel Emelyanov 64)), GFP_KERNEL); 68a925aa00SPavel Emelyanov if (!rep) 69a925aa00SPavel Emelyanov goto out; 70a925aa00SPavel Emelyanov 71a925aa00SPavel Emelyanov err = inet_sk_diag_fill(sk, NULL, rep, req, 72a925aa00SPavel Emelyanov NETLINK_CB(in_skb).pid, 73a925aa00SPavel Emelyanov nlh->nlmsg_seq, 0, nlh); 74a925aa00SPavel Emelyanov if (err < 0) { 75a925aa00SPavel Emelyanov WARN_ON(err == -EMSGSIZE); 76a925aa00SPavel Emelyanov kfree_skb(rep); 77a925aa00SPavel Emelyanov goto out; 78a925aa00SPavel Emelyanov } 79a925aa00SPavel Emelyanov err = netlink_unicast(sock_diag_nlsk, rep, NETLINK_CB(in_skb).pid, 80a925aa00SPavel Emelyanov MSG_DONTWAIT); 81a925aa00SPavel Emelyanov if (err > 0) 82a925aa00SPavel Emelyanov err = 0; 83a925aa00SPavel Emelyanov out: 84a925aa00SPavel Emelyanov if (sk) 85a925aa00SPavel Emelyanov sock_put(sk); 86a925aa00SPavel Emelyanov out_nosk: 87a925aa00SPavel Emelyanov return err; 8852b7c59bSPavel Emelyanov } 8952b7c59bSPavel Emelyanov 9052b7c59bSPavel Emelyanov static void udp_dump(struct udp_table *table, struct sk_buff *skb, struct netlink_callback *cb, 9152b7c59bSPavel Emelyanov struct inet_diag_req *r, struct nlattr *bc) 9252b7c59bSPavel Emelyanov { 93b6d640c2SPavel Emelyanov int num, s_num, slot, s_slot; 94b6d640c2SPavel Emelyanov 95b6d640c2SPavel Emelyanov s_slot = cb->args[0]; 96b6d640c2SPavel Emelyanov num = s_num = cb->args[1]; 97b6d640c2SPavel Emelyanov 98b6d640c2SPavel Emelyanov for (slot = s_slot; slot <= table->mask; num = s_num = 0, slot++) { 99b6d640c2SPavel Emelyanov struct sock *sk; 100b6d640c2SPavel Emelyanov struct hlist_nulls_node *node; 101b6d640c2SPavel Emelyanov struct udp_hslot *hslot = &table->hash[slot]; 102b6d640c2SPavel Emelyanov 103b6d640c2SPavel Emelyanov if (hlist_nulls_empty(&hslot->head)) 104b6d640c2SPavel Emelyanov continue; 105b6d640c2SPavel Emelyanov 106b6d640c2SPavel Emelyanov spin_lock_bh(&hslot->lock); 107b6d640c2SPavel Emelyanov sk_nulls_for_each(sk, node, &hslot->head) { 108b6d640c2SPavel Emelyanov struct inet_sock *inet = inet_sk(sk); 109b6d640c2SPavel Emelyanov 110b6d640c2SPavel Emelyanov if (num < s_num) 111b6d640c2SPavel Emelyanov goto next; 112b6d640c2SPavel Emelyanov if (!(r->idiag_states & (1 << sk->sk_state))) 113b6d640c2SPavel Emelyanov goto next; 114b6d640c2SPavel Emelyanov if (r->sdiag_family != AF_UNSPEC && 115b6d640c2SPavel Emelyanov sk->sk_family != r->sdiag_family) 116b6d640c2SPavel Emelyanov goto next; 117b6d640c2SPavel Emelyanov if (r->id.idiag_sport != inet->inet_sport && 118b6d640c2SPavel Emelyanov r->id.idiag_sport) 119b6d640c2SPavel Emelyanov goto next; 120b6d640c2SPavel Emelyanov if (r->id.idiag_dport != inet->inet_dport && 121b6d640c2SPavel Emelyanov r->id.idiag_dport) 122b6d640c2SPavel Emelyanov goto next; 123b6d640c2SPavel Emelyanov 124b6d640c2SPavel Emelyanov if (sk_diag_dump(sk, skb, cb, r, bc) < 0) { 125b6d640c2SPavel Emelyanov spin_unlock_bh(&hslot->lock); 126b6d640c2SPavel Emelyanov goto done; 127b6d640c2SPavel Emelyanov } 128b6d640c2SPavel Emelyanov next: 129b6d640c2SPavel Emelyanov num++; 130b6d640c2SPavel Emelyanov } 131b6d640c2SPavel Emelyanov spin_unlock_bh(&hslot->lock); 132b6d640c2SPavel Emelyanov } 133b6d640c2SPavel Emelyanov done: 134b6d640c2SPavel Emelyanov cb->args[0] = slot; 135b6d640c2SPavel Emelyanov cb->args[1] = num; 13652b7c59bSPavel Emelyanov } 13752b7c59bSPavel Emelyanov 13852b7c59bSPavel Emelyanov static void udp_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, 13952b7c59bSPavel Emelyanov struct inet_diag_req *r, struct nlattr *bc) 14052b7c59bSPavel Emelyanov { 14152b7c59bSPavel Emelyanov udp_dump(&udp_table, skb, cb, r, bc); 14252b7c59bSPavel Emelyanov } 14352b7c59bSPavel Emelyanov 14452b7c59bSPavel Emelyanov static int udp_diag_dump_one(struct sk_buff *in_skb, const struct nlmsghdr *nlh, 14552b7c59bSPavel Emelyanov struct inet_diag_req *req) 14652b7c59bSPavel Emelyanov { 14752b7c59bSPavel Emelyanov return udp_dump_one(&udp_table, in_skb, nlh, req); 14852b7c59bSPavel Emelyanov } 14952b7c59bSPavel Emelyanov 15052b7c59bSPavel Emelyanov static const struct inet_diag_handler udp_diag_handler = { 15152b7c59bSPavel Emelyanov .dump = udp_diag_dump, 15252b7c59bSPavel Emelyanov .dump_one = udp_diag_dump_one, 15352b7c59bSPavel Emelyanov .idiag_type = IPPROTO_UDP, 15452b7c59bSPavel Emelyanov }; 15552b7c59bSPavel Emelyanov 15652b7c59bSPavel Emelyanov static void udplite_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, 15752b7c59bSPavel Emelyanov struct inet_diag_req *r, struct nlattr *bc) 15852b7c59bSPavel Emelyanov { 15952b7c59bSPavel Emelyanov udp_dump(&udplite_table, skb, cb, r, bc); 16052b7c59bSPavel Emelyanov } 16152b7c59bSPavel Emelyanov 16252b7c59bSPavel Emelyanov static int udplite_diag_dump_one(struct sk_buff *in_skb, const struct nlmsghdr *nlh, 16352b7c59bSPavel Emelyanov struct inet_diag_req *req) 16452b7c59bSPavel Emelyanov { 16552b7c59bSPavel Emelyanov return udp_dump_one(&udplite_table, in_skb, nlh, req); 16652b7c59bSPavel Emelyanov } 16752b7c59bSPavel Emelyanov 16852b7c59bSPavel Emelyanov static const struct inet_diag_handler udplite_diag_handler = { 16952b7c59bSPavel Emelyanov .dump = udplite_diag_dump, 17052b7c59bSPavel Emelyanov .dump_one = udplite_diag_dump_one, 17152b7c59bSPavel Emelyanov .idiag_type = IPPROTO_UDPLITE, 17252b7c59bSPavel Emelyanov }; 17352b7c59bSPavel Emelyanov 17452b7c59bSPavel Emelyanov static int __init udp_diag_init(void) 17552b7c59bSPavel Emelyanov { 17652b7c59bSPavel Emelyanov int err; 17752b7c59bSPavel Emelyanov 17852b7c59bSPavel Emelyanov err = inet_diag_register(&udp_diag_handler); 17952b7c59bSPavel Emelyanov if (err) 18052b7c59bSPavel Emelyanov goto out; 18152b7c59bSPavel Emelyanov err = inet_diag_register(&udplite_diag_handler); 18252b7c59bSPavel Emelyanov if (err) 18352b7c59bSPavel Emelyanov goto out_lite; 18452b7c59bSPavel Emelyanov out: 18552b7c59bSPavel Emelyanov return err; 18652b7c59bSPavel Emelyanov out_lite: 18752b7c59bSPavel Emelyanov inet_diag_unregister(&udp_diag_handler); 18852b7c59bSPavel Emelyanov goto out; 18952b7c59bSPavel Emelyanov } 19052b7c59bSPavel Emelyanov 19152b7c59bSPavel Emelyanov static void __exit udp_diag_exit(void) 19252b7c59bSPavel Emelyanov { 19352b7c59bSPavel Emelyanov inet_diag_unregister(&udplite_diag_handler); 19452b7c59bSPavel Emelyanov inet_diag_unregister(&udp_diag_handler); 19552b7c59bSPavel Emelyanov } 19652b7c59bSPavel Emelyanov 19752b7c59bSPavel Emelyanov module_init(udp_diag_init); 19852b7c59bSPavel Emelyanov module_exit(udp_diag_exit); 19952b7c59bSPavel Emelyanov MODULE_LICENSE("GPL"); 200aec8dc62SPavel Emelyanov MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 2-17 /* AF_INET - IPPROTO_UDP */); 201aec8dc62SPavel Emelyanov MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 2-136 /* AF_INET - IPPROTO_UDPLITE */); 202