lkml.org 
[lkml]   [2023]   [Feb]   [15]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH v4 07/21] net/tcp: Add tcp_parse_auth_options()
    Date
    Introduce a helper that:
    (1) shares the common code with TCP-MD5 header options parsing
    (2) looks for hash signature only once for both TCP-MD5 and TCP-AO
    (3) fails with -EEXIST if any TCP sign option is present twice, see
    RFC5925 (2.2):
    ">> A single TCP segment MUST NOT have more than one TCP-AO in its
    options sequence. When multiple TCP-AOs appear, TCP MUST discard
    the segment."

    Co-developed-by: Francesco Ruggeri <fruggeri@arista.com>
    Signed-off-by: Francesco Ruggeri <fruggeri@arista.com>
    Co-developed-by: Salam Noureddine <noureddine@arista.com>
    Signed-off-by: Salam Noureddine <noureddine@arista.com>
    Signed-off-by: Dmitry Safonov <dima@arista.com>
    ---
    include/net/tcp.h | 24 +++++++++++++++++++++++-
    include/net/tcp_ao.h | 17 ++++++++++++++++-
    net/ipv4/tcp.c | 3 ++-
    net/ipv4/tcp_input.c | 39 +++++++++++++++++++++++++++++----------
    net/ipv4/tcp_ipv4.c | 15 ++++++++++-----
    net/ipv6/tcp_ipv6.c | 11 +++++++----
    6 files changed, 87 insertions(+), 22 deletions(-)

    diff --git a/include/net/tcp.h b/include/net/tcp.h
    index fa26e18528a1..2bd9456572f7 100644
    --- a/include/net/tcp.h
    +++ b/include/net/tcp.h
    @@ -434,7 +434,6 @@ int tcp_mmap(struct file *file, struct socket *sock,
    void tcp_parse_options(const struct net *net, const struct sk_buff *skb,
    struct tcp_options_received *opt_rx,
    int estab, struct tcp_fastopen_cookie *foc);
    -const u8 *tcp_parse_md5sig_option(const struct tcphdr *th);

    /*
    * BPF SKB-less helpers
    @@ -2533,4 +2532,27 @@ static inline u64 tcp_transmit_time(const struct sock *sk)
    return 0;
    }

    +static inline int tcp_parse_auth_options(const struct tcphdr *th,
    + const u8 **md5_hash, const struct tcp_ao_hdr **aoh)
    +{
    + const u8 *md5_tmp, *ao_tmp;
    + int ret;
    +
    + ret = tcp_do_parse_auth_options(th, &md5_tmp, &ao_tmp);
    + if (ret)
    + return ret;
    +
    + if (md5_hash)
    + *md5_hash = md5_tmp;
    +
    + if (aoh) {
    + if (!ao_tmp)
    + *aoh = NULL;
    + else
    + *aoh = (struct tcp_ao_hdr *)(ao_tmp - 2);
    + }
    +
    + return 0;
    +}
    +
    #endif /* _TCP_H */
    diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h
    index e233e7ff3184..e600064379ae 100644
    --- a/include/net/tcp_ao.h
    +++ b/include/net/tcp_ao.h
    @@ -151,7 +151,9 @@ int tcp_v6_parse_ao(struct sock *sk, int cmd,
    sockptr_t optval, int optlen);
    void tcp_ao_finish_connect(struct sock *sk, struct sk_buff *skb);
    void tcp_ao_connect_init(struct sock *sk);
    -
    +void tcp_ao_syncookie(struct sock *sk, const struct sk_buff *skb,
    + struct tcp_request_sock *treq,
    + unsigned short int family);
    #else /* CONFIG_TCP_AO */

    static inline struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk,
    @@ -174,4 +176,17 @@ static inline void tcp_ao_connect_init(struct sock *sk)
    }
    #endif

    +#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO)
    +int tcp_do_parse_auth_options(const struct tcphdr *th,
    + const u8 **md5_hash, const u8 **ao_hash);
    +#else
    +static int tcp_do_parse_auth_options(const struct tcphdr *th,
    + const u8 **md5_hash, const u8 **ao_hash)
    +{
    + *md5_hash = NULL;
    + *ao_hash = NULL;
    + return 0;
    +}
    +#endif
    +
    #endif /* _TCP_AO_H */
    diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
    index 6d66719df5a2..e4136e87a162 100644
    --- a/net/ipv4/tcp.c
    +++ b/net/ipv4/tcp.c
    @@ -4540,7 +4540,8 @@ tcp_inbound_md5_hash(const struct sock *sk, const struct sk_buff *skb,
    l3index = sdif ? dif : 0;

    hash_expected = tcp_md5_do_lookup(sk, l3index, saddr, family);
    - hash_location = tcp_parse_md5sig_option(th);
    + if (tcp_parse_auth_options(th, &hash_location, NULL))
    + return true;

    /* We've parsed the options - do we have a hash? */
    if (!hash_expected && !hash_location)
    diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
    index 6baeb3fe4352..78c2f238205f 100644
    --- a/net/ipv4/tcp_input.c
    +++ b/net/ipv4/tcp_input.c
    @@ -4202,39 +4202,58 @@ static bool tcp_fast_parse_options(const struct net *net,
    return true;
    }

    -#ifdef CONFIG_TCP_MD5SIG
    +#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO)
    /*
    - * Parse MD5 Signature option
    + * Parse Signature options
    */
    -const u8 *tcp_parse_md5sig_option(const struct tcphdr *th)
    +int tcp_do_parse_auth_options(const struct tcphdr *th,
    + const u8 **md5_hash, const u8 **ao_hash)
    {
    int length = (th->doff << 2) - sizeof(*th);
    const u8 *ptr = (const u8 *)(th + 1);
    + unsigned int minlen = TCPOLEN_MD5SIG;
    +
    + if (IS_ENABLED(CONFIG_TCP_AO))
    + minlen = sizeof(struct tcp_ao_hdr) + 1;
    +
    + *md5_hash = NULL;
    + *ao_hash = NULL;

    /* If not enough data remaining, we can short cut */
    - while (length >= TCPOLEN_MD5SIG) {
    + while (length >= minlen) {
    int opcode = *ptr++;
    int opsize;

    switch (opcode) {
    case TCPOPT_EOL:
    - return NULL;
    + return 0;
    case TCPOPT_NOP:
    length--;
    continue;
    default:
    opsize = *ptr++;
    if (opsize < 2 || opsize > length)
    - return NULL;
    - if (opcode == TCPOPT_MD5SIG)
    - return opsize == TCPOLEN_MD5SIG ? ptr : NULL;
    + return -EINVAL;
    + if (opcode == TCPOPT_MD5SIG) {
    + if (opsize != TCPOLEN_MD5SIG)
    + return -EINVAL;
    + if (unlikely(*md5_hash || *ao_hash))
    + return -EEXIST;
    + *md5_hash = ptr;
    + } else if (opcode == TCPOPT_AO) {
    + if (opsize <= sizeof(struct tcp_ao_hdr))
    + return -EINVAL;
    + if (unlikely(*md5_hash || *ao_hash))
    + return -EEXIST;
    + *ao_hash = ptr;
    + }
    }
    ptr += opsize - 2;
    length -= opsize;
    }
    - return NULL;
    + return 0;
    }
    -EXPORT_SYMBOL(tcp_parse_md5sig_option);
    +EXPORT_SYMBOL(tcp_do_parse_auth_options);
    #endif

    /* Sorry, PAWS as specified is broken wrt. pure-ACKs -DaveM
    diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
    index e999e00a5398..bda371f8c055 100644
    --- a/net/ipv4/tcp_ipv4.c
    +++ b/net/ipv4/tcp_ipv4.c
    @@ -668,7 +668,9 @@ EXPORT_SYMBOL(tcp_v4_send_check);
    * Exception: precedence violation. We do not implement it in any case.
    */

    -#ifdef CONFIG_TCP_MD5SIG
    +#ifdef CONFIG_TCP_AO
    +#define OPTION_BYTES MAX_TCP_OPTION_SPACE
    +#elif defined(CONFIG_TCP_MD5SIG)
    #define OPTION_BYTES TCPOLEN_MD5SIG_ALIGNED
    #else
    #define OPTION_BYTES sizeof(__be32)
    @@ -684,7 +686,7 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb)
    struct ip_reply_arg arg;
    #ifdef CONFIG_TCP_MD5SIG
    struct tcp_md5sig_key *key = NULL;
    - const __u8 *hash_location = NULL;
    + const __u8 *md5_hash_location = NULL;
    unsigned char newhash[16];
    int genhash;
    struct sock *sk1 = NULL;
    @@ -724,8 +726,11 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb)

    net = sk ? sock_net(sk) : dev_net(skb_dst(skb)->dev);
    #ifdef CONFIG_TCP_MD5SIG
    + /* Invalid TCP option size or twice included auth */
    + if (tcp_parse_auth_options(tcp_hdr(skb), &md5_hash_location, NULL))
    + return;
    +
    rcu_read_lock();
    - hash_location = tcp_parse_md5sig_option(th);
    if (sk && sk_fullsock(sk)) {
    const union tcp_md5_addr *addr;
    int l3index;
    @@ -736,7 +741,7 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb)
    l3index = tcp_v4_sdif(skb) ? inet_iif(skb) : 0;
    addr = (union tcp_md5_addr *)&ip_hdr(skb)->saddr;
    key = tcp_md5_do_lookup(sk, l3index, addr, AF_INET);
    - } else if (hash_location) {
    + } else if (md5_hash_location) {
    const union tcp_md5_addr *addr;
    int sdif = tcp_v4_sdif(skb);
    int dif = inet_iif(skb);
    @@ -768,7 +773,7 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb)


    genhash = tcp_v4_md5_hash_skb(newhash, key, NULL, skb);
    - if (genhash || memcmp(hash_location, newhash, 16) != 0)
    + if (genhash || memcmp(md5_hash_location, newhash, 16) != 0)
    goto out;

    }
    diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
    index 50d394d9d5fc..2970c195c917 100644
    --- a/net/ipv6/tcp_ipv6.c
    +++ b/net/ipv6/tcp_ipv6.c
    @@ -987,7 +987,7 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb)
    u32 seq = 0, ack_seq = 0;
    struct tcp_md5sig_key *key = NULL;
    #ifdef CONFIG_TCP_MD5SIG
    - const __u8 *hash_location = NULL;
    + const __u8 *md5_hash_location = NULL;
    unsigned char newhash[16];
    int genhash;
    struct sock *sk1 = NULL;
    @@ -1009,8 +1009,11 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb)

    net = sk ? sock_net(sk) : dev_net(skb_dst(skb)->dev);
    #ifdef CONFIG_TCP_MD5SIG
    + /* Invalid TCP option size or twice included auth */
    + if (tcp_parse_auth_options(th, &md5_hash_location, NULL))
    + return;
    +
    rcu_read_lock();
    - hash_location = tcp_parse_md5sig_option(th);
    if (sk && sk_fullsock(sk)) {
    int l3index;

    @@ -1019,7 +1022,7 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb)
    */
    l3index = tcp_v6_sdif(skb) ? tcp_v6_iif_l3_slave(skb) : 0;
    key = tcp_v6_md5_do_lookup(sk, &ipv6h->saddr, l3index);
    - } else if (hash_location) {
    + } else if (md5_hash_location) {
    int dif = tcp_v6_iif_l3_slave(skb);
    int sdif = tcp_v6_sdif(skb);
    int l3index;
    @@ -1048,7 +1051,7 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb)
    goto out;

    genhash = tcp_v6_md5_hash_skb(newhash, key, NULL, skb);
    - if (genhash || memcmp(hash_location, newhash, 16) != 0)
    + if (genhash || memcmp(md5_hash_location, newhash, 16) != 0)
    goto out;
    }
    #endif
    --
    2.39.1
    \
     
     \ /
      Last update: 2023-03-27 00:24    [W:4.318 / U:0.104 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site