1 // SPDX-License-Identifier: GPL-2.0-only 2 #include <linux/module.h> 3 4 #include <net/sock.h> 5 #include <linux/netlink.h> 6 #include <linux/sock_diag.h> 7 #include <linux/netlink_diag.h> 8 #include <linux/rhashtable.h> 9 10 #include "af_netlink.h" 11 12 static int sk_diag_dump_groups(struct sock *sk, struct sk_buff *nlskb) 13 { 14 struct netlink_sock *nlk = nlk_sk(sk); 15 16 if (nlk->groups == NULL) 17 return 0; 18 19 return nla_put(nlskb, NETLINK_DIAG_GROUPS, NLGRPSZ(nlk->ngroups), 20 nlk->groups); 21 } 22 23 static int sk_diag_put_flags(struct sock *sk, struct sk_buff *skb) 24 { 25 struct netlink_sock *nlk = nlk_sk(sk); 26 u32 flags = 0; 27 28 if (nlk->cb_running) 29 flags |= NDIAG_FLAG_CB_RUNNING; 30 if (nlk_test_bit(RECV_PKTINFO, sk)) 31 flags |= NDIAG_FLAG_PKTINFO; 32 if (nlk_test_bit(BROADCAST_SEND_ERROR, sk)) 33 flags |= NDIAG_FLAG_BROADCAST_ERROR; 34 if (nlk_test_bit(RECV_NO_ENOBUFS, sk)) 35 flags |= NDIAG_FLAG_NO_ENOBUFS; 36 if (nlk_test_bit(LISTEN_ALL_NSID, sk)) 37 flags |= NDIAG_FLAG_LISTEN_ALL_NSID; 38 if (nlk_test_bit(CAP_ACK, sk)) 39 flags |= NDIAG_FLAG_CAP_ACK; 40 41 return nla_put_u32(skb, NETLINK_DIAG_FLAGS, flags); 42 } 43 44 static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, 45 struct netlink_diag_req *req, 46 u32 portid, u32 seq, u32 flags, int sk_ino) 47 { 48 struct nlmsghdr *nlh; 49 struct netlink_diag_msg *rep; 50 struct netlink_sock *nlk = nlk_sk(sk); 51 52 nlh = nlmsg_put(skb, portid, seq, SOCK_DIAG_BY_FAMILY, sizeof(*rep), 53 flags); 54 if (!nlh) 55 return -EMSGSIZE; 56 57 rep = nlmsg_data(nlh); 58 rep->ndiag_family = AF_NETLINK; 59 rep->ndiag_type = sk->sk_type; 60 rep->ndiag_protocol = sk->sk_protocol; 61 rep->ndiag_state = sk->sk_state; 62 63 rep->ndiag_ino = sk_ino; 64 rep->ndiag_portid = nlk->portid; 65 rep->ndiag_dst_portid = nlk->dst_portid; 66 rep->ndiag_dst_group = nlk->dst_group; 67 sock_diag_save_cookie(sk, rep->ndiag_cookie); 68 69 if ((req->ndiag_show & NDIAG_SHOW_GROUPS) && 70 sk_diag_dump_groups(sk, skb)) 71 goto out_nlmsg_trim; 72 73 if ((req->ndiag_show & NDIAG_SHOW_MEMINFO) && 74 sock_diag_put_meminfo(sk, skb, NETLINK_DIAG_MEMINFO)) 75 goto out_nlmsg_trim; 76 77 if ((req->ndiag_show & NDIAG_SHOW_FLAGS) && 78 sk_diag_put_flags(sk, skb)) 79 goto out_nlmsg_trim; 80 81 nlmsg_end(skb, nlh); 82 return 0; 83 84 out_nlmsg_trim: 85 nlmsg_cancel(skb, nlh); 86 return -EMSGSIZE; 87 } 88 89 static int __netlink_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, 90 int protocol, int s_num) 91 { 92 struct rhashtable_iter *hti = (void *)cb->args[2]; 93 struct netlink_table *tbl = &nl_table[protocol]; 94 struct net *net = sock_net(skb->sk); 95 struct netlink_diag_req *req; 96 struct netlink_sock *nlsk; 97 unsigned long flags; 98 struct sock *sk; 99 int num = 2; 100 int ret = 0; 101 102 req = nlmsg_data(cb->nlh); 103 104 if (s_num > 1) 105 goto mc_list; 106 107 num--; 108 109 if (!hti) { 110 hti = kmalloc(sizeof(*hti), GFP_KERNEL); 111 if (!hti) 112 return -ENOMEM; 113 114 cb->args[2] = (long)hti; 115 } 116 117 if (!s_num) 118 rhashtable_walk_enter(&tbl->hash, hti); 119 120 rhashtable_walk_start(hti); 121 122 while ((nlsk = rhashtable_walk_next(hti))) { 123 if (IS_ERR(nlsk)) { 124 ret = PTR_ERR(nlsk); 125 if (ret == -EAGAIN) { 126 ret = 0; 127 continue; 128 } 129 break; 130 } 131 132 sk = (struct sock *)nlsk; 133 134 if (!net_eq(sock_net(sk), net)) 135 continue; 136 137 if (sk_diag_fill(sk, skb, req, 138 NETLINK_CB(cb->skb).portid, 139 cb->nlh->nlmsg_seq, 140 NLM_F_MULTI, 141 sock_i_ino(sk)) < 0) { 142 ret = 1; 143 break; 144 } 145 } 146 147 rhashtable_walk_stop(hti); 148 149 if (ret) 150 goto done; 151 152 rhashtable_walk_exit(hti); 153 num++; 154 155 mc_list: 156 read_lock_irqsave(&nl_table_lock, flags); 157 sk_for_each_bound(sk, &tbl->mc_list) { 158 if (sk_hashed(sk)) 159 continue; 160 if (!net_eq(sock_net(sk), net)) 161 continue; 162 if (num < s_num) { 163 num++; 164 continue; 165 } 166 167 if (sk_diag_fill(sk, skb, req, 168 NETLINK_CB(cb->skb).portid, 169 cb->nlh->nlmsg_seq, 170 NLM_F_MULTI, 171 __sock_i_ino(sk)) < 0) { 172 ret = 1; 173 break; 174 } 175 num++; 176 } 177 read_unlock_irqrestore(&nl_table_lock, flags); 178 179 done: 180 cb->args[0] = num; 181 182 return ret; 183 } 184 185 static int netlink_diag_dump(struct sk_buff *skb, struct netlink_callback *cb) 186 { 187 struct netlink_diag_req *req; 188 int s_num = cb->args[0]; 189 int err = 0; 190 191 req = nlmsg_data(cb->nlh); 192 193 if (req->sdiag_protocol == NDIAG_PROTO_ALL) { 194 int i; 195 196 for (i = cb->args[1]; i < MAX_LINKS; i++) { 197 err = __netlink_diag_dump(skb, cb, i, s_num); 198 if (err) 199 break; 200 s_num = 0; 201 } 202 cb->args[1] = i; 203 } else { 204 if (req->sdiag_protocol >= MAX_LINKS) 205 return -ENOENT; 206 207 err = __netlink_diag_dump(skb, cb, req->sdiag_protocol, s_num); 208 } 209 210 return err <= 0 ? err : skb->len; 211 } 212 213 static int netlink_diag_dump_done(struct netlink_callback *cb) 214 { 215 struct rhashtable_iter *hti = (void *)cb->args[2]; 216 217 if (cb->args[0] == 1) 218 rhashtable_walk_exit(hti); 219 220 kfree(hti); 221 222 return 0; 223 } 224 225 static int netlink_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h) 226 { 227 int hdrlen = sizeof(struct netlink_diag_req); 228 struct net *net = sock_net(skb->sk); 229 230 if (nlmsg_len(h) < hdrlen) 231 return -EINVAL; 232 233 if (h->nlmsg_flags & NLM_F_DUMP) { 234 struct netlink_dump_control c = { 235 .dump = netlink_diag_dump, 236 .done = netlink_diag_dump_done, 237 }; 238 return netlink_dump_start(net->diag_nlsk, skb, h, &c); 239 } else 240 return -EOPNOTSUPP; 241 } 242 243 static const struct sock_diag_handler netlink_diag_handler = { 244 .owner = THIS_MODULE, 245 .family = AF_NETLINK, 246 .dump = netlink_diag_handler_dump, 247 }; 248 249 static int __init netlink_diag_init(void) 250 { 251 return sock_diag_register(&netlink_diag_handler); 252 } 253 254 static void __exit netlink_diag_exit(void) 255 { 256 sock_diag_unregister(&netlink_diag_handler); 257 } 258 259 module_init(netlink_diag_init); 260 module_exit(netlink_diag_exit); 261 MODULE_DESCRIPTION("Netlink-based socket monitoring/diagnostic interface (sock_diag)"); 262 MODULE_LICENSE("GPL"); 263 MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 16 /* AF_NETLINK */); 264
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.