1 /* 2 * udp_diag.c Module for monitoring UDP transport protocols sockets. 3 * 4 * Authors: Pavel Emelyanov, <xemul@parallels.com> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License 8 * as published by the Free Software Foundation; either version 9 * 2 of the License, or (at your option) any later version. 10 */ 11 12 13 #include <linux/module.h> 14 #include <linux/inet_diag.h> 15 #include <linux/udp.h> 16 #include <net/udp.h> 17 #include <net/udplite.h> 18 #include <linux/inet_diag.h> 19 #include <linux/sock_diag.h> 20 21 static int sk_diag_dump(struct sock *sk, struct sk_buff *skb, 22 struct netlink_callback *cb, struct inet_diag_req *req, 23 struct nlattr *bc) 24 { 25 if (!inet_diag_bc_sk(bc, sk)) 26 return 0; 27 28 return inet_sk_diag_fill(sk, NULL, skb, req, NETLINK_CB(cb->skb).pid, 29 cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh); 30 } 31 32 static int udp_dump_one(struct udp_table *tbl, struct sk_buff *in_skb, 33 const struct nlmsghdr *nlh, struct inet_diag_req *req) 34 { 35 int err = -EINVAL; 36 struct sock *sk; 37 struct sk_buff *rep; 38 39 if (req->sdiag_family == AF_INET) 40 sk = __udp4_lib_lookup(&init_net, 41 req->id.idiag_src[0], req->id.idiag_sport, 42 req->id.idiag_dst[0], req->id.idiag_dport, 43 req->id.idiag_if, tbl); 44 #if IS_ENABLED(CONFIG_IPV6) 45 else if (req->sdiag_family == AF_INET6) 46 sk = __udp6_lib_lookup(&init_net, 47 (struct in6_addr *)req->id.idiag_src, 48 req->id.idiag_sport, 49 (struct in6_addr *)req->id.idiag_dst, 50 req->id.idiag_dport, 51 req->id.idiag_if, tbl); 52 #endif 53 else 54 goto out_nosk; 55 56 err = -ENOENT; 57 if (sk == NULL) 58 goto out_nosk; 59 60 err = sock_diag_check_cookie(sk, req->id.idiag_cookie); 61 if (err) 62 goto out; 63 64 err = -ENOMEM; 65 rep = alloc_skb(NLMSG_SPACE((sizeof(struct inet_diag_msg) + 66 sizeof(struct inet_diag_meminfo) + 67 64)), GFP_KERNEL); 68 if (!rep) 69 goto out; 70 71 err = inet_sk_diag_fill(sk, NULL, rep, req, 72 NETLINK_CB(in_skb).pid, 73 nlh->nlmsg_seq, 0, nlh); 74 if (err < 0) { 75 WARN_ON(err == -EMSGSIZE); 76 kfree_skb(rep); 77 goto out; 78 } 79 err = netlink_unicast(sock_diag_nlsk, rep, NETLINK_CB(in_skb).pid, 80 MSG_DONTWAIT); 81 if (err > 0) 82 err = 0; 83 out: 84 if (sk) 85 sock_put(sk); 86 out_nosk: 87 return err; 88 } 89 90 static void udp_dump(struct udp_table *table, struct sk_buff *skb, struct netlink_callback *cb, 91 struct inet_diag_req *r, struct nlattr *bc) 92 { 93 int num, s_num, slot, s_slot; 94 95 s_slot = cb->args[0]; 96 num = s_num = cb->args[1]; 97 98 for (slot = s_slot; slot <= table->mask; num = s_num = 0, slot++) { 99 struct sock *sk; 100 struct hlist_nulls_node *node; 101 struct udp_hslot *hslot = &table->hash[slot]; 102 103 if (hlist_nulls_empty(&hslot->head)) 104 continue; 105 106 spin_lock_bh(&hslot->lock); 107 sk_nulls_for_each(sk, node, &hslot->head) { 108 struct inet_sock *inet = inet_sk(sk); 109 110 if (num < s_num) 111 goto next; 112 if (!(r->idiag_states & (1 << sk->sk_state))) 113 goto next; 114 if (r->sdiag_family != AF_UNSPEC && 115 sk->sk_family != r->sdiag_family) 116 goto next; 117 if (r->id.idiag_sport != inet->inet_sport && 118 r->id.idiag_sport) 119 goto next; 120 if (r->id.idiag_dport != inet->inet_dport && 121 r->id.idiag_dport) 122 goto next; 123 124 if (sk_diag_dump(sk, skb, cb, r, bc) < 0) { 125 spin_unlock_bh(&hslot->lock); 126 goto done; 127 } 128 next: 129 num++; 130 } 131 spin_unlock_bh(&hslot->lock); 132 } 133 done: 134 cb->args[0] = slot; 135 cb->args[1] = num; 136 } 137 138 static void udp_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, 139 struct inet_diag_req *r, struct nlattr *bc) 140 { 141 udp_dump(&udp_table, skb, cb, r, bc); 142 } 143 144 static int udp_diag_dump_one(struct sk_buff *in_skb, const struct nlmsghdr *nlh, 145 struct inet_diag_req *req) 146 { 147 return udp_dump_one(&udp_table, in_skb, nlh, req); 148 } 149 150 static const struct inet_diag_handler udp_diag_handler = { 151 .dump = udp_diag_dump, 152 .dump_one = udp_diag_dump_one, 153 .idiag_type = IPPROTO_UDP, 154 }; 155 156 static void udplite_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, 157 struct inet_diag_req *r, struct nlattr *bc) 158 { 159 udp_dump(&udplite_table, skb, cb, r, bc); 160 } 161 162 static int udplite_diag_dump_one(struct sk_buff *in_skb, const struct nlmsghdr *nlh, 163 struct inet_diag_req *req) 164 { 165 return udp_dump_one(&udplite_table, in_skb, nlh, req); 166 } 167 168 static const struct inet_diag_handler udplite_diag_handler = { 169 .dump = udplite_diag_dump, 170 .dump_one = udplite_diag_dump_one, 171 .idiag_type = IPPROTO_UDPLITE, 172 }; 173 174 static int __init udp_diag_init(void) 175 { 176 int err; 177 178 err = inet_diag_register(&udp_diag_handler); 179 if (err) 180 goto out; 181 err = inet_diag_register(&udplite_diag_handler); 182 if (err) 183 goto out_lite; 184 out: 185 return err; 186 out_lite: 187 inet_diag_unregister(&udp_diag_handler); 188 goto out; 189 } 190 191 static void __exit udp_diag_exit(void) 192 { 193 inet_diag_unregister(&udplite_diag_handler); 194 inet_diag_unregister(&udp_diag_handler); 195 } 196 197 module_init(udp_diag_init); 198 module_exit(udp_diag_exit); 199 MODULE_LICENSE("GPL"); 200 MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 2-17 /* AF_INET - IPPROTO_UDP */); 201 MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 2-136 /* AF_INET - IPPROTO_UDPLITE */); 202