Commit af09a341 authored by Dmitry Safonov's avatar Dmitry Safonov Committed by David S. Miller

net/tcp: Add TCP-AO segments counters

Introduce segment counters that are useful for troubleshooting/debugging
as well as for writing tests.
Now there are global snmp counters as well as per-socket and per-key.
Co-developed-by: default avatarFrancesco Ruggeri <fruggeri@arista.com>
Signed-off-by: default avatarFrancesco Ruggeri <fruggeri@arista.com>
Co-developed-by: default avatarSalam Noureddine <noureddine@arista.com>
Signed-off-by: default avatarSalam Noureddine <noureddine@arista.com>
Signed-off-by: default avatarDmitry Safonov <dima@arista.com>
Acked-by: default avatarDavid Ahern <dsahern@kernel.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 0a3a8090
......@@ -168,17 +168,24 @@ enum skb_drop_reason {
*/
SKB_DROP_REASON_TCP_MD5FAILURE,
/**
* @SKB_DROP_REASON_TCP_AONOTFOUND: no TCP-AO hash and one was expected
* @SKB_DROP_REASON_TCP_AONOTFOUND: no TCP-AO hash and one was expected,
* corresponding to LINUX_MIB_TCPAOREQUIRED
*/
SKB_DROP_REASON_TCP_AONOTFOUND,
/**
* @SKB_DROP_REASON_TCP_AOUNEXPECTED: TCP-AO hash is present and it
* was not expected.
* was not expected, corresponding to LINUX_MIB_TCPAOKEYNOTFOUND
*/
SKB_DROP_REASON_TCP_AOUNEXPECTED,
/** @SKB_DROP_REASON_TCP_AOKEYNOTFOUND: TCP-AO key is unknown */
/**
* @SKB_DROP_REASON_TCP_AOKEYNOTFOUND: TCP-AO key is unknown,
* corresponding to LINUX_MIB_TCPAOKEYNOTFOUND
*/
SKB_DROP_REASON_TCP_AOKEYNOTFOUND,
/** @SKB_DROP_REASON_TCP_AOFAILURE: TCP-AO hash is wrong */
/**
* @SKB_DROP_REASON_TCP_AOFAILURE: TCP-AO hash is wrong,
* corresponding to LINUX_MIB_TCPAOBAD
*/
SKB_DROP_REASON_TCP_AOFAILURE,
/**
* @SKB_DROP_REASON_SOCKET_BACKLOG: failed to add skb to socket backlog (
......
......@@ -2712,7 +2712,7 @@ static inline int tcp_parse_auth_options(const struct tcphdr *th,
}
static inline bool tcp_ao_required(struct sock *sk, const void *saddr,
int family)
int family, bool stat_inc)
{
#ifdef CONFIG_TCP_AO
struct tcp_ao_info *ao_info;
......@@ -2724,8 +2724,13 @@ static inline bool tcp_ao_required(struct sock *sk, const void *saddr,
return false;
ao_key = tcp_ao_do_lookup(sk, saddr, family, -1, -1);
if (ao_info->ao_required || ao_key)
if (ao_info->ao_required || ao_key) {
if (stat_inc) {
NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOREQUIRED);
atomic64_inc(&ao_info->counters.ao_required);
}
return true;
}
#endif
return false;
}
......@@ -2747,9 +2752,11 @@ tcp_inbound_hash(struct sock *sk, const struct request_sock *req,
return SKB_DROP_REASON_TCP_AUTH_HDR;
if (req) {
if (tcp_rsk_used_ao(req) != !!aoh)
if (tcp_rsk_used_ao(req) != !!aoh) {
NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOBAD);
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
......@@ -2763,7 +2770,7 @@ tcp_inbound_hash(struct sock *sk, const struct request_sock *req,
* the last key is impossible to remove, so there's
* always at least one current_key.
*/
if (tcp_ao_required(sk, saddr, family))
if (tcp_ao_required(sk, saddr, family, true))
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);
......
......@@ -19,6 +19,13 @@ struct tcp_ao_hdr {
u8 rnext_keyid;
};
struct tcp_ao_counters {
atomic64_t pkt_good;
atomic64_t pkt_bad;
atomic64_t key_not_found;
atomic64_t ao_required;
};
struct tcp_ao_key {
struct hlist_node node;
union tcp_ao_addr addr;
......@@ -33,6 +40,8 @@ struct tcp_ao_key {
u8 rcvid;
u8 maclen;
struct rcu_head rcu;
atomic64_t pkt_good;
atomic64_t pkt_bad;
u8 traffic_keys[];
};
......@@ -81,6 +90,7 @@ struct tcp_ao_info {
*/
struct tcp_ao_key *current_key;
struct tcp_ao_key *rnext_key;
struct tcp_ao_counters counters;
u32 ao_required :1,
__unused :31;
__be32 lisn;
......
......@@ -297,6 +297,10 @@ enum
LINUX_MIB_TCPMIGRATEREQSUCCESS, /* TCPMigrateReqSuccess */
LINUX_MIB_TCPMIGRATEREQFAILURE, /* TCPMigrateReqFailure */
LINUX_MIB_TCPPLBREHASH, /* TCPPLBRehash */
LINUX_MIB_TCPAOREQUIRED, /* TCPAORequired */
LINUX_MIB_TCPAOBAD, /* TCPAOBad */
LINUX_MIB_TCPAOKEYNOTFOUND, /* TCPAOKeyNotFound */
LINUX_MIB_TCPAOGOOD, /* TCPAOGood */
__LINUX_MIB_MAX
};
......
......@@ -404,9 +404,15 @@ struct tcp_ao_info_opt { /* setsockopt(TCP_AO_INFO) */
__u32 set_current :1, /* corresponding ::current_key */
set_rnext :1, /* corresponding ::rnext */
ao_required :1, /* don't accept non-AO connects */
reserved :29; /* must be 0 */
set_counters :1, /* set/clear ::pkt_* counters */
reserved :28; /* must be 0 */
__u16 reserved2; /* padding, must be 0 */
__u8 current_key; /* KeyID to set as Current_key */
__u8 rnext; /* KeyID to set as Rnext_key */
__u64 pkt_good; /* verified segments */
__u64 pkt_bad; /* failed verification */
__u64 pkt_key_not_found; /* could not find a key to verify */
__u64 pkt_ao_required; /* segments missing TCP-AO sign */
} __attribute__((aligned(8)));
/* setsockopt(fd, IPPROTO_TCP, TCP_ZEROCOPY_RECEIVE, ...) */
......
......@@ -299,6 +299,10 @@ static const struct snmp_mib snmp4_net_list[] = {
SNMP_MIB_ITEM("TCPMigrateReqSuccess", LINUX_MIB_TCPMIGRATEREQSUCCESS),
SNMP_MIB_ITEM("TCPMigrateReqFailure", LINUX_MIB_TCPMIGRATEREQFAILURE),
SNMP_MIB_ITEM("TCPPLBRehash", LINUX_MIB_TCPPLBREHASH),
SNMP_MIB_ITEM("TCPAORequired", LINUX_MIB_TCPAOREQUIRED),
SNMP_MIB_ITEM("TCPAOBad", LINUX_MIB_TCPAOBAD),
SNMP_MIB_ITEM("TCPAOKeyNotFound", LINUX_MIB_TCPAOKEYNOTFOUND),
SNMP_MIB_ITEM("TCPAOGood", LINUX_MIB_TCPAOGOOD),
SNMP_MIB_SENTINEL
};
......
......@@ -182,6 +182,8 @@ static struct tcp_ao_key *tcp_ao_copy_key(struct sock *sk,
*new_key = *key;
INIT_HLIST_NODE(&new_key->node);
tcp_sigpool_get(new_key->tcp_sigpool_id);
atomic64_set(&new_key->pkt_good, 0);
atomic64_set(&new_key->pkt_bad, 0);
return new_key;
}
......@@ -771,8 +773,12 @@ tcp_ao_verify_hash(const struct sock *sk, const struct sk_buff *skb,
const struct tcphdr *th = tcp_hdr(skb);
void *hash_buf = NULL;
if (maclen != tcp_ao_maclen(key))
if (maclen != tcp_ao_maclen(key)) {
NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOBAD);
atomic64_inc(&info->counters.pkt_bad);
atomic64_inc(&key->pkt_bad);
return SKB_DROP_REASON_TCP_AOFAILURE;
}
hash_buf = kmalloc(tcp_ao_digest_size(key), GFP_ATOMIC);
if (!hash_buf)
......@@ -782,9 +788,15 @@ tcp_ao_verify_hash(const struct sock *sk, const struct sk_buff *skb,
tcp_ao_hash_skb(family, hash_buf, key, sk, skb, traffic_key,
(phash - (u8 *)th), sne);
if (memcmp(phash, hash_buf, maclen)) {
NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOBAD);
atomic64_inc(&info->counters.pkt_bad);
atomic64_inc(&key->pkt_bad);
kfree(hash_buf);
return SKB_DROP_REASON_TCP_AOFAILURE;
}
NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOGOOD);
atomic64_inc(&info->counters.pkt_good);
atomic64_inc(&key->pkt_good);
kfree(hash_buf);
return SKB_NOT_DROPPED_YET;
}
......@@ -804,8 +816,10 @@ tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb,
u32 sne = 0;
info = rcu_dereference(tcp_sk(sk)->ao_info);
if (!info)
if (!info) {
NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOKEYNOTFOUND);
return SKB_DROP_REASON_TCP_AOUNEXPECTED;
}
if (unlikely(th->syn)) {
sisn = th->seq;
......@@ -900,6 +914,8 @@ tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb,
return ret;
key_not_found:
NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOKEYNOTFOUND);
atomic64_inc(&info->counters.key_not_found);
return SKB_DROP_REASON_TCP_AOKEYNOTFOUND;
}
......@@ -1483,6 +1499,8 @@ static int tcp_ao_add_cmd(struct sock *sk, unsigned short int family,
key->keyflags = cmd.keyflags;
key->sndid = cmd.sndid;
key->rcvid = cmd.rcvid;
atomic64_set(&key->pkt_good, 0);
atomic64_set(&key->pkt_bad, 0);
ret = tcp_ao_parse_crypto(&cmd, key);
if (ret < 0)
......@@ -1699,7 +1717,7 @@ static int tcp_ao_info_cmd(struct sock *sk, unsigned short int family,
return -EINVAL;
}
if (cmd.reserved != 0)
if (cmd.reserved != 0 || cmd.reserved2 != 0)
return -EINVAL;
ao_info = setsockopt_ao_info(sk);
......@@ -1734,6 +1752,12 @@ static int tcp_ao_info_cmd(struct sock *sk, unsigned short int family,
goto out;
}
}
if (cmd.set_counters) {
atomic64_set(&ao_info->counters.pkt_good, cmd.pkt_good);
atomic64_set(&ao_info->counters.pkt_bad, cmd.pkt_bad);
atomic64_set(&ao_info->counters.key_not_found, cmd.pkt_key_not_found);
atomic64_set(&ao_info->counters.ao_required, cmd.pkt_ao_required);
}
ao_info->ao_required = cmd.ao_required;
if (new_current)
......
......@@ -1531,7 +1531,7 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, int optname,
/* Don't allow keys for peers that have a matching TCP-AO key.
* See the comment in tcp_ao_add_cmd()
*/
if (tcp_ao_required(sk, addr, AF_INET))
if (tcp_ao_required(sk, addr, AF_INET, false))
return -EKEYREJECTED;
return tcp_md5_do_add(sk, addr, AF_INET, prefixlen, l3index, flags,
......
......@@ -661,7 +661,7 @@ static int tcp_v6_parse_md5_keys(struct sock *sk, int optname,
/* Don't allow keys for peers that have a matching TCP-AO key.
* See the comment in tcp_ao_add_cmd()
*/
if (tcp_ao_required(sk, addr, AF_INET))
if (tcp_ao_required(sk, addr, AF_INET, false))
return -EKEYREJECTED;
return tcp_md5_do_add(sk, addr,
AF_INET, prefixlen, l3index, flags,
......@@ -673,7 +673,7 @@ static int tcp_v6_parse_md5_keys(struct sock *sk, int optname,
/* Don't allow keys for peers that have a matching TCP-AO key.
* See the comment in tcp_ao_add_cmd()
*/
if (tcp_ao_required(sk, addr, AF_INET6))
if (tcp_ao_required(sk, addr, AF_INET6, false))
return -EKEYREJECTED;
return tcp_md5_do_add(sk, addr, AF_INET6, prefixlen, l3index, flags,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment