LCOV - code coverage report
Current view: top level - net/ipv6 - syncookies.c (source / functions) Hit Total Coverage
Test: gcov_data_raspi2_qemu_modules_combined.info Lines: 0 98 0.0 %
Date: 2020-09-30 20:25:01 Functions: 0 7 0.0 %
Branches: 0 44 0.0 %

           Branch data     Line data    Source code
       1                 :            : // SPDX-License-Identifier: GPL-2.0-or-later
       2                 :            : /*
       3                 :            :  *  IPv6 Syncookies implementation for the Linux kernel
       4                 :            :  *
       5                 :            :  *  Authors:
       6                 :            :  *  Glenn Griffin       <ggriffin.kernel@gmail.com>
       7                 :            :  *
       8                 :            :  *  Based on IPv4 implementation by Andi Kleen
       9                 :            :  *  linux/net/ipv4/syncookies.c
      10                 :            :  */
      11                 :            : 
      12                 :            : #include <linux/tcp.h>
      13                 :            : #include <linux/random.h>
      14                 :            : #include <linux/siphash.h>
      15                 :            : #include <linux/kernel.h>
      16                 :            : #include <net/secure_seq.h>
      17                 :            : #include <net/ipv6.h>
      18                 :            : #include <net/tcp.h>
      19                 :            : 
      20                 :            : #define COOKIEBITS 24   /* Upper bits store count */
      21                 :            : #define COOKIEMASK (((__u32)1 << COOKIEBITS) - 1)
      22                 :            : 
      23                 :            : static siphash_key_t syncookie6_secret[2] __read_mostly;
      24                 :            : 
      25                 :            : /* RFC 2460, Section 8.3:
      26                 :            :  * [ipv6 tcp] MSS must be computed as the maximum packet size minus 60 [..]
      27                 :            :  *
      28                 :            :  * Due to IPV6_MIN_MTU=1280 the lowest possible MSS is 1220, which allows
      29                 :            :  * using higher values than ipv4 tcp syncookies.
      30                 :            :  * The other values are chosen based on ethernet (1500 and 9k MTU), plus
      31                 :            :  * one that accounts for common encap (PPPoe) overhead. Table must be sorted.
      32                 :            :  */
      33                 :            : static __u16 const msstab[] = {
      34                 :            :         1280 - 60, /* IPV6_MIN_MTU - 60 */
      35                 :            :         1480 - 60,
      36                 :            :         1500 - 60,
      37                 :            :         9000 - 60,
      38                 :            : };
      39                 :            : 
      40                 :          0 : static u32 cookie_hash(const struct in6_addr *saddr,
      41                 :            :                        const struct in6_addr *daddr,
      42                 :            :                        __be16 sport, __be16 dport, u32 count, int c)
      43                 :            : {
      44                 :            :         const struct {
      45                 :            :                 struct in6_addr saddr;
      46                 :            :                 struct in6_addr daddr;
      47                 :            :                 u32 count;
      48                 :            :                 __be16 sport;
      49                 :            :                 __be16 dport;
      50                 :          0 :         } __aligned(SIPHASH_ALIGNMENT) combined = {
      51                 :            :                 .saddr = *saddr,
      52                 :            :                 .daddr = *daddr,
      53                 :            :                 .count = count,
      54                 :            :                 .sport = sport,
      55                 :            :                 .dport = dport
      56                 :            :         };
      57                 :            : 
      58   [ #  #  #  # ]:          0 :         net_get_random_once(syncookie6_secret, sizeof(syncookie6_secret));
      59                 :          0 :         return siphash(&combined, offsetofend(typeof(combined), dport),
      60                 :          0 :                        &syncookie6_secret[c]);
      61                 :            : }
      62                 :            : 
      63                 :          0 : static __u32 secure_tcp_syn_cookie(const struct in6_addr *saddr,
      64                 :            :                                    const struct in6_addr *daddr,
      65                 :            :                                    __be16 sport, __be16 dport, __u32 sseq,
      66                 :            :                                    __u32 data)
      67                 :            : {
      68                 :          0 :         u32 count = tcp_cookie_time();
      69                 :          0 :         return (cookie_hash(saddr, daddr, sport, dport, 0, 0) +
      70                 :          0 :                 sseq + (count << COOKIEBITS) +
      71                 :          0 :                 ((cookie_hash(saddr, daddr, sport, dport, count, 1) + data)
      72                 :          0 :                 & COOKIEMASK));
      73                 :            : }
      74                 :            : 
      75                 :          0 : static __u32 check_tcp_syn_cookie(__u32 cookie, const struct in6_addr *saddr,
      76                 :            :                                   const struct in6_addr *daddr, __be16 sport,
      77                 :            :                                   __be16 dport, __u32 sseq)
      78                 :            : {
      79                 :          0 :         __u32 diff, count = tcp_cookie_time();
      80                 :            : 
      81                 :          0 :         cookie -= cookie_hash(saddr, daddr, sport, dport, 0, 0) + sseq;
      82                 :            : 
      83                 :          0 :         diff = (count - (cookie >> COOKIEBITS)) & ((__u32) -1 >> COOKIEBITS);
      84         [ #  # ]:          0 :         if (diff >= MAX_SYNCOOKIE_AGE)
      85                 :            :                 return (__u32)-1;
      86                 :            : 
      87                 :          0 :         return (cookie -
      88                 :          0 :                 cookie_hash(saddr, daddr, sport, dport, count - diff, 1))
      89                 :            :                 & COOKIEMASK;
      90                 :            : }
      91                 :            : 
      92                 :          0 : u32 __cookie_v6_init_sequence(const struct ipv6hdr *iph,
      93                 :            :                               const struct tcphdr *th, __u16 *mssp)
      94                 :            : {
      95                 :            :         int mssind;
      96                 :          0 :         const __u16 mss = *mssp;
      97                 :            : 
      98         [ #  # ]:          0 :         for (mssind = ARRAY_SIZE(msstab) - 1; mssind ; mssind--)
      99         [ #  # ]:          0 :                 if (mss >= msstab[mssind])
     100                 :            :                         break;
     101                 :            : 
     102                 :          0 :         *mssp = msstab[mssind];
     103                 :            : 
     104                 :          0 :         return secure_tcp_syn_cookie(&iph->saddr, &iph->daddr, th->source,
     105                 :            :                                      th->dest, ntohl(th->seq), mssind);
     106                 :            : }
     107                 :            : EXPORT_SYMBOL_GPL(__cookie_v6_init_sequence);
     108                 :            : 
     109                 :          0 : __u32 cookie_v6_init_sequence(const struct sk_buff *skb, __u16 *mssp)
     110                 :            : {
     111                 :            :         const struct ipv6hdr *iph = ipv6_hdr(skb);
     112                 :            :         const struct tcphdr *th = tcp_hdr(skb);
     113                 :            : 
     114                 :          0 :         return __cookie_v6_init_sequence(iph, th, mssp);
     115                 :            : }
     116                 :            : 
     117                 :          0 : int __cookie_v6_check(const struct ipv6hdr *iph, const struct tcphdr *th,
     118                 :            :                       __u32 cookie)
     119                 :            : {
     120                 :          0 :         __u32 seq = ntohl(th->seq) - 1;
     121                 :          0 :         __u32 mssind = check_tcp_syn_cookie(cookie, &iph->saddr, &iph->daddr,
     122                 :            :                                             th->source, th->dest, seq);
     123                 :            : 
     124         [ #  # ]:          0 :         return mssind < ARRAY_SIZE(msstab) ? msstab[mssind] : 0;
     125                 :            : }
     126                 :            : EXPORT_SYMBOL_GPL(__cookie_v6_check);
     127                 :            : 
     128                 :          0 : struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
     129                 :            : {
     130                 :            :         struct tcp_options_received tcp_opt;
     131                 :            :         struct inet_request_sock *ireq;
     132                 :            :         struct tcp_request_sock *treq;
     133                 :            :         struct ipv6_pinfo *np = inet6_sk(sk);
     134                 :            :         struct tcp_sock *tp = tcp_sk(sk);
     135                 :            :         const struct tcphdr *th = tcp_hdr(skb);
     136                 :          0 :         __u32 cookie = ntohl(th->ack_seq) - 1;
     137                 :            :         struct sock *ret = sk;
     138                 :            :         struct request_sock *req;
     139                 :            :         int mss;
     140                 :            :         struct dst_entry *dst;
     141                 :            :         __u8 rcv_wscale;
     142                 :            :         u32 tsoff = 0;
     143                 :            : 
     144   [ #  #  #  # ]:          0 :         if (!sock_net(sk)->ipv4.sysctl_tcp_syncookies || !th->ack || th->rst)
     145                 :            :                 goto out;
     146                 :            : 
     147         [ #  # ]:          0 :         if (tcp_synq_no_recent_overflow(sk))
     148                 :            :                 goto out;
     149                 :            : 
     150                 :          0 :         mss = __cookie_v6_check(ipv6_hdr(skb), th, cookie);
     151         [ #  # ]:          0 :         if (mss == 0) {
     152                 :          0 :                 __NET_INC_STATS(sock_net(sk), LINUX_MIB_SYNCOOKIESFAILED);
     153                 :            :                 goto out;
     154                 :            :         }
     155                 :            : 
     156                 :          0 :         __NET_INC_STATS(sock_net(sk), LINUX_MIB_SYNCOOKIESRECV);
     157                 :            : 
     158                 :            :         /* check for timestamp cookie support */
     159                 :          0 :         memset(&tcp_opt, 0, sizeof(tcp_opt));
     160                 :          0 :         tcp_parse_options(sock_net(sk), skb, &tcp_opt, 0, NULL);
     161                 :            : 
     162   [ #  #  #  # ]:          0 :         if (tcp_opt.saw_tstamp && tcp_opt.rcv_tsecr) {
     163                 :          0 :                 tsoff = secure_tcpv6_ts_off(sock_net(sk),
     164                 :          0 :                                             ipv6_hdr(skb)->daddr.s6_addr32,
     165                 :          0 :                                             ipv6_hdr(skb)->saddr.s6_addr32);
     166                 :          0 :                 tcp_opt.rcv_tsecr -= tsoff;
     167                 :            :         }
     168                 :            : 
     169         [ #  # ]:          0 :         if (!cookie_timestamp_decode(sock_net(sk), &tcp_opt))
     170                 :            :                 goto out;
     171                 :            : 
     172                 :            :         ret = NULL;
     173                 :          0 :         req = inet_reqsk_alloc(&tcp6_request_sock_ops, sk, false);
     174         [ #  # ]:          0 :         if (!req)
     175                 :            :                 goto out;
     176                 :            : 
     177                 :            :         ireq = inet_rsk(req);
     178                 :            :         treq = tcp_rsk(req);
     179                 :          0 :         treq->tfo_listener = false;
     180                 :            : 
     181         [ #  # ]:          0 :         if (security_inet_conn_request(sk, skb, req))
     182                 :            :                 goto out_free;
     183                 :            : 
     184                 :          0 :         req->mss = mss;
     185                 :          0 :         ireq->ir_rmt_port = th->source;
     186                 :          0 :         ireq->ir_num = ntohs(th->dest);
     187                 :          0 :         ireq->ir_v6_rmt_addr = ipv6_hdr(skb)->saddr;
     188                 :          0 :         ireq->ir_v6_loc_addr = ipv6_hdr(skb)->daddr;
     189         [ #  # ]:          0 :         if (ipv6_opt_accepted(sk, skb, &TCP_SKB_CB(skb)->header.h6) ||
     190                 :            :             np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo ||
     191         [ #  # ]:          0 :             np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim) {
     192                 :          0 :                 refcount_inc(&skb->users);
     193                 :          0 :                 ireq->pktopts = skb;
     194                 :            :         }
     195                 :            : 
     196                 :          0 :         ireq->ir_iif = inet_request_bound_dev_if(sk, skb);
     197                 :            :         /* So that link locals have meaning */
     198   [ #  #  #  # ]:          0 :         if (!sk->sk_bound_dev_if &&
     199                 :          0 :             ipv6_addr_type(&ireq->ir_v6_rmt_addr) & IPV6_ADDR_LINKLOCAL)
     200                 :          0 :                 ireq->ir_iif = tcp_v6_iif(skb);
     201                 :            : 
     202                 :          0 :         ireq->ir_mark = inet_request_mark(sk, skb);
     203                 :            : 
     204                 :          0 :         req->num_retrans = 0;
     205                 :          0 :         ireq->snd_wscale     = tcp_opt.snd_wscale;
     206                 :          0 :         ireq->sack_ok                = tcp_opt.sack_ok;
     207                 :          0 :         ireq->wscale_ok              = tcp_opt.wscale_ok;
     208                 :          0 :         ireq->tstamp_ok              = tcp_opt.saw_tstamp;
     209         [ #  # ]:          0 :         req->ts_recent               = tcp_opt.saw_tstamp ? tcp_opt.rcv_tsval : 0;
     210                 :          0 :         treq->snt_synack     = 0;
     211                 :          0 :         treq->rcv_isn = ntohl(th->seq) - 1;
     212                 :          0 :         treq->snt_isn = cookie;
     213                 :          0 :         treq->ts_off = 0;
     214                 :          0 :         treq->txhash = net_tx_rndhash();
     215                 :            :         if (IS_ENABLED(CONFIG_SMC))
     216                 :            :                 ireq->smc_ok = 0;
     217                 :            : 
     218                 :            :         /*
     219                 :            :          * We need to lookup the dst_entry to get the correct window size.
     220                 :            :          * This is taken from tcp_v6_syn_recv_sock.  Somebody please enlighten
     221                 :            :          * me if there is a preferred way.
     222                 :            :          */
     223                 :            :         {
     224                 :            :                 struct in6_addr *final_p, final;
     225                 :            :                 struct flowi6 fl6;
     226                 :          0 :                 memset(&fl6, 0, sizeof(fl6));
     227                 :          0 :                 fl6.flowi6_proto = IPPROTO_TCP;
     228                 :          0 :                 fl6.daddr = ireq->ir_v6_rmt_addr;
     229                 :          0 :                 final_p = fl6_update_dst(&fl6, rcu_dereference(np->opt), &final);
     230                 :          0 :                 fl6.saddr = ireq->ir_v6_loc_addr;
     231                 :          0 :                 fl6.flowi6_oif = ireq->ir_iif;
     232                 :          0 :                 fl6.flowi6_mark = ireq->ir_mark;
     233                 :          0 :                 fl6.fl6_dport = ireq->ir_rmt_port;
     234                 :          0 :                 fl6.fl6_sport = inet_sk(sk)->inet_sport;
     235                 :          0 :                 fl6.flowi6_uid = sk->sk_uid;
     236                 :          0 :                 security_req_classify_flow(req, flowi6_to_flowi(&fl6));
     237                 :            : 
     238                 :          0 :                 dst = ip6_dst_lookup_flow(sock_net(sk), sk, &fl6, final_p);
     239         [ #  # ]:          0 :                 if (IS_ERR(dst))
     240                 :            :                         goto out_free;
     241                 :            :         }
     242                 :            : 
     243         [ #  # ]:          0 :         req->rsk_window_clamp = tp->window_clamp ? :dst_metric(dst, RTAX_WINDOW);
     244                 :          0 :         tcp_select_initial_window(sk, tcp_full_space(sk), req->mss,
     245                 :          0 :                                   &req->rsk_rcv_wnd, &req->rsk_window_clamp,
     246                 :          0 :                                   ireq->wscale_ok, &rcv_wscale,
     247                 :            :                                   dst_metric(dst, RTAX_INITRWND));
     248                 :            : 
     249                 :          0 :         ireq->rcv_wscale = rcv_wscale;
     250                 :          0 :         ireq->ecn_ok = cookie_ecn_ok(&tcp_opt, sock_net(sk), dst);
     251                 :            : 
     252                 :          0 :         ret = tcp_get_cookie_sock(sk, skb, req, dst, tsoff);
     253                 :            : out:
     254                 :          0 :         return ret;
     255                 :            : out_free:
     256                 :          0 :         reqsk_free(req);
     257                 :          0 :         return NULL;
     258                 :            : }

Generated by: LCOV version 1.14