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

tcp_diag.c (5583B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * tcp_diag.c	Module for monitoring TCP transport protocols sockets.
      4 *
      5 * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
      6 */
      7
      8#include <linux/module.h>
      9#include <linux/net.h>
     10#include <linux/sock_diag.h>
     11#include <linux/inet_diag.h>
     12
     13#include <linux/tcp.h>
     14
     15#include <net/netlink.h>
     16#include <net/tcp.h>
     17
     18static void tcp_diag_get_info(struct sock *sk, struct inet_diag_msg *r,
     19			      void *_info)
     20{
     21	struct tcp_info *info = _info;
     22
     23	if (inet_sk_state_load(sk) == TCP_LISTEN) {
     24		r->idiag_rqueue = READ_ONCE(sk->sk_ack_backlog);
     25		r->idiag_wqueue = READ_ONCE(sk->sk_max_ack_backlog);
     26	} else if (sk->sk_type == SOCK_STREAM) {
     27		const struct tcp_sock *tp = tcp_sk(sk);
     28
     29		r->idiag_rqueue = max_t(int, READ_ONCE(tp->rcv_nxt) -
     30					     READ_ONCE(tp->copied_seq), 0);
     31		r->idiag_wqueue = READ_ONCE(tp->write_seq) - tp->snd_una;
     32	}
     33	if (info)
     34		tcp_get_info(sk, info);
     35}
     36
     37#ifdef CONFIG_TCP_MD5SIG
     38static void tcp_diag_md5sig_fill(struct tcp_diag_md5sig *info,
     39				 const struct tcp_md5sig_key *key)
     40{
     41	info->tcpm_family = key->family;
     42	info->tcpm_prefixlen = key->prefixlen;
     43	info->tcpm_keylen = key->keylen;
     44	memcpy(info->tcpm_key, key->key, key->keylen);
     45
     46	if (key->family == AF_INET)
     47		info->tcpm_addr[0] = key->addr.a4.s_addr;
     48	#if IS_ENABLED(CONFIG_IPV6)
     49	else if (key->family == AF_INET6)
     50		memcpy(&info->tcpm_addr, &key->addr.a6,
     51		       sizeof(info->tcpm_addr));
     52	#endif
     53}
     54
     55static int tcp_diag_put_md5sig(struct sk_buff *skb,
     56			       const struct tcp_md5sig_info *md5sig)
     57{
     58	const struct tcp_md5sig_key *key;
     59	struct tcp_diag_md5sig *info;
     60	struct nlattr *attr;
     61	int md5sig_count = 0;
     62
     63	hlist_for_each_entry_rcu(key, &md5sig->head, node)
     64		md5sig_count++;
     65	if (md5sig_count == 0)
     66		return 0;
     67
     68	attr = nla_reserve(skb, INET_DIAG_MD5SIG,
     69			   md5sig_count * sizeof(struct tcp_diag_md5sig));
     70	if (!attr)
     71		return -EMSGSIZE;
     72
     73	info = nla_data(attr);
     74	memset(info, 0, md5sig_count * sizeof(struct tcp_diag_md5sig));
     75	hlist_for_each_entry_rcu(key, &md5sig->head, node) {
     76		tcp_diag_md5sig_fill(info++, key);
     77		if (--md5sig_count == 0)
     78			break;
     79	}
     80
     81	return 0;
     82}
     83#endif
     84
     85static int tcp_diag_put_ulp(struct sk_buff *skb, struct sock *sk,
     86			    const struct tcp_ulp_ops *ulp_ops)
     87{
     88	struct nlattr *nest;
     89	int err;
     90
     91	nest = nla_nest_start_noflag(skb, INET_DIAG_ULP_INFO);
     92	if (!nest)
     93		return -EMSGSIZE;
     94
     95	err = nla_put_string(skb, INET_ULP_INFO_NAME, ulp_ops->name);
     96	if (err)
     97		goto nla_failure;
     98
     99	if (ulp_ops->get_info)
    100		err = ulp_ops->get_info(sk, skb);
    101	if (err)
    102		goto nla_failure;
    103
    104	nla_nest_end(skb, nest);
    105	return 0;
    106
    107nla_failure:
    108	nla_nest_cancel(skb, nest);
    109	return err;
    110}
    111
    112static int tcp_diag_get_aux(struct sock *sk, bool net_admin,
    113			    struct sk_buff *skb)
    114{
    115	struct inet_connection_sock *icsk = inet_csk(sk);
    116	int err = 0;
    117
    118#ifdef CONFIG_TCP_MD5SIG
    119	if (net_admin) {
    120		struct tcp_md5sig_info *md5sig;
    121
    122		rcu_read_lock();
    123		md5sig = rcu_dereference(tcp_sk(sk)->md5sig_info);
    124		if (md5sig)
    125			err = tcp_diag_put_md5sig(skb, md5sig);
    126		rcu_read_unlock();
    127		if (err < 0)
    128			return err;
    129	}
    130#endif
    131
    132	if (net_admin) {
    133		const struct tcp_ulp_ops *ulp_ops;
    134
    135		ulp_ops = icsk->icsk_ulp_ops;
    136		if (ulp_ops)
    137			err = tcp_diag_put_ulp(skb, sk, ulp_ops);
    138		if (err)
    139			return err;
    140	}
    141	return 0;
    142}
    143
    144static size_t tcp_diag_get_aux_size(struct sock *sk, bool net_admin)
    145{
    146	struct inet_connection_sock *icsk = inet_csk(sk);
    147	size_t size = 0;
    148
    149#ifdef CONFIG_TCP_MD5SIG
    150	if (net_admin && sk_fullsock(sk)) {
    151		const struct tcp_md5sig_info *md5sig;
    152		const struct tcp_md5sig_key *key;
    153		size_t md5sig_count = 0;
    154
    155		rcu_read_lock();
    156		md5sig = rcu_dereference(tcp_sk(sk)->md5sig_info);
    157		if (md5sig) {
    158			hlist_for_each_entry_rcu(key, &md5sig->head, node)
    159				md5sig_count++;
    160		}
    161		rcu_read_unlock();
    162		size += nla_total_size(md5sig_count *
    163				       sizeof(struct tcp_diag_md5sig));
    164	}
    165#endif
    166
    167	if (net_admin && sk_fullsock(sk)) {
    168		const struct tcp_ulp_ops *ulp_ops;
    169
    170		ulp_ops = icsk->icsk_ulp_ops;
    171		if (ulp_ops) {
    172			size += nla_total_size(0) +
    173				nla_total_size(TCP_ULP_NAME_MAX);
    174			if (ulp_ops->get_info_size)
    175				size += ulp_ops->get_info_size(sk);
    176		}
    177	}
    178	return size;
    179}
    180
    181static void tcp_diag_dump(struct sk_buff *skb, struct netlink_callback *cb,
    182			  const struct inet_diag_req_v2 *r)
    183{
    184	inet_diag_dump_icsk(&tcp_hashinfo, skb, cb, r);
    185}
    186
    187static int tcp_diag_dump_one(struct netlink_callback *cb,
    188			     const struct inet_diag_req_v2 *req)
    189{
    190	return inet_diag_dump_one_icsk(&tcp_hashinfo, cb, req);
    191}
    192
    193#ifdef CONFIG_INET_DIAG_DESTROY
    194static int tcp_diag_destroy(struct sk_buff *in_skb,
    195			    const struct inet_diag_req_v2 *req)
    196{
    197	struct net *net = sock_net(in_skb->sk);
    198	struct sock *sk = inet_diag_find_one_icsk(net, &tcp_hashinfo, req);
    199	int err;
    200
    201	if (IS_ERR(sk))
    202		return PTR_ERR(sk);
    203
    204	err = sock_diag_destroy(sk, ECONNABORTED);
    205
    206	sock_gen_put(sk);
    207
    208	return err;
    209}
    210#endif
    211
    212static const struct inet_diag_handler tcp_diag_handler = {
    213	.dump			= tcp_diag_dump,
    214	.dump_one		= tcp_diag_dump_one,
    215	.idiag_get_info		= tcp_diag_get_info,
    216	.idiag_get_aux		= tcp_diag_get_aux,
    217	.idiag_get_aux_size	= tcp_diag_get_aux_size,
    218	.idiag_type		= IPPROTO_TCP,
    219	.idiag_info_size	= sizeof(struct tcp_info),
    220#ifdef CONFIG_INET_DIAG_DESTROY
    221	.destroy		= tcp_diag_destroy,
    222#endif
    223};
    224
    225static int __init tcp_diag_init(void)
    226{
    227	return inet_diag_register(&tcp_diag_handler);
    228}
    229
    230static void __exit tcp_diag_exit(void)
    231{
    232	inet_diag_unregister(&tcp_diag_handler);
    233}
    234
    235module_init(tcp_diag_init);
    236module_exit(tcp_diag_exit);
    237MODULE_LICENSE("GPL");
    238MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 2-6 /* AF_INET - IPPROTO_TCP */);