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