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