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

inet6_hashtables.c (9368B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * INET		An implementation of the TCP/IP protocol suite for the LINUX
      4 *		operating system.  INET is implemented using the BSD Socket
      5 *		interface as the means of communication with the user level.
      6 *
      7 *		Generic INET6 transport hashtables
      8 *
      9 * Authors:	Lotsa people, from code originally in tcp, generalised here
     10 *		by Arnaldo Carvalho de Melo <acme@mandriva.com>
     11 */
     12
     13#include <linux/module.h>
     14#include <linux/random.h>
     15
     16#include <net/addrconf.h>
     17#include <net/inet_connection_sock.h>
     18#include <net/inet_hashtables.h>
     19#include <net/inet6_hashtables.h>
     20#include <net/secure_seq.h>
     21#include <net/ip.h>
     22#include <net/sock_reuseport.h>
     23
     24extern struct inet_hashinfo tcp_hashinfo;
     25
     26u32 inet6_ehashfn(const struct net *net,
     27		  const struct in6_addr *laddr, const u16 lport,
     28		  const struct in6_addr *faddr, const __be16 fport)
     29{
     30	static u32 inet6_ehash_secret __read_mostly;
     31	static u32 ipv6_hash_secret __read_mostly;
     32
     33	u32 lhash, fhash;
     34
     35	net_get_random_once(&inet6_ehash_secret, sizeof(inet6_ehash_secret));
     36	net_get_random_once(&ipv6_hash_secret, sizeof(ipv6_hash_secret));
     37
     38	lhash = (__force u32)laddr->s6_addr32[3];
     39	fhash = __ipv6_addr_jhash(faddr, ipv6_hash_secret);
     40
     41	return __inet6_ehashfn(lhash, lport, fhash, fport,
     42			       inet6_ehash_secret + net_hash_mix(net));
     43}
     44
     45/*
     46 * Sockets in TCP_CLOSE state are _always_ taken out of the hash, so
     47 * we need not check it for TCP lookups anymore, thanks Alexey. -DaveM
     48 *
     49 * The sockhash lock must be held as a reader here.
     50 */
     51struct sock *__inet6_lookup_established(struct net *net,
     52					struct inet_hashinfo *hashinfo,
     53					   const struct in6_addr *saddr,
     54					   const __be16 sport,
     55					   const struct in6_addr *daddr,
     56					   const u16 hnum,
     57					   const int dif, const int sdif)
     58{
     59	struct sock *sk;
     60	const struct hlist_nulls_node *node;
     61	const __portpair ports = INET_COMBINED_PORTS(sport, hnum);
     62	/* Optimize here for direct hit, only listening connections can
     63	 * have wildcards anyways.
     64	 */
     65	unsigned int hash = inet6_ehashfn(net, daddr, hnum, saddr, sport);
     66	unsigned int slot = hash & hashinfo->ehash_mask;
     67	struct inet_ehash_bucket *head = &hashinfo->ehash[slot];
     68
     69
     70begin:
     71	sk_nulls_for_each_rcu(sk, node, &head->chain) {
     72		if (sk->sk_hash != hash)
     73			continue;
     74		if (!inet6_match(net, sk, saddr, daddr, ports, dif, sdif))
     75			continue;
     76		if (unlikely(!refcount_inc_not_zero(&sk->sk_refcnt)))
     77			goto out;
     78
     79		if (unlikely(!inet6_match(net, sk, saddr, daddr, ports, dif, sdif))) {
     80			sock_gen_put(sk);
     81			goto begin;
     82		}
     83		goto found;
     84	}
     85	if (get_nulls_value(node) != slot)
     86		goto begin;
     87out:
     88	sk = NULL;
     89found:
     90	return sk;
     91}
     92EXPORT_SYMBOL(__inet6_lookup_established);
     93
     94static inline int compute_score(struct sock *sk, struct net *net,
     95				const unsigned short hnum,
     96				const struct in6_addr *daddr,
     97				const int dif, const int sdif)
     98{
     99	int score = -1;
    100
    101	if (net_eq(sock_net(sk), net) && inet_sk(sk)->inet_num == hnum &&
    102	    sk->sk_family == PF_INET6) {
    103		if (!ipv6_addr_equal(&sk->sk_v6_rcv_saddr, daddr))
    104			return -1;
    105
    106		if (!inet_sk_bound_dev_eq(net, sk->sk_bound_dev_if, dif, sdif))
    107			return -1;
    108
    109		score =  sk->sk_bound_dev_if ? 2 : 1;
    110		if (READ_ONCE(sk->sk_incoming_cpu) == raw_smp_processor_id())
    111			score++;
    112	}
    113	return score;
    114}
    115
    116static inline struct sock *lookup_reuseport(struct net *net, struct sock *sk,
    117					    struct sk_buff *skb, int doff,
    118					    const struct in6_addr *saddr,
    119					    __be16 sport,
    120					    const struct in6_addr *daddr,
    121					    unsigned short hnum)
    122{
    123	struct sock *reuse_sk = NULL;
    124	u32 phash;
    125
    126	if (sk->sk_reuseport) {
    127		phash = inet6_ehashfn(net, daddr, hnum, saddr, sport);
    128		reuse_sk = reuseport_select_sock(sk, phash, skb, doff);
    129	}
    130	return reuse_sk;
    131}
    132
    133/* called with rcu_read_lock() */
    134static struct sock *inet6_lhash2_lookup(struct net *net,
    135		struct inet_listen_hashbucket *ilb2,
    136		struct sk_buff *skb, int doff,
    137		const struct in6_addr *saddr,
    138		const __be16 sport, const struct in6_addr *daddr,
    139		const unsigned short hnum, const int dif, const int sdif)
    140{
    141	struct sock *sk, *result = NULL;
    142	struct hlist_nulls_node *node;
    143	int score, hiscore = 0;
    144
    145	sk_nulls_for_each_rcu(sk, node, &ilb2->nulls_head) {
    146		score = compute_score(sk, net, hnum, daddr, dif, sdif);
    147		if (score > hiscore) {
    148			result = lookup_reuseport(net, sk, skb, doff,
    149						  saddr, sport, daddr, hnum);
    150			if (result)
    151				return result;
    152
    153			result = sk;
    154			hiscore = score;
    155		}
    156	}
    157
    158	return result;
    159}
    160
    161static inline struct sock *inet6_lookup_run_bpf(struct net *net,
    162						struct inet_hashinfo *hashinfo,
    163						struct sk_buff *skb, int doff,
    164						const struct in6_addr *saddr,
    165						const __be16 sport,
    166						const struct in6_addr *daddr,
    167						const u16 hnum, const int dif)
    168{
    169	struct sock *sk, *reuse_sk;
    170	bool no_reuseport;
    171
    172	if (hashinfo != &tcp_hashinfo)
    173		return NULL; /* only TCP is supported */
    174
    175	no_reuseport = bpf_sk_lookup_run_v6(net, IPPROTO_TCP, saddr, sport,
    176					    daddr, hnum, dif, &sk);
    177	if (no_reuseport || IS_ERR_OR_NULL(sk))
    178		return sk;
    179
    180	reuse_sk = lookup_reuseport(net, sk, skb, doff, saddr, sport, daddr, hnum);
    181	if (reuse_sk)
    182		sk = reuse_sk;
    183	return sk;
    184}
    185
    186struct sock *inet6_lookup_listener(struct net *net,
    187		struct inet_hashinfo *hashinfo,
    188		struct sk_buff *skb, int doff,
    189		const struct in6_addr *saddr,
    190		const __be16 sport, const struct in6_addr *daddr,
    191		const unsigned short hnum, const int dif, const int sdif)
    192{
    193	struct inet_listen_hashbucket *ilb2;
    194	struct sock *result = NULL;
    195	unsigned int hash2;
    196
    197	/* Lookup redirect from BPF */
    198	if (static_branch_unlikely(&bpf_sk_lookup_enabled)) {
    199		result = inet6_lookup_run_bpf(net, hashinfo, skb, doff,
    200					      saddr, sport, daddr, hnum, dif);
    201		if (result)
    202			goto done;
    203	}
    204
    205	hash2 = ipv6_portaddr_hash(net, daddr, hnum);
    206	ilb2 = inet_lhash2_bucket(hashinfo, hash2);
    207
    208	result = inet6_lhash2_lookup(net, ilb2, skb, doff,
    209				     saddr, sport, daddr, hnum,
    210				     dif, sdif);
    211	if (result)
    212		goto done;
    213
    214	/* Lookup lhash2 with in6addr_any */
    215	hash2 = ipv6_portaddr_hash(net, &in6addr_any, hnum);
    216	ilb2 = inet_lhash2_bucket(hashinfo, hash2);
    217
    218	result = inet6_lhash2_lookup(net, ilb2, skb, doff,
    219				     saddr, sport, &in6addr_any, hnum,
    220				     dif, sdif);
    221done:
    222	if (IS_ERR(result))
    223		return NULL;
    224	return result;
    225}
    226EXPORT_SYMBOL_GPL(inet6_lookup_listener);
    227
    228struct sock *inet6_lookup(struct net *net, struct inet_hashinfo *hashinfo,
    229			  struct sk_buff *skb, int doff,
    230			  const struct in6_addr *saddr, const __be16 sport,
    231			  const struct in6_addr *daddr, const __be16 dport,
    232			  const int dif)
    233{
    234	struct sock *sk;
    235	bool refcounted;
    236
    237	sk = __inet6_lookup(net, hashinfo, skb, doff, saddr, sport, daddr,
    238			    ntohs(dport), dif, 0, &refcounted);
    239	if (sk && !refcounted && !refcount_inc_not_zero(&sk->sk_refcnt))
    240		sk = NULL;
    241	return sk;
    242}
    243EXPORT_SYMBOL_GPL(inet6_lookup);
    244
    245static int __inet6_check_established(struct inet_timewait_death_row *death_row,
    246				     struct sock *sk, const __u16 lport,
    247				     struct inet_timewait_sock **twp)
    248{
    249	struct inet_hashinfo *hinfo = death_row->hashinfo;
    250	struct inet_sock *inet = inet_sk(sk);
    251	const struct in6_addr *daddr = &sk->sk_v6_rcv_saddr;
    252	const struct in6_addr *saddr = &sk->sk_v6_daddr;
    253	const int dif = sk->sk_bound_dev_if;
    254	struct net *net = sock_net(sk);
    255	const int sdif = l3mdev_master_ifindex_by_index(net, dif);
    256	const __portpair ports = INET_COMBINED_PORTS(inet->inet_dport, lport);
    257	const unsigned int hash = inet6_ehashfn(net, daddr, lport, saddr,
    258						inet->inet_dport);
    259	struct inet_ehash_bucket *head = inet_ehash_bucket(hinfo, hash);
    260	spinlock_t *lock = inet_ehash_lockp(hinfo, hash);
    261	struct sock *sk2;
    262	const struct hlist_nulls_node *node;
    263	struct inet_timewait_sock *tw = NULL;
    264
    265	spin_lock(lock);
    266
    267	sk_nulls_for_each(sk2, node, &head->chain) {
    268		if (sk2->sk_hash != hash)
    269			continue;
    270
    271		if (likely(inet6_match(net, sk2, saddr, daddr, ports,
    272				       dif, sdif))) {
    273			if (sk2->sk_state == TCP_TIME_WAIT) {
    274				tw = inet_twsk(sk2);
    275				if (twsk_unique(sk, sk2, twp))
    276					break;
    277			}
    278			goto not_unique;
    279		}
    280	}
    281
    282	/* Must record num and sport now. Otherwise we will see
    283	 * in hash table socket with a funny identity.
    284	 */
    285	inet->inet_num = lport;
    286	inet->inet_sport = htons(lport);
    287	sk->sk_hash = hash;
    288	WARN_ON(!sk_unhashed(sk));
    289	__sk_nulls_add_node_rcu(sk, &head->chain);
    290	if (tw) {
    291		sk_nulls_del_node_init_rcu((struct sock *)tw);
    292		__NET_INC_STATS(net, LINUX_MIB_TIMEWAITRECYCLED);
    293	}
    294	spin_unlock(lock);
    295	sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
    296
    297	if (twp) {
    298		*twp = tw;
    299	} else if (tw) {
    300		/* Silly. Should hash-dance instead... */
    301		inet_twsk_deschedule_put(tw);
    302	}
    303	return 0;
    304
    305not_unique:
    306	spin_unlock(lock);
    307	return -EADDRNOTAVAIL;
    308}
    309
    310static u64 inet6_sk_port_offset(const struct sock *sk)
    311{
    312	const struct inet_sock *inet = inet_sk(sk);
    313
    314	return secure_ipv6_port_ephemeral(sk->sk_v6_rcv_saddr.s6_addr32,
    315					  sk->sk_v6_daddr.s6_addr32,
    316					  inet->inet_dport);
    317}
    318
    319int inet6_hash_connect(struct inet_timewait_death_row *death_row,
    320		       struct sock *sk)
    321{
    322	u64 port_offset = 0;
    323
    324	if (!inet_sk(sk)->inet_num)
    325		port_offset = inet6_sk_port_offset(sk);
    326	return __inet_hash_connect(death_row, sk, port_offset,
    327				   __inet6_check_established);
    328}
    329EXPORT_SYMBOL_GPL(inet6_hash_connect);
    330
    331int inet6_hash(struct sock *sk)
    332{
    333	int err = 0;
    334
    335	if (sk->sk_state != TCP_CLOSE)
    336		err = __inet_hash(sk, NULL);
    337
    338	return err;
    339}
    340EXPORT_SYMBOL_GPL(inet6_hash);