162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2016 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/kernel.h>
762306a36Sopenharmony_ci#include <linux/init.h>
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <linux/cache.h>
1062306a36Sopenharmony_ci#include <linux/random.h>
1162306a36Sopenharmony_ci#include <linux/hrtimer.h>
1262306a36Sopenharmony_ci#include <linux/ktime.h>
1362306a36Sopenharmony_ci#include <linux/string.h>
1462306a36Sopenharmony_ci#include <linux/net.h>
1562306a36Sopenharmony_ci#include <linux/siphash.h>
1662306a36Sopenharmony_ci#include <net/secure_seq.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) || IS_ENABLED(CONFIG_INET)
1962306a36Sopenharmony_ci#include <linux/in6.h>
2062306a36Sopenharmony_ci#include <net/tcp.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistatic siphash_aligned_key_t net_secret;
2362306a36Sopenharmony_cistatic siphash_aligned_key_t ts_secret;
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define EPHEMERAL_PORT_SHUFFLE_PERIOD (10 * HZ)
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic __always_inline void net_secret_init(void)
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	net_get_random_once(&net_secret, sizeof(net_secret));
3062306a36Sopenharmony_ci}
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic __always_inline void ts_secret_init(void)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	net_get_random_once(&ts_secret, sizeof(ts_secret));
3562306a36Sopenharmony_ci}
3662306a36Sopenharmony_ci#endif
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#ifdef CONFIG_INET
3962306a36Sopenharmony_cistatic u32 seq_scale(u32 seq)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	/*
4262306a36Sopenharmony_ci	 *	As close as possible to RFC 793, which
4362306a36Sopenharmony_ci	 *	suggests using a 250 kHz clock.
4462306a36Sopenharmony_ci	 *	Further reading shows this assumes 2 Mb/s networks.
4562306a36Sopenharmony_ci	 *	For 10 Mb/s Ethernet, a 1 MHz clock is appropriate.
4662306a36Sopenharmony_ci	 *	For 10 Gb/s Ethernet, a 1 GHz clock should be ok, but
4762306a36Sopenharmony_ci	 *	we also need to limit the resolution so that the u32 seq
4862306a36Sopenharmony_ci	 *	overlaps less than one time per MSL (2 minutes).
4962306a36Sopenharmony_ci	 *	Choosing a clock of 64 ns period is OK. (period of 274 s)
5062306a36Sopenharmony_ci	 */
5162306a36Sopenharmony_ci	return seq + (ktime_get_real_ns() >> 6);
5262306a36Sopenharmony_ci}
5362306a36Sopenharmony_ci#endif
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
5662306a36Sopenharmony_ciu32 secure_tcpv6_ts_off(const struct net *net,
5762306a36Sopenharmony_ci			const __be32 *saddr, const __be32 *daddr)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	const struct {
6062306a36Sopenharmony_ci		struct in6_addr saddr;
6162306a36Sopenharmony_ci		struct in6_addr daddr;
6262306a36Sopenharmony_ci	} __aligned(SIPHASH_ALIGNMENT) combined = {
6362306a36Sopenharmony_ci		.saddr = *(struct in6_addr *)saddr,
6462306a36Sopenharmony_ci		.daddr = *(struct in6_addr *)daddr,
6562306a36Sopenharmony_ci	};
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	if (READ_ONCE(net->ipv4.sysctl_tcp_timestamps) != 1)
6862306a36Sopenharmony_ci		return 0;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	ts_secret_init();
7162306a36Sopenharmony_ci	return siphash(&combined, offsetofend(typeof(combined), daddr),
7262306a36Sopenharmony_ci		       &ts_secret);
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ciEXPORT_SYMBOL(secure_tcpv6_ts_off);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ciu32 secure_tcpv6_seq(const __be32 *saddr, const __be32 *daddr,
7762306a36Sopenharmony_ci		     __be16 sport, __be16 dport)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	const struct {
8062306a36Sopenharmony_ci		struct in6_addr saddr;
8162306a36Sopenharmony_ci		struct in6_addr daddr;
8262306a36Sopenharmony_ci		__be16 sport;
8362306a36Sopenharmony_ci		__be16 dport;
8462306a36Sopenharmony_ci	} __aligned(SIPHASH_ALIGNMENT) combined = {
8562306a36Sopenharmony_ci		.saddr = *(struct in6_addr *)saddr,
8662306a36Sopenharmony_ci		.daddr = *(struct in6_addr *)daddr,
8762306a36Sopenharmony_ci		.sport = sport,
8862306a36Sopenharmony_ci		.dport = dport
8962306a36Sopenharmony_ci	};
9062306a36Sopenharmony_ci	u32 hash;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	net_secret_init();
9362306a36Sopenharmony_ci	hash = siphash(&combined, offsetofend(typeof(combined), dport),
9462306a36Sopenharmony_ci		       &net_secret);
9562306a36Sopenharmony_ci	return seq_scale(hash);
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ciEXPORT_SYMBOL(secure_tcpv6_seq);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ciu64 secure_ipv6_port_ephemeral(const __be32 *saddr, const __be32 *daddr,
10062306a36Sopenharmony_ci			       __be16 dport)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	const struct {
10362306a36Sopenharmony_ci		struct in6_addr saddr;
10462306a36Sopenharmony_ci		struct in6_addr daddr;
10562306a36Sopenharmony_ci		unsigned int timeseed;
10662306a36Sopenharmony_ci		__be16 dport;
10762306a36Sopenharmony_ci	} __aligned(SIPHASH_ALIGNMENT) combined = {
10862306a36Sopenharmony_ci		.saddr = *(struct in6_addr *)saddr,
10962306a36Sopenharmony_ci		.daddr = *(struct in6_addr *)daddr,
11062306a36Sopenharmony_ci		.timeseed = jiffies / EPHEMERAL_PORT_SHUFFLE_PERIOD,
11162306a36Sopenharmony_ci		.dport = dport,
11262306a36Sopenharmony_ci	};
11362306a36Sopenharmony_ci	net_secret_init();
11462306a36Sopenharmony_ci	return siphash(&combined, offsetofend(typeof(combined), dport),
11562306a36Sopenharmony_ci		       &net_secret);
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ciEXPORT_SYMBOL(secure_ipv6_port_ephemeral);
11862306a36Sopenharmony_ci#endif
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci#ifdef CONFIG_INET
12162306a36Sopenharmony_ciu32 secure_tcp_ts_off(const struct net *net, __be32 saddr, __be32 daddr)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	if (READ_ONCE(net->ipv4.sysctl_tcp_timestamps) != 1)
12462306a36Sopenharmony_ci		return 0;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	ts_secret_init();
12762306a36Sopenharmony_ci	return siphash_2u32((__force u32)saddr, (__force u32)daddr,
12862306a36Sopenharmony_ci			    &ts_secret);
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci/* secure_tcp_seq_and_tsoff(a, b, 0, d) == secure_ipv4_port_ephemeral(a, b, d),
13262306a36Sopenharmony_ci * but fortunately, `sport' cannot be 0 in any circumstances. If this changes,
13362306a36Sopenharmony_ci * it would be easy enough to have the former function use siphash_4u32, passing
13462306a36Sopenharmony_ci * the arguments as separate u32.
13562306a36Sopenharmony_ci */
13662306a36Sopenharmony_ciu32 secure_tcp_seq(__be32 saddr, __be32 daddr,
13762306a36Sopenharmony_ci		   __be16 sport, __be16 dport)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	u32 hash;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	net_secret_init();
14262306a36Sopenharmony_ci	hash = siphash_3u32((__force u32)saddr, (__force u32)daddr,
14362306a36Sopenharmony_ci			    (__force u32)sport << 16 | (__force u32)dport,
14462306a36Sopenharmony_ci			    &net_secret);
14562306a36Sopenharmony_ci	return seq_scale(hash);
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(secure_tcp_seq);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ciu64 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	net_secret_init();
15262306a36Sopenharmony_ci	return siphash_4u32((__force u32)saddr, (__force u32)daddr,
15362306a36Sopenharmony_ci			    (__force u16)dport,
15462306a36Sopenharmony_ci			    jiffies / EPHEMERAL_PORT_SHUFFLE_PERIOD,
15562306a36Sopenharmony_ci			    &net_secret);
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(secure_ipv4_port_ephemeral);
15862306a36Sopenharmony_ci#endif
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IP_DCCP)
16162306a36Sopenharmony_ciu64 secure_dccp_sequence_number(__be32 saddr, __be32 daddr,
16262306a36Sopenharmony_ci				__be16 sport, __be16 dport)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	u64 seq;
16562306a36Sopenharmony_ci	net_secret_init();
16662306a36Sopenharmony_ci	seq = siphash_3u32((__force u32)saddr, (__force u32)daddr,
16762306a36Sopenharmony_ci			   (__force u32)sport << 16 | (__force u32)dport,
16862306a36Sopenharmony_ci			   &net_secret);
16962306a36Sopenharmony_ci	seq += ktime_get_real_ns();
17062306a36Sopenharmony_ci	seq &= (1ull << 48) - 1;
17162306a36Sopenharmony_ci	return seq;
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ciEXPORT_SYMBOL(secure_dccp_sequence_number);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
17662306a36Sopenharmony_ciu64 secure_dccpv6_sequence_number(__be32 *saddr, __be32 *daddr,
17762306a36Sopenharmony_ci				  __be16 sport, __be16 dport)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	const struct {
18062306a36Sopenharmony_ci		struct in6_addr saddr;
18162306a36Sopenharmony_ci		struct in6_addr daddr;
18262306a36Sopenharmony_ci		__be16 sport;
18362306a36Sopenharmony_ci		__be16 dport;
18462306a36Sopenharmony_ci	} __aligned(SIPHASH_ALIGNMENT) combined = {
18562306a36Sopenharmony_ci		.saddr = *(struct in6_addr *)saddr,
18662306a36Sopenharmony_ci		.daddr = *(struct in6_addr *)daddr,
18762306a36Sopenharmony_ci		.sport = sport,
18862306a36Sopenharmony_ci		.dport = dport
18962306a36Sopenharmony_ci	};
19062306a36Sopenharmony_ci	u64 seq;
19162306a36Sopenharmony_ci	net_secret_init();
19262306a36Sopenharmony_ci	seq = siphash(&combined, offsetofend(typeof(combined), dport),
19362306a36Sopenharmony_ci		      &net_secret);
19462306a36Sopenharmony_ci	seq += ktime_get_real_ns();
19562306a36Sopenharmony_ci	seq &= (1ull << 48) - 1;
19662306a36Sopenharmony_ci	return seq;
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ciEXPORT_SYMBOL(secure_dccpv6_sequence_number);
19962306a36Sopenharmony_ci#endif
20062306a36Sopenharmony_ci#endif
201