1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
27cd23e53SJason A. Donenfeld /*
37cd23e53SJason A. Donenfeld * Copyright (C) 2016 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
47cd23e53SJason A. Donenfeld */
57cd23e53SJason A. Donenfeld
66e5714eaSDavid S. Miller #include <linux/kernel.h>
76e5714eaSDavid S. Miller #include <linux/init.h>
86e5714eaSDavid S. Miller #include <linux/module.h>
96e5714eaSDavid S. Miller #include <linux/cache.h>
106e5714eaSDavid S. Miller #include <linux/random.h>
116e5714eaSDavid S. Miller #include <linux/hrtimer.h>
126e5714eaSDavid S. Miller #include <linux/ktime.h>
136e5714eaSDavid S. Miller #include <linux/string.h>
14e34c9a69SHannes Frederic Sowa #include <linux/net.h>
157cd23e53SJason A. Donenfeld #include <linux/siphash.h>
166e5714eaSDavid S. Miller #include <net/secure_seq.h>
176e5714eaSDavid S. Miller
18cb03db9dSFabio Estevam #if IS_ENABLED(CONFIG_IPV6) || IS_ENABLED(CONFIG_INET)
197cd23e53SJason A. Donenfeld #include <linux/in6.h>
2025429d7bSFlorian Westphal #include <net/tcp.h>
216e5714eaSDavid S. Miller
2249ecc2e9SEric Dumazet static siphash_aligned_key_t net_secret;
2349ecc2e9SEric Dumazet static siphash_aligned_key_t ts_secret;
249a3bab6bSEric Dumazet
254dfa9b43SEric Dumazet #define EPHEMERAL_PORT_SHUFFLE_PERIOD (10 * HZ)
264dfa9b43SEric Dumazet
net_secret_init(void)2734d92d53SHannes Frederic Sowa static __always_inline void net_secret_init(void)
286e5714eaSDavid S. Miller {
297cd23e53SJason A. Donenfeld net_get_random_once(&net_secret, sizeof(net_secret));
306e5714eaSDavid S. Miller }
3184b114b9SEric Dumazet
ts_secret_init(void)3284b114b9SEric Dumazet static __always_inline void ts_secret_init(void)
3384b114b9SEric Dumazet {
3484b114b9SEric Dumazet net_get_random_once(&ts_secret, sizeof(ts_secret));
3584b114b9SEric Dumazet }
36cb03db9dSFabio Estevam #endif
376e5714eaSDavid S. Miller
3868109090SStephen Boyd #ifdef CONFIG_INET
seq_scale(u32 seq)396e5714eaSDavid S. Miller static u32 seq_scale(u32 seq)
406e5714eaSDavid S. Miller {
416e5714eaSDavid S. Miller /*
426e5714eaSDavid S. Miller * As close as possible to RFC 793, which
436e5714eaSDavid S. Miller * suggests using a 250 kHz clock.
446e5714eaSDavid S. Miller * Further reading shows this assumes 2 Mb/s networks.
456e5714eaSDavid S. Miller * For 10 Mb/s Ethernet, a 1 MHz clock is appropriate.
466e5714eaSDavid S. Miller * For 10 Gb/s Ethernet, a 1 GHz clock should be ok, but
476e5714eaSDavid S. Miller * we also need to limit the resolution so that the u32 seq
486e5714eaSDavid S. Miller * overlaps less than one time per MSL (2 minutes).
496e5714eaSDavid S. Miller * Choosing a clock of 64 ns period is OK. (period of 274 s)
506e5714eaSDavid S. Miller */
51d2de875cSEric Dumazet return seq + (ktime_get_real_ns() >> 6);
526e5714eaSDavid S. Miller }
5368109090SStephen Boyd #endif
546e5714eaSDavid S. Miller
55dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
secure_tcpv6_ts_off(const struct net * net,const __be32 * saddr,const __be32 * daddr)565d2ed052SEric Dumazet u32 secure_tcpv6_ts_off(const struct net *net,
575d2ed052SEric Dumazet const __be32 *saddr, const __be32 *daddr)
5828ee1b74SFlorian Westphal {
5928ee1b74SFlorian Westphal const struct {
6028ee1b74SFlorian Westphal struct in6_addr saddr;
6128ee1b74SFlorian Westphal struct in6_addr daddr;
6228ee1b74SFlorian Westphal } __aligned(SIPHASH_ALIGNMENT) combined = {
6328ee1b74SFlorian Westphal .saddr = *(struct in6_addr *)saddr,
6428ee1b74SFlorian Westphal .daddr = *(struct in6_addr *)daddr,
6528ee1b74SFlorian Westphal };
6628ee1b74SFlorian Westphal
67*3666f666SKuniyuki Iwashima if (READ_ONCE(net->ipv4.sysctl_tcp_timestamps) != 1)
6828ee1b74SFlorian Westphal return 0;
6928ee1b74SFlorian Westphal
7084b114b9SEric Dumazet ts_secret_init();
7128ee1b74SFlorian Westphal return siphash(&combined, offsetofend(typeof(combined), daddr),
7228ee1b74SFlorian Westphal &ts_secret);
7328ee1b74SFlorian Westphal }
7484b114b9SEric Dumazet EXPORT_SYMBOL(secure_tcpv6_ts_off);
7528ee1b74SFlorian Westphal
secure_tcpv6_seq(const __be32 * saddr,const __be32 * daddr,__be16 sport,__be16 dport)7684b114b9SEric Dumazet u32 secure_tcpv6_seq(const __be32 *saddr, const __be32 *daddr,
7784b114b9SEric Dumazet __be16 sport, __be16 dport)
786e5714eaSDavid S. Miller {
797cd23e53SJason A. Donenfeld const struct {
807cd23e53SJason A. Donenfeld struct in6_addr saddr;
817cd23e53SJason A. Donenfeld struct in6_addr daddr;
827cd23e53SJason A. Donenfeld __be16 sport;
837cd23e53SJason A. Donenfeld __be16 dport;
847cd23e53SJason A. Donenfeld } __aligned(SIPHASH_ALIGNMENT) combined = {
857cd23e53SJason A. Donenfeld .saddr = *(struct in6_addr *)saddr,
867cd23e53SJason A. Donenfeld .daddr = *(struct in6_addr *)daddr,
877cd23e53SJason A. Donenfeld .sport = sport,
887cd23e53SJason A. Donenfeld .dport = dport
897cd23e53SJason A. Donenfeld };
9084b114b9SEric Dumazet u32 hash;
9184b114b9SEric Dumazet
929a3bab6bSEric Dumazet net_secret_init();
937cd23e53SJason A. Donenfeld hash = siphash(&combined, offsetofend(typeof(combined), dport),
947cd23e53SJason A. Donenfeld &net_secret);
957cd23e53SJason A. Donenfeld return seq_scale(hash);
966e5714eaSDavid S. Miller }
9784b114b9SEric Dumazet EXPORT_SYMBOL(secure_tcpv6_seq);
986e5714eaSDavid S. Miller
secure_ipv6_port_ephemeral(const __be32 * saddr,const __be32 * daddr,__be16 dport)99b2d05756SWilly Tarreau u64 secure_ipv6_port_ephemeral(const __be32 *saddr, const __be32 *daddr,
1006e5714eaSDavid S. Miller __be16 dport)
1016e5714eaSDavid S. Miller {
1027cd23e53SJason A. Donenfeld const struct {
1037cd23e53SJason A. Donenfeld struct in6_addr saddr;
1047cd23e53SJason A. Donenfeld struct in6_addr daddr;
1054dfa9b43SEric Dumazet unsigned int timeseed;
1067cd23e53SJason A. Donenfeld __be16 dport;
1077cd23e53SJason A. Donenfeld } __aligned(SIPHASH_ALIGNMENT) combined = {
1087cd23e53SJason A. Donenfeld .saddr = *(struct in6_addr *)saddr,
1097cd23e53SJason A. Donenfeld .daddr = *(struct in6_addr *)daddr,
1104dfa9b43SEric Dumazet .timeseed = jiffies / EPHEMERAL_PORT_SHUFFLE_PERIOD,
1114dfa9b43SEric Dumazet .dport = dport,
1127cd23e53SJason A. Donenfeld };
1139a3bab6bSEric Dumazet net_secret_init();
1147cd23e53SJason A. Donenfeld return siphash(&combined, offsetofend(typeof(combined), dport),
1157cd23e53SJason A. Donenfeld &net_secret);
1166e5714eaSDavid S. Miller }
11758a317f1SPatrick McHardy EXPORT_SYMBOL(secure_ipv6_port_ephemeral);
1186e5714eaSDavid S. Miller #endif
1196e5714eaSDavid S. Miller
1206e5714eaSDavid S. Miller #ifdef CONFIG_INET
secure_tcp_ts_off(const struct net * net,__be32 saddr,__be32 daddr)1215d2ed052SEric Dumazet u32 secure_tcp_ts_off(const struct net *net, __be32 saddr, __be32 daddr)
12228ee1b74SFlorian Westphal {
123*3666f666SKuniyuki Iwashima if (READ_ONCE(net->ipv4.sysctl_tcp_timestamps) != 1)
12428ee1b74SFlorian Westphal return 0;
12528ee1b74SFlorian Westphal
12684b114b9SEric Dumazet ts_secret_init();
12728ee1b74SFlorian Westphal return siphash_2u32((__force u32)saddr, (__force u32)daddr,
12828ee1b74SFlorian Westphal &ts_secret);
12928ee1b74SFlorian Westphal }
1306e5714eaSDavid S. Miller
131a30aad50SAlexey Kodanev /* secure_tcp_seq_and_tsoff(a, b, 0, d) == secure_ipv4_port_ephemeral(a, b, d),
1327cd23e53SJason A. Donenfeld * but fortunately, `sport' cannot be 0 in any circumstances. If this changes,
1337cd23e53SJason A. Donenfeld * it would be easy enough to have the former function use siphash_4u32, passing
1347cd23e53SJason A. Donenfeld * the arguments as separate u32.
1357cd23e53SJason A. Donenfeld */
secure_tcp_seq(__be32 saddr,__be32 daddr,__be16 sport,__be16 dport)13684b114b9SEric Dumazet u32 secure_tcp_seq(__be32 saddr, __be32 daddr,
13784b114b9SEric Dumazet __be16 sport, __be16 dport)
1386e5714eaSDavid S. Miller {
13984b114b9SEric Dumazet u32 hash;
14084b114b9SEric Dumazet
1419a3bab6bSEric Dumazet net_secret_init();
1427cd23e53SJason A. Donenfeld hash = siphash_3u32((__force u32)saddr, (__force u32)daddr,
1437cd23e53SJason A. Donenfeld (__force u32)sport << 16 | (__force u32)dport,
1447cd23e53SJason A. Donenfeld &net_secret);
1457cd23e53SJason A. Donenfeld return seq_scale(hash);
1466e5714eaSDavid S. Miller }
14707f3355dSArnd Bergmann EXPORT_SYMBOL_GPL(secure_tcp_seq);
1486e5714eaSDavid S. Miller
secure_ipv4_port_ephemeral(__be32 saddr,__be32 daddr,__be16 dport)149b2d05756SWilly Tarreau u64 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport)
1506e5714eaSDavid S. Miller {
1519a3bab6bSEric Dumazet net_secret_init();
1524dfa9b43SEric Dumazet return siphash_4u32((__force u32)saddr, (__force u32)daddr,
1534dfa9b43SEric Dumazet (__force u16)dport,
1544dfa9b43SEric Dumazet jiffies / EPHEMERAL_PORT_SHUFFLE_PERIOD,
1554dfa9b43SEric Dumazet &net_secret);
1566e5714eaSDavid S. Miller }
1576e5714eaSDavid S. Miller EXPORT_SYMBOL_GPL(secure_ipv4_port_ephemeral);
1586e5714eaSDavid S. Miller #endif
1596e5714eaSDavid S. Miller
160a3bf7ae9SIgor Maravić #if IS_ENABLED(CONFIG_IP_DCCP)
secure_dccp_sequence_number(__be32 saddr,__be32 daddr,__be16 sport,__be16 dport)1616e5714eaSDavid S. Miller u64 secure_dccp_sequence_number(__be32 saddr, __be32 daddr,
1626e5714eaSDavid S. Miller __be16 sport, __be16 dport)
1636e5714eaSDavid S. Miller {
1646e5714eaSDavid S. Miller u64 seq;
1659a3bab6bSEric Dumazet net_secret_init();
166c1ce1560SEric Dumazet seq = siphash_3u32((__force u32)saddr, (__force u32)daddr,
167c1ce1560SEric Dumazet (__force u32)sport << 16 | (__force u32)dport,
168c1ce1560SEric Dumazet &net_secret);
169d2de875cSEric Dumazet seq += ktime_get_real_ns();
1706e5714eaSDavid S. Miller seq &= (1ull << 48) - 1;
1716e5714eaSDavid S. Miller return seq;
1726e5714eaSDavid S. Miller }
1736e5714eaSDavid S. Miller EXPORT_SYMBOL(secure_dccp_sequence_number);
1746e5714eaSDavid S. Miller
175dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
secure_dccpv6_sequence_number(__be32 * saddr,__be32 * daddr,__be16 sport,__be16 dport)1766e5714eaSDavid S. Miller u64 secure_dccpv6_sequence_number(__be32 *saddr, __be32 *daddr,
1776e5714eaSDavid S. Miller __be16 sport, __be16 dport)
1786e5714eaSDavid S. Miller {
1797cd23e53SJason A. Donenfeld const struct {
1807cd23e53SJason A. Donenfeld struct in6_addr saddr;
1817cd23e53SJason A. Donenfeld struct in6_addr daddr;
1827cd23e53SJason A. Donenfeld __be16 sport;
1837cd23e53SJason A. Donenfeld __be16 dport;
1847cd23e53SJason A. Donenfeld } __aligned(SIPHASH_ALIGNMENT) combined = {
1857cd23e53SJason A. Donenfeld .saddr = *(struct in6_addr *)saddr,
1867cd23e53SJason A. Donenfeld .daddr = *(struct in6_addr *)daddr,
1877cd23e53SJason A. Donenfeld .sport = sport,
1887cd23e53SJason A. Donenfeld .dport = dport
1897cd23e53SJason A. Donenfeld };
1906e5714eaSDavid S. Miller u64 seq;
1919a3bab6bSEric Dumazet net_secret_init();
1927cd23e53SJason A. Donenfeld seq = siphash(&combined, offsetofend(typeof(combined), dport),
1937cd23e53SJason A. Donenfeld &net_secret);
194d2de875cSEric Dumazet seq += ktime_get_real_ns();
1956e5714eaSDavid S. Miller seq &= (1ull << 48) - 1;
1966e5714eaSDavid S. Miller return seq;
1976e5714eaSDavid S. Miller }
1986e5714eaSDavid S. Miller EXPORT_SYMBOL(secure_dccpv6_sequence_number);
1996e5714eaSDavid S. Miller #endif
2006e5714eaSDavid S. Miller #endif
201