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

udp_diag.c (7304B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * udp_diag.c	Module for monitoring UDP transport protocols sockets.
      4 *
      5 * Authors:	Pavel Emelyanov, <xemul@parallels.com>
      6 */
      7
      8
      9#include <linux/module.h>
     10#include <linux/inet_diag.h>
     11#include <linux/udp.h>
     12#include <net/udp.h>
     13#include <net/udplite.h>
     14#include <linux/sock_diag.h>
     15
     16static int sk_diag_dump(struct sock *sk, struct sk_buff *skb,
     17			struct netlink_callback *cb,
     18			const struct inet_diag_req_v2 *req,
     19			struct nlattr *bc, bool net_admin)
     20{
     21	if (!inet_diag_bc_sk(bc, sk))
     22		return 0;
     23
     24	return inet_sk_diag_fill(sk, NULL, skb, cb, req, NLM_F_MULTI,
     25				 net_admin);
     26}
     27
     28static int udp_dump_one(struct udp_table *tbl,
     29			struct netlink_callback *cb,
     30			const struct inet_diag_req_v2 *req)
     31{
     32	struct sk_buff *in_skb = cb->skb;
     33	int err;
     34	struct sock *sk = NULL;
     35	struct sk_buff *rep;
     36	struct net *net = sock_net(in_skb->sk);
     37
     38	rcu_read_lock();
     39	if (req->sdiag_family == AF_INET)
     40		/* src and dst are swapped for historical reasons */
     41		sk = __udp4_lib_lookup(net,
     42				req->id.idiag_src[0], req->id.idiag_sport,
     43				req->id.idiag_dst[0], req->id.idiag_dport,
     44				req->id.idiag_if, 0, tbl, NULL);
     45#if IS_ENABLED(CONFIG_IPV6)
     46	else if (req->sdiag_family == AF_INET6)
     47		sk = __udp6_lib_lookup(net,
     48				(struct in6_addr *)req->id.idiag_src,
     49				req->id.idiag_sport,
     50				(struct in6_addr *)req->id.idiag_dst,
     51				req->id.idiag_dport,
     52				req->id.idiag_if, 0, tbl, NULL);
     53#endif
     54	if (sk && !refcount_inc_not_zero(&sk->sk_refcnt))
     55		sk = NULL;
     56	rcu_read_unlock();
     57	err = -ENOENT;
     58	if (!sk)
     59		goto out_nosk;
     60
     61	err = sock_diag_check_cookie(sk, req->id.idiag_cookie);
     62	if (err)
     63		goto out;
     64
     65	err = -ENOMEM;
     66	rep = nlmsg_new(nla_total_size(sizeof(struct inet_diag_msg)) +
     67			inet_diag_msg_attrs_size() +
     68			nla_total_size(sizeof(struct inet_diag_meminfo)) + 64,
     69			GFP_KERNEL);
     70	if (!rep)
     71		goto out;
     72
     73	err = inet_sk_diag_fill(sk, NULL, rep, cb, req, 0,
     74				netlink_net_capable(in_skb, CAP_NET_ADMIN));
     75	if (err < 0) {
     76		WARN_ON(err == -EMSGSIZE);
     77		kfree_skb(rep);
     78		goto out;
     79	}
     80	err = nlmsg_unicast(net->diag_nlsk, rep, NETLINK_CB(in_skb).portid);
     81
     82out:
     83	if (sk)
     84		sock_put(sk);
     85out_nosk:
     86	return err;
     87}
     88
     89static void udp_dump(struct udp_table *table, struct sk_buff *skb,
     90		     struct netlink_callback *cb,
     91		     const struct inet_diag_req_v2 *r)
     92{
     93	bool net_admin = netlink_net_capable(cb->skb, CAP_NET_ADMIN);
     94	struct net *net = sock_net(skb->sk);
     95	struct inet_diag_dump_data *cb_data;
     96	int num, s_num, slot, s_slot;
     97	struct nlattr *bc;
     98
     99	cb_data = cb->data;
    100	bc = cb_data->inet_diag_nla_bc;
    101	s_slot = cb->args[0];
    102	num = s_num = cb->args[1];
    103
    104	for (slot = s_slot; slot <= table->mask; s_num = 0, slot++) {
    105		struct udp_hslot *hslot = &table->hash[slot];
    106		struct sock *sk;
    107
    108		num = 0;
    109
    110		if (hlist_empty(&hslot->head))
    111			continue;
    112
    113		spin_lock_bh(&hslot->lock);
    114		sk_for_each(sk, &hslot->head) {
    115			struct inet_sock *inet = inet_sk(sk);
    116
    117			if (!net_eq(sock_net(sk), net))
    118				continue;
    119			if (num < s_num)
    120				goto next;
    121			if (!(r->idiag_states & (1 << sk->sk_state)))
    122				goto next;
    123			if (r->sdiag_family != AF_UNSPEC &&
    124					sk->sk_family != r->sdiag_family)
    125				goto next;
    126			if (r->id.idiag_sport != inet->inet_sport &&
    127			    r->id.idiag_sport)
    128				goto next;
    129			if (r->id.idiag_dport != inet->inet_dport &&
    130			    r->id.idiag_dport)
    131				goto next;
    132
    133			if (sk_diag_dump(sk, skb, cb, r, bc, net_admin) < 0) {
    134				spin_unlock_bh(&hslot->lock);
    135				goto done;
    136			}
    137next:
    138			num++;
    139		}
    140		spin_unlock_bh(&hslot->lock);
    141	}
    142done:
    143	cb->args[0] = slot;
    144	cb->args[1] = num;
    145}
    146
    147static void udp_diag_dump(struct sk_buff *skb, struct netlink_callback *cb,
    148			  const struct inet_diag_req_v2 *r)
    149{
    150	udp_dump(&udp_table, skb, cb, r);
    151}
    152
    153static int udp_diag_dump_one(struct netlink_callback *cb,
    154			     const struct inet_diag_req_v2 *req)
    155{
    156	return udp_dump_one(&udp_table, cb, req);
    157}
    158
    159static void udp_diag_get_info(struct sock *sk, struct inet_diag_msg *r,
    160		void *info)
    161{
    162	r->idiag_rqueue = udp_rqueue_get(sk);
    163	r->idiag_wqueue = sk_wmem_alloc_get(sk);
    164}
    165
    166#ifdef CONFIG_INET_DIAG_DESTROY
    167static int __udp_diag_destroy(struct sk_buff *in_skb,
    168			      const struct inet_diag_req_v2 *req,
    169			      struct udp_table *tbl)
    170{
    171	struct net *net = sock_net(in_skb->sk);
    172	struct sock *sk;
    173	int err;
    174
    175	rcu_read_lock();
    176
    177	if (req->sdiag_family == AF_INET)
    178		sk = __udp4_lib_lookup(net,
    179				req->id.idiag_dst[0], req->id.idiag_dport,
    180				req->id.idiag_src[0], req->id.idiag_sport,
    181				req->id.idiag_if, 0, tbl, NULL);
    182#if IS_ENABLED(CONFIG_IPV6)
    183	else if (req->sdiag_family == AF_INET6) {
    184		if (ipv6_addr_v4mapped((struct in6_addr *)req->id.idiag_dst) &&
    185		    ipv6_addr_v4mapped((struct in6_addr *)req->id.idiag_src))
    186			sk = __udp4_lib_lookup(net,
    187					req->id.idiag_dst[3], req->id.idiag_dport,
    188					req->id.idiag_src[3], req->id.idiag_sport,
    189					req->id.idiag_if, 0, tbl, NULL);
    190
    191		else
    192			sk = __udp6_lib_lookup(net,
    193					(struct in6_addr *)req->id.idiag_dst,
    194					req->id.idiag_dport,
    195					(struct in6_addr *)req->id.idiag_src,
    196					req->id.idiag_sport,
    197					req->id.idiag_if, 0, tbl, NULL);
    198	}
    199#endif
    200	else {
    201		rcu_read_unlock();
    202		return -EINVAL;
    203	}
    204
    205	if (sk && !refcount_inc_not_zero(&sk->sk_refcnt))
    206		sk = NULL;
    207
    208	rcu_read_unlock();
    209
    210	if (!sk)
    211		return -ENOENT;
    212
    213	if (sock_diag_check_cookie(sk, req->id.idiag_cookie)) {
    214		sock_put(sk);
    215		return -ENOENT;
    216	}
    217
    218	err = sock_diag_destroy(sk, ECONNABORTED);
    219
    220	sock_put(sk);
    221
    222	return err;
    223}
    224
    225static int udp_diag_destroy(struct sk_buff *in_skb,
    226			    const struct inet_diag_req_v2 *req)
    227{
    228	return __udp_diag_destroy(in_skb, req, &udp_table);
    229}
    230
    231static int udplite_diag_destroy(struct sk_buff *in_skb,
    232				const struct inet_diag_req_v2 *req)
    233{
    234	return __udp_diag_destroy(in_skb, req, &udplite_table);
    235}
    236
    237#endif
    238
    239static const struct inet_diag_handler udp_diag_handler = {
    240	.dump		 = udp_diag_dump,
    241	.dump_one	 = udp_diag_dump_one,
    242	.idiag_get_info  = udp_diag_get_info,
    243	.idiag_type	 = IPPROTO_UDP,
    244	.idiag_info_size = 0,
    245#ifdef CONFIG_INET_DIAG_DESTROY
    246	.destroy	 = udp_diag_destroy,
    247#endif
    248};
    249
    250static void udplite_diag_dump(struct sk_buff *skb, struct netlink_callback *cb,
    251			      const struct inet_diag_req_v2 *r)
    252{
    253	udp_dump(&udplite_table, skb, cb, r);
    254}
    255
    256static int udplite_diag_dump_one(struct netlink_callback *cb,
    257				 const struct inet_diag_req_v2 *req)
    258{
    259	return udp_dump_one(&udplite_table, cb, req);
    260}
    261
    262static const struct inet_diag_handler udplite_diag_handler = {
    263	.dump		 = udplite_diag_dump,
    264	.dump_one	 = udplite_diag_dump_one,
    265	.idiag_get_info  = udp_diag_get_info,
    266	.idiag_type	 = IPPROTO_UDPLITE,
    267	.idiag_info_size = 0,
    268#ifdef CONFIG_INET_DIAG_DESTROY
    269	.destroy	 = udplite_diag_destroy,
    270#endif
    271};
    272
    273static int __init udp_diag_init(void)
    274{
    275	int err;
    276
    277	err = inet_diag_register(&udp_diag_handler);
    278	if (err)
    279		goto out;
    280	err = inet_diag_register(&udplite_diag_handler);
    281	if (err)
    282		goto out_lite;
    283out:
    284	return err;
    285out_lite:
    286	inet_diag_unregister(&udp_diag_handler);
    287	goto out;
    288}
    289
    290static void __exit udp_diag_exit(void)
    291{
    292	inet_diag_unregister(&udplite_diag_handler);
    293	inet_diag_unregister(&udp_diag_handler);
    294}
    295
    296module_init(udp_diag_init);
    297module_exit(udp_diag_exit);
    298MODULE_LICENSE("GPL");
    299MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 2-17 /* AF_INET - IPPROTO_UDP */);
    300MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 2-136 /* AF_INET - IPPROTO_UDPLITE */);