162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci#include <linux/module.h>
362306a36Sopenharmony_ci#include <linux/sock_diag.h>
462306a36Sopenharmony_ci#include <linux/net.h>
562306a36Sopenharmony_ci#include <linux/netdevice.h>
662306a36Sopenharmony_ci#include <linux/packet_diag.h>
762306a36Sopenharmony_ci#include <linux/percpu.h>
862306a36Sopenharmony_ci#include <net/net_namespace.h>
962306a36Sopenharmony_ci#include <net/sock.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include "internal.h"
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_cistatic int pdiag_put_info(const struct packet_sock *po, struct sk_buff *nlskb)
1462306a36Sopenharmony_ci{
1562306a36Sopenharmony_ci	struct packet_diag_info pinfo;
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci	pinfo.pdi_index = po->ifindex;
1862306a36Sopenharmony_ci	pinfo.pdi_version = po->tp_version;
1962306a36Sopenharmony_ci	pinfo.pdi_reserve = po->tp_reserve;
2062306a36Sopenharmony_ci	pinfo.pdi_copy_thresh = po->copy_thresh;
2162306a36Sopenharmony_ci	pinfo.pdi_tstamp = READ_ONCE(po->tp_tstamp);
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci	pinfo.pdi_flags = 0;
2462306a36Sopenharmony_ci	if (packet_sock_flag(po, PACKET_SOCK_RUNNING))
2562306a36Sopenharmony_ci		pinfo.pdi_flags |= PDI_RUNNING;
2662306a36Sopenharmony_ci	if (packet_sock_flag(po, PACKET_SOCK_AUXDATA))
2762306a36Sopenharmony_ci		pinfo.pdi_flags |= PDI_AUXDATA;
2862306a36Sopenharmony_ci	if (packet_sock_flag(po, PACKET_SOCK_ORIGDEV))
2962306a36Sopenharmony_ci		pinfo.pdi_flags |= PDI_ORIGDEV;
3062306a36Sopenharmony_ci	if (READ_ONCE(po->vnet_hdr_sz))
3162306a36Sopenharmony_ci		pinfo.pdi_flags |= PDI_VNETHDR;
3262306a36Sopenharmony_ci	if (packet_sock_flag(po, PACKET_SOCK_TP_LOSS))
3362306a36Sopenharmony_ci		pinfo.pdi_flags |= PDI_LOSS;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	return nla_put(nlskb, PACKET_DIAG_INFO, sizeof(pinfo), &pinfo);
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic int pdiag_put_mclist(const struct packet_sock *po, struct sk_buff *nlskb)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	struct nlattr *mca;
4162306a36Sopenharmony_ci	struct packet_mclist *ml;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	mca = nla_nest_start_noflag(nlskb, PACKET_DIAG_MCLIST);
4462306a36Sopenharmony_ci	if (!mca)
4562306a36Sopenharmony_ci		return -EMSGSIZE;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	rtnl_lock();
4862306a36Sopenharmony_ci	for (ml = po->mclist; ml; ml = ml->next) {
4962306a36Sopenharmony_ci		struct packet_diag_mclist *dml;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci		dml = nla_reserve_nohdr(nlskb, sizeof(*dml));
5262306a36Sopenharmony_ci		if (!dml) {
5362306a36Sopenharmony_ci			rtnl_unlock();
5462306a36Sopenharmony_ci			nla_nest_cancel(nlskb, mca);
5562306a36Sopenharmony_ci			return -EMSGSIZE;
5662306a36Sopenharmony_ci		}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci		dml->pdmc_index = ml->ifindex;
5962306a36Sopenharmony_ci		dml->pdmc_type = ml->type;
6062306a36Sopenharmony_ci		dml->pdmc_alen = ml->alen;
6162306a36Sopenharmony_ci		dml->pdmc_count = ml->count;
6262306a36Sopenharmony_ci		BUILD_BUG_ON(sizeof(dml->pdmc_addr) != sizeof(ml->addr));
6362306a36Sopenharmony_ci		memcpy(dml->pdmc_addr, ml->addr, sizeof(ml->addr));
6462306a36Sopenharmony_ci	}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	rtnl_unlock();
6762306a36Sopenharmony_ci	nla_nest_end(nlskb, mca);
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	return 0;
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic int pdiag_put_ring(struct packet_ring_buffer *ring, int ver, int nl_type,
7362306a36Sopenharmony_ci		struct sk_buff *nlskb)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	struct packet_diag_ring pdr;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	if (!ring->pg_vec)
7862306a36Sopenharmony_ci		return 0;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	pdr.pdr_block_size = ring->pg_vec_pages << PAGE_SHIFT;
8162306a36Sopenharmony_ci	pdr.pdr_block_nr = ring->pg_vec_len;
8262306a36Sopenharmony_ci	pdr.pdr_frame_size = ring->frame_size;
8362306a36Sopenharmony_ci	pdr.pdr_frame_nr = ring->frame_max + 1;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	if (ver > TPACKET_V2) {
8662306a36Sopenharmony_ci		pdr.pdr_retire_tmo = ring->prb_bdqc.retire_blk_tov;
8762306a36Sopenharmony_ci		pdr.pdr_sizeof_priv = ring->prb_bdqc.blk_sizeof_priv;
8862306a36Sopenharmony_ci		pdr.pdr_features = ring->prb_bdqc.feature_req_word;
8962306a36Sopenharmony_ci	} else {
9062306a36Sopenharmony_ci		pdr.pdr_retire_tmo = 0;
9162306a36Sopenharmony_ci		pdr.pdr_sizeof_priv = 0;
9262306a36Sopenharmony_ci		pdr.pdr_features = 0;
9362306a36Sopenharmony_ci	}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	return nla_put(nlskb, nl_type, sizeof(pdr), &pdr);
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic int pdiag_put_rings_cfg(struct packet_sock *po, struct sk_buff *skb)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	int ret;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	mutex_lock(&po->pg_vec_lock);
10362306a36Sopenharmony_ci	ret = pdiag_put_ring(&po->rx_ring, po->tp_version,
10462306a36Sopenharmony_ci			PACKET_DIAG_RX_RING, skb);
10562306a36Sopenharmony_ci	if (!ret)
10662306a36Sopenharmony_ci		ret = pdiag_put_ring(&po->tx_ring, po->tp_version,
10762306a36Sopenharmony_ci				PACKET_DIAG_TX_RING, skb);
10862306a36Sopenharmony_ci	mutex_unlock(&po->pg_vec_lock);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	return ret;
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic int pdiag_put_fanout(struct packet_sock *po, struct sk_buff *nlskb)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	int ret = 0;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	mutex_lock(&fanout_mutex);
11862306a36Sopenharmony_ci	if (po->fanout) {
11962306a36Sopenharmony_ci		u32 val;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci		val = (u32)po->fanout->id | ((u32)po->fanout->type << 16);
12262306a36Sopenharmony_ci		ret = nla_put_u32(nlskb, PACKET_DIAG_FANOUT, val);
12362306a36Sopenharmony_ci	}
12462306a36Sopenharmony_ci	mutex_unlock(&fanout_mutex);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	return ret;
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic int sk_diag_fill(struct sock *sk, struct sk_buff *skb,
13062306a36Sopenharmony_ci			struct packet_diag_req *req,
13162306a36Sopenharmony_ci			bool may_report_filterinfo,
13262306a36Sopenharmony_ci			struct user_namespace *user_ns,
13362306a36Sopenharmony_ci			u32 portid, u32 seq, u32 flags, int sk_ino)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	struct nlmsghdr *nlh;
13662306a36Sopenharmony_ci	struct packet_diag_msg *rp;
13762306a36Sopenharmony_ci	struct packet_sock *po = pkt_sk(sk);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	nlh = nlmsg_put(skb, portid, seq, SOCK_DIAG_BY_FAMILY, sizeof(*rp), flags);
14062306a36Sopenharmony_ci	if (!nlh)
14162306a36Sopenharmony_ci		return -EMSGSIZE;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	rp = nlmsg_data(nlh);
14462306a36Sopenharmony_ci	rp->pdiag_family = AF_PACKET;
14562306a36Sopenharmony_ci	rp->pdiag_type = sk->sk_type;
14662306a36Sopenharmony_ci	rp->pdiag_num = ntohs(READ_ONCE(po->num));
14762306a36Sopenharmony_ci	rp->pdiag_ino = sk_ino;
14862306a36Sopenharmony_ci	sock_diag_save_cookie(sk, rp->pdiag_cookie);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	if ((req->pdiag_show & PACKET_SHOW_INFO) &&
15162306a36Sopenharmony_ci			pdiag_put_info(po, skb))
15262306a36Sopenharmony_ci		goto out_nlmsg_trim;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	if ((req->pdiag_show & PACKET_SHOW_INFO) &&
15562306a36Sopenharmony_ci	    nla_put_u32(skb, PACKET_DIAG_UID,
15662306a36Sopenharmony_ci			from_kuid_munged(user_ns, sock_i_uid(sk))))
15762306a36Sopenharmony_ci		goto out_nlmsg_trim;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	if ((req->pdiag_show & PACKET_SHOW_MCLIST) &&
16062306a36Sopenharmony_ci			pdiag_put_mclist(po, skb))
16162306a36Sopenharmony_ci		goto out_nlmsg_trim;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	if ((req->pdiag_show & PACKET_SHOW_RING_CFG) &&
16462306a36Sopenharmony_ci			pdiag_put_rings_cfg(po, skb))
16562306a36Sopenharmony_ci		goto out_nlmsg_trim;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	if ((req->pdiag_show & PACKET_SHOW_FANOUT) &&
16862306a36Sopenharmony_ci			pdiag_put_fanout(po, skb))
16962306a36Sopenharmony_ci		goto out_nlmsg_trim;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	if ((req->pdiag_show & PACKET_SHOW_MEMINFO) &&
17262306a36Sopenharmony_ci	    sock_diag_put_meminfo(sk, skb, PACKET_DIAG_MEMINFO))
17362306a36Sopenharmony_ci		goto out_nlmsg_trim;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	if ((req->pdiag_show & PACKET_SHOW_FILTER) &&
17662306a36Sopenharmony_ci	    sock_diag_put_filterinfo(may_report_filterinfo, sk, skb,
17762306a36Sopenharmony_ci				     PACKET_DIAG_FILTER))
17862306a36Sopenharmony_ci		goto out_nlmsg_trim;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	nlmsg_end(skb, nlh);
18162306a36Sopenharmony_ci	return 0;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ciout_nlmsg_trim:
18462306a36Sopenharmony_ci	nlmsg_cancel(skb, nlh);
18562306a36Sopenharmony_ci	return -EMSGSIZE;
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_cistatic int packet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	int num = 0, s_num = cb->args[0];
19162306a36Sopenharmony_ci	struct packet_diag_req *req;
19262306a36Sopenharmony_ci	struct net *net;
19362306a36Sopenharmony_ci	struct sock *sk;
19462306a36Sopenharmony_ci	bool may_report_filterinfo;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	net = sock_net(skb->sk);
19762306a36Sopenharmony_ci	req = nlmsg_data(cb->nlh);
19862306a36Sopenharmony_ci	may_report_filterinfo = netlink_net_capable(cb->skb, CAP_NET_ADMIN);
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	mutex_lock(&net->packet.sklist_lock);
20162306a36Sopenharmony_ci	sk_for_each(sk, &net->packet.sklist) {
20262306a36Sopenharmony_ci		if (!net_eq(sock_net(sk), net))
20362306a36Sopenharmony_ci			continue;
20462306a36Sopenharmony_ci		if (num < s_num)
20562306a36Sopenharmony_ci			goto next;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci		if (sk_diag_fill(sk, skb, req,
20862306a36Sopenharmony_ci				 may_report_filterinfo,
20962306a36Sopenharmony_ci				 sk_user_ns(NETLINK_CB(cb->skb).sk),
21062306a36Sopenharmony_ci				 NETLINK_CB(cb->skb).portid,
21162306a36Sopenharmony_ci				 cb->nlh->nlmsg_seq, NLM_F_MULTI,
21262306a36Sopenharmony_ci				 sock_i_ino(sk)) < 0)
21362306a36Sopenharmony_ci			goto done;
21462306a36Sopenharmony_cinext:
21562306a36Sopenharmony_ci		num++;
21662306a36Sopenharmony_ci	}
21762306a36Sopenharmony_cidone:
21862306a36Sopenharmony_ci	mutex_unlock(&net->packet.sklist_lock);
21962306a36Sopenharmony_ci	cb->args[0] = num;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	return skb->len;
22262306a36Sopenharmony_ci}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_cistatic int packet_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	int hdrlen = sizeof(struct packet_diag_req);
22762306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
22862306a36Sopenharmony_ci	struct packet_diag_req *req;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	if (nlmsg_len(h) < hdrlen)
23162306a36Sopenharmony_ci		return -EINVAL;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	req = nlmsg_data(h);
23462306a36Sopenharmony_ci	/* Make it possible to support protocol filtering later */
23562306a36Sopenharmony_ci	if (req->sdiag_protocol)
23662306a36Sopenharmony_ci		return -EINVAL;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	if (h->nlmsg_flags & NLM_F_DUMP) {
23962306a36Sopenharmony_ci		struct netlink_dump_control c = {
24062306a36Sopenharmony_ci			.dump = packet_diag_dump,
24162306a36Sopenharmony_ci		};
24262306a36Sopenharmony_ci		return netlink_dump_start(net->diag_nlsk, skb, h, &c);
24362306a36Sopenharmony_ci	} else
24462306a36Sopenharmony_ci		return -EOPNOTSUPP;
24562306a36Sopenharmony_ci}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_cistatic const struct sock_diag_handler packet_diag_handler = {
24862306a36Sopenharmony_ci	.family = AF_PACKET,
24962306a36Sopenharmony_ci	.dump = packet_diag_handler_dump,
25062306a36Sopenharmony_ci};
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cistatic int __init packet_diag_init(void)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	return sock_diag_register(&packet_diag_handler);
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_cistatic void __exit packet_diag_exit(void)
25862306a36Sopenharmony_ci{
25962306a36Sopenharmony_ci	sock_diag_unregister(&packet_diag_handler);
26062306a36Sopenharmony_ci}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_cimodule_init(packet_diag_init);
26362306a36Sopenharmony_cimodule_exit(packet_diag_exit);
26462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
26562306a36Sopenharmony_ciMODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 17 /* AF_PACKET */);
266