cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

diag.c (5546B)


      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
     12static 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
     23static 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->flags & NETLINK_F_RECV_PKTINFO)
     31		flags |= NDIAG_FLAG_PKTINFO;
     32	if (nlk->flags & NETLINK_F_BROADCAST_SEND_ERROR)
     33		flags |= NDIAG_FLAG_BROADCAST_ERROR;
     34	if (nlk->flags & NETLINK_F_RECV_NO_ENOBUFS)
     35		flags |= NDIAG_FLAG_NO_ENOBUFS;
     36	if (nlk->flags & NETLINK_F_LISTEN_ALL_NSID)
     37		flags |= NDIAG_FLAG_LISTEN_ALL_NSID;
     38	if (nlk->flags & NETLINK_F_CAP_ACK)
     39		flags |= NDIAG_FLAG_CAP_ACK;
     40
     41	return nla_put_u32(skb, NETLINK_DIAG_FLAGS, flags);
     42}
     43
     44static 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
     84out_nlmsg_trim:
     85	nlmsg_cancel(skb, nlh);
     86	return -EMSGSIZE;
     87}
     88
     89static 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	struct sock *sk;
     98	int num = 2;
     99	int ret = 0;
    100
    101	req = nlmsg_data(cb->nlh);
    102
    103	if (s_num > 1)
    104		goto mc_list;
    105
    106	num--;
    107
    108	if (!hti) {
    109		hti = kmalloc(sizeof(*hti), GFP_KERNEL);
    110		if (!hti)
    111			return -ENOMEM;
    112
    113		cb->args[2] = (long)hti;
    114	}
    115
    116	if (!s_num)
    117		rhashtable_walk_enter(&tbl->hash, hti);
    118
    119	rhashtable_walk_start(hti);
    120
    121	while ((nlsk = rhashtable_walk_next(hti))) {
    122		if (IS_ERR(nlsk)) {
    123			ret = PTR_ERR(nlsk);
    124			if (ret == -EAGAIN) {
    125				ret = 0;
    126				continue;
    127			}
    128			break;
    129		}
    130
    131		sk = (struct sock *)nlsk;
    132
    133		if (!net_eq(sock_net(sk), net))
    134			continue;
    135
    136		if (sk_diag_fill(sk, skb, req,
    137				 NETLINK_CB(cb->skb).portid,
    138				 cb->nlh->nlmsg_seq,
    139				 NLM_F_MULTI,
    140				 sock_i_ino(sk)) < 0) {
    141			ret = 1;
    142			break;
    143		}
    144	}
    145
    146	rhashtable_walk_stop(hti);
    147
    148	if (ret)
    149		goto done;
    150
    151	rhashtable_walk_exit(hti);
    152	num++;
    153
    154mc_list:
    155	read_lock(&nl_table_lock);
    156	sk_for_each_bound(sk, &tbl->mc_list) {
    157		if (sk_hashed(sk))
    158			continue;
    159		if (!net_eq(sock_net(sk), net))
    160			continue;
    161		if (num < s_num) {
    162			num++;
    163			continue;
    164		}
    165
    166		if (sk_diag_fill(sk, skb, req,
    167				 NETLINK_CB(cb->skb).portid,
    168				 cb->nlh->nlmsg_seq,
    169				 NLM_F_MULTI,
    170				 sock_i_ino(sk)) < 0) {
    171			ret = 1;
    172			break;
    173		}
    174		num++;
    175	}
    176	read_unlock(&nl_table_lock);
    177
    178done:
    179	cb->args[0] = num;
    180
    181	return ret;
    182}
    183
    184static int netlink_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
    185{
    186	struct netlink_diag_req *req;
    187	int s_num = cb->args[0];
    188	int err = 0;
    189
    190	req = nlmsg_data(cb->nlh);
    191
    192	if (req->sdiag_protocol == NDIAG_PROTO_ALL) {
    193		int i;
    194
    195		for (i = cb->args[1]; i < MAX_LINKS; i++) {
    196			err = __netlink_diag_dump(skb, cb, i, s_num);
    197			if (err)
    198				break;
    199			s_num = 0;
    200		}
    201		cb->args[1] = i;
    202	} else {
    203		if (req->sdiag_protocol >= MAX_LINKS)
    204			return -ENOENT;
    205
    206		err = __netlink_diag_dump(skb, cb, req->sdiag_protocol, s_num);
    207	}
    208
    209	return err < 0 ? err : skb->len;
    210}
    211
    212static int netlink_diag_dump_done(struct netlink_callback *cb)
    213{
    214	struct rhashtable_iter *hti = (void *)cb->args[2];
    215
    216	if (cb->args[0] == 1)
    217		rhashtable_walk_exit(hti);
    218
    219	kfree(hti);
    220
    221	return 0;
    222}
    223
    224static int netlink_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h)
    225{
    226	int hdrlen = sizeof(struct netlink_diag_req);
    227	struct net *net = sock_net(skb->sk);
    228
    229	if (nlmsg_len(h) < hdrlen)
    230		return -EINVAL;
    231
    232	if (h->nlmsg_flags & NLM_F_DUMP) {
    233		struct netlink_dump_control c = {
    234			.dump = netlink_diag_dump,
    235			.done = netlink_diag_dump_done,
    236		};
    237		return netlink_dump_start(net->diag_nlsk, skb, h, &c);
    238	} else
    239		return -EOPNOTSUPP;
    240}
    241
    242static const struct sock_diag_handler netlink_diag_handler = {
    243	.family = AF_NETLINK,
    244	.dump = netlink_diag_handler_dump,
    245};
    246
    247static int __init netlink_diag_init(void)
    248{
    249	return sock_diag_register(&netlink_diag_handler);
    250}
    251
    252static void __exit netlink_diag_exit(void)
    253{
    254	sock_diag_unregister(&netlink_diag_handler);
    255}
    256
    257module_init(netlink_diag_init);
    258module_exit(netlink_diag_exit);
    259MODULE_LICENSE("GPL");
    260MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 16 /* AF_NETLINK */);