Skip to content
Permalink
Browse files
tcp: authopt: Hook into tcp core
The tcp_authopt features exposes a minimal interface to the rest of the
TCP stack. Only a few functions are exposed and if the feature is
disabled they return neutral values, avoiding ifdefs in the rest of the
code. This approach is different from MD5.

There very few interactions with MD5 but tcp_parse_md5sig_option was
modifed to parse AO and MD5 simultaneously. If both are present the
packet is droppped as required by RFC5925.

Add calls into tcp authopt from send, receive and accept code.

Signed-off-by: Leonard Crestez <cdleonard@gmail.com>
  • Loading branch information
cdleonard authored and intel-lab-lkp committed Dec 8, 2021
1 parent a07399d commit 5935c41094c73eec0e3c39119d7bfb22de066c3b
Show file tree
Hide file tree
Showing 10 changed files with 665 additions and 25 deletions.
@@ -424,7 +424,29 @@ 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);
#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AUTHOPT)
int tcp_parse_sig_options(const struct tcphdr *th,
const u8 **md5ptr,
const u8 **aoptr);
#else
static inline int tcp_parse_sig_options(const struct tcphdr *th,
const u8 **md5ptr,
const u8 **aoptr)
{
aoptr = NULL;
md5ptr = NULL;
return 0;
}
#endif
static inline const u8 *tcp_parse_md5sig_option(const struct tcphdr *th)
{
const u8 *md5, *ao;
int ret;

ret = tcp_parse_sig_options(th, &md5, &ao);

return (md5 && !ao && !ret) ? md5 : NULL;
}

/*
* BPF SKB-less helpers
@@ -76,10 +76,111 @@ struct tcphdr_authopt {
};

#ifdef CONFIG_TCP_AUTHOPT
DECLARE_STATIC_KEY_FALSE(tcp_authopt_needed_key);
#define tcp_authopt_needed (static_branch_unlikely(&tcp_authopt_needed_key))

void tcp_authopt_free(struct sock *sk, struct tcp_authopt_info *info);
void tcp_authopt_clear(struct sock *sk);
int tcp_set_authopt(struct sock *sk, sockptr_t optval, unsigned int optlen);
int tcp_get_authopt_val(struct sock *sk, struct tcp_authopt *key);
int tcp_set_authopt_key(struct sock *sk, sockptr_t optval, unsigned int optlen);
struct tcp_authopt_key_info *__tcp_authopt_select_key(
const struct sock *sk,
struct tcp_authopt_info *info,
const struct sock *addr_sk,
u8 *rnextkeyid);
static inline struct tcp_authopt_key_info *tcp_authopt_select_key(
const struct sock *sk,
const struct sock *addr_sk,
struct tcp_authopt_info **info,
u8 *rnextkeyid)
{
if (tcp_authopt_needed) {
*info = rcu_dereference(tcp_sk(sk)->authopt_info);

if (*info)
return __tcp_authopt_select_key(sk, *info, addr_sk, rnextkeyid);
}
return NULL;
}
int tcp_authopt_hash(
char *hash_location,
struct tcp_authopt_key_info *key,
struct tcp_authopt_info *info,
struct sock *sk, struct sk_buff *skb);
int __tcp_authopt_openreq(struct sock *newsk, const struct sock *oldsk, struct request_sock *req);
static inline int tcp_authopt_openreq(
struct sock *newsk,
const struct sock *oldsk,
struct request_sock *req)
{
if (!rcu_dereference(tcp_sk(oldsk)->authopt_info))
return 0;
else
return __tcp_authopt_openreq(newsk, oldsk, req);
}
void __tcp_authopt_finish_connect(struct sock *sk, struct sk_buff *skb,
struct tcp_authopt_info *info);
static inline void tcp_authopt_finish_connect(struct sock *sk, struct sk_buff *skb)
{
struct tcp_authopt_info *info;

if (tcp_authopt_needed) {
info = rcu_dereference_protected(tcp_sk(sk)->authopt_info,
lockdep_sock_is_held(sk));

if (info)
__tcp_authopt_finish_connect(sk, skb, info);
}
}
static inline void tcp_authopt_time_wait(
struct tcp_timewait_sock *tcptw,
struct tcp_sock *tp)
{
if (tcp_authopt_needed) {
/* Transfer ownership of authopt_info to the twsk
* This requires no other users of the origin sock.
*/
sock_owned_by_me((struct sock *)tp);
tcptw->tw_authopt_info = tp->authopt_info;
tp->authopt_info = NULL;
} else {
tcptw->tw_authopt_info = NULL;
}
}
/** tcp_authopt_inbound_check - check for valid TCP-AO signature.
*
* Return negative ERRNO on error, 0 if not present and 1 if present and valid.
*
* If the AO signature is present and valid then caller skips MD5 check.
*/
int __tcp_authopt_inbound_check(
struct sock *sk,
struct sk_buff *skb,
struct tcp_authopt_info *info,
const u8 *opt);
static inline int tcp_authopt_inbound_check(struct sock *sk, struct sk_buff *skb, const u8 *opt)
{
if (tcp_authopt_needed) {
struct tcp_authopt_info *info = rcu_dereference(tcp_sk(sk)->authopt_info);

if (info)
return __tcp_authopt_inbound_check(sk, skb, info, opt);
}
return 0;
}
static inline int tcp_authopt_inbound_check_req(struct request_sock *req, struct sk_buff *skb,
const u8 *opt)
{
if (tcp_authopt_needed) {
struct sock *lsk = req->rsk_listener;
struct tcp_authopt_info *info = rcu_dereference(tcp_sk(lsk)->authopt_info);

if (info)
return __tcp_authopt_inbound_check((struct sock *)req, skb, info, opt);
}
return 0;
}
#else
static inline int tcp_set_authopt(struct sock *sk, sockptr_t optval, unsigned int optlen)
{
@@ -89,13 +190,47 @@ static inline int tcp_get_authopt_val(struct sock *sk, struct tcp_authopt *key)
{
return -ENOPROTOOPT;
}
static inline void tcp_authopt_free(struct sock *sk, struct tcp_authopt_info *info)
{
}
static inline void tcp_authopt_clear(struct sock *sk)
{
}
static inline int tcp_set_authopt_key(struct sock *sk, sockptr_t optval, unsigned int optlen)
{
return -ENOPROTOOPT;
}
static inline int tcp_authopt_hash(
char *hash_location,
struct tcp_authopt_key_info *key,
struct tcp_authopt_key *info,
struct sock *sk, struct sk_buff *skb)
{
return -EINVAL;
}
static inline int tcp_authopt_openreq(struct sock *newsk,
const struct sock *oldsk,
struct request_sock *req)
{
return 0;
}
static inline void tcp_authopt_finish_connect(struct sock *sk, struct sk_buff *skb)
{
}
static inline void tcp_authopt_time_wait(
struct tcp_timewait_sock *tcptw,
struct tcp_sock *tp)
{
}
static inline int tcp_authopt_inbound_check(struct sock *sk, struct sk_buff *skb, const u8 *opt)
{
return 0;
}
static inline int tcp_authopt_inbound_check_req(struct request_sock *sk, struct sk_buff *skb,
const u8 *opt)
{
return 0;
}
#endif

#endif /* _LINUX_TCP_AUTHOPT_H */
@@ -292,6 +292,7 @@ enum
LINUX_MIB_TCPDSACKIGNOREDDUBIOUS, /* TCPDSACKIgnoredDubious */
LINUX_MIB_TCPMIGRATEREQSUCCESS, /* TCPMigrateReqSuccess */
LINUX_MIB_TCPMIGRATEREQFAILURE, /* TCPMigrateReqFailure */
LINUX_MIB_TCPAUTHOPTFAILURE, /* TCPAuthOptFailure */
__LINUX_MIB_MAX
};

@@ -297,6 +297,7 @@ static const struct snmp_mib snmp4_net_list[] = {
SNMP_MIB_ITEM("TCPDSACKIgnoredDubious", LINUX_MIB_TCPDSACKIGNOREDDUBIOUS),
SNMP_MIB_ITEM("TCPMigrateReqSuccess", LINUX_MIB_TCPMIGRATEREQSUCCESS),
SNMP_MIB_ITEM("TCPMigrateReqFailure", LINUX_MIB_TCPMIGRATEREQFAILURE),
SNMP_MIB_ITEM("TCPAuthOptFailure", LINUX_MIB_TCPAUTHOPTFAILURE),
SNMP_MIB_SENTINEL
};

0 comments on commit 5935c41

Please sign in to comment.