Commit 91579c93 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'tcp-ao-md5-racepoits'

Dmitry Safonov says:

====================
net/tcp: TCP-AO and TCP-MD5 tracepoints

Changes in v2:
- Fix the build with CONFIG_IPV6=m (Eric Dumazet)
- Move unused keyid/rnext/maclen later in the series to the patch
  that uses them (Simon Horman)
- Reworked tcp_ao selftest lib to allow async tracing non-tcp events
  (was working on a stress-test that needs trace_kfree_skb() event,
   not in this series).
- Separated selftest changes from kernel, as they now have a couple
  of unrelated to tracepoints changes
- Wrote a few lines of Documentation/
- Link to v1: https://lore.kernel.org/r/20240224-tcp-ao-tracepoints-v1-0-15f31b7f30a7@arista.com


====================

Signed-off-by: default avatarDmitry Safonov <0x7f454c46@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents d4cea2ca efe46fb1
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -337,6 +337,15 @@ TCP-AO per-socket counters are also duplicated with per-netns counters,
exposed with SNMP. Those are ``TCPAOGood``, ``TCPAOBad``, ``TCPAOKeyNotFound``,
``TCPAORequired`` and ``TCPAODroppedIcmps``.

For monitoring purposes, there are following TCP-AO trace events:
``tcp_hash_bad_header``, ``tcp_hash_ao_required``, ``tcp_ao_handshake_failure``,
``tcp_ao_wrong_maclen``, ``tcp_ao_wrong_maclen``, ``tcp_ao_key_not_found``,
``tcp_ao_rnext_request``, ``tcp_ao_synack_no_key``, ``tcp_ao_snd_sne_update``,
``tcp_ao_rcv_sne_update``. It's possible to separately enable any of them and
one can filter them by net-namespace, 4-tuple, family, L3 index, and TCP header
flags. If a segment has a TCP-AO header, the filters may also include
keyid, rnext, and maclen. SNE updates include the rolled-over numbers.

RFC 5925 very permissively specifies how TCP port matching can be done for
MKTs::

+8 −84
Original line number Diff line number Diff line
@@ -1863,12 +1863,6 @@ tcp_md5_do_lookup_any_l3index(const struct sock *sk,
	return __tcp_md5_do_lookup(sk, 0, addr, family, true);
}

enum skb_drop_reason
tcp_inbound_md5_hash(const struct sock *sk, const struct sk_buff *skb,
		     const void *saddr, const void *daddr,
		     int family, int l3index, const __u8 *hash_location);


#define tcp_twsk_md5_key(twsk)	((twsk)->tw_md5_key)
#else
static inline struct tcp_md5sig_key *
@@ -1885,13 +1879,6 @@ tcp_md5_do_lookup_any_l3index(const struct sock *sk,
	return NULL;
}

static inline enum skb_drop_reason
tcp_inbound_md5_hash(const struct sock *sk, const struct sk_buff *skb,
		     const void *saddr, const void *daddr,
		     int family, int l3index, const __u8 *hash_location)
{
	return SKB_NOT_DROPPED_YET;
}
#define tcp_twsk_md5_key(twsk)	NULL
#endif

@@ -2386,21 +2373,15 @@ static inline void tcp_get_current_key(const struct sock *sk,

static inline bool tcp_key_is_md5(const struct tcp_key *key)
{
#ifdef CONFIG_TCP_MD5SIG
	if (static_branch_unlikely(&tcp_md5_needed.key) &&
	    key->type == TCP_KEY_MD5)
		return true;
#endif
	if (static_branch_tcp_md5())
		return key->type == TCP_KEY_MD5;
	return false;
}

static inline bool tcp_key_is_ao(const struct tcp_key *key)
{
#ifdef CONFIG_TCP_AO
	if (static_branch_unlikely(&tcp_ao_needed.key) &&
	    key->type == TCP_KEY_AO)
		return true;
#endif
	if (static_branch_tcp_ao())
		return key->type == TCP_KEY_AO;
	return false;
}

@@ -2812,66 +2793,9 @@ static inline bool tcp_ao_required(struct sock *sk, const void *saddr,
	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,
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)) {
		tcp_hash_fail("TCP segment has incorrect auth options set",
			      family, skb, "");
		return SKB_DROP_REASON_TCP_AUTH_HDR;
	}

	if (req) {
		if (tcp_rsk_used_ao(req) != !!aoh) {
			NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOBAD);
			tcp_hash_fail("TCP connection can't start/end using TCP-AO",
				      family, skb, "%s",
				      !aoh ? "missing AO" : "AO signed");
			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, l3index, true)) {
			tcp_hash_fail("AO hash is required, but not found",
					family, skb, "L3 index %d", l3index);
			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);
			tcp_hash_fail("MD5 Hash not found",
				      family, skb, "L3 index %d", l3index);
			return SKB_DROP_REASON_TCP_MD5NOTFOUND;
		}
		return SKB_NOT_DROPPED_YET;
	}

	if (aoh)
		return tcp_inbound_ao_hash(sk, skb, family, req, l3index, aoh);

	return tcp_inbound_md5_hash(sk, skb, saddr, daddr, family,
				    l3index, md5_location);
}
		int family, int dif, int sdif);

#endif	/* _TCP_H */
+5 −37
Original line number Diff line number Diff line
@@ -19,6 +19,11 @@ struct tcp_ao_hdr {
	u8	rnext_keyid;
};

static inline u8 tcp_ao_hdr_maclen(const struct tcp_ao_hdr *aoh)
{
	return aoh->length - sizeof(struct tcp_ao_hdr);
}

struct tcp_ao_counters {
	atomic64_t	pkt_good;
	atomic64_t	pkt_bad;
@@ -144,43 +149,6 @@ extern struct static_key_false_deferred tcp_ao_needed;
#define static_branch_tcp_ao()	false
#endif

static inline bool tcp_hash_should_produce_warnings(void)
{
	return static_branch_tcp_md5() || static_branch_tcp_ao();
}

#define tcp_hash_fail(msg, family, skb, fmt, ...)			\
do {									\
	const struct tcphdr *th = tcp_hdr(skb);				\
	char hdr_flags[6];						\
	char *f = hdr_flags;						\
									\
	if (!tcp_hash_should_produce_warnings())			\
		break;							\
	if (th->fin)							\
		*f++ = 'F';						\
	if (th->syn)							\
		*f++ = 'S';						\
	if (th->rst)							\
		*f++ = 'R';						\
	if (th->psh)							\
		*f++ = 'P';						\
	if (th->ack)							\
		*f++ = '.';						\
	*f = 0;								\
	if ((family) == AF_INET) {					\
		net_info_ratelimited("%s for %pI4.%d->%pI4.%d [%s] " fmt "\n", \
				msg, &ip_hdr(skb)->saddr, ntohs(th->source), \
				&ip_hdr(skb)->daddr, ntohs(th->dest),	\
				hdr_flags, ##__VA_ARGS__);		\
	} else {							\
		net_info_ratelimited("%s for [%pI6c].%d->[%pI6c].%d [%s]" fmt "\n", \
				msg, &ipv6_hdr(skb)->saddr, ntohs(th->source), \
				&ipv6_hdr(skb)->daddr, ntohs(th->dest),	\
				hdr_flags, ##__VA_ARGS__);		\
	}								\
} while (0)

#ifdef CONFIG_TCP_AO
/* TCP-AO structures and functions */
struct tcp4_ao_context {
+317 −0
Original line number Diff line number Diff line
@@ -411,6 +411,323 @@ TRACE_EVENT(tcp_cong_state_set,
		  __entry->cong_state)
);

DECLARE_EVENT_CLASS(tcp_hash_event,

	TP_PROTO(const struct sock *sk, const struct sk_buff *skb),

	TP_ARGS(sk, skb),

	TP_STRUCT__entry(
		__field(__u64, net_cookie)
		__field(const void *, skbaddr)
		__field(const void *, skaddr)
		__field(int, state)

		/* sockaddr_in6 is always bigger than sockaddr_in */
		__array(__u8, saddr, sizeof(struct sockaddr_in6))
		__array(__u8, daddr, sizeof(struct sockaddr_in6))
		__field(int, l3index)

		__field(__u16, sport)
		__field(__u16, dport)
		__field(__u16, family)

		__field(bool, fin)
		__field(bool, syn)
		__field(bool, rst)
		__field(bool, psh)
		__field(bool, ack)
	),

	TP_fast_assign(
		const struct tcphdr *th = (const struct tcphdr *)skb->data;

		__entry->net_cookie = sock_net(sk)->net_cookie;
		__entry->skbaddr = skb;
		__entry->skaddr = sk;
		__entry->state = sk->sk_state;

		memset(__entry->saddr, 0, sizeof(struct sockaddr_in6));
		memset(__entry->daddr, 0, sizeof(struct sockaddr_in6));
		TP_STORE_ADDR_PORTS_SKB(skb, th, __entry->saddr, __entry->daddr);
		__entry->l3index = inet_sdif(skb) ? inet_iif(skb) : 0;

		/* For filtering use */
		__entry->sport = ntohs(th->source);
		__entry->dport = ntohs(th->dest);
		__entry->family = sk->sk_family;

		__entry->fin = th->fin;
		__entry->syn = th->syn;
		__entry->rst = th->rst;
		__entry->psh = th->psh;
		__entry->ack = th->ack;
	),

	TP_printk("net=%llu state=%s family=%s src=%pISpc dest=%pISpc L3index=%d [%c%c%c%c%c]",
		  __entry->net_cookie,
		  show_tcp_state_name(__entry->state),
		  show_family_name(__entry->family),
		  __entry->saddr, __entry->daddr,
		  __entry->l3index,
		  __entry->fin ? 'F' : ' ',
		  __entry->syn ? 'S' : ' ',
		  __entry->rst ? 'R' : ' ',
		  __entry->psh ? 'P' : ' ',
		  __entry->ack ? '.' : ' ')
);

DEFINE_EVENT(tcp_hash_event, tcp_hash_bad_header,

	TP_PROTO(const struct sock *sk, const struct sk_buff *skb),
	TP_ARGS(sk, skb)
);

DEFINE_EVENT(tcp_hash_event, tcp_hash_md5_required,

	TP_PROTO(const struct sock *sk, const struct sk_buff *skb),
	TP_ARGS(sk, skb)
);

DEFINE_EVENT(tcp_hash_event, tcp_hash_md5_unexpected,

	TP_PROTO(const struct sock *sk, const struct sk_buff *skb),
	TP_ARGS(sk, skb)
);

DEFINE_EVENT(tcp_hash_event, tcp_hash_md5_mismatch,

	TP_PROTO(const struct sock *sk, const struct sk_buff *skb),
	TP_ARGS(sk, skb)
);

DEFINE_EVENT(tcp_hash_event, tcp_hash_ao_required,

	TP_PROTO(const struct sock *sk, const struct sk_buff *skb),
	TP_ARGS(sk, skb)
);

DECLARE_EVENT_CLASS(tcp_ao_event,

	TP_PROTO(const struct sock *sk, const struct sk_buff *skb,
		 const __u8 keyid, const __u8 rnext, const __u8 maclen),

	TP_ARGS(sk, skb, keyid, rnext, maclen),

	TP_STRUCT__entry(
		__field(__u64, net_cookie)
		__field(const void *, skbaddr)
		__field(const void *, skaddr)
		__field(int, state)

		/* sockaddr_in6 is always bigger than sockaddr_in */
		__array(__u8, saddr, sizeof(struct sockaddr_in6))
		__array(__u8, daddr, sizeof(struct sockaddr_in6))
		__field(int, l3index)

		__field(__u16, sport)
		__field(__u16, dport)
		__field(__u16, family)

		__field(bool, fin)
		__field(bool, syn)
		__field(bool, rst)
		__field(bool, psh)
		__field(bool, ack)

		__field(__u8, keyid)
		__field(__u8, rnext)
		__field(__u8, maclen)
	),

	TP_fast_assign(
		const struct tcphdr *th = (const struct tcphdr *)skb->data;

		__entry->net_cookie = sock_net(sk)->net_cookie;
		__entry->skbaddr = skb;
		__entry->skaddr = sk;
		__entry->state = sk->sk_state;

		memset(__entry->saddr, 0, sizeof(struct sockaddr_in6));
		memset(__entry->daddr, 0, sizeof(struct sockaddr_in6));
		TP_STORE_ADDR_PORTS_SKB(skb, th, __entry->saddr, __entry->daddr);
		__entry->l3index = inet_sdif(skb) ? inet_iif(skb) : 0;

		/* For filtering use */
		__entry->sport = ntohs(th->source);
		__entry->dport = ntohs(th->dest);
		__entry->family = sk->sk_family;

		__entry->fin = th->fin;
		__entry->syn = th->syn;
		__entry->rst = th->rst;
		__entry->psh = th->psh;
		__entry->ack = th->ack;

		__entry->keyid = keyid;
		__entry->rnext = rnext;
		__entry->maclen = maclen;
	),

	TP_printk("net=%llu state=%s family=%s src=%pISpc dest=%pISpc L3index=%d [%c%c%c%c%c] keyid=%u rnext=%u maclen=%u",
		  __entry->net_cookie,
		  show_tcp_state_name(__entry->state),
		  show_family_name(__entry->family),
		  __entry->saddr, __entry->daddr,
		  __entry->l3index,
		  __entry->fin ? 'F' : ' ',
		  __entry->syn ? 'S' : ' ',
		  __entry->rst ? 'R' : ' ',
		  __entry->psh ? 'P' : ' ',
		  __entry->ack ? '.' : ' ',
		  __entry->keyid, __entry->rnext, __entry->maclen)
);

DEFINE_EVENT(tcp_ao_event, tcp_ao_handshake_failure,
	TP_PROTO(const struct sock *sk, const struct sk_buff *skb,
		 const __u8 keyid, const __u8 rnext, const __u8 maclen),
	TP_ARGS(sk, skb, keyid, rnext, maclen)
);

DEFINE_EVENT(tcp_ao_event, tcp_ao_wrong_maclen,
	TP_PROTO(const struct sock *sk, const struct sk_buff *skb,
		 const __u8 keyid, const __u8 rnext, const __u8 maclen),
	TP_ARGS(sk, skb, keyid, rnext, maclen)
);

DEFINE_EVENT(tcp_ao_event, tcp_ao_mismatch,
	TP_PROTO(const struct sock *sk, const struct sk_buff *skb,
		 const __u8 keyid, const __u8 rnext, const __u8 maclen),
	TP_ARGS(sk, skb, keyid, rnext, maclen)
);

DEFINE_EVENT(tcp_ao_event, tcp_ao_key_not_found,
	TP_PROTO(const struct sock *sk, const struct sk_buff *skb,
		 const __u8 keyid, const __u8 rnext, const __u8 maclen),
	TP_ARGS(sk, skb, keyid, rnext, maclen)
);

DEFINE_EVENT(tcp_ao_event, tcp_ao_rnext_request,
	TP_PROTO(const struct sock *sk, const struct sk_buff *skb,
		 const __u8 keyid, const __u8 rnext, const __u8 maclen),
	TP_ARGS(sk, skb, keyid, rnext, maclen)
);

DECLARE_EVENT_CLASS(tcp_ao_event_sk,

	TP_PROTO(const struct sock *sk, const __u8 keyid, const __u8 rnext),

	TP_ARGS(sk, keyid, rnext),

	TP_STRUCT__entry(
		__field(__u64, net_cookie)
		__field(const void *, skaddr)
		__field(int, state)

		/* sockaddr_in6 is always bigger than sockaddr_in */
		__array(__u8, saddr, sizeof(struct sockaddr_in6))
		__array(__u8, daddr, sizeof(struct sockaddr_in6))

		__field(__u16, sport)
		__field(__u16, dport)
		__field(__u16, family)

		__field(__u8, keyid)
		__field(__u8, rnext)
	),

	TP_fast_assign(
		const struct inet_sock *inet = inet_sk(sk);

		__entry->net_cookie = sock_net(sk)->net_cookie;
		__entry->skaddr = sk;
		__entry->state = sk->sk_state;

		memset(__entry->saddr, 0, sizeof(struct sockaddr_in6));
		memset(__entry->daddr, 0, sizeof(struct sockaddr_in6));
		TP_STORE_ADDR_PORTS(__entry, inet, sk);

		/* For filtering use */
		__entry->sport = ntohs(inet->inet_sport);
		__entry->dport = ntohs(inet->inet_dport);
		__entry->family = sk->sk_family;

		__entry->keyid = keyid;
		__entry->rnext = rnext;
	),

	TP_printk("net=%llu state=%s family=%s src=%pISpc dest=%pISpc keyid=%u rnext=%u",
		  __entry->net_cookie,
		  show_tcp_state_name(__entry->state),
		  show_family_name(__entry->family),
		  __entry->saddr, __entry->daddr,
		  __entry->keyid, __entry->rnext)
);

DEFINE_EVENT(tcp_ao_event_sk, tcp_ao_synack_no_key,
	TP_PROTO(const struct sock *sk, const __u8 keyid, const __u8 rnext),
	TP_ARGS(sk, keyid, rnext)
);

DECLARE_EVENT_CLASS(tcp_ao_event_sne,

	TP_PROTO(const struct sock *sk, __u32 new_sne),

	TP_ARGS(sk, new_sne),

	TP_STRUCT__entry(
		__field(__u64, net_cookie)
		__field(const void *, skaddr)
		__field(int, state)

		/* sockaddr_in6 is always bigger than sockaddr_in */
		__array(__u8, saddr, sizeof(struct sockaddr_in6))
		__array(__u8, daddr, sizeof(struct sockaddr_in6))

		__field(__u16, sport)
		__field(__u16, dport)
		__field(__u16, family)

		__field(__u32, new_sne)
	),

	TP_fast_assign(
		const struct inet_sock *inet = inet_sk(sk);

		__entry->net_cookie = sock_net(sk)->net_cookie;
		__entry->skaddr = sk;
		__entry->state = sk->sk_state;

		memset(__entry->saddr, 0, sizeof(struct sockaddr_in6));
		memset(__entry->daddr, 0, sizeof(struct sockaddr_in6));
		TP_STORE_ADDR_PORTS(__entry, inet, sk);

		/* For filtering use */
		__entry->sport = ntohs(inet->inet_sport);
		__entry->dport = ntohs(inet->inet_dport);
		__entry->family = sk->sk_family;

		__entry->new_sne = new_sne;
	),

	TP_printk("net=%llu state=%s family=%s src=%pISpc dest=%pISpc sne=%u",
		  __entry->net_cookie,
		  show_tcp_state_name(__entry->state),
		  show_family_name(__entry->family),
		  __entry->saddr, __entry->daddr,
		  __entry->new_sne)
);

DEFINE_EVENT(tcp_ao_event_sne, tcp_ao_snd_sne_update,
	TP_PROTO(const struct sock *sk, __u32 new_sne),
	TP_ARGS(sk, new_sne)
);

DEFINE_EVENT(tcp_ao_event_sne, tcp_ao_rcv_sne_update,
	TP_PROTO(const struct sock *sk, __u32 new_sne),
	TP_ARGS(sk, new_sne)
);

#endif /* _TRACE_TCP_H */

/* This part must be outside protection */
+80 −18
Original line number Diff line number Diff line
@@ -282,6 +282,7 @@
#include <asm/ioctls.h>
#include <net/busy_poll.h>
#include <net/hotdata.h>
#include <trace/events/tcp.h>
#include <net/rps.h>

/* Track pending CMSGs. */
@@ -4463,7 +4464,7 @@ int tcp_md5_hash_key(struct tcp_sigpool *hp,
EXPORT_SYMBOL(tcp_md5_hash_key);

/* Called with rcu_read_lock() */
enum skb_drop_reason
static enum skb_drop_reason
tcp_inbound_md5_hash(const struct sock *sk, const struct sk_buff *skb,
		     const void *saddr, const void *daddr,
		     int family, int l3index, const __u8 *hash_location)
@@ -4483,7 +4484,7 @@ tcp_inbound_md5_hash(const struct sock *sk, const struct sk_buff *skb,

	if (!key && hash_location) {
		NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMD5UNEXPECTED);
		tcp_hash_fail("Unexpected MD5 Hash found", family, skb, "");
		trace_tcp_hash_md5_unexpected(sk, skb);
		return SKB_DROP_REASON_TCP_MD5UNEXPECTED;
	}

@@ -4498,28 +4499,89 @@ tcp_inbound_md5_hash(const struct sock *sk, const struct sk_buff *skb,
							 NULL, skb);
	if (genhash || memcmp(hash_location, newhash, 16) != 0) {
		NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMD5FAILURE);
		if (family == AF_INET) {
			tcp_hash_fail("MD5 Hash failed", AF_INET, skb, "%s L3 index %d",
				      genhash ? "tcp_v4_calc_md5_hash failed"
				      : "", l3index);
		} else {
			if (genhash) {
				tcp_hash_fail("MD5 Hash failed",
					      AF_INET6, skb, "L3 index %d",
					      l3index);
		trace_tcp_hash_md5_mismatch(sk, skb);
		return SKB_DROP_REASON_TCP_MD5FAILURE;
	}
	return SKB_NOT_DROPPED_YET;
}
#else
static inline enum skb_drop_reason
tcp_inbound_md5_hash(const struct sock *sk, const struct sk_buff *skb,
		     const void *saddr, const void *daddr,
		     int family, int l3index, const __u8 *hash_location)
{
	return SKB_NOT_DROPPED_YET;
}

#endif

/* Called with rcu_read_lock() */
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)) {
		trace_tcp_hash_bad_header(sk, skb);
		return SKB_DROP_REASON_TCP_AUTH_HDR;
	}

	if (req) {
		if (tcp_rsk_used_ao(req) != !!aoh) {
			u8 keyid, rnext, maclen;

			if (aoh) {
				keyid = aoh->keyid;
				rnext = aoh->rnext_keyid;
				maclen = tcp_ao_hdr_maclen(aoh);
			} else {
				tcp_hash_fail("MD5 Hash mismatch",
					      AF_INET6, skb, "L3 index %d",
					      l3index);
				keyid = rnext = maclen = 0;
			}

			NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOBAD);
			trace_tcp_ao_handshake_failure(sk, skb, keyid, rnext, maclen);
			return SKB_DROP_REASON_TCP_AOFAILURE;
		}
		return SKB_DROP_REASON_TCP_MD5FAILURE;
	}

	/* 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, l3index, true)) {
			trace_tcp_hash_ao_required(sk, skb);
			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);
			trace_tcp_hash_md5_required(sk, skb);
			return SKB_DROP_REASON_TCP_MD5NOTFOUND;
		}
		return SKB_NOT_DROPPED_YET;
	}
EXPORT_SYMBOL(tcp_inbound_md5_hash);

#endif
	if (aoh)
		return tcp_inbound_ao_hash(sk, skb, family, req, l3index, aoh);

	return tcp_inbound_md5_hash(sk, skb, saddr, daddr, family,
				    l3index, md5_location);
}
EXPORT_SYMBOL_GPL(tcp_inbound_hash);

void tcp_done(struct sock *sk)
{
Loading