1 /* 2 * Copyright (C) 2016 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. 3 */ 4 5 #include <linux/kernel.h> 6 #include <linux/init.h> 7 #include <linux/cryptohash.h> 8 #include <linux/module.h> 9 #include <linux/cache.h> 10 #include <linux/random.h> 11 #include <linux/hrtimer.h> 12 #include <linux/ktime.h> 13 #include <linux/string.h> 14 #include <linux/net.h> 15 #include <linux/siphash.h> 16 #include <net/secure_seq.h> 17 18 #if IS_ENABLED(CONFIG_IPV6) || IS_ENABLED(CONFIG_INET) 19 #include <linux/in6.h> 20 #include <net/tcp.h> 21 22 static siphash_key_t net_secret __read_mostly; 23 24 static __always_inline void net_secret_init(void) 25 { 26 net_get_random_once(&net_secret, sizeof(net_secret)); 27 } 28 #endif 29 30 #ifdef CONFIG_INET 31 static u32 seq_scale(u32 seq) 32 { 33 /* 34 * As close as possible to RFC 793, which 35 * suggests using a 250 kHz clock. 36 * Further reading shows this assumes 2 Mb/s networks. 37 * For 10 Mb/s Ethernet, a 1 MHz clock is appropriate. 38 * For 10 Gb/s Ethernet, a 1 GHz clock should be ok, but 39 * we also need to limit the resolution so that the u32 seq 40 * overlaps less than one time per MSL (2 minutes). 41 * Choosing a clock of 64 ns period is OK. (period of 274 s) 42 */ 43 return seq + (ktime_get_real_ns() >> 6); 44 } 45 #endif 46 47 #if IS_ENABLED(CONFIG_IPV6) 48 u32 secure_tcpv6_sequence_number(const __be32 *saddr, const __be32 *daddr, 49 __be16 sport, __be16 dport, u32 *tsoff) 50 { 51 const struct { 52 struct in6_addr saddr; 53 struct in6_addr daddr; 54 __be16 sport; 55 __be16 dport; 56 } __aligned(SIPHASH_ALIGNMENT) combined = { 57 .saddr = *(struct in6_addr *)saddr, 58 .daddr = *(struct in6_addr *)daddr, 59 .sport = sport, 60 .dport = dport 61 }; 62 u64 hash; 63 net_secret_init(); 64 hash = siphash(&combined, offsetofend(typeof(combined), dport), 65 &net_secret); 66 *tsoff = sysctl_tcp_timestamps == 1 ? (hash >> 32) : 0; 67 return seq_scale(hash); 68 } 69 EXPORT_SYMBOL(secure_tcpv6_sequence_number); 70 71 u32 secure_ipv6_port_ephemeral(const __be32 *saddr, const __be32 *daddr, 72 __be16 dport) 73 { 74 const struct { 75 struct in6_addr saddr; 76 struct in6_addr daddr; 77 __be16 dport; 78 } __aligned(SIPHASH_ALIGNMENT) combined = { 79 .saddr = *(struct in6_addr *)saddr, 80 .daddr = *(struct in6_addr *)daddr, 81 .dport = dport 82 }; 83 net_secret_init(); 84 return siphash(&combined, offsetofend(typeof(combined), dport), 85 &net_secret); 86 } 87 EXPORT_SYMBOL(secure_ipv6_port_ephemeral); 88 #endif 89 90 #ifdef CONFIG_INET 91 92 /* secure_tcp_sequence_number(a, b, 0, d) == secure_ipv4_port_ephemeral(a, b, d), 93 * but fortunately, `sport' cannot be 0 in any circumstances. If this changes, 94 * it would be easy enough to have the former function use siphash_4u32, passing 95 * the arguments as separate u32. 96 */ 97 98 u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr, 99 __be16 sport, __be16 dport, u32 *tsoff) 100 { 101 u64 hash; 102 net_secret_init(); 103 hash = siphash_3u32((__force u32)saddr, (__force u32)daddr, 104 (__force u32)sport << 16 | (__force u32)dport, 105 &net_secret); 106 *tsoff = sysctl_tcp_timestamps == 1 ? (hash >> 32) : 0; 107 return seq_scale(hash); 108 } 109 110 u32 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport) 111 { 112 net_secret_init(); 113 return siphash_3u32((__force u32)saddr, (__force u32)daddr, 114 (__force u16)dport, &net_secret); 115 } 116 EXPORT_SYMBOL_GPL(secure_ipv4_port_ephemeral); 117 #endif 118 119 #if IS_ENABLED(CONFIG_IP_DCCP) 120 u64 secure_dccp_sequence_number(__be32 saddr, __be32 daddr, 121 __be16 sport, __be16 dport) 122 { 123 u64 seq; 124 net_secret_init(); 125 seq = siphash_3u32((__force u32)saddr, (__force u32)daddr, 126 (__force u32)sport << 16 | (__force u32)dport, 127 &net_secret); 128 seq += ktime_get_real_ns(); 129 seq &= (1ull << 48) - 1; 130 return seq; 131 } 132 EXPORT_SYMBOL(secure_dccp_sequence_number); 133 134 #if IS_ENABLED(CONFIG_IPV6) 135 u64 secure_dccpv6_sequence_number(__be32 *saddr, __be32 *daddr, 136 __be16 sport, __be16 dport) 137 { 138 const struct { 139 struct in6_addr saddr; 140 struct in6_addr daddr; 141 __be16 sport; 142 __be16 dport; 143 } __aligned(SIPHASH_ALIGNMENT) combined = { 144 .saddr = *(struct in6_addr *)saddr, 145 .daddr = *(struct in6_addr *)daddr, 146 .sport = sport, 147 .dport = dport 148 }; 149 u64 seq; 150 net_secret_init(); 151 seq = siphash(&combined, offsetofend(typeof(combined), dport), 152 &net_secret); 153 seq += ktime_get_real_ns(); 154 seq &= (1ull << 48) - 1; 155 return seq; 156 } 157 EXPORT_SYMBOL(secure_dccpv6_sequence_number); 158 #endif 159 #endif 160