mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	net/tcp: Verify inbound TCP-AO signed segments
Now there is a common function to verify signature on TCP segments:
tcp_inbound_hash(). It has checks for all possible cross-interactions
with MD5 signs as well as with unsigned segments.
The rules from RFC5925 are:
(1) Any TCP segment can have at max only one signature.
(2) TCP connections can't switch between using TCP-MD5 and TCP-AO.
(3) TCP-AO connections can't stop using AO, as well as unsigned
    connections can't suddenly start using AO.
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>
Acked-by: David Ahern <dsahern@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
			
			
This commit is contained in:
		
							parent
							
								
									9427c6aa3e
								
							
						
					
					
						commit
						0a3a809089
					
				
					 8 changed files with 248 additions and 47 deletions
				
			
		| 
						 | 
					@ -24,6 +24,10 @@
 | 
				
			||||||
	FN(TCP_MD5NOTFOUND)		\
 | 
						FN(TCP_MD5NOTFOUND)		\
 | 
				
			||||||
	FN(TCP_MD5UNEXPECTED)		\
 | 
						FN(TCP_MD5UNEXPECTED)		\
 | 
				
			||||||
	FN(TCP_MD5FAILURE)		\
 | 
						FN(TCP_MD5FAILURE)		\
 | 
				
			||||||
 | 
						FN(TCP_AONOTFOUND)		\
 | 
				
			||||||
 | 
						FN(TCP_AOUNEXPECTED)		\
 | 
				
			||||||
 | 
						FN(TCP_AOKEYNOTFOUND)		\
 | 
				
			||||||
 | 
						FN(TCP_AOFAILURE)		\
 | 
				
			||||||
	FN(SOCKET_BACKLOG)		\
 | 
						FN(SOCKET_BACKLOG)		\
 | 
				
			||||||
	FN(TCP_FLAGS)			\
 | 
						FN(TCP_FLAGS)			\
 | 
				
			||||||
	FN(TCP_ZEROWINDOW)		\
 | 
						FN(TCP_ZEROWINDOW)		\
 | 
				
			||||||
| 
						 | 
					@ -163,6 +167,19 @@ enum skb_drop_reason {
 | 
				
			||||||
	 * to LINUX_MIB_TCPMD5FAILURE
 | 
						 * to LINUX_MIB_TCPMD5FAILURE
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	SKB_DROP_REASON_TCP_MD5FAILURE,
 | 
						SKB_DROP_REASON_TCP_MD5FAILURE,
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * @SKB_DROP_REASON_TCP_AONOTFOUND: no TCP-AO hash and one was expected
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						SKB_DROP_REASON_TCP_AONOTFOUND,
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * @SKB_DROP_REASON_TCP_AOUNEXPECTED: TCP-AO hash is present and it
 | 
				
			||||||
 | 
						 * was not expected.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						SKB_DROP_REASON_TCP_AOUNEXPECTED,
 | 
				
			||||||
 | 
						/** @SKB_DROP_REASON_TCP_AOKEYNOTFOUND: TCP-AO key is unknown */
 | 
				
			||||||
 | 
						SKB_DROP_REASON_TCP_AOKEYNOTFOUND,
 | 
				
			||||||
 | 
						/** @SKB_DROP_REASON_TCP_AOFAILURE: TCP-AO hash is wrong */
 | 
				
			||||||
 | 
						SKB_DROP_REASON_TCP_AOFAILURE,
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * @SKB_DROP_REASON_SOCKET_BACKLOG: failed to add skb to socket backlog (
 | 
						 * @SKB_DROP_REASON_SOCKET_BACKLOG: failed to add skb to socket backlog (
 | 
				
			||||||
	 * see LINUX_MIB_TCPBACKLOGDROP)
 | 
						 * see LINUX_MIB_TCPBACKLOGDROP)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1809,7 +1809,7 @@ tcp_md5_do_lookup_any_l3index(const struct sock *sk,
 | 
				
			||||||
enum skb_drop_reason
 | 
					enum skb_drop_reason
 | 
				
			||||||
tcp_inbound_md5_hash(const struct sock *sk, const struct sk_buff *skb,
 | 
					tcp_inbound_md5_hash(const struct sock *sk, const struct sk_buff *skb,
 | 
				
			||||||
		     const void *saddr, const void *daddr,
 | 
							     const void *saddr, const void *daddr,
 | 
				
			||||||
		     int family, int dif, int sdif);
 | 
							     int family, int l3index, const __u8 *hash_location);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define tcp_twsk_md5_key(twsk)	((twsk)->tw_md5_key)
 | 
					#define tcp_twsk_md5_key(twsk)	((twsk)->tw_md5_key)
 | 
				
			||||||
| 
						 | 
					@ -1831,7 +1831,7 @@ tcp_md5_do_lookup_any_l3index(const struct sock *sk,
 | 
				
			||||||
static inline enum skb_drop_reason
 | 
					static inline enum skb_drop_reason
 | 
				
			||||||
tcp_inbound_md5_hash(const struct sock *sk, const struct sk_buff *skb,
 | 
					tcp_inbound_md5_hash(const struct sock *sk, const struct sk_buff *skb,
 | 
				
			||||||
		     const void *saddr, const void *daddr,
 | 
							     const void *saddr, const void *daddr,
 | 
				
			||||||
		     int family, int dif, int sdif)
 | 
							     int family, int l3index, const __u8 *hash_location)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	return SKB_NOT_DROPPED_YET;
 | 
						return SKB_NOT_DROPPED_YET;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -2730,4 +2730,53 @@ static inline bool tcp_ao_required(struct sock *sk, const void *saddr,
 | 
				
			||||||
	return false;
 | 
						return false;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Called with rcu_read_lock() */
 | 
				
			||||||
 | 
					static inline enum skb_drop_reason
 | 
				
			||||||
 | 
					tcp_inbound_hash(struct sock *sk, const struct request_sock *req,
 | 
				
			||||||
 | 
							 const struct sk_buff *skb,
 | 
				
			||||||
 | 
							 const void *saddr, const void *daddr,
 | 
				
			||||||
 | 
							 int family, int dif, int sdif)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const struct tcphdr *th = tcp_hdr(skb);
 | 
				
			||||||
 | 
						const struct tcp_ao_hdr *aoh;
 | 
				
			||||||
 | 
						const __u8 *md5_location;
 | 
				
			||||||
 | 
						int l3index;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Invalid option or two times meet any of auth options */
 | 
				
			||||||
 | 
						if (tcp_parse_auth_options(th, &md5_location, &aoh))
 | 
				
			||||||
 | 
							return SKB_DROP_REASON_TCP_AUTH_HDR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (req) {
 | 
				
			||||||
 | 
							if (tcp_rsk_used_ao(req) != !!aoh)
 | 
				
			||||||
 | 
								return SKB_DROP_REASON_TCP_AOFAILURE;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* sdif set, means packet ingressed via a device
 | 
				
			||||||
 | 
						 * in an L3 domain and dif is set to the l3mdev
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						l3index = sdif ? dif : 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Fast path: unsigned segments */
 | 
				
			||||||
 | 
						if (likely(!md5_location && !aoh)) {
 | 
				
			||||||
 | 
							/* Drop if there's TCP-MD5 or TCP-AO key with any rcvid/sndid
 | 
				
			||||||
 | 
							 * for the remote peer. On TCP-AO established connection
 | 
				
			||||||
 | 
							 * the last key is impossible to remove, so there's
 | 
				
			||||||
 | 
							 * always at least one current_key.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							if (tcp_ao_required(sk, saddr, family))
 | 
				
			||||||
 | 
								return SKB_DROP_REASON_TCP_AONOTFOUND;
 | 
				
			||||||
 | 
							if (unlikely(tcp_md5_do_lookup(sk, l3index, saddr, family))) {
 | 
				
			||||||
 | 
								NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMD5NOTFOUND);
 | 
				
			||||||
 | 
								return SKB_DROP_REASON_TCP_MD5NOTFOUND;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return SKB_NOT_DROPPED_YET;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (aoh)
 | 
				
			||||||
 | 
							return tcp_inbound_ao_hash(sk, skb, family, req, aoh);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return tcp_inbound_md5_hash(sk, skb, saddr, daddr, family,
 | 
				
			||||||
 | 
									    l3index, md5_location);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif	/* _TCP_H */
 | 
					#endif	/* _TCP_H */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -111,6 +111,9 @@ struct tcp6_ao_context {
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct tcp_sigpool;
 | 
					struct tcp_sigpool;
 | 
				
			||||||
 | 
					#define TCP_AO_ESTABLISHED (TCPF_ESTABLISHED | TCPF_FIN_WAIT1 | TCPF_FIN_WAIT2 | \
 | 
				
			||||||
 | 
								    TCPF_CLOSE | TCPF_CLOSE_WAIT | \
 | 
				
			||||||
 | 
								    TCPF_LAST_ACK | TCPF_CLOSING)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb,
 | 
					int tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb,
 | 
				
			||||||
			struct tcp_ao_key *key, struct tcphdr *th,
 | 
								struct tcp_ao_key *key, struct tcphdr *th,
 | 
				
			||||||
| 
						 | 
					@ -130,6 +133,10 @@ int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx,
 | 
				
			||||||
			    unsigned int len, struct tcp_sigpool *hp);
 | 
								    unsigned int len, struct tcp_sigpool *hp);
 | 
				
			||||||
void tcp_ao_destroy_sock(struct sock *sk, bool twsk);
 | 
					void tcp_ao_destroy_sock(struct sock *sk, bool twsk);
 | 
				
			||||||
void tcp_ao_time_wait(struct tcp_timewait_sock *tcptw, struct tcp_sock *tp);
 | 
					void tcp_ao_time_wait(struct tcp_timewait_sock *tcptw, struct tcp_sock *tp);
 | 
				
			||||||
 | 
					enum skb_drop_reason tcp_inbound_ao_hash(struct sock *sk,
 | 
				
			||||||
 | 
								const struct sk_buff *skb, unsigned short int family,
 | 
				
			||||||
 | 
								const struct request_sock *req,
 | 
				
			||||||
 | 
								const struct tcp_ao_hdr *aoh);
 | 
				
			||||||
struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk,
 | 
					struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk,
 | 
				
			||||||
				    const union tcp_ao_addr *addr,
 | 
									    const union tcp_ao_addr *addr,
 | 
				
			||||||
				    int family, int sndid, int rcvid);
 | 
									    int family, int sndid, int rcvid);
 | 
				
			||||||
| 
						 | 
					@ -208,6 +215,13 @@ static inline void tcp_ao_syncookie(struct sock *sk, const struct sk_buff *skb,
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline enum skb_drop_reason tcp_inbound_ao_hash(struct sock *sk,
 | 
				
			||||||
 | 
							const struct sk_buff *skb, unsigned short int family,
 | 
				
			||||||
 | 
							const struct request_sock *req, const struct tcp_ao_hdr *aoh)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return SKB_NOT_DROPPED_YET;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk,
 | 
					static inline struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk,
 | 
				
			||||||
		const union tcp_ao_addr *addr, int family, int sndid, int rcvid)
 | 
							const union tcp_ao_addr *addr, int family, int sndid, int rcvid)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4375,42 +4375,23 @@ EXPORT_SYMBOL(tcp_md5_hash_key);
 | 
				
			||||||
enum skb_drop_reason
 | 
					enum skb_drop_reason
 | 
				
			||||||
tcp_inbound_md5_hash(const struct sock *sk, const struct sk_buff *skb,
 | 
					tcp_inbound_md5_hash(const struct sock *sk, const struct sk_buff *skb,
 | 
				
			||||||
		     const void *saddr, const void *daddr,
 | 
							     const void *saddr, const void *daddr,
 | 
				
			||||||
		     int family, int dif, int sdif)
 | 
							     int family, int l3index, const __u8 *hash_location)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	/*
 | 
						/* This gets called for each TCP segment that has TCP-MD5 option.
 | 
				
			||||||
	 * This gets called for each TCP segment that arrives
 | 
					 | 
				
			||||||
	 * so we want to be efficient.
 | 
					 | 
				
			||||||
	 * We have 3 drop cases:
 | 
						 * We have 3 drop cases:
 | 
				
			||||||
	 * o No MD5 hash and one expected.
 | 
						 * o No MD5 hash and one expected.
 | 
				
			||||||
	 * o MD5 hash and we're not expecting one.
 | 
						 * o MD5 hash and we're not expecting one.
 | 
				
			||||||
	 * o MD5 hash and its wrong.
 | 
						 * o MD5 hash and its wrong.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	const __u8 *hash_location = NULL;
 | 
					 | 
				
			||||||
	struct tcp_md5sig_key *hash_expected;
 | 
					 | 
				
			||||||
	const struct tcphdr *th = tcp_hdr(skb);
 | 
						const struct tcphdr *th = tcp_hdr(skb);
 | 
				
			||||||
	const struct tcp_sock *tp = tcp_sk(sk);
 | 
						const struct tcp_sock *tp = tcp_sk(sk);
 | 
				
			||||||
	int genhash, l3index;
 | 
						struct tcp_md5sig_key *key;
 | 
				
			||||||
	u8 newhash[16];
 | 
						u8 newhash[16];
 | 
				
			||||||
 | 
						int genhash;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* sdif set, means packet ingressed via a device
 | 
						key = tcp_md5_do_lookup(sk, l3index, saddr, family);
 | 
				
			||||||
	 * in an L3 domain and dif is set to the l3mdev
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	l3index = sdif ? dif : 0;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	hash_expected = tcp_md5_do_lookup(sk, l3index, saddr, family);
 | 
						if (!key && hash_location) {
 | 
				
			||||||
	if (tcp_parse_auth_options(th, &hash_location, NULL))
 | 
					 | 
				
			||||||
		return SKB_DROP_REASON_TCP_AUTH_HDR;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* We've parsed the options - do we have a hash? */
 | 
					 | 
				
			||||||
	if (!hash_expected && !hash_location)
 | 
					 | 
				
			||||||
		return SKB_NOT_DROPPED_YET;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (hash_expected && !hash_location) {
 | 
					 | 
				
			||||||
		NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMD5NOTFOUND);
 | 
					 | 
				
			||||||
		return SKB_DROP_REASON_TCP_MD5NOTFOUND;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!hash_expected && hash_location) {
 | 
					 | 
				
			||||||
		NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMD5UNEXPECTED);
 | 
							NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMD5UNEXPECTED);
 | 
				
			||||||
		return SKB_DROP_REASON_TCP_MD5UNEXPECTED;
 | 
							return SKB_DROP_REASON_TCP_MD5UNEXPECTED;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -4420,14 +4401,10 @@ tcp_inbound_md5_hash(const struct sock *sk, const struct sk_buff *skb,
 | 
				
			||||||
	 * IPv4-mapped case.
 | 
						 * IPv4-mapped case.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	if (family == AF_INET)
 | 
						if (family == AF_INET)
 | 
				
			||||||
		genhash = tcp_v4_md5_hash_skb(newhash,
 | 
							genhash = tcp_v4_md5_hash_skb(newhash, key, NULL, skb);
 | 
				
			||||||
					      hash_expected,
 | 
					 | 
				
			||||||
					      NULL, skb);
 | 
					 | 
				
			||||||
	else
 | 
						else
 | 
				
			||||||
		genhash = tp->af_specific->calc_md5_hash(newhash,
 | 
							genhash = tp->af_specific->calc_md5_hash(newhash, key,
 | 
				
			||||||
							 hash_expected,
 | 
					 | 
				
			||||||
							 NULL, skb);
 | 
												 NULL, skb);
 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (genhash || memcmp(hash_location, newhash, 16) != 0) {
 | 
						if (genhash || memcmp(hash_location, newhash, 16) != 0) {
 | 
				
			||||||
		NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMD5FAILURE);
 | 
							NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMD5FAILURE);
 | 
				
			||||||
		if (family == AF_INET) {
 | 
							if (family == AF_INET) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -761,6 +761,148 @@ void tcp_ao_syncookie(struct sock *sk, const struct sk_buff *skb,
 | 
				
			||||||
	treq->maclen = tcp_ao_maclen(key);
 | 
						treq->maclen = tcp_ao_maclen(key);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static enum skb_drop_reason
 | 
				
			||||||
 | 
					tcp_ao_verify_hash(const struct sock *sk, const struct sk_buff *skb,
 | 
				
			||||||
 | 
							   unsigned short int family, struct tcp_ao_info *info,
 | 
				
			||||||
 | 
							   const struct tcp_ao_hdr *aoh, struct tcp_ao_key *key,
 | 
				
			||||||
 | 
							   u8 *traffic_key, u8 *phash, u32 sne)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						u8 maclen = aoh->length - sizeof(struct tcp_ao_hdr);
 | 
				
			||||||
 | 
						const struct tcphdr *th = tcp_hdr(skb);
 | 
				
			||||||
 | 
						void *hash_buf = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (maclen != tcp_ao_maclen(key))
 | 
				
			||||||
 | 
							return SKB_DROP_REASON_TCP_AOFAILURE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						hash_buf = kmalloc(tcp_ao_digest_size(key), GFP_ATOMIC);
 | 
				
			||||||
 | 
						if (!hash_buf)
 | 
				
			||||||
 | 
							return SKB_DROP_REASON_NOT_SPECIFIED;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* XXX: make it per-AF callback? */
 | 
				
			||||||
 | 
						tcp_ao_hash_skb(family, hash_buf, key, sk, skb, traffic_key,
 | 
				
			||||||
 | 
								(phash - (u8 *)th), sne);
 | 
				
			||||||
 | 
						if (memcmp(phash, hash_buf, maclen)) {
 | 
				
			||||||
 | 
							kfree(hash_buf);
 | 
				
			||||||
 | 
							return SKB_DROP_REASON_TCP_AOFAILURE;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						kfree(hash_buf);
 | 
				
			||||||
 | 
						return SKB_NOT_DROPPED_YET;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum skb_drop_reason
 | 
				
			||||||
 | 
					tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb,
 | 
				
			||||||
 | 
							    unsigned short int family, const struct request_sock *req,
 | 
				
			||||||
 | 
							    const struct tcp_ao_hdr *aoh)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const struct tcphdr *th = tcp_hdr(skb);
 | 
				
			||||||
 | 
						u8 *phash = (u8 *)(aoh + 1); /* hash goes just after the header */
 | 
				
			||||||
 | 
						struct tcp_ao_info *info;
 | 
				
			||||||
 | 
						enum skb_drop_reason ret;
 | 
				
			||||||
 | 
						struct tcp_ao_key *key;
 | 
				
			||||||
 | 
						__be32 sisn, disn;
 | 
				
			||||||
 | 
						u8 *traffic_key;
 | 
				
			||||||
 | 
						u32 sne = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						info = rcu_dereference(tcp_sk(sk)->ao_info);
 | 
				
			||||||
 | 
						if (!info)
 | 
				
			||||||
 | 
							return SKB_DROP_REASON_TCP_AOUNEXPECTED;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (unlikely(th->syn)) {
 | 
				
			||||||
 | 
							sisn = th->seq;
 | 
				
			||||||
 | 
							disn = 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Fast-path */
 | 
				
			||||||
 | 
						if (likely((1 << sk->sk_state) & TCP_AO_ESTABLISHED)) {
 | 
				
			||||||
 | 
							enum skb_drop_reason err;
 | 
				
			||||||
 | 
							struct tcp_ao_key *current_key;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Check if this socket's rnext_key matches the keyid in the
 | 
				
			||||||
 | 
							 * packet. If not we lookup the key based on the keyid
 | 
				
			||||||
 | 
							 * matching the rcvid in the mkt.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							key = READ_ONCE(info->rnext_key);
 | 
				
			||||||
 | 
							if (key->rcvid != aoh->keyid) {
 | 
				
			||||||
 | 
								key = tcp_ao_established_key(info, -1, aoh->keyid);
 | 
				
			||||||
 | 
								if (!key)
 | 
				
			||||||
 | 
									goto key_not_found;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Delayed retransmitted SYN */
 | 
				
			||||||
 | 
							if (unlikely(th->syn && !th->ack))
 | 
				
			||||||
 | 
								goto verify_hash;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							sne = 0;
 | 
				
			||||||
 | 
							/* Established socket, traffic key are cached */
 | 
				
			||||||
 | 
							traffic_key = rcv_other_key(key);
 | 
				
			||||||
 | 
							err = tcp_ao_verify_hash(sk, skb, family, info, aoh, key,
 | 
				
			||||||
 | 
										 traffic_key, phash, sne);
 | 
				
			||||||
 | 
							if (err)
 | 
				
			||||||
 | 
								return err;
 | 
				
			||||||
 | 
							current_key = READ_ONCE(info->current_key);
 | 
				
			||||||
 | 
							/* Key rotation: the peer asks us to use new key (RNext) */
 | 
				
			||||||
 | 
							if (unlikely(aoh->rnext_keyid != current_key->sndid)) {
 | 
				
			||||||
 | 
								/* If the key is not found we do nothing. */
 | 
				
			||||||
 | 
								key = tcp_ao_established_key(info, aoh->rnext_keyid, -1);
 | 
				
			||||||
 | 
								if (key)
 | 
				
			||||||
 | 
									/* pairs with tcp_ao_del_cmd */
 | 
				
			||||||
 | 
									WRITE_ONCE(info->current_key, key);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return SKB_NOT_DROPPED_YET;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Lookup key based on peer address and keyid.
 | 
				
			||||||
 | 
						 * current_key and rnext_key must not be used on tcp listen
 | 
				
			||||||
 | 
						 * sockets as otherwise:
 | 
				
			||||||
 | 
						 * - request sockets would race on those key pointers
 | 
				
			||||||
 | 
						 * - tcp_ao_del_cmd() allows async key removal
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						key = tcp_ao_inbound_lookup(family, sk, skb, -1, aoh->keyid);
 | 
				
			||||||
 | 
						if (!key)
 | 
				
			||||||
 | 
							goto key_not_found;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (th->syn && !th->ack)
 | 
				
			||||||
 | 
							goto verify_hash;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_NEW_SYN_RECV)) {
 | 
				
			||||||
 | 
							/* Make the initial syn the likely case here */
 | 
				
			||||||
 | 
							if (unlikely(req)) {
 | 
				
			||||||
 | 
								sne = 0;
 | 
				
			||||||
 | 
								sisn = htonl(tcp_rsk(req)->rcv_isn);
 | 
				
			||||||
 | 
								disn = htonl(tcp_rsk(req)->snt_isn);
 | 
				
			||||||
 | 
							} else if (unlikely(th->ack && !th->syn)) {
 | 
				
			||||||
 | 
								/* Possible syncookie packet */
 | 
				
			||||||
 | 
								sisn = htonl(ntohl(th->seq) - 1);
 | 
				
			||||||
 | 
								disn = htonl(ntohl(th->ack_seq) - 1);
 | 
				
			||||||
 | 
								sne = 0;
 | 
				
			||||||
 | 
							} else if (unlikely(!th->syn)) {
 | 
				
			||||||
 | 
								/* no way to figure out initial sisn/disn - drop */
 | 
				
			||||||
 | 
								return SKB_DROP_REASON_TCP_FLAGS;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else if ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) {
 | 
				
			||||||
 | 
							disn = info->lisn;
 | 
				
			||||||
 | 
							if (th->syn || th->rst)
 | 
				
			||||||
 | 
								sisn = th->seq;
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								sisn = info->risn;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							WARN_ONCE(1, "TCP-AO: Unexpected sk_state %d", sk->sk_state);
 | 
				
			||||||
 | 
							return SKB_DROP_REASON_TCP_AOFAILURE;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					verify_hash:
 | 
				
			||||||
 | 
						traffic_key = kmalloc(tcp_ao_digest_size(key), GFP_ATOMIC);
 | 
				
			||||||
 | 
						if (!traffic_key)
 | 
				
			||||||
 | 
							return SKB_DROP_REASON_NOT_SPECIFIED;
 | 
				
			||||||
 | 
						tcp_ao_calc_key_skb(key, traffic_key, skb, sisn, disn, family);
 | 
				
			||||||
 | 
						ret = tcp_ao_verify_hash(sk, skb, family, info, aoh, key,
 | 
				
			||||||
 | 
									 traffic_key, phash, sne);
 | 
				
			||||||
 | 
						kfree(traffic_key);
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					key_not_found:
 | 
				
			||||||
 | 
						return SKB_DROP_REASON_TCP_AOKEYNOTFOUND;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int tcp_ao_cache_traffic_keys(const struct sock *sk,
 | 
					static int tcp_ao_cache_traffic_keys(const struct sock *sk,
 | 
				
			||||||
				     struct tcp_ao_info *ao,
 | 
									     struct tcp_ao_info *ao,
 | 
				
			||||||
				     struct tcp_ao_key *ao_key)
 | 
									     struct tcp_ao_key *ao_key)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2204,9 +2204,9 @@ int tcp_v4_rcv(struct sk_buff *skb)
 | 
				
			||||||
		if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb))
 | 
							if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb))
 | 
				
			||||||
			drop_reason = SKB_DROP_REASON_XFRM_POLICY;
 | 
								drop_reason = SKB_DROP_REASON_XFRM_POLICY;
 | 
				
			||||||
		else
 | 
							else
 | 
				
			||||||
			drop_reason = tcp_inbound_md5_hash(sk, skb,
 | 
								drop_reason = tcp_inbound_hash(sk, req, skb,
 | 
				
			||||||
						   &iph->saddr, &iph->daddr,
 | 
											       &iph->saddr, &iph->daddr,
 | 
				
			||||||
						   AF_INET, dif, sdif);
 | 
											       AF_INET, dif, sdif);
 | 
				
			||||||
		if (unlikely(drop_reason)) {
 | 
							if (unlikely(drop_reason)) {
 | 
				
			||||||
			sk_drops_add(sk, skb);
 | 
								sk_drops_add(sk, skb);
 | 
				
			||||||
			reqsk_put(req);
 | 
								reqsk_put(req);
 | 
				
			||||||
| 
						 | 
					@ -2283,8 +2283,8 @@ int tcp_v4_rcv(struct sk_buff *skb)
 | 
				
			||||||
		goto discard_and_relse;
 | 
							goto discard_and_relse;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	drop_reason = tcp_inbound_md5_hash(sk, skb, &iph->saddr,
 | 
						drop_reason = tcp_inbound_hash(sk, NULL, skb, &iph->saddr, &iph->daddr,
 | 
				
			||||||
					   &iph->daddr, AF_INET, dif, sdif);
 | 
									       AF_INET, dif, sdif);
 | 
				
			||||||
	if (drop_reason)
 | 
						if (drop_reason)
 | 
				
			||||||
		goto discard_and_relse;
 | 
							goto discard_and_relse;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -53,11 +53,12 @@ int tcp_v6_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key,
 | 
				
			||||||
			   const struct sk_buff *skb,
 | 
								   const struct sk_buff *skb,
 | 
				
			||||||
			   __be32 sisn, __be32 disn)
 | 
								   __be32 sisn, __be32 disn)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
       const struct ipv6hdr *iph = ipv6_hdr(skb);
 | 
						const struct ipv6hdr *iph = ipv6_hdr(skb);
 | 
				
			||||||
       const struct tcphdr *th = tcp_hdr(skb);
 | 
						const struct tcphdr *th = tcp_hdr(skb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
       return tcp_v6_ao_calc_key(mkt, key, &iph->saddr, &iph->daddr,
 | 
						return tcp_v6_ao_calc_key(mkt, key, &iph->saddr,
 | 
				
			||||||
				 th->source, th->dest, sisn, disn);
 | 
									  &iph->daddr, th->source,
 | 
				
			||||||
 | 
									  th->dest, sisn, disn);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int tcp_v6_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
 | 
					int tcp_v6_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1785,9 +1785,9 @@ INDIRECT_CALLABLE_SCOPE int tcp_v6_rcv(struct sk_buff *skb)
 | 
				
			||||||
		if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb))
 | 
							if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb))
 | 
				
			||||||
			drop_reason = SKB_DROP_REASON_XFRM_POLICY;
 | 
								drop_reason = SKB_DROP_REASON_XFRM_POLICY;
 | 
				
			||||||
		else
 | 
							else
 | 
				
			||||||
			drop_reason = tcp_inbound_md5_hash(sk, skb,
 | 
								drop_reason = tcp_inbound_hash(sk, req, skb,
 | 
				
			||||||
							   &hdr->saddr, &hdr->daddr,
 | 
											       &hdr->saddr, &hdr->daddr,
 | 
				
			||||||
							   AF_INET6, dif, sdif);
 | 
											       AF_INET6, dif, sdif);
 | 
				
			||||||
		if (drop_reason) {
 | 
							if (drop_reason) {
 | 
				
			||||||
			sk_drops_add(sk, skb);
 | 
								sk_drops_add(sk, skb);
 | 
				
			||||||
			reqsk_put(req);
 | 
								reqsk_put(req);
 | 
				
			||||||
| 
						 | 
					@ -1861,8 +1861,8 @@ INDIRECT_CALLABLE_SCOPE int tcp_v6_rcv(struct sk_buff *skb)
 | 
				
			||||||
		goto discard_and_relse;
 | 
							goto discard_and_relse;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	drop_reason = tcp_inbound_md5_hash(sk, skb, &hdr->saddr, &hdr->daddr,
 | 
						drop_reason = tcp_inbound_hash(sk, NULL, skb, &hdr->saddr, &hdr->daddr,
 | 
				
			||||||
					   AF_INET6, dif, sdif);
 | 
									       AF_INET6, dif, sdif);
 | 
				
			||||||
	if (drop_reason)
 | 
						if (drop_reason)
 | 
				
			||||||
		goto discard_and_relse;
 | 
							goto discard_and_relse;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2089,6 +2089,7 @@ static const struct tcp_sock_af_ops tcp_sock_ipv6_mapped_specific = {
 | 
				
			||||||
	.ao_lookup	=	tcp_v6_ao_lookup,
 | 
						.ao_lookup	=	tcp_v6_ao_lookup,
 | 
				
			||||||
	.calc_ao_hash	=	tcp_v4_ao_hash_skb,
 | 
						.calc_ao_hash	=	tcp_v4_ao_hash_skb,
 | 
				
			||||||
	.ao_parse	=	tcp_v6_parse_ao,
 | 
						.ao_parse	=	tcp_v6_parse_ao,
 | 
				
			||||||
 | 
						.ao_calc_key_sk	=	tcp_v4_ao_calc_key_sk,
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue