109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
296ec6327SPavel Emelyanov #include <linux/module.h>
396ec6327SPavel Emelyanov #include <linux/sock_diag.h>
496ec6327SPavel Emelyanov #include <linux/net.h>
5eea68e2fSPavel Emelyanov #include <linux/netdevice.h>
696ec6327SPavel Emelyanov #include <linux/packet_diag.h>
7b0138408SDaniel Borkmann #include <linux/percpu.h>
896ec6327SPavel Emelyanov #include <net/net_namespace.h>
996ec6327SPavel Emelyanov #include <net/sock.h>
1096ec6327SPavel Emelyanov
1196ec6327SPavel Emelyanov #include "internal.h"
1296ec6327SPavel Emelyanov
pdiag_put_info(const struct packet_sock * po,struct sk_buff * nlskb)138a360be0SPavel Emelyanov static int pdiag_put_info(const struct packet_sock *po, struct sk_buff *nlskb)
148a360be0SPavel Emelyanov {
158a360be0SPavel Emelyanov struct packet_diag_info pinfo;
168a360be0SPavel Emelyanov
178a360be0SPavel Emelyanov pinfo.pdi_index = po->ifindex;
188a360be0SPavel Emelyanov pinfo.pdi_version = po->tp_version;
198a360be0SPavel Emelyanov pinfo.pdi_reserve = po->tp_reserve;
208a360be0SPavel Emelyanov pinfo.pdi_copy_thresh = po->copy_thresh;
211051ce4aSEric Dumazet pinfo.pdi_tstamp = READ_ONCE(po->tp_tstamp);
228a360be0SPavel Emelyanov
238a360be0SPavel Emelyanov pinfo.pdi_flags = 0;
2461edf479SEric Dumazet if (packet_sock_flag(po, PACKET_SOCK_RUNNING))
258a360be0SPavel Emelyanov pinfo.pdi_flags |= PDI_RUNNING;
26fd53c297SEric Dumazet if (packet_sock_flag(po, PACKET_SOCK_AUXDATA))
278a360be0SPavel Emelyanov pinfo.pdi_flags |= PDI_AUXDATA;
28ee5675ecSEric Dumazet if (packet_sock_flag(po, PACKET_SOCK_ORIGDEV))
298a360be0SPavel Emelyanov pinfo.pdi_flags |= PDI_ORIGDEV;
30dfc39d40SJianfeng Tan if (READ_ONCE(po->vnet_hdr_sz))
318a360be0SPavel Emelyanov pinfo.pdi_flags |= PDI_VNETHDR;
32164bddacSEric Dumazet if (packet_sock_flag(po, PACKET_SOCK_TP_LOSS))
338a360be0SPavel Emelyanov pinfo.pdi_flags |= PDI_LOSS;
348a360be0SPavel Emelyanov
358a360be0SPavel Emelyanov return nla_put(nlskb, PACKET_DIAG_INFO, sizeof(pinfo), &pinfo);
368a360be0SPavel Emelyanov }
378a360be0SPavel Emelyanov
pdiag_put_mclist(const struct packet_sock * po,struct sk_buff * nlskb)38eea68e2fSPavel Emelyanov static int pdiag_put_mclist(const struct packet_sock *po, struct sk_buff *nlskb)
39eea68e2fSPavel Emelyanov {
40eea68e2fSPavel Emelyanov struct nlattr *mca;
41eea68e2fSPavel Emelyanov struct packet_mclist *ml;
42eea68e2fSPavel Emelyanov
43ae0be8deSMichal Kubecek mca = nla_nest_start_noflag(nlskb, PACKET_DIAG_MCLIST);
44eea68e2fSPavel Emelyanov if (!mca)
45eea68e2fSPavel Emelyanov return -EMSGSIZE;
46eea68e2fSPavel Emelyanov
47eea68e2fSPavel Emelyanov rtnl_lock();
48eea68e2fSPavel Emelyanov for (ml = po->mclist; ml; ml = ml->next) {
49eea68e2fSPavel Emelyanov struct packet_diag_mclist *dml;
50eea68e2fSPavel Emelyanov
51eea68e2fSPavel Emelyanov dml = nla_reserve_nohdr(nlskb, sizeof(*dml));
52eea68e2fSPavel Emelyanov if (!dml) {
53eea68e2fSPavel Emelyanov rtnl_unlock();
54eea68e2fSPavel Emelyanov nla_nest_cancel(nlskb, mca);
55eea68e2fSPavel Emelyanov return -EMSGSIZE;
56eea68e2fSPavel Emelyanov }
57eea68e2fSPavel Emelyanov
58eea68e2fSPavel Emelyanov dml->pdmc_index = ml->ifindex;
59eea68e2fSPavel Emelyanov dml->pdmc_type = ml->type;
60eea68e2fSPavel Emelyanov dml->pdmc_alen = ml->alen;
61eea68e2fSPavel Emelyanov dml->pdmc_count = ml->count;
62eea68e2fSPavel Emelyanov BUILD_BUG_ON(sizeof(dml->pdmc_addr) != sizeof(ml->addr));
63eea68e2fSPavel Emelyanov memcpy(dml->pdmc_addr, ml->addr, sizeof(ml->addr));
64eea68e2fSPavel Emelyanov }
65eea68e2fSPavel Emelyanov
66eea68e2fSPavel Emelyanov rtnl_unlock();
67eea68e2fSPavel Emelyanov nla_nest_end(nlskb, mca);
68eea68e2fSPavel Emelyanov
69eea68e2fSPavel Emelyanov return 0;
70eea68e2fSPavel Emelyanov }
71eea68e2fSPavel Emelyanov
pdiag_put_ring(struct packet_ring_buffer * ring,int ver,int nl_type,struct sk_buff * nlskb)7216f01365SPavel Emelyanov static int pdiag_put_ring(struct packet_ring_buffer *ring, int ver, int nl_type,
7316f01365SPavel Emelyanov struct sk_buff *nlskb)
7416f01365SPavel Emelyanov {
7516f01365SPavel Emelyanov struct packet_diag_ring pdr;
7616f01365SPavel Emelyanov
77a505e582SSowmini Varadhan if (!ring->pg_vec)
7816f01365SPavel Emelyanov return 0;
7916f01365SPavel Emelyanov
8016f01365SPavel Emelyanov pdr.pdr_block_size = ring->pg_vec_pages << PAGE_SHIFT;
8116f01365SPavel Emelyanov pdr.pdr_block_nr = ring->pg_vec_len;
8216f01365SPavel Emelyanov pdr.pdr_frame_size = ring->frame_size;
8316f01365SPavel Emelyanov pdr.pdr_frame_nr = ring->frame_max + 1;
8416f01365SPavel Emelyanov
8516f01365SPavel Emelyanov if (ver > TPACKET_V2) {
8616f01365SPavel Emelyanov pdr.pdr_retire_tmo = ring->prb_bdqc.retire_blk_tov;
8716f01365SPavel Emelyanov pdr.pdr_sizeof_priv = ring->prb_bdqc.blk_sizeof_priv;
8816f01365SPavel Emelyanov pdr.pdr_features = ring->prb_bdqc.feature_req_word;
8916f01365SPavel Emelyanov } else {
9016f01365SPavel Emelyanov pdr.pdr_retire_tmo = 0;
9116f01365SPavel Emelyanov pdr.pdr_sizeof_priv = 0;
9216f01365SPavel Emelyanov pdr.pdr_features = 0;
9316f01365SPavel Emelyanov }
9416f01365SPavel Emelyanov
9516f01365SPavel Emelyanov return nla_put(nlskb, nl_type, sizeof(pdr), &pdr);
9616f01365SPavel Emelyanov }
9716f01365SPavel Emelyanov
pdiag_put_rings_cfg(struct packet_sock * po,struct sk_buff * skb)9816f01365SPavel Emelyanov static int pdiag_put_rings_cfg(struct packet_sock *po, struct sk_buff *skb)
9916f01365SPavel Emelyanov {
10016f01365SPavel Emelyanov int ret;
10116f01365SPavel Emelyanov
10216f01365SPavel Emelyanov mutex_lock(&po->pg_vec_lock);
10316f01365SPavel Emelyanov ret = pdiag_put_ring(&po->rx_ring, po->tp_version,
10416f01365SPavel Emelyanov PACKET_DIAG_RX_RING, skb);
10516f01365SPavel Emelyanov if (!ret)
10616f01365SPavel Emelyanov ret = pdiag_put_ring(&po->tx_ring, po->tp_version,
10716f01365SPavel Emelyanov PACKET_DIAG_TX_RING, skb);
10816f01365SPavel Emelyanov mutex_unlock(&po->pg_vec_lock);
10916f01365SPavel Emelyanov
11016f01365SPavel Emelyanov return ret;
11116f01365SPavel Emelyanov }
11216f01365SPavel Emelyanov
pdiag_put_fanout(struct packet_sock * po,struct sk_buff * nlskb)113fff3321dSPavel Emelyanov static int pdiag_put_fanout(struct packet_sock *po, struct sk_buff *nlskb)
114fff3321dSPavel Emelyanov {
115fff3321dSPavel Emelyanov int ret = 0;
116fff3321dSPavel Emelyanov
117fff3321dSPavel Emelyanov mutex_lock(&fanout_mutex);
118fff3321dSPavel Emelyanov if (po->fanout) {
119fff3321dSPavel Emelyanov u32 val;
120fff3321dSPavel Emelyanov
121fff3321dSPavel Emelyanov val = (u32)po->fanout->id | ((u32)po->fanout->type << 16);
122fff3321dSPavel Emelyanov ret = nla_put_u32(nlskb, PACKET_DIAG_FANOUT, val);
123fff3321dSPavel Emelyanov }
124fff3321dSPavel Emelyanov mutex_unlock(&fanout_mutex);
125fff3321dSPavel Emelyanov
126fff3321dSPavel Emelyanov return ret;
127fff3321dSPavel Emelyanov }
128fff3321dSPavel Emelyanov
sk_diag_fill(struct sock * sk,struct sk_buff * skb,struct packet_diag_req * req,bool may_report_filterinfo,struct user_namespace * user_ns,u32 portid,u32 seq,u32 flags,int sk_ino)12962641903SNicolas Dichtel static int sk_diag_fill(struct sock *sk, struct sk_buff *skb,
13062641903SNicolas Dichtel struct packet_diag_req *req,
131a53b72c8SEric W. Biederman bool may_report_filterinfo,
13262641903SNicolas Dichtel struct user_namespace *user_ns,
13315e47304SEric W. Biederman u32 portid, u32 seq, u32 flags, int sk_ino)
13496ec6327SPavel Emelyanov {
13596ec6327SPavel Emelyanov struct nlmsghdr *nlh;
13696ec6327SPavel Emelyanov struct packet_diag_msg *rp;
13716f01365SPavel Emelyanov struct packet_sock *po = pkt_sk(sk);
13896ec6327SPavel Emelyanov
13915e47304SEric W. Biederman nlh = nlmsg_put(skb, portid, seq, SOCK_DIAG_BY_FAMILY, sizeof(*rp), flags);
14096ec6327SPavel Emelyanov if (!nlh)
14196ec6327SPavel Emelyanov return -EMSGSIZE;
14296ec6327SPavel Emelyanov
14396ec6327SPavel Emelyanov rp = nlmsg_data(nlh);
14496ec6327SPavel Emelyanov rp->pdiag_family = AF_PACKET;
14596ec6327SPavel Emelyanov rp->pdiag_type = sk->sk_type;
146822b5a1cSKuniyuki Iwashima rp->pdiag_num = ntohs(READ_ONCE(po->num));
14796ec6327SPavel Emelyanov rp->pdiag_ino = sk_ino;
14896ec6327SPavel Emelyanov sock_diag_save_cookie(sk, rp->pdiag_cookie);
14996ec6327SPavel Emelyanov
1508a360be0SPavel Emelyanov if ((req->pdiag_show & PACKET_SHOW_INFO) &&
1518a360be0SPavel Emelyanov pdiag_put_info(po, skb))
1528a360be0SPavel Emelyanov goto out_nlmsg_trim;
1538a360be0SPavel Emelyanov
15462641903SNicolas Dichtel if ((req->pdiag_show & PACKET_SHOW_INFO) &&
15562641903SNicolas Dichtel nla_put_u32(skb, PACKET_DIAG_UID,
15662641903SNicolas Dichtel from_kuid_munged(user_ns, sock_i_uid(sk))))
15762641903SNicolas Dichtel goto out_nlmsg_trim;
15862641903SNicolas Dichtel
159eea68e2fSPavel Emelyanov if ((req->pdiag_show & PACKET_SHOW_MCLIST) &&
160eea68e2fSPavel Emelyanov pdiag_put_mclist(po, skb))
161eea68e2fSPavel Emelyanov goto out_nlmsg_trim;
162eea68e2fSPavel Emelyanov
16316f01365SPavel Emelyanov if ((req->pdiag_show & PACKET_SHOW_RING_CFG) &&
16416f01365SPavel Emelyanov pdiag_put_rings_cfg(po, skb))
16516f01365SPavel Emelyanov goto out_nlmsg_trim;
16616f01365SPavel Emelyanov
167fff3321dSPavel Emelyanov if ((req->pdiag_show & PACKET_SHOW_FANOUT) &&
168fff3321dSPavel Emelyanov pdiag_put_fanout(po, skb))
169fff3321dSPavel Emelyanov goto out_nlmsg_trim;
170fff3321dSPavel Emelyanov
17176d0eeb1SNicolas Dichtel if ((req->pdiag_show & PACKET_SHOW_MEMINFO) &&
17276d0eeb1SNicolas Dichtel sock_diag_put_meminfo(sk, skb, PACKET_DIAG_MEMINFO))
17376d0eeb1SNicolas Dichtel goto out_nlmsg_trim;
17476d0eeb1SNicolas Dichtel
175e8d9612cSNicolas Dichtel if ((req->pdiag_show & PACKET_SHOW_FILTER) &&
176a53b72c8SEric W. Biederman sock_diag_put_filterinfo(may_report_filterinfo, sk, skb,
177a53b72c8SEric W. Biederman PACKET_DIAG_FILTER))
178e8d9612cSNicolas Dichtel goto out_nlmsg_trim;
179e8d9612cSNicolas Dichtel
180053c095aSJohannes Berg nlmsg_end(skb, nlh);
181053c095aSJohannes Berg return 0;
1828a360be0SPavel Emelyanov
1838a360be0SPavel Emelyanov out_nlmsg_trim:
1848a360be0SPavel Emelyanov nlmsg_cancel(skb, nlh);
1858a360be0SPavel Emelyanov return -EMSGSIZE;
18696ec6327SPavel Emelyanov }
18796ec6327SPavel Emelyanov
packet_diag_dump(struct sk_buff * skb,struct netlink_callback * cb)18896ec6327SPavel Emelyanov static int packet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
18996ec6327SPavel Emelyanov {
19096ec6327SPavel Emelyanov int num = 0, s_num = cb->args[0];
19196ec6327SPavel Emelyanov struct packet_diag_req *req;
19296ec6327SPavel Emelyanov struct net *net;
19396ec6327SPavel Emelyanov struct sock *sk;
194a53b72c8SEric W. Biederman bool may_report_filterinfo;
19596ec6327SPavel Emelyanov
19696ec6327SPavel Emelyanov net = sock_net(skb->sk);
19796ec6327SPavel Emelyanov req = nlmsg_data(cb->nlh);
19890f62cf3SEric W. Biederman may_report_filterinfo = netlink_net_capable(cb->skb, CAP_NET_ADMIN);
19996ec6327SPavel Emelyanov
2000fa7fa98SPavel Emelyanov mutex_lock(&net->packet.sklist_lock);
201b67bfe0dSSasha Levin sk_for_each(sk, &net->packet.sklist) {
20296ec6327SPavel Emelyanov if (!net_eq(sock_net(sk), net))
20396ec6327SPavel Emelyanov continue;
20496ec6327SPavel Emelyanov if (num < s_num)
20596ec6327SPavel Emelyanov goto next;
20696ec6327SPavel Emelyanov
20762641903SNicolas Dichtel if (sk_diag_fill(sk, skb, req,
208a53b72c8SEric W. Biederman may_report_filterinfo,
20962641903SNicolas Dichtel sk_user_ns(NETLINK_CB(cb->skb).sk),
21062641903SNicolas Dichtel NETLINK_CB(cb->skb).portid,
21196ec6327SPavel Emelyanov cb->nlh->nlmsg_seq, NLM_F_MULTI,
21296ec6327SPavel Emelyanov sock_i_ino(sk)) < 0)
21396ec6327SPavel Emelyanov goto done;
21496ec6327SPavel Emelyanov next:
21596ec6327SPavel Emelyanov num++;
21696ec6327SPavel Emelyanov }
21796ec6327SPavel Emelyanov done:
2180fa7fa98SPavel Emelyanov mutex_unlock(&net->packet.sklist_lock);
21996ec6327SPavel Emelyanov cb->args[0] = num;
22096ec6327SPavel Emelyanov
22196ec6327SPavel Emelyanov return skb->len;
22296ec6327SPavel Emelyanov }
22396ec6327SPavel Emelyanov
packet_diag_handler_dump(struct sk_buff * skb,struct nlmsghdr * h)22496ec6327SPavel Emelyanov static int packet_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h)
22596ec6327SPavel Emelyanov {
22696ec6327SPavel Emelyanov int hdrlen = sizeof(struct packet_diag_req);
22796ec6327SPavel Emelyanov struct net *net = sock_net(skb->sk);
22896ec6327SPavel Emelyanov struct packet_diag_req *req;
22996ec6327SPavel Emelyanov
23096ec6327SPavel Emelyanov if (nlmsg_len(h) < hdrlen)
23196ec6327SPavel Emelyanov return -EINVAL;
23296ec6327SPavel Emelyanov
23396ec6327SPavel Emelyanov req = nlmsg_data(h);
23496ec6327SPavel Emelyanov /* Make it possible to support protocol filtering later */
23596ec6327SPavel Emelyanov if (req->sdiag_protocol)
23696ec6327SPavel Emelyanov return -EINVAL;
23796ec6327SPavel Emelyanov
23896ec6327SPavel Emelyanov if (h->nlmsg_flags & NLM_F_DUMP) {
23996ec6327SPavel Emelyanov struct netlink_dump_control c = {
24096ec6327SPavel Emelyanov .dump = packet_diag_dump,
24196ec6327SPavel Emelyanov };
24296ec6327SPavel Emelyanov return netlink_dump_start(net->diag_nlsk, skb, h, &c);
24396ec6327SPavel Emelyanov } else
24496ec6327SPavel Emelyanov return -EOPNOTSUPP;
24596ec6327SPavel Emelyanov }
24696ec6327SPavel Emelyanov
24796ec6327SPavel Emelyanov static const struct sock_diag_handler packet_diag_handler = {
248*37103a9dSEric Dumazet .owner = THIS_MODULE,
24996ec6327SPavel Emelyanov .family = AF_PACKET,
25096ec6327SPavel Emelyanov .dump = packet_diag_handler_dump,
25196ec6327SPavel Emelyanov };
25296ec6327SPavel Emelyanov
packet_diag_init(void)25396ec6327SPavel Emelyanov static int __init packet_diag_init(void)
25496ec6327SPavel Emelyanov {
25596ec6327SPavel Emelyanov return sock_diag_register(&packet_diag_handler);
25696ec6327SPavel Emelyanov }
25796ec6327SPavel Emelyanov
packet_diag_exit(void)25896ec6327SPavel Emelyanov static void __exit packet_diag_exit(void)
25996ec6327SPavel Emelyanov {
26096ec6327SPavel Emelyanov sock_diag_unregister(&packet_diag_handler);
26196ec6327SPavel Emelyanov }
26296ec6327SPavel Emelyanov
26396ec6327SPavel Emelyanov module_init(packet_diag_init);
26496ec6327SPavel Emelyanov module_exit(packet_diag_exit);
26596ec6327SPavel Emelyanov MODULE_LICENSE("GPL");
26696ec6327SPavel Emelyanov MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 17 /* AF_PACKET */);
267