summaryrefslogtreecommitdiff
path: root/net/ipv6/tcp_ao.c
blob: d08735b6f3c5b00b6e96a51cf048b769b9150d5a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * INET		An implementation of the TCP Authentication Option (TCP-AO).
 *		See RFC5925.
 *
 * Authors:	Dmitry Safonov <dima@arista.com>
 *		Francesco Ruggeri <fruggeri@arista.com>
 *		Salam Noureddine <noureddine@arista.com>
 */
#include <crypto/hash.h>
#include <linux/tcp.h>

#include <net/tcp.h>
#include <net/ipv6.h>

static int tcp_v6_ao_calc_key(struct tcp_ao_key *mkt, u8 *key,
			      const struct in6_addr *saddr,
			      const struct in6_addr *daddr,
			      __be16 sport, __be16 dport,
			      __be32 sisn, __be32 disn)
{
	struct kdf_input_block {
		u8			counter;
		u8			label[6];
		struct tcp6_ao_context	ctx;
		__be16			outlen;
	} __packed * tmp;
	struct tcp_sigpool hp;
	int err;

	err = tcp_sigpool_start(mkt->tcp_sigpool_id, &hp);
	if (err)
		return err;

	tmp = hp.scratch;
	tmp->counter	= 1;
	memcpy(tmp->label, "TCP-AO", 6);
	tmp->ctx.saddr	= *saddr;
	tmp->ctx.daddr	= *daddr;
	tmp->ctx.sport	= sport;
	tmp->ctx.dport	= dport;
	tmp->ctx.sisn	= sisn;
	tmp->ctx.disn	= disn;
	tmp->outlen	= htons(tcp_ao_digest_size(mkt) * 8); /* in bits */

	err = tcp_ao_calc_traffic_key(mkt, key, tmp, sizeof(*tmp), &hp);
	tcp_sigpool_end(&hp);

	return err;
}

int tcp_v6_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
			  const struct sock *sk, __be32 sisn,
			  __be32 disn, bool send)
{
	if (send)
		return tcp_v6_ao_calc_key(mkt, key, &sk->sk_v6_rcv_saddr,
					  &sk->sk_v6_daddr, htons(sk->sk_num),
					  sk->sk_dport, sisn, disn);
	else
		return tcp_v6_ao_calc_key(mkt, key, &sk->sk_v6_daddr,
					  &sk->sk_v6_rcv_saddr, sk->sk_dport,
					  htons(sk->sk_num), disn, sisn);
}

static struct tcp_ao_key *tcp_v6_ao_do_lookup(const struct sock *sk,
					      const struct in6_addr *addr,
					      int sndid, int rcvid)
{
	return tcp_ao_do_lookup(sk, (union tcp_ao_addr *)addr, AF_INET6,
				sndid, rcvid);
}

struct tcp_ao_key *tcp_v6_ao_lookup(const struct sock *sk,
				    struct sock *addr_sk,
				    int sndid, int rcvid)
{
	struct in6_addr *addr = &addr_sk->sk_v6_daddr;

	return tcp_v6_ao_do_lookup(sk, addr, sndid, rcvid);
}

int tcp_v6_ao_hash_pseudoheader(struct tcp_sigpool *hp,
				const struct in6_addr *daddr,
				const struct in6_addr *saddr, int nbytes)
{
	struct tcp6_pseudohdr *bp;
	struct scatterlist sg;

	bp = hp->scratch;
	/* 1. TCP pseudo-header (RFC2460) */
	bp->saddr = *saddr;
	bp->daddr = *daddr;
	bp->len = cpu_to_be32(nbytes);
	bp->protocol = cpu_to_be32(IPPROTO_TCP);

	sg_init_one(&sg, bp, sizeof(*bp));
	ahash_request_set_crypt(hp->req, &sg, NULL, sizeof(*bp));
	return crypto_ahash_update(hp->req);
}

int tcp_v6_ao_hash_skb(char *ao_hash, struct tcp_ao_key *key,
		       const struct sock *sk, const struct sk_buff *skb,
		       const u8 *tkey, int hash_offset, u32 sne)
{
	return tcp_ao_hash_skb(AF_INET6, ao_hash, key, sk, skb, tkey,
			hash_offset, sne);
}

int tcp_v6_parse_ao(struct sock *sk, int cmd,
		    sockptr_t optval, int optlen)
{
	return tcp_parse_ao(sk, cmd, AF_INET6, optval, optlen);
}