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